Merge branch 'mkirk/gcm-verification'

pull/1/head
Michael Kirk 8 years ago
commit 9f45ddd393

@ -410,22 +410,23 @@ const NSUInteger kAES128_KeyByteLength = 16;
// Comment was:
// tagLength is actual an input <rdar://problem/30660074>
size_t tagLength = kAESGCM128_TagLength;
CCCryptorStatus status = CCCryptorGCM(kCCEncrypt, // CCOperation op, /* kCCEncrypt, kCCDecrypt */
kCCAlgorithmAES128, // CCAlgorithm alg,
key.keyData.bytes, // const void *key, /* raw key material */
key.keyData.length, // size_t keyLength,
initializationVector.bytes, // const void *iv,
initializationVector.length, // size_t ivLen,
NULL, // const void *aData,
0, // size_t aDataLen,
plainTextData.bytes, // const void *dataIn,
plainTextData.length, // size_t dataInLength,
cipherTextBytes, // void *dataOut,
authTagBytes, // const void *tag,
&tagLength //size_t *tagLength)
);
CCCryptorStatus status
= CCCryptorGCM(kCCEncrypt, // CCOperation op, /* kCCEncrypt, kCCDecrypt */
kCCAlgorithmAES128, // CCAlgorithm alg,
key.keyData.bytes, // const void *key, /* raw key material */
key.keyData.length, // size_t keyLength,
initializationVector.bytes, // const void *iv,
initializationVector.length, // size_t ivLen,
NULL, // const void *aData,
0, // size_t aDataLen,
plainTextData.bytes, // const void *dataIn,
plainTextData.length, // size_t dataInLength,
cipherTextBytes, // void *dataOut,
authTagBytes, // const void *tag,
&tagLength // size_t *tagLength)
);
if (status != kCCSuccess) {
OWSFail(@"CCCryptorGCM encrypt failed with status: %d", status);
free(cipherTextBytes);
@ -452,15 +453,31 @@ const NSUInteger kAES128_KeyByteLength = 16;
// encryptedData layout: initializationVector || cipherText || authTag
NSData *initializationVector = [encryptedData subdataWithRange:NSMakeRange(0, kAESGCM128_IVLength)];
NSData *cipherText = [encryptedData subdataWithRange:NSMakeRange(kAESGCM128_IVLength, cipherTextLength)];
NSData *authTag = [encryptedData subdataWithRange:NSMakeRange(kAESGCM128_IVLength + cipherTextLength,
kAESGCM128_TagLength)];
NSData *authTag =
[encryptedData subdataWithRange:NSMakeRange(kAESGCM128_IVLength + cipherTextLength, kAESGCM128_TagLength)];
void * plainTextBytes = malloc(cipherTextLength);
return
[self decryptAESGCMWithInitializationVector:initializationVector cipherText:cipherText authTag:authTag key:key];
}
+ (nullable NSData *)decryptAESGCMWithInitializationVector:(NSData *)initializationVector
cipherText:(NSData *)cipherText
authTag:(NSData *)authTagFromEncrypt
key:(OWSAES128Key *)key
{
void *plainTextBytes = malloc(cipherText.length);
if (plainTextBytes == NULL) {
OWSFail(@"Failed to malloc plainTextBytes");
return nil;
}
void *decryptAuthTagBytes = malloc(kAESGCM128_TagLength);
if (decryptAuthTagBytes == NULL) {
OWSFail(@"Failed to malloc decryptAuthTagBytes");
free(plainTextBytes);
return nil;
}
// NOTE: Since `tagLength` is an input parameter, it seems weird that the signature for tagLength is a `size_t*` rather than just a `size_t`.
//
// I found a vague reference in the Safari repository implying that this may be a bug:
@ -470,28 +487,36 @@ const NSUInteger kAES128_KeyByteLength = 16;
// tagLength is actual an input <rdar://problem/30660074>
size_t tagLength = kAESGCM128_TagLength;
CCCryptorStatus status = CCCryptorGCM(kCCDecrypt, // CCOperation op, /* kCCEncrypt, kCCDecrypt */
kCCAlgorithmAES128, // CCAlgorithm alg,
key.keyData.bytes, // const void *key, /* raw key material */
key.keyData.length, // size_t keyLength,
initializationVector.bytes, // const void *iv,
initializationVector.length, // size_t ivLen,
NULL, // const void *aData,
0, // size_t aDataLen,
cipherText.bytes, // const void *dataIn,
cipherText.length, // size_t dataInLength,
plainTextBytes, // void *dataOut,
authTag.bytes, // const void *tag,
&tagLength //size_t *tagLength)
);
CCCryptorStatus status
= CCCryptorGCM(kCCDecrypt, // CCOperation op, /* kCCEncrypt, kCCDecrypt */
kCCAlgorithmAES128, // CCAlgorithm alg,
key.keyData.bytes, // const void *key, /* raw key material */
key.keyData.length, // size_t keyLength,
initializationVector.bytes, // const void *iv,
initializationVector.length, // size_t ivLen,
NULL, // const void *aData,
0, // size_t aDataLen,
cipherText.bytes, // const void *dataIn,
cipherText.length, // size_t dataInLength,
plainTextBytes, // void *dataOut,
decryptAuthTagBytes, // const void *tag,
&tagLength // size_t *tagLength
);
NSData *decryptAuthTag = [NSData dataWithBytesNoCopy:decryptAuthTagBytes length:tagLength freeWhenDone:YES];
if (![decryptAuthTag ows_constantTimeIsEqualToData:authTagFromEncrypt]) {
OWSFail(@"Auth tags don't match given tag: %@ computed tag: %@", authTagFromEncrypt, decryptAuthTag);
free(plainTextBytes);
return nil;
}
if (status != kCCSuccess) {
OWSFail(@"CCCryptorGCM decrypt failed with status: %d", status);
free(plainTextBytes);
return nil;
}
return [NSData dataWithBytesNoCopy:plainTextBytes length:cipherTextLength freeWhenDone:YES];
return [NSData dataWithBytesNoCopy:plainTextBytes length:cipherText.length freeWhenDone:YES];
}
#pragma mark - Logging

@ -8,6 +8,15 @@
NS_ASSUME_NONNULL_BEGIN
@interface Cryptography (TestingPrivateMethods)
+ (nullable NSData *)decryptAESGCMWithInitializationVector:(NSData *)initializationVector
cipherText:(NSData *)cipherText
authTag:(NSData *)authTagFromEncrypt
key:(OWSAES128Key *)key;
@end
@interface CryptographyTests : XCTestCase
@end
@ -127,19 +136,71 @@ NS_ASSUME_NONNULL_BEGIN
XCTAssertEqual(39, plainTextData.length);
OWSAES128Key *key = [OWSAES128Key new];
NSData *encryptedData = [Cryptography encryptAESGCMWithData:plainTextData key:key];
NSData *_Nullable encryptedData = [Cryptography encryptAESGCMWithData:plainTextData key:key];
const NSUInteger ivLength = 12;
const NSUInteger tagLength = 16;
XCTAssertEqual(ivLength + plainTextData.length + tagLength, encryptedData.length);
NSData *decryptedData = [Cryptography decryptAESGCMWithData:encryptedData key:key];
NSData *_Nullable decryptedData = [Cryptography decryptAESGCMWithData:encryptedData key:key];
XCTAssert(decryptedData != nil);
XCTAssertEqual(39, decryptedData.length);
XCTAssertEqualObjects(plainTextData, decryptedData);
XCTAssertEqualObjects(@"Super🔥secret🔥test🔥data🏁🏁", [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding]);
}
- (void)testGCMWithBadTag
{
NSData *plainTextData = [@"Super🔥secret🔥test🔥data🏁🏁" dataUsingEncoding:NSUTF8StringEncoding];
// Sanity Check
XCTAssertEqual(39, plainTextData.length);
OWSAES128Key *key = [OWSAES128Key new];
NSData *_Nullable encryptedData = [Cryptography encryptAESGCMWithData:plainTextData key:key];
const NSUInteger ivLength = 12;
const NSUInteger tagLength = 16;
XCTAssertEqual(ivLength + plainTextData.length + tagLength, encryptedData.length);
// Logic to slice up encryptedData copied from `[Cryptography decryptAESGCMWithData:key:]`
// encryptedData layout: initializationVector || cipherText || authTag
NSUInteger cipherTextLength = encryptedData.length - ivLength - tagLength;
NSData *initializationVector = [encryptedData subdataWithRange:NSMakeRange(0, ivLength)];
NSData *cipherText = [encryptedData subdataWithRange:NSMakeRange(ivLength, cipherTextLength)];
NSData *authTag = [encryptedData subdataWithRange:NSMakeRange(ivLength + cipherTextLength, tagLength)];
NSData *_Nullable decryptedData = [Cryptography decryptAESGCMWithInitializationVector:initializationVector
cipherText:cipherText
authTag:authTag
key:key];
// Before we corrupt the tag, make sure we can decrypt the text as a sanity check to ensure we divided up the
// encryptedData correctly.
XCTAssert(decryptedData != nil);
XCTAssertEqualObjects(
@"Super🔥secret🔥test🔥data🏁🏁", [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding]);
// Now that we know it decrypts, try again with a bogus authTag
NSMutableData *bogusAuthTag = [authTag mutableCopy];
// Corrupt one byte in the bogusAuthTag
uint8_t flippedByte;
[bogusAuthTag getBytes:&flippedByte length:1];
flippedByte = flippedByte ^ 0xff;
[bogusAuthTag replaceBytesInRange:NSMakeRange(0, 1) withBytes:&flippedByte];
decryptedData = [Cryptography decryptAESGCMWithInitializationVector:initializationVector
cipherText:cipherText
authTag:bogusAuthTag
key:key];
XCTAssertNil(decryptedData, @"Should have failed to decrypt");
}
@end
NS_ASSUME_NONNULL_END

Loading…
Cancel
Save