Allow HMAC verify with nil cipher in CryptoCBC

This commit is contained in:
Davide De Rosa 2018-09-10 16:02:01 +02:00
parent 401d999b3d
commit d53e7add10
7 changed files with 143 additions and 59 deletions

View File

@ -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];

View File

@ -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

View File

@ -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];

View File

@ -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

View File

@ -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];

View File

@ -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;

View File

@ -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)