Operations/QCCAESPadCryptor.m
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
Implements AES encryption and decryption with PKCS#7 padding. |
*/ |
#import "QCCAESPadCryptor.h" |
#include <CommonCrypto/CommonCrypto.h> |
NS_ASSUME_NONNULL_BEGIN |
@interface QCCAESPadCryptor () |
- (instancetype)initWithOp:(CCOperation)op inputData:(NSData *)inputData keyData:(NSData *)keyData NS_DESIGNATED_INITIALIZER; |
@property (atomic, assign, readonly ) CCOperation op; |
// read/write versions of public properties |
@property (atomic, copy, readwrite, nullable) NSError * error; |
@property (atomic, copy, readwrite, nullable) NSData * outputData; |
@end |
NS_ASSUME_NONNULL_END |
@implementation QCCAESPadCryptor |
- (instancetype)init { |
abort(); |
} |
- (instancetype)initWithOp:(CCOperation)op inputData:(NSData *)inputData keyData:(NSData *)keyData { |
NSParameterAssert(inputData != nil); |
NSParameterAssert(keyData != nil); |
self = [super init]; |
if (self != nil) { |
self->_op = op; |
self->_inputData = [inputData copy]; |
self->_keyData = [keyData copy]; |
self->_ivData = [[NSMutableData alloc] initWithLength:kCCBlockSizeAES128]; |
} |
return self; |
} |
- (instancetype)initToEncryptInputData:(NSData *)inputData keyData:(NSData *)keyData { |
return [self initWithOp:kCCEncrypt inputData:inputData keyData:keyData]; |
} |
- (instancetype)initToDecryptInputData:(NSData *)inputData keyData:(NSData *)keyData { |
return [self initWithOp:kCCDecrypt inputData:inputData keyData:keyData]; |
} |
- (void)main { |
CCCryptorStatus err; |
NSUInteger keyDataLength; |
NSMutableData * result; |
size_t resultLength; |
// We check for common input problems to make it easier for someone tracing through |
// the code to find problems (rather than just getting a mysterious kCCParamError back |
// from CCCrypt). |
err = kCCSuccess; |
if ( (self.op == kCCDecrypt) && ((self.inputData.length % kCCBlockSizeAES128) != 0) ) { |
err = kCCParamError; |
} |
keyDataLength = self.keyData.length; |
if ( (keyDataLength != kCCKeySizeAES128) && (keyDataLength != kCCKeySizeAES192) && (keyDataLength != kCCKeySizeAES256) ) { |
err = kCCParamError; |
} |
if ( (self.ivData != nil) && (self.ivData.length != kCCBlockSizeAES128) ) { |
err = kCCParamError; |
} |
if (err == kCCSuccess) { |
NSUInteger padLength; |
// Padding can expand the data, so we have to allocate space for that. The rule for block |
// cyphers, like AES, is that the padding only adds space on encryption (on decryption it |
// can reduce space, obviously, but we don't need to account for that) and it will only add |
// at most one block size worth of space. |
if (self.op == kCCEncrypt) { |
padLength = kCCBlockSizeAES128; |
} else { |
padLength = 0; |
} |
result = [[NSMutableData alloc] initWithLength:self.inputData.length + padLength]; |
err = CCCrypt( |
self.op, |
kCCAlgorithmAES128, |
((self.ivData == nil) ? kCCOptionECBMode : 0) | kCCOptionPKCS7Padding, |
self.keyData.bytes, self.keyData.length, |
self.ivData.bytes, // will be NULL if ivData is nil |
self.inputData.bytes, self.inputData.length, |
result.mutableBytes, result.length, |
&resultLength |
); |
} |
if (err == kCCSuccess) { |
// Set the output length to the value returned by CCCrypt. This is necessary because |
// we have padding enabled, meaning that we might have allocated more space than we needed |
// (in the encrypt case, this is the space we allocated above for padding; in the decrypt |
// case, the output is actually shorter than the input because the padding is removed). |
result.length = resultLength; |
self.outputData = result; |
} else { |
self.error = [NSError errorWithDomain:QCCAESPadCryptorErrorDomain code:err userInfo:nil]; |
} |
} |
@end |
NSString * QCCAESPadCryptorErrorDomain = @"QCCAESPadCryptorErrorDomain"; |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-11-17