summaryrefslogtreecommitdiff
path: root/sshkey.c
diff options
context:
space:
mode:
Diffstat (limited to 'sshkey.c')
-rw-r--r--sshkey.c741
1 files changed, 571 insertions, 170 deletions
diff --git a/sshkey.c b/sshkey.c
index e91c54f53bbd..7712fba2363b 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshkey.c,v 1.56 2017/08/12 06:42:52 djm Exp $ */
+/* $OpenBSD: sshkey.c,v 1.64 2018/03/22 07:05:48 markus Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Alexander von Gernler. All rights reserved.
@@ -55,8 +55,11 @@
#include "digest.h"
#define SSHKEY_INTERNAL
#include "sshkey.h"
+#include "sshkey-xmss.h"
#include "match.h"
+#include "xmss_fast.h"
+
/* openssh private key file format */
#define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n"
#define MARK_END "-----END OPENSSH PRIVATE KEY-----\n"
@@ -71,6 +74,8 @@
/* Version identification string for SSH v1 identity files. */
#define LEGACY_BEGIN "SSH PRIVATE KEY FILE FORMAT 1.1\n"
+int sshkey_private_serialize_opt(const struct sshkey *key,
+ struct sshbuf *buf, enum sshkey_serialize_rep);
static int sshkey_from_blob_internal(struct sshbuf *buf,
struct sshkey **keyp, int allow_cert);
@@ -87,6 +92,11 @@ static const struct keytype keytypes[] = {
{ "ssh-ed25519", "ED25519", KEY_ED25519, 0, 0, 0 },
{ "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT",
KEY_ED25519_CERT, 0, 1, 0 },
+#ifdef WITH_XMSS
+ { "ssh-xmss@openssh.com", "XMSS", KEY_XMSS, 0, 0, 0 },
+ { "ssh-xmss-cert-v01@openssh.com", "XMSS-CERT",
+ KEY_XMSS_CERT, 0, 1, 0 },
+#endif /* WITH_XMSS */
#ifdef WITH_OPENSSL
{ "ssh-rsa", "RSA", KEY_RSA, 0, 0, 0 },
{ "rsa-sha2-256", "RSA", KEY_RSA, 0, 0, 1 },
@@ -274,6 +284,8 @@ sshkey_size(const struct sshkey *k)
#endif /* WITH_OPENSSL */
case KEY_ED25519:
case KEY_ED25519_CERT:
+ case KEY_XMSS:
+ case KEY_XMSS_CERT:
return 256; /* XXX */
}
return 0;
@@ -287,6 +299,7 @@ sshkey_type_is_valid_ca(int type)
case KEY_DSA:
case KEY_ECDSA:
case KEY_ED25519:
+ case KEY_XMSS:
return 1;
default:
return 0;
@@ -314,6 +327,8 @@ sshkey_type_plain(int type)
return KEY_ECDSA;
case KEY_ED25519_CERT:
return KEY_ED25519;
+ case KEY_XMSS_CERT:
+ return KEY_XMSS;
default:
return type;
}
@@ -420,8 +435,7 @@ cert_free(struct sshkey_cert *cert)
free(cert->principals[i]);
free(cert->principals);
sshkey_free(cert->signature_key);
- explicit_bzero(cert, sizeof(*cert));
- free(cert);
+ freezero(cert, sizeof(*cert));
}
static struct sshkey_cert *
@@ -462,6 +476,8 @@ sshkey_new(int type)
k->cert = NULL;
k->ed25519_sk = NULL;
k->ed25519_pk = NULL;
+ k->xmss_sk = NULL;
+ k->xmss_pk = NULL;
switch (k->type) {
#ifdef WITH_OPENSSL
case KEY_RSA:
@@ -469,8 +485,7 @@ sshkey_new(int type)
if ((rsa = RSA_new()) == NULL ||
(rsa->n = BN_new()) == NULL ||
(rsa->e = BN_new()) == NULL) {
- if (rsa != NULL)
- RSA_free(rsa);
+ RSA_free(rsa);
free(k);
return NULL;
}
@@ -483,8 +498,7 @@ sshkey_new(int type)
(dsa->q = BN_new()) == NULL ||
(dsa->g = BN_new()) == NULL ||
(dsa->pub_key = BN_new()) == NULL) {
- if (dsa != NULL)
- DSA_free(dsa);
+ DSA_free(dsa);
free(k);
return NULL;
}
@@ -497,6 +511,8 @@ sshkey_new(int type)
#endif /* WITH_OPENSSL */
case KEY_ED25519:
case KEY_ED25519_CERT:
+ case KEY_XMSS:
+ case KEY_XMSS_CERT:
/* no need to prealloc */
break;
case KEY_UNSPEC:
@@ -545,6 +561,8 @@ sshkey_add_private(struct sshkey *k)
#endif /* WITH_OPENSSL */
case KEY_ED25519:
case KEY_ED25519_CERT:
+ case KEY_XMSS:
+ case KEY_XMSS_CERT:
/* no need to prealloc */
break;
case KEY_UNSPEC:
@@ -578,38 +596,43 @@ sshkey_free(struct sshkey *k)
#ifdef WITH_OPENSSL
case KEY_RSA:
case KEY_RSA_CERT:
- if (k->rsa != NULL)
- RSA_free(k->rsa);
+ RSA_free(k->rsa);
k->rsa = NULL;
break;
case KEY_DSA:
case KEY_DSA_CERT:
- if (k->dsa != NULL)
- DSA_free(k->dsa);
+ DSA_free(k->dsa);
k->dsa = NULL;
break;
# ifdef OPENSSL_HAS_ECC
case KEY_ECDSA:
case KEY_ECDSA_CERT:
- if (k->ecdsa != NULL)
- EC_KEY_free(k->ecdsa);
+ EC_KEY_free(k->ecdsa);
k->ecdsa = NULL;
break;
# endif /* OPENSSL_HAS_ECC */
#endif /* WITH_OPENSSL */
case KEY_ED25519:
case KEY_ED25519_CERT:
- if (k->ed25519_pk) {
- explicit_bzero(k->ed25519_pk, ED25519_PK_SZ);
- free(k->ed25519_pk);
- k->ed25519_pk = NULL;
- }
- if (k->ed25519_sk) {
- explicit_bzero(k->ed25519_sk, ED25519_SK_SZ);
- free(k->ed25519_sk);
- k->ed25519_sk = NULL;
- }
+ freezero(k->ed25519_pk, ED25519_PK_SZ);
+ k->ed25519_pk = NULL;
+ freezero(k->ed25519_sk, ED25519_SK_SZ);
+ k->ed25519_sk = NULL;
+ break;
+#ifdef WITH_XMSS
+ case KEY_XMSS:
+ case KEY_XMSS_CERT:
+ freezero(k->xmss_pk, sshkey_xmss_pklen(k));
+ k->xmss_pk = NULL;
+ freezero(k->xmss_sk, sshkey_xmss_sklen(k));
+ k->xmss_sk = NULL;
+ sshkey_xmss_free_state(k);
+ free(k->xmss_name);
+ k->xmss_name = NULL;
+ free(k->xmss_filename);
+ k->xmss_filename = NULL;
break;
+#endif /* WITH_XMSS */
case KEY_UNSPEC:
break;
default:
@@ -617,8 +640,7 @@ sshkey_free(struct sshkey *k)
}
if (sshkey_is_cert(k))
cert_free(k->cert);
- explicit_bzero(k, sizeof(*k));
- free(k);
+ freezero(k, sizeof(*k));
}
static int
@@ -690,6 +712,13 @@ sshkey_equal_public(const struct sshkey *a, const struct sshkey *b)
case KEY_ED25519_CERT:
return a->ed25519_pk != NULL && b->ed25519_pk != NULL &&
memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) == 0;
+#ifdef WITH_XMSS
+ case KEY_XMSS:
+ case KEY_XMSS_CERT:
+ return a->xmss_pk != NULL && b->xmss_pk != NULL &&
+ sshkey_xmss_pklen(a) == sshkey_xmss_pklen(b) &&
+ memcmp(a->xmss_pk, b->xmss_pk, sshkey_xmss_pklen(a)) == 0;
+#endif /* WITH_XMSS */
default:
return 0;
}
@@ -709,7 +738,8 @@ sshkey_equal(const struct sshkey *a, const struct sshkey *b)
}
static int
-to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain)
+to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain,
+ enum sshkey_serialize_rep opts)
{
int type, ret = SSH_ERR_INTERNAL_ERROR;
const char *typename;
@@ -733,6 +763,9 @@ to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain)
case KEY_RSA_CERT:
#endif /* WITH_OPENSSL */
case KEY_ED25519_CERT:
+#ifdef WITH_XMSS
+ case KEY_XMSS_CERT:
+#endif /* WITH_XMSS */
/* Use the existing blob */
/* XXX modified flag? */
if ((ret = sshbuf_putb(b, key->cert->certblob)) != 0)
@@ -777,6 +810,19 @@ to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain)
key->ed25519_pk, ED25519_PK_SZ)) != 0)
return ret;
break;
+#ifdef WITH_XMSS
+ case KEY_XMSS:
+ if (key->xmss_name == NULL || key->xmss_pk == NULL ||
+ sshkey_xmss_pklen(key) == 0)
+ return SSH_ERR_INVALID_ARGUMENT;
+ if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
+ (ret = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
+ (ret = sshbuf_put_string(b,
+ key->xmss_pk, sshkey_xmss_pklen(key))) != 0 ||
+ (ret = sshkey_xmss_serialize_pk_info(key, b, opts)) != 0)
+ return ret;
+ break;
+#endif /* WITH_XMSS */
default:
return SSH_ERR_KEY_TYPE_UNKNOWN;
}
@@ -786,18 +832,19 @@ to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain)
int
sshkey_putb(const struct sshkey *key, struct sshbuf *b)
{
- return to_blob_buf(key, b, 0);
+ return to_blob_buf(key, b, 0, SSHKEY_SERIALIZE_DEFAULT);
}
int
-sshkey_puts(const struct sshkey *key, struct sshbuf *b)
+sshkey_puts_opts(const struct sshkey *key, struct sshbuf *b,
+ enum sshkey_serialize_rep opts)
{
struct sshbuf *tmp;
int r;
if ((tmp = sshbuf_new()) == NULL)
return SSH_ERR_ALLOC_FAIL;
- r = to_blob_buf(key, tmp, 0);
+ r = to_blob_buf(key, tmp, 0, opts);
if (r == 0)
r = sshbuf_put_stringb(b, tmp);
sshbuf_free(tmp);
@@ -805,13 +852,20 @@ sshkey_puts(const struct sshkey *key, struct sshbuf *b)
}
int
+sshkey_puts(const struct sshkey *key, struct sshbuf *b)
+{
+ return sshkey_puts_opts(key, b, SSHKEY_SERIALIZE_DEFAULT);
+}
+
+int
sshkey_putb_plain(const struct sshkey *key, struct sshbuf *b)
{
- return to_blob_buf(key, b, 1);
+ return to_blob_buf(key, b, 1, SSHKEY_SERIALIZE_DEFAULT);
}
static int
-to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain)
+to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain,
+ enum sshkey_serialize_rep opts)
{
int ret = SSH_ERR_INTERNAL_ERROR;
size_t len;
@@ -823,7 +877,7 @@ to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain)
*blobp = NULL;
if ((b = sshbuf_new()) == NULL)
return SSH_ERR_ALLOC_FAIL;
- if ((ret = to_blob_buf(key, b, force_plain)) != 0)
+ if ((ret = to_blob_buf(key, b, force_plain, opts)) != 0)
goto out;
len = sshbuf_len(b);
if (lenp != NULL)
@@ -844,13 +898,13 @@ to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain)
int
sshkey_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp)
{
- return to_blob(key, blobp, lenp, 0);
+ return to_blob(key, blobp, lenp, 0, SSHKEY_SERIALIZE_DEFAULT);
}
int
sshkey_plain_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp)
{
- return to_blob(key, blobp, lenp, 1);
+ return to_blob(key, blobp, lenp, 1, SSHKEY_SERIALIZE_DEFAULT);
}
int
@@ -869,7 +923,8 @@ sshkey_fingerprint_raw(const struct sshkey *k, int dgst_alg,
r = SSH_ERR_INVALID_ARGUMENT;
goto out;
}
- if ((r = to_blob(k, &blob, &blob_len, 1)) != 0)
+ if ((r = to_blob(k, &blob, &blob_len, 1, SSHKEY_SERIALIZE_DEFAULT))
+ != 0)
goto out;
if ((ret = calloc(1, SSH_DIGEST_MAX_LENGTH)) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
@@ -911,8 +966,7 @@ fingerprint_b64(const char *alg, u_char *dgst_raw, size_t dgst_raw_len)
return ret;
if ((r = b64_ntop(dgst_raw, dgst_raw_len,
ret + plen, rlen - plen)) == -1) {
- explicit_bzero(ret, rlen);
- free(ret);
+ freezero(ret, rlen);
return NULL;
}
/* Trim padding characters from end */
@@ -1161,22 +1215,37 @@ sshkey_fingerprint(const struct sshkey *k, int dgst_alg,
return retval;
}
+static int
+peek_type_nid(const char *s, size_t l, int *nid)
+{
+ const struct keytype *kt;
+
+ for (kt = keytypes; kt->type != -1; kt++) {
+ if (kt->name == NULL || strlen(kt->name) != l)
+ continue;
+ if (memcmp(s, kt->name, l) == 0) {
+ *nid = -1;
+ if (kt->type == KEY_ECDSA || kt->type == KEY_ECDSA_CERT)
+ *nid = kt->nid;
+ return kt->type;
+ }
+ }
+ return KEY_UNSPEC;
+}
-/* returns 0 ok, and < 0 error */
+/* XXX this can now be made const char * */
int
sshkey_read(struct sshkey *ret, char **cpp)
{
struct sshkey *k;
- int retval = SSH_ERR_INVALID_FORMAT;
- char *ep, *cp, *space;
+ char *cp, *blobcopy;
+ size_t space;
int r, type, curve_nid = -1;
struct sshbuf *blob;
if (ret == NULL)
return SSH_ERR_INVALID_ARGUMENT;
- cp = *cpp;
-
switch (ret->type) {
case KEY_UNSPEC:
case KEY_RSA:
@@ -1187,120 +1256,147 @@ sshkey_read(struct sshkey *ret, char **cpp)
case KEY_ECDSA_CERT:
case KEY_RSA_CERT:
case KEY_ED25519_CERT:
- space = strchr(cp, ' ');
- if (space == NULL)
- return SSH_ERR_INVALID_FORMAT;
- *space = '\0';
- type = sshkey_type_from_name(cp);
- if (sshkey_type_plain(type) == KEY_ECDSA &&
- (curve_nid = sshkey_ecdsa_nid_from_name(cp)) == -1)
- return SSH_ERR_EC_CURVE_INVALID;
- *space = ' ';
- if (type == KEY_UNSPEC)
- return SSH_ERR_INVALID_FORMAT;
- cp = space+1;
- if (*cp == '\0')
- return SSH_ERR_INVALID_FORMAT;
- if (ret->type != KEY_UNSPEC && ret->type != type)
- return SSH_ERR_KEY_TYPE_MISMATCH;
- if ((blob = sshbuf_new()) == NULL)
- return SSH_ERR_ALLOC_FAIL;
- /* trim comment */
- space = strchr(cp, ' ');
- if (space) {
- /* advance 'space': skip whitespace */
- *space++ = '\0';
- while (*space == ' ' || *space == '\t')
- space++;
- ep = space;
- } else
- ep = cp + strlen(cp);
- if ((r = sshbuf_b64tod(blob, cp)) != 0) {
- sshbuf_free(blob);
- return r;
- }
- if ((r = sshkey_from_blob(sshbuf_ptr(blob),
- sshbuf_len(blob), &k)) != 0) {
- sshbuf_free(blob);
- return r;
- }
+#ifdef WITH_XMSS
+ case KEY_XMSS:
+ case KEY_XMSS_CERT:
+#endif /* WITH_XMSS */
+ break; /* ok */
+ default:
+ return SSH_ERR_INVALID_ARGUMENT;
+ }
+
+ /* Decode type */
+ cp = *cpp;
+ space = strcspn(cp, " \t");
+ if (space == strlen(cp))
+ return SSH_ERR_INVALID_FORMAT;
+ if ((type = peek_type_nid(cp, space, &curve_nid)) == KEY_UNSPEC)
+ return SSH_ERR_INVALID_FORMAT;
+
+ /* skip whitespace */
+ for (cp += space; *cp == ' ' || *cp == '\t'; cp++)
+ ;
+ if (*cp == '\0')
+ return SSH_ERR_INVALID_FORMAT;
+ if (ret->type != KEY_UNSPEC && ret->type != type)
+ return SSH_ERR_KEY_TYPE_MISMATCH;
+ if ((blob = sshbuf_new()) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+
+ /* find end of keyblob and decode */
+ space = strcspn(cp, " \t");
+ if ((blobcopy = strndup(cp, space)) == NULL) {
sshbuf_free(blob);
- if (k->type != type) {
- sshkey_free(k);
- return SSH_ERR_KEY_TYPE_MISMATCH;
- }
- if (sshkey_type_plain(type) == KEY_ECDSA &&
- curve_nid != k->ecdsa_nid) {
+ return SSH_ERR_ALLOC_FAIL;
+ }
+ if ((r = sshbuf_b64tod(blob, blobcopy)) != 0) {
+ free(blobcopy);
+ sshbuf_free(blob);
+ return r;
+ }
+ free(blobcopy);
+ if ((r = sshkey_fromb(blob, &k)) != 0) {
+ sshbuf_free(blob);
+ return r;
+ }
+ sshbuf_free(blob);
+
+ /* skip whitespace and leave cp at start of comment */
+ for (cp += space; *cp == ' ' || *cp == '\t'; cp++)
+ ;
+
+ /* ensure type of blob matches type at start of line */
+ if (k->type != type) {
+ sshkey_free(k);
+ return SSH_ERR_KEY_TYPE_MISMATCH;
+ }
+ if (sshkey_type_plain(type) == KEY_ECDSA && curve_nid != k->ecdsa_nid) {
+ sshkey_free(k);
+ return SSH_ERR_EC_CURVE_MISMATCH;
+ }
+
+ /* Fill in ret from parsed key */
+ ret->type = type;
+ if (sshkey_is_cert(ret)) {
+ if (!sshkey_is_cert(k)) {
sshkey_free(k);
- return SSH_ERR_EC_CURVE_MISMATCH;
- }
- ret->type = type;
- if (sshkey_is_cert(ret)) {
- if (!sshkey_is_cert(k)) {
- sshkey_free(k);
- return SSH_ERR_EXPECTED_CERT;
- }
- if (ret->cert != NULL)
- cert_free(ret->cert);
- ret->cert = k->cert;
- k->cert = NULL;
+ return SSH_ERR_EXPECTED_CERT;
}
- switch (sshkey_type_plain(ret->type)) {
+ if (ret->cert != NULL)
+ cert_free(ret->cert);
+ ret->cert = k->cert;
+ k->cert = NULL;
+ }
+ switch (sshkey_type_plain(ret->type)) {
#ifdef WITH_OPENSSL
- case KEY_RSA:
- if (ret->rsa != NULL)
- RSA_free(ret->rsa);
- ret->rsa = k->rsa;
- k->rsa = NULL;
+ case KEY_RSA:
+ RSA_free(ret->rsa);
+ ret->rsa = k->rsa;
+ k->rsa = NULL;
#ifdef DEBUG_PK
- RSA_print_fp(stderr, ret->rsa, 8);
+ RSA_print_fp(stderr, ret->rsa, 8);
#endif
- break;
- case KEY_DSA:
- if (ret->dsa != NULL)
- DSA_free(ret->dsa);
- ret->dsa = k->dsa;
- k->dsa = NULL;
+ break;
+ case KEY_DSA:
+ DSA_free(ret->dsa);
+ ret->dsa = k->dsa;
+ k->dsa = NULL;
#ifdef DEBUG_PK
- DSA_print_fp(stderr, ret->dsa, 8);
+ DSA_print_fp(stderr, ret->dsa, 8);
#endif
- break;
+ break;
# ifdef OPENSSL_HAS_ECC
- case KEY_ECDSA:
- if (ret->ecdsa != NULL)
- EC_KEY_free(ret->ecdsa);
- ret->ecdsa = k->ecdsa;
- ret->ecdsa_nid = k->ecdsa_nid;
- k->ecdsa = NULL;
- k->ecdsa_nid = -1;
+ case KEY_ECDSA:
+ EC_KEY_free(ret->ecdsa);
+ ret->ecdsa = k->ecdsa;
+ ret->ecdsa_nid = k->ecdsa_nid;
+ k->ecdsa = NULL;
+ k->ecdsa_nid = -1;
#ifdef DEBUG_PK
- sshkey_dump_ec_key(ret->ecdsa);
+ sshkey_dump_ec_key(ret->ecdsa);
#endif
- break;
+ break;
# endif /* OPENSSL_HAS_ECC */
#endif /* WITH_OPENSSL */
- case KEY_ED25519:
- free(ret->ed25519_pk);
- ret->ed25519_pk = k->ed25519_pk;
- k->ed25519_pk = NULL;
+ case KEY_ED25519:
+ freezero(ret->ed25519_pk, ED25519_PK_SZ);
+ ret->ed25519_pk = k->ed25519_pk;
+ k->ed25519_pk = NULL;
#ifdef DEBUG_PK
- /* XXX */
+ /* XXX */
#endif
- break;
- }
- *cpp = ep;
- retval = 0;
-/*XXXX*/
- sshkey_free(k);
- if (retval != 0)
- break;
break;
+#ifdef WITH_XMSS
+ case KEY_XMSS:
+ free(ret->xmss_pk);
+ ret->xmss_pk = k->xmss_pk;
+ k->xmss_pk = NULL;
+ free(ret->xmss_state);
+ ret->xmss_state = k->xmss_state;
+ k->xmss_state = NULL;
+ free(ret->xmss_name);
+ ret->xmss_name = k->xmss_name;
+ k->xmss_name = NULL;
+ free(ret->xmss_filename);
+ ret->xmss_filename = k->xmss_filename;
+ k->xmss_filename = NULL;
+#ifdef DEBUG_PK
+ /* XXX */
+#endif
+ break;
+#endif /* WITH_XMSS */
default:
- return SSH_ERR_INVALID_ARGUMENT;
+ sshkey_free(k);
+ return SSH_ERR_INTERNAL_ERROR;
}
- return retval;
+ sshkey_free(k);
+
+ /* success */
+ *cpp = cp;
+ return 0;
}
+
int
sshkey_to_base64(const struct sshkey *key, char **b64p)
{
@@ -1410,10 +1506,8 @@ rsa_generate_private_key(u_int bits, RSA **rsap)
private = NULL;
ret = 0;
out:
- if (private != NULL)
- RSA_free(private);
- if (f4 != NULL)
- BN_free(f4);
+ RSA_free(private);
+ BN_free(f4);
return ret;
}
@@ -1441,8 +1535,7 @@ dsa_generate_private_key(u_int bits, DSA **dsap)
private = NULL;
ret = 0;
out:
- if (private != NULL)
- DSA_free(private);
+ DSA_free(private);
return ret;
}
@@ -1521,8 +1614,7 @@ ecdsa_generate_private_key(u_int bits, int *nid, EC_KEY **ecdsap)
private = NULL;
ret = 0;
out:
- if (private != NULL)
- EC_KEY_free(private);
+ EC_KEY_free(private);
return ret;
}
# endif /* OPENSSL_HAS_ECC */
@@ -1549,6 +1641,11 @@ sshkey_generate(int type, u_int bits, struct sshkey **keyp)
crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk);
ret = 0;
break;
+#ifdef WITH_XMSS
+ case KEY_XMSS:
+ ret = sshkey_xmss_generate_private_key(k, bits);
+ break;
+#endif /* WITH_XMSS */
#ifdef WITH_OPENSSL
case KEY_DSA:
ret = dsa_generate_private_key(bits, &k->dsa);
@@ -1692,6 +1789,29 @@ sshkey_from_private(const struct sshkey *k, struct sshkey **pkp)
memcpy(n->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ);
}
break;
+#ifdef WITH_XMSS
+ case KEY_XMSS:
+ case KEY_XMSS_CERT:
+ if ((n = sshkey_new(k->type)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if ((ret = sshkey_xmss_init(n, k->xmss_name)) != 0) {
+ sshkey_free(n);
+ return ret;
+ }
+ if (k->xmss_pk != NULL) {
+ size_t pklen = sshkey_xmss_pklen(k);
+ if (pklen == 0 || sshkey_xmss_pklen(n) != pklen) {
+ sshkey_free(n);
+ return SSH_ERR_INTERNAL_ERROR;
+ }
+ if ((n->xmss_pk = malloc(pklen)) == NULL) {
+ sshkey_free(n);
+ return SSH_ERR_ALLOC_FAIL;
+ }
+ memcpy(n->xmss_pk, k->xmss_pk, pklen);
+ }
+ break;
+#endif /* WITH_XMSS */
default:
return SSH_ERR_KEY_TYPE_UNKNOWN;
}
@@ -1814,7 +1934,7 @@ cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf)
goto out;
}
if ((ret = sshkey_verify(key->cert->signature_key, sig, slen,
- sshbuf_ptr(key->cert->certblob), signed_len, 0)) != 0)
+ sshbuf_ptr(key->cert->certblob), signed_len, NULL, 0)) != 0)
goto out;
/* Success */
@@ -1833,7 +1953,7 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
int allow_cert)
{
int type, ret = SSH_ERR_INTERNAL_ERROR;
- char *ktype = NULL, *curve = NULL;
+ char *ktype = NULL, *curve = NULL, *xmss_name = NULL;
struct sshkey *key = NULL;
size_t len;
u_char *pk = NULL;
@@ -1933,8 +2053,7 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
ret = SSH_ERR_EC_CURVE_MISMATCH;
goto out;
}
- if (key->ecdsa != NULL)
- EC_KEY_free(key->ecdsa);
+ EC_KEY_free(key->ecdsa);
if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid))
== NULL) {
ret = SSH_ERR_EC_CURVE_INVALID;
@@ -1985,6 +2104,36 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
key->ed25519_pk = pk;
pk = NULL;
break;
+#ifdef WITH_XMSS
+ case KEY_XMSS_CERT:
+ /* Skip nonce */
+ if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ /* FALLTHROUGH */
+ case KEY_XMSS:
+ if ((ret = sshbuf_get_cstring(b, &xmss_name, NULL)) != 0)
+ goto out;
+ if ((key = sshkey_new(type)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((ret = sshkey_xmss_init(key, xmss_name)) != 0)
+ goto out;
+ if ((ret = sshbuf_get_string(b, &pk, &len)) != 0)
+ goto out;
+ if (len == 0 || len != sshkey_xmss_pklen(key)) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ key->xmss_pk = pk;
+ pk = NULL;
+ if (type != KEY_XMSS_CERT &&
+ (ret = sshkey_xmss_deserialize_pk_info(key, b)) != 0)
+ goto out;
+ break;
+#endif /* WITH_XMSS */
case KEY_UNSPEC:
default:
ret = SSH_ERR_KEY_TYPE_UNKNOWN;
@@ -2007,12 +2156,12 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
out:
sshbuf_free(copy);
sshkey_free(key);
+ free(xmss_name);
free(ktype);
free(curve);
free(pk);
#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
- if (q != NULL)
- EC_POINT_free(q);
+ EC_POINT_free(q);
#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
return ret;
}
@@ -2050,6 +2199,31 @@ sshkey_froms(struct sshbuf *buf, struct sshkey **keyp)
}
int
+sshkey_sigtype(const u_char *sig, size_t siglen, char **sigtypep)
+{
+ int r;
+ struct sshbuf *b = NULL;
+ char *sigtype = NULL;
+
+ if (sigtypep != NULL)
+ *sigtypep = NULL;
+ if ((b = sshbuf_from(sig, siglen)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if ((r = sshbuf_get_cstring(b, &sigtype, NULL)) != 0)
+ goto out;
+ /* success */
+ if (sigtypep != NULL) {
+ *sigtypep = sigtype;
+ sigtype = NULL;
+ }
+ r = 0;
+ out:
+ free(sigtype);
+ sshbuf_free(b);
+ return r;
+}
+
+int
sshkey_sign(const struct sshkey *key,
u_char **sigp, size_t *lenp,
const u_char *data, size_t datalen, const char *alg, u_int compat)
@@ -2077,6 +2251,11 @@ sshkey_sign(const struct sshkey *key,
case KEY_ED25519:
case KEY_ED25519_CERT:
return ssh_ed25519_sign(key, sigp, lenp, data, datalen, compat);
+#ifdef WITH_XMSS
+ case KEY_XMSS:
+ case KEY_XMSS_CERT:
+ return ssh_xmss_sign(key, sigp, lenp, data, datalen, compat);
+#endif /* WITH_XMSS */
default:
return SSH_ERR_KEY_TYPE_UNKNOWN;
}
@@ -2084,11 +2263,12 @@ sshkey_sign(const struct sshkey *key,
/*
* ssh_key_verify returns 0 for a correct signature and < 0 on error.
+ * If "alg" specified, then the signature must use that algorithm.
*/
int
sshkey_verify(const struct sshkey *key,
const u_char *sig, size_t siglen,
- const u_char *data, size_t dlen, u_int compat)
+ const u_char *data, size_t dlen, const char *alg, u_int compat)
{
if (siglen == 0 || dlen > SSH_KEY_MAX_SIGN_DATA_SIZE)
return SSH_ERR_INVALID_ARGUMENT;
@@ -2104,11 +2284,16 @@ sshkey_verify(const struct sshkey *key,
# endif /* OPENSSL_HAS_ECC */
case KEY_RSA_CERT:
case KEY_RSA:
- return ssh_rsa_verify(key, sig, siglen, data, dlen);
+ return ssh_rsa_verify(key, sig, siglen, data, dlen, alg);
#endif /* WITH_OPENSSL */
case KEY_ED25519:
case KEY_ED25519_CERT:
return ssh_ed25519_verify(key, sig, siglen, data, dlen, compat);
+#ifdef WITH_XMSS
+ case KEY_XMSS:
+ case KEY_XMSS_CERT:
+ return ssh_xmss_verify(key, sig, siglen, data, dlen, compat);
+#endif /* WITH_XMSS */
default:
return SSH_ERR_KEY_TYPE_UNKNOWN;
}
@@ -2132,6 +2317,8 @@ sshkey_demote(const struct sshkey *k, struct sshkey **dkp)
pk->rsa = NULL;
pk->ed25519_pk = NULL;
pk->ed25519_sk = NULL;
+ pk->xmss_pk = NULL;
+ pk->xmss_sk = NULL;
switch (k->type) {
#ifdef WITH_OPENSSL
@@ -2193,6 +2380,29 @@ sshkey_demote(const struct sshkey *k, struct sshkey **dkp)
memcpy(pk->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ);
}
break;
+#ifdef WITH_XMSS
+ case KEY_XMSS_CERT:
+ if ((ret = sshkey_cert_copy(k, pk)) != 0)
+ goto fail;
+ /* FALLTHROUGH */
+ case KEY_XMSS:
+ if ((ret = sshkey_xmss_init(pk, k->xmss_name)) != 0)
+ goto fail;
+ if (k->xmss_pk != NULL) {
+ size_t pklen = sshkey_xmss_pklen(k);
+
+ if (pklen == 0 || sshkey_xmss_pklen(pk) != pklen) {
+ ret = SSH_ERR_INTERNAL_ERROR;
+ goto fail;
+ }
+ if ((pk->xmss_pk = malloc(pklen)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto fail;
+ }
+ memcpy(pk->xmss_pk, k->xmss_pk, pklen);
+ }
+ break;
+#endif /* WITH_XMSS */
default:
ret = SSH_ERR_KEY_TYPE_UNKNOWN;
fail:
@@ -2224,6 +2434,11 @@ sshkey_to_certified(struct sshkey *k)
case KEY_ED25519:
newtype = KEY_ED25519_CERT;
break;
+#ifdef WITH_XMSS
+ case KEY_XMSS:
+ newtype = KEY_XMSS_CERT;
+ break;
+#endif /* WITH_XMSS */
default:
return SSH_ERR_INVALID_ARGUMENT;
}
@@ -2308,6 +2523,18 @@ sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg,
k->ed25519_pk, ED25519_PK_SZ)) != 0)
goto out;
break;
+#ifdef WITH_XMSS
+ case KEY_XMSS_CERT:
+ if (k->xmss_name == NULL) {
+ ret = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ if ((ret = sshbuf_put_cstring(cert, k->xmss_name)) ||
+ (ret = sshbuf_put_string(cert,
+ k->xmss_pk, sshkey_xmss_pklen(k))) != 0)
+ goto out;
+ break;
+#endif /* WITH_XMSS */
default:
ret = SSH_ERR_INVALID_ARGUMENT;
goto out;
@@ -2465,7 +2692,8 @@ sshkey_format_cert_validity(const struct sshkey_cert *cert, char *s, size_t l)
}
int
-sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b)
+sshkey_private_serialize_opt(const struct sshkey *key, struct sshbuf *b,
+ enum sshkey_serialize_rep opts)
{
int r = SSH_ERR_INTERNAL_ERROR;
@@ -2551,6 +2779,36 @@ sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b)
ED25519_SK_SZ)) != 0)
goto out;
break;
+#ifdef WITH_XMSS
+ case KEY_XMSS:
+ if (key->xmss_name == NULL) {
+ r = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ if ((r = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
+ (r = sshbuf_put_string(b, key->xmss_pk,
+ sshkey_xmss_pklen(key))) != 0 ||
+ (r = sshbuf_put_string(b, key->xmss_sk,
+ sshkey_xmss_sklen(key))) != 0 ||
+ (r = sshkey_xmss_serialize_state_opt(key, b, opts)) != 0)
+ goto out;
+ break;
+ case KEY_XMSS_CERT:
+ if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0 ||
+ key->xmss_name == NULL) {
+ r = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
+ (r = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
+ (r = sshbuf_put_string(b, key->xmss_pk,
+ sshkey_xmss_pklen(key))) != 0 ||
+ (r = sshbuf_put_string(b, key->xmss_sk,
+ sshkey_xmss_sklen(key))) != 0 ||
+ (r = sshkey_xmss_serialize_state_opt(key, b, opts)) != 0)
+ goto out;
+ break;
+#endif /* WITH_XMSS */
default:
r = SSH_ERR_INVALID_ARGUMENT;
goto out;
@@ -2562,13 +2820,21 @@ sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b)
}
int
+sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b)
+{
+ return sshkey_private_serialize_opt(key, b,
+ SSHKEY_SERIALIZE_DEFAULT);
+}
+
+int
sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp)
{
- char *tname = NULL, *curve = NULL;
+ char *tname = NULL, *curve = NULL, *xmss_name = NULL;
struct sshkey *k = NULL;
size_t pklen = 0, sklen = 0;
int type, r = SSH_ERR_INTERNAL_ERROR;
u_char *ed25519_pk = NULL, *ed25519_sk = NULL;
+ u_char *xmss_pk = NULL, *xmss_sk = NULL;
#ifdef WITH_OPENSSL
BIGNUM *exponent = NULL;
#endif /* WITH_OPENSSL */
@@ -2713,6 +2979,53 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp)
k->ed25519_sk = ed25519_sk;
ed25519_pk = ed25519_sk = NULL;
break;
+#ifdef WITH_XMSS
+ case KEY_XMSS:
+ if ((k = sshkey_new_private(type)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((r = sshbuf_get_cstring(buf, &xmss_name, NULL)) != 0 ||
+ (r = sshkey_xmss_init(k, xmss_name)) != 0 ||
+ (r = sshbuf_get_string(buf, &xmss_pk, &pklen)) != 0 ||
+ (r = sshbuf_get_string(buf, &xmss_sk, &sklen)) != 0)
+ goto out;
+ if (pklen != sshkey_xmss_pklen(k) ||
+ sklen != sshkey_xmss_sklen(k)) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ k->xmss_pk = xmss_pk;
+ k->xmss_sk = xmss_sk;
+ xmss_pk = xmss_sk = NULL;
+ /* optional internal state */
+ if ((r = sshkey_xmss_deserialize_state_opt(k, buf)) != 0)
+ goto out;
+ break;
+ case KEY_XMSS_CERT:
+ if ((r = sshkey_froms(buf, &k)) != 0 ||
+ (r = sshkey_add_private(k)) != 0 ||
+ (r = sshbuf_get_cstring(buf, &xmss_name, NULL)) != 0 ||
+ (r = sshbuf_get_string(buf, &xmss_pk, &pklen)) != 0 ||
+ (r = sshbuf_get_string(buf, &xmss_sk, &sklen)) != 0)
+ goto out;
+ if (strcmp(xmss_name, k->xmss_name)) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if (pklen != sshkey_xmss_pklen(k) ||
+ sklen != sshkey_xmss_sklen(k)) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ k->xmss_pk = xmss_pk;
+ k->xmss_sk = xmss_sk;
+ xmss_pk = xmss_sk = NULL;
+ /* optional internal state */
+ if ((r = sshkey_xmss_deserialize_state_opt(k, buf)) != 0)
+ goto out;
+ break;
+#endif /* WITH_XMSS */
default:
r = SSH_ERR_KEY_TYPE_UNKNOWN;
goto out;
@@ -2739,18 +3052,14 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp)
free(tname);
free(curve);
#ifdef WITH_OPENSSL
- if (exponent != NULL)
- BN_clear_free(exponent);
+ BN_clear_free(exponent);
#endif /* WITH_OPENSSL */
sshkey_free(k);
- if (ed25519_pk != NULL) {
- explicit_bzero(ed25519_pk, pklen);
- free(ed25519_pk);
- }
- if (ed25519_sk != NULL) {
- explicit_bzero(ed25519_sk, sklen);
- free(ed25519_sk);
- }
+ freezero(ed25519_pk, pklen);
+ freezero(ed25519_sk, sklen);
+ free(xmss_name);
+ freezero(xmss_pk, pklen);
+ freezero(xmss_sk, sklen);
return r;
}
@@ -2828,8 +3137,7 @@ sshkey_ec_validate_public(const EC_GROUP *group, const EC_POINT *public)
ret = 0;
out:
BN_CTX_free(bnctx);
- if (nq != NULL)
- EC_POINT_free(nq);
+ EC_POINT_free(nq);
return ret;
}
@@ -3006,7 +3314,8 @@ sshkey_private_to_blob2(const struct sshkey *prv, struct sshbuf *blob,
goto out;
/* append private key and comment*/
- if ((r = sshkey_private_serialize(prv, encrypted)) != 0 ||
+ if ((r = sshkey_private_serialize_opt(prv, encrypted,
+ SSHKEY_SERIALIZE_FULL)) != 0 ||
(r = sshbuf_put_cstring(encrypted, comment)) != 0)
goto out;
@@ -3304,7 +3613,7 @@ sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *blob,
int blen, len = strlen(_passphrase);
u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL;
const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL;
- const u_char *bptr;
+ char *bptr;
BIO *bio = NULL;
if (len > 0 && len <= 4)
@@ -3367,6 +3676,9 @@ sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob,
passphrase, comment);
#endif /* WITH_OPENSSL */
case KEY_ED25519:
+#ifdef WITH_XMSS
+ case KEY_XMSS:
+#endif /* WITH_XMSS */
return sshkey_private_to_blob2(key, blob, passphrase,
comment, new_format_cipher, new_format_rounds);
default:
@@ -3524,8 +3836,7 @@ sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
}
out:
BIO_free(bio);
- if (pk != NULL)
- EVP_PKEY_free(pk);
+ EVP_PKEY_free(pk);
sshkey_free(prv);
return r;
}
@@ -3551,6 +3862,9 @@ sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type,
passphrase, keyp);
#endif /* WITH_OPENSSL */
case KEY_ED25519:
+#ifdef WITH_XMSS
+ case KEY_XMSS:
+#endif /* WITH_XMSS */
return sshkey_parse_private2(blob, type, passphrase,
keyp, commentp);
case KEY_UNSPEC:
@@ -3582,3 +3896,90 @@ sshkey_parse_private_fileblob(struct sshbuf *buffer, const char *passphrase,
return sshkey_parse_private_fileblob_type(buffer, KEY_UNSPEC,
passphrase, keyp, commentp);
}
+
+#ifdef WITH_XMSS
+/*
+ * serialize the key with the current state and forward the state
+ * maxsign times.
+ */
+int
+sshkey_private_serialize_maxsign(const struct sshkey *k, struct sshbuf *b,
+ u_int32_t maxsign, sshkey_printfn *pr)
+{
+ int r, rupdate;
+
+ if (maxsign == 0 ||
+ sshkey_type_plain(k->type) != KEY_XMSS)
+ return sshkey_private_serialize_opt(k, b,
+ SSHKEY_SERIALIZE_DEFAULT);
+ if ((r = sshkey_xmss_get_state(k, pr)) != 0 ||
+ (r = sshkey_private_serialize_opt(k, b,
+ SSHKEY_SERIALIZE_STATE)) != 0 ||
+ (r = sshkey_xmss_forward_state(k, maxsign)) != 0)
+ goto out;
+ r = 0;
+out:
+ if ((rupdate = sshkey_xmss_update_state(k, pr)) != 0) {
+ if (r == 0)
+ r = rupdate;
+ }
+ return r;
+}
+
+u_int32_t
+sshkey_signatures_left(const struct sshkey *k)
+{
+ if (sshkey_type_plain(k->type) == KEY_XMSS)
+ return sshkey_xmss_signatures_left(k);
+ return 0;
+}
+
+int
+sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign)
+{
+ if (sshkey_type_plain(k->type) != KEY_XMSS)
+ return SSH_ERR_INVALID_ARGUMENT;
+ return sshkey_xmss_enable_maxsign(k, maxsign);
+}
+
+int
+sshkey_set_filename(struct sshkey *k, const char *filename)
+{
+ if (k == NULL)
+ return SSH_ERR_INVALID_ARGUMENT;
+ if (sshkey_type_plain(k->type) != KEY_XMSS)
+ return 0;
+ if (filename == NULL)
+ return SSH_ERR_INVALID_ARGUMENT;
+ if ((k->xmss_filename = strdup(filename)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ return 0;
+}
+#else
+int
+sshkey_private_serialize_maxsign(const struct sshkey *k, struct sshbuf *b,
+ u_int32_t maxsign, sshkey_printfn *pr)
+{
+ return sshkey_private_serialize_opt(k, b, SSHKEY_SERIALIZE_DEFAULT);
+}
+
+u_int32_t
+sshkey_signatures_left(const struct sshkey *k)
+{
+ return 0;
+}
+
+int
+sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign)
+{
+ return SSH_ERR_INVALID_ARGUMENT;
+}
+
+int
+sshkey_set_filename(struct sshkey *k, const char *filename)
+{
+ if (k == NULL)
+ return SSH_ERR_INVALID_ARGUMENT;
+ return 0;
+}
+#endif /* WITH_XMSS */