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: // Comment was:
// tagLength is actual an input <rdar://problem/30660074> // tagLength is actual an input <rdar://problem/30660074>
size_t tagLength = kAESGCM128_TagLength; size_t tagLength = kAESGCM128_TagLength;
CCCryptorStatus status = CCCryptorGCM(kCCEncrypt, // CCOperation op, /* kCCEncrypt, kCCDecrypt */ CCCryptorStatus status
kCCAlgorithmAES128, // CCAlgorithm alg, = CCCryptorGCM(kCCEncrypt, // CCOperation op, /* kCCEncrypt, kCCDecrypt */
key.keyData.bytes, // const void *key, /* raw key material */ kCCAlgorithmAES128, // CCAlgorithm alg,
key.keyData.length, // size_t keyLength, key.keyData.bytes, // const void *key, /* raw key material */
initializationVector.bytes, // const void *iv, key.keyData.length, // size_t keyLength,
initializationVector.length, // size_t ivLen, initializationVector.bytes, // const void *iv,
NULL, // const void *aData, initializationVector.length, // size_t ivLen,
0, // size_t aDataLen, NULL, // const void *aData,
plainTextData.bytes, // const void *dataIn, 0, // size_t aDataLen,
plainTextData.length, // size_t dataInLength, plainTextData.bytes, // const void *dataIn,
cipherTextBytes, // void *dataOut, plainTextData.length, // size_t dataInLength,
authTagBytes, // const void *tag, cipherTextBytes, // void *dataOut,
&tagLength //size_t *tagLength) authTagBytes, // const void *tag,
); &tagLength // size_t *tagLength)
);
if (status != kCCSuccess) { if (status != kCCSuccess) {
OWSFail(@"CCCryptorGCM encrypt failed with status: %d", status); OWSFail(@"CCCryptorGCM encrypt failed with status: %d", status);
free(cipherTextBytes); free(cipherTextBytes);
@ -452,15 +453,31 @@ const NSUInteger kAES128_KeyByteLength = 16;
// encryptedData layout: initializationVector || cipherText || authTag // encryptedData layout: initializationVector || cipherText || authTag
NSData *initializationVector = [encryptedData subdataWithRange:NSMakeRange(0, kAESGCM128_IVLength)]; NSData *initializationVector = [encryptedData subdataWithRange:NSMakeRange(0, kAESGCM128_IVLength)];
NSData *cipherText = [encryptedData subdataWithRange:NSMakeRange(kAESGCM128_IVLength, cipherTextLength)]; NSData *cipherText = [encryptedData subdataWithRange:NSMakeRange(kAESGCM128_IVLength, cipherTextLength)];
NSData *authTag = [encryptedData subdataWithRange:NSMakeRange(kAESGCM128_IVLength + cipherTextLength, NSData *authTag =
kAESGCM128_TagLength)]; [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) { if (plainTextBytes == NULL) {
OWSFail(@"Failed to malloc plainTextBytes"); OWSFail(@"Failed to malloc plainTextBytes");
return nil; 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`. // 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: // 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> // tagLength is actual an input <rdar://problem/30660074>
size_t tagLength = kAESGCM128_TagLength; size_t tagLength = kAESGCM128_TagLength;
CCCryptorStatus status = CCCryptorGCM(kCCDecrypt, // CCOperation op, /* kCCEncrypt, kCCDecrypt */ CCCryptorStatus status
kCCAlgorithmAES128, // CCAlgorithm alg, = CCCryptorGCM(kCCDecrypt, // CCOperation op, /* kCCEncrypt, kCCDecrypt */
key.keyData.bytes, // const void *key, /* raw key material */ kCCAlgorithmAES128, // CCAlgorithm alg,
key.keyData.length, // size_t keyLength, key.keyData.bytes, // const void *key, /* raw key material */
initializationVector.bytes, // const void *iv, key.keyData.length, // size_t keyLength,
initializationVector.length, // size_t ivLen, initializationVector.bytes, // const void *iv,
NULL, // const void *aData, initializationVector.length, // size_t ivLen,
0, // size_t aDataLen, NULL, // const void *aData,
cipherText.bytes, // const void *dataIn, 0, // size_t aDataLen,
cipherText.length, // size_t dataInLength, cipherText.bytes, // const void *dataIn,
plainTextBytes, // void *dataOut, cipherText.length, // size_t dataInLength,
authTag.bytes, // const void *tag, plainTextBytes, // void *dataOut,
&tagLength //size_t *tagLength) 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) { if (status != kCCSuccess) {
OWSFail(@"CCCryptorGCM decrypt failed with status: %d", status); OWSFail(@"CCCryptorGCM decrypt failed with status: %d", status);
free(plainTextBytes); free(plainTextBytes);
return nil; return nil;
} }
return [NSData dataWithBytesNoCopy:plainTextBytes length:cipherTextLength freeWhenDone:YES]; return [NSData dataWithBytesNoCopy:plainTextBytes length:cipherText.length freeWhenDone:YES];
} }
#pragma mark - Logging #pragma mark - Logging

@ -8,6 +8,15 @@
NS_ASSUME_NONNULL_BEGIN 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 @interface CryptographyTests : XCTestCase
@end @end
@ -127,19 +136,71 @@ NS_ASSUME_NONNULL_BEGIN
XCTAssertEqual(39, plainTextData.length); XCTAssertEqual(39, plainTextData.length);
OWSAES128Key *key = [OWSAES128Key new]; 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 ivLength = 12;
const NSUInteger tagLength = 16; const NSUInteger tagLength = 16;
XCTAssertEqual(ivLength + plainTextData.length + tagLength, encryptedData.length); 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); XCTAssertEqual(39, decryptedData.length);
XCTAssertEqualObjects(plainTextData, decryptedData); XCTAssertEqualObjects(plainTextData, decryptedData);
XCTAssertEqualObjects(@"Super🔥secret🔥test🔥data🏁🏁", [[NSString alloc] initWithData:decryptedData encoding:NSUTF8StringEncoding]); 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 @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END

Loading…
Cancel
Save