Allow HMAC verify with nil cipher in CryptoCBC
This commit is contained in:
parent
401d999b3d
commit
d53e7add10
|
@ -107,6 +107,7 @@ const NSInteger CryptoAEADTagLength = 16;
|
|||
- (void)configureEncryptionWithCipherKey:(ZeroingData *)cipherKey hmacKey:(ZeroingData *)hmacKey
|
||||
{
|
||||
NSParameterAssert(cipherKey.count >= self.cipherKeyLength);
|
||||
NSParameterAssert(hmacKey);
|
||||
|
||||
EVP_CIPHER_CTX_reset(self.cipherCtxEnc);
|
||||
EVP_CipherInit(self.cipherCtxEnc, self.cipher, cipherKey.bytes, NULL, 1);
|
||||
|
@ -170,7 +171,8 @@ const NSInteger CryptoAEADTagLength = 16;
|
|||
- (void)configureDecryptionWithCipherKey:(ZeroingData *)cipherKey hmacKey:(ZeroingData *)hmacKey
|
||||
{
|
||||
NSParameterAssert(cipherKey.count >= self.cipherKeyLength);
|
||||
|
||||
NSParameterAssert(hmacKey);
|
||||
|
||||
EVP_CIPHER_CTX_reset(self.cipherCtxDec);
|
||||
EVP_CipherInit(self.cipherCtxDec, self.cipher, cipherKey.bytes, NULL, 0);
|
||||
|
||||
|
@ -223,6 +225,18 @@ const NSInteger CryptoAEADTagLength = 16;
|
|||
TUNNEL_CRYPTO_RETURN_STATUS(code)
|
||||
}
|
||||
|
||||
- (BOOL)verifyData:(NSData *)data offset:(NSInteger)offset extra:(const uint8_t *)extra error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
NSAssert(NO, @"Verification not supported");
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)verifyBytes:(const uint8_t *)bytes length:(NSInteger)length extra:(const uint8_t *)extra error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
NSAssert(NO, @"Verification not supported");
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (id<DataPathDecrypter>)dataPathDecrypter
|
||||
{
|
||||
return [[DataPathCryptoAEAD alloc] initWithCrypto:self];
|
||||
|
|
|
@ -45,13 +45,13 @@
|
|||
|
||||
+ (BOOL)preparePRNGWithSeed:(nonnull const uint8_t *)seed length:(NSInteger)length;
|
||||
|
||||
- (nonnull instancetype)initWithCipherAlgorithm:(nonnull NSString *)cipherAlgorithm
|
||||
- (nonnull instancetype)initWithCipherAlgorithm:(nullable NSString *)cipherAlgorithm
|
||||
digestAlgorithm:(nullable NSString *)digestAlgorithm;
|
||||
|
||||
- (BOOL)configureWithCipherEncKey:(nonnull ZeroingData *)cipherEncKey
|
||||
cipherDecKey:(nonnull ZeroingData *)cipherDecKey
|
||||
hmacEncKey:(nonnull ZeroingData *)hmacEncKey
|
||||
hmacDecKey:(nonnull ZeroingData *)hmacDecKey
|
||||
- (BOOL)configureWithCipherEncKey:(nullable ZeroingData *)cipherEncKey
|
||||
cipherDecKey:(nullable ZeroingData *)cipherDecKey
|
||||
hmacEncKey:(nullable ZeroingData *)hmacEncKey
|
||||
hmacDecKey:(nullable ZeroingData *)hmacDecKey
|
||||
error:(NSError **)error;
|
||||
|
||||
// WARNING: hmac must be able to hold HMAC result
|
||||
|
|
|
@ -76,8 +76,7 @@
|
|||
|
||||
- (instancetype)initWithCipherAlgorithm:(NSString *)cipherAlgorithm digestAlgorithm:(NSString *)digestAlgorithm
|
||||
{
|
||||
NSParameterAssert(cipherAlgorithm);
|
||||
// NSParameterAssert(digestAlgorithm);
|
||||
NSParameterAssert(cipherAlgorithm || digestAlgorithm);
|
||||
|
||||
if ((self = [super init])) {
|
||||
self.cipherAlgorithm = [cipherAlgorithm lowercaseString];
|
||||
|
@ -99,35 +98,38 @@
|
|||
hmacDecKey:(ZeroingData *)hmacDecKey
|
||||
error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
NSParameterAssert(cipherEncKey);
|
||||
NSParameterAssert(cipherDecKey);
|
||||
NSParameterAssert(hmacEncKey);
|
||||
NSParameterAssert(hmacDecKey);
|
||||
NSParameterAssert((cipherEncKey && cipherDecKey) || (hmacEncKey && hmacDecKey));
|
||||
|
||||
if ([self.cipherAlgorithm hasSuffix:@"-cbc"]) {
|
||||
if (!self.digestAlgorithm) {
|
||||
if (self.cipherAlgorithm) {
|
||||
if ([self.cipherAlgorithm hasSuffix:@"-cbc"]) {
|
||||
if (!self.digestAlgorithm) {
|
||||
if (error) {
|
||||
*error = TunnelKitErrorWithCode(TunnelKitErrorCodeCryptoBoxAlgorithm);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
CryptoCBC *cbc = [[CryptoCBC alloc] initWithCipherName:self.cipherAlgorithm digestName:self.digestAlgorithm];
|
||||
self.encrypter = cbc;
|
||||
self.decrypter = cbc;
|
||||
}
|
||||
else if ([self.cipherAlgorithm hasSuffix:@"-gcm"]) {
|
||||
CryptoAEAD *gcm = [[CryptoAEAD alloc] initWithCipherName:self.cipherAlgorithm];
|
||||
self.encrypter = gcm;
|
||||
self.decrypter = gcm;
|
||||
}
|
||||
// not supported
|
||||
else {
|
||||
if (error) {
|
||||
*error = TunnelKitErrorWithCode(TunnelKitErrorCodeCryptoBoxAlgorithm);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
CryptoCBC *cbc = [[CryptoCBC alloc] initWithCipherName:self.cipherAlgorithm
|
||||
digestName:self.digestAlgorithm];
|
||||
}
|
||||
else {
|
||||
CryptoCBC *cbc = [[CryptoCBC alloc] initWithCipherName:nil digestName:self.digestAlgorithm];
|
||||
self.encrypter = cbc;
|
||||
self.decrypter = cbc;
|
||||
}
|
||||
else if ([self.cipherAlgorithm hasSuffix:@"-gcm"]) {
|
||||
CryptoAEAD *gcm = [[CryptoAEAD alloc] initWithCipherName:self.cipherAlgorithm];
|
||||
self.encrypter = gcm;
|
||||
self.decrypter = gcm;
|
||||
}
|
||||
// not supported
|
||||
else {
|
||||
if (error) {
|
||||
*error = TunnelKitErrorWithCode(TunnelKitErrorCodeCryptoBoxAlgorithm);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
[self.encrypter configureEncryptionWithCipherKey:cipherEncKey hmacKey:hmacEncKey];
|
||||
[self.decrypter configureDecryptionWithCipherKey:cipherDecKey hmacKey:hmacDecKey];
|
||||
|
|
|
@ -43,8 +43,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@interface CryptoCBC : NSObject <Encrypter, Decrypter>
|
||||
|
||||
- (instancetype)initWithCipherName:(nonnull NSString *)cipherName
|
||||
digestName:(nonnull NSString *)digestName;
|
||||
- (instancetype)initWithCipherName:(nullable NSString *)cipherName digestName:(nonnull NSString *)digestName;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -67,22 +67,29 @@ const NSInteger CryptoCBCMaxHMACLength = 100;
|
|||
|
||||
- (instancetype)initWithCipherName:(NSString *)cipherName digestName:(NSString *)digestName
|
||||
{
|
||||
NSParameterAssert([[cipherName uppercaseString] hasSuffix:@"CBC"]);
|
||||
|
||||
NSParameterAssert(!cipherName || [[cipherName uppercaseString] hasSuffix:@"CBC"]);
|
||||
NSParameterAssert(digestName);
|
||||
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.cipher = EVP_get_cipherbyname([cipherName cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||
NSAssert(self.cipher, @"Unknown cipher '%@'", cipherName);
|
||||
if (cipherName) {
|
||||
self.cipher = EVP_get_cipherbyname([cipherName cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||
NSAssert(self.cipher, @"Unknown cipher '%@'", cipherName);
|
||||
}
|
||||
self.digest = EVP_get_digestbyname([digestName cStringUsingEncoding:NSASCIIStringEncoding]);
|
||||
NSAssert(self.digest, @"Unknown digest '%@'", digestName);
|
||||
|
||||
self.cipherKeyLength = EVP_CIPHER_key_length(self.cipher);
|
||||
self.cipherIVLength = EVP_CIPHER_iv_length(self.cipher);
|
||||
if (cipherName) {
|
||||
self.cipherKeyLength = EVP_CIPHER_key_length(self.cipher);
|
||||
self.cipherIVLength = EVP_CIPHER_iv_length(self.cipher);
|
||||
}
|
||||
self.digestLength = EVP_MD_size(self.digest);
|
||||
self.overheadLength = self.cipherIVLength + self.digestLength;
|
||||
|
||||
self.cipherCtxEnc = EVP_CIPHER_CTX_new();
|
||||
self.cipherCtxDec = EVP_CIPHER_CTX_new();
|
||||
if (cipherName) {
|
||||
self.cipherCtxEnc = EVP_CIPHER_CTX_new();
|
||||
self.cipherCtxDec = EVP_CIPHER_CTX_new();
|
||||
}
|
||||
self.hmacCtxEnc = HMAC_CTX_new();
|
||||
self.hmacCtxDec = HMAC_CTX_new();
|
||||
self.bufferDecHMAC = allocate_safely(CryptoCBCMaxHMACLength);
|
||||
|
@ -92,8 +99,10 @@ const NSInteger CryptoCBCMaxHMACLength = 100;
|
|||
|
||||
- (void)dealloc
|
||||
{
|
||||
EVP_CIPHER_CTX_free(self.cipherCtxEnc);
|
||||
EVP_CIPHER_CTX_free(self.cipherCtxDec);
|
||||
if (self.cipher) {
|
||||
EVP_CIPHER_CTX_free(self.cipherCtxEnc);
|
||||
EVP_CIPHER_CTX_free(self.cipherCtxDec);
|
||||
}
|
||||
HMAC_CTX_free(self.hmacCtxEnc);
|
||||
HMAC_CTX_free(self.hmacCtxDec);
|
||||
bzero(self.bufferDecHMAC, CryptoCBCMaxHMACLength);
|
||||
|
@ -112,10 +121,14 @@ const NSInteger CryptoCBCMaxHMACLength = 100;
|
|||
|
||||
- (void)configureEncryptionWithCipherKey:(ZeroingData *)cipherKey hmacKey:(ZeroingData *)hmacKey
|
||||
{
|
||||
NSParameterAssert(cipherKey.count >= self.cipherKeyLength);
|
||||
NSParameterAssert(hmacKey);
|
||||
|
||||
EVP_CIPHER_CTX_reset(self.cipherCtxEnc);
|
||||
EVP_CipherInit(self.cipherCtxEnc, self.cipher, cipherKey.bytes, NULL, 1);
|
||||
if (self.cipher) {
|
||||
NSParameterAssert(cipherKey.count >= self.cipherKeyLength);
|
||||
|
||||
EVP_CIPHER_CTX_reset(self.cipherCtxEnc);
|
||||
EVP_CipherInit(self.cipherCtxEnc, self.cipher, cipherKey.bytes, NULL, 1);
|
||||
}
|
||||
|
||||
HMAC_CTX_reset(self.hmacCtxEnc);
|
||||
HMAC_Init_ex(self.hmacCtxEnc, hmacKey.bytes, self.digestLength, self.digest, NULL);
|
||||
|
@ -145,17 +158,25 @@ const NSInteger CryptoCBCMaxHMACLength = 100;
|
|||
int l1 = 0, l2 = 0;
|
||||
unsigned int l3 = 0;
|
||||
int code = 1;
|
||||
|
||||
if (RAND_bytes(outIV, self.cipherIVLength) != 1) {
|
||||
if (error) {
|
||||
*error = TunnelKitErrorWithCode(TunnelKitErrorCodeCryptoBoxRandomGenerator);
|
||||
|
||||
if (self.cipher) {
|
||||
if (RAND_bytes(outIV, self.cipherIVLength) != 1) {
|
||||
if (error) {
|
||||
*error = TunnelKitErrorWithCode(TunnelKitErrorCodeCryptoBoxRandomGenerator);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return NO;
|
||||
|
||||
TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherInit(self.cipherCtxEnc, NULL, NULL, outIV, -1);
|
||||
TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherUpdate(self.cipherCtxEnc, outEncrypted, &l1, bytes, (int)length);
|
||||
TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherFinal(self.cipherCtxEnc, outEncrypted + l1, &l2);
|
||||
}
|
||||
else {
|
||||
NSAssert(outEncrypted == outIV, @"cipherIVLength is non-zero");
|
||||
|
||||
memcpy(outEncrypted, bytes, length);
|
||||
l1 = (int)length;
|
||||
}
|
||||
|
||||
TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherInit(self.cipherCtxEnc, NULL, NULL, outIV, -1);
|
||||
TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherUpdate(self.cipherCtxEnc, outEncrypted, &l1, bytes, (int)length);
|
||||
TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherFinal(self.cipherCtxEnc, outEncrypted + l1, &l2);
|
||||
|
||||
TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Init_ex(self.hmacCtxEnc, NULL, 0, NULL, NULL);
|
||||
TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Update(self.hmacCtxEnc, outIV, l1 + l2 + self.cipherIVLength);
|
||||
|
@ -175,10 +196,14 @@ const NSInteger CryptoCBCMaxHMACLength = 100;
|
|||
|
||||
- (void)configureDecryptionWithCipherKey:(ZeroingData *)cipherKey hmacKey:(ZeroingData *)hmacKey
|
||||
{
|
||||
NSParameterAssert(cipherKey.count >= self.cipherKeyLength);
|
||||
NSParameterAssert(hmacKey);
|
||||
|
||||
EVP_CIPHER_CTX_reset(self.cipherCtxDec);
|
||||
EVP_CipherInit(self.cipherCtxDec, self.cipher, cipherKey.bytes, NULL, 0);
|
||||
if (self.cipher) {
|
||||
NSParameterAssert(cipherKey.count >= self.cipherKeyLength);
|
||||
|
||||
EVP_CIPHER_CTX_reset(self.cipherCtxDec);
|
||||
EVP_CipherInit(self.cipherCtxDec, self.cipher, cipherKey.bytes, NULL, 0);
|
||||
}
|
||||
|
||||
HMAC_CTX_reset(self.hmacCtxDec);
|
||||
HMAC_Init_ex(self.hmacCtxDec, hmacKey.bytes, self.digestLength, self.digest, NULL);
|
||||
|
@ -186,6 +211,7 @@ const NSInteger CryptoCBCMaxHMACLength = 100;
|
|||
|
||||
- (NSData *)decryptData:(NSData *)data offset:(NSInteger)offset extra:(const uint8_t *)extra error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
NSAssert(self.cipher, @"No cipher provided");
|
||||
NSParameterAssert(data);
|
||||
|
||||
const uint8_t *bytes = data.bytes + offset;
|
||||
|
@ -203,6 +229,8 @@ const NSInteger CryptoCBCMaxHMACLength = 100;
|
|||
|
||||
- (BOOL)decryptBytes:(const uint8_t *)bytes length:(NSInteger)length dest:(uint8_t *)dest destLength:(NSInteger *)destLength extra:(const uint8_t *)extra error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
NSAssert(self.cipher, @"No cipher provided");
|
||||
|
||||
const uint8_t *iv = bytes + self.digestLength;
|
||||
const uint8_t *encrypted = bytes + self.digestLength + self.cipherIVLength;
|
||||
int l1 = 0, l2 = 0;
|
||||
|
@ -222,12 +250,35 @@ const NSInteger CryptoCBCMaxHMACLength = 100;
|
|||
TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherInit(self.cipherCtxDec, NULL, NULL, iv, -1);
|
||||
TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherUpdate(self.cipherCtxDec, dest, &l1, encrypted, (int)length - self.digestLength - self.cipherIVLength);
|
||||
TUNNEL_CRYPTO_TRACK_STATUS(code) EVP_CipherFinal(self.cipherCtxDec, dest + l1, &l2);
|
||||
|
||||
|
||||
*destLength = l1 + l2;
|
||||
|
||||
|
||||
TUNNEL_CRYPTO_RETURN_STATUS(code)
|
||||
}
|
||||
|
||||
- (BOOL)verifyData:(NSData *)data offset:(NSInteger)offset extra:(const uint8_t *)extra error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
return [self verifyBytes:data.bytes length:data.length extra:extra error:error];
|
||||
}
|
||||
|
||||
- (BOOL)verifyBytes:(const uint8_t *)bytes length:(NSInteger)length extra:(const uint8_t *)extra error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
int l1 = 0;
|
||||
int code = 1;
|
||||
|
||||
TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Init_ex(self.hmacCtxDec, NULL, 0, NULL, NULL);
|
||||
TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Update(self.hmacCtxDec, bytes + self.digestLength, length - self.digestLength);
|
||||
TUNNEL_CRYPTO_TRACK_STATUS(code) HMAC_Final(self.hmacCtxDec, self.bufferDecHMAC, (unsigned *)&l1);
|
||||
|
||||
if (TUNNEL_CRYPTO_SUCCESS(code) && CRYPTO_memcmp(self.bufferDecHMAC, bytes, self.digestLength) != 0) {
|
||||
if (error) {
|
||||
*error = TunnelKitErrorWithCode(TunnelKitErrorCodeCryptoBoxHMAC);
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (id<DataPathDecrypter>)dataPathDecrypter
|
||||
{
|
||||
return [[DataPathCryptoCBC alloc] initWithCrypto:self];
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
// WARNING: dest must be able to hold ciphertext
|
||||
@protocol Encrypter
|
||||
|
||||
- (void)configureEncryptionWithCipherKey:(nonnull ZeroingData *)cipherKey hmacKey:(nonnull ZeroingData *)hmacKey;
|
||||
- (void)configureEncryptionWithCipherKey:(nullable ZeroingData *)cipherKey hmacKey:(nullable ZeroingData *)hmacKey;
|
||||
- (int)digestLength;
|
||||
- (int)overheadLength;
|
||||
- (int)extraLength;
|
||||
|
@ -59,13 +59,15 @@
|
|||
// WARNING: dest must be able to hold plaintext
|
||||
@protocol Decrypter
|
||||
|
||||
- (void)configureDecryptionWithCipherKey:(nonnull ZeroingData *)cipherKey hmacKey:(nonnull ZeroingData *)hmacKey;
|
||||
- (void)configureDecryptionWithCipherKey:(nullable ZeroingData *)cipherKey hmacKey:(nullable ZeroingData *)hmacKey;
|
||||
- (int)digestLength;
|
||||
- (int)overheadLength;
|
||||
- (int)extraLength;
|
||||
|
||||
- (NSData *)decryptData:(nonnull NSData *)data offset:(NSInteger)offset extra:(const uint8_t *)extra error:(NSError **)error;
|
||||
- (BOOL)decryptBytes:(nonnull const uint8_t *)bytes length:(NSInteger)length dest:(nonnull uint8_t *)dest destLength:(nonnull NSInteger *)destLength extra:(const uint8_t *)extra error:(NSError **)error;
|
||||
- (BOOL)verifyData:(nonnull NSData *)data offset:(NSInteger)offset extra:(const uint8_t *)extra error:(NSError **)error;
|
||||
- (BOOL)verifyBytes:(nonnull const uint8_t *)bytes length:(NSInteger)length extra:(const uint8_t *)extra error:(NSError **)error;
|
||||
|
||||
- (nonnull id<DataPathDecrypter>)dataPathDecrypter;
|
||||
|
||||
|
|
|
@ -65,6 +65,22 @@ class EncryptionTests: XCTestCase {
|
|||
XCTAssertEqual(plain, decrypted)
|
||||
}
|
||||
|
||||
func testHMAC() {
|
||||
let cbc = CryptoBox(cipherAlgorithm: nil, digestAlgorithm: "sha256")
|
||||
try! cbc.configure(withCipherEncKey: nil, cipherDecKey: nil, hmacEncKey: hmacKey, hmacDecKey: hmacKey)
|
||||
let enc = cbc.encrypter()
|
||||
let dec = cbc.decrypter()
|
||||
|
||||
let plain = Data(hex: "00112233445566778899")
|
||||
let encrypted = try! enc.encryptData(plain, offset: 0, extra: nil)
|
||||
do {
|
||||
try dec.verifyData(encrypted, offset: 0, extra: nil)
|
||||
XCTAssert(true)
|
||||
} catch {
|
||||
XCTAssert(false)
|
||||
}
|
||||
}
|
||||
|
||||
func testGCM() {
|
||||
let gcm = CryptoBox(cipherAlgorithm: "aes-256-gcm", digestAlgorithm: nil)
|
||||
try! gcm.configure(withCipherEncKey: cipherKey, cipherDecKey: cipherKey, hmacEncKey: hmacKey, hmacDecKey: hmacKey)
|
||||
|
|
Loading…
Reference in New Issue