1 /* $NetBSD: ssh-dss.c,v 1.19 2024/06/25 16:36:54 christos Exp $ */ 2 /* $OpenBSD: ssh-dss.c,v 1.50 2024/01/11 01:45:36 djm Exp $ */ 3 4 /* 5 * Copyright (c) 2000 Markus Friedl. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "includes.h" 29 __RCSID("$NetBSD: ssh-dss.c,v 1.19 2024/06/25 16:36:54 christos Exp $"); 30 #include <sys/types.h> 31 32 #include <openssl/bn.h> 33 #include <openssl/evp.h> 34 35 #include <string.h> 36 37 #include "sshbuf.h" 38 #include "ssherr.h" 39 #include "digest.h" 40 #define SSHKEY_INTERNAL 41 #include "sshkey.h" 42 43 #ifdef WITH_DSA 44 45 #define INTBLOB_LEN 20 46 #define SIGBLOB_LEN (2*INTBLOB_LEN) 47 48 static u_int 49 ssh_dss_size(const struct sshkey *key) 50 { 51 const BIGNUM *dsa_p; 52 53 if (key->dsa == NULL) 54 return 0; 55 DSA_get0_pqg(key->dsa, &dsa_p, NULL, NULL); 56 return BN_num_bits(dsa_p); 57 } 58 59 static int 60 ssh_dss_alloc(struct sshkey *k) 61 { 62 if ((k->dsa = DSA_new()) == NULL) 63 return SSH_ERR_ALLOC_FAIL; 64 return 0; 65 } 66 67 static void 68 ssh_dss_cleanup(struct sshkey *k) 69 { 70 DSA_free(k->dsa); 71 k->dsa = NULL; 72 } 73 74 static int 75 ssh_dss_equal(const struct sshkey *a, const struct sshkey *b) 76 { 77 const BIGNUM *dsa_p_a, *dsa_q_a, *dsa_g_a, *dsa_pub_key_a; 78 const BIGNUM *dsa_p_b, *dsa_q_b, *dsa_g_b, *dsa_pub_key_b; 79 80 if (a->dsa == NULL || b->dsa == NULL) 81 return 0; 82 DSA_get0_pqg(a->dsa, &dsa_p_a, &dsa_q_a, &dsa_g_a); 83 DSA_get0_pqg(b->dsa, &dsa_p_b, &dsa_q_b, &dsa_g_b); 84 DSA_get0_key(a->dsa, &dsa_pub_key_a, NULL); 85 DSA_get0_key(b->dsa, &dsa_pub_key_b, NULL); 86 if (dsa_p_a == NULL || dsa_p_b == NULL || 87 dsa_q_a == NULL || dsa_q_b == NULL || 88 dsa_g_a == NULL || dsa_g_b == NULL || 89 dsa_pub_key_a == NULL || dsa_pub_key_b == NULL) 90 return 0; 91 if (BN_cmp(dsa_p_a, dsa_p_b) != 0) 92 return 0; 93 if (BN_cmp(dsa_q_a, dsa_q_b) != 0) 94 return 0; 95 if (BN_cmp(dsa_g_a, dsa_g_b) != 0) 96 return 0; 97 if (BN_cmp(dsa_pub_key_a, dsa_pub_key_b) != 0) 98 return 0; 99 return 1; 100 } 101 102 static int 103 ssh_dss_serialize_public(const struct sshkey *key, struct sshbuf *b, 104 enum sshkey_serialize_rep opts) 105 { 106 int r; 107 const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key; 108 109 if (key->dsa == NULL) 110 return SSH_ERR_INVALID_ARGUMENT; 111 DSA_get0_pqg(key->dsa, &dsa_p, &dsa_q, &dsa_g); 112 DSA_get0_key(key->dsa, &dsa_pub_key, NULL); 113 if (dsa_p == NULL || dsa_q == NULL || 114 dsa_g == NULL || dsa_pub_key == NULL) 115 return SSH_ERR_INTERNAL_ERROR; 116 if ((r = sshbuf_put_bignum2(b, dsa_p)) != 0 || 117 (r = sshbuf_put_bignum2(b, dsa_q)) != 0 || 118 (r = sshbuf_put_bignum2(b, dsa_g)) != 0 || 119 (r = sshbuf_put_bignum2(b, dsa_pub_key)) != 0) 120 return r; 121 122 return 0; 123 } 124 125 static int 126 ssh_dss_serialize_private(const struct sshkey *key, struct sshbuf *b, 127 enum sshkey_serialize_rep opts) 128 { 129 int r; 130 const BIGNUM *dsa_priv_key; 131 132 DSA_get0_key(key->dsa, NULL, &dsa_priv_key); 133 if (!sshkey_is_cert(key)) { 134 if ((r = ssh_dss_serialize_public(key, b, opts)) != 0) 135 return r; 136 } 137 if ((r = sshbuf_put_bignum2(b, dsa_priv_key)) != 0) 138 return r; 139 140 return 0; 141 } 142 143 static int 144 ssh_dss_generate(struct sshkey *k, int bits) 145 { 146 DSA *private; 147 148 if (bits != 1024) 149 return SSH_ERR_KEY_LENGTH; 150 if ((private = DSA_new()) == NULL) 151 return SSH_ERR_ALLOC_FAIL; 152 if (!DSA_generate_parameters_ex(private, bits, NULL, 0, NULL, 153 NULL, NULL) || !DSA_generate_key(private)) { 154 DSA_free(private); 155 return SSH_ERR_LIBCRYPTO_ERROR; 156 } 157 k->dsa = private; 158 return 0; 159 } 160 161 static int 162 ssh_dss_copy_public(const struct sshkey *from, struct sshkey *to) 163 { 164 const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key; 165 BIGNUM *dsa_p_dup = NULL, *dsa_q_dup = NULL, *dsa_g_dup = NULL; 166 BIGNUM *dsa_pub_key_dup = NULL; 167 int r = SSH_ERR_INTERNAL_ERROR; 168 169 DSA_get0_pqg(from->dsa, &dsa_p, &dsa_q, &dsa_g); 170 DSA_get0_key(from->dsa, &dsa_pub_key, NULL); 171 if ((dsa_p_dup = BN_dup(dsa_p)) == NULL || 172 (dsa_q_dup = BN_dup(dsa_q)) == NULL || 173 (dsa_g_dup = BN_dup(dsa_g)) == NULL || 174 (dsa_pub_key_dup = BN_dup(dsa_pub_key)) == NULL) { 175 r = SSH_ERR_ALLOC_FAIL; 176 goto out; 177 } 178 if (!DSA_set0_pqg(to->dsa, dsa_p_dup, dsa_q_dup, dsa_g_dup)) { 179 r = SSH_ERR_LIBCRYPTO_ERROR; 180 goto out; 181 } 182 dsa_p_dup = dsa_q_dup = dsa_g_dup = NULL; /* transferred */ 183 if (!DSA_set0_key(to->dsa, dsa_pub_key_dup, NULL)) { 184 r = SSH_ERR_LIBCRYPTO_ERROR; 185 goto out; 186 } 187 dsa_pub_key_dup = NULL; /* transferred */ 188 /* success */ 189 r = 0; 190 out: 191 BN_clear_free(dsa_p_dup); 192 BN_clear_free(dsa_q_dup); 193 BN_clear_free(dsa_g_dup); 194 BN_clear_free(dsa_pub_key_dup); 195 return r; 196 } 197 198 static int 199 ssh_dss_deserialize_public(const char *ktype, struct sshbuf *b, 200 struct sshkey *key) 201 { 202 int ret = SSH_ERR_INTERNAL_ERROR; 203 BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL, *dsa_pub_key = NULL; 204 205 if (sshbuf_get_bignum2(b, &dsa_p) != 0 || 206 sshbuf_get_bignum2(b, &dsa_q) != 0 || 207 sshbuf_get_bignum2(b, &dsa_g) != 0 || 208 sshbuf_get_bignum2(b, &dsa_pub_key) != 0) { 209 ret = SSH_ERR_INVALID_FORMAT; 210 goto out; 211 } 212 if (!DSA_set0_pqg(key->dsa, dsa_p, dsa_q, dsa_g)) { 213 ret = SSH_ERR_LIBCRYPTO_ERROR; 214 goto out; 215 } 216 dsa_p = dsa_q = dsa_g = NULL; /* transferred */ 217 if (!DSA_set0_key(key->dsa, dsa_pub_key, NULL)) { 218 ret = SSH_ERR_LIBCRYPTO_ERROR; 219 goto out; 220 } 221 dsa_pub_key = NULL; /* transferred */ 222 #ifdef DEBUG_PK 223 DSA_print_fp(stderr, key->dsa, 8); 224 #endif 225 /* success */ 226 ret = 0; 227 out: 228 BN_clear_free(dsa_p); 229 BN_clear_free(dsa_q); 230 BN_clear_free(dsa_g); 231 BN_clear_free(dsa_pub_key); 232 return ret; 233 } 234 235 static int 236 ssh_dss_deserialize_private(const char *ktype, struct sshbuf *b, 237 struct sshkey *key) 238 { 239 int r; 240 BIGNUM *dsa_priv_key = NULL; 241 242 if (!sshkey_is_cert(key)) { 243 if ((r = ssh_dss_deserialize_public(ktype, b, key)) != 0) 244 return r; 245 } 246 247 if ((r = sshbuf_get_bignum2(b, &dsa_priv_key)) != 0) 248 return r; 249 if (!DSA_set0_key(key->dsa, NULL, dsa_priv_key)) { 250 BN_clear_free(dsa_priv_key); 251 return SSH_ERR_LIBCRYPTO_ERROR; 252 } 253 return 0; 254 } 255 256 static int 257 ssh_dss_sign(struct sshkey *key, 258 u_char **sigp, size_t *lenp, 259 const u_char *data, size_t datalen, 260 const char *alg, const char *sk_provider, const char *sk_pin, u_int compat) 261 { 262 DSA_SIG *sig = NULL; 263 const BIGNUM *sig_r, *sig_s; 264 u_char digest[SSH_DIGEST_MAX_LENGTH], sigblob[SIGBLOB_LEN]; 265 size_t rlen, slen, len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1); 266 struct sshbuf *b = NULL; 267 int ret = SSH_ERR_INVALID_ARGUMENT; 268 269 if (lenp != NULL) 270 *lenp = 0; 271 if (sigp != NULL) 272 *sigp = NULL; 273 274 if (key == NULL || key->dsa == NULL || 275 sshkey_type_plain(key->type) != KEY_DSA) 276 return SSH_ERR_INVALID_ARGUMENT; 277 if (dlen == 0) 278 return SSH_ERR_INTERNAL_ERROR; 279 280 if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen, 281 digest, sizeof(digest))) != 0) 282 goto out; 283 284 if ((sig = DSA_do_sign(digest, dlen, key->dsa)) == NULL) { 285 ret = SSH_ERR_LIBCRYPTO_ERROR; 286 goto out; 287 } 288 289 DSA_SIG_get0(sig, &sig_r, &sig_s); 290 rlen = BN_num_bytes(sig_r); 291 slen = BN_num_bytes(sig_s); 292 if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) { 293 ret = SSH_ERR_INTERNAL_ERROR; 294 goto out; 295 } 296 explicit_bzero(sigblob, SIGBLOB_LEN); 297 BN_bn2bin(sig_r, sigblob + SIGBLOB_LEN - INTBLOB_LEN - rlen); 298 BN_bn2bin(sig_s, sigblob + SIGBLOB_LEN - slen); 299 300 if ((b = sshbuf_new()) == NULL) { 301 ret = SSH_ERR_ALLOC_FAIL; 302 goto out; 303 } 304 if ((ret = sshbuf_put_cstring(b, "ssh-dss")) != 0 || 305 (ret = sshbuf_put_string(b, sigblob, SIGBLOB_LEN)) != 0) 306 goto out; 307 308 len = sshbuf_len(b); 309 if (sigp != NULL) { 310 if ((*sigp = malloc(len)) == NULL) { 311 ret = SSH_ERR_ALLOC_FAIL; 312 goto out; 313 } 314 memcpy(*sigp, sshbuf_ptr(b), len); 315 } 316 if (lenp != NULL) 317 *lenp = len; 318 ret = 0; 319 out: 320 explicit_bzero(digest, sizeof(digest)); 321 DSA_SIG_free(sig); 322 sshbuf_free(b); 323 return ret; 324 } 325 326 static int 327 ssh_dss_verify(const struct sshkey *key, 328 const u_char *sig, size_t siglen, 329 const u_char *data, size_t dlen, const char *alg, u_int compat, 330 struct sshkey_sig_details **detailsp) 331 { 332 DSA_SIG *dsig = NULL; 333 BIGNUM *sig_r = NULL, *sig_s = NULL; 334 u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob = NULL; 335 size_t len, hlen = ssh_digest_bytes(SSH_DIGEST_SHA1); 336 int ret = SSH_ERR_INTERNAL_ERROR; 337 struct sshbuf *b = NULL; 338 char *ktype = NULL; 339 340 if (key == NULL || key->dsa == NULL || 341 sshkey_type_plain(key->type) != KEY_DSA || 342 sig == NULL || siglen == 0) 343 return SSH_ERR_INVALID_ARGUMENT; 344 if (hlen == 0) 345 return SSH_ERR_INTERNAL_ERROR; 346 347 /* fetch signature */ 348 if ((b = sshbuf_from(sig, siglen)) == NULL) 349 return SSH_ERR_ALLOC_FAIL; 350 if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || 351 sshbuf_get_string(b, &sigblob, &len) != 0) { 352 ret = SSH_ERR_INVALID_FORMAT; 353 goto out; 354 } 355 if (strcmp("ssh-dss", ktype) != 0) { 356 ret = SSH_ERR_KEY_TYPE_MISMATCH; 357 goto out; 358 } 359 if (sshbuf_len(b) != 0) { 360 ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; 361 goto out; 362 } 363 364 if (len != SIGBLOB_LEN) { 365 ret = SSH_ERR_INVALID_FORMAT; 366 goto out; 367 } 368 369 /* parse signature */ 370 if ((dsig = DSA_SIG_new()) == NULL || 371 (sig_r = BN_new()) == NULL || 372 (sig_s = BN_new()) == NULL) { 373 ret = SSH_ERR_ALLOC_FAIL; 374 goto out; 375 } 376 if ((BN_bin2bn(sigblob, INTBLOB_LEN, sig_r) == NULL) || 377 (BN_bin2bn(sigblob + INTBLOB_LEN, INTBLOB_LEN, sig_s) == NULL)) { 378 ret = SSH_ERR_LIBCRYPTO_ERROR; 379 goto out; 380 } 381 if (!DSA_SIG_set0(dsig, sig_r, sig_s)) { 382 ret = SSH_ERR_LIBCRYPTO_ERROR; 383 goto out; 384 } 385 sig_r = sig_s = NULL; /* transferred */ 386 387 /* sha1 the data */ 388 if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, dlen, 389 digest, sizeof(digest))) != 0) 390 goto out; 391 392 switch (DSA_do_verify(digest, hlen, dsig, key->dsa)) { 393 case 1: 394 ret = 0; 395 break; 396 case 0: 397 ret = SSH_ERR_SIGNATURE_INVALID; 398 goto out; 399 default: 400 ret = SSH_ERR_LIBCRYPTO_ERROR; 401 goto out; 402 } 403 404 out: 405 explicit_bzero(digest, sizeof(digest)); 406 DSA_SIG_free(dsig); 407 BN_clear_free(sig_r); 408 BN_clear_free(sig_s); 409 sshbuf_free(b); 410 free(ktype); 411 if (sigblob != NULL) 412 freezero(sigblob, len); 413 return ret; 414 } 415 416 static const struct sshkey_impl_funcs sshkey_dss_funcs = { 417 /* .size = */ ssh_dss_size, 418 /* .alloc = */ ssh_dss_alloc, 419 /* .cleanup = */ ssh_dss_cleanup, 420 /* .equal = */ ssh_dss_equal, 421 /* .ssh_serialize_public = */ ssh_dss_serialize_public, 422 /* .ssh_deserialize_public = */ ssh_dss_deserialize_public, 423 /* .ssh_serialize_private = */ ssh_dss_serialize_private, 424 /* .ssh_deserialize_private = */ ssh_dss_deserialize_private, 425 /* .generate = */ ssh_dss_generate, 426 /* .copy_public = */ ssh_dss_copy_public, 427 /* .sign = */ ssh_dss_sign, 428 /* .verify = */ ssh_dss_verify, 429 }; 430 431 const struct sshkey_impl sshkey_dss_impl = { 432 /* .name = */ "ssh-dss", 433 /* .shortname = */ "DSA", 434 /* .sigalg = */ NULL, 435 /* .type = */ KEY_DSA, 436 /* .nid = */ 0, 437 /* .cert = */ 0, 438 /* .sigonly = */ 0, 439 /* .keybits = */ 0, 440 /* .funcs = */ &sshkey_dss_funcs, 441 }; 442 443 const struct sshkey_impl sshkey_dsa_cert_impl = { 444 /* .name = */ "ssh-dss-cert-v01@openssh.com", 445 /* .shortname = */ "DSA-CERT", 446 /* .sigalg = */ NULL, 447 /* .type = */ KEY_DSA_CERT, 448 /* .nid = */ 0, 449 /* .cert = */ 1, 450 /* .sigonly = */ 0, 451 /* .keybits = */ 0, 452 /* .funcs = */ &sshkey_dss_funcs, 453 }; 454 455 #endif /* WITH_DSA */ 456