diff --git a/src/per_curve/eddsa.tmpl.c b/src/per_curve/eddsa.tmpl.c index 36b9d8c..97e9be5 100644 --- a/src/per_curve/eddsa.tmpl.c +++ b/src/per_curve/eddsa.tmpl.c @@ -128,8 +128,8 @@ void decaf_ed$(gf_shortname)_derive_public_key ( API_NS(point_destroy)(p); decaf_bzero(secret_scalar_ser, sizeof(secret_scalar_ser)); } - -void decaf_ed$(gf_shortname)_sign ( + +static void decaf_ed$(gf_shortname)_sign_internal ( uint8_t signature[DECAF_EDDSA_$(gf_shortname)_SIGNATURE_BYTES], const uint8_t privkey[DECAF_EDDSA_$(gf_shortname)_PRIVATE_BYTES], const uint8_t pubkey[DECAF_EDDSA_$(gf_shortname)_PUBLIC_BYTES], @@ -214,6 +214,24 @@ void decaf_ed$(gf_shortname)_sign ( API_NS(scalar_destroy)(challenge_scalar); } +void decaf_ed$(gf_shortname)_sign ( + uint8_t signature[DECAF_EDDSA_$(gf_shortname)_SIGNATURE_BYTES], + const uint8_t privkey[DECAF_EDDSA_$(gf_shortname)_PRIVATE_BYTES], + const uint8_t pubkey[DECAF_EDDSA_$(gf_shortname)_PUBLIC_BYTES], + const uint8_t *message, + size_t message_len, + uint8_t prehashed, + const uint8_t *context, + uint8_t context_len +) { + uint8_t rederived_pubkey[DECAF_EDDSA_$(gf_shortname)_PUBLIC_BYTES]; + decaf_ed$(gf_shortname)_derive_public_key(rederived_pubkey, privkey); + if (DECAF_TRUE != decaf_memeq(rederived_pubkey, pubkey, sizeof(rederived_pubkey))) { + abort(); + } + decaf_ed$(gf_shortname)_sign_internal(signature,privkey,rederived_pubkey,message, + message_len,prehashed,context,context_len); +} void decaf_ed$(gf_shortname)_sign_prehash ( uint8_t signature[DECAF_EDDSA_$(gf_shortname)_SIGNATURE_BYTES], @@ -231,7 +249,75 @@ void decaf_ed$(gf_shortname)_sign_prehash ( hash_destroy(hash_too); } - decaf_ed$(gf_shortname)_sign(signature,privkey,pubkey,hash_output,sizeof(hash_output),1,context,context_len); + uint8_t rederived_pubkey[DECAF_EDDSA_$(gf_shortname)_PUBLIC_BYTES]; + decaf_ed$(gf_shortname)_derive_public_key(rederived_pubkey, privkey); + if (DECAF_TRUE != decaf_memeq(rederived_pubkey, pubkey, sizeof(rederived_pubkey))) { + abort(); + } + + decaf_ed$(gf_shortname)_sign_internal(signature,privkey,rederived_pubkey,hash_output, + sizeof(hash_output),1,context,context_len); + decaf_bzero(hash_output,sizeof(hash_output)); +} + +void decaf_ed$(gf_shortname)_derive_keypair ( + decaf_eddsa_$(gf_shortname)_keypair_t keypair, + const uint8_t privkey[DECAF_EDDSA_$(gf_shortname)_PRIVATE_BYTES] +) { + memcpy(keypair->privkey, privkey, DECAF_EDDSA_$(gf_shortname)_PRIVATE_BYTES); + decaf_ed$(gf_shortname)_derive_public_key(keypair->pubkey, keypair->privkey); +} + +void decaf_ed$(gf_shortname)_keypair_extract_public_key ( + uint8_t pubkey[DECAF_EDDSA_$(gf_shortname)_PUBLIC_BYTES], + const decaf_eddsa_$(gf_shortname)_keypair_t keypair +) { + memcpy(pubkey,keypair->pubkey,DECAF_EDDSA_$(gf_shortname)_PUBLIC_BYTES); +} + +void decaf_ed$(gf_shortname)_keypair_extract_private_key ( + uint8_t privkey[DECAF_EDDSA_$(gf_shortname)_PRIVATE_BYTES], + const decaf_eddsa_$(gf_shortname)_keypair_t keypair +) { + memcpy(privkey,keypair->privkey,DECAF_EDDSA_$(gf_shortname)_PUBLIC_BYTES); +} + +void decaf_ed$(gf_shortname)_keypair_destroy ( + decaf_eddsa_$(gf_shortname)_keypair_t keypair +) { + decaf_bzero(keypair, sizeof(decaf_eddsa_$(gf_shortname)_keypair_t)); +} + +void decaf_ed$(gf_shortname)_keypair_sign ( + uint8_t signature[DECAF_EDDSA_$(gf_shortname)_SIGNATURE_BYTES], + const decaf_eddsa_$(gf_shortname)_keypair_t keypair, + const uint8_t *message, + size_t message_len, + uint8_t prehashed, + const uint8_t *context, + uint8_t context_len +) { + decaf_ed$(gf_shortname)_sign_internal(signature,keypair->privkey,keypair->pubkey,message, + message_len,prehashed,context,context_len); +} + +void decaf_ed$(gf_shortname)_keypair_sign_prehash ( + uint8_t signature[DECAF_EDDSA_$(gf_shortname)_SIGNATURE_BYTES], + const decaf_eddsa_$(gf_shortname)_keypair_t keypair, + const decaf_ed$(gf_shortname)_prehash_ctx_t hash, + const uint8_t *context, + uint8_t context_len +) { + uint8_t hash_output[EDDSA_PREHASH_BYTES]; + { + decaf_ed$(gf_shortname)_prehash_ctx_t hash_too; + memcpy(hash_too,hash,sizeof(hash_too)); + hash_final(hash_too,hash_output,sizeof(hash_output)); + hash_destroy(hash_too); + } + + decaf_ed$(gf_shortname)_sign_internal(signature,keypair->privkey,keypair->pubkey,hash_output, + sizeof(hash_output),1,context,context_len); decaf_bzero(hash_output,sizeof(hash_output)); } diff --git a/src/per_curve/eddsa.tmpl.h b/src/per_curve/eddsa.tmpl.h index 9e4ea6d..69b5303 100644 --- a/src/per_curve/eddsa.tmpl.h +++ b/src/per_curve/eddsa.tmpl.h @@ -43,6 +43,19 @@ $("DECAF_API_VIS extern const uint8_t * const DECAF_ED" + gf_shortname + "_NO_CO /** EdDSA decoding ratio. */ #define $(C_NS)_EDDSA_DECODE_RATIO ($(cofactor) / $(eddsa_encode_ratio)) + +#ifndef DECAF_EDDSA_NON_KEYPAIR_API_IS_DEPRECATED +/** If 1, add deprecation attribute to non-keypair API functions. For now, deprecate in Doxygen only. */ +#define DECAF_EDDSA_NON_KEYPAIR_API_IS_DEPRECATED 0 +#endif + +/** @cond internal */ +/** @brief Scheduled EdDSA keypair */ +typedef struct decaf_eddsa_$(gf_shortname)_keypair_s { + uint8_t privkey[DECAF_EDDSA_$(gf_shortname)_PRIVATE_BYTES]; + uint8_t pubkey[DECAF_EDDSA_$(gf_shortname)_PUBLIC_BYTES]; +} decaf_eddsa_$(gf_shortname)_keypair_s, decaf_eddsa_$(gf_shortname)_keypair_t[1]; +/** @endcond */ /** * @brief EdDSA key generation. This function uses a different (non-Decaf) @@ -57,7 +70,53 @@ void DECAF_API_VIS decaf_ed$(gf_shortname)_derive_public_key ( ) DECAF_NONNULL DECAF_NOINLINE; /** - * @brief EdDSA signing. + * @brief EdDSA keypair scheduling. This is to add a safer version of the signing algorithm, + * where it is harder to use the wrong pubkey for your private key.. + * + * @param [out] keypair The scheduled keypair. + * @param [in] privkey The private key. + */ +void DECAF_API_VIS decaf_ed$(gf_shortname)_derive_keypair ( + decaf_eddsa_$(gf_shortname)_keypair_t keypair, + const uint8_t privkey[DECAF_EDDSA_$(gf_shortname)_PRIVATE_BYTES] +) DECAF_NONNULL DECAF_NOINLINE; + +/** + * @brief Extract the public key from an EdDSA keypair. + * + * @param [out] pubkey The public key. + * @param [in] keypair The keypair. + */ +void DECAF_API_VIS decaf_ed$(gf_shortname)_keypair_extract_public_key ( + uint8_t pubkey[DECAF_EDDSA_$(gf_shortname)_PUBLIC_BYTES], + const decaf_eddsa_$(gf_shortname)_keypair_t keypair +) DECAF_NONNULL DECAF_NOINLINE; + +/** + * @brief Extract the private key from an EdDSA keypair. + * + * @param [out] privkey The private key. + * @param [in] keypair The keypair. + */ +void DECAF_API_VIS decaf_ed$(gf_shortname)_keypair_extract_private_key ( + uint8_t privkey[DECAF_EDDSA_$(gf_shortname)_PRIVATE_BYTES], + const decaf_eddsa_$(gf_shortname)_keypair_t keypair +) DECAF_NONNULL DECAF_NOINLINE; + +/** + * @brief EdDSA keypair destructor. + * @param [in] pubkey The keypair. + */ +void DECAF_API_VIS decaf_ed$(gf_shortname)_keypair_destroy ( + decaf_eddsa_$(gf_shortname)_keypair_t keypair +) DECAF_NONNULL DECAF_NOINLINE; + +/** + * @brief EdDSA signing. However, this API is deprecated because it isn't safe: if the wrong + * public key is passed, it would reveal the private key. Instead, this function checks that + * the public key is correct, and otherwise aborts. + * + * @deprecated Use DECAF_API_VIS decaf_ed$(gf_shortname)_keypair_sign instead. * * @param [out] signature The signature. * @param [in] privkey The private key. @@ -82,10 +141,19 @@ void DECAF_API_VIS decaf_ed$(gf_shortname)_sign ( uint8_t prehashed, const uint8_t *context, uint8_t context_len -) __attribute__((nonnull(1,2,3))) DECAF_NOINLINE; +) __attribute__((nonnull(1,2,3))) DECAF_NOINLINE +#if DECAF_EDDSA_NON_KEYPAIR_API_IS_DEPRECATED + __attribute__((deprecated("Passing the pubkey and privkey separately is unsafe", + "decaf_ed$(gf_shortname)_keypair_sign"))) +#endif +; /** - * @brief EdDSA signing with prehash. + * @brief EdDSA signing with prehash. However, this API is deprecated because it isn't safe: if the wrong + * public key is passed, it would reveal the private key. Instead, this function checks that + * the public key is correct, and otherwise aborts. + * + * @deprecated Use DECAF_API_VIS decaf_ed$(gf_shortname)_keypair_sign_prehash instead. * * @param [out] signature The signature. * @param [in] privkey The private key. @@ -93,11 +161,6 @@ void DECAF_API_VIS decaf_ed$(gf_shortname)_sign ( * @param [in] hash The hash of the message. This object will not be modified by the call. * @param [in] context A "context" for this signature of up to 255 bytes. Must be the same as what was used for the prehash. * @param [in] context_len Length of the context. - * - * @warning For Ed25519, it is unsafe to use the same key for both prehashed and non-prehashed - * messages, at least without some very careful protocol-level disambiguation. For Ed448 it is - * safe. The C++ wrapper is designed to make it harder to screw this up, but this C code gives - * you no seat belt. */ void DECAF_API_VIS decaf_ed$(gf_shortname)_sign_prehash ( uint8_t signature[DECAF_EDDSA_$(gf_shortname)_SIGNATURE_BYTES], @@ -106,6 +169,49 @@ void DECAF_API_VIS decaf_ed$(gf_shortname)_sign_prehash ( const decaf_ed$(gf_shortname)_prehash_ctx_t hash, const uint8_t *context, uint8_t context_len +) __attribute__((nonnull(1,2,3,4))) DECAF_NOINLINE +#if DECAF_EDDSA_NON_KEYPAIR_API_IS_DEPRECATED + __attribute__((deprecated("Passing the pubkey and privkey separately is unsafe", + "decaf_ed$(gf_shortname)_keypair_sign_prehash"))) +#endif +; + +/** + * @brief EdDSA signing. + * + * @param [out] signature The signature. + * @param [in] keypair The private and public key. + * @param [in] message The message to sign. + * @param [in] message_len The length of the message. + * @param [in] prehashed Nonzero if the message is actually the hash of something you want to sign. + * @param [in] context A "context" for this signature of up to 255 bytes. + * @param [in] context_len Length of the context. + */ +void DECAF_API_VIS decaf_ed$(gf_shortname)_keypair_sign ( + uint8_t signature[DECAF_EDDSA_$(gf_shortname)_SIGNATURE_BYTES], + const decaf_eddsa_$(gf_shortname)_keypair_t keypair, + const uint8_t *message, + size_t message_len, + uint8_t prehashed, + const uint8_t *context, + uint8_t context_len +) __attribute__((nonnull(1,2,3))) DECAF_NOINLINE; + +/** + * @brief EdDSA signing with prehash. + * + * @param [out] signature The signature. + * @param [in] keypair The private and public key. + * @param [in] hash The hash of the message. This object will not be modified by the call. + * @param [in] context A "context" for this signature of up to 255 bytes. Must be the same as what was used for the prehash. + * @param [in] context_len Length of the context. + */ +void DECAF_API_VIS decaf_ed$(gf_shortname)_keypair_sign_prehash ( + uint8_t signature[DECAF_EDDSA_$(gf_shortname)_SIGNATURE_BYTES], + const decaf_eddsa_$(gf_shortname)_keypair_t keypair, + const decaf_ed$(gf_shortname)_prehash_ctx_t hash, + const uint8_t *context, + uint8_t context_len ) __attribute__((nonnull(1,2,3,4))) DECAF_NOINLINE; /** diff --git a/src/per_curve/eddsa.tmpl.hxx b/src/per_curve/eddsa.tmpl.hxx index 997c653..1d15e71 100644 --- a/src/per_curve/eddsa.tmpl.hxx +++ b/src/per_curve/eddsa.tmpl.hxx @@ -123,10 +123,9 @@ public: } SecureBuffer out(CRTP::SIG_BYTES); - decaf_ed$(gf_shortname)_sign ( + decaf_ed$(gf_shortname)_keypair_sign ( out.data(), - ((const CRTP*)this)->priv_.data(), - ((const CRTP*)this)->pub_.data(), + ((const CRTP*)this)->keypair_, message.data(), message.size(), 0, @@ -143,10 +142,9 @@ public: /** Sign a prehash context, and reset the context */ inline SecureBuffer sign_prehashed ( const Prehash &ph ) const /*throw(std::bad_alloc)*/ { SecureBuffer out(CRTP::SIG_BYTES); - decaf_ed$(gf_shortname)_sign_prehash ( + decaf_ed$(gf_shortname)_keypair_sign_prehash ( out.data(), - ((const CRTP*)this)->priv_.data(), - ((const CRTP*)this)->pub_.data(), + ((const CRTP*)this)->keypair_, (const decaf_ed$(gf_shortname)_prehash_ctx_s*)ph.wrapped, ph.context_.data(), ph.context_.size() @@ -180,11 +178,8 @@ private: friend class Signing; /** @endcond */ - /** The pre-expansion form of the signing key. */ - FixedArrayBuffer priv_; - - /** The post-expansion public key. */ - FixedArrayBuffer pub_; + /** The expanded keypair. */ + decaf_eddsa_$(gf_shortname)_keypair_t keypair_; public: /** Underlying group */ @@ -198,30 +193,32 @@ public: /** Create but don't initialize */ - inline explicit PrivateKeyBase(const NOINIT&) DECAF_NOEXCEPT : priv_((NOINIT())), pub_((NOINIT())) { } + inline explicit PrivateKeyBase(const NOINIT&) DECAF_NOEXCEPT { } /** Read a private key from a string */ inline explicit PrivateKeyBase(const FixedBlock &b) DECAF_NOEXCEPT { *this = b; } /** Copy constructor */ - inline PrivateKeyBase(const PrivateKey &k) DECAF_NOEXCEPT { *this = k; } + inline PrivateKeyBase(const PrivateKeyBase &k) DECAF_NOEXCEPT { *this = k; } /** Create at random */ - inline explicit PrivateKeyBase(Rng &r) DECAF_NOEXCEPT : priv_(r) { - decaf_ed$(gf_shortname)_derive_public_key(pub_.data(), priv_.data()); + inline explicit PrivateKeyBase(Rng &r) DECAF_NOEXCEPT { + FixedArrayBuffer priv(r); + decaf_ed$(gf_shortname)_derive_keypair(keypair_, priv.data()); } - /** Assignment from string */ - inline PrivateKeyBase &operator=(const FixedBlock &b) DECAF_NOEXCEPT { - memcpy(priv_.data(),b.data(),b.size()); - decaf_ed$(gf_shortname)_derive_public_key(pub_.data(), priv_.data()); + /** Copy assignment */ + inline PrivateKeyBase &operator=(const PrivateKey &k) DECAF_NOEXCEPT { + memcpy(keypair_,k.keypair_,sizeof(keypair_)); return *this; } - /** Copy assignment */ - inline PrivateKeyBase &operator=(const PrivateKey &k) DECAF_NOEXCEPT { - memcpy(priv_.data(),k.priv_.data(), priv_.size()); - memcpy(pub_.data(),k.pub_.data(), pub_.size()); + /** Create at random */ + inline ~PrivateKeyBase() { decaf_ed$(gf_shortname)_keypair_destroy(keypair_); } + + /** Assignment from string */ + inline PrivateKeyBase &operator=(const FixedBlock &b) DECAF_NOEXCEPT { + decaf_ed$(gf_shortname)_derive_keypair(keypair_, b.data()); return *this; } @@ -230,13 +227,15 @@ public: /** Serialize into a buffer. */ inline void serialize_into(unsigned char *x) const DECAF_NOEXCEPT { - memcpy(x,priv_.data(), priv_.size()); + decaf_ed$(gf_shortname)_keypair_extract_private_key(x, keypair_); } /** Convert to X format (to be used for key exchange) */ inline SecureBuffer convert_to_x() const { SecureBuffer out(DECAF_X$(gf_shortname)_PRIVATE_BYTES); - decaf_ed$(gf_shortname)_convert_private_key_to_x$(gf_shortname)(out.data(), priv_.data()); + FixedArrayBuffer priv; + serialize_into(priv.data()); + decaf_ed$(gf_shortname)_convert_private_key_to_x$(gf_shortname)(out.data(), priv.data()); return out; } @@ -388,14 +387,15 @@ public: return *this; } - /** Assignment from private key */ + /** Assignment from public key */ inline PublicKey &operator=(const PublicKey &p) DECAF_NOEXCEPT { return *this = p.pub_; } /** Assignment from private key */ inline PublicKey &operator=(const PrivateKey &p) DECAF_NOEXCEPT { - return *this = p.pub_; + decaf_ed$(gf_shortname)_keypair_extract_public_key(pub_.data(), p.keypair_); + return *this; } /** Serialization size. */