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