* Some cleanup. Got the test cases to pass again.
* Added some missing copyright notices.
     5 //  Created by Jens Alfke on 4/7/09.
 
     6 //  Copyright 2009 Jens Alfke. All rights reserved.
 
     9 #import "MYPrivateKey.h"
 
    10 #import "MYCrypto_Private.h"
 
    12 #import <CommonCrypto/CommonDigest.h>
 
    18 @implementation MYPrivateKey
 
    21 - (id) initWithKeyRef: (SecKeyRef)privateKey
 
    23     self = [super initWithKeyRef: privateKey];
 
    25         // No public key given, so look it up:
 
    26         MYSHA1Digest *digest = self._keyDigest;
 
    28             _publicKey = [[self.keychain publicKeyWithDigest: digest] retain];
 
    30             // The matching public key won't turn up if it's embedded in a certificate;
 
    31             // I'd have to search for certs if I wanted to look that up. Skip it for now.
 
    32             Log(@"MYPrivateKey(%p): Couldn't find matching public key for private key! digest=%@",
 
    42 - (id) _initWithKeyRef: (SecKeyRef)privateKey
 
    43              publicKey: (MYPublicKey*)publicKey 
 
    46     self = [super initWithKeyRef: privateKey];
 
    48         _publicKey = [publicKey retain];
 
    53 - (id) initWithKeyRef: (SecKeyRef)privateKey
 
    54          publicKeyRef: (SecKeyRef)publicKeyRef
 
    56     MYPublicKey *publicKey = [[MYPublicKey alloc] initWithKeyRef: publicKeyRef];
 
    57     self = [self _initWithKeyRef: privateKey publicKey: publicKey];
 
    62 - (id) _initWithKeyRef: (SecKeyRef)privateKey 
 
    63          publicKeyData: (NSData*)pubKeyData
 
    64            forKeychain: (SecKeychainRef)keychain 
 
    70     MYPublicKey *pubKey = [[MYPublicKey alloc] _initWithKeyData: pubKeyData forKeychain: keychain];
 
    75     self = [super initWithKeyRef: privateKey];
 
    79         [pubKey removeFromKeychain];
 
    88 // The public API for this is in MYKeychain.
 
    89 - (id) _initWithKeyData: (NSData*)privKeyData 
 
    90           publicKeyData: (NSData*)pubKeyData
 
    91             forKeychain: (SecKeychainRef)keychain 
 
    92              alertTitle: (NSString*)title
 
    93             alertPrompt: (NSString*)prompt
 
    95     // Try to import the private key first, since the user might cancel the passphrase alert.
 
    96     SecKeyImportExportParameters params = {
 
    97         .flags = kSecKeySecurePassphrase,
 
    98         .alertTitle = (CFStringRef) title,
 
    99         .alertPrompt = (CFStringRef) prompt
 
   101     SecKeyRef privateKey = importKey(privKeyData,kSecItemTypePrivateKey,keychain,¶ms);
 
   102     return [self _initWithKeyRef: privateKey publicKeyData: pubKeyData forKeychain: keychain];
 
   105 // This method is for testing, so unit-tests don't require user intervention.
 
   106 // It's deliberately not made public, to discourage clients from trying to manage the passphrases
 
   107 // themselves (this is less secure than letting the Security agent do it.)
 
   108 - (id) _initWithKeyData: (NSData*)privKeyData 
 
   109           publicKeyData: (NSData*)pubKeyData
 
   110             forKeychain: (SecKeychainRef)keychain 
 
   111              passphrase: (NSString*)passphrase
 
   113     SecKeyImportExportParameters params = {
 
   114         .passphrase = (CFStringRef) passphrase,
 
   116     SecKeyRef privateKey = importKey(privKeyData,kSecItemTypePrivateKey,keychain,¶ms);
 
   117     return [self _initWithKeyRef: privateKey publicKeyData: pubKeyData forKeychain: keychain];
 
   121 - (MYIdentity*) createSelfSignedIdentityWithAttributes: (NSDictionary*)attributes {
 
   122     return MYIdentityCreateSelfSigned(self, attributes);
 
   131     [_publicKey release];
 
   136 + (MYPrivateKey*) _generateRSAKeyPairOfSize: (unsigned)keySize
 
   137                                  inKeychain: (MYKeychain*)keychain 
 
   139     Assert( keySize == 512 || keySize == 1024 || keySize == 2048, @"Unsupported key size %u", keySize );
 
   140     SecKeyRef pubKey=NULL, privKey=NULL;
 
   143 #if MYCRYPTO_USE_IPHONE_API
 
   144     NSDictionary *pubKeyAttrs = $dict({(id)kSecAttrIsPermanent, $true});
 
   145     NSDictionary *privKeyAttrs = $dict({(id)kSecAttrIsPermanent, $true});
 
   146     NSDictionary *keyAttrs = $dict( {(id)kSecAttrKeyType, (id)kSecAttrKeyTypeRSA},
 
   147                                     {(id)kSecAttrKeySizeInBits, $object(keySize)},
 
   148                                     {(id)kSecPublicKeyAttrs, pubKeyAttrs},
 
   149                                     {(id)kSecPrivateKeyAttrs, privKeyAttrs} );
 
   150     err = SecKeyGeneratePair((CFDictionaryRef)keyAttrs,&pubKey,&privKey);
 
   152     err = SecKeyCreatePair(keychain.keychainRefOrDefault,
 
   156                            CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_WRAP,        // public key
 
   157                            CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT,
 
   158                            CSSM_KEYUSE_ANY,                                 // private key
 
   159                            CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE,
 
   160                            NULL,                                            // SecAccessRef
 
   163     if (!check(err, @"SecKeyCreatePair")) {
 
   166         return [[[self alloc] initWithKeyRef: privKey publicKeyRef: pubKey] autorelease];
 
   171 #pragma mark ACCESSORS:
 
   174 - (NSString*) description {
 
   175     return $sprintf(@"%@[%@ %@ /%p]", [self class], 
 
   176                     self.publicKeyDigest.abbreviatedHexString,
 
   178                     self.keychainItemRef);
 
   181 @synthesize publicKey=_publicKey;
 
   183 - (MYSHA1Digest*) publicKeyDigest {
 
   184     return _publicKey.publicKeyDigest;
 
   187 - (SecExternalItemType) keyType {
 
   188 #if MYCRYPTO_USE_IPHONE_API
 
   189     return kSecAttrKeyClassPublic;
 
   191     return kSecItemTypePrivateKey;
 
   195 - (NSData *) keyData {
 
   196     [NSException raise: NSGenericException format: @"Can't access keyData of a PrivateKey"];
 
   200 - (BOOL) setValue: (NSString*)valueStr ofAttribute: (SecKeychainAttrType)attr {
 
   201     return [super setValue: valueStr ofAttribute: attr]
 
   202         && [_publicKey setValue: valueStr ofAttribute: attr];
 
   207 #pragma mark OPERATIONS:
 
   210 - (BOOL) removeFromKeychain {
 
   211     return [super removeFromKeychain]
 
   212         && [_publicKey removeFromKeychain];
 
   216 - (NSData*) rawDecryptData: (NSData*)data {
 
   217     return [self _crypt: data operation: NO];
 
   221 - (NSData*) signData: (NSData*)data {
 
   223 #if MYCRYPTO_USE_IPHONE_API
 
   224     uint8_t digest[CC_SHA1_DIGEST_LENGTH];
 
   225     CC_SHA1(data.bytes,data.length, digest);
 
   227     size_t sigLen = 1024;
 
   228     uint8_t sigBuf[sigLen];
 
   229     OSStatus err = SecKeyRawSign(self.keyRef, kSecPaddingPKCS1SHA1,
 
   230                                  digest,sizeof(digest), //data.bytes, data.length,
 
   233         Warn(@"SecKeyRawSign failed: %i",err);
 
   236         return [NSData dataWithBytes: sigBuf length: sigLen];
 
   238     NSData *signature = nil;
 
   239     CSSM_CC_HANDLE ccHandle = [self _createSignatureContext: CSSM_ALGID_SHA256WithRSA];
 
   240     if (!ccHandle) return nil;
 
   241     CSSM_DATA original = {data.length, (void*)data.bytes};
 
   242     CSSM_DATA result = {0,NULL};
 
   243     if (checkcssm(CSSM_SignData(ccHandle, &original, 1, CSSM_ALGID_NONE, &result), @"CSSM_SignData"))
 
   244         signature = [NSData dataWithBytesNoCopy: result.Data length: result.Length
 
   246     CSSM_DeleteContext(ccHandle);
 
   252 #if !TARGET_OS_IPHONE
 
   254 - (NSData*) exportKeyInFormat: (SecExternalFormat)format 
 
   255                       withPEM: (BOOL)withPEM
 
   256                    alertTitle: (NSString*)title
 
   257                   alertPrompt: (NSString*)prompt
 
   259     SecKeyImportExportParameters params = {
 
   260         .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
 
   261         .flags = kSecKeySecurePassphrase,
 
   262         .alertTitle = (CFStringRef)title,
 
   263         .alertPrompt = (CFStringRef)prompt
 
   265     CFDataRef data = NULL;
 
   266     if (check(SecKeychainItemExport(self.keyRef,
 
   267                                     format, (withPEM ?kSecItemPemArmour :0), 
 
   269               @"SecKeychainItemExport"))
 
   270         return [(id)CFMakeCollectable(data) autorelease];
 
   275 - (NSData*) exportKey {
 
   276     return [self exportKeyInFormat: kSecFormatWrappedOpenSSL withPEM: YES
 
   277                         alertTitle: @"Export Private Key"
 
   278                        alertPrompt: @"Enter a passphrase to protect the private-key file.\n"
 
   279             "You will need to re-enter the passphrase later when importing the key from this file, "
 
   280             "so keep it in a safe place."];
 
   281     //FIX: Should make these messages localizable.
 
   285 - (NSData*) _exportKeyInFormat: (SecExternalFormat)format
 
   286                        withPEM: (BOOL)withPEM
 
   287                     passphrase: (NSString*)passphrase
 
   289     SecKeyImportExportParameters params = {
 
   290         .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
 
   291         .passphrase = (CFStringRef)passphrase
 
   293     CFDataRef data = NULL;
 
   294     if (check(SecKeychainItemExport(self.keyRef,
 
   295                                     format, (withPEM ?kSecItemPemArmour :0), 
 
   297               @"SecKeychainItemExport"))
 
   298         return [(id)CFMakeCollectable(data) autorelease];
 
   304 - (MYSymmetricKey*) unwrapSessionKey: (NSData*)wrappedData
 
   305                        withAlgorithm: (CCAlgorithm)algorithm
 
   306                           sizeInBits: (unsigned)sizeInBits
 
   308     // First create a wrapped-key structure from the data:
 
   309     CSSM_WRAP_KEY wrappedKey = {
 
   311             .BlobType = CSSM_KEYBLOB_WRAPPED,
 
   312             .Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS3,
 
   313             .AlgorithmId = CSSMFromCCAlgorithm(algorithm),
 
   314             .KeyClass = CSSM_KEYCLASS_SESSION_KEY,
 
   315             .LogicalKeySizeInBits = sizeInBits,
 
   316             .KeyAttr = CSSM_KEYATTR_EXTRACTABLE,
 
   317             .KeyUsage = CSSM_KEYUSE_ANY,
 
   318             .WrapAlgorithmId = self.cssmAlgorithm,
 
   321             .Data = (void*)wrappedData.bytes,
 
   322             .Length = wrappedData.length
 
   326     const CSSM_ACCESS_CREDENTIALS* credentials;
 
   327     credentials = [self cssmCredentialsForOperation: CSSM_ACL_AUTHORIZATION_IMPORT_WRAPPED
 
   328                                                type: kSecCredentialTypeDefault error: nil];
 
   329     CSSM_CSP_HANDLE cspHandle = self.cssmCSPHandle;
 
   331     if (!checkcssm(CSSM_CSP_CreateAsymmetricContext(cspHandle,
 
   337                    @"CSSM_CSP_CreateAsymmetricContext"))
 
   340     // Now unwrap the key:
 
   341     MYSymmetricKey *result = nil;
 
   342     CSSM_KEY *unwrappedKey = calloc(1,sizeof(CSSM_KEY));
 
   343     CSSM_DATA label = {.Data=(void*)"Imported key", .Length=strlen("Imported key")};
 
   344     CSSM_DATA descriptiveData = {};
 
   345     if (checkcssm(CSSM_UnwrapKey(ctx, 
 
   348                                  wrappedKey.KeyHeader.KeyUsage,
 
   349                                  wrappedKey.KeyHeader.KeyAttr,
 
   354                   @"CSSM_UnwrapKey")) {
 
   355         result = [[[MYSymmetricKey alloc] _initWithCSSMKey: unwrappedKey] autorelease];
 
   357     // Finally, delete the context
 
   360     CSSM_DeleteContext(ctx);
 
   365 #endif !TARGET_OS_IPHONE
 
   372  Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
 
   374  Redistribution and use in source and binary forms, with or without modification, are permitted
 
   375  provided that the following conditions are met:
 
   377  * Redistributions of source code must retain the above copyright notice, this list of conditions
 
   378  and the following disclaimer.
 
   379  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
 
   380  and the following disclaimer in the documentation and/or other materials provided with the
 
   383  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 
   384  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
 
   385  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
 
   386  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 
   387  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 
   388   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 
   389  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
 
   390  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.