Skip to content

Commit 3ee04ff

Browse files
committed
Finish up RFC6979 ECDSA keygen
Signed-off-by: Steffen Jaeckel <[email protected]>
1 parent b3f393b commit 3ee04ff

File tree

10 files changed

+504
-87
lines changed

10 files changed

+504
-87
lines changed

doc/crypt.tex

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5816,6 +5816,7 @@ \subsection{Signature Generation}
58165816
To sign a message digest (hash) use the following function:
58175817

58185818
\index{ecc\_sign\_hash()}
5819+
\index{ECC\_SET\_RFC6979\_HASH\_ALG()}
58195820
\begin{verbatim}
58205821
int ecc_sign_hash(const unsigned char *in,
58215822
unsigned long inlen,
@@ -5830,8 +5831,14 @@ \subsection{Signature Generation}
58305831
will be stored in the array pointed to by \code{out} of length \code{outlen} octets. The function requires that the \textit{ECC}
58315832
\code{key} provided must be a private key.
58325833

5833-
It requires a properly seeded \textit{PRNG} for standard \textit{ECDSA}, or if \code{prng} is NULL and/or \code{wprng} is less than zero,
5834-
the private key and the message itself will be used to create a deterministic signature according to \textit{RFC6979}.
5834+
In order to execute standard \textit{ECDSA} it requires a properly seeded \textit{PRNG} which gets passed via \code{prng} and \code{wprng}.
5835+
5836+
The deterministic signature mechanism according to \textit{RFC6979} is also supported. This does not require a \textit{PRNG}, but
5837+
instead a valid hash function shall be set via the macro
5838+
5839+
\code{ECC\_SET\_RFC6979\_HASH\_ALG(key, hash\_alg)}
5840+
5841+
The expected types of the arguments to that macro are \code{(ecc\_key*, const char*)}.
58355842

58365843
\index{ecc\_sign\_hash\_rfc7518()}
58375844
\begin{verbatim}

src/headers/tomcrypt_pk.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,18 @@ typedef struct {
281281

282282
/** The private key */
283283
void *k;
284+
285+
/** The hash algorithm to use when creating a signature.
286+
* Setting this will enable RFC6979 compatible signature generation.
287+
* The macro ECC_SET_RFC6979_HASH_ALG() is provided as a helper
288+
* to set this.*/
289+
const char *rfc6979_hash_alg;
284290
} ecc_key;
285291

292+
#define ECC_SET_RFC6979_HASH_ALG(key, alg) do { \
293+
(key)->rfc6979_hash_alg = (alg); \
294+
} while(0)
295+
286296
/** Formats of ECC signatures */
287297
typedef enum ecc_signature_type_ {
288298
/* ASN.1 encoded, ANSI X9.62 */
@@ -304,7 +314,6 @@ int ecc_get_size(const ecc_key *key);
304314
int ecc_find_curve(const char* name_or_oid, const ltc_ecc_curve** cu);
305315
int ecc_set_curve(const ltc_ecc_curve *cu, ecc_key *key);
306316
int ecc_generate_key(prng_state *prng, int wprng, ecc_key *key);
307-
int ecc_rfc6979_key(const ecc_key *priv, const unsigned char *in, int inlen, ecc_key *key);
308317
int ecc_set_key(const unsigned char *in, unsigned long inlen, int type, ecc_key *key);
309318
int ecc_get_key(unsigned char *out, unsigned long *outlen, int type, const ecc_key *key);
310319
int ecc_get_oid_str(char *out, unsigned long *outlen, const ecc_key *key);

src/headers/tomcrypt_private.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,8 @@ int ecc_verify_hash_internal(void *r, void *s,
441441
const unsigned char *hash, unsigned long hashlen,
442442
int *stat, const ecc_key *key);
443443

444+
int ecc_rfc6979_key(const ecc_key *priv, const unsigned char *in, unsigned long inlen, ecc_key *key);
445+
444446
#ifdef LTC_SSH
445447
int ecc_ssh_ecdsa_encode_name(char *buffer, unsigned long *buflen, const ecc_key *key);
446448
#endif

src/pk/ecc/ecc_make_key.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ int ecc_generate_key(prng_state *prng, int wprng, ecc_key *key)
5959
goto error;
6060
}
6161
key->type = PK_PRIVATE;
62+
key->rfc6979_hash_alg = NULL;
6263

6364
/* success */
6465
err = CRYPT_OK;

src/pk/ecc/ecc_rfc6979_key.c

Lines changed: 86 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
1-
/* LibTomCrypt, modular cryptographic library -- Tom St Denis
2-
*
3-
* LibTomCrypt is a library that provides various cryptographic
4-
* algorithms in a highly modular and flexible manner.
5-
*
6-
* The library is free for all purposes without any express
7-
* guarantee it works.
8-
*/
1+
/* LibTomCrypt, modular cryptographic library -- Tom St Denis */
2+
/* SPDX-License-Identifier: Unlicense */
93

104
#include "tomcrypt_private.h"
115

@@ -25,99 +19,127 @@
2519
@param key [out] Newly created deterministic key
2620
@return CRYPT_OK if successful, upon error all allocated memory will be freed
2721
*/
28-
int ecc_rfc6979_key(const ecc_key *priv, const unsigned char *in, int inlen, ecc_key *key)
22+
int ecc_rfc6979_key(const ecc_key *priv, const unsigned char *in, unsigned long inlen, ecc_key *key)
2923
{
30-
int err, hash, i;
31-
unsigned char v[32], k[32], digest[32]; /* No way to determine hash so always use SHA256 */
32-
unsigned char buffer[256];
33-
unsigned long outlen, buflen, qlen;
24+
int err, hash = -1;
25+
unsigned char v[MAXBLOCKSIZE], k[MAXBLOCKSIZE];
26+
unsigned char buffer[256], sep[1], privkey[128];
27+
unsigned long order_bits, len_diff, pk_len, zero_extend, outlen, klen, vlen, buflen, qlen, hashsize;
28+
void *r, *d;
3429

3530
LTC_ARGCHK(ltc_mp.name != NULL);
31+
LTC_ARGCHK(priv != NULL);
3632
LTC_ARGCHK(key != NULL);
3733
LTC_ARGCHK(key->dp.size > 0);
3834

39-
hash = find_hash("sha256");
40-
if (hash == -1) {err = CRYPT_ERROR; goto error;}
35+
if (priv->rfc6979_hash_alg == NULL) {
36+
return CRYPT_INVALID_ARG;
37+
}
38+
hash = find_hash(priv->rfc6979_hash_alg);
39+
if ((err = hash_is_valid(hash)) != CRYPT_OK) {
40+
return err;
41+
}
42+
43+
hashsize = hash_descriptor[hash].hashsize;
44+
45+
if ((err = ltc_mp_init_multi(&r, &d, NULL)) != CRYPT_OK) {
46+
return err;
47+
}
4148

4249
/* Length, in bytes, of key */
43-
i = mp_count_bits(key->dp.order);
44-
qlen = (i+7) >> 3;
50+
order_bits = ltc_mp_count_bits(key->dp.order);
51+
qlen = (order_bits+7) >> 3;
52+
len_diff = qlen > inlen ? qlen - inlen : 0;
53+
pk_len = (ltc_mp_count_bits(priv->k)+7) >> 3;
54+
zero_extend = qlen - pk_len;
55+
XMEMSET(buffer, 0x00, len_diff + zero_extend);
4556

4657
/* RFC6979 3.2b, set V */
47-
for (i=0; i<32; i++) v[i] = 0x01;
58+
XMEMSET(v, 0x01, hashsize);
4859

4960
/* RFC6979 3.2c, set K */
50-
for (i=0; i<32; i++) k[i] = 0x00;
61+
XMEMSET(k, 0x00, hashsize);
5162

63+
if ((err = ltc_mp_to_unsigned_bin(priv->k, privkey) != CRYPT_OK)) { goto error; }
5264
/* RFC6979 3.2d, set K to HMAC_K(V::0x00::priv::in) */
53-
XMEMCPY(&buffer[0], v, 32);
54-
buffer[32] = 0x00;
55-
if ((err = mp_to_unsigned_bin(priv->k, &buffer[33]) != CRYPT_OK)) { goto error; }
56-
XMEMCPY(&buffer[33+qlen], in, inlen);
57-
buflen = 32 + 1 + qlen + inlen;
58-
outlen = sizeof(digest);
59-
if((err = hmac_memory(hash, k, 32, buffer, buflen, digest, &outlen)) != CRYPT_OK) { goto error; }
60-
XMEMCPY(k, digest, 32);
65+
sep[0] = 0;
66+
klen = sizeof(k);
67+
if((err = hmac_memory_multi(hash,
68+
k, hashsize,
69+
k, &klen,
70+
v, hashsize,
71+
sep, 1,
72+
buffer, zero_extend,
73+
privkey, qlen - zero_extend,
74+
buffer, len_diff,
75+
in, qlen - len_diff,
76+
LTC_NULL)) != CRYPT_OK) { goto error; }
6177

6278
/* RFC6979 3.2e, set V = HMAC_K(V) */
63-
outlen = sizeof(digest);
64-
if((err = hmac_memory(hash, k, 32, v, 32, digest, &outlen)) != CRYPT_OK) { goto error; }
65-
XMEMCPY(v, digest, 32);
79+
vlen = sizeof(v);
80+
if((err = hmac_memory(hash, k, klen, v, hashsize, v, &vlen)) != CRYPT_OK) { goto error; }
6681

6782
/* RFC6979 3.2f, set K to HMAC_K(V::0x01::priv::in) */
68-
XMEMCPY(&buffer[0], v, 32);
69-
buffer[32] = 0x01;
70-
if ((err = mp_to_unsigned_bin(priv->k, &buffer[33]) != CRYPT_OK)) { goto error; }
71-
XMEMCPY(&buffer[33+qlen], in, inlen);
72-
buflen = 32 + 1 + qlen + inlen;
73-
outlen = sizeof(digest);
74-
if((err = hmac_memory(hash, k, 32, buffer, buflen, digest, &outlen)) != CRYPT_OK) { goto error; }
75-
XMEMCPY(k, digest, 32);
83+
sep[0] = 0x01;
84+
outlen = sizeof(k);
85+
if((err = hmac_memory_multi(hash,
86+
k, klen,
87+
k, &klen,
88+
v, hashsize,
89+
sep, 1,
90+
buffer, zero_extend,
91+
privkey, qlen - zero_extend,
92+
buffer, len_diff,
93+
in, qlen - len_diff,
94+
LTC_NULL)) != CRYPT_OK) { goto error; }
7695

7796
/* RFC6979 3.2g, set V = HMAC_K(V) */
78-
outlen = sizeof(digest);
79-
if((err = hmac_memory(hash, k, 32, v, 32, digest, &outlen)) != CRYPT_OK) { goto error; }
80-
XMEMCPY(v, digest, 32);
97+
outlen = sizeof(v);
98+
if((err = hmac_memory(hash, k, klen, v, hashsize, v, &outlen)) != CRYPT_OK) { goto error; }
8199

82100
/* RFC6979 3.2h, generate and check key */
83101
do {
84102
/* concatenate hash bits into T */
85103
buflen = 0;
86104
while (buflen < qlen) {
87-
outlen = sizeof(digest);
88-
if((err = hmac_memory(hash, k, 32, v, 32, digest, &outlen)) != CRYPT_OK) { goto error; }
89-
XMEMCPY(v, digest, 32);
90-
XMEMCPY(&buffer[buflen], v, 32);
91-
buflen += 32;
105+
outlen = sizeof(v);
106+
if((err = hmac_memory(hash, k, klen, v, hashsize, v, &outlen)) != CRYPT_OK) { goto error; }
107+
XMEMCPY(&buffer[buflen], v, hashsize);
108+
buflen += hashsize;
92109
}
93110

94111
/* key->k = bits2int(T) */
95-
if ((err = mp_read_unsigned_bin(key->k, (unsigned char *)buffer, qlen)) != CRYPT_OK) { goto error; }
96-
97-
/* make the public key */
98-
if ((err = ltc_mp.ecc_ptmul(key->k, &key->dp.base, &key->pubkey, key->dp.A, key->dp.prime, 1)) != CRYPT_OK) {
99-
goto error;
112+
if ((err = ltc_mp_read_unsigned_bin(r, buffer, qlen)) != CRYPT_OK) { goto error; }
113+
if ((qlen * 8) > order_bits) {
114+
if ((err = ltc_mp_2expt(d, (qlen * 8) - order_bits)) != CRYPT_OK) { goto error; }
115+
if ((err = ltc_mp_div(r, d, r, NULL)) != CRYPT_OK) { goto error; }
116+
if ((err = ltc_mp_to_unsigned_bin(r, buffer)) != CRYPT_OK) { goto error; }
117+
qlen = ltc_mp_unsigned_bin_size(r);
100118
}
101119

120+
if ((err = ecc_set_key(buffer, qlen, PK_PRIVATE, key))!= CRYPT_OK) { goto error; }
121+
102122
/* check that k is in range [1,q-1] */
103-
if (mp_cmp_d(key->k, 0) == LTC_MP_GT && mp_cmp(key->k, key->dp.order) == LTC_MP_LT) {
104-
/* TODO: Check that pubkey.x != 0 (mod p) */
123+
if (ltc_mp_cmp_d(key->k, 0) == LTC_MP_GT && ltc_mp_cmp(key->k, key->dp.order) == LTC_MP_LT) {
124+
/* Check that pubkey.x != 0 (mod p) */
125+
if ((err = ltc_mp_mod(key->pubkey.x, key->dp.order, r)) != CRYPT_OK) { goto error; }
105126

106127
/* if we have a valid key, exit loop */
107-
break;
128+
if (ltc_mp_iszero(r) == LTC_MP_NO)
129+
break;
108130
} else {
131+
if (hashsize == sizeof(buffer)) {
132+
err = CRYPT_BUFFER_OVERFLOW;
133+
goto error;
134+
}
109135
/* K = HMAC_K(V::0x00) */
110-
XMEMCPY(&buffer[0], v, 32);
111-
buffer[32] = 0x00;
112-
buflen = 32 + 1;
113-
outlen = sizeof(digest);
114-
if((err = hmac_memory(hash, k, 32, buffer, buflen, digest, &outlen)) != CRYPT_OK) { goto error; }
115-
XMEMCPY(k, digest, 32);
136+
buffer[0] = 0x0;
137+
outlen = sizeof(k);
138+
if((err = hmac_memory_multi(hash, k, klen, k, &klen, v, hashsize, buffer, 1, LTC_NULL)) != CRYPT_OK) { goto error; }
116139

117140
/* V = HMAC_K(V) */
118-
outlen = sizeof(digest);
119-
if((err = hmac_memory(hash, k, 32, v, 32, digest, &outlen)) != CRYPT_OK) { goto error; }
120-
XMEMCPY(v, digest, 32);
141+
outlen = sizeof(v);
142+
if((err = hmac_memory(hash, k, klen, v, hashsize, v, &outlen)) != CRYPT_OK) { goto error; }
121143

122144
/* ... and try again! */
123145
}
@@ -132,12 +154,9 @@ int ecc_rfc6979_key(const ecc_key *priv, const unsigned char *in, int inlen, ecc
132154
error:
133155
ecc_free(key);
134156
cleanup:
157+
ltc_mp_cleanup_multi(&d, &r, NULL);
135158
return err;
136159
}
137160

138161
#endif
139162
#endif
140-
/* ref: $Format:%D$ */
141-
/* git commit: $Format:%H$ */
142-
/* commit time: $Format:%ai$ */
143-

src/pk/ecc/ecc_set_curve.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ int ecc_set_curve(const ltc_ecc_curve *cu, ecc_key *key)
1919
return err;
2020
}
2121

22+
key->rfc6979_hash_alg = NULL;
23+
2224
/* A, B, order, prime, Gx, Gy */
2325
if ((err = ltc_mp_read_radix(key->dp.prime, cu->prime, 16)) != CRYPT_OK) { goto error; }
2426
if ((err = ltc_mp_read_radix(key->dp.order, cu->order, 16)) != CRYPT_OK) { goto error; }

src/pk/ecc/ecc_set_key.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ int ecc_set_key(const unsigned char *in, unsigned long inlen, int type, ecc_key
3333
else if (type == PK_PUBLIC) {
3434
/* load public key */
3535
if ((err = ltc_ecc_import_point(in, inlen, prime, a, b, key->pubkey.x, key->pubkey.y)) != CRYPT_OK) { goto error; }
36-
if ((err = ltc_mp_set(key->pubkey.z, 1)) != CRYPT_OK) { goto error; }
36+
if ((err = ltc_mp_set(key->pubkey.z, 1)) != CRYPT_OK) { goto error; }
3737
}
3838
else {
3939
err = CRYPT_INVALID_PACKET;
@@ -46,6 +46,7 @@ int ecc_set_key(const unsigned char *in, unsigned long inlen, int type, ecc_key
4646
}
4747

4848
key->type = type;
49+
key->rfc6979_hash_alg = NULL;
4950
return CRYPT_OK;
5051

5152
error:

src/pk/ecc/ecc_sign_hash.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,8 @@
1111
@param inlen The length of the digest
1212
@param out [out] The destination for the signature
1313
@param outlen [in/out] The max size and resulting size of the signature
14-
@param prng An active PRNG state, NULL for RFC6979 deterministic signatures
15-
@param wprng The index of the PRNG you wish to use, -1 for RFC6979 deterministic signatures
16-
@param sigformat The format of the signature to generate (ecc_signature_type)
17-
@param recid [out] The recovery ID for this signature (optional)
14+
@param prng An active PRNG state
15+
@param wprng The index of the PRNG you wish to use
1816
@param key A private ECC key
1917
@return CRYPT_OK if successful
2018
*/

src/pk/ecc/ecc_sign_hash_internal.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,12 @@ int ecc_sign_hash_internal(const unsigned char *in, unsigned long inlen,
5757

5858
/* make up a key and export the public copy */
5959
do {
60-
if ((err = ecc_copy_curve(key, &pubkey)) != CRYPT_OK) { goto errnokey; }
61-
if ((err = ecc_generate_key(prng, wprng, &pubkey)) != CRYPT_OK) { goto errnokey; }
60+
if ((err = ecc_copy_curve(key, &pubkey)) != CRYPT_OK) { goto errnokey; }
61+
if (key->rfc6979_hash_alg != NULL) {
62+
if ((err = ecc_rfc6979_key(key, in, inlen, &pubkey)) != CRYPT_OK) { goto errnokey; }
63+
} else {
64+
if ((err = ecc_generate_key(prng, wprng, &pubkey)) != CRYPT_OK) { goto errnokey; }
65+
}
6266

6367
/* find r = x1 mod n */
6468
if ((err = ltc_mp_mod(pubkey.pubkey.x, p, r)) != CRYPT_OK) { goto error; }
@@ -78,7 +82,7 @@ int ecc_sign_hash_internal(const unsigned char *in, unsigned long inlen,
7882
if (ltc_mp_iszero(r) == LTC_MP_YES) {
7983
ecc_free(&pubkey);
8084
} else {
81-
if ((err = rand_bn_upto(b, p, prng, wprng)) != CRYPT_OK) { goto error; } /* b = blinding value */
85+
if ((err = rand_bn_upto(b, p, prng, wprng)) != CRYPT_OK) { goto error; } /* b = blinding value */
8286
/* find s = (e + xr)/k */
8387
if ((err = ltc_mp_mulmod(pubkey.k, b, p, pubkey.k)) != CRYPT_OK) { goto error; } /* k = kb */
8488
if ((err = ltc_mp_invmod(pubkey.k, p, pubkey.k)) != CRYPT_OK) { goto error; } /* k = 1/kb */

0 commit comments

Comments
 (0)