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