Mercurial > dropbear
comparison dss.c @ 391:00fcf5045160
propagate from branch 'au.asn.ucc.matt.ltc.dropbear' (head c1db4398d56c56c6d06ae1e20c1e0d04dbb598ed)
to branch 'au.asn.ucc.matt.dropbear' (head d26d5eb2837f46b56a33fb0e7573aa0201abd4d5)
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Thu, 11 Jan 2007 04:29:08 +0000 |
parents | a124aff0cbf1 |
children | b50f0107e505 76097ec1a29a |
comparison
equal
deleted
inserted
replaced
390:d8e44bef7917 | 391:00fcf5045160 |
---|---|
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 */ | |
46 int 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 */ | |
79 int 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 */ | |
102 void 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 */ | |
141 void 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 */ | |
153 void 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 */ | |
164 int 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 | |
252 out: | |
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 */ | |
265 static 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 */ | |
295 void 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 */ |