From 07fbbfbedc6751df050f7b1a33b7442329fe91b9 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Tue, 10 Nov 2020 17:03:06 -0800 Subject: [PATCH] Adopt SignalClient for en/decryption in SMKSecretSessionCipher --- SignalMetadataKit.podspec | 2 + SignalMetadataKit/src/NSData+messagePadding.h | 8 +- .../src/SMKSecretSessionCipher.swift | 522 +++++------------- SignalMetadataKitTests/src/SMKMiscTest.swift | 29 +- .../src/SMKSecretSessionCipherTest.swift | 99 ++-- .../src/SMKSessionCipherTest.swift | 2 + SignalMetadataKitTests/src/SMKTestUtils.swift | 122 ++-- 7 files changed, 250 insertions(+), 534 deletions(-) diff --git a/SignalMetadataKit.podspec b/SignalMetadataKit.podspec index 2adb5ff..6e6d843 100644 --- a/SignalMetadataKit.podspec +++ b/SignalMetadataKit.podspec @@ -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 diff --git a/SignalMetadataKit/src/NSData+messagePadding.h b/SignalMetadataKit/src/NSData+messagePadding.h index 117bfa1..5f729a8 100644 --- a/SignalMetadataKit/src/NSData+messagePadding.h +++ b/SignalMetadataKit/src/NSData+messagePadding.h @@ -1,7 +1,11 @@ // -// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// Copyright (c) 2020 Open Whisper Systems. All rights reserved. // +#import + +NS_ASSUME_NONNULL_BEGIN + @interface NSData (messagePadding) - (NSData *)removePadding; @@ -9,3 +13,5 @@ - (NSData *)paddedMessageBody; @end + +NS_ASSUME_NONNULL_END diff --git a/SignalMetadataKit/src/SMKSecretSessionCipher.swift b/SignalMetadataKit/src/SMKSecretSessionCipher.swift index 0df2944..21457c4 100644 --- a/SignalMetadataKit/src/SMKSecretSessionCipher.swift +++ b/SignalMetadataKit/src/SMKSecretSessionCipher.swift @@ -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 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.. 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) } } diff --git a/SignalMetadataKitTests/src/SMKSessionCipherTest.swift b/SignalMetadataKitTests/src/SMKSessionCipherTest.swift index c5698d2..e443c1f 100644 --- a/SignalMetadataKitTests/src/SMKSessionCipherTest.swift +++ b/SignalMetadataKitTests/src/SMKSessionCipherTest.swift @@ -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 diff --git a/SignalMetadataKitTests/src/SMKTestUtils.swift b/SignalMetadataKitTests/src/SMKTestUtils.swift index 7644130..91b11d0 100644 --- a/SignalMetadataKitTests/src/SMKTestUtils.swift +++ b/SignalMetadataKitTests/src/SMKTestUtils.swift @@ -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. } }