|
| 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 | + */ |
| 9 | + |
| 10 | +#include "tomcrypt_private.h" |
| 11 | + |
| 12 | +#ifdef LTC_MECC |
| 13 | + |
| 14 | +#ifdef LTC_ECC_SHAMIR |
| 15 | + |
| 16 | +/** |
| 17 | + @file ecc_recover_key.c |
| 18 | + ECC Crypto, Russ Williams |
| 19 | +*/ |
| 20 | + |
| 21 | +static int _ecc_recover_key(const unsigned char *sig, unsigned long siglen, |
| 22 | + const unsigned char *hash, unsigned long hashlen, |
| 23 | + int recid, ecc_key *key) |
| 24 | +{ |
| 25 | + ecc_point *mG = NULL, *mQ = NULL, *mR = NULL; |
| 26 | + void *p, *m, *a, *b; |
| 27 | + void *r, *s, *v, *w, *t1, *t2, *u1, *u2, *v1, *v2, *e, *x, *y, *a_plus3; |
| 28 | + void *mu = NULL, *ma = NULL; |
| 29 | + void *mp = NULL; |
| 30 | + int err; |
| 31 | + unsigned long pbits, pbytes, i, shift_right; |
| 32 | + unsigned char ch, buf[MAXBLOCKSIZE]; |
| 33 | + |
| 34 | + LTC_ARGCHK(sig != NULL); |
| 35 | + LTC_ARGCHK(hash != NULL); |
| 36 | + LTC_ARGCHK(key != NULL); |
| 37 | + |
| 38 | + /* BEWARE: requires sqrtmod_prime */ |
| 39 | + if (ltc_mp.sqrtmod_prime == NULL) { |
| 40 | + return CRYPT_ERROR; |
| 41 | + } |
| 42 | + |
| 43 | + /* allocate ints */ |
| 44 | + if ((err = mp_init_multi(&r, &s, &v, &w, &t1, &t2, &u1, &u2, &v1, &v2, &e, &x, &y, &a_plus3, NULL)) != CRYPT_OK) { |
| 45 | + return err; |
| 46 | + } |
| 47 | + |
| 48 | + p = key->dp.order; |
| 49 | + m = key->dp.prime; |
| 50 | + a = key->dp.A; |
| 51 | + b = key->dp.B; |
| 52 | + if ((err = mp_add_d(a, 3, a_plus3)) != CRYPT_OK) { |
| 53 | + goto error; |
| 54 | + } |
| 55 | + |
| 56 | + /* allocate points */ |
| 57 | + mG = ltc_ecc_new_point(); |
| 58 | + mQ = ltc_ecc_new_point(); |
| 59 | + mR = ltc_ecc_new_point(); |
| 60 | + if (mR == NULL || mQ == NULL || mG == NULL) { |
| 61 | + err = CRYPT_MEM; |
| 62 | + goto error; |
| 63 | + } |
| 64 | + |
| 65 | + /* Only ASN.1 format signatures supported for now */ |
| 66 | + if ((err = der_decode_sequence_multi_ex(sig, siglen, LTC_DER_SEQ_SEQUENCE | LTC_DER_SEQ_STRICT, |
| 67 | + LTC_ASN1_INTEGER, 1UL, r, |
| 68 | + LTC_ASN1_INTEGER, 1UL, s, |
| 69 | + LTC_ASN1_EOL, 0UL, NULL)) != CRYPT_OK) { goto error; } |
| 70 | + |
| 71 | + /* check for zero */ |
| 72 | + if (mp_cmp_d(r, 0) != LTC_MP_GT || mp_cmp_d(s, 0) != LTC_MP_GT || |
| 73 | + mp_cmp(r, p) != LTC_MP_LT || mp_cmp(s, p) != LTC_MP_LT) { |
| 74 | + err = CRYPT_INVALID_PACKET; |
| 75 | + goto error; |
| 76 | + } |
| 77 | + |
| 78 | + /* read hash - truncate if needed */ |
| 79 | + pbits = mp_count_bits(p); |
| 80 | + pbytes = (pbits+7) >> 3; |
| 81 | + if (pbits > hashlen*8) { |
| 82 | + if ((err = mp_read_unsigned_bin(e, (unsigned char *)hash, hashlen)) != CRYPT_OK) { goto error; } |
| 83 | + } |
| 84 | + else if (pbits % 8 == 0) { |
| 85 | + if ((err = mp_read_unsigned_bin(e, (unsigned char *)hash, pbytes)) != CRYPT_OK) { goto error; } |
| 86 | + } |
| 87 | + else { |
| 88 | + shift_right = 8 - pbits % 8; |
| 89 | + for (i=0, ch=0; i<pbytes; i++) { |
| 90 | + buf[i] = ch; |
| 91 | + ch = (hash[i] << (8-shift_right)); |
| 92 | + buf[i] = buf[i] ^ (hash[i] >> shift_right); |
| 93 | + } |
| 94 | + if ((err = mp_read_unsigned_bin(e, (unsigned char *)buf, pbytes)) != CRYPT_OK) { goto error; } |
| 95 | + } |
| 96 | + |
| 97 | + /* decompress point from r=(x mod p) - BEWARE: requires sqrtmod_prime */ |
| 98 | + /* x = r + p*(recid/2) */ |
| 99 | + if ((err = mp_set(x, recid/2)) != CRYPT_OK) { goto error; } |
| 100 | + if ((err = mp_mulmod(p, x, m, x)) != CRYPT_OK) { goto error; } |
| 101 | + if ((err = mp_add(x, r, x)) != CRYPT_OK) { goto error; } |
| 102 | + /* compute x^3 */ |
| 103 | + if ((err = mp_sqr(x, t1)) != CRYPT_OK) { goto error; } |
| 104 | + if ((err = mp_mulmod(t1, x, m, t1)) != CRYPT_OK) { goto error; } |
| 105 | + /* compute x^3 + a*x */ |
| 106 | + if ((err = mp_mulmod(a, x, m, t2)) != CRYPT_OK) { goto error; } |
| 107 | + if ((err = mp_add(t1, t2, t1)) != CRYPT_OK) { goto error; } |
| 108 | + /* compute x^3 + a*x + b */ |
| 109 | + if ((err = mp_add(t1, b, t1)) != CRYPT_OK) { goto error; } |
| 110 | + /* compute sqrt(x^3 + a*x + b) */ |
| 111 | + if ((err = mp_sqrtmod_prime(t1, m, t2)) != CRYPT_OK) { goto error; } |
| 112 | + |
| 113 | + /* fill in mR */ |
| 114 | + if ((err = mp_copy(x, mR->x)) != CRYPT_OK) { goto error; } |
| 115 | + if ((mp_isodd(t2) && (recid%2)) || (!mp_isodd(t2) && !(recid%2))) { |
| 116 | + if ((err = mp_mod(t2, m, mR->y)) != CRYPT_OK) { goto error; } |
| 117 | + } |
| 118 | + else { |
| 119 | + if ((err = mp_submod(m, t2, m, mR->y)) != CRYPT_OK) { goto error; } |
| 120 | + } |
| 121 | + if ((err = mp_set(mR->z, 1)) != CRYPT_OK) { goto error; } |
| 122 | + |
| 123 | + /* w = r^-1 mod n */ |
| 124 | + if ((err = mp_invmod(r, p, w)) != CRYPT_OK) { goto error; } |
| 125 | + /* v1 = sw */ |
| 126 | + if ((err = mp_mulmod(s, w, p, v1)) != CRYPT_OK) { goto error; } |
| 127 | + /* v2 = -ew */ |
| 128 | + if ((err = mp_mulmod(e, w, p, v2)) != CRYPT_OK) { goto error; } |
| 129 | + if ((err = mp_submod(p, v2, p, v2)) != CRYPT_OK) { goto error; } |
| 130 | + |
| 131 | + /* w = s^-1 mod n */ |
| 132 | + if ((err = mp_invmod(s, p, w)) != CRYPT_OK) { goto error; } |
| 133 | + /* u1 = ew */ |
| 134 | + if ((err = mp_mulmod(e, w, p, u1)) != CRYPT_OK) { goto error; } |
| 135 | + /* u2 = rw */ |
| 136 | + if ((err = mp_mulmod(r, w, p, u2)) != CRYPT_OK) { goto error; } |
| 137 | + |
| 138 | + /* find mG */ |
| 139 | + if ((err = ltc_ecc_copy_point(&key->dp.base, mG)) != CRYPT_OK) { goto error; } |
| 140 | + |
| 141 | + /* find the montgomery mp */ |
| 142 | + if ((err = mp_montgomery_setup(m, &mp)) != CRYPT_OK) { goto error; } |
| 143 | + |
| 144 | + /* for curves with a == -3 keep ma == NULL */ |
| 145 | + if (mp_cmp(a_plus3, m) != LTC_MP_EQ) { |
| 146 | + if ((err = mp_init_multi(&mu, &ma, NULL)) != CRYPT_OK) { goto error; } |
| 147 | + if ((err = mp_montgomery_normalization(mu, m)) != CRYPT_OK) { goto error; } |
| 148 | + if ((err = mp_mulmod(a, mu, m, ma)) != CRYPT_OK) { goto error; } |
| 149 | + } |
| 150 | + |
| 151 | + /* recover mQ from mR */ |
| 152 | + /* compute v1*mR + v2*mG = mQ using Shamir's trick */ |
| 153 | + if ((err = ltc_mp.ecc_mul2add(mR, v1, mG, v2, mQ, ma, m)) != CRYPT_OK) { goto error; } |
| 154 | + |
| 155 | + /* compute u1*mG + u2*mQ = mG using Shamir's trick */ |
| 156 | + if ((err = ltc_mp.ecc_mul2add(mG, u1, mQ, u2, mG, ma, m)) != CRYPT_OK) { goto error; } |
| 157 | + |
| 158 | + /* v = X_x1 mod n */ |
| 159 | + if ((err = mp_mod(mG->x, p, v)) != CRYPT_OK) { goto error; } |
| 160 | + |
| 161 | + /* does v == r */ |
| 162 | + if (mp_cmp(v, r) == LTC_MP_EQ) { |
| 163 | + /* found public key which verifies signature */ |
| 164 | + if ((err = ltc_ecc_copy_point(mQ, &key->pubkey)) != CRYPT_OK) { goto error; } |
| 165 | + /* point on the curve + other checks */ |
| 166 | + if ((err = ltc_ecc_verify_key(key)) != CRYPT_OK) { goto error; } |
| 167 | + |
| 168 | + key->type = PK_PUBLIC; |
| 169 | + |
| 170 | + err = CRYPT_OK; |
| 171 | + } |
| 172 | + else { |
| 173 | + /* not found - recid is wrong or we're unable to calculate public key for some other reason */ |
| 174 | + err = CRYPT_INVALID_ARG; |
| 175 | + } |
| 176 | + |
| 177 | +error: |
| 178 | + if (ma != NULL) mp_clear(ma); |
| 179 | + if (mu != NULL) mp_clear(mu); |
| 180 | + if (mp != NULL) mp_montgomery_free(mp); |
| 181 | + if (mR != NULL) ltc_ecc_del_point(mR); |
| 182 | + if (mQ != NULL) ltc_ecc_del_point(mQ); |
| 183 | + if (mG != NULL) ltc_ecc_del_point(mG); |
| 184 | + mp_clear_multi(r, s, v, w, t1, t2, u1, u2, v1, v2, e, x, y, a_plus3, NULL); |
| 185 | + return err; |
| 186 | +} |
| 187 | + |
| 188 | +/** |
| 189 | + Recover ECC public key from signature and hash |
| 190 | + @param sig The signature to verify |
| 191 | + @param siglen The length of the signature (octets) |
| 192 | + @param hash The hash (message digest) that was signed |
| 193 | + @param hashlen The length of the hash (octets) |
| 194 | + @param recid 0 or 1 to select parity ("v") |
| 195 | + @param key The recovered public ECC key |
| 196 | + @return CRYPT_OK if successful (even if the signature is not valid) |
| 197 | +*/ |
| 198 | +int ecc_recover_key(const unsigned char *sig, unsigned long siglen, |
| 199 | + const unsigned char *hash, unsigned long hashlen, |
| 200 | + int recid, ecc_key *key) |
| 201 | +{ |
| 202 | + return _ecc_recover_key(sig, siglen, hash, hashlen, recid, key); |
| 203 | +} |
| 204 | + |
| 205 | +#endif |
| 206 | +#endif |
| 207 | + |
| 208 | +/* ref: $Format:%D$ */ |
| 209 | +/* git commit: $Format:%H$ */ |
| 210 | +/* commit time: $Format:%ai$ */ |
0 commit comments