Adopt SignalClient for en/decryption in SMKSecretSessionCipher

This commit is contained in:
Jordan Rose 2020-11-10 17:03:06 -08:00
parent 50fbd4fd21
commit 07fbbfbedc
7 changed files with 250 additions and 534 deletions

View File

@ -46,6 +46,8 @@ A Swift & Objective-C library used by other Signal libraries.
s.dependency 'SignalCoreKit'
s.dependency 'SwiftProtobuf'
s.dependency 'SignalClient'
s.test_spec 'Tests' do |test_spec|
test_spec.source_files = 'SignalMetadataKitTests/src/**/*.{h,m,swift}'
end

View File

@ -1,7 +1,11 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
//
#import <Foundation/NSData.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSData (messagePadding)
- (NSData *)removePadding;
@ -9,3 +13,5 @@
- (NSData *)paddedMessageBody;
@end
NS_ASSUME_NONNULL_END

View File

@ -5,7 +5,7 @@
import Foundation
import AxolotlKit
import Curve25519Kit
import HKDFKit
import SignalClient
@objc
public class SecretSessionKnownSenderError: NSObject, CustomNSError {
@ -124,22 +124,51 @@ public class SMKDecryptResult: NSObject {
// MARK: -
fileprivate extension ProtocolAddress {
convenience init(from recipientAddress: SMKAddress, deviceId: UInt32) throws {
try self.init(name: recipientAddress.uuid?.uuidString ?? recipientAddress.e164!, deviceId: deviceId)
}
convenience init(from senderAddress: SealedSenderAddress) throws {
try self.init(name: senderAddress.uuidString ?? senderAddress.e164!, deviceId: senderAddress.deviceId)
}
}
fileprivate extension SMKAddress {
init(_ address: SealedSenderAddress) {
try! self.init(uuid: address.uuidString.flatMap(UUID.init(uuidString:)), e164: address.e164)
}
}
fileprivate extension SMKMessageType {
init(_ messageType: CiphertextMessage.MessageType) {
switch messageType {
case .whisper:
self = .whisper
case .preKey:
self = .prekey
default:
fatalError("not ready for other kinds of messages yet")
}
}
}
@objc public class SMKSecretSessionCipher: NSObject {
private let kUDPrefixString = "UnidentifiedDelivery"
private let kSMKSecretSessionCipherMacLength: UInt = 10
private let sessionStore: SessionStore
private let preKeyStore: PreKeyStore
private let signedPreKeyStore: SignedPreKeyStore
private let identityStore: IdentityKeyStore
private let sessionStore: SignalClient.SessionStore
private let preKeyStore: SignalClient.PreKeyStore
private let signedPreKeyStore: SignalClient.SignedPreKeyStore
private let identityStore: SignalClient.IdentityKeyStore
// public SecretSessionCipher(SignalProtocolStore signalProtocolStore) {
@objc public init(sessionStore: SessionStore,
preKeyStore: PreKeyStore,
signedPreKeyStore: SignedPreKeyStore,
identityStore: IdentityKeyStore) throws {
public init(sessionStore: SignalClient.SessionStore,
preKeyStore: SignalClient.PreKeyStore,
signedPreKeyStore: SignalClient.SignedPreKeyStore,
identityStore: SignalClient.IdentityKeyStore) throws {
self.sessionStore = sessionStore
self.preKeyStore = preKeyStore
@ -147,346 +176,97 @@ public class SMKDecryptResult: NSObject {
self.identityStore = identityStore
}
@objc(initWithSessionStore:preKeyStore:signedPreKeyStore:identityStore:error:)
public convenience init(transitionalSessionStore sessionStore: AxolotlKit.SessionStore,
preKeyStore: AxolotlKit.PreKeyStore,
signedPreKeyStore: AxolotlKit.SignedPreKeyStore,
identityStore: AxolotlKit.IdentityKeyStore) throws {
try self.init(
sessionStore: sessionStore as! SignalClient.SessionStore,
preKeyStore: preKeyStore as! SignalClient.PreKeyStore,
signedPreKeyStore: signedPreKeyStore as! SignalClient.SignedPreKeyStore,
identityStore: identityStore as! SignalClient.IdentityKeyStore)
}
// MARK: - Public
// public byte[] encrypt(SignalProtocolAddress destinationAddress, SenderCertificate senderCertificate, byte[] paddedPlaintext)
@objc
public func throwswrapped_encryptMessage(recipientId: String,
public func throwswrapped_encryptMessage(recipient: SMKAddress,
deviceId: Int32,
paddedPlaintext: Data,
senderCertificate: SMKSenderCertificate,
protocolContext: SPKProtocolWriteContext?) throws -> Data {
guard recipientId.count > 0 else {
throw SMKError.assertionError(description: "\(SMKSecretSessionCipher.logTag) invalid recipientId")
}
guard deviceId > 0 else {
throw SMKError.assertionError(description: "\(SMKSecretSessionCipher.logTag) invalid deviceId")
}
// CiphertextMessage message = new SessionCipher(signalProtocolStore, destinationAddress).encrypt(paddedPlaintext);
let cipher = SessionCipher(sessionStore: sessionStore,
preKeyStore: preKeyStore,
signedPreKeyStore: signedPreKeyStore,
identityKeyStore: identityStore,
recipientId: recipientId,
deviceId: deviceId)
let encryptedMessage = try cipher.encryptMessage(paddedPlaintext, protocolContext: protocolContext)
// IdentityKeyPair ourIdentity = signalProtocolStore.getIdentityKeyPair();
guard let ourIdentityKeyPair = identityStore.identityKeyPair(protocolContext) else {
throw SMKError.assertionError(description: "\(logTag) Missing our identity key pair.")
}
// ECPublicKey theirIdentity = signalProtocolStore.getIdentity(destinationAddress).getPublicKey();
guard let theirIdentityKeyData = identityStore.identityKey(forRecipientId: recipientId, protocolContext: protocolContext) else {
throw SMKError.assertionError(description: "\(logTag) Missing their public identity key.")
}
// NOTE: we don't use ECPublicKey(serializedKeyData) since `theirIdentityKeyData` doesn't
// have a type byte.
let theirIdentityKey = try ECPublicKey(keyData: theirIdentityKeyData)
// ECKeyPair ephemeral = Curve.generateKeyPair();
let ephemeral = Curve25519.generateKeyPair()
// byte[] ephemeralSalt = ByteUtil.combine("UnidentifiedDelivery".getBytes(), theirIdentity.serialize(), ephemeral.getPublicKey().serialize());
guard let prefixData = kUDPrefixString.data(using: String.Encoding.utf8) else {
throw SMKError.assertionError(description: "\(logTag) Could not encode prefix.")
}
let ephemeralSalt = NSData.join([
prefixData,
theirIdentityKey.serialized,
try ephemeral.ecPublicKey().serialized
])
// EphemeralKeys ephemeralKeys = calculateEphemeralKeys(theirIdentity, ephemeral.getPrivateKey(), ephemeralSalt);
let ephemeralKeys = try throwswrapped_calculateEphemeralKeys(ephemeralPublicKey: theirIdentityKey,
ephemeralPrivateKey: ephemeral.ecPrivateKey(),
salt: ephemeralSalt)
// byte[] staticKeyCiphertext = encrypt(ephemeralKeys.cipherKey, ephemeralKeys.macKey, ourIdentity.getPublicKey().getPublicKey().serialize());
let staticKeyCipherData = try encrypt(cipherKey: ephemeralKeys.cipherKey,
macKey: ephemeralKeys.macKey,
plaintextData: ourIdentityKeyPair.ecPublicKey().serialized)
// byte[] staticSalt = ByteUtil.combine(ephemeralKeys.chainKey, staticKeyCiphertext);
let staticSalt = NSData.join([
ephemeralKeys.chainKey,
staticKeyCipherData
])
// StaticKeys staticKeys = calculateStaticKeys(theirIdentity, ourIdentity.getPrivateKey(), staticSalt);
let staticKeys = try throwswrapped_calculateStaticKeys(staticPublicKey: theirIdentityKey,
staticPrivateKey: ourIdentityKeyPair.ecPrivateKey(),
salt: staticSalt)
// UnidentifiedSenderMessageContent content = new UnidentifiedSenderMessageContent(message.getType(), senderCertificate, message.serialize());
var messageType: SMKMessageType
switch encryptedMessage.cipherMessageType {
case .prekey:
messageType = .prekey
case .whisper:
messageType = .whisper
default:
throw SMKError.assertionError(description: "\(logTag) Unknown cipher message type.")
}
guard let encryptedMessageData = encryptedMessage.serialized() else {
throw SMKError.assertionError(description: "\(logTag) Could not serialize encrypted message.")
}
let messageContent = try SMKUnidentifiedSenderMessageContent(messageType: messageType,
senderCertificate: senderCertificate,
contentData: encryptedMessageData)
// byte[] messageBytes = encrypt(staticKeys.cipherKey, staticKeys.macKey, content.getSerialized());
let messageData = try encrypt(cipherKey: staticKeys.cipherKey,
macKey: staticKeys.macKey,
plaintextData: messageContent.serializedData)
// return new UnidentifiedSenderMessage(ephemeral.getPublicKey(), staticKeyCiphertext, messageBytes).getSerialized();
let message = try SMKUnidentifiedSenderMessage(ephemeralKey: try ephemeral.ecPublicKey(),
encryptedStatic: staticKeyCipherData,
encryptedMessage: messageData)
return message.serializedData
let recipientAddress = try ProtocolAddress(from: recipient, deviceId: UInt32(bitPattern: deviceId))
var protocolContextAsPtr = protocolContext
return Data(try sealedSenderEncrypt(message: paddedPlaintext,
for: recipientAddress,
from: SenderCertificate(senderCertificate.serializedData),
sessionStore: sessionStore,
identityStore: identityStore,
context: &protocolContextAsPtr))
}
// public Pair<SignalProtocolAddress, byte[]> decrypt(CertificateValidator validator, byte[] ciphertext, long timestamp)
// throws InvalidMetadataMessageException, InvalidMetadataVersionException, ProtocolInvalidMessageException, ProtocolInvalidKeyException, ProtocolNoSessionException, ProtocolLegacyMessageException, ProtocolInvalidVersionException, ProtocolDuplicateMessageException, ProtocolInvalidKeyIdException, ProtocolUntrustedIdentityException
@objc
public func throwswrapped_decryptMessage(certificateValidator: SMKCertificateValidator,
cipherTextData: Data,
timestamp: UInt64,
localE164: String?,
localUuid: UUID?,
localDeviceId: Int32,
protocolContext: SPKProtocolWriteContext?) throws -> SMKDecryptResult {
cipherTextData: Data,
timestamp: UInt64,
localE164: String?,
localUuid: UUID?,
localDeviceId: Int32,
protocolContext: SPKProtocolWriteContext?) throws -> SMKDecryptResult {
guard timestamp > 0 else {
throw SMKError.assertionError(description: "\(logTag) invalid timestamp")
}
// IdentityKeyPair ourIdentity = signalProtocolStore.getIdentityKeyPair();
var protocolContextAsPtr = protocolContext
let messageContent = try UnidentifiedSenderMessageContent(message: cipherTextData,
identityStore: self.identityStore,
context: &protocolContextAsPtr)
let senderAddress = try messageContent.senderCertificate().sender()
let localAddress = try SMKAddress(uuid: localUuid, e164: localE164)
guard let ourIdentityKeyPair = identityStore.identityKeyPair(protocolContext) else {
throw SMKError.assertionError(description: "\(logTag) Missing our identity key pair.")
}
// UnidentifiedSenderMessage wrapper = new UnidentifiedSenderMessage(ciphertext);
let wrapper = try SMKUnidentifiedSenderMessage(serializedData: cipherTextData)
// byte[] ephemeralSalt = ByteUtil.combine("UnidentifiedDelivery".getBytes(), ourIdentity.getPublicKey().getPublicKey().serialize(), wrapper.getEphemeral().serialize());
guard let prefixData = kUDPrefixString.data(using: String.Encoding.utf8) else {
throw SMKError.assertionError(description: "\(logTag) Could not encode prefix.")
}
let ephemeralSalt = NSData.join([
prefixData,
try ourIdentityKeyPair.ecPublicKey().serialized,
wrapper.ephemeralKey.serialized
])
// EphemeralKeys ephemeralKeys = calculateEphemeralKeys(wrapper.getEphemeral(), ourIdentity.getPrivateKey(), ephemeralSalt);
let ephemeralKeys = try throwswrapped_calculateEphemeralKeys(ephemeralPublicKey: wrapper.ephemeralKey,
ephemeralPrivateKey: ourIdentityKeyPair.ecPrivateKey(),
salt: ephemeralSalt)
// byte[] staticKeyBytes = decrypt(ephemeralKeys.cipherKey, ephemeralKeys.macKey, wrapper.getEncryptedStatic());
let staticKeyBytes = try decrypt(cipherKey: ephemeralKeys.cipherKey,
macKey: ephemeralKeys.macKey,
cipherTextWithMac: wrapper.encryptedStatic)
// ECPublicKey staticKey = Curve.decodePoint(staticKeyBytes, 0);
let staticKey = try ECPublicKey(serializedKeyData: staticKeyBytes)
// byte[] staticSalt = ByteUtil.combine(ephemeralKeys.chainKey, wrapper.getEncryptedStatic());
let staticSalt = NSData.join([ephemeralKeys.chainKey, wrapper.encryptedStatic])
// StaticKeys staticKeys = calculateStaticKeys(staticKey, ourIdentity.getPrivateKey(), staticSalt);
let staticKeys = try throwswrapped_calculateStaticKeys(staticPublicKey: staticKey,
staticPrivateKey: ourIdentityKeyPair.ecPrivateKey(),
salt: staticSalt)
// byte[] messageBytes = decrypt(staticKeys.cipherKey, staticKeys.macKey, wrapper.getEncryptedMessage());
let messageBytes = try decrypt(cipherKey: staticKeys.cipherKey,
macKey: staticKeys.macKey,
cipherTextWithMac: wrapper.encryptedMessage)
// content = new UnidentifiedSenderMessageContent(messageBytes);
let messageContent = try SMKUnidentifiedSenderMessageContent(serializedData: messageBytes)
let senderAddress = messageContent.senderCertificate.senderAddress
let senderDeviceId = messageContent.senderCertificate.senderDeviceId
guard !senderAddress.matches(localAddress) || senderDeviceId != localDeviceId else {
guard !SMKAddress(senderAddress).matches(localAddress) ||
Int32(bitPattern: senderAddress.deviceId) != localDeviceId else {
Logger.info("Discarding self-sent message")
throw SMKSecretSessionCipherError.selfSentMessage
}
// validator.validate(content.getSenderCertificate(), timestamp);
let wrapAsKnownSenderError = { (underlyingError: Error) in
return SecretSessionKnownSenderError(senderAddress: senderAddress, senderDeviceId: senderDeviceId, underlyingError: underlyingError)
}
do {
try certificateValidator.throwswrapped_validate(senderCertificate: messageContent.senderCertificate,
validationTime: timestamp)
let certificateData = Data(try! messageContent.senderCertificate().serialize())
try certificateValidator.throwswrapped_validate(
senderCertificate: try! SMKSenderCertificate(serializedData: certificateData),
validationTime: timestamp)
let paddedMessagePlaintext = try throwswrapped_decrypt(messageContent: messageContent,
protocolContext: protocolContext)
// return new Pair<>(new SignalProtocolAddress(content.getSenderCertificate().getSender(),
// content.getSenderCertificate().getSenderDeviceId()),
// decrypt(content));
//
// NOTE: We use the sender properties from the sender certificate, not from this class' properties.
guard senderAddress.deviceId <= INT_MAX else {
throw SMKError.assertionError(description: "\(logTag) Invalid senderDeviceId.")
}
return SMKDecryptResult(senderAddress: SMKAddress(senderAddress),
senderDeviceId: Int(senderAddress.deviceId),
paddedPayload: Data(paddedMessagePlaintext),
messageType: SMKMessageType(try messageContent.messageType()))
} catch {
throw wrapAsKnownSenderError(error)
throw SecretSessionKnownSenderError(senderAddress: SMKAddress(senderAddress),
senderDeviceId: senderAddress.deviceId,
underlyingError: error)
}
// if (!MessageDigest.isEqual(content.getSenderCertificate().getKey().serialize(), staticKeyBytes)) {
// throw new InvalidKeyException("Sender's certificate key does not match key used in message");
// }
//
// NOTE: Constant time comparison.
let certificateKeyBytes = Data(try! messageContent.senderCertificate.key.serialize())
guard certificateKeyBytes.ows_constantTimeIsEqual(to: staticKeyBytes) else {
let underlyingError = SMKError.assertionError(description: "\(logTag) Sender's certificate key does not match key used in message.")
throw wrapAsKnownSenderError(underlyingError)
}
let paddedMessagePlaintext: Data
do {
paddedMessagePlaintext = try throwswrapped_decrypt(messageContent: messageContent, protocolContext: protocolContext)
} catch {
throw wrapAsKnownSenderError(error)
}
// return new Pair<>(new SignalProtocolAddress(content.getSenderCertificate().getSender(),
// content.getSenderCertificate().getSenderDeviceId()),
// decrypt(content));
//
// NOTE: We use the sender properties from the sender certificate, not from this class' properties.
guard senderDeviceId >= 0 && senderDeviceId <= INT_MAX else {
let underlyingError = SMKError.assertionError(description: "\(logTag) Invalid senderDeviceId.")
throw wrapAsKnownSenderError(underlyingError)
}
return SMKDecryptResult(senderAddress: senderAddress,
senderDeviceId: Int(senderDeviceId),
paddedPayload: paddedMessagePlaintext,
messageType: messageContent.messageType)
}
// MARK: - Encrypt
// private EphemeralKeys calculateEphemeralKeys(ECPublicKey ephemeralPublic, ECPrivateKey ephemeralPrivate, byte[] salt)
// throws InvalidKeyException {
private func throwswrapped_calculateEphemeralKeys(ephemeralPublicKey: ECPublicKey,
ephemeralPrivateKey: ECPrivateKey,
salt: Data) throws -> SMKEphemeralKeys {
guard ephemeralPublicKey.keyData.count > 0 else {
throw SMKError.assertionError(description: "\(logTag) invalid ephemeralPublicKey")
}
guard ephemeralPrivateKey.keyData.count > 0 else {
throw SMKError.assertionError(description: "\(logTag) invalid ephemeralPrivateKey")
}
guard salt.count > 0 else {
throw SMKError.assertionError(description: "\(logTag) invalid salt")
}
// byte[] ephemeralSecret = Curve.calculateAgreement(ephemeralPublic, ephemeralPrivate);
//
// See:
// https://github.com/signalapp/libsignal-protocol-java/blob/master/java/src/main/java/org/whispersystems/libsignal/ecc/Curve.java#L30
let ephemeralSecret = try Curve25519.generateSharedSecret(fromPublicKey: ephemeralPublicKey.keyData, privateKey: ephemeralPrivateKey.keyData)
// byte[] ephemeralDerived = new HKDFv3().deriveSecrets(ephemeralSecret, salt, new byte[0], 96);
let kEphemeralDerivedLength: UInt = 96
let ephemeralDerived: Data =
try HKDFKit.deriveKey(ephemeralSecret, info: Data(), salt: salt, outputSize: Int32(kEphemeralDerivedLength))
guard ephemeralDerived.count == kEphemeralDerivedLength else {
throw SMKError.assertionError(description: "\(logTag) derived ephemeral has unexpected length: \(ephemeralDerived.count).")
}
let ephemeralDerivedParser = OWSDataParser(data: ephemeralDerived)
let chainKey = try ephemeralDerivedParser.nextData(length: 32, name: "chain key")
let cipherKey = try ephemeralDerivedParser.nextData(length: 32, name: "cipher key")
let macKey = try ephemeralDerivedParser.nextData(length: 32, name: "mac key")
guard ephemeralDerivedParser.isEmpty else {
throw SMKError.assertionError(description: "\(logTag) could not parse derived ephemeral.")
}
return SMKEphemeralKeys(chainKey: chainKey, cipherKey: cipherKey, macKey: macKey)
}
// private StaticKeys calculateStaticKeys(ECPublicKey staticPublic, ECPrivateKey staticPrivate, byte[] salt) throws
// InvalidKeyException {
private func throwswrapped_calculateStaticKeys(staticPublicKey: ECPublicKey,
staticPrivateKey: ECPrivateKey,
salt: Data) throws -> SMKStaticKeys {
guard staticPublicKey.keyData.count > 0 else {
throw SMKError.assertionError(description: "\(logTag) invalid staticPublicKey")
}
guard staticPrivateKey.keyData.count > 0 else {
throw SMKError.assertionError(description: "\(logTag) invalid staticPrivateKey")
}
guard salt.count > 0 else {
throw SMKError.assertionError(description: "\(logTag) invalid salt")
}
// byte[] staticSecret = Curve.calculateAgreement(staticPublic, staticPrivate);
//
// See:
// https://github.com/signalapp/libsignal-protocol-java/blob/master/java/src/main/java/org/whispersystems/libsignal/ecc/Curve.java#L30
let staticSecret = try Curve25519.generateSharedSecret(fromPublicKey: staticPublicKey.keyData, privateKey: staticPrivateKey.keyData)
// byte[] staticDerived = new HKDFv3().deriveSecrets(staticSecret, salt, new byte[0], 96);
let kStaticDerivedLength: UInt = 96
let staticDerived: Data =
try HKDFKit.deriveKey(staticSecret, info: Data(), salt: salt, outputSize: Int32(kStaticDerivedLength))
guard staticDerived.count == kStaticDerivedLength else {
throw SMKError.assertionError(description: "\(logTag) could not derive static.")
}
// byte[][] staticDerivedParts = ByteUtil.split(staticDerived, 32, 32, 32);
let staticDerivedParser = OWSDataParser(data: staticDerived)
// NOTE: javalib doesn't use the first 32 bytes.
_ = try staticDerivedParser.nextData(length: 32)
let cipherKey = try staticDerivedParser.nextData(length: 32)
let macKey = try staticDerivedParser.nextData(length: 32)
guard staticDerivedParser.isEmpty else {
throw SMKError.assertionError(description: "\(logTag) invalid derived static.")
}
// return new StaticKeys(staticDerivedParts[1], staticDerivedParts[2]);
return SMKStaticKeys(cipherKey: cipherKey, macKey: macKey)
}
// private byte[] encrypt(SecretKeySpec cipherKey, SecretKeySpec macKey, byte[] plaintext) {
private func encrypt(cipherKey: SMKSecretKeySpec,
macKey: SMKSecretKeySpec,
plaintextData: Data) throws -> Data {
// Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
// cipher.init(Cipher.ENCRYPT_MODE, cipherKey, new IvParameterSpec(new byte[16]));
// byte[] ciphertext = cipher.doFinal(plaintext);
guard let aesKey = OWSAES256Key(data: cipherKey.keyData) else {
throw SMKError.assertionError(description: "\(logTag) Invalid encryption key.")
}
// NOTE: The IV is all zeroes. This is fine since we're using a unique key.
let initializationVector = Data(count: Int(kAES256CTR_IVLength))
guard let encryptionResult = Cryptography.encryptAESCTR(plaintextData: plaintextData, initializationVector: initializationVector, key: aesKey) else {
throw SMKError.assertionError(description: "\(logTag) Could not encrypt data.")
}
let cipherText = encryptionResult.ciphertext
// Mac mac = Mac.getInstance("HmacSHA256");
// mac.init(macKey);
//
// byte[] ourFullMac = mac.doFinal(ciphertext);
// byte[] ourMac = ByteUtil.trim(ourFullMac, 10);
guard let ourMac = Cryptography.truncatedSHA256HMAC(cipherText, withHMACKey: macKey.keyData, truncation: 10) else {
throw SMKError.assertionError(description: "\(logTag) Could not compute HmacSHA256.")
}
// return ByteUtil.combine(ciphertext, ourMac);
let result = NSData.join([cipherText, ourMac])
return result
}
var accountIdFinder: SMKAccountIdFinder {
return SMKEnvironment.shared.accountIdFinder
}
// MARK: - Decrypt
@ -494,16 +274,15 @@ public class SMKDecryptResult: NSObject {
// private byte[] decrypt(UnidentifiedSenderMessageContent message)
// throws InvalidVersionException, InvalidMessageException, InvalidKeyException, DuplicateMessageException,
// InvalidKeyIdException, UntrustedIdentityException, LegacyMessageException, NoSessionException
private func throwswrapped_decrypt(messageContent: SMKUnidentifiedSenderMessageContent,
private func throwswrapped_decrypt(messageContent: UnidentifiedSenderMessageContent,
protocolContext: SPKProtocolWriteContext?) throws -> Data {
// SignalProtocolAddress sender = new SignalProtocolAddress(message.getSenderCertificate().getSender(),
// message.getSenderCertificate().getSenderDeviceId());
//
// NOTE: We use the sender properties from the sender certificate, not from this class' properties.
let senderAddress = messageContent.senderCertificate.senderAddress
let senderDeviceId = messageContent.senderCertificate.senderDeviceId
guard senderDeviceId >= 0 && senderDeviceId <= INT32_MAX else {
let sender = try! messageContent.senderCertificate().sender()
guard sender.deviceId >= 0 && sender.deviceId <= INT32_MAX else {
throw SMKError.assertionError(description: "\(logTag) Invalid senderDeviceId.")
}
@ -513,86 +292,31 @@ public class SMKDecryptResult: NSObject {
// SessionCipher(signalProtocolStore, sender).decrypt(new PreKeySignalMessage(message.getContent())); default: throw
// new InvalidMessageException("Unknown type: " + message.getType());
// }
var cipherMessage: CipherMessage
switch (messageContent.messageType) {
var protocolContextAsPtr = protocolContext
let plaintextData: [UInt8]
switch try messageContent.messageType() {
case .whisper:
cipherMessage = try WhisperMessage(data: messageContent.contentData)
case .prekey:
cipherMessage = try PreKeyWhisperMessage(data: messageContent.contentData)
let cipherMessage = try SignalMessage(bytes: messageContent.contents())
plaintextData = try signalDecrypt(
message: cipherMessage,
from: ProtocolAddress(from: sender),
sessionStore: sessionStore,
identityStore: identityStore,
context: &protocolContextAsPtr)
case .preKey:
let cipherMessage = try PreKeySignalMessage(bytes: messageContent.contents())
plaintextData = try signalDecryptPreKey(
message: cipherMessage,
from: ProtocolAddress(from: sender),
sessionStore: sessionStore,
identityStore: identityStore,
preKeyStore: preKeyStore,
signedPreKeyStore: signedPreKeyStore,
context: &protocolContextAsPtr)
case let unknownType:
throw SMKError.assertionError(
description: "\(logTag) Not prepared to handle this message type: \(unknownType.rawValue)")
}
guard let accountId = accountIdFinder.accountId(forUuid: senderAddress.uuid, phoneNumber: senderAddress.e164, protocolContext: protocolContext) else {
throw SMKError.assertionError(description: "\(logTag) accountId was unexpectedly nil")
}
let cipher = SessionCipher(sessionStore: sessionStore,
preKeyStore: preKeyStore,
signedPreKeyStore: signedPreKeyStore,
identityKeyStore: identityStore,
recipientId: accountId,
deviceId: Int32(senderDeviceId))
let plaintextData = try cipher.decrypt(cipherMessage, protocolContext: protocolContext)
return plaintextData
}
// private byte[] decrypt(SecretKeySpec cipherKey, SecretKeySpec macKey, byte[] ciphertext) throws InvalidMacException {
private func decrypt(cipherKey: SMKSecretKeySpec,
macKey: SMKSecretKeySpec,
cipherTextWithMac: Data) throws -> Data {
// if (ciphertext.count < 10) {
// throw new InvalidMacException("Ciphertext not long enough for MAC!");
// }
if (cipherTextWithMac.count < kSMKSecretSessionCipherMacLength) {
throw SMKError.assertionError(description: "\(logTag) Cipher text not long enough for MAC.")
}
// byte[][] ciphertextParts = ByteUtil.split(ciphertext, ciphertext.count - 10, 10);
let cipherTextWithMacParser = OWSDataParser(data: cipherTextWithMac)
let cipherTextLength = UInt(cipherTextWithMac.count) - kSMKSecretSessionCipherMacLength
let cipherText = try cipherTextWithMacParser.nextData(length: cipherTextLength, name: "cipher text")
let theirMac = try cipherTextWithMacParser.nextData(length: kSMKSecretSessionCipherMacLength, name: "their mac")
guard cipherTextWithMacParser.isEmpty else {
throw SMKError.assertionError(description: "\(logTag) Could not parse cipher text.")
}
// Mac mac = Mac.getInstance("HmacSHA256");
// mac.init(macKey);
//
// byte[] digest = mac.doFinal(ciphertextParts[0]);
guard let ourFullMac = Cryptography.computeSHA256HMAC(cipherText, withHMACKey: macKey.keyData) else {
throw SMKError.assertionError(description: "\(logTag) Could not compute HmacSHA256.")
}
// byte[] ourMac = ByteUtil.trim(digest, 10);
guard ourFullMac.count >= kSMKSecretSessionCipherMacLength else {
throw SMKError.assertionError(description: "\(logTag) HmacSHA256 has unexpected length.")
}
let ourMac = ourFullMac[0..<kSMKSecretSessionCipherMacLength]
// if (!MessageDigest.isEqual(ourMac, theirMac)) {
// throw new InvalidMacException("Bad mac!");
// }
//
// NOTE: Constant time comparison.
guard ourMac.ows_constantTimeIsEqual(to: theirMac) else {
throw SMKError.assertionError(description: "\(logTag) macs do not match.")
}
// Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
// cipher.init(Cipher.DECRYPT_MODE, cipherKey, new IvParameterSpec(new byte[16]));
guard let aesKey = OWSAES256Key(data: cipherKey.keyData) else {
throw SMKError.assertionError(description: "\(logTag) could not parse AES256 key.")
}
// NOTE: The IV is all zeroes. This is fine since we're using a unique key.
let initializationVector = Data(count: Int(kAES256CTR_IVLength))
guard let plaintext = Cryptography.decryptAESCTR(cipherText: cipherText, initializationVector: initializationVector, key: aesKey) else {
throw SMKError.assertionError(description: "\(logTag) could not decrypt AESGCM.")
}
return plaintext
return Data(plaintextData)
}
}

View File

@ -6,7 +6,7 @@ import XCTest
import SignalMetadataKit
import SignalCoreKit
import Curve25519Kit
import AxolotlKit
import SignalClient
class SMKTest: XCTestCase {
@ -108,27 +108,14 @@ class SMKTest: XCTestCase {
let certificateValidator = MockCertificateValidator()
let bobPrekey = bobMockClient.generateMockPreKey()
let bobSignedPrekey = bobMockClient.generateMockSignedPreKey()
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)
try! aliceToBobSessionBuilder.processPrekeyBundle(bobPreKeyBundle, protocolContext: nil)
aliceMockClient.initializeSession(with: bobMockClient)
let aliceToBobCipher = try! aliceMockClient.createSecretSessionCipher()
let plaintext = Randomness.generateRandomBytes(200)
let paddedPlaintext = (plaintext as NSData).paddedMessageBody()!
let paddedPlaintext = (plaintext as NSData).paddedMessageBody()
let senderCertificate = try! SMKSenderCertificate(serializedData: try! buildSenderCertificateProto(senderClient: aliceMockClient).serializedData())
let encryptedMessage = try! aliceToBobCipher.throwswrapped_encryptMessage(recipientId: bobMockClient.accountId,
let encryptedMessage = try! aliceToBobCipher.throwswrapped_encryptMessage(recipient: bobMockClient.address,
deviceId: bobMockClient.deviceId,
paddedPlaintext: paddedPlaintext,
senderCertificate: senderCertificate,
@ -170,23 +157,23 @@ class SMKTest: XCTestCase {
let senderAddress: SMKAddress
let senderDevice: UInt32
let expires = NSDate.ows_millisecondTimeStamp() + kWeekInMs
let identityKey: ECPublicKey
let identityKey: IdentityKey
let signer = buildServerCertificateProto()
if let senderClient = senderClient {
senderAddress = senderClient.address
senderDevice = UInt32(senderClient.deviceId)
identityKey = try! senderClient.identityKeyPair.ecPublicKey()
identityKey = senderClient.identityKeyPair.identityKey
} else {
senderAddress = .e164("+1235551234")
senderDevice = 123
identityKey = try! Curve25519.generateKeyPair().ecPublicKey()
identityKey = try! IdentityKeyPair.generate().identityKey
}
let certificateData: Data = {
let builder = SMKProtoSenderCertificateCertificate.builder(senderDevice: senderDevice,
expires: expires,
identityKey: identityKey.serialized,
identityKey: Data(try! identityKey.serialize()),
signer: signer)
if let e164 = senderAddress.e164 {
builder.setSenderE164(e164)

View File

@ -4,7 +4,7 @@
import XCTest
import SignalMetadataKit
import AxolotlKit
import SignalClient
import Curve25519Kit
// https://github.com/signalapp/libsignal-metadata-java/blob/master/tests/src/test/java/org/signal/libsignal/metadata/SecretSessionCipherTest.java
@ -28,13 +28,13 @@ class SMKSecretSessionCipherTest: XCTestCase {
initializeSessions(aliceMockClient: aliceMockClient, bobMockClient: bobMockClient)
// ECKeyPair trustRoot = Curve.generateKeyPair();
let trustRoot = Curve25519.generateKeyPair()
let trustRoot = try! IdentityKeyPair.generate()
// SenderCertificate senderCertificate = createCertificateFor(trustRoot, "+14151111111", 1, aliceStore.getIdentityKeyPair().getPublicKey().getPublicKey(), 31337);
let senderCertificate = createCertificateFor(trustRoot: trustRoot,
senderAddress: aliceMockClient.address,
senderDeviceId: UInt32(aliceMockClient.deviceId),
identityKey: try! aliceMockClient.identityKeyPair.ecPublicKey(),
identityKey: aliceMockClient.identityKeyPair.publicKey,
expirationTimestamp: 31337)
// SecretSessionCipher aliceCipher = new SecretSessionCipher(aliceStore);
@ -44,7 +44,7 @@ class SMKSecretSessionCipherTest: XCTestCase {
// senderCertificate, "smert za smert".getBytes());
// NOTE: The java tests don't bother padding the plaintext.
let alicePlaintext = "smert za smert".data(using: String.Encoding.utf8)!
let ciphertext = try! aliceCipher.throwswrapped_encryptMessage(recipientId: bobMockClient.accountId,
let ciphertext = try! aliceCipher.throwswrapped_encryptMessage(recipient: bobMockClient.address,
deviceId: bobMockClient.deviceId,
paddedPlaintext: alicePlaintext,
senderCertificate: senderCertificate,
@ -54,7 +54,7 @@ class SMKSecretSessionCipherTest: XCTestCase {
let bobCipher: SMKSecretSessionCipher = try! bobMockClient.createSecretSessionCipher()
// Pair<SignalProtocolAddress, byte[]> plaintext = bobCipher.decrypt(new CertificateValidator(trustRoot.getPublicKey()), ciphertext, 31335);
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: try! trustRoot.ecPublicKey())
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: ECPublicKey(trustRoot.publicKey))
let bobPlaintext = try! bobCipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator,
cipherTextData: ciphertext,
timestamp: 31335,
@ -84,13 +84,13 @@ class SMKSecretSessionCipherTest: XCTestCase {
// ECKeyPair trustRoot = Curve.generateKeyPair();
// ECKeyPair falseTrustRoot = Curve.generateKeyPair();
let trustRoot = Curve25519.generateKeyPair()
let falseTrustRoot = Curve25519.generateKeyPair()
let trustRoot = try! IdentityKeyPair.generate()
let falseTrustRoot = try! IdentityKeyPair.generate()
// SenderCertificate senderCertificate = createCertificateFor(falseTrustRoot, "+14151111111", 1, aliceStore.getIdentityKeyPair().getPublicKey().getPublicKey(), 31337);
let senderCertificate = createCertificateFor(trustRoot: falseTrustRoot,
senderAddress: aliceMockClient.address,
senderDeviceId: UInt32(aliceMockClient.deviceId),
identityKey: try! aliceMockClient.identityKeyPair.ecPublicKey(),
identityKey: aliceMockClient.identityKeyPair.publicKey,
expirationTimestamp: 31337)
// SecretSessionCipher aliceCipher = new SecretSessionCipher(aliceStore);
@ -100,7 +100,7 @@ class SMKSecretSessionCipherTest: XCTestCase {
// senderCertificate, "и вот я".getBytes());
// NOTE: The java tests don't bother padding the plaintext.
let alicePlaintext = "и вот я".data(using: String.Encoding.utf8)!
let ciphertext = try! aliceCipher.throwswrapped_encryptMessage(recipientId: bobMockClient.accountId,
let ciphertext = try! aliceCipher.throwswrapped_encryptMessage(recipient: bobMockClient.address,
deviceId: bobMockClient.deviceId,
paddedPlaintext: alicePlaintext,
senderCertificate: senderCertificate,
@ -115,7 +115,7 @@ class SMKSecretSessionCipherTest: XCTestCase {
// } catch (InvalidMetadataMessageException e) {
// // good
// }
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: try! trustRoot.ecPublicKey())
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: ECPublicKey(trustRoot.publicKey))
do {
_ = try bobCipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator,
cipherTextData: ciphertext,
@ -145,13 +145,13 @@ class SMKSecretSessionCipherTest: XCTestCase {
initializeSessions(aliceMockClient: aliceMockClient, bobMockClient: bobMockClient)
// ECKeyPair trustRoot = Curve.generateKeyPair();
let trustRoot = Curve25519.generateKeyPair()
let trustRoot = try! IdentityKeyPair.generate()
// SenderCertificate senderCertificate = createCertificateFor(trustRoot, "+14151111111", 1, aliceStore.getIdentityKeyPair().getPublicKey().getPublicKey(), 31337);
let senderCertificate = createCertificateFor(trustRoot: trustRoot,
senderAddress: aliceMockClient.address,
senderDeviceId: UInt32(aliceMockClient.deviceId),
identityKey: try! aliceMockClient.identityKeyPair.ecPublicKey(),
identityKey: aliceMockClient.identityKeyPair.publicKey,
expirationTimestamp: 31337)
// SecretSessionCipher aliceCipher = new SecretSessionCipher(aliceStore);
@ -161,7 +161,7 @@ class SMKSecretSessionCipherTest: XCTestCase {
// senderCertificate, "и вот я".getBytes());
// NOTE: The java tests don't bother padding the plaintext.
let alicePlaintext = "и вот я".data(using: String.Encoding.utf8)!
let ciphertext = try! aliceCipher.throwswrapped_encryptMessage(recipientId: bobMockClient.accountId,
let ciphertext = try! aliceCipher.throwswrapped_encryptMessage(recipient: bobMockClient.address,
deviceId: bobMockClient.deviceId,
paddedPlaintext: alicePlaintext,
senderCertificate: senderCertificate,
@ -176,7 +176,7 @@ class SMKSecretSessionCipherTest: XCTestCase {
// } catch (InvalidMetadataMessageException e) {
// // good
// }
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: try! trustRoot.ecPublicKey())
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: ECPublicKey(trustRoot.publicKey))
do {
_ = try bobCipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator,
cipherTextData: ciphertext,
@ -207,14 +207,14 @@ class SMKSecretSessionCipherTest: XCTestCase {
bobMockClient: bobMockClient)
// ECKeyPair trustRoot = Curve.generateKeyPair();
let trustRoot = Curve25519.generateKeyPair()
let trustRoot = try! IdentityKeyPair.generate()
// ECKeyPair randomKeyPair = Curve.generateKeyPair();
let randomKeyPair = Curve25519.generateKeyPair()
let randomKeyPair = try! IdentityKeyPair.generate()
// SenderCertificate senderCertificate = createCertificateFor(trustRoot, "+14151111111", 1, randomKeyPair.getPublicKey(), 31337);
let senderCertificate = createCertificateFor(trustRoot: trustRoot,
senderAddress: aliceMockClient.address,
senderDeviceId: UInt32(aliceMockClient.deviceId),
identityKey: try! randomKeyPair.ecPublicKey(),
identityKey: randomKeyPair.publicKey,
expirationTimestamp: 31337)
// SecretSessionCipher aliceCipher = new SecretSessionCipher(aliceStore);
let aliceCipher: SMKSecretSessionCipher = try! aliceMockClient.createSecretSessionCipher()
@ -223,7 +223,7 @@ class SMKSecretSessionCipherTest: XCTestCase {
// senderCertificate, "smert za smert".getBytes());
// NOTE: The java tests don't bother padding the plaintext.
let alicePlaintext = "smert za smert".data(using: String.Encoding.utf8)!
let ciphertext = try! aliceCipher.throwswrapped_encryptMessage(recipientId: bobMockClient.accountId,
let ciphertext = try! aliceCipher.throwswrapped_encryptMessage(recipient: bobMockClient.address,
deviceId: bobMockClient.deviceId,
paddedPlaintext: alicePlaintext,
senderCertificate: senderCertificate,
@ -237,7 +237,7 @@ class SMKSecretSessionCipherTest: XCTestCase {
// } catch (InvalidMetadataMessageException e) {
// // good
// }
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: try! trustRoot.ecPublicKey())
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: ECPublicKey(trustRoot.publicKey))
do {
_ = try bobCipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator,
cipherTextData: ciphertext,
@ -247,12 +247,11 @@ class SMKSecretSessionCipherTest: XCTestCase {
localDeviceId: bobMockClient.deviceId,
protocolContext: nil)
XCTFail("Decryption should have failed.")
} catch let knownSenderError as SecretSessionKnownSenderError {
} catch SignalError.invalidMessage(_) {
// Decryption is expected to fail.
guard case SMKError.assertionError = knownSenderError.underlyingError else {
XCTFail("unexpected error: \(knownSenderError.underlyingError)")
return
}
// FIXME: This particular failure doesn't get wrapped as a SecretSessionKnownSenderError
// because it's checked before the unwrapped message is returned.
// Why? Because it uses crypto values calculated during unwrapping to validate the sender certificate.
} catch {
XCTFail("unexpected error: \(error)")
}
@ -262,25 +261,26 @@ class SMKSecretSessionCipherTest: XCTestCase {
// private SenderCertificate createCertificateFor(ECKeyPair trustRoot, String sender, int deviceId, ECPublicKey identityKey, long expires)
// throws InvalidKeyException, InvalidCertificateException, InvalidProtocolBufferException {
private func createCertificateFor(trustRoot: ECKeyPair,
private func createCertificateFor(trustRoot: IdentityKeyPair,
senderAddress: SMKAddress,
senderDeviceId: UInt32,
identityKey: ECPublicKey,
identityKey: PublicKey,
expirationTimestamp: UInt64) -> SMKSenderCertificate {
// ECKeyPair serverKey = Curve.generateKeyPair();
let serverKey = Curve25519.generateKeyPair()
let serverKey = try! IdentityKeyPair.generate()
// byte[] serverCertificateBytes = SignalProtos.ServerCertificate.Certificate.newBuilder()
// .setId(1)
// .setKey(ByteString.copyFrom(serverKey.getPublicKey().serialize()))
// .build()
// .toByteArray();
let serverCertificateBuilder = SMKProtoServerCertificateCertificate.builder(id: 1,
key: try! serverKey.ecPublicKey().serialized)
let serverCertificateBuilder = SMKProtoServerCertificateCertificate.builder(
id: 1,
key: Data(try! serverKey.publicKey.serialize()))
let serverCertificateData = try! serverCertificateBuilder.build().serializedData()
// byte[] serverCertificateSignature = Curve.calculateSignature(trustRoot.getPrivateKey(), serverCertificateBytes);
let serverCertificateSignature = try! Ed25519.sign(serverCertificateData, with: trustRoot)
let serverCertificateSignature = try! trustRoot.privateKey.generateSignature(message: serverCertificateData)
// ServerCertificate serverCertificate = new ServerCertificate(SignalProtos.ServerCertificate.newBuilder()
// .setCertificate(ByteString.copyFrom(serverCertificateBytes))
@ -289,7 +289,7 @@ class SMKSecretSessionCipherTest: XCTestCase {
// .toByteArray());
let serverCertificate: SMKServerCertificate = {
let builder = SMKProtoServerCertificate.builder(certificate: serverCertificateData,
signature: serverCertificateSignature)
signature: Data(serverCertificateSignature))
return try! SMKServerCertificate(serializedData: try! builder.buildSerializedData())
}()
@ -306,7 +306,7 @@ class SMKSecretSessionCipherTest: XCTestCase {
let signer = try! SMKProtoServerCertificate.parseData(serverCertificate.serializedData)
let builder = SMKProtoSenderCertificateCertificate.builder(senderDevice: senderDeviceId,
expires: expirationTimestamp,
identityKey: identityKey.serialized,
identityKey: Data(try! identityKey.serialize()),
signer: signer)
if let e164 = senderAddress.e164 {
builder.setSenderE164(e164)
@ -320,7 +320,7 @@ class SMKSecretSessionCipherTest: XCTestCase {
}()
// byte[] senderCertificateSignature = Curve.calculateSignature(serverKey.getPrivateKey(), senderCertificateBytes);
let senderCertificateSignature = try! Ed25519.sign(senderCertificateData, with: serverKey)
let senderCertificateSignature = try! serverKey.privateKey.generateSignature(message: senderCertificateData)
// return new SenderCertificate(SignalProtos.SenderCertificate.newBuilder()
// .setCertificate(ByteString.copyFrom(senderCertificateBytes))
@ -329,7 +329,7 @@ class SMKSecretSessionCipherTest: XCTestCase {
// .toByteArray());
return {
let builder = SMKProtoSenderCertificate.builder(certificate: senderCertificateData,
signature: senderCertificateSignature)
signature: Data(senderCertificateSignature))
return try! SMKSenderCertificate(serializedData: try! builder.buildSerializedData())
}()
}
@ -337,35 +337,6 @@ class SMKSecretSessionCipherTest: XCTestCase {
// private void initializeSessions(TestInMemorySignalProtocolStore aliceStore, TestInMemorySignalProtocolStore bobStore)
// throws InvalidKeyException, UntrustedIdentityException
private func initializeSessions(aliceMockClient: MockClient, bobMockClient: MockClient) {
// ECKeyPair bobPreKey = Curve.generateKeyPair();
let bobPreKey = bobMockClient.generateMockPreKey()
// IdentityKeyPair bobIdentityKey = bobStore.getIdentityKeyPair();
let bobIdentityKey = bobMockClient.identityKeyPair
// SignedPreKeyRecord bobSignedPreKey = KeyHelper.generateSignedPreKey(bobIdentityKey, 2);
let bobSignedPreKey = bobMockClient.generateMockSignedPreKey()
// PreKeyBundle bobBundle = new PreKeyBundle(1, 1, 1, bobPreKey.getPublicKey(), 2, bobSignedPreKey.getKeyPair().getPublicKey(), bobSignedPreKey.getSignature(), bobIdentityKey.getPublicKey());
let bobSignedPreKeyData = Data(try! bobSignedPreKey.keyPair.identityKeyPair.publicKey.serialize())
let bobIdentityKeyData = Data(try! bobIdentityKey.identityKeyPair.publicKey.serialize())
let bobBundle = PreKeyBundle(registrationId: bobMockClient.registrationId,
deviceId: bobMockClient.deviceId,
preKeyId: bobPreKey.id,
preKeyPublic: try! bobPreKey.keyPair.ecPublicKey().serialized,
signedPreKeyPublic: bobSignedPreKeyData,
signedPreKeyId: bobSignedPreKey.id,
signedPreKeySignature: bobSignedPreKey.signature,
identityKey: bobIdentityKeyData)!
// SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, new SignalProtocolAddress("+14152222222", 1));
let aliceSessionBuilder = aliceMockClient.createSessionBuilder(forRecipient: bobMockClient)
// aliceSessionBuilder.process(bobBundle);
try! aliceSessionBuilder.processPrekeyBundle(bobBundle, protocolContext: nil)
// bobStore.storeSignedPreKey(2, bobSignedPreKey);
// bobStore.storePreKey(1, new PreKeyRecord(1, bobPreKey));
// NOTE: These stores are taken care of in the mocks' createKey() methods above.
aliceMockClient.initializeSession(with: bobMockClient)
}
}

View File

@ -33,6 +33,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 {
#if false
class SMKSessionCipherTest: XCTestCase {
override func setUp() {
@ -345,3 +346,4 @@ class SMKSessionCipherTest: XCTestCase {
try! RatchetingSession.initializeSession(bobSessionState, sessionVersion: currentVersion, bobParameters: bobParameters)
}
}
#endif

View File

@ -5,7 +5,6 @@
import Foundation
import SignalMetadataKit
import SignalClient
import AxolotlKit
class MockCertificateValidator: NSObject, SMKCertificateValidator {
@ -33,20 +32,21 @@ class MockClient: NSObject {
let deviceId: Int32
let registrationId: Int32
let identityKeyPair: ECKeyPair
let identityKeyPair: IdentityKeyPair
let sessionStore: SPKMockProtocolStore
let preKeyStore: SPKMockProtocolStore
let signedPreKeyStore: SPKMockProtocolStore
let identityStore: SPKMockProtocolStore
let sessionStore: InMemorySignalProtocolStore
let preKeyStore: InMemorySignalProtocolStore
let signedPreKeyStore: InMemorySignalProtocolStore
let identityStore: InMemorySignalProtocolStore
init(address: SMKAddress, deviceId: Int32, registrationId: Int32) {
self.address = address
self.deviceId = deviceId
self.registrationId = registrationId
self.identityKeyPair = Curve25519.generateKeyPair()
self.identityKeyPair = try! IdentityKeyPair.generate()
let protocolStore = SPKMockProtocolStore(identityKeyPair: identityKeyPair, localRegistrationId: registrationId)
let protocolStore = InMemorySignalProtocolStore(identity: identityKeyPair,
deviceId: UInt32(bitPattern: deviceId))
sessionStore = protocolStore
preKeyStore = protocolStore
@ -54,14 +54,14 @@ class MockClient: NSObject {
identityStore = protocolStore
}
func createSessionCipher() -> SessionCipher {
return SessionCipher(sessionStore: sessionStore,
preKeyStore: preKeyStore,
signedPreKeyStore: signedPreKeyStore,
identityKeyStore: identityStore,
recipientId: accountId,
deviceId: deviceId)
}
// func createSessionCipher() -> SessionCipher {
// return SessionCipher(sessionStore: sessionStore,
// preKeyStore: preKeyStore,
// signedPreKeyStore: signedPreKeyStore,
// identityKeyStore: identityStore,
// recipientId: accountId,
// deviceId: deviceId)
// }
func createSecretSessionCipher() throws -> SMKSecretSessionCipher {
return try SMKSecretSessionCipher(sessionStore: sessionStore,
@ -70,34 +70,33 @@ class MockClient: NSObject {
identityStore: identityStore)
}
func createSessionBuilder(forRecipient recipient: MockClient) -> SessionBuilder {
return SessionBuilder(sessionStore: sessionStore,
preKeyStore: preKeyStore,
signedPreKeyStore: signedPreKeyStore,
identityKeyStore: identityStore,
recipientId: recipient.accountId,
deviceId: recipient.deviceId)
}
// func createSessionBuilder(forRecipient recipient: MockClient) -> SessionBuilder {
// return SessionBuilder(sessionStore: sessionStore,
// preKeyStore: preKeyStore,
// signedPreKeyStore: signedPreKeyStore,
// identityKeyStore: identityStore,
// recipientId: recipient.accountId,
// deviceId: recipient.deviceId)
// }
func generateMockPreKey() -> AxolotlKit.PreKeyRecord {
let preKeyId: Int32 = Int32(arc4random_uniform(UInt32(INT32_MAX)))
let keyPair = Curve25519.generateKeyPair()
let preKey = AxolotlKit.PreKeyRecord(id: preKeyId, keyPair: keyPair, createdAt: Date())
self.preKeyStore.storePreKey(preKeyId, preKeyRecord: preKey, protocolContext: nil)
func generateMockPreKey() -> PreKeyRecord {
let preKeyId = UInt32(Int32.random(in: 0...Int32.max))
let preKey = try! PreKeyRecord(id: preKeyId, privateKey: try PrivateKey.generate())
try! self.preKeyStore.storePreKey(preKey, id: preKeyId, context: nil)
return preKey
}
func generateMockSignedPreKey() -> AxolotlKit.SignedPreKeyRecord {
let signedPreKeyId: Int32 = Int32(arc4random_uniform(UInt32(INT32_MAX)))
func generateMockSignedPreKey() -> SignedPreKeyRecord {
let signedPreKeyId = UInt32(Int32.random(in: 0...Int32.max))
let keyPair = try! IdentityKeyPair.generate()
let generatedAt = Date()
let identityKeyPair = self.identityStore.identityKeyPair(nil)!.identityKeyPair
let signature = Data(try! identityKeyPair.privateKey.generateSignature(message: keyPair.publicKey.serialize()))
let signedPreKey = SignedPreKeyRecord(id: signedPreKeyId,
keyPair: ECKeyPair(keyPair),
signature: signature,
generatedAt: generatedAt)
self.signedPreKeyStore.storeSignedPreKey(signedPreKeyId, signedPreKeyRecord: signedPreKey, protocolContext: nil)
let identityKeyPair = try! self.identityStore.identityKeyPair(context: nil)
let signature = try! identityKeyPair.privateKey.generateSignature(message: try! keyPair.publicKey.serialize())
let signedPreKey = try! SignedPreKeyRecord(id: signedPreKeyId,
timestamp: UInt64(generatedAt.timeIntervalSince1970),
privateKey: keyPair.privateKey,
signature: signature)
try! self.signedPreKeyStore.storeSignedPreKey(signedPreKey, id: signedPreKeyId, context: nil)
return signedPreKey
}
@ -109,18 +108,43 @@ class MockClient: NSObject {
protocolContext: nil)!
}
func storeSession(address: SMKAddress,
deviceId: Int32,
session: AxolotlKit.SessionRecord,
protocolContext: SPKProtocolWriteContext?) {
// Moved from SMKSecretSessionCipherTest.
// private void initializeSessions(TestInMemorySignalProtocolStore aliceStore, TestInMemorySignalProtocolStore bobStore)
// throws InvalidKeyException, UntrustedIdentityException
func initializeSession(with bobMockClient: MockClient) {
// ECKeyPair bobPreKey = Curve.generateKeyPair();
let bobPreKey = bobMockClient.generateMockPreKey()
let accountId = accountIdFinder.accountId(forUuid: address.uuid,
phoneNumber: address.e164,
protocolContext: protocolContext)!
sessionStore.storeSession(accountId,
deviceId: deviceId,
session: session,
protocolContext: protocolContext)
// IdentityKeyPair bobIdentityKey = bobStore.getIdentityKeyPair();
let bobIdentityKey = bobMockClient.identityKeyPair
// SignedPreKeyRecord bobSignedPreKey = KeyHelper.generateSignedPreKey(bobIdentityKey, 2);
let bobSignedPreKey = bobMockClient.generateMockSignedPreKey()
// PreKeyBundle bobBundle = new PreKeyBundle(1, 1, 1, bobPreKey.getPublicKey(), 2, bobSignedPreKey.getKeyPair().getPublicKey(), bobSignedPreKey.getSignature(), bobIdentityKey.getPublicKey());
let bobBundle = try! SignalClient.PreKeyBundle(registrationId: UInt32(bitPattern: bobMockClient.registrationId),
deviceId: UInt32(bitPattern: bobMockClient.deviceId),
prekeyId: try! bobPreKey.id(),
prekey: try! bobPreKey.publicKey(),
signedPrekeyId: try! bobSignedPreKey.id(),
signedPrekey: try! bobSignedPreKey.publicKey(),
signedPrekeySignature: try! bobSignedPreKey.signature(),
identity: bobIdentityKey.identityKey)
// SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, new SignalProtocolAddress("+14152222222", 1));
// aliceSessionBuilder.process(bobBundle);
let bobProtocolAddress = try! ProtocolAddress(
name: bobMockClient.address.uuid?.uuidString ?? bobMockClient.address.e164!,
deviceId: UInt32(bitPattern: bobMockClient.deviceId))
try! processPreKeyBundle(bobBundle,
for: bobProtocolAddress,
sessionStore: sessionStore,
identityStore: identityStore,
context: nil)
// bobStore.storeSignedPreKey(2, bobSignedPreKey);
// bobStore.storePreKey(1, new PreKeyRecord(1, bobPreKey));
// NOTE: These stores are taken care of in the mocks' createKey() methods above.
}
}