Mercurial > dropbear
comparison signkey.c @ 285:1b9e69c058d2
propagate from branch 'au.asn.ucc.matt.ltc.dropbear' (head 20dccfc09627970a312d77fb41dc2970b62689c3)
to branch 'au.asn.ucc.matt.dropbear' (head fdf4a7a3b97ae5046139915de7e40399cceb2c01)
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Wed, 08 Mar 2006 13:23:58 +0000 |
parents | eb7b9f2bb8e8 |
children | dc6173e09ff7 7282370416a0 |
comparison
equal
deleted
inserted
replaced
281:997e6f7dc01e | 285:1b9e69c058d2 |
---|---|
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 "signkey.h" | |
28 #include "buffer.h" | |
29 #include "ssh.h" | |
30 | |
31 /* malloc a new sign_key and set the dss and rsa keys to NULL */ | |
32 sign_key * new_sign_key() { | |
33 | |
34 sign_key * ret; | |
35 | |
36 ret = (sign_key*)m_malloc(sizeof(sign_key)); | |
37 #ifdef DROPBEAR_DSS | |
38 ret->dsskey = NULL; | |
39 #endif | |
40 #ifdef DROPBEAR_RSA | |
41 ret->rsakey = NULL; | |
42 #endif | |
43 return ret; | |
44 | |
45 } | |
46 | |
47 /* Returns "ssh-dss" or "ssh-rsa" corresponding to the type. Exits fatally | |
48 * if the type is invalid */ | |
49 const char* signkey_name_from_type(int type, int *namelen) { | |
50 | |
51 #ifdef DROPBEAR_RSA | |
52 if (type == DROPBEAR_SIGNKEY_RSA) { | |
53 *namelen = SSH_SIGNKEY_RSA_LEN; | |
54 return SSH_SIGNKEY_RSA; | |
55 } | |
56 #endif | |
57 #ifdef DROPBEAR_DSS | |
58 if (type == DROPBEAR_SIGNKEY_DSS) { | |
59 *namelen = SSH_SIGNKEY_DSS_LEN; | |
60 return SSH_SIGNKEY_DSS; | |
61 } | |
62 #endif | |
63 dropbear_exit("bad key type %d", type); | |
64 return NULL; /* notreached */ | |
65 } | |
66 | |
67 /* Returns DROPBEAR_SIGNKEY_RSA, DROPBEAR_SIGNKEY_DSS, | |
68 * or DROPBEAR_SIGNKEY_NONE */ | |
69 int signkey_type_from_name(const char* name, int namelen) { | |
70 | |
71 #ifdef DROPBEAR_RSA | |
72 if (namelen == SSH_SIGNKEY_RSA_LEN | |
73 && memcmp(name, SSH_SIGNKEY_RSA, SSH_SIGNKEY_RSA_LEN) == 0) { | |
74 return DROPBEAR_SIGNKEY_RSA; | |
75 } | |
76 #endif | |
77 #ifdef DROPBEAR_DSS | |
78 if (namelen == SSH_SIGNKEY_DSS_LEN | |
79 && memcmp(name, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN) == 0) { | |
80 return DROPBEAR_SIGNKEY_DSS; | |
81 } | |
82 #endif | |
83 | |
84 return DROPBEAR_SIGNKEY_NONE; | |
85 } | |
86 | |
87 /* returns DROPBEAR_SUCCESS on success, DROPBEAR_FAILURE on fail. | |
88 * type should be set by the caller to specify the type to read, and | |
89 * on return is set to the type read (useful when type = _ANY) */ | |
90 int buf_get_pub_key(buffer *buf, sign_key *key, int *type) { | |
91 | |
92 unsigned char* ident; | |
93 unsigned int len; | |
94 int keytype; | |
95 int ret = DROPBEAR_FAILURE; | |
96 | |
97 TRACE(("enter buf_get_pub_key")) | |
98 | |
99 ident = buf_getstring(buf, &len); | |
100 keytype = signkey_type_from_name(ident, len); | |
101 m_free(ident); | |
102 | |
103 if (*type != DROPBEAR_SIGNKEY_ANY && *type != keytype) { | |
104 return DROPBEAR_FAILURE; | |
105 } | |
106 | |
107 *type = keytype; | |
108 | |
109 /* Rewind the buffer back before "ssh-rsa" etc */ | |
110 buf_incrpos(buf, -len - 4); | |
111 | |
112 #ifdef DROPBEAR_DSS | |
113 if (keytype == DROPBEAR_SIGNKEY_DSS) { | |
114 dss_key_free(key->dsskey); | |
115 key->dsskey = (dss_key*)m_malloc(sizeof(dss_key)); | |
116 ret = buf_get_dss_pub_key(buf, key->dsskey); | |
117 if (ret == DROPBEAR_FAILURE) { | |
118 m_free(key->dsskey); | |
119 } | |
120 } | |
121 #endif | |
122 #ifdef DROPBEAR_RSA | |
123 if (keytype == DROPBEAR_SIGNKEY_RSA) { | |
124 rsa_key_free(key->rsakey); | |
125 key->rsakey = (rsa_key*)m_malloc(sizeof(rsa_key)); | |
126 ret = buf_get_rsa_pub_key(buf, key->rsakey); | |
127 if (ret == DROPBEAR_FAILURE) { | |
128 m_free(key->rsakey); | |
129 } | |
130 } | |
131 #endif | |
132 | |
133 TRACE(("leave buf_get_pub_key")) | |
134 | |
135 return ret; | |
136 | |
137 } | |
138 | |
139 /* returns DROPBEAR_SUCCESS on success, DROPBEAR_FAILURE on fail. | |
140 * type should be set by the caller to specify the type to read, and | |
141 * on return is set to the type read (useful when type = _ANY) */ | |
142 int buf_get_priv_key(buffer *buf, sign_key *key, int *type) { | |
143 | |
144 unsigned char* ident; | |
145 unsigned int len; | |
146 int keytype; | |
147 int ret = DROPBEAR_FAILURE; | |
148 | |
149 TRACE(("enter buf_get_priv_key")) | |
150 | |
151 ident = buf_getstring(buf, &len); | |
152 keytype = signkey_type_from_name(ident, len); | |
153 m_free(ident); | |
154 | |
155 if (*type != DROPBEAR_SIGNKEY_ANY && *type != keytype) { | |
156 TRACE(("wrong key type: %d %d", *type, keytype)) | |
157 return DROPBEAR_FAILURE; | |
158 } | |
159 | |
160 *type = keytype; | |
161 | |
162 /* Rewind the buffer back before "ssh-rsa" etc */ | |
163 buf_incrpos(buf, -len - 4); | |
164 | |
165 #ifdef DROPBEAR_DSS | |
166 if (keytype == DROPBEAR_SIGNKEY_DSS) { | |
167 dss_key_free(key->dsskey); | |
168 key->dsskey = (dss_key*)m_malloc(sizeof(dss_key)); | |
169 ret = buf_get_dss_priv_key(buf, key->dsskey); | |
170 if (ret == DROPBEAR_FAILURE) { | |
171 m_free(key->dsskey); | |
172 } | |
173 } | |
174 #endif | |
175 #ifdef DROPBEAR_RSA | |
176 if (keytype == DROPBEAR_SIGNKEY_RSA) { | |
177 rsa_key_free(key->rsakey); | |
178 key->rsakey = (rsa_key*)m_malloc(sizeof(rsa_key)); | |
179 ret = buf_get_rsa_priv_key(buf, key->rsakey); | |
180 if (ret == DROPBEAR_FAILURE) { | |
181 m_free(key->rsakey); | |
182 } | |
183 } | |
184 #endif | |
185 | |
186 TRACE(("leave buf_get_priv_key")) | |
187 | |
188 return ret; | |
189 | |
190 } | |
191 | |
192 /* type is either DROPBEAR_SIGNKEY_DSS or DROPBEAR_SIGNKEY_RSA */ | |
193 void buf_put_pub_key(buffer* buf, sign_key *key, int type) { | |
194 | |
195 buffer *pubkeys; | |
196 | |
197 TRACE(("enter buf_put_pub_key")) | |
198 pubkeys = buf_new(MAX_PUBKEY_SIZE); | |
199 | |
200 #ifdef DROPBEAR_DSS | |
201 if (type == DROPBEAR_SIGNKEY_DSS) { | |
202 buf_put_dss_pub_key(pubkeys, key->dsskey); | |
203 } | |
204 #endif | |
205 #ifdef DROPBEAR_RSA | |
206 if (type == DROPBEAR_SIGNKEY_RSA) { | |
207 buf_put_rsa_pub_key(pubkeys, key->rsakey); | |
208 } | |
209 #endif | |
210 if (pubkeys->len == 0) { | |
211 dropbear_exit("bad key types in buf_put_pub_key"); | |
212 } | |
213 | |
214 buf_setpos(pubkeys, 0); | |
215 buf_putstring(buf, buf_getptr(pubkeys, pubkeys->len), | |
216 pubkeys->len); | |
217 | |
218 buf_free(pubkeys); | |
219 TRACE(("leave buf_put_pub_key")) | |
220 } | |
221 | |
222 /* type is either DROPBEAR_SIGNKEY_DSS or DROPBEAR_SIGNKEY_RSA */ | |
223 void buf_put_priv_key(buffer* buf, sign_key *key, int type) { | |
224 | |
225 TRACE(("enter buf_put_priv_key")) | |
226 TRACE(("type is %d", type)) | |
227 | |
228 #ifdef DROPBEAR_DSS | |
229 if (type == DROPBEAR_SIGNKEY_DSS) { | |
230 buf_put_dss_priv_key(buf, key->dsskey); | |
231 TRACE(("leave buf_put_priv_key: dss done")) | |
232 return; | |
233 } | |
234 #endif | |
235 #ifdef DROPBEAR_RSA | |
236 if (type == DROPBEAR_SIGNKEY_RSA) { | |
237 buf_put_rsa_priv_key(buf, key->rsakey); | |
238 TRACE(("leave buf_put_priv_key: rsa done")) | |
239 return; | |
240 } | |
241 #endif | |
242 dropbear_exit("bad key types in put pub key"); | |
243 } | |
244 | |
245 void sign_key_free(sign_key *key) { | |
246 | |
247 TRACE(("enter sign_key_free")) | |
248 | |
249 #ifdef DROPBEAR_DSS | |
250 dss_key_free(key->dsskey); | |
251 key->dsskey = NULL; | |
252 #endif | |
253 #ifdef DROPBEAR_RSA | |
254 rsa_key_free(key->rsakey); | |
255 key->rsakey = NULL; | |
256 #endif | |
257 | |
258 m_free(key); | |
259 TRACE(("leave sign_key_free")) | |
260 } | |
261 | |
262 static char hexdig(unsigned char x) { | |
263 | |
264 if (x > 0xf) | |
265 return 'X'; | |
266 | |
267 if (x < 10) | |
268 return '0' + x; | |
269 else | |
270 return 'a' + x - 10; | |
271 } | |
272 | |
273 /* Since we're not sure if we'll have md5 or sha1, we present both. | |
274 * MD5 is used in preference, but sha1 could still be useful */ | |
275 #ifdef DROPBEAR_MD5_HMAC | |
276 static char * sign_key_md5_fingerprint(unsigned char* keyblob, | |
277 unsigned int keybloblen) { | |
278 | |
279 char * ret; | |
280 hash_state hs; | |
281 unsigned char hash[MD5_HASH_SIZE]; | |
282 unsigned int i; | |
283 unsigned int buflen; | |
284 | |
285 md5_init(&hs); | |
286 | |
287 /* skip the size int of the string - this is a bit messy */ | |
288 md5_process(&hs, keyblob, keybloblen); | |
289 | |
290 md5_done(&hs, hash); | |
291 | |
292 /* "md5 hexfingerprinthere\0", each hex digit is "AB:" etc */ | |
293 buflen = 4 + 3*MD5_HASH_SIZE; | |
294 ret = (char*)m_malloc(buflen); | |
295 | |
296 memset(ret, 'Z', buflen); | |
297 strcpy(ret, "md5 "); | |
298 | |
299 for (i = 0; i < MD5_HASH_SIZE; i++) { | |
300 unsigned int pos = 4 + i*3; | |
301 ret[pos] = hexdig(hash[i] >> 4); | |
302 ret[pos+1] = hexdig(hash[i] & 0x0f); | |
303 ret[pos+2] = ':'; | |
304 } | |
305 ret[buflen-1] = 0x0; | |
306 | |
307 return ret; | |
308 } | |
309 | |
310 #else /* use SHA1 rather than MD5 for fingerprint */ | |
311 static char * sign_key_sha1_fingerprint(unsigned char* keyblob, | |
312 unsigned int keybloblen) { | |
313 | |
314 char * ret; | |
315 hash_state hs; | |
316 unsigned char hash[SHA1_HASH_SIZE]; | |
317 unsigned int i; | |
318 unsigned int buflen; | |
319 | |
320 sha1_init(&hs); | |
321 | |
322 /* skip the size int of the string - this is a bit messy */ | |
323 sha1_process(&hs, keyblob, keybloblen); | |
324 | |
325 sha1_done(&hs, hash); | |
326 | |
327 /* "sha1 hexfingerprinthere\0", each hex digit is "AB:" etc */ | |
328 buflen = 5 + 3*SHA1_HASH_SIZE; | |
329 ret = (char*)m_malloc(buflen); | |
330 | |
331 strcpy(ret, "sha1 "); | |
332 | |
333 for (i = 0; i < SHA1_HASH_SIZE; i++) { | |
334 unsigned int pos = 5 + 3*i; | |
335 ret[pos] = hexdig(hash[i] >> 4); | |
336 ret[pos+1] = hexdig(hash[i] & 0x0f); | |
337 ret[pos+2] = ':'; | |
338 } | |
339 ret[buflen-1] = 0x0; | |
340 | |
341 return ret; | |
342 } | |
343 | |
344 #endif /* MD5/SHA1 switch */ | |
345 | |
346 /* This will return a freshly malloced string, containing a fingerprint | |
347 * in either sha1 or md5 */ | |
348 char * sign_key_fingerprint(unsigned char* keyblob, unsigned int keybloblen) { | |
349 | |
350 #ifdef DROPBEAR_MD5_HMAC | |
351 return sign_key_md5_fingerprint(keyblob, keybloblen); | |
352 #else | |
353 return sign_key_sha1_fingerprint(keyblob, keybloblen); | |
354 #endif | |
355 } | |
356 | |
357 void buf_put_sign(buffer* buf, sign_key *key, int type, | |
358 const unsigned char *data, unsigned int len) { | |
359 | |
360 buffer *sigblob; | |
361 | |
362 sigblob = buf_new(MAX_PUBKEY_SIZE); | |
363 | |
364 #ifdef DROPBEAR_DSS | |
365 if (type == DROPBEAR_SIGNKEY_DSS) { | |
366 buf_put_dss_sign(sigblob, key->dsskey, data, len); | |
367 } | |
368 #endif | |
369 #ifdef DROPBEAR_RSA | |
370 if (type == DROPBEAR_SIGNKEY_RSA) { | |
371 buf_put_rsa_sign(sigblob, key->rsakey, data, len); | |
372 } | |
373 #endif | |
374 if (sigblob->len == 0) { | |
375 dropbear_exit("non-matching signing type"); | |
376 } | |
377 | |
378 buf_setpos(sigblob, 0); | |
379 buf_putstring(buf, buf_getptr(sigblob, sigblob->len), | |
380 sigblob->len); | |
381 | |
382 buf_free(sigblob); | |
383 | |
384 } | |
385 | |
386 #ifdef DROPBEAR_SIGNKEY_VERIFY | |
387 /* Return DROPBEAR_SUCCESS or DROPBEAR_FAILURE. | |
388 * If FAILURE is returned, the position of | |
389 * buf is undefined. If SUCCESS is returned, buf will be positioned after the | |
390 * signature blob */ | |
391 int buf_verify(buffer * buf, sign_key *key, const unsigned char *data, | |
392 unsigned int len) { | |
393 | |
394 unsigned int bloblen; | |
395 unsigned char * ident = NULL; | |
396 unsigned int identlen = 0; | |
397 | |
398 TRACE(("enter buf_verify")) | |
399 | |
400 bloblen = buf_getint(buf); | |
401 ident = buf_getstring(buf, &identlen); | |
402 | |
403 #ifdef DROPBEAR_DSS | |
404 if (bloblen == DSS_SIGNATURE_SIZE && | |
405 memcmp(ident, SSH_SIGNKEY_DSS, identlen) == 0) { | |
406 m_free(ident); | |
407 if (key->dsskey == NULL) { | |
408 dropbear_exit("no dss key to verify signature"); | |
409 } | |
410 return buf_dss_verify(buf, key->dsskey, data, len); | |
411 } | |
412 #endif | |
413 | |
414 #ifdef DROPBEAR_RSA | |
415 if (memcmp(ident, SSH_SIGNKEY_RSA, identlen) == 0) { | |
416 m_free(ident); | |
417 if (key->rsakey == NULL) { | |
418 dropbear_exit("no rsa key to verify signature"); | |
419 } | |
420 return buf_rsa_verify(buf, key->rsakey, data, len); | |
421 } | |
422 #endif | |
423 | |
424 m_free(ident); | |
425 dropbear_exit("non-matching signing type"); | |
426 return DROPBEAR_FAILURE; | |
427 } | |
428 #endif /* DROPBEAR_SIGNKEY_VERIFY */ | |
429 | |
430 #ifdef DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */ | |
431 | |
432 /* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE when given a buffer containing | |
433 * a key, a key, and a type. The buffer is positioned at the start of the | |
434 * base64 data, and contains no trailing data */ | |
435 int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen, | |
436 const unsigned char* algoname, unsigned int algolen, | |
437 buffer * line) { | |
438 | |
439 buffer * decodekey = NULL; | |
440 int ret = DROPBEAR_FAILURE; | |
441 unsigned int len, filealgolen; | |
442 unsigned long decodekeylen; | |
443 unsigned char* filealgo = NULL; | |
444 | |
445 /* now we have the actual data */ | |
446 len = line->len - line->pos; | |
447 decodekeylen = len * 2; /* big to be safe */ | |
448 decodekey = buf_new(decodekeylen); | |
449 | |
450 if (base64_decode(buf_getptr(line, len), len, | |
451 buf_getwriteptr(decodekey, decodekey->size), | |
452 &decodekeylen) != CRYPT_OK) { | |
453 TRACE(("checkpubkey: base64 decode failed")) | |
454 goto out; | |
455 } | |
456 TRACE(("checkpubkey: base64_decode success")) | |
457 buf_incrlen(decodekey, decodekeylen); | |
458 | |
459 /* compare the keys */ | |
460 if ( ( decodekeylen != keybloblen ) | |
461 || memcmp( buf_getptr(decodekey, decodekey->len), | |
462 keyblob, decodekey->len) != 0) { | |
463 TRACE(("checkpubkey: compare failed")) | |
464 goto out; | |
465 } | |
466 | |
467 /* ... and also check that the algo specified and the algo in the key | |
468 * itself match */ | |
469 filealgolen = buf_getint(decodekey); | |
470 filealgo = buf_getptr(decodekey, filealgolen); | |
471 if (filealgolen != algolen || memcmp(filealgo, algoname, algolen) != 0) { | |
472 TRACE(("checkpubkey: algo match failed")) | |
473 goto out; | |
474 } | |
475 | |
476 /* All checks passed */ | |
477 ret = DROPBEAR_SUCCESS; | |
478 | |
479 out: | |
480 buf_free(decodekey); | |
481 decodekey = NULL; | |
482 return ret; | |
483 } | |
484 #endif |