summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormarkus@openbsd.org <markus@openbsd.org>2018-02-23 15:58:37 +0000
committerDamien Miller <djm@mindrot.org>2018-02-26 11:40:41 +1100
commit1b11ea7c58cd5c59838b5fa574cd456d6047b2d4 (patch)
tree7e96cb41b5234b9d327f7c8f41392f09aed0994e
parent7d330a1ac02076de98cfc8fda05353d57b603755 (diff)
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
-rw-r--r--Makefile.in12
-rw-r--r--authfd.c39
-rw-r--r--authfd.h5
-rw-r--r--authfile.c8
-rw-r--r--cipher.c4
-rw-r--r--dns.c7
-rw-r--r--dns.h5
-rw-r--r--pathnames.h4
-rw-r--r--readconf.c3
-rw-r--r--servconf.c4
-rw-r--r--ssh-add.c74
-rw-r--r--ssh-agent.c24
-rw-r--r--ssh-keygen.c19
-rw-r--r--ssh-keyscan.c12
-rw-r--r--ssh-keysign.c5
-rw-r--r--ssh-xmss.c188
-rw-r--r--ssh.c15
-rw-r--r--sshconnect.c5
-rw-r--r--sshd.c6
-rw-r--r--sshkey-xmss.c1048
-rw-r--r--sshkey-xmss.h56
-rw-r--r--sshkey.c410
-rw-r--r--sshkey.h35
-rw-r--r--xmss_commons.c27
-rw-r--r--xmss_commons.h15
-rw-r--r--xmss_fast.c1099
-rw-r--r--xmss_fast.h109
-rw-r--r--xmss_hash.c133
-rw-r--r--xmss_hash.h19
-rw-r--r--xmss_hash_address.c59
-rw-r--r--xmss_hash_address.h37
-rw-r--r--xmss_wots.c185
-rw-r--r--xmss_wots.h59
33 files changed, 3657 insertions, 73 deletions
diff --git a/Makefile.in b/Makefile.in
index 2d26e83af..25d45eafb 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -62,6 +62,15 @@ MKDIR_P=@MKDIR_P@
62 62
63TARGETS=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) 63TARGETS=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)
64 64
65XMSS_OBJS=\
66 ssh-xmss.c \
67 sshkey-xmss.c \
68 xmss_commons.c \
69 xmss_fast.c \
70 xmss_hash.c \
71 xmss_hash_address.c \
72 xmss_wots.c
73
65LIBOPENSSH_OBJS=\ 74LIBOPENSSH_OBJS=\
66 ssh_api.o \ 75 ssh_api.o \
67 ssherr.o \ 76 ssherr.o \
@@ -71,7 +80,8 @@ LIBOPENSSH_OBJS=\
71 sshbuf-misc.o \ 80 sshbuf-misc.o \
72 sshbuf-getput-crypto.o \ 81 sshbuf-getput-crypto.o \
73 krl.o \ 82 krl.o \
74 bitmap.o 83 bitmap.o \
84 ${XMSS_OBJS}
75 85
76LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ 86LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
77 authfd.o authfile.o bufaux.o bufbn.o bufec.o buffer.o \ 87 authfd.o authfile.o bufaux.o bufbn.o bufec.o buffer.o \
diff --git a/authfd.c b/authfd.c
index 148bc9bfb..1eff7ba94 100644
--- a/authfd.c
+++ b/authfd.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: authfd.c,v 1.107 2018/02/10 09:25:34 djm Exp $ */ 1/* $OpenBSD: authfd.c,v 1.108 2018/02/23 15:58:37 markus Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * 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)
129 129
130 /* Get the length of the message, and format it in the buffer. */ 130 /* Get the length of the message, and format it in the buffer. */
131 len = sshbuf_len(request); 131 len = sshbuf_len(request);
132 put_u32(buf, len); 132 POKE_U32(buf, len);
133 133
134 /* Send the length and then the packet to the agent. */ 134 /* Send the length and then the packet to the agent. */
135 if (atomicio(vwrite, sock, buf, 4) != 4 || 135 if (atomicio(vwrite, sock, buf, 4) != 4 ||
@@ -144,7 +144,7 @@ ssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply)
144 return SSH_ERR_AGENT_COMMUNICATION; 144 return SSH_ERR_AGENT_COMMUNICATION;
145 145
146 /* Extract the length, and check it for sanity. */ 146 /* Extract the length, and check it for sanity. */
147 len = get_u32(buf); 147 len = PEEK_U32(buf);
148 if (len > MAX_AGENT_REPLY_LEN) 148 if (len > MAX_AGENT_REPLY_LEN)
149 return SSH_ERR_INVALID_FORMAT; 149 return SSH_ERR_INVALID_FORMAT;
150 150
@@ -391,19 +391,7 @@ ssh_agent_sign(int sock, const struct sshkey *key,
391 391
392 392
393static int 393static int
394ssh_encode_identity_ssh2(struct sshbuf *b, const struct sshkey *key, 394encode_constraints(struct sshbuf *m, u_int life, u_int confirm, u_int maxsign)
395 const char *comment)
396{
397 int r;
398
399 if ((r = sshkey_private_serialize(key, b)) != 0 ||
400 (r = sshbuf_put_cstring(b, comment)) != 0)
401 return r;
402 return 0;
403}
404
405static int
406encode_constraints(struct sshbuf *m, u_int life, u_int confirm)
407{ 395{
408 int r; 396 int r;
409 397
@@ -416,6 +404,11 @@ encode_constraints(struct sshbuf *m, u_int life, u_int confirm)
416 if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_CONFIRM)) != 0) 404 if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_CONFIRM)) != 0)
417 goto out; 405 goto out;
418 } 406 }
407 if (maxsign != 0) {
408 if ((r = sshbuf_put_u8(m, SSH_AGENT_CONSTRAIN_MAXSIGN)) != 0 ||
409 (r = sshbuf_put_u32(m, maxsign)) != 0)
410 goto out;
411 }
419 r = 0; 412 r = 0;
420 out: 413 out:
421 return r; 414 return r;
@@ -427,10 +420,10 @@ encode_constraints(struct sshbuf *m, u_int life, u_int confirm)
427 */ 420 */
428int 421int
429ssh_add_identity_constrained(int sock, const struct sshkey *key, 422ssh_add_identity_constrained(int sock, const struct sshkey *key,
430 const char *comment, u_int life, u_int confirm) 423 const char *comment, u_int life, u_int confirm, u_int maxsign)
431{ 424{
432 struct sshbuf *msg; 425 struct sshbuf *msg;
433 int r, constrained = (life || confirm); 426 int r, constrained = (life || confirm || maxsign);
434 u_char type; 427 u_char type;
435 428
436 if ((msg = sshbuf_new()) == NULL) 429 if ((msg = sshbuf_new()) == NULL)
@@ -447,11 +440,15 @@ ssh_add_identity_constrained(int sock, const struct sshkey *key,
447#endif 440#endif
448 case KEY_ED25519: 441 case KEY_ED25519:
449 case KEY_ED25519_CERT: 442 case KEY_ED25519_CERT:
443 case KEY_XMSS:
444 case KEY_XMSS_CERT:
450 type = constrained ? 445 type = constrained ?
451 SSH2_AGENTC_ADD_ID_CONSTRAINED : 446 SSH2_AGENTC_ADD_ID_CONSTRAINED :
452 SSH2_AGENTC_ADD_IDENTITY; 447 SSH2_AGENTC_ADD_IDENTITY;
453 if ((r = sshbuf_put_u8(msg, type)) != 0 || 448 if ((r = sshbuf_put_u8(msg, type)) != 0 ||
454 (r = ssh_encode_identity_ssh2(msg, key, comment)) != 0) 449 (r = sshkey_private_serialize_maxsign(key, msg, maxsign,
450 NULL)) != 0 ||
451 (r = sshbuf_put_cstring(msg, comment)) != 0)
455 goto out; 452 goto out;
456 break; 453 break;
457 default: 454 default:
@@ -459,7 +456,7 @@ ssh_add_identity_constrained(int sock, const struct sshkey *key,
459 goto out; 456 goto out;
460 } 457 }
461 if (constrained && 458 if (constrained &&
462 (r = encode_constraints(msg, life, confirm)) != 0) 459 (r = encode_constraints(msg, life, confirm, maxsign)) != 0)
463 goto out; 460 goto out;
464 if ((r = ssh_request_reply(sock, msg, msg)) != 0) 461 if ((r = ssh_request_reply(sock, msg, msg)) != 0)
465 goto out; 462 goto out;
@@ -537,7 +534,7 @@ ssh_update_card(int sock, int add, const char *reader_id, const char *pin,
537 (r = sshbuf_put_cstring(msg, pin)) != 0) 534 (r = sshbuf_put_cstring(msg, pin)) != 0)
538 goto out; 535 goto out;
539 if (constrained && 536 if (constrained &&
540 (r = encode_constraints(msg, life, confirm)) != 0) 537 (r = encode_constraints(msg, life, confirm, 0)) != 0)
541 goto out; 538 goto out;
542 if ((r = ssh_request_reply(sock, msg, msg)) != 0) 539 if ((r = ssh_request_reply(sock, msg, msg)) != 0)
543 goto out; 540 goto out;
diff --git a/authfd.h b/authfd.h
index 41997ce6d..ab954ffc0 100644
--- a/authfd.h
+++ b/authfd.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: authfd.h,v 1.42 2018/02/10 09:25:34 djm Exp $ */ 1/* $OpenBSD: authfd.h,v 1.43 2018/02/23 15:58:37 markus Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -30,7 +30,7 @@ int ssh_lock_agent(int sock, int lock, const char *password);
30int ssh_fetch_identitylist(int sock, struct ssh_identitylist **idlp); 30int ssh_fetch_identitylist(int sock, struct ssh_identitylist **idlp);
31void ssh_free_identitylist(struct ssh_identitylist *idl); 31void ssh_free_identitylist(struct ssh_identitylist *idl);
32int ssh_add_identity_constrained(int sock, const struct sshkey *key, 32int ssh_add_identity_constrained(int sock, const struct sshkey *key,
33 const char *comment, u_int life, u_int confirm); 33 const char *comment, u_int life, u_int confirm, u_int maxsign);
34int ssh_remove_identity(int sock, struct sshkey *key); 34int ssh_remove_identity(int sock, struct sshkey *key);
35int ssh_update_card(int sock, int add, const char *reader_id, 35int ssh_update_card(int sock, int add, const char *reader_id,
36 const char *pin, u_int life, u_int confirm); 36 const char *pin, u_int life, u_int confirm);
@@ -77,6 +77,7 @@ int ssh_agent_sign(int sock, const struct sshkey *key,
77 77
78#define SSH_AGENT_CONSTRAIN_LIFETIME 1 78#define SSH_AGENT_CONSTRAIN_LIFETIME 1
79#define SSH_AGENT_CONSTRAIN_CONFIRM 2 79#define SSH_AGENT_CONSTRAIN_CONFIRM 2
80#define SSH_AGENT_CONSTRAIN_MAXSIGN 3
80 81
81/* extended failure messages */ 82/* extended failure messages */
82#define SSH2_AGENT_FAILURE 30 83#define SSH2_AGENT_FAILURE 30
diff --git a/authfile.c b/authfile.c
index d09b700d2..57dcd808c 100644
--- a/authfile.c
+++ b/authfile.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: authfile.c,v 1.127 2017/07/01 13:50:45 djm Exp $ */ 1/* $OpenBSD: authfile.c,v 1.128 2018/02/23 15:58:37 markus Exp $ */
2/* 2/*
3 * Copyright (c) 2000, 2013 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000, 2013 Markus Friedl. All rights reserved.
4 * 4 *
@@ -191,6 +191,8 @@ sshkey_load_private_type(int type, const char *filename, const char *passphrase,
191 *perm_ok = 1; 191 *perm_ok = 1;
192 192
193 r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp); 193 r = sshkey_load_private_type_fd(fd, type, passphrase, keyp, commentp);
194 if (r == 0 && keyp && *keyp)
195 r = sshkey_set_filename(*keyp, filename);
194 out: 196 out:
195 close(fd); 197 close(fd);
196 return r; 198 return r;
@@ -249,6 +251,9 @@ sshkey_load_private(const char *filename, const char *passphrase,
249 (r = sshkey_parse_private_fileblob(buffer, passphrase, keyp, 251 (r = sshkey_parse_private_fileblob(buffer, passphrase, keyp,
250 commentp)) != 0) 252 commentp)) != 0)
251 goto out; 253 goto out;
254 if (keyp && *keyp &&
255 (r = sshkey_set_filename(*keyp, filename)) != 0)
256 goto out;
252 r = 0; 257 r = 0;
253 out: 258 out:
254 close(fd); 259 close(fd);
@@ -397,6 +402,7 @@ sshkey_load_private_cert(int type, const char *filename, const char *passphrase,
397 case KEY_ECDSA: 402 case KEY_ECDSA:
398#endif /* WITH_OPENSSL */ 403#endif /* WITH_OPENSSL */
399 case KEY_ED25519: 404 case KEY_ED25519:
405 case KEY_XMSS:
400 case KEY_UNSPEC: 406 case KEY_UNSPEC:
401 break; 407 break;
402 default: 408 default:
diff --git a/cipher.c b/cipher.c
index 9f4546759..578763616 100644
--- a/cipher.c
+++ b/cipher.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: cipher.c,v 1.110 2018/02/13 03:36:56 djm Exp $ */ 1/* $OpenBSD: cipher.c,v 1.111 2018/02/23 15:58:37 markus Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * 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,
401 cp, len); 401 cp, len);
402 if (len < 4) 402 if (len < 4)
403 return SSH_ERR_MESSAGE_INCOMPLETE; 403 return SSH_ERR_MESSAGE_INCOMPLETE;
404 *plenp = get_u32(cp); 404 *plenp = PEEK_U32(cp);
405 return 0; 405 return 0;
406} 406}
407 407
diff --git a/dns.c b/dns.c
index 6e1abb530..ff1a2c41c 100644
--- a/dns.c
+++ b/dns.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: dns.c,v 1.37 2017/09/14 04:32:21 djm Exp $ */ 1/* $OpenBSD: dns.c,v 1.38 2018/02/23 15:58:37 markus Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2003 Wesley Griffin. All rights reserved. 4 * Copyright (c) 2003 Wesley Griffin. All rights reserved.
@@ -105,6 +105,11 @@ dns_read_key(u_int8_t *algorithm, u_int8_t *digest_type,
105 if (!*digest_type) 105 if (!*digest_type)
106 *digest_type = SSHFP_HASH_SHA256; 106 *digest_type = SSHFP_HASH_SHA256;
107 break; 107 break;
108 case KEY_XMSS:
109 *algorithm = SSHFP_KEY_XMSS;
110 if (!*digest_type)
111 *digest_type = SSHFP_HASH_SHA256;
112 break;
108 default: 113 default:
109 *algorithm = SSHFP_KEY_RESERVED; /* 0 */ 114 *algorithm = SSHFP_KEY_RESERVED; /* 0 */
110 *digest_type = SSHFP_HASH_RESERVED; /* 0 */ 115 *digest_type = SSHFP_HASH_RESERVED; /* 0 */
diff --git a/dns.h b/dns.h
index 68443f7cb..91f3c632d 100644
--- a/dns.h
+++ b/dns.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: dns.h,v 1.17 2017/09/14 04:32:21 djm Exp $ */ 1/* $OpenBSD: dns.h,v 1.18 2018/02/23 15:58:37 markus Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2003 Wesley Griffin. All rights reserved. 4 * Copyright (c) 2003 Wesley Griffin. All rights reserved.
@@ -33,7 +33,8 @@ enum sshfp_types {
33 SSHFP_KEY_RSA = 1, 33 SSHFP_KEY_RSA = 1,
34 SSHFP_KEY_DSA = 2, 34 SSHFP_KEY_DSA = 2,
35 SSHFP_KEY_ECDSA = 3, 35 SSHFP_KEY_ECDSA = 3,
36 SSHFP_KEY_ED25519 = 4 36 SSHFP_KEY_ED25519 = 4,
37 SSHFP_KEY_XMSS = 5
37}; 38};
38 39
39enum sshfp_hashes { 40enum sshfp_hashes {
diff --git a/pathnames.h b/pathnames.h
index 1c221b01b..cb44caa4d 100644
--- a/pathnames.h
+++ b/pathnames.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: pathnames.h,v 1.27 2017/05/05 10:42:49 naddy Exp $ */ 1/* $OpenBSD: pathnames.h,v 1.28 2018/02/23 15:58:37 markus Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -39,6 +39,7 @@
39#define _PATH_HOST_DSA_KEY_FILE SSHDIR "/ssh_host_dsa_key" 39#define _PATH_HOST_DSA_KEY_FILE SSHDIR "/ssh_host_dsa_key"
40#define _PATH_HOST_ECDSA_KEY_FILE SSHDIR "/ssh_host_ecdsa_key" 40#define _PATH_HOST_ECDSA_KEY_FILE SSHDIR "/ssh_host_ecdsa_key"
41#define _PATH_HOST_ED25519_KEY_FILE SSHDIR "/ssh_host_ed25519_key" 41#define _PATH_HOST_ED25519_KEY_FILE SSHDIR "/ssh_host_ed25519_key"
42#define _PATH_HOST_XMSS_KEY_FILE SSHDIR "/ssh_host_xmss_key"
42#define _PATH_HOST_RSA_KEY_FILE SSHDIR "/ssh_host_rsa_key" 43#define _PATH_HOST_RSA_KEY_FILE SSHDIR "/ssh_host_rsa_key"
43#define _PATH_DH_MODULI SSHDIR "/moduli" 44#define _PATH_DH_MODULI SSHDIR "/moduli"
44 45
@@ -75,6 +76,7 @@
75#define _PATH_SSH_CLIENT_ID_ECDSA _PATH_SSH_USER_DIR "/id_ecdsa" 76#define _PATH_SSH_CLIENT_ID_ECDSA _PATH_SSH_USER_DIR "/id_ecdsa"
76#define _PATH_SSH_CLIENT_ID_RSA _PATH_SSH_USER_DIR "/id_rsa" 77#define _PATH_SSH_CLIENT_ID_RSA _PATH_SSH_USER_DIR "/id_rsa"
77#define _PATH_SSH_CLIENT_ID_ED25519 _PATH_SSH_USER_DIR "/id_ed25519" 78#define _PATH_SSH_CLIENT_ID_ED25519 _PATH_SSH_USER_DIR "/id_ed25519"
79#define _PATH_SSH_CLIENT_ID_XMSS _PATH_SSH_USER_DIR "/id_xmss"
78 80
79/* 81/*
80 * Configuration file in user's home directory. This file need not be 82 * Configuration file in user's home directory. This file need not be
diff --git a/readconf.c b/readconf.c
index 56bff850a..88051db57 100644
--- a/readconf.c
+++ b/readconf.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: readconf.c,v 1.282 2018/02/23 02:34:33 djm Exp $ */ 1/* $OpenBSD: readconf.c,v 1.283 2018/02/23 15:58:37 markus Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1943,6 +1943,7 @@ fill_default_options(Options * options)
1943#endif 1943#endif
1944 add_identity_file(options, "~/", 1944 add_identity_file(options, "~/",
1945 _PATH_SSH_CLIENT_ID_ED25519, 0); 1945 _PATH_SSH_CLIENT_ID_ED25519, 0);
1946 add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_XMSS, 0);
1946 } 1947 }
1947 if (options->escape_char == -1) 1948 if (options->escape_char == -1)
1948 options->escape_char = '~'; 1949 options->escape_char = '~';
diff --git a/servconf.c b/servconf.c
index bf8ad671d..c87a9a2b6 100644
--- a/servconf.c
+++ b/servconf.c
@@ -1,5 +1,5 @@
1 1
2/* $OpenBSD: servconf.c,v 1.324 2018/02/16 02:32:40 djm Exp $ */ 2/* $OpenBSD: servconf.c,v 1.325 2018/02/23 15:58:37 markus Exp $ */
3/* 3/*
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5 * All rights reserved 5 * All rights reserved
@@ -253,6 +253,8 @@ fill_default_server_options(ServerOptions *options)
253#endif 253#endif
254 servconf_add_hostkey("[default]", 0, options, 254 servconf_add_hostkey("[default]", 0, options,
255 _PATH_HOST_ED25519_KEY_FILE); 255 _PATH_HOST_ED25519_KEY_FILE);
256 servconf_add_hostkey("[default]", 0, options,
257 _PATH_HOST_XMSS_KEY_FILE);
256 } 258 }
257 /* No certificates by default */ 259 /* No certificates by default */
258 if (options->num_ports == 0) 260 if (options->num_ports == 0)
diff --git a/ssh-add.c b/ssh-add.c
index 2afd48330..adcc45998 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-add.c,v 1.134 2017/08/29 09:42:29 dlg Exp $ */ 1/* $OpenBSD: ssh-add.c,v 1.135 2018/02/23 15:58:37 markus Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -78,6 +78,7 @@ static char *default_files[] = {
78#endif 78#endif
79#endif /* WITH_OPENSSL */ 79#endif /* WITH_OPENSSL */
80 _PATH_SSH_CLIENT_ID_ED25519, 80 _PATH_SSH_CLIENT_ID_ED25519,
81 _PATH_SSH_CLIENT_ID_XMSS,
81 NULL 82 NULL
82}; 83};
83 84
@@ -89,6 +90,10 @@ static int lifetime = 0;
89/* User has to confirm key use */ 90/* User has to confirm key use */
90static int confirm = 0; 91static int confirm = 0;
91 92
93/* Maximum number of signatures (XMSS) */
94static u_int maxsign = 0;
95static u_int minleft = 0;
96
92/* we keep a cache of one passphrase */ 97/* we keep a cache of one passphrase */
93static char *pass = NULL; 98static char *pass = NULL;
94static void 99static void
@@ -190,7 +195,10 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag)
190 char *comment = NULL; 195 char *comment = NULL;
191 char msg[1024], *certpath = NULL; 196 char msg[1024], *certpath = NULL;
192 int r, fd, ret = -1; 197 int r, fd, ret = -1;
198 size_t i;
199 u_int32_t left;
193 struct sshbuf *keyblob; 200 struct sshbuf *keyblob;
201 struct ssh_identitylist *idlist;
194 202
195 if (strcmp(filename, "-") == 0) { 203 if (strcmp(filename, "-") == 0) {
196 fd = STDIN_FILENO; 204 fd = STDIN_FILENO;
@@ -268,8 +276,40 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag)
268 comment = xstrdup(filename); 276 comment = xstrdup(filename);
269 sshbuf_free(keyblob); 277 sshbuf_free(keyblob);
270 278
279 /* For XMSS */
280 if ((r = sshkey_set_filename(private, filename)) != 0) {
281 fprintf(stderr, "Could not add filename to private key: %s (%s)\n",
282 filename, comment);
283 goto out;
284 }
285 if (maxsign && minleft &&
286 (r = ssh_fetch_identitylist(agent_fd, &idlist)) == 0) {
287 for (i = 0; i < idlist->nkeys; i++) {
288 if (!sshkey_equal_public(idlist->keys[i], private))
289 continue;
290 left = sshkey_signatures_left(idlist->keys[i]);
291 if (left < minleft) {
292 fprintf(stderr,
293 "Only %d signatures left.\n", left);
294 break;
295 }
296 fprintf(stderr, "Skipping update: ");
297 if (left == minleft) {
298 fprintf(stderr,
299 "required signatures left (%d).\n", left);
300 } else {
301 fprintf(stderr,
302 "more signatures left (%d) than"
303 " required (%d).\n", left, minleft);
304 }
305 ssh_free_identitylist(idlist);
306 goto out;
307 }
308 ssh_free_identitylist(idlist);
309 }
310
271 if ((r = ssh_add_identity_constrained(agent_fd, private, comment, 311 if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
272 lifetime, confirm)) == 0) { 312 lifetime, confirm, maxsign)) == 0) {
273 fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); 313 fprintf(stderr, "Identity added: %s (%s)\n", filename, comment);
274 ret = 0; 314 ret = 0;
275 if (lifetime != 0) 315 if (lifetime != 0)
@@ -317,7 +357,7 @@ add_file(int agent_fd, const char *filename, int key_only, int qflag)
317 sshkey_free(cert); 357 sshkey_free(cert);
318 358
319 if ((r = ssh_add_identity_constrained(agent_fd, private, comment, 359 if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
320 lifetime, confirm)) != 0) { 360 lifetime, confirm, maxsign)) != 0) {
321 error("Certificate %s (%s) add failed: %s", certpath, 361 error("Certificate %s (%s) add failed: %s", certpath,
322 private->cert->key_id, ssh_err(r)); 362 private->cert->key_id, ssh_err(r));
323 goto out; 363 goto out;
@@ -368,6 +408,7 @@ list_identities(int agent_fd, int do_fp)
368 char *fp; 408 char *fp;
369 int r; 409 int r;
370 struct ssh_identitylist *idlist; 410 struct ssh_identitylist *idlist;
411 u_int32_t left;
371 size_t i; 412 size_t i;
372 413
373 if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) { 414 if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) {
@@ -392,7 +433,12 @@ list_identities(int agent_fd, int do_fp)
392 ssh_err(r)); 433 ssh_err(r));
393 continue; 434 continue;
394 } 435 }
395 fprintf(stdout, " %s\n", idlist->comments[i]); 436 fprintf(stdout, " %s", idlist->comments[i]);
437 left = sshkey_signatures_left(idlist->keys[i]);
438 if (left > 0)
439 fprintf(stdout,
440 " [signatures left %d]", left);
441 fprintf(stdout, "\n");
396 } 442 }
397 } 443 }
398 ssh_free_identitylist(idlist); 444 ssh_free_identitylist(idlist);
@@ -454,6 +500,8 @@ usage(void)
454 fprintf(stderr, " -L List public key parameters of all identities.\n"); 500 fprintf(stderr, " -L List public key parameters of all identities.\n");
455 fprintf(stderr, " -k Load only keys and not certificates.\n"); 501 fprintf(stderr, " -k Load only keys and not certificates.\n");
456 fprintf(stderr, " -c Require confirmation to sign using identities\n"); 502 fprintf(stderr, " -c Require confirmation to sign using identities\n");
503 fprintf(stderr, " -m minleft Maxsign is only changed if less than minleft are left (for XMSS)\n");
504 fprintf(stderr, " -M maxsign Maximum number of signatures allowed (for XMSS)\n");
457 fprintf(stderr, " -t life Set lifetime (in seconds) when adding identities.\n"); 505 fprintf(stderr, " -t life Set lifetime (in seconds) when adding identities.\n");
458 fprintf(stderr, " -d Delete identity.\n"); 506 fprintf(stderr, " -d Delete identity.\n");
459 fprintf(stderr, " -D Delete all identities.\n"); 507 fprintf(stderr, " -D Delete all identities.\n");
@@ -500,7 +548,7 @@ main(int argc, char **argv)
500 exit(2); 548 exit(2);
501 } 549 }
502 550
503 while ((ch = getopt(argc, argv, "klLcdDxXE:e:qs:t:")) != -1) { 551 while ((ch = getopt(argc, argv, "klLcdDxXE:e:M:m:qs:t:")) != -1) {
504 switch (ch) { 552 switch (ch) {
505 case 'E': 553 case 'E':
506 fingerprint_hash = ssh_digest_alg_by_name(optarg); 554 fingerprint_hash = ssh_digest_alg_by_name(optarg);
@@ -525,6 +573,22 @@ main(int argc, char **argv)
525 case 'c': 573 case 'c':
526 confirm = 1; 574 confirm = 1;
527 break; 575 break;
576 case 'm':
577 minleft = (int)strtonum(optarg, 1, UINT_MAX, NULL);
578 if (minleft == 0) {
579 usage();
580 ret = 1;
581 goto done;
582 }
583 break;
584 case 'M':
585 maxsign = (int)strtonum(optarg, 1, UINT_MAX, NULL);
586 if (maxsign == 0) {
587 usage();
588 ret = 1;
589 goto done;
590 }
591 break;
528 case 'd': 592 case 'd':
529 deleting = 1; 593 deleting = 1;
530 break; 594 break;
diff --git a/ssh-agent.c b/ssh-agent.c
index 39888a72c..2a4578b03 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-agent.c,v 1.227 2018/01/23 05:27:21 djm Exp $ */ 1/* $OpenBSD: ssh-agent.c,v 1.228 2018/02/23 15:58:37 markus Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -245,7 +245,8 @@ process_request_identities(SocketEntry *e)
245 (r = sshbuf_put_u32(msg, idtab->nentries)) != 0) 245 (r = sshbuf_put_u32(msg, idtab->nentries)) != 0)
246 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 246 fatal("%s: buffer error: %s", __func__, ssh_err(r));
247 TAILQ_FOREACH(id, &idtab->idlist, next) { 247 TAILQ_FOREACH(id, &idtab->idlist, next) {
248 if ((r = sshkey_puts(id->key, msg)) != 0 || 248 if ((r = sshkey_puts_opts(id->key, msg, SSHKEY_SERIALIZE_INFO))
249 != 0 ||
249 (r = sshbuf_put_cstring(msg, id->comment)) != 0) { 250 (r = sshbuf_put_cstring(msg, id->comment)) != 0) {
250 error("%s: put key/comment: %s", __func__, 251 error("%s: put key/comment: %s", __func__,
251 ssh_err(r)); 252 ssh_err(r));
@@ -402,7 +403,7 @@ process_add_identity(SocketEntry *e)
402{ 403{
403 Identity *id; 404 Identity *id;
404 int success = 0, confirm = 0; 405 int success = 0, confirm = 0;
405 u_int seconds; 406 u_int seconds, maxsign;
406 char *comment = NULL; 407 char *comment = NULL;
407 time_t death = 0; 408 time_t death = 0;
408 struct sshkey *k = NULL; 409 struct sshkey *k = NULL;
@@ -433,6 +434,18 @@ process_add_identity(SocketEntry *e)
433 case SSH_AGENT_CONSTRAIN_CONFIRM: 434 case SSH_AGENT_CONSTRAIN_CONFIRM:
434 confirm = 1; 435 confirm = 1;
435 break; 436 break;
437 case SSH_AGENT_CONSTRAIN_MAXSIGN:
438 if ((r = sshbuf_get_u32(e->request, &maxsign)) != 0) {
439 error("%s: bad maxsign constraint: %s",
440 __func__, ssh_err(r));
441 goto err;
442 }
443 if ((r = sshkey_enable_maxsign(k, maxsign)) != 0) {
444 error("%s: cannot enable maxsign: %s",
445 __func__, ssh_err(r));
446 goto err;
447 }
448 break;
436 default: 449 default:
437 error("%s: Unknown constraint %d", __func__, ctype); 450 error("%s: Unknown constraint %d", __func__, ctype);
438 err: 451 err:
@@ -448,14 +461,15 @@ process_add_identity(SocketEntry *e)
448 death = monotime() + lifetime; 461 death = monotime() + lifetime;
449 if ((id = lookup_identity(k)) == NULL) { 462 if ((id = lookup_identity(k)) == NULL) {
450 id = xcalloc(1, sizeof(Identity)); 463 id = xcalloc(1, sizeof(Identity));
451 id->key = k;
452 TAILQ_INSERT_TAIL(&idtab->idlist, id, next); 464 TAILQ_INSERT_TAIL(&idtab->idlist, id, next);
453 /* Increment the number of identities. */ 465 /* Increment the number of identities. */
454 idtab->nentries++; 466 idtab->nentries++;
455 } else { 467 } else {
456 sshkey_free(k); 468 /* key state might have been updated */
469 sshkey_free(id->key);
457 free(id->comment); 470 free(id->comment);
458 } 471 }
472 id->key = k;
459 id->comment = comment; 473 id->comment = comment;
460 id->death = death; 474 id->death = death;
461 id->confirm = confirm; 475 id->confirm = confirm;
diff --git a/ssh-keygen.c b/ssh-keygen.c
index 9812c0d2a..d80930eeb 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-keygen.c,v 1.312 2018/02/10 05:48:46 djm Exp $ */ 1/* $OpenBSD: ssh-keygen.c,v 1.313 2018/02/23 15:58:38 markus Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -275,6 +275,10 @@ ask_filename(struct passwd *pw, const char *prompt)
275 case KEY_ED25519_CERT: 275 case KEY_ED25519_CERT:
276 name = _PATH_SSH_CLIENT_ID_ED25519; 276 name = _PATH_SSH_CLIENT_ID_ED25519;
277 break; 277 break;
278 case KEY_XMSS:
279 case KEY_XMSS_CERT:
280 name = _PATH_SSH_CLIENT_ID_XMSS;
281 break;
278 default: 282 default:
279 fatal("bad key type"); 283 fatal("bad key type");
280 } 284 }
@@ -969,6 +973,9 @@ do_gen_all_hostkeys(struct passwd *pw)
969#endif /* OPENSSL_HAS_ECC */ 973#endif /* OPENSSL_HAS_ECC */
970#endif /* WITH_OPENSSL */ 974#endif /* WITH_OPENSSL */
971 { "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE }, 975 { "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE },
976#ifdef WITH_XMSS
977 { "xmss", "XMSS",_PATH_HOST_XMSS_KEY_FILE },
978#endif /* WITH_XMSS */
972 { NULL, NULL, NULL } 979 { NULL, NULL, NULL }
973 }; 980 };
974 981
@@ -1455,7 +1462,8 @@ do_change_comment(struct passwd *pw)
1455 } 1462 }
1456 } 1463 }
1457 1464
1458 if (private->type != KEY_ED25519 && !use_new_format) { 1465 if (private->type != KEY_ED25519 && private->type != KEY_XMSS &&
1466 !use_new_format) {
1459 error("Comments are only supported for keys stored in " 1467 error("Comments are only supported for keys stored in "
1460 "the new format (-o)."); 1468 "the new format (-o).");
1461 explicit_bzero(passphrase, strlen(passphrase)); 1469 explicit_bzero(passphrase, strlen(passphrase));
@@ -1705,7 +1713,8 @@ do_ca_sign(struct passwd *pw, int argc, char **argv)
1705 fatal("%s: unable to open \"%s\": %s", 1713 fatal("%s: unable to open \"%s\": %s",
1706 __func__, tmp, ssh_err(r)); 1714 __func__, tmp, ssh_err(r));
1707 if (public->type != KEY_RSA && public->type != KEY_DSA && 1715 if (public->type != KEY_RSA && public->type != KEY_DSA &&
1708 public->type != KEY_ECDSA && public->type != KEY_ED25519) 1716 public->type != KEY_ECDSA && public->type != KEY_ED25519 &&
1717 public->type != KEY_XMSS)
1709 fatal("%s: key \"%s\" type %s cannot be certified", 1718 fatal("%s: key \"%s\" type %s cannot be certified",
1710 __func__, tmp, sshkey_type(public)); 1719 __func__, tmp, sshkey_type(public));
1711 1720
@@ -2405,7 +2414,7 @@ main(int argc, char **argv)
2405 gen_all_hostkeys = 1; 2414 gen_all_hostkeys = 1;
2406 break; 2415 break;
2407 case 'b': 2416 case 'b':
2408 bits = (u_int32_t)strtonum(optarg, 256, 32768, &errstr); 2417 bits = (u_int32_t)strtonum(optarg, 10, 32768, &errstr);
2409 if (errstr) 2418 if (errstr)
2410 fatal("Bits has bad value %s (%s)", 2419 fatal("Bits has bad value %s (%s)",
2411 optarg, errstr); 2420 optarg, errstr);
@@ -2683,6 +2692,8 @@ main(int argc, char **argv)
2683 _PATH_HOST_ECDSA_KEY_FILE, rr_hostname); 2692 _PATH_HOST_ECDSA_KEY_FILE, rr_hostname);
2684 n += do_print_resource_record(pw, 2693 n += do_print_resource_record(pw,
2685 _PATH_HOST_ED25519_KEY_FILE, rr_hostname); 2694 _PATH_HOST_ED25519_KEY_FILE, rr_hostname);
2695 n += do_print_resource_record(pw,
2696 _PATH_HOST_XMSS_KEY_FILE, rr_hostname);
2686 if (n == 0) 2697 if (n == 0)
2687 fatal("no keys found."); 2698 fatal("no keys found.");
2688 exit(0); 2699 exit(0);
diff --git a/ssh-keyscan.c b/ssh-keyscan.c
index 15059f6fa..535368602 100644
--- a/ssh-keyscan.c
+++ b/ssh-keyscan.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-keyscan.c,v 1.117 2018/02/23 05:14:05 djm Exp $ */ 1/* $OpenBSD: ssh-keyscan.c,v 1.118 2018/02/23 15:58:38 markus Exp $ */
2/* 2/*
3 * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>. 3 * Copyright 1995, 1996 by David Mazieres <dm@lcs.mit.edu>.
4 * 4 *
@@ -58,9 +58,10 @@ int ssh_port = SSH_DEFAULT_PORT;
58#define KT_RSA (1<<1) 58#define KT_RSA (1<<1)
59#define KT_ECDSA (1<<2) 59#define KT_ECDSA (1<<2)
60#define KT_ED25519 (1<<3) 60#define KT_ED25519 (1<<3)
61#define KT_XMSS (1<<4)
61 62
62#define KT_MIN KT_DSA 63#define KT_MIN KT_DSA
63#define KT_MAX KT_ED25519 64#define KT_MAX KT_XMSS
64 65
65int get_cert = 0; 66int get_cert = 0;
66int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519; 67int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519;
@@ -238,6 +239,10 @@ keygrab_ssh2(con *c)
238 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 239 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
239 "ssh-ed25519-cert-v01@openssh.com" : "ssh-ed25519"; 240 "ssh-ed25519-cert-v01@openssh.com" : "ssh-ed25519";
240 break; 241 break;
242 case KT_XMSS:
243 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
244 "ssh-xmss-cert-v01@openssh.com" : "ssh-xmss@openssh.com";
245 break;
241 case KT_ECDSA: 246 case KT_ECDSA:
242 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? 247 myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
243 "ecdsa-sha2-nistp256-cert-v01@openssh.com," 248 "ecdsa-sha2-nistp256-cert-v01@openssh.com,"
@@ -718,6 +723,9 @@ main(int argc, char **argv)
718 case KEY_ED25519: 723 case KEY_ED25519:
719 get_keytypes |= KT_ED25519; 724 get_keytypes |= KT_ED25519;
720 break; 725 break;
726 case KEY_XMSS:
727 get_keytypes |= KT_XMSS;
728 break;
721 case KEY_UNSPEC: 729 case KEY_UNSPEC:
722 default: 730 default:
723 fatal("Unknown key type \"%s\"", tname); 731 fatal("Unknown key type \"%s\"", tname);
diff --git a/ssh-keysign.c b/ssh-keysign.c
index 17e87a281..78bb66b08 100644
--- a/ssh-keysign.c
+++ b/ssh-keysign.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh-keysign.c,v 1.53 2018/02/07 22:52:45 dtucker Exp $ */ 1/* $OpenBSD: ssh-keysign.c,v 1.54 2018/02/23 15:58:38 markus Exp $ */
2/* 2/*
3 * Copyright (c) 2002 Markus Friedl. All rights reserved. 3 * Copyright (c) 2002 Markus Friedl. All rights reserved.
4 * 4 *
@@ -171,7 +171,7 @@ main(int argc, char **argv)
171{ 171{
172 struct sshbuf *b; 172 struct sshbuf *b;
173 Options options; 173 Options options;
174#define NUM_KEYTYPES 4 174#define NUM_KEYTYPES 5
175 struct sshkey *keys[NUM_KEYTYPES], *key = NULL; 175 struct sshkey *keys[NUM_KEYTYPES], *key = NULL;
176 struct passwd *pw; 176 struct passwd *pw;
177 int r, key_fd[NUM_KEYTYPES], i, found, version = 2, fd; 177 int r, key_fd[NUM_KEYTYPES], i, found, version = 2, fd;
@@ -198,6 +198,7 @@ main(int argc, char **argv)
198 key_fd[i++] = open(_PATH_HOST_DSA_KEY_FILE, O_RDONLY); 198 key_fd[i++] = open(_PATH_HOST_DSA_KEY_FILE, O_RDONLY);
199 key_fd[i++] = open(_PATH_HOST_ECDSA_KEY_FILE, O_RDONLY); 199 key_fd[i++] = open(_PATH_HOST_ECDSA_KEY_FILE, O_RDONLY);
200 key_fd[i++] = open(_PATH_HOST_ED25519_KEY_FILE, O_RDONLY); 200 key_fd[i++] = open(_PATH_HOST_ED25519_KEY_FILE, O_RDONLY);
201 key_fd[i++] = open(_PATH_HOST_XMSS_KEY_FILE, O_RDONLY);
201 key_fd[i++] = open(_PATH_HOST_RSA_KEY_FILE, O_RDONLY); 202 key_fd[i++] = open(_PATH_HOST_RSA_KEY_FILE, O_RDONLY);
202 203
203 original_real_uid = getuid(); /* XXX readconf.c needs this */ 204 original_real_uid = getuid(); /* XXX readconf.c needs this */
diff --git a/ssh-xmss.c b/ssh-xmss.c
new file mode 100644
index 000000000..d9dafd762
--- /dev/null
+++ b/ssh-xmss.c
@@ -0,0 +1,188 @@
1/* $OpenBSD: ssh-xmss.c,v 1.1 2018/02/23 15:58:38 markus Exp $*/
2/*
3 * Copyright (c) 2017 Stefan-Lukas Gazdag.
4 * Copyright (c) 2017 Markus Friedl.
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18#define SSHKEY_INTERNAL
19#include <sys/types.h>
20#include <limits.h>
21
22#include <string.h>
23#include <stdarg.h>
24#include <unistd.h>
25
26#include "log.h"
27#include "sshbuf.h"
28#include "sshkey.h"
29#include "sshkey-xmss.h"
30#include "ssherr.h"
31#include "ssh.h"
32
33#include "xmss_fast.h"
34
35int
36ssh_xmss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
37 const u_char *data, size_t datalen, u_int compat)
38{
39 u_char *sig = NULL;
40 size_t slen = 0, len = 0, required_siglen;
41 unsigned long long smlen;
42 int r, ret;
43 struct sshbuf *b = NULL;
44
45 if (lenp != NULL)
46 *lenp = 0;
47 if (sigp != NULL)
48 *sigp = NULL;
49
50 if (key == NULL ||
51 sshkey_type_plain(key->type) != KEY_XMSS ||
52 key->xmss_sk == NULL ||
53 sshkey_xmss_params(key) == NULL)
54 return SSH_ERR_INVALID_ARGUMENT;
55 if ((r = sshkey_xmss_siglen(key, &required_siglen)) != 0)
56 return r;
57 if (datalen >= INT_MAX - required_siglen)
58 return SSH_ERR_INVALID_ARGUMENT;
59 smlen = slen = datalen + required_siglen;
60 if ((sig = malloc(slen)) == NULL)
61 return SSH_ERR_ALLOC_FAIL;
62 if ((r = sshkey_xmss_get_state(key, error)) != 0)
63 goto out;
64 if ((ret = xmss_sign(key->xmss_sk, sshkey_xmss_bds_state(key), sig, &smlen,
65 data, datalen, sshkey_xmss_params(key))) != 0 || smlen <= datalen) {
66 r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */
67 goto out;
68 }
69 /* encode signature */
70 if ((b = sshbuf_new()) == NULL) {
71 r = SSH_ERR_ALLOC_FAIL;
72 goto out;
73 }
74 if ((r = sshbuf_put_cstring(b, "ssh-xmss@openssh.com")) != 0 ||
75 (r = sshbuf_put_string(b, sig, smlen - datalen)) != 0)
76 goto out;
77 len = sshbuf_len(b);
78 if (sigp != NULL) {
79 if ((*sigp = malloc(len)) == NULL) {
80 r = SSH_ERR_ALLOC_FAIL;
81 goto out;
82 }
83 memcpy(*sigp, sshbuf_ptr(b), len);
84 }
85 if (lenp != NULL)
86 *lenp = len;
87 /* success */
88 r = 0;
89 out:
90 if ((ret = sshkey_xmss_update_state(key, error)) != 0) {
91 /* discard signature since we cannot update the state */
92 if (r == 0 && sigp != NULL && *sigp != NULL) {
93 explicit_bzero(*sigp, len);
94 free(*sigp);
95 }
96 if (sigp != NULL)
97 *sigp = NULL;
98 if (lenp != NULL)
99 *lenp = 0;
100 r = ret;
101 }
102 sshbuf_free(b);
103 if (sig != NULL) {
104 explicit_bzero(sig, slen);
105 free(sig);
106 }
107
108 return r;
109}
110
111int
112ssh_xmss_verify(const struct sshkey *key,
113 const u_char *signature, size_t signaturelen,
114 const u_char *data, size_t datalen, u_int compat)
115{
116 struct sshbuf *b = NULL;
117 char *ktype = NULL;
118 const u_char *sigblob;
119 u_char *sm = NULL, *m = NULL;
120 size_t len, required_siglen;
121 unsigned long long smlen = 0, mlen = 0;
122 int r, ret;
123
124 if (key == NULL ||
125 sshkey_type_plain(key->type) != KEY_XMSS ||
126 key->xmss_pk == NULL ||
127 sshkey_xmss_params(key) == NULL ||
128 signature == NULL || signaturelen == 0)
129 return SSH_ERR_INVALID_ARGUMENT;
130 if ((r = sshkey_xmss_siglen(key, &required_siglen)) != 0)
131 return r;
132 if (datalen >= INT_MAX - required_siglen)
133 return SSH_ERR_INVALID_ARGUMENT;
134
135 if ((b = sshbuf_from(signature, signaturelen)) == NULL)
136 return SSH_ERR_ALLOC_FAIL;
137 if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 ||
138 (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0)
139 goto out;
140 if (strcmp("ssh-xmss@openssh.com", ktype) != 0) {
141 r = SSH_ERR_KEY_TYPE_MISMATCH;
142 goto out;
143 }
144 if (sshbuf_len(b) != 0) {
145 r = SSH_ERR_UNEXPECTED_TRAILING_DATA;
146 goto out;
147 }
148 if (len != required_siglen) {
149 r = SSH_ERR_INVALID_FORMAT;
150 goto out;
151 }
152 if (datalen >= SIZE_MAX - len) {
153 r = SSH_ERR_INVALID_ARGUMENT;
154 goto out;
155 }
156 smlen = len + datalen;
157 mlen = smlen;
158 if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) {
159 r = SSH_ERR_ALLOC_FAIL;
160 goto out;
161 }
162 memcpy(sm, sigblob, len);
163 memcpy(sm+len, data, datalen);
164 if ((ret = xmss_sign_open(m, &mlen, sm, smlen,
165 key->xmss_pk, sshkey_xmss_params(key))) != 0) {
166 debug2("%s: crypto_sign_xmss_open failed: %d",
167 __func__, ret);
168 }
169 if (ret != 0 || mlen != datalen) {
170 r = SSH_ERR_SIGNATURE_INVALID;
171 goto out;
172 }
173 /* XXX compare 'm' and 'data' ? */
174 /* success */
175 r = 0;
176 out:
177 if (sm != NULL) {
178 explicit_bzero(sm, smlen);
179 free(sm);
180 }
181 if (m != NULL) {
182 explicit_bzero(m, smlen); /* NB mlen may be invalid if r != 0 */
183 free(m);
184 }
185 sshbuf_free(b);
186 free(ktype);
187 return r;
188}
diff --git a/ssh.c b/ssh.c
index fa57290be..d3619fe29 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: ssh.c,v 1.474 2018/02/23 02:34:33 djm Exp $ */ 1/* $OpenBSD: ssh.c,v 1.475 2018/02/23 15:58:38 markus Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1384,7 +1384,7 @@ main(int ac, char **av)
1384 sensitive_data.keys = NULL; 1384 sensitive_data.keys = NULL;
1385 sensitive_data.external_keysign = 0; 1385 sensitive_data.external_keysign = 0;
1386 if (options.hostbased_authentication) { 1386 if (options.hostbased_authentication) {
1387 sensitive_data.nkeys = 9; 1387 sensitive_data.nkeys = 11;
1388 sensitive_data.keys = xcalloc(sensitive_data.nkeys, 1388 sensitive_data.keys = xcalloc(sensitive_data.nkeys,
1389 sizeof(struct sshkey)); /* XXX */ 1389 sizeof(struct sshkey)); /* XXX */
1390 for (i = 0; i < sensitive_data.nkeys; i++) 1390 for (i = 0; i < sensitive_data.nkeys; i++)
@@ -1411,6 +1411,10 @@ main(int ac, char **av)
1411 _PATH_HOST_RSA_KEY_FILE, "", NULL, NULL); 1411 _PATH_HOST_RSA_KEY_FILE, "", NULL, NULL);
1412 sensitive_data.keys[8] = key_load_private_type(KEY_DSA, 1412 sensitive_data.keys[8] = key_load_private_type(KEY_DSA,
1413 _PATH_HOST_DSA_KEY_FILE, "", NULL, NULL); 1413 _PATH_HOST_DSA_KEY_FILE, "", NULL, NULL);
1414 sensitive_data.keys[9] = key_load_private_cert(KEY_XMSS,
1415 _PATH_HOST_XMSS_KEY_FILE, "", NULL);
1416 sensitive_data.keys[10] = key_load_private_type(KEY_XMSS,
1417 _PATH_HOST_XMSS_KEY_FILE, "", NULL, NULL);
1414 PRIV_END; 1418 PRIV_END;
1415 1419
1416 if (options.hostbased_authentication == 1 && 1420 if (options.hostbased_authentication == 1 &&
@@ -1418,7 +1422,8 @@ main(int ac, char **av)
1418 sensitive_data.keys[5] == NULL && 1422 sensitive_data.keys[5] == NULL &&
1419 sensitive_data.keys[6] == NULL && 1423 sensitive_data.keys[6] == NULL &&
1420 sensitive_data.keys[7] == NULL && 1424 sensitive_data.keys[7] == NULL &&
1421 sensitive_data.keys[8] == NULL) { 1425 sensitive_data.keys[8] == NULL &&
1426 sensitive_data.keys[9] == NULL) {
1422#ifdef OPENSSL_HAS_ECC 1427#ifdef OPENSSL_HAS_ECC
1423 sensitive_data.keys[1] = key_load_cert( 1428 sensitive_data.keys[1] = key_load_cert(
1424 _PATH_HOST_ECDSA_KEY_FILE); 1429 _PATH_HOST_ECDSA_KEY_FILE);
@@ -1439,6 +1444,10 @@ main(int ac, char **av)
1439 _PATH_HOST_RSA_KEY_FILE, NULL); 1444 _PATH_HOST_RSA_KEY_FILE, NULL);
1440 sensitive_data.keys[8] = key_load_public( 1445 sensitive_data.keys[8] = key_load_public(
1441 _PATH_HOST_DSA_KEY_FILE, NULL); 1446 _PATH_HOST_DSA_KEY_FILE, NULL);
1447 sensitive_data.keys[9] = key_load_cert(
1448 _PATH_HOST_XMSS_KEY_FILE);
1449 sensitive_data.keys[10] = key_load_public(
1450 _PATH_HOST_XMSS_KEY_FILE, NULL);
1442 sensitive_data.external_keysign = 1; 1451 sensitive_data.external_keysign = 1;
1443 } 1452 }
1444 } 1453 }
diff --git a/sshconnect.c b/sshconnect.c
index 07eae09f6..3805d35d9 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sshconnect.c,v 1.296 2018/02/23 04:18:46 dtucker Exp $ */ 1/* $OpenBSD: sshconnect.c,v 1.297 2018/02/23 15:58:38 markus Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1475,6 +1475,7 @@ show_other_keys(struct hostkeys *hostkeys, struct sshkey *key)
1475 KEY_DSA, 1475 KEY_DSA,
1476 KEY_ECDSA, 1476 KEY_ECDSA,
1477 KEY_ED25519, 1477 KEY_ED25519,
1478 KEY_XMSS,
1478 -1 1479 -1
1479 }; 1480 };
1480 int i, ret = 0; 1481 int i, ret = 0;
@@ -1592,7 +1593,7 @@ maybe_add_key_to_agent(char *authfile, const struct sshkey *private,
1592 } 1593 }
1593 1594
1594 if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0, 1595 if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0,
1595 (options.add_keys_to_agent == 3))) == 0) 1596 (options.add_keys_to_agent == 3), 0)) == 0)
1596 debug("identity added to agent: %s", authfile); 1597 debug("identity added to agent: %s", authfile);
1597 else 1598 else
1598 debug("could not add identity to agent: %s (%d)", authfile, r); 1599 debug("could not add identity to agent: %s (%d)", authfile, r);
diff --git a/sshd.c b/sshd.c
index 7466d5a44..0b9a7ec46 100644
--- a/sshd.c
+++ b/sshd.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sshd.c,v 1.504 2018/02/11 21:16:56 dtucker Exp $ */ 1/* $OpenBSD: sshd.c,v 1.505 2018/02/23 15:58:38 markus Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -705,6 +705,7 @@ list_hostkey_types(void)
705 case KEY_DSA: 705 case KEY_DSA:
706 case KEY_ECDSA: 706 case KEY_ECDSA:
707 case KEY_ED25519: 707 case KEY_ED25519:
708 case KEY_XMSS:
708 if (buffer_len(&b) > 0) 709 if (buffer_len(&b) > 0)
709 buffer_append(&b, ",", 1); 710 buffer_append(&b, ",", 1);
710 p = key_ssh_name(key); 711 p = key_ssh_name(key);
@@ -726,6 +727,7 @@ list_hostkey_types(void)
726 case KEY_DSA_CERT: 727 case KEY_DSA_CERT:
727 case KEY_ECDSA_CERT: 728 case KEY_ECDSA_CERT:
728 case KEY_ED25519_CERT: 729 case KEY_ED25519_CERT:
730 case KEY_XMSS_CERT:
729 if (buffer_len(&b) > 0) 731 if (buffer_len(&b) > 0)
730 buffer_append(&b, ",", 1); 732 buffer_append(&b, ",", 1);
731 p = key_ssh_name(key); 733 p = key_ssh_name(key);
@@ -752,6 +754,7 @@ get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh)
752 case KEY_DSA_CERT: 754 case KEY_DSA_CERT:
753 case KEY_ECDSA_CERT: 755 case KEY_ECDSA_CERT:
754 case KEY_ED25519_CERT: 756 case KEY_ED25519_CERT:
757 case KEY_XMSS_CERT:
755 key = sensitive_data.host_certificates[i]; 758 key = sensitive_data.host_certificates[i];
756 break; 759 break;
757 default: 760 default:
@@ -1734,6 +1737,7 @@ main(int ac, char **av)
1734 case KEY_DSA: 1737 case KEY_DSA:
1735 case KEY_ECDSA: 1738 case KEY_ECDSA:
1736 case KEY_ED25519: 1739 case KEY_ED25519:
1740 case KEY_XMSS:
1737 if (have_agent || key != NULL) 1741 if (have_agent || key != NULL)
1738 sensitive_data.have_ssh2_key = 1; 1742 sensitive_data.have_ssh2_key = 1;
1739 break; 1743 break;
diff --git a/sshkey-xmss.c b/sshkey-xmss.c
new file mode 100644
index 000000000..41cc1bade
--- /dev/null
+++ b/sshkey-xmss.c
@@ -0,0 +1,1048 @@
1/* $OpenBSD: sshkey-xmss.c,v 1.1 2018/02/23 15:58:38 markus Exp $ */
2/*
3 * Copyright (c) 2017 Markus Friedl. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include <sys/types.h>
27#include <sys/uio.h>
28
29#include <stdio.h>
30#include <string.h>
31#include <unistd.h>
32#include <fcntl.h>
33#include <errno.h>
34
35#include "ssh2.h"
36#include "ssherr.h"
37#include "sshbuf.h"
38#include "cipher.h"
39#include "sshkey.h"
40#include "sshkey-xmss.h"
41#include "atomicio.h"
42
43#include "xmss_fast.h"
44
45/* opaque internal XMSS state */
46#define XMSS_MAGIC "xmss-state-v1"
47#define XMSS_CIPHERNAME "aes256-gcm@openssh.com"
48struct ssh_xmss_state {
49 xmss_params params;
50 u_int32_t n, w, h, k;
51
52 bds_state bds;
53 u_char *stack;
54 u_int32_t stackoffset;
55 u_char *stacklevels;
56 u_char *auth;
57 u_char *keep;
58 u_char *th_nodes;
59 u_char *retain;
60 treehash_inst *treehash;
61
62 u_int32_t idx; /* state read from file */
63 u_int32_t maxidx; /* resticted # of signatures */
64 int have_state; /* .state file exists */
65 int lockfd; /* locked in sshkey_xmss_get_state() */
66 int allow_update; /* allow sshkey_xmss_update_state() */
67 char *enc_ciphername;/* encrypt state with cipher */
68 u_char *enc_keyiv; /* encrypt state with key */
69 u_int32_t enc_keyiv_len; /* length of enc_keyiv */
70};
71
72int sshkey_xmss_init_bds_state(struct sshkey *);
73int sshkey_xmss_init_enc_key(struct sshkey *, const char *);
74void sshkey_xmss_free_bds(struct sshkey *);
75int sshkey_xmss_get_state_from_file(struct sshkey *, const char *,
76 int *, sshkey_printfn *);
77int sshkey_xmss_encrypt_state(const struct sshkey *, struct sshbuf *,
78 struct sshbuf **);
79int sshkey_xmss_decrypt_state(const struct sshkey *, struct sshbuf *,
80 struct sshbuf **);
81int sshkey_xmss_serialize_enc_key(const struct sshkey *, struct sshbuf *);
82int sshkey_xmss_deserialize_enc_key(struct sshkey *, struct sshbuf *);
83
84#define PRINT(s...) do { if (pr) pr(s); } while (0)
85
86int
87sshkey_xmss_init(struct sshkey *key, const char *name)
88{
89 struct ssh_xmss_state *state;
90
91 if (key->xmss_state != NULL)
92 return SSH_ERR_INVALID_FORMAT;
93 if (name == NULL)
94 return SSH_ERR_INVALID_FORMAT;
95 state = calloc(sizeof(struct ssh_xmss_state), 1);
96 if (state == NULL)
97 return SSH_ERR_ALLOC_FAIL;
98 if (strcmp(name, XMSS_SHA2_256_W16_H10_NAME) == 0) {
99 state->n = 32;
100 state->w = 16;
101 state->h = 10;
102 } else if (strcmp(name, XMSS_SHA2_256_W16_H16_NAME) == 0) {
103 state->n = 32;
104 state->w = 16;
105 state->h = 16;
106 } else if (strcmp(name, XMSS_SHA2_256_W16_H20_NAME) == 0) {
107 state->n = 32;
108 state->w = 16;
109 state->h = 20;
110 } else {
111 free(state);
112 return SSH_ERR_KEY_TYPE_UNKNOWN;
113 }
114 if ((key->xmss_name = strdup(name)) == NULL) {
115 free(state);
116 return SSH_ERR_ALLOC_FAIL;
117 }
118 state->k = 2; /* XXX hardcoded */
119 state->lockfd = -1;
120 if (xmss_set_params(&state->params, state->n, state->h, state->w,
121 state->k) != 0) {
122 free(state);
123 return SSH_ERR_INVALID_FORMAT;
124 }
125 key->xmss_state = state;
126 return 0;
127}
128
129void
130sshkey_xmss_free_state(struct sshkey *key)
131{
132 struct ssh_xmss_state *state = key->xmss_state;
133
134 sshkey_xmss_free_bds(key);
135 if (state) {
136 if (state->enc_keyiv) {
137 explicit_bzero(state->enc_keyiv, state->enc_keyiv_len);
138 free(state->enc_keyiv);
139 }
140 free(state->enc_ciphername);
141 free(state);
142 }
143 key->xmss_state = NULL;
144}
145
146#define SSH_XMSS_K2_MAGIC "k=2"
147#define num_stack(x) ((x->h+1)*(x->n))
148#define num_stacklevels(x) (x->h+1)
149#define num_auth(x) ((x->h)*(x->n))
150#define num_keep(x) ((x->h >> 1)*(x->n))
151#define num_th_nodes(x) ((x->h - x->k)*(x->n))
152#define num_retain(x) (((1ULL << x->k) - x->k - 1) * (x->n))
153#define num_treehash(x) ((x->h) - (x->k))
154
155int
156sshkey_xmss_init_bds_state(struct sshkey *key)
157{
158 struct ssh_xmss_state *state = key->xmss_state;
159 u_int32_t i;
160
161 state->stackoffset = 0;
162 if ((state->stack = calloc(num_stack(state), 1)) == NULL ||
163 (state->stacklevels = calloc(num_stacklevels(state), 1))== NULL ||
164 (state->auth = calloc(num_auth(state), 1)) == NULL ||
165 (state->keep = calloc(num_keep(state), 1)) == NULL ||
166 (state->th_nodes = calloc(num_th_nodes(state), 1)) == NULL ||
167 (state->retain = calloc(num_retain(state), 1)) == NULL ||
168 (state->treehash = calloc(num_treehash(state),
169 sizeof(treehash_inst))) == NULL) {
170 sshkey_xmss_free_bds(key);
171 return SSH_ERR_ALLOC_FAIL;
172 }
173 for (i = 0; i < state->h - state->k; i++)
174 state->treehash[i].node = &state->th_nodes[state->n*i];
175 xmss_set_bds_state(&state->bds, state->stack, state->stackoffset,
176 state->stacklevels, state->auth, state->keep, state->treehash,
177 state->retain, 0);
178 return 0;
179}
180
181void
182sshkey_xmss_free_bds(struct sshkey *key)
183{
184 struct ssh_xmss_state *state = key->xmss_state;
185
186 if (state == NULL)
187 return;
188 free(state->stack);
189 free(state->stacklevels);
190 free(state->auth);
191 free(state->keep);
192 free(state->th_nodes);
193 free(state->retain);
194 free(state->treehash);
195 state->stack = NULL;
196 state->stacklevels = NULL;
197 state->auth = NULL;
198 state->keep = NULL;
199 state->th_nodes = NULL;
200 state->retain = NULL;
201 state->treehash = NULL;
202}
203
204void *
205sshkey_xmss_params(const struct sshkey *key)
206{
207 struct ssh_xmss_state *state = key->xmss_state;
208
209 if (state == NULL)
210 return NULL;
211 return &state->params;
212}
213
214void *
215sshkey_xmss_bds_state(const struct sshkey *key)
216{
217 struct ssh_xmss_state *state = key->xmss_state;
218
219 if (state == NULL)
220 return NULL;
221 return &state->bds;
222}
223
224int
225sshkey_xmss_siglen(const struct sshkey *key, size_t *lenp)
226{
227 struct ssh_xmss_state *state = key->xmss_state;
228
229 if (lenp == NULL)
230 return SSH_ERR_INVALID_ARGUMENT;
231 if (state == NULL)
232 return SSH_ERR_INVALID_FORMAT;
233 *lenp = 4 + state->n +
234 state->params.wots_par.keysize +
235 state->h * state->n;
236 return 0;
237}
238
239size_t
240sshkey_xmss_pklen(const struct sshkey *key)
241{
242 struct ssh_xmss_state *state = key->xmss_state;
243
244 if (state == NULL)
245 return 0;
246 return state->n * 2;
247}
248
249size_t
250sshkey_xmss_sklen(const struct sshkey *key)
251{
252 struct ssh_xmss_state *state = key->xmss_state;
253
254 if (state == NULL)
255 return 0;
256 return state->n * 4 + 4;
257}
258
259int
260sshkey_xmss_init_enc_key(struct sshkey *k, const char *ciphername)
261{
262 struct ssh_xmss_state *state = k->xmss_state;
263 const struct sshcipher *cipher;
264 size_t keylen = 0, ivlen = 0;
265
266 if (state == NULL)
267 return SSH_ERR_INVALID_ARGUMENT;
268 if ((cipher = cipher_by_name(ciphername)) == NULL)
269 return SSH_ERR_INTERNAL_ERROR;
270 if ((state->enc_ciphername = strdup(ciphername)) == NULL)
271 return SSH_ERR_ALLOC_FAIL;
272 keylen = cipher_keylen(cipher);
273 ivlen = cipher_ivlen(cipher);
274 state->enc_keyiv_len = keylen + ivlen;
275 if ((state->enc_keyiv = calloc(state->enc_keyiv_len, 1)) == NULL) {
276 free(state->enc_ciphername);
277 state->enc_ciphername = NULL;
278 return SSH_ERR_ALLOC_FAIL;
279 }
280 arc4random_buf(state->enc_keyiv, state->enc_keyiv_len);
281 return 0;
282}
283
284int
285sshkey_xmss_serialize_enc_key(const struct sshkey *k, struct sshbuf *b)
286{
287 struct ssh_xmss_state *state = k->xmss_state;
288 int r;
289
290 if (state == NULL || state->enc_keyiv == NULL ||
291 state->enc_ciphername == NULL)
292 return SSH_ERR_INVALID_ARGUMENT;
293 if ((r = sshbuf_put_cstring(b, state->enc_ciphername)) != 0 ||
294 (r = sshbuf_put_string(b, state->enc_keyiv,
295 state->enc_keyiv_len)) != 0)
296 return r;
297 return 0;
298}
299
300int
301sshkey_xmss_deserialize_enc_key(struct sshkey *k, struct sshbuf *b)
302{
303 struct ssh_xmss_state *state = k->xmss_state;
304 size_t len;
305 int r;
306
307 if (state == NULL)
308 return SSH_ERR_INVALID_ARGUMENT;
309 if ((r = sshbuf_get_cstring(b, &state->enc_ciphername, NULL)) != 0 ||
310 (r = sshbuf_get_string(b, &state->enc_keyiv, &len)) != 0)
311 return r;
312 state->enc_keyiv_len = len;
313 return 0;
314}
315
316int
317sshkey_xmss_serialize_pk_info(const struct sshkey *k, struct sshbuf *b,
318 enum sshkey_serialize_rep opts)
319{
320 struct ssh_xmss_state *state = k->xmss_state;
321 u_char have_info = 1;
322 u_int32_t idx;
323 int r;
324
325 if (state == NULL)
326 return SSH_ERR_INVALID_ARGUMENT;
327 if (opts != SSHKEY_SERIALIZE_INFO)
328 return 0;
329 idx = k->xmss_sk ? PEEK_U32(k->xmss_sk) : state->idx;
330 if ((r = sshbuf_put_u8(b, have_info)) != 0 ||
331 (r = sshbuf_put_u32(b, idx)) != 0 ||
332 (r = sshbuf_put_u32(b, state->maxidx)) != 0)
333 return r;
334 return 0;
335}
336
337int
338sshkey_xmss_deserialize_pk_info(struct sshkey *k, struct sshbuf *b)
339{
340 struct ssh_xmss_state *state = k->xmss_state;
341 u_char have_info;
342 int r;
343
344 if (state == NULL)
345 return SSH_ERR_INVALID_ARGUMENT;
346 /* optional */
347 if (sshbuf_len(b) == 0)
348 return 0;
349 if ((r = sshbuf_get_u8(b, &have_info)) != 0)
350 return r;
351 if (have_info != 1)
352 return SSH_ERR_INVALID_ARGUMENT;
353 if ((r = sshbuf_get_u32(b, &state->idx)) != 0 ||
354 (r = sshbuf_get_u32(b, &state->maxidx)) != 0)
355 return r;
356 return 0;
357}
358
359int
360sshkey_xmss_generate_private_key(struct sshkey *k, u_int bits)
361{
362 int r;
363 const char *name;
364
365 if (bits == 10) {
366 name = XMSS_SHA2_256_W16_H10_NAME;
367 } else if (bits == 16) {
368 name = XMSS_SHA2_256_W16_H16_NAME;
369 } else if (bits == 20) {
370 name = XMSS_SHA2_256_W16_H20_NAME;
371 } else {
372 name = XMSS_DEFAULT_NAME;
373 }
374 if ((r = sshkey_xmss_init(k, name)) != 0 ||
375 (r = sshkey_xmss_init_bds_state(k)) != 0 ||
376 (r = sshkey_xmss_init_enc_key(k, XMSS_CIPHERNAME)) != 0)
377 return r;
378 if ((k->xmss_pk = malloc(sshkey_xmss_pklen(k))) == NULL ||
379 (k->xmss_sk = malloc(sshkey_xmss_sklen(k))) == NULL) {
380 return SSH_ERR_ALLOC_FAIL;
381 }
382 xmss_keypair(k->xmss_pk, k->xmss_sk, sshkey_xmss_bds_state(k),
383 sshkey_xmss_params(k));
384 return 0;
385}
386
387int
388sshkey_xmss_get_state_from_file(struct sshkey *k, const char *filename,
389 int *have_file, sshkey_printfn *pr)
390{
391 struct sshbuf *b = NULL, *enc = NULL;
392 int ret = SSH_ERR_SYSTEM_ERROR, r, fd = -1;
393 u_int32_t len;
394 unsigned char buf[4], *data = NULL;
395
396 *have_file = 0;
397 if ((fd = open(filename, O_RDONLY)) >= 0) {
398 *have_file = 1;
399 if (atomicio(read, fd, buf, sizeof(buf)) != sizeof(buf)) {
400 PRINT("%s: corrupt state file: %s", __func__, filename);
401 goto done;
402 }
403 len = PEEK_U32(buf);
404 if ((data = calloc(len, 1)) == NULL) {
405 ret = SSH_ERR_ALLOC_FAIL;
406 goto done;
407 }
408 if (atomicio(read, fd, data, len) != len) {
409 PRINT("%s: cannot read blob: %s", __func__, filename);
410 goto done;
411 }
412 if ((enc = sshbuf_from(data, len)) == NULL) {
413 ret = SSH_ERR_ALLOC_FAIL;
414 goto done;
415 }
416 sshkey_xmss_free_bds(k);
417 if ((r = sshkey_xmss_decrypt_state(k, enc, &b)) != 0) {
418 ret = r;
419 goto done;
420 }
421 if ((r = sshkey_xmss_deserialize_state(k, b)) != 0) {
422 ret = r;
423 goto done;
424 }
425 ret = 0;
426 }
427done:
428 if (fd != -1)
429 close(fd);
430 free(data);
431 sshbuf_free(enc);
432 sshbuf_free(b);
433 return ret;
434}
435
436int
437sshkey_xmss_get_state(const struct sshkey *k, sshkey_printfn *pr)
438{
439 struct ssh_xmss_state *state = k->xmss_state;
440 u_int32_t idx = 0;
441 char *filename = NULL;
442 char *statefile = NULL, *ostatefile = NULL, *lockfile = NULL;
443 int lockfd = -1, have_state = 0, have_ostate, tries = 0;
444 int ret = SSH_ERR_INVALID_ARGUMENT, r;
445
446 if (state == NULL)
447 goto done;
448 /*
449 * If maxidx is set, then we are allowed a limited number
450 * of signatures, but don't need to access the disk.
451 * Otherwise we need to deal with the on-disk state.
452 */
453 if (state->maxidx) {
454 /* xmss_sk always contains the current state */
455 idx = PEEK_U32(k->xmss_sk);
456 if (idx < state->maxidx) {
457 state->allow_update = 1;
458 return 0;
459 }
460 return SSH_ERR_INVALID_ARGUMENT;
461 }
462 if ((filename = k->xmss_filename) == NULL)
463 goto done;
464 if (asprintf(&lockfile, "%s.lock", filename) < 0 ||
465 asprintf(&statefile, "%s.state", filename) < 0 ||
466 asprintf(&ostatefile, "%s.ostate", filename) < 0) {
467 ret = SSH_ERR_ALLOC_FAIL;
468 goto done;
469 }
470 if ((lockfd = open(lockfile, O_CREAT|O_RDONLY, 0600)) < 0) {
471 ret = SSH_ERR_SYSTEM_ERROR;
472 PRINT("%s: cannot open/create: %s", __func__, lockfile);
473 goto done;
474 }
475 while (flock(lockfd, LOCK_EX|LOCK_NB) < 0) {
476 if (errno != EWOULDBLOCK) {
477 ret = SSH_ERR_SYSTEM_ERROR;
478 PRINT("%s: cannot lock: %s", __func__, lockfile);
479 goto done;
480 }
481 if (++tries > 10) {
482 ret = SSH_ERR_SYSTEM_ERROR;
483 PRINT("%s: giving up on: %s", __func__, lockfile);
484 goto done;
485 }
486 usleep(1000*100*tries);
487 }
488 /* XXX no longer const */
489 if ((r = sshkey_xmss_get_state_from_file((struct sshkey *)k,
490 statefile, &have_state, pr)) != 0) {
491 if ((r = sshkey_xmss_get_state_from_file((struct sshkey *)k,
492 ostatefile, &have_ostate, pr)) == 0) {
493 state->allow_update = 1;
494 r = sshkey_xmss_forward_state(k, 1);
495 state->idx = PEEK_U32(k->xmss_sk);
496 state->allow_update = 0;
497 }
498 }
499 if (!have_state && !have_ostate) {
500 /* check that bds state is initialized */
501 if (state->bds.auth == NULL)
502 goto done;
503 PRINT("%s: start from scratch idx 0: %u", __func__, state->idx);
504 } else if (r != 0) {
505 ret = r;
506 goto done;
507 }
508 if (state->idx + 1 < state->idx) {
509 PRINT("%s: state wrap: %u", __func__, state->idx);
510 goto done;
511 }
512 state->have_state = have_state;
513 state->lockfd = lockfd;
514 state->allow_update = 1;
515 lockfd = -1;
516 ret = 0;
517done:
518 if (lockfd != -1)
519 close(lockfd);
520 free(lockfile);
521 free(statefile);
522 free(ostatefile);
523 return ret;
524}
525
526int
527sshkey_xmss_forward_state(const struct sshkey *k, u_int32_t reserve)
528{
529 struct ssh_xmss_state *state = k->xmss_state;
530 u_char *sig = NULL;
531 size_t required_siglen;
532 unsigned long long smlen;
533 u_char data;
534 int ret, r;
535
536 if (state == NULL || !state->allow_update)
537 return SSH_ERR_INVALID_ARGUMENT;
538 if (reserve == 0)
539 return SSH_ERR_INVALID_ARGUMENT;
540 if (state->idx + reserve <= state->idx)
541 return SSH_ERR_INVALID_ARGUMENT;
542 if ((r = sshkey_xmss_siglen(k, &required_siglen)) != 0)
543 return r;
544 if ((sig = malloc(required_siglen)) == NULL)
545 return SSH_ERR_ALLOC_FAIL;
546 while (reserve-- > 0) {
547 state->idx = PEEK_U32(k->xmss_sk);
548 smlen = required_siglen;
549 if ((ret = xmss_sign(k->xmss_sk, sshkey_xmss_bds_state(k),
550 sig, &smlen, &data, 0, sshkey_xmss_params(k))) != 0) {
551 r = SSH_ERR_INVALID_ARGUMENT;
552 break;
553 }
554 }
555 free(sig);
556 return r;
557}
558
559int
560sshkey_xmss_update_state(const struct sshkey *k, sshkey_printfn *pr)
561{
562 struct ssh_xmss_state *state = k->xmss_state;
563 struct sshbuf *b = NULL, *enc = NULL;
564 u_int32_t idx = 0;
565 unsigned char buf[4];
566 char *filename = NULL;
567 char *statefile = NULL, *ostatefile = NULL, *nstatefile = NULL;
568 int fd = -1;
569 int ret = SSH_ERR_INVALID_ARGUMENT;
570
571 if (state == NULL || !state->allow_update)
572 return ret;
573 if (state->maxidx) {
574 /* no update since the number of signatures is limited */
575 ret = 0;
576 goto done;
577 }
578 idx = PEEK_U32(k->xmss_sk);
579 if (idx == state->idx) {
580 /* no signature happend, no need to update */
581 ret = 0;
582 goto done;
583 } else if (idx != state->idx + 1) {
584 PRINT("%s: more than one signature happened: idx %u state %u",
585 __func__, idx, state->idx);
586 goto done;
587 }
588 state->idx = idx;
589 if ((filename = k->xmss_filename) == NULL)
590 goto done;
591 if (asprintf(&statefile, "%s.state", filename) < 0 ||
592 asprintf(&ostatefile, "%s.ostate", filename) < 0 ||
593 asprintf(&nstatefile, "%s.nstate", filename) < 0) {
594 ret = SSH_ERR_ALLOC_FAIL;
595 goto done;
596 }
597 unlink(nstatefile);
598 if ((b = sshbuf_new()) == NULL) {
599 ret = SSH_ERR_ALLOC_FAIL;
600 goto done;
601 }
602 if ((ret = sshkey_xmss_serialize_state(k, b)) != 0) {
603 PRINT("%s: SERLIALIZE FAILED: %d", __func__, ret);
604 goto done;
605 }
606 if ((ret = sshkey_xmss_encrypt_state(k, b, &enc)) != 0) {
607 PRINT("%s: ENCRYPT FAILED: %d", __func__, ret);
608 goto done;
609 }
610 if ((fd = open(nstatefile, O_CREAT|O_WRONLY|O_EXCL, 0600)) < 0) {
611 ret = SSH_ERR_SYSTEM_ERROR;
612 PRINT("%s: open new state file: %s", __func__, nstatefile);
613 goto done;
614 }
615 POKE_U32(buf, sshbuf_len(enc));
616 if (atomicio(vwrite, fd, buf, sizeof(buf)) != sizeof(buf)) {
617 ret = SSH_ERR_SYSTEM_ERROR;
618 PRINT("%s: write new state file hdr: %s", __func__, nstatefile);
619 close(fd);
620 goto done;
621 }
622 if (atomicio(vwrite, fd, (void *)sshbuf_ptr(enc), sshbuf_len(enc)) !=
623 sshbuf_len(enc)) {
624 ret = SSH_ERR_SYSTEM_ERROR;
625 PRINT("%s: write new state file data: %s", __func__, nstatefile);
626 close(fd);
627 goto done;
628 }
629 if (fsync(fd) < 0) {
630 ret = SSH_ERR_SYSTEM_ERROR;
631 PRINT("%s: sync new state file: %s", __func__, nstatefile);
632 close(fd);
633 goto done;
634 }
635 if (close(fd) < 0) {
636 ret = SSH_ERR_SYSTEM_ERROR;
637 PRINT("%s: close new state file: %s", __func__, nstatefile);
638 goto done;
639 }
640 if (state->have_state) {
641 unlink(ostatefile);
642 if (link(statefile, ostatefile)) {
643 ret = SSH_ERR_SYSTEM_ERROR;
644 PRINT("%s: backup state %s to %s", __func__, statefile,
645 ostatefile);
646 goto done;
647 }
648 }
649 if (rename(nstatefile, statefile) < 0) {
650 ret = SSH_ERR_SYSTEM_ERROR;
651 PRINT("%s: rename %s to %s", __func__, nstatefile, statefile);
652 goto done;
653 }
654 ret = 0;
655done:
656 if (state->lockfd != -1) {
657 close(state->lockfd);
658 state->lockfd = -1;
659 }
660 if (nstatefile)
661 unlink(nstatefile);
662 free(statefile);
663 free(ostatefile);
664 free(nstatefile);
665 sshbuf_free(b);
666 sshbuf_free(enc);
667 return ret;
668}
669
670int
671sshkey_xmss_serialize_state(const struct sshkey *k, struct sshbuf *b)
672{
673 struct ssh_xmss_state *state = k->xmss_state;
674 treehash_inst *th;
675 u_int32_t i, node;
676 int r;
677
678 if (state == NULL)
679 return SSH_ERR_INVALID_ARGUMENT;
680 if (state->stack == NULL)
681 return SSH_ERR_INVALID_ARGUMENT;
682 state->stackoffset = state->bds.stackoffset; /* copy back */
683 if ((r = sshbuf_put_cstring(b, SSH_XMSS_K2_MAGIC)) != 0 ||
684 (r = sshbuf_put_u32(b, state->idx)) != 0 ||
685 (r = sshbuf_put_string(b, state->stack, num_stack(state))) != 0 ||
686 (r = sshbuf_put_u32(b, state->stackoffset)) != 0 ||
687 (r = sshbuf_put_string(b, state->stacklevels, num_stacklevels(state))) != 0 ||
688 (r = sshbuf_put_string(b, state->auth, num_auth(state))) != 0 ||
689 (r = sshbuf_put_string(b, state->keep, num_keep(state))) != 0 ||
690 (r = sshbuf_put_string(b, state->th_nodes, num_th_nodes(state))) != 0 ||
691 (r = sshbuf_put_string(b, state->retain, num_retain(state))) != 0 ||
692 (r = sshbuf_put_u32(b, num_treehash(state))) != 0)
693 return r;
694 for (i = 0; i < num_treehash(state); i++) {
695 th = &state->treehash[i];
696 node = th->node - state->th_nodes;
697 if ((r = sshbuf_put_u32(b, th->h)) != 0 ||
698 (r = sshbuf_put_u32(b, th->next_idx)) != 0 ||
699 (r = sshbuf_put_u32(b, th->stackusage)) != 0 ||
700 (r = sshbuf_put_u8(b, th->completed)) != 0 ||
701 (r = sshbuf_put_u32(b, node)) != 0)
702 return r;
703 }
704 return 0;
705}
706
707int
708sshkey_xmss_serialize_state_opt(const struct sshkey *k, struct sshbuf *b,
709 enum sshkey_serialize_rep opts)
710{
711 struct ssh_xmss_state *state = k->xmss_state;
712 int r = SSH_ERR_INVALID_ARGUMENT;
713
714 if (state == NULL)
715 return SSH_ERR_INVALID_ARGUMENT;
716 if ((r = sshbuf_put_u8(b, opts)) != 0)
717 return r;
718 switch (opts) {
719 case SSHKEY_SERIALIZE_STATE:
720 r = sshkey_xmss_serialize_state(k, b);
721 break;
722 case SSHKEY_SERIALIZE_FULL:
723 if ((r = sshkey_xmss_serialize_enc_key(k, b)) != 0)
724 break;
725 r = sshkey_xmss_serialize_state(k, b);
726 break;
727 case SSHKEY_SERIALIZE_DEFAULT:
728 r = 0;
729 break;
730 default:
731 r = SSH_ERR_INVALID_ARGUMENT;
732 break;
733 }
734 return r;
735}
736
737int
738sshkey_xmss_deserialize_state(struct sshkey *k, struct sshbuf *b)
739{
740 struct ssh_xmss_state *state = k->xmss_state;
741 treehash_inst *th;
742 u_int32_t i, lh, node;
743 size_t ls, lsl, la, lk, ln, lr;
744 char *magic;
745 int r;
746
747 if (state == NULL)
748 return SSH_ERR_INVALID_ARGUMENT;
749 if (k->xmss_sk == NULL)
750 return SSH_ERR_INVALID_ARGUMENT;
751 if ((state->treehash = calloc(num_treehash(state),
752 sizeof(treehash_inst))) == NULL)
753 return SSH_ERR_ALLOC_FAIL;
754 if ((r = sshbuf_get_cstring(b, &magic, NULL)) != 0 ||
755 (r = sshbuf_get_u32(b, &state->idx)) != 0 ||
756 (r = sshbuf_get_string(b, &state->stack, &ls)) != 0 ||
757 (r = sshbuf_get_u32(b, &state->stackoffset)) != 0 ||
758 (r = sshbuf_get_string(b, &state->stacklevels, &lsl)) != 0 ||
759 (r = sshbuf_get_string(b, &state->auth, &la)) != 0 ||
760 (r = sshbuf_get_string(b, &state->keep, &lk)) != 0 ||
761 (r = sshbuf_get_string(b, &state->th_nodes, &ln)) != 0 ||
762 (r = sshbuf_get_string(b, &state->retain, &lr)) != 0 ||
763 (r = sshbuf_get_u32(b, &lh)) != 0)
764 return r;
765 if (strcmp(magic, SSH_XMSS_K2_MAGIC) != 0)
766 return SSH_ERR_INVALID_ARGUMENT;
767 /* XXX check stackoffset */
768 if (ls != num_stack(state) ||
769 lsl != num_stacklevels(state) ||
770 la != num_auth(state) ||
771 lk != num_keep(state) ||
772 ln != num_th_nodes(state) ||
773 lr != num_retain(state) ||
774 lh != num_treehash(state))
775 return SSH_ERR_INVALID_ARGUMENT;
776 for (i = 0; i < num_treehash(state); i++) {
777 th = &state->treehash[i];
778 if ((r = sshbuf_get_u32(b, &th->h)) != 0 ||
779 (r = sshbuf_get_u32(b, &th->next_idx)) != 0 ||
780 (r = sshbuf_get_u32(b, &th->stackusage)) != 0 ||
781 (r = sshbuf_get_u8(b, &th->completed)) != 0 ||
782 (r = sshbuf_get_u32(b, &node)) != 0)
783 return r;
784 if (node < num_th_nodes(state))
785 th->node = &state->th_nodes[node];
786 }
787 POKE_U32(k->xmss_sk, state->idx);
788 xmss_set_bds_state(&state->bds, state->stack, state->stackoffset,
789 state->stacklevels, state->auth, state->keep, state->treehash,
790 state->retain, 0);
791 return 0;
792}
793
794int
795sshkey_xmss_deserialize_state_opt(struct sshkey *k, struct sshbuf *b)
796{
797 enum sshkey_serialize_rep opts;
798 u_char have_state;
799 int r;
800
801 if ((r = sshbuf_get_u8(b, &have_state)) != 0)
802 return r;
803
804 opts = have_state;
805 switch (opts) {
806 case SSHKEY_SERIALIZE_DEFAULT:
807 r = 0;
808 break;
809 case SSHKEY_SERIALIZE_STATE:
810 if ((r = sshkey_xmss_deserialize_state(k, b)) != 0)
811 return r;
812 break;
813 case SSHKEY_SERIALIZE_FULL:
814 if ((r = sshkey_xmss_deserialize_enc_key(k, b)) != 0 ||
815 (r = sshkey_xmss_deserialize_state(k, b)) != 0)
816 return r;
817 break;
818 default:
819 r = SSH_ERR_INVALID_FORMAT;
820 break;
821 }
822 return r;
823}
824
825int
826sshkey_xmss_encrypt_state(const struct sshkey *k, struct sshbuf *b,
827 struct sshbuf **retp)
828{
829 struct ssh_xmss_state *state = k->xmss_state;
830 struct sshbuf *encrypted = NULL, *encoded = NULL, *padded = NULL;
831 struct sshcipher_ctx *ciphercontext = NULL;
832 const struct sshcipher *cipher;
833 u_char *cp, *key, *iv = NULL;
834 size_t i, keylen, ivlen, blocksize, authlen, encrypted_len, aadlen;
835 int r = SSH_ERR_INTERNAL_ERROR;
836
837 if (retp != NULL)
838 *retp = NULL;
839 if (state == NULL ||
840 state->enc_keyiv == NULL ||
841 state->enc_ciphername == NULL)
842 return SSH_ERR_INTERNAL_ERROR;
843 if ((cipher = cipher_by_name(state->enc_ciphername)) == NULL) {
844 r = SSH_ERR_INTERNAL_ERROR;
845 goto out;
846 }
847 blocksize = cipher_blocksize(cipher);
848 keylen = cipher_keylen(cipher);
849 ivlen = cipher_ivlen(cipher);
850 authlen = cipher_authlen(cipher);
851 if (state->enc_keyiv_len != keylen + ivlen) {
852 r = SSH_ERR_INVALID_FORMAT;
853 goto out;
854 }
855 key = state->enc_keyiv;
856 if ((encrypted = sshbuf_new()) == NULL ||
857 (encoded = sshbuf_new()) == NULL ||
858 (padded = sshbuf_new()) == NULL ||
859 (iv = malloc(ivlen)) == NULL) {
860 r = SSH_ERR_ALLOC_FAIL;
861 goto out;
862 }
863
864 /* replace first 4 bytes of IV with index to ensure uniqueness */
865 memcpy(iv, key + keylen, ivlen);
866 POKE_U32(iv, state->idx);
867
868 if ((r = sshbuf_put(encoded, XMSS_MAGIC, sizeof(XMSS_MAGIC))) != 0 ||
869 (r = sshbuf_put_u32(encoded, state->idx)) != 0)
870 goto out;
871
872 /* padded state will be encrypted */
873 if ((r = sshbuf_putb(padded, b)) != 0)
874 goto out;
875 i = 0;
876 while (sshbuf_len(padded) % blocksize) {
877 if ((r = sshbuf_put_u8(padded, ++i & 0xff)) != 0)
878 goto out;
879 }
880 encrypted_len = sshbuf_len(padded);
881
882 /* header including the length of state is used as AAD */
883 if ((r = sshbuf_put_u32(encoded, encrypted_len)) != 0)
884 goto out;
885 aadlen = sshbuf_len(encoded);
886
887 /* concat header and state */
888 if ((r = sshbuf_putb(encoded, padded)) != 0)
889 goto out;
890
891 /* reserve space for encryption of encoded data plus auth tag */
892 /* encrypt at offset addlen */
893 if ((r = sshbuf_reserve(encrypted,
894 encrypted_len + aadlen + authlen, &cp)) != 0 ||
895 (r = cipher_init(&ciphercontext, cipher, key, keylen,
896 iv, ivlen, 1)) != 0 ||
897 (r = cipher_crypt(ciphercontext, 0, cp, sshbuf_ptr(encoded),
898 encrypted_len, aadlen, authlen)) != 0)
899 goto out;
900
901 /* success */
902 r = 0;
903 out:
904 if (retp != NULL) {
905 *retp = encrypted;
906 encrypted = NULL;
907 }
908 sshbuf_free(padded);
909 sshbuf_free(encoded);
910 sshbuf_free(encrypted);
911 cipher_free(ciphercontext);
912 free(iv);
913 return r;
914}
915
916int
917sshkey_xmss_decrypt_state(const struct sshkey *k, struct sshbuf *encoded,
918 struct sshbuf **retp)
919{
920 struct ssh_xmss_state *state = k->xmss_state;
921 struct sshbuf *copy = NULL, *decrypted = NULL;
922 struct sshcipher_ctx *ciphercontext = NULL;
923 const struct sshcipher *cipher = NULL;
924 u_char *key, *iv = NULL, *dp;
925 size_t keylen, ivlen, authlen, aadlen;
926 u_int blocksize, encrypted_len, index;
927 int r = SSH_ERR_INTERNAL_ERROR;
928
929 if (retp != NULL)
930 *retp = NULL;
931 if (state == NULL ||
932 state->enc_keyiv == NULL ||
933 state->enc_ciphername == NULL)
934 return SSH_ERR_INTERNAL_ERROR;
935 if ((cipher = cipher_by_name(state->enc_ciphername)) == NULL) {
936 r = SSH_ERR_INVALID_FORMAT;
937 goto out;
938 }
939 blocksize = cipher_blocksize(cipher);
940 keylen = cipher_keylen(cipher);
941 ivlen = cipher_ivlen(cipher);
942 authlen = cipher_authlen(cipher);
943 if (state->enc_keyiv_len != keylen + ivlen) {
944 r = SSH_ERR_INTERNAL_ERROR;
945 goto out;
946 }
947 key = state->enc_keyiv;
948
949 if ((copy = sshbuf_fromb(encoded)) == NULL ||
950 (decrypted = sshbuf_new()) == NULL ||
951 (iv = malloc(ivlen)) == NULL) {
952 r = SSH_ERR_ALLOC_FAIL;
953 goto out;
954 }
955
956 /* check magic */
957 if (sshbuf_len(encoded) < sizeof(XMSS_MAGIC) ||
958 memcmp(sshbuf_ptr(encoded), XMSS_MAGIC, sizeof(XMSS_MAGIC))) {
959 r = SSH_ERR_INVALID_FORMAT;
960 goto out;
961 }
962 /* parse public portion */
963 if ((r = sshbuf_consume(encoded, sizeof(XMSS_MAGIC))) != 0 ||
964 (r = sshbuf_get_u32(encoded, &index)) != 0 ||
965 (r = sshbuf_get_u32(encoded, &encrypted_len)) != 0)
966 goto out;
967
968 /* check size of encrypted key blob */
969 if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) {
970 r = SSH_ERR_INVALID_FORMAT;
971 goto out;
972 }
973 /* check that an appropriate amount of auth data is present */
974 if (sshbuf_len(encoded) < encrypted_len + authlen) {
975 r = SSH_ERR_INVALID_FORMAT;
976 goto out;
977 }
978
979 aadlen = sshbuf_len(copy) - sshbuf_len(encoded);
980
981 /* replace first 4 bytes of IV with index to ensure uniqueness */
982 memcpy(iv, key + keylen, ivlen);
983 POKE_U32(iv, index);
984
985 /* decrypt private state of key */
986 if ((r = sshbuf_reserve(decrypted, aadlen + encrypted_len, &dp)) != 0 ||
987 (r = cipher_init(&ciphercontext, cipher, key, keylen,
988 iv, ivlen, 0)) != 0 ||
989 (r = cipher_crypt(ciphercontext, 0, dp, sshbuf_ptr(copy),
990 encrypted_len, aadlen, authlen)) != 0)
991 goto out;
992
993 /* there should be no trailing data */
994 if ((r = sshbuf_consume(encoded, encrypted_len + authlen)) != 0)
995 goto out;
996 if (sshbuf_len(encoded) != 0) {
997 r = SSH_ERR_INVALID_FORMAT;
998 goto out;
999 }
1000
1001 /* remove AAD */
1002 if ((r = sshbuf_consume(decrypted, aadlen)) != 0)
1003 goto out;
1004 /* XXX encrypted includes unchecked padding */
1005
1006 /* success */
1007 r = 0;
1008 if (retp != NULL) {
1009 *retp = decrypted;
1010 decrypted = NULL;
1011 }
1012 out:
1013 cipher_free(ciphercontext);
1014 sshbuf_free(copy);
1015 sshbuf_free(decrypted);
1016 free(iv);
1017 return r;
1018}
1019
1020u_int32_t
1021sshkey_xmss_signatures_left(const struct sshkey *k)
1022{
1023 struct ssh_xmss_state *state = k->xmss_state;
1024 u_int32_t idx;
1025
1026 if (sshkey_type_plain(k->type) == KEY_XMSS && state &&
1027 state->maxidx) {
1028 idx = k->xmss_sk ? PEEK_U32(k->xmss_sk) : state->idx;
1029 if (idx < state->maxidx)
1030 return state->maxidx - idx;
1031 }
1032 return 0;
1033}
1034
1035int
1036sshkey_xmss_enable_maxsign(struct sshkey *k, u_int32_t maxsign)
1037{
1038 struct ssh_xmss_state *state = k->xmss_state;
1039
1040 if (sshkey_type_plain(k->type) != KEY_XMSS)
1041 return SSH_ERR_INVALID_ARGUMENT;
1042 if (maxsign == 0)
1043 return 0;
1044 if (state->idx + maxsign < state->idx)
1045 return SSH_ERR_INVALID_ARGUMENT;
1046 state->maxidx = state->idx + maxsign;
1047 return 0;
1048}
diff --git a/sshkey-xmss.h b/sshkey-xmss.h
new file mode 100644
index 000000000..b9f8ead10
--- /dev/null
+++ b/sshkey-xmss.h
@@ -0,0 +1,56 @@
1/* $OpenBSD: sshkey-xmss.h,v 1.1 2018/02/23 15:58:38 markus Exp $ */
2/*
3 * Copyright (c) 2017 Markus Friedl. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25#ifndef SSHKEY_XMSS_H
26#define SSHKEY_XMSS_H
27
28#define XMSS_SHA2_256_W16_H10_NAME "XMSS_SHA2-256_W16_H10"
29#define XMSS_SHA2_256_W16_H16_NAME "XMSS_SHA2-256_W16_H16"
30#define XMSS_SHA2_256_W16_H20_NAME "XMSS_SHA2-256_W16_H20"
31#define XMSS_DEFAULT_NAME XMSS_SHA2_256_W16_H10_NAME
32
33size_t sshkey_xmss_pklen(const struct sshkey *);
34size_t sshkey_xmss_sklen(const struct sshkey *);
35int sshkey_xmss_init(struct sshkey *, const char *);
36void sshkey_xmss_free_state(struct sshkey *);
37int sshkey_xmss_generate_private_key(struct sshkey *, u_int);
38int sshkey_xmss_serialize_state(const struct sshkey *, struct sshbuf *);
39int sshkey_xmss_serialize_state_opt(const struct sshkey *, struct sshbuf *,
40 enum sshkey_serialize_rep);
41int sshkey_xmss_serialize_pk_info(const struct sshkey *, struct sshbuf *,
42 enum sshkey_serialize_rep);
43int sshkey_xmss_deserialize_state(struct sshkey *, struct sshbuf *);
44int sshkey_xmss_deserialize_state_opt(struct sshkey *, struct sshbuf *);
45int sshkey_xmss_deserialize_pk_info(struct sshkey *, struct sshbuf *);
46
47int sshkey_xmss_siglen(const struct sshkey *, size_t *);
48void *sshkey_xmss_params(const struct sshkey *);
49void *sshkey_xmss_bds_state(const struct sshkey *);
50int sshkey_xmss_get_state(const struct sshkey *, sshkey_printfn *);
51int sshkey_xmss_enable_maxsign(struct sshkey *, u_int32_t);
52int sshkey_xmss_forward_state(const struct sshkey *, u_int32_t);
53int sshkey_xmss_update_state(const struct sshkey *, sshkey_printfn *);
54u_int32_t sshkey_xmss_signatures_left(const struct sshkey *);
55
56#endif /* SSHKEY_XMSS_H */
diff --git a/sshkey.c b/sshkey.c
index 0e146d4d6..d8ee70ca0 100644
--- a/sshkey.c
+++ b/sshkey.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sshkey.c,v 1.61 2018/02/14 16:03:32 jsing Exp $ */ 1/* $OpenBSD: sshkey.c,v 1.62 2018/02/23 15:58:38 markus Exp $ */
2/* 2/*
3 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
4 * Copyright (c) 2008 Alexander von Gernler. All rights reserved. 4 * Copyright (c) 2008 Alexander von Gernler. All rights reserved.
@@ -55,8 +55,11 @@
55#include "digest.h" 55#include "digest.h"
56#define SSHKEY_INTERNAL 56#define SSHKEY_INTERNAL
57#include "sshkey.h" 57#include "sshkey.h"
58#include "sshkey-xmss.h"
58#include "match.h" 59#include "match.h"
59 60
61#include "xmss_fast.h"
62
60/* openssh private key file format */ 63/* openssh private key file format */
61#define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n" 64#define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n"
62#define MARK_END "-----END OPENSSH PRIVATE KEY-----\n" 65#define MARK_END "-----END OPENSSH PRIVATE KEY-----\n"
@@ -71,6 +74,8 @@
71/* Version identification string for SSH v1 identity files. */ 74/* Version identification string for SSH v1 identity files. */
72#define LEGACY_BEGIN "SSH PRIVATE KEY FILE FORMAT 1.1\n" 75#define LEGACY_BEGIN "SSH PRIVATE KEY FILE FORMAT 1.1\n"
73 76
77int sshkey_private_serialize_opt(const struct sshkey *key,
78 struct sshbuf *buf, enum sshkey_serialize_rep);
74static int sshkey_from_blob_internal(struct sshbuf *buf, 79static int sshkey_from_blob_internal(struct sshbuf *buf,
75 struct sshkey **keyp, int allow_cert); 80 struct sshkey **keyp, int allow_cert);
76 81
@@ -87,6 +92,11 @@ static const struct keytype keytypes[] = {
87 { "ssh-ed25519", "ED25519", KEY_ED25519, 0, 0, 0 }, 92 { "ssh-ed25519", "ED25519", KEY_ED25519, 0, 0, 0 },
88 { "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT", 93 { "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT",
89 KEY_ED25519_CERT, 0, 1, 0 }, 94 KEY_ED25519_CERT, 0, 1, 0 },
95#ifdef WITH_XMSS
96 { "ssh-xmss@openssh.com", "XMSS", KEY_XMSS, 0, 0, 0 },
97 { "ssh-xmss-cert-v01@openssh.com", "XMSS-CERT",
98 KEY_XMSS_CERT, 0, 1, 0 },
99#endif /* WITH_XMSS */
90#ifdef WITH_OPENSSL 100#ifdef WITH_OPENSSL
91 { "ssh-rsa", "RSA", KEY_RSA, 0, 0, 0 }, 101 { "ssh-rsa", "RSA", KEY_RSA, 0, 0, 0 },
92 { "rsa-sha2-256", "RSA", KEY_RSA, 0, 0, 1 }, 102 { "rsa-sha2-256", "RSA", KEY_RSA, 0, 0, 1 },
@@ -274,6 +284,8 @@ sshkey_size(const struct sshkey *k)
274#endif /* WITH_OPENSSL */ 284#endif /* WITH_OPENSSL */
275 case KEY_ED25519: 285 case KEY_ED25519:
276 case KEY_ED25519_CERT: 286 case KEY_ED25519_CERT:
287 case KEY_XMSS:
288 case KEY_XMSS_CERT:
277 return 256; /* XXX */ 289 return 256; /* XXX */
278 } 290 }
279 return 0; 291 return 0;
@@ -287,6 +299,7 @@ sshkey_type_is_valid_ca(int type)
287 case KEY_DSA: 299 case KEY_DSA:
288 case KEY_ECDSA: 300 case KEY_ECDSA:
289 case KEY_ED25519: 301 case KEY_ED25519:
302 case KEY_XMSS:
290 return 1; 303 return 1;
291 default: 304 default:
292 return 0; 305 return 0;
@@ -314,6 +327,8 @@ sshkey_type_plain(int type)
314 return KEY_ECDSA; 327 return KEY_ECDSA;
315 case KEY_ED25519_CERT: 328 case KEY_ED25519_CERT:
316 return KEY_ED25519; 329 return KEY_ED25519;
330 case KEY_XMSS_CERT:
331 return KEY_XMSS;
317 default: 332 default:
318 return type; 333 return type;
319 } 334 }
@@ -461,6 +476,8 @@ sshkey_new(int type)
461 k->cert = NULL; 476 k->cert = NULL;
462 k->ed25519_sk = NULL; 477 k->ed25519_sk = NULL;
463 k->ed25519_pk = NULL; 478 k->ed25519_pk = NULL;
479 k->xmss_sk = NULL;
480 k->xmss_pk = NULL;
464 switch (k->type) { 481 switch (k->type) {
465#ifdef WITH_OPENSSL 482#ifdef WITH_OPENSSL
466 case KEY_RSA: 483 case KEY_RSA:
@@ -494,6 +511,8 @@ sshkey_new(int type)
494#endif /* WITH_OPENSSL */ 511#endif /* WITH_OPENSSL */
495 case KEY_ED25519: 512 case KEY_ED25519:
496 case KEY_ED25519_CERT: 513 case KEY_ED25519_CERT:
514 case KEY_XMSS:
515 case KEY_XMSS_CERT:
497 /* no need to prealloc */ 516 /* no need to prealloc */
498 break; 517 break;
499 case KEY_UNSPEC: 518 case KEY_UNSPEC:
@@ -542,6 +561,8 @@ sshkey_add_private(struct sshkey *k)
542#endif /* WITH_OPENSSL */ 561#endif /* WITH_OPENSSL */
543 case KEY_ED25519: 562 case KEY_ED25519:
544 case KEY_ED25519_CERT: 563 case KEY_ED25519_CERT:
564 case KEY_XMSS:
565 case KEY_XMSS_CERT:
545 /* no need to prealloc */ 566 /* no need to prealloc */
546 break; 567 break;
547 case KEY_UNSPEC: 568 case KEY_UNSPEC:
@@ -598,6 +619,20 @@ sshkey_free(struct sshkey *k)
598 freezero(k->ed25519_sk, ED25519_SK_SZ); 619 freezero(k->ed25519_sk, ED25519_SK_SZ);
599 k->ed25519_sk = NULL; 620 k->ed25519_sk = NULL;
600 break; 621 break;
622#ifdef WITH_XMSS
623 case KEY_XMSS:
624 case KEY_XMSS_CERT:
625 freezero(k->xmss_pk, sshkey_xmss_pklen(k));
626 k->xmss_pk = NULL;
627 freezero(k->xmss_sk, sshkey_xmss_sklen(k));
628 k->xmss_sk = NULL;
629 sshkey_xmss_free_state(k);
630 free(k->xmss_name);
631 k->xmss_name = NULL;
632 free(k->xmss_filename);
633 k->xmss_filename = NULL;
634 break;
635#endif /* WITH_XMSS */
601 case KEY_UNSPEC: 636 case KEY_UNSPEC:
602 break; 637 break;
603 default: 638 default:
@@ -677,6 +712,13 @@ sshkey_equal_public(const struct sshkey *a, const struct sshkey *b)
677 case KEY_ED25519_CERT: 712 case KEY_ED25519_CERT:
678 return a->ed25519_pk != NULL && b->ed25519_pk != NULL && 713 return a->ed25519_pk != NULL && b->ed25519_pk != NULL &&
679 memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) == 0; 714 memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) == 0;
715#ifdef WITH_XMSS
716 case KEY_XMSS:
717 case KEY_XMSS_CERT:
718 return a->xmss_pk != NULL && b->xmss_pk != NULL &&
719 sshkey_xmss_pklen(a) == sshkey_xmss_pklen(b) &&
720 memcmp(a->xmss_pk, b->xmss_pk, sshkey_xmss_pklen(a)) == 0;
721#endif /* WITH_XMSS */
680 default: 722 default:
681 return 0; 723 return 0;
682 } 724 }
@@ -696,7 +738,8 @@ sshkey_equal(const struct sshkey *a, const struct sshkey *b)
696} 738}
697 739
698static int 740static int
699to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain) 741to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain,
742 enum sshkey_serialize_rep opts)
700{ 743{
701 int type, ret = SSH_ERR_INTERNAL_ERROR; 744 int type, ret = SSH_ERR_INTERNAL_ERROR;
702 const char *typename; 745 const char *typename;
@@ -720,6 +763,9 @@ to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain)
720 case KEY_RSA_CERT: 763 case KEY_RSA_CERT:
721#endif /* WITH_OPENSSL */ 764#endif /* WITH_OPENSSL */
722 case KEY_ED25519_CERT: 765 case KEY_ED25519_CERT:
766#ifdef WITH_XMSS
767 case KEY_XMSS_CERT:
768#endif /* WITH_XMSS */
723 /* Use the existing blob */ 769 /* Use the existing blob */
724 /* XXX modified flag? */ 770 /* XXX modified flag? */
725 if ((ret = sshbuf_putb(b, key->cert->certblob)) != 0) 771 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)
764 key->ed25519_pk, ED25519_PK_SZ)) != 0) 810 key->ed25519_pk, ED25519_PK_SZ)) != 0)
765 return ret; 811 return ret;
766 break; 812 break;
813#ifdef WITH_XMSS
814 case KEY_XMSS:
815 if (key->xmss_name == NULL || key->xmss_pk == NULL ||
816 sshkey_xmss_pklen(key) == 0)
817 return SSH_ERR_INVALID_ARGUMENT;
818 if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
819 (ret = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
820 (ret = sshbuf_put_string(b,
821 key->xmss_pk, sshkey_xmss_pklen(key))) != 0 ||
822 (ret = sshkey_xmss_serialize_pk_info(key, b, opts)) != 0)
823 return ret;
824 break;
825#endif /* WITH_XMSS */
767 default: 826 default:
768 return SSH_ERR_KEY_TYPE_UNKNOWN; 827 return SSH_ERR_KEY_TYPE_UNKNOWN;
769 } 828 }
@@ -773,18 +832,19 @@ to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain)
773int 832int
774sshkey_putb(const struct sshkey *key, struct sshbuf *b) 833sshkey_putb(const struct sshkey *key, struct sshbuf *b)
775{ 834{
776 return to_blob_buf(key, b, 0); 835 return to_blob_buf(key, b, 0, SSHKEY_SERIALIZE_DEFAULT);
777} 836}
778 837
779int 838int
780sshkey_puts(const struct sshkey *key, struct sshbuf *b) 839sshkey_puts_opts(const struct sshkey *key, struct sshbuf *b,
840 enum sshkey_serialize_rep opts)
781{ 841{
782 struct sshbuf *tmp; 842 struct sshbuf *tmp;
783 int r; 843 int r;
784 844
785 if ((tmp = sshbuf_new()) == NULL) 845 if ((tmp = sshbuf_new()) == NULL)
786 return SSH_ERR_ALLOC_FAIL; 846 return SSH_ERR_ALLOC_FAIL;
787 r = to_blob_buf(key, tmp, 0); 847 r = to_blob_buf(key, tmp, 0, opts);
788 if (r == 0) 848 if (r == 0)
789 r = sshbuf_put_stringb(b, tmp); 849 r = sshbuf_put_stringb(b, tmp);
790 sshbuf_free(tmp); 850 sshbuf_free(tmp);
@@ -792,13 +852,20 @@ sshkey_puts(const struct sshkey *key, struct sshbuf *b)
792} 852}
793 853
794int 854int
855sshkey_puts(const struct sshkey *key, struct sshbuf *b)
856{
857 return sshkey_puts_opts(key, b, SSHKEY_SERIALIZE_DEFAULT);
858}
859
860int
795sshkey_putb_plain(const struct sshkey *key, struct sshbuf *b) 861sshkey_putb_plain(const struct sshkey *key, struct sshbuf *b)
796{ 862{
797 return to_blob_buf(key, b, 1); 863 return to_blob_buf(key, b, 1, SSHKEY_SERIALIZE_DEFAULT);
798} 864}
799 865
800static int 866static int
801to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain) 867to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain,
868 enum sshkey_serialize_rep opts)
802{ 869{
803 int ret = SSH_ERR_INTERNAL_ERROR; 870 int ret = SSH_ERR_INTERNAL_ERROR;
804 size_t len; 871 size_t len;
@@ -810,7 +877,7 @@ to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain)
810 *blobp = NULL; 877 *blobp = NULL;
811 if ((b = sshbuf_new()) == NULL) 878 if ((b = sshbuf_new()) == NULL)
812 return SSH_ERR_ALLOC_FAIL; 879 return SSH_ERR_ALLOC_FAIL;
813 if ((ret = to_blob_buf(key, b, force_plain)) != 0) 880 if ((ret = to_blob_buf(key, b, force_plain, opts)) != 0)
814 goto out; 881 goto out;
815 len = sshbuf_len(b); 882 len = sshbuf_len(b);
816 if (lenp != NULL) 883 if (lenp != NULL)
@@ -831,13 +898,13 @@ to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain)
831int 898int
832sshkey_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp) 899sshkey_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp)
833{ 900{
834 return to_blob(key, blobp, lenp, 0); 901 return to_blob(key, blobp, lenp, 0, SSHKEY_SERIALIZE_DEFAULT);
835} 902}
836 903
837int 904int
838sshkey_plain_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp) 905sshkey_plain_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp)
839{ 906{
840 return to_blob(key, blobp, lenp, 1); 907 return to_blob(key, blobp, lenp, 1, SSHKEY_SERIALIZE_DEFAULT);
841} 908}
842 909
843int 910int
@@ -856,7 +923,8 @@ sshkey_fingerprint_raw(const struct sshkey *k, int dgst_alg,
856 r = SSH_ERR_INVALID_ARGUMENT; 923 r = SSH_ERR_INVALID_ARGUMENT;
857 goto out; 924 goto out;
858 } 925 }
859 if ((r = to_blob(k, &blob, &blob_len, 1)) != 0) 926 if ((r = to_blob(k, &blob, &blob_len, 1, SSHKEY_SERIALIZE_DEFAULT))
927 != 0)
860 goto out; 928 goto out;
861 if ((ret = calloc(1, SSH_DIGEST_MAX_LENGTH)) == NULL) { 929 if ((ret = calloc(1, SSH_DIGEST_MAX_LENGTH)) == NULL) {
862 r = SSH_ERR_ALLOC_FAIL; 930 r = SSH_ERR_ALLOC_FAIL;
@@ -1173,6 +1241,10 @@ sshkey_read(struct sshkey *ret, char **cpp)
1173 case KEY_ECDSA_CERT: 1241 case KEY_ECDSA_CERT:
1174 case KEY_RSA_CERT: 1242 case KEY_RSA_CERT:
1175 case KEY_ED25519_CERT: 1243 case KEY_ED25519_CERT:
1244#ifdef WITH_XMSS
1245 case KEY_XMSS:
1246 case KEY_XMSS_CERT:
1247#endif /* WITH_XMSS */
1176 space = strchr(cp, ' '); 1248 space = strchr(cp, ' ');
1177 if (space == NULL) 1249 if (space == NULL)
1178 return SSH_ERR_INVALID_FORMAT; 1250 return SSH_ERR_INVALID_FORMAT;
@@ -1270,6 +1342,25 @@ sshkey_read(struct sshkey *ret, char **cpp)
1270 /* XXX */ 1342 /* XXX */
1271#endif 1343#endif
1272 break; 1344 break;
1345#ifdef WITH_XMSS
1346 case KEY_XMSS:
1347 free(ret->xmss_pk);
1348 ret->xmss_pk = k->xmss_pk;
1349 k->xmss_pk = NULL;
1350 free(ret->xmss_state);
1351 ret->xmss_state = k->xmss_state;
1352 k->xmss_state = NULL;
1353 free(ret->xmss_name);
1354 ret->xmss_name = k->xmss_name;
1355 k->xmss_name = NULL;
1356 free(ret->xmss_filename);
1357 ret->xmss_filename = k->xmss_filename;
1358 k->xmss_filename = NULL;
1359#ifdef DEBUG_PK
1360 /* XXX */
1361#endif
1362 break;
1363#endif /* WITH_XMSS */
1273 } 1364 }
1274 *cpp = ep; 1365 *cpp = ep;
1275 retval = 0; 1366 retval = 0;
@@ -1528,6 +1619,11 @@ sshkey_generate(int type, u_int bits, struct sshkey **keyp)
1528 crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk); 1619 crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk);
1529 ret = 0; 1620 ret = 0;
1530 break; 1621 break;
1622#ifdef WITH_XMSS
1623 case KEY_XMSS:
1624 ret = sshkey_xmss_generate_private_key(k, bits);
1625 break;
1626#endif /* WITH_XMSS */
1531#ifdef WITH_OPENSSL 1627#ifdef WITH_OPENSSL
1532 case KEY_DSA: 1628 case KEY_DSA:
1533 ret = dsa_generate_private_key(bits, &k->dsa); 1629 ret = dsa_generate_private_key(bits, &k->dsa);
@@ -1671,6 +1767,29 @@ sshkey_from_private(const struct sshkey *k, struct sshkey **pkp)
1671 memcpy(n->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ); 1767 memcpy(n->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ);
1672 } 1768 }
1673 break; 1769 break;
1770#ifdef WITH_XMSS
1771 case KEY_XMSS:
1772 case KEY_XMSS_CERT:
1773 if ((n = sshkey_new(k->type)) == NULL)
1774 return SSH_ERR_ALLOC_FAIL;
1775 if ((ret = sshkey_xmss_init(n, k->xmss_name)) != 0) {
1776 sshkey_free(n);
1777 return ret;
1778 }
1779 if (k->xmss_pk != NULL) {
1780 size_t pklen = sshkey_xmss_pklen(k);
1781 if (pklen == 0 || sshkey_xmss_pklen(n) != pklen) {
1782 sshkey_free(n);
1783 return SSH_ERR_INTERNAL_ERROR;
1784 }
1785 if ((n->xmss_pk = malloc(pklen)) == NULL) {
1786 sshkey_free(n);
1787 return SSH_ERR_ALLOC_FAIL;
1788 }
1789 memcpy(n->xmss_pk, k->xmss_pk, pklen);
1790 }
1791 break;
1792#endif /* WITH_XMSS */
1674 default: 1793 default:
1675 return SSH_ERR_KEY_TYPE_UNKNOWN; 1794 return SSH_ERR_KEY_TYPE_UNKNOWN;
1676 } 1795 }
@@ -1812,7 +1931,7 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
1812 int allow_cert) 1931 int allow_cert)
1813{ 1932{
1814 int type, ret = SSH_ERR_INTERNAL_ERROR; 1933 int type, ret = SSH_ERR_INTERNAL_ERROR;
1815 char *ktype = NULL, *curve = NULL; 1934 char *ktype = NULL, *curve = NULL, *xmss_name = NULL;
1816 struct sshkey *key = NULL; 1935 struct sshkey *key = NULL;
1817 size_t len; 1936 size_t len;
1818 u_char *pk = NULL; 1937 u_char *pk = NULL;
@@ -1963,6 +2082,36 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
1963 key->ed25519_pk = pk; 2082 key->ed25519_pk = pk;
1964 pk = NULL; 2083 pk = NULL;
1965 break; 2084 break;
2085#ifdef WITH_XMSS
2086 case KEY_XMSS_CERT:
2087 /* Skip nonce */
2088 if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
2089 ret = SSH_ERR_INVALID_FORMAT;
2090 goto out;
2091 }
2092 /* FALLTHROUGH */
2093 case KEY_XMSS:
2094 if ((ret = sshbuf_get_cstring(b, &xmss_name, NULL)) != 0)
2095 goto out;
2096 if ((key = sshkey_new(type)) == NULL) {
2097 ret = SSH_ERR_ALLOC_FAIL;
2098 goto out;
2099 }
2100 if ((ret = sshkey_xmss_init(key, xmss_name)) != 0)
2101 goto out;
2102 if ((ret = sshbuf_get_string(b, &pk, &len)) != 0)
2103 goto out;
2104 if (len == 0 || len != sshkey_xmss_pklen(key)) {
2105 ret = SSH_ERR_INVALID_FORMAT;
2106 goto out;
2107 }
2108 key->xmss_pk = pk;
2109 pk = NULL;
2110 if (type != KEY_XMSS_CERT &&
2111 (ret = sshkey_xmss_deserialize_pk_info(key, b)) != 0)
2112 goto out;
2113 break;
2114#endif /* WITH_XMSS */
1966 case KEY_UNSPEC: 2115 case KEY_UNSPEC:
1967 default: 2116 default:
1968 ret = SSH_ERR_KEY_TYPE_UNKNOWN; 2117 ret = SSH_ERR_KEY_TYPE_UNKNOWN;
@@ -1985,6 +2134,7 @@ sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp,
1985 out: 2134 out:
1986 sshbuf_free(copy); 2135 sshbuf_free(copy);
1987 sshkey_free(key); 2136 sshkey_free(key);
2137 free(xmss_name);
1988 free(ktype); 2138 free(ktype);
1989 free(curve); 2139 free(curve);
1990 free(pk); 2140 free(pk);
@@ -2079,6 +2229,11 @@ sshkey_sign(const struct sshkey *key,
2079 case KEY_ED25519: 2229 case KEY_ED25519:
2080 case KEY_ED25519_CERT: 2230 case KEY_ED25519_CERT:
2081 return ssh_ed25519_sign(key, sigp, lenp, data, datalen, compat); 2231 return ssh_ed25519_sign(key, sigp, lenp, data, datalen, compat);
2232#ifdef WITH_XMSS
2233 case KEY_XMSS:
2234 case KEY_XMSS_CERT:
2235 return ssh_xmss_sign(key, sigp, lenp, data, datalen, compat);
2236#endif /* WITH_XMSS */
2082 default: 2237 default:
2083 return SSH_ERR_KEY_TYPE_UNKNOWN; 2238 return SSH_ERR_KEY_TYPE_UNKNOWN;
2084 } 2239 }
@@ -2112,6 +2267,11 @@ sshkey_verify(const struct sshkey *key,
2112 case KEY_ED25519: 2267 case KEY_ED25519:
2113 case KEY_ED25519_CERT: 2268 case KEY_ED25519_CERT:
2114 return ssh_ed25519_verify(key, sig, siglen, data, dlen, compat); 2269 return ssh_ed25519_verify(key, sig, siglen, data, dlen, compat);
2270#ifdef WITH_XMSS
2271 case KEY_XMSS:
2272 case KEY_XMSS_CERT:
2273 return ssh_xmss_verify(key, sig, siglen, data, dlen, compat);
2274#endif /* WITH_XMSS */
2115 default: 2275 default:
2116 return SSH_ERR_KEY_TYPE_UNKNOWN; 2276 return SSH_ERR_KEY_TYPE_UNKNOWN;
2117 } 2277 }
@@ -2135,6 +2295,8 @@ sshkey_demote(const struct sshkey *k, struct sshkey **dkp)
2135 pk->rsa = NULL; 2295 pk->rsa = NULL;
2136 pk->ed25519_pk = NULL; 2296 pk->ed25519_pk = NULL;
2137 pk->ed25519_sk = NULL; 2297 pk->ed25519_sk = NULL;
2298 pk->xmss_pk = NULL;
2299 pk->xmss_sk = NULL;
2138 2300
2139 switch (k->type) { 2301 switch (k->type) {
2140#ifdef WITH_OPENSSL 2302#ifdef WITH_OPENSSL
@@ -2196,6 +2358,29 @@ sshkey_demote(const struct sshkey *k, struct sshkey **dkp)
2196 memcpy(pk->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ); 2358 memcpy(pk->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ);
2197 } 2359 }
2198 break; 2360 break;
2361#ifdef WITH_XMSS
2362 case KEY_XMSS_CERT:
2363 if ((ret = sshkey_cert_copy(k, pk)) != 0)
2364 goto fail;
2365 /* FALLTHROUGH */
2366 case KEY_XMSS:
2367 if ((ret = sshkey_xmss_init(pk, k->xmss_name)) != 0)
2368 goto fail;
2369 if (k->xmss_pk != NULL) {
2370 size_t pklen = sshkey_xmss_pklen(k);
2371
2372 if (pklen == 0 || sshkey_xmss_pklen(pk) != pklen) {
2373 ret = SSH_ERR_INTERNAL_ERROR;
2374 goto fail;
2375 }
2376 if ((pk->xmss_pk = malloc(pklen)) == NULL) {
2377 ret = SSH_ERR_ALLOC_FAIL;
2378 goto fail;
2379 }
2380 memcpy(pk->xmss_pk, k->xmss_pk, pklen);
2381 }
2382 break;
2383#endif /* WITH_XMSS */
2199 default: 2384 default:
2200 ret = SSH_ERR_KEY_TYPE_UNKNOWN; 2385 ret = SSH_ERR_KEY_TYPE_UNKNOWN;
2201 fail: 2386 fail:
@@ -2227,6 +2412,11 @@ sshkey_to_certified(struct sshkey *k)
2227 case KEY_ED25519: 2412 case KEY_ED25519:
2228 newtype = KEY_ED25519_CERT; 2413 newtype = KEY_ED25519_CERT;
2229 break; 2414 break;
2415#ifdef WITH_XMSS
2416 case KEY_XMSS:
2417 newtype = KEY_XMSS_CERT;
2418 break;
2419#endif /* WITH_XMSS */
2230 default: 2420 default:
2231 return SSH_ERR_INVALID_ARGUMENT; 2421 return SSH_ERR_INVALID_ARGUMENT;
2232 } 2422 }
@@ -2311,6 +2501,18 @@ sshkey_certify_custom(struct sshkey *k, struct sshkey *ca, const char *alg,
2311 k->ed25519_pk, ED25519_PK_SZ)) != 0) 2501 k->ed25519_pk, ED25519_PK_SZ)) != 0)
2312 goto out; 2502 goto out;
2313 break; 2503 break;
2504#ifdef WITH_XMSS
2505 case KEY_XMSS_CERT:
2506 if (k->xmss_name == NULL) {
2507 ret = SSH_ERR_INVALID_ARGUMENT;
2508 goto out;
2509 }
2510 if ((ret = sshbuf_put_cstring(cert, k->xmss_name)) ||
2511 (ret = sshbuf_put_string(cert,
2512 k->xmss_pk, sshkey_xmss_pklen(k))) != 0)
2513 goto out;
2514 break;
2515#endif /* WITH_XMSS */
2314 default: 2516 default:
2315 ret = SSH_ERR_INVALID_ARGUMENT; 2517 ret = SSH_ERR_INVALID_ARGUMENT;
2316 goto out; 2518 goto out;
@@ -2468,7 +2670,8 @@ sshkey_format_cert_validity(const struct sshkey_cert *cert, char *s, size_t l)
2468} 2670}
2469 2671
2470int 2672int
2471sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b) 2673sshkey_private_serialize_opt(const struct sshkey *key, struct sshbuf *b,
2674 enum sshkey_serialize_rep opts)
2472{ 2675{
2473 int r = SSH_ERR_INTERNAL_ERROR; 2676 int r = SSH_ERR_INTERNAL_ERROR;
2474 2677
@@ -2554,6 +2757,36 @@ sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b)
2554 ED25519_SK_SZ)) != 0) 2757 ED25519_SK_SZ)) != 0)
2555 goto out; 2758 goto out;
2556 break; 2759 break;
2760#ifdef WITH_XMSS
2761 case KEY_XMSS:
2762 if (key->xmss_name == NULL) {
2763 r = SSH_ERR_INVALID_ARGUMENT;
2764 goto out;
2765 }
2766 if ((r = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
2767 (r = sshbuf_put_string(b, key->xmss_pk,
2768 sshkey_xmss_pklen(key))) != 0 ||
2769 (r = sshbuf_put_string(b, key->xmss_sk,
2770 sshkey_xmss_sklen(key))) != 0 ||
2771 (r = sshkey_xmss_serialize_state_opt(key, b, opts)) != 0)
2772 goto out;
2773 break;
2774 case KEY_XMSS_CERT:
2775 if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0 ||
2776 key->xmss_name == NULL) {
2777 r = SSH_ERR_INVALID_ARGUMENT;
2778 goto out;
2779 }
2780 if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
2781 (r = sshbuf_put_cstring(b, key->xmss_name)) != 0 ||
2782 (r = sshbuf_put_string(b, key->xmss_pk,
2783 sshkey_xmss_pklen(key))) != 0 ||
2784 (r = sshbuf_put_string(b, key->xmss_sk,
2785 sshkey_xmss_sklen(key))) != 0 ||
2786 (r = sshkey_xmss_serialize_state_opt(key, b, opts)) != 0)
2787 goto out;
2788 break;
2789#endif /* WITH_XMSS */
2557 default: 2790 default:
2558 r = SSH_ERR_INVALID_ARGUMENT; 2791 r = SSH_ERR_INVALID_ARGUMENT;
2559 goto out; 2792 goto out;
@@ -2565,13 +2798,21 @@ sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b)
2565} 2798}
2566 2799
2567int 2800int
2801sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b)
2802{
2803 return sshkey_private_serialize_opt(key, b,
2804 SSHKEY_SERIALIZE_DEFAULT);
2805}
2806
2807int
2568sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) 2808sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp)
2569{ 2809{
2570 char *tname = NULL, *curve = NULL; 2810 char *tname = NULL, *curve = NULL, *xmss_name = NULL;
2571 struct sshkey *k = NULL; 2811 struct sshkey *k = NULL;
2572 size_t pklen = 0, sklen = 0; 2812 size_t pklen = 0, sklen = 0;
2573 int type, r = SSH_ERR_INTERNAL_ERROR; 2813 int type, r = SSH_ERR_INTERNAL_ERROR;
2574 u_char *ed25519_pk = NULL, *ed25519_sk = NULL; 2814 u_char *ed25519_pk = NULL, *ed25519_sk = NULL;
2815 u_char *xmss_pk = NULL, *xmss_sk = NULL;
2575#ifdef WITH_OPENSSL 2816#ifdef WITH_OPENSSL
2576 BIGNUM *exponent = NULL; 2817 BIGNUM *exponent = NULL;
2577#endif /* WITH_OPENSSL */ 2818#endif /* WITH_OPENSSL */
@@ -2716,6 +2957,48 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp)
2716 k->ed25519_sk = ed25519_sk; 2957 k->ed25519_sk = ed25519_sk;
2717 ed25519_pk = ed25519_sk = NULL; 2958 ed25519_pk = ed25519_sk = NULL;
2718 break; 2959 break;
2960#ifdef WITH_XMSS
2961 case KEY_XMSS:
2962 if ((k = sshkey_new_private(type)) == NULL) {
2963 r = SSH_ERR_ALLOC_FAIL;
2964 goto out;
2965 }
2966 if ((r = sshbuf_get_cstring(buf, &xmss_name, NULL)) != 0 ||
2967 (r = sshkey_xmss_init(k, xmss_name)) != 0 ||
2968 (r = sshbuf_get_string(buf, &xmss_pk, &pklen)) != 0 ||
2969 (r = sshbuf_get_string(buf, &xmss_sk, &sklen)) != 0)
2970 goto out;
2971 if (pklen != sshkey_xmss_pklen(k) ||
2972 sklen != sshkey_xmss_sklen(k)) {
2973 r = SSH_ERR_INVALID_FORMAT;
2974 goto out;
2975 }
2976 k->xmss_pk = xmss_pk;
2977 k->xmss_sk = xmss_sk;
2978 xmss_pk = xmss_sk = NULL;
2979 /* optional internal state */
2980 if ((r = sshkey_xmss_deserialize_state_opt(k, buf)) != 0)
2981 goto out;
2982 break;
2983 case KEY_XMSS_CERT:
2984 if ((r = sshkey_froms(buf, &k)) != 0 ||
2985 (r = sshkey_add_private(k)) != 0 ||
2986 (r = sshbuf_get_string(buf, &xmss_pk, &pklen)) != 0 ||
2987 (r = sshbuf_get_string(buf, &xmss_sk, &sklen)) != 0)
2988 goto out;
2989 if (pklen != sshkey_xmss_pklen(k) ||
2990 sklen != sshkey_xmss_sklen(k)) {
2991 r = SSH_ERR_INVALID_FORMAT;
2992 goto out;
2993 }
2994 k->xmss_pk = xmss_pk;
2995 k->xmss_sk = xmss_sk;
2996 xmss_pk = xmss_sk = NULL;
2997 /* optional internal state */
2998 if ((r = sshkey_xmss_deserialize_state_opt(k, buf)) != 0)
2999 goto out;
3000 break;
3001#endif /* WITH_XMSS */
2719 default: 3002 default:
2720 r = SSH_ERR_KEY_TYPE_UNKNOWN; 3003 r = SSH_ERR_KEY_TYPE_UNKNOWN;
2721 goto out; 3004 goto out;
@@ -2747,6 +3030,9 @@ sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp)
2747 sshkey_free(k); 3030 sshkey_free(k);
2748 freezero(ed25519_pk, pklen); 3031 freezero(ed25519_pk, pklen);
2749 freezero(ed25519_sk, sklen); 3032 freezero(ed25519_sk, sklen);
3033 free(xmss_name);
3034 freezero(xmss_pk, pklen);
3035 freezero(xmss_sk, sklen);
2750 return r; 3036 return r;
2751} 3037}
2752 3038
@@ -3001,7 +3287,8 @@ sshkey_private_to_blob2(const struct sshkey *prv, struct sshbuf *blob,
3001 goto out; 3287 goto out;
3002 3288
3003 /* append private key and comment*/ 3289 /* append private key and comment*/
3004 if ((r = sshkey_private_serialize(prv, encrypted)) != 0 || 3290 if ((r = sshkey_private_serialize_opt(prv, encrypted,
3291 SSHKEY_SERIALIZE_FULL)) != 0 ||
3005 (r = sshbuf_put_cstring(encrypted, comment)) != 0) 3292 (r = sshbuf_put_cstring(encrypted, comment)) != 0)
3006 goto out; 3293 goto out;
3007 3294
@@ -3362,6 +3649,9 @@ sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob,
3362 passphrase, comment); 3649 passphrase, comment);
3363#endif /* WITH_OPENSSL */ 3650#endif /* WITH_OPENSSL */
3364 case KEY_ED25519: 3651 case KEY_ED25519:
3652#ifdef WITH_XMSS
3653 case KEY_XMSS:
3654#endif /* WITH_XMSS */
3365 return sshkey_private_to_blob2(key, blob, passphrase, 3655 return sshkey_private_to_blob2(key, blob, passphrase,
3366 comment, new_format_cipher, new_format_rounds); 3656 comment, new_format_cipher, new_format_rounds);
3367 default: 3657 default:
@@ -3545,6 +3835,9 @@ sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type,
3545 passphrase, keyp); 3835 passphrase, keyp);
3546#endif /* WITH_OPENSSL */ 3836#endif /* WITH_OPENSSL */
3547 case KEY_ED25519: 3837 case KEY_ED25519:
3838#ifdef WITH_XMSS
3839 case KEY_XMSS:
3840#endif /* WITH_XMSS */
3548 return sshkey_parse_private2(blob, type, passphrase, 3841 return sshkey_parse_private2(blob, type, passphrase,
3549 keyp, commentp); 3842 keyp, commentp);
3550 case KEY_UNSPEC: 3843 case KEY_UNSPEC:
@@ -3576,3 +3869,90 @@ sshkey_parse_private_fileblob(struct sshbuf *buffer, const char *passphrase,
3576 return sshkey_parse_private_fileblob_type(buffer, KEY_UNSPEC, 3869 return sshkey_parse_private_fileblob_type(buffer, KEY_UNSPEC,
3577 passphrase, keyp, commentp); 3870 passphrase, keyp, commentp);
3578} 3871}
3872
3873#ifdef WITH_XMSS
3874/*
3875 * serialize the key with the current state and forward the state
3876 * maxsign times.
3877 */
3878int
3879sshkey_private_serialize_maxsign(const struct sshkey *k, struct sshbuf *b,
3880 u_int32_t maxsign, sshkey_printfn *pr)
3881{
3882 int r, rupdate;
3883
3884 if (maxsign == 0 ||
3885 sshkey_type_plain(k->type) != KEY_XMSS)
3886 return sshkey_private_serialize_opt(k, b,
3887 SSHKEY_SERIALIZE_DEFAULT);
3888 if ((r = sshkey_xmss_get_state(k, pr)) != 0 ||
3889 (r = sshkey_private_serialize_opt(k, b,
3890 SSHKEY_SERIALIZE_STATE)) != 0 ||
3891 (r = sshkey_xmss_forward_state(k, maxsign)) != 0)
3892 goto out;
3893 r = 0;
3894out:
3895 if ((rupdate = sshkey_xmss_update_state(k, pr)) != 0) {
3896 if (r == 0)
3897 r = rupdate;
3898 }
3899 return r;
3900}
3901
3902u_int32_t
3903sshkey_signatures_left(const struct sshkey *k)
3904{
3905 if (sshkey_type_plain(k->type) == KEY_XMSS)
3906 return sshkey_xmss_signatures_left(k);
3907 return 0;
3908}
3909
3910int
3911sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign)
3912{
3913 if (sshkey_type_plain(k->type) != KEY_XMSS)
3914 return SSH_ERR_INVALID_ARGUMENT;
3915 return sshkey_xmss_enable_maxsign(k, maxsign);
3916}
3917
3918int
3919sshkey_set_filename(struct sshkey *k, const char *filename)
3920{
3921 if (k == NULL)
3922 return SSH_ERR_INVALID_ARGUMENT;
3923 if (sshkey_type_plain(k->type) != KEY_XMSS)
3924 return 0;
3925 if (filename == NULL)
3926 return SSH_ERR_INVALID_ARGUMENT;
3927 if ((k->xmss_filename = strdup(filename)) == NULL)
3928 return SSH_ERR_ALLOC_FAIL;
3929 return 0;
3930}
3931#else
3932int
3933sshkey_private_serialize_maxsign(const struct sshkey *k, struct sshbuf *b,
3934 u_int32_t maxsign, sshkey_printfn *pr)
3935{
3936 return sshkey_private_serialize_opt(k, b, SSHKEY_SERIALIZE_DEFAULT);
3937}
3938
3939u_int32_t
3940sshkey_signatures_left(const struct sshkey *k)
3941{
3942 return 0;
3943}
3944
3945int
3946sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign)
3947{
3948 return SSH_ERR_INVALID_ARGUMENT;
3949}
3950
3951int
3952sshkey_set_filename(struct sshkey *k, const char *filename)
3953{
3954 if (k == NULL)
3955 return SSH_ERR_INVALID_ARGUMENT;
3956 return 0;
3957}
3958#endif /* WITH_XMSS */
diff --git a/sshkey.h b/sshkey.h
index 7efa16ff9..c795815fa 100644
--- a/sshkey.h
+++ b/sshkey.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: sshkey.h,v 1.23 2017/12/18 02:25:15 djm Exp $ */ 1/* $OpenBSD: sshkey.h,v 1.24 2018/02/23 15:58:38 markus Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. 4 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@@ -61,6 +61,8 @@ enum sshkey_types {
61 KEY_DSA_CERT, 61 KEY_DSA_CERT,
62 KEY_ECDSA_CERT, 62 KEY_ECDSA_CERT,
63 KEY_ED25519_CERT, 63 KEY_ED25519_CERT,
64 KEY_XMSS,
65 KEY_XMSS_CERT,
64 KEY_UNSPEC 66 KEY_UNSPEC
65}; 67};
66 68
@@ -76,6 +78,14 @@ enum sshkey_fp_rep {
76 SSH_FP_RANDOMART 78 SSH_FP_RANDOMART
77}; 79};
78 80
81/* Private key serialisation formats, used on the wire */
82enum sshkey_serialize_rep {
83 SSHKEY_SERIALIZE_DEFAULT = 0,
84 SSHKEY_SERIALIZE_STATE = 1,
85 SSHKEY_SERIALIZE_FULL = 2,
86 SSHKEY_SERIALIZE_INFO = 254,
87};
88
79/* key is stored in external hardware */ 89/* key is stored in external hardware */
80#define SSHKEY_FLAG_EXT 0x0001 90#define SSHKEY_FLAG_EXT 0x0001
81 91
@@ -104,6 +114,11 @@ struct sshkey {
104 EC_KEY *ecdsa; 114 EC_KEY *ecdsa;
105 u_char *ed25519_sk; 115 u_char *ed25519_sk;
106 u_char *ed25519_pk; 116 u_char *ed25519_pk;
117 char *xmss_name;
118 char *xmss_filename; /* for state file updates */
119 void *xmss_state; /* depends on xmss_name, opaque */
120 u_char *xmss_sk;
121 u_char *xmss_pk;
107 struct sshkey_cert *cert; 122 struct sshkey_cert *cert;
108}; 123};
109 124
@@ -171,6 +186,8 @@ int sshkey_to_blob(const struct sshkey *, u_char **, size_t *);
171int sshkey_to_base64(const struct sshkey *, char **); 186int sshkey_to_base64(const struct sshkey *, char **);
172int sshkey_putb(const struct sshkey *, struct sshbuf *); 187int sshkey_putb(const struct sshkey *, struct sshbuf *);
173int sshkey_puts(const struct sshkey *, struct sshbuf *); 188int sshkey_puts(const struct sshkey *, struct sshbuf *);
189int sshkey_puts_opts(const struct sshkey *, struct sshbuf *,
190 enum sshkey_serialize_rep);
174int sshkey_plain_to_blob(const struct sshkey *, u_char **, size_t *); 191int sshkey_plain_to_blob(const struct sshkey *, u_char **, size_t *);
175int sshkey_putb_plain(const struct sshkey *, struct sshbuf *); 192int sshkey_putb_plain(const struct sshkey *, struct sshbuf *);
176 193
@@ -186,6 +203,8 @@ void sshkey_dump_ec_key(const EC_KEY *);
186 203
187/* private key parsing and serialisation */ 204/* private key parsing and serialisation */
188int sshkey_private_serialize(const struct sshkey *key, struct sshbuf *buf); 205int sshkey_private_serialize(const struct sshkey *key, struct sshbuf *buf);
206int sshkey_private_serialize_opt(const struct sshkey *key, struct sshbuf *buf,
207 enum sshkey_serialize_rep);
189int sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **keyp); 208int sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **keyp);
190 209
191/* private key file format parsing and serialisation */ 210/* private key file format parsing and serialisation */
@@ -200,6 +219,15 @@ int sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type,
200/* XXX should be internal, but used by ssh-keygen */ 219/* XXX should be internal, but used by ssh-keygen */
201int ssh_rsa_generate_additional_parameters(struct sshkey *); 220int ssh_rsa_generate_additional_parameters(struct sshkey *);
202 221
222/* stateful keys (e.g. XMSS) */
223typedef void sshkey_printfn(const char *, ...) __attribute__((format(printf, 1, 2)));
224int sshkey_set_filename(struct sshkey *, const char *);
225int sshkey_enable_maxsign(struct sshkey *, u_int32_t);
226u_int32_t sshkey_signatures_left(const struct sshkey *);
227int sshkey_forward_state(const struct sshkey *, u_int32_t, sshkey_printfn *);
228int sshkey_private_serialize_maxsign(const struct sshkey *key, struct sshbuf *buf,
229 u_int32_t maxsign, sshkey_printfn *pr);
230
203#ifdef SSHKEY_INTERNAL 231#ifdef SSHKEY_INTERNAL
204int ssh_rsa_sign(const struct sshkey *key, 232int ssh_rsa_sign(const struct sshkey *key,
205 u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, 233 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,
222int ssh_ed25519_verify(const struct sshkey *key, 250int ssh_ed25519_verify(const struct sshkey *key,
223 const u_char *signature, size_t signaturelen, 251 const u_char *signature, size_t signaturelen,
224 const u_char *data, size_t datalen, u_int compat); 252 const u_char *data, size_t datalen, u_int compat);
253int ssh_xmss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
254 const u_char *data, size_t datalen, u_int compat);
255int ssh_xmss_verify(const struct sshkey *key,
256 const u_char *signature, size_t signaturelen,
257 const u_char *data, size_t datalen, u_int compat);
225#endif 258#endif
226 259
227#if !defined(WITH_OPENSSL) 260#if !defined(WITH_OPENSSL)
diff --git a/xmss_commons.c b/xmss_commons.c
new file mode 100644
index 000000000..51171af91
--- /dev/null
+++ b/xmss_commons.c
@@ -0,0 +1,27 @@
1/*
2xmss_commons.c 20160722
3Andreas Hülsing
4Joost Rijneveld
5Public domain.
6*/
7
8#include "xmss_commons.h"
9#include <stdlib.h>
10#include <stdio.h>
11#include <stdint.h>
12
13void to_byte(unsigned char *out, unsigned long long in, uint32_t bytes)
14{
15 int32_t i;
16 for (i = bytes-1; i >= 0; i--) {
17 out[i] = in & 0xff;
18 in = in >> 8;
19 }
20}
21
22void hexdump(const unsigned char *a, size_t len)
23{
24 size_t i;
25 for (i = 0; i < len; i++)
26 printf("%02x", a[i]);
27} \ No newline at end of file
diff --git a/xmss_commons.h b/xmss_commons.h
new file mode 100644
index 000000000..32fd4e2dc
--- /dev/null
+++ b/xmss_commons.h
@@ -0,0 +1,15 @@
1/*
2xmss_commons.h 20160722
3Andreas Hülsing
4Joost Rijneveld
5Public domain.
6*/
7#ifndef XMSS_COMMONS_H
8#define XMSS_COMMONS_H
9
10#include <stdlib.h>
11#include <stdint.h>
12
13void to_byte(unsigned char *output, unsigned long long in, uint32_t bytes);
14void hexdump(const unsigned char *a, size_t len);
15#endif \ No newline at end of file
diff --git a/xmss_fast.c b/xmss_fast.c
new file mode 100644
index 000000000..7ddc92f83
--- /dev/null
+++ b/xmss_fast.c
@@ -0,0 +1,1099 @@
1/*
2xmss_fast.c version 20160722
3Andreas Hülsing
4Joost Rijneveld
5Public domain.
6*/
7
8#include "xmss_fast.h"
9#include <stdlib.h>
10#include <string.h>
11#include <stdint.h>
12
13#include "crypto_api.h"
14#include "xmss_wots.h"
15#include "xmss_hash.h"
16
17#include "xmss_commons.h"
18#include "xmss_hash_address.h"
19// For testing
20#include "stdio.h"
21
22
23
24/**
25 * Used for pseudorandom keygeneration,
26 * generates the seed for the WOTS keypair at address addr
27 *
28 * takes n byte sk_seed and returns n byte seed using 32 byte address addr.
29 */
30static void get_seed(unsigned char *seed, const unsigned char *sk_seed, int n, uint32_t addr[8])
31{
32 unsigned char bytes[32];
33 // Make sure that chain addr, hash addr, and key bit are 0!
34 setChainADRS(addr,0);
35 setHashADRS(addr,0);
36 setKeyAndMask(addr,0);
37 // Generate pseudorandom value
38 addr_to_byte(bytes, addr);
39 prf(seed, bytes, sk_seed, n);
40}
41
42/**
43 * Initialize xmss params struct
44 * parameter names are the same as in the draft
45 * parameter k is K as used in the BDS algorithm
46 */
47int xmss_set_params(xmss_params *params, int n, int h, int w, int k)
48{
49 if (k >= h || k < 2 || (h - k) % 2) {
50 fprintf(stderr, "For BDS traversal, H - K must be even, with H > K >= 2!\n");
51 return 1;
52 }
53 params->h = h;
54 params->n = n;
55 params->k = k;
56 wots_params wots_par;
57 wots_set_params(&wots_par, n, w);
58 params->wots_par = wots_par;
59 return 0;
60}
61
62/**
63 * Initialize BDS state struct
64 * parameter names are the same as used in the description of the BDS traversal
65 */
66void 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)
67{
68 state->stack = stack;
69 state->stackoffset = stackoffset;
70 state->stacklevels = stacklevels;
71 state->auth = auth;
72 state->keep = keep;
73 state->treehash = treehash;
74 state->retain = retain;
75 state->next_leaf = next_leaf;
76}
77
78/**
79 * Initialize xmssmt_params struct
80 * parameter names are the same as in the draft
81 *
82 * Especially h is the total tree height, i.e. the XMSS trees have height h/d
83 */
84int xmssmt_set_params(xmssmt_params *params, int n, int h, int d, int w, int k)
85{
86 if (h % d) {
87 fprintf(stderr, "d must divide h without remainder!\n");
88 return 1;
89 }
90 params->h = h;
91 params->d = d;
92 params->n = n;
93 params->index_len = (h + 7) / 8;
94 xmss_params xmss_par;
95 if (xmss_set_params(&xmss_par, n, (h/d), w, k)) {
96 return 1;
97 }
98 params->xmss_par = xmss_par;
99 return 0;
100}
101
102/**
103 * Computes a leaf from a WOTS public key using an L-tree.
104 */
105static void l_tree(unsigned char *leaf, unsigned char *wots_pk, const xmss_params *params, const unsigned char *pub_seed, uint32_t addr[8])
106{
107 unsigned int l = params->wots_par.len;
108 unsigned int n = params->n;
109 uint32_t i = 0;
110 uint32_t height = 0;
111 uint32_t bound;
112
113 //ADRS.setTreeHeight(0);
114 setTreeHeight(addr, height);
115
116 while (l > 1) {
117 bound = l >> 1; //floor(l / 2);
118 for (i = 0; i < bound; i++) {
119 //ADRS.setTreeIndex(i);
120 setTreeIndex(addr, i);
121 //wots_pk[i] = RAND_HASH(pk[2i], pk[2i + 1], SEED, ADRS);
122 hash_h(wots_pk+i*n, wots_pk+i*2*n, pub_seed, addr, n);
123 }
124 //if ( l % 2 == 1 ) {
125 if (l & 1) {
126 //pk[floor(l / 2) + 1] = pk[l];
127 memcpy(wots_pk+(l>>1)*n, wots_pk+(l-1)*n, n);
128 //l = ceil(l / 2);
129 l=(l>>1)+1;
130 }
131 else {
132 //l = ceil(l / 2);
133 l=(l>>1);
134 }
135 //ADRS.setTreeHeight(ADRS.getTreeHeight() + 1);
136 height++;
137 setTreeHeight(addr, height);
138 }
139 //return pk[0];
140 memcpy(leaf, wots_pk, n);
141}
142
143/**
144 * Computes the leaf at a given address. First generates the WOTS key pair, then computes leaf using l_tree. As this happens position independent, we only require that addr encodes the right ltree-address.
145 */
146static void gen_leaf_wots(unsigned char *leaf, const unsigned char *sk_seed, const xmss_params *params, const unsigned char *pub_seed, uint32_t ltree_addr[8], uint32_t ots_addr[8])
147{
148 unsigned char seed[params->n];
149 unsigned char pk[params->wots_par.keysize];
150
151 get_seed(seed, sk_seed, params->n, ots_addr);
152 wots_pkgen(pk, seed, &(params->wots_par), pub_seed, ots_addr);
153
154 l_tree(leaf, pk, params, pub_seed, ltree_addr);
155}
156
157static int treehash_minheight_on_stack(bds_state* state, const xmss_params *params, const treehash_inst *treehash) {
158 unsigned int r = params->h, i;
159 for (i = 0; i < treehash->stackusage; i++) {
160 if (state->stacklevels[state->stackoffset - i - 1] < r) {
161 r = state->stacklevels[state->stackoffset - i - 1];
162 }
163 }
164 return r;
165}
166
167/**
168 * Merkle's TreeHash algorithm. The address only needs to initialize the first 78 bits of addr. Everything else will be set by treehash.
169 * Currently only used for key generation.
170 *
171 */
172static void treehash_setup(unsigned char *node, int height, int index, bds_state *state, const unsigned char *sk_seed, const xmss_params *params, const unsigned char *pub_seed, const uint32_t addr[8])
173{
174 unsigned int idx = index;
175 unsigned int n = params->n;
176 unsigned int h = params->h;
177 unsigned int k = params->k;
178 // use three different addresses because at this point we use all three formats in parallel
179 uint32_t ots_addr[8];
180 uint32_t ltree_addr[8];
181 uint32_t node_addr[8];
182 // only copy layer and tree address parts
183 memcpy(ots_addr, addr, 12);
184 // type = ots
185 setType(ots_addr, 0);
186 memcpy(ltree_addr, addr, 12);
187 setType(ltree_addr, 1);
188 memcpy(node_addr, addr, 12);
189 setType(node_addr, 2);
190
191 uint32_t lastnode, i;
192 unsigned char stack[(height+1)*n];
193 unsigned int stacklevels[height+1];
194 unsigned int stackoffset=0;
195 unsigned int nodeh;
196
197 lastnode = idx+(1<<height);
198
199 for (i = 0; i < h-k; i++) {
200 state->treehash[i].h = i;
201 state->treehash[i].completed = 1;
202 state->treehash[i].stackusage = 0;
203 }
204
205 i = 0;
206 for (; idx < lastnode; idx++) {
207 setLtreeADRS(ltree_addr, idx);
208 setOTSADRS(ots_addr, idx);
209 gen_leaf_wots(stack+stackoffset*n, sk_seed, params, pub_seed, ltree_addr, ots_addr);
210 stacklevels[stackoffset] = 0;
211 stackoffset++;
212 if (h - k > 0 && i == 3) {
213 memcpy(state->treehash[0].node, stack+stackoffset*n, n);
214 }
215 while (stackoffset>1 && stacklevels[stackoffset-1] == stacklevels[stackoffset-2])
216 {
217 nodeh = stacklevels[stackoffset-1];
218 if (i >> nodeh == 1) {
219 memcpy(state->auth + nodeh*n, stack+(stackoffset-1)*n, n);
220 }
221 else {
222 if (nodeh < h - k && i >> nodeh == 3) {
223 memcpy(state->treehash[nodeh].node, stack+(stackoffset-1)*n, n);
224 }
225 else if (nodeh >= h - k) {
226 memcpy(state->retain + ((1 << (h - 1 - nodeh)) + nodeh - h + (((i >> nodeh) - 3) >> 1)) * n, stack+(stackoffset-1)*n, n);
227 }
228 }
229 setTreeHeight(node_addr, stacklevels[stackoffset-1]);
230 setTreeIndex(node_addr, (idx >> (stacklevels[stackoffset-1]+1)));
231 hash_h(stack+(stackoffset-2)*n, stack+(stackoffset-2)*n, pub_seed,
232 node_addr, n);
233 stacklevels[stackoffset-2]++;
234 stackoffset--;
235 }
236 i++;
237 }
238
239 for (i = 0; i < n; i++)
240 node[i] = stack[i];
241}
242
243static void treehash_update(treehash_inst *treehash, bds_state *state, const unsigned char *sk_seed, const xmss_params *params, const unsigned char *pub_seed, const uint32_t addr[8]) {
244 int n = params->n;
245
246 uint32_t ots_addr[8];
247 uint32_t ltree_addr[8];
248 uint32_t node_addr[8];
249 // only copy layer and tree address parts
250 memcpy(ots_addr, addr, 12);
251 // type = ots
252 setType(ots_addr, 0);
253 memcpy(ltree_addr, addr, 12);
254 setType(ltree_addr, 1);
255 memcpy(node_addr, addr, 12);
256 setType(node_addr, 2);
257
258 setLtreeADRS(ltree_addr, treehash->next_idx);
259 setOTSADRS(ots_addr, treehash->next_idx);
260
261 unsigned char nodebuffer[2 * n];
262 unsigned int nodeheight = 0;
263 gen_leaf_wots(nodebuffer, sk_seed, params, pub_seed, ltree_addr, ots_addr);
264 while (treehash->stackusage > 0 && state->stacklevels[state->stackoffset-1] == nodeheight) {
265 memcpy(nodebuffer + n, nodebuffer, n);
266 memcpy(nodebuffer, state->stack + (state->stackoffset-1)*n, n);
267 setTreeHeight(node_addr, nodeheight);
268 setTreeIndex(node_addr, (treehash->next_idx >> (nodeheight+1)));
269 hash_h(nodebuffer, nodebuffer, pub_seed, node_addr, n);
270 nodeheight++;
271 treehash->stackusage--;
272 state->stackoffset--;
273 }
274 if (nodeheight == treehash->h) { // this also implies stackusage == 0
275 memcpy(treehash->node, nodebuffer, n);
276 treehash->completed = 1;
277 }
278 else {
279 memcpy(state->stack + state->stackoffset*n, nodebuffer, n);
280 treehash->stackusage++;
281 state->stacklevels[state->stackoffset] = nodeheight;
282 state->stackoffset++;
283 treehash->next_idx++;
284 }
285}
286
287/**
288 * Computes a root node given a leaf and an authapth
289 */
290static void validate_authpath(unsigned char *root, const unsigned char *leaf, unsigned long leafidx, const unsigned char *authpath, const xmss_params *params, const unsigned char *pub_seed, uint32_t addr[8])
291{
292 unsigned int n = params->n;
293
294 uint32_t i, j;
295 unsigned char buffer[2*n];
296
297 // If leafidx is odd (last bit = 1), current path element is a right child and authpath has to go to the left.
298 // Otherwise, it is the other way around
299 if (leafidx & 1) {
300 for (j = 0; j < n; j++)
301 buffer[n+j] = leaf[j];
302 for (j = 0; j < n; j++)
303 buffer[j] = authpath[j];
304 }
305 else {
306 for (j = 0; j < n; j++)
307 buffer[j] = leaf[j];
308 for (j = 0; j < n; j++)
309 buffer[n+j] = authpath[j];
310 }
311 authpath += n;
312
313 for (i=0; i < params->h-1; i++) {
314 setTreeHeight(addr, i);
315 leafidx >>= 1;
316 setTreeIndex(addr, leafidx);
317 if (leafidx&1) {
318 hash_h(buffer+n, buffer, pub_seed, addr, n);
319 for (j = 0; j < n; j++)
320 buffer[j] = authpath[j];
321 }
322 else {
323 hash_h(buffer, buffer, pub_seed, addr, n);
324 for (j = 0; j < n; j++)
325 buffer[j+n] = authpath[j];
326 }
327 authpath += n;
328 }
329 setTreeHeight(addr, (params->h-1));
330 leafidx >>= 1;
331 setTreeIndex(addr, leafidx);
332 hash_h(root, buffer, pub_seed, addr, n);
333}
334
335/**
336 * Performs one treehash update on the instance that needs it the most.
337 * Returns 1 if such an instance was not found
338 **/
339static char bds_treehash_update(bds_state *state, unsigned int updates, const unsigned char *sk_seed, const xmss_params *params, unsigned char *pub_seed, const uint32_t addr[8]) {
340 uint32_t i, j;
341 unsigned int level, l_min, low;
342 unsigned int h = params->h;
343 unsigned int k = params->k;
344 unsigned int used = 0;
345
346 for (j = 0; j < updates; j++) {
347 l_min = h;
348 level = h - k;
349 for (i = 0; i < h - k; i++) {
350 if (state->treehash[i].completed) {
351 low = h;
352 }
353 else if (state->treehash[i].stackusage == 0) {
354 low = i;
355 }
356 else {
357 low = treehash_minheight_on_stack(state, params, &(state->treehash[i]));
358 }
359 if (low < l_min) {
360 level = i;
361 l_min = low;
362 }
363 }
364 if (level == h - k) {
365 break;
366 }
367 treehash_update(&(state->treehash[level]), state, sk_seed, params, pub_seed, addr);
368 used++;
369 }
370 return updates - used;
371}
372
373/**
374 * Updates the state (typically NEXT_i) by adding a leaf and updating the stack
375 * Returns 1 if all leaf nodes have already been processed
376 **/
377static char bds_state_update(bds_state *state, const unsigned char *sk_seed, const xmss_params *params, unsigned char *pub_seed, const uint32_t addr[8]) {
378 uint32_t ltree_addr[8];
379 uint32_t node_addr[8];
380 uint32_t ots_addr[8];
381
382 int n = params->n;
383 int h = params->h;
384 int k = params->k;
385
386 int nodeh;
387 int idx = state->next_leaf;
388 if (idx == 1 << h) {
389 return 1;
390 }
391
392 // only copy layer and tree address parts
393 memcpy(ots_addr, addr, 12);
394 // type = ots
395 setType(ots_addr, 0);
396 memcpy(ltree_addr, addr, 12);
397 setType(ltree_addr, 1);
398 memcpy(node_addr, addr, 12);
399 setType(node_addr, 2);
400
401 setOTSADRS(ots_addr, idx);
402 setLtreeADRS(ltree_addr, idx);
403
404 gen_leaf_wots(state->stack+state->stackoffset*n, sk_seed, params, pub_seed, ltree_addr, ots_addr);
405
406 state->stacklevels[state->stackoffset] = 0;
407 state->stackoffset++;
408 if (h - k > 0 && idx == 3) {
409 memcpy(state->treehash[0].node, state->stack+state->stackoffset*n, n);
410 }
411 while (state->stackoffset>1 && state->stacklevels[state->stackoffset-1] == state->stacklevels[state->stackoffset-2]) {
412 nodeh = state->stacklevels[state->stackoffset-1];
413 if (idx >> nodeh == 1) {
414 memcpy(state->auth + nodeh*n, state->stack+(state->stackoffset-1)*n, n);
415 }
416 else {
417 if (nodeh < h - k && idx >> nodeh == 3) {
418 memcpy(state->treehash[nodeh].node, state->stack+(state->stackoffset-1)*n, n);
419 }
420 else if (nodeh >= h - k) {
421 memcpy(state->retain + ((1 << (h - 1 - nodeh)) + nodeh - h + (((idx >> nodeh) - 3) >> 1)) * n, state->stack+(state->stackoffset-1)*n, n);
422 }
423 }
424 setTreeHeight(node_addr, state->stacklevels[state->stackoffset-1]);
425 setTreeIndex(node_addr, (idx >> (state->stacklevels[state->stackoffset-1]+1)));
426 hash_h(state->stack+(state->stackoffset-2)*n, state->stack+(state->stackoffset-2)*n, pub_seed, node_addr, n);
427
428 state->stacklevels[state->stackoffset-2]++;
429 state->stackoffset--;
430 }
431 state->next_leaf++;
432 return 0;
433}
434
435/**
436 * Returns the auth path for node leaf_idx and computes the auth path for the
437 * next leaf node, using the algorithm described by Buchmann, Dahmen and Szydlo
438 * in "Post Quantum Cryptography", Springer 2009.
439 */
440static void bds_round(bds_state *state, const unsigned long leaf_idx, const unsigned char *sk_seed, const xmss_params *params, unsigned char *pub_seed, uint32_t addr[8])
441{
442 unsigned int i;
443 unsigned int n = params->n;
444 unsigned int h = params->h;
445 unsigned int k = params->k;
446
447 unsigned int tau = h;
448 unsigned int startidx;
449 unsigned int offset, rowidx;
450 unsigned char buf[2 * n];
451
452 uint32_t ots_addr[8];
453 uint32_t ltree_addr[8];
454 uint32_t node_addr[8];
455 // only copy layer and tree address parts
456 memcpy(ots_addr, addr, 12);
457 // type = ots
458 setType(ots_addr, 0);
459 memcpy(ltree_addr, addr, 12);
460 setType(ltree_addr, 1);
461 memcpy(node_addr, addr, 12);
462 setType(node_addr, 2);
463
464 for (i = 0; i < h; i++) {
465 if (! ((leaf_idx >> i) & 1)) {
466 tau = i;
467 break;
468 }
469 }
470
471 if (tau > 0) {
472 memcpy(buf, state->auth + (tau-1) * n, n);
473 // we need to do this before refreshing state->keep to prevent overwriting
474 memcpy(buf + n, state->keep + ((tau-1) >> 1) * n, n);
475 }
476 if (!((leaf_idx >> (tau + 1)) & 1) && (tau < h - 1)) {
477 memcpy(state->keep + (tau >> 1)*n, state->auth + tau*n, n);
478 }
479 if (tau == 0) {
480 setLtreeADRS(ltree_addr, leaf_idx);
481 setOTSADRS(ots_addr, leaf_idx);
482 gen_leaf_wots(state->auth, sk_seed, params, pub_seed, ltree_addr, ots_addr);
483 }
484 else {
485 setTreeHeight(node_addr, (tau-1));
486 setTreeIndex(node_addr, leaf_idx >> tau);
487 hash_h(state->auth + tau * n, buf, pub_seed, node_addr, n);
488 for (i = 0; i < tau; i++) {
489 if (i < h - k) {
490 memcpy(state->auth + i * n, state->treehash[i].node, n);
491 }
492 else {
493 offset = (1 << (h - 1 - i)) + i - h;
494 rowidx = ((leaf_idx >> i) - 1) >> 1;
495 memcpy(state->auth + i * n, state->retain + (offset + rowidx) * n, n);
496 }
497 }
498
499 for (i = 0; i < ((tau < h - k) ? tau : (h - k)); i++) {
500 startidx = leaf_idx + 1 + 3 * (1 << i);
501 if (startidx < 1U << h) {
502 state->treehash[i].h = i;
503 state->treehash[i].next_idx = startidx;
504 state->treehash[i].completed = 0;
505 state->treehash[i].stackusage = 0;
506 }
507 }
508 }
509}
510
511/*
512 * Generates a XMSS key pair for a given parameter set.
513 * Format sk: [(32bit) idx || SK_SEED || SK_PRF || PUB_SEED || root]
514 * Format pk: [root || PUB_SEED] omitting algo oid.
515 */
516int xmss_keypair(unsigned char *pk, unsigned char *sk, bds_state *state, xmss_params *params)
517{
518 unsigned int n = params->n;
519 // Set idx = 0
520 sk[0] = 0;
521 sk[1] = 0;
522 sk[2] = 0;
523 sk[3] = 0;
524 // Init SK_SEED (n byte), SK_PRF (n byte), and PUB_SEED (n byte)
525 randombytes(sk+4, 3*n);
526 // Copy PUB_SEED to public key
527 memcpy(pk+n, sk+4+2*n, n);
528
529 uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
530
531 // Compute root
532 treehash_setup(pk, params->h, 0, state, sk+4, params, sk+4+2*n, addr);
533 // copy root to sk
534 memcpy(sk+4+3*n, pk, n);
535 return 0;
536}
537
538/**
539 * Signs a message.
540 * Returns
541 * 1. an array containing the signature followed by the message AND
542 * 2. an updated secret key!
543 *
544 */
545int 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)
546{
547 unsigned int h = params->h;
548 unsigned int n = params->n;
549 unsigned int k = params->k;
550 uint16_t i = 0;
551
552 // Extract SK
553 unsigned long idx = ((unsigned long)sk[0] << 24) | ((unsigned long)sk[1] << 16) | ((unsigned long)sk[2] << 8) | sk[3];
554 unsigned char sk_seed[n];
555 memcpy(sk_seed, sk+4, n);
556 unsigned char sk_prf[n];
557 memcpy(sk_prf, sk+4+n, n);
558 unsigned char pub_seed[n];
559 memcpy(pub_seed, sk+4+2*n, n);
560
561 // index as 32 bytes string
562 unsigned char idx_bytes_32[32];
563 to_byte(idx_bytes_32, idx, 32);
564
565 unsigned char hash_key[3*n];
566
567 // Update SK
568 sk[0] = ((idx + 1) >> 24) & 255;
569 sk[1] = ((idx + 1) >> 16) & 255;
570 sk[2] = ((idx + 1) >> 8) & 255;
571 sk[3] = (idx + 1) & 255;
572 // -- Secret key for this non-forward-secure version is now updated.
573 // -- A productive implementation should use a file handle instead and write the updated secret key at this point!
574
575 // Init working params
576 unsigned char R[n];
577 unsigned char msg_h[n];
578 unsigned char ots_seed[n];
579 uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
580
581 // ---------------------------------
582 // Message Hashing
583 // ---------------------------------
584
585 // Message Hash:
586 // First compute pseudorandom value
587 prf(R, idx_bytes_32, sk_prf, n);
588 // Generate hash key (R || root || idx)
589 memcpy(hash_key, R, n);
590 memcpy(hash_key+n, sk+4+3*n, n);
591 to_byte(hash_key+2*n, idx, n);
592 // Then use it for message digest
593 h_msg(msg_h, msg, msglen, hash_key, 3*n, n);
594
595 // Start collecting signature
596 *sig_msg_len = 0;
597
598 // Copy index to signature
599 sig_msg[0] = (idx >> 24) & 255;
600 sig_msg[1] = (idx >> 16) & 255;
601 sig_msg[2] = (idx >> 8) & 255;
602 sig_msg[3] = idx & 255;
603
604 sig_msg += 4;
605 *sig_msg_len += 4;
606
607 // Copy R to signature
608 for (i = 0; i < n; i++)
609 sig_msg[i] = R[i];
610
611 sig_msg += n;
612 *sig_msg_len += n;
613
614 // ----------------------------------
615 // Now we start to "really sign"
616 // ----------------------------------
617
618 // Prepare Address
619 setType(ots_addr, 0);
620 setOTSADRS(ots_addr, idx);
621
622 // Compute seed for OTS key pair
623 get_seed(ots_seed, sk_seed, n, ots_addr);
624
625 // Compute WOTS signature
626 wots_sign(sig_msg, msg_h, ots_seed, &(params->wots_par), pub_seed, ots_addr);
627
628 sig_msg += params->wots_par.keysize;
629 *sig_msg_len += params->wots_par.keysize;
630
631 // the auth path was already computed during the previous round
632 memcpy(sig_msg, state->auth, h*n);
633
634 if (idx < (1U << h) - 1) {
635 bds_round(state, idx, sk_seed, params, pub_seed, ots_addr);
636 bds_treehash_update(state, (h - k) >> 1, sk_seed, params, pub_seed, ots_addr);
637 }
638
639/* TODO: save key/bds state here! */
640
641 sig_msg += params->h*n;
642 *sig_msg_len += params->h*n;
643
644 //Whipe secret elements?
645 //zerobytes(tsk, CRYPTO_SECRETKEYBYTES);
646
647
648 memcpy(sig_msg, msg, msglen);
649 *sig_msg_len += msglen;
650
651 return 0;
652}
653
654/**
655 * Verifies a given message signature pair under a given public key.
656 */
657int 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)
658{
659 unsigned int n = params->n;
660
661 unsigned long long i, m_len;
662 unsigned long idx=0;
663 unsigned char wots_pk[params->wots_par.keysize];
664 unsigned char pkhash[n];
665 unsigned char root[n];
666 unsigned char msg_h[n];
667 unsigned char hash_key[3*n];
668
669 unsigned char pub_seed[n];
670 memcpy(pub_seed, pk+n, n);
671
672 // Init addresses
673 uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
674 uint32_t ltree_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
675 uint32_t node_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
676
677 setType(ots_addr, 0);
678 setType(ltree_addr, 1);
679 setType(node_addr, 2);
680
681 // Extract index
682 idx = ((unsigned long)sig_msg[0] << 24) | ((unsigned long)sig_msg[1] << 16) | ((unsigned long)sig_msg[2] << 8) | sig_msg[3];
683 printf("verify:: idx = %lu\n", idx);
684
685 // Generate hash key (R || root || idx)
686 memcpy(hash_key, sig_msg+4,n);
687 memcpy(hash_key+n, pk, n);
688 to_byte(hash_key+2*n, idx, n);
689
690 sig_msg += (n+4);
691 sig_msg_len -= (n+4);
692
693 // hash message
694 unsigned long long tmp_sig_len = params->wots_par.keysize+params->h*n;
695 m_len = sig_msg_len - tmp_sig_len;
696 h_msg(msg_h, sig_msg + tmp_sig_len, m_len, hash_key, 3*n, n);
697
698 //-----------------------
699 // Verify signature
700 //-----------------------
701
702 // Prepare Address
703 setOTSADRS(ots_addr, idx);
704 // Check WOTS signature
705 wots_pkFromSig(wots_pk, sig_msg, msg_h, &(params->wots_par), pub_seed, ots_addr);
706
707 sig_msg += params->wots_par.keysize;
708 sig_msg_len -= params->wots_par.keysize;
709
710 // Compute Ltree
711 setLtreeADRS(ltree_addr, idx);
712 l_tree(pkhash, wots_pk, params, pub_seed, ltree_addr);
713
714 // Compute root
715 validate_authpath(root, pkhash, idx, sig_msg, params, pub_seed, node_addr);
716
717 sig_msg += params->h*n;
718 sig_msg_len -= params->h*n;
719
720 for (i = 0; i < n; i++)
721 if (root[i] != pk[i])
722 goto fail;
723
724 *msglen = sig_msg_len;
725 for (i = 0; i < *msglen; i++)
726 msg[i] = sig_msg[i];
727
728 return 0;
729
730
731fail:
732 *msglen = sig_msg_len;
733 for (i = 0; i < *msglen; i++)
734 msg[i] = 0;
735 *msglen = -1;
736 return -1;
737}
738
739/*
740 * Generates a XMSSMT key pair for a given parameter set.
741 * Format sk: [(ceil(h/8) bit) idx || SK_SEED || SK_PRF || PUB_SEED || root]
742 * Format pk: [root || PUB_SEED] omitting algo oid.
743 */
744int xmssmt_keypair(unsigned char *pk, unsigned char *sk, bds_state *states, unsigned char *wots_sigs, xmssmt_params *params)
745{
746 unsigned int n = params->n;
747 unsigned int i;
748 unsigned char ots_seed[params->n];
749 // Set idx = 0
750 for (i = 0; i < params->index_len; i++) {
751 sk[i] = 0;
752 }
753 // Init SK_SEED (n byte), SK_PRF (n byte), and PUB_SEED (n byte)
754 randombytes(sk+params->index_len, 3*n);
755 // Copy PUB_SEED to public key
756 memcpy(pk+n, sk+params->index_len+2*n, n);
757
758 // Set address to point on the single tree on layer d-1
759 uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
760 setLayerADRS(addr, (params->d-1));
761 // Set up state and compute wots signatures for all but topmost tree root
762 for (i = 0; i < params->d - 1; i++) {
763 // Compute seed for OTS key pair
764 treehash_setup(pk, params->xmss_par.h, 0, states + i, sk+params->index_len, &(params->xmss_par), pk+n, addr);
765 setLayerADRS(addr, (i+1));
766 get_seed(ots_seed, sk+params->index_len, n, addr);
767 wots_sign(wots_sigs + i*params->xmss_par.wots_par.keysize, pk, ots_seed, &(params->xmss_par.wots_par), pk+n, addr);
768 }
769 treehash_setup(pk, params->xmss_par.h, 0, states + i, sk+params->index_len, &(params->xmss_par), pk+n, addr);
770 memcpy(sk+params->index_len+3*n, pk, n);
771 return 0;
772}
773
774/**
775 * Signs a message.
776 * Returns
777 * 1. an array containing the signature followed by the message AND
778 * 2. an updated secret key!
779 *
780 */
781int xmssmt_sign(unsigned char *sk, bds_state *states, 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)
782{
783 unsigned int n = params->n;
784
785 unsigned int tree_h = params->xmss_par.h;
786 unsigned int h = params->h;
787 unsigned int k = params->xmss_par.k;
788 unsigned int idx_len = params->index_len;
789 uint64_t idx_tree;
790 uint32_t idx_leaf;
791 uint64_t i, j;
792 int needswap_upto = -1;
793 unsigned int updates;
794
795 unsigned char sk_seed[n];
796 unsigned char sk_prf[n];
797 unsigned char pub_seed[n];
798 // Init working params
799 unsigned char R[n];
800 unsigned char msg_h[n];
801 unsigned char hash_key[3*n];
802 unsigned char ots_seed[n];
803 uint32_t addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
804 uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
805 unsigned char idx_bytes_32[32];
806 bds_state tmp;
807
808 // Extract SK
809 unsigned long long idx = 0;
810 for (i = 0; i < idx_len; i++) {
811 idx |= ((unsigned long long)sk[i]) << 8*(idx_len - 1 - i);
812 }
813
814 memcpy(sk_seed, sk+idx_len, n);
815 memcpy(sk_prf, sk+idx_len+n, n);
816 memcpy(pub_seed, sk+idx_len+2*n, n);
817
818 // Update SK
819 for (i = 0; i < idx_len; i++) {
820 sk[i] = ((idx + 1) >> 8*(idx_len - 1 - i)) & 255;
821 }
822 // -- Secret key for this non-forward-secure version is now updated.
823 // -- A productive implementation should use a file handle instead and write the updated secret key at this point!
824
825
826 // ---------------------------------
827 // Message Hashing
828 // ---------------------------------
829
830 // Message Hash:
831 // First compute pseudorandom value
832 to_byte(idx_bytes_32, idx, 32);
833 prf(R, idx_bytes_32, sk_prf, n);
834 // Generate hash key (R || root || idx)
835 memcpy(hash_key, R, n);
836 memcpy(hash_key+n, sk+idx_len+3*n, n);
837 to_byte(hash_key+2*n, idx, n);
838
839 // Then use it for message digest
840 h_msg(msg_h, msg, msglen, hash_key, 3*n, n);
841
842 // Start collecting signature
843 *sig_msg_len = 0;
844
845 // Copy index to signature
846 for (i = 0; i < idx_len; i++) {
847 sig_msg[i] = (idx >> 8*(idx_len - 1 - i)) & 255;
848 }
849
850 sig_msg += idx_len;
851 *sig_msg_len += idx_len;
852
853 // Copy R to signature
854 for (i = 0; i < n; i++)
855 sig_msg[i] = R[i];
856
857 sig_msg += n;
858 *sig_msg_len += n;
859
860 // ----------------------------------
861 // Now we start to "really sign"
862 // ----------------------------------
863
864 // Handle lowest layer separately as it is slightly different...
865
866 // Prepare Address
867 setType(ots_addr, 0);
868 idx_tree = idx >> tree_h;
869 idx_leaf = (idx & ((1 << tree_h)-1));
870 setLayerADRS(ots_addr, 0);
871 setTreeADRS(ots_addr, idx_tree);
872 setOTSADRS(ots_addr, idx_leaf);
873
874 // Compute seed for OTS key pair
875 get_seed(ots_seed, sk_seed, n, ots_addr);
876
877 // Compute WOTS signature
878 wots_sign(sig_msg, msg_h, ots_seed, &(params->xmss_par.wots_par), pub_seed, ots_addr);
879
880 sig_msg += params->xmss_par.wots_par.keysize;
881 *sig_msg_len += params->xmss_par.wots_par.keysize;
882
883 memcpy(sig_msg, states[0].auth, tree_h*n);
884 sig_msg += tree_h*n;
885 *sig_msg_len += tree_h*n;
886
887 // prepare signature of remaining layers
888 for (i = 1; i < params->d; i++) {
889 // put WOTS signature in place
890 memcpy(sig_msg, wots_sigs + (i-1)*params->xmss_par.wots_par.keysize, params->xmss_par.wots_par.keysize);
891
892 sig_msg += params->xmss_par.wots_par.keysize;
893 *sig_msg_len += params->xmss_par.wots_par.keysize;
894
895 // put AUTH nodes in place
896 memcpy(sig_msg, states[i].auth, tree_h*n);
897 sig_msg += tree_h*n;
898 *sig_msg_len += tree_h*n;
899 }
900
901 updates = (tree_h - k) >> 1;
902
903 setTreeADRS(addr, (idx_tree + 1));
904 // mandatory update for NEXT_0 (does not count towards h-k/2) if NEXT_0 exists
905 if ((1 + idx_tree) * (1 << tree_h) + idx_leaf < (1ULL << h)) {
906 bds_state_update(&states[params->d], sk_seed, &(params->xmss_par), pub_seed, addr);
907 }
908
909 for (i = 0; i < params->d; i++) {
910 // check if we're not at the end of a tree
911 if (! (((idx + 1) & ((1ULL << ((i+1)*tree_h)) - 1)) == 0)) {
912 idx_leaf = (idx >> (tree_h * i)) & ((1 << tree_h)-1);
913 idx_tree = (idx >> (tree_h * (i+1)));
914 setLayerADRS(addr, i);
915 setTreeADRS(addr, idx_tree);
916 if (i == (unsigned int) (needswap_upto + 1)) {
917 bds_round(&states[i], idx_leaf, sk_seed, &(params->xmss_par), pub_seed, addr);
918 }
919 updates = bds_treehash_update(&states[i], updates, sk_seed, &(params->xmss_par), pub_seed, addr);
920 setTreeADRS(addr, (idx_tree + 1));
921 // if a NEXT-tree exists for this level;
922 if ((1 + idx_tree) * (1 << tree_h) + idx_leaf < (1ULL << (h - tree_h * i))) {
923 if (i > 0 && updates > 0 && states[params->d + i].next_leaf < (1ULL << h)) {
924 bds_state_update(&states[params->d + i], sk_seed, &(params->xmss_par), pub_seed, addr);
925 updates--;
926 }
927 }
928 }
929 else if (idx < (1ULL << h) - 1) {
930 memcpy(&tmp, states+params->d + i, sizeof(bds_state));
931 memcpy(states+params->d + i, states + i, sizeof(bds_state));
932 memcpy(states + i, &tmp, sizeof(bds_state));
933
934 setLayerADRS(ots_addr, (i+1));
935 setTreeADRS(ots_addr, ((idx + 1) >> ((i+2) * tree_h)));
936 setOTSADRS(ots_addr, (((idx >> ((i+1) * tree_h)) + 1) & ((1 << tree_h)-1)));
937
938 get_seed(ots_seed, sk+params->index_len, n, ots_addr);
939 wots_sign(wots_sigs + i*params->xmss_par.wots_par.keysize, states[i].stack, ots_seed, &(params->xmss_par.wots_par), pub_seed, ots_addr);
940
941 states[params->d + i].stackoffset = 0;
942 states[params->d + i].next_leaf = 0;
943
944 updates--; // WOTS-signing counts as one update
945 needswap_upto = i;
946 for (j = 0; j < tree_h-k; j++) {
947 states[i].treehash[j].completed = 1;
948 }
949 }
950 }
951
952 //Whipe secret elements?
953 //zerobytes(tsk, CRYPTO_SECRETKEYBYTES);
954
955 memcpy(sig_msg, msg, msglen);
956 *sig_msg_len += msglen;
957
958 return 0;
959}
960
961/**
962 * Verifies a given message signature pair under a given public key.
963 */
964int 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)
965{
966 unsigned int n = params->n;
967
968 unsigned int tree_h = params->xmss_par.h;
969 unsigned int idx_len = params->index_len;
970 uint64_t idx_tree;
971 uint32_t idx_leaf;
972
973 unsigned long long i, m_len;
974 unsigned long long idx=0;
975 unsigned char wots_pk[params->xmss_par.wots_par.keysize];
976 unsigned char pkhash[n];
977 unsigned char root[n];
978 unsigned char msg_h[n];
979 unsigned char hash_key[3*n];
980
981 unsigned char pub_seed[n];
982 memcpy(pub_seed, pk+n, n);
983
984 // Init addresses
985 uint32_t ots_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
986 uint32_t ltree_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
987 uint32_t node_addr[8] = {0, 0, 0, 0, 0, 0, 0, 0};
988
989 // Extract index
990 for (i = 0; i < idx_len; i++) {
991 idx |= ((unsigned long long)sig_msg[i]) << (8*(idx_len - 1 - i));
992 }
993 printf("verify:: idx = %llu\n", idx);
994 sig_msg += idx_len;
995 sig_msg_len -= idx_len;
996
997 // Generate hash key (R || root || idx)
998 memcpy(hash_key, sig_msg,n);
999 memcpy(hash_key+n, pk, n);
1000 to_byte(hash_key+2*n, idx, n);
1001
1002 sig_msg += n;
1003 sig_msg_len -= n;
1004
1005
1006 // hash message (recall, R is now on pole position at sig_msg
1007 unsigned long long tmp_sig_len = (params->d * params->xmss_par.wots_par.keysize) + (params->h * n);
1008 m_len = sig_msg_len - tmp_sig_len;
1009 h_msg(msg_h, sig_msg + tmp_sig_len, m_len, hash_key, 3*n, n);
1010
1011
1012 //-----------------------
1013 // Verify signature
1014 //-----------------------
1015
1016 // Prepare Address
1017 idx_tree = idx >> tree_h;
1018 idx_leaf = (idx & ((1 << tree_h)-1));
1019 setLayerADRS(ots_addr, 0);
1020 setTreeADRS(ots_addr, idx_tree);
1021 setType(ots_addr, 0);
1022
1023 memcpy(ltree_addr, ots_addr, 12);
1024 setType(ltree_addr, 1);
1025
1026 memcpy(node_addr, ltree_addr, 12);
1027 setType(node_addr, 2);
1028
1029 setOTSADRS(ots_addr, idx_leaf);
1030
1031 // Check WOTS signature
1032 wots_pkFromSig(wots_pk, sig_msg, msg_h, &(params->xmss_par.wots_par), pub_seed, ots_addr);
1033
1034 sig_msg += params->xmss_par.wots_par.keysize;
1035 sig_msg_len -= params->xmss_par.wots_par.keysize;
1036
1037 // Compute Ltree
1038 setLtreeADRS(ltree_addr, idx_leaf);
1039 l_tree(pkhash, wots_pk, &(params->xmss_par), pub_seed, ltree_addr);
1040
1041 // Compute root
1042 validate_authpath(root, pkhash, idx_leaf, sig_msg, &(params->xmss_par), pub_seed, node_addr);
1043
1044 sig_msg += tree_h*n;
1045 sig_msg_len -= tree_h*n;
1046
1047 for (i = 1; i < params->d; i++) {
1048 // Prepare Address
1049 idx_leaf = (idx_tree & ((1 << tree_h)-1));
1050 idx_tree = idx_tree >> tree_h;
1051
1052 setLayerADRS(ots_addr, i);
1053 setTreeADRS(ots_addr, idx_tree);
1054 setType(ots_addr, 0);
1055
1056 memcpy(ltree_addr, ots_addr, 12);
1057 setType(ltree_addr, 1);
1058
1059 memcpy(node_addr, ltree_addr, 12);
1060 setType(node_addr, 2);
1061
1062 setOTSADRS(ots_addr, idx_leaf);
1063
1064 // Check WOTS signature
1065 wots_pkFromSig(wots_pk, sig_msg, root, &(params->xmss_par.wots_par), pub_seed, ots_addr);
1066
1067 sig_msg += params->xmss_par.wots_par.keysize;
1068 sig_msg_len -= params->xmss_par.wots_par.keysize;
1069
1070 // Compute Ltree
1071 setLtreeADRS(ltree_addr, idx_leaf);
1072 l_tree(pkhash, wots_pk, &(params->xmss_par), pub_seed, ltree_addr);
1073
1074 // Compute root
1075 validate_authpath(root, pkhash, idx_leaf, sig_msg, &(params->xmss_par), pub_seed, node_addr);
1076
1077 sig_msg += tree_h*n;
1078 sig_msg_len -= tree_h*n;
1079
1080 }
1081
1082 for (i = 0; i < n; i++)
1083 if (root[i] != pk[i])
1084 goto fail;
1085
1086 *msglen = sig_msg_len;
1087 for (i = 0; i < *msglen; i++)
1088 msg[i] = sig_msg[i];
1089
1090 return 0;
1091
1092
1093fail:
1094 *msglen = sig_msg_len;
1095 for (i = 0; i < *msglen; i++)
1096 msg[i] = 0;
1097 *msglen = -1;
1098 return -1;
1099}
diff --git a/xmss_fast.h b/xmss_fast.h
new file mode 100644
index 000000000..657cd27f4
--- /dev/null
+++ b/xmss_fast.h
@@ -0,0 +1,109 @@
1/*
2xmss_fast.h version 20160722
3Andreas Hülsing
4Joost Rijneveld
5Public domain.
6*/
7
8#include "xmss_wots.h"
9
10#ifndef XMSS_H
11#define XMSS_H
12typedef struct{
13 unsigned int level;
14 unsigned long long subtree;
15 unsigned int subleaf;
16} leafaddr;
17
18typedef struct{
19 wots_params wots_par;
20 unsigned int n;
21 unsigned int h;
22 unsigned int k;
23} xmss_params;
24
25typedef struct{
26 xmss_params xmss_par;
27 unsigned int n;
28 unsigned int h;
29 unsigned int d;
30 unsigned int index_len;
31} xmssmt_params;
32
33typedef struct{
34 unsigned int h;
35 unsigned int next_idx;
36 unsigned int stackusage;
37 unsigned char completed;
38 unsigned char *node;
39} treehash_inst;
40
41typedef struct {
42 unsigned char *stack;
43 unsigned int stackoffset;
44 unsigned char *stacklevels;
45 unsigned char *auth;
46 unsigned char *keep;
47 treehash_inst *treehash;
48 unsigned char *retain;
49 unsigned int next_leaf;
50} bds_state;
51
52/**
53 * Initialize BDS state struct
54 * parameter names are the same as used in the description of the BDS traversal
55 */
56void 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);
57/**
58 * Initializes parameter set.
59 * Needed, for any of the other methods.
60 */
61int xmss_set_params(xmss_params *params, int n, int h, int w, int k);
62/**
63 * Initialize xmssmt_params struct
64 * parameter names are the same as in the draft
65 *
66 * Especially h is the total tree height, i.e. the XMSS trees have height h/d
67 */
68int xmssmt_set_params(xmssmt_params *params, int n, int h, int d, int w, int k);
69/**
70 * Generates a XMSS key pair for a given parameter set.
71 * Format sk: [(32bit) idx || SK_SEED || SK_PRF || PUB_SEED || root]
72 * Format pk: [root || PUB_SEED] omitting algo oid.
73 */
74int xmss_keypair(unsigned char *pk, unsigned char *sk, bds_state *state, xmss_params *params);
75/**
76 * Signs a message.
77 * Returns
78 * 1. an array containing the signature followed by the message AND
79 * 2. an updated secret key!
80 *
81 */
82int 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);
83/**
84 * Verifies a given message signature pair under a given public key.
85 *
86 * 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).
87 */
88int 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);
89
90/*
91 * Generates a XMSSMT key pair for a given parameter set.
92 * Format sk: [(ceil(h/8) bit) idx || SK_SEED || SK_PRF || PUB_SEED || root]
93 * Format pk: [root || PUB_SEED] omitting algo oid.
94 */
95int xmssmt_keypair(unsigned char *pk, unsigned char *sk, bds_state *states, unsigned char *wots_sigs, xmssmt_params *params);
96/**
97 * Signs a message.
98 * Returns
99 * 1. an array containing the signature followed by the message AND
100 * 2. an updated secret key!
101 *
102 */
103int 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);
104/**
105 * Verifies a given message signature pair under a given public key.
106 */
107int 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);
108#endif
109
diff --git a/xmss_hash.c b/xmss_hash.c
new file mode 100644
index 000000000..963b584b9
--- /dev/null
+++ b/xmss_hash.c
@@ -0,0 +1,133 @@
1/*
2hash.c version 20160722
3Andreas Hülsing
4Joost Rijneveld
5Public domain.
6*/
7
8#include "xmss_hash_address.h"
9#include "xmss_commons.h"
10#include "xmss_hash.h"
11
12#include <stddef.h>
13#include <stdint.h>
14#include <stdio.h>
15#include <string.h>
16#include <openssl/sha.h>
17#include <openssl/hmac.h>
18#include <openssl/evp.h>
19
20int core_hash_SHA2(unsigned char *, const unsigned int, const unsigned char *,
21 unsigned int, const unsigned char *, unsigned long long, unsigned int);
22
23unsigned char* addr_to_byte(unsigned char *bytes, const uint32_t addr[8]){
24#if IS_LITTLE_ENDIAN==1
25 int i = 0;
26 for(i=0;i<8;i++)
27 to_byte(bytes+i*4, addr[i],4);
28 return bytes;
29#else
30 memcpy(bytes, addr, 32);
31 return bytes;
32#endif
33}
34
35int 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){
36 unsigned long long i = 0;
37 unsigned char buf[inlen + n + keylen];
38
39 // Input is (toByte(X, 32) || KEY || M)
40
41 // set toByte
42 to_byte(buf, type, n);
43
44 for (i=0; i < keylen; i++) {
45 buf[i+n] = key[i];
46 }
47
48 for (i=0; i < inlen; i++) {
49 buf[keylen + n + i] = in[i];
50 }
51
52 if (n == 32) {
53 SHA256(buf, inlen + keylen + n, out);
54 return 0;
55 }
56 else {
57 if (n == 64) {
58 SHA512(buf, inlen + keylen + n, out);
59 return 0;
60 }
61 }
62 return 1;
63}
64
65/**
66 * Implements PRF
67 */
68int prf(unsigned char *out, const unsigned char *in, const unsigned char *key, unsigned int keylen)
69{
70 return core_hash_SHA2(out, 3, key, keylen, in, 32, keylen);
71}
72
73/*
74 * Implemts H_msg
75 */
76int 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)
77{
78 if (keylen != 3*n){
79 // H_msg takes 3n-bit keys, but n does not match the keylength of keylen
80 return -1;
81 }
82 return core_hash_SHA2(out, 2, key, keylen, in, inlen, n);
83}
84
85/**
86 * We assume the left half is in in[0]...in[n-1]
87 */
88int hash_h(unsigned char *out, const unsigned char *in, const unsigned char *pub_seed, uint32_t addr[8], const unsigned int n)
89{
90
91 unsigned char buf[2*n];
92 unsigned char key[n];
93 unsigned char bitmask[2*n];
94 unsigned char byte_addr[32];
95 unsigned int i;
96
97 setKeyAndMask(addr, 0);
98 addr_to_byte(byte_addr, addr);
99 prf(key, byte_addr, pub_seed, n);
100 // Use MSB order
101 setKeyAndMask(addr, 1);
102 addr_to_byte(byte_addr, addr);
103 prf(bitmask, byte_addr, pub_seed, n);
104 setKeyAndMask(addr, 2);
105 addr_to_byte(byte_addr, addr);
106 prf(bitmask+n, byte_addr, pub_seed, n);
107 for (i = 0; i < 2*n; i++) {
108 buf[i] = in[i] ^ bitmask[i];
109 }
110 return core_hash_SHA2(out, 1, key, n, buf, 2*n, n);
111}
112
113int hash_f(unsigned char *out, const unsigned char *in, const unsigned char *pub_seed, uint32_t addr[8], const unsigned int n)
114{
115 unsigned char buf[n];
116 unsigned char key[n];
117 unsigned char bitmask[n];
118 unsigned char byte_addr[32];
119 unsigned int i;
120
121 setKeyAndMask(addr, 0);
122 addr_to_byte(byte_addr, addr);
123 prf(key, byte_addr, pub_seed, n);
124
125 setKeyAndMask(addr, 1);
126 addr_to_byte(byte_addr, addr);
127 prf(bitmask, byte_addr, pub_seed, n);
128
129 for (i = 0; i < n; i++) {
130 buf[i] = in[i] ^ bitmask[i];
131 }
132 return core_hash_SHA2(out, 0, key, n, buf, n, n);
133}
diff --git a/xmss_hash.h b/xmss_hash.h
new file mode 100644
index 000000000..2fed73009
--- /dev/null
+++ b/xmss_hash.h
@@ -0,0 +1,19 @@
1/*
2hash.h version 20160722
3Andreas Hülsing
4Joost Rijneveld
5Public domain.
6*/
7
8#ifndef HASH_H
9#define HASH_H
10
11#define IS_LITTLE_ENDIAN 1
12
13unsigned char* addr_to_byte(unsigned char *bytes, const uint32_t addr[8]);
14int prf(unsigned char *out, const unsigned char *in, const unsigned char *key, unsigned int keylen);
15int 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);
16int hash_h(unsigned char *out, const unsigned char *in, const unsigned char *pub_seed, uint32_t addr[8], const unsigned int n);
17int hash_f(unsigned char *out, const unsigned char *in, const unsigned char *pub_seed, uint32_t addr[8], const unsigned int n);
18
19#endif
diff --git a/xmss_hash_address.c b/xmss_hash_address.c
new file mode 100644
index 000000000..223c6f8ab
--- /dev/null
+++ b/xmss_hash_address.c
@@ -0,0 +1,59 @@
1/*
2hash_address.c version 20160722
3Andreas Hülsing
4Joost Rijneveld
5Public domain.
6*/
7#include <stdint.h>
8#include "xmss_hash_address.h" /* prototypes */
9
10void setLayerADRS(uint32_t adrs[8], uint32_t layer){
11 adrs[0] = layer;
12}
13
14void setTreeADRS(uint32_t adrs[8], uint64_t tree){
15 adrs[1] = (uint32_t) (tree >> 32);
16 adrs[2] = (uint32_t) tree;
17}
18
19void setType(uint32_t adrs[8], uint32_t type){
20 adrs[3] = type;
21 int i;
22 for(i = 4; i < 8; i++){
23 adrs[i] = 0;
24 }
25}
26
27void setKeyAndMask(uint32_t adrs[8], uint32_t keyAndMask){
28 adrs[7] = keyAndMask;
29}
30
31// OTS
32
33void setOTSADRS(uint32_t adrs[8], uint32_t ots){
34 adrs[4] = ots;
35}
36
37void setChainADRS(uint32_t adrs[8], uint32_t chain){
38 adrs[5] = chain;
39}
40
41void setHashADRS(uint32_t adrs[8], uint32_t hash){
42 adrs[6] = hash;
43}
44
45// L-tree
46
47void setLtreeADRS(uint32_t adrs[8], uint32_t ltree){
48 adrs[4] = ltree;
49}
50
51// Hash Tree & L-tree
52
53void setTreeHeight(uint32_t adrs[8], uint32_t treeHeight){
54 adrs[5] = treeHeight;
55}
56
57void setTreeIndex(uint32_t adrs[8], uint32_t treeIndex){
58 adrs[6] = treeIndex;
59}
diff --git a/xmss_hash_address.h b/xmss_hash_address.h
new file mode 100644
index 000000000..73cbfd61c
--- /dev/null
+++ b/xmss_hash_address.h
@@ -0,0 +1,37 @@
1/*
2hash_address.h version 20160722
3Andreas Hülsing
4Joost Rijneveld
5Public domain.
6*/
7
8#include <stdint.h>
9
10void setLayerADRS(uint32_t adrs[8], uint32_t layer);
11
12void setTreeADRS(uint32_t adrs[8], uint64_t tree);
13
14void setType(uint32_t adrs[8], uint32_t type);
15
16void setKeyAndMask(uint32_t adrs[8], uint32_t keyAndMask);
17
18// OTS
19
20void setOTSADRS(uint32_t adrs[8], uint32_t ots);
21
22void setChainADRS(uint32_t adrs[8], uint32_t chain);
23
24void setHashADRS(uint32_t adrs[8], uint32_t hash);
25
26// L-tree
27
28void setLtreeADRS(uint32_t adrs[8], uint32_t ltree);
29
30// Hash Tree & L-tree
31
32void setTreeHeight(uint32_t adrs[8], uint32_t treeHeight);
33
34void setTreeIndex(uint32_t adrs[8], uint32_t treeIndex);
35
36
37
diff --git a/xmss_wots.c b/xmss_wots.c
new file mode 100644
index 000000000..fcd033405
--- /dev/null
+++ b/xmss_wots.c
@@ -0,0 +1,185 @@
1/*
2wots.c version 20160722
3Andreas Hülsing
4Joost Rijneveld
5Public domain.
6*/
7
8#include <stdlib.h>
9#include <stdint.h>
10#include <limits.h>
11#include "xmss_commons.h"
12#include "xmss_hash.h"
13#include "xmss_wots.h"
14#include "xmss_hash_address.h"
15
16
17/* libm-free version of log2() for wots */
18static inline int
19wots_log2(uint32_t v)
20{
21 int b;
22
23 for (b = sizeof (v) * CHAR_BIT - 1; b >= 0; b--) {
24 if ((1U << b) & v) {
25 return b;
26 }
27 }
28 return 0;
29}
30
31void
32wots_set_params(wots_params *params, int n, int w)
33{
34 params->n = n;
35 params->w = w;
36 params->log_w = wots_log2(params->w);
37 params->len_1 = (CHAR_BIT * n) / params->log_w;
38 params->len_2 = (wots_log2(params->len_1 * (w - 1)) / params->log_w) + 1;
39 params->len = params->len_1 + params->len_2;
40 params->keysize = params->len * params->n;
41}
42
43/**
44 * Helper method for pseudorandom key generation
45 * Expands an n-byte array into a len*n byte array
46 * this is done using PRF
47 */
48static void expand_seed(unsigned char *outseeds, const unsigned char *inseed, const wots_params *params)
49{
50 uint32_t i = 0;
51 unsigned char ctr[32];
52 for(i = 0; i < params->len; i++){
53 to_byte(ctr, i, 32);
54 prf((outseeds + (i*params->n)), ctr, inseed, params->n);
55 }
56}
57
58/**
59 * Computes the chaining function.
60 * out and in have to be n-byte arrays
61 *
62 * interpretes in as start-th value of the chain
63 * addr has to contain the address of the chain
64 */
65static 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])
66{
67 uint32_t i, j;
68 for (j = 0; j < params->n; j++)
69 out[j] = in[j];
70
71 for (i = start; i < (start+steps) && i < params->w; i++) {
72 setHashADRS(addr, i);
73 hash_f(out, out, pub_seed, addr, params->n);
74 }
75}
76
77/**
78 * base_w algorithm as described in draft.
79 *
80 *
81 */
82static void base_w(int *output, const int out_len, const unsigned char *input, const wots_params *params)
83{
84 int in = 0;
85 int out = 0;
86 uint32_t total = 0;
87 int bits = 0;
88 int consumed = 0;
89
90 for (consumed = 0; consumed < out_len; consumed++) {
91 if (bits == 0) {
92 total = input[in];
93 in++;
94 bits += 8;
95 }
96 bits -= params->log_w;
97 output[out] = (total >> bits) & (params->w - 1);
98 out++;
99 }
100}
101
102void wots_pkgen(unsigned char *pk, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8])
103{
104 uint32_t i;
105 expand_seed(pk, sk, params);
106 for (i=0; i < params->len; i++) {
107 setChainADRS(addr, i);
108 gen_chain(pk+i*params->n, pk+i*params->n, 0, params->w-1, params, pub_seed, addr);
109 }
110}
111
112
113int 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])
114{
115 //int basew[params->len];
116 int csum = 0;
117 uint32_t i = 0;
118 int *basew = calloc(params->len, sizeof(int));
119 if (basew == NULL)
120 return -1;
121
122 base_w(basew, params->len_1, msg, params);
123
124 for (i=0; i < params->len_1; i++) {
125 csum += params->w - 1 - basew[i];
126 }
127
128 csum = csum << (8 - ((params->len_2 * params->log_w) % 8));
129
130 int len_2_bytes = ((params->len_2 * params->log_w) + 7) / 8;
131
132 unsigned char csum_bytes[len_2_bytes];
133 to_byte(csum_bytes, csum, len_2_bytes);
134
135 int csum_basew[params->len_2];
136 base_w(csum_basew, params->len_2, csum_bytes, params);
137
138 for (i = 0; i < params->len_2; i++) {
139 basew[params->len_1 + i] = csum_basew[i];
140 }
141
142 expand_seed(sig, sk, params);
143
144 for (i = 0; i < params->len; i++) {
145 setChainADRS(addr, i);
146 gen_chain(sig+i*params->n, sig+i*params->n, 0, basew[i], params, pub_seed, addr);
147 }
148 free(basew);
149 return 0;
150}
151
152int 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])
153{
154 int csum = 0;
155 uint32_t i = 0;
156 int *basew = calloc(params->len, sizeof(int));
157 if (basew == NULL)
158 return -1;
159
160 base_w(basew, params->len_1, msg, params);
161
162 for (i=0; i < params->len_1; i++) {
163 csum += params->w - 1 - basew[i];
164 }
165
166 csum = csum << (8 - ((params->len_2 * params->log_w) % 8));
167
168 int len_2_bytes = ((params->len_2 * params->log_w) + 7) / 8;
169
170 unsigned char csum_bytes[len_2_bytes];
171 to_byte(csum_bytes, csum, len_2_bytes);
172
173 int csum_basew[params->len_2];
174 base_w(csum_basew, params->len_2, csum_bytes, params);
175
176 for (i = 0; i < params->len_2; i++) {
177 basew[params->len_1 + i] = csum_basew[i];
178 }
179 for (i=0; i < params->len; i++) {
180 setChainADRS(addr, i);
181 gen_chain(pk+i*params->n, sig+i*params->n, basew[i], params->w-1-basew[i], params, pub_seed, addr);
182 }
183 free(basew);
184 return 0;
185}
diff --git a/xmss_wots.h b/xmss_wots.h
new file mode 100644
index 000000000..495431087
--- /dev/null
+++ b/xmss_wots.h
@@ -0,0 +1,59 @@
1/*
2wots.h version 20160722
3Andreas Hülsing
4Joost Rijneveld
5Public domain.
6*/
7
8#ifndef WOTS_H
9#define WOTS_H
10
11#include "stdint.h"
12
13/**
14 * WOTS parameter set
15 *
16 * Meaning as defined in draft-irtf-cfrg-xmss-hash-based-signatures-02
17 */
18typedef struct {
19 uint32_t len_1;
20 uint32_t len_2;
21 uint32_t len;
22 uint32_t n;
23 uint32_t w;
24 uint32_t log_w;
25 uint32_t keysize;
26} wots_params;
27
28/**
29 * Set the WOTS parameters,
30 * only m, n, w are required as inputs,
31 * len, len_1, and len_2 are computed from those.
32 *
33 * Assumes w is a power of 2
34 */
35void wots_set_params(wots_params *params, int n, int w);
36
37/**
38 * 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.
39 * 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
40 *
41 * params, must have been initialized before using wots_set params for params ! This is not done in this function
42 *
43 * Places the computed public key at address pk.
44 */
45void wots_pkgen(unsigned char *pk, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]);
46
47/**
48 * Takes a m-byte message and the 32-byte seed for the secret key to compute a signature that is placed at "sig".
49 *
50 */
51int 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]);
52
53/**
54 * Takes a WOTS signature, a m-byte message and computes a WOTS public key that it places at pk.
55 *
56 */
57int 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]);
58
59#endif