upstream commit

implement SHA2-{256,512} for RSASSA-PKCS1-v1_5 signatures
 (user and host auth) based on draft-rsa-dsa-sha2-256-03.txt and
 draft-ssh-ext-info-04.txt; with & ok djm@

Upstream-ID: cf82ce532b2733e5c4b34bb7b7c94835632db309
diff --git a/ssh-rsa.c b/ssh-rsa.c
index 08090d1..81dab05 100644
--- a/ssh-rsa.c
+++ b/ssh-rsa.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-rsa.c,v 1.54 2015/09/09 00:52:44 djm Exp $ */
+/* $OpenBSD: ssh-rsa.c,v 1.55 2015/12/04 16:41:28 markus Exp $ */
 /*
  * Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org>
  *
@@ -36,16 +36,60 @@
 
 static int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *);
 
+static const char *
+rsa_hash_alg_ident(int hash_alg)
+{
+	switch (hash_alg) {
+	case SSH_DIGEST_SHA1:
+		return "ssh-rsa";
+	case SSH_DIGEST_SHA256:
+		return "rsa-sha2-256";
+	case SSH_DIGEST_SHA512:
+		return "rsa-sha2-512";
+	}
+	return NULL;
+}
+
+static int
+rsa_hash_alg_from_ident(const char *ident)
+{
+	if (ident == NULL || strlen(ident) == 0)
+		return SSH_DIGEST_SHA1;
+	if (strcmp(ident, "ssh-rsa") == 0)
+		return SSH_DIGEST_SHA1;
+	if (strcmp(ident, "rsa-sha2-256") == 0)
+		return SSH_DIGEST_SHA256;
+	if (strcmp(ident, "rsa-sha2-512") == 0)
+		return SSH_DIGEST_SHA512;
+	if (strncmp(ident, "ssh-rsa-cert", strlen("ssh-rsa-cert")) == 0)
+		return SSH_DIGEST_SHA1;
+	return -1;
+}
+
+static int
+rsa_hash_alg_nid(int type)
+{
+	switch (type) {
+	case SSH_DIGEST_SHA1:
+		return NID_sha1;
+	case SSH_DIGEST_SHA256:
+		return NID_sha256;
+	case SSH_DIGEST_SHA512:
+		return NID_sha512;
+	default:
+		return -1;
+	}
+}
+
 /* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */
 int
 ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
-    const u_char *data, size_t datalen, u_int compat)
+    const u_char *data, size_t datalen, const char *alg_ident)
 {
-	int hash_alg;
 	u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL;
 	size_t slen;
 	u_int dlen, len;
-	int nid, ret = SSH_ERR_INTERNAL_ERROR;
+	int nid, hash_alg, ret = SSH_ERR_INTERNAL_ERROR;
 	struct sshbuf *b = NULL;
 
 	if (lenp != NULL)
@@ -53,16 +97,17 @@
 	if (sigp != NULL)
 		*sigp = NULL;
 
-	if (key == NULL || key->rsa == NULL ||
-	    sshkey_type_plain(key->type) != KEY_RSA)
+	hash_alg = rsa_hash_alg_from_ident(alg_ident);
+	if (key == NULL || key->rsa == NULL || hash_alg == -1 ||
+	    sshkey_type_plain(key->type) != KEY_RSA ||
+	    BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE)
 		return SSH_ERR_INVALID_ARGUMENT;
 	slen = RSA_size(key->rsa);
 	if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM)
 		return SSH_ERR_INVALID_ARGUMENT;
 
 	/* hash the data */
-	hash_alg = SSH_DIGEST_SHA1;
-	nid = NID_sha1;
+	nid = rsa_hash_alg_nid(hash_alg);
 	if ((dlen = ssh_digest_bytes(hash_alg)) == 0)
 		return SSH_ERR_INTERNAL_ERROR;
 	if ((ret = ssh_digest_memory(hash_alg, data, datalen,
@@ -91,7 +136,7 @@
 		ret = SSH_ERR_ALLOC_FAIL;
 		goto out;
 	}
-	if ((ret = sshbuf_put_cstring(b, "ssh-rsa")) != 0 ||
+	if ((ret = sshbuf_put_cstring(b, rsa_hash_alg_ident(hash_alg))) != 0 ||
 	    (ret = sshbuf_put_string(b, sig, slen)) != 0)
 		goto out;
 	len = sshbuf_len(b);
@@ -118,8 +163,7 @@
 
 int
 ssh_rsa_verify(const struct sshkey *key,
-    const u_char *signature, size_t signaturelen,
-    const u_char *data, size_t datalen, u_int compat)
+    const u_char *sig, size_t siglen, const u_char *data, size_t datalen)
 {
 	char *ktype = NULL;
 	int hash_alg, ret = SSH_ERR_INTERNAL_ERROR;
@@ -132,13 +176,13 @@
 	    BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE)
 		return SSH_ERR_INVALID_ARGUMENT;
 
-	if ((b = sshbuf_from(signature, signaturelen)) == NULL)
+	if ((b = sshbuf_from(sig, siglen)) == NULL)
 		return SSH_ERR_ALLOC_FAIL;
 	if (sshbuf_get_cstring(b, &ktype, NULL) != 0) {
 		ret = SSH_ERR_INVALID_FORMAT;
 		goto out;
 	}
-	if (strcmp("ssh-rsa", ktype) != 0) {
+	if ((hash_alg = rsa_hash_alg_from_ident(ktype)) == -1) {
 		ret = SSH_ERR_KEY_TYPE_MISMATCH;
 		goto out;
 	}
@@ -167,7 +211,6 @@
 		explicit_bzero(sigblob, diff);
 		len = modlen;
 	}
-	hash_alg = SSH_DIGEST_SHA1;
 	if ((dlen = ssh_digest_bytes(hash_alg)) == 0) {
 		ret = SSH_ERR_INTERNAL_ERROR;
 		goto out;
@@ -196,6 +239,7 @@
  * http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/
  * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn
  */
+
 /*
  * id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
  *	oiw(14) secsig(3) algorithms(2) 26 }
@@ -209,6 +253,58 @@
 	0x04, 0x14  /* Octet string, length 0x14 (20), followed by sha1 hash */
 };
 
+/*
+ * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html
+ * id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840)
+ *      organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2)
+ *      id-sha256(1) }
+ */
+static const u_char id_sha256[] = {
+	0x30, 0x31, /* type Sequence, length 0x31 (49) */
+	0x30, 0x0d, /* type Sequence, length 0x0d (13) */
+	0x06, 0x09, /* type OID, length 0x09 */
+	0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, /* id-sha256 */
+	0x05, 0x00, /* NULL */
+	0x04, 0x20  /* Octet string, length 0x20 (32), followed by sha256 hash */
+};
+
+/*
+ * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html
+ * id-sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840)
+ *      organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2)
+ *      id-sha256(3) }
+ */
+static const u_char id_sha512[] = {
+	0x30, 0x51, /* type Sequence, length 0x51 (81) */
+	0x30, 0x0d, /* type Sequence, length 0x0d (13) */
+	0x06, 0x09, /* type OID, length 0x09 */
+	0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, /* id-sha512 */
+	0x05, 0x00, /* NULL */
+	0x04, 0x40  /* Octet string, length 0x40 (64), followed by sha512 hash */
+};
+
+static int
+rsa_hash_alg_oid(int hash_alg, const u_char **oidp, size_t *oidlenp)
+{
+	switch (hash_alg) {
+	case SSH_DIGEST_SHA1:
+		*oidp = id_sha1;
+		*oidlenp = sizeof(id_sha1);
+		break;
+	case SSH_DIGEST_SHA256:
+		*oidp = id_sha256;
+		*oidlenp = sizeof(id_sha256);
+		break;
+	case SSH_DIGEST_SHA512:
+		*oidp = id_sha512;
+		*oidlenp = sizeof(id_sha512);
+		break;
+	default:
+		return SSH_ERR_INVALID_ARGUMENT;
+	}
+	return 0;
+}
+
 static int
 openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen,
     u_char *sigbuf, size_t siglen, RSA *rsa)
@@ -218,16 +314,10 @@
 	const u_char *oid = NULL;
 	u_char *decrypted = NULL;
 
+	if ((ret = rsa_hash_alg_oid(hash_alg, &oid, &oidlen)) != 0)
+		return ret;
 	ret = SSH_ERR_INTERNAL_ERROR;
-	switch (hash_alg) {
-	case SSH_DIGEST_SHA1:
-		oid = id_sha1;
-		oidlen = sizeof(id_sha1);
-		hlen = 20;
-		break;
-	default:
-		goto done;
-	}
+	hlen = ssh_digest_bytes(hash_alg);
 	if (hashlen != hlen) {
 		ret = SSH_ERR_INVALID_ARGUMENT;
 		goto done;