diff --git a/Scripts/precommit.py b/Scripts/precommit.py index 4b8a2df16..08a21261a 100755 --- a/Scripts/precommit.py +++ b/Scripts/precommit.py @@ -313,7 +313,7 @@ def process(filepath): filename = os.path.basename(filepath) if filename.startswith('.'): - return + raise "shouldn't call process with dotfile" file_ext = os.path.splitext(filename)[1] if file_ext in ('.swift'): env_copy = os.environ.copy() @@ -376,7 +376,7 @@ def should_ignore_path(path): return False - + def process_if_appropriate(filepath): filename = os.path.basename(filepath) if filename.startswith('.'): @@ -388,7 +388,33 @@ def process_if_appropriate(filepath): return process(filepath) - + +def check_diff_for_keywords(): + keywords = ["OWSAssert\(", "OWSFail\(", "ows_add_overflow\(", "ows_sub_overflow\("] + matching_expression = "|".join(keywords) + command_line = 'git diff --staged | grep --color=always -C 3 -E "%s"' % matching_expression + print(command_line) + try: + output = subprocess.check_output(command_line, shell=True) + except subprocess.CalledProcessError, e: + # > man grep + # EXIT STATUS + # The grep utility exits with one of the following values: + # 0 One or more lines were selected. + # 1 No lines were selected. + # >1 An error occurred. + if e.returncode == 1: + # no keywords in diff output + return + else: + # some other error - bad grep expression? + raise e + + if len(output) > 0: + print("⚠️ keywords detected in diff:") + print(output) + + if __name__ == "__main__": parser = argparse.ArgumentParser(description='Precommit script.') @@ -426,3 +452,5 @@ if __name__ == "__main__": print 'git clang-format...' print commands.getoutput('git clang-format') + + check_diff_for_keywords() diff --git a/Signal/src/ViewControllers/DebugUI/DebugUIBackup.m b/Signal/src/ViewControllers/DebugUI/DebugUIBackup.m index f1d57eb2d..ab13077e6 100644 --- a/Signal/src/ViewControllers/DebugUI/DebugUIBackup.m +++ b/Signal/src/ViewControllers/DebugUI/DebugUIBackup.m @@ -134,7 +134,8 @@ NS_ASSUME_NONNULL_BEGIN NSData *_Nullable data = [NSKeyedArchiver archivedDataWithRootObject:interaction]; OWSAssertDebug(data); - interactionSizeTotal += data.length; + ows_add_overflow( + interactionSizeTotal, data.length, &interactionSizeTotal); }]; [transaction enumerateKeysAndObjectsInCollection:[TSAttachment collection] usingBlock:^(NSString *key, id object, BOOL *stop) { @@ -143,7 +144,8 @@ NS_ASSUME_NONNULL_BEGIN NSData *_Nullable data = [NSKeyedArchiver archivedDataWithRootObject:attachment]; OWSAssertDebug(data); - attachmentSizeTotal += data.length; + ows_add_overflow( + attachmentSizeTotal, data.length, &attachmentSizeTotal); }]; }]; diff --git a/Signal/src/util/FunctionalUtil.h b/Signal/src/util/FunctionalUtil.h index 02fcdeabe..021997a8f 100644 --- a/Signal/src/util/FunctionalUtil.h +++ b/Signal/src/util/FunctionalUtil.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // @interface NSArray (FunctionalUtil) @@ -19,15 +19,6 @@ /// Returns an array of all the results of passing items from this array through the given projection function. - (NSArray *)filter:(int (^)(id item))predicate; -/// Returns the sum of the doubles in this array of doubles. -- (double)sumDouble; - -/// Returns the sum of the unsigned integers in this array of unsigned integers. -- (NSUInteger)sumNSUInteger; - -/// Returns the sum of the integers in this array of integers. -- (NSInteger)sumNSInteger; - - (NSDictionary *)keyedBy:(id (^)(id))keySelector; - (NSDictionary *)groupBy:(id (^)(id value))keySelector; diff --git a/Signal/src/util/FunctionalUtil.m b/Signal/src/util/FunctionalUtil.m index 148f3c522..7a433b443 100644 --- a/Signal/src/util/FunctionalUtil.m +++ b/Signal/src/util/FunctionalUtil.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "FunctionalUtil.h" @@ -52,27 +52,7 @@ } return r; } -- (double)sumDouble { - double s = 0.0; - for (NSNumber *e in self) { - s += [e doubleValue]; - } - return s; -} -- (NSUInteger)sumNSUInteger { - NSUInteger s = 0; - for (NSNumber *e in self) { - s += [e unsignedIntegerValue]; - } - return s; -} -- (NSInteger)sumNSInteger { - NSInteger s = 0; - for (NSNumber *e in self) { - s += [e integerValue]; - } - return s; -} + - (NSDictionary *)keyedBy:(id (^)(id value))keySelector { OWSAssertDebug(keySelector != nil); diff --git a/Signal/src/util/OWSBackupExportJob.m b/Signal/src/util/OWSBackupExportJob.m index a541e1072..778851429 100644 --- a/Signal/src/util/OWSBackupExportJob.m +++ b/Signal/src/util/OWSBackupExportJob.m @@ -630,27 +630,30 @@ NS_ASSUME_NONNULL_BEGIN { unsigned long long databaseFileSize = 0; for (OWSBackupExportItem *item in self.unsavedDatabaseItems) { - databaseFileSize += [OWSFileSystem fileSizeOfPath:item.encryptedItem.filePath].unsignedLongLongValue; + unsigned long long fileSize = + [OWSFileSystem fileSizeOfPath:item.encryptedItem.filePath].unsignedLongLongValue; + ows_add_overflow(databaseFileSize, fileSize, &databaseFileSize); } OWSLogInfo(@"exporting %@: count: %zd, bytes: %llu.", @"database items", self.unsavedDatabaseItems.count, databaseFileSize); - totalFileSize += databaseFileSize; - totalFileCount += self.unsavedDatabaseItems.count; + ows_add_overflow(totalFileSize, databaseFileSize, &totalFileSize); + ows_add_overflow(totalFileCount, self.unsavedDatabaseItems.count, &totalFileCount); } { unsigned long long attachmentFileSize = 0; for (OWSAttachmentExport *attachmentExport in self.unsavedAttachmentExports) { - attachmentFileSize += + unsigned long long fileSize = [OWSFileSystem fileSizeOfPath:attachmentExport.attachmentFilePath].unsignedLongLongValue; + ows_add_overflow(attachmentFileSize, fileSize, &attachmentFileSize); } OWSLogInfo(@"exporting %@: count: %zd, bytes: %llu.", @"attachment items", self.unsavedAttachmentExports.count, attachmentFileSize); - totalFileSize += attachmentFileSize; - totalFileCount += self.unsavedAttachmentExports.count; + ows_add_overflow(totalFileSize, attachmentFileSize, &totalFileSize); + ows_add_overflow(totalFileCount, self.unsavedAttachmentExports.count, &totalFileSize); } OWSLogInfo(@"exporting %@: count: %zd, bytes: %llu.", @"all items", totalFileCount, totalFileSize); diff --git a/SignalServiceKit/src/Util/Cryptography.h b/SignalServiceKit/src/Util/Cryptography.h index 6437a4e72..7ff72fe70 100755 --- a/SignalServiceKit/src/Util/Cryptography.h +++ b/SignalServiceKit/src/Util/Cryptography.h @@ -46,7 +46,6 @@ extern const NSUInteger kAES256_KeyByteLength; @interface Cryptography : NSObject typedef NS_ENUM(NSInteger, TSMACType) { - TSHMACSHA1Truncated10Bytes = 1, TSHMACSHA256Truncated10Bytes = 2, TSHMACSHA256AttachementType = 3 }; diff --git a/SignalServiceKit/src/Util/Cryptography.m b/SignalServiceKit/src/Util/Cryptography.m index 60a719b61..2c6c0b239 100755 --- a/SignalServiceKit/src/Util/Cryptography.m +++ b/SignalServiceKit/src/Util/Cryptography.m @@ -26,6 +26,9 @@ static const NSUInteger kAESGCM256_IVLength = 12; // length of authentication tag for AES256-GCM 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; @implementation OWSAES256Key @@ -230,42 +233,13 @@ const NSUInteger kAES256_KeyByteLength = 32; 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 withHMACKey:(NSData *)HMACKey truncation:(NSUInteger)truncation { - OWSAssertDebug(truncation <= CC_SHA256_DIGEST_LENGTH); + OWSAssert(truncation <= CC_SHA256_DIGEST_LENGTH); + OWSAssert(dataToHMAC); + OWSAssert(HMACKey); return [[Cryptography computeSHA256HMAC:dataToHMAC withHMACKey:HMACKey] subdataWithRange:NSMakeRange(0, truncation)]; @@ -287,29 +261,60 @@ const NSUInteger kAES256_KeyByteLength = 32; matchingHMAC:(NSData *)hmac digest:(nullable NSData *)digest { - if (dataToDecrypt.length >= (SIZE_MAX - kCCBlockSizeAES128)) { - OWSFailDebug(@"data is too long."); + OWSAssert(dataToDecrypt); + 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; } // 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]; if (version != nil) { [dataToAuth appendData:version]; } - [dataToAuth appendData:iv]; [dataToAuth appendData:dataToDecrypt]; NSData *_Nullable ourHmacData; - if (hmacType == TSHMACSHA1Truncated10Bytes) { - ourHmacData = [Cryptography truncatedSHA1HMAC:dataToAuth withHMACKey:hmacKey truncation:10]; - } else if (hmacType == TSHMACSHA256Truncated10Bytes) { + if (hmacType == TSHMACSHA256Truncated10Bytes) { + // used to authenticate envelope from websocket + OWSAssert(hmacKey.length == kHMAC256_EnvelopeKeyLength); ourHmacData = [Cryptography truncatedSHA256HMAC:dataToAuth withHMACKey:hmacKey truncation:10]; + OWSAssert(ourHmacData.length == 10); } else if (hmacType == TSHMACSHA256AttachementType) { + OWSAssert(hmacKey.length == HMAC256_KEY_LENGTH); ourHmacData = [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]) { @@ -333,7 +338,6 @@ const NSUInteger kAES256_KeyByteLength = 32; } // decrypt - size_t bufferSize = [dataToDecrypt length] + kCCBlockSizeAES128; NSMutableData *_Nullable bufferData = [NSMutableData dataWithLength:bufferSize]; if (!bufferData) { OWSLogError(@"Failed to allocate buffer."); @@ -372,7 +376,9 @@ const NSUInteger kAES256_KeyByteLength = 32; size_t ivLength = 16; size_t macLength = 10; 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) { OWSFailDebug(@"Invalid payload"); @@ -389,12 +395,12 @@ const NSUInteger kAES256_KeyByteLength = 32; NSData *ivData = [payload subdataWithRange:NSMakeRange(cursor, ivLength)]; cursor += ivLength; NSData *ciphertextData = [payload subdataWithRange:NSMakeRange(cursor, ciphertextLength)]; - cursor += ciphertextLength; + ows_add_overflow(cursor, ciphertextLength, &cursor); NSData *macData = [payload subdataWithRange:NSMakeRange(cursor, macLength)]; NSData *signalingKey = [NSData dataFromBase64String:signalingKeyString]; 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 key:signalingKeyAESKeyMaterial IV:ivData @@ -434,11 +440,14 @@ const NSUInteger kAES256_KeyByteLength = 32; // dataToDecrypt: IV || Ciphertext || truncated MAC(IV||Ciphertext) NSData *iv = [dataToDecrypt subdataWithRange:NSMakeRange(0, AES_CBC_IV_LENGTH)]; - NSData *encryptedAttachment = [dataToDecrypt - subdataWithRange:NSMakeRange(AES_CBC_IV_LENGTH, - [dataToDecrypt length] - AES_CBC_IV_LENGTH - HMAC256_OUTPUT_LENGTH)]; - NSData *hmac = [dataToDecrypt - subdataWithRange:NSMakeRange([dataToDecrypt length] - HMAC256_OUTPUT_LENGTH, HMAC256_OUTPUT_LENGTH)]; + + NSUInteger cipherTextLength; + ows_sub_overflow(dataToDecrypt.length, (AES_CBC_IV_LENGTH + HMAC256_OUTPUT_LENGTH), &cipherTextLength); + NSData *encryptedAttachment = [dataToDecrypt subdataWithRange:NSMakeRange(AES_CBC_IV_LENGTH, cipherTextLength)]; + + 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 key:encryptionKey @@ -469,7 +478,9 @@ const NSUInteger kAES256_KeyByteLength = 32; OWSLogInfo(@"decrypted unpadded attachment."); return [paddedPlainText copy]; } 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", (unsigned long)unpaddedSize, paddingSize); @@ -517,7 +528,8 @@ const NSUInteger kAES256_KeyByteLength = 32; paddedAttachmentData.length = desiredSize; // Encrypt - size_t bufferSize = [paddedAttachmentData length] + kCCBlockSizeAES128; + size_t bufferSize; + ows_add_overflow(paddedAttachmentData.length, kCCBlockSizeAES128, &bufferSize); NSMutableData *_Nullable bufferData = [NSMutableData dataWithLength:bufferSize]; if (!bufferData) { OWSLogError(@"Failed to allocate buffer."); @@ -790,14 +802,22 @@ const NSUInteger kAES256_KeyByteLength = 32; + (nullable NSData *)decryptAESGCMWithProfileData:(NSData *)encryptedData key:(OWSAES256Key *)key { - OWSAssertDebug(encryptedData.length > kAESGCM256_IVLength + kAESGCM256_TagLength); - NSUInteger cipherTextLength = encryptedData.length - kAESGCM256_IVLength - kAESGCM256_TagLength; + NSUInteger cipherTextLength; + BOOL didOverflow + = __builtin_sub_overflow(encryptedData.length, (kAESGCM256_IVLength + kAESGCM256_TagLength), &cipherTextLength); + if (didOverflow) { + OWSFailDebug(@"unexpectedly short encryptedData.length: %lu", (unsigned long)encryptedData.length); + return nil; + } // encryptedData layout: initializationVector || ciphertext || authTag NSData *initializationVector = [encryptedData subdataWithRange:NSMakeRange(0, kAESGCM256_IVLength)]; 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 ciphertext:ciphertext diff --git a/SignalServiceKit/src/Util/FunctionalUtil.h b/SignalServiceKit/src/Util/FunctionalUtil.h index 02fcdeabe..021997a8f 100644 --- a/SignalServiceKit/src/Util/FunctionalUtil.h +++ b/SignalServiceKit/src/Util/FunctionalUtil.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // @interface NSArray (FunctionalUtil) @@ -19,15 +19,6 @@ /// Returns an array of all the results of passing items from this array through the given projection function. - (NSArray *)filter:(int (^)(id item))predicate; -/// Returns the sum of the doubles in this array of doubles. -- (double)sumDouble; - -/// Returns the sum of the unsigned integers in this array of unsigned integers. -- (NSUInteger)sumNSUInteger; - -/// Returns the sum of the integers in this array of integers. -- (NSInteger)sumNSInteger; - - (NSDictionary *)keyedBy:(id (^)(id))keySelector; - (NSDictionary *)groupBy:(id (^)(id value))keySelector; diff --git a/SignalServiceKit/src/Util/FunctionalUtil.m b/SignalServiceKit/src/Util/FunctionalUtil.m index a16fad521..a33279b79 100644 --- a/SignalServiceKit/src/Util/FunctionalUtil.m +++ b/SignalServiceKit/src/Util/FunctionalUtil.m @@ -75,27 +75,7 @@ } return r; } -- (double)sumDouble { - double s = 0.0; - for (NSNumber *e in self) { - s += [e doubleValue]; - } - return s; -} -- (NSUInteger)sumNSUInteger { - NSUInteger s = 0; - for (NSNumber *e in self) { - s += [e unsignedIntegerValue]; - } - return s; -} -- (NSInteger)sumNSInteger { - NSInteger s = 0; - for (NSNumber *e in self) { - s += [e integerValue]; - } - return s; -} + - (NSDictionary *)keyedBy:(id (^)(id value))keySelector { tskit_require(keySelector != nil);