diff ecdsa.c @ 910:89555751c489 asm

merge up to 2013.63, improve ASM makefile rules a bit
author Matt Johnston <matt@ucc.asn.au>
date Thu, 27 Feb 2014 21:35:58 +0800
parents c19acba28590
children c0b1b7eb5c84
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ecdsa.c	Thu Feb 27 21:35:58 2014 +0800
@@ -0,0 +1,420 @@
+#include "options.h"
+#include "includes.h"
+#include "dbutil.h"
+#include "crypto_desc.h"
+#include "ecc.h"
+#include "ecdsa.h"
+#include "signkey.h"
+
+#ifdef 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(ecc_key * key) {
+#ifdef DROPBEAR_ECC_256
+	if (key->dp == ecc_curve_nistp256.dp) {
+		return DROPBEAR_SIGNKEY_ECDSA_NISTP256;
+	}
+#endif
+#ifdef DROPBEAR_ECC_384
+	if (key->dp == ecc_curve_nistp384.dp) {
+		return DROPBEAR_SIGNKEY_ECDSA_NISTP384;
+	}
+#endif
+#ifdef 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) {
+#ifdef DROPBEAR_ECC_256
+		case 256:
+			dp = ecc_curve_nistp256.dp;
+			break;
+#endif
+#ifdef DROPBEAR_ECC_384
+		case 384:
+			dp = ecc_curve_nistp384.dp;
+			break;
+#endif
+#ifdef DROPBEAR_ECC_521
+		case 521:
+			dp = ecc_curve_nistp521.dp;
+			break;
+#endif
+	}
+	if (!dp) {
+		dropbear_exit("Key size %d isn't valid. Try "
+#ifdef DROPBEAR_ECC_256
+			"256 "
+#endif
+#ifdef DROPBEAR_ECC_384
+			"384 "
+#endif
+#ifdef 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 = buf_getstring(buf, &key_ident_len);
+	/* string   "[identifier]" */
+	identifier = 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);
+		return NULL;
+	}
+
+	return new_key;
+}
+
+void buf_put_ecdsa_pub_key(buffer *buf, ecc_key *key) {
+	struct dropbear_ecc_curve *curve = NULL;
+	unsigned char key_ident[30];
+
+	curve = curve_for_dp(key->dp);
+	snprintf((char*)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, ecc_key *key, 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;
+	unsigned 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((char*)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, ecc_key *key, 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);
+	mp_clear_multi(r, s, v, w, u1, u2, p, e, m, NULL);
+	if (mp != NULL) { 
+		ltc_mp.montgomery_deinit(mp);
+	}
+	return ret;
+}
+
+
+
+#endif /* DROPBEAR_ECDSA */