]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2006,2011-2012,2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | ||
25 | /* | |
26 | * opensshCoding.cpp - Encoding and decoding of OpenSSH format public keys. | |
27 | * | |
b1ab9ed8 A |
28 | */ |
29 | ||
30 | #include "opensshCoding.h" | |
31 | #include <CoreFoundation/CFData.h> | |
6b200bc3 A |
32 | #include <openssl/bn_legacy.h> |
33 | #include <openssl/crypto_legacy.h> | |
b1ab9ed8 A |
34 | #include <security_cdsa_utils/cuEnc64.h> |
35 | ||
36 | #define SSH2_RSA_HEADER "ssh-rsa" | |
37 | #define SSH2_DSA_HEADER "ssh-dss" | |
38 | ||
39 | #ifndef NDEBUG | |
40 | #include <stdio.h> | |
41 | #define dprintf(s...) printf(s) | |
42 | #else | |
43 | #define dprintf(...) | |
44 | #endif | |
45 | ||
46 | #pragma mark --- commmon code --- | |
47 | ||
48 | uint32_t readUint32( | |
49 | const unsigned char *&cp, // IN/OUT | |
50 | unsigned &len) // IN/OUT | |
51 | { | |
52 | uint32_t r = 0; | |
53 | ||
54 | for(unsigned dex=0; dex<sizeof(uint32_t); dex++) { | |
55 | r <<= 8; | |
56 | r |= *cp++; | |
57 | } | |
58 | len -= 4; | |
59 | return r; | |
60 | } | |
61 | ||
62 | void appendUint32( | |
63 | CFMutableDataRef cfOut, | |
64 | uint32_t ui) | |
65 | { | |
66 | UInt8 buf[sizeof(uint32_t)]; | |
67 | ||
68 | for(int dex=(sizeof(uint32_t) - 1); dex>=0; dex--) { | |
69 | buf[dex] = ui & 0xff; | |
70 | ui >>= 8; | |
71 | } | |
72 | CFDataAppendBytes(cfOut, buf, sizeof(uint32_t)); | |
73 | } | |
74 | ||
75 | ||
76 | /* parse text as decimal, return BIGNUM */ | |
77 | static BIGNUM *parseDecimalBn( | |
78 | const unsigned char *cp, | |
79 | unsigned len) | |
80 | { | |
81 | for(unsigned dex=0; dex<len; dex++) { | |
82 | char c = *cp; | |
83 | if((c < '0') || (c > '9')) { | |
84 | return NULL; | |
85 | } | |
86 | } | |
87 | char *str = (char *)malloc(len + 1); | |
88 | memmove(str, cp, len); | |
89 | str[len] = '\0'; | |
90 | BIGNUM *bn = NULL; | |
91 | BN_dec2bn(&bn, str); | |
92 | free(str); | |
93 | return bn; | |
94 | } | |
95 | ||
96 | /* write BIGNUM, OpenSSH v2 format (with a 4-byte byte count) */ | |
97 | static CSSM_RETURN appendBigNum2( | |
98 | CFMutableDataRef cfOut, | |
99 | const BIGNUM *bn) | |
100 | { | |
101 | if(bn == NULL) { | |
102 | dprintf("appendBigNum2: NULL bn"); | |
103 | return CSSMERR_CSP_INTERNAL_ERROR; | |
104 | } | |
105 | if (BN_is_zero(bn)) { | |
106 | appendUint32(cfOut, 0); | |
107 | return 0; | |
108 | } | |
109 | if(bn->neg) { | |
110 | dprintf("appendBigNum2: negative numbers not supported\n"); | |
111 | return CSSMERR_CSP_INTERNAL_ERROR; | |
112 | } | |
113 | int numBytes = BN_num_bytes(bn); | |
79b9da22 A |
114 | unsigned char *buf = (unsigned char*)malloc(numBytes); |
115 | if (buf == NULL) { | |
116 | dprintf("appendBigNum: Cannot allocate a temp BN buffer\n"); | |
117 | return CSSMERR_CSSM_MEMORY_ERROR; | |
118 | } | |
b1ab9ed8 A |
119 | int moved = BN_bn2bin(bn, buf); |
120 | if(moved != numBytes) { | |
121 | dprintf("appendBigNum: BN_bn2bin() screwup\n"); | |
79b9da22 | 122 | free(buf); |
b1ab9ed8 A |
123 | return CSSMERR_CSP_INTERNAL_ERROR; |
124 | } | |
125 | bool appendZero = false; | |
126 | if(buf[0] & 0x80) { | |
127 | /* prepend leading zero to make it positive */ | |
128 | appendZero = true; | |
129 | numBytes++; // to encode the correct 4-byte length | |
130 | } | |
131 | appendUint32(cfOut, (uint32_t)numBytes); | |
132 | if(appendZero) { | |
133 | UInt8 z = 0; | |
134 | CFDataAppendBytes(cfOut, &z, 1); | |
135 | numBytes--; // to append the correct number of bytes | |
136 | } | |
137 | CFDataAppendBytes(cfOut, buf, numBytes); | |
138 | memset(buf, 0, numBytes); | |
79b9da22 | 139 | free(buf); |
b1ab9ed8 A |
140 | return CSSM_OK; |
141 | } | |
142 | ||
143 | /* read BIGNUM, OpenSSH-2 mpint version */ | |
144 | static BIGNUM *readBigNum2( | |
145 | const unsigned char *&cp, // IN/OUT | |
146 | unsigned &remLen) // IN/OUT | |
147 | { | |
148 | if(remLen < 4) { | |
149 | dprintf("readBigNum2: short record(1)\n"); | |
150 | return NULL; | |
151 | } | |
152 | uint32_t bytes = readUint32(cp, remLen); | |
153 | if(remLen < bytes) { | |
154 | dprintf("readBigNum2: short record(2)\n"); | |
155 | return NULL; | |
156 | } | |
157 | BIGNUM *bn = BN_bin2bn(cp, bytes, NULL); | |
158 | if(bn == NULL) { | |
159 | dprintf("readBigNum2: BN_bin2bn error\n"); | |
160 | return NULL; | |
161 | } | |
162 | cp += bytes; | |
163 | remLen -= bytes; | |
164 | return bn; | |
165 | } | |
166 | ||
167 | /* Write BIGNUM, OpenSSH-1 decimal (public key) version */ | |
168 | static CSSM_RETURN appendBigNumDec( | |
169 | CFMutableDataRef cfOut, | |
170 | const BIGNUM *bn) | |
171 | { | |
172 | char *buf = BN_bn2dec(bn); | |
173 | if(buf == NULL) { | |
174 | dprintf("appendBigNumDec: BN_bn2dec() error"); | |
175 | return CSSMERR_CSP_INTERNAL_ERROR; | |
176 | } | |
177 | CFDataAppendBytes(cfOut, (const UInt8 *)buf, strlen(buf)); | |
178 | Free(buf); | |
179 | return CSSM_OK; | |
180 | } | |
181 | ||
182 | /* write string, OpenSSH v2 format (with a 4-byte byte count) */ | |
183 | static void appendString( | |
184 | CFMutableDataRef cfOut, | |
185 | const char *str, | |
186 | unsigned strLen) | |
187 | { | |
188 | appendUint32(cfOut, (uint32_t)strLen); | |
189 | CFDataAppendBytes(cfOut, (UInt8 *)str, strLen); | |
190 | } | |
191 | ||
192 | /* skip whitespace */ | |
193 | static void skipWhite( | |
194 | const unsigned char *&cp, | |
195 | unsigned &bytesLeft) | |
196 | { | |
197 | while(bytesLeft != 0) { | |
198 | if(isspace((int)(*cp))) { | |
199 | cp++; | |
200 | bytesLeft--; | |
201 | } | |
202 | else { | |
203 | return; | |
204 | } | |
205 | } | |
206 | } | |
207 | ||
208 | /* find next whitespace or EOF - if EOF, rtn pointer points to one past EOF */ | |
209 | static const unsigned char *findNextWhite( | |
210 | const unsigned char *cp, | |
211 | unsigned &bytesLeft) | |
212 | { | |
213 | while(bytesLeft != 0) { | |
214 | if(isspace((int)(*cp))) { | |
215 | return cp; | |
216 | } | |
217 | cp++; | |
218 | bytesLeft--; | |
219 | } | |
220 | return cp; | |
221 | } | |
222 | ||
223 | ||
224 | /* | |
225 | * Decode components from an SSHv2 public key. | |
226 | * Also verifies the leading header, e.g. "ssh-rsa". | |
227 | * The returned decodedBlob is algorithm-specific. | |
228 | */ | |
229 | static CSSM_RETURN parseSSH2PubKey( | |
230 | const unsigned char *key, | |
231 | unsigned keyLen, | |
232 | const char *header, // SSH2_RSA_HEADER, SSH2_DSA_HEADER | |
233 | unsigned char **decodedBlob, // mallocd and RETURNED | |
234 | unsigned *decodedBlobLen) // RETURNED | |
235 | { | |
427c49bc | 236 | size_t len = strlen(header); |
b1ab9ed8 A |
237 | *decodedBlob = NULL; |
238 | ||
239 | /* ID string plus at least one space */ | |
240 | if(keyLen < (len + 1)) { | |
241 | dprintf("parseSSH2PubKey: short record(1)\n"); | |
242 | return CSSMERR_CSP_INVALID_KEY; | |
243 | } | |
244 | ||
245 | if(memcmp(header, key, len)) { | |
246 | dprintf("parseSSH2PubKey: bad header (1)\n"); | |
247 | return CSSMERR_CSP_INVALID_KEY; | |
248 | } | |
249 | key += len; | |
250 | if(*key++ != ' ') { | |
251 | dprintf("parseSSH2PubKey: bad header (2)\n"); | |
252 | return CSSMERR_CSP_INVALID_KEY; | |
253 | } | |
254 | keyLen -= (len + 1); | |
255 | ||
256 | /* key points to first whitespace after header */ | |
257 | skipWhite(key, keyLen); | |
258 | if(keyLen == 0) { | |
259 | dprintf("parseSSH2PubKey: short key\n"); | |
260 | return CSSMERR_CSP_INVALID_KEY; | |
261 | } | |
262 | ||
263 | /* key is start of base64 blob */ | |
264 | const unsigned char *encodedBlob = key; | |
265 | const unsigned char *endBlob = findNextWhite(key, keyLen); | |
427c49bc | 266 | unsigned encodedBlobLen = (unsigned)(endBlob - encodedBlob); |
b1ab9ed8 A |
267 | |
268 | /* decode base 64 */ | |
269 | *decodedBlob = cuDec64(encodedBlob, encodedBlobLen, decodedBlobLen); | |
270 | if(*decodedBlob == NULL) { | |
271 | dprintf("parseSSH2PubKey: base64 decode error\n"); | |
272 | return CSSMERR_CSP_INVALID_KEY; | |
273 | } | |
274 | ||
275 | /* skip remainder; it's comment */ | |
276 | ||
277 | return CSSM_OK; | |
278 | } | |
279 | ||
280 | ||
281 | #pragma mark -- RSA OpenSSHv1 --- | |
282 | ||
283 | CSSM_RETURN RSAPublicKeyEncodeOpenSSH1( | |
284 | RSA *rsa, | |
285 | const CssmData &descData, | |
286 | CssmOwnedData &encodedKey) | |
287 | { | |
288 | CFMutableDataRef cfOut = CFDataCreateMutable(NULL, 0); | |
289 | CSSM_RETURN ourRtn = CSSM_OK; | |
290 | ||
291 | /* | |
292 | * Format is | |
293 | * num_bits in decimal | |
294 | * <space> | |
295 | * e, bignum in decimal | |
296 | * <space> | |
297 | * n, bignum in decimal | |
298 | * <space> | |
299 | * optional comment | |
300 | * newline | |
301 | */ | |
302 | unsigned numBits = BN_num_bits(rsa->n); | |
303 | char bitString[20]; | |
304 | UInt8 c = ' '; | |
305 | ||
306 | snprintf(bitString, sizeof(bitString), "%u ", numBits); | |
307 | CFDataAppendBytes(cfOut, (const UInt8 *)bitString, strlen(bitString)); | |
427c49bc | 308 | if((ourRtn = appendBigNumDec(cfOut, rsa->e))) { |
b1ab9ed8 A |
309 | goto errOut; |
310 | } | |
311 | CFDataAppendBytes(cfOut, &c, 1); | |
427c49bc | 312 | if((ourRtn = appendBigNumDec(cfOut, rsa->n))) { |
b1ab9ed8 A |
313 | goto errOut; |
314 | } | |
315 | ||
316 | if(descData.Length) { | |
317 | /* optional comment */ | |
318 | CFDataAppendBytes(cfOut, &c, 1); | |
319 | CFDataAppendBytes(cfOut, (UInt8 *)descData.Data, descData.Length); | |
320 | } | |
321 | ||
322 | c = '\n'; | |
323 | CFDataAppendBytes(cfOut, &c, 1); | |
324 | encodedKey.copy(CFDataGetBytePtr(cfOut), CFDataGetLength(cfOut)); | |
325 | errOut: | |
326 | CFRelease(cfOut); | |
327 | return ourRtn; | |
328 | } | |
329 | ||
330 | CSSM_RETURN RSAPublicKeyDecodeOpenSSH1( | |
331 | RSA *rsa, | |
332 | void *p, | |
333 | size_t length) | |
334 | { | |
335 | const unsigned char *cp = (const unsigned char *)p; | |
427c49bc | 336 | unsigned remLen = (unsigned)length; |
b1ab9ed8 A |
337 | |
338 | skipWhite(cp, remLen); | |
339 | ||
340 | /* | |
341 | * cp points to start of size_in_bits in ASCII decimal; we really don't care about | |
342 | * this field. Find next space. | |
343 | */ | |
344 | cp = findNextWhite(cp, remLen); | |
345 | if(remLen == 0) { | |
346 | dprintf("RSAPublicKeyDecodeOpenSSH1: short key (1)\n"); | |
347 | return CSSMERR_CSP_INVALID_KEY; | |
348 | } | |
349 | skipWhite(cp, remLen); | |
350 | if(remLen == 0) { | |
351 | dprintf("RSAPublicKeyDecodeOpenSSH1: short key (2)\n"); | |
352 | return CSSMERR_CSP_INVALID_KEY; | |
353 | } | |
354 | ||
355 | /* | |
356 | * cp points to start of e | |
357 | */ | |
358 | const unsigned char *ep = findNextWhite(cp, remLen); | |
359 | if(remLen == 0) { | |
360 | dprintf("RSAPublicKeyDecodeOpenSSH1: short key (3)\n"); | |
361 | return CSSMERR_CSP_INVALID_KEY; | |
362 | } | |
427c49bc | 363 | unsigned len = (unsigned)(ep - cp); |
b1ab9ed8 A |
364 | rsa->e = parseDecimalBn(cp, len); |
365 | if(rsa->e == NULL) { | |
366 | return CSSMERR_CSP_INVALID_KEY; | |
367 | } | |
368 | cp += len; | |
369 | ||
370 | skipWhite(cp, remLen); | |
371 | if(remLen == 0) { | |
372 | dprintf("RSAPublicKeyDecodeOpenSSH1: short key (4)\n"); | |
373 | return -1; | |
374 | } | |
375 | ||
376 | /* cp points to start of n */ | |
377 | ep = findNextWhite(cp, remLen); | |
427c49bc | 378 | len = (unsigned)(ep - cp); |
b1ab9ed8 A |
379 | rsa->n = parseDecimalBn(cp, len); |
380 | if(rsa->n == NULL) { | |
381 | return CSSMERR_CSP_INVALID_KEY; | |
382 | } | |
383 | ||
384 | /* remainder is comment, we ignore */ | |
385 | return CSSM_OK; | |
386 | ||
387 | } | |
388 | ||
389 | CSSM_RETURN RSAPrivateKeyEncodeOpenSSH1( | |
390 | RSA *rsa, | |
391 | const CssmData &descData, | |
392 | CssmOwnedData &encodedKey) | |
393 | { | |
394 | CFDataRef cfOut; | |
395 | CSSM_RETURN ourRtn; | |
396 | ||
427c49bc | 397 | ourRtn = encodeOpenSSHv1PrivKey(rsa, descData.Data, (unsigned)descData.Length, NULL, &cfOut); |
b1ab9ed8 A |
398 | if(ourRtn) { |
399 | return ourRtn; | |
400 | } | |
401 | encodedKey.copy(CFDataGetBytePtr(cfOut), CFDataGetLength(cfOut)); | |
402 | CFRelease(cfOut); | |
403 | return CSSM_OK; | |
404 | } | |
405 | ||
406 | extern CSSM_RETURN RSAPrivateKeyDecodeOpenSSH1( | |
407 | RSA *openKey, | |
408 | void *p, | |
409 | size_t length) | |
410 | { | |
427c49bc | 411 | return decodeOpenSSHv1PrivKey((const unsigned char *)p, (unsigned)length, |
b1ab9ed8 A |
412 | openKey, NULL, NULL, NULL); |
413 | } | |
414 | ||
415 | #pragma mark -- RSA OpenSSHv2 --- | |
416 | ||
417 | CSSM_RETURN RSAPublicKeyEncodeOpenSSH2( | |
418 | RSA *rsa, | |
419 | const CssmData &descData, | |
420 | CssmOwnedData &encodedKey) | |
421 | { | |
422 | unsigned char *b64 = NULL; | |
423 | unsigned b64Len; | |
424 | UInt8 c; | |
425 | ||
426 | /* | |
427 | * First, the inner base64-encoded blob, consisting of | |
428 | * ssh-rsa | |
429 | * e | |
430 | * n | |
431 | */ | |
432 | CFMutableDataRef cfOut = CFDataCreateMutable(NULL, 0); | |
433 | CSSM_RETURN ourRtn = CSSM_OK; | |
434 | appendString(cfOut, SSH2_RSA_HEADER, strlen(SSH2_RSA_HEADER)); | |
427c49bc | 435 | if((ourRtn = appendBigNum2(cfOut, rsa->e))) { |
b1ab9ed8 A |
436 | goto errOut; |
437 | } | |
427c49bc | 438 | if((ourRtn = appendBigNum2(cfOut, rsa->n))) { |
b1ab9ed8 A |
439 | goto errOut; |
440 | } | |
441 | ||
442 | /* base64 encode that */ | |
427c49bc | 443 | b64 = cuEnc64((unsigned char *)CFDataGetBytePtr(cfOut), (unsigned)CFDataGetLength(cfOut), &b64Len); |
b1ab9ed8 A |
444 | |
445 | /* cuEnc64 added newline and NULL, which we really don't want */ | |
446 | b64Len -= 2; | |
447 | ||
448 | /* Now start over, dropping that base64 into a public blob. */ | |
449 | CFDataSetLength(cfOut, 0); | |
450 | CFDataAppendBytes(cfOut, (UInt8 *)SSH2_RSA_HEADER, strlen(SSH2_RSA_HEADER)); | |
451 | c = ' '; | |
452 | CFDataAppendBytes(cfOut, &c, 1); | |
453 | CFDataAppendBytes(cfOut, b64, b64Len); | |
454 | ||
455 | if(descData.Length) { | |
456 | /* optional comment */ | |
457 | CFDataAppendBytes(cfOut, &c, 1); | |
458 | CFDataAppendBytes(cfOut, (UInt8 *)descData.Data, descData.Length); | |
459 | } | |
460 | ||
461 | /* finish it with a newline */ | |
462 | c = '\n'; | |
463 | CFDataAppendBytes(cfOut, &c, 1); | |
464 | ||
465 | encodedKey.copy(CFDataGetBytePtr(cfOut), CFDataGetLength(cfOut)); | |
466 | errOut: | |
467 | CFRelease(cfOut); | |
468 | if(b64) { | |
469 | free(b64); | |
470 | } | |
471 | return ourRtn; | |
472 | } | |
473 | ||
474 | CSSM_RETURN RSAPublicKeyDecodeOpenSSH2( | |
475 | RSA *rsa, | |
476 | void *p, | |
477 | size_t length) | |
478 | { | |
479 | const unsigned char *key = (const unsigned char *)p; | |
427c49bc | 480 | unsigned keyLen = (unsigned)length; |
b1ab9ed8 A |
481 | CSSM_RETURN ourRtn; |
482 | ||
483 | /* | |
484 | * Verify header | |
485 | * get base64-decoded blob | |
486 | */ | |
487 | unsigned char *decodedBlob = NULL; | |
488 | unsigned decodedBlobLen = 0; | |
427c49bc | 489 | if((ourRtn = parseSSH2PubKey(key, keyLen, SSH2_RSA_HEADER, &decodedBlob, &decodedBlobLen))) { |
b1ab9ed8 A |
490 | return ourRtn; |
491 | } | |
492 | /* subsequent errors to errOut: */ | |
493 | ||
494 | /* | |
495 | * The inner base64-decoded blob, consisting of | |
496 | * ssh-rsa | |
497 | * e | |
498 | * n | |
499 | */ | |
500 | uint32_t decLen; | |
501 | unsigned len; | |
502 | ||
503 | key = decodedBlob; | |
504 | keyLen = decodedBlobLen; | |
505 | if(keyLen < 12) { | |
506 | /* three length fields at least */ | |
507 | dprintf("RSAPublicKeyDecodeOpenSSH2: short record(2)\n"); | |
508 | ourRtn = -1; | |
509 | goto errOut; | |
510 | } | |
511 | decLen = readUint32(key, keyLen); | |
512 | len = strlen(SSH2_RSA_HEADER); | |
513 | if(decLen != len) { | |
514 | dprintf("RSAPublicKeyDecodeOpenSSH2: bad header (2)\n"); | |
515 | ourRtn = CSSMERR_CSP_INVALID_KEY; | |
516 | goto errOut; | |
517 | } | |
518 | if(memcmp(SSH2_RSA_HEADER, key, len)) { | |
519 | dprintf("RSAPublicKeyDecodeOpenSSH2: bad header (1)\n"); | |
520 | return CSSMERR_CSP_INVALID_KEY; | |
521 | } | |
522 | key += len; | |
523 | keyLen -= len; | |
524 | ||
525 | rsa->e = readBigNum2(key, keyLen); | |
526 | if(rsa->e == NULL) { | |
527 | ourRtn = CSSMERR_CSP_INVALID_KEY; | |
528 | goto errOut; | |
529 | } | |
530 | rsa->n = readBigNum2(key, keyLen); | |
531 | if(rsa->n == NULL) { | |
532 | ourRtn = CSSMERR_CSP_INVALID_KEY; | |
533 | goto errOut; | |
534 | } | |
535 | ||
536 | errOut: | |
537 | free(decodedBlob); | |
538 | return ourRtn; | |
539 | } | |
540 | ||
541 | #pragma mark -- DSA OpenSSHv2 --- | |
542 | ||
543 | CSSM_RETURN DSAPublicKeyEncodeOpenSSH2( | |
544 | DSA *dsa, | |
545 | const CssmData &descData, | |
546 | CssmOwnedData &encodedKey) | |
547 | { | |
548 | unsigned char *b64 = NULL; | |
549 | unsigned b64Len; | |
550 | UInt8 c; | |
551 | ||
552 | /* | |
553 | * First, the inner base64-encoded blob, consisting of | |
554 | * ssh-dss | |
555 | * p | |
556 | * q | |
557 | * g | |
558 | * pub_key | |
559 | */ | |
560 | CFMutableDataRef cfOut = CFDataCreateMutable(NULL, 0); | |
561 | int ourRtn = 0; | |
562 | appendString(cfOut, SSH2_DSA_HEADER, strlen(SSH2_DSA_HEADER)); | |
427c49bc | 563 | if((ourRtn = appendBigNum2(cfOut, dsa->p))) { |
b1ab9ed8 A |
564 | goto errOut; |
565 | } | |
427c49bc | 566 | if((ourRtn = appendBigNum2(cfOut, dsa->q))) { |
b1ab9ed8 A |
567 | goto errOut; |
568 | } | |
427c49bc | 569 | if((ourRtn = appendBigNum2(cfOut, dsa->g))) { |
b1ab9ed8 A |
570 | goto errOut; |
571 | } | |
427c49bc | 572 | if((ourRtn = appendBigNum2(cfOut, dsa->pub_key))) { |
b1ab9ed8 A |
573 | goto errOut; |
574 | } | |
575 | ||
576 | /* base64 encode that */ | |
427c49bc | 577 | b64 = cuEnc64((unsigned char *)CFDataGetBytePtr(cfOut), (unsigned)CFDataGetLength(cfOut), &b64Len); |
b1ab9ed8 A |
578 | |
579 | /* cuEnc64 added newline and NULL, which we really don't want */ | |
580 | b64Len -= 2; | |
581 | ||
582 | /* Now start over, dropping that base64 into a public blob. */ | |
583 | CFDataSetLength(cfOut, 0); | |
584 | CFDataAppendBytes(cfOut, (UInt8 *)SSH2_DSA_HEADER, strlen(SSH2_DSA_HEADER)); | |
585 | c = ' '; | |
586 | CFDataAppendBytes(cfOut, &c, 1); | |
587 | CFDataAppendBytes(cfOut, b64, b64Len); | |
588 | ||
589 | if(descData.Length) { | |
590 | /* optional comment */ | |
591 | CFDataAppendBytes(cfOut, &c, 1); | |
592 | CFDataAppendBytes(cfOut, (UInt8 *)descData.Data, descData.Length); | |
593 | } | |
594 | ||
595 | /* finish it with a newline */ | |
596 | c = '\n'; | |
597 | CFDataAppendBytes(cfOut, &c, 1); | |
598 | ||
599 | encodedKey.copy(CFDataGetBytePtr(cfOut), CFDataGetLength(cfOut)); | |
600 | ||
601 | errOut: | |
602 | CFRelease(cfOut); | |
603 | if(b64) { | |
604 | free(b64); | |
605 | } | |
606 | return ourRtn; | |
607 | } | |
608 | ||
609 | CSSM_RETURN DSAPublicKeyDecodeOpenSSH2( | |
610 | DSA *dsa, | |
611 | void *p, | |
612 | size_t length) | |
613 | { | |
614 | const unsigned char *key = (const unsigned char *)p; | |
427c49bc | 615 | unsigned keyLen = (unsigned)length; |
b1ab9ed8 A |
616 | CSSM_RETURN ourRtn; |
617 | ||
618 | /* | |
619 | * Verify header | |
620 | * get base64-decoded blob | |
621 | */ | |
622 | unsigned char *decodedBlob = NULL; | |
623 | unsigned decodedBlobLen = 0; | |
427c49bc | 624 | if((ourRtn = parseSSH2PubKey(key, keyLen, SSH2_DSA_HEADER, &decodedBlob, &decodedBlobLen))) { |
b1ab9ed8 A |
625 | return ourRtn; |
626 | } | |
627 | /* subsequent errors to errOut: */ | |
628 | ||
629 | /* | |
630 | * The inner base64-decoded blob, consisting of | |
631 | * ssh-dss | |
632 | * p | |
633 | * q | |
634 | * g | |
635 | * pub_key | |
636 | */ | |
637 | uint32_t decLen; | |
638 | unsigned len; | |
639 | ||
640 | key = decodedBlob; | |
641 | keyLen = decodedBlobLen; | |
642 | if(keyLen < 20) { | |
643 | /* five length fields at least */ | |
644 | dprintf("DSAPublicKeyDecodeOpenSSH2: short record(2)\n"); | |
645 | ourRtn = CSSMERR_CSP_INVALID_KEY; | |
646 | goto errOut; | |
647 | } | |
648 | decLen = readUint32(key, keyLen); | |
649 | len = strlen(SSH2_DSA_HEADER); | |
650 | if(decLen != len) { | |
651 | dprintf("DSAPublicKeyDecodeOpenSSH2: bad header (2)\n"); | |
652 | ourRtn = CSSMERR_CSP_INVALID_KEY; | |
653 | goto errOut; | |
654 | } | |
655 | if(memcmp(SSH2_DSA_HEADER, key, len)) { | |
656 | dprintf("DSAPublicKeyDecodeOpenSSH2: bad header (1)\n"); | |
657 | return CSSMERR_CSP_INVALID_KEY; | |
658 | } | |
659 | key += len; | |
660 | keyLen -= len; | |
661 | ||
662 | dsa->p = readBigNum2(key, keyLen); | |
663 | if(dsa->p == NULL) { | |
664 | ourRtn = CSSMERR_CSP_INVALID_KEY; | |
665 | goto errOut; | |
666 | } | |
667 | dsa->q = readBigNum2(key, keyLen); | |
668 | if(dsa->q == NULL) { | |
669 | ourRtn = CSSMERR_CSP_INVALID_KEY; | |
670 | goto errOut; | |
671 | } | |
672 | dsa->g = readBigNum2(key, keyLen); | |
673 | if(dsa->g == NULL) { | |
674 | ourRtn = CSSMERR_CSP_INVALID_KEY; | |
675 | goto errOut; | |
676 | } | |
677 | dsa->pub_key = readBigNum2(key, keyLen); | |
678 | if(dsa->pub_key == NULL) { | |
679 | ourRtn = CSSMERR_CSP_INVALID_KEY; | |
680 | goto errOut; | |
681 | } | |
682 | ||
683 | errOut: | |
684 | free(decodedBlob); | |
685 | return ourRtn; | |
686 | ||
687 | } | |
688 |