diff --git a/TunnelKit/Sources/Core/CryptoAEAD.m b/TunnelKit/Sources/Core/CryptoAEAD.m index 45723e2..4d0fb12 100644 --- a/TunnelKit/Sources/Core/CryptoAEAD.m +++ b/TunnelKit/Sources/Core/CryptoAEAD.m @@ -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 { return [[DataPathCryptoAEAD alloc] initWithCrypto:self]; diff --git a/TunnelKit/Sources/Core/CryptoBox.h b/TunnelKit/Sources/Core/CryptoBox.h index 3a5ff9a..e66604a 100644 --- a/TunnelKit/Sources/Core/CryptoBox.h +++ b/TunnelKit/Sources/Core/CryptoBox.h @@ -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 diff --git a/TunnelKit/Sources/Core/CryptoBox.m b/TunnelKit/Sources/Core/CryptoBox.m index 89492f0..73ba6c9 100644 --- a/TunnelKit/Sources/Core/CryptoBox.m +++ b/TunnelKit/Sources/Core/CryptoBox.m @@ -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]; diff --git a/TunnelKit/Sources/Core/CryptoCBC.h b/TunnelKit/Sources/Core/CryptoCBC.h index 89af6ff..40d494f 100644 --- a/TunnelKit/Sources/Core/CryptoCBC.h +++ b/TunnelKit/Sources/Core/CryptoCBC.h @@ -43,8 +43,7 @@ NS_ASSUME_NONNULL_BEGIN @interface CryptoCBC : NSObject -- (instancetype)initWithCipherName:(nonnull NSString *)cipherName - digestName:(nonnull NSString *)digestName; +- (instancetype)initWithCipherName:(nullable NSString *)cipherName digestName:(nonnull NSString *)digestName; @end diff --git a/TunnelKit/Sources/Core/CryptoCBC.m b/TunnelKit/Sources/Core/CryptoCBC.m index 12b9c46..40e97b6 100644 --- a/TunnelKit/Sources/Core/CryptoCBC.m +++ b/TunnelKit/Sources/Core/CryptoCBC.m @@ -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 { return [[DataPathCryptoCBC alloc] initWithCrypto:self]; diff --git a/TunnelKit/Sources/Core/Encryption.h b/TunnelKit/Sources/Core/Encryption.h index ca0bb2b..098b692 100644 --- a/TunnelKit/Sources/Core/Encryption.h +++ b/TunnelKit/Sources/Core/Encryption.h @@ -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; diff --git a/TunnelKitTests/EncryptionTests.swift b/TunnelKitTests/EncryptionTests.swift index b448355..aa1899d 100644 --- a/TunnelKitTests/EncryptionTests.swift +++ b/TunnelKitTests/EncryptionTests.swift @@ -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)