mirror of https://github.com/oxen-io/session-ios
Re-add missing Signal protocol bits
parent
8f443a38af
commit
82b12901b9
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
@interface NSData (messagePadding)
|
||||
|
||||
- (NSData *)removePadding;
|
||||
|
||||
- (NSData *)paddedMessageBody;
|
||||
|
||||
@end
|
@ -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
|
@ -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"),
|
||||
]
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
|
@ -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);
|
||||
// }
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum SMKError: Error {
|
||||
case assertionError(description: String)
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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])
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
|
||||
@objc(LKSessionRestorationStatus)
|
||||
public enum SessionRestorationStatus : Int {
|
||||
case none, initiated, requestReceived
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue