#include "includes.h" #include "dbutil.h" #include "crypto_desc.h" #include "ecc.h" #include "ecdsa.h" #include "signkey.h" #if DROPBEAR_ECDSA int signkey_is_ecdsa(enum signkey_type type) { return type == DROPBEAR_SIGNKEY_ECDSA_NISTP256 || type == DROPBEAR_SIGNKEY_ECDSA_NISTP384 || type == DROPBEAR_SIGNKEY_ECDSA_NISTP521; } enum signkey_type ecdsa_signkey_type(const ecc_key * key) { #if DROPBEAR_ECC_256 if (key->dp == ecc_curve_nistp256.dp) { return DROPBEAR_SIGNKEY_ECDSA_NISTP256; } #endif #if DROPBEAR_ECC_384 if (key->dp == ecc_curve_nistp384.dp) { return DROPBEAR_SIGNKEY_ECDSA_NISTP384; } #endif #if DROPBEAR_ECC_521 if (key->dp == ecc_curve_nistp521.dp) { return DROPBEAR_SIGNKEY_ECDSA_NISTP521; } #endif return DROPBEAR_SIGNKEY_NONE; } ecc_key *gen_ecdsa_priv_key(unsigned int bit_size) { const ltc_ecc_set_type *dp = NULL; /* curve domain parameters */ ecc_key *new_key = NULL; switch (bit_size) { #if DROPBEAR_ECC_256 case 256: dp = ecc_curve_nistp256.dp; break; #endif #if DROPBEAR_ECC_384 case 384: dp = ecc_curve_nistp384.dp; break; #endif #if DROPBEAR_ECC_521 case 521: dp = ecc_curve_nistp521.dp; break; #endif } if (!dp) { dropbear_exit("Key size %d isn't valid. Try " #if DROPBEAR_ECC_256 "256 " #endif #if DROPBEAR_ECC_384 "384 " #endif #if DROPBEAR_ECC_521 "521 " #endif , bit_size); } new_key = m_malloc(sizeof(*new_key)); if (ecc_make_key_ex(NULL, dropbear_ltc_prng, new_key, dp) != CRYPT_OK) { dropbear_exit("ECC error"); } return new_key; } ecc_key *buf_get_ecdsa_pub_key(buffer* buf) { unsigned char *key_ident = NULL, *identifier = NULL; unsigned int key_ident_len, identifier_len; buffer *q_buf = NULL; struct dropbear_ecc_curve **curve; ecc_key *new_key = NULL; /* string "ecdsa-sha2-[identifier]" */ key_ident = (unsigned char*)buf_getstring(buf, &key_ident_len); /* string "[identifier]" */ identifier = (unsigned char*)buf_getstring(buf, &identifier_len); if (key_ident_len != identifier_len + strlen("ecdsa-sha2-")) { TRACE(("Bad identifier lengths")) goto out; } if (memcmp(&key_ident[strlen("ecdsa-sha2-")], identifier, identifier_len) != 0) { TRACE(("mismatching identifiers")) goto out; } for (curve = dropbear_ecc_curves; *curve; curve++) { if (memcmp(identifier, (char*)(*curve)->name, strlen((char*)(*curve)->name)) == 0) { break; } } if (!*curve) { TRACE(("couldn't match ecc curve")) goto out; } /* string Q */ q_buf = buf_getstringbuf(buf); new_key = buf_get_ecc_raw_pubkey(q_buf, *curve); out: m_free(key_ident); m_free(identifier); if (q_buf) { buf_free(q_buf); q_buf = NULL; } TRACE(("leave buf_get_ecdsa_pub_key")) return new_key; } ecc_key *buf_get_ecdsa_priv_key(buffer *buf) { ecc_key *new_key = NULL; TRACE(("enter buf_get_ecdsa_priv_key")) new_key = buf_get_ecdsa_pub_key(buf); if (!new_key) { return NULL; } if (buf_getmpint(buf, new_key->k) != DROPBEAR_SUCCESS) { ecc_free(new_key); m_free(new_key); return NULL; } return new_key; } void buf_put_ecdsa_pub_key(buffer *buf, ecc_key *key) { struct dropbear_ecc_curve *curve = NULL; char key_ident[30]; curve = curve_for_dp(key->dp); snprintf(key_ident, sizeof(key_ident), "ecdsa-sha2-%s", curve->name); buf_putstring(buf, key_ident, strlen(key_ident)); buf_putstring(buf, curve->name, strlen(curve->name)); buf_put_ecc_raw_pubkey_string(buf, key); } void buf_put_ecdsa_priv_key(buffer *buf, ecc_key *key) { buf_put_ecdsa_pub_key(buf, key); buf_putmpint(buf, key->k); } void buf_put_ecdsa_sign(buffer *buf, const ecc_key *key, const buffer *data_buf) { /* Based on libtomcrypt's ecc_sign_hash but without the asn1 */ int err = DROPBEAR_FAILURE; struct dropbear_ecc_curve *curve = NULL; hash_state hs; unsigned char hash[64]; void *e = NULL, *p = NULL, *s = NULL, *r; char key_ident[30]; buffer *sigbuf = NULL; TRACE(("buf_put_ecdsa_sign")) curve = curve_for_dp(key->dp); if (ltc_init_multi(&r, &s, &p, &e, NULL) != CRYPT_OK) { goto out; } curve->hash_desc->init(&hs); curve->hash_desc->process(&hs, data_buf->data, data_buf->len); curve->hash_desc->done(&hs, hash); if (ltc_mp.unsigned_read(e, hash, curve->hash_desc->hashsize) != CRYPT_OK) { goto out; } if (ltc_mp.read_radix(p, (char *)key->dp->order, 16) != CRYPT_OK) { goto out; } for (;;) { ecc_key R_key; /* ephemeral key */ if (ecc_make_key_ex(NULL, dropbear_ltc_prng, &R_key, key->dp) != CRYPT_OK) { goto out; } if (ltc_mp.mpdiv(R_key.pubkey.x, p, NULL, r) != CRYPT_OK) { goto out; } if (ltc_mp.compare_d(r, 0) == LTC_MP_EQ) { /* try again */ ecc_free(&R_key); continue; } /* k = 1/k */ if (ltc_mp.invmod(R_key.k, p, R_key.k) != CRYPT_OK) { goto out; } /* s = xr */ if (ltc_mp.mulmod(key->k, r, p, s) != CRYPT_OK) { goto out; } /* s = e + xr */ if (ltc_mp.add(e, s, s) != CRYPT_OK) { goto out; } if (ltc_mp.mpdiv(s, p, NULL, s) != CRYPT_OK) { goto out; } /* s = (e + xr)/k */ if (ltc_mp.mulmod(s, R_key.k, p, s) != CRYPT_OK) { goto out; } ecc_free(&R_key); if (ltc_mp.compare_d(s, 0) != LTC_MP_EQ) { break; } } snprintf(key_ident, sizeof(key_ident), "ecdsa-sha2-%s", curve->name); buf_putstring(buf, key_ident, strlen(key_ident)); /* enough for nistp521 */ sigbuf = buf_new(200); buf_putmpint(sigbuf, (mp_int*)r); buf_putmpint(sigbuf, (mp_int*)s); buf_putbufstring(buf, sigbuf); err = DROPBEAR_SUCCESS; out: if (r && s && p && e) { ltc_deinit_multi(r, s, p, e, NULL); } if (sigbuf) { buf_free(sigbuf); } if (err == DROPBEAR_FAILURE) { dropbear_exit("ECC error"); } } /* returns values in s and r returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ static int buf_get_ecdsa_verify_params(buffer *buf, void *r, void* s) { int ret = DROPBEAR_FAILURE; unsigned int sig_len; unsigned int sig_pos; sig_len = buf_getint(buf); sig_pos = buf->pos; if (buf_getmpint(buf, r) != DROPBEAR_SUCCESS) { goto out; } if (buf_getmpint(buf, s) != DROPBEAR_SUCCESS) { goto out; } if (buf->pos - sig_pos != sig_len) { goto out; } ret = DROPBEAR_SUCCESS; out: return ret; } int buf_ecdsa_verify(buffer *buf, const ecc_key *key, const buffer *data_buf) { /* Based on libtomcrypt's ecc_verify_hash but without the asn1 */ int ret = DROPBEAR_FAILURE; hash_state hs; struct dropbear_ecc_curve *curve = NULL; unsigned char hash[64]; ecc_point *mG = NULL, *mQ = NULL; void *r = NULL, *s = NULL, *v = NULL, *w = NULL, *u1 = NULL, *u2 = NULL, *e = NULL, *p = NULL, *m = NULL; void *mp = NULL; /* verify * * w = s^-1 mod n * u1 = xw * u2 = rw * X = u1*G + u2*Q * v = X_x1 mod n * accept if v == r */ TRACE(("buf_ecdsa_verify")) curve = curve_for_dp(key->dp); mG = ltc_ecc_new_point(); mQ = ltc_ecc_new_point(); if (ltc_init_multi(&r, &s, &v, &w, &u1, &u2, &p, &e, &m, NULL) != CRYPT_OK || !mG || !mQ) { dropbear_exit("ECC error"); } if (buf_get_ecdsa_verify_params(buf, r, s) != DROPBEAR_SUCCESS) { goto out; } curve->hash_desc->init(&hs); curve->hash_desc->process(&hs, data_buf->data, data_buf->len); curve->hash_desc->done(&hs, hash); if (ltc_mp.unsigned_read(e, hash, curve->hash_desc->hashsize) != CRYPT_OK) { goto out; } /* get the order */ if (ltc_mp.read_radix(p, (char *)key->dp->order, 16) != CRYPT_OK) { goto out; } /* get the modulus */ if (ltc_mp.read_radix(m, (char *)key->dp->prime, 16) != CRYPT_OK) { goto out; } /* check for zero */ if (ltc_mp.compare_d(r, 0) == LTC_MP_EQ || ltc_mp.compare_d(s, 0) == LTC_MP_EQ || ltc_mp.compare(r, p) != LTC_MP_LT || ltc_mp.compare(s, p) != LTC_MP_LT) { goto out; } /* w = s^-1 mod n */ if (ltc_mp.invmod(s, p, w) != CRYPT_OK) { goto out; } /* u1 = ew */ if (ltc_mp.mulmod(e, w, p, u1) != CRYPT_OK) { goto out; } /* u2 = rw */ if (ltc_mp.mulmod(r, w, p, u2) != CRYPT_OK) { goto out; } /* find mG and mQ */ if (ltc_mp.read_radix(mG->x, (char *)key->dp->Gx, 16) != CRYPT_OK) { goto out; } if (ltc_mp.read_radix(mG->y, (char *)key->dp->Gy, 16) != CRYPT_OK) { goto out; } if (ltc_mp.set_int(mG->z, 1) != CRYPT_OK) { goto out; } if (ltc_mp.copy(key->pubkey.x, mQ->x) != CRYPT_OK || ltc_mp.copy(key->pubkey.y, mQ->y) != CRYPT_OK || ltc_mp.copy(key->pubkey.z, mQ->z) != CRYPT_OK) { goto out; } /* compute u1*mG + u2*mQ = mG */ if (ltc_mp.ecc_mul2add == NULL) { if (ltc_mp.ecc_ptmul(u1, mG, mG, m, 0) != CRYPT_OK) { goto out; } if (ltc_mp.ecc_ptmul(u2, mQ, mQ, m, 0) != CRYPT_OK) { goto out; } /* find the montgomery mp */ if (ltc_mp.montgomery_setup(m, &mp) != CRYPT_OK) { goto out; } /* add them */ if (ltc_mp.ecc_ptadd(mQ, mG, mG, m, mp) != CRYPT_OK) { goto out; } /* reduce */ if (ltc_mp.ecc_map(mG, m, mp) != CRYPT_OK) { goto out; } } else { /* use Shamir's trick to compute u1*mG + u2*mQ using half of the doubles */ if (ltc_mp.ecc_mul2add(mG, u1, mQ, u2, mG, m) != CRYPT_OK) { goto out; } } /* v = X_x1 mod n */ if (ltc_mp.mpdiv(mG->x, p, NULL, v) != CRYPT_OK) { goto out; } /* does v == r */ if (ltc_mp.compare(v, r) == LTC_MP_EQ) { ret = DROPBEAR_SUCCESS; } out: ltc_ecc_del_point(mG); ltc_ecc_del_point(mQ); ltc_deinit_multi(r, s, v, w, u1, u2, p, e, m, NULL); if (mp != NULL) { ltc_mp.montgomery_deinit(mp); } return ret; } #endif /* DROPBEAR_ECDSA */