Get SMK tests building and passing.

This commit is contained in:
Matthew Chen 2018-09-25 13:56:06 -04:00
parent d9bbe67d0b
commit 3ccdadee4f
11 changed files with 158 additions and 176 deletions

View File

@ -0,0 +1,11 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
@interface NSData (messagePadding)
- (NSData *)removePadding;
- (NSData *)paddedMessageBody;
@end

View File

@ -0,0 +1,58 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "NSData+messagePadding.h"
@implementation NSData (messagePadding)
- (NSData *)removePadding {
unsigned long paddingStart = self.length;
Byte data[self.length];
[self getBytes:data length:self.length];
for (long i = (long)self.length - 1; i >= 0; i--) {
if (data[i] == (Byte)0x80) {
paddingStart = (unsigned long)i;
break;
} else if (data[i] != (Byte)0x00) {
OWSLogWarn(@"Failed to remove padding, returning unstripped padding");
return self;
}
}
return [self subdataWithRange:NSMakeRange(0, paddingStart)];
}
- (NSData *)paddedMessageBody {
// From
// https://github.com/signalapp/TextSecure/blob/master/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PushTransportDetails.java#L55
// NOTE: This is dumb. We have our own padding scheme, but so does the cipher.
// The +1 -1 here is to make sure the Cipher has room to add one padding byte,
// otherwise it'll add a full 16 extra bytes.
NSUInteger paddedMessageLength = [self paddedMessageLength:(self.length + 1)] - 1;
NSMutableData *paddedMessage = [NSMutableData dataWithLength:paddedMessageLength];
Byte paddingByte = 0x80;
[paddedMessage replaceBytesInRange:NSMakeRange(0, self.length) withBytes:[self bytes]];
[paddedMessage replaceBytesInRange:NSMakeRange(self.length, 1) withBytes:&paddingByte];
return paddedMessage;
}
- (NSUInteger)paddedMessageLength:(NSUInteger)messageLength {
NSUInteger messageLengthWithTerminator = messageLength + 1;
NSUInteger messagePartCount = messageLengthWithTerminator / 160;
if (messageLengthWithTerminator % 160 != 0) {
messagePartCount++;
}
return messagePartCount * 160;
}
@end

View File

@ -0,0 +1,42 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "NSData+messagePadding.h"
#import <SignalCoreKit/Cryptography.h>
#import <XCTest/XCTest.h>
@interface MessagePaddingTests : XCTestCase
@end
#pragma mark -
@implementation MessagePaddingTests
- (void)testV3Padding {
for (NSUInteger i=0;i<159;i++) {
NSData *data = [NSMutableData dataWithLength:i];
XCTAssertTrue([data paddedMessageBody].length == 159);
}
for (NSUInteger i=159;i<319;i++) {
NSData *data = [NSMutableData dataWithLength:i];
XCTAssertTrue([data paddedMessageBody].length == 319);
}
for (NSUInteger i=319;i<479;i++) {
NSData *data = [NSMutableData dataWithLength:i];
XCTAssertTrue([data paddedMessageBody].length == 479);
}
}
- (void)testV3RandomPadding{
for (int i = 0; i < 1000; i++) {
NSData *randomMessage = [Cryptography generateRandomBytes:501];
NSData *paddedMessage = [randomMessage paddedMessageBody];
XCTAssertTrue([[paddedMessage removePadding] isEqualToData:randomMessage]);
}
}
@end

View File

@ -2,7 +2,10 @@
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
class SMKTest: SignalBaseTest {
import XCTest
import SignalMetadataKit
class SMKTest: XCTestCase {
override func setUp() {
super.setUp()

View File

@ -0,0 +1,19 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <XCTest/XCTest.h>
NS_ASSUME_NONNULL_BEGIN
@interface SMKObjCTest : XCTestCase
@end
#pragma mark -
@implementation SMKObjCTest
@end
NS_ASSUME_NONNULL_END

View File

@ -2,9 +2,12 @@
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import XCTest
import SignalMetadataKit
// https://github.com/signalapp/libsignal-metadata-java/blob/master/tests/src/test/java/org/signal/libsignal/metadata/SecretSessionCipherTest.java
// public class SecretSessionCipherTest extends TestCase {
class SMKSecretSessionCipherTest: SignalBaseTest {
class SMKSecretSessionCipherTest: XCTestCase {
override func setUp() {
super.setUp()
@ -121,9 +124,10 @@ class SMKSecretSessionCipherTest: SignalBaseTest {
timestamp: 31335,
protocolContext: nil)
XCTFail("Decryption should have failed.")
} catch {
} catch _ as SMKCertificateError {
// Decryption is expected to fail.
XCTAssertTrue(error is SMKError)
} catch {
XCTFail("Unexpected error: \(error)")
}
}
@ -177,9 +181,10 @@ class SMKSecretSessionCipherTest: SignalBaseTest {
timestamp: 31338,
protocolContext: nil)
XCTFail("Decryption should have failed.")
} catch {
} catch _ as SMKCertificateError {
// Decryption is expected to fail.
XCTAssertTrue(error is SMKError)
} catch {
XCTFail("Unexpected error: \(error)")
}
}

View File

@ -2,10 +2,13 @@
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import XCTest
import SignalMetadataKit
// See: https://github.com/signalapp/libsignal-metadata-java/blob/master/tests/src/test/java/org/signal/libsignal/metadata/certificate/SenderCertificateTest.java
//
//public class SenderCertificateTest extends TestCase {
class SMKSenderCertificateTest: SignalBaseTest {
class SMKSenderCertificateTest: XCTestCase {
override func setUp() {
super.setUp()

View File

@ -2,12 +2,14 @@
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import XCTest
import SwiftProtobuf
import SignalMetadataKit
// See: https://github.com/signalapp/libsignal-metadata-java/blob/master/tests/src/test/java/org/signal/libsignal/metadata/certificate/ServerCertificateTest.java
//
// public class ServerCertificateTest extends TestCase {
class SMKServerCertificateTest: SignalBaseTest {
class SMKServerCertificateTest: XCTestCase {
override func setUp() {
super.setUp()

View File

@ -2,6 +2,9 @@
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import XCTest
import SignalMetadataKit
extension MutableCollection {
/// Shuffles the contents of this collection.
mutating func ows_shuffle() {
@ -28,7 +31,7 @@ extension Sequence {
// See: https://github.com/signalapp/libsignal-metadata-java/blob/master/tests/src/test/java/org/signal/libsignal/metadata/SessionCipherTest.java
// public class SessionCipherTest extends TestCase {
class SMKSessionCipherTest: SignalBaseTest {
class SMKSessionCipherTest: XCTestCase {
override func setUp() {
super.setUp()

View File

@ -1,167 +0,0 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
class SMKMiscTest: SignalBaseTest {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testECPrivateKey() {
let keyData = Randomness.generateRandomBytes(Int32(ECCKeyLength))!
let key = try! ECPrivateKey(keyData: keyData)
let key2 = try! ECPrivateKey(keyData: keyData)
XCTAssertEqual(key, key2)
}
func testECPublicKey() {
let keyData = Randomness.generateRandomBytes(Int32(ECCKeyLength))!
let key = try! ECPublicKey(rawKeyData: keyData)
XCTAssertEqual(key.keyData, keyData)
let serializedData = key.serialized
let parsedKey = try! ECPublicKey(serializedKeyData: serializedData)
XCTAssertEqual(parsedKey.keyData, keyData)
XCTAssertEqual(key, parsedKey)
}
func testUDMessage() {
let keyData = Randomness.generateRandomBytes(Int32(ECCKeyLength))!
let ephemeralKey = try! ECPublicKey(rawKeyData: keyData)
let encryptedStatic = Randomness.generateRandomBytes(100)!
let encryptedMessage = Randomness.generateRandomBytes(200)!
let message = SMKUnidentifiedSenderMessage(ephemeralKey: ephemeralKey,
encryptedStatic: encryptedStatic,
encryptedMessage: encryptedMessage)
let messageData = try! message.serialized()
let parsedMessage = try! SMKUnidentifiedSenderMessage.parse(dataAndPrefix: messageData)
XCTAssertEqual(message.cipherTextVersion, parsedMessage.cipherTextVersion)
XCTAssertEqual(message.ephemeralKey.keyData, parsedMessage.ephemeralKey.keyData)
XCTAssertEqual(message.encryptedStatic, parsedMessage.encryptedStatic)
XCTAssertEqual(message.encryptedMessage, parsedMessage.encryptedMessage)
}
func testUDServerCertificate() {
let keyId: UInt32 = 123
let key = try! ECPublicKey(rawKeyData: Randomness.generateRandomBytes(Int32(ECCKeyLength))!)
let signatureData = Randomness.generateRandomBytes(100)!
let serverCertificate = SMKServerCertificate(keyId: keyId,
key: key,
signatureData: signatureData)
let serializedData = try! serverCertificate.serialized()
let parsed = try! SMKServerCertificate.parse(data: serializedData)
XCTAssertEqual(serverCertificate.keyId, parsed.keyId)
XCTAssertEqual(serverCertificate.key, parsed.key)
XCTAssertEqual(serverCertificate.signatureData, parsed.signatureData)
}
func testUDSenderCertificate() {
let serverCertificate = SMKServerCertificate(keyId: 123,
key: try! ECPublicKey(rawKeyData: Randomness.generateRandomBytes(Int32(ECCKeyLength))!),
signatureData: Randomness.generateRandomBytes(100)!)
let key = try! ECPublicKey(rawKeyData: Randomness.generateRandomBytes(Int32(ECCKeyLength))!)
let senderDeviceId: UInt32 = 456
let senderRecipientId = "+13213214321"
let expirationTimestamp: UInt64 = 789
let signatureData = Randomness.generateRandomBytes(100)!
let senderCertificate = SMKSenderCertificate(serverCertificate: serverCertificate,
key: key,
senderDeviceId: senderDeviceId,
senderRecipientId: senderRecipientId,
expirationTimestamp: expirationTimestamp,
signatureData: signatureData)
let serializedData = try! senderCertificate.serialized()
let parsed = try! SMKSenderCertificate.parse(data: serializedData)
XCTAssertEqual(senderCertificate.serverCertificate, parsed.serverCertificate)
XCTAssertEqual(senderCertificate.key, parsed.key)
XCTAssertEqual(senderCertificate.senderDeviceId, parsed.senderDeviceId)
XCTAssertEqual(senderCertificate.senderRecipientId, parsed.senderRecipientId)
XCTAssertEqual(senderCertificate.expirationTimestamp, parsed.expirationTimestamp)
XCTAssertEqual(senderCertificate.signatureData, parsed.signatureData)
}
func testUDMessageContent() {
let serverCertificate = SMKServerCertificate(keyId: 123,
key: try! ECPublicKey(rawKeyData: Randomness.generateRandomBytes(Int32(ECCKeyLength))!),
signatureData: Randomness.generateRandomBytes(100)!)
let senderCertificate = SMKSenderCertificate(serverCertificate: serverCertificate,
key: try! ECPublicKey(rawKeyData: Randomness.generateRandomBytes(Int32(ECCKeyLength))!),
senderDeviceId: 456,
senderRecipientId: "+13213214321",
expirationTimestamp: 789,
signatureData: Randomness.generateRandomBytes(100)!)
let contentData = Randomness.generateRandomBytes(200)!
let message = SMKUnidentifiedSenderMessageContent(messageType: .whisper,
senderCertificate: senderCertificate,
contentData: contentData)
let messageData = try! message.serialized()
let parsed = try! SMKUnidentifiedSenderMessageContent.parse(data: messageData)
XCTAssertEqual(message.messageType, parsed.messageType)
XCTAssertEqual(message.senderCertificate, parsed.senderCertificate)
XCTAssertEqual(message.contentData, parsed.contentData)
}
func testUDSessionCipher_encrypt() {
// NOTE: We use MockClient to ensure consistency between of our session state.
let aliceMockClient = MockClient(recipientId: "+13213214321", deviceId: 456, registrationId: 123)
let bobMockClient = MockClient(recipientId: "+13213214322", deviceId: 321, registrationId: 512)
let certificateValidator = MockCertificateValidator()
let bobPrekey = bobMockClient.preKeyStore.createKey()
let bobSignedPrekey = bobMockClient.signedPreKeyStore.createKey()
let bobPreKeyBundle = PreKeyBundle(registrationId: bobMockClient.registrationId,
deviceId: bobMockClient.deviceId,
preKeyId: bobPrekey.id,
preKeyPublic: try! bobPrekey.keyPair.ecPublicKey().serialized,
signedPreKeyPublic: try! bobSignedPrekey.keyPair.ecPublicKey().serialized,
signedPreKeyId: bobSignedPrekey.id,
signedPreKeySignature: bobSignedPrekey.signature,
identityKey: try! bobMockClient.identityKeyPair.ecPublicKey().serialized)!
let aliceToBobSessionBuilder = aliceMockClient.createSessionBuilder(forRecipient: bobMockClient)
aliceToBobSessionBuilder.processPrekeyBundle(bobPreKeyBundle, protocolContext: nil)
let aliceToBobCipher = try! aliceMockClient.createSecretSessionCipher()
let plaintext = Randomness.generateRandomBytes(200)!
let paddedPlaintext = (plaintext as NSData).paddedMessageBody()!
let serverCertificate = SMKServerCertificate(keyId: 123,
key: try! ECPublicKey(rawKeyData: Randomness.generateRandomBytes(Int32(ECCKeyLength))!),
signatureData: Randomness.generateRandomBytes(100)!)
let senderCertificate = SMKSenderCertificate(serverCertificate: serverCertificate,
key: try! aliceMockClient.identityKeyPair.ecPublicKey(),
senderDeviceId: UInt32(aliceMockClient.deviceId),
senderRecipientId: aliceMockClient.recipientId,
expirationTimestamp: 789,
signatureData: Randomness.generateRandomBytes(100)!)
let encryptedMessage = try! aliceToBobCipher.encryptMessage(recipientId: bobMockClient.recipientId,
deviceId: bobMockClient.deviceId,
paddedPlaintext: paddedPlaintext, senderCertificate: senderCertificate, protocolContext: nil)
let messageTimestamp = NSDate.ows_millisecondTimeStamp()
let bobToAliceCipher = try! bobMockClient.createSecretSessionCipher()
let decryptedMessage = try! bobToAliceCipher.decryptMessage(certificateValidator: certificateValidator, cipherTextData: encryptedMessage, timestamp: messageTimestamp, protocolContext: nil)
let payload = (decryptedMessage.paddedPayload as NSData).removePadding()
XCTAssertEqual(aliceMockClient.recipientId, decryptedMessage.senderRecipientId)
XCTAssertEqual(aliceMockClient.deviceId, Int32(decryptedMessage.senderDeviceId))
XCTAssertEqual(plaintext, payload)
}
}

View File

@ -2,6 +2,9 @@
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
import SignalMetadataKit
class MockCertificateValidator: NSObject, SMKCertificateValidator {
@objc public func validate(senderCertificate: SMKSenderCertificate, validationTime: UInt64) throws {