verification on decrypt
// FREEBIE
This commit is contained in:
parent
b3ddd73cee
commit
97afa4d48a
@ -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…
Reference in New Issue
Block a user