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