Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added public API to support BIP32-Ed25519 style keys. #1278

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions donna.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,76 @@ ed25519_sign_open(const byte *message, size_t messageLength, const byte publicKe
int
ed25519_sign_open(std::istream& stream, const byte publicKey[32], const byte signature[64]);

//**************************** bip32-ed25519 ****************************//

/// \brief Extend the Ed25519 key.
/// \param secretKey byte array for the extended private key
/// \param secretKey byte array with the private key (seed)
/// \return 0 on success, non-0 otherwise
int bip32_ed25519_extend(byte secretKey[64], const byte seed[32]);

/// \brief Creates a public key from an extended secret key
/// \param publicKey byte array for the public key
/// \param secretKey byte array with the extended private key
/// \return 0 on success, non-0 otherwise
/// \details ed25519_publickey() generates a public key from an extended
/// secret key. Internally ed25519_publickey() performs a scalar
/// multiplication using the secret key and then writes the result to
/// <tt>publicKey</tt>.
int bip32_ed25519_publickey(byte publicKey[32], const byte secretKey[64]);

/// \brief Creates a signature on a message
/// \param message byte array with the message
/// \param messageLength size of the message, in bytes
/// \param publicKey byte array with the public key
/// \param secretKey byte array with the extended private key
/// \param signature byte array for the signature
/// \return 0 on success, non-0 otherwise
/// \details ed25519_sign() generates a signature on a message using
/// the public and private keys. The various buffers can be exact
/// sizes, and do not require extra space like when using the
/// NaCl library functions.
/// \details At the moment the hash function for signing is fixed at
/// SHA512.
int bip32_ed25519_sign(const byte* message, size_t messageLength, const byte secretKey[64], const byte publicKey[32], byte signature[64]);

/// \brief Creates a signature on a message
/// \param stream std::istream derived class
/// \param publicKey byte array with the public key
/// \param secretKey byte array with the extended private key
/// \param signature byte array for the signature
/// \return 0 on success, non-0 otherwise
/// \details ed25519_sign() generates a signature on a message using
/// the public and private keys. The various buffers can be exact
/// sizes, and do not require extra space like when using the
/// NaCl library functions.
/// \details This ed25519_sign() overload handles large streams. It
/// was added for signing and verifying files that are too large
/// for a memory allocation.
/// \details At the moment the hash function for signing is fixed at
/// SHA512.
int bip32_ed25519_sign(std::istream& stream, const byte secretKey[64], const byte publicKey[32], byte signature[64]);

/// \brief Add the lower bytes of two secret keys as scalar values.
/// \param secretKey1 byte array with the extended private key
/// \param secretKey2 byte array with the extended private key
/// \param res 32 byte array for the result
/// \details Add the lower 32 bytes of two extended secret keys as two large scalars.
/// The result is a 32 byte array. This may be used during child key
/// derivation when the keys are part of BIP32 style wallets.
/// \details We only need the leftmost 32 bytes of the extended secret key.
int bip32_ed25519_scalar_add(const byte secretKey1[64], const byte secretKey2[64], byte res[32]);

/// \brief Add two public keys as curve 25519 points.
/// \param publicKey1 byte array with the first public key to add.
/// \param publicKey2 byte array with the second public key to add.
/// \param res byte array with the public key result.
/// \return A public key that is the result of the summation.
/// \details Add two public keys as two points on the elliptic curve 25519. This is
/// useful during child key derivation when the keys are part of BIP32 style
/// wallets.
int bip32_ed25519_point_add(const byte publicKey1[32], const byte publicKey2[32], byte res[32]);

//****************************** Internal ******************************//

#ifndef CRYPTOPP_DOXYGEN_PROCESSING
Expand Down
170 changes: 170 additions & 0 deletions donna_32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2099,4 +2099,174 @@ ed25519_sign_open(std::istream& stream, const byte publicKey[32], const byte sig
NAMESPACE_END // Donna
NAMESPACE_END // CryptoPP

//**************************** bip32-ed25519 ****************************//

NAMESPACE_BEGIN(CryptoPP)
NAMESPACE_BEGIN(Donna)

int
bip32_ed25519_extend(byte secretKey[64], const byte seed[32])
{
using namespace CryptoPP::Donna::Ed25519;

ed25519_extsk(secretKey, seed);
return 0;
}

int
bip32_ed25519_publickey_CXX(byte publicKey[32], const byte secretKey[64])
{
using namespace CryptoPP::Donna::Ed25519;

bignum256modm a;
ALIGN(ALIGN_SPEC) ge25519 A;

/* A = aB */
expand256_modm(a, secretKey, 32);
ge25519_scalarmult_base_niels(&A, ge25519_niels_base_multiples, a);
ge25519_pack(publicKey, &A);

return 0;
}

int
bip32_ed25519_publickey(byte publicKey[32], const byte secretKey[32])
{
return bip32_ed25519_publickey_CXX(publicKey, secretKey);
}

int
bip32_ed25519_sign_CXX(std::istream& stream, const byte extsk[64], const byte pk[32], byte RS[64])
{
using namespace CryptoPP::Donna::Ed25519;

bignum256modm r, S, a;
ALIGN(ALIGN_SPEC) ge25519 R;
hash_512bits hashr, hram;

// Unfortunately we need to read the stream twice. The first time calculates
// 'r = H(aExt[32..64], m)'. The second time calculates 'S = H(R,A,m)'. There
// is a data dependency due to hashing 'RS' with 'R = [r]B' that does not
// allow us to read the stream once.
std::streampos where = stream.tellg();

/* r = H(aExt[32..64], m) */
SHA512 hash;
hash.Update(extsk + 32, 32);
UpdateFromStream(hash, stream);
hash.Final(hashr);
expand256_modm(r, hashr, 64);

/* R = rB */
ge25519_scalarmult_base_niels(&R, ge25519_niels_base_multiples, r);
ge25519_pack(RS, &R);

// Reset stream for the second digest
stream.clear();
stream.seekg(where);

/* S = H(R,A,m).. */
ed25519_hram(hram, RS, pk, stream);
expand256_modm(S, hram, 64);

/* S = H(R,A,m)a */
expand256_modm(a, extsk, 32);
mul256_modm(S, S, a);

/* S = (r + H(R,A,m)a) */
add256_modm(S, S, r);

/* S = (r + H(R,A,m)a) mod L */
contract256_modm(RS + 32, S);

return 0;
}

int
bip32_ed25519_sign_CXX(const byte *m, size_t mlen, const byte extsk[64], const byte pk[32], byte RS[64])
{
using namespace CryptoPP::Donna::Ed25519;

bignum256modm r, S, a;
ALIGN(ALIGN_SPEC) ge25519 R;
hash_512bits hashr, hram;

/* r = H(aExt[32..64], m) */
SHA512 hash;
hash.Update(extsk + 32, 32);
hash.Update(m, mlen);
hash.Final(hashr);
expand256_modm(r, hashr, 64);

/* R = rB */
ge25519_scalarmult_base_niels(&R, ge25519_niels_base_multiples, r);
ge25519_pack(RS, &R);

/* S = H(R,A,m).. */
ed25519_hram(hram, RS, pk, m, mlen);
expand256_modm(S, hram, 64);

/* S = H(R,A,m)a */
expand256_modm(a, extsk, 32);
mul256_modm(S, S, a);

/* S = (r + H(R,A,m)a) */
add256_modm(S, S, r);

/* S = (r + H(R,A,m)a) mod L */
contract256_modm(RS + 32, S);

return 0;
}

int
bip32_ed25519_sign(std::istream& stream, const byte secretKey[64], const byte publicKey[32],
byte signature[64])
{
return bip32_ed25519_sign_CXX(stream, secretKey, publicKey, signature);
}

int
bip32_ed25519_sign(const byte* message, size_t messageLength, const byte secretKey[64],
const byte publicKey[32], byte signature[64])
{
return bip32_ed25519_sign_CXX(message, messageLength, secretKey, publicKey, signature);
}

int
bip32_ed25519_scalar_add(const byte secretKey1[64], const byte secretKey2[64], byte res[32])
{
using namespace CryptoPP::Donna::Ed25519;

bignum256modm s1, s2;
expand256_modm(s1, secretKey1, 32);
expand256_modm(s2, secretKey2, 32);
add256_modm(s1, s1, s2);
contract256_modm(res, s1);

return 0;
}

int
bip32_ed25519_point_add(const byte publicKey1[32], const byte publicKey2[32], byte res[32])
{
using namespace CryptoPP::Donna::Ed25519;

ALIGN(ALIGN_SPEC) ge25519 R, P, Q;

if (!ge25519_unpack_negative_vartime(&P, publicKey1))
return -1;
if (!ge25519_unpack_negative_vartime(&Q, publicKey2))
return -1;

ge25519_add(&R, &P, &Q);
ge25519_pack(res, &R);

res[31] ^= 0x80;
return 0;
}

NAMESPACE_END // Donna
NAMESPACE_END // CryptoPP

#endif // CRYPTOPP_CURVE25519_32BIT
Loading