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