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 42static 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 59static 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 71static 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 */ 83static 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 */ 104static 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 */ 132static 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 */ 171static 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 */ 267void 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 */ 319CSSM_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; 417errOut: 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 423void 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 */ 501CSSM_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 662errOut: 663 if(ptext) { 664 memset(ptext, 0, ptextLen); 665 free(ptext); 666 } 667 return ourRtn; 668} 669 670void 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 720errOut: 721 if(ourRtn) { 722 if(rsa) { 723 RSA_free(rsa); 724 } 725 CssmError::throwMe(ourRtn); 726 } 727} 728