- djm@cvs.openbsd.org 2010/04/16 01:47:26
     [PROTOCOL.certkeys auth-options.c auth-options.h auth-rsa.c]
     [auth2-pubkey.c authfd.c key.c key.h myproposal.h ssh-add.c]
     [ssh-agent.c ssh-dss.c ssh-keygen.1 ssh-keygen.c ssh-rsa.c]
     [sshconnect.c sshconnect2.c sshd.c]
     revised certificate format ssh-{dss,rsa}-cert-v01@openssh.com with the
     following changes:

     move the nonce field to the beginning of the certificate where it can
     better protect against chosen-prefix attacks on the signature hash

     Rename "constraints" field to "critical options"

     Add a new non-critical "extensions" field

     Add a serial number

     The older format is still support for authentication and cert generation
     (use "ssh-keygen -t v00 -s ca_key ..." to generate a v00 certificate)

     ok markus@
diff --git a/key.c b/key.c
index 66592c7..34f678b 100644
--- a/key.c
+++ b/key.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: key.c,v 1.86 2010/03/15 19:40:02 stevesk Exp $ */
+/* $OpenBSD: key.c,v 1.87 2010/04/16 01:47:26 djm Exp $ */
 /*
  * read_bignum():
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -61,7 +61,8 @@
 
 	cert = xcalloc(1, sizeof(*cert));
 	buffer_init(&cert->certblob);
-	buffer_init(&cert->constraints);
+	buffer_init(&cert->critical);
+	buffer_init(&cert->extensions);
 	cert->key_id = NULL;
 	cert->principals = NULL;
 	cert->signature_key = NULL;
@@ -82,6 +83,7 @@
 	switch (k->type) {
 	case KEY_RSA1:
 	case KEY_RSA:
+	case KEY_RSA_CERT_V00:
 	case KEY_RSA_CERT:
 		if ((rsa = RSA_new()) == NULL)
 			fatal("key_new: RSA_new failed");
@@ -92,6 +94,7 @@
 		k->rsa = rsa;
 		break;
 	case KEY_DSA:
+	case KEY_DSA_CERT_V00:
 	case KEY_DSA_CERT:
 		if ((dsa = DSA_new()) == NULL)
 			fatal("key_new: DSA_new failed");
@@ -124,6 +127,7 @@
 	switch (k->type) {
 	case KEY_RSA1:
 	case KEY_RSA:
+	case KEY_RSA_CERT_V00:
 	case KEY_RSA_CERT:
 		if ((k->rsa->d = BN_new()) == NULL)
 			fatal("key_new_private: BN_new failed");
@@ -139,6 +143,7 @@
 			fatal("key_new_private: BN_new failed");
 		break;
 	case KEY_DSA:
+	case KEY_DSA_CERT_V00:
 	case KEY_DSA_CERT:
 		if ((k->dsa->priv_key = BN_new()) == NULL)
 			fatal("key_new_private: BN_new failed");
@@ -165,7 +170,8 @@
 	u_int i;
 
 	buffer_free(&cert->certblob);
-	buffer_free(&cert->constraints);
+	buffer_free(&cert->critical);
+	buffer_free(&cert->extensions);
 	if (cert->key_id != NULL)
 		xfree(cert->key_id);
 	for (i = 0; i < cert->nprincipals; i++)
@@ -184,12 +190,14 @@
 	switch (k->type) {
 	case KEY_RSA1:
 	case KEY_RSA:
+	case KEY_RSA_CERT_V00:
 	case KEY_RSA_CERT:
 		if (k->rsa != NULL)
 			RSA_free(k->rsa);
 		k->rsa = NULL;
 		break;
 	case KEY_DSA:
+	case KEY_DSA_CERT_V00:
 	case KEY_DSA_CERT:
 		if (k->dsa != NULL)
 			DSA_free(k->dsa);
@@ -238,11 +246,13 @@
 
 	switch (a->type) {
 	case KEY_RSA1:
+	case KEY_RSA_CERT_V00:
 	case KEY_RSA_CERT:
 	case KEY_RSA:
 		return a->rsa != NULL && b->rsa != NULL &&
 		    BN_cmp(a->rsa->e, b->rsa->e) == 0 &&
 		    BN_cmp(a->rsa->n, b->rsa->n) == 0;
+	case KEY_DSA_CERT_V00:
 	case KEY_DSA_CERT:
 	case KEY_DSA:
 		return a->dsa != NULL && b->dsa != NULL &&
@@ -304,6 +314,8 @@
 	case KEY_RSA:
 		key_to_blob(k, &blob, &len);
 		break;
+	case KEY_DSA_CERT_V00:
+	case KEY_RSA_CERT_V00:
 	case KEY_DSA_CERT:
 	case KEY_RSA_CERT:
 		/* We want a fingerprint of the _key_ not of the cert */
@@ -631,6 +643,8 @@
 	case KEY_UNSPEC:
 	case KEY_RSA:
 	case KEY_DSA:
+	case KEY_DSA_CERT_V00:
+	case KEY_RSA_CERT_V00:
 	case KEY_DSA_CERT:
 	case KEY_RSA_CERT:
 		space = strchr(cp, ' ');
@@ -757,11 +771,13 @@
 		error("key_write: failed for RSA key");
 		return 0;
 	case KEY_DSA:
+	case KEY_DSA_CERT_V00:
 	case KEY_DSA_CERT:
 		if (key->dsa == NULL)
 			return 0;
 		break;
 	case KEY_RSA:
+	case KEY_RSA_CERT_V00:
 	case KEY_RSA_CERT:
 		if (key->rsa == NULL)
 			return 0;
@@ -793,6 +809,10 @@
 		return "RSA";
 	case KEY_DSA:
 		return "DSA";
+	case KEY_RSA_CERT_V00:
+		return "RSA-CERT-V00";
+	case KEY_DSA_CERT_V00:
+		return "DSA-CERT-V00";
 	case KEY_RSA_CERT:
 		return "RSA-CERT";
 	case KEY_DSA_CERT:
@@ -822,10 +842,14 @@
 		return "ssh-rsa";
 	case KEY_DSA:
 		return "ssh-dss";
-	case KEY_RSA_CERT:
+	case KEY_RSA_CERT_V00:
 		return "ssh-rsa-cert-v00@openssh.com";
-	case KEY_DSA_CERT:
+	case KEY_DSA_CERT_V00:
 		return "ssh-dss-cert-v00@openssh.com";
+	case KEY_RSA_CERT:
+		return "ssh-rsa-cert-v01@openssh.com";
+	case KEY_DSA_CERT:
+		return "ssh-dss-cert-v01@openssh.com";
 	}
 	return "ssh-unknown";
 }
@@ -836,9 +860,11 @@
 	switch (k->type) {
 	case KEY_RSA1:
 	case KEY_RSA:
+	case KEY_RSA_CERT_V00:
 	case KEY_RSA_CERT:
 		return BN_num_bits(k->rsa->n);
 	case KEY_DSA:
+	case KEY_DSA_CERT_V00:
 	case KEY_DSA_CERT:
 		return BN_num_bits(k->dsa->p);
 	}
@@ -882,6 +908,8 @@
 	case KEY_RSA1:
 		k->rsa = rsa_generate_private_key(bits);
 		break;
+	case KEY_RSA_CERT_V00:
+	case KEY_DSA_CERT_V00:
 	case KEY_RSA_CERT:
 	case KEY_DSA_CERT:
 		fatal("key_generate: cert keys cannot be generated directly");
@@ -912,9 +940,12 @@
 	buffer_append(&to->certblob, buffer_ptr(&from->certblob),
 	    buffer_len(&from->certblob));
 
-	buffer_append(&to->constraints, buffer_ptr(&from->constraints),
-	    buffer_len(&from->constraints));
+	buffer_append(&to->critical,
+	    buffer_ptr(&from->critical), buffer_len(&from->critical));
+	buffer_append(&to->extensions,
+	    buffer_ptr(&from->extensions), buffer_len(&from->extensions));
 
+	to->serial = from->serial;
 	to->type = from->type;
 	to->key_id = from->key_id == NULL ? NULL : xstrdup(from->key_id);
 	to->valid_after = from->valid_after;
@@ -940,6 +971,7 @@
 	Key *n = NULL;
 	switch (k->type) {
 	case KEY_DSA:
+	case KEY_DSA_CERT_V00:
 	case KEY_DSA_CERT:
 		n = key_new(k->type);
 		if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) ||
@@ -950,6 +982,7 @@
 		break;
 	case KEY_RSA:
 	case KEY_RSA1:
+	case KEY_RSA_CERT_V00:
 	case KEY_RSA_CERT:
 		n = key_new(k->type);
 		if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) ||
@@ -979,8 +1012,12 @@
 	} else if (strcmp(name, "ssh-dss") == 0) {
 		return KEY_DSA;
 	} else if (strcmp(name, "ssh-rsa-cert-v00@openssh.com") == 0) {
-		return KEY_RSA_CERT;
+		return KEY_RSA_CERT_V00;
 	} else if (strcmp(name, "ssh-dss-cert-v00@openssh.com") == 0) {
+		return KEY_DSA_CERT_V00;
+	} else if (strcmp(name, "ssh-rsa-cert-v01@openssh.com") == 0) {
+		return KEY_RSA_CERT;
+	} else if (strcmp(name, "ssh-dss-cert-v01@openssh.com") == 0) {
 		return KEY_DSA_CERT;
 	}
 	debug2("key_type_from_name: unknown key type '%s'", name);
@@ -1012,26 +1049,31 @@
 static int
 cert_parse(Buffer *b, Key *key, const u_char *blob, u_int blen)
 {
-	u_char *principals, *constraints, *sig_key, *sig;
-	u_int signed_len, plen, clen, sklen, slen, kidlen;
+	u_char *principals, *critical, *exts, *sig_key, *sig;
+	u_int signed_len, plen, clen, sklen, slen, kidlen, elen;
 	Buffer tmp;
 	char *principal;
 	int ret = -1;
+	int v00 = key->type == KEY_DSA_CERT_V00 ||
+	    key->type == KEY_RSA_CERT_V00;
 
 	buffer_init(&tmp);
 
 	/* Copy the entire key blob for verification and later serialisation */
 	buffer_append(&key->cert->certblob, blob, blen);
 
-	principals = constraints = sig_key = sig = NULL;
-	if (buffer_get_int_ret(&key->cert->type, b) != 0 ||
+	elen = 0; /* Not touched for v00 certs */
+	principals = exts = critical = sig_key = sig = NULL;
+	if ((!v00 && buffer_get_int64_ret(&key->cert->serial, b) != 0) ||
+	    buffer_get_int_ret(&key->cert->type, b) != 0 ||
 	    (key->cert->key_id = buffer_get_string_ret(b, &kidlen)) == NULL ||
 	    (principals = buffer_get_string_ret(b, &plen)) == NULL ||
 	    buffer_get_int64_ret(&key->cert->valid_after, b) != 0 ||
 	    buffer_get_int64_ret(&key->cert->valid_before, b) != 0 ||
-	    (constraints = buffer_get_string_ret(b, &clen)) == NULL ||
-	    /* skip nonce */ buffer_get_string_ptr_ret(b, NULL) == NULL ||
-	    /* skip reserved */ buffer_get_string_ptr_ret(b, NULL) == NULL ||
+	    (critical = buffer_get_string_ret(b, &clen)) == NULL ||
+	    (!v00 && (exts = buffer_get_string_ret(b, &elen)) == NULL) ||
+	    (v00 && buffer_get_string_ptr_ret(b, NULL) == NULL) || /* nonce */
+	    buffer_get_string_ptr_ret(b, NULL) == NULL || /* reserved */
 	    (sig_key = buffer_get_string_ret(b, &sklen)) == NULL) {
 		error("%s: parse error", __func__);
 		goto out;
@@ -1078,13 +1120,25 @@
 
 	buffer_clear(&tmp);
 
-	buffer_append(&key->cert->constraints, constraints, clen);
-	buffer_append(&tmp, constraints, clen);
+	buffer_append(&key->cert->critical, critical, clen);
+	buffer_append(&tmp, critical, clen);
 	/* validate structure */
 	while (buffer_len(&tmp) != 0) {
 		if (buffer_get_string_ptr_ret(&tmp, NULL) == NULL ||
 		    buffer_get_string_ptr_ret(&tmp, NULL) == NULL) {
-			error("%s: Constraints data invalid", __func__);
+			error("%s: critical option data invalid", __func__);
+			goto out;
+		}
+	}
+	buffer_clear(&tmp);
+
+	buffer_append(&key->cert->extensions, exts, elen);
+	buffer_append(&tmp, exts, elen);
+	/* validate structure */
+	while (buffer_len(&tmp) != 0) {
+		if (buffer_get_string_ptr_ret(&tmp, NULL) == NULL ||
+		    buffer_get_string_ptr_ret(&tmp, NULL) == NULL) {
+			error("%s: extension data invalid", __func__);
 			goto out;
 		}
 	}
@@ -1121,8 +1175,10 @@
 	buffer_free(&tmp);
 	if (principals != NULL)
 		xfree(principals);
-	if (constraints != NULL)
-		xfree(constraints);
+	if (critical != NULL)
+		xfree(critical);
+	if (exts != NULL)
+		xfree(exts);
 	if (sig_key != NULL)
 		xfree(sig_key);
 	if (sig != NULL)
@@ -1151,8 +1207,11 @@
 	type = key_type_from_name(ktype);
 
 	switch (type) {
-	case KEY_RSA:
 	case KEY_RSA_CERT:
+		(void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */
+		/* FALLTHROUGH */
+	case KEY_RSA:
+	case KEY_RSA_CERT_V00:
 		key = key_new(type);
 		if (buffer_get_bignum2_ret(&b, key->rsa->e) == -1 ||
 		    buffer_get_bignum2_ret(&b, key->rsa->n) == -1) {
@@ -1166,8 +1225,11 @@
 		RSA_print_fp(stderr, key->rsa, 8);
 #endif
 		break;
-	case KEY_DSA:
 	case KEY_DSA_CERT:
+		(void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */
+		/* FALLTHROUGH */
+	case KEY_DSA:
+	case KEY_DSA_CERT_V00:
 		key = key_new(type);
 		if (buffer_get_bignum2_ret(&b, key->dsa->p) == -1 ||
 		    buffer_get_bignum2_ret(&b, key->dsa->q) == -1 ||
@@ -1213,6 +1275,8 @@
 	}
 	buffer_init(&b);
 	switch (key->type) {
+	case KEY_DSA_CERT_V00:
+	case KEY_RSA_CERT_V00:
 	case KEY_DSA_CERT:
 	case KEY_RSA_CERT:
 		/* Use the existing blob */
@@ -1255,9 +1319,11 @@
     const u_char *data, u_int datalen)
 {
 	switch (key->type) {
+	case KEY_DSA_CERT_V00:
 	case KEY_DSA_CERT:
 	case KEY_DSA:
 		return ssh_dss_sign(key, sigp, lenp, data, datalen);
+	case KEY_RSA_CERT_V00:
 	case KEY_RSA_CERT:
 	case KEY_RSA:
 		return ssh_rsa_sign(key, sigp, lenp, data, datalen);
@@ -1281,9 +1347,11 @@
 		return -1;
 
 	switch (key->type) {
+	case KEY_DSA_CERT_V00:
 	case KEY_DSA_CERT:
 	case KEY_DSA:
 		return ssh_dss_verify(key, signature, signaturelen, data, datalen);
+	case KEY_RSA_CERT_V00:
 	case KEY_RSA_CERT:
 	case KEY_RSA:
 		return ssh_rsa_verify(key, signature, signaturelen, data, datalen);
@@ -1306,6 +1374,7 @@
 	pk->rsa = NULL;
 
 	switch (k->type) {
+	case KEY_RSA_CERT_V00:
 	case KEY_RSA_CERT:
 		key_cert_copy(k, pk);
 		/* FALLTHROUGH */
@@ -1318,6 +1387,7 @@
 		if ((pk->rsa->n = BN_dup(k->rsa->n)) == NULL)
 			fatal("key_demote: BN_dup failed");
 		break;
+	case KEY_DSA_CERT_V00:
 	case KEY_DSA_CERT:
 		key_cert_copy(k, pk);
 		/* FALLTHROUGH */
@@ -1344,8 +1414,17 @@
 int
 key_is_cert(const Key *k)
 {
-	return k != NULL &&
-	    (k->type == KEY_RSA_CERT || k->type == KEY_DSA_CERT);
+	if (k == NULL)
+		return 0;
+	switch (k->type) {
+	case KEY_RSA_CERT_V00:
+	case KEY_DSA_CERT_V00:
+	case KEY_RSA_CERT:
+	case KEY_DSA_CERT:
+		return 1;
+	default:
+		return 0;
+	}
 }
 
 /* Return the cert-less equivalent to a certified key type */
@@ -1353,8 +1432,10 @@
 key_type_plain(int type)
 {
 	switch (type) {
+	case KEY_RSA_CERT_V00:
 	case KEY_RSA_CERT:
 		return KEY_RSA;
+	case KEY_DSA_CERT_V00:
 	case KEY_DSA_CERT:
 		return KEY_DSA;
 	default:
@@ -1364,16 +1445,16 @@
 
 /* Convert a KEY_RSA or KEY_DSA to their _CERT equivalent */
 int
-key_to_certified(Key *k)
+key_to_certified(Key *k, int legacy)
 {
 	switch (k->type) {
 	case KEY_RSA:
 		k->cert = cert_new();
-		k->type = KEY_RSA_CERT;
+		k->type = legacy ? KEY_RSA_CERT_V00 : KEY_RSA_CERT;
 		return 0;
 	case KEY_DSA:
 		k->cert = cert_new();
-		k->type = KEY_DSA_CERT;
+		k->type = legacy ? KEY_DSA_CERT_V00 : KEY_DSA_CERT;
 		return 0;
 	default:
 		error("%s: key has incorrect type %s", __func__, key_type(k));
@@ -1386,10 +1467,12 @@
 key_drop_cert(Key *k)
 {
 	switch (k->type) {
+	case KEY_RSA_CERT_V00:
 	case KEY_RSA_CERT:
 		cert_free(k->cert);
 		k->type = KEY_RSA;
 		return 0;
+	case KEY_DSA_CERT_V00:
 	case KEY_DSA_CERT:
 		cert_free(k->cert);
 		k->type = KEY_DSA;
@@ -1430,13 +1513,21 @@
 	buffer_clear(&k->cert->certblob);
 	buffer_put_cstring(&k->cert->certblob, key_ssh_name(k));
 
+	/* -v01 certs put nonce first */
+	if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT) {
+		arc4random_buf(&nonce, sizeof(nonce));
+		buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce));
+	}
+
 	switch (k->type) {
+	case KEY_DSA_CERT_V00:
 	case KEY_DSA_CERT:
 		buffer_put_bignum2(&k->cert->certblob, k->dsa->p);
 		buffer_put_bignum2(&k->cert->certblob, k->dsa->q);
 		buffer_put_bignum2(&k->cert->certblob, k->dsa->g);
 		buffer_put_bignum2(&k->cert->certblob, k->dsa->pub_key);
 		break;
+	case KEY_RSA_CERT_V00:
 	case KEY_RSA_CERT:
 		buffer_put_bignum2(&k->cert->certblob, k->rsa->e);
 		buffer_put_bignum2(&k->cert->certblob, k->rsa->n);
@@ -1448,6 +1539,10 @@
 		return -1;
 	}
 
+	/* -v01 certs have a serial number next */
+	if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT)
+		buffer_put_int64(&k->cert->certblob, k->cert->serial);
+
 	buffer_put_int(&k->cert->certblob, k->cert->type);
 	buffer_put_cstring(&k->cert->certblob, k->cert->key_id);
 
@@ -1461,11 +1556,19 @@
 	buffer_put_int64(&k->cert->certblob, k->cert->valid_after);
 	buffer_put_int64(&k->cert->certblob, k->cert->valid_before);
 	buffer_put_string(&k->cert->certblob,
-	    buffer_ptr(&k->cert->constraints),
-	    buffer_len(&k->cert->constraints));
+	    buffer_ptr(&k->cert->critical), buffer_len(&k->cert->critical));
 
-	arc4random_buf(&nonce, sizeof(nonce));
-	buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce));
+	/* -v01 certs have non-critical options here */
+	if (k->type == KEY_DSA_CERT || k->type == KEY_RSA_CERT) {
+		buffer_put_string(&k->cert->certblob,
+		    buffer_ptr(&k->cert->extensions),
+		    buffer_len(&k->cert->extensions));
+	}
+
+	/* -v00 certs put the nonce at the end */
+	if (k->type == KEY_DSA_CERT_V00 || k->type == KEY_RSA_CERT_V00)
+		buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce));
+
 	buffer_put_string(&k->cert->certblob, NULL, 0); /* reserved */
 	buffer_put_string(&k->cert->certblob, ca_blob, ca_len);
 	xfree(ca_blob);
@@ -1536,3 +1639,15 @@
 	}
 	return 0;
 }
+
+int
+key_cert_is_legacy(Key *k)
+{
+	switch (k->type) {
+	case KEY_DSA_CERT_V00:
+	case KEY_RSA_CERT_V00:
+		return 1;
+	default:
+		return 0;
+	}
+}