From 82b12901b9e2b4348eeaa5502eebd03b3cdbe8e3 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Mon, 9 Nov 2020 09:17:47 +1100 Subject: [PATCH] Re-add missing Signal protocol bits --- .../MessageSender+Encryption.swift | 47 +- SessionProtocolKit/Meta/SessionProtocolKit.h | 4 + SessionProtocolKit/Signal/ECPrivateKey.swift | 34 + SessionProtocolKit/Signal/ECPublicKey.swift | 61 ++ .../Signal/FallbackSessionCipher.swift | 52 ++ .../Signal/LokiSessionCipher.swift | 104 +++ .../Signal/NSData+messagePadding.h | 11 + .../Signal/NSData+messagePadding.m | 60 ++ SessionProtocolKit/Signal/Protos/Makefile | 8 +- .../Protos/OWSUnidentifiedDelivery.pb.swift | 571 +++++++++++++ .../Protos/OWSUnidentifiedDelivery.proto | 73 ++ .../Signal/Protos/SMKProto.swift | 782 ++++++++++++++++++ .../Signal/SMKCertificateValidator.swift | 106 +++ SessionProtocolKit/Signal/SMKError.swift | 9 + .../Signal/SMKSecretSessionCipher.swift | 590 +++++++++++++ .../Signal/SMKSenderCertificate.swift | 53 ++ .../Signal/SMKServerCertificate.swift | 64 ++ .../Signal/SMKUDAccessKey.swift | 50 ++ .../Signal/SMKUnidentifiedSenderMessage.swift | 94 +++ .../SMKUnidentifiedSenderMessageContent.swift | 71 ++ .../Signal/SessionRestorationProtocol.swift | 8 + .../Signal/SessionRestorationStatus.swift | 5 + SessionUtilities/DiffieHellman.swift | 51 ++ SessionUtilities/ECKeyPair+Utilities.h | 1 + SessionUtilities/ECKeyPair+Utilities.m | 5 + Signal.xcodeproj/project.pbxproj | 76 ++ 26 files changed, 2988 insertions(+), 2 deletions(-) create mode 100644 SessionProtocolKit/Signal/ECPrivateKey.swift create mode 100644 SessionProtocolKit/Signal/ECPublicKey.swift create mode 100644 SessionProtocolKit/Signal/FallbackSessionCipher.swift create mode 100644 SessionProtocolKit/Signal/LokiSessionCipher.swift create mode 100644 SessionProtocolKit/Signal/NSData+messagePadding.h create mode 100644 SessionProtocolKit/Signal/NSData+messagePadding.m create mode 100644 SessionProtocolKit/Signal/Protos/OWSUnidentifiedDelivery.pb.swift create mode 100644 SessionProtocolKit/Signal/Protos/OWSUnidentifiedDelivery.proto create mode 100644 SessionProtocolKit/Signal/Protos/SMKProto.swift create mode 100644 SessionProtocolKit/Signal/SMKCertificateValidator.swift create mode 100644 SessionProtocolKit/Signal/SMKError.swift create mode 100644 SessionProtocolKit/Signal/SMKSecretSessionCipher.swift create mode 100644 SessionProtocolKit/Signal/SMKSenderCertificate.swift create mode 100644 SessionProtocolKit/Signal/SMKServerCertificate.swift create mode 100644 SessionProtocolKit/Signal/SMKUDAccessKey.swift create mode 100644 SessionProtocolKit/Signal/SMKUnidentifiedSenderMessage.swift create mode 100644 SessionProtocolKit/Signal/SMKUnidentifiedSenderMessageContent.swift create mode 100644 SessionProtocolKit/Signal/SessionRestorationProtocol.swift create mode 100644 SessionProtocolKit/Signal/SessionRestorationStatus.swift create mode 100644 SessionUtilities/DiffieHellman.swift diff --git a/SessionMessagingKit/Sending & Receiving/MessageSender+Encryption.swift b/SessionMessagingKit/Sending & Receiving/MessageSender+Encryption.swift index 55f83db85..cd7ab4b0e 100644 --- a/SessionMessagingKit/Sending & Receiving/MessageSender+Encryption.swift +++ b/SessionMessagingKit/Sending & Receiving/MessageSender+Encryption.swift @@ -4,9 +4,54 @@ import SessionUtilities internal extension MessageSender { static func encryptWithSignalProtocol(_ plaintext: Data, for publicKey: String, using transaction: Any) throws -> Data { - return Data() + } +// NSError *error; +// LKSessionResetImplementation *sessionResetImplementation = [LKSessionResetImplementation new]; +// +// SMKSecretSessionCipher *_Nullable secretCipher = +// [[SMKSecretSessionCipher alloc] initWithSessionResetImplementation:sessionResetImplementation +// sessionStore:self.primaryStorage +// preKeyStore:self.primaryStorage +// signedPreKeyStore:self.primaryStorage +// identityStore:self.identityManager +// error:&error]; +// if (error || !secretCipher) { +// OWSRaiseException(@"SecretSessionCipherFailure", @"Can't create secret session cipher."); +// } +// +// // Loki: The way this works is: +// // • Alice sends a session request (i.e. a pre key bundle) to Bob using fallback encryption. +// // • She may send any number of subsequent messages also encrypted using fallback encryption. +// // • When Bob receives the session request, he sets up his Signal cipher session locally and sends back a null message, +// // now encrypted using Signal encryption. +// // • Alice receives this, sets up her Signal cipher session locally, and sends any subsequent messages +// // using Signal encryption. +// +// BOOL shouldUseFallbackEncryption = [LKSessionManagementProtocol shouldUseFallbackEncryptionForMessage:message recipientID:recipientID transaction:transaction]; +// +// if (shouldUseFallbackEncryption) { +// [LKLogger print:@"[Loki] Using fallback encryption"]; +// } else { +// [LKLogger print:@"[Loki] Using Signal Encryption"]; +// } +// +// serializedMessage = [secretCipher throwswrapped_encryptMessageWithRecipientPublicKey:recipientID +// deviceID:@(OWSDevicePrimaryDeviceId).intValue +// paddedPlaintext:plainText.paddedMessageBody +// senderCertificate:messageSend.senderCertificate +// protocolContext:transaction +// useFallbackSessionCipher:shouldUseFallbackEncryption +// error:&error]; +// +// SCKRaiseIfExceptionWrapperError(error); +// if (serializedMessage == nil || error != nil) { +// OWSFailDebug(@"Error while UD encrypting message: %@.", error); +// return nil; +// } +// messageType = TSUnidentifiedSenderMessageType; + static func encryptWithSharedSenderKeys(_ plaintext: Data, for groupPublicKey: String, using transaction: Any) throws -> Data { // 1. ) Encrypt the data with the user's sender key guard let userPublicKey = Configuration.shared.storage.getUserPublicKey() else { diff --git a/SessionProtocolKit/Meta/SessionProtocolKit.h b/SessionProtocolKit/Meta/SessionProtocolKit.h index d9084d8de..ff44b9ac7 100644 --- a/SessionProtocolKit/Meta/SessionProtocolKit.h +++ b/SessionProtocolKit/Meta/SessionProtocolKit.h @@ -5,8 +5,12 @@ FOUNDATION_EXPORT const unsigned char SessionProtocolKitVersionString[]; #import #import +#import +#import #import #import #import #import #import +#import +#import diff --git a/SessionProtocolKit/Signal/ECPrivateKey.swift b/SessionProtocolKit/Signal/ECPrivateKey.swift new file mode 100644 index 000000000..2af140eef --- /dev/null +++ b/SessionProtocolKit/Signal/ECPrivateKey.swift @@ -0,0 +1,34 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +// See: +// https://github.com/signalapp/libsignal-protocol-java/blob/87fae0f98332e98a32bbb82515428b4edeb4181f/java/src/main/java/org/whispersystems/libsignal/ecc/ECPrivateKey.java +@objc public class ECPrivateKey: NSObject { + + @objc + public let keyData: Data + + @objc + public init(keyData: Data) throws { + guard keyData.count == ECCKeyLength else { + throw SMKError.assertionError(description: "\(ECPrivateKey.logTag) key has invalid length") + } + + self.keyData = keyData + } + + open override func isEqual(_ object: Any?) -> Bool { + if let object = object as? ECPrivateKey { + return keyData == object.keyData + } else { + return false + } + } + + public override var hash: Int { + return keyData.hashValue + } +} diff --git a/SessionProtocolKit/Signal/ECPublicKey.swift b/SessionProtocolKit/Signal/ECPublicKey.swift new file mode 100644 index 000000000..a27d985b2 --- /dev/null +++ b/SessionProtocolKit/Signal/ECPublicKey.swift @@ -0,0 +1,61 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +// See: +// https://github.com/signalapp/libsignal-protocol-java/blob/87fae0f98332e98a32bbb82515428b4edeb4181f/java/src/main/java/org/whispersystems/libsignal/ecc/DjbECPublicKey.java +@objc public class ECPublicKey: NSObject { + + @objc + public static let keyTypeDJB: UInt8 = 0x05 + + @objc + public let keyData: Data + + @objc + public init(keyData: Data) throws { + guard keyData.count == ECCKeyLength else { + throw SMKError.assertionError(description: "\(ECPublicKey.logTag) key has invalid length") + } + + self.keyData = keyData + } + + // https://github.com/signalapp/libsignal-protocol-java/blob/master/java/src/main/java/org/whispersystems/libsignal/ecc/Curve.java#L30 + @objc + public init(serializedKeyData: Data) throws { + let parser = OWSDataParser(data: serializedKeyData) + + let typeByte = try parser.nextByte(name: "type byte") + guard typeByte == ECPublicKey.keyTypeDJB else { + throw SMKError.assertionError(description: "\(ECPublicKey.logTag) key data has invalid type byte") + } + + let keyData = try parser.remainder(name: "key data") + guard keyData.count == ECCKeyLength else { + throw SMKError.assertionError(description: "\(ECPublicKey.logTag) key has invalid length") + } + + self.keyData = keyData + } + + @objc public var serialized: Data { + let typeBytes = [ECPublicKey.keyTypeDJB] + let typeData = Data(bytes: typeBytes) + return NSData.join([typeData, keyData]) + } + + open override func isEqual(_ object: Any?) -> Bool { + if let object = object as? ECPublicKey { + return keyData == object.keyData + } else { + return false + } + } + + public override var hash: Int { + return keyData.hashValue + } +} diff --git a/SessionProtocolKit/Signal/FallbackSessionCipher.swift b/SessionProtocolKit/Signal/FallbackSessionCipher.swift new file mode 100644 index 000000000..a91c09fe0 --- /dev/null +++ b/SessionProtocolKit/Signal/FallbackSessionCipher.swift @@ -0,0 +1,52 @@ +import CryptoSwift +import Curve25519Kit +import SessionUtilities + +/// A fallback session cipher which uses the the recipient's public key to encrypt data. +@objc public final class FallBackSessionCipher : NSObject { + /// The hex encoded public key of the recipient. + private let recipientPublicKey: String + private let privateKey: Data? + private let ivSize: Int32 = 16 + + private lazy var recipientPublicKeyAsData: Data = { + var recipientPublicKey = self.recipientPublicKey + if recipientPublicKey.count == 66 && recipientPublicKey.hasPrefix("05") { + let index = recipientPublicKey.index(recipientPublicKey.startIndex, offsetBy: 2) + recipientPublicKey = recipientPublicKey.substring(from: index) + } + return Data(hex: recipientPublicKey) + }() + + private lazy var symmetricKey: Data? = { + guard let privateKey = privateKey else { return nil } + let keyPair = ECKeyPair(publicKey: recipientPublicKeyAsData, privateKey: privateKey) + return Curve25519.generateSharedSecret(fromPublicKey: recipientPublicKeyAsData, andKeyPair: keyPair) + }() + + @objc public init(recipientPublicKey: String, privateKey: Data?) { + self.recipientPublicKey = recipientPublicKey + self.privateKey = privateKey + super.init() + } + + @objc public func encrypt(_ plaintext: Data) -> Data? { + guard let symmetricKey = symmetricKey else { return nil } + do { + return try DiffieHellman.encrypt(plaintext, using: symmetricKey) + } catch { + print("[Loki] Couldn't encrypt message using fallback session cipher due to error: \(error).") + return nil + } + } + + @objc public func decrypt(_ ivAndCiphertext: Data) -> Data? { + guard let symmetricKey = symmetricKey else { return nil } + do { + return try DiffieHellman.decrypt(ivAndCiphertext, using: symmetricKey) + } catch { + print("[Loki] Couldn't decrypt message using fallback session cipher due to error: \(error).") + return nil + } + } +} diff --git a/SessionProtocolKit/Signal/LokiSessionCipher.swift b/SessionProtocolKit/Signal/LokiSessionCipher.swift new file mode 100644 index 000000000..a3bc7d2c0 --- /dev/null +++ b/SessionProtocolKit/Signal/LokiSessionCipher.swift @@ -0,0 +1,104 @@ +import Foundation + +@objc(LKSessionCipher) +public final class LokiSessionCipher : SessionCipher { + private let sessionResetImplementation: SessionRestorationProtocol? + private let sessionStore: SessionStore + private let preKeyStore: PreKeyStore + private let recipientID: String + private let deviceID: Int32 + + @objc public static let newSessionAdoptedNotification = "LKNewSessionAdoptedNotification" + @objc public static let contactKey = "LKContactKey" + + @objc public init(sessionResetImplementation: SessionRestorationProtocol, sessionStore: SessionStore, preKeyStore: PreKeyStore, signedPreKeyStore: SignedPreKeyStore, identityKeyStore: IdentityKeyStore, recipientID: String, deviceID: Int32) { + self.sessionResetImplementation = sessionResetImplementation + self.sessionStore = sessionStore + self.preKeyStore = preKeyStore + self.recipientID = recipientID + self.deviceID = deviceID + super.init(sessionStore: sessionStore, preKeyStore: preKeyStore, signedPreKeyStore: signedPreKeyStore, identityKeyStore: identityKeyStore, recipientId: recipientID, deviceId: deviceID) + } + + @available(*, unavailable) + override convenience private init(axolotlStore sessionStore: AxolotlStore, recipientId: String, deviceId: Int32) { + self.init(sessionStore: sessionStore, preKeyStore: sessionStore, signedPreKeyStore: sessionStore, identityKeyStore: sessionStore, recipientId: recipientId, deviceId: deviceId) + } + + override private init(sessionStore: SessionStore, preKeyStore: PreKeyStore, signedPreKeyStore: SignedPreKeyStore, identityKeyStore: IdentityKeyStore, recipientId: String, deviceId: Int32) { + self.sessionResetImplementation = nil + self.sessionStore = sessionStore + self.preKeyStore = preKeyStore + self.recipientID = recipientId + self.deviceID = deviceId + super.init(sessionStore: sessionStore, preKeyStore: preKeyStore, signedPreKeyStore: signedPreKeyStore, identityKeyStore: identityKeyStore, recipientId: recipientId, deviceId: deviceId) + } + + override public func decrypt(_ whisperMessage: CipherMessage, protocolContext: Any?) throws -> Data { + // Note that while decrypting our state may change internally + let currentState = getCurrentState(protocolContext: protocolContext) + if (currentState == nil && whisperMessage.cipherMessageType == .prekey) { + try sessionResetImplementation?.validatePreKeyWhisperMessage(for: recipientID, whisperMessage: whisperMessage, using: protocolContext!) + } + let plainText = try super.decrypt(whisperMessage, protocolContext: protocolContext) + handleSessionReset(for: whisperMessage, previousState: currentState, protocolContext: protocolContext!) + return plainText + } + + private func getCurrentState(protocolContext: Any?) -> SessionState? { + let record = sessionStore.loadSession(recipientID, deviceId: deviceID, protocolContext: protocolContext) + return record.isFresh() ? nil : record.sessionState() + } + + private func handleSessionReset(for whisperMessage: CipherMessage, previousState: SessionState?, protocolContext: Any) { + // Don't bother doing anything if we didn't have a session before + guard let previousState = previousState else { return } + let sessionResetStatus = sessionResetImplementation?.getSessionRestorationStatus(for: recipientID) ?? SessionRestorationStatus.none + // Bail early if no session reset is in progress + guard sessionResetStatus != .none else { return } + let currentState = getCurrentState(protocolContext: protocolContext) + // Check if our previous state and our current state differ + if (currentState == nil || currentState!.aliceBaseKey != previousState.aliceBaseKey) { + if sessionResetStatus == .requestReceived { + // The other user used an old session to contact us. Wait for them to use a new one + restoreSession(previousState, protocolContext: protocolContext) + } else { + // Our session reset went through successfully. + // We initiated a session reset and got a different session back from the user. + deleteAllSessions(except: currentState, protocolContext: protocolContext) + notifySessionAdopted(protocolContext) + } + } else if sessionResetStatus == .requestReceived { + // Our session reset went through successfully. + // We got a message with the same session from the other user. + deleteAllSessions(except: previousState, protocolContext: protocolContext) + notifySessionAdopted(protocolContext) + } + } + + private func notifySessionAdopted(_ protocolContext: Any) { + self.sessionResetImplementation?.handleNewSessionAdopted(for: recipientID, using: protocolContext) + NotificationCenter.default.post(name: NSNotification.Name(rawValue: LokiSessionCipher.newSessionAdoptedNotification), object: nil, userInfo: [ LokiSessionCipher.contactKey : recipientID ]) + } + + private func deleteAllSessions(except state: SessionState?, protocolContext: Any?) { + let record = sessionStore.loadSession(recipientID, deviceId: deviceID, protocolContext: protocolContext) + record.removePreviousSessionStates() + let newState = state ?? SessionState() + record.setState(newState) + sessionStore.storeSession(recipientID, deviceId: deviceID, session: record, protocolContext: protocolContext) + } + + private func restoreSession(_ state: SessionState, protocolContext: Any?) { + let record = sessionStore.loadSession(recipientID, deviceId: deviceID, protocolContext: protocolContext) + // Remove the state from previous session states + record.previousSessionStates()?.enumerateObjects(options: .reverse) { obj, index, stop in + guard let obj = obj as? SessionState, state.aliceBaseKey == obj.aliceBaseKey else { return } + record.previousSessionStates()?.removeObject(at: index) + stop.pointee = true + } + // Promote so the previous state gets archived + record.promoteState(state) + sessionStore.storeSession(recipientID, deviceId: deviceID, session: record, protocolContext: protocolContext) + } +} diff --git a/SessionProtocolKit/Signal/NSData+messagePadding.h b/SessionProtocolKit/Signal/NSData+messagePadding.h new file mode 100644 index 000000000..117bfa12b --- /dev/null +++ b/SessionProtocolKit/Signal/NSData+messagePadding.h @@ -0,0 +1,11 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +@interface NSData (messagePadding) + +- (NSData *)removePadding; + +- (NSData *)paddedMessageBody; + +@end diff --git a/SessionProtocolKit/Signal/NSData+messagePadding.m b/SessionProtocolKit/Signal/NSData+messagePadding.m new file mode 100644 index 000000000..563396662 --- /dev/null +++ b/SessionProtocolKit/Signal/NSData+messagePadding.m @@ -0,0 +1,60 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +#import +#import "OWSAsserts.h" +#import "NSData+messagePadding.h" + +@implementation NSData (messagePadding) + +- (NSData *)removePadding { + unsigned long paddingStart = self.length; + + Byte data[self.length]; + [self getBytes:data length:self.length]; + + for (long i = (long)self.length - 1; i >= 0; i--) { + if (data[i] == (Byte)0x80) { + paddingStart = (unsigned long)i; + break; + } else if (data[i] != (Byte)0x00) { + OWSLogWarn(@"Failed to remove padding, returning unstripped padding"); + return self; + } + } + + return [self subdataWithRange:NSMakeRange(0, paddingStart)]; +} + + +- (NSData *)paddedMessageBody { + // From + // https://github.com/signalapp/TextSecure/blob/master/libtextsecure/src/main/java/org/whispersystems/textsecure/internal/push/PushTransportDetails.java#L55 + // NOTE: This is dumb. We have our own padding scheme, but so does the cipher. + // The +1 -1 here is to make sure the Cipher has room to add one padding byte, + // otherwise it'll add a full 16 extra bytes. + + NSUInteger paddedMessageLength = [self paddedMessageLength:(self.length + 1)] - 1; + NSMutableData *paddedMessage = [NSMutableData dataWithLength:paddedMessageLength]; + + Byte paddingByte = 0x80; + + [paddedMessage replaceBytesInRange:NSMakeRange(0, self.length) withBytes:[self bytes]]; + [paddedMessage replaceBytesInRange:NSMakeRange(self.length, 1) withBytes:&paddingByte]; + + return paddedMessage; +} + +- (NSUInteger)paddedMessageLength:(NSUInteger)messageLength { + NSUInteger messageLengthWithTerminator = messageLength + 1; + NSUInteger messagePartCount = messageLengthWithTerminator / 160; + + if (messageLengthWithTerminator % 160 != 0) { + messagePartCount++; + } + + return messagePartCount * 160; +} + +@end diff --git a/SessionProtocolKit/Signal/Protos/Makefile b/SessionProtocolKit/Signal/Protos/Makefile index 24dd640b2..55a4f1e3e 100644 --- a/SessionProtocolKit/Signal/Protos/Makefile +++ b/SessionProtocolKit/Signal/Protos/Makefile @@ -3,10 +3,16 @@ PROTOC=protoc \ WRAPPER_SCRIPT=../../../../session-ios/Scripts/ProtoWrappers.py \ --proto-dir='./' --verbose --add-log-tag -all: webrtc_data_proto +all: webrtc_data_proto unidentified_delivery_protos webrtc_data_proto: WhisperTextProtocol.proto $(PROTOC) --swift_out=. \ WhisperTextProtocol.proto $(WRAPPER_SCRIPT) --dst-dir=. \ --wrapper-prefix=SPKProto --proto-prefix=SPKProtos --proto-file=WhisperTextProtocol.proto + +unidentified_delivery_protos: OWSUnidentifiedDelivery.proto + $(PROTOC) --swift_out=../SignalMetadataKit/src/Generated \ + OWSUnidentifiedDelivery.proto + $(WRAPPER_SCRIPT) --dst-dir=../SignalMetadataKit/src/Generated \ + --wrapper-prefix=SMKProto --proto-prefix=SMKProtos --proto-file=OWSUnidentifiedDelivery.proto diff --git a/SessionProtocolKit/Signal/Protos/OWSUnidentifiedDelivery.pb.swift b/SessionProtocolKit/Signal/Protos/OWSUnidentifiedDelivery.pb.swift new file mode 100644 index 000000000..ad0de59b8 --- /dev/null +++ b/SessionProtocolKit/Signal/Protos/OWSUnidentifiedDelivery.pb.swift @@ -0,0 +1,571 @@ +// DO NOT EDIT. +// swift-format-ignore-file +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: OWSUnidentifiedDelivery.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +//* +// Copyright (C) 2014-2016 Open Whisper Systems +// +// Licensed according to the LICENSE file in this repository. + +/// iOS - since we use a modern proto-compiler, we must specify +/// the legacy proto format. + +import Foundation +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that you are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +struct SMKProtos_ServerCertificate { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// @required + var certificate: Data { + get {return _certificate ?? SwiftProtobuf.Internal.emptyData} + set {_certificate = newValue} + } + /// Returns true if `certificate` has been explicitly set. + var hasCertificate: Bool {return self._certificate != nil} + /// Clears the value of `certificate`. Subsequent reads from it will return its default value. + mutating func clearCertificate() {self._certificate = nil} + + /// @required + var signature: Data { + get {return _signature ?? SwiftProtobuf.Internal.emptyData} + set {_signature = newValue} + } + /// Returns true if `signature` has been explicitly set. + var hasSignature: Bool {return self._signature != nil} + /// Clears the value of `signature`. Subsequent reads from it will return its default value. + mutating func clearSignature() {self._signature = nil} + + var unknownFields = SwiftProtobuf.UnknownStorage() + + struct Certificate { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// @required + var id: UInt32 { + get {return _id ?? 0} + set {_id = newValue} + } + /// Returns true if `id` has been explicitly set. + var hasID: Bool {return self._id != nil} + /// Clears the value of `id`. Subsequent reads from it will return its default value. + mutating func clearID() {self._id = nil} + + /// @required + var key: Data { + get {return _key ?? SwiftProtobuf.Internal.emptyData} + set {_key = newValue} + } + /// Returns true if `key` has been explicitly set. + var hasKey: Bool {return self._key != nil} + /// Clears the value of `key`. Subsequent reads from it will return its default value. + mutating func clearKey() {self._key = nil} + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} + + fileprivate var _id: UInt32? = nil + fileprivate var _key: Data? = nil + } + + init() {} + + fileprivate var _certificate: Data? = nil + fileprivate var _signature: Data? = nil +} + +struct SMKProtos_SenderCertificate { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// @required + var sender: String { + get {return _sender ?? String()} + set {_sender = newValue} + } + /// Returns true if `sender` has been explicitly set. + var hasSender: Bool {return self._sender != nil} + /// Clears the value of `sender`. Subsequent reads from it will return its default value. + mutating func clearSender() {self._sender = nil} + + /// @required + var senderDevice: UInt32 { + get {return _senderDevice ?? 0} + set {_senderDevice = newValue} + } + /// Returns true if `senderDevice` has been explicitly set. + var hasSenderDevice: Bool {return self._senderDevice != nil} + /// Clears the value of `senderDevice`. Subsequent reads from it will return its default value. + mutating func clearSenderDevice() {self._senderDevice = nil} + + var unknownFields = SwiftProtobuf.UnknownStorage() + + struct Certificate { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// @required + var sender: String { + get {return _sender ?? String()} + set {_sender = newValue} + } + /// Returns true if `sender` has been explicitly set. + var hasSender: Bool {return self._sender != nil} + /// Clears the value of `sender`. Subsequent reads from it will return its default value. + mutating func clearSender() {self._sender = nil} + + /// @required + var senderDevice: UInt32 { + get {return _senderDevice ?? 0} + set {_senderDevice = newValue} + } + /// Returns true if `senderDevice` has been explicitly set. + var hasSenderDevice: Bool {return self._senderDevice != nil} + /// Clears the value of `senderDevice`. Subsequent reads from it will return its default value. + mutating func clearSenderDevice() {self._senderDevice = nil} + + /// @required + var expires: UInt64 { + get {return _expires ?? 0} + set {_expires = newValue} + } + /// Returns true if `expires` has been explicitly set. + var hasExpires: Bool {return self._expires != nil} + /// Clears the value of `expires`. Subsequent reads from it will return its default value. + mutating func clearExpires() {self._expires = nil} + + /// @required + var identityKey: Data { + get {return _identityKey ?? SwiftProtobuf.Internal.emptyData} + set {_identityKey = newValue} + } + /// Returns true if `identityKey` has been explicitly set. + var hasIdentityKey: Bool {return self._identityKey != nil} + /// Clears the value of `identityKey`. Subsequent reads from it will return its default value. + mutating func clearIdentityKey() {self._identityKey = nil} + + /// @required + var signer: SMKProtos_ServerCertificate { + get {return _signer ?? SMKProtos_ServerCertificate()} + set {_signer = newValue} + } + /// Returns true if `signer` has been explicitly set. + var hasSigner: Bool {return self._signer != nil} + /// Clears the value of `signer`. Subsequent reads from it will return its default value. + mutating func clearSigner() {self._signer = nil} + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} + + fileprivate var _sender: String? = nil + fileprivate var _senderDevice: UInt32? = nil + fileprivate var _expires: UInt64? = nil + fileprivate var _identityKey: Data? = nil + fileprivate var _signer: SMKProtos_ServerCertificate? = nil + } + + init() {} + + fileprivate var _sender: String? = nil + fileprivate var _senderDevice: UInt32? = nil +} + +struct SMKProtos_UnidentifiedSenderMessage { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// @required + var ephemeralPublic: Data { + get {return _ephemeralPublic ?? SwiftProtobuf.Internal.emptyData} + set {_ephemeralPublic = newValue} + } + /// Returns true if `ephemeralPublic` has been explicitly set. + var hasEphemeralPublic: Bool {return self._ephemeralPublic != nil} + /// Clears the value of `ephemeralPublic`. Subsequent reads from it will return its default value. + mutating func clearEphemeralPublic() {self._ephemeralPublic = nil} + + /// @required + var encryptedStatic: Data { + get {return _encryptedStatic ?? SwiftProtobuf.Internal.emptyData} + set {_encryptedStatic = newValue} + } + /// Returns true if `encryptedStatic` has been explicitly set. + var hasEncryptedStatic: Bool {return self._encryptedStatic != nil} + /// Clears the value of `encryptedStatic`. Subsequent reads from it will return its default value. + mutating func clearEncryptedStatic() {self._encryptedStatic = nil} + + /// @required + var encryptedMessage: Data { + get {return _encryptedMessage ?? SwiftProtobuf.Internal.emptyData} + set {_encryptedMessage = newValue} + } + /// Returns true if `encryptedMessage` has been explicitly set. + var hasEncryptedMessage: Bool {return self._encryptedMessage != nil} + /// Clears the value of `encryptedMessage`. Subsequent reads from it will return its default value. + mutating func clearEncryptedMessage() {self._encryptedMessage = nil} + + var unknownFields = SwiftProtobuf.UnknownStorage() + + struct Message { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// @required + var type: SMKProtos_UnidentifiedSenderMessage.Message.TypeEnum { + get {return _type ?? .prekeyMessage} + set {_type = newValue} + } + /// Returns true if `type` has been explicitly set. + var hasType: Bool {return self._type != nil} + /// Clears the value of `type`. Subsequent reads from it will return its default value. + mutating func clearType() {self._type = nil} + + /// @required + var senderCertificate: SMKProtos_SenderCertificate { + get {return _senderCertificate ?? SMKProtos_SenderCertificate()} + set {_senderCertificate = newValue} + } + /// Returns true if `senderCertificate` has been explicitly set. + var hasSenderCertificate: Bool {return self._senderCertificate != nil} + /// Clears the value of `senderCertificate`. Subsequent reads from it will return its default value. + mutating func clearSenderCertificate() {self._senderCertificate = nil} + + /// @required + var content: Data { + get {return _content ?? SwiftProtobuf.Internal.emptyData} + set {_content = newValue} + } + /// Returns true if `content` has been explicitly set. + var hasContent: Bool {return self._content != nil} + /// Clears the value of `content`. Subsequent reads from it will return its default value. + mutating func clearContent() {self._content = nil} + + var unknownFields = SwiftProtobuf.UnknownStorage() + + enum TypeEnum: SwiftProtobuf.Enum { + typealias RawValue = Int + case prekeyMessage // = 1 + case message // = 2 + case fallbackMessage // = 3 + + init() { + self = .prekeyMessage + } + + init?(rawValue: Int) { + switch rawValue { + case 1: self = .prekeyMessage + case 2: self = .message + case 3: self = .fallbackMessage + default: return nil + } + } + + var rawValue: Int { + switch self { + case .prekeyMessage: return 1 + case .message: return 2 + case .fallbackMessage: return 3 + } + } + + } + + init() {} + + fileprivate var _type: SMKProtos_UnidentifiedSenderMessage.Message.TypeEnum? = nil + fileprivate var _senderCertificate: SMKProtos_SenderCertificate? = nil + fileprivate var _content: Data? = nil + } + + init() {} + + fileprivate var _ephemeralPublic: Data? = nil + fileprivate var _encryptedStatic: Data? = nil + fileprivate var _encryptedMessage: Data? = nil +} + +#if swift(>=4.2) + +extension SMKProtos_UnidentifiedSenderMessage.Message.TypeEnum: CaseIterable { + // Support synthesized by the compiler. +} + +#endif // swift(>=4.2) + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "SMKProtos" + +extension SMKProtos_ServerCertificate: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".ServerCertificate" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "certificate"), + 2: .same(proto: "signature"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + switch fieldNumber { + case 1: try decoder.decodeSingularBytesField(value: &self._certificate) + case 2: try decoder.decodeSingularBytesField(value: &self._signature) + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if let v = self._certificate { + try visitor.visitSingularBytesField(value: v, fieldNumber: 1) + } + if let v = self._signature { + try visitor.visitSingularBytesField(value: v, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: SMKProtos_ServerCertificate, rhs: SMKProtos_ServerCertificate) -> Bool { + if lhs._certificate != rhs._certificate {return false} + if lhs._signature != rhs._signature {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension SMKProtos_ServerCertificate.Certificate: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = SMKProtos_ServerCertificate.protoMessageName + ".Certificate" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "id"), + 2: .same(proto: "key"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + switch fieldNumber { + case 1: try decoder.decodeSingularUInt32Field(value: &self._id) + case 2: try decoder.decodeSingularBytesField(value: &self._key) + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if let v = self._id { + try visitor.visitSingularUInt32Field(value: v, fieldNumber: 1) + } + if let v = self._key { + try visitor.visitSingularBytesField(value: v, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: SMKProtos_ServerCertificate.Certificate, rhs: SMKProtos_ServerCertificate.Certificate) -> Bool { + if lhs._id != rhs._id {return false} + if lhs._key != rhs._key {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension SMKProtos_SenderCertificate: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".SenderCertificate" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "sender"), + 2: .same(proto: "senderDevice"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + switch fieldNumber { + case 1: try decoder.decodeSingularStringField(value: &self._sender) + case 2: try decoder.decodeSingularUInt32Field(value: &self._senderDevice) + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if let v = self._sender { + try visitor.visitSingularStringField(value: v, fieldNumber: 1) + } + if let v = self._senderDevice { + try visitor.visitSingularUInt32Field(value: v, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: SMKProtos_SenderCertificate, rhs: SMKProtos_SenderCertificate) -> Bool { + if lhs._sender != rhs._sender {return false} + if lhs._senderDevice != rhs._senderDevice {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension SMKProtos_SenderCertificate.Certificate: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = SMKProtos_SenderCertificate.protoMessageName + ".Certificate" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "sender"), + 2: .same(proto: "senderDevice"), + 3: .same(proto: "expires"), + 4: .same(proto: "identityKey"), + 5: .same(proto: "signer"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + switch fieldNumber { + case 1: try decoder.decodeSingularStringField(value: &self._sender) + case 2: try decoder.decodeSingularUInt32Field(value: &self._senderDevice) + case 3: try decoder.decodeSingularFixed64Field(value: &self._expires) + case 4: try decoder.decodeSingularBytesField(value: &self._identityKey) + case 5: try decoder.decodeSingularMessageField(value: &self._signer) + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if let v = self._sender { + try visitor.visitSingularStringField(value: v, fieldNumber: 1) + } + if let v = self._senderDevice { + try visitor.visitSingularUInt32Field(value: v, fieldNumber: 2) + } + if let v = self._expires { + try visitor.visitSingularFixed64Field(value: v, fieldNumber: 3) + } + if let v = self._identityKey { + try visitor.visitSingularBytesField(value: v, fieldNumber: 4) + } + if let v = self._signer { + try visitor.visitSingularMessageField(value: v, fieldNumber: 5) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: SMKProtos_SenderCertificate.Certificate, rhs: SMKProtos_SenderCertificate.Certificate) -> Bool { + if lhs._sender != rhs._sender {return false} + if lhs._senderDevice != rhs._senderDevice {return false} + if lhs._expires != rhs._expires {return false} + if lhs._identityKey != rhs._identityKey {return false} + if lhs._signer != rhs._signer {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension SMKProtos_UnidentifiedSenderMessage: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".UnidentifiedSenderMessage" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "ephemeralPublic"), + 2: .same(proto: "encryptedStatic"), + 3: .same(proto: "encryptedMessage"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + switch fieldNumber { + case 1: try decoder.decodeSingularBytesField(value: &self._ephemeralPublic) + case 2: try decoder.decodeSingularBytesField(value: &self._encryptedStatic) + case 3: try decoder.decodeSingularBytesField(value: &self._encryptedMessage) + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if let v = self._ephemeralPublic { + try visitor.visitSingularBytesField(value: v, fieldNumber: 1) + } + if let v = self._encryptedStatic { + try visitor.visitSingularBytesField(value: v, fieldNumber: 2) + } + if let v = self._encryptedMessage { + try visitor.visitSingularBytesField(value: v, fieldNumber: 3) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: SMKProtos_UnidentifiedSenderMessage, rhs: SMKProtos_UnidentifiedSenderMessage) -> Bool { + if lhs._ephemeralPublic != rhs._ephemeralPublic {return false} + if lhs._encryptedStatic != rhs._encryptedStatic {return false} + if lhs._encryptedMessage != rhs._encryptedMessage {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension SMKProtos_UnidentifiedSenderMessage.Message: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = SMKProtos_UnidentifiedSenderMessage.protoMessageName + ".Message" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "type"), + 2: .same(proto: "senderCertificate"), + 3: .same(proto: "content"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + switch fieldNumber { + case 1: try decoder.decodeSingularEnumField(value: &self._type) + case 2: try decoder.decodeSingularMessageField(value: &self._senderCertificate) + case 3: try decoder.decodeSingularBytesField(value: &self._content) + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if let v = self._type { + try visitor.visitSingularEnumField(value: v, fieldNumber: 1) + } + if let v = self._senderCertificate { + try visitor.visitSingularMessageField(value: v, fieldNumber: 2) + } + if let v = self._content { + try visitor.visitSingularBytesField(value: v, fieldNumber: 3) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: SMKProtos_UnidentifiedSenderMessage.Message, rhs: SMKProtos_UnidentifiedSenderMessage.Message) -> Bool { + if lhs._type != rhs._type {return false} + if lhs._senderCertificate != rhs._senderCertificate {return false} + if lhs._content != rhs._content {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension SMKProtos_UnidentifiedSenderMessage.Message.TypeEnum: SwiftProtobuf._ProtoNameProviding { + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "PREKEY_MESSAGE"), + 2: .same(proto: "MESSAGE"), + 3: .same(proto: "FALLBACK_MESSAGE"), + ] +} diff --git a/SessionProtocolKit/Signal/Protos/OWSUnidentifiedDelivery.proto b/SessionProtocolKit/Signal/Protos/OWSUnidentifiedDelivery.proto new file mode 100644 index 000000000..64d1ff045 --- /dev/null +++ b/SessionProtocolKit/Signal/Protos/OWSUnidentifiedDelivery.proto @@ -0,0 +1,73 @@ +/** + * Copyright (C) 2014-2016 Open Whisper Systems + * + * Licensed according to the LICENSE file in this repository. + */ + +// iOS - since we use a modern proto-compiler, we must specify +// the legacy proto format. +syntax = "proto2"; + +// iOS - package name determines class prefix +package SMKProtos; + +option java_package = "org.signal.libsignal.metadata"; +option java_outer_classname = "SignalProtos"; + +message ServerCertificate { + message Certificate { + // @required + optional uint32 id = 1; + // @required + optional bytes key = 2; + } + + // @required + optional bytes certificate = 1; + // @required + optional bytes signature = 2; +} + +message SenderCertificate { + message Certificate { + // @required + optional string sender = 1; + // @required + optional uint32 senderDevice = 2; + // @required + optional fixed64 expires = 3; + // @required + optional bytes identityKey = 4; + // @required + optional ServerCertificate signer = 5; + } + + // @required + optional string sender = 1; + // @required + optional uint32 senderDevice = 2; +} + +message UnidentifiedSenderMessage { + message Message { + enum Type { + PREKEY_MESSAGE = 1; + MESSAGE = 2; + FALLBACK_MESSAGE = 3; + } + + // @required + optional Type type = 1; + // @required + optional SenderCertificate senderCertificate = 2; + // @required + optional bytes content = 3; + } + + // @required + optional bytes ephemeralPublic = 1; + // @required + optional bytes encryptedStatic = 2; + // @required + optional bytes encryptedMessage = 3; +} diff --git a/SessionProtocolKit/Signal/Protos/SMKProto.swift b/SessionProtocolKit/Signal/Protos/SMKProto.swift new file mode 100644 index 000000000..182e86693 --- /dev/null +++ b/SessionProtocolKit/Signal/Protos/SMKProto.swift @@ -0,0 +1,782 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +// WARNING: This code is generated. Only edit within the markers. + +public enum SMKProtoError: Error { + case invalidProtobuf(description: String) +} + +// MARK: - SMKProtoServerCertificateCertificate + +@objc public class SMKProtoServerCertificateCertificate: NSObject { + + // MARK: - SMKProtoServerCertificateCertificateBuilder + + @objc public class func builder(id: UInt32, key: Data) -> SMKProtoServerCertificateCertificateBuilder { + return SMKProtoServerCertificateCertificateBuilder(id: id, key: key) + } + + // asBuilder() constructs a builder that reflects the proto's contents. + @objc public func asBuilder() -> SMKProtoServerCertificateCertificateBuilder { + let builder = SMKProtoServerCertificateCertificateBuilder(id: id, key: key) + return builder + } + + @objc public class SMKProtoServerCertificateCertificateBuilder: NSObject { + + private var proto = SMKProtos_ServerCertificate.Certificate() + + @objc fileprivate override init() {} + + @objc fileprivate init(id: UInt32, key: Data) { + super.init() + + setId(id) + setKey(key) + } + + @objc public func setId(_ valueParam: UInt32) { + proto.id = valueParam + } + + @objc public func setKey(_ valueParam: Data) { + proto.key = valueParam + } + + @objc public func build() throws -> SMKProtoServerCertificateCertificate { + return try SMKProtoServerCertificateCertificate.parseProto(proto) + } + + @objc public func buildSerializedData() throws -> Data { + return try SMKProtoServerCertificateCertificate.parseProto(proto).serializedData() + } + } + + fileprivate let proto: SMKProtos_ServerCertificate.Certificate + + @objc public let id: UInt32 + + @objc public let key: Data + + private init(proto: SMKProtos_ServerCertificate.Certificate, + id: UInt32, + key: Data) { + self.proto = proto + self.id = id + self.key = key + } + + @objc + public func serializedData() throws -> Data { + return try self.proto.serializedData() + } + + @objc public class func parseData(_ serializedData: Data) throws -> SMKProtoServerCertificateCertificate { + let proto = try SMKProtos_ServerCertificate.Certificate(serializedData: serializedData) + return try parseProto(proto) + } + + fileprivate class func parseProto(_ proto: SMKProtos_ServerCertificate.Certificate) throws -> SMKProtoServerCertificateCertificate { + guard proto.hasID else { + throw SMKProtoError.invalidProtobuf(description: "\(logTag) missing required field: id") + } + let id = proto.id + + guard proto.hasKey else { + throw SMKProtoError.invalidProtobuf(description: "\(logTag) missing required field: key") + } + let key = proto.key + + // MARK: - Begin Validation Logic for SMKProtoServerCertificateCertificate - + + // MARK: - End Validation Logic for SMKProtoServerCertificateCertificate - + + let result = SMKProtoServerCertificateCertificate(proto: proto, + id: id, + key: key) + return result + } + + @objc public override var debugDescription: String { + return "\(proto)" + } +} + +#if DEBUG + +extension SMKProtoServerCertificateCertificate { + @objc public func serializedDataIgnoringErrors() -> Data? { + return try! self.serializedData() + } +} + +extension SMKProtoServerCertificateCertificate.SMKProtoServerCertificateCertificateBuilder { + @objc public func buildIgnoringErrors() -> SMKProtoServerCertificateCertificate? { + return try! self.build() + } +} + +#endif + +// MARK: - SMKProtoServerCertificate + +@objc public class SMKProtoServerCertificate: NSObject { + + // MARK: - SMKProtoServerCertificateBuilder + + @objc public class func builder(certificate: Data, signature: Data) -> SMKProtoServerCertificateBuilder { + return SMKProtoServerCertificateBuilder(certificate: certificate, signature: signature) + } + + // asBuilder() constructs a builder that reflects the proto's contents. + @objc public func asBuilder() -> SMKProtoServerCertificateBuilder { + let builder = SMKProtoServerCertificateBuilder(certificate: certificate, signature: signature) + return builder + } + + @objc public class SMKProtoServerCertificateBuilder: NSObject { + + private var proto = SMKProtos_ServerCertificate() + + @objc fileprivate override init() {} + + @objc fileprivate init(certificate: Data, signature: Data) { + super.init() + + setCertificate(certificate) + setSignature(signature) + } + + @objc public func setCertificate(_ valueParam: Data) { + proto.certificate = valueParam + } + + @objc public func setSignature(_ valueParam: Data) { + proto.signature = valueParam + } + + @objc public func build() throws -> SMKProtoServerCertificate { + return try SMKProtoServerCertificate.parseProto(proto) + } + + @objc public func buildSerializedData() throws -> Data { + return try SMKProtoServerCertificate.parseProto(proto).serializedData() + } + } + + fileprivate let proto: SMKProtos_ServerCertificate + + @objc public let certificate: Data + + @objc public let signature: Data + + private init(proto: SMKProtos_ServerCertificate, + certificate: Data, + signature: Data) { + self.proto = proto + self.certificate = certificate + self.signature = signature + } + + @objc + public func serializedData() throws -> Data { + return try self.proto.serializedData() + } + + @objc public class func parseData(_ serializedData: Data) throws -> SMKProtoServerCertificate { + let proto = try SMKProtos_ServerCertificate(serializedData: serializedData) + return try parseProto(proto) + } + + fileprivate class func parseProto(_ proto: SMKProtos_ServerCertificate) throws -> SMKProtoServerCertificate { + guard proto.hasCertificate else { + throw SMKProtoError.invalidProtobuf(description: "\(logTag) missing required field: certificate") + } + let certificate = proto.certificate + + guard proto.hasSignature else { + throw SMKProtoError.invalidProtobuf(description: "\(logTag) missing required field: signature") + } + let signature = proto.signature + + // MARK: - Begin Validation Logic for SMKProtoServerCertificate - + + // MARK: - End Validation Logic for SMKProtoServerCertificate - + + let result = SMKProtoServerCertificate(proto: proto, + certificate: certificate, + signature: signature) + return result + } + + @objc public override var debugDescription: String { + return "\(proto)" + } +} + +#if DEBUG + +extension SMKProtoServerCertificate { + @objc public func serializedDataIgnoringErrors() -> Data? { + return try! self.serializedData() + } +} + +extension SMKProtoServerCertificate.SMKProtoServerCertificateBuilder { + @objc public func buildIgnoringErrors() -> SMKProtoServerCertificate? { + return try! self.build() + } +} + +#endif + +// MARK: - SMKProtoSenderCertificateCertificate + +@objc public class SMKProtoSenderCertificateCertificate: NSObject { + + // MARK: - SMKProtoSenderCertificateCertificateBuilder + + @objc public class func builder(sender: String, senderDevice: UInt32, expires: UInt64, identityKey: Data, signer: SMKProtoServerCertificate) -> SMKProtoSenderCertificateCertificateBuilder { + return SMKProtoSenderCertificateCertificateBuilder(sender: sender, senderDevice: senderDevice, expires: expires, identityKey: identityKey, signer: signer) + } + + // asBuilder() constructs a builder that reflects the proto's contents. + @objc public func asBuilder() -> SMKProtoSenderCertificateCertificateBuilder { + let builder = SMKProtoSenderCertificateCertificateBuilder(sender: sender, senderDevice: senderDevice, expires: expires, identityKey: identityKey, signer: signer) + return builder + } + + @objc public class SMKProtoSenderCertificateCertificateBuilder: NSObject { + + private var proto = SMKProtos_SenderCertificate.Certificate() + + @objc fileprivate override init() {} + + @objc fileprivate init(sender: String, senderDevice: UInt32, expires: UInt64, identityKey: Data, signer: SMKProtoServerCertificate) { + super.init() + + setSender(sender) + setSenderDevice(senderDevice) + setExpires(expires) + setIdentityKey(identityKey) + setSigner(signer) + } + + @objc public func setSender(_ valueParam: String) { + proto.sender = valueParam + } + + @objc public func setSenderDevice(_ valueParam: UInt32) { + proto.senderDevice = valueParam + } + + @objc public func setExpires(_ valueParam: UInt64) { + proto.expires = valueParam + } + + @objc public func setIdentityKey(_ valueParam: Data) { + proto.identityKey = valueParam + } + + @objc public func setSigner(_ valueParam: SMKProtoServerCertificate) { + proto.signer = valueParam.proto + } + + @objc public func build() throws -> SMKProtoSenderCertificateCertificate { + return try SMKProtoSenderCertificateCertificate.parseProto(proto) + } + + @objc public func buildSerializedData() throws -> Data { + return try SMKProtoSenderCertificateCertificate.parseProto(proto).serializedData() + } + } + + fileprivate let proto: SMKProtos_SenderCertificate.Certificate + + @objc public let sender: String + + @objc public let senderDevice: UInt32 + + @objc public let expires: UInt64 + + @objc public let identityKey: Data + + @objc public let signer: SMKProtoServerCertificate + + private init(proto: SMKProtos_SenderCertificate.Certificate, + sender: String, + senderDevice: UInt32, + expires: UInt64, + identityKey: Data, + signer: SMKProtoServerCertificate) { + self.proto = proto + self.sender = sender + self.senderDevice = senderDevice + self.expires = expires + self.identityKey = identityKey + self.signer = signer + } + + @objc + public func serializedData() throws -> Data { + return try self.proto.serializedData() + } + + @objc public class func parseData(_ serializedData: Data) throws -> SMKProtoSenderCertificateCertificate { + let proto = try SMKProtos_SenderCertificate.Certificate(serializedData: serializedData) + return try parseProto(proto) + } + + fileprivate class func parseProto(_ proto: SMKProtos_SenderCertificate.Certificate) throws -> SMKProtoSenderCertificateCertificate { + guard proto.hasSender else { + throw SMKProtoError.invalidProtobuf(description: "\(logTag) missing required field: sender") + } + let sender = proto.sender + + guard proto.hasSenderDevice else { + throw SMKProtoError.invalidProtobuf(description: "\(logTag) missing required field: senderDevice") + } + let senderDevice = proto.senderDevice + + guard proto.hasExpires else { + throw SMKProtoError.invalidProtobuf(description: "\(logTag) missing required field: expires") + } + let expires = proto.expires + + guard proto.hasIdentityKey else { + throw SMKProtoError.invalidProtobuf(description: "\(logTag) missing required field: identityKey") + } + let identityKey = proto.identityKey + + guard proto.hasSigner else { + throw SMKProtoError.invalidProtobuf(description: "\(logTag) missing required field: signer") + } + let signer = try SMKProtoServerCertificate.parseProto(proto.signer) + + // MARK: - Begin Validation Logic for SMKProtoSenderCertificateCertificate - + + // MARK: - End Validation Logic for SMKProtoSenderCertificateCertificate - + + let result = SMKProtoSenderCertificateCertificate(proto: proto, + sender: sender, + senderDevice: senderDevice, + expires: expires, + identityKey: identityKey, + signer: signer) + return result + } + + @objc public override var debugDescription: String { + return "\(proto)" + } +} + +#if DEBUG + +extension SMKProtoSenderCertificateCertificate { + @objc public func serializedDataIgnoringErrors() -> Data? { + return try! self.serializedData() + } +} + +extension SMKProtoSenderCertificateCertificate.SMKProtoSenderCertificateCertificateBuilder { + @objc public func buildIgnoringErrors() -> SMKProtoSenderCertificateCertificate? { + return try! self.build() + } +} + +#endif + +// MARK: - SMKProtoSenderCertificate + +@objc public class SMKProtoSenderCertificate: NSObject { + + // MARK: - SMKProtoSenderCertificateBuilder + + @objc public class func builder(sender: String, senderDevice: UInt32) -> SMKProtoSenderCertificateBuilder { + return SMKProtoSenderCertificateBuilder(sender: sender, senderDevice: senderDevice) + } + + // asBuilder() constructs a builder that reflects the proto's contents. + @objc public func asBuilder() -> SMKProtoSenderCertificateBuilder { + let builder = SMKProtoSenderCertificateBuilder(sender: sender, senderDevice: senderDevice) + return builder + } + + @objc public class SMKProtoSenderCertificateBuilder: NSObject { + + private var proto = SMKProtos_SenderCertificate() + + @objc fileprivate override init() {} + + @objc fileprivate init(sender: String, senderDevice: UInt32) { + super.init() + + setSender(sender) + setSenderDevice(senderDevice) + } + + @objc public func setSender(_ valueParam: String) { + proto.sender = valueParam + } + + @objc public func setSenderDevice(_ valueParam: UInt32) { + proto.senderDevice = valueParam + } + + @objc public func build() throws -> SMKProtoSenderCertificate { + return try SMKProtoSenderCertificate.parseProto(proto) + } + + @objc public func buildSerializedData() throws -> Data { + return try SMKProtoSenderCertificate.parseProto(proto).serializedData() + } + } + + fileprivate let proto: SMKProtos_SenderCertificate + + @objc public let sender: String + + @objc public let senderDevice: UInt32 + + private init(proto: SMKProtos_SenderCertificate, + sender: String, + senderDevice: UInt32) { + self.proto = proto + self.sender = sender + self.senderDevice = senderDevice + } + + @objc + public func serializedData() throws -> Data { + return try self.proto.serializedData() + } + + @objc public class func parseData(_ serializedData: Data) throws -> SMKProtoSenderCertificate { + let proto = try SMKProtos_SenderCertificate(serializedData: serializedData) + return try parseProto(proto) + } + + fileprivate class func parseProto(_ proto: SMKProtos_SenderCertificate) throws -> SMKProtoSenderCertificate { + guard proto.hasSender else { + throw SMKProtoError.invalidProtobuf(description: "\(logTag) missing required field: sender") + } + let sender = proto.sender + + guard proto.hasSenderDevice else { + throw SMKProtoError.invalidProtobuf(description: "\(logTag) missing required field: senderDevice") + } + let senderDevice = proto.senderDevice + + // MARK: - Begin Validation Logic for SMKProtoSenderCertificate - + + // MARK: - End Validation Logic for SMKProtoSenderCertificate - + + let result = SMKProtoSenderCertificate(proto: proto, + sender: sender, + senderDevice: senderDevice) + return result + } + + @objc public override var debugDescription: String { + return "\(proto)" + } +} + +#if DEBUG + +extension SMKProtoSenderCertificate { + @objc public func serializedDataIgnoringErrors() -> Data? { + return try! self.serializedData() + } +} + +extension SMKProtoSenderCertificate.SMKProtoSenderCertificateBuilder { + @objc public func buildIgnoringErrors() -> SMKProtoSenderCertificate? { + return try! self.build() + } +} + +#endif + +// MARK: - SMKProtoUnidentifiedSenderMessageMessage + +@objc public class SMKProtoUnidentifiedSenderMessageMessage: NSObject { + + // MARK: - SMKProtoUnidentifiedSenderMessageMessageType + + @objc public enum SMKProtoUnidentifiedSenderMessageMessageType: Int32 { + case prekeyMessage = 1 + case message = 2 + case fallbackMessage = 3 + } + + private class func SMKProtoUnidentifiedSenderMessageMessageTypeWrap(_ value: SMKProtos_UnidentifiedSenderMessage.Message.TypeEnum) -> SMKProtoUnidentifiedSenderMessageMessageType { + switch value { + case .prekeyMessage: return .prekeyMessage + case .message: return .message + case .fallbackMessage: return .fallbackMessage + } + } + + private class func SMKProtoUnidentifiedSenderMessageMessageTypeUnwrap(_ value: SMKProtoUnidentifiedSenderMessageMessageType) -> SMKProtos_UnidentifiedSenderMessage.Message.TypeEnum { + switch value { + case .prekeyMessage: return .prekeyMessage + case .message: return .message + case .fallbackMessage: return .fallbackMessage + } + } + + // MARK: - SMKProtoUnidentifiedSenderMessageMessageBuilder + + @objc public class func builder(type: SMKProtoUnidentifiedSenderMessageMessageType, senderCertificate: SMKProtoSenderCertificate, content: Data) -> SMKProtoUnidentifiedSenderMessageMessageBuilder { + return SMKProtoUnidentifiedSenderMessageMessageBuilder(type: type, senderCertificate: senderCertificate, content: content) + } + + // asBuilder() constructs a builder that reflects the proto's contents. + @objc public func asBuilder() -> SMKProtoUnidentifiedSenderMessageMessageBuilder { + let builder = SMKProtoUnidentifiedSenderMessageMessageBuilder(type: type, senderCertificate: senderCertificate, content: content) + return builder + } + + @objc public class SMKProtoUnidentifiedSenderMessageMessageBuilder: NSObject { + + private var proto = SMKProtos_UnidentifiedSenderMessage.Message() + + @objc fileprivate override init() {} + + @objc fileprivate init(type: SMKProtoUnidentifiedSenderMessageMessageType, senderCertificate: SMKProtoSenderCertificate, content: Data) { + super.init() + + setType(type) + setSenderCertificate(senderCertificate) + setContent(content) + } + + @objc public func setType(_ valueParam: SMKProtoUnidentifiedSenderMessageMessageType) { + proto.type = SMKProtoUnidentifiedSenderMessageMessageTypeUnwrap(valueParam) + } + + @objc public func setSenderCertificate(_ valueParam: SMKProtoSenderCertificate) { + proto.senderCertificate = valueParam.proto + } + + @objc public func setContent(_ valueParam: Data) { + proto.content = valueParam + } + + @objc public func build() throws -> SMKProtoUnidentifiedSenderMessageMessage { + return try SMKProtoUnidentifiedSenderMessageMessage.parseProto(proto) + } + + @objc public func buildSerializedData() throws -> Data { + return try SMKProtoUnidentifiedSenderMessageMessage.parseProto(proto).serializedData() + } + } + + fileprivate let proto: SMKProtos_UnidentifiedSenderMessage.Message + + @objc public let type: SMKProtoUnidentifiedSenderMessageMessageType + + @objc public let senderCertificate: SMKProtoSenderCertificate + + @objc public let content: Data + + private init(proto: SMKProtos_UnidentifiedSenderMessage.Message, + type: SMKProtoUnidentifiedSenderMessageMessageType, + senderCertificate: SMKProtoSenderCertificate, + content: Data) { + self.proto = proto + self.type = type + self.senderCertificate = senderCertificate + self.content = content + } + + @objc + public func serializedData() throws -> Data { + return try self.proto.serializedData() + } + + @objc public class func parseData(_ serializedData: Data) throws -> SMKProtoUnidentifiedSenderMessageMessage { + let proto = try SMKProtos_UnidentifiedSenderMessage.Message(serializedData: serializedData) + return try parseProto(proto) + } + + fileprivate class func parseProto(_ proto: SMKProtos_UnidentifiedSenderMessage.Message) throws -> SMKProtoUnidentifiedSenderMessageMessage { + guard proto.hasType else { + throw SMKProtoError.invalidProtobuf(description: "\(logTag) missing required field: type") + } + let type = SMKProtoUnidentifiedSenderMessageMessageTypeWrap(proto.type) + + guard proto.hasSenderCertificate else { + throw SMKProtoError.invalidProtobuf(description: "\(logTag) missing required field: senderCertificate") + } + let senderCertificate = try SMKProtoSenderCertificate.parseProto(proto.senderCertificate) + + guard proto.hasContent else { + throw SMKProtoError.invalidProtobuf(description: "\(logTag) missing required field: content") + } + let content = proto.content + + // MARK: - Begin Validation Logic for SMKProtoUnidentifiedSenderMessageMessage - + + // MARK: - End Validation Logic for SMKProtoUnidentifiedSenderMessageMessage - + + let result = SMKProtoUnidentifiedSenderMessageMessage(proto: proto, + type: type, + senderCertificate: senderCertificate, + content: content) + return result + } + + @objc public override var debugDescription: String { + return "\(proto)" + } +} + +#if DEBUG + +extension SMKProtoUnidentifiedSenderMessageMessage { + @objc public func serializedDataIgnoringErrors() -> Data? { + return try! self.serializedData() + } +} + +extension SMKProtoUnidentifiedSenderMessageMessage.SMKProtoUnidentifiedSenderMessageMessageBuilder { + @objc public func buildIgnoringErrors() -> SMKProtoUnidentifiedSenderMessageMessage? { + return try! self.build() + } +} + +#endif + +// MARK: - SMKProtoUnidentifiedSenderMessage + +@objc public class SMKProtoUnidentifiedSenderMessage: NSObject { + + // MARK: - SMKProtoUnidentifiedSenderMessageBuilder + + @objc public class func builder(ephemeralPublic: Data, encryptedStatic: Data, encryptedMessage: Data) -> SMKProtoUnidentifiedSenderMessageBuilder { + return SMKProtoUnidentifiedSenderMessageBuilder(ephemeralPublic: ephemeralPublic, encryptedStatic: encryptedStatic, encryptedMessage: encryptedMessage) + } + + // asBuilder() constructs a builder that reflects the proto's contents. + @objc public func asBuilder() -> SMKProtoUnidentifiedSenderMessageBuilder { + let builder = SMKProtoUnidentifiedSenderMessageBuilder(ephemeralPublic: ephemeralPublic, encryptedStatic: encryptedStatic, encryptedMessage: encryptedMessage) + return builder + } + + @objc public class SMKProtoUnidentifiedSenderMessageBuilder: NSObject { + + private var proto = SMKProtos_UnidentifiedSenderMessage() + + @objc fileprivate override init() {} + + @objc fileprivate init(ephemeralPublic: Data, encryptedStatic: Data, encryptedMessage: Data) { + super.init() + + setEphemeralPublic(ephemeralPublic) + setEncryptedStatic(encryptedStatic) + setEncryptedMessage(encryptedMessage) + } + + @objc public func setEphemeralPublic(_ valueParam: Data) { + proto.ephemeralPublic = valueParam + } + + @objc public func setEncryptedStatic(_ valueParam: Data) { + proto.encryptedStatic = valueParam + } + + @objc public func setEncryptedMessage(_ valueParam: Data) { + proto.encryptedMessage = valueParam + } + + @objc public func build() throws -> SMKProtoUnidentifiedSenderMessage { + return try SMKProtoUnidentifiedSenderMessage.parseProto(proto) + } + + @objc public func buildSerializedData() throws -> Data { + return try SMKProtoUnidentifiedSenderMessage.parseProto(proto).serializedData() + } + } + + fileprivate let proto: SMKProtos_UnidentifiedSenderMessage + + @objc public let ephemeralPublic: Data + + @objc public let encryptedStatic: Data + + @objc public let encryptedMessage: Data + + private init(proto: SMKProtos_UnidentifiedSenderMessage, + ephemeralPublic: Data, + encryptedStatic: Data, + encryptedMessage: Data) { + self.proto = proto + self.ephemeralPublic = ephemeralPublic + self.encryptedStatic = encryptedStatic + self.encryptedMessage = encryptedMessage + } + + @objc + public func serializedData() throws -> Data { + return try self.proto.serializedData() + } + + @objc public class func parseData(_ serializedData: Data) throws -> SMKProtoUnidentifiedSenderMessage { + let proto = try SMKProtos_UnidentifiedSenderMessage(serializedData: serializedData) + return try parseProto(proto) + } + + fileprivate class func parseProto(_ proto: SMKProtos_UnidentifiedSenderMessage) throws -> SMKProtoUnidentifiedSenderMessage { + guard proto.hasEphemeralPublic else { + throw SMKProtoError.invalidProtobuf(description: "\(logTag) missing required field: ephemeralPublic") + } + let ephemeralPublic = proto.ephemeralPublic + + guard proto.hasEncryptedStatic else { + throw SMKProtoError.invalidProtobuf(description: "\(logTag) missing required field: encryptedStatic") + } + let encryptedStatic = proto.encryptedStatic + + guard proto.hasEncryptedMessage else { + throw SMKProtoError.invalidProtobuf(description: "\(logTag) missing required field: encryptedMessage") + } + let encryptedMessage = proto.encryptedMessage + + // MARK: - Begin Validation Logic for SMKProtoUnidentifiedSenderMessage - + + // MARK: - End Validation Logic for SMKProtoUnidentifiedSenderMessage - + + let result = SMKProtoUnidentifiedSenderMessage(proto: proto, + ephemeralPublic: ephemeralPublic, + encryptedStatic: encryptedStatic, + encryptedMessage: encryptedMessage) + return result + } + + @objc public override var debugDescription: String { + return "\(proto)" + } +} + +#if DEBUG + +extension SMKProtoUnidentifiedSenderMessage { + @objc public func serializedDataIgnoringErrors() -> Data? { + return try! self.serializedData() + } +} + +extension SMKProtoUnidentifiedSenderMessage.SMKProtoUnidentifiedSenderMessageBuilder { + @objc public func buildIgnoringErrors() -> SMKProtoUnidentifiedSenderMessage? { + return try! self.build() + } +} + +#endif diff --git a/SessionProtocolKit/Signal/SMKCertificateValidator.swift b/SessionProtocolKit/Signal/SMKCertificateValidator.swift new file mode 100644 index 000000000..4f4a7b7d5 --- /dev/null +++ b/SessionProtocolKit/Signal/SMKCertificateValidator.swift @@ -0,0 +1,106 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +public enum SMKCertificateError: Error { + case invalidCertificate(description: String) +} + +@objc public protocol SMKCertificateValidator: class { + + @objc func throwswrapped_validate(senderCertificate: SMKSenderCertificate, validationTime: UInt64) throws + + @objc func throwswrapped_validate(serverCertificate: SMKServerCertificate) throws +} + +// See: https://github.com/signalapp/libsignal-metadata-java/blob/master/java/src/main/java/org/signal/libsignal/metadata/certificate/CertificateValidator.java +//public class CertificateValidator { +@objc public class SMKCertificateDefaultValidator: NSObject, SMKCertificateValidator { + +// @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") +// private static final Set REVOKED = new HashSet() {{ +// +// }}; + private static let kRevokedCertificateIds = Set() + +// +// private final ECPublicKey trustRoot; + private let trustRoot: ECPublicKey + +// public CertificateValidator(ECPublicKey trustRoot) { +// this.trustRoot = trustRoot; +// } + @objc public init(trustRoot: ECPublicKey ) { + self.trustRoot = trustRoot + } + +// public void validate(SenderCertificate certificate, long validationTime) throws InvalidCertificateException { + @objc public func throwswrapped_validate(senderCertificate: SMKSenderCertificate, validationTime: UInt64) throws { +// try { +// ServerCertificate serverCertificate = certificate.getSigner(); +// let serverCertificate = senderCertificate.signer + +// validate(serverCertificate); +// try throwswrapped_validate(serverCertificate: serverCertificate) + +// if (!Curve.verifySignature(serverCertificate.getKey(), certificate.getCertificate(), certificate.getSignature())) { +// throw new InvalidCertificateException("Signature failed"); +// } +// let certificateData = try senderCertificate.toProto().certificate +// guard try Ed25519.verifySignature(senderCertificate.signatureData, +// publicKey: serverCertificate.key.keyData, +// data: certificateData) else { +// Logger.error("Sender certificate signature verification failed.") +// let error = SMKCertificateError.invalidCertificate(description: "Sender certificate signature verification failed.") +// Logger.error("\(error)") +// throw error +// } + +// if (validationTime > certificate.getExpiration()) { +// throw new InvalidCertificateException("Certificate is expired"); +// } +// guard validationTime <= senderCertificate.expirationTimestamp else { +// let error = SMKCertificateError.invalidCertificate(description: "Certficate is expired.") +// Logger.error("\(error)") +// throw error +// } + +// } catch (InvalidKeyException e) { +// throw new InvalidCertificateException(e); +// } + } + +// // VisibleForTesting +// void validate(ServerCertificate certificate) throws InvalidCertificateException { + @objc public func throwswrapped_validate(serverCertificate: SMKServerCertificate) throws { +// try { +// if (!Curve.verifySignature(trustRoot, certificate.getCertificate(), certificate.getSignature())) { +// throw new InvalidCertificateException("Signature failed"); +// } + let certificateBuilder = SMKProtoServerCertificateCertificate.builder(id: serverCertificate.keyId, + key: serverCertificate.key.serialized) + let certificateData = try certificateBuilder.build().serializedData() + +// let certificateData = try serverCertificate.toProto().certificate + guard try Ed25519.verifySignature(serverCertificate.signatureData, + publicKey: trustRoot.keyData, + data: certificateData) else { + let error = SMKCertificateError.invalidCertificate(description: "Server certificate signature verification failed.") + Logger.error("\(error)") + throw error + } +// if (REVOKED.contains(certificate.getKeyId())) { +// throw new InvalidCertificateException("Server certificate has been revoked"); +// } + guard !SMKCertificateDefaultValidator.kRevokedCertificateIds.contains(serverCertificate.keyId) else { + let error = SMKCertificateError.invalidCertificate(description: "Revoked certificate.") + Logger.error("\(error)") + throw error + } +// } catch (InvalidKeyException e) { +// throw new InvalidCertificateException(e); +// } + } +} diff --git a/SessionProtocolKit/Signal/SMKError.swift b/SessionProtocolKit/Signal/SMKError.swift new file mode 100644 index 000000000..912eb76ea --- /dev/null +++ b/SessionProtocolKit/Signal/SMKError.swift @@ -0,0 +1,9 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +public enum SMKError: Error { + case assertionError(description: String) +} diff --git a/SessionProtocolKit/Signal/SMKSecretSessionCipher.swift b/SessionProtocolKit/Signal/SMKSecretSessionCipher.swift new file mode 100644 index 000000000..2289b8cf8 --- /dev/null +++ b/SessionProtocolKit/Signal/SMKSecretSessionCipher.swift @@ -0,0 +1,590 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation +import HKDFKit + +@objc +public class SecretSessionKnownSenderError: NSObject, CustomNSError { + + @objc + public static let kSenderRecipientIdKey = "kSenderRecipientIdKey" + + @objc + public static let kSenderDeviceIdKey = "kSenderDeviceIdKey" + + public let senderRecipientId: String + public let senderDeviceId: UInt32 + public let underlyingError: Error + + init(senderRecipientId: String, senderDeviceId: UInt32, underlyingError: Error) { + self.senderRecipientId = senderRecipientId + self.senderDeviceId = senderDeviceId + self.underlyingError = underlyingError + } + + public var errorUserInfo: [String: Any] { + return [ + type(of: self).kSenderRecipientIdKey: self.senderRecipientId, + type(of: self).kSenderDeviceIdKey: self.senderDeviceId, + NSUnderlyingErrorKey: (underlyingError as NSError) + ] + } +} + +@objc +public enum SMKSecretSessionCipherError: Int, Error { + case selfSentMessage +} + +// See: +// https://github.com/signalapp/libsignal-metadata-java/blob/master/java/src/main/java/org/signal/libsignal/metadata/SecretSessionCipher.java + +public extension ECKeyPair { + + // TODO: Rename to publicKey(), rename existing publicKey() method to publicKeyData(). + func ecPublicKey() throws -> ECPublicKey { + guard publicKey().count == ECCKeyLength else { + throw SMKError.assertionError(description: "\(logTag) public key has invalid length") + } + + // NOTE: we don't use ECPublicKey(serializedKeyData:) since the + // key data should not have a type byte. + return try ECPublicKey(keyData: publicKey()) + } + + // TODO: Rename to privateKey(), rename existing privateKey() method to privateKeyData(). + func ecPrivateKey() throws -> ECPrivateKey { + guard privateKey().count == ECCKeyLength else { + throw SMKError.assertionError(description: "\(logTag) private key has invalid length") + } + + return try ECPrivateKey(keyData: privateKey()) + } +} + +// MARK: - + +private class SMKSecretKeySpec: NSObject { + + @objc public let keyData: Data + @objc public let algorithm: String + + init(keyData: Data, algorithm: String) { + self.keyData = keyData + self.algorithm = algorithm + } +} + +// MARK: - + +private class SMKEphemeralKeys: NSObject { + + @objc public let chainKey: Data + @objc public let cipherKey: SMKSecretKeySpec + @objc public let macKey: SMKSecretKeySpec + + init(chainKey: Data, cipherKey: Data, macKey: Data) { + self.chainKey = chainKey + self.cipherKey = SMKSecretKeySpec(keyData: cipherKey, algorithm: "AES") + self.macKey = SMKSecretKeySpec(keyData: macKey, algorithm: "HmacSHA256") + } +} + +// MARK: - + +private class SMKStaticKeys: NSObject { + + @objc public let cipherKey: SMKSecretKeySpec + @objc public let macKey: SMKSecretKeySpec + + init(cipherKey: Data, macKey: Data) { + self.cipherKey = SMKSecretKeySpec(keyData: cipherKey, algorithm: "AES") + self.macKey = SMKSecretKeySpec(keyData: macKey, algorithm: "HmacSHA256") + } +} + +// MARK: - + +@objc +public class SMKDecryptResult: NSObject { + + @objc public let senderRecipientId: String + @objc public let senderDeviceId: Int + @objc public let paddedPayload: Data + @objc public let messageType: SMKMessageType + + init(senderRecipientId: String, + senderDeviceId: Int, + paddedPayload: Data, + messageType: SMKMessageType) { + self.senderRecipientId = senderRecipientId + self.senderDeviceId = senderDeviceId + self.paddedPayload = paddedPayload + self.messageType = messageType + } +} + +// MARK: - + +@objc public class SMKSecretSessionCipher: NSObject { + + private let kUDPrefixString = "UnidentifiedDelivery" + + private let kSMKSecretSessionCipherMacLength: UInt = 10 + + private let sessionResetImplementation: SessionRestorationProtocol! + private let sessionStore: SessionStore + private let preKeyStore: PreKeyStore + private let signedPreKeyStore: SignedPreKeyStore + private let identityStore: IdentityKeyStore + + @objc public init(sessionResetImplementation: SessionRestorationProtocol!, + sessionStore: SessionStore, + preKeyStore: PreKeyStore, + signedPreKeyStore: SignedPreKeyStore, + identityStore: IdentityKeyStore) throws { + self.sessionResetImplementation = sessionResetImplementation + self.sessionStore = sessionStore + self.preKeyStore = preKeyStore + self.signedPreKeyStore = signedPreKeyStore + self.identityStore = identityStore + } + + @objc public convenience init(sessionStore: SessionStore, + preKeyStore: PreKeyStore, + signedPreKeyStore: SignedPreKeyStore, + identityStore: IdentityKeyStore) throws { + try self.init(sessionResetImplementation: nil, sessionStore: sessionStore, preKeyStore: preKeyStore, signedPreKeyStore: signedPreKeyStore, identityStore: identityStore) + } + + // MARK: - Public + + @objc + public func throwswrapped_encryptMessage(recipientPublicKey: String, + deviceID: Int32, + paddedPlaintext: Data, + senderCertificate: SMKSenderCertificate, + protocolContext: Any, + useFallbackSessionCipher: Bool) throws -> Data { + guard recipientPublicKey.count > 0 else { + throw SMKError.assertionError(description: "\(SMKSecretSessionCipher.logTag) invalid recipientId") + } + + guard deviceID > 0 else { + throw SMKError.assertionError(description: "\(SMKSecretSessionCipher.logTag) invalid deviceId") + } + + guard let ourIdentityKeyPair = identityStore.identityKeyPair(protocolContext) else { + throw SMKError.assertionError(description: "\(logTag) Missing our identity key pair.") + } + + let encryptedMessage: CipherMessage + if useFallbackSessionCipher { + let cipher = FallBackSessionCipher(recipientPublicKey: recipientPublicKey, privateKey: try ourIdentityKeyPair.privateKey()) + let ivAndCiphertext = cipher.encrypt(paddedPlaintext)! + encryptedMessage = FallbackMessage(_throws_with: ivAndCiphertext) + } else { + let cipher = SessionCipher(sessionStore: sessionStore, + preKeyStore: preKeyStore, + signedPreKeyStore: signedPreKeyStore, + identityKeyStore: identityStore, + recipientId: recipientPublicKey, + deviceId: deviceID) + encryptedMessage = try cipher.encryptMessage(paddedPlaintext, protocolContext: protocolContext) + } + + guard let encryptedMessageData = encryptedMessage.serialized() else { + throw SMKError.assertionError(description: "\(logTag) Could not serialize encrypted message.") + } + + guard let theirIdentityKeyData = Data.data(fromHex: recipientPublicKey.substring(from: recipientPublicKey.index(recipientPublicKey.startIndex, offsetBy: 2))) else { + throw SMKError.assertionError(description: "\(logTag) Missing their public identity key.") + } + + // NOTE: we don't use ECPublicKey(serializedKeyData) since the + // key data should not have a type byte. + let theirIdentityKey = try ECPublicKey(keyData: theirIdentityKeyData) + + let ephemeral = Curve25519.generateKeyPair()! + + 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 + ]) + + let ephemeralKeys = try throwswrapped_calculateEphemeralKeys(ephemeralPublicKey: theirIdentityKey, + ephemeralPrivateKey: ephemeral.ecPrivateKey(), + salt: ephemeralSalt) + + let staticKeyCipherData = try encrypt(cipherKey: ephemeralKeys.cipherKey, + macKey: ephemeralKeys.macKey, + plaintextData: ourIdentityKeyPair.ecPublicKey().serialized) + + let staticSalt = NSData.join([ + ephemeralKeys.chainKey, + staticKeyCipherData + ]) + + let staticKeys = try throwswrapped_calculateStaticKeys(staticPublicKey: theirIdentityKey, + staticPrivateKey: ourIdentityKeyPair.ecPrivateKey(), + salt: staticSalt) + + let messageType: SMKMessageType + switch encryptedMessage.cipherMessageType { + case .prekey: + messageType = .prekey + case .whisper: + messageType = .whisper + case .fallback: + messageType = .fallback + default: + throw SMKError.assertionError(description: "\(logTag) Unknown cipher message type.") + } + + let messageContent = SMKUnidentifiedSenderMessageContent(messageType: messageType, + senderCertificate: senderCertificate, + contentData: encryptedMessageData) + + let messageData = try encrypt(cipherKey: staticKeys.cipherKey, + macKey: staticKeys.macKey, + plaintextData: try messageContent.serialized()) + + let message = SMKUnidentifiedSenderMessage(ephemeralKey: try ephemeral.ecPublicKey(), + encryptedStatic: staticKeyCipherData, + encryptedMessage: messageData) + + return try message.serialized() + } + + @objc + public func throwswrapped_decryptMessage(certificateValidator: SMKCertificateValidator, + cipherTextData: Data, + timestamp: UInt64, + localRecipientId: String, + localDeviceId: Int32, + protocolContext: Any) throws -> SMKDecryptResult { + guard timestamp > 0 else { + throw SMKError.assertionError(description: "\(logTag) invalid timestamp") + } + + guard let ourIdentityKeyPair = identityStore.identityKeyPair(protocolContext) else { + throw SMKError.assertionError(description: "\(logTag) Missing our identity key pair.") + } + + let wrapper = try SMKUnidentifiedSenderMessage.parse(dataAndPrefix: cipherTextData) + + 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 + ]) + + let ephemeralKeys = try throwswrapped_calculateEphemeralKeys(ephemeralPublicKey: wrapper.ephemeralKey, + ephemeralPrivateKey: ourIdentityKeyPair.ecPrivateKey(), + salt: ephemeralSalt) + + let staticKeyBytes = try decrypt(cipherKey: ephemeralKeys.cipherKey, + macKey: ephemeralKeys.macKey, + cipherTextWithMac: wrapper.encryptedStatic) + + let staticKey = try ECPublicKey(serializedKeyData: staticKeyBytes) + + let staticSalt = NSData.join([ + ephemeralKeys.chainKey, + wrapper.encryptedStatic + ]) + + let staticKeys = try throwswrapped_calculateStaticKeys(staticPublicKey: staticKey, + staticPrivateKey: ourIdentityKeyPair.ecPrivateKey(), + salt: staticSalt) + + let messageBytes = try decrypt(cipherKey: staticKeys.cipherKey, + macKey: staticKeys.macKey, + cipherTextWithMac: wrapper.encryptedMessage) + + let messageContent = try SMKUnidentifiedSenderMessageContent.parse(data: messageBytes) + + let senderRecipientId = messageContent.senderCertificate.senderRecipientId + let senderDeviceId = messageContent.senderCertificate.senderDeviceId + + guard senderRecipientId != localRecipientId || senderDeviceId != localDeviceId else { + Logger.info("Discarding self-sent message") + throw SMKSecretSessionCipherError.selfSentMessage + } + + // validator.validate(content.getSenderCertificate(), timestamp); + + let wrapAsKnownSenderError = { (underlyingError: Error) in + return SecretSessionKnownSenderError(senderRecipientId: senderRecipientId, senderDeviceId: senderDeviceId, underlyingError: underlyingError) + } + + do { + try certificateValidator.throwswrapped_validate(senderCertificate: messageContent.senderCertificate, + validationTime: timestamp) + } catch { + throw wrapAsKnownSenderError(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. +// guard messageContent.senderCertificate.key.serialized.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) + } + + // 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(senderRecipientId: senderRecipientId, + 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 keyPair = ECKeyPair(publicKey: ephemeralPublicKey.keyData, privateKey: ephemeralPrivateKey.keyData) + let ephemeralSecret = try Curve25519.generateSharedSecret(fromPublicKey: ephemeralPublicKey.keyData, andKeyPair: keyPair) + + // 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 keyPair = ECKeyPair(publicKey: staticPublicKey.keyData, privateKey: staticPrivateKey.keyData) + let staticSecret = Curve25519.generateSharedSecret(fromPublicKey: staticPublicKey.keyData, andKeyPair: keyPair) + + // byte[] staticDerived = new HKDFv3().deriveSecrets(staticSecret, salt, new byte[0], 96); + let kStaticDerivedLength: UInt = 96 + let staticDerived: Data = + 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) + _ = 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 + } + + // MARK: - Decrypt + + private func throwswrapped_decrypt(messageContent: SMKUnidentifiedSenderMessageContent, + protocolContext: Any) throws -> Data { + // NOTE: We use the sender properties from the sender certificate, not from this class' properties. + let senderRecipientId = messageContent.senderCertificate.senderRecipientId + let senderDeviceId = messageContent.senderCertificate.senderDeviceId + guard senderDeviceId >= 0 && senderDeviceId <= INT32_MAX else { + throw SMKError.assertionError(description: "\(logTag) Invalid senderDeviceId.") + } + + let cipherMessage: CipherMessage + switch (messageContent.messageType) { + case .whisper: + cipherMessage = try WhisperMessage(data: messageContent.contentData) + case .prekey: + cipherMessage = try PreKeyWhisperMessage(data: messageContent.contentData) + case .fallback: + let privateKey = try? identityStore.identityKeyPair(protocolContext)?.privateKey() + let cipher = FallBackSessionCipher(recipientPublicKey: senderRecipientId, privateKey: privateKey) + let plaintext = cipher.decrypt(messageContent.contentData)! + return plaintext + } + + let cipher = LokiSessionCipher(sessionResetImplementation: sessionResetImplementation, + sessionStore: sessionStore, + preKeyStore: preKeyStore, + signedPreKeyStore: signedPreKeyStore, + identityKeyStore: identityStore, + recipientID: senderRecipientId, + 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.. SMKSenderCertificate { + let proto = try SMKProtoSenderCertificate.parseData(data) + return try parse(proto: proto) + } + + @objc public class func parse(proto: SMKProtoSenderCertificate) throws -> SMKSenderCertificate { + + let sender = proto.sender + let senderDevice = proto.senderDevice + + return SMKSenderCertificate(senderDeviceId: senderDevice, senderRecipientId: sender) + } + + @objc public func toProto() throws -> SMKProtoSenderCertificate { + let builder = + SMKProtoSenderCertificate.builder(sender: senderRecipientId, senderDevice: senderDeviceId) + return try builder.build() + } + + @objc public func serialized() throws -> Data { + return try toProto().serializedData() + } + + open override func isEqual(_ other: Any?) -> Bool { + if let other = other as? SMKSenderCertificate { + return (senderDeviceId == other.senderDeviceId && senderRecipientId == other.senderRecipientId) + } else { + return false + } + } + + public override var hash: Int { + return senderDeviceId.hashValue ^ senderRecipientId.hashValue + } +} diff --git a/SessionProtocolKit/Signal/SMKServerCertificate.swift b/SessionProtocolKit/Signal/SMKServerCertificate.swift new file mode 100644 index 000000000..0a01878d5 --- /dev/null +++ b/SessionProtocolKit/Signal/SMKServerCertificate.swift @@ -0,0 +1,64 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +// See: +// https://github.com/signalapp/libsignal-metadata-java/blob/cac0dde9de416a192e64a8940503982820870090/java/src/main/java/org/signal/libsignal/metadata/certificate/ServerCertificate.java +@objc public class SMKServerCertificate: NSObject { + + @objc public let keyId: UInt32 + @objc public let key: ECPublicKey + @objc public let signatureData: Data + + public init(keyId: UInt32, + key: ECPublicKey, + signatureData: Data) { + self.keyId = keyId + self.key = key + self.signatureData = signatureData + } + + @objc public class func parse(data: Data) throws -> SMKServerCertificate { + let proto = try SMKProtoServerCertificate.parseData(data) + return try parse(proto: proto) + } + + @objc public class func parse(proto: SMKProtoServerCertificate) throws -> SMKServerCertificate { + let signatureData = proto.signature + let certificateData = proto.certificate + let certificateProto = try SMKProtoServerCertificateCertificate.parseData(certificateData) + let keyId = certificateProto.id + let keyData = certificateProto.key + let key = try ECPublicKey(serializedKeyData: keyData) + return SMKServerCertificate(keyId: keyId, key: key, signatureData: signatureData) + } + + @objc public func toProto() throws -> SMKProtoServerCertificate { + let certificateBuilder = SMKProtoServerCertificateCertificate.builder(id: keyId, key: key.serialized) + + let builder = + SMKProtoServerCertificate.builder(certificate: try certificateBuilder.buildSerializedData(), + signature: signatureData) + return try builder.build() + } + + @objc public func serialized() throws -> Data { + return try toProto().serializedData() + } + + open override func isEqual(_ other: Any?) -> Bool { + if let other = other as? SMKServerCertificate { + return (keyId == other.keyId && + key.isEqual(other.key) && + (signatureData == other.signatureData)) + } else { + return false + } + } + + public override var hash: Int { + return keyId.hashValue ^ key.hashValue ^ signatureData.hashValue + } +} diff --git a/SessionProtocolKit/Signal/SMKUDAccessKey.swift b/SessionProtocolKit/Signal/SMKUDAccessKey.swift new file mode 100644 index 000000000..a0c312473 --- /dev/null +++ b/SessionProtocolKit/Signal/SMKUDAccessKey.swift @@ -0,0 +1,50 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +@objc +public class SMKUDAccessKey: NSObject { + + @objc + public static let kUDAccessKeyLength: Int = 16 + + @objc + public let keyData: Data + + @objc + public init(profileKey: Data) throws { + guard let aesGcmKey = OWSAES256Key(data: profileKey) else { + throw SMKError.assertionError(description: "Profile key is not valid AES GCM key.") + } + + // We derive the "ud access key" from the private key by encrypting zeroes. + let emptyPlaintextLength = 16 + let emptyPlaintext = Data(count: Int(emptyPlaintextLength)) + let initializationVector = Data(count: Int(kAESGCM256_IVLength)) + guard let keyData = Cryptography.encryptAESGCM(plainTextData: emptyPlaintext, + initializationVector: initializationVector, + additionalAuthenticatedData: nil, + key: aesGcmKey) else { + throw SMKError.assertionError(description: "Could not derive UD access key from profile key.") + } + guard keyData.ciphertext.count == SMKUDAccessKey.kUDAccessKeyLength else { + throw SMKError.assertionError(description: "\(SMKUDAccessKey.logTag) key has invalid length") + } + + self.keyData = keyData.ciphertext + } + + @objc + public init(randomKeyData: ()) { + self.keyData = Randomness.generateRandomBytes(Int32(SMKUDAccessKey.kUDAccessKeyLength)) + } + + // MARK: + + override public func isEqual(_ object: Any?) -> Bool { + guard let other = object as? SMKUDAccessKey else { return false } + return self.keyData == other.keyData + } +} diff --git a/SessionProtocolKit/Signal/SMKUnidentifiedSenderMessage.swift b/SessionProtocolKit/Signal/SMKUnidentifiedSenderMessage.swift new file mode 100644 index 000000000..5592d2e05 --- /dev/null +++ b/SessionProtocolKit/Signal/SMKUnidentifiedSenderMessage.swift @@ -0,0 +1,94 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +// https://github.com/signalapp/libsignal-metadata-java/blob/master/java/src/main/java/org/signal/libsignal/metadata/protocol/UnidentifiedSenderMessage.java +@objc public class SMKUnidentifiedSenderMessage: NSObject { + + @objc public static let kSMKMessageCipherTextVersion: UInt = 1 + + public let cipherTextVersion: UInt + public let ephemeralKey: ECPublicKey + public let encryptedStatic: Data + public let encryptedMessage: Data + + public init(cipherTextVersion: UInt, + ephemeralKey: ECPublicKey, + encryptedStatic: Data, + encryptedMessage: Data) { + self.cipherTextVersion = cipherTextVersion + self.ephemeralKey = ephemeralKey + self.encryptedStatic = encryptedStatic + self.encryptedMessage = encryptedMessage + } + + public init(ephemeralKey: ECPublicKey, + encryptedStatic: Data, + encryptedMessage: Data) { + self.cipherTextVersion = SMKUnidentifiedSenderMessage.kSMKMessageCipherTextVersion + self.ephemeralKey = ephemeralKey + self.encryptedStatic = encryptedStatic + self.encryptedMessage = encryptedMessage + } + + @objc public class func parse(dataAndPrefix: Data) throws -> SMKUnidentifiedSenderMessage { + // public UnidentifiedSenderMessage(byte[] serialized) + // throws InvalidMetadataMessageException, InvalidMetadataVersionException + + let parser = OWSDataParser(data: dataAndPrefix) + + // this.version = ByteUtil.highBitsToInt(serialized[0]); + let versionByte = try parser.nextByte(name: "version byte") + let cipherTextVersion = UInt(SerializationUtilities.highBitsToInt(fromByte: versionByte)) + + // if (version > CIPHERTEXT_VERSION) { + // throw new InvalidMetadataVersionException("Unknown version: " + this.version); + // } + guard cipherTextVersion <= SMKUnidentifiedSenderMessage.kSMKMessageCipherTextVersion else { + throw SMKError.assertionError(description: "\(logTag) unknown cipher text version: \(cipherTextVersion)") + } + + // SignalProtos.UnidentifiedSenderMessage unidentifiedSenderMessage = + // SignalProtos.UnidentifiedSenderMessage.parseFrom(ByteString.copyFrom(serialized, 1, serialized.length - 1)); + let protoData = try parser.remainder(name: "proto data") + let proto = try SMKProtoUnidentifiedSenderMessage.parseData(protoData) + + // if (!unidentifiedSenderMessage.hasEphemeralPublic() || + // !unidentifiedSenderMessage.hasEncryptedStatic() || + // !unidentifiedSenderMessage.hasEncryptedMessage()) + // { + // throw new InvalidMetadataMessageException("Missing fields"); + // } + // NOTE: These fields are required in the proto schema. + + // this.ephemeral = Curve.decodePoint(unidentifiedSenderMessage.getEphemeralPublic().toByteArray(), 0); + let ephemeralKeyData = proto.ephemeralPublic + let ephemeralKey = try ECPublicKey(serializedKeyData: ephemeralKeyData) + + // this.encryptedStatic = unidentifiedSenderMessage.getEncryptedStatic().toByteArray(); + let encryptedStatic = proto.encryptedStatic + + // this.encryptedMessage = unidentifiedSenderMessage.getEncryptedMessage().toByteArray(); + let encryptedMessage = proto.encryptedMessage + + return SMKUnidentifiedSenderMessage(cipherTextVersion: cipherTextVersion, ephemeralKey: ephemeralKey, encryptedStatic: encryptedStatic, encryptedMessage: encryptedMessage) + } + + @objc public func toProto() throws -> SMKProtoUnidentifiedSenderMessage { + let builder = SMKProtoUnidentifiedSenderMessage.builder(ephemeralPublic: ephemeralKey.serialized, + encryptedStatic: encryptedStatic, + encryptedMessage: encryptedMessage) + return try builder.build() + } + + @objc public func serialized() throws -> Data { + let versionByte: UInt8 = UInt8((self.cipherTextVersion << 4 | self.cipherTextVersion) & 0xFF) + let versionBytes = [versionByte] + let versionData = Data(bytes: versionBytes) + let messageData = try toProto().serializedData() + + return NSData.join([versionData, messageData]) + } +} diff --git a/SessionProtocolKit/Signal/SMKUnidentifiedSenderMessageContent.swift b/SessionProtocolKit/Signal/SMKUnidentifiedSenderMessageContent.swift new file mode 100644 index 000000000..68116bafe --- /dev/null +++ b/SessionProtocolKit/Signal/SMKUnidentifiedSenderMessageContent.swift @@ -0,0 +1,71 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +import Foundation + +@objc public enum SMKMessageType: Int { + case whisper + case prekey + case fallback +} + +// See: +// https://github.com/signalapp/libsignal-metadata-java/blob/0cbbbf23eaf9f46fdf2d9463f3dfab2fb3271292/java/src/main/java/org/signal/libsignal/metadata/protocol/UnidentifiedSenderMessageContent.java +@objc public class SMKUnidentifiedSenderMessageContent: NSObject { + + @objc public let messageType: SMKMessageType + @objc public let senderCertificate: SMKSenderCertificate + @objc public let contentData: Data + + @objc public init(messageType: SMKMessageType, + senderCertificate: SMKSenderCertificate, + contentData: Data) { + self.messageType = messageType + self.senderCertificate = senderCertificate + self.contentData = contentData + } + + @objc public class func parse(data: Data) throws -> SMKUnidentifiedSenderMessageContent { + + let proto = try SMKProtoUnidentifiedSenderMessageMessage.parseData(data) + + // TODO: Should we have a default case in our switches? Probably. + var messageType: SMKMessageType + switch (proto.type) { + case .prekeyMessage: + messageType = .prekey + case .message: + messageType = .whisper + case .fallbackMessage: + messageType = .fallback + } + + let contentData = proto.content + let senderCertificateProto = proto.senderCertificate + let senderCertificate = try SMKSenderCertificate.parse(proto: senderCertificateProto) + + return SMKUnidentifiedSenderMessageContent(messageType: messageType, senderCertificate: senderCertificate, contentData: contentData) + } + + @objc public func toProto() throws -> SMKProtoUnidentifiedSenderMessageMessage { + let builderType: SMKProtoUnidentifiedSenderMessageMessage.SMKProtoUnidentifiedSenderMessageMessageType + switch messageType { + case .whisper: + builderType = .message + case .prekey: + builderType = .prekeyMessage + case .fallback: + builderType = .fallbackMessage + } + + let builder = SMKProtoUnidentifiedSenderMessageMessage.builder(type: builderType, + senderCertificate: try senderCertificate.toProto(), + content: contentData) + return try builder.build() + } + + @objc public func serialized() throws -> Data { + return try toProto().serializedData() + } +} diff --git a/SessionProtocolKit/Signal/SessionRestorationProtocol.swift b/SessionProtocolKit/Signal/SessionRestorationProtocol.swift new file mode 100644 index 000000000..9e2f93d47 --- /dev/null +++ b/SessionProtocolKit/Signal/SessionRestorationProtocol.swift @@ -0,0 +1,8 @@ + +@objc(LKSessionRestorationProtocol) +public protocol SessionRestorationProtocol { + + func validatePreKeyWhisperMessage(for recipientPublicKey: String, whisperMessage: CipherMessage, using transaction: Any) throws + func getSessionRestorationStatus(for recipientPublicKey: String) -> SessionRestorationStatus + func handleNewSessionAdopted(for recipientPublicKey: String, using transaction: Any) +} diff --git a/SessionProtocolKit/Signal/SessionRestorationStatus.swift b/SessionProtocolKit/Signal/SessionRestorationStatus.swift new file mode 100644 index 000000000..fa701f01f --- /dev/null +++ b/SessionProtocolKit/Signal/SessionRestorationStatus.swift @@ -0,0 +1,5 @@ + +@objc(LKSessionRestorationStatus) +public enum SessionRestorationStatus : Int { + case none, initiated, requestReceived +} diff --git a/SessionUtilities/DiffieHellman.swift b/SessionUtilities/DiffieHellman.swift new file mode 100644 index 000000000..3858d1094 --- /dev/null +++ b/SessionUtilities/DiffieHellman.swift @@ -0,0 +1,51 @@ +import CryptoSwift +import Curve25519Kit + +public final class DiffieHellman : NSObject { + public static let ivSize: UInt = 16 + + public enum Error : LocalizedError { + case decryptionFailed + case sharedSecretGenerationFailed + + public var errorDescription: String { + switch self { + case .decryptionFailed: return "Couldn't decrypt data" + case .sharedSecretGenerationFailed: return "Couldn't generate a shared secret." + } + } + } + + private override init() { } + + public static func encrypt(_ plaintext: Data, using symmetricKey: Data) throws -> Data { + let iv = Data.getSecureRandomData(ofSize: ivSize)! + let cbc = CBC(iv: iv.bytes) + let aes = try AES(key: symmetricKey.bytes, blockMode: cbc) + let ciphertext = try aes.encrypt(plaintext.bytes) + let ivAndCiphertext = iv.bytes + ciphertext + return Data(ivAndCiphertext) + } + + public static func encrypt(_ plaintext: Data, publicKey: Data, privateKey: Data) throws -> Data { + let keyPair = ECKeyPair(publicKey: publicKey, privateKey: privateKey) + guard let symmetricKey = Curve25519.generateSharedSecret(fromPublicKey: publicKey, andKeyPair: keyPair) else { throw Error.sharedSecretGenerationFailed } + return try encrypt(plaintext, using: symmetricKey) + } + + public static func decrypt(_ ivAndCiphertext: Data, using symmetricKey: Data) throws -> Data { + guard ivAndCiphertext.count >= ivSize else { throw Error.decryptionFailed } + let iv = ivAndCiphertext[.. Data { + let keyPair = ECKeyPair(publicKey: publicKey, privateKey: privateKey) + guard let symmetricKey = Curve25519.generateSharedSecret(fromPublicKey: publicKey, andKeyPair: keyPair) else { throw Error.sharedSecretGenerationFailed } + return try decrypt(ivAndCiphertext, using: symmetricKey) + } +} diff --git a/SessionUtilities/ECKeyPair+Utilities.h b/SessionUtilities/ECKeyPair+Utilities.h index 0369555f1..6435f2e64 100644 --- a/SessionUtilities/ECKeyPair+Utilities.h +++ b/SessionUtilities/ECKeyPair+Utilities.h @@ -6,6 +6,7 @@ NS_ASSUME_NONNULL_BEGIN @interface ECKeyPair (Utilities) - (ECKeyPair *)initWithPublicKey:(NSData *)publicKey privateKey:(NSData *)privateKey; +- (NSData *)privateKey; @end diff --git a/SessionUtilities/ECKeyPair+Utilities.m b/SessionUtilities/ECKeyPair+Utilities.m index 2e421f2f1..aaf731bef 100644 --- a/SessionUtilities/ECKeyPair+Utilities.m +++ b/SessionUtilities/ECKeyPair+Utilities.m @@ -12,6 +12,11 @@ NS_ASSUME_NONNULL_BEGIN return keyPair; } +- (NSData *)privateKey +{ + return [NSData dataWithBytes:self->privateKey length:32]; +} + @end NS_ASSUME_NONNULL_END diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index 44b025fdc..f5a98ff93 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -634,6 +634,25 @@ C3A71D0B2558989C0043A11F /* MessageWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D0A2558989C0043A11F /* MessageWrapper.swift */; }; C3A71D1E25589AC30043A11F /* WebSocketProto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D1C25589AC30043A11F /* WebSocketProto.swift */; }; C3A71D1F25589AC30043A11F /* WebSocketResources.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D1D25589AC30043A11F /* WebSocketResources.pb.swift */; }; + C3A71D2925589EBF0043A11F /* SessionRestorationProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D2825589EBF0043A11F /* SessionRestorationProtocol.swift */; }; + C3A71D3B25589F2B0043A11F /* SessionRestorationStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D3A25589F2B0043A11F /* SessionRestorationStatus.swift */; }; + C3A71D5025589FF30043A11F /* SMKUDAccessKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D4425589FF10043A11F /* SMKUDAccessKey.swift */; }; + C3A71D5125589FF30043A11F /* ECPublicKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D4525589FF10043A11F /* ECPublicKey.swift */; }; + C3A71D5225589FF30043A11F /* SMKError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D4625589FF10043A11F /* SMKError.swift */; }; + C3A71D5325589FF30043A11F /* ECPrivateKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D4725589FF10043A11F /* ECPrivateKey.swift */; }; + C3A71D5425589FF30043A11F /* NSData+messagePadding.m in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D4825589FF20043A11F /* NSData+messagePadding.m */; }; + C3A71D5525589FF30043A11F /* SMKSenderCertificate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D4925589FF20043A11F /* SMKSenderCertificate.swift */; }; + C3A71D5625589FF30043A11F /* SMKServerCertificate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D4A25589FF20043A11F /* SMKServerCertificate.swift */; }; + C3A71D5725589FF30043A11F /* SMKCertificateValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D4B25589FF20043A11F /* SMKCertificateValidator.swift */; }; + C3A71D5825589FF30043A11F /* SMKSecretSessionCipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D4C25589FF30043A11F /* SMKSecretSessionCipher.swift */; }; + C3A71D5925589FF30043A11F /* SMKUnidentifiedSenderMessageContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D4D25589FF30043A11F /* SMKUnidentifiedSenderMessageContent.swift */; }; + C3A71D5A25589FF30043A11F /* NSData+messagePadding.h in Headers */ = {isa = PBXBuildFile; fileRef = C3A71D4E25589FF30043A11F /* NSData+messagePadding.h */; }; + C3A71D5B25589FF30043A11F /* SMKUnidentifiedSenderMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D4F25589FF30043A11F /* SMKUnidentifiedSenderMessage.swift */; }; + C3A71D672558A0170043A11F /* FallbackSessionCipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D642558A0170043A11F /* FallbackSessionCipher.swift */; }; + C3A71D682558A0170043A11F /* LokiSessionCipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D652558A0170043A11F /* LokiSessionCipher.swift */; }; + C3A71D742558A0F60043A11F /* SMKProto.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D722558A0F60043A11F /* SMKProto.swift */; }; + C3A71D752558A0F60043A11F /* OWSUnidentifiedDelivery.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D732558A0F60043A11F /* OWSUnidentifiedDelivery.pb.swift */; }; + C3A71D862558A28A0043A11F /* DiffieHellman.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3A71D662558A0170043A11F /* DiffieHellman.swift */; }; C3AABDDF2553ECF00042FF4C /* Array+Description.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3C2A5D12553860800C340D1 /* Array+Description.swift */; }; C3BBE0762554CDA60050F1E3 /* Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3BBE0752554CDA60050F1E3 /* Configuration.swift */; }; C3BBE0802554CDD70050F1E3 /* Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3BBE07F2554CDD70050F1E3 /* Storage.swift */; }; @@ -1651,6 +1670,25 @@ C3A71D0A2558989C0043A11F /* MessageWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageWrapper.swift; sourceTree = ""; }; C3A71D1C25589AC30043A11F /* WebSocketProto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebSocketProto.swift; sourceTree = ""; }; C3A71D1D25589AC30043A11F /* WebSocketResources.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebSocketResources.pb.swift; sourceTree = ""; }; + C3A71D2825589EBF0043A11F /* SessionRestorationProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionRestorationProtocol.swift; sourceTree = ""; }; + C3A71D3A25589F2B0043A11F /* SessionRestorationStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionRestorationStatus.swift; sourceTree = ""; }; + C3A71D4425589FF10043A11F /* SMKUDAccessKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMKUDAccessKey.swift; sourceTree = ""; }; + C3A71D4525589FF10043A11F /* ECPublicKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ECPublicKey.swift; sourceTree = ""; }; + C3A71D4625589FF10043A11F /* SMKError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMKError.swift; sourceTree = ""; }; + C3A71D4725589FF10043A11F /* ECPrivateKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ECPrivateKey.swift; sourceTree = ""; }; + C3A71D4825589FF20043A11F /* NSData+messagePadding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+messagePadding.m"; sourceTree = ""; }; + C3A71D4925589FF20043A11F /* SMKSenderCertificate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMKSenderCertificate.swift; sourceTree = ""; }; + C3A71D4A25589FF20043A11F /* SMKServerCertificate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMKServerCertificate.swift; sourceTree = ""; }; + C3A71D4B25589FF20043A11F /* SMKCertificateValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMKCertificateValidator.swift; sourceTree = ""; }; + C3A71D4C25589FF30043A11F /* SMKSecretSessionCipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMKSecretSessionCipher.swift; sourceTree = ""; }; + C3A71D4D25589FF30043A11F /* SMKUnidentifiedSenderMessageContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMKUnidentifiedSenderMessageContent.swift; sourceTree = ""; }; + C3A71D4E25589FF30043A11F /* NSData+messagePadding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+messagePadding.h"; sourceTree = ""; }; + C3A71D4F25589FF30043A11F /* SMKUnidentifiedSenderMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMKUnidentifiedSenderMessage.swift; sourceTree = ""; }; + C3A71D642558A0170043A11F /* FallbackSessionCipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FallbackSessionCipher.swift; sourceTree = ""; }; + C3A71D652558A0170043A11F /* LokiSessionCipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LokiSessionCipher.swift; sourceTree = ""; }; + C3A71D662558A0170043A11F /* DiffieHellman.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiffieHellman.swift; sourceTree = ""; }; + C3A71D722558A0F60043A11F /* SMKProto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SMKProto.swift; path = Protos/SMKProto.swift; sourceTree = ""; }; + C3A71D732558A0F60043A11F /* OWSUnidentifiedDelivery.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSUnidentifiedDelivery.pb.swift; path = Protos/OWSUnidentifiedDelivery.pb.swift; sourceTree = ""; }; C3AA6BB824CE8F1B002358B6 /* Migrating Translations from Android.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "Migrating Translations from Android.md"; sourceTree = ""; }; C3AECBEA24EF5244005743DE /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = translations/fa.lproj/Localizable.strings; sourceTree = ""; }; C3BBE0752554CDA60050F1E3 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = ""; }; @@ -3368,6 +3406,7 @@ C3C2A5D12553860800C340D1 /* Array+Description.swift */, C3C2ABD12553C6C900C340D1 /* Data+SecureRandom.swift */, C3C2A5D52553860A00C340D1 /* Dictionary+Description.swift */, + C3A71D662558A0170043A11F /* DiffieHellman.swift */, C3471F5525553DA000297E91 /* ECKeyPair+Utilities.h */, C3471F6725553E7600297E91 /* ECKeyPair+Utilities.m */, C3C2A5BC255385EE00C340D1 /* HTTP.swift */, @@ -3501,6 +3540,8 @@ children = ( C3C2A8E52553B59B00C340D1 /* SPKProto.swift */, C3C2A8E42553B59B00C340D1 /* WhisperTextProtocol.pb.swift */, + C3A71D722558A0F60043A11F /* SMKProto.swift */, + C3A71D732558A0F60043A11F /* OWSUnidentifiedDelivery.pb.swift */, ); name = Protos; sourceTree = ""; @@ -3613,6 +3654,22 @@ C3C2A9352553B5C300C340D1 /* Sessions */, C3C2A9562553B5F600C340D1 /* State */, C3C2A9692553B62B00C340D1 /* Utility */, + C3A71D4725589FF10043A11F /* ECPrivateKey.swift */, + C3A71D4525589FF10043A11F /* ECPublicKey.swift */, + C3A71D4E25589FF30043A11F /* NSData+messagePadding.h */, + C3A71D4825589FF20043A11F /* NSData+messagePadding.m */, + C3A71D4B25589FF20043A11F /* SMKCertificateValidator.swift */, + C3A71D4625589FF10043A11F /* SMKError.swift */, + C3A71D4C25589FF30043A11F /* SMKSecretSessionCipher.swift */, + C3A71D4925589FF20043A11F /* SMKSenderCertificate.swift */, + C3A71D4A25589FF20043A11F /* SMKServerCertificate.swift */, + C3A71D4425589FF10043A11F /* SMKUDAccessKey.swift */, + C3A71D4F25589FF30043A11F /* SMKUnidentifiedSenderMessage.swift */, + C3A71D4D25589FF30043A11F /* SMKUnidentifiedSenderMessageContent.swift */, + C3A71D642558A0170043A11F /* FallbackSessionCipher.swift */, + C3A71D652558A0170043A11F /* LokiSessionCipher.swift */, + C3A71D2825589EBF0043A11F /* SessionRestorationProtocol.swift */, + C3A71D3A25589F2B0043A11F /* SessionRestorationStatus.swift */, ); path = Signal; sourceTree = ""; @@ -3871,6 +3928,7 @@ C3C2A9F42553B9C400C340D1 /* Threading.h in Headers */, C3C2A90A2553B5B200C340D1 /* Chain.h in Headers */, C3C2A8882553B4CC00C340D1 /* AxolotlExceptions.h in Headers */, + C3A71D5A25589FF30043A11F /* NSData+messagePadding.h in Headers */, C3C2A89E2553B4F600C340D1 /* WhisperMessage.h in Headers */, C3C2A8D92553B57C00C340D1 /* PreKeyRecord.h in Headers */, C3C2A90B2553B5B200C340D1 /* MessageKeys.h in Headers */, @@ -4959,6 +5017,7 @@ C3BBE0A72554D4DE0050F1E3 /* Promise+Retrying.swift in Sources */, C3BBE0A92554D4DE0050F1E3 /* HTTP.swift in Sources */, C300A60D2554B31900555489 /* Logging.swift in Sources */, + C3A71D862558A28A0043A11F /* DiffieHellman.swift in Sources */, C300A6322554B6D100555489 /* NSDate+Timestamp.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -5012,38 +5071,52 @@ files = ( C3C2A91B2553B5B200C340D1 /* BobAxolotlParameters.m in Sources */, C3C2A8D82553B57C00C340D1 /* PreKeyRecord.m in Sources */, + C3A71D3B25589F2B0043A11F /* SessionRestorationStatus.swift in Sources */, + C3A71D5825589FF30043A11F /* SMKSecretSessionCipher.swift in Sources */, C3C2A9712553B63C00C340D1 /* SerializationUtilities.m in Sources */, + C3A71D5925589FF30043A11F /* SMKUnidentifiedSenderMessageContent.swift in Sources */, C3C2ABEE2553C76900C340D1 /* ClosedGroupSenderKey.swift in Sources */, C3C2A9172553B5B200C340D1 /* RootKey.m in Sources */, C3C2A9112553B5B200C340D1 /* ChainKey.m in Sources */, C3C2A8C32553B55600C340D1 /* AES-CBC.m in Sources */, C3C2AA012553B9C400C340D1 /* Threading.m in Sources */, C3C2A9F32553B9C400C340D1 /* String+OWS.swift in Sources */, + C3A71D682558A0170043A11F /* LokiSessionCipher.swift in Sources */, C3C2A9F12553B9C400C340D1 /* OWSLogs.m in Sources */, C3C2A9F22553B9C400C340D1 /* NSObject+OWS.m in Sources */, + C3A71D5B25589FF30043A11F /* SMKUnidentifiedSenderMessage.swift in Sources */, C3C2A9182553B5B200C340D1 /* SendingChain.m in Sources */, C3C2A8A02553B4F600C340D1 /* PreKeyWhisperMessage.m in Sources */, + C3A71D5225589FF30043A11F /* SMKError.swift in Sources */, C3C2A93F2553B5D700C340D1 /* SessionState.m in Sources */, C3C2A9F02553B9C400C340D1 /* OWSAsserts.m in Sources */, + C3A71D5125589FF30043A11F /* ECPublicKey.swift in Sources */, C3C2ABF82553C8A300C340D1 /* Storage.swift in Sources */, C3C2A9F72553B9C400C340D1 /* NSData+OWS.m in Sources */, C3C2A90D2553B5B200C340D1 /* RKCK.m in Sources */, C3C2A8E72553B59B00C340D1 /* SPKProto.swift in Sources */, + C3A71D5325589FF30043A11F /* ECPrivateKey.swift in Sources */, + C3A71D5525589FF30043A11F /* SMKSenderCertificate.swift in Sources */, C3C2A91E2553B5B200C340D1 /* ChainAndIndex.m in Sources */, C3C2A91A2553B5B200C340D1 /* TSDerivedSecrets.m in Sources */, + C3A71D5025589FF30043A11F /* SMKUDAccessKey.swift in Sources */, C3C2A91C2553B5B200C340D1 /* ReceivingChain.m in Sources */, C3C2AA032553B9C400C340D1 /* Randomness.m in Sources */, C3C2A9EF2553B9C400C340D1 /* OWSDataParser.swift in Sources */, C3C2A8A32553B4F600C340D1 /* FallbackMessage.m in Sources */, + C3A71D5425589FF30043A11F /* NSData+messagePadding.m in Sources */, C3C2AA072553B9C400C340D1 /* Cryptography.m in Sources */, C3C2A9F52553B9C400C340D1 /* NSDate+OWS.mm in Sources */, C3C2ABE42553C74400C340D1 /* ClosedGroupRatchet.swift in Sources */, C3C2A93E2553B5D700C340D1 /* SessionRecord.m in Sources */, + C3A71D742558A0F60043A11F /* SMKProto.swift in Sources */, C3C2AA052553B9C400C340D1 /* SCKExceptionWrapper.m in Sources */, + C3A71D2925589EBF0043A11F /* SessionRestorationProtocol.swift in Sources */, C3C2A8D62553B57C00C340D1 /* PreKeyBundle.m in Sources */, C3C2A9F62553B9C400C340D1 /* Data+OWS.swift in Sources */, C3C2A89D2553B4F600C340D1 /* ClosedGroupCiphertextMessage.m in Sources */, C3C2A9FB2553B9C400C340D1 /* Logger.swift in Sources */, + C3A71D672558A0170043A11F /* FallbackSessionCipher.swift in Sources */, C3C2AC0A2553C9A100C340D1 /* Configuration.swift in Sources */, C3C2A96E2553B63C00C340D1 /* NSData+keyVersionByte.m in Sources */, C3C2A8E62553B59B00C340D1 /* WhisperTextProtocol.pb.swift in Sources */, @@ -5052,9 +5125,12 @@ C3C2A9FA2553B9C400C340D1 /* OWSSwiftUtils.swift in Sources */, C3C2A92C2553B5BE00C340D1 /* SessionCipher.m in Sources */, C3C2A8D42553B57C00C340D1 /* SignedPrekeyRecord.m in Sources */, + C3A71D752558A0F60043A11F /* OWSUnidentifiedDelivery.pb.swift in Sources */, + C3A71D5625589FF30043A11F /* SMKServerCertificate.swift in Sources */, C3C2A8A12553B4F600C340D1 /* WhisperMessage.m in Sources */, C3C2A9192553B5B200C340D1 /* MessageKeys.m in Sources */, C3C2A90F2553B5B200C340D1 /* RatchetingSession.m in Sources */, + C3A71D5725589FF30043A11F /* SMKCertificateValidator.swift in Sources */, C3C2A93D2553B5D700C340D1 /* SessionBuilder.m in Sources */, C3C2AA082553B9C400C340D1 /* NSString+OWS.m in Sources */, );