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