Skip to content

Commit

Permalink
Introduce explicit lower-S normalization
Browse files Browse the repository at this point in the history
ECDSA signature verification now requires normalized signatures (with S in the
lower half of the range). In case the input cannot be guaranteed to provide this,
a new function secp256k1_ecdsa_signature_normalize is provided to preprocess it.
  • Loading branch information
sipa committed Oct 21, 2015
1 parent fea19e7 commit 0c6ab2f
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 28 deletions.
85 changes: 59 additions & 26 deletions include/secp256k1.h
Expand Up @@ -355,6 +355,15 @@ SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact(
* In: sig: the signature being verified (cannot be NULL)
* msg32: the 32-byte message hash being verified (cannot be NULL)
* pubkey: pointer to an initialized public key to verify with (cannot be NULL)
*
* To avoid accepting malleable signatures, only ECDSA signatures in lower-S
* form are accepted.
*
* If you need to accept ECDSA signatures from sources that do not obey this
* rule, apply secp256k1_ecdsa_signature_normalize to the signature prior to
* validation, but be aware that doing so results in malleable signatures.
*
* For details, see the comments for that function.
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify(
const secp256k1_context* ctx,
Expand All @@ -363,6 +372,54 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify(
const secp256k1_pubkey *pubkey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);

/** Convert a signature to a normalized lower-S form.
*
* Returns: 1 if sigin was not normalized, 0 if it already was.
* Args: ctx: a secp256k1 context object
* Out: sigout: a pointer to a signature to fill with the normalized form,
* or copy if the input was already normalized. (can be NULL if
* you're only interested in whether the input was already
* normalized).
* In: sigin: a pointer to a signature to check/normalize (cannot be NULL,
* can be identical to sigout)
*
* With ECDSA a third-party can forge a second distinct signature of the same
* message, given a single initial signature, but without knowing the key. This
* is done by negating the S value modulo the order of the curve, 'flipping'
* the sign of the random point R which is not included in the signature.
*
* Forgery of the same message isn't universally problematic, but in systems
* where message malleability or uniqueness of signatures is important this can
* cause issues. This forgery can be blocked by all verifiers forcing signers
* to use a normalized form.
*
* The lower-S form reduces the size of signatures slightly on average when
* variable length encodings (such as DER) are used and is cheap to verify,
* making it a good choice. Security of always using lower-S is assured because
* anyone can trivially modify a signature after the fact to enforce this
* property anyway.
*
* The lower S value is always between 0x1 and
* 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
* inclusive.
*
* No other forms of ECDSA malleability are known and none seem likely, but
* there is no formal proof that ECDSA, even with this additional restriction,
* is free of other malleability. Commonly used serialization schemes will also
* accept various non-unique encodings, so care should be taken when this
* property is required for an application.
*
* The secp256k1_ecdsa_sign function will by default create signatures in the
* lower-S form, and secp256k1_ecdsa_verify will not accept others. In case
* signatures come from a system that cannot enforce this property,
* secp256k1_ecdsa_signature_normalize must be called before verification.
*/
SECP256K1_API int secp256k1_ecdsa_signature_normalize(
const secp256k1_context* ctx,
secp256k1_ecdsa_signature *sigout,
const secp256k1_ecdsa_signature *sigin
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3);

/** An implementation of RFC6979 (using HMAC-SHA256) as nonce generation function.
* If a data pointer is passed, it is assumed to be a pointer to 32 bytes of
* extra entropy.
Expand All @@ -383,32 +440,8 @@ SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_def
* noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used
* ndata: pointer to arbitrary data used by the nonce generation function (can be NULL)
*
* The sig always has an s value in the lower half of the range (From 0x1
* to 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
* inclusive), unlike many other implementations.
*
* With ECDSA a third-party can can forge a second distinct signature
* of the same message given a single initial signature without knowing
* the key by setting s to its additive inverse mod-order, 'flipping' the
* sign of the random point R which is not included in the signature.
* Since the forgery is of the same message this isn't universally
* problematic, but in systems where message malleability or uniqueness
* of signatures is important this can cause issues. This forgery can be
* blocked by all verifiers forcing signers to use a canonical form. The
* lower-S form reduces the size of signatures slightly on average when
* variable length encodings (such as DER) are used and is cheap to
* verify, making it a good choice. Security of always using lower-S is
* assured because anyone can trivially modify a signature after the
* fact to enforce this property. Adjusting it inside the signing
* function avoids the need to re-serialize or have curve specific
* constants outside of the library. By always using a canonical form
* even in applications where it isn't needed it becomes possible to
* impose a requirement later if a need is discovered.
* No other forms of ECDSA malleability are known and none seem likely,
* but there is no formal proof that ECDSA, even with this additional
* restriction, is free of other malleability. Commonly used serialization
* schemes will also accept various non-unique encodings, so care should
* be taken when this property is required for an application.
* The created signature is always in lower-S form. See
* secp256k1_ecdsa_signature_normalize for more details.
*/
SECP256K1_API int secp256k1_ecdsa_sign(
const secp256k1_context* ctx,
Expand Down
22 changes: 21 additions & 1 deletion src/secp256k1.c
Expand Up @@ -256,6 +256,25 @@ int secp256k1_ecdsa_signature_serialize_compact(const secp256k1_context* ctx, un
return 1;
}

int secp256k1_ecdsa_signature_normalize(const secp256k1_context* ctx, secp256k1_ecdsa_signature *sigout, const secp256k1_ecdsa_signature *sigin) {
secp256k1_scalar r, s;
int ret = 0;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(sigin != NULL);

secp256k1_ecdsa_signature_load(ctx, &r, &s, sigin);
ret = secp256k1_scalar_is_high(&s);
if (sigout != NULL) {
if (ret) {
secp256k1_scalar_negate(&s, &s);
}
secp256k1_ecdsa_signature_save(sigout, &r, &s);
}

return ret;
}

int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey) {
secp256k1_ge q;
secp256k1_scalar r, s;
Expand All @@ -268,7 +287,8 @@ int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_s

secp256k1_scalar_set_b32(&m, msg32, NULL);
secp256k1_ecdsa_signature_load(ctx, &r, &s, sig);
return (secp256k1_pubkey_load(ctx, &q, pubkey) &&
return (!secp256k1_scalar_is_high(&s) &&
secp256k1_pubkey_load(ctx, &q, pubkey) &&
secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &r, &s, &q, &m));
}

Expand Down
18 changes: 17 additions & 1 deletion src/tests.c
Expand Up @@ -2322,7 +2322,8 @@ void test_ecdsa_end_to_end(void) {
unsigned char privkey[32];
unsigned char message[32];
unsigned char privkey2[32];
secp256k1_ecdsa_signature signature[5];
secp256k1_ecdsa_signature signature[6];
secp256k1_scalar r, s;
unsigned char sig[74];
size_t siglen = 74;
unsigned char pubkeyc[65];
Expand Down Expand Up @@ -2409,6 +2410,21 @@ void test_ecdsa_end_to_end(void) {
CHECK(secp256k1_ecdsa_verify(ctx, &signature[1], message, &pubkey) == 1);
CHECK(secp256k1_ecdsa_verify(ctx, &signature[2], message, &pubkey) == 1);
CHECK(secp256k1_ecdsa_verify(ctx, &signature[3], message, &pubkey) == 1);
/* Test lower-S form, malleate, verify and fail, test again, malleate again */
CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[0]));
secp256k1_ecdsa_signature_load(ctx, &r, &s, &signature[0]);
secp256k1_scalar_negate(&s, &s);
secp256k1_ecdsa_signature_save(&signature[5], &r, &s);
CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 0);
CHECK(secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5]));
CHECK(secp256k1_ecdsa_signature_normalize(ctx, &signature[5], &signature[5]));
CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5]));
CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 1);
secp256k1_scalar_negate(&s, &s);
secp256k1_ecdsa_signature_save(&signature[5], &r, &s);
CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5]));
CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 1);
CHECK(memcmp(&signature[5], &signature[0], 64) == 0);

/* Serialize/parse DER and verify again */
CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature[0]) == 1);
Expand Down

0 comments on commit 0c6ab2f

Please sign in to comment.