Introduce explicit lower-S normalization
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.
This commit is contained in:
@@ -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;
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
18
src/tests.c
18
src/tests.c
@@ -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];
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user