snej@0: // snej@0: // MYKey-iPhone.m snej@0: // MYCrypto-iPhone snej@0: // snej@0: // Created by Jens Alfke on 4/4/09. snej@0: // Copyright 2009 Jens Alfke. All rights reserved. snej@0: // snej@0: snej@0: snej@0: #import "MYCrypto_Private.h" snej@0: snej@2: #if MYCRYPTO_USE_IPHONE_API snej@0: snej@0: #import "MYDigest.h" snej@0: #import "MYErrorUtils.h" snej@0: snej@0: snej@0: #pragma mark - snej@0: @implementation MYKey snej@0: snej@0: snej@0: - (id) initWithKeyRef: (SecKeyRef)key { jens@24: return [self initWithKeychainItemRef: (SecKeychainItemRef)key]; snej@0: } snej@0: snej@0: snej@0: - (id) _initWithKeyData: (NSData*)data snej@0: forKeychain: (SecKeychainRef)keychain snej@0: { jens@23: NSMutableDictionary *info = $mdict({(id)kSecClass, (id)kSecClassKey}, jens@23: {(id)kSecAttrKeyType, (id)self.keyType}, jens@23: {(id)kSecValueData, data}, jens@23: {(id)kSecAttrIsPermanent, (keychain ?$true :$false)}, jens@26: {(id)kSecReturnPersistentRef, (keychain ?$true :$false)} ); jens@26: SecKeyRef key = (SecKeyRef)[MYKeychain _addItemWithInfo: info]; jens@23: if (!key) { jens@23: [self release]; snej@0: return nil; jens@23: } jens@23: self = [self initWithKeyRef: (SecKeyRef)key]; jens@23: if (self) { jens@23: if (!keychain) jens@23: _keyData = [data copy]; jens@26: jens@26: //TEMP For debugging: jens@26: AssertEqual(self.keyData, data); jens@23: } jens@23: return self; snej@0: } snej@0: snej@0: - (id) initWithKeyData: (NSData*)data { snej@0: return [self _initWithKeyData: data forKeychain: nil]; snej@0: } snej@0: jens@23: - (void) dealloc jens@23: { jens@23: [_keyData release]; jens@23: [super dealloc]; jens@23: } snej@0: jens@23: jens@26: /*- (NSData*) persistentRef { jens@26: NSDictionary *info = $dict( {(id)kSecValueRef, (id)self.keyRef}, jens@26: //{(id)kSecAttrIsPermanent, (self.isPersistent ?$true :$false)}, jens@26: {(id)kSecReturnPersistentRef, $true} ); jens@26: CFDataRef data; jens@26: if (!check(SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&data), @"SecItemCopyMatching")) jens@26: return nil; jens@26: if (!data) jens@26: Warn(@"MYKey persistentRef couldn't get ref"); jens@26: return [NSMakeCollectable(data) autorelease]; jens@26: }*/ jens@26: jens@26: jens@23: - (SecExternalItemType) keyClass { snej@0: AssertAbstractMethod(); snej@0: } snej@0: jens@23: - (SecExternalItemType) keyType { jens@23: return NULL; jens@23: } snej@0: snej@0: - (NSData*) keyData { jens@23: if (_keyData) jens@23: return _keyData; jens@23: jens@23: NSDictionary *info = $dict( {(id)kSecValueRef, (id)self.keyRef}, jens@26: //{(id)kSecAttrIsPermanent, (self.isPersistent ?$true :$false)}, snej@0: {(id)kSecReturnData, $true} ); snej@0: CFDataRef data; jens@26: if (!check(SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&data), @"SecItemCopyMatching")) { jens@26: Log(@"SecItemCopyMatching failed; input = %@", info); snej@0: return nil; jens@26: } else { jens@23: Assert(data!=NULL); jens@24: _keyData = NSMakeCollectable(data); jens@24: return _keyData; jens@23: } jens@16: // The format of this data is not documented. There's been some reverse-engineering: jens@16: // https://devforums.apple.com/message/32089#32089 jens@16: // Apparently it is a DER-formatted sequence of a modulus followed by an exponent. jens@16: // This can be converted to OpenSSL format by wrapping it in some additional DER goop. snej@0: } snej@0: jens@23: - (MYSHA1Digest*) _keyDigest { jens@23: return [self.keyData my_SHA1Digest]; jens@23: } snej@0: jens@28: - (unsigned) keySizeInBits { jens@28: return [[self _attribute: kSecAttrKeySizeInBits] intValue]; jens@28: } jens@28: snej@1: - (SecKeyRef) keyRef { snej@1: return (SecKeyRef) self.keychainItemRef; snej@0: } snej@0: snej@0: snej@0: - (id) _attribute: (CFTypeRef)attribute { jens@23: NSDictionary *info = $dict({(id)kSecValueRef, (id)self.keyRef}, jens@26: {(id)kSecAttrIsPermanent, (self.isPersistent ?$true :$false)}, jens@23: {(id)kSecReturnAttributes, $true}); snej@2: CFDictionaryRef attrs = NULL; snej@0: if (!check(SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&attrs), @"SecItemCopyMatching")) snej@0: return nil; snej@0: CFTypeRef rawValue = CFDictionaryGetValue(attrs,attribute); snej@0: id value = rawValue ?[[(id)CFMakeCollectable(rawValue) retain] autorelease] :nil; snej@0: CFRelease(attrs); snej@0: return value; snej@0: } snej@0: snej@0: - (BOOL) setValue: (NSString*)value ofAttribute: (SecKeychainAttrType)attribute { snej@0: if (!value) snej@0: value = (id)[NSNull null]; jens@24: NSDictionary *query = $dict( {(id)kSecValueRef, (id)self.keyRef} ); snej@0: NSDictionary *attrs = $dict( {(id)attribute, value} ); snej@0: return check(SecItemUpdate((CFDictionaryRef)query, (CFDictionaryRef)attrs), @"SecItemUpdate"); snej@0: } snej@0: snej@0: snej@0: - (NSString*) name { snej@0: return [self _attribute: kSecAttrLabel]; snej@0: } snej@0: snej@0: - (void) setName: (NSString*)name { snej@0: [self setValue: name ofAttribute: kSecAttrLabel]; snej@0: } snej@0: snej@0: - (NSString*) alias { snej@0: return [self _attribute: kSecAttrApplicationTag]; snej@0: } snej@0: snej@0: - (void) setAlias: (NSString*)alias { snej@0: [self setValue: alias ofAttribute: kSecAttrApplicationTag]; snej@0: } snej@0: snej@0: snej@3: snej@3: snej@3: /** Asymmetric encryption/decryption; used by MYPublicKey and MYPrivateKey. */ snej@3: - (NSData*) _crypt: (NSData*)data operation: (BOOL)operation { snej@3: CAssert(data); snej@3: size_t dataLength = data.length; snej@3: SecKeyRef key = self.keyRef; snej@3: size_t outputLength = MAX(dataLength, SecKeyGetBlockSize(key)); snej@3: void *outputBuf = malloc(outputLength); snej@3: if (!outputBuf) return nil; snej@3: OSStatus err; snej@3: if (operation) snej@3: err = SecKeyEncrypt(key, kSecPaddingNone,//PKCS1, snej@3: data.bytes, dataLength, snej@3: outputBuf, &outputLength); snej@3: else snej@3: err = SecKeyDecrypt(key, kSecPaddingNone,//PKCS1, snej@3: data.bytes, dataLength, snej@3: outputBuf, &outputLength); snej@3: if (err) { snej@3: free(outputBuf); snej@3: Warn(@"%scrypting failed (%i)", (operation ?"En" :"De"), err); snej@3: // Note: One of the errors I've seen is -9809, which is errSSLCrypto (SecureTransport.h) snej@3: return nil; snej@3: } else snej@3: return [NSData dataWithBytesNoCopy: outputBuf length: outputLength freeWhenDone: YES]; snej@3: } snej@3: snej@3: snej@0: @end snej@0: snej@0: snej@2: #endif MYCRYPTO_USE_IPHONE_API snej@0: snej@0: snej@0: snej@0: /* snej@0: Copyright (c) 2009, Jens Alfke . All rights reserved. snej@0: snej@0: Redistribution and use in source and binary forms, with or without modification, are permitted snej@0: provided that the following conditions are met: snej@0: snej@0: * Redistributions of source code must retain the above copyright notice, this list of conditions snej@0: and the following disclaimer. snej@0: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions snej@0: and the following disclaimer in the documentation and/or other materials provided with the snej@0: distribution. snej@0: snej@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR snej@0: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND snej@0: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- snej@0: BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES snej@0: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR snej@0: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN snej@0: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF snej@0: THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. snej@0: */