Merge pull request #446 from libtom/pr/wycheproof-fail-changed-seq-tag

Wycheproof failing ECC tests
This commit is contained in:
Steffen Jaeckel 2026-04-27 14:23:07 +02:00 committed by GitHub
commit d062d0e31f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 300 additions and 8 deletions

View file

@ -960,9 +960,17 @@ enum ltc_der_seq {
LTC_DER_SEQ_RELAXED = LTC_DER_SEQ_ZERO,
LTC_DER_SEQ_STRICT = 0x2u,
/** Bit2 - [0]=Relaxed Length Check
* [1]=Strict Length Check */
LTC_DER_SEQ_LEN_RELAXED = LTC_DER_SEQ_ZERO,
LTC_DER_SEQ_LEN_STRICT = 0x4u,
/** Alternative naming */
LTC_DER_SEQ_SET = LTC_DER_SEQ_UNORDERED,
LTC_DER_SEQ_SEQUENCE = LTC_DER_SEQ_ORDERED,
LTC_DER_SEQ_ALL_STRICT = LTC_DER_SEQ_STRICT | LTC_DER_SEQ_LEN_STRICT,
};
int der_decode_sequence_ex(const unsigned char *in, unsigned long inlen,

View file

@ -756,7 +756,8 @@ int der_decode_asn1_identifier(const unsigned char *in, unsigned long *inlen, lt
int der_length_asn1_identifier(const ltc_asn1_list *id, unsigned long *idlen);
int der_encode_asn1_length(unsigned long len, unsigned char* out, unsigned long* outlen);
int der_decode_asn1_length(const unsigned char *in, unsigned long *inlen, unsigned long *outlen);
int der_decode_asn1_length_ex(const unsigned char *in, unsigned long *inlen, unsigned long *outlen, unsigned int flags);
#define der_decode_asn1_length(i, il, ol) der_decode_asn1_length_ex(i, il, ol, 0)
int der_length_asn1_length(unsigned long len, unsigned long *outlen);
int der_length_sequence_ex(const ltc_asn1_list *list, unsigned long inlen,

View file

@ -1224,6 +1224,11 @@ int ltc_ecc_fp_mulmod(void *k, ecc_point *G, ecc_point *R, void *a, void *modulu
int idx, err;
void *mp, *mu;
if (ltc_mp_iszero(k) == LTC_MP_YES) {
/* 0 * G is the point at infinity */
return ltc_ecc_set_point_xyz(1, 1, 0, R);
}
mp = NULL;
mu = NULL;
LTC_MUTEX_LOCK(&ltc_ecc_fp_lock);

View file

@ -469,6 +469,14 @@ static int tfm_ecc_projective_dbl_point(const ecc_point *P, ecc_point *R, const
return CRYPT_OK;
}
/* Y == 0 or Z == 0 doubles to infinity. */
if (fp_cmp_d(R->y, 0) == FP_EQ || fp_cmp_d(R->z, 0) == FP_EQ) {
ltc_mp.set_int(R->x, 1);
ltc_mp.set_int(R->y, 1);
ltc_mp.set_int(R->z, 0);
return CRYPT_OK;
}
/* t1 = Z * Z */
fp_sqr(R->z, &t1);
fp_montgomery_reduce(&t1, TFM_UNCONST(void *)modulus, mp);
@ -597,7 +605,7 @@ static int tfm_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q,
{
fp_int t1, t2, x, y, z;
fp_digit mp;
int err, inf;
int err, inf, x_or_y_is_zero;
LTC_ARGCHK(P != NULL);
LTC_ARGCHK(Q != NULL);
@ -613,6 +621,29 @@ static int tfm_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q,
fp_init(&y);
fp_init(&z);
/* treat internal Z == 0 states as infinity */
if (P->z != NULL && fp_cmp_d(P->z, 0) == FP_EQ) {
if (Q->z != NULL && fp_cmp_d(Q->z, 0) == FP_EQ) {
/* inf + inf = inf */
ltc_mp.set_int(R->x, 1);
ltc_mp.set_int(R->y, 1);
ltc_mp.set_int(R->z, 0);
} else {
/* inf + Q = Q */
ltc_mp.copy(Q->x, R->x);
ltc_mp.copy(Q->y, R->y);
ltc_mp.copy(Q->z, R->z);
}
return CRYPT_OK;
}
if (Q->z != NULL && fp_cmp_d(Q->z, 0) == FP_EQ) {
/* P + inf = P */
ltc_mp.copy(P->x, R->x);
ltc_mp.copy(P->y, R->y);
ltc_mp.copy(P->z, R->z);
return CRYPT_OK;
}
if ((err = ltc_ecc_is_point_at_infinity(P, modulus, &inf)) != CRYPT_OK) return err;
if (inf) {
/* P is point at infinity >> Result = Q */
@ -636,6 +667,7 @@ static int tfm_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q,
if ( (fp_cmp(P->x, Q->x) == FP_EQ) &&
(Q->z != NULL && fp_cmp(P->z, Q->z) == FP_EQ) &&
(fp_cmp(P->y, Q->y) == FP_EQ || fp_cmp(P->y, &t1) == FP_EQ)) {
dbl_point:
return tfm_ecc_projective_dbl_point(P, R, ma, modulus, Mp);
}
@ -735,6 +767,7 @@ static int tfm_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q,
if (fp_cmp_d(&x, 0) == FP_LT) {
fp_add(&x, TFM_UNCONST(void *)modulus, &x);
}
x_or_y_is_zero = fp_cmp_d(&x, 0) == FP_EQ;
/* T2 = T2 - X */
fp_sub(&t2, &x, &t2);
@ -759,6 +792,11 @@ static int tfm_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q,
fp_add(&y, TFM_UNCONST(void *)modulus, &y);
}
fp_div_2(&y, &y);
x_or_y_is_zero |= fp_cmp_d(&y, 0) == LTC_MP_EQ;
if (x_or_y_is_zero) {
goto dbl_point;
}
fp_copy(&x, R->x);
fp_copy(&y, R->y);

View file

@ -47,6 +47,7 @@ int der_decode_custom_type_ex(const unsigned char *in, unsigned long inlen,
int err, seq_err, i, ordered;
ltc_asn1_type type;
ltc_asn1_list ident;
unsigned int f;
unsigned long size, x, y, z, blksize;
unsigned char* in_new = NULL;
void *data;
@ -63,7 +64,8 @@ int der_decode_custom_type_ex(const unsigned char *in, unsigned long inlen,
LTC_ARGCHK(list != NULL);
/* sequence type? We allow 0x30 SEQUENCE and 0x31 SET since fundamentally they're the same structure */
if (in[x] != 0x30 && in[x] != 0x31) {
f = flags & ~(LTC_DER_SEQ_ALL_STRICT);
if (((f == LTC_DER_SEQ_SEQUENCE) && (in[x] != 0x30)) || (((f == LTC_DER_SEQ_SET) && (in[x] != 0x31)))) {
return CRYPT_INVALID_PACKET;
}
++x;
@ -116,7 +118,7 @@ int der_decode_custom_type_ex(const unsigned char *in, unsigned long inlen,
} else {
y = inlen - x;
if ((err = der_decode_asn1_length(&in[x], &y, &blksize)) != CRYPT_OK) {
if ((err = der_decode_asn1_length_ex(&in[x], &y, &blksize, flags)) != CRYPT_OK) {
goto LBL_ERR;
}
x += y;

View file

@ -15,7 +15,7 @@
@param outlen [out] The decoded ASN.1 length
@return CRYPT_OK if successful
*/
int der_decode_asn1_length(const unsigned char *in, unsigned long *inlen, unsigned long *outlen)
int der_decode_asn1_length_ex(const unsigned char *in, unsigned long *inlen, unsigned long *outlen, unsigned int flags)
{
unsigned long real_len, decoded_len, offset, i;
@ -42,10 +42,17 @@ int der_decode_asn1_length(const unsigned char *in, unsigned long *inlen, unsign
if (real_len > (*inlen - 1)) {
return CRYPT_BUFFER_OVERFLOW;
}
flags &= LTC_DER_SEQ_LEN_STRICT;
decoded_len = 0;
offset = 1 + real_len;
for (i = 0; i < real_len; i++) {
decoded_len = (decoded_len << 8) | in[1 + i];
if ((flags == LTC_DER_SEQ_LEN_STRICT) && (decoded_len == 0)) {
return CRYPT_PK_ASN1_ERROR;
}
}
if ((flags == LTC_DER_SEQ_LEN_STRICT) && (real_len == 1) && (decoded_len < 128)) {
return CRYPT_PK_ASN1_ERROR;
}
}

View file

@ -46,6 +46,8 @@ int ecc_verify_hash_v2(const unsigned char *sig,
int *stat,
const ecc_key *key)
{
LTC_ARGCHK(stat != NULL);
*stat = 0;
if (opts->type < 0 || opts->type >= LTC_ARRAY_SIZE(s_ecc_verify_hash))
return CRYPT_PK_INVALID_TYPE;
if (s_ecc_verify_hash[opts->type] == NULL)

View file

@ -22,7 +22,7 @@ int ecc_verify_hash_x962(const unsigned char *sig, unsigned long siglen,
if ((err = ltc_mp_init_multi(&r, &s, NULL)) != CRYPT_OK) return err;
/* ANSI X9.62 format - ASN.1 encoded SEQUENCE{ INTEGER(r), INTEGER(s) } */
if ((err = der_decode_sequence_multi_ex(sig, siglen, LTC_DER_SEQ_SEQUENCE | LTC_DER_SEQ_STRICT,
if ((err = der_decode_sequence_multi_ex(sig, siglen, LTC_DER_SEQ_SEQUENCE | LTC_DER_SEQ_ALL_STRICT,
LTC_ASN1_INTEGER, 1UL, r,
LTC_ASN1_INTEGER, 1UL, s,
LTC_ASN1_EOL, 0UL, LTC_NULL)) != CRYPT_OK) { goto error; }

View file

@ -42,6 +42,10 @@ int ltc_ecc_mulmod(const void *k, const ecc_point *G, ecc_point *R, const void *
/* return the point at infinity */
return ltc_ecc_set_point_xyz(1, 1, 0, R);
}
if (ltc_mp_iszero(k) == LTC_MP_YES) {
/* 0 * G is the point at infinity */
return ltc_ecc_set_point_xyz(1, 1, 0, R);
}
/* init montgomery reduction */
if ((err = ltc_mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { goto error; }

View file

@ -40,6 +40,10 @@ int ltc_ecc_mulmod(const void *k, const ecc_point *G, ecc_point *R, const void *
/* return the point at infinity */
return ltc_ecc_set_point_xyz(1, 1, 0, R);
}
if (ltc_mp_iszero(k) == LTC_MP_YES) {
/* 0 * G is the point at infinity */
return ltc_ecc_set_point_xyz(1, 1, 0, R);
}
/* init montgomery reduction */
if ((err = ltc_mp_montgomery_setup(modulus, &mp)) != CRYPT_OK) { goto error; }

View file

@ -24,7 +24,7 @@ int ltc_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q, ecc_poi
const void *ma, const void *modulus, void *mp)
{
void *t1, *t2, *x, *y, *z;
int err, inf;
int err, inf, x_or_y_is_zero;
LTC_ARGCHK(P != NULL);
LTC_ARGCHK(Q != NULL);
@ -36,6 +36,24 @@ int ltc_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q, ecc_poi
return err;
}
/* treat internal Z == 0 states as infinity (for elliptic-curve addition: infinity = identity element) */
if (P->z != NULL && ltc_mp_iszero(P->z)) {
if (Q->z != NULL && ltc_mp_iszero(Q->z)) {
/* inf + inf = inf */
err = ltc_ecc_set_point_xyz(1, 1, 0, R);
}
else {
/* inf + Q = Q */
err = ltc_ecc_copy_point(Q, R);
}
goto done;
}
if (Q->z != NULL && ltc_mp_iszero(Q->z)) {
/* P + inf = P */
err = ltc_ecc_copy_point(P, R);
goto done;
}
if ((err = ltc_ecc_is_point_at_infinity(P, modulus, &inf)) != CRYPT_OK) return err;
if (inf) {
/* P is point at infinity >> Result = Q */
@ -52,6 +70,7 @@ int ltc_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q, ecc_poi
if ((ltc_mp_cmp(P->x, Q->x) == LTC_MP_EQ) && (ltc_mp_cmp(P->z, Q->z) == LTC_MP_EQ)) {
if (ltc_mp_cmp(P->y, Q->y) == LTC_MP_EQ) {
dbl_point:
/* here P = Q >> Result = 2 * P (use doubling) */
ltc_mp_deinit_multi(t1, t2, x, y, z, LTC_NULL);
return ltc_ecc_projective_dbl_point(P, R, ma, modulus, mp);
@ -160,6 +179,7 @@ int ltc_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q, ecc_poi
if (ltc_mp_cmp_d(x, 0) == LTC_MP_LT) {
if ((err = ltc_mp_add(x, modulus, x)) != CRYPT_OK) { goto done; }
}
x_or_y_is_zero = ltc_mp_cmp_d(x, 0) == LTC_MP_EQ;
/* T2 = T2 - X */
if ((err = ltc_mp_sub(t2, x, t2)) != CRYPT_OK) { goto done; }
@ -184,6 +204,11 @@ int ltc_ecc_projective_add_point(const ecc_point *P, const ecc_point *Q, ecc_poi
if ((err = ltc_mp_add(y, modulus, y)) != CRYPT_OK) { goto done; }
}
if ((err = ltc_mp_div_2(y, y)) != CRYPT_OK) { goto done; }
x_or_y_is_zero |= ltc_mp_cmp_d(y, 0) == LTC_MP_EQ;
if (x_or_y_is_zero) {
goto dbl_point;
}
if ((err = ltc_mp_copy(x, R->x)) != CRYPT_OK) { goto done; }
if ((err = ltc_mp_copy(y, R->y)) != CRYPT_OK) { goto done; }
@ -196,4 +221,3 @@ done:
}
#endif

View file

@ -62,6 +62,12 @@ int ltc_ecc_projective_dbl_point(const ecc_point *P, ecc_point *R, const void *m
goto done;
}
/* Y == 0 or Z == 0 doubles to infinity */
if (ltc_mp_iszero(R->y) || ltc_mp_iszero(R->z)) {
err = ltc_ecc_set_point_xyz(1, 1, 0, R);
goto done;
}
/* t1 = Z * Z */
if ((err = ltc_mp_sqr(R->z, t1)) != CRYPT_OK) { goto done; }
if ((err = ltc_mp_montgomery_reduce(t1, modulus, mp)) != CRYPT_OK) { goto done; }

View file

@ -245,6 +245,41 @@ static int s_ecc_issue630(void)
return 0;
}
/* https://github.com/libtom/libtomcrypt/issues/116
verifies that ECDSA verification succeeds for an all-zero hash when the non-Shamir fallback is used
i.e. when u1 == 0 and the code path computes 0 * G + u2 * Q via two separate point multiplications
*/
static int s_ecc_issue116(void)
{
const ltc_ecc_curve *dp;
ecc_key key;
int err, stat;
unsigned long siglen;
unsigned char hash[20];
unsigned char sig[128];
ltc_ecc_sig_opts sig_opts = { .type = LTC_ECCSIG_RFC7518, .prng = &yarrow_prng, .wprng = find_prng("yarrow") };
int (*saved_mul2add)(const ecc_point *A, void *kA, const ecc_point *B, void *kB, ecc_point *C, const void *ma, const void *modulus);
XMEMSET(hash, 0, sizeof(hash));
DO(ecc_find_curve("SECP256R1", &dp));
DO(ecc_make_key_ex(&yarrow_prng, find_prng("yarrow"), &key, dp));
siglen = sizeof(sig);
DO(ecc_sign_hash_v2(hash, sizeof(hash), sig, &siglen, &sig_opts, &key));
saved_mul2add = ltc_mp.ecc_mul2add;
ltc_mp.ecc_mul2add = NULL;
stat = 0;
err = ecc_verify_hash_v2(sig, siglen, hash, sizeof(hash), &(ltc_ecc_sig_opts){ .type = LTC_ECCSIG_RFC7518 }, &stat, &key);
ltc_mp.ecc_mul2add = saved_mul2add;
ecc_free(&key);
if (err != CRYPT_OK) return err;
if (stat != 1) return CRYPT_FAIL_TESTVECTOR;
return CRYPT_OK;
}
/* https://github.com/libtom/libtomcrypt/issues/108 */
static int s_ecc_issue108(void)
{
@ -332,6 +367,160 @@ static int s_ecc_issue443_447(void)
return CRYPT_OK;
}
/* https://github.com/libtom/libtomcrypt/issues/446 */
static int s_ecc_issue446(void)
{
const ltc_ecc_curve* cu;
ecc_key key1, key2, key3, key4;
int stat = 0;
ltc_ecc_sig_opts sig_opts = { .type = LTC_ECCSIG_ANSIX962 };
unsigned char hash[64];
unsigned long hashlen;
const unsigned char msg1[] = { 0x31,0x32,0x33,0x34,0x30,0x30 };
const unsigned char msg2[] = { 0x36,0x39,0x38,0x31,0x39 };
const unsigned char msg3[] = { 0x4d,0x73,0x67 };
const unsigned char msg4[] = { 0x54,0x65,0x73,0x74 };
const unsigned char pub1[] = {
0x04, /* secp256r1 */
0x29,0x27,0xb1,0x05,0x12,0xba,0xe3,0xed,0xdc,0xfe,0x46,0x78,0x28,0x12,0x8b,0xad,
0x29,0x03,0x26,0x99,0x19,0xf7,0x08,0x60,0x69,0xc8,0xc4,0xdf,0x6c,0x73,0x28,0x38,
0xc7,0x78,0x79,0x64,0xea,0xac,0x00,0xe5,0x92,0x1f,0xb1,0x49,0x8a,0x60,0xf4,0x60,
0x67,0x66,0xb3,0xd9,0x68,0x50,0x01,0x55,0x8d,0x1a,0x97,0x4e,0x73,0x41,0x51,0x3e
};
const unsigned char pub2[] = {
0x04, /* secp224r1 */
0x53,0x88,0x35,0x38,0xd8,0x66,0x09,0x21,0x7a,0x4d,0x9c,0x27,0x99,0x2e,
0x2b,0xd7,0x04,0xa4,0xbb,0x12,0xfb,0x91,0x25,0x3c,0xd4,0xf4,0x96,0xfa,
0x00,0xb3,0x2f,0x3a,0x2d,0x15,0x3a,0x52,0xbb,0x57,0x76,0x50,0xcd,0xf1,
0xb7,0x3a,0x34,0x05,0x35,0xc7,0xe7,0x3d,0xeb,0x59,0x9a,0xa9,0x50,0x88
};
const unsigned char pub3[] = {
0x04, /* brainpoolP256r1 */
0x30,0x29,0x1b,0xca,0x4e,0xb9,0x3c,0x41,0x99,0x1d,0xae,0xc0,0xfd,0x2e,0x87,0xc2,
0x77,0x8d,0x95,0x8d,0x9a,0x8e,0x2f,0xa4,0xe6,0xd3,0x83,0x5d,0x48,0x5b,0xc2,0x3e,
0x42,0x17,0xf6,0x84,0xcf,0xdd,0x83,0x6d,0x7c,0xf6,0x09,0x9a,0x34,0xc6,0x82,0x61,
0xe5,0xa0,0x94,0xad,0xb5,0xb3,0xe4,0xa9,0x26,0x2f,0xf5,0x95,0x7c,0x1b,0x1b,0xa8
};
const unsigned char pub4[] = {
0x04, /* brainpoolP256t1 */
0x44,0x24,0xf0,0x9e,0x4f,0x86,0x92,0x31,0xe4,0x7b,0x1e,0x34,0x26,0xec,0x26,0x9e,
0x5b,0xef,0x19,0xec,0x64,0xcc,0x96,0xda,0x5b,0x2e,0xfd,0xd2,0x11,0x6b,0x83,0xf0,
0x7a,0x86,0x3d,0x25,0x74,0x3b,0xb2,0x60,0xa6,0x38,0xde,0x64,0x97,0x96,0xc6,0x76,
0xe1,0x38,0x05,0x86,0x5c,0x8b,0xb9,0xda,0xc3,0x8e,0x16,0xfa,0xe5,0x4b,0x0c,0xef
};
/* msg1+pub1+sig1 test vector is from wycheproof/ecdsa_test VALID */
const unsigned char sig1[] = {
0x30,0x45,0x02,0x20,0x2b,0xa3,0xa8,0xbe,0x6b,0x94,0xd5,0xec,0x80,0xa6,0xd9,0xd1,0x19,0x0a,
0x43,0x6e,0xff,0xe5,0x0d,0x85,0xa1,0xee,0xe8,0x59,0xb8,0xcc,0x6a,0xf9,0xbd,0x5c,0x2e,0x18,
0x02,0x21,0x00,0xb3,0x29,0xf4,0x79,0xa2,0xbb,0xd0,0xa5,0xc3,0x84,0xee,0x14,0x93,0xb1,0xf5,
0x18,0x6a,0x87,0x13,0x9c,0xac,0x5d,0xf4,0x08,0x7c,0x13,0x4b,0x49,0x15,0x68,0x47,0xdb
};
/* msg1+pub1+sig2 test vector is from wycheproof/ecdsa_test INVALID (changing tag value of sequence) */
const unsigned char sig2[] = {
0x31,0x45,0x02,0x20,0x2b,0xa3,0xa8,0xbe,0x6b,0x94,0xd5,0xec,0x80,0xa6,0xd9,0xd1,0x19,0x0a,
0x43,0x6e,0xff,0xe5,0x0d,0x85,0xa1,0xee,0xe8,0x59,0xb8,0xcc,0x6a,0xf9,0xbd,0x5c,0x2e,0x18,
0x02,0x21,0x00,0xb3,0x29,0xf4,0x79,0xa2,0xbb,0xd0,0xa5,0xc3,0x84,0xee,0x14,0x93,0xb1,0xf5,
0x18,0x6a,0x87,0x13,0x9c,0xac,0x5d,0xf4,0x08,0x7c,0x13,0x4b,0x49,0x15,0x68,0x47,0xdb
};
/* msg2+pub1+sig3 test vector is from wycheproof/ecdsa_test VALID (Edge case for Shamir multiplication) */
const unsigned char sig3[] = {
0x30,0x44,0x02,0x20,0x64,0xa1,0xaa,0xb5,0x00,0x0d,0x0e,0x80,0x4f,0x3e,0x2f,0xc0,0x2b,0xde,
0xe9,0xbe,0x8f,0xf3,0x12,0x33,0x4e,0x2b,0xa1,0x6d,0x11,0x54,0x7c,0x97,0x71,0x1c,0x89,0x8e,
0x02,0x20,0x6a,0xf0,0x15,0x97,0x1c,0xc3,0x0b,0xe6,0xd1,0xa2,0x06,0xd4,0xe0,0x13,0xe0,0x99,
0x77,0x72,0xa2,0xf9,0x1d,0x73,0x28,0x6f,0xfd,0x68,0x3b,0x9b,0xb2,0xcf,0x4f,0x1b
};
/* msg1+pub1+sig4 test vector is from wycheproof/ecdsa_test INVALID (long form encoding of length) */
const unsigned char sig4[] = {
0x30,0x81,0x45,0x02,0x20,0x2b,0xa3,0xa8,0xbe,0x6b,0x94,0xd5,0xec,0x80,0xa6,0xd9,0xd1,0x19,
0x0a,0x43,0x6e,0xff,0xe5,0x0d,0x85,0xa1,0xee,0xe8,0x59,0xb8,0xcc,0x6a,0xf9,0xbd,0x5c,0x2e,
0x18,0x02,0x21,0x00,0xb3,0x29,0xf4,0x79,0xa2,0xbb,0xd0,0xa5,0xc3,0x84,0xee,0x14,0x93,0xb1,
0xf5,0x18,0x6a,0x87,0x13,0x9c,0xac,0x5d,0xf4,0x08,0x7c,0x13,0x4b,0x49,0x15,0x68,0x47,0xdb
};
/* msg1+pub1+sig5 test vector is from wycheproof/ecdsa_test INVALID (length contains leading 0) */
const unsigned char sig5[] = {
0x30,0x82,0x00,0x45,0x02,0x20,0x2b,0xa3,0xa8,0xbe,0x6b,0x94,0xd5,0xec,0x80,0xa6,0xd9,0xd1,
0x19,0x0a,0x43,0x6e,0xff,0xe5,0x0d,0x85,0xa1,0xee,0xe8,0x59,0xb8,0xcc,0x6a,0xf9,0xbd,0x5c,
0x2e,0x18,0x02,0x21,0x00,0xb3,0x29,0xf4,0x79,0xa2,0xbb,0xd0,0xa5,0xc3,0x84,0xee,0x14,0x93,
0xb1,0xf5,0x18,0x6a,0x87,0x13,0x9c,0xac,0x5d,0xf4,0x08,0x7c,0x13,0x4b,0x49,0x15,0x68,0x47,0xdb
};
/* msg3+pub2+sig6 test vector is from wycheproof/ecdsa_test VALID (extreme value for k and edgecase s) */
const unsigned char sig6[] = {
0x30,0x3c,0x02,0x1c,0x70,0x6a,0x46,0xdc,0x76,0xdc,0xb7,0x67,0x98,0xe6,0x0e,0x6d,0x89,0x47,0x47,0x88,0xd1,
0x6d,0xc1,0x80,0x32,0xd2,0x68,0xfd,0x1a,0x70,0x4f,0xa6,0x02,0x1c,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,
0x55,0x55,0x55,0x55,0x55,0x55,0x07,0x8b,0xa0,0x3d,0xa5,0x6a,0x06,0x9f,0x0d,0xc1,0xc9,0x74,0x0e,0x14
};
/* msg4+pub3+sig7 test vector is from wycheproof/ecdsa_test VALID (extreme value for k) */
const unsigned char sig7[] = {
0x30,0x44,0x02,0x20,0x74,0x3c,0xf1,0xb8,0xb5,0xcd,0x4f,0x2e,0xb5,0x5f,0x8a,0xa3,0x69,0x59,
0x3a,0xc4,0x36,0xef,0x04,0x41,0x66,0x69,0x9e,0x37,0xd5,0x1a,0x14,0xc2,0xce,0x13,0xea,0x0e,
0x02,0x20,0x77,0x47,0x2d,0x9a,0x28,0xb4,0xec,0xe7,0x1c,0xf4,0x13,0xa6,0x8e,0xac,0x0e,0xb4,
0x23,0xa1,0x6f,0xb4,0x62,0xb1,0xf4,0x87,0x06,0xfe,0xd4,0x8c,0xa4,0x37,0xbd,0x2d
};
/* msg4+pub4+sig8 test vector is from wycheproof/ecdsa_test VALID (extreme value for k and s^-1) */
const unsigned char sig8[] = {
0x30,0x45,0x02,0x21,0x00,0x83,0x38,0x42,0x7c,0x7c,0xf4,0xd1,0x1c,0xb9,0x81,0xd9,0xb1,0x87,
0x93,0xe3,0x77,0x9c,0x49,0x4c,0x50,0x2c,0x75,0xbd,0x73,0x9e,0x57,0x8d,0xe2,0xa7,0x00,0x57,
0x8d,0x02,0x20,0x30,0x90,0xf4,0x87,0xe5,0x1f,0x9e,0x35,0xc8,0xaf,0x70,0xbb,0x9a,0xb7,0xdf,
0x45,0x03,0x7e,0x23,0x0a,0x33,0xd2,0xc1,0xfd,0x96,0xe4,0x04,0x25,0x4f,0xcb,0x86,0x79
};
DO(ecc_find_curve("secp256r1", &cu));
DO(ecc_set_curve(cu, &key1));
DO(ecc_set_key(pub1, sizeof(pub1), PK_PUBLIC, &key1));
DO(ecc_find_curve("secp224r1", &cu));
DO(ecc_set_curve(cu, &key2));
DO(ecc_set_key(pub2, sizeof(pub2), PK_PUBLIC, &key2));
DO(ecc_find_curve("brainpoolP256r1", &cu));
DO(ecc_set_curve(cu, &key3));
DO(ecc_set_key(pub3, sizeof(pub3), PK_PUBLIC, &key3));
DO(ecc_find_curve("brainpoolP256t1", &cu));
DO(ecc_set_curve(cu, &key4));
DO(ecc_set_key(pub4, sizeof(pub4), PK_PUBLIC, &key4));
hashlen = sizeof(hash);
DO(hash_memory(find_hash("sha256"), msg1, sizeof(msg1), hash, &hashlen));
DO(ecc_verify_hash_v2(sig1, sizeof(sig1), hash, hashlen, &sig_opts, &stat, &key1));
ENSUREX(stat == 1, "sig1 - wycheproof / Valid");
SHOULD_FAIL(ecc_verify_hash_v2(sig2, sizeof(sig2), hash, hashlen, &sig_opts, &stat, &key1));
ENSUREX(stat == 0, "sig2 - wycheproof / changing tag value of sequence");
SHOULD_FAIL(ecc_verify_hash_v2(sig4, sizeof(sig4), hash, hashlen, &sig_opts, &stat, &key1));
ENSUREX(stat == 0, "sig4 - wycheproof / long form encoding of length");
SHOULD_FAIL(ecc_verify_hash_v2(sig5, sizeof(sig5), hash, hashlen, &sig_opts, &stat, &key1));
ENSUREX(stat == 0, "sig5 - wycheproof / length contains leading 0");
hashlen = sizeof(hash);
DO(hash_memory(find_hash("sha256"), msg2, sizeof(msg2), hash, &hashlen));
DO(ecc_verify_hash_v2(sig3, sizeof(sig3), hash, hashlen, &sig_opts, &stat, &key1));
ENSUREX(stat == 1, "wycheproof / Edge case for Shamir multiplication");
hashlen = sizeof(hash);
DO(hash_memory(find_hash("sha224"), msg3, sizeof(msg3), hash, &hashlen));
DO(ecc_verify_hash_v2(sig6, sizeof(sig6), hash, hashlen, &sig_opts, &stat, &key2));
ENSUREX(stat == 1, "wycheproof / extreme value for k and edgecase s");
hashlen = sizeof(hash);
DO(hash_memory(find_hash("sha256"), msg4, sizeof(msg4), hash, &hashlen));
DO(ecc_verify_hash_v2(sig7, sizeof(sig7), hash, hashlen, &sig_opts, &stat, &key3));
ENSUREX(stat == 1, "wycheproof / extreme value for k");
DO(ecc_verify_hash_v2(sig8, sizeof(sig8), hash, hashlen, &sig_opts, &stat, &key4));
ENSUREX(stat == 1, "wycheproof / extreme value for k and s^-1");
ecc_free(&key1);
ecc_free(&key2);
ecc_free(&key3);
ecc_free(&key4);
return CRYPT_OK;
}
static int s_ecc_test_mp(void)
{
void *a, *modulus, *order;
@ -2083,6 +2272,7 @@ int ecc_test(void)
{
if (ltc_mp.name == NULL) return CRYPT_NOP;
DO(s_ecc_issue446());
DO(s_ecc_rfc6979());
DO(s_ecc_old_api()); /* up to 1.18 */
DO(s_ecc_new_api());
@ -2091,6 +2281,7 @@ int ecc_test(void)
DO(s_ecc_issue108());
DO(s_ecc_issue443_447());
DO(s_ecc_issue630());
DO(s_ecc_issue116());
#ifdef LTC_ECC_SHAMIR
DO(s_ecc_test_shamir());
DO(s_ecc_test_recovery());