1/*
2 * Dropbear - a SSH2 server
3 *
4 * Copyright (c) 2002,2003 Matt Johnston
5 * All rights reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE. */
24
25#include "includes.h"
26#include "dbutil.h"
27#include "bignum.h"
28#include "dss.h"
29#include "buffer.h"
30#include "ssh.h"
31#include "random.h"
32
33/* Handle DSS (Digital Signature Standard), aka DSA (D.S. Algorithm),
34 * operations, such as key reading, signing, verification. Key generation
35 * is in gendss.c, since it isn't required in the server itself.
36 *
37 * See FIPS186 or the Handbook of Applied Cryptography for details of the
38 * algorithm */
39
40#ifdef DROPBEAR_DSS
41
42/* Load a dss key from a buffer, initialising the values.
43 * The key will have the same format as buf_put_dss_key.
44 * These should be freed with dss_key_free.
45 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
46int buf_get_dss_pub_key(buffer* buf, dss_key *key) {
47
48	TRACE(("enter buf_get_dss_pub_key"))
49	dropbear_assert(key != NULL);
50	key->p = m_malloc(sizeof(mp_int));
51	key->q = m_malloc(sizeof(mp_int));
52	key->g = m_malloc(sizeof(mp_int));
53	key->y = m_malloc(sizeof(mp_int));
54	m_mp_init_multi(key->p, key->q, key->g, key->y, NULL);
55	key->x = NULL;
56
57	buf_incrpos(buf, 4+SSH_SIGNKEY_DSS_LEN); /* int + "ssh-dss" */
58	if (buf_getmpint(buf, key->p) == DROPBEAR_FAILURE
59	 || buf_getmpint(buf, key->q) == DROPBEAR_FAILURE
60	 || buf_getmpint(buf, key->g) == DROPBEAR_FAILURE
61	 || buf_getmpint(buf, key->y) == DROPBEAR_FAILURE) {
62		TRACE(("leave buf_get_dss_pub_key: failed reading mpints"))
63		return DROPBEAR_FAILURE;
64	}
65
66	if (mp_count_bits(key->p) < MIN_DSS_KEYLEN) {
67		dropbear_log(LOG_WARNING, "DSS key too short");
68		TRACE(("leave buf_get_dss_pub_key: short key"))
69		return DROPBEAR_FAILURE;
70	}
71
72	TRACE(("leave buf_get_dss_pub_key: success"))
73	return DROPBEAR_SUCCESS;
74}
75
76/* Same as buf_get_dss_pub_key, but reads a private "x" key at the end.
77 * Loads a private dss key from a buffer
78 * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
79int buf_get_dss_priv_key(buffer* buf, dss_key *key) {
80
81	int ret = DROPBEAR_FAILURE;
82
83	dropbear_assert(key != NULL);
84
85	ret = buf_get_dss_pub_key(buf, key);
86	if (ret == DROPBEAR_FAILURE) {
87		return DROPBEAR_FAILURE;
88	}
89
90	key->x = m_malloc(sizeof(mp_int));
91	m_mp_init(key->x);
92	ret = buf_getmpint(buf, key->x);
93	if (ret == DROPBEAR_FAILURE) {
94		m_free(key->x);
95	}
96
97	return ret;
98}
99
100
101/* Clear and free the memory used by a public or private key */
102void dss_key_free(dss_key *key) {
103
104	TRACE(("enter dsa_key_free"))
105	if (key == NULL) {
106		TRACE(("enter dsa_key_free: key == NULL"))
107		return;
108	}
109	if (key->p) {
110		mp_clear(key->p);
111		m_free(key->p);
112	}
113	if (key->q) {
114		mp_clear(key->q);
115		m_free(key->q);
116	}
117	if (key->g) {
118		mp_clear(key->g);
119		m_free(key->g);
120	}
121	if (key->y) {
122		mp_clear(key->y);
123		m_free(key->y);
124	}
125	if (key->x) {
126		mp_clear(key->x);
127		m_free(key->x);
128	}
129	m_free(key);
130	TRACE(("leave dsa_key_free"))
131}
132
133/* put the dss public key into the buffer in the required format:
134 *
135 * string	"ssh-dss"
136 * mpint	p
137 * mpint	q
138 * mpint	g
139 * mpint	y
140 */
141void buf_put_dss_pub_key(buffer* buf, dss_key *key) {
142
143	dropbear_assert(key != NULL);
144	buf_putstring(buf, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN);
145	buf_putmpint(buf, key->p);
146	buf_putmpint(buf, key->q);
147	buf_putmpint(buf, key->g);
148	buf_putmpint(buf, key->y);
149
150}
151
152/* Same as buf_put_dss_pub_key, but with the private "x" key appended */
153void buf_put_dss_priv_key(buffer* buf, dss_key *key) {
154
155	dropbear_assert(key != NULL);
156	buf_put_dss_pub_key(buf, key);
157	buf_putmpint(buf, key->x);
158
159}
160
161#ifdef DROPBEAR_SIGNKEY_VERIFY
162/* Verify a DSS signature (in buf) made on data by the key given.
163 * returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
164int buf_dss_verify(buffer* buf, dss_key *key, const unsigned char* data,
165		unsigned int len) {
166
167	unsigned char msghash[SHA1_HASH_SIZE];
168	hash_state hs;
169	int ret = DROPBEAR_FAILURE;
170	DEF_MP_INT(val1);
171	DEF_MP_INT(val2);
172	DEF_MP_INT(val3);
173	DEF_MP_INT(val4);
174	char * string = NULL;
175	int stringlen;
176
177	TRACE(("enter buf_dss_verify"))
178	dropbear_assert(key != NULL);
179
180	m_mp_init_multi(&val1, &val2, &val3, &val4, NULL);
181
182	/* get blob, check length */
183	string = buf_getstring(buf, &stringlen);
184	if (stringlen != 2*SHA1_HASH_SIZE) {
185		goto out;
186	}
187
188	/* hash the data */
189	sha1_init(&hs);
190	sha1_process(&hs, data, len);
191	sha1_done(&hs, msghash);
192
193	/* create the signature - s' and r' are the received signatures in buf */
194	/* w = (s')-1 mod q */
195	/* let val1 = s' */
196	bytes_to_mp(&val1, &string[SHA1_HASH_SIZE], SHA1_HASH_SIZE);
197
198	if (mp_cmp(&val1, key->q) != MP_LT) {
199		TRACE(("verify failed, s' >= q"))
200		goto out;
201	}
202	/* let val2 = w = (s')^-1 mod q*/
203	if (mp_invmod(&val1, key->q, &val2) != MP_OKAY) {
204		goto out;
205	}
206
207	/* u1 = ((SHA(M')w) mod q */
208	/* let val1 = SHA(M') = msghash */
209	bytes_to_mp(&val1, msghash, SHA1_HASH_SIZE);
210
211	/* let val3 = u1 = ((SHA(M')w) mod q */
212	if (mp_mulmod(&val1, &val2, key->q, &val3) != MP_OKAY) {
213		goto out;
214	}
215
216	/* u2 = ((r')w) mod q */
217	/* let val1 = r' */
218	bytes_to_mp(&val1, &string[0], SHA1_HASH_SIZE);
219	if (mp_cmp(&val1, key->q) != MP_LT) {
220		TRACE(("verify failed, r' >= q"))
221		goto out;
222	}
223	/* let val4 = u2 = ((r')w) mod q */
224	if (mp_mulmod(&val1, &val2, key->q, &val4) != MP_OKAY) {
225		goto out;
226	}
227
228	/* v = (((g)^u1 (y)^u2) mod p) mod q */
229	/* val2 = g^u1 mod p */
230	if (mp_exptmod(key->g, &val3, key->p, &val2) != MP_OKAY) {
231		goto out;
232	}
233	/* val3 = y^u2 mod p */
234	if (mp_exptmod(key->y, &val4, key->p, &val3) != MP_OKAY) {
235		goto out;
236	}
237	/* val4 = ((g)^u1 (y)^u2) mod p */
238	if (mp_mulmod(&val2, &val3, key->p, &val4) != MP_OKAY) {
239		goto out;
240	}
241	/* val2 = v = (((g)^u1 (y)^u2) mod p) mod q */
242	if (mp_mod(&val4, key->q, &val2) != MP_OKAY) {
243		goto out;
244	}
245
246	/* check whether signatures verify */
247	if (mp_cmp(&val2, &val1) == MP_EQ) {
248		/* good sig */
249		ret = DROPBEAR_SUCCESS;
250	}
251
252out:
253	mp_clear_multi(&val1, &val2, &val3, &val4, NULL);
254	m_free(string);
255
256	return ret;
257
258}
259#endif /* DROPBEAR_SIGNKEY_VERIFY */
260
261#ifdef DSS_PROTOK
262/* convert an unsigned mp into an array of bytes, malloced.
263 * This array must be freed after use, len contains the length of the array,
264 * if len != NULL */
265static unsigned char* mptobytes(mp_int *mp, int *len) {
266
267	unsigned char* ret;
268	int size;
269
270	size = mp_unsigned_bin_size(mp);
271	ret = m_malloc(size);
272	if (mp_to_unsigned_bin(mp, ret) != MP_OKAY) {
273		dropbear_exit("mem alloc error");
274	}
275	if (len != NULL) {
276		*len = size;
277	}
278	return ret;
279}
280#endif
281
282/* Sign the data presented with key, writing the signature contents
283 * to the buffer
284 *
285 * When DSS_PROTOK is #defined:
286 * The alternate k generation method is based on the method used in PuTTY.
287 * In particular to avoid being vulnerable to attacks using flaws in random
288 * generation of k, we use the following:
289 *
290 * proto_k = SHA512 ( SHA512(x) || SHA160(message) )
291 * k = proto_k mod q
292 *
293 * Now we aren't relying on the random number generation to protect the private
294 * key x, which is a long term secret */
295void buf_put_dss_sign(buffer* buf, dss_key *key, const unsigned char* data,
296		unsigned int len) {
297
298	unsigned char msghash[SHA1_HASH_SIZE];
299	unsigned int writelen;
300	unsigned int i;
301#ifdef DSS_PROTOK
302	unsigned char privkeyhash[SHA512_HASH_SIZE];
303	unsigned char *privkeytmp;
304	unsigned char proto_k[SHA512_HASH_SIZE];
305	DEF_MP_INT(dss_protok);
306#endif
307	DEF_MP_INT(dss_k);
308	DEF_MP_INT(dss_m);
309	DEF_MP_INT(dss_temp1);
310	DEF_MP_INT(dss_temp2);
311	DEF_MP_INT(dss_r);
312	DEF_MP_INT(dss_s);
313	hash_state hs;
314
315	TRACE(("enter buf_put_dss_sign"))
316	dropbear_assert(key != NULL);
317
318	/* hash the data */
319	sha1_init(&hs);
320	sha1_process(&hs, data, len);
321	sha1_done(&hs, msghash);
322
323	m_mp_init_multi(&dss_k, &dss_temp1, &dss_temp2, &dss_r, &dss_s,
324			&dss_m, NULL);
325#ifdef DSS_PROTOK
326	/* hash the privkey */
327	privkeytmp = mptobytes(key->x, &i);
328	sha512_init(&hs);
329	sha512_process(&hs, "the quick brown fox jumped over the lazy dog", 44);
330	sha512_process(&hs, privkeytmp, i);
331	sha512_done(&hs, privkeyhash);
332	m_burn(privkeytmp, i);
333	m_free(privkeytmp);
334
335	/* calculate proto_k */
336	sha512_init(&hs);
337	sha512_process(&hs, privkeyhash, SHA512_HASH_SIZE);
338	sha512_process(&hs, msghash, SHA1_HASH_SIZE);
339	sha512_done(&hs, proto_k);
340
341	/* generate k */
342	m_mp_init(&dss_protok);
343	bytes_to_mp(&dss_protok, proto_k, SHA512_HASH_SIZE);
344	if (mp_mod(&dss_protok, key->q, &dss_k) != MP_OKAY) {
345		dropbear_exit("dss error");
346	}
347	mp_clear(&dss_protok);
348	m_burn(proto_k, SHA512_HASH_SIZE);
349#else /* DSS_PROTOK not defined*/
350	gen_random_mpint(key->q, &dss_k);
351#endif
352
353	/* now generate the actual signature */
354	bytes_to_mp(&dss_m, msghash, SHA1_HASH_SIZE);
355
356	/* g^k mod p */
357	if (mp_exptmod(key->g, &dss_k, key->p, &dss_temp1) !=  MP_OKAY) {
358		dropbear_exit("dss error");
359	}
360	/* r = (g^k mod p) mod q */
361	if (mp_mod(&dss_temp1, key->q, &dss_r) != MP_OKAY) {
362		dropbear_exit("dss error");
363	}
364
365	/* x*r mod q */
366	if (mp_mulmod(&dss_r, key->x, key->q, &dss_temp1) != MP_OKAY) {
367		dropbear_exit("dss error");
368	}
369	/* (SHA1(M) + xr) mod q) */
370	if (mp_addmod(&dss_m, &dss_temp1, key->q, &dss_temp2) != MP_OKAY) {
371		dropbear_exit("dss error");
372	}
373
374	/* (k^-1) mod q */
375	if (mp_invmod(&dss_k, key->q, &dss_temp1) != MP_OKAY) {
376		dropbear_exit("dss error");
377	}
378
379	/* s = (k^-1(SHA1(M) + xr)) mod q */
380	if (mp_mulmod(&dss_temp1, &dss_temp2, key->q, &dss_s) != MP_OKAY) {
381		dropbear_exit("dss error");
382	}
383
384	buf_putstring(buf, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN);
385	buf_putint(buf, 2*SHA1_HASH_SIZE);
386
387	writelen = mp_unsigned_bin_size(&dss_r);
388	dropbear_assert(writelen <= SHA1_HASH_SIZE);
389	/* need to pad to 160 bits with leading zeros */
390	for (i = 0; i < SHA1_HASH_SIZE - writelen; i++) {
391		buf_putbyte(buf, 0);
392	}
393	if (mp_to_unsigned_bin(&dss_r, buf_getwriteptr(buf, writelen))
394			!= MP_OKAY) {
395		dropbear_exit("dss error");
396	}
397	mp_clear(&dss_r);
398	buf_incrwritepos(buf, writelen);
399
400	writelen = mp_unsigned_bin_size(&dss_s);
401	dropbear_assert(writelen <= SHA1_HASH_SIZE);
402	/* need to pad to 160 bits with leading zeros */
403	for (i = 0; i < SHA1_HASH_SIZE - writelen; i++) {
404		buf_putbyte(buf, 0);
405	}
406	if (mp_to_unsigned_bin(&dss_s, buf_getwriteptr(buf, writelen))
407			!= MP_OKAY) {
408		dropbear_exit("dss error");
409	}
410	mp_clear(&dss_s);
411	buf_incrwritepos(buf, writelen);
412
413	mp_clear_multi(&dss_k, &dss_temp1, &dss_temp2, &dss_r, &dss_s,
414			&dss_m, NULL);
415
416	/* create the signature to return */
417
418	TRACE(("leave buf_put_dss_sign"))
419}
420
421#endif /* DROPBEAR_DSS */
422