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