1 /* $OpenBSD: ssh-dss.c,v 1.48 2022/10/28 00:44:44 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 #ifdef WITH_OPENSSL 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 "compat.h" 41 #include "ssherr.h" 42 #include "digest.h" 43 #define SSHKEY_INTERNAL 44 #include "sshkey.h" 45 46 #include "openbsd-compat/openssl-compat.h" 47 48 #define INTBLOB_LEN 20 49 #define SIGBLOB_LEN (2*INTBLOB_LEN) 50 51 static u_int 52 ssh_dss_size(const struct sshkey *key) 53 { 54 const BIGNUM *dsa_p; 55 56 if (key->dsa == NULL) 57 return 0; 58 DSA_get0_pqg(key->dsa, &dsa_p, NULL, NULL); 59 return BN_num_bits(dsa_p); 60 } 61 62 static int 63 ssh_dss_alloc(struct sshkey *k) 64 { 65 if ((k->dsa = DSA_new()) == NULL) 66 return SSH_ERR_ALLOC_FAIL; 67 return 0; 68 } 69 70 static void 71 ssh_dss_cleanup(struct sshkey *k) 72 { 73 DSA_free(k->dsa); 74 k->dsa = NULL; 75 } 76 77 static int 78 ssh_dss_equal(const struct sshkey *a, const struct sshkey *b) 79 { 80 const BIGNUM *dsa_p_a, *dsa_q_a, *dsa_g_a, *dsa_pub_key_a; 81 const BIGNUM *dsa_p_b, *dsa_q_b, *dsa_g_b, *dsa_pub_key_b; 82 83 if (a->dsa == NULL || b->dsa == NULL) 84 return 0; 85 DSA_get0_pqg(a->dsa, &dsa_p_a, &dsa_q_a, &dsa_g_a); 86 DSA_get0_pqg(b->dsa, &dsa_p_b, &dsa_q_b, &dsa_g_b); 87 DSA_get0_key(a->dsa, &dsa_pub_key_a, NULL); 88 DSA_get0_key(b->dsa, &dsa_pub_key_b, NULL); 89 if (dsa_p_a == NULL || dsa_p_b == NULL || 90 dsa_q_a == NULL || dsa_q_b == NULL || 91 dsa_g_a == NULL || dsa_g_b == NULL || 92 dsa_pub_key_a == NULL || dsa_pub_key_b == NULL) 93 return 0; 94 if (BN_cmp(dsa_p_a, dsa_p_b) != 0) 95 return 0; 96 if (BN_cmp(dsa_q_a, dsa_q_b) != 0) 97 return 0; 98 if (BN_cmp(dsa_g_a, dsa_g_b) != 0) 99 return 0; 100 if (BN_cmp(dsa_pub_key_a, dsa_pub_key_b) != 0) 101 return 0; 102 return 1; 103 } 104 105 static int 106 ssh_dss_serialize_public(const struct sshkey *key, struct sshbuf *b, 107 enum sshkey_serialize_rep opts) 108 { 109 int r; 110 const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key; 111 112 if (key->dsa == NULL) 113 return SSH_ERR_INVALID_ARGUMENT; 114 DSA_get0_pqg(key->dsa, &dsa_p, &dsa_q, &dsa_g); 115 DSA_get0_key(key->dsa, &dsa_pub_key, NULL); 116 if (dsa_p == NULL || dsa_q == NULL || 117 dsa_g == NULL || dsa_pub_key == NULL) 118 return SSH_ERR_INTERNAL_ERROR; 119 if ((r = sshbuf_put_bignum2(b, dsa_p)) != 0 || 120 (r = sshbuf_put_bignum2(b, dsa_q)) != 0 || 121 (r = sshbuf_put_bignum2(b, dsa_g)) != 0 || 122 (r = sshbuf_put_bignum2(b, dsa_pub_key)) != 0) 123 return r; 124 125 return 0; 126 } 127 128 static int 129 ssh_dss_serialize_private(const struct sshkey *key, struct sshbuf *b, 130 enum sshkey_serialize_rep opts) 131 { 132 int r; 133 const BIGNUM *dsa_priv_key; 134 135 DSA_get0_key(key->dsa, NULL, &dsa_priv_key); 136 if (!sshkey_is_cert(key)) { 137 if ((r = ssh_dss_serialize_public(key, b, opts)) != 0) 138 return r; 139 } 140 if ((r = sshbuf_put_bignum2(b, dsa_priv_key)) != 0) 141 return r; 142 143 return 0; 144 } 145 146 static int 147 ssh_dss_generate(struct sshkey *k, int bits) 148 { 149 DSA *private; 150 151 if (bits != 1024) 152 return SSH_ERR_KEY_LENGTH; 153 if ((private = DSA_new()) == NULL) 154 return SSH_ERR_ALLOC_FAIL; 155 if (!DSA_generate_parameters_ex(private, bits, NULL, 0, NULL, 156 NULL, NULL) || !DSA_generate_key(private)) { 157 DSA_free(private); 158 return SSH_ERR_LIBCRYPTO_ERROR; 159 } 160 k->dsa = private; 161 return 0; 162 } 163 164 static int 165 ssh_dss_copy_public(const struct sshkey *from, struct sshkey *to) 166 { 167 const BIGNUM *dsa_p, *dsa_q, *dsa_g, *dsa_pub_key; 168 BIGNUM *dsa_p_dup = NULL, *dsa_q_dup = NULL, *dsa_g_dup = NULL; 169 BIGNUM *dsa_pub_key_dup = NULL; 170 int r = SSH_ERR_INTERNAL_ERROR; 171 172 DSA_get0_pqg(from->dsa, &dsa_p, &dsa_q, &dsa_g); 173 DSA_get0_key(from->dsa, &dsa_pub_key, NULL); 174 if ((dsa_p_dup = BN_dup(dsa_p)) == NULL || 175 (dsa_q_dup = BN_dup(dsa_q)) == NULL || 176 (dsa_g_dup = BN_dup(dsa_g)) == NULL || 177 (dsa_pub_key_dup = BN_dup(dsa_pub_key)) == NULL) { 178 r = SSH_ERR_ALLOC_FAIL; 179 goto out; 180 } 181 if (!DSA_set0_pqg(to->dsa, dsa_p_dup, dsa_q_dup, dsa_g_dup)) { 182 r = SSH_ERR_LIBCRYPTO_ERROR; 183 goto out; 184 } 185 dsa_p_dup = dsa_q_dup = dsa_g_dup = NULL; /* transferred */ 186 if (!DSA_set0_key(to->dsa, dsa_pub_key_dup, NULL)) { 187 r = SSH_ERR_LIBCRYPTO_ERROR; 188 goto out; 189 } 190 dsa_pub_key_dup = NULL; /* transferred */ 191 /* success */ 192 r = 0; 193 out: 194 BN_clear_free(dsa_p_dup); 195 BN_clear_free(dsa_q_dup); 196 BN_clear_free(dsa_g_dup); 197 BN_clear_free(dsa_pub_key_dup); 198 return r; 199 } 200 201 static int 202 ssh_dss_deserialize_public(const char *ktype, struct sshbuf *b, 203 struct sshkey *key) 204 { 205 int ret = SSH_ERR_INTERNAL_ERROR; 206 BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL, *dsa_pub_key = NULL; 207 208 if (sshbuf_get_bignum2(b, &dsa_p) != 0 || 209 sshbuf_get_bignum2(b, &dsa_q) != 0 || 210 sshbuf_get_bignum2(b, &dsa_g) != 0 || 211 sshbuf_get_bignum2(b, &dsa_pub_key) != 0) { 212 ret = SSH_ERR_INVALID_FORMAT; 213 goto out; 214 } 215 if (!DSA_set0_pqg(key->dsa, dsa_p, dsa_q, dsa_g)) { 216 ret = SSH_ERR_LIBCRYPTO_ERROR; 217 goto out; 218 } 219 dsa_p = dsa_q = dsa_g = NULL; /* transferred */ 220 if (!DSA_set0_key(key->dsa, dsa_pub_key, NULL)) { 221 ret = SSH_ERR_LIBCRYPTO_ERROR; 222 goto out; 223 } 224 dsa_pub_key = NULL; /* transferred */ 225 #ifdef DEBUG_PK 226 DSA_print_fp(stderr, key->dsa, 8); 227 #endif 228 /* success */ 229 ret = 0; 230 out: 231 BN_clear_free(dsa_p); 232 BN_clear_free(dsa_q); 233 BN_clear_free(dsa_g); 234 BN_clear_free(dsa_pub_key); 235 return ret; 236 } 237 238 static int 239 ssh_dss_deserialize_private(const char *ktype, struct sshbuf *b, 240 struct sshkey *key) 241 { 242 int r; 243 BIGNUM *dsa_priv_key = NULL; 244 245 if (!sshkey_is_cert(key)) { 246 if ((r = ssh_dss_deserialize_public(ktype, b, key)) != 0) 247 return r; 248 } 249 250 if ((r = sshbuf_get_bignum2(b, &dsa_priv_key)) != 0) 251 return r; 252 if (!DSA_set0_key(key->dsa, NULL, dsa_priv_key)) { 253 BN_clear_free(dsa_priv_key); 254 return SSH_ERR_LIBCRYPTO_ERROR; 255 } 256 return 0; 257 } 258 259 static int 260 ssh_dss_sign(struct sshkey *key, 261 u_char **sigp, size_t *lenp, 262 const u_char *data, size_t datalen, 263 const char *alg, const char *sk_provider, const char *sk_pin, u_int compat) 264 { 265 DSA_SIG *sig = NULL; 266 const BIGNUM *sig_r, *sig_s; 267 u_char digest[SSH_DIGEST_MAX_LENGTH], sigblob[SIGBLOB_LEN]; 268 size_t rlen, slen, len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1); 269 struct sshbuf *b = NULL; 270 int ret = SSH_ERR_INVALID_ARGUMENT; 271 272 if (lenp != NULL) 273 *lenp = 0; 274 if (sigp != NULL) 275 *sigp = NULL; 276 277 if (key == NULL || key->dsa == NULL || 278 sshkey_type_plain(key->type) != KEY_DSA) 279 return SSH_ERR_INVALID_ARGUMENT; 280 if (dlen == 0) 281 return SSH_ERR_INTERNAL_ERROR; 282 283 if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen, 284 digest, sizeof(digest))) != 0) 285 goto out; 286 287 if ((sig = DSA_do_sign(digest, dlen, key->dsa)) == NULL) { 288 ret = SSH_ERR_LIBCRYPTO_ERROR; 289 goto out; 290 } 291 292 DSA_SIG_get0(sig, &sig_r, &sig_s); 293 rlen = BN_num_bytes(sig_r); 294 slen = BN_num_bytes(sig_s); 295 if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) { 296 ret = SSH_ERR_INTERNAL_ERROR; 297 goto out; 298 } 299 explicit_bzero(sigblob, SIGBLOB_LEN); 300 BN_bn2bin(sig_r, sigblob + SIGBLOB_LEN - INTBLOB_LEN - rlen); 301 BN_bn2bin(sig_s, sigblob + SIGBLOB_LEN - slen); 302 303 if ((b = sshbuf_new()) == NULL) { 304 ret = SSH_ERR_ALLOC_FAIL; 305 goto out; 306 } 307 if ((ret = sshbuf_put_cstring(b, "ssh-dss")) != 0 || 308 (ret = sshbuf_put_string(b, sigblob, SIGBLOB_LEN)) != 0) 309 goto out; 310 311 len = sshbuf_len(b); 312 if (sigp != NULL) { 313 if ((*sigp = malloc(len)) == NULL) { 314 ret = SSH_ERR_ALLOC_FAIL; 315 goto out; 316 } 317 memcpy(*sigp, sshbuf_ptr(b), len); 318 } 319 if (lenp != NULL) 320 *lenp = len; 321 ret = 0; 322 out: 323 explicit_bzero(digest, sizeof(digest)); 324 DSA_SIG_free(sig); 325 sshbuf_free(b); 326 return ret; 327 } 328 329 static int 330 ssh_dss_verify(const struct sshkey *key, 331 const u_char *sig, size_t siglen, 332 const u_char *data, size_t dlen, const char *alg, u_int compat, 333 struct sshkey_sig_details **detailsp) 334 { 335 DSA_SIG *dsig = NULL; 336 BIGNUM *sig_r = NULL, *sig_s = NULL; 337 u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob = NULL; 338 size_t len, hlen = ssh_digest_bytes(SSH_DIGEST_SHA1); 339 int ret = SSH_ERR_INTERNAL_ERROR; 340 struct sshbuf *b = NULL; 341 char *ktype = NULL; 342 343 if (key == NULL || key->dsa == NULL || 344 sshkey_type_plain(key->type) != KEY_DSA || 345 sig == NULL || siglen == 0) 346 return SSH_ERR_INVALID_ARGUMENT; 347 if (hlen == 0) 348 return SSH_ERR_INTERNAL_ERROR; 349 350 /* fetch signature */ 351 if ((b = sshbuf_from(sig, siglen)) == NULL) 352 return SSH_ERR_ALLOC_FAIL; 353 if (sshbuf_get_cstring(b, &ktype, NULL) != 0 || 354 sshbuf_get_string(b, &sigblob, &len) != 0) { 355 ret = SSH_ERR_INVALID_FORMAT; 356 goto out; 357 } 358 if (strcmp("ssh-dss", ktype) != 0) { 359 ret = SSH_ERR_KEY_TYPE_MISMATCH; 360 goto out; 361 } 362 if (sshbuf_len(b) != 0) { 363 ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; 364 goto out; 365 } 366 367 if (len != SIGBLOB_LEN) { 368 ret = SSH_ERR_INVALID_FORMAT; 369 goto out; 370 } 371 372 /* parse signature */ 373 if ((dsig = DSA_SIG_new()) == NULL || 374 (sig_r = BN_new()) == NULL || 375 (sig_s = BN_new()) == NULL) { 376 ret = SSH_ERR_ALLOC_FAIL; 377 goto out; 378 } 379 if ((BN_bin2bn(sigblob, INTBLOB_LEN, sig_r) == NULL) || 380 (BN_bin2bn(sigblob + INTBLOB_LEN, INTBLOB_LEN, sig_s) == NULL)) { 381 ret = SSH_ERR_LIBCRYPTO_ERROR; 382 goto out; 383 } 384 if (!DSA_SIG_set0(dsig, sig_r, sig_s)) { 385 ret = SSH_ERR_LIBCRYPTO_ERROR; 386 goto out; 387 } 388 sig_r = sig_s = NULL; /* transferred */ 389 390 /* sha1 the data */ 391 if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, dlen, 392 digest, sizeof(digest))) != 0) 393 goto out; 394 395 switch (DSA_do_verify(digest, hlen, dsig, key->dsa)) { 396 case 1: 397 ret = 0; 398 break; 399 case 0: 400 ret = SSH_ERR_SIGNATURE_INVALID; 401 goto out; 402 default: 403 ret = SSH_ERR_LIBCRYPTO_ERROR; 404 goto out; 405 } 406 407 out: 408 explicit_bzero(digest, sizeof(digest)); 409 DSA_SIG_free(dsig); 410 BN_clear_free(sig_r); 411 BN_clear_free(sig_s); 412 sshbuf_free(b); 413 free(ktype); 414 if (sigblob != NULL) 415 freezero(sigblob, len); 416 return ret; 417 } 418 419 static const struct sshkey_impl_funcs sshkey_dss_funcs = { 420 /* .size = */ ssh_dss_size, 421 /* .alloc = */ ssh_dss_alloc, 422 /* .cleanup = */ ssh_dss_cleanup, 423 /* .equal = */ ssh_dss_equal, 424 /* .ssh_serialize_public = */ ssh_dss_serialize_public, 425 /* .ssh_deserialize_public = */ ssh_dss_deserialize_public, 426 /* .ssh_serialize_private = */ ssh_dss_serialize_private, 427 /* .ssh_deserialize_private = */ ssh_dss_deserialize_private, 428 /* .generate = */ ssh_dss_generate, 429 /* .copy_public = */ ssh_dss_copy_public, 430 /* .sign = */ ssh_dss_sign, 431 /* .verify = */ ssh_dss_verify, 432 }; 433 434 const struct sshkey_impl sshkey_dss_impl = { 435 /* .name = */ "ssh-dss", 436 /* .shortname = */ "DSA", 437 /* .sigalg = */ NULL, 438 /* .type = */ KEY_DSA, 439 /* .nid = */ 0, 440 /* .cert = */ 0, 441 /* .sigonly = */ 0, 442 /* .keybits = */ 0, 443 /* .funcs = */ &sshkey_dss_funcs, 444 }; 445 446 const struct sshkey_impl sshkey_dsa_cert_impl = { 447 /* .name = */ "ssh-dss-cert-v01@openssh.com", 448 /* .shortname = */ "DSA-CERT", 449 /* .sigalg = */ NULL, 450 /* .type = */ KEY_DSA_CERT, 451 /* .nid = */ 0, 452 /* .cert = */ 1, 453 /* .sigonly = */ 0, 454 /* .keybits = */ 0, 455 /* .funcs = */ &sshkey_dss_funcs, 456 }; 457 #endif /* WITH_OPENSSL */ 458