Browse Source

Add safer version of EdDSA signing API.

The old API takes the public key as an input.  If the wrong public key
is passed, then the private key may be revealed to the attacker (via the
usual two-signatures-one-nonce vulnerability).  As a countermeasure, the
API now instead rederives the pubkey and asserts equality.  This makes it
twice as slow.

The new API stores the public and private keypair in one struct, which is
initialized by expanding the private key.  This protects against mistakes.

The old API is soft-deprecated.  I might add a deprecation attribute
later.
master
Mike Hamburg 4 years ago
parent
commit
326dba2530
3 changed files with 230 additions and 38 deletions
  1. +89
    -3
      src/per_curve/eddsa.tmpl.c
  2. +114
    -8
      src/per_curve/eddsa.tmpl.h
  3. +27
    -27
      src/per_curve/eddsa.tmpl.hxx

+ 89
- 3
src/per_curve/eddsa.tmpl.c View File

@@ -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));
}



+ 114
- 8
src/per_curve/eddsa.tmpl.h View File

@@ -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;
/**


+ 27
- 27
src/per_curve/eddsa.tmpl.hxx View File

@@ -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<PrivateKey,PREHASHED>;
/** @endcond */
/** The pre-expansion form of the signing key. */
FixedArrayBuffer<DECAF_EDDSA_$(gf_shortname)_PRIVATE_BYTES> priv_;
/** The post-expansion public key. */
FixedArrayBuffer<DECAF_EDDSA_$(gf_shortname)_PUBLIC_BYTES> 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<SER_BYTES> &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<DECAF_EDDSA_$(gf_shortname)_PRIVATE_BYTES> priv(r);
decaf_ed$(gf_shortname)_derive_keypair(keypair_, priv.data());
}
/** Assignment from string */
inline PrivateKeyBase &operator=(const FixedBlock<SER_BYTES> &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<SER_BYTES> &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<DECAF_EDDSA_$(gf_shortname)_PRIVATE_BYTES> 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. */


Loading…
Cancel
Save