MYKey-iPhone.m
author Jens Alfke <jens@mooseyard.com>
Tue Jun 09 23:58:03 2009 -0700 (2009-06-09)
changeset 24 6856e071d25a
parent 23 39fec79de6e8
child 26 d9c2a06d4e4e
permissions -rw-r--r--
* More work on iPhone compatibility.
* Restored the signature-verification code to MYCertInfo, which I'd removed earlier. I now need it to verify self-signed certs, since the Security framework won't do it for me.
* Merged MYCertificate-iPhone.m into MYCertificate.m since there's more shared code now.
snej@0
     1
//
snej@0
     2
//  MYKey-iPhone.m
snej@0
     3
//  MYCrypto-iPhone
snej@0
     4
//
snej@0
     5
//  Created by Jens Alfke on 4/4/09.
snej@0
     6
//  Copyright 2009 Jens Alfke. All rights reserved.
snej@0
     7
//
snej@0
     8
snej@0
     9
snej@0
    10
#import "MYCrypto_Private.h"
snej@0
    11
snej@2
    12
#if MYCRYPTO_USE_IPHONE_API
snej@0
    13
snej@0
    14
#import "MYDigest.h"
snej@0
    15
#import "MYErrorUtils.h"
snej@0
    16
snej@0
    17
snej@0
    18
#pragma mark -
snej@0
    19
@implementation MYKey
snej@0
    20
snej@0
    21
jens@23
    22
+ (SecKeyRef) _addKeyWithInfo: (NSMutableDictionary*)info {
jens@23
    23
    if (![info objectForKey: (id)kSecAttrApplicationTag]) {
jens@23
    24
        // Every keychain item has to have a unique tag, apparently, or you'll get spurious
jens@23
    25
        // duplicate-item errors. If none was given, make up a random one:
jens@23
    26
        UInt8 tag[16];
jens@23
    27
        Assert(check(SecRandomCopyBytes(kSecRandomDefault, sizeof(tag), tag), @"SecRandomCopyBytes"));
jens@23
    28
        [info setObject: [NSData dataWithBytes: tag length: sizeof(tag)] 
jens@23
    29
                 forKey: (id)kSecAttrApplicationTag];
jens@23
    30
    }
jens@23
    31
    CFDataRef keyPersistentRef;
jens@23
    32
    SecKeyRef key;
jens@23
    33
    OSStatus err = SecItemAdd((CFDictionaryRef)info, (CFTypeRef*)&keyPersistentRef);
jens@23
    34
    if (err==errSecDuplicateItem) {
jens@23
    35
        // it's already in the keychain -- get a reference to it:
jens@23
    36
		[info removeObjectForKey: (id)kSecReturnPersistentRef];
jens@23
    37
		[info setObject: $true forKey: (id)kSecReturnRef];
jens@23
    38
		if (check(SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef *)&key), 
jens@23
    39
                   @"SecItemCopyMatching"))
jens@23
    40
            return key;
jens@23
    41
    } else if (check(err, @"SecItemAdd")) {
jens@23
    42
        // It was added
jens@23
    43
        if ([[info objectForKey: (id)kSecReturnPersistentRef] boolValue]) {
jens@23
    44
            // now get its SecKeyRef:
jens@23
    45
            info = $mdict({(id)kSecValuePersistentRef, (id)keyPersistentRef},
jens@23
    46
                          {(id)kSecReturnRef, $true});
jens@23
    47
            err = SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef *)&key);
jens@23
    48
            CFRelease(keyPersistentRef);
jens@23
    49
            if (check(err,@"SecItemCopyMatching")) {
jens@23
    50
                Assert(key!=nil);
jens@23
    51
                return key;
jens@23
    52
            }
jens@23
    53
        } else {
jens@23
    54
            return (SecKeyRef)keyPersistentRef;
jens@23
    55
        }
jens@23
    56
    }
jens@23
    57
    return NULL;
jens@23
    58
}
jens@23
    59
jens@23
    60
snej@0
    61
- (id) initWithKeyRef: (SecKeyRef)key {
jens@24
    62
    return [self initWithKeychainItemRef: (SecKeychainItemRef)key];
snej@0
    63
}
snej@0
    64
snej@0
    65
snej@0
    66
- (id) _initWithKeyData: (NSData*)data
snej@0
    67
            forKeychain: (SecKeychainRef)keychain
snej@0
    68
{
jens@23
    69
    NSMutableDictionary *info = $mdict({(id)kSecClass, (id)kSecClassKey},
jens@23
    70
                                        {(id)kSecAttrKeyType, (id)self.keyType},
jens@23
    71
                                        {(id)kSecValueData, data},
jens@23
    72
                                        {(id)kSecAttrIsPermanent, (keychain ?$true :$false)},
jens@23
    73
                                        {(id)kSecReturnPersistentRef, $true} );
jens@23
    74
    SecKeyRef key = [[self class] _addKeyWithInfo: info];
jens@23
    75
    if (!key) {
jens@23
    76
        [self release];
snej@0
    77
        return nil;
jens@23
    78
    }
jens@23
    79
    self = [self initWithKeyRef: (SecKeyRef)key];
jens@23
    80
    if (self) {
jens@23
    81
        if (!keychain)
jens@23
    82
            _keyData = [data copy];
jens@23
    83
    }
jens@23
    84
    return self;
snej@0
    85
}
snej@0
    86
snej@0
    87
- (id) initWithKeyData: (NSData*)data {
snej@0
    88
    return [self _initWithKeyData: data forKeychain: nil];
snej@0
    89
}
snej@0
    90
jens@23
    91
- (void) dealloc
jens@23
    92
{
jens@23
    93
    [_keyData release];
jens@23
    94
    [super dealloc];
jens@23
    95
}
snej@0
    96
jens@23
    97
jens@23
    98
- (SecExternalItemType) keyClass {
snej@0
    99
    AssertAbstractMethod();
snej@0
   100
}
snej@0
   101
jens@23
   102
- (SecExternalItemType) keyType {
jens@23
   103
    return NULL;
jens@23
   104
}
snej@0
   105
snej@0
   106
- (NSData*) keyData {
jens@23
   107
    if (_keyData)
jens@23
   108
        return _keyData;
jens@23
   109
    
jens@23
   110
    NSDictionary *info = $dict( {(id)kSecValueRef, (id)self.keyRef},
snej@0
   111
                                {(id)kSecReturnData, $true} );
snej@0
   112
    CFDataRef data;
snej@0
   113
    if (!check(SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&data), @"SecItemCopyMatching"))
snej@0
   114
        return nil;
jens@23
   115
    else {
jens@23
   116
        Assert(data!=NULL);
jens@24
   117
        _keyData = NSMakeCollectable(data);
jens@24
   118
        return _keyData;
jens@23
   119
    }
jens@16
   120
    // The format of this data is not documented. There's been some reverse-engineering:
jens@16
   121
    // https://devforums.apple.com/message/32089#32089
jens@16
   122
    // Apparently it is a DER-formatted sequence of a modulus followed by an exponent.
jens@16
   123
    // This can be converted to OpenSSL format by wrapping it in some additional DER goop.
snej@0
   124
}
snej@0
   125
jens@23
   126
- (MYSHA1Digest*) _keyDigest {
jens@23
   127
    return [self.keyData my_SHA1Digest];
jens@23
   128
}
snej@0
   129
snej@1
   130
- (SecKeyRef) keyRef {
snej@1
   131
    return (SecKeyRef) self.keychainItemRef;
snej@0
   132
}
snej@0
   133
snej@0
   134
snej@0
   135
- (id) _attribute: (CFTypeRef)attribute {
jens@23
   136
    NSDictionary *info = $dict({(id)kSecValueRef, (id)self.keyRef},
jens@23
   137
                               {(id)kSecReturnAttributes, $true});
snej@2
   138
    CFDictionaryRef attrs = NULL;
snej@0
   139
    if (!check(SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&attrs), @"SecItemCopyMatching"))
snej@0
   140
        return nil;
jens@23
   141
    Log(@"_attribute: %@ of %@ %p", attribute, [self class], self.keyRef);//TEMP
snej@0
   142
    CFTypeRef rawValue = CFDictionaryGetValue(attrs,attribute);
snej@0
   143
    id value = rawValue ?[[(id)CFMakeCollectable(rawValue) retain] autorelease] :nil;
snej@0
   144
    CFRelease(attrs);
snej@0
   145
    return value;
snej@0
   146
}
snej@0
   147
snej@0
   148
- (BOOL) setValue: (NSString*)value ofAttribute: (SecKeychainAttrType)attribute {
snej@0
   149
    if (!value)
snej@0
   150
        value = (id)[NSNull null];
jens@24
   151
    NSDictionary *query = $dict( {(id)kSecValueRef, (id)self.keyRef} );
snej@0
   152
    NSDictionary *attrs = $dict( {(id)attribute, value} );
snej@0
   153
    return check(SecItemUpdate((CFDictionaryRef)query, (CFDictionaryRef)attrs), @"SecItemUpdate");
snej@0
   154
}
snej@0
   155
snej@0
   156
snej@0
   157
- (NSString*) name {
snej@0
   158
    return [self _attribute: kSecAttrLabel];
snej@0
   159
}
snej@0
   160
snej@0
   161
- (void) setName: (NSString*)name {
snej@0
   162
    [self setValue: name ofAttribute: kSecAttrLabel];
snej@0
   163
}
snej@0
   164
snej@0
   165
- (NSString*) alias {
snej@0
   166
    return [self _attribute: kSecAttrApplicationTag];
snej@0
   167
}
snej@0
   168
snej@0
   169
- (void) setAlias: (NSString*)alias {
snej@0
   170
    [self setValue: alias ofAttribute: kSecAttrApplicationTag];
snej@0
   171
}
snej@0
   172
snej@0
   173
snej@3
   174
snej@3
   175
snej@3
   176
/** Asymmetric encryption/decryption; used by MYPublicKey and MYPrivateKey. */
snej@3
   177
- (NSData*) _crypt: (NSData*)data operation: (BOOL)operation {
snej@3
   178
    CAssert(data);
snej@3
   179
    size_t dataLength = data.length;
snej@3
   180
    SecKeyRef key = self.keyRef;
snej@3
   181
    size_t outputLength = MAX(dataLength, SecKeyGetBlockSize(key));
snej@3
   182
    void *outputBuf = malloc(outputLength);
snej@3
   183
    if (!outputBuf) return nil;
snej@3
   184
    OSStatus err;
snej@3
   185
    if (operation)
snej@3
   186
        err = SecKeyEncrypt(key, kSecPaddingNone,//PKCS1, 
snej@3
   187
                            data.bytes, dataLength,
snej@3
   188
                            outputBuf, &outputLength);
snej@3
   189
    else
snej@3
   190
        err = SecKeyDecrypt(key, kSecPaddingNone,//PKCS1, 
snej@3
   191
                            data.bytes, dataLength,
snej@3
   192
                            outputBuf, &outputLength);
snej@3
   193
    if (err) {
snej@3
   194
        free(outputBuf);
snej@3
   195
        Warn(@"%scrypting failed (%i)", (operation ?"En" :"De"), err);
snej@3
   196
        // Note: One of the errors I've seen is -9809, which is errSSLCrypto (SecureTransport.h)
snej@3
   197
        return nil;
snej@3
   198
    } else
snej@3
   199
        return [NSData dataWithBytesNoCopy: outputBuf length: outputLength freeWhenDone: YES];
snej@3
   200
}
snej@3
   201
snej@3
   202
snej@0
   203
@end
snej@0
   204
snej@0
   205
snej@2
   206
#endif MYCRYPTO_USE_IPHONE_API
snej@0
   207
snej@0
   208
snej@0
   209
snej@0
   210
/*
snej@0
   211
 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
snej@0
   212
 
snej@0
   213
 Redistribution and use in source and binary forms, with or without modification, are permitted
snej@0
   214
 provided that the following conditions are met:
snej@0
   215
 
snej@0
   216
 * Redistributions of source code must retain the above copyright notice, this list of conditions
snej@0
   217
 and the following disclaimer.
snej@0
   218
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
snej@0
   219
 and the following disclaimer in the documentation and/or other materials provided with the
snej@0
   220
 distribution.
snej@0
   221
 
snej@0
   222
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
snej@0
   223
 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
snej@0
   224
 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
snej@0
   225
 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
snej@0
   226
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
snej@0
   227
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
snej@0
   228
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
snej@0
   229
 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
snej@0
   230
 */