Re-add missing Signal protocol bits

This commit is contained in:
nielsandriesse 2020-11-09 09:17:47 +11:00
parent 8f443a38af
commit 82b12901b9
26 changed files with 2988 additions and 2 deletions

View File

@ -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 {

View File

@ -5,8 +5,12 @@ FOUNDATION_EXPORT const unsigned char SessionProtocolKitVersionString[];
#import <SessionProtocolKit/AxolotlStore.h>
#import <SessionProtocolKit/ClosedGroupCiphertextMessage.h>
#import <SessionProtocolKit/Cryptography.h>
#import <SessionProtocolKit/FallbackMessage.h>
#import <SessionProtocolKit/NSData+OWS.h>
#import <SessionProtocolKit/NSObject+OWS.h>
#import <SessionProtocolKit/NSString+OWS.h>
#import <SessionProtocolKit/OWSLogs.h>
#import <SessionProtocolKit/PreKeyBundle.h>
#import <SessionProtocolKit/SerializationUtilities.h>
#import <SessionProtocolKit/SessionCipher.h>

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}
}

View File

@ -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)
}
}

View File

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

View File

@ -0,0 +1,60 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import <Foundation/Foundation.h>
#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

View File

@ -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

View File

@ -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<D: SwiftProtobuf.Decoder>(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<V: SwiftProtobuf.Visitor>(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<D: SwiftProtobuf.Decoder>(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<V: SwiftProtobuf.Visitor>(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<D: SwiftProtobuf.Decoder>(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<V: SwiftProtobuf.Visitor>(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<D: SwiftProtobuf.Decoder>(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<V: SwiftProtobuf.Visitor>(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<D: SwiftProtobuf.Decoder>(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<V: SwiftProtobuf.Visitor>(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<D: SwiftProtobuf.Decoder>(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<V: SwiftProtobuf.Visitor>(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"),
]
}

View File

@ -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;
}

View File

@ -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

View File

@ -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<Integer> REVOKED = new HashSet<Integer>() {{
//
// }};
private static let kRevokedCertificateIds = Set<UInt32>()
//
// 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);
// }
}
}

View File

@ -0,0 +1,9 @@
//
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
import Foundation
public enum SMKError: Error {
case assertionError(description: String)
}

View File

@ -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..<kSMKSecretSessionCipherMacLength]
// if (!MessageDigest.isEqual(ourMac, theirMac)) {
// throw new InvalidMacException("Bad mac!");
// }
//
// NOTE: Constant time comparison.
guard ourMac.ows_constantTimeIsEqual(to: theirMac) else {
throw SMKError.assertionError(description: "\(logTag) macs do not match.")
}
// Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
// cipher.init(Cipher.DECRYPT_MODE, cipherKey, new IvParameterSpec(new byte[16]));
guard let aesKey = OWSAES256Key(data: cipherKey.keyData) else {
throw SMKError.assertionError(description: "\(logTag) could not parse AES256 key.")
}
// NOTE: The IV is all zeroes. This is fine since we're using a unique key.
let initializationVector = Data(count: Int(kAES256CTR_IVLength))
guard let plaintext = Cryptography.decryptAESCTR(cipherText: cipherText, initializationVector: initializationVector, key: aesKey) else {
throw SMKError.assertionError(description: "\(logTag) could not decrypt AESGCM.")
}
return plaintext
}
}

View File

@ -0,0 +1,53 @@
//
// 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/SenderCertificate.java
@objc public class SMKSenderCertificate: NSObject {
@objc public let senderDeviceId: UInt32
@objc public let senderRecipientId: String
@objc public init(senderDeviceId: UInt32, senderRecipientId: String) {
self.senderDeviceId = senderDeviceId
self.senderRecipientId = senderRecipientId
}
@objc public class func parse(data: Data) throws -> 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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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])
}
}

View File

@ -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()
}
}

View File

@ -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)
}

View File

@ -0,0 +1,5 @@
@objc(LKSessionRestorationStatus)
public enum SessionRestorationStatus : Int {
case none, initiated, requestReceived
}

View File

@ -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[..<ivSize]
let ciphertext = ivAndCiphertext[ivSize...]
let cbc = CBC(iv: iv.bytes)
let aes = try AES(key: symmetricKey.bytes, blockMode: cbc)
let plaintext = try aes.decrypt(ciphertext.bytes)
return Data(plaintext)
}
public static func decrypt(_ ivAndCiphertext: 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 decrypt(ivAndCiphertext, using: symmetricKey)
}
}

View File

@ -6,6 +6,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface ECKeyPair (Utilities)
- (ECKeyPair *)initWithPublicKey:(NSData *)publicKey privateKey:(NSData *)privateKey;
- (NSData *)privateKey;
@end

View File

@ -12,6 +12,11 @@ NS_ASSUME_NONNULL_BEGIN
return keyPair;
}
- (NSData *)privateKey
{
return [NSData dataWithBytes:self->privateKey length:32];
}
@end
NS_ASSUME_NONNULL_END

View File

@ -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 = "<group>"; };
C3A71D1C25589AC30043A11F /* WebSocketProto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebSocketProto.swift; sourceTree = "<group>"; };
C3A71D1D25589AC30043A11F /* WebSocketResources.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebSocketResources.pb.swift; sourceTree = "<group>"; };
C3A71D2825589EBF0043A11F /* SessionRestorationProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionRestorationProtocol.swift; sourceTree = "<group>"; };
C3A71D3A25589F2B0043A11F /* SessionRestorationStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionRestorationStatus.swift; sourceTree = "<group>"; };
C3A71D4425589FF10043A11F /* SMKUDAccessKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMKUDAccessKey.swift; sourceTree = "<group>"; };
C3A71D4525589FF10043A11F /* ECPublicKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ECPublicKey.swift; sourceTree = "<group>"; };
C3A71D4625589FF10043A11F /* SMKError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMKError.swift; sourceTree = "<group>"; };
C3A71D4725589FF10043A11F /* ECPrivateKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ECPrivateKey.swift; sourceTree = "<group>"; };
C3A71D4825589FF20043A11F /* NSData+messagePadding.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+messagePadding.m"; sourceTree = "<group>"; };
C3A71D4925589FF20043A11F /* SMKSenderCertificate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMKSenderCertificate.swift; sourceTree = "<group>"; };
C3A71D4A25589FF20043A11F /* SMKServerCertificate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMKServerCertificate.swift; sourceTree = "<group>"; };
C3A71D4B25589FF20043A11F /* SMKCertificateValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMKCertificateValidator.swift; sourceTree = "<group>"; };
C3A71D4C25589FF30043A11F /* SMKSecretSessionCipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMKSecretSessionCipher.swift; sourceTree = "<group>"; };
C3A71D4D25589FF30043A11F /* SMKUnidentifiedSenderMessageContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMKUnidentifiedSenderMessageContent.swift; sourceTree = "<group>"; };
C3A71D4E25589FF30043A11F /* NSData+messagePadding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+messagePadding.h"; sourceTree = "<group>"; };
C3A71D4F25589FF30043A11F /* SMKUnidentifiedSenderMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMKUnidentifiedSenderMessage.swift; sourceTree = "<group>"; };
C3A71D642558A0170043A11F /* FallbackSessionCipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FallbackSessionCipher.swift; sourceTree = "<group>"; };
C3A71D652558A0170043A11F /* LokiSessionCipher.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LokiSessionCipher.swift; sourceTree = "<group>"; };
C3A71D662558A0170043A11F /* DiffieHellman.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DiffieHellman.swift; sourceTree = "<group>"; };
C3A71D722558A0F60043A11F /* SMKProto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SMKProto.swift; path = Protos/SMKProto.swift; sourceTree = "<group>"; };
C3A71D732558A0F60043A11F /* OWSUnidentifiedDelivery.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OWSUnidentifiedDelivery.pb.swift; path = Protos/OWSUnidentifiedDelivery.pb.swift; sourceTree = "<group>"; };
C3AA6BB824CE8F1B002358B6 /* Migrating Translations from Android.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "Migrating Translations from Android.md"; sourceTree = "<group>"; };
C3AECBEA24EF5244005743DE /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = translations/fa.lproj/Localizable.strings; sourceTree = "<group>"; };
C3BBE0752554CDA60050F1E3 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = "<group>"; };
@ -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 = "<group>";
@ -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 = "<group>";
@ -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 */,
);