2  * Copyright (c) 2006,2011-2012,2014 Apple Inc. All Rights Reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   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 
  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. 
  21  * @APPLE_LICENSE_HEADER_END@ 
  25  * opensshCoding.h - Encoding and decoding of OpenSSH format public keys. 
  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> 
  42 static const char *authfile_id_string 
= "SSH PRIVATE KEY FILE FORMAT 1.1\n"; 
  44 /* default comment on encode if app doesn't provide DescriptiveData */ 
  45 #define OPENSSH1_COMMENT                "Encoded by Mac OS X Security.framework" 
  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 
  57 #pragma mark --- utilities --- 
  59 static void appendUint16( 
  60         CFMutableDataRef cfOut
, 
  63         UInt8 buf
[sizeof(uint16_t)]; 
  68         CFDataAppendBytes(cfOut
, buf
, sizeof(uint16_t)); 
  71 static uint16_t readUint16( 
  72         const unsigned char *&cp
,               // IN/OUT 
  73         unsigned &len
)                                  // IN/OUT  
  82 /* Write BIGNUM, OpenSSH-1 version */ 
  83 static CSSM_RETURN 
appendBigNum( 
  84         CFMutableDataRef cfOut
,  
  87         /* 16 bits of numbits */ 
  88         unsigned numBits 
= BN_num_bits(bn
); 
  89         appendUint16(cfOut
, numBits
); 
  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
; 
  99         CFDataAppendBytes(cfOut
, (UInt8 
*)outBytes
, numBytes
); 
 103 /* Read BIGNUM, OpenSSH-1 version */ 
 104 static BIGNUM 
*readBigNum( 
 105         const unsigned char *&cp
,       // IN/OUT 
 106         unsigned &remLen
)                       // IN/OUT 
 108         if(remLen 
< sizeof(uint16_t)) { 
 109                 errorLog0("readBigNum: short record(1)\n"); 
 112         uint16_t numBits 
= readUint16(cp
, remLen
); 
 113         unsigned bytes 
= (numBits 
+ 7) / 8; 
 115                 errorLog0("readBigNum: short record(2)\n"); 
 118         BIGNUM 
*bn 
= BN_bin2bn(cp
, bytes
, NULL
); 
 120                 errorLog0("readBigNum: BN_bin2bn error\n"); 
 129  * Calculate d mod{p-1,q-1} 
 130  * Used when decoding OpenSSH-1 private RSA key. 
 132 static CSSM_RETURN 
rsa_generate_additional_parameters(RSA 
*rsa
) 
 137         if((rsa
->dmq1 
= BN_new()) == NULL
) { 
 138                 errorLog0("rsa_generate_additional_parameters: BN_new failed"); 
 139                 return CSSMERR_CSP_INTERNAL_ERROR
; 
 141         if((rsa
->dmp1 
= BN_new()) == NULL
) { 
 142                 errorLog0("rsa_generate_additional_parameters: BN_new failed"); 
 143                 return CSSMERR_CSP_INTERNAL_ERROR
; 
 145         if ((aux 
= BN_new()) == NULL
) { 
 146                 errorLog0("rsa_generate_additional_parameters: BN_new failed"); 
 147                 return CSSMERR_CSP_INTERNAL_ERROR
; 
 149         if ((ctx 
= BN_CTX_new()) == NULL
) { 
 150                 errorLog0("rsa_generate_additional_parameters: BN_CTX_new failed"); 
 152                 return CSSMERR_CSP_INTERNAL_ERROR
; 
 155         BN_sub(aux
, rsa
->q
, BN_value_one()); 
 156         BN_mod(rsa
->dmq1
, rsa
->d
, aux
, ctx
); 
 158         BN_sub(aux
, rsa
->p
, BN_value_one()); 
 159         BN_mod(rsa
->dmp1
, rsa
->d
, aux
, ctx
); 
 166 #pragma mark --- encrypt/decrypt --- 
 169  * Encrypt/decrypt the secret portion of an OpenSSHv1 format RSA private key. 
 171 static CSSM_RETURN 
ssh1DES3Crypt( 
 172         unsigned char cipher
, 
 174         const unsigned char *inText
, 
 176         const uint8 
*key
,                       // MD5(password) 
 178         unsigned char *outText
,         // data RETURNED here, caller mallocs. 
 179         unsigned *outTextLen
)           // RETURNED 
 182                 case SSH_CIPHER_3DES
: 
 184                 case SSH_CIPHER_NONE
: 
 185                         /* cleartext RSA private key, e.g. host key. */ 
 186                         memmove(outText
, inText
, inTextLen
); 
 187                         *outTextLen 
= inTextLen
; 
 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
; 
 195         if(keyLen 
!= CC_MD5_DIGEST_LENGTH
) { 
 196                 errorLog0("ssh1DES3Crypt: bad key length\n"); 
 197                 return CSSMERR_CSP_INVALID_KEY
; 
 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
); 
 219         /* the openssh v1 pseudo triple DES. Each DES pass has its own CBC. */ 
 222         CCCryptorStatus cstat 
= CCCrypt(op1_3
, kCCAlgorithmDES
,  
 227                 outText
, inTextLen
, &moved
); 
 229                 /* should never happen */ 
 230                 errorLog1("***ssh1DES3Crypt: CCCrypt()(1) returned %u\n", (unsigned)cstat
); 
 231                 return CSSMERR_CSP_INTERNAL_ERROR
; 
 233         cstat 
= CCCrypt(op2
, kCCAlgorithmDES
,  
 234                 0,                                              // no padding - SSH does that itself  
 238                 outText
, inTextLen
, &moved
); 
 240                 errorLog1("***ssh1DES3Crypt: CCCrypt()(2) returned %u\n", (unsigned)cstat
); 
 241                 return CSSMERR_CSP_INTERNAL_ERROR
; 
 243         cstat 
= CCCrypt(op1_3
, kCCAlgorithmDES
,  
 244                 0,                                              // no padding - SSH does that itself  
 248                 outText
, inTextLen
, &moved
); 
 250                 errorLog1("***ssh1DES3Crypt: CCCrypt()(3) returned %u\n", (unsigned)cstat
); 
 251                 return CSSMERR_CSP_INTERNAL_ERROR
; 
 254         *outTextLen 
= (unsigned)moved
; 
 258 #pragma mark --- DeriveKey --- 
 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. 
 267 void AppleCSPSession::DeriveKey_OpenSSH1( 
 268         const Context 
&context
, 
 269         CSSM_ALGORITHMS algId
, 
 270         const CssmData 
&Param
,                  // IV optional, mallocd by app to indicate 
 272         CSSM_DATA 
*keyData
)                             // mallocd by caller to indicate size - must be 
 273                                                                         // size of MD5 digest! 
 275         CSSM_DATA pwd 
= {0, NULL
}; 
 277         if(keyData
->Length 
!= CC_MD5_DIGEST_LENGTH
) { 
 278                 errorLog0("DeriveKey_OpenSSH1: invalid key length\n"); 
 279                 CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_SIZE
); 
 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
; 
 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
); 
 298         if(pwd
.Data 
== NULL
) { 
 299                 errorLog0("DeriveKey_PKCS5_V1_5: null Passphrase\n"); 
 300                 CssmError::throwMe(CSSMERR_CSP_INVALID_DATA
); 
 302         if(pwd
.Length 
== 0) {            
 303                 errorLog0("DeriveKey_PKCS5_V1_5: zero length passphrase\n"); 
 304                 CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER
); 
 308         CC_MD5(pwd
.Data
, (CC_LONG
)pwd
.Length
, keyData
->Data
); 
 312 #pragma mark --- Encode/Wrap OpenSSHv1 private key --- 
 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. 
 319 CSSM_RETURN 
encodeOpenSSHv1PrivKey( 
 321         const uint8 
*comment
,           /* optional */ 
 323         const uint8 
*encryptKey
,        /* optional; if present, it's 16 bytes of MD5(password) */ 
 324         CFDataRef 
*encodedKey
)          /* RETURNED */ 
 326         CFMutableDataRef cfOut 
= CFDataCreateMutable(NULL
, 0); 
 327         CSSM_RETURN ourRtn 
= CSSM_OK
; 
 329         /* ID string including NULL */ 
 330         CFDataAppendBytes(cfOut
, (const UInt8 
*)authfile_id_string
, strlen(authfile_id_string
) + 1); 
 332         /* one byte cipher */ 
 333         UInt8 cipherSpec 
= encryptKey 
? SSH_CIPHER_3DES 
: SSH_CIPHER_NONE
; 
 334         CFDataAppendBytes(cfOut
, &cipherSpec
, 1); 
 337         UInt8 spares
[4] = {0}; 
 338         CFDataAppendBytes(cfOut
, spares
, 4); 
 341          * Clear text public key: 
 346         uint32_t keybits 
= RSA_size(rsa
) * 8; 
 347         appendUint32(cfOut
, keybits
); 
 348         appendBigNum(cfOut
, rsa
->n
); 
 349         appendBigNum(cfOut
, rsa
->e
); 
 353          * The format appears to require this, or else we wouldn't know 
 354          * when we've got to the ciphertext on decode. 
 356         if((comment 
== NULL
) || (commentLen 
== 0)) { 
 357                 comment 
= (const UInt8 
*)OPENSSH1_COMMENT
; 
 358                 commentLen 
= strlen(OPENSSH1_COMMENT
); 
 360         appendUint32(cfOut
, commentLen
); 
 361         CFDataAppendBytes(cfOut
, comment
, commentLen
); 
 364          * Remainder is encrypted, consisting of 
 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) 
 374         CFMutableDataRef ptext 
= CFDataCreateMutable(NULL
, 0); 
 376         /* [0..3] check bytes */ 
 378         DevRandomGenerator rng 
= DevRandomGenerator(); 
 379         rng
.random(checkBytes
, 2); 
 380         checkBytes
[2] = checkBytes
[0]; 
 381         checkBytes
[3] = checkBytes
[1]; 
 382         CFDataAppendBytes(ptext
, checkBytes
, 4); 
 385         appendBigNum(ptext
, rsa
->d
); 
 386         appendBigNum(ptext
, rsa
->iqmp
); 
 387         appendBigNum(ptext
, rsa
->q
); 
 388         appendBigNum(ptext
, rsa
->p
); 
 390         /* pad to block boundary */ 
 391         CFIndex ptextLen 
= CFDataGetLength(ptext
); 
 392         unsigned padding 
= 0; 
 393         unsigned rem 
= (unsigned)ptextLen 
& 0x7; 
 398         for(unsigned dex
=0; dex
<padding
; dex
++) { 
 399                 CFDataAppendBytes(ptext
, &padByte
, 1); 
 403         ptextLen 
= CFDataGetLength(ptext
); 
 404         unsigned char ctext
[ptextLen
]; 
 406         ourRtn 
= ssh1DES3Crypt(cipherSpec
, true,  
 407                 (unsigned char *)CFDataGetBytePtr(ptext
), (unsigned)ptextLen
, 
 408                 encryptKey
, encryptKey 
? CC_MD5_DIGEST_LENGTH 
: 0, 
 414         /* appended encrypted portion */ 
 415         CFDataAppendBytes(cfOut
, ctext
, ctextLen
); 
 418         /* it would be proper to zero out ptext here, but we can't do that to a CFData */ 
 423 void AppleCSPSession::WrapKeyOpenSSH1( 
 424         CSSM_CC_HANDLE CCHandle
, 
 425         const Context 
&context
, 
 426         const AccessCredentials 
&AccessCred
, 
 427         BinaryKey 
&unwrappedBinKey
, 
 429         bool allocdRawBlob
,                     // callee has to free rawBlob 
 430         const CssmData 
*DescriptiveData
, 
 432         CSSM_PRIVILEGE Privilege
) 
 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). 
 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. 
 442         RSABinaryKey 
&rPubBinKey 
= dynamic_cast<RSABinaryKey 
&>(unwrappedBinKey
); 
 443         RSA 
*rsa 
= rPubBinKey
.mRsaKey
; 
 444         CASSERT(rsa 
!= NULL
); 
 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.  
 452         CSSM_SIZE       wrappingKeyLen 
= 0; 
 453         uint8           
*wrappingKey 
= NULL
; 
 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
); 
 463         CFDataRef cfOut 
= NULL
; 
 466          * Optional comment string from DescriptiveData. 
 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
; 
 475         /* generate the encrypted blob */ 
 476         CSSM_RETURN crtn 
= encodeOpenSSHv1PrivKey(rsa
, comment
, commentLen
, wrappingKey
, &cfOut
); 
 478                 CssmError::throwMe(crtn
); 
 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
); 
 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
; 
 494 #pragma mark --- Decode/Unwrap OpenSSHv1 private key --- 
 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. 
 501 CSSM_RETURN 
decodeOpenSSHv1PrivKey( 
 502         const unsigned char *encodedKey
, 
 503         unsigned encodedKeyLen
, 
 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 */ 
 509         unsigned len 
= (unsigned)strlen(authfile_id_string
); 
 510         const unsigned char *cp 
= encodedKey
; 
 511         unsigned remLen 
= encodedKeyLen
; 
 512         CSSM_RETURN ourRtn 
= CSSM_OK
; 
 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
; 
 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
; 
 529         unsigned char cipherSpec 
= *cp
; 
 531                 case SSH_CIPHER_NONE
: 
 532                         if(decryptKey 
!= NULL
) { 
 533                                 errorLog0("decodeOpenSSHv1PrivKey: Attempt to decrypt plaintext key\n"); 
 534                                 return CSSMERR_CSP_INVALID_KEY
; 
 537                 case SSH_CIPHER_3DES
: 
 538                         if(decryptKey 
== NULL
) { 
 539                                 errorLog0("decodeOpenSSHv1PrivKey: Encrypted key with no decryptKey\n"); 
 540                                 return CSSMERR_CSP_INVALID_KEY
; 
 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
; 
 549         /* skip cipher, spares */ 
 554          * Clear text public key: 
 559         if(remLen 
< sizeof(uint32_t)) { 
 560                 errorLog0("decodeOpenSSHv1PrivKey: bad len(1)\n"); 
 561                 return CSSMERR_CSP_INVALID_KEY
; 
 564         readUint32(cp
, remLen
); 
 565         rsa
->n 
= readBigNum(cp
, remLen
); 
 567                 errorLog0("decodeOpenSSHv1PrivKey: error decoding n\n"); 
 568                 return CSSMERR_CSP_INVALID_KEY
; 
 570         rsa
->e 
= readBigNum(cp
, remLen
); 
 572                 errorLog0("decodeOpenSSHv1PrivKey: error decoding e\n"); 
 573                 return CSSMERR_CSP_INVALID_KEY
; 
 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
; 
 581         uint32_t commLen 
= readUint32(cp
, remLen
); 
 582         if(commLen 
> remLen
) { 
 583                 errorLog0("decodeOpenSSHv1PrivKey: bad len(3)\n"); 
 584                 return CSSMERR_CSP_INVALID_KEY
; 
 587                 *comment 
= (uint8 
*)malloc(commLen
); 
 588                 *commentLen 
= commLen
; 
 589                 memcpy(*comment
, cp
, commLen
); 
 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,  
 602                 errorLog0("UnwrapKeyOpenSSH1: decrypt error\n"); 
 603                 ourRtn 
= CSSMERR_CSP_INVALID_KEY
; 
 607         /* plaintext contents: 
 609         [0-1]           -- random bytes 
 610         [2-3]           -- copy of [01] for passphrase validity checking 
 612         buffer_put_bignum(iqmp) 
 620                 errorLog0("UnwrapKeyOpenSSH1: bad len(4)\n"); 
 621                 ourRtn 
= CSSMERR_CSP_INVALID_KEY
; 
 624         if((cp
[0] != cp
[2]) || (cp
[1] != cp
[3])) { 
 626                 errorLog0("UnwrapKeyOpenSSH1: check byte error\n"); 
 627                 ourRtn 
= CSSMERR_CSP_INVALID_KEY
; 
 633         /* remainder comprises private portion of RSA key */ 
 634         rsa
->d 
= readBigNum(cp
, remLen
); 
 636                 errorLog0("UnwrapKeyOpenSSH1: error decoding d\n"); 
 637                 ourRtn 
= CSSMERR_CSP_INVALID_KEY
; 
 640         rsa
->iqmp 
= readBigNum(cp
, remLen
); 
 641         if(rsa
->iqmp 
== NULL
) { 
 642                 errorLog0("UnwrapKeyOpenSSH1: error decoding iqmp\n"); 
 643                 ourRtn 
= CSSMERR_CSP_INVALID_KEY
; 
 646         rsa
->q 
= readBigNum(cp
, remLen
); 
 648                 errorLog0("UnwrapKeyOpenSSH1: error decoding q\n"); 
 649                 ourRtn 
= CSSMERR_CSP_INVALID_KEY
; 
 652         rsa
->p 
= readBigNum(cp
, remLen
); 
 654                 errorLog0("UnwrapKeyOpenSSH1: error decoding p\n"); 
 655                 ourRtn 
= CSSMERR_CSP_INVALID_KEY
; 
 659         /* calculate d mod{p-1,q-1} */ 
 660         ourRtn 
= rsa_generate_additional_parameters(rsa
); 
 664                 memset(ptext
, 0, ptextLen
); 
 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
) 
 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. 
 685         CSSM_SIZE               unwrapKeyLen 
= 0; 
 686         uint8                   
*unwrapKey 
= NULL
; 
 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
); 
 696         RSA 
*rsa 
= RSA_new(); 
 697         CSSM_RETURN ourRtn 
= CSSM_OK
; 
 698         unsigned char *comment 
= NULL
; 
 699         unsigned commentLen 
= 0; 
 700         RSABinaryKey 
*binKey 
= NULL
; 
 702         ourRtn 
= decodeOpenSSHv1PrivKey((const unsigned char *)WrappedKey
.KeyData
.Data
, 
 703                 (unsigned)WrappedKey
.KeyData
.Length
, 
 704                 rsa
, unwrapKey
, &comment
, &commentLen
); 
 709                 setUpCssmData(DescriptiveData
, commentLen
, normAllocator
); 
 710                 memcpy(DescriptiveData
.Data
, comment
, commentLen
); 
 714          * Our caller ensured that we're only generating a reference key, 
 715          * which we do like so: 
 717         binKey 
= new RSABinaryKey(rsa
); 
 718         addRefKey(*binKey
, UnwrappedKey
); 
 725                 CssmError::throwMe(ourRtn
);