1 /* $OpenBSD: ssh-xmss.c,v 1.14 2022/10/28 00:44:44 djm 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 <stdlib.h> 26 #include <string.h> 27 #include <stdarg.h> 28 #ifdef HAVE_STDINT_H 29 # include <stdint.h> 30 #endif 31 #include <unistd.h> 32 33 #include "log.h" 34 #include "sshbuf.h" 35 #include "sshkey.h" 36 #include "sshkey-xmss.h" 37 #include "ssherr.h" 38 #include "ssh.h" 39 40 #include "xmss_fast.h" 41 42 static void 43 ssh_xmss_cleanup(struct sshkey *k) 44 { 45 freezero(k->xmss_pk, sshkey_xmss_pklen(k)); 46 freezero(k->xmss_sk, sshkey_xmss_sklen(k)); 47 sshkey_xmss_free_state(k); 48 free(k->xmss_name); 49 free(k->xmss_filename); 50 k->xmss_pk = NULL; 51 k->xmss_sk = NULL; 52 k->xmss_name = NULL; 53 k->xmss_filename = NULL; 54 } 55 56 static int 57 ssh_xmss_equal(const struct sshkey *a, const struct sshkey *b) 58 { 59 if (a->xmss_pk == NULL || b->xmss_pk == NULL) 60 return 0; 61 if (sshkey_xmss_pklen(a) != sshkey_xmss_pklen(b)) 62 return 0; 63 if (memcmp(a->xmss_pk, b->xmss_pk, sshkey_xmss_pklen(a)) != 0) 64 return 0; 65 return 1; 66 } 67 68 static int 69 ssh_xmss_serialize_public(const struct sshkey *key, struct sshbuf *b, 70 enum sshkey_serialize_rep opts) 71 { 72 int r; 73 74 if (key->xmss_name == NULL || key->xmss_pk == NULL || 75 sshkey_xmss_pklen(key) == 0) 76 return SSH_ERR_INVALID_ARGUMENT; 77 if ((r = sshbuf_put_cstring(b, key->xmss_name)) != 0 || 78 (r = sshbuf_put_string(b, key->xmss_pk, 79 sshkey_xmss_pklen(key))) != 0 || 80 (r = sshkey_xmss_serialize_pk_info(key, b, opts)) != 0) 81 return r; 82 83 return 0; 84 } 85 86 static int 87 ssh_xmss_serialize_private(const struct sshkey *key, struct sshbuf *b, 88 enum sshkey_serialize_rep opts) 89 { 90 int r; 91 92 if (key->xmss_name == NULL) 93 return SSH_ERR_INVALID_ARGUMENT; 94 /* Note: can't reuse ssh_xmss_serialize_public because of sk order */ 95 if ((r = sshbuf_put_cstring(b, key->xmss_name)) != 0 || 96 (r = sshbuf_put_string(b, key->xmss_pk, 97 sshkey_xmss_pklen(key))) != 0 || 98 (r = sshbuf_put_string(b, key->xmss_sk, 99 sshkey_xmss_sklen(key))) != 0 || 100 (r = sshkey_xmss_serialize_state_opt(key, b, opts)) != 0) 101 return r; 102 103 return 0; 104 } 105 106 static int 107 ssh_xmss_copy_public(const struct sshkey *from, struct sshkey *to) 108 { 109 int r = SSH_ERR_INTERNAL_ERROR; 110 u_int32_t left; 111 size_t pklen; 112 113 if ((r = sshkey_xmss_init(to, from->xmss_name)) != 0) 114 return r; 115 if (from->xmss_pk == NULL) 116 return 0; /* XXX SSH_ERR_INTERNAL_ERROR ? */ 117 118 if ((pklen = sshkey_xmss_pklen(from)) == 0 || 119 sshkey_xmss_pklen(to) != pklen) 120 return SSH_ERR_INTERNAL_ERROR; 121 if ((to->xmss_pk = malloc(pklen)) == NULL) 122 return SSH_ERR_ALLOC_FAIL; 123 memcpy(to->xmss_pk, from->xmss_pk, pklen); 124 /* simulate number of signatures left on pubkey */ 125 left = sshkey_xmss_signatures_left(from); 126 if (left) 127 sshkey_xmss_enable_maxsign(to, left); 128 return 0; 129 } 130 131 static int 132 ssh_xmss_deserialize_public(const char *ktype, struct sshbuf *b, 133 struct sshkey *key) 134 { 135 size_t len = 0; 136 char *xmss_name = NULL; 137 u_char *pk = NULL; 138 int ret = SSH_ERR_INTERNAL_ERROR; 139 140 if ((ret = sshbuf_get_cstring(b, &xmss_name, NULL)) != 0) 141 goto out; 142 if ((ret = sshkey_xmss_init(key, xmss_name)) != 0) 143 goto out; 144 if ((ret = sshbuf_get_string(b, &pk, &len)) != 0) 145 goto out; 146 if (len == 0 || len != sshkey_xmss_pklen(key)) { 147 ret = SSH_ERR_INVALID_FORMAT; 148 goto out; 149 } 150 key->xmss_pk = pk; 151 pk = NULL; 152 if (!sshkey_is_cert(key) && 153 (ret = sshkey_xmss_deserialize_pk_info(key, b)) != 0) 154 goto out; 155 /* success */ 156 ret = 0; 157 out: 158 free(xmss_name); 159 freezero(pk, len); 160 return ret; 161 } 162 163 static int 164 ssh_xmss_deserialize_private(const char *ktype, struct sshbuf *b, 165 struct sshkey *key) 166 { 167 int r; 168 char *xmss_name = NULL; 169 size_t pklen = 0, sklen = 0; 170 u_char *xmss_pk = NULL, *xmss_sk = NULL; 171 172 /* Note: can't reuse ssh_xmss_deserialize_public because of sk order */ 173 if ((r = sshbuf_get_cstring(b, &xmss_name, NULL)) != 0 || 174 (r = sshbuf_get_string(b, &xmss_pk, &pklen)) != 0 || 175 (r = sshbuf_get_string(b, &xmss_sk, &sklen)) != 0) 176 goto out; 177 if (!sshkey_is_cert(key) && 178 (r = sshkey_xmss_init(key, xmss_name)) != 0) 179 goto out; 180 if (pklen != sshkey_xmss_pklen(key) || 181 sklen != sshkey_xmss_sklen(key)) { 182 r = SSH_ERR_INVALID_FORMAT; 183 goto out; 184 } 185 key->xmss_pk = xmss_pk; 186 key->xmss_sk = xmss_sk; 187 xmss_pk = xmss_sk = NULL; 188 /* optional internal state */ 189 if ((r = sshkey_xmss_deserialize_state_opt(key, b)) != 0) 190 goto out; 191 /* success */ 192 r = 0; 193 out: 194 free(xmss_name); 195 freezero(xmss_pk, pklen); 196 freezero(xmss_sk, sklen); 197 return r; 198 } 199 200 static int 201 ssh_xmss_sign(struct sshkey *key, 202 u_char **sigp, size_t *lenp, 203 const u_char *data, size_t datalen, 204 const char *alg, const char *sk_provider, const char *sk_pin, u_int compat) 205 { 206 u_char *sig = NULL; 207 size_t slen = 0, len = 0, required_siglen; 208 unsigned long long smlen; 209 int r, ret; 210 struct sshbuf *b = NULL; 211 212 if (lenp != NULL) 213 *lenp = 0; 214 if (sigp != NULL) 215 *sigp = NULL; 216 217 if (key == NULL || 218 sshkey_type_plain(key->type) != KEY_XMSS || 219 key->xmss_sk == NULL || 220 sshkey_xmss_params(key) == NULL) 221 return SSH_ERR_INVALID_ARGUMENT; 222 if ((r = sshkey_xmss_siglen(key, &required_siglen)) != 0) 223 return r; 224 if (datalen >= INT_MAX - required_siglen) 225 return SSH_ERR_INVALID_ARGUMENT; 226 smlen = slen = datalen + required_siglen; 227 if ((sig = malloc(slen)) == NULL) 228 return SSH_ERR_ALLOC_FAIL; 229 if ((r = sshkey_xmss_get_state(key, 1)) != 0) 230 goto out; 231 if ((ret = xmss_sign(key->xmss_sk, sshkey_xmss_bds_state(key), sig, &smlen, 232 data, datalen, sshkey_xmss_params(key))) != 0 || smlen <= datalen) { 233 r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */ 234 goto out; 235 } 236 /* encode signature */ 237 if ((b = sshbuf_new()) == NULL) { 238 r = SSH_ERR_ALLOC_FAIL; 239 goto out; 240 } 241 if ((r = sshbuf_put_cstring(b, "ssh-xmss@openssh.com")) != 0 || 242 (r = sshbuf_put_string(b, sig, smlen - datalen)) != 0) 243 goto out; 244 len = sshbuf_len(b); 245 if (sigp != NULL) { 246 if ((*sigp = malloc(len)) == NULL) { 247 r = SSH_ERR_ALLOC_FAIL; 248 goto out; 249 } 250 memcpy(*sigp, sshbuf_ptr(b), len); 251 } 252 if (lenp != NULL) 253 *lenp = len; 254 /* success */ 255 r = 0; 256 out: 257 if ((ret = sshkey_xmss_update_state(key, 1)) != 0) { 258 /* discard signature since we cannot update the state */ 259 if (r == 0 && sigp != NULL && *sigp != NULL) { 260 explicit_bzero(*sigp, len); 261 free(*sigp); 262 } 263 if (sigp != NULL) 264 *sigp = NULL; 265 if (lenp != NULL) 266 *lenp = 0; 267 r = ret; 268 } 269 sshbuf_free(b); 270 if (sig != NULL) 271 freezero(sig, slen); 272 273 return r; 274 } 275 276 static int 277 ssh_xmss_verify(const struct sshkey *key, 278 const u_char *sig, size_t siglen, 279 const u_char *data, size_t dlen, const char *alg, u_int compat, 280 struct sshkey_sig_details **detailsp) 281 { 282 struct sshbuf *b = NULL; 283 char *ktype = NULL; 284 const u_char *sigblob; 285 u_char *sm = NULL, *m = NULL; 286 size_t len, required_siglen; 287 unsigned long long smlen = 0, mlen = 0; 288 int r, ret; 289 290 if (key == NULL || 291 sshkey_type_plain(key->type) != KEY_XMSS || 292 key->xmss_pk == NULL || 293 sshkey_xmss_params(key) == NULL || 294 sig == NULL || siglen == 0) 295 return SSH_ERR_INVALID_ARGUMENT; 296 if ((r = sshkey_xmss_siglen(key, &required_siglen)) != 0) 297 return r; 298 if (dlen >= INT_MAX - required_siglen) 299 return SSH_ERR_INVALID_ARGUMENT; 300 301 if ((b = sshbuf_from(sig, siglen)) == NULL) 302 return SSH_ERR_ALLOC_FAIL; 303 if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 || 304 (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0) 305 goto out; 306 if (strcmp("ssh-xmss@openssh.com", ktype) != 0) { 307 r = SSH_ERR_KEY_TYPE_MISMATCH; 308 goto out; 309 } 310 if (sshbuf_len(b) != 0) { 311 r = SSH_ERR_UNEXPECTED_TRAILING_DATA; 312 goto out; 313 } 314 if (len != required_siglen) { 315 r = SSH_ERR_INVALID_FORMAT; 316 goto out; 317 } 318 if (dlen >= SIZE_MAX - len) { 319 r = SSH_ERR_INVALID_ARGUMENT; 320 goto out; 321 } 322 smlen = len + dlen; 323 mlen = smlen; 324 if ((sm = malloc(smlen)) == NULL || (m = malloc(mlen)) == NULL) { 325 r = SSH_ERR_ALLOC_FAIL; 326 goto out; 327 } 328 memcpy(sm, sigblob, len); 329 memcpy(sm+len, data, dlen); 330 if ((ret = xmss_sign_open(m, &mlen, sm, smlen, 331 key->xmss_pk, sshkey_xmss_params(key))) != 0) { 332 debug2_f("xmss_sign_open failed: %d", ret); 333 } 334 if (ret != 0 || mlen != dlen) { 335 r = SSH_ERR_SIGNATURE_INVALID; 336 goto out; 337 } 338 /* XXX compare 'm' and 'data' ? */ 339 /* success */ 340 r = 0; 341 out: 342 if (sm != NULL) 343 freezero(sm, smlen); 344 if (m != NULL) 345 freezero(m, smlen); 346 sshbuf_free(b); 347 free(ktype); 348 return r; 349 } 350 351 static const struct sshkey_impl_funcs sshkey_xmss_funcs = { 352 /* .size = */ NULL, 353 /* .alloc = */ NULL, 354 /* .cleanup = */ ssh_xmss_cleanup, 355 /* .equal = */ ssh_xmss_equal, 356 /* .ssh_serialize_public = */ ssh_xmss_serialize_public, 357 /* .ssh_deserialize_public = */ ssh_xmss_deserialize_public, 358 /* .ssh_serialize_private = */ ssh_xmss_serialize_private, 359 /* .ssh_deserialize_private = */ ssh_xmss_deserialize_private, 360 /* .generate = */ sshkey_xmss_generate_private_key, 361 /* .copy_public = */ ssh_xmss_copy_public, 362 /* .sign = */ ssh_xmss_sign, 363 /* .verify = */ ssh_xmss_verify, 364 }; 365 366 const struct sshkey_impl sshkey_xmss_impl = { 367 /* .name = */ "ssh-xmss@openssh.com", 368 /* .shortname = */ "XMSS", 369 /* .sigalg = */ NULL, 370 /* .type = */ KEY_XMSS, 371 /* .nid = */ 0, 372 /* .cert = */ 0, 373 /* .sigonly = */ 0, 374 /* .keybits = */ 256, 375 /* .funcs = */ &sshkey_xmss_funcs, 376 }; 377 378 const struct sshkey_impl sshkey_xmss_cert_impl = { 379 /* .name = */ "ssh-xmss-cert-v01@openssh.com", 380 /* .shortname = */ "XMSS-CERT", 381 /* .sigalg = */ NULL, 382 /* .type = */ KEY_XMSS_CERT, 383 /* .nid = */ 0, 384 /* .cert = */ 1, 385 /* .sigonly = */ 0, 386 /* .keybits = */ 256, 387 /* .funcs = */ &sshkey_xmss_funcs, 388 }; 389 #endif /* WITH_XMSS */ 390