From d62674b56145b6988cd0a9d8a658dff7cb6d8ec8 Mon Sep 17 00:00:00 2001 From: Jeffrey Walton Date: Mon, 24 Dec 2018 17:17:32 -0500 Subject: Add ed25519 (GH #764, PR #767) Add ed25519 --- xed25519.cpp | 790 ++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 680 insertions(+), 110 deletions(-) (limited to 'xed25519.cpp') diff --git a/xed25519.cpp b/xed25519.cpp index 3d118839..c149c9ed 100644 --- a/xed25519.cpp +++ b/xed25519.cpp @@ -1,7 +1,8 @@ // xed25519.cpp - written and placed in public domain by Jeffrey Walton // Crypto++ specific implementation wrapped around Andrew -// Moon's public domain curve25519-donna. Also see -// https://github.com/floodyberry/curve25519-donna. +// Moon's public domain curve25519-donna and ed25519-donna, +// https://github.com/floodyberry/curve25519-donna and +// https://github.com/floodyberry/ed25519-donna. #include "pch.h" @@ -9,6 +10,7 @@ #include "asn.h" #include "integer.h" #include "filters.h" +#include "stdcpp.h" #include "xed25519.h" #include "donna.h" @@ -43,68 +45,36 @@ const byte blacklist[][32] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, { 0xdb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } - }; +}; ANONYMOUS_NAMESPACE_END NAMESPACE_BEGIN(CryptoPP) -bool x25519::IsClamped(const byte x[32]) -{ - return (x[0] & 248) == x[0] && (x[31] & 127) == x[31] && (x[31] | 64) == x[31]; -} +// ******************** x25519 Agreement ************************* // -// See the comments for the code in tweetnacl.cpp -bool x25519::IsSmallOrder(const byte y[32]) +x25519::x25519(const byte y[PUBLIC_KEYLENGTH], const byte x[SECRET_KEYLENGTH]) { - // The magic 12 is the count of blaklisted points - byte c[12] = { 0 }; - for (size_t j = 0; j < 32; j++) { - for (size_t i = 0; i < COUNTOF(blacklist); i++) { - c[i] |= y[j] ^ blacklist[i][j]; - } - } - - unsigned int k = 0; - for (size_t i = 0; i < COUNTOF(blacklist); i++) { - k |= (c[i] - 1); - } - - return (bool) ((k >> 8) & 1); -} - -void x25519::ClampKey(byte x[32]) -{ - x[0] &= 248; - x[31] &= 127; - x[31] |= 64; -} - -x25519::x25519(const byte y[32], const byte x[32]) -{ - std::memcpy(m_pk, y, 32); - std::memcpy(m_sk, x, 32); + std::memcpy(m_pk, y, SECRET_KEYLENGTH); + std::memcpy(m_sk, x, PUBLIC_KEYLENGTH); CRYPTOPP_ASSERT(IsClamped(m_sk) == true); CRYPTOPP_ASSERT(IsSmallOrder(m_pk) == false); } -x25519::x25519(const byte x[32]) +x25519::x25519(const byte x[SECRET_KEYLENGTH]) { - std::memcpy(m_sk, x, 32); - GeneratePublicKey(NullRNG(), m_sk, m_pk); - - CRYPTOPP_ASSERT(IsClamped(m_sk) == true); - CRYPTOPP_ASSERT(IsSmallOrder(m_pk) == false); + std::memcpy(m_sk, x, SECRET_KEYLENGTH); + Donna::curve25519_mult(m_pk, m_sk); } x25519::x25519(const Integer &y, const Integer &x) { - ArraySink ys(m_pk, 32); - y.Encode(ys, 32); + CRYPTOPP_ASSERT(y.MinEncodedSize() <= PUBLIC_KEYLENGTH); + CRYPTOPP_ASSERT(x.MinEncodedSize() <= SECRET_KEYLENGTH); - ArraySink xs(m_sk, 32); - x.Encode(xs, 32); + y.Encode(m_pk, PUBLIC_KEYLENGTH); std::reverse(m_pk+0, m_pk+PUBLIC_KEYLENGTH); + x.Encode(m_sk, SECRET_KEYLENGTH); std::reverse(m_sk+0, m_sk+SECRET_KEYLENGTH); CRYPTOPP_ASSERT(IsClamped(m_sk) == true); CRYPTOPP_ASSERT(IsSmallOrder(m_pk) == false); @@ -112,9 +82,11 @@ x25519::x25519(const Integer &y, const Integer &x) x25519::x25519(const Integer &x) { - ArraySink xs(m_sk, 32); - x.Encode(xs, 32); - GeneratePublicKey(NullRNG(), m_sk, m_pk); + CRYPTOPP_ASSERT(x.MinEncodedSize() <= SECRET_KEYLENGTH); + + x.Encode(m_sk, SECRET_KEYLENGTH); + std::reverse(m_sk+0, m_sk+SECRET_KEYLENGTH); + Donna::curve25519_mult(m_pk, m_sk); CRYPTOPP_ASSERT(IsClamped(m_sk) == true); CRYPTOPP_ASSERT(IsSmallOrder(m_pk) == false); @@ -122,68 +94,156 @@ x25519::x25519(const Integer &x) x25519::x25519(RandomNumberGenerator &rng) { - GeneratePrivateKey(rng, m_sk); - GeneratePublicKey(NullRNG(), m_sk, m_pk); - - CRYPTOPP_ASSERT(IsClamped(m_sk) == true); - CRYPTOPP_ASSERT(IsSmallOrder(m_pk) == false); + rng.GenerateBlock(m_sk, SECRET_KEYLENGTH); + m_sk[0] &= 248; m_sk[31] &= 127; m_sk[31] |= 64; + Donna::curve25519_mult(m_pk, m_sk); } x25519::x25519(BufferedTransformation ¶ms) { - // TODO: Fix the on-disk format once we determine what it is. - BERSequenceDecoder seq(params); + Load(params); +} - size_t read; byte unused; +void x25519::ClampKeys(byte y[PUBLIC_KEYLENGTH], byte x[SECRET_KEYLENGTH]) const +{ + x[0] &= 248; x[31] &= 127; x[31] |= 64; + Donna::curve25519_mult(y, x); +} + +bool x25519::IsClamped(const byte x[SECRET_KEYLENGTH]) const +{ + return (x[0] & 248) == x[0] && (x[31] & 127) == x[31] && (x[31] | 64) == x[31]; +} - BERSequenceDecoder sk(seq, BIT_STRING); - CRYPTOPP_ASSERT(sk.MaxRetrievable() >= 33); +bool x25519::IsSmallOrder(const byte y[PUBLIC_KEYLENGTH]) const +{ + // The magic 12 is the count of blaklisted points + byte c[12] = { 0 }; + for (size_t j = 0; j < PUBLIC_KEYLENGTH; j++) { + for (size_t i = 0; i < COUNTOF(blacklist); i++) { + c[i] |= y[j] ^ blacklist[i][j]; + } + } - read = sk.Get(unused); // unused bits - CRYPTOPP_ASSERT(read == 1 && unused == 0); + unsigned int k = 0; + for (size_t i = 0; i < COUNTOF(blacklist); i++) { + k |= (c[i] - 1); + } - read = sk.Get(m_sk, 32); - sk.MessageEnd(); + return (bool)((k >> 8) & 1); +} - if (read != 32) - throw BERDecodeErr(); +void x25519::BERDecodeAndCheckAlgorithmID(BufferedTransformation &bt) +{ + // We have not yet determined the OID to use for this object. + // We can't use OID's decoder because it throws BERDecodeError + // if the OIDs do not match. + OID oid(bt); + + if (!m_oid.Empty() && m_oid != oid) + BERDecodeError(); // Only accept user specified OID + else if (oid == ASN1::curve25519() || oid == ASN1::X25519()) + m_oid = oid; // Accept any of the x25519 OIDs + else + BERDecodeError(); +} - if (seq.EndReached()) - { - GeneratePublicKey(NullRNG(), m_sk, m_pk); - } - else - { - BERSequenceDecoder pk(seq, OCTET_STRING); - CRYPTOPP_ASSERT(pk.MaxRetrievable() >= 32); - read = pk.Get(m_pk, 32); - pk.MessageEnd(); +void x25519::BERDecode(BufferedTransformation &bt) +{ + // https://tools.ietf.org/html/rfc8410, section 7 and + // https://www.cryptopp.com/wiki/curve25519_keys + BERSequenceDecoder privateKeyInfo(bt); + word32 version; + BERDecodeUnsigned(privateKeyInfo, version, INTEGER, 0, 1); // check version + + BERSequenceDecoder algorithm(privateKeyInfo); + // GetAlgorithmID().BERDecodeAndCheck(algorithm); + BERDecodeAndCheckAlgorithmID(algorithm); + algorithm.MessageEnd(); + + BERGeneralDecoder octetString(privateKeyInfo, OCTET_STRING); + BERDecodePrivateKey(octetString, false, (size_t)privateKeyInfo.RemainingLength()); + octetString.MessageEnd(); + + bool generatePublicKey = true; + if (version == 1) + { + BERGeneralDecoder publicKey(privateKeyInfo, CONTEXT_SPECIFIC | CONSTRUCTED | 1); + SecByteBlock subjectPublicKey; + unsigned int unusedBits; + BERDecodeBitString(publicKey, subjectPublicKey, unusedBits); + CRYPTOPP_ASSERT(unusedBits == 0); + CRYPTOPP_ASSERT(subjectPublicKey.size() == PUBLIC_KEYLENGTH); + if (subjectPublicKey.size() != PUBLIC_KEYLENGTH) + BERDecodeError(); + std::memcpy(m_pk.begin(), subjectPublicKey, PUBLIC_KEYLENGTH); + generatePublicKey = false; + publicKey.MessageEnd(); + } - if (read != 32) - throw BERDecodeErr(); - } + privateKeyInfo.MessageEnd(); - seq.MessageEnd(); + if (generatePublicKey) + Donna::curve25519_mult(m_pk, m_sk); CRYPTOPP_ASSERT(IsClamped(m_sk) == true); CRYPTOPP_ASSERT(IsSmallOrder(m_pk) == false); } -void x25519::DEREncode(BufferedTransformation ¶ms) const +void x25519::DEREncode(BufferedTransformation &bt, int version) const { - // TODO: Fix the on-disk format once we determine what it is. - DERSequenceEncoder seq(params); + // https://tools.ietf.org/html/rfc8410, section 7 and + // https://www.cryptopp.com/wiki/curve25519_keys + CRYPTOPP_ASSERT(version == 0 || version == 1); - DERSequenceEncoder sk(seq, BIT_STRING); - sk.Put((byte)0); // unused bits - sk.Put(m_sk, 32); - sk.MessageEnd(); + DERSequenceEncoder privateKeyInfo(bt); + DEREncodeUnsigned(privateKeyInfo, version); - DERSequenceEncoder pk(seq, OCTET_STRING); - pk.Put(m_pk, 32); - pk.MessageEnd(); + DERSequenceEncoder algorithm(privateKeyInfo); + GetAlgorithmID().DEREncode(algorithm); + algorithm.MessageEnd(); - seq.MessageEnd(); + DERGeneralEncoder octetString(privateKeyInfo, OCTET_STRING); + DEREncodePrivateKey(octetString); + octetString.MessageEnd(); + + if (version == 1) + { + DERGeneralEncoder publicKey(privateKeyInfo, CONTEXT_SPECIFIC | CONSTRUCTED | 1); + DEREncodeBitString(publicKey, m_pk, PUBLIC_KEYLENGTH); + publicKey.MessageEnd(); + } + + privateKeyInfo.MessageEnd(); +} + +void x25519::BERDecodePrivateKey(BufferedTransformation &bt, bool parametersPresent, size_t /*size*/) +{ + // https://tools.ietf.org/html/rfc8410 and + // https://www.cryptopp.com/wiki/curve25519_keys + + BERGeneralDecoder privateKey(bt, OCTET_STRING); + + if (!privateKey.IsDefiniteLength()) + BERDecodeError(); + + size_t size = privateKey.Get(m_sk, SECRET_KEYLENGTH); + if (size != SECRET_KEYLENGTH) + BERDecodeError(); + + // We don't know how to decode them + if (parametersPresent) + BERDecodeError(); + + privateKey.MessageEnd(); +} + +void x25519::DEREncodePrivateKey(BufferedTransformation &bt) const +{ + // https://tools.ietf.org/html/rfc8410 + DERGeneralEncoder privateKey(bt, OCTET_STRING); + privateKey.Put(m_sk, SECRET_KEYLENGTH); + privateKey.MessageEnd(); } bool x25519::Validate(RandomNumberGenerator &rng, unsigned int level) const @@ -202,18 +262,28 @@ bool x25519::Validate(RandomNumberGenerator &rng, unsigned int level) const bool x25519::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const { - if (valueType == typeid(ConstByteArrayParameter)) + if (std::strcmp(name, Name::PrivateExponent()) == 0 || std::strcmp(name, "SecretKey") == 0) { - if (std::strcmp(name, "SecretKey") == 0) - { - std::memcpy(pValue, m_sk, 32); - return true; - } - else if (std::strcmp(name, "PublicKey") == 0) - { - std::memcpy(pValue, m_pk, 32); - return true; - } + this->ThrowIfTypeMismatch(name, typeid(ConstByteArrayParameter), valueType); + reinterpret_cast(pValue)->Assign(m_sk, SECRET_KEYLENGTH, false); + return true; + } + + if (std::strcmp(name, Name::PublicElement()) == 0) + { + this->ThrowIfTypeMismatch(name, typeid(ConstByteArrayParameter), valueType); + reinterpret_cast(pValue)->Assign(m_pk, PUBLIC_KEYLENGTH, false); + return true; + } + + if (std::strcmp(name, Name::GroupOID()) == 0) + { + if (m_oid.Empty()) + return false; + + this->ThrowIfTypeMismatch(name, typeid(OID), valueType); + *reinterpret_cast(pValue) = m_oid; + return true; } return false; @@ -222,27 +292,44 @@ bool x25519::GetVoidValue(const char *name, const std::type_info &valueType, voi void x25519::AssignFrom(const NameValuePairs &source) { ConstByteArrayParameter val; - if (source.GetValue("SecretKey", val)) + if (source.GetValue(Name::PrivateExponent(), val) || source.GetValue("SecretKey", val)) { - std::memcpy(m_sk, val.begin(), 32); + std::memcpy(m_sk, val.begin(), SECRET_KEYLENGTH); } - else if (source.GetValue("PublicKey", val)) + + if (source.GetValue(Name::PublicElement(), val)) { - std::memcpy(m_pk, val.begin(), 32); + std::memcpy(m_pk, val.begin(), PUBLIC_KEYLENGTH); + } + + OID oid; + if (source.GetValue(Name::GroupOID(), oid)) + { + m_oid = oid; } } +void x25519::GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs ¶ms) +{ + ConstByteArrayParameter seed; + if (params.GetValue(Name::Seed(), seed) && rng.CanIncorporateEntropy()) + rng.IncorporateEntropy(seed.begin(), seed.size()); + + rng.GenerateBlock(m_sk, SECRET_KEYLENGTH); + m_sk[0] &= 248; m_sk[31] &= 127; m_sk[31] |= 64; + Donna::curve25519_mult(m_pk, m_sk); +} + void x25519::GeneratePrivateKey(RandomNumberGenerator &rng, byte *privateKey) const { - rng.GenerateBlock(privateKey, 32); - ClampKey(privateKey); + rng.GenerateBlock(privateKey, SECRET_KEYLENGTH); + privateKey[0] &= 248; privateKey[31] &= 127; privateKey[31] |= 64; } void x25519::GeneratePublicKey(RandomNumberGenerator &rng, const byte *privateKey, byte *publicKey) const { CRYPTOPP_UNUSED(rng); - - (void)Donna::curve25519(publicKey, privateKey); + Donna::curve25519_mult(publicKey, privateKey); } bool x25519::Agree(byte *agreedValue, const byte *privateKey, const byte *otherPublicKey, bool validateOtherPublicKey) const @@ -253,7 +340,490 @@ bool x25519::Agree(byte *agreedValue, const byte *privateKey, const byte *otherP if (validateOtherPublicKey && IsSmallOrder(otherPublicKey)) return false; - return Donna::curve25519(agreedValue, privateKey, otherPublicKey) == 0; + return Donna::curve25519_mult(agreedValue, privateKey, otherPublicKey) == 0; +} + +// ******************** ed25519 Signer ************************* // + +void ed25519PrivateKey::ClampKeys(byte y[PUBLIC_KEYLENGTH], byte x[SECRET_KEYLENGTH]) const +{ + x[0] &= 248; x[31] &= 127; x[31] |= 64; + int ret = Donna::ed25519_publickey(y, x); + CRYPTOPP_ASSERT(ret == 0); +} + +bool ed25519PrivateKey::IsClamped(const byte x[SECRET_KEYLENGTH]) const +{ + return (x[0] & 248) == x[0] && (x[31] & 127) == x[31] && (x[31] | 64) == x[31]; +} + +bool ed25519PrivateKey::Validate(RandomNumberGenerator &rng, unsigned int level) const +{ + CRYPTOPP_UNUSED(rng); CRYPTOPP_UNUSED(level); + return true; +} + +bool ed25519PrivateKey::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const +{ + if (std::strcmp(name, Name::PrivateExponent()) == 0 || std::strcmp(name, "SecretKey") == 0) + { + this->ThrowIfTypeMismatch(name, typeid(ConstByteArrayParameter), valueType); + reinterpret_cast(pValue)->Assign(m_sk, SECRET_KEYLENGTH, false); + return true; + } + + if (std::strcmp(name, Name::PublicElement()) == 0) + { + this->ThrowIfTypeMismatch(name, typeid(ConstByteArrayParameter), valueType); + reinterpret_cast(pValue)->Assign(m_pk, PUBLIC_KEYLENGTH, false); + return true; + } + + if (std::strcmp(name, Name::GroupOID()) == 0) + { + if (m_oid.Empty()) + return false; + + this->ThrowIfTypeMismatch(name, typeid(OID), valueType); + *reinterpret_cast(pValue) = m_oid; + return true; + } + + return false; +} + +void ed25519PrivateKey::AssignFrom(const NameValuePairs &source) +{ + ConstByteArrayParameter val; + if (source.GetValue(Name::PrivateExponent(), val) || source.GetValue("SecretKey", val)) + { + CRYPTOPP_ASSERT(val.size() == SECRET_KEYLENGTH); + std::memcpy(m_sk, val.begin(), SECRET_KEYLENGTH); + } + if (source.GetValue(Name::PublicElement(), val)) + { + CRYPTOPP_ASSERT(val.size() == PUBLIC_KEYLENGTH); + std::memcpy(m_pk, val.begin(), PUBLIC_KEYLENGTH); + } + + OID oid; + if (source.GetValue(Name::GroupOID(), oid)) + { + m_oid = oid; + } + + bool clamp = false; + if (source.GetValue("Clamp", clamp) && clamp == true) + ClampKeys(m_pk, m_sk); +} + +void ed25519PrivateKey::GenerateRandom(RandomNumberGenerator &rng, const NameValuePairs ¶ms=g_nullNameValuePairs) +{ + ConstByteArrayParameter seed; + if (params.GetValue(Name::Seed(), seed) && rng.CanIncorporateEntropy()) + rng.IncorporateEntropy(seed.begin(), seed.size()); + + rng.GenerateBlock(m_sk, 32); + m_sk[0] &= 248; m_sk[31] &= 127; m_sk[31] |= 64; + int ret = Donna::ed25519_publickey(m_pk, m_sk); + CRYPTOPP_ASSERT(ret == 0); +} + +void ed25519PrivateKey::MakePublicKey (PublicKey &pub) const +{ + pub.AssignFrom(MakeParameters + (Name::PublicElement(), ConstByteArrayParameter(m_pk.begin(), PUBLIC_KEYLENGTH)) + (Name::GroupOID(), GetAlgorithmID())); +} + +void ed25519PrivateKey::BERDecodeAndCheckAlgorithmID(BufferedTransformation &bt) +{ + // We have not yet determined the OID to use for this object. + // We can't use OID's decoder because it throws BERDecodeError + // if the OIDs do not match. + OID oid(bt); + + if (!m_oid.Empty() && m_oid != oid) + BERDecodeError(); // Only accept user specified OID + else if (oid == ASN1::curve25519() || oid == ASN1::Ed25519()) + m_oid = oid; // Accept any of the ed25519PrivateKey OIDs + else + BERDecodeError(); +} + +void ed25519PrivateKey::BERDecode(BufferedTransformation &bt) +{ + // https://tools.ietf.org/html/rfc8410, section 7 and + // https://www.cryptopp.com/wiki/curve25519_keys + BERSequenceDecoder privateKeyInfo(bt); + word32 version; + BERDecodeUnsigned(privateKeyInfo, version, INTEGER, 0, 1); // check version + + BERSequenceDecoder algorithm(privateKeyInfo); + // GetAlgorithmID().BERDecodeAndCheck(algorithm); + BERDecodeAndCheckAlgorithmID(algorithm); + algorithm.MessageEnd(); + + BERGeneralDecoder octetString(privateKeyInfo, OCTET_STRING); + BERDecodePrivateKey(octetString, false, (size_t)privateKeyInfo.RemainingLength()); + octetString.MessageEnd(); + + bool generatePublicKey = true; + if (version == 1) + { + BERGeneralDecoder publicKey(privateKeyInfo, CONTEXT_SPECIFIC | CONSTRUCTED | 1); + SecByteBlock subjectPublicKey; + unsigned int unusedBits; + BERDecodeBitString(publicKey, subjectPublicKey, unusedBits); + CRYPTOPP_ASSERT(unusedBits == 0); + CRYPTOPP_ASSERT(subjectPublicKey.size() == PUBLIC_KEYLENGTH); + if (subjectPublicKey.size() != PUBLIC_KEYLENGTH) + BERDecodeError(); + std::memcpy(m_pk.begin(), subjectPublicKey, PUBLIC_KEYLENGTH); + generatePublicKey = false; + publicKey.MessageEnd(); + } + + privateKeyInfo.MessageEnd(); + + if (generatePublicKey) + Donna::ed25519_publickey(m_pk, m_sk); + + CRYPTOPP_ASSERT(IsClamped(m_sk) == true); +} + +void ed25519PrivateKey::DEREncode(BufferedTransformation &bt, int version) const +{ + // https://tools.ietf.org/html/rfc8410, section 7 and + // https://www.cryptopp.com/wiki/curve25519_keys + CRYPTOPP_ASSERT(version == 0 || version == 1); + + DERSequenceEncoder privateKeyInfo(bt); + DEREncodeUnsigned(privateKeyInfo, version); + + DERSequenceEncoder algorithm(privateKeyInfo); + GetAlgorithmID().DEREncode(algorithm); + algorithm.MessageEnd(); + + DERGeneralEncoder octetString(privateKeyInfo, OCTET_STRING); + DEREncodePrivateKey(octetString); + octetString.MessageEnd(); + + if (version == 1) + { + DERGeneralEncoder publicKey(privateKeyInfo, CONTEXT_SPECIFIC | CONSTRUCTED | 1); + DEREncodeBitString(publicKey, m_pk, PUBLIC_KEYLENGTH); + publicKey.MessageEnd(); + } + + privateKeyInfo.MessageEnd(); +} + +void ed25519PrivateKey::BERDecodePrivateKey(BufferedTransformation &bt, bool parametersPresent, size_t /*size*/) +{ + // https://tools.ietf.org/html/rfc8410 and + // https://www.cryptopp.com/wiki/curve25519_keys + + BERGeneralDecoder privateKey(bt, OCTET_STRING); + + if (!privateKey.IsDefiniteLength()) + BERDecodeError(); + + size_t size = privateKey.Get(m_sk, SECRET_KEYLENGTH); + if (size != SECRET_KEYLENGTH) + BERDecodeError(); + + // We don't know how to decode them + if (parametersPresent) + BERDecodeError(); + + privateKey.MessageEnd(); +} + +void ed25519PrivateKey::DEREncodePrivateKey(BufferedTransformation &bt) const +{ + // https://tools.ietf.org/html/rfc8410 + DERGeneralEncoder privateKey(bt, OCTET_STRING); + privateKey.Put(m_sk, SECRET_KEYLENGTH); + privateKey.MessageEnd(); +} + +void ed25519PrivateKey::SetPrivateExponent (const byte x[SECRET_KEYLENGTH]) +{ + AssignFrom(MakeParameters + (Name::PrivateExponent(), ConstByteArrayParameter(x, SECRET_KEYLENGTH)) + ("Clamp", true)); +} + +void ed25519PrivateKey::SetPrivateExponent (const Integer &x) +{ + CRYPTOPP_ASSERT(x.MinEncodedSize() <= SECRET_KEYLENGTH); + + SecByteBlock bx(SECRET_KEYLENGTH); + x.Encode(bx, SECRET_KEYLENGTH); std::reverse(bx+0, bx+SECRET_KEYLENGTH); + + AssignFrom(MakeParameters + (Name::PrivateExponent(), ConstByteArrayParameter(bx, SECRET_KEYLENGTH, false)) + ("Clamp", true)); +} + +const Integer& ed25519PrivateKey::GetPrivateExponent() const +{ + m_x = Integer(m_sk, SECRET_KEYLENGTH, Integer::UNSIGNED, LITTLE_ENDIAN_ORDER); + return m_x; +} + +//////////////////////// + +ed25519Signer::ed25519Signer(const byte y[PUBLIC_KEYLENGTH], const byte x[SECRET_KEYLENGTH]) +{ + AccessPrivateKey().AssignFrom(MakeParameters + (Name::PrivateExponent(), ConstByteArrayParameter(x, SECRET_KEYLENGTH, false)) + (Name::PublicElement(), ConstByteArrayParameter(y, PUBLIC_KEYLENGTH, false))); +} + +ed25519Signer::ed25519Signer(const byte x[SECRET_KEYLENGTH]) +{ + AccessPrivateKey().AssignFrom(MakeParameters + (Name::PrivateExponent(), ConstByteArrayParameter(x, SECRET_KEYLENGTH, false)) + ("Clamp", true)); +} + +ed25519Signer::ed25519Signer(const Integer &y, const Integer &x) +{ + CRYPTOPP_ASSERT(y.MinEncodedSize() <= PUBLIC_KEYLENGTH); + CRYPTOPP_ASSERT(x.MinEncodedSize() <= SECRET_KEYLENGTH); + + SecByteBlock by(PUBLIC_KEYLENGTH), bx(SECRET_KEYLENGTH); + y.Encode(by, PUBLIC_KEYLENGTH); std::reverse(by+0, by+PUBLIC_KEYLENGTH); + x.Encode(bx, SECRET_KEYLENGTH); std::reverse(bx+0, bx+SECRET_KEYLENGTH); + + AccessPrivateKey().AssignFrom(MakeParameters + (Name::PublicElement(), ConstByteArrayParameter(by, PUBLIC_KEYLENGTH, false)) + (Name::PrivateExponent(), ConstByteArrayParameter(bx, SECRET_KEYLENGTH, false))); +} + +ed25519Signer::ed25519Signer(const Integer &x) +{ + CRYPTOPP_ASSERT(x.MinEncodedSize() <= SECRET_KEYLENGTH); + + SecByteBlock bx(SECRET_KEYLENGTH); + x.Encode(bx, SECRET_KEYLENGTH); std::reverse(bx+0, bx+SECRET_KEYLENGTH); + + AccessPrivateKey().AssignFrom(MakeParameters + (Name::PrivateExponent(), ConstByteArrayParameter(bx, SECRET_KEYLENGTH, false)) + ("Clamp", true)); +} + +ed25519Signer::ed25519Signer(RandomNumberGenerator &rng) +{ + AccessPrivateKey().GenerateRandom(rng); +} + +ed25519Signer::ed25519Signer(BufferedTransformation ¶ms) +{ + ed25519PrivateKey& key = static_cast(AccessPrivateKey()); + key.BERDecode(params); +} + +size_t ed25519Signer::SignAndRestart(RandomNumberGenerator &rng, PK_MessageAccumulator &messageAccumulator, byte *signature, bool restart) const +{ + CRYPTOPP_ASSERT(signature != NULLPTR); CRYPTOPP_UNUSED(rng); + + ed25519_MessageAccumulator& accum = static_cast(messageAccumulator); + const ed25519PrivateKey& pk = static_cast(GetPrivateKey()); + int ret = Donna::ed25519_sign(accum.data(), accum.size(), pk.m_sk, pk.m_pk, signature); + CRYPTOPP_ASSERT(ret == 0); + + if (restart) + accum.Restart(); + + return ret == 0 ? SIGNATURE_LENGTH : 0; +} + +// ******************** ed25519 Verifier ************************* // + +bool ed25519PublicKey::GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const +{ + if (std::strcmp(name, Name::PublicElement()) == 0) + { + this->ThrowIfTypeMismatch(name, typeid(ConstByteArrayParameter), valueType); + reinterpret_cast(pValue)->Assign(m_pk, PUBLIC_KEYLENGTH, false); + return true; + } + + if (std::strcmp(name, Name::GroupOID()) == 0) + { + if (m_oid.Empty()) + return false; + + this->ThrowIfTypeMismatch(name, typeid(OID), valueType); + *reinterpret_cast(pValue) = m_oid; + return true; + } + + return false; +} + +void ed25519PublicKey::AssignFrom(const NameValuePairs &source) +{ + ConstByteArrayParameter ba; + if (source.GetValue(Name::PublicElement(), ba)) + { + std::memcpy(m_pk, ba.begin(), PUBLIC_KEYLENGTH); + } + + OID oid; + if (source.GetValue(Name::GroupOID(), oid)) + { + m_oid = oid; + } +} + +void ed25519PublicKey::BERDecodeAndCheckAlgorithmID(BufferedTransformation& bt) +{ + // We have not yet determined the OID to use for this object. + // We can't use OID's decoder because it throws BERDecodeError + // if the OIDs do not match. + OID oid(bt); + + if (!m_oid.Empty() && m_oid != oid) + BERDecodeError(); // Only accept user specified OID + else if (oid == ASN1::curve25519() || oid == ASN1::Ed25519()) + m_oid = oid; // Accept any of the ed25519PublicKey OIDs + else + BERDecodeError(); +} + +void ed25519PublicKey::BERDecode(BufferedTransformation &bt) +{ + BERSequenceDecoder publicKeyInfo(bt); + + BERSequenceDecoder algorithm(publicKeyInfo); + // GetAlgorithmID().BERDecodeAndCheck(algorithm); + BERDecodeAndCheckAlgorithmID(algorithm); + algorithm.MessageEnd(); + + BERDecodePublicKey(publicKeyInfo, false, (size_t)publicKeyInfo.RemainingLength()); + + publicKeyInfo.MessageEnd(); +} + +void ed25519PublicKey::DEREncode(BufferedTransformation &bt) const +{ + DERSequenceEncoder publicKeyInfo(bt); + + DERSequenceEncoder algorithm(publicKeyInfo); + GetAlgorithmID().DEREncode(algorithm); + algorithm.MessageEnd(); + + DEREncodePublicKey(publicKeyInfo); + + publicKeyInfo.MessageEnd(); +} + +void ed25519PublicKey::BERDecodePublicKey(BufferedTransformation &bt, bool parametersPresent, size_t /*size*/) +{ + // We don't know how to decode them + if (parametersPresent) + BERDecodeError(); + + SecByteBlock subjectPublicKey; + unsigned int unusedBits; + BERDecodeBitString(bt, subjectPublicKey, unusedBits); + + CRYPTOPP_ASSERT(unusedBits == 0); + CRYPTOPP_ASSERT(subjectPublicKey.size() == PUBLIC_KEYLENGTH); + if (subjectPublicKey.size() != PUBLIC_KEYLENGTH) + BERDecodeError(); + + std::memcpy(m_pk.begin(), subjectPublicKey, PUBLIC_KEYLENGTH); +} + +void ed25519PublicKey::DEREncodePublicKey(BufferedTransformation &bt) const +{ + DEREncodeBitString(bt, m_pk, PUBLIC_KEYLENGTH); +} + +void ed25519PublicKey::SetPublicElement (const byte y[PUBLIC_KEYLENGTH]) +{ + std::memcpy(m_pk, y, PUBLIC_KEYLENGTH); +} + +void ed25519PublicKey::SetPublicElement (const Integer &y) +{ + CRYPTOPP_ASSERT(y.MinEncodedSize() <= PUBLIC_KEYLENGTH); + + SecByteBlock by(PUBLIC_KEYLENGTH); + y.Encode(by, PUBLIC_KEYLENGTH); std::reverse(by+0, by+PUBLIC_KEYLENGTH); + + std::memcpy(m_pk, by, PUBLIC_KEYLENGTH); +} + +const Integer& ed25519PublicKey::GetPublicElement() const +{ + m_y = Integer(m_pk, PUBLIC_KEYLENGTH, Integer::UNSIGNED, LITTLE_ENDIAN_ORDER); + return m_y; +} + +bool ed25519PublicKey::Validate(RandomNumberGenerator &rng, unsigned int level) const +{ + CRYPTOPP_UNUSED(rng); CRYPTOPP_UNUSED(level); + return true; +} + +//////////////////////// + +ed25519Verifier::ed25519Verifier(const byte y[PUBLIC_KEYLENGTH]) +{ + AccessPublicKey().AssignFrom(MakeParameters + (Name::PublicElement(), ConstByteArrayParameter(y, PUBLIC_KEYLENGTH))); +} + +ed25519Verifier::ed25519Verifier(const Integer &y) +{ + CRYPTOPP_ASSERT(y.MinEncodedSize() <= PUBLIC_KEYLENGTH); + + SecByteBlock by(PUBLIC_KEYLENGTH); + y.Encode(by, PUBLIC_KEYLENGTH); std::reverse(by+0, by+PUBLIC_KEYLENGTH); + + AccessPublicKey().AssignFrom(MakeParameters + (Name::PublicElement(), ConstByteArrayParameter(by, PUBLIC_KEYLENGTH, false))); +} + +ed25519Verifier::ed25519Verifier(BufferedTransformation ¶ms) +{ + // TODO: Fix the on-disk format once we determine what it is. + BERSequenceDecoder seq(params); + + size_t read; + BERSequenceDecoder pk(seq, OCTET_STRING); + + CRYPTOPP_ASSERT(pk.MaxRetrievable() >= PUBLIC_KEYLENGTH); + read = pk.Get(m_key.m_pk, PUBLIC_KEYLENGTH); + + pk.MessageEnd(); + + if (read != PUBLIC_KEYLENGTH) + throw BERDecodeErr(); + + seq.MessageEnd(); +} + +ed25519Verifier::ed25519Verifier(const ed25519Signer& signer) +{ + const ed25519PrivateKey& priv = static_cast(signer.GetPrivateKey()); + priv.MakePublicKey(AccessPublicKey()); +} + +bool ed25519Verifier::VerifyAndRestart(PK_MessageAccumulator &messageAccumulator) const +{ + ed25519_MessageAccumulator& accum = static_cast(messageAccumulator); + const ed25519PublicKey& pk = static_cast(GetPublicKey()); + int ret = Donna::ed25519_sign_open(accum.data(), accum.size(), pk.m_pk.begin(), accum.signature()); + accum.Restart(); + + return ret == 0; } NAMESPACE_END // CryptoPP -- cgit v1.2.1