diff options
Diffstat (limited to 'sshkey.c')
-rw-r--r-- | sshkey.c | 410 |
1 files changed, 395 insertions, 15 deletions
@@ -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 */ | ||