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