Get SMK tests building and passing.
This commit is contained in:
parent
d9bbe67d0b
commit
3ccdadee4f
11
SignalMetadataKit/src/NSData+messagePadding.h
Normal file
11
SignalMetadataKit/src/NSData+messagePadding.h
Normal file
@ -0,0 +1,11 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
@interface NSData (messagePadding)
|
||||
|
||||
- (NSData *)removePadding;
|
||||
|
||||
- (NSData *)paddedMessageBody;
|
||||
|
||||
@end
|
||||
58
SignalMetadataKit/src/NSData+messagePadding.m
Normal file
58
SignalMetadataKit/src/NSData+messagePadding.m
Normal 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
|
||||
42
SignalMetadataKitTests/src/MessagePaddingTests.m
Normal file
42
SignalMetadataKitTests/src/MessagePaddingTests.m
Normal 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
|
||||
@ -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()
|
||||
|
||||
19
SignalMetadataKitTests/src/SMKObjCTest.m
Normal file
19
SignalMetadataKitTests/src/SMKObjCTest.m
Normal 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
|
||||
@ -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)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user