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