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 #include "includes.h" 19 #ifdef WITH_XMSS 20 21 #define SSHKEY_INTERNAL 22 #include <sys/types.h> 23 #include <limits.h> 24 25 #include <string.h> 26 #include <stdarg.h> 27 #include <unistd.h> 28 29 #include "log.h" 30 #include "sshbuf.h" 31 #include "sshkey.h" 32 #include "sshkey-xmss.h" 33 #include "ssherr.h" 34 #include "ssh.h" 35 36 #include "xmss_fast.h" 37 38 int 39 ssh_xmss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, 40 const u_char *data, size_t datalen, u_int compat) 41 { 42 u_char *sig = NULL; 43 size_t slen = 0, len = 0, required_siglen; 44 unsigned long long smlen; 45 int r, ret; 46 struct sshbuf *b = NULL; 47 48 if (lenp != NULL) 49 *lenp = 0; 50 if (sigp != NULL) 51 *sigp = NULL; 52 53 if (key == NULL || 54 sshkey_type_plain(key->type) != KEY_XMSS || 55 key->xmss_sk == NULL || 56 sshkey_xmss_params(key) == NULL) 57 return SSH_ERR_INVALID_ARGUMENT; 58 if ((r = sshkey_xmss_siglen(key, &required_siglen)) != 0) 59 return r; 60 if (datalen >= INT_MAX - required_siglen) 61 return SSH_ERR_INVALID_ARGUMENT; 62 smlen = slen = datalen + required_siglen; 63 if ((sig = malloc(slen)) == NULL) 64 return SSH_ERR_ALLOC_FAIL; 65 if ((r = sshkey_xmss_get_state(key, error)) != 0) 66 goto out; 67 if ((ret = xmss_sign(key->xmss_sk, sshkey_xmss_bds_state(key), sig, &smlen, 68 data, datalen, sshkey_xmss_params(key))) != 0 || smlen <= datalen) { 69 r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */ 70 goto out; 71 } 72 /* encode signature */ 73 if ((b = sshbuf_new()) == NULL) { 74 r = SSH_ERR_ALLOC_FAIL; 75 goto out; 76 } 77 if ((r = sshbuf_put_cstring(b, "ssh-xmss@openssh.com")) != 0 || 78 (r = sshbuf_put_string(b, sig, smlen - datalen)) != 0) 79 goto out; 80 len = sshbuf_len(b); 81 if (sigp != NULL) { 82 if ((*sigp = malloc(len)) == NULL) { 83 r = SSH_ERR_ALLOC_FAIL; 84 goto out; 85 } 86 memcpy(*sigp, sshbuf_ptr(b), len); 87 } 88 if (lenp != NULL) 89 *lenp = len; 90 /* success */ 91 r = 0; 92 out: 93 if ((ret = sshkey_xmss_update_state(key, error)) != 0) { 94 /* discard signature since we cannot update the state */ 95 if (r == 0 && sigp != NULL && *sigp != NULL) { 96 explicit_bzero(*sigp, len); 97 free(*sigp); 98 } 99 if (sigp != NULL) 100 *sigp = NULL; 101 if (lenp != NULL) 102 *lenp = 0; 103 r = ret; 104 } 105 sshbuf_free(b); 106 if (sig != NULL) { 107 explicit_bzero(sig, slen); 108 free(sig); 109 } 110 111 return r; 112 } 113 114 int 115 ssh_xmss_verify(const struct sshkey *key, 116 const u_char *signature, size_t signaturelen, 117 const u_char *data, size_t datalen, u_int compat) 118 { 119 struct sshbuf *b = NULL; 120 char *ktype = NULL; 121 const u_char *sigblob; 122 u_char *sm = NULL, *m = NULL; 123 size_t len, required_siglen; 124 unsigned long long smlen = 0, mlen = 0; 125 int r, ret; 126 127 if (key == NULL || 128 sshkey_type_plain(key->type) != KEY_XMSS || 129 key->xmss_pk == NULL || 130 sshkey_xmss_params(key) == NULL || 131 signature == NULL || signaturelen == 0) 132 return SSH_ERR_INVALID_ARGUMENT; 133 if ((r = sshkey_xmss_siglen(key, &required_siglen)) != 0) 134 return r; 135 if (datalen >= INT_MAX - required_siglen) 136 return SSH_ERR_INVALID_ARGUMENT; 137 138 if ((b = sshbuf_from(signature, signaturelen)) == NULL) 139 return SSH_ERR_ALLOC_FAIL; 140 if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 || 141 (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0) 142 goto out; 143 if (strcmp("ssh-xmss@openssh.com", ktype) != 0) { 144 r = SSH_ERR_KEY_TYPE_MISMATCH; 145 goto out; 146 } 147 if (sshbuf_len(b) != 0) { 148 r = SSH_ERR_UNEXPECTED_TRAILING_DATA; 149 goto out; 150 } 151 if (len != required_siglen) { 152 r = SSH_ERR_INVALID_FORMAT; 153 goto out; 154 } 155 if (datalen >= SIZE_MAX - len) { 156 r = SSH_ERR_INVALID_ARGUMENT; 157 goto out; 158 } 159 smlen = len + datalen; 160 mlen = smlen; 161 if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) { 162 r = SSH_ERR_ALLOC_FAIL; 163 goto out; 164 } 165 memcpy(sm, sigblob, len); 166 memcpy(sm+len, data, datalen); 167 if ((ret = xmss_sign_open(m, &mlen, sm, smlen, 168 key->xmss_pk, sshkey_xmss_params(key))) != 0) { 169 debug2("%s: crypto_sign_xmss_open failed: %d", 170 __func__, ret); 171 } 172 if (ret != 0 || mlen != datalen) { 173 r = SSH_ERR_SIGNATURE_INVALID; 174 goto out; 175 } 176 /* XXX compare 'm' and 'data' ? */ 177 /* success */ 178 r = 0; 179 out: 180 if (sm != NULL) { 181 explicit_bzero(sm, smlen); 182 free(sm); 183 } 184 if (m != NULL) { 185 explicit_bzero(m, smlen); /* NB mlen may be invalid if r != 0 */ 186 free(m); 187 } 188 sshbuf_free(b); 189 free(ktype); 190 return r; 191 } 192 #endif /* WITH_XMSS */ 193