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