|  |  |  | // | 
					
						
							|  |  |  | //  Copyright (c) 2017 Open Whisper Systems. All rights reserved. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #import <XCTest/XCTest.h> | 
					
						
							|  |  |  | #import "Cryptography.h" | 
					
						
							|  |  |  | #import "NSData+Base64.h" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | NS_ASSUME_NONNULL_BEGIN | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @interface Cryptography (TestingPrivateMethods) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + (nullable NSData *)decryptAESGCMWithInitializationVector:(NSData *)initializationVector | 
					
						
							|  |  |  |                                                 ciphertext:(NSData *)ciphertext | 
					
						
							|  |  |  |                                                    authTag:(NSData *)authTagFromEncrypt | 
					
						
							|  |  |  |                                                        key:(OWSAES256Key *)key; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @interface CryptographyTests : XCTestCase | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @interface Cryptography (Test) | 
					
						
							|  |  |  | + (NSData *)truncatedSHA256HMAC:(NSData *)dataToHMAC withHMACKey:(NSData *)HMACKey truncation:(int)bytes; | 
					
						
							|  |  |  | + (NSData *)encryptCBCMode:(NSData *)dataToEncrypt | 
					
						
							|  |  |  |                    withKey:(NSData *)key | 
					
						
							|  |  |  |                     withIV:(NSData *)iv | 
					
						
							|  |  |  |                withVersion:(NSData *)version | 
					
						
							|  |  |  |                withHMACKey:(NSData *)hmacKey | 
					
						
							|  |  |  |               withHMACType:(TSMACType)hmacType | 
					
						
							|  |  |  |               computedHMAC:(NSData **)hmac; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | + (NSData *)decryptCBCMode:(NSData *)dataToDecrypt | 
					
						
							|  |  |  |                        key:(NSData *)key | 
					
						
							|  |  |  |                         IV:(NSData *)iv | 
					
						
							|  |  |  |                    version:(NSData *)version | 
					
						
							|  |  |  |                    HMACKey:(NSData *)hmacKey | 
					
						
							|  |  |  |                   HMACType:(TSMACType)hmacType | 
					
						
							|  |  |  |               matchingHMAC:(NSData *)hmac; | 
					
						
							|  |  |  | @end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @implementation CryptographyTests | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testEncryptAttachmentData | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSString *plainText = @"SGF3YWlpIGlzIEF3ZXNvbWUh"; | 
					
						
							|  |  |  |     NSData *plainTextData = [NSData dataFromBase64String:plainText]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Sanity | 
					
						
							|  |  |  |     XCTAssertNotNil(plainTextData); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSData *generatedKey; | 
					
						
							|  |  |  |     NSData *generatedDigest; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSData *cipherText = | 
					
						
							|  |  |  |         [Cryptography encryptAttachmentData:plainTextData outKey:&generatedKey outDigest:&generatedDigest]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSData *decryptedData = [Cryptography decryptAttachment:cipherText withKey:generatedKey digest:generatedDigest]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTAssertEqualObjects(plainTextData, decryptedData); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testDecryptAttachmentWithBadKey | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     NSString *plainText = @"SGF3YWlpIGlzIEF3ZXNvbWUh"; | 
					
						
							|  |  |  |     NSData *plainTextData = [NSData dataFromBase64String:plainText]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Sanity | 
					
						
							|  |  |  |     XCTAssertNotNil(plainTextData); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSData *generatedKey; | 
					
						
							|  |  |  |     NSData *generatedDigest; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSData *cipherText = | 
					
						
							|  |  |  |         [Cryptography encryptAttachmentData:plainTextData outKey:&generatedKey outDigest:&generatedDigest]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSData *badKey = [Cryptography generateRandomBytes:64]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSData *decryptedData = [Cryptography decryptAttachment:cipherText withKey:badKey digest:generatedDigest]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTAssertNil(decryptedData); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testDecryptAttachmentWithBadDigest | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     NSString *plainText = @"SGF3YWlpIGlzIEF3ZXNvbWUh"; | 
					
						
							|  |  |  |     NSData *plainTextData = [NSData dataFromBase64String:plainText]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Sanity | 
					
						
							|  |  |  |     XCTAssertNotNil(plainTextData); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSData *generatedKey; | 
					
						
							|  |  |  |     NSData *generatedDigest; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSData *cipherText = | 
					
						
							|  |  |  |         [Cryptography encryptAttachmentData:plainTextData outKey:&generatedKey outDigest:&generatedDigest]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSData *badDigest = [Cryptography generateRandomBytes:32]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSData *decryptedData = [Cryptography decryptAttachment:cipherText withKey:generatedKey digest:badDigest]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     XCTAssertNil(decryptedData); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testComputeSHA256Digest | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     NSString *plainText = @"SGF3YWlpIGlzIEF3ZXNvbWUh"; | 
					
						
							|  |  |  |     NSData *plainTextData = [NSData dataFromBase64String:plainText]; | 
					
						
							|  |  |  |     NSData *digest = [Cryptography computeSHA256Digest:plainTextData]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const uint8_t expectedBytes[] = { | 
					
						
							|  |  |  |         0xba, 0x5f, 0xf1, 0x26, | 
					
						
							|  |  |  |         0x82, 0xbb, 0xb2, 0x51, | 
					
						
							|  |  |  |         0x8b, 0xe6, 0x06, 0x48, | 
					
						
							|  |  |  |         0xc5, 0x53, 0xd0, 0xa2, | 
					
						
							|  |  |  |         0xbf, 0x71, 0xf1, 0xec, | 
					
						
							|  |  |  |         0xb4, 0xdb, 0x02, 0x12, | 
					
						
							|  |  |  |         0x5f, 0x80, 0xea, 0x34, | 
					
						
							|  |  |  |         0xc9, 0x8d, 0xee, 0x1f | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSData *expectedDigest = [NSData dataWithBytes:expectedBytes length:32]; | 
					
						
							|  |  |  |     XCTAssertEqualObjects(expectedDigest, digest); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     NSData *expectedTruncatedDigest = [NSData dataWithBytes:expectedBytes length:10]; | 
					
						
							|  |  |  |     NSData *truncatedDigest = [Cryptography computeSHA256Digest:plainTextData truncatedToBytes:10]; | 
					
						
							|  |  |  |     XCTAssertEqualObjects(expectedTruncatedDigest, truncatedDigest); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | - (void)testGCMRoundTrip | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     NSData *plainTextData = [@"Super🔥secret🔥test🔥data🏁🏁" dataUsingEncoding:NSUTF8StringEncoding]; | 
					
						
							|  |  |  |     // Sanity Check | 
					
						
							|  |  |  |     XCTAssertEqual(39, plainTextData.length); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     OWSAES256Key *key = [OWSAES256Key new]; | 
					
						
							|  |  |  |     NSData *_Nullable encryptedData = [Cryptography encryptAESGCMWithData:plainTextData key:key]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const NSUInteger ivLength = 12; | 
					
						
							|  |  |  |     const NSUInteger tagLength = 16; | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     XCTAssertEqual(ivLength + plainTextData.length + tagLength, encryptedData.length); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     OWSAES256Key *key = [OWSAES256Key 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 |