]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_csp/lib/opensshWrap.cpp
Security-59306.11.20.tar.gz
[apple/security.git] / OSX / libsecurity_apple_csp / lib / opensshWrap.cpp
1 /*
2 * Copyright (c) 2006,2011-2012,2014 Apple Inc. All Rights Reserved.
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 * opensshCoding.h - Encoding and decoding of OpenSSH format public keys.
26 *
27 */
28
29 #include "AppleCSPSession.h"
30 #include "AppleCSPContext.h"
31 #include "AppleCSPUtils.h"
32 #include "AppleCSPKeys.h"
33 #include "RSA_DSA_keys.h"
34 #include "SecRandom.h"
35 #include "opensshCoding.h"
36 #include "cspdebugging.h"
37 #include <CommonCrypto/CommonDigest.h>
38 #include <CommonCrypto/CommonCryptor.h>
39 #include <openssl/rsa_legacy.h>
40 #include <openssl/bn_legacy.h>
41 #include <utilities/SecCFRelease.h>
42
43 static const char *authfile_id_string = "SSH PRIVATE KEY FILE FORMAT 1.1\n";
44
45 /* default comment on encode if app doesn't provide DescriptiveData */
46 #define OPENSSH1_COMMENT "Encoded by Mac OS X Security.framework"
47
48 /* from openssh cipher.h */
49 #define SSH_CIPHER_NONE 0 /* no encryption */
50 #define SSH_CIPHER_IDEA 1 /* IDEA CFB */
51 #define SSH_CIPHER_DES 2 /* DES CBC */
52 #define SSH_CIPHER_3DES 3 /* 3DES CBC */
53 #define SSH_CIPHER_BROKEN_TSS 4 /* TRI's Simple Stream encryption CBC */
54 #define SSH_CIPHER_BROKEN_RC4 5 /* Alleged RC4 */
55 #define SSH_CIPHER_BLOWFISH 6
56 #define SSH_CIPHER_RESERVED 7
57
58 #pragma mark --- utilities ---
59
60 static void appendUint16(
61 CFMutableDataRef cfOut,
62 uint16_t ui)
63 {
64 UInt8 buf[sizeof(uint16_t)];
65
66 buf[1] = ui & 0xff;
67 ui >>= 8;
68 buf[0] = ui;
69 CFDataAppendBytes(cfOut, buf, sizeof(uint16_t));
70 }
71
72 static uint16_t readUint16(
73 const unsigned char *&cp, // IN/OUT
74 unsigned &len) // IN/OUT
75 {
76 uint16_t r = *cp++;
77 r <<= 8;
78 r |= *cp++;
79 len -= 2;
80 return r;
81 }
82
83 /* Write BIGNUM, OpenSSH-1 version */
84 static CSSM_RETURN appendBigNum(
85 CFMutableDataRef cfOut,
86 const BIGNUM *bn)
87 {
88 /* 16 bits of numbits */
89 unsigned numBits = BN_num_bits(bn);
90 appendUint16(cfOut, numBits);
91
92 /* serialize the bytes */
93 int numBytes = (numBits + 7) / 8;
94 unsigned char outBytes[numBytes]; // gcc is so cool...
95 int moved = BN_bn2bin(bn, outBytes);
96 if(moved != numBytes) {
97 errorLog0("appendBigNum: BN_bn2bin() screwup\n");
98 return CSSMERR_CSP_INTERNAL_ERROR;
99 }
100 CFDataAppendBytes(cfOut, (UInt8 *)outBytes, numBytes);
101 return CSSM_OK;
102 }
103
104 /* Read BIGNUM, OpenSSH-1 version */
105 static BIGNUM *readBigNum(
106 const unsigned char *&cp, // IN/OUT
107 unsigned &remLen) // IN/OUT
108 {
109 if(remLen < sizeof(uint16_t)) {
110 errorLog0("readBigNum: short record(1)\n");
111 return NULL;
112 }
113 uint16_t numBits = readUint16(cp, remLen);
114 unsigned bytes = (numBits + 7) / 8;
115 if(remLen < bytes) {
116 errorLog0("readBigNum: short record(2)\n");
117 return NULL;
118 }
119 BIGNUM *bn = BN_bin2bn(cp, bytes, NULL);
120 if(bn == NULL) {
121 errorLog0("readBigNum: BN_bin2bn error\n");
122 return NULL;
123 }
124 cp += bytes;
125 remLen -= bytes;
126 return bn;
127 }
128
129 /*
130 * Calculate d mod{p-1,q-1}
131 * Used when decoding OpenSSH-1 private RSA key.
132 */
133 static CSSM_RETURN rsa_generate_additional_parameters(RSA *rsa)
134 {
135 BIGNUM *aux;
136 BN_CTX *ctx;
137
138 if((rsa->dmq1 = BN_new()) == NULL) {
139 errorLog0("rsa_generate_additional_parameters: BN_new failed");
140 return CSSMERR_CSP_INTERNAL_ERROR;
141 }
142 if((rsa->dmp1 = BN_new()) == NULL) {
143 errorLog0("rsa_generate_additional_parameters: BN_new failed");
144 return CSSMERR_CSP_INTERNAL_ERROR;
145 }
146 if ((aux = BN_new()) == NULL) {
147 errorLog0("rsa_generate_additional_parameters: BN_new failed");
148 return CSSMERR_CSP_INTERNAL_ERROR;
149 }
150 if ((ctx = BN_CTX_new()) == NULL) {
151 errorLog0("rsa_generate_additional_parameters: BN_CTX_new failed");
152 BN_clear_free(aux);
153 return CSSMERR_CSP_INTERNAL_ERROR;
154 }
155
156 BN_sub(aux, rsa->q, BN_value_one());
157 BN_mod(rsa->dmq1, rsa->d, aux, ctx);
158
159 BN_sub(aux, rsa->p, BN_value_one());
160 BN_mod(rsa->dmp1, rsa->d, aux, ctx);
161
162 BN_clear_free(aux);
163 BN_CTX_free(ctx);
164 return CSSM_OK;
165 }
166
167 #pragma mark --- encrypt/decrypt ---
168
169 /*
170 * Encrypt/decrypt the secret portion of an OpenSSHv1 format RSA private key.
171 */
172 static CSSM_RETURN ssh1DES3Crypt(
173 unsigned char cipher,
174 bool doEncrypt,
175 const unsigned char *inText,
176 unsigned inTextLen,
177 const uint8 *key, // MD5(password)
178 CSSM_SIZE keyLen,
179 unsigned char *outText, // data RETURNED here, caller mallocs.
180 unsigned *outTextLen) // RETURNED
181 {
182 switch(cipher) {
183 case SSH_CIPHER_3DES:
184 break;
185 case SSH_CIPHER_NONE:
186 /* cleartext RSA private key, e.g. host key. */
187 memmove(outText, inText, inTextLen);
188 *outTextLen = inTextLen;
189 return CSSM_OK;
190 default:
191 /* who knows how we're going to figure these out */
192 errorLog1("***ssh1DES3Crypt: Unsupported cipher (%u)\n", cipher);
193 return CSSMERR_CSP_INVALID_KEY;
194 }
195
196 if(keyLen != CC_MD5_DIGEST_LENGTH) {
197 errorLog0("ssh1DES3Crypt: bad key length\n");
198 return CSSMERR_CSP_INVALID_KEY;
199 }
200
201 /* three keys from that, like so: */
202 unsigned char k1[kCCKeySizeDES];
203 unsigned char k2[kCCKeySizeDES];
204 unsigned char k3[kCCKeySizeDES];
205 memmove(k1, key, kCCKeySizeDES);
206 memmove(k2, key + kCCKeySizeDES, kCCKeySizeDES);
207 memmove(k3, key, kCCKeySizeDES);
208
209 CCOperation op1_3;
210 CCOperation op2;
211 if(doEncrypt) {
212 op1_3 = kCCEncrypt;
213 op2 = kCCDecrypt;
214 }
215 else {
216 op1_3 = kCCDecrypt;
217 op2 = kCCEncrypt;
218 }
219
220 /* the openssh v1 pseudo triple DES. Each DES pass has its own CBC. */
221 size_t moved = 0;
222
223 CCCryptorStatus cstat = CCCrypt(op1_3, kCCAlgorithmDES,
224 0, // no padding
225 k1, kCCKeySizeDES,
226 NULL, // IV
227 inText, inTextLen,
228 outText, inTextLen, &moved);
229 if(cstat) {
230 /* should never happen */
231 errorLog1("***ssh1DES3Crypt: CCCrypt()(1) returned %u\n", (unsigned)cstat);
232 return CSSMERR_CSP_INTERNAL_ERROR;
233 }
234 cstat = CCCrypt(op2, kCCAlgorithmDES,
235 0, // no padding - SSH does that itself
236 k2, kCCKeySizeDES,
237 NULL, // IV
238 outText, moved,
239 outText, inTextLen, &moved);
240 if(cstat) {
241 errorLog1("***ssh1DES3Crypt: CCCrypt()(2) returned %u\n", (unsigned)cstat);
242 return CSSMERR_CSP_INTERNAL_ERROR;
243 }
244 cstat = CCCrypt(op1_3, kCCAlgorithmDES,
245 0, // no padding - SSH does that itself
246 k3, kCCKeySizeDES,
247 NULL, // IV
248 outText, moved,
249 outText, inTextLen, &moved);
250 if(cstat) {
251 errorLog1("***ssh1DES3Crypt: CCCrypt()(3) returned %u\n", (unsigned)cstat);
252 return CSSMERR_CSP_INTERNAL_ERROR;
253 }
254
255 *outTextLen = (unsigned)moved;
256 return CSSM_OK;
257 }
258
259 #pragma mark --- DeriveKey ---
260
261 /*
262 * Key derivation for OpenSSH1 private key wrap/unwrap.
263 * This is pretty trivial, it's just an MD5() operation. The main
264 * purpose for doing this in a DeriveKey operation is to enable the
265 * use of either Secure Passphrases, obtained by securityd/SecurityAgent,
266 * or app-specified data.
267 */
268 void AppleCSPSession::DeriveKey_OpenSSH1(
269 const Context &context,
270 CSSM_ALGORITHMS algId,
271 const CssmData &Param, // IV optional, mallocd by app to indicate
272 // size
273 CSSM_DATA *keyData) // mallocd by caller to indicate size - must be
274 // size of MD5 digest!
275 {
276 CSSM_DATA pwd = {0, NULL};
277
278 if(keyData->Length != CC_MD5_DIGEST_LENGTH) {
279 errorLog0("DeriveKey_OpenSSH1: invalid key length\n");
280 CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_SIZE);
281 }
282
283 /* password from either Seed.Param or from base key */
284 CssmCryptoData *cryptData =
285 context.get<CssmCryptoData>(CSSM_ATTRIBUTE_SEED);
286 if((cryptData != NULL) && (cryptData->Param.Length != 0)) {
287 pwd = cryptData->Param;
288 }
289 else {
290 /* Get secure passphrase from base key */
291 CssmKey *passKey = context.get<CssmKey>(CSSM_ATTRIBUTE_KEY);
292 if (passKey != NULL) {
293 AppleCSPContext::symmetricKeyBits(context, *this,
294 CSSM_ALGID_SECURE_PASSPHRASE, CSSM_KEYUSE_DERIVE,
295 pwd.Data, pwd.Length);
296 }
297 }
298
299 if(pwd.Data == NULL) {
300 errorLog0("DeriveKey_PKCS5_V1_5: null Passphrase\n");
301 CssmError::throwMe(CSSMERR_CSP_INVALID_DATA);
302 }
303 if(pwd.Length == 0) {
304 errorLog0("DeriveKey_PKCS5_V1_5: zero length passphrase\n");
305 CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER);
306 }
307
308 /* here it is */
309 CC_MD5(pwd.Data, (CC_LONG)pwd.Length, keyData->Data);
310
311 }
312
313 #pragma mark --- Encode/Wrap OpenSSHv1 private key ---
314
315 /*
316 * Encode OpenSSHv1 private key, with or without encryption.
317 * This used for generating key blobs of format CSSM_KEYBLOB_RAW_FORMAT_OPENSSH
318 * as well as wrapping keys in format CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1.
319 */
320 CSSM_RETURN encodeOpenSSHv1PrivKey(
321 RSA *rsa,
322 const uint8 *comment, /* optional */
323 unsigned commentLen,
324 const uint8 *encryptKey, /* optional; if present, it's 16 bytes of MD5(password) */
325 CFDataRef *encodedKey) /* RETURNED */
326 {
327 CFMutableDataRef cfOut = CFDataCreateMutable(NULL, 0);
328 CSSM_RETURN ourRtn = CSSM_OK;
329
330 /* ID string including NULL */
331 CFDataAppendBytes(cfOut, (const UInt8 *)authfile_id_string, strlen(authfile_id_string) + 1);
332
333 /* one byte cipher */
334 UInt8 cipherSpec = encryptKey ? SSH_CIPHER_3DES : SSH_CIPHER_NONE;
335 CFDataAppendBytes(cfOut, &cipherSpec, 1);
336
337 /* spares */
338 UInt8 spares[4] = {0};
339 CFDataAppendBytes(cfOut, spares, 4);
340
341 /*
342 * Clear text public key:
343 * uint32 bits
344 * bignum n
345 * bignum e
346 */
347 uint32_t keybits = RSA_size(rsa) * 8;
348 appendUint32(cfOut, keybits);
349 appendBigNum(cfOut, rsa->n);
350 appendBigNum(cfOut, rsa->e);
351
352 /*
353 * Comment string.
354 * The format appears to require this, or else we wouldn't know
355 * when we've got to the ciphertext on decode.
356 */
357 if((comment == NULL) || (commentLen == 0)) {
358 comment = (const UInt8 *)OPENSSH1_COMMENT;
359 commentLen = strlen(OPENSSH1_COMMENT);
360 }
361 appendUint32(cfOut, commentLen);
362 CFDataAppendBytes(cfOut, comment, commentLen);
363
364 /*
365 * Remainder is encrypted, consisting of
366 *
367 * [0-1] -- random bytes
368 * [2-3] -- copy of [01] for passphrase validity checking
369 * buffer_put_bignum(d)
370 * buffer_put_bignum(iqmp)
371 * buffer_put_bignum(q)
372 * buffer_put_bignum(p)
373 * pad to block size
374 */
375 CFMutableDataRef ptext = CFDataCreateMutable(NULL, 0);
376
377 /* [0..3] check bytes */
378 UInt8 checkBytes[4];
379 MacOSError::check(SecRandomCopyBytes(kSecRandomDefault, 2, checkBytes)) ;
380 checkBytes[2] = checkBytes[0];
381 checkBytes[3] = checkBytes[1];
382 CFDataAppendBytes(ptext, checkBytes, 4);
383
384 /* d, iqmp, q, p */
385 appendBigNum(ptext, rsa->d);
386 appendBigNum(ptext, rsa->iqmp);
387 appendBigNum(ptext, rsa->q);
388 appendBigNum(ptext, rsa->p);
389
390 /* pad to block boundary */
391 CFIndex ptextLen = CFDataGetLength(ptext);
392 unsigned padding = 0;
393 unsigned rem = (unsigned)ptextLen & 0x7;
394 if(rem) {
395 padding = 8 - rem;
396 }
397 UInt8 padByte = 0;
398 for(unsigned dex=0; dex<padding; dex++) {
399 CFDataAppendBytes(ptext, &padByte, 1);
400 }
401
402 /* encrypt it */
403 ptextLen = CFDataGetLength(ptext);
404 unsigned char *ctext = (unsigned char*)malloc(ptextLen);
405 if(ctext == NULL) {
406 ourRtn = CSSMERR_CSSM_MEMORY_ERROR;
407 goto errOut;
408 }
409
410 unsigned ctextLen;
411 ourRtn = ssh1DES3Crypt(cipherSpec, true,
412 (unsigned char *)CFDataGetBytePtr(ptext), (unsigned)ptextLen,
413 encryptKey, encryptKey ? CC_MD5_DIGEST_LENGTH : 0,
414 ctext, &ctextLen);
415 if(ourRtn != 0) {
416 goto errOut;
417 }
418
419 /* appended encrypted portion */
420 CFDataAppendBytes(cfOut, ctext, ctextLen);
421 *encodedKey = cfOut;
422 goto cleanup;
423 errOut:
424 CFReleaseNull(cfOut);
425 cleanup:
426 /* it would be proper to zero out ptext here, but we can't do that to a CFData */
427 free(ctext);
428 CFRelease(ptext);
429 return ourRtn;
430 }
431
432 void AppleCSPSession::WrapKeyOpenSSH1(
433 CSSM_CC_HANDLE CCHandle,
434 const Context &context,
435 const AccessCredentials &AccessCred,
436 BinaryKey &unwrappedBinKey,
437 CssmData &rawBlob,
438 bool allocdRawBlob, // callee has to free rawBlob
439 const CssmData *DescriptiveData,
440 CssmKey &WrappedKey,
441 CSSM_PRIVILEGE Privilege)
442 {
443 /*
444 * The job here is to convert the RSA key in binKey to the OpenSSHv1 private
445 * key format, and drop that into WrappedKey.KeyData (allocated by the session).
446 *
447 * This cast throws an exception if the key is not an RSA key, which
448 * would be a major bogon, since our caller verified that the unwrapped key
449 * is a private RSA key.
450 */
451 RSABinaryKey &rPubBinKey = dynamic_cast<RSABinaryKey &>(unwrappedBinKey);
452 RSA *rsa = rPubBinKey.mRsaKey;
453 CASSERT(rsa != NULL);
454
455 /*
456 * Get the raw password bits from the wrapping key.
457 * Our caller verified that the context has a symmetric key; this call
458 * ensures that the key is of algorithm CSSM_ALGID_OPENSSH1.
459 * Key length 0 means no encryption.
460 */
461 CSSM_SIZE wrappingKeyLen = 0;
462 uint8 *wrappingKey = NULL;
463
464 AppleCSPContext::symmetricKeyBits(context, *this,
465 CSSM_ALGID_OPENSSH1, CSSM_KEYUSE_WRAP,
466 wrappingKey, wrappingKeyLen);
467 if(wrappingKeyLen != CC_MD5_DIGEST_LENGTH) {
468 errorLog0("AppleCSPSession::WrapKeyOpenSSH1: bad wrapping key length\n");
469 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
470 }
471
472 CFDataRef cfOut = NULL;
473
474 /*
475 * Optional comment string from DescriptiveData.
476 */
477 const UInt8 *comment = NULL;
478 unsigned commentLen = 0;
479 if((DescriptiveData != NULL) && (DescriptiveData->Length != 0)) {
480 comment = (const UInt8 *)DescriptiveData->Data;
481 commentLen = (unsigned)DescriptiveData->Length;
482 }
483
484 /* generate the encrypted blob */
485 CSSM_RETURN crtn = encodeOpenSSHv1PrivKey(rsa, comment, commentLen, wrappingKey, &cfOut);
486 if(crtn) {
487 CssmError::throwMe(crtn);
488 }
489
490 /* allocate key data in session's memory space */
491 CFIndex len = CFDataGetLength(cfOut);
492 setUpData(WrappedKey.KeyData, len, normAllocator);
493 memmove(WrappedKey.KeyData.Data, CFDataGetBytePtr(cfOut), len);
494 CFReleaseNull(cfOut);
495
496 /* outgoing header */
497 WrappedKey.KeyHeader.BlobType = CSSM_KEYBLOB_WRAPPED;
498 // OK to be zero or not present
499 WrappedKey.KeyHeader.WrapMode = CSSM_ALGMODE_NONE;
500 WrappedKey.KeyHeader.Format = CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1;
501 }
502
503 #pragma mark --- Decode/Unwrap OpenSSHv1 private key ---
504
505 /*
506 * Decode OpenSSHv1 private, optionally decrypting the secret portion.
507 * This used for decoding key blobs of format CSSM_KEYBLOB_RAW_FORMAT_OPENSSH
508 * as well as unwrapping keys in format CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1.
509 */
510 CSSM_RETURN decodeOpenSSHv1PrivKey(
511 const unsigned char *encodedKey,
512 unsigned encodedKeyLen,
513 RSA *rsa,
514 const uint8 *decryptKey, /* optional; if present, it's 16 bytes of MD5(password) */
515 uint8 **comment, /* optional, mallocd and RETURNED */
516 unsigned *commentLen) /* RETURNED */
517 {
518 unsigned len = (unsigned)strlen(authfile_id_string);
519 const unsigned char *cp = encodedKey;
520 unsigned remLen = encodedKeyLen;
521 CSSM_RETURN ourRtn = CSSM_OK;
522
523 /* length: ID string, NULL, Cipher, 4-byte spare */
524 if(remLen < (len + 6)) {
525 errorLog0("decodeOpenSSHv1PrivKey: short record(1)\n");
526 return CSSMERR_CSP_INVALID_KEY;
527 }
528
529 /* ID string plus a NULL */
530 if(memcmp(authfile_id_string, cp, len)) {
531 errorLog0("decodeOpenSSHv1PrivKey: bad header\n");
532 return CSSMERR_CSP_INVALID_KEY;
533 }
534 cp += (len + 1);
535 remLen -= (len + 1);
536
537 /* cipher */
538 unsigned char cipherSpec = *cp;
539 switch(cipherSpec) {
540 case SSH_CIPHER_NONE:
541 if(decryptKey != NULL) {
542 errorLog0("decodeOpenSSHv1PrivKey: Attempt to decrypt plaintext key\n");
543 return CSSMERR_CSP_INVALID_KEY;
544 }
545 break;
546 case SSH_CIPHER_3DES:
547 if(decryptKey == NULL) {
548 errorLog0("decodeOpenSSHv1PrivKey: Encrypted key with no decryptKey\n");
549 return CSSMERR_CSP_INVALID_KEY;
550 }
551 break;
552 default:
553 /* I hope we don't see any other values here */
554 errorLog1("decodeOpenSSHv1PrivKey: unknown cipherSpec (%u)\n", cipherSpec);
555 return CSSMERR_CSP_INVALID_KEY;
556 }
557
558 /* skip cipher, spares */
559 cp += 5;
560 remLen -= 5;
561
562 /*
563 * Clear text public key:
564 * uint32 bits
565 * bignum n
566 * bignum e
567 */
568 if(remLen < sizeof(uint32_t)) {
569 errorLog0("decodeOpenSSHv1PrivKey: bad len(1)\n");
570 return CSSMERR_CSP_INVALID_KEY;
571 }
572 /* skip over bits */
573 readUint32(cp, remLen);
574 rsa->n = readBigNum(cp, remLen);
575 if(rsa->n == NULL) {
576 errorLog0("decodeOpenSSHv1PrivKey: error decoding n\n");
577 return CSSMERR_CSP_INVALID_KEY;
578 }
579 rsa->e = readBigNum(cp, remLen);
580 if(rsa->e == NULL) {
581 errorLog0("decodeOpenSSHv1PrivKey: error decoding e\n");
582 return CSSMERR_CSP_INVALID_KEY;
583 }
584
585 /* comment string: 4-byte length and the string w/o NULL */
586 if(remLen < sizeof(uint32_t)) {
587 errorLog0("decodeOpenSSHv1PrivKey: bad len(2)\n");
588 return CSSMERR_CSP_INVALID_KEY;
589 }
590 uint32_t commLen = readUint32(cp, remLen);
591 if(commLen > remLen) {
592 errorLog0("decodeOpenSSHv1PrivKey: bad len(3)\n");
593 return CSSMERR_CSP_INVALID_KEY;
594 }
595 if(comment) {
596 *comment = (uint8 *)malloc(commLen);
597 *commentLen = commLen;
598 memcpy(*comment, cp, commLen);
599 }
600
601 cp += commLen;
602 remLen -= commLen;
603
604 /* everything that remains is ciphertext */
605 unsigned char *ptext = (unsigned char *)malloc(remLen);
606 unsigned ptextLen = 0;
607 ourRtn = ssh1DES3Crypt(cipherSpec, false, cp, remLen,
608 decryptKey, decryptKey ? CC_MD5_DIGEST_LENGTH : 0,
609 ptext, &ptextLen);
610 if(ourRtn) {
611 errorLog0("UnwrapKeyOpenSSH1: decrypt error\n");
612 ourRtn = CSSMERR_CSP_INVALID_KEY;
613 goto errOut;
614 }
615
616 /* plaintext contents:
617
618 [0-1] -- random bytes
619 [2-3] -- copy of [01] for passphrase validity checking
620 buffer_put_bignum(d)
621 buffer_put_bignum(iqmp)
622 buffer_put_bignum(q)
623 buffer_put_bignum(p)
624 pad to block size
625 */
626 cp = ptext;
627 remLen = ptextLen;
628 if(remLen < 4) {
629 errorLog0("UnwrapKeyOpenSSH1: bad len(4)\n");
630 ourRtn = CSSMERR_CSP_INVALID_KEY;
631 goto errOut;
632 }
633 if((cp[0] != cp[2]) || (cp[1] != cp[3])) {
634 /* decrypt fail */
635 errorLog0("UnwrapKeyOpenSSH1: check byte error\n");
636 ourRtn = CSSMERR_CSP_INVALID_KEY;
637 goto errOut;
638 }
639 cp += 4;
640 remLen -= 4;
641
642 /* remainder comprises private portion of RSA key */
643 rsa->d = readBigNum(cp, remLen);
644 if(rsa->d == NULL) {
645 errorLog0("UnwrapKeyOpenSSH1: error decoding d\n");
646 ourRtn = CSSMERR_CSP_INVALID_KEY;
647 goto errOut;
648 }
649 rsa->iqmp = readBigNum(cp, remLen);
650 if(rsa->iqmp == NULL) {
651 errorLog0("UnwrapKeyOpenSSH1: error decoding iqmp\n");
652 ourRtn = CSSMERR_CSP_INVALID_KEY;
653 goto errOut;
654 }
655 rsa->q = readBigNum(cp, remLen);
656 if(rsa->q == NULL) {
657 errorLog0("UnwrapKeyOpenSSH1: error decoding q\n");
658 ourRtn = CSSMERR_CSP_INVALID_KEY;
659 goto errOut;
660 }
661 rsa->p = readBigNum(cp, remLen);
662 if(rsa->p == NULL) {
663 errorLog0("UnwrapKeyOpenSSH1: error decoding p\n");
664 ourRtn = CSSMERR_CSP_INVALID_KEY;
665 goto errOut;
666 }
667
668 /* calculate d mod{p-1,q-1} */
669 ourRtn = rsa_generate_additional_parameters(rsa);
670
671 errOut:
672 if(ptext) {
673 memset(ptext, 0, ptextLen);
674 free(ptext);
675 }
676 return ourRtn;
677 }
678
679 void AppleCSPSession::UnwrapKeyOpenSSH1(
680 CSSM_CC_HANDLE CCHandle,
681 const Context &context,
682 const CssmKey &WrappedKey,
683 const CSSM_RESOURCE_CONTROL_CONTEXT *CredAndAclEntry,
684 CssmKey &UnwrappedKey,
685 CssmData &DescriptiveData,
686 CSSM_PRIVILEGE Privilege,
687 cspKeyStorage keyStorage)
688 {
689 /*
690 * Get the raw password bits from the unwrapping key.
691 * Our caller verified that the context has a symmetric key; this call
692 * ensures that the key is of algorithm CSSM_ALGID_OPENSSH1.
693 */
694 CSSM_SIZE unwrapKeyLen = 0;
695 uint8 *unwrapKey = NULL;
696
697 AppleCSPContext::symmetricKeyBits(context, *this,
698 CSSM_ALGID_OPENSSH1, CSSM_KEYUSE_UNWRAP,
699 unwrapKey, unwrapKeyLen);
700 if((unwrapKey == NULL) || (unwrapKeyLen != CC_MD5_DIGEST_LENGTH)) {
701 errorLog0("AppleCSPSession::UnwrapKeyOpenSSH1: bad unwrapping key length\n");
702 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
703 }
704
705 RSA *rsa = RSA_new();
706 CSSM_RETURN ourRtn = CSSM_OK;
707 unsigned char *comment = NULL;
708 unsigned commentLen = 0;
709 RSABinaryKey *binKey = NULL;
710
711 ourRtn = decodeOpenSSHv1PrivKey((const unsigned char *)WrappedKey.KeyData.Data,
712 (unsigned)WrappedKey.KeyData.Length,
713 rsa, unwrapKey, &comment, &commentLen);
714 if(ourRtn) {
715 goto errOut;
716 }
717 if(comment) {
718 setUpCssmData(DescriptiveData, commentLen, normAllocator);
719 memcpy(DescriptiveData.Data, comment, commentLen);
720 free(comment);
721 }
722
723 /*
724 * Our caller ensured that we're only generating a reference key,
725 * which we do like so:
726 */
727 binKey = new RSABinaryKey(rsa);
728 addRefKey(*binKey, UnwrappedKey);
729
730 errOut:
731 if(ourRtn) {
732 if(rsa) {
733 RSA_free(rsa);
734 }
735 CssmError::throwMe(ourRtn);
736 }
737 }