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