123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546 |
- /*
- * NSData+CommonCrypto.m
- * AQToolkit
- *
- * Created by Jim Dovey on 31/8/2008.
- *
- * Copyright (c) 2008-2009, Jim Dovey
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of this project's author nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
- #import <Foundation/Foundation.h>
- #import "NSData+CommonCrypto.h"
- #import <CommonCrypto/CommonDigest.h>
- #import <CommonCrypto/CommonCryptor.h>
- #import <CommonCrypto/CommonHMAC.h>
- NSString * const kCommonCryptoErrorDomain = @"CommonCryptoErrorDomain";
- @implementation NSError (CommonCryptoErrorDomain)
- + (NSError *) errorWithCCCryptorStatus: (CCCryptorStatus) status
- {
- NSString * description = nil, * reason = nil;
-
- switch ( status )
- {
- case kCCSuccess:
- description = NSLocalizedString(@"Success", @"Error description");
- break;
-
- case kCCParamError:
- description = NSLocalizedString(@"Parameter Error", @"Error description");
- reason = NSLocalizedString(@"Illegal parameter supplied to encryption/decryption algorithm", @"Error reason");
- break;
-
- case kCCBufferTooSmall:
- description = NSLocalizedString(@"Buffer Too Small", @"Error description");
- reason = NSLocalizedString(@"Insufficient buffer provided for specified operation", @"Error reason");
- break;
-
- case kCCMemoryFailure:
- description = NSLocalizedString(@"Memory Failure", @"Error description");
- reason = NSLocalizedString(@"Failed to allocate memory", @"Error reason");
- break;
-
- case kCCAlignmentError:
- description = NSLocalizedString(@"Alignment Error", @"Error description");
- reason = NSLocalizedString(@"Input size to encryption algorithm was not aligned correctly", @"Error reason");
- break;
-
- case kCCDecodeError:
- description = NSLocalizedString(@"Decode Error", @"Error description");
- reason = NSLocalizedString(@"Input data did not decode or decrypt correctly", @"Error reason");
- break;
-
- case kCCUnimplemented:
- description = NSLocalizedString(@"Unimplemented Function", @"Error description");
- reason = NSLocalizedString(@"Function not implemented for the current algorithm", @"Error reason");
- break;
-
- default:
- description = NSLocalizedString(@"Unknown Error", @"Error description");
- break;
- }
-
- NSMutableDictionary * userInfo = [[NSMutableDictionary alloc] init];
- [userInfo setObject: description forKey: NSLocalizedDescriptionKey];
-
- if ( reason != nil )
- [userInfo setObject: reason forKey: NSLocalizedFailureReasonErrorKey];
-
- NSError * result = [NSError errorWithDomain: kCommonCryptoErrorDomain code: status userInfo: userInfo];
- #if !__has_feature(objc_arc)
- [userInfo release];
- #endif
-
- return ( result );
- }
- @end
- #pragma mark -
- @implementation NSData (CommonDigest)
- - (NSData *) MD2Sum
- {
- unsigned char hash[CC_MD2_DIGEST_LENGTH];
- (void) CC_MD2( [self bytes], (CC_LONG)[self length], hash );
- return ( [NSData dataWithBytes: hash length: CC_MD2_DIGEST_LENGTH] );
- }
- - (NSData *) MD4Sum
- {
- unsigned char hash[CC_MD4_DIGEST_LENGTH];
- (void) CC_MD4( [self bytes], (CC_LONG)[self length], hash );
- return ( [NSData dataWithBytes: hash length: CC_MD4_DIGEST_LENGTH] );
- }
- - (NSData *) MD5Sum
- {
- unsigned char hash[CC_MD5_DIGEST_LENGTH];
- (void) CC_MD5( [self bytes], (CC_LONG)[self length], hash );
- return ( [NSData dataWithBytes: hash length: CC_MD5_DIGEST_LENGTH] );
- }
- - (NSData *) SHA1Hash
- {
- unsigned char hash[CC_SHA1_DIGEST_LENGTH];
- (void) CC_SHA1( [self bytes], (CC_LONG)[self length], hash );
- return ( [NSData dataWithBytes: hash length: CC_SHA1_DIGEST_LENGTH] );
- }
- - (NSData *) SHA224Hash
- {
- unsigned char hash[CC_SHA224_DIGEST_LENGTH];
- (void) CC_SHA224( [self bytes], (CC_LONG)[self length], hash );
- return ( [NSData dataWithBytes: hash length: CC_SHA224_DIGEST_LENGTH] );
- }
- - (NSData *) SHA256Hash
- {
- unsigned char hash[CC_SHA256_DIGEST_LENGTH];
- (void) CC_SHA256( [self bytes], (CC_LONG)[self length], hash );
- return ( [NSData dataWithBytes: hash length: CC_SHA256_DIGEST_LENGTH] );
- }
- - (NSData *) SHA384Hash
- {
- unsigned char hash[CC_SHA384_DIGEST_LENGTH];
- (void) CC_SHA384( [self bytes], (CC_LONG)[self length], hash );
- return ( [NSData dataWithBytes: hash length: CC_SHA384_DIGEST_LENGTH] );
- }
- - (NSData *) SHA512Hash
- {
- unsigned char hash[CC_SHA512_DIGEST_LENGTH];
- (void) CC_SHA512( [self bytes], (CC_LONG)[self length], hash );
- return ( [NSData dataWithBytes: hash length: CC_SHA512_DIGEST_LENGTH] );
- }
- @end
- @implementation NSData (CommonCryptor)
- - (NSData *) AES256EncryptedDataUsingKey: (id) key error: (NSError **) error
- {
- CCCryptorStatus status = kCCSuccess;
- NSData * result = [self dataEncryptedUsingAlgorithm: kCCAlgorithmAES128
- key: key
- options: kCCOptionPKCS7Padding
- error: &status];
-
- if ( result != nil )
- return ( result );
-
- if ( error != NULL )
- *error = [NSError errorWithCCCryptorStatus: status];
-
- return ( nil );
- }
- - (NSData *) decryptedAES256DataUsingKey: (id) key error: (NSError **) error
- {
- CCCryptorStatus status = kCCSuccess;
- NSData * result = [self decryptedDataUsingAlgorithm: kCCAlgorithmAES128
- key: key
- options: kCCOptionPKCS7Padding
- error: &status];
-
- if ( result != nil )
- return ( result );
-
- if ( error != NULL )
- *error = [NSError errorWithCCCryptorStatus: status];
-
- return ( nil );
- }
- - (NSData *) DESEncryptedDataUsingKey: (id) key error: (NSError **) error
- {
- CCCryptorStatus status = kCCSuccess;
- NSData * result = [self dataEncryptedUsingAlgorithm: kCCAlgorithmDES
- key: key
- options: kCCOptionPKCS7Padding
- error: &status];
-
- if ( result != nil )
- return ( result );
-
- if ( error != NULL )
- *error = [NSError errorWithCCCryptorStatus: status];
-
- return ( nil );
- }
- - (NSData *) decryptedDESDataUsingKey: (id) key error: (NSError **) error
- {
- CCCryptorStatus status = kCCSuccess;
- NSData * result = [self decryptedDataUsingAlgorithm: kCCAlgorithmDES
- key: key
- options: kCCOptionPKCS7Padding
- error: &status];
-
- if ( result != nil )
- return ( result );
-
- if ( error != NULL )
- *error = [NSError errorWithCCCryptorStatus: status];
-
- return ( nil );
- }
- - (NSData *) CASTEncryptedDataUsingKey: (id) key error: (NSError **) error
- {
- CCCryptorStatus status = kCCSuccess;
- NSData * result = [self dataEncryptedUsingAlgorithm: kCCAlgorithmCAST
- key: key
- options: kCCOptionPKCS7Padding
- error: &status];
-
- if ( result != nil )
- return ( result );
-
- if ( error != NULL )
- *error = [NSError errorWithCCCryptorStatus: status];
-
- return ( nil );
- }
- - (NSData *) decryptedCASTDataUsingKey: (id) key error: (NSError **) error
- {
- CCCryptorStatus status = kCCSuccess;
- NSData * result = [self decryptedDataUsingAlgorithm: kCCAlgorithmCAST
- key: key
- options: kCCOptionPKCS7Padding
- error: &status];
-
- if ( result != nil )
- return ( result );
-
- if ( error != NULL )
- *error = [NSError errorWithCCCryptorStatus: status];
-
- return ( nil );
- }
- @end
- static void FixKeyLengths( CCAlgorithm algorithm, NSMutableData * keyData, NSMutableData * ivData )
- {
- NSUInteger keyLength = [keyData length];
- switch ( algorithm )
- {
- case kCCAlgorithmAES128:
- {
- if ( keyLength < 16 )
- {
- [keyData setLength: 16];
- }
- else if ( keyLength < 24 )
- {
- [keyData setLength: 24];
- }
- else
- {
- [keyData setLength: 32];
- }
-
- break;
- }
-
- case kCCAlgorithmDES:
- {
- [keyData setLength: 8];
- break;
- }
-
- case kCCAlgorithm3DES:
- {
- [keyData setLength: 24];
- break;
- }
-
- case kCCAlgorithmCAST:
- {
- if ( keyLength < 5 )
- {
- [keyData setLength: 5];
- }
- else if ( keyLength > 16 )
- {
- [keyData setLength: 16];
- }
-
- break;
- }
-
- case kCCAlgorithmRC4:
- {
- if ( keyLength > 512 )
- [keyData setLength: 512];
- break;
- }
-
- default:
- break;
- }
-
- [ivData setLength: [keyData length]];
- }
- @implementation NSData (LowLevelCommonCryptor)
- - (NSData *) _runCryptor: (CCCryptorRef) cryptor result: (CCCryptorStatus *) status
- {
- size_t bufsize = CCCryptorGetOutputLength( cryptor, (size_t)[self length], true );
- void * buf = malloc( bufsize );
- size_t bufused = 0;
- size_t bytesTotal = 0;
- *status = CCCryptorUpdate( cryptor, [self bytes], (size_t)[self length],
- buf, bufsize, &bufused );
- if ( *status != kCCSuccess )
- {
- free( buf );
- return ( nil );
- }
-
- bytesTotal += bufused;
-
- // From Brent Royal-Gordon (Twitter: architechies):
- // Need to update buf ptr past used bytes when calling CCCryptorFinal()
- *status = CCCryptorFinal( cryptor, buf + bufused, bufsize - bufused, &bufused );
- if ( *status != kCCSuccess )
- {
- free( buf );
- return ( nil );
- }
-
- bytesTotal += bufused;
-
- return ( [NSData dataWithBytesNoCopy: buf length: bytesTotal] );
- }
- - (NSData *) dataEncryptedUsingAlgorithm: (CCAlgorithm) algorithm
- key: (id) key
- error: (CCCryptorStatus *) error
- {
- return ( [self dataEncryptedUsingAlgorithm: algorithm
- key: key
- initializationVector: nil
- options: 0
- error: error] );
- }
- - (NSData *) dataEncryptedUsingAlgorithm: (CCAlgorithm) algorithm
- key: (id) key
- options: (CCOptions) options
- error: (CCCryptorStatus *) error
- {
- return ( [self dataEncryptedUsingAlgorithm: algorithm
- key: key
- initializationVector: nil
- options: options
- error: error] );
- }
- - (NSData *) dataEncryptedUsingAlgorithm: (CCAlgorithm) algorithm
- key: (id) key
- initializationVector: (id) iv
- options: (CCOptions) options
- error: (CCCryptorStatus *) error
- {
- CCCryptorRef cryptor = NULL;
- CCCryptorStatus status = kCCSuccess;
-
- NSParameterAssert([key isKindOfClass: [NSData class]] || [key isKindOfClass: [NSString class]]);
- NSParameterAssert(iv == nil || [iv isKindOfClass: [NSData class]] || [iv isKindOfClass: [NSString class]]);
-
- NSMutableData * keyData, * ivData;
- if ( [key isKindOfClass: [NSData class]] )
- keyData = (NSMutableData *) [key mutableCopy];
- else
- keyData = [[key dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
-
- if ( [iv isKindOfClass: [NSString class]] )
- ivData = [[iv dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
- else
- ivData = (NSMutableData *) [iv mutableCopy]; // data or nil
-
- #if !__has_feature(objc_arc)
- [keyData autorelease];
- [ivData autorelease];
- #endif
- // ensure correct lengths for key and iv data, based on algorithms
- FixKeyLengths( algorithm, keyData, ivData );
-
- status = CCCryptorCreate( kCCEncrypt, algorithm, options,
- [keyData bytes], [keyData length], [ivData bytes],
- &cryptor );
-
- if ( status != kCCSuccess )
- {
- if ( error != NULL )
- *error = status;
- return ( nil );
- }
-
- NSData * result = [self _runCryptor: cryptor result: &status];
- if ( (result == nil) && (error != NULL) )
- *error = status;
-
- CCCryptorRelease( cryptor );
-
- return ( result );
- }
- - (NSData *) decryptedDataUsingAlgorithm: (CCAlgorithm) algorithm
- key: (id) key // data or string
- error: (CCCryptorStatus *) error
- {
- return ( [self decryptedDataUsingAlgorithm: algorithm
- key: key
- initializationVector: nil
- options: 0
- error: error] );
- }
- - (NSData *) decryptedDataUsingAlgorithm: (CCAlgorithm) algorithm
- key: (id) key // data or string
- options: (CCOptions) options
- error: (CCCryptorStatus *) error
- {
- return ( [self decryptedDataUsingAlgorithm: algorithm
- key: key
- initializationVector: nil
- options: options
- error: error] );
- }
- - (NSData *) decryptedDataUsingAlgorithm: (CCAlgorithm) algorithm
- key: (id) key // data or string
- initializationVector: (id) iv // data or string
- options: (CCOptions) options
- error: (CCCryptorStatus *) error
- {
- CCCryptorRef cryptor = NULL;
- CCCryptorStatus status = kCCSuccess;
-
- NSParameterAssert([key isKindOfClass: [NSData class]] || [key isKindOfClass: [NSString class]]);
- NSParameterAssert(iv == nil || [iv isKindOfClass: [NSData class]] || [iv isKindOfClass: [NSString class]]);
-
- NSMutableData * keyData, * ivData;
- if ( [key isKindOfClass: [NSData class]] )
- keyData = (NSMutableData *) [key mutableCopy];
- else
- keyData = [[key dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
-
- if ( [iv isKindOfClass: [NSString class]] )
- ivData = [[iv dataUsingEncoding: NSUTF8StringEncoding] mutableCopy];
- else
- ivData = (NSMutableData *) [iv mutableCopy]; // data or nil
-
- #if !__has_feature(objc_arc)
- [keyData autorelease];
- [ivData autorelease];
- #endif
-
- // ensure correct lengths for key and iv data, based on algorithms
- FixKeyLengths( algorithm, keyData, ivData );
-
- status = CCCryptorCreate( kCCDecrypt, algorithm, options,
- [keyData bytes], [keyData length], [ivData bytes],
- &cryptor );
-
- if ( status != kCCSuccess )
- {
- if ( error != NULL )
- *error = status;
- return ( nil );
- }
-
- NSData * result = [self _runCryptor: cryptor result: &status];
- if ( (result == nil) && (error != NULL) )
- *error = status;
-
- CCCryptorRelease( cryptor );
-
- return ( result );
- }
- @end
- @implementation NSData (CommonHMAC)
- - (NSData *) HMACWithAlgorithm: (CCHmacAlgorithm) algorithm
- {
- return ( [self HMACWithAlgorithm: algorithm key: nil] );
- }
- - (NSData *) HMACWithAlgorithm: (CCHmacAlgorithm) algorithm key: (id) key
- {
- NSParameterAssert(key == nil || [key isKindOfClass: [NSData class]] || [key isKindOfClass: [NSString class]]);
-
- NSData * keyData = nil;
- if ( [key isKindOfClass: [NSString class]] )
- keyData = [key dataUsingEncoding: NSUTF8StringEncoding];
- else
- keyData = (NSData *) key;
-
- // this could be either CC_SHA1_DIGEST_LENGTH or CC_MD5_DIGEST_LENGTH. SHA1 is larger.
- unsigned char buf[CC_SHA1_DIGEST_LENGTH];
- CCHmac( algorithm, [keyData bytes], [keyData length], [self bytes], [self length], buf );
-
- return ( [NSData dataWithBytes: buf length: (algorithm == kCCHmacAlgMD5 ? CC_MD5_DIGEST_LENGTH : CC_SHA1_DIGEST_LENGTH)] );
- }
- @end
|