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