upstream: Add experimental support for PQC XMSS keys (Extended

Hash-Based Signatures) The code is not compiled in by default (see WITH_XMSS
in Makefile.inc) Joint work with stefan-lukas_gazdag at genua.eu See
https://tools.ietf.org/html/draft-irtf-cfrg-xmss-hash-based-signatures-12 ok
djm@

OpenBSD-Commit-ID: ef3eccb96762a5d6f135d7daeef608df7776a7ac
This commit is contained in:
markus@openbsd.org 2018-02-23 15:58:37 +00:00 committed by Damien Miller
parent 7d330a1ac0
commit 1b11ea7c58
33 changed files with 3657 additions and 73 deletions

View File

@ -62,6 +62,15 @@ MKDIR_P=@MKDIR_P@
TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT)
XMSS_OBJS=\
ssh-xmss.c \
sshkey-xmss.c \
xmss_commons.c \
xmss_fast.c \
xmss_hash.c \
xmss_hash_address.c \
xmss_wots.c
LIBOPENSSH_OBJS=\
ssh_api.o \
ssherr.o \
@ -71,7 +80,8 @@ LIBOPENSSH_OBJS=\
sshbuf-misc.o \
sshbuf-getput-crypto.o \
krl.o \
bitmap.o
bitmap.o \
${XMSS_OBJS}
LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
authfd.o authfile.o bufaux.o bufbn.o bufec.o buffer.o \

View File

@ -1,4 +1,4 @@
/* $OpenBSD: authfd.c,v 1.107 2018/02/10 09:25:34 djm Exp $ */
/* $OpenBSD: authfd.c,v 1.108 2018/02/23 15:58:37 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -129,7 +129,7 @@ ssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply)
/* Get the length of the message, and format it in the buffer. */
len = sshbuf_len(request);
put_u32(buf, len);
POKE_U32(buf, len);
/* Send the length and then the packet to the agent. */
if (atomicio(vwrite, sock, buf, 4) != 4 ||
@ -144,7 +144,7 @@ ssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply)
return SSH_ERR_AGENT_COMMUNICATION;
/* Extract the length, and check it for sanity. */
len = get_u32(buf);
len = PEEK_U32(buf);
if (len > MAX_AGENT_REPLY_LEN)
return SSH_ERR_INVALID_FORMAT;
@ -391,19 +391,7 @@ ssh_agent_sign(int sock, const struct sshkey *key,
static int
ssh_encode_identity_ssh2(struct sshbuf *b, const struct sshkey *key,
const char *comment)
{
int r;
if ((r = sshkey_private_serialize(key, b)) != 0 ||
(r = sshbuf_put_cstring(b, comment)) != 0)
return r;
return 0;
}
static int
encode_constraints(struct sshbuf *m, u_int life, u_int confirm)
encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign)
{
int r;
@ -416,6 +404,11 @@ encode_constraints(struct sshbuf *m, u_int life, u_int confirm)
if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_CONFIRM)) != 0)
goto out;
}
if (maxsign != 0) {
if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_MAXSIGN)) != 0 ||
(r = sshbuf_put_u32(m, maxsign)) != 0)
goto out;
}
r = 0;
out:
return r;
@ -427,10 +420,10 @@ encode_constraints(struct sshbuf *m, u_int life, u_int confirm)
*/
int
ssh_add_identity_constrained(int sock, const struct sshkey *key,
const char *comment, u_int life, u_int confirm)
const char *comment, u_int life, u_int confirm, u_int maxsign)
{
struct sshbuf *msg;
int r, constrained = (life || confirm);
int r, constrained = (life || confirm || maxsign);
u_char type;
if ((msg = sshbuf_new()) == NULL)
@ -447,11 +440,15 @@ ssh_add_identity_constrained(int sock, const struct sshkey *key,
#endif
case KEY_ED25519:
case KEY_ED25519_CERT:
case KEY_XMSS:
case KEY_XMSS_CERT:
type = constrained ?
SSH2_AGENTC_ADD_ID_CONSTRAINED :
SSH2_AGENTC_ADD_IDENTITY;
if ((r = sshbuf_put_u8(msg, type)) != 0 ||
(r = ssh_encode_identity_ssh2(msg, key, comment)) != 0)
(r = sshkey_private_serialize_maxsign(key, msg, maxsign,
NULL)) != 0 ||
(r = sshbuf_put_cstring(msg, comment)) != 0)
goto out;
break;
default:
@ -459,7 +456,7 @@ ssh_add_identity_constrained(int sock, const struct sshkey *key,
goto out;
}
if (constrained &&
(r = encode_constraints(msg, life, confirm)) != 0)
(r = encode_constraints(msg, life, confirm, maxsign)) != 0)
goto out;
if ((r = ssh_request_reply(sock, msg, msg)) != 0)
goto out;
@ -537,7 +534,7 @@ ssh_update_card(int sock, int add, const char *reader_id, const char *pin,
(r = sshbuf_put_cstring(msg, pin)) != 0)
goto out;
if (constrained &&
(r = encode_constraints(msg, life, confirm)) != 0)
(r = encode_constraints(msg, life, confirm, 0)) != 0)
goto out;
if ((r = ssh_request_reply(sock, msg, msg)) != 0)
goto out;

View File

@ -1,4 +1,4 @@
/* $OpenBSD: authfd.h,v 1.42 2018/02/10 09:25:34 djm Exp $ */
/* $OpenBSD: authfd.h,v 1.43 2018/02/23 15:58:37 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -30,7 +30,7 @@ int ssh_lock_agent(int sock, int lock, const char *password);
int ssh_fetch_identitylist(int sock, struct ssh_identitylist **idlp);
void ssh_free_identitylist(struct ssh_identitylist *idl);
int ssh_add_identity_constrained(int sock, const struct sshkey *key,
const char *comment, u_int life, u_int confirm);
const char *comment, u_int life, u_int confirm, u_int maxsign);
int ssh_remove_identity(int sock, struct sshkey *key);
int ssh_update_card(int sock, int add, const char *reader_id,
const char *pin, u_int life, u_int confirm);
@ -77,6 +77,7 @@ int ssh_agent_sign(int sock, const struct sshkey *key,
#define SSH_AGENT_CONSTRAIN_LIFETIME 1
#define SSH_AGENT_CONSTRAIN_CONFIRM 2
#define SSH_AGENT_CONSTRAIN_MAXSIGN 3
/* extended failure messages */
#define SSH2_AGENT_FAILURE 30

View File

@ -1,4 +1,4 @@
/* $OpenBSD: authfile.c,v 1.127 2017/07/01 13:50:45 djm Exp $ */
/* $OpenBSD: authfile.c,v 1.128 2018/02/23 15:58:37 markus Exp $ */
/*
* Copyright (c) 2000, 2013 Markus Friedl. All rights reserved.
*
@ -191,6 +191,8 @@ sshkey_load_private_type(int type, const char *filename, const char *passphrase,
*perm_ok = 1;
r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp);
if (r == 0 && keyp && *keyp)
r = sshkey_set_filename(*keyp, filename);
out:
close(fd);
return r;
@ -249,6 +251,9 @@ sshkey_load_private(const char *filename, const char *passphrase,
(r = sshkey_parse_private_fileblob(buffer, passphrase, keyp,
commentp)) != 0)
goto out;
if (keyp && *keyp &&
(r = sshkey_set_filename(*keyp, filename)) != 0)
goto out;
r = 0;
out:
close(fd);
@ -397,6 +402,7 @@ sshkey_load_private_cert(int type, const char *filename, const char *passphrase,
case KEY_ECDSA:
#endif /* WITH_OPENSSL */
case KEY_ED25519:
case KEY_XMSS:
case KEY_UNSPEC:
break;
default:

View File

@ -1,4 +1,4 @@
/* $OpenBSD: cipher.c,v 1.110 2018/02/13 03:36:56 djm Exp $ */
/* $OpenBSD: cipher.c,v 1.111 2018/02/23 15:58:37 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -401,7 +401,7 @@ cipher_get_length(struct sshcipher_ctx *cc, u_int *plenp, u_int seqnr,
cp, len);
if (len < 4)
return SSH_ERR_MESSAGE_INCOMPLETE;
*plenp = get_u32(cp);
*plenp = PEEK_U32(cp);
return 0;
}

7
dns.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: dns.c,v 1.37 2017/09/14 04:32:21 djm Exp $ */
/* $OpenBSD: dns.c,v 1.38 2018/02/23 15:58:37 markus Exp $ */
/*
* Copyright (c) 2003 Wesley Griffin. All rights reserved.
@ -105,6 +105,11 @@ dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type,
if (!*digest_type)
*digest_type = SSHFP_HASH_SHA256;
break;
case KEY_XMSS:
*algorithm = SSHFP_KEY_XMSS;
if (!*digest_type)
*digest_type = SSHFP_HASH_SHA256;
break;
default:
*algorithm = SSHFP_KEY_RESERVED; /* 0 */
*digest_type = SSHFP_HASH_RESERVED; /* 0 */

5
dns.h
View File

@ -1,4 +1,4 @@
/* $OpenBSD: dns.h,v 1.17 2017/09/14 04:32:21 djm Exp $ */
/* $OpenBSD: dns.h,v 1.18 2018/02/23 15:58:37 markus Exp $ */
/*
* Copyright (c) 2003 Wesley Griffin. All rights reserved.
@ -33,7 +33,8 @@ enum sshfp_types {
SSHFP_KEY_RSA = 1,
SSHFP_KEY_DSA = 2,
SSHFP_KEY_ECDSA = 3,
SSHFP_KEY_ED25519 = 4
SSHFP_KEY_ED25519 = 4,
SSHFP_KEY_XMSS = 5
};
enum sshfp_hashes {

View File

@ -1,4 +1,4 @@
/* $OpenBSD: pathnames.h,v 1.27 2017/05/05 10:42:49 naddy Exp $ */
/* $OpenBSD: pathnames.h,v 1.28 2018/02/23 15:58:37 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@ -39,6 +39,7 @@
#define _PATH_HOST_DSA_KEY_FILE SSHDIR "/ssh_host_dsa_key"
#define _PATH_HOST_ECDSA_KEY_FILE SSHDIR "/ssh_host_ecdsa_key"
#define _PATH_HOST_ED25519_KEY_FILE SSHDIR "/ssh_host_ed25519_key"
#define _PATH_HOST_XMSS_KEY_FILE SSHDIR "/ssh_host_xmss_key"
#define _PATH_HOST_RSA_KEY_FILE SSHDIR "/ssh_host_rsa_key"
#define _PATH_DH_MODULI SSHDIR "/moduli"
@ -75,6 +76,7 @@
#define _PATH_SSH_CLIENT_ID_ECDSA _PATH_SSH_USER_DIR "/id_ecdsa"
#define _PATH_SSH_CLIENT_ID_RSA _PATH_SSH_USER_DIR "/id_rsa"
#define _PATH_SSH_CLIENT_ID_ED25519 _PATH_SSH_USER_DIR "/id_ed25519"
#define _PATH_SSH_CLIENT_ID_XMSS _PATH_SSH_USER_DIR "/id_xmss"
/*
* Configuration file in user's home directory. This file need not be

View File

@ -1,4 +1,4 @@
/* $OpenBSD: readconf.c,v 1.282 2018/02/23 02:34:33 djm Exp $ */
/* $OpenBSD: readconf.c,v 1.283 2018/02/23 15:58:37 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -1943,6 +1943,7 @@ fill_default_options(Options * options)
#endif
add_identity_file(options, "~/",
_PATH_SSH_CLIENT_ID_ED25519, 0);
add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_XMSS, 0);
}
if (options->escape_char == -1)
options->escape_char = '~';

View File

@ -1,5 +1,5 @@
/* $OpenBSD: servconf.c,v 1.324 2018/02/16 02:32:40 djm Exp $ */
/* $OpenBSD: servconf.c,v 1.325 2018/02/23 15:58:37 markus Exp $ */
/*
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
* All rights reserved
@ -253,6 +253,8 @@ fill_default_server_options(ServerOptions *options)
#endif
servconf_add_hostkey("[default]", 0, options,
_PATH_HOST_ED25519_KEY_FILE);
servconf_add_hostkey("[default]", 0, options,
_PATH_HOST_XMSS_KEY_FILE);
}
/* No certificates by default */
if (options->num_ports == 0)

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-add.c,v 1.134 2017/08/29 09:42:29 dlg Exp $ */
/* $OpenBSD: ssh-add.c,v 1.135 2018/02/23 15:58:37 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -78,6 +78,7 @@ static char *default_files[] = {
#endif
#endif /* WITH_OPENSSL */
_PATH_SSH_CLIENT_ID_ED25519,
_PATH_SSH_CLIENT_ID_XMSS,
NULL
};
@ -89,6 +90,10 @@ static int lifetime = 0;
/* User has to confirm key use */
static int confirm = 0;
/* Maximum number of signatures (XMSS) */
static u_int maxsign = 0;
static u_int minleft = 0;
/* we keep a cache of one passphrase */
static char *pass = NULL;
static void
@ -190,7 +195,10 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag)
char *comment = NULL;
char msg[1024], *certpath = NULL;
int r, fd, ret = -1;
size_t i;
u_int32_t left;
struct sshbuf *keyblob;
struct ssh_identitylist *idlist;
if (strcmp(filename, "-") == 0) {
fd = STDIN_FILENO;
@ -268,8 +276,40 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag)
comment = xstrdup(filename);
sshbuf_free(keyblob);
/* For XMSS */
if ((r = sshkey_set_filename(private, filename)) != 0) {
fprintf(stderr, "Could not add filename to private key: %s (%s)\n",
filename, comment);
goto out;
}
if (maxsign && minleft &&
(r = ssh_fetch_identitylist(agent_fd, &idlist)) == 0) {
for (i = 0; i < idlist->nkeys; i++) {
if (!sshkey_equal_public(idlist->keys[i], private))
continue;
left = sshkey_signatures_left(idlist->keys[i]);
if (left < minleft) {
fprintf(stderr,
"Only %d signatures left.\n", left);
break;
}
fprintf(stderr, "Skipping update: ");
if (left == minleft) {
fprintf(stderr,
"required signatures left (%d).\n", left);
} else {
fprintf(stderr,
"more signatures left (%d) than"
" required (%d).\n", left, minleft);
}
ssh_free_identitylist(idlist);
goto out;
}
ssh_free_identitylist(idlist);
}
if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
lifetime, confirm)) == 0) {
lifetime, confirm, maxsign)) == 0) {
fprintf(stderr, "Identity added: %s (%s)\n", filename, comment);
ret = 0;
if (lifetime != 0)
@ -317,7 +357,7 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag)
sshkey_free(cert);
if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
lifetime, confirm)) != 0) {
lifetime, confirm, maxsign)) != 0) {
error("Certificate %s (%s) add failed: %s", certpath,
private->cert->key_id, ssh_err(r));
goto out;
@ -368,6 +408,7 @@ list_identities(int agent_fd, int do_fp)
char *fp;
int r;
struct ssh_identitylist *idlist;
u_int32_t left;
size_t i;
if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) {
@ -392,7 +433,12 @@ list_identities(int agent_fd, int do_fp)
ssh_err(r));
continue;
}
fprintf(stdout, " %s\n", idlist->comments[i]);
fprintf(stdout, " %s", idlist->comments[i]);
left = sshkey_signatures_left(idlist->keys[i]);
if (left > 0)
fprintf(stdout,
" [signatures left %d]", left);
fprintf(stdout, "\n");
}
}
ssh_free_identitylist(idlist);
@ -454,6 +500,8 @@ usage(void)
fprintf(stderr, " -L List public key parameters of all identities.\n");
fprintf(stderr, " -k Load only keys and not certificates.\n");
fprintf(stderr, " -c Require confirmation to sign using identities\n");
fprintf(stderr, " -m minleft Maxsign is only changed if less than minleft are left (for XMSS)\n");
fprintf(stderr, " -M maxsign Maximum number of signatures allowed (for XMSS)\n");
fprintf(stderr, " -t life Set lifetime (in seconds) when adding identities.\n");
fprintf(stderr, " -d Delete identity.\n");
fprintf(stderr, " -D Delete all identities.\n");
@ -500,7 +548,7 @@ main(int argc, char **argv)
exit(2);
}
while ((ch = getopt(argc, argv, "klLcdDxXE:e:qs:t:")) != -1) {
while ((ch = getopt(argc, argv, "klLcdDxXE:e:M:m:qs:t:")) != -1) {
switch (ch) {
case 'E':
fingerprint_hash = ssh_digest_alg_by_name(optarg);
@ -525,6 +573,22 @@ main(int argc, char **argv)
case 'c':
confirm = 1;
break;
case 'm':
minleft = (int)strtonum(optarg, 1, UINT_MAX, NULL);
if (minleft == 0) {
usage();
ret = 1;
goto done;
}
break;
case 'M':
maxsign = (int)strtonum(optarg, 1, UINT_MAX, NULL);
if (maxsign == 0) {
usage();
ret = 1;
goto done;
}
break;
case 'd':
deleting = 1;
break;

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-agent.c,v 1.227 2018/01/23 05:27:21 djm Exp $ */
/* $OpenBSD: ssh-agent.c,v 1.228 2018/02/23 15:58:37 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -245,7 +245,8 @@ process_request_identities(SocketEntry *e)
(r = sshbuf_put_u32(msg, idtab->nentries)) != 0)
fatal("%s: buffer error: %s", __func__, ssh_err(r));
TAILQ_FOREACH(id, &idtab->idlist, next) {
if ((r = sshkey_puts(id->key, msg)) != 0 ||
if ((r = sshkey_puts_opts(id->key, msg, SSHKEY_SERIALIZE_INFO))
!= 0 ||
(r = sshbuf_put_cstring(msg, id->comment)) != 0) {
error("%s: put key/comment: %s", __func__,
ssh_err(r));
@ -402,7 +403,7 @@ process_add_identity(SocketEntry *e)
{
Identity *id;
int success = 0, confirm = 0;
u_int seconds;
u_int seconds, maxsign;
char *comment = NULL;
time_t death = 0;
struct sshkey *k = NULL;
@ -433,6 +434,18 @@ process_add_identity(SocketEntry *e)
case SSH_AGENT_CONSTRAIN_CONFIRM:
confirm = 1;
break;
case SSH_AGENT_CONSTRAIN_MAXSIGN:
if ((r = sshbuf_get_u32(e->request, &maxsign)) != 0) {
error("%s: bad maxsign constraint: %s",
__func__, ssh_err(r));
goto err;
}
if ((r = sshkey_enable_maxsign(k, maxsign)) != 0) {
error("%s: cannot enable maxsign: %s",
__func__, ssh_err(r));
goto err;
}
break;
default:
error("%s: Unknown constraint %d", __func__, ctype);
err:
@ -448,14 +461,15 @@ process_add_identity(SocketEntry *e)
death = monotime() + lifetime;
if ((id = lookup_identity(k)) == NULL) {
id = xcalloc(1, sizeof(Identity));
id->key = k;
TAILQ_INSERT_TAIL(&idtab->idlist, id, next);
/* Increment the number of identities. */
idtab->nentries++;
} else {
sshkey_free(k);
/* key state might have been updated */
sshkey_free(id->key);
free(id->comment);
}
id->key = k;
id->comment = comment;
id->death = death;
id->confirm = confirm;

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-keygen.c,v 1.312 2018/02/10 05:48:46 djm Exp $ */
/* $OpenBSD: ssh-keygen.c,v 1.313 2018/02/23 15:58:38 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -275,6 +275,10 @@ ask_filename(struct passwd *pw, const char *prompt)
case KEY_ED25519_CERT:
name = _PATH_SSH_CLIENT_ID_ED25519;
break;
case KEY_XMSS:
case KEY_XMSS_CERT:
name = _PATH_SSH_CLIENT_ID_XMSS;
break;
default:
fatal("bad key type");
}
@ -969,6 +973,9 @@ do_gen_all_hostkeys(struct passwd *pw)
#endif /* OPENSSL_HAS_ECC */
#endif /* WITH_OPENSSL */
{ "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE },
#ifdef WITH_XMSS
{ "xmss", "XMSS",_PATH_HOST_XMSS_KEY_FILE },
#endif /* WITH_XMSS */
{ NULL, NULL, NULL }
};
@ -1455,7 +1462,8 @@ do_change_comment(struct passwd *pw)
}
}
if (private->type != KEY_ED25519 && !use_new_format) {
if (private->type != KEY_ED25519 && private->type != KEY_XMSS &&
!use_new_format) {
error("Comments are only supported for keys stored in "
"the new format (-o).");
explicit_bzero(passphrase, strlen(passphrase));
@ -1705,7 +1713,8 @@ do_ca_sign(struct passwd *pw, int argc, char **argv)
fatal("%s: unable to open \"%s\": %s",
__func__, tmp, ssh_err(r));
if (public->type != KEY_RSA && public->type != KEY_DSA &&
public->type != KEY_ECDSA && public->type != KEY_ED25519)
public->type != KEY_ECDSA && public->type != KEY_ED25519 &&
public->type != KEY_XMSS)
fatal("%s: key \"%s\" type %s cannot be certified",
__func__, tmp, sshkey_type(public));
@ -2405,7 +2414,7 @@ main(int argc, char **argv)
gen_all_hostkeys = 1;
break;
case 'b':
bits = (u_int32_t)strtonum(optarg, 256, 32768, &errstr);
bits = (u_int32_t)strtonum(optarg, 10, 32768, &errstr);
if (errstr)
fatal("Bits has bad value %s (%s)",
optarg, errstr);
@ -2683,6 +2692,8 @@ main(int argc, char **argv)
_PATH_HOST_ECDSA_KEY_FILE, rr_hostname);
n += do_print_resource_record(pw,
_PATH_HOST_ED25519_KEY_FILE, rr_hostname);
n += do_print_resource_record(pw,
_PATH_HOST_XMSS_KEY_FILE, rr_hostname);
if (n == 0)
fatal("no keys found.");
exit(0);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-keyscan.c,v 1.117 2018/02/23 05:14:05 djm Exp $ */
/* $OpenBSD: ssh-keyscan.c,v 1.118 2018/02/23 15:58:38 markus Exp $ */
/*
* Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
*
@ -58,9 +58,10 @@ int ssh_port = SSH_DEFAULT_PORT;
#define KT_RSA (1<<1)
#define KT_ECDSA (1<<2)
#define KT_ED25519 (1<<3)
#define KT_XMSS (1<<4)
#define KT_MIN KT_DSA
#define KT_MAX KT_ED25519
#define KT_MAX KT_XMSS
int get_cert = 0;
int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519;
@ -238,6 +239,10 @@ keygrab_ssh2(con *c)
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
"ssh-ed25519-cert-v01@openssh.com" : "ssh-ed25519";
break;
case KT_XMSS:
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
"ssh-xmss-cert-v01@openssh.com" : "ssh-xmss@openssh.com";
break;
case KT_ECDSA:
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
"ecdsa-sha2-nistp256-cert-v01@openssh.com,"
@ -718,6 +723,9 @@ main(int argc, char **argv)
case KEY_ED25519:
get_keytypes |= KT_ED25519;
break;
case KEY_XMSS:
get_keytypes |= KT_XMSS;
break;
case KEY_UNSPEC:
default:
fatal("Unknown key type \"%s\"", tname);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-keysign.c,v 1.53 2018/02/07 22:52:45 dtucker Exp $ */
/* $OpenBSD: ssh-keysign.c,v 1.54 2018/02/23 15:58:38 markus Exp $ */
/*
* Copyright (c) 2002 Markus Friedl. All rights reserved.
*
@ -171,7 +171,7 @@ main(int argc, char **argv)
{
struct sshbuf *b;
Options options;
#define NUM_KEYTYPES 4
#define NUM_KEYTYPES 5
struct sshkey *keys[NUM_KEYTYPES], *key = NULL;
struct passwd *pw;
int r, key_fd[NUM_KEYTYPES], i, found, version = 2, fd;
@ -198,6 +198,7 @@ main(int argc, char **argv)
key_fd[i++] = open(_PATH_HOST_DSA_KEY_FILE, O_RDONLY);
key_fd[i++] = open(_PATH_HOST_ECDSA_KEY_FILE, O_RDONLY);
key_fd[i++] = open(_PATH_HOST_ED25519_KEY_FILE, O_RDONLY);
key_fd[i++] = open(_PATH_HOST_XMSS_KEY_FILE, O_RDONLY);
key_fd[i++] = open(_PATH_HOST_RSA_KEY_FILE, O_RDONLY);
original_real_uid = getuid(); /* XXX readconf.c needs this */

188
ssh-xmss.c Normal file
View File

@ -0,0 +1,188 @@
/* $OpenBSD: ssh-xmss.c,v 1.1 2018/02/23 15:58:38 markus Exp $*/
/*
* Copyright (c) 2017 Stefan-Lukas Gazdag.
* Copyright (c) 2017 Markus Friedl.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define SSHKEY_INTERNAL
#include <sys/types.h>
#include <limits.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include "log.h"
#include "sshbuf.h"
#include "sshkey.h"
#include "sshkey-xmss.h"
#include "ssherr.h"
#include "ssh.h"
#include "xmss_fast.h"
int
ssh_xmss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
const u_char *data, size_t datalen, u_int compat)
{
u_char *sig = NULL;
size_t slen = 0, len = 0, required_siglen;
unsigned long long smlen;
int r, ret;
struct sshbuf *b = NULL;
if (lenp != NULL)
*lenp = 0;
if (sigp != NULL)
*sigp = NULL;
if (key == NULL ||
sshkey_type_plain(key->type) != KEY_XMSS ||
key->xmss_sk == NULL ||
sshkey_xmss_params(key) == NULL)
return SSH_ERR_INVALID_ARGUMENT;
if ((r = sshkey_xmss_siglen(key, &required_siglen)) != 0)
return r;
if (datalen >= INT_MAX - required_siglen)
return SSH_ERR_INVALID_ARGUMENT;
smlen = slen = datalen + required_siglen;
if ((sig = malloc(slen)) == NULL)
return SSH_ERR_ALLOC_FAIL;
if ((r = sshkey_xmss_get_state(key, error)) != 0)
goto out;
if ((ret = xmss_sign(key->xmss_sk, sshkey_xmss_bds_state(key), sig, &smlen,
data, datalen, sshkey_xmss_params(key))) != 0 || smlen <= datalen) {
r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */
goto out;
}
/* encode signature */
if ((b = sshbuf_new()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
if ((r = sshbuf_put_cstring(b, "ssh-xmss@openssh.com")) != 0 ||
(r = sshbuf_put_string(b, sig, smlen - datalen)) != 0)
goto out;
len = sshbuf_len(b);
if (sigp != NULL) {
if ((*sigp = malloc(len)) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
memcpy(*sigp, sshbuf_ptr(b), len);
}
if (lenp != NULL)
*lenp = len;
/* success */
r = 0;
out:
if ((ret = sshkey_xmss_update_state(key, error)) != 0) {
/* discard signature since we cannot update the state */
if (r == 0 && sigp != NULL && *sigp != NULL) {
explicit_bzero(*sigp, len);
free(*sigp);
}
if (sigp != NULL)
*sigp = NULL;
if (lenp != NULL)
*lenp = 0;
r = ret;
}
sshbuf_free(b);
if (sig != NULL) {
explicit_bzero(sig, slen);
free(sig);
}
return r;
}
int
ssh_xmss_verify(const struct sshkey *key,
const u_char *signature, size_t signaturelen,
const u_char *data, size_t datalen, u_int compat)
{
struct sshbuf *b = NULL;
char *ktype = NULL;
const u_char *sigblob;
u_char *sm = NULL, *m = NULL;
size_t len, required_siglen;
unsigned long long smlen = 0, mlen = 0;
int r, ret;
if (key == NULL ||
sshkey_type_plain(key->type) != KEY_XMSS ||
key->xmss_pk == NULL ||
sshkey_xmss_params(key) == NULL ||
signature == NULL || signaturelen == 0)
return SSH_ERR_INVALID_ARGUMENT;
if ((r = sshkey_xmss_siglen(key, &required_siglen)) != 0)
return r;
if (datalen >= INT_MAX - required_siglen)
return SSH_ERR_INVALID_ARGUMENT;
if ((b = sshbuf_from(signature, signaturelen)) == NULL)
return SSH_ERR_ALLOC_FAIL;
if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 ||
(r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0)
goto out;
if (strcmp("ssh-xmss@openssh.com", ktype) != 0) {
r = SSH_ERR_KEY_TYPE_MISMATCH;
goto out;
}
if (sshbuf_len(b) != 0) {
r = SSH_ERR_UNEXPECTED_TRAILING_DATA;
goto out;
}
if (len != required_siglen) {
r = SSH_ERR_INVALID_FORMAT;
goto out;
}
if (datalen >= SIZE_MAX - len) {
r = SSH_ERR_INVALID_ARGUMENT;
goto out;
}
smlen = len + datalen;
mlen = smlen;
if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
memcpy(sm, sigblob, len);
memcpy(sm+len, data, datalen);
if ((ret = xmss_sign_open(m, &mlen, sm, smlen,
key->xmss_pk, sshkey_xmss_params(key))) != 0) {
debug2("%s: crypto_sign_xmss_open failed: %d",
__func__, ret);
}
if (ret != 0 || mlen != datalen) {
r = SSH_ERR_SIGNATURE_INVALID;
goto out;
}
/* XXX compare 'm' and 'data' ? */
/* success */
r = 0;
out:
if (sm != NULL) {
explicit_bzero(sm, smlen);
free(sm);
}
if (m != NULL) {
explicit_bzero(m, smlen); /* NB mlen may be invalid if r != 0 */
free(m);
}
sshbuf_free(b);
free(ktype);
return r;
}

15
ssh.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh.c,v 1.474 2018/02/23 02:34:33 djm Exp $ */
/* $OpenBSD: ssh.c,v 1.475 2018/02/23 15:58:38 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -1384,7 +1384,7 @@ main(int ac, char **av)
sensitive_data.keys = NULL;
sensitive_data.external_keysign = 0;
if (options.hostbased_authentication) {
sensitive_data.nkeys = 9;
sensitive_data.nkeys = 11;
sensitive_data.keys = xcalloc(sensitive_data.nkeys,
sizeof(struct sshkey)); /* XXX */
for (i = 0; i < sensitive_data.nkeys; i++)
@ -1411,6 +1411,10 @@ main(int ac, char **av)
_PATH_HOST_RSA_KEY_FILE, "", NULL, NULL);
sensitive_data.keys[8] = key_load_private_type(KEY_DSA,
_PATH_HOST_DSA_KEY_FILE, "", NULL, NULL);
sensitive_data.keys[9] = key_load_private_cert(KEY_XMSS,
_PATH_HOST_XMSS_KEY_FILE, "", NULL);
sensitive_data.keys[10] = key_load_private_type(KEY_XMSS,
_PATH_HOST_XMSS_KEY_FILE, "", NULL, NULL);
PRIV_END;
if (options.hostbased_authentication == 1 &&
@ -1418,7 +1422,8 @@ main(int ac, char **av)
sensitive_data.keys[5] == NULL &&
sensitive_data.keys[6] == NULL &&
sensitive_data.keys[7] == NULL &&
sensitive_data.keys[8] == NULL) {
sensitive_data.keys[8] == NULL &&
sensitive_data.keys[9] == NULL) {
#ifdef OPENSSL_HAS_ECC
sensitive_data.keys[1] = key_load_cert(
_PATH_HOST_ECDSA_KEY_FILE);
@ -1439,6 +1444,10 @@ main(int ac, char **av)
_PATH_HOST_RSA_KEY_FILE, NULL);
sensitive_data.keys[8] = key_load_public(
_PATH_HOST_DSA_KEY_FILE, NULL);
sensitive_data.keys[9] = key_load_cert(
_PATH_HOST_XMSS_KEY_FILE);
sensitive_data.keys[10] = key_load_public(
_PATH_HOST_XMSS_KEY_FILE, NULL);
sensitive_data.external_keysign = 1;
}
}

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshconnect.c,v 1.296 2018/02/23 04:18:46 dtucker Exp $ */
/* $OpenBSD: sshconnect.c,v 1.297 2018/02/23 15:58:38 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -1475,6 +1475,7 @@ show_other_keys(struct hostkeys *hostkeys, struct sshkey *key)
KEY_DSA,
KEY_ECDSA,
KEY_ED25519,
KEY_XMSS,
-1
};
int i, ret = 0;
@ -1592,7 +1593,7 @@ maybe_add_key_to_agent(char *authfile, const struct sshkey *private,
}
if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0,
(options.add_keys_to_agent == 3))) == 0)
(options.add_keys_to_agent == 3), 0)) == 0)
debug("identity added to agent: %s", authfile);
else
debug("could not add identity to agent: %s (%d)", authfile, r);

6
sshd.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshd.c,v 1.504 2018/02/11 21:16:56 dtucker Exp $ */
/* $OpenBSD: sshd.c,v 1.505 2018/02/23 15:58:38 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -705,6 +705,7 @@ list_hostkey_types(void)
case KEY_DSA:
case KEY_ECDSA:
case KEY_ED25519:
case KEY_XMSS:
if (buffer_len(&b) > 0)
buffer_append(&b, ",", 1);
p = key_ssh_name(key);
@ -726,6 +727,7 @@ list_hostkey_types(void)
case KEY_DSA_CERT:
case KEY_ECDSA_CERT:
case KEY_ED25519_CERT:
case KEY_XMSS_CERT:
if (buffer_len(&b) > 0)
buffer_append(&b, ",", 1);
p = key_ssh_name(key);
@ -752,6 +754,7 @@ get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh)
case KEY_DSA_CERT:
case KEY_ECDSA_CERT:
case KEY_ED25519_CERT:
case KEY_XMSS_CERT:
key = sensitive_data.host_certificates[i];
break;
default:
@ -1734,6 +1737,7 @@ main(int ac, char **av)
case KEY_DSA:
case KEY_ECDSA:
case KEY_ED25519:
case KEY_XMSS:
if (have_agent || key != NULL)
sensitive_data.have_ssh2_key = 1;
break;

1048
sshkey-xmss.c Normal file

File diff suppressed because it is too large Load Diff

56
sshkey-xmss.h Normal file
View File

@ -0,0 +1,56 @@
/* $OpenBSD: sshkey-xmss.h,v 1.1 2018/02/23 15:58:38 markus Exp $ */
/*
* Copyright (c) 2017 Markus Friedl. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SSHKEY_XMSS_H
#define SSHKEY_XMSS_H
#define XMSS_SHA2_256_W16_H10_NAME "XMSS_SHA2-256_W16_H10"
#define XMSS_SHA2_256_W16_H16_NAME "XMSS_SHA2-256_W16_H16"
#define XMSS_SHA2_256_W16_H20_NAME "XMSS_SHA2-256_W16_H20"
#define XMSS_DEFAULT_NAME XMSS_SHA2_256_W16_H10_NAME
size_t sshkey_xmss_pklen(const struct sshkey *);
size_t sshkey_xmss_sklen(const struct sshkey *);
int sshkey_xmss_init(struct sshkey *, const char *);
void sshkey_xmss_free_state(struct sshkey *);
int sshkey_xmss_generate_private_key(struct sshkey *, u_int);
int sshkey_xmss_serialize_state(const struct sshkey *, struct sshbuf *);
int sshkey_xmss_serialize_state_opt(const struct sshkey *, struct sshbuf *,
enum sshkey_serialize_rep);
int sshkey_xmss_serialize_pk_info(const struct sshkey *, struct sshbuf *,
enum sshkey_serialize_rep);
int sshkey_xmss_deserialize_state(struct sshkey *, struct sshbuf *);
int sshkey_xmss_deserialize_state_opt(struct sshkey *, struct sshbuf *);
int sshkey_xmss_deserialize_pk_info(struct sshkey *, struct sshbuf *);
int sshkey_xmss_siglen(const struct sshkey *, size_t *);
void *sshkey_xmss_params(const struct sshkey *);
void *sshkey_xmss_bds_state(const struct sshkey *);
int sshkey_xmss_get_state(const struct sshkey *, sshkey_printfn *);
int sshkey_xmss_enable_maxsign(struct sshkey *, u_int32_t);
int sshkey_xmss_forward_state(const struct sshkey *, u_int32_t);
int sshkey_xmss_update_state(const struct sshkey *, sshkey_printfn *);
u_int32_t sshkey_xmss_signatures_left(const struct sshkey *);
#endif /* SSHKEY_XMSS_H */

410
sshkey.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshkey.c,v 1.61 2018/02/14 16:03:32 jsing Exp $ */
/* $OpenBSD: sshkey.c,v 1.62 2018/02/23 15:58:38 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;
}
@ -461,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:
@ -494,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:
@ -542,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:
@ -598,6 +619,20 @@ sshkey_free(struct sshkey *k)
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:
@ -677,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;
}
@ -696,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;
@ -720,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)
@ -764,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;
}
@ -773,32 +832,40 @@ 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);
return r;
}
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;
@ -810,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)
@ -831,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
@ -856,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;
@ -1173,6 +1241,10 @@ sshkey_read(struct sshkey *ret, char **cpp)
case KEY_ECDSA_CERT:
case KEY_RSA_CERT:
case KEY_ED25519_CERT:
#ifdef WITH_XMSS
case KEY_XMSS:
case KEY_XMSS_CERT:
#endif /* WITH_XMSS */
space = strchr(cp, ' ');
if (space == NULL)
return SSH_ERR_INVALID_FORMAT;
@ -1270,6 +1342,25 @@ sshkey_read(struct sshkey *ret, char **cpp)
/* XXX */
#endif
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 */
}
*cpp = ep;
retval = 0;
@ -1528,6 +1619,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);
@ -1671,6 +1767,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;
}
@ -1812,7 +1931,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;
@ -1963,6 +2082,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;
@ -1985,6 +2134,7 @@ 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);
@ -2079,6 +2229,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;
}
@ -2112,6 +2267,11 @@ sshkey_verify(const struct sshkey *key,
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;
}
@ -2135,6 +2295,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
@ -2196,6 +2358,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:
@ -2227,6 +2412,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;
}
@ -2311,6 +2501,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;
@ -2468,7 +2670,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;
@ -2554,6 +2757,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;
@ -2564,14 +2797,22 @@ sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b)
return r;
}
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 */
@ -2716,6 +2957,48 @@ 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_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;
#endif /* WITH_XMSS */
default:
r = SSH_ERR_KEY_TYPE_UNKNOWN;
goto out;
@ -2747,6 +3030,9 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp)
sshkey_free(k);
freezero(ed25519_pk, pklen);
freezero(ed25519_sk, sklen);
free(xmss_name);
freezero(xmss_pk, pklen);
freezero(xmss_sk, sklen);
return r;
}
@ -3001,7 +3287,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;
@ -3362,6 +3649,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:
@ -3545,6 +3835,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:
@ -3576,3 +3869,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 */

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshkey.h,v 1.23 2017/12/18 02:25:15 djm Exp $ */
/* $OpenBSD: sshkey.h,v 1.24 2018/02/23 15:58:38 markus Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@ -61,6 +61,8 @@ enum sshkey_types {
KEY_DSA_CERT,
KEY_ECDSA_CERT,
KEY_ED25519_CERT,
KEY_XMSS,
KEY_XMSS_CERT,
KEY_UNSPEC
};
@ -76,6 +78,14 @@ enum sshkey_fp_rep {
SSH_FP_RANDOMART
};
/* Private key serialisation formats, used on the wire */
enum sshkey_serialize_rep {
SSHKEY_SERIALIZE_DEFAULT = 0,
SSHKEY_SERIALIZE_STATE = 1,
SSHKEY_SERIALIZE_FULL = 2,
SSHKEY_SERIALIZE_INFO = 254,
};
/* key is stored in external hardware */
#define SSHKEY_FLAG_EXT 0x0001
@ -104,6 +114,11 @@ struct sshkey {
EC_KEY *ecdsa;
u_char *ed25519_sk;
u_char *ed25519_pk;
char *xmss_name;
char *xmss_filename; /* for state file updates */
void *xmss_state; /* depends on xmss_name, opaque */
u_char *xmss_sk;
u_char *xmss_pk;
struct sshkey_cert *cert;
};
@ -171,6 +186,8 @@ int sshkey_to_blob(const struct sshkey *, u_char **, size_t *);
int sshkey_to_base64(const struct sshkey *, char **);
int sshkey_putb(const struct sshkey *, struct sshbuf *);
int sshkey_puts(const struct sshkey *, struct sshbuf *);
int sshkey_puts_opts(const struct sshkey *, struct sshbuf *,
enum sshkey_serialize_rep);
int sshkey_plain_to_blob(const struct sshkey *, u_char **, size_t *);
int sshkey_putb_plain(const struct sshkey *, struct sshbuf *);
@ -186,6 +203,8 @@ void sshkey_dump_ec_key(const EC_KEY *);
/* private key parsing and serialisation */
int sshkey_private_serialize(const struct sshkey *key, struct sshbuf *buf);
int sshkey_private_serialize_opt(const struct sshkey *key, struct sshbuf *buf,
enum sshkey_serialize_rep);
int sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **keyp);
/* private key file format parsing and serialisation */
@ -200,6 +219,15 @@ int sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type,
/* XXX should be internal, but used by ssh-keygen */
int ssh_rsa_generate_additional_parameters(struct sshkey *);
/* stateful keys (e.g. XMSS) */
typedef void sshkey_printfn(const char *, ...) __attribute__((format(printf, 1, 2)));
int sshkey_set_filename(struct sshkey *, const char *);
int sshkey_enable_maxsign(struct sshkey *, u_int32_t);
u_int32_t sshkey_signatures_left(const struct sshkey *);
int sshkey_forward_state(const struct sshkey *, u_int32_t, sshkey_printfn *);
int sshkey_private_serialize_maxsign(const struct sshkey *key, struct sshbuf *buf,
u_int32_t maxsign, sshkey_printfn *pr);
#ifdef SSHKEY_INTERNAL
int ssh_rsa_sign(const struct sshkey *key,
u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
@ -222,6 +250,11 @@ int ssh_ed25519_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
int ssh_ed25519_verify(const struct sshkey *key,
const u_char *signature, size_t signaturelen,
const u_char *data, size_t datalen, u_int compat);
int ssh_xmss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
const u_char *data, size_t datalen, u_int compat);
int ssh_xmss_verify(const struct sshkey *key,
const u_char *signature, size_t signaturelen,
const u_char *data, size_t datalen, u_int compat);
#endif
#if !defined(WITH_OPENSSL)

27
xmss_commons.c Normal file
View File

@ -0,0 +1,27 @@
/*
xmss_commons.c 20160722
Andreas Hülsing
Joost Rijneveld
Public domain.
*/
#include "xmss_commons.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
void to_byte(unsigned char *out, unsigned long long in, uint32_t bytes)
{
int32_t i;
for (i = bytes-1; i >= 0; i--) {
out[i] = in & 0xff;
in = in >> 8;
}
}
void hexdump(const unsigned char *a, size_t len)
{
size_t i;
for (i = 0; i < len; i++)
printf("%02x", a[i]);
}

15
xmss_commons.h Normal file
View File

@ -0,0 +1,15 @@
/*
xmss_commons.h 20160722
Andreas Hülsing
Joost Rijneveld
Public domain.
*/
#ifndef XMSS_COMMONS_H
#define XMSS_COMMONS_H
#include <stdlib.h>
#include <stdint.h>
void to_byte(unsigned char *output, unsigned long long in, uint32_t bytes);
void hexdump(const unsigned char *a, size_t len);
#endif

1099
xmss_fast.c Normal file

File diff suppressed because it is too large Load Diff

109
xmss_fast.h Normal file
View File

@ -0,0 +1,109 @@
/*
xmss_fast.h version 20160722
Andreas Hülsing
Joost Rijneveld
Public domain.
*/
#include "xmss_wots.h"
#ifndef XMSS_H
#define XMSS_H
typedef struct{
unsigned int level;
unsigned long long subtree;
unsigned int subleaf;
} leafaddr;
typedef struct{
wots_params wots_par;
unsigned int n;
unsigned int h;
unsigned int k;
} xmss_params;
typedef struct{
xmss_params xmss_par;
unsigned int n;
unsigned int h;
unsigned int d;
unsigned int index_len;
} xmssmt_params;
typedef struct{
unsigned int h;
unsigned int next_idx;
unsigned int stackusage;
unsigned char completed;
unsigned char *node;
} treehash_inst;
typedef struct {
unsigned char *stack;
unsigned int stackoffset;
unsigned char *stacklevels;
unsigned char *auth;
unsigned char *keep;
treehash_inst *treehash;
unsigned char *retain;
unsigned int next_leaf;
} bds_state;
/**
* Initialize BDS state struct
* parameter names are the same as used in the description of the BDS traversal
*/
void xmss_set_bds_state(bds_state *state, unsigned char *stack, int stackoffset, unsigned char *stacklevels, unsigned char *auth, unsigned char *keep, treehash_inst *treehash, unsigned char *retain, int next_leaf);
/**
* Initializes parameter set.
* Needed, for any of the other methods.
*/
int xmss_set_params(xmss_params *params, int n, int h, int w, int k);
/**
* Initialize xmssmt_params struct
* parameter names are the same as in the draft
*
* Especially h is the total tree height, i.e. the XMSS trees have height h/d
*/
int xmssmt_set_params(xmssmt_params *params, int n, int h, int d, int w, int k);
/**
* Generates a XMSS key pair for a given parameter set.
* Format sk: [(32bit) idx || SK_SEED || SK_PRF || PUB_SEED || root]
* Format pk: [root || PUB_SEED] omitting algo oid.
*/
int xmss_keypair(unsigned char *pk, unsigned char *sk, bds_state *state, xmss_params *params);
/**
* Signs a message.
* Returns
* 1. an array containing the signature followed by the message AND
* 2. an updated secret key!
*
*/
int xmss_sign(unsigned char *sk, bds_state *state, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg,unsigned long long msglen, const xmss_params *params);
/**
* Verifies a given message signature pair under a given public key.
*
* Note: msg and msglen are pure outputs which carry the message in case verification succeeds. The (input) message is assumed to be within sig_msg which has the form (sig||msg).
*/
int xmss_sign_open(unsigned char *msg,unsigned long long *msglen, const unsigned char *sig_msg,unsigned long long sig_msg_len, const unsigned char *pk, const xmss_params *params);
/*
* Generates a XMSSMT key pair for a given parameter set.
* Format sk: [(ceil(h/8) bit) idx || SK_SEED || SK_PRF || PUB_SEED || root]
* Format pk: [root || PUB_SEED] omitting algo oid.
*/
int xmssmt_keypair(unsigned char *pk, unsigned char *sk, bds_state *states, unsigned char *wots_sigs, xmssmt_params *params);
/**
* Signs a message.
* Returns
* 1. an array containing the signature followed by the message AND
* 2. an updated secret key!
*
*/
int xmssmt_sign(unsigned char *sk, bds_state *state, unsigned char *wots_sigs, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen, const xmssmt_params *params);
/**
* Verifies a given message signature pair under a given public key.
*/
int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk, const xmssmt_params *params);
#endif

133
xmss_hash.c Normal file
View File

@ -0,0 +1,133 @@
/*
hash.c version 20160722
Andreas Hülsing
Joost Rijneveld
Public domain.
*/
#include "xmss_hash_address.h"
#include "xmss_commons.h"
#include "xmss_hash.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
int core_hash_SHA2(unsigned char *, const unsigned int, const unsigned char *,
unsigned int, const unsigned char *, unsigned long long, unsigned int);
unsigned char* addr_to_byte(unsigned char *bytes, const uint32_t addr[8]){
#if IS_LITTLE_ENDIAN==1
int i = 0;
for(i=0;i<8;i++)
to_byte(bytes+i*4, addr[i],4);
return bytes;
#else
memcpy(bytes, addr, 32);
return bytes;
#endif
}
int core_hash_SHA2(unsigned char *out, const unsigned int type, const unsigned char *key, unsigned int keylen, const unsigned char *in, unsigned long long inlen, unsigned int n){
unsigned long long i = 0;
unsigned char buf[inlen + n + keylen];
// Input is (toByte(X, 32) || KEY || M)
// set toByte
to_byte(buf, type, n);
for (i=0; i < keylen; i++) {
buf[i+n] = key[i];
}
for (i=0; i < inlen; i++) {
buf[keylen + n + i] = in[i];
}
if (n == 32) {
SHA256(buf, inlen + keylen + n, out);
return 0;
}
else {
if (n == 64) {
SHA512(buf, inlen + keylen + n, out);
return 0;
}
}
return 1;
}
/**
* Implements PRF
*/
int prf(unsigned char *out, const unsigned char *in, const unsigned char *key, unsigned int keylen)
{
return core_hash_SHA2(out, 3, key, keylen, in, 32, keylen);
}
/*
* Implemts H_msg
*/
int h_msg(unsigned char *out, const unsigned char *in, unsigned long long inlen, const unsigned char *key, const unsigned int keylen, const unsigned int n)
{
if (keylen != 3*n){
// H_msg takes 3n-bit keys, but n does not match the keylength of keylen
return -1;
}
return core_hash_SHA2(out, 2, key, keylen, in, inlen, n);
}
/**
* We assume the left half is in in[0]...in[n-1]
*/
int hash_h(unsigned char *out, const unsigned char *in, const unsigned char *pub_seed, uint32_t addr[8], const unsigned int n)
{
unsigned char buf[2*n];
unsigned char key[n];
unsigned char bitmask[2*n];
unsigned char byte_addr[32];
unsigned int i;
setKeyAndMask(addr, 0);
addr_to_byte(byte_addr, addr);
prf(key, byte_addr, pub_seed, n);
// Use MSB order
setKeyAndMask(addr, 1);
addr_to_byte(byte_addr, addr);
prf(bitmask, byte_addr, pub_seed, n);
setKeyAndMask(addr, 2);
addr_to_byte(byte_addr, addr);
prf(bitmask+n, byte_addr, pub_seed, n);
for (i = 0; i < 2*n; i++) {
buf[i] = in[i] ^ bitmask[i];
}
return core_hash_SHA2(out, 1, key, n, buf, 2*n, n);
}
int hash_f(unsigned char *out, const unsigned char *in, const unsigned char *pub_seed, uint32_t addr[8], const unsigned int n)
{
unsigned char buf[n];
unsigned char key[n];
unsigned char bitmask[n];
unsigned char byte_addr[32];
unsigned int i;
setKeyAndMask(addr, 0);
addr_to_byte(byte_addr, addr);
prf(key, byte_addr, pub_seed, n);
setKeyAndMask(addr, 1);
addr_to_byte(byte_addr, addr);
prf(bitmask, byte_addr, pub_seed, n);
for (i = 0; i < n; i++) {
buf[i] = in[i] ^ bitmask[i];
}
return core_hash_SHA2(out, 0, key, n, buf, n, n);
}

19
xmss_hash.h Normal file
View File

@ -0,0 +1,19 @@
/*
hash.h version 20160722
Andreas Hülsing
Joost Rijneveld
Public domain.
*/
#ifndef HASH_H
#define HASH_H
#define IS_LITTLE_ENDIAN 1
unsigned char* addr_to_byte(unsigned char *bytes, const uint32_t addr[8]);
int prf(unsigned char *out, const unsigned char *in, const unsigned char *key, unsigned int keylen);
int h_msg(unsigned char *out,const unsigned char *in,unsigned long long inlen, const unsigned char *key, const unsigned int keylen, const unsigned int n);
int hash_h(unsigned char *out, const unsigned char *in, const unsigned char *pub_seed, uint32_t addr[8], const unsigned int n);
int hash_f(unsigned char *out, const unsigned char *in, const unsigned char *pub_seed, uint32_t addr[8], const unsigned int n);
#endif

59
xmss_hash_address.c Normal file
View File

@ -0,0 +1,59 @@
/*
hash_address.c version 20160722
Andreas Hülsing
Joost Rijneveld
Public domain.
*/
#include <stdint.h>
#include "xmss_hash_address.h" /* prototypes */
void setLayerADRS(uint32_t adrs[8], uint32_t layer){
adrs[0] = layer;
}
void setTreeADRS(uint32_t adrs[8], uint64_t tree){
adrs[1] = (uint32_t) (tree >> 32);
adrs[2] = (uint32_t) tree;
}
void setType(uint32_t adrs[8], uint32_t type){
adrs[3] = type;
int i;
for(i = 4; i < 8; i++){
adrs[i] = 0;
}
}
void setKeyAndMask(uint32_t adrs[8], uint32_t keyAndMask){
adrs[7] = keyAndMask;
}
// OTS
void setOTSADRS(uint32_t adrs[8], uint32_t ots){
adrs[4] = ots;
}
void setChainADRS(uint32_t adrs[8], uint32_t chain){
adrs[5] = chain;
}
void setHashADRS(uint32_t adrs[8], uint32_t hash){
adrs[6] = hash;
}
// L-tree
void setLtreeADRS(uint32_t adrs[8], uint32_t ltree){
adrs[4] = ltree;
}
// Hash Tree & L-tree
void setTreeHeight(uint32_t adrs[8], uint32_t treeHeight){
adrs[5] = treeHeight;
}
void setTreeIndex(uint32_t adrs[8], uint32_t treeIndex){
adrs[6] = treeIndex;
}

37
xmss_hash_address.h Normal file
View File

@ -0,0 +1,37 @@
/*
hash_address.h version 20160722
Andreas Hülsing
Joost Rijneveld
Public domain.
*/
#include <stdint.h>
void setLayerADRS(uint32_t adrs[8], uint32_t layer);
void setTreeADRS(uint32_t adrs[8], uint64_t tree);
void setType(uint32_t adrs[8], uint32_t type);
void setKeyAndMask(uint32_t adrs[8], uint32_t keyAndMask);
// OTS
void setOTSADRS(uint32_t adrs[8], uint32_t ots);
void setChainADRS(uint32_t adrs[8], uint32_t chain);
void setHashADRS(uint32_t adrs[8], uint32_t hash);
// L-tree
void setLtreeADRS(uint32_t adrs[8], uint32_t ltree);
// Hash Tree & L-tree
void setTreeHeight(uint32_t adrs[8], uint32_t treeHeight);
void setTreeIndex(uint32_t adrs[8], uint32_t treeIndex);

185
xmss_wots.c Normal file
View File

@ -0,0 +1,185 @@
/*
wots.c version 20160722
Andreas Hülsing
Joost Rijneveld
Public domain.
*/
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>
#include "xmss_commons.h"
#include "xmss_hash.h"
#include "xmss_wots.h"
#include "xmss_hash_address.h"
/* libm-free version of log2() for wots */
static inline int
wots_log2(uint32_t v)
{
int b;
for (b = sizeof (v) * CHAR_BIT - 1; b >= 0; b--) {
if ((1U << b) & v) {
return b;
}
}
return 0;
}
void
wots_set_params(wots_params *params, int n, int w)
{
params->n = n;
params->w = w;
params->log_w = wots_log2(params->w);
params->len_1 = (CHAR_BIT * n) / params->log_w;
params->len_2 = (wots_log2(params->len_1 * (w - 1)) / params->log_w) + 1;
params->len = params->len_1 + params->len_2;
params->keysize = params->len * params->n;
}
/**
* Helper method for pseudorandom key generation
* Expands an n-byte array into a len*n byte array
* this is done using PRF
*/
static void expand_seed(unsigned char *outseeds, const unsigned char *inseed, const wots_params *params)
{
uint32_t i = 0;
unsigned char ctr[32];
for(i = 0; i < params->len; i++){
to_byte(ctr, i, 32);
prf((outseeds + (i*params->n)), ctr, inseed, params->n);
}
}
/**
* Computes the chaining function.
* out and in have to be n-byte arrays
*
* interpretes in as start-th value of the chain
* addr has to contain the address of the chain
*/
static void gen_chain(unsigned char *out, const unsigned char *in, unsigned int start, unsigned int steps, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8])
{
uint32_t i, j;
for (j = 0; j < params->n; j++)
out[j] = in[j];
for (i = start; i < (start+steps) && i < params->w; i++) {
setHashADRS(addr, i);
hash_f(out, out, pub_seed, addr, params->n);
}
}
/**
* base_w algorithm as described in draft.
*
*
*/
static void base_w(int *output, const int out_len, const unsigned char *input, const wots_params *params)
{
int in = 0;
int out = 0;
uint32_t total = 0;
int bits = 0;
int consumed = 0;
for (consumed = 0; consumed < out_len; consumed++) {
if (bits == 0) {
total = input[in];
in++;
bits += 8;
}
bits -= params->log_w;
output[out] = (total >> bits) & (params->w - 1);
out++;
}
}
void wots_pkgen(unsigned char *pk, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8])
{
uint32_t i;
expand_seed(pk, sk, params);
for (i=0; i < params->len; i++) {
setChainADRS(addr, i);
gen_chain(pk+i*params->n, pk+i*params->n, 0, params->w-1, params, pub_seed, addr);
}
}
int wots_sign(unsigned char *sig, const unsigned char *msg, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8])
{
//int basew[params->len];
int csum = 0;
uint32_t i = 0;
int *basew = calloc(params->len, sizeof(int));
if (basew == NULL)
return -1;
base_w(basew, params->len_1, msg, params);
for (i=0; i < params->len_1; i++) {
csum += params->w - 1 - basew[i];
}
csum = csum << (8 - ((params->len_2 * params->log_w) % 8));
int len_2_bytes = ((params->len_2 * params->log_w) + 7) / 8;
unsigned char csum_bytes[len_2_bytes];
to_byte(csum_bytes, csum, len_2_bytes);
int csum_basew[params->len_2];
base_w(csum_basew, params->len_2, csum_bytes, params);
for (i = 0; i < params->len_2; i++) {
basew[params->len_1 + i] = csum_basew[i];
}
expand_seed(sig, sk, params);
for (i = 0; i < params->len; i++) {
setChainADRS(addr, i);
gen_chain(sig+i*params->n, sig+i*params->n, 0, basew[i], params, pub_seed, addr);
}
free(basew);
return 0;
}
int wots_pkFromSig(unsigned char *pk, const unsigned char *sig, const unsigned char *msg, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8])
{
int csum = 0;
uint32_t i = 0;
int *basew = calloc(params->len, sizeof(int));
if (basew == NULL)
return -1;
base_w(basew, params->len_1, msg, params);
for (i=0; i < params->len_1; i++) {
csum += params->w - 1 - basew[i];
}
csum = csum << (8 - ((params->len_2 * params->log_w) % 8));
int len_2_bytes = ((params->len_2 * params->log_w) + 7) / 8;
unsigned char csum_bytes[len_2_bytes];
to_byte(csum_bytes, csum, len_2_bytes);
int csum_basew[params->len_2];
base_w(csum_basew, params->len_2, csum_bytes, params);
for (i = 0; i < params->len_2; i++) {
basew[params->len_1 + i] = csum_basew[i];
}
for (i=0; i < params->len; i++) {
setChainADRS(addr, i);
gen_chain(pk+i*params->n, sig+i*params->n, basew[i], params->w-1-basew[i], params, pub_seed, addr);
}
free(basew);
return 0;
}

59
xmss_wots.h Normal file
View File

@ -0,0 +1,59 @@
/*
wots.h version 20160722
Andreas Hülsing
Joost Rijneveld
Public domain.
*/
#ifndef WOTS_H
#define WOTS_H
#include "stdint.h"
/**
* WOTS parameter set
*
* Meaning as defined in draft-irtf-cfrg-xmss-hash-based-signatures-02
*/
typedef struct {
uint32_t len_1;
uint32_t len_2;
uint32_t len;
uint32_t n;
uint32_t w;
uint32_t log_w;
uint32_t keysize;
} wots_params;
/**
* Set the WOTS parameters,
* only m, n, w are required as inputs,
* len, len_1, and len_2 are computed from those.
*
* Assumes w is a power of 2
*/
void wots_set_params(wots_params *params, int n, int w);
/**
* WOTS key generation. Takes a 32byte seed for the secret key, expands it to a full WOTS secret key and computes the corresponding public key.
* For this it takes the seed pub_seed which is used to generate bitmasks and hash keys and the address of this WOTS key pair addr
*
* params, must have been initialized before using wots_set params for params ! This is not done in this function
*
* Places the computed public key at address pk.
*/
void wots_pkgen(unsigned char *pk, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]);
/**
* Takes a m-byte message and the 32-byte seed for the secret key to compute a signature that is placed at "sig".
*
*/
int wots_sign(unsigned char *sig, const unsigned char *msg, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]);
/**
* Takes a WOTS signature, a m-byte message and computes a WOTS public key that it places at pk.
*
*/
int wots_pkFromSig(unsigned char *pk, const unsigned char *sig, const unsigned char *msg, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]);
#endif