diff options
-rw-r--r-- | Makefile.in | 12 | ||||
-rw-r--r-- | authfd.c | 39 | ||||
-rw-r--r-- | authfd.h | 5 | ||||
-rw-r--r-- | authfile.c | 8 | ||||
-rw-r--r-- | cipher.c | 4 | ||||
-rw-r--r-- | dns.c | 7 | ||||
-rw-r--r-- | dns.h | 5 | ||||
-rw-r--r-- | pathnames.h | 4 | ||||
-rw-r--r-- | readconf.c | 3 | ||||
-rw-r--r-- | servconf.c | 4 | ||||
-rw-r--r-- | ssh-add.c | 74 | ||||
-rw-r--r-- | ssh-agent.c | 24 | ||||
-rw-r--r-- | ssh-keygen.c | 19 | ||||
-rw-r--r-- | ssh-keyscan.c | 12 | ||||
-rw-r--r-- | ssh-keysign.c | 5 | ||||
-rw-r--r-- | ssh-xmss.c | 188 | ||||
-rw-r--r-- | ssh.c | 15 | ||||
-rw-r--r-- | sshconnect.c | 5 | ||||
-rw-r--r-- | sshd.c | 6 | ||||
-rw-r--r-- | sshkey-xmss.c | 1048 | ||||
-rw-r--r-- | sshkey-xmss.h | 56 | ||||
-rw-r--r-- | sshkey.c | 410 | ||||
-rw-r--r-- | sshkey.h | 35 | ||||
-rw-r--r-- | xmss_commons.c | 27 | ||||
-rw-r--r-- | xmss_commons.h | 15 | ||||
-rw-r--r-- | xmss_fast.c | 1099 | ||||
-rw-r--r-- | xmss_fast.h | 109 | ||||
-rw-r--r-- | xmss_hash.c | 133 | ||||
-rw-r--r-- | xmss_hash.h | 19 | ||||
-rw-r--r-- | xmss_hash_address.c | 59 | ||||
-rw-r--r-- | xmss_hash_address.h | 37 | ||||
-rw-r--r-- | xmss_wots.c | 185 | ||||
-rw-r--r-- | xmss_wots.h | 59 |
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 | ||
63 | TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) | 63 | TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) |
64 | 64 | ||
65 | XMSS_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 | |||
65 | LIBOPENSSH_OBJS=\ | 74 | LIBOPENSSH_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 | ||
76 | LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ | 86 | LIBSSH_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 \ |
@@ -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 | ||
393 | static int | 393 | static int |
394 | ssh_encode_identity_ssh2(struct sshbuf *b, const struct sshkey *key, | 394 | encode_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 | |||
405 | static int | ||
406 | encode_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 | */ |
428 | int | 421 | int |
429 | ssh_add_identity_constrained(int sock, const struct sshkey *key, | 422 | ssh_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; |
@@ -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); | |||
30 | int ssh_fetch_identitylist(int sock, struct ssh_identitylist **idlp); | 30 | int ssh_fetch_identitylist(int sock, struct ssh_identitylist **idlp); |
31 | void ssh_free_identitylist(struct ssh_identitylist *idl); | 31 | void ssh_free_identitylist(struct ssh_identitylist *idl); |
32 | int ssh_add_identity_constrained(int sock, const struct sshkey *key, | 32 | int 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); |
34 | int ssh_remove_identity(int sock, struct sshkey *key); | 34 | int ssh_remove_identity(int sock, struct sshkey *key); |
35 | int ssh_update_card(int sock, int add, const char *reader_id, | 35 | int 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: |
@@ -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 | ||
@@ -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 */ |
@@ -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 | ||
39 | enum sshfp_hashes { | 40 | enum 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) |
@@ -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 */ |
90 | static int confirm = 0; | 91 | static int confirm = 0; |
91 | 92 | ||
93 | /* Maximum number of signatures (XMSS) */ | ||
94 | static u_int maxsign = 0; | ||
95 | static u_int minleft = 0; | ||
96 | |||
92 | /* we keep a cache of one passphrase */ | 97 | /* we keep a cache of one passphrase */ |
93 | static char *pass = NULL; | 98 | static char *pass = NULL; |
94 | static void | 99 | static 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 | ||
65 | int get_cert = 0; | 66 | int get_cert = 0; |
66 | int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519; | 67 | int 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 | |||
35 | int | ||
36 | ssh_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 | |||
111 | int | ||
112 | ssh_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 | } | ||
@@ -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); |
@@ -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" | ||
48 | struct 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 | |||
72 | int sshkey_xmss_init_bds_state(struct sshkey *); | ||
73 | int sshkey_xmss_init_enc_key(struct sshkey *, const char *); | ||
74 | void sshkey_xmss_free_bds(struct sshkey *); | ||
75 | int sshkey_xmss_get_state_from_file(struct sshkey *, const char *, | ||
76 | int *, sshkey_printfn *); | ||
77 | int sshkey_xmss_encrypt_state(const struct sshkey *, struct sshbuf *, | ||
78 | struct sshbuf **); | ||
79 | int sshkey_xmss_decrypt_state(const struct sshkey *, struct sshbuf *, | ||
80 | struct sshbuf **); | ||
81 | int sshkey_xmss_serialize_enc_key(const struct sshkey *, struct sshbuf *); | ||
82 | int sshkey_xmss_deserialize_enc_key(struct sshkey *, struct sshbuf *); | ||
83 | |||
84 | #define PRINT(s...) do { if (pr) pr(s); } while (0) | ||
85 | |||
86 | int | ||
87 | sshkey_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 | |||
129 | void | ||
130 | sshkey_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 | |||
155 | int | ||
156 | sshkey_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 | |||
181 | void | ||
182 | sshkey_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 | |||
204 | void * | ||
205 | sshkey_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 | |||
214 | void * | ||
215 | sshkey_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 | |||
224 | int | ||
225 | sshkey_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 | |||
239 | size_t | ||
240 | sshkey_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 | |||
249 | size_t | ||
250 | sshkey_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 | |||
259 | int | ||
260 | sshkey_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 | |||
284 | int | ||
285 | sshkey_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 | |||
300 | int | ||
301 | sshkey_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 | |||
316 | int | ||
317 | sshkey_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 | |||
337 | int | ||
338 | sshkey_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 | |||
359 | int | ||
360 | sshkey_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 | |||
387 | int | ||
388 | sshkey_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 | } | ||
427 | done: | ||
428 | if (fd != -1) | ||
429 | close(fd); | ||
430 | free(data); | ||
431 | sshbuf_free(enc); | ||
432 | sshbuf_free(b); | ||
433 | return ret; | ||
434 | } | ||
435 | |||
436 | int | ||
437 | sshkey_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; | ||
517 | done: | ||
518 | if (lockfd != -1) | ||
519 | close(lockfd); | ||
520 | free(lockfile); | ||
521 | free(statefile); | ||
522 | free(ostatefile); | ||
523 | return ret; | ||
524 | } | ||
525 | |||
526 | int | ||
527 | sshkey_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 | |||
559 | int | ||
560 | sshkey_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; | ||
655 | done: | ||
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 | |||
670 | int | ||
671 | sshkey_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 | |||
707 | int | ||
708 | sshkey_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 | |||
737 | int | ||
738 | sshkey_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 | |||
794 | int | ||
795 | sshkey_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 | |||
825 | int | ||
826 | sshkey_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 | |||
916 | int | ||
917 | sshkey_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 | |||
1020 | u_int32_t | ||
1021 | sshkey_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 | |||
1035 | int | ||
1036 | sshkey_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 | |||
33 | size_t sshkey_xmss_pklen(const struct sshkey *); | ||
34 | size_t sshkey_xmss_sklen(const struct sshkey *); | ||
35 | int sshkey_xmss_init(struct sshkey *, const char *); | ||
36 | void sshkey_xmss_free_state(struct sshkey *); | ||
37 | int sshkey_xmss_generate_private_key(struct sshkey *, u_int); | ||
38 | int sshkey_xmss_serialize_state(const struct sshkey *, struct sshbuf *); | ||
39 | int sshkey_xmss_serialize_state_opt(const struct sshkey *, struct sshbuf *, | ||
40 | enum sshkey_serialize_rep); | ||
41 | int sshkey_xmss_serialize_pk_info(const struct sshkey *, struct sshbuf *, | ||
42 | enum sshkey_serialize_rep); | ||
43 | int sshkey_xmss_deserialize_state(struct sshkey *, struct sshbuf *); | ||
44 | int sshkey_xmss_deserialize_state_opt(struct sshkey *, struct sshbuf *); | ||
45 | int sshkey_xmss_deserialize_pk_info(struct sshkey *, struct sshbuf *); | ||
46 | |||
47 | int sshkey_xmss_siglen(const struct sshkey *, size_t *); | ||
48 | void *sshkey_xmss_params(const struct sshkey *); | ||
49 | void *sshkey_xmss_bds_state(const struct sshkey *); | ||
50 | int sshkey_xmss_get_state(const struct sshkey *, sshkey_printfn *); | ||
51 | int sshkey_xmss_enable_maxsign(struct sshkey *, u_int32_t); | ||
52 | int sshkey_xmss_forward_state(const struct sshkey *, u_int32_t); | ||
53 | int sshkey_xmss_update_state(const struct sshkey *, sshkey_printfn *); | ||
54 | u_int32_t sshkey_xmss_signatures_left(const struct sshkey *); | ||
55 | |||
56 | #endif /* SSHKEY_XMSS_H */ | ||
@@ -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 | ||
77 | int sshkey_private_serialize_opt(const struct sshkey *key, | ||
78 | struct sshbuf *buf, enum sshkey_serialize_rep); | ||
74 | static int sshkey_from_blob_internal(struct sshbuf *buf, | 79 | static 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 | ||
698 | static int | 740 | static int |
699 | to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain) | 741 | to_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) | |||
773 | int | 832 | int |
774 | sshkey_putb(const struct sshkey *key, struct sshbuf *b) | 833 | sshkey_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 | ||
779 | int | 838 | int |
780 | sshkey_puts(const struct sshkey *key, struct sshbuf *b) | 839 | sshkey_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 | ||
794 | int | 854 | int |
855 | sshkey_puts(const struct sshkey *key, struct sshbuf *b) | ||
856 | { | ||
857 | return sshkey_puts_opts(key, b, SSHKEY_SERIALIZE_DEFAULT); | ||
858 | } | ||
859 | |||
860 | int | ||
795 | sshkey_putb_plain(const struct sshkey *key, struct sshbuf *b) | 861 | sshkey_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 | ||
800 | static int | 866 | static int |
801 | to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain) | 867 | to_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) | |||
831 | int | 898 | int |
832 | sshkey_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp) | 899 | sshkey_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 | ||
837 | int | 904 | int |
838 | sshkey_plain_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp) | 905 | sshkey_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 | ||
843 | int | 910 | int |
@@ -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 | ||
2470 | int | 2672 | int |
2471 | sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b) | 2673 | sshkey_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 | ||
2567 | int | 2800 | int |
2801 | sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b) | ||
2802 | { | ||
2803 | return sshkey_private_serialize_opt(key, b, | ||
2804 | SSHKEY_SERIALIZE_DEFAULT); | ||
2805 | } | ||
2806 | |||
2807 | int | ||
2568 | sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) | 2808 | sshkey_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 | */ | ||
3878 | int | ||
3879 | sshkey_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; | ||
3894 | out: | ||
3895 | if ((rupdate = sshkey_xmss_update_state(k, pr)) != 0) { | ||
3896 | if (r == 0) | ||
3897 | r = rupdate; | ||
3898 | } | ||
3899 | return r; | ||
3900 | } | ||
3901 | |||
3902 | u_int32_t | ||
3903 | sshkey_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 | |||
3910 | int | ||
3911 | sshkey_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 | |||
3918 | int | ||
3919 | sshkey_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 | ||
3932 | int | ||
3933 | sshkey_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 | |||
3939 | u_int32_t | ||
3940 | sshkey_signatures_left(const struct sshkey *k) | ||
3941 | { | ||
3942 | return 0; | ||
3943 | } | ||
3944 | |||
3945 | int | ||
3946 | sshkey_enable_maxsign(struct sshkey *k, u_int32_t maxsign) | ||
3947 | { | ||
3948 | return SSH_ERR_INVALID_ARGUMENT; | ||
3949 | } | ||
3950 | |||
3951 | int | ||
3952 | sshkey_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 */ | ||
@@ -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 */ | ||
82 | enum 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 *); | |||
171 | int sshkey_to_base64(const struct sshkey *, char **); | 186 | int sshkey_to_base64(const struct sshkey *, char **); |
172 | int sshkey_putb(const struct sshkey *, struct sshbuf *); | 187 | int sshkey_putb(const struct sshkey *, struct sshbuf *); |
173 | int sshkey_puts(const struct sshkey *, struct sshbuf *); | 188 | int sshkey_puts(const struct sshkey *, struct sshbuf *); |
189 | int sshkey_puts_opts(const struct sshkey *, struct sshbuf *, | ||
190 | enum sshkey_serialize_rep); | ||
174 | int sshkey_plain_to_blob(const struct sshkey *, u_char **, size_t *); | 191 | int sshkey_plain_to_blob(const struct sshkey *, u_char **, size_t *); |
175 | int sshkey_putb_plain(const struct sshkey *, struct sshbuf *); | 192 | int 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 */ |
188 | int sshkey_private_serialize(const struct sshkey *key, struct sshbuf *buf); | 205 | int sshkey_private_serialize(const struct sshkey *key, struct sshbuf *buf); |
206 | int sshkey_private_serialize_opt(const struct sshkey *key, struct sshbuf *buf, | ||
207 | enum sshkey_serialize_rep); | ||
189 | int sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **keyp); | 208 | int 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 */ |
201 | int ssh_rsa_generate_additional_parameters(struct sshkey *); | 220 | int ssh_rsa_generate_additional_parameters(struct sshkey *); |
202 | 221 | ||
222 | /* stateful keys (e.g. XMSS) */ | ||
223 | typedef void sshkey_printfn(const char *, ...) __attribute__((format(printf, 1, 2))); | ||
224 | int sshkey_set_filename(struct sshkey *, const char *); | ||
225 | int sshkey_enable_maxsign(struct sshkey *, u_int32_t); | ||
226 | u_int32_t sshkey_signatures_left(const struct sshkey *); | ||
227 | int sshkey_forward_state(const struct sshkey *, u_int32_t, sshkey_printfn *); | ||
228 | int 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 |
204 | int ssh_rsa_sign(const struct sshkey *key, | 232 | int 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, | |||
222 | int ssh_ed25519_verify(const struct sshkey *key, | 250 | int 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); |
253 | int ssh_xmss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, | ||
254 | const u_char *data, size_t datalen, u_int compat); | ||
255 | int 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 | /* | ||
2 | xmss_commons.c 20160722 | ||
3 | Andreas Hülsing | ||
4 | Joost Rijneveld | ||
5 | Public domain. | ||
6 | */ | ||
7 | |||
8 | #include "xmss_commons.h" | ||
9 | #include <stdlib.h> | ||
10 | #include <stdio.h> | ||
11 | #include <stdint.h> | ||
12 | |||
13 | void 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 | |||
22 | void 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 | /* | ||
2 | xmss_commons.h 20160722 | ||
3 | Andreas Hülsing | ||
4 | Joost Rijneveld | ||
5 | Public domain. | ||
6 | */ | ||
7 | #ifndef XMSS_COMMONS_H | ||
8 | #define XMSS_COMMONS_H | ||
9 | |||
10 | #include <stdlib.h> | ||
11 | #include <stdint.h> | ||
12 | |||
13 | void to_byte(unsigned char *output, unsigned long long in, uint32_t bytes); | ||
14 | void 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 | /* | ||
2 | xmss_fast.c version 20160722 | ||
3 | Andreas Hülsing | ||
4 | Joost Rijneveld | ||
5 | Public 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 | */ | ||
30 | static 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 | */ | ||
47 | int 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 | */ | ||
66 | void xmss_set_bds_state(bds_state *state, unsigned char *stack, int stackoffset, unsigned char *stacklevels, unsigned char *auth, unsigned char *keep, treehash_inst *treehash, unsigned char *retain, int next_leaf) | ||
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 | */ | ||
84 | int 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 | */ | ||
105 | static 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 | */ | ||
146 | static 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 | |||
157 | static 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 | */ | ||
172 | static 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 | |||
243 | static 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 | */ | ||
290 | static 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 | **/ | ||
339 | static 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 | **/ | ||
377 | static 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 | */ | ||
440 | static 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 | */ | ||
516 | int 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 | */ | ||
545 | int xmss_sign(unsigned char *sk, bds_state *state, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen, const xmss_params *params) | ||
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 | */ | ||
657 | int xmss_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk, const xmss_params *params) | ||
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 | |||
731 | fail: | ||
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 | */ | ||
744 | int 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 | */ | ||
781 | int 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 | */ | ||
964 | int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk, const xmssmt_params *params) | ||
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 | |||
1093 | fail: | ||
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 | /* | ||
2 | xmss_fast.h version 20160722 | ||
3 | Andreas Hülsing | ||
4 | Joost Rijneveld | ||
5 | Public domain. | ||
6 | */ | ||
7 | |||
8 | #include "xmss_wots.h" | ||
9 | |||
10 | #ifndef XMSS_H | ||
11 | #define XMSS_H | ||
12 | typedef struct{ | ||
13 | unsigned int level; | ||
14 | unsigned long long subtree; | ||
15 | unsigned int subleaf; | ||
16 | } leafaddr; | ||
17 | |||
18 | typedef struct{ | ||
19 | wots_params wots_par; | ||
20 | unsigned int n; | ||
21 | unsigned int h; | ||
22 | unsigned int k; | ||
23 | } xmss_params; | ||
24 | |||
25 | typedef 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 | |||
33 | typedef 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 | |||
41 | typedef 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 | */ | ||
56 | void xmss_set_bds_state(bds_state *state, unsigned char *stack, int stackoffset, unsigned char *stacklevels, unsigned char *auth, unsigned char *keep, treehash_inst *treehash, unsigned char *retain, int next_leaf); | ||
57 | /** | ||
58 | * Initializes parameter set. | ||
59 | * Needed, for any of the other methods. | ||
60 | */ | ||
61 | int 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 | */ | ||
68 | int 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 | */ | ||
74 | int 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 | */ | ||
82 | int xmss_sign(unsigned char *sk, bds_state *state, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg,unsigned long long msglen, const xmss_params *params); | ||
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 | */ | ||
88 | int xmss_sign_open(unsigned char *msg,unsigned long long *msglen, const unsigned char *sig_msg,unsigned long long sig_msg_len, const unsigned char *pk, const xmss_params *params); | ||
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 | */ | ||
95 | int 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 | */ | ||
103 | int xmssmt_sign(unsigned char *sk, bds_state *state, unsigned char *wots_sigs, unsigned char *sig_msg, unsigned long long *sig_msg_len, const unsigned char *msg, unsigned long long msglen, const xmssmt_params *params); | ||
104 | /** | ||
105 | * Verifies a given message signature pair under a given public key. | ||
106 | */ | ||
107 | int xmssmt_sign_open(unsigned char *msg, unsigned long long *msglen, const unsigned char *sig_msg, unsigned long long sig_msg_len, const unsigned char *pk, const xmssmt_params *params); | ||
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 | /* | ||
2 | hash.c version 20160722 | ||
3 | Andreas Hülsing | ||
4 | Joost Rijneveld | ||
5 | Public 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 | |||
20 | int core_hash_SHA2(unsigned char *, const unsigned int, const unsigned char *, | ||
21 | unsigned int, const unsigned char *, unsigned long long, unsigned int); | ||
22 | |||
23 | unsigned 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 | |||
35 | int core_hash_SHA2(unsigned char *out, const unsigned int type, const unsigned char *key, unsigned int keylen, const unsigned char *in, unsigned long long inlen, unsigned int n){ | ||
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 | */ | ||
68 | int 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 | */ | ||
76 | int h_msg(unsigned char *out, const unsigned char *in, unsigned long long inlen, const unsigned char *key, const unsigned int keylen, const unsigned int n) | ||
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 | */ | ||
88 | int 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 | |||
113 | int 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 | /* | ||
2 | hash.h version 20160722 | ||
3 | Andreas Hülsing | ||
4 | Joost Rijneveld | ||
5 | Public domain. | ||
6 | */ | ||
7 | |||
8 | #ifndef HASH_H | ||
9 | #define HASH_H | ||
10 | |||
11 | #define IS_LITTLE_ENDIAN 1 | ||
12 | |||
13 | unsigned char* addr_to_byte(unsigned char *bytes, const uint32_t addr[8]); | ||
14 | int prf(unsigned char *out, const unsigned char *in, const unsigned char *key, unsigned int keylen); | ||
15 | int h_msg(unsigned char *out,const unsigned char *in,unsigned long long inlen, const unsigned char *key, const unsigned int keylen, const unsigned int n); | ||
16 | int hash_h(unsigned char *out, const unsigned char *in, const unsigned char *pub_seed, uint32_t addr[8], const unsigned int n); | ||
17 | int 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 | /* | ||
2 | hash_address.c version 20160722 | ||
3 | Andreas Hülsing | ||
4 | Joost Rijneveld | ||
5 | Public domain. | ||
6 | */ | ||
7 | #include <stdint.h> | ||
8 | #include "xmss_hash_address.h" /* prototypes */ | ||
9 | |||
10 | void setLayerADRS(uint32_t adrs[8], uint32_t layer){ | ||
11 | adrs[0] = layer; | ||
12 | } | ||
13 | |||
14 | void setTreeADRS(uint32_t adrs[8], uint64_t tree){ | ||
15 | adrs[1] = (uint32_t) (tree >> 32); | ||
16 | adrs[2] = (uint32_t) tree; | ||
17 | } | ||
18 | |||
19 | void 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 | |||
27 | void setKeyAndMask(uint32_t adrs[8], uint32_t keyAndMask){ | ||
28 | adrs[7] = keyAndMask; | ||
29 | } | ||
30 | |||
31 | // OTS | ||
32 | |||
33 | void setOTSADRS(uint32_t adrs[8], uint32_t ots){ | ||
34 | adrs[4] = ots; | ||
35 | } | ||
36 | |||
37 | void setChainADRS(uint32_t adrs[8], uint32_t chain){ | ||
38 | adrs[5] = chain; | ||
39 | } | ||
40 | |||
41 | void setHashADRS(uint32_t adrs[8], uint32_t hash){ | ||
42 | adrs[6] = hash; | ||
43 | } | ||
44 | |||
45 | // L-tree | ||
46 | |||
47 | void setLtreeADRS(uint32_t adrs[8], uint32_t ltree){ | ||
48 | adrs[4] = ltree; | ||
49 | } | ||
50 | |||
51 | // Hash Tree & L-tree | ||
52 | |||
53 | void setTreeHeight(uint32_t adrs[8], uint32_t treeHeight){ | ||
54 | adrs[5] = treeHeight; | ||
55 | } | ||
56 | |||
57 | void 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 | /* | ||
2 | hash_address.h version 20160722 | ||
3 | Andreas Hülsing | ||
4 | Joost Rijneveld | ||
5 | Public domain. | ||
6 | */ | ||
7 | |||
8 | #include <stdint.h> | ||
9 | |||
10 | void setLayerADRS(uint32_t adrs[8], uint32_t layer); | ||
11 | |||
12 | void setTreeADRS(uint32_t adrs[8], uint64_t tree); | ||
13 | |||
14 | void setType(uint32_t adrs[8], uint32_t type); | ||
15 | |||
16 | void setKeyAndMask(uint32_t adrs[8], uint32_t keyAndMask); | ||
17 | |||
18 | // OTS | ||
19 | |||
20 | void setOTSADRS(uint32_t adrs[8], uint32_t ots); | ||
21 | |||
22 | void setChainADRS(uint32_t adrs[8], uint32_t chain); | ||
23 | |||
24 | void setHashADRS(uint32_t adrs[8], uint32_t hash); | ||
25 | |||
26 | // L-tree | ||
27 | |||
28 | void setLtreeADRS(uint32_t adrs[8], uint32_t ltree); | ||
29 | |||
30 | // Hash Tree & L-tree | ||
31 | |||
32 | void setTreeHeight(uint32_t adrs[8], uint32_t treeHeight); | ||
33 | |||
34 | void 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 | /* | ||
2 | wots.c version 20160722 | ||
3 | Andreas Hülsing | ||
4 | Joost Rijneveld | ||
5 | Public 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 */ | ||
18 | static inline int | ||
19 | wots_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 | |||
31 | void | ||
32 | wots_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 | */ | ||
48 | static 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 | */ | ||
65 | static void gen_chain(unsigned char *out, const unsigned char *in, unsigned int start, unsigned int steps, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]) | ||
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 | */ | ||
82 | static 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 | |||
102 | void 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 | |||
113 | int wots_sign(unsigned char *sig, const unsigned char *msg, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]) | ||
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 | |||
152 | int wots_pkFromSig(unsigned char *pk, const unsigned char *sig, const unsigned char *msg, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]) | ||
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 | /* | ||
2 | wots.h version 20160722 | ||
3 | Andreas Hülsing | ||
4 | Joost Rijneveld | ||
5 | Public 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 | */ | ||
18 | typedef 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 | */ | ||
35 | void 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 | */ | ||
45 | void 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 | */ | ||
51 | int wots_sign(unsigned char *sig, const unsigned char *msg, const unsigned char *sk, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]); | ||
52 | |||
53 | /** | ||
54 | * Takes a WOTS signature, a m-byte message and computes a WOTS public key that it places at pk. | ||
55 | * | ||
56 | */ | ||
57 | int wots_pkFromSig(unsigned char *pk, const unsigned char *sig, const unsigned char *msg, const wots_params *params, const unsigned char *pub_seed, uint32_t addr[8]); | ||
58 | |||
59 | #endif | ||