]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_csp/lib/opensshWrap.cpp
Security-58286.200.222.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 "opensshCoding.h"
35 #include "cspdebugging.h"
36 #include <CommonCrypto/CommonDigest.h>
37 #include <CommonCrypto/CommonCryptor.h>
38 #include <openssl/rsa_legacy.h>
39 #include <openssl/bn_legacy.h>
40 #include <security_utilities/devrandom.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 DevRandomGenerator rng = DevRandomGenerator();
380 rng.random(checkBytes, 2);
381 checkBytes[2] = checkBytes[0];
382 checkBytes[3] = checkBytes[1];
383 CFDataAppendBytes(ptext, checkBytes, 4);
384
385 /* d, iqmp, q, p */
386 appendBigNum(ptext, rsa->d);
387 appendBigNum(ptext, rsa->iqmp);
388 appendBigNum(ptext, rsa->q);
389 appendBigNum(ptext, rsa->p);
390
391 /* pad to block boundary */
392 CFIndex ptextLen = CFDataGetLength(ptext);
393 unsigned padding = 0;
394 unsigned rem = (unsigned)ptextLen & 0x7;
395 if(rem) {
396 padding = 8 - rem;
397 }
398 UInt8 padByte = 0;
399 for(unsigned dex=0; dex<padding; dex++) {
400 CFDataAppendBytes(ptext, &padByte, 1);
401 }
402
403 /* encrypt it */
404 ptextLen = CFDataGetLength(ptext);
405 unsigned char *ctext = (unsigned char*)malloc(ptextLen);
406 if(ctext == NULL) {
407 ourRtn = CSSMERR_CSSM_MEMORY_ERROR;
408 goto errOut;
409 }
410
411 unsigned ctextLen;
412 ourRtn = ssh1DES3Crypt(cipherSpec, true,
413 (unsigned char *)CFDataGetBytePtr(ptext), (unsigned)ptextLen,
414 encryptKey, encryptKey ? CC_MD5_DIGEST_LENGTH : 0,
415 ctext, &ctextLen);
416 if(ourRtn != 0) {
417 goto errOut;
418 }
419
420 /* appended encrypted portion */
421 CFDataAppendBytes(cfOut, ctext, ctextLen);
422 *encodedKey = cfOut;
423 goto cleanup;
424 errOut:
425 CFReleaseNull(cfOut);
426 cleanup:
427 /* it would be proper to zero out ptext here, but we can't do that to a CFData */
428 free(ctext);
429 CFRelease(ptext);
430 return ourRtn;
431 }
432
433 void AppleCSPSession::WrapKeyOpenSSH1(
434 CSSM_CC_HANDLE CCHandle,
435 const Context &context,
436 const AccessCredentials &AccessCred,
437 BinaryKey &unwrappedBinKey,
438 CssmData &rawBlob,
439 bool allocdRawBlob, // callee has to free rawBlob
440 const CssmData *DescriptiveData,
441 CssmKey &WrappedKey,
442 CSSM_PRIVILEGE Privilege)
443 {
444 /*
445 * The job here is to convert the RSA key in binKey to the OpenSSHv1 private
446 * key format, and drop that into WrappedKey.KeyData (allocated by the session).
447 *
448 * This cast throws an exception if the key is not an RSA key, which
449 * would be a major bogon, since our caller verified that the unwrapped key
450 * is a private RSA key.
451 */
452 RSABinaryKey &rPubBinKey = dynamic_cast<RSABinaryKey &>(unwrappedBinKey);
453 RSA *rsa = rPubBinKey.mRsaKey;
454 CASSERT(rsa != NULL);
455
456 /*
457 * Get the raw password bits from the wrapping key.
458 * Our caller verified that the context has a symmetric key; this call
459 * ensures that the key is of algorithm CSSM_ALGID_OPENSSH1.
460 * Key length 0 means no encryption.
461 */
462 CSSM_SIZE wrappingKeyLen = 0;
463 uint8 *wrappingKey = NULL;
464
465 AppleCSPContext::symmetricKeyBits(context, *this,
466 CSSM_ALGID_OPENSSH1, CSSM_KEYUSE_WRAP,
467 wrappingKey, wrappingKeyLen);
468 if(wrappingKeyLen != CC_MD5_DIGEST_LENGTH) {
469 errorLog0("AppleCSPSession::WrapKeyOpenSSH1: bad wrapping key length\n");
470 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
471 }
472
473 CFDataRef cfOut = NULL;
474
475 /*
476 * Optional comment string from DescriptiveData.
477 */
478 const UInt8 *comment = NULL;
479 unsigned commentLen = 0;
480 if((DescriptiveData != NULL) && (DescriptiveData->Length != 0)) {
481 comment = (const UInt8 *)DescriptiveData->Data;
482 commentLen = (unsigned)DescriptiveData->Length;
483 }
484
485 /* generate the encrypted blob */
486 CSSM_RETURN crtn = encodeOpenSSHv1PrivKey(rsa, comment, commentLen, wrappingKey, &cfOut);
487 if(crtn) {
488 CssmError::throwMe(crtn);
489 }
490
491 /* allocate key data in session's memory space */
492 CFIndex len = CFDataGetLength(cfOut);
493 setUpData(WrappedKey.KeyData, len, normAllocator);
494 memmove(WrappedKey.KeyData.Data, CFDataGetBytePtr(cfOut), len);
495 CFReleaseNull(cfOut);
496
497 /* outgoing header */
498 WrappedKey.KeyHeader.BlobType = CSSM_KEYBLOB_WRAPPED;
499 // OK to be zero or not present
500 WrappedKey.KeyHeader.WrapMode = CSSM_ALGMODE_NONE;
501 WrappedKey.KeyHeader.Format = CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1;
502 }
503
504 #pragma mark --- Decode/Unwrap OpenSSHv1 private key ---
505
506 /*
507 * Decode OpenSSHv1 private, optionally decrypting the secret portion.
508 * This used for decoding key blobs of format CSSM_KEYBLOB_RAW_FORMAT_OPENSSH
509 * as well as unwrapping keys in format CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1.
510 */
511 CSSM_RETURN decodeOpenSSHv1PrivKey(
512 const unsigned char *encodedKey,
513 unsigned encodedKeyLen,
514 RSA *rsa,
515 const uint8 *decryptKey, /* optional; if present, it's 16 bytes of MD5(password) */
516 uint8 **comment, /* optional, mallocd and RETURNED */
517 unsigned *commentLen) /* RETURNED */
518 {
519 unsigned len = (unsigned)strlen(authfile_id_string);
520 const unsigned char *cp = encodedKey;
521 unsigned remLen = encodedKeyLen;
522 CSSM_RETURN ourRtn = CSSM_OK;
523
524 /* length: ID string, NULL, Cipher, 4-byte spare */
525 if(remLen < (len + 6)) {
526 errorLog0("decodeOpenSSHv1PrivKey: short record(1)\n");
527 return CSSMERR_CSP_INVALID_KEY;
528 }
529
530 /* ID string plus a NULL */
531 if(memcmp(authfile_id_string, cp, len)) {
532 errorLog0("decodeOpenSSHv1PrivKey: bad header\n");
533 return CSSMERR_CSP_INVALID_KEY;
534 }
535 cp += (len + 1);
536 remLen -= (len + 1);
537
538 /* cipher */
539 unsigned char cipherSpec = *cp;
540 switch(cipherSpec) {
541 case SSH_CIPHER_NONE:
542 if(decryptKey != NULL) {
543 errorLog0("decodeOpenSSHv1PrivKey: Attempt to decrypt plaintext key\n");
544 return CSSMERR_CSP_INVALID_KEY;
545 }
546 break;
547 case SSH_CIPHER_3DES:
548 if(decryptKey == NULL) {
549 errorLog0("decodeOpenSSHv1PrivKey: Encrypted key with no decryptKey\n");
550 return CSSMERR_CSP_INVALID_KEY;
551 }
552 break;
553 default:
554 /* I hope we don't see any other values here */
555 errorLog1("decodeOpenSSHv1PrivKey: unknown cipherSpec (%u)\n", cipherSpec);
556 return CSSMERR_CSP_INVALID_KEY;
557 }
558
559 /* skip cipher, spares */
560 cp += 5;
561 remLen -= 5;
562
563 /*
564 * Clear text public key:
565 * uint32 bits
566 * bignum n
567 * bignum e
568 */
569 if(remLen < sizeof(uint32_t)) {
570 errorLog0("decodeOpenSSHv1PrivKey: bad len(1)\n");
571 return CSSMERR_CSP_INVALID_KEY;
572 }
573 /* skip over bits */
574 readUint32(cp, remLen);
575 rsa->n = readBigNum(cp, remLen);
576 if(rsa->n == NULL) {
577 errorLog0("decodeOpenSSHv1PrivKey: error decoding n\n");
578 return CSSMERR_CSP_INVALID_KEY;
579 }
580 rsa->e = readBigNum(cp, remLen);
581 if(rsa->e == NULL) {
582 errorLog0("decodeOpenSSHv1PrivKey: error decoding e\n");
583 return CSSMERR_CSP_INVALID_KEY;
584 }
585
586 /* comment string: 4-byte length and the string w/o NULL */
587 if(remLen < sizeof(uint32_t)) {
588 errorLog0("decodeOpenSSHv1PrivKey: bad len(2)\n");
589 return CSSMERR_CSP_INVALID_KEY;
590 }
591 uint32_t commLen = readUint32(cp, remLen);
592 if(commLen > remLen) {
593 errorLog0("decodeOpenSSHv1PrivKey: bad len(3)\n");
594 return CSSMERR_CSP_INVALID_KEY;
595 }
596 if(comment) {
597 *comment = (uint8 *)malloc(commLen);
598 *commentLen = commLen;
599 memcpy(*comment, cp, commLen);
600 }
601
602 cp += commLen;
603 remLen -= commLen;
604
605 /* everything that remains is ciphertext */
606 unsigned char *ptext = (unsigned char *)malloc(remLen);
607 unsigned ptextLen = 0;
608 ourRtn = ssh1DES3Crypt(cipherSpec, false, cp, remLen,
609 decryptKey, decryptKey ? CC_MD5_DIGEST_LENGTH : 0,
610 ptext, &ptextLen);
611 if(ourRtn) {
612 errorLog0("UnwrapKeyOpenSSH1: decrypt error\n");
613 ourRtn = CSSMERR_CSP_INVALID_KEY;
614 goto errOut;
615 }
616
617 /* plaintext contents:
618
619 [0-1] -- random bytes
620 [2-3] -- copy of [01] for passphrase validity checking
621 buffer_put_bignum(d)
622 buffer_put_bignum(iqmp)
623 buffer_put_bignum(q)
624 buffer_put_bignum(p)
625 pad to block size
626 */
627 cp = ptext;
628 remLen = ptextLen;
629 if(remLen < 4) {
630 errorLog0("UnwrapKeyOpenSSH1: bad len(4)\n");
631 ourRtn = CSSMERR_CSP_INVALID_KEY;
632 goto errOut;
633 }
634 if((cp[0] != cp[2]) || (cp[1] != cp[3])) {
635 /* decrypt fail */
636 errorLog0("UnwrapKeyOpenSSH1: check byte error\n");
637 ourRtn = CSSMERR_CSP_INVALID_KEY;
638 goto errOut;
639 }
640 cp += 4;
641 remLen -= 4;
642
643 /* remainder comprises private portion of RSA key */
644 rsa->d = readBigNum(cp, remLen);
645 if(rsa->d == NULL) {
646 errorLog0("UnwrapKeyOpenSSH1: error decoding d\n");
647 ourRtn = CSSMERR_CSP_INVALID_KEY;
648 goto errOut;
649 }
650 rsa->iqmp = readBigNum(cp, remLen);
651 if(rsa->iqmp == NULL) {
652 errorLog0("UnwrapKeyOpenSSH1: error decoding iqmp\n");
653 ourRtn = CSSMERR_CSP_INVALID_KEY;
654 goto errOut;
655 }
656 rsa->q = readBigNum(cp, remLen);
657 if(rsa->q == NULL) {
658 errorLog0("UnwrapKeyOpenSSH1: error decoding q\n");
659 ourRtn = CSSMERR_CSP_INVALID_KEY;
660 goto errOut;
661 }
662 rsa->p = readBigNum(cp, remLen);
663 if(rsa->p == NULL) {
664 errorLog0("UnwrapKeyOpenSSH1: error decoding p\n");
665 ourRtn = CSSMERR_CSP_INVALID_KEY;
666 goto errOut;
667 }
668
669 /* calculate d mod{p-1,q-1} */
670 ourRtn = rsa_generate_additional_parameters(rsa);
671
672 errOut:
673 if(ptext) {
674 memset(ptext, 0, ptextLen);
675 free(ptext);
676 }
677 return ourRtn;
678 }
679
680 void AppleCSPSession::UnwrapKeyOpenSSH1(
681 CSSM_CC_HANDLE CCHandle,
682 const Context &context,
683 const CssmKey &WrappedKey,
684 const CSSM_RESOURCE_CONTROL_CONTEXT *CredAndAclEntry,
685 CssmKey &UnwrappedKey,
686 CssmData &DescriptiveData,
687 CSSM_PRIVILEGE Privilege,
688 cspKeyStorage keyStorage)
689 {
690 /*
691 * Get the raw password bits from the unwrapping key.
692 * Our caller verified that the context has a symmetric key; this call
693 * ensures that the key is of algorithm CSSM_ALGID_OPENSSH1.
694 */
695 CSSM_SIZE unwrapKeyLen = 0;
696 uint8 *unwrapKey = NULL;
697
698 AppleCSPContext::symmetricKeyBits(context, *this,
699 CSSM_ALGID_OPENSSH1, CSSM_KEYUSE_UNWRAP,
700 unwrapKey, unwrapKeyLen);
701 if((unwrapKey == NULL) || (unwrapKeyLen != CC_MD5_DIGEST_LENGTH)) {
702 errorLog0("AppleCSPSession::UnwrapKeyOpenSSH1: bad unwrapping key length\n");
703 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
704 }
705
706 RSA *rsa = RSA_new();
707 CSSM_RETURN ourRtn = CSSM_OK;
708 unsigned char *comment = NULL;
709 unsigned commentLen = 0;
710 RSABinaryKey *binKey = NULL;
711
712 ourRtn = decodeOpenSSHv1PrivKey((const unsigned char *)WrappedKey.KeyData.Data,
713 (unsigned)WrappedKey.KeyData.Length,
714 rsa, unwrapKey, &comment, &commentLen);
715 if(ourRtn) {
716 goto errOut;
717 }
718 if(comment) {
719 setUpCssmData(DescriptiveData, commentLen, normAllocator);
720 memcpy(DescriptiveData.Data, comment, commentLen);
721 free(comment);
722 }
723
724 /*
725 * Our caller ensured that we're only generating a reference key,
726 * which we do like so:
727 */
728 binKey = new RSABinaryKey(rsa);
729 addRefKey(*binKey, UnwrappedKey);
730
731 errOut:
732 if(ourRtn) {
733 if(rsa) {
734 RSA_free(rsa);
735 }
736 CssmError::throwMe(ourRtn);
737 }
738 }