Overflow math and safer asserts in Cryptography.m

Also removed some unused code
pull/1/head
Michael Kirk 7 years ago
parent ccea3ff0f1
commit a2852ee930

@ -46,7 +46,6 @@ extern const NSUInteger kAES256_KeyByteLength;
@interface Cryptography : NSObject @interface Cryptography : NSObject
typedef NS_ENUM(NSInteger, TSMACType) { typedef NS_ENUM(NSInteger, TSMACType) {
TSHMACSHA1Truncated10Bytes = 1,
TSHMACSHA256Truncated10Bytes = 2, TSHMACSHA256Truncated10Bytes = 2,
TSHMACSHA256AttachementType = 3 TSHMACSHA256AttachementType = 3
}; };

@ -26,6 +26,9 @@ static const NSUInteger kAESGCM256_IVLength = 12;
// length of authentication tag for AES256-GCM // length of authentication tag for AES256-GCM
static const NSUInteger kAESGCM256_TagLength = 16; static const NSUInteger kAESGCM256_TagLength = 16;
// length of key used for websocket envelope authentication
static const NSUInteger kHMAC256_EnvelopeKeyLength = 20;
const NSUInteger kAES256_KeyByteLength = 32; const NSUInteger kAES256_KeyByteLength = 32;
@implementation OWSAES256Key @implementation OWSAES256Key
@ -230,42 +233,13 @@ const NSUInteger kAES256_KeyByteLength = 32;
return [ourHmacData copy]; return [ourHmacData copy];
} }
+ (nullable NSData *)computeSHA1HMAC:(NSData *)data withHMACKey:(NSData *)HMACKey
{
if (data.length >= SIZE_MAX) {
OWSFailDebug(@"data is too long.");
return nil;
}
size_t dataLength = (size_t)data.length;
if (HMACKey.length >= SIZE_MAX) {
OWSFailDebug(@"HMAC key is too long.");
return nil;
}
size_t hmacKeyLength = (size_t)HMACKey.length;
NSMutableData *_Nullable ourHmacData = [[NSMutableData alloc] initWithLength:CC_SHA1_DIGEST_LENGTH];
if (!ourHmacData) {
OWSFailDebug(@"could not allocate buffer.");
return nil;
}
CCHmac(kCCHmacAlgSHA1, [HMACKey bytes], hmacKeyLength, [data bytes], dataLength, ourHmacData.mutableBytes);
return [ourHmacData copy];
}
+ (nullable NSData *)truncatedSHA1HMAC:(NSData *)dataToHMAC
withHMACKey:(NSData *)HMACKey
truncation:(NSUInteger)truncation
{
OWSAssertDebug(truncation <= CC_SHA1_DIGEST_LENGTH);
return [[Cryptography computeSHA1HMAC:dataToHMAC withHMACKey:HMACKey] subdataWithRange:NSMakeRange(0, truncation)];
}
+ (nullable NSData *)truncatedSHA256HMAC:(NSData *)dataToHMAC + (nullable NSData *)truncatedSHA256HMAC:(NSData *)dataToHMAC
withHMACKey:(NSData *)HMACKey withHMACKey:(NSData *)HMACKey
truncation:(NSUInteger)truncation truncation:(NSUInteger)truncation
{ {
OWSAssertDebug(truncation <= CC_SHA256_DIGEST_LENGTH); OWSAssert(truncation <= CC_SHA256_DIGEST_LENGTH);
OWSAssert(dataToHMAC);
OWSAssert(HMACKey);
return return
[[Cryptography computeSHA256HMAC:dataToHMAC withHMACKey:HMACKey] subdataWithRange:NSMakeRange(0, truncation)]; [[Cryptography computeSHA256HMAC:dataToHMAC withHMACKey:HMACKey] subdataWithRange:NSMakeRange(0, truncation)];
@ -287,29 +261,60 @@ const NSUInteger kAES256_KeyByteLength = 32;
matchingHMAC:(NSData *)hmac matchingHMAC:(NSData *)hmac
digest:(nullable NSData *)digest digest:(nullable NSData *)digest
{ {
if (dataToDecrypt.length >= (SIZE_MAX - kCCBlockSizeAES128)) { OWSAssert(dataToDecrypt);
OWSFailDebug(@"data is too long."); OWSAssert(key);
if (key.length != kCCKeySizeAES256) {
OWSFailDebug(@"key had wrong size.");
return nil;
}
OWSAssert(iv);
if (iv.length != kCCBlockSizeAES128) {
OWSFailDebug(@"iv had wrong size.");
return nil;
}
OWSAssert(hmacKey);
OWSAssert(hmac);
size_t bufferSize;
BOOL didOverflow = __builtin_add_overflow(dataToDecrypt.length, kCCBlockSizeAES128, &bufferSize);
if (didOverflow) {
OWSFailDebug(@"bufferSize was too large.");
return nil; return nil;
} }
// Verify hmac of: version? || iv || encrypted data // Verify hmac of: version? || iv || encrypted data
NSUInteger dataToAuthLength = 0;
if (__builtin_add_overflow(dataToDecrypt.length, iv.length, &dataToAuthLength)) {
OWSFailDebug(@"dataToAuth was too large.");
return nil;
}
if (version != nil && __builtin_add_overflow(dataToAuthLength, version.length, &dataToAuthLength)) {
OWSFailDebug(@"dataToAuth was too large.");
return nil;
}
NSMutableData *dataToAuth = [NSMutableData data]; NSMutableData *dataToAuth = [NSMutableData data];
if (version != nil) { if (version != nil) {
[dataToAuth appendData:version]; [dataToAuth appendData:version];
} }
[dataToAuth appendData:iv]; [dataToAuth appendData:iv];
[dataToAuth appendData:dataToDecrypt]; [dataToAuth appendData:dataToDecrypt];
NSData *_Nullable ourHmacData; NSData *_Nullable ourHmacData;
if (hmacType == TSHMACSHA1Truncated10Bytes) { if (hmacType == TSHMACSHA256Truncated10Bytes) {
ourHmacData = [Cryptography truncatedSHA1HMAC:dataToAuth withHMACKey:hmacKey truncation:10]; // used to authenticate envelope from websocket
} else if (hmacType == TSHMACSHA256Truncated10Bytes) { OWSAssert(hmacKey.length == kHMAC256_EnvelopeKeyLength);
ourHmacData = [Cryptography truncatedSHA256HMAC:dataToAuth withHMACKey:hmacKey truncation:10]; ourHmacData = [Cryptography truncatedSHA256HMAC:dataToAuth withHMACKey:hmacKey truncation:10];
OWSAssert(ourHmacData.length == 10);
} else if (hmacType == TSHMACSHA256AttachementType) { } else if (hmacType == TSHMACSHA256AttachementType) {
OWSAssert(hmacKey.length == HMAC256_KEY_LENGTH);
ourHmacData = ourHmacData =
[Cryptography truncatedSHA256HMAC:dataToAuth withHMACKey:hmacKey truncation:HMAC256_OUTPUT_LENGTH]; [Cryptography truncatedSHA256HMAC:dataToAuth withHMACKey:hmacKey truncation:HMAC256_OUTPUT_LENGTH];
OWSAssert(ourHmacData.length == HMAC256_OUTPUT_LENGTH);
} else {
OWSFail(@"unknown HMAC scheme: %ld", (long)hmacType);
} }
if (hmac == nil || ![ourHmacData ows_constantTimeIsEqualToData:hmac]) { if (hmac == nil || ![ourHmacData ows_constantTimeIsEqualToData:hmac]) {
@ -333,7 +338,6 @@ const NSUInteger kAES256_KeyByteLength = 32;
} }
// decrypt // decrypt
size_t bufferSize = [dataToDecrypt length] + kCCBlockSizeAES128;
NSMutableData *_Nullable bufferData = [NSMutableData dataWithLength:bufferSize]; NSMutableData *_Nullable bufferData = [NSMutableData dataWithLength:bufferSize];
if (!bufferData) { if (!bufferData) {
OWSLogError(@"Failed to allocate buffer."); OWSLogError(@"Failed to allocate buffer.");
@ -372,7 +376,9 @@ const NSUInteger kAES256_KeyByteLength = 32;
size_t ivLength = 16; size_t ivLength = 16;
size_t macLength = 10; size_t macLength = 10;
size_t nonCiphertextLength = versionLength + ivLength + macLength; size_t nonCiphertextLength = versionLength + ivLength + macLength;
size_t ciphertextLength = payload.length - nonCiphertextLength;
size_t ciphertextLength;
ows_sub_overflow(payload.length, nonCiphertextLength, &ciphertextLength);
if (payload.length < nonCiphertextLength) { if (payload.length < nonCiphertextLength) {
OWSFailDebug(@"Invalid payload"); OWSFailDebug(@"Invalid payload");
@ -389,12 +395,12 @@ const NSUInteger kAES256_KeyByteLength = 32;
NSData *ivData = [payload subdataWithRange:NSMakeRange(cursor, ivLength)]; NSData *ivData = [payload subdataWithRange:NSMakeRange(cursor, ivLength)];
cursor += ivLength; cursor += ivLength;
NSData *ciphertextData = [payload subdataWithRange:NSMakeRange(cursor, ciphertextLength)]; NSData *ciphertextData = [payload subdataWithRange:NSMakeRange(cursor, ciphertextLength)];
cursor += ciphertextLength; ows_add_overflow(cursor, ciphertextLength, &cursor);
NSData *macData = [payload subdataWithRange:NSMakeRange(cursor, macLength)]; NSData *macData = [payload subdataWithRange:NSMakeRange(cursor, macLength)];
NSData *signalingKey = [NSData dataFromBase64String:signalingKeyString]; NSData *signalingKey = [NSData dataFromBase64String:signalingKeyString];
NSData *signalingKeyAESKeyMaterial = [signalingKey subdataWithRange:NSMakeRange(0, 32)]; NSData *signalingKeyAESKeyMaterial = [signalingKey subdataWithRange:NSMakeRange(0, 32)];
NSData *signalingKeyHMACKeyMaterial = [signalingKey subdataWithRange:NSMakeRange(32, 20)]; NSData *signalingKeyHMACKeyMaterial = [signalingKey subdataWithRange:NSMakeRange(32, kHMAC256_EnvelopeKeyLength)];
return [Cryptography decryptCBCMode:ciphertextData return [Cryptography decryptCBCMode:ciphertextData
key:signalingKeyAESKeyMaterial key:signalingKeyAESKeyMaterial
IV:ivData IV:ivData
@ -434,11 +440,14 @@ const NSUInteger kAES256_KeyByteLength = 32;
// dataToDecrypt: IV || Ciphertext || truncated MAC(IV||Ciphertext) // dataToDecrypt: IV || Ciphertext || truncated MAC(IV||Ciphertext)
NSData *iv = [dataToDecrypt subdataWithRange:NSMakeRange(0, AES_CBC_IV_LENGTH)]; NSData *iv = [dataToDecrypt subdataWithRange:NSMakeRange(0, AES_CBC_IV_LENGTH)];
NSData *encryptedAttachment = [dataToDecrypt
subdataWithRange:NSMakeRange(AES_CBC_IV_LENGTH, NSUInteger cipherTextLength;
[dataToDecrypt length] - AES_CBC_IV_LENGTH - HMAC256_OUTPUT_LENGTH)]; ows_sub_overflow(dataToDecrypt.length, (AES_CBC_IV_LENGTH + HMAC256_OUTPUT_LENGTH), &cipherTextLength);
NSData *hmac = [dataToDecrypt NSData *encryptedAttachment = [dataToDecrypt subdataWithRange:NSMakeRange(AES_CBC_IV_LENGTH, cipherTextLength)];
subdataWithRange:NSMakeRange([dataToDecrypt length] - HMAC256_OUTPUT_LENGTH, HMAC256_OUTPUT_LENGTH)];
NSUInteger hmacOffset;
ows_sub_overflow(dataToDecrypt.length, HMAC256_OUTPUT_LENGTH, &hmacOffset);
NSData *hmac = [dataToDecrypt subdataWithRange:NSMakeRange(hmacOffset, HMAC256_OUTPUT_LENGTH)];
NSData *_Nullable paddedPlainText = [Cryptography decryptCBCMode:encryptedAttachment NSData *_Nullable paddedPlainText = [Cryptography decryptCBCMode:encryptedAttachment
key:encryptionKey key:encryptionKey
@ -469,7 +478,9 @@ const NSUInteger kAES256_KeyByteLength = 32;
OWSLogInfo(@"decrypted unpadded attachment."); OWSLogInfo(@"decrypted unpadded attachment.");
return [paddedPlainText copy]; return [paddedPlainText copy];
} else { } else {
unsigned long paddingSize = paddedPlainText.length - unpaddedSize; unsigned long paddingSize;
ows_sub_overflow(paddedPlainText.length, unpaddedSize, &paddingSize);
OWSLogInfo(@"decrypted padded attachment with unpaddedSize: %lu, paddingSize: %lu", OWSLogInfo(@"decrypted padded attachment with unpaddedSize: %lu, paddingSize: %lu",
(unsigned long)unpaddedSize, (unsigned long)unpaddedSize,
paddingSize); paddingSize);
@ -517,7 +528,8 @@ const NSUInteger kAES256_KeyByteLength = 32;
paddedAttachmentData.length = desiredSize; paddedAttachmentData.length = desiredSize;
// Encrypt // Encrypt
size_t bufferSize = [paddedAttachmentData length] + kCCBlockSizeAES128; size_t bufferSize;
ows_add_overflow(paddedAttachmentData.length, kCCBlockSizeAES128, &bufferSize);
NSMutableData *_Nullable bufferData = [NSMutableData dataWithLength:bufferSize]; NSMutableData *_Nullable bufferData = [NSMutableData dataWithLength:bufferSize];
if (!bufferData) { if (!bufferData) {
OWSLogError(@"Failed to allocate buffer."); OWSLogError(@"Failed to allocate buffer.");
@ -790,14 +802,19 @@ const NSUInteger kAES256_KeyByteLength = 32;
+ (nullable NSData *)decryptAESGCMWithProfileData:(NSData *)encryptedData key:(OWSAES256Key *)key + (nullable NSData *)decryptAESGCMWithProfileData:(NSData *)encryptedData key:(OWSAES256Key *)key
{ {
OWSAssertDebug(encryptedData.length > kAESGCM256_IVLength + kAESGCM256_TagLength); OWSAssert(encryptedData.length > kAESGCM256_IVLength + kAESGCM256_TagLength);
NSUInteger cipherTextLength = encryptedData.length - kAESGCM256_IVLength - kAESGCM256_TagLength;
NSUInteger cipherTextLength;
ows_sub_overflow(encryptedData.length, (kAESGCM256_IVLength + kAESGCM256_TagLength), &cipherTextLength);
// encryptedData layout: initializationVector || ciphertext || authTag // encryptedData layout: initializationVector || ciphertext || authTag
NSData *initializationVector = [encryptedData subdataWithRange:NSMakeRange(0, kAESGCM256_IVLength)]; NSData *initializationVector = [encryptedData subdataWithRange:NSMakeRange(0, kAESGCM256_IVLength)];
NSData *ciphertext = [encryptedData subdataWithRange:NSMakeRange(kAESGCM256_IVLength, cipherTextLength)]; NSData *ciphertext = [encryptedData subdataWithRange:NSMakeRange(kAESGCM256_IVLength, cipherTextLength)];
NSData *authTag =
[encryptedData subdataWithRange:NSMakeRange(kAESGCM256_IVLength + cipherTextLength, kAESGCM256_TagLength)]; NSUInteger tagOffset;
ows_add_overflow(kAESGCM256_IVLength, cipherTextLength, &tagOffset);
NSData *authTag = [encryptedData subdataWithRange:NSMakeRange(tagOffset, kAESGCM256_TagLength)];
return [self decryptAESGCMWithInitializationVector:initializationVector return [self decryptAESGCMWithInitializationVector:initializationVector
ciphertext:ciphertext ciphertext:ciphertext

Loading…
Cancel
Save