Compare commits
1 Commits
master
...
mkirk/fix-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49ae3c6250 |
@ -21,7 +21,9 @@ A Swift & Objective-C library used by other Signal libraries.
|
||||
s.source = { :git => "https://github.com/signalapp/SignalMetadataKit.git", :tag => s.version.to_s }
|
||||
s.social_media_url = 'https://twitter.com/signalapp'
|
||||
|
||||
s.platform = :ios, '10.0'
|
||||
s.platform = :ios, '9.0'
|
||||
#s.ios.deployment_target = '9.0'
|
||||
#s.osx.deployment_target = '10.9'
|
||||
s.requires_arc = true
|
||||
|
||||
s.source_files = 'SignalMetadataKit/src/**/*.{h,m,mm,swift}', 'SignalMetadataKit/Private/**/*.{h,m,mm,swift}'
|
||||
@ -39,9 +41,10 @@ A Swift & Objective-C library used by other Signal libraries.
|
||||
|
||||
s.resources = ["SignalMetadataKit/Resources/Certificates/*"]
|
||||
|
||||
s.dependency 'AxolotlKit'
|
||||
s.dependency 'CocoaLumberjack'
|
||||
s.dependency 'Curve25519Kit'
|
||||
s.dependency 'SignalClient'
|
||||
s.dependency 'HKDFKit'
|
||||
s.dependency 'SignalCoreKit'
|
||||
s.dependency 'SwiftProtobuf'
|
||||
|
||||
|
||||
34
SignalMetadataKit/src/ECPrivateKey.swift
Normal file
34
SignalMetadataKit/src/ECPrivateKey.swift
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// See:
|
||||
// https://github.com/signalapp/libsignal-protocol-java/blob/87fae0f98332e98a32bbb82515428b4edeb4181f/java/src/main/java/org/whispersystems/libsignal/ecc/ECPrivateKey.java
|
||||
@objc public class ECPrivateKey: NSObject {
|
||||
|
||||
@objc
|
||||
public let keyData: Data
|
||||
|
||||
@objc
|
||||
public init(keyData: Data) throws {
|
||||
guard keyData.count == ECCKeyLength else {
|
||||
throw SMKError.assertionError(description: "\(ECPrivateKey.logTag) key has invalid length")
|
||||
}
|
||||
|
||||
self.keyData = keyData
|
||||
}
|
||||
|
||||
open override func isEqual(_ object: Any?) -> Bool {
|
||||
if let object = object as? ECPrivateKey {
|
||||
return keyData == object.keyData
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public override var hash: Int {
|
||||
return keyData.hashValue
|
||||
}
|
||||
}
|
||||
61
SignalMetadataKit/src/ECPublicKey.swift
Normal file
61
SignalMetadataKit/src/ECPublicKey.swift
Normal file
@ -0,0 +1,61 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// See:
|
||||
// https://github.com/signalapp/libsignal-protocol-java/blob/87fae0f98332e98a32bbb82515428b4edeb4181f/java/src/main/java/org/whispersystems/libsignal/ecc/DjbECPublicKey.java
|
||||
@objc public class ECPublicKey: NSObject {
|
||||
|
||||
@objc
|
||||
public static let keyTypeDJB: UInt8 = 0x05
|
||||
|
||||
@objc
|
||||
public let keyData: Data
|
||||
|
||||
@objc
|
||||
public init(keyData: Data) throws {
|
||||
guard keyData.count == ECCKeyLength else {
|
||||
throw SMKError.assertionError(description: "\(ECPublicKey.logTag) key has invalid length")
|
||||
}
|
||||
|
||||
self.keyData = keyData
|
||||
}
|
||||
|
||||
// https://github.com/signalapp/libsignal-protocol-java/blob/master/java/src/main/java/org/whispersystems/libsignal/ecc/Curve.java#L30
|
||||
@objc
|
||||
public init(serializedKeyData: Data) throws {
|
||||
let parser = OWSDataParser(data: serializedKeyData)
|
||||
|
||||
let typeByte = try parser.nextByte(name: "type byte")
|
||||
guard typeByte == ECPublicKey.keyTypeDJB else {
|
||||
throw SMKError.assertionError(description: "\(ECPublicKey.logTag) key data has invalid type byte")
|
||||
}
|
||||
|
||||
let keyData = try parser.remainder(name: "key data")
|
||||
guard keyData.count == ECCKeyLength else {
|
||||
throw SMKError.assertionError(description: "\(ECPublicKey.logTag) key has invalid length")
|
||||
}
|
||||
|
||||
self.keyData = keyData
|
||||
}
|
||||
|
||||
@objc public var serialized: Data {
|
||||
let typeBytes = [ECPublicKey.keyTypeDJB]
|
||||
let typeData = Data(bytes: typeBytes)
|
||||
return NSData.join([typeData, keyData])
|
||||
}
|
||||
|
||||
open override func isEqual(_ object: Any?) -> Bool {
|
||||
if let object = object as? ECPublicKey {
|
||||
return keyData == object.keyData
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public override var hash: Int {
|
||||
return keyData.hashValue
|
||||
}
|
||||
}
|
||||
@ -125,23 +125,15 @@ struct SMKProtos_SenderCertificate {
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
var senderE164: String {
|
||||
get {return _storage._senderE164 ?? String()}
|
||||
set {_uniqueStorage()._senderE164 = newValue}
|
||||
/// @required
|
||||
var sender: String {
|
||||
get {return _storage._sender ?? String()}
|
||||
set {_uniqueStorage()._sender = newValue}
|
||||
}
|
||||
/// Returns true if `senderE164` has been explicitly set.
|
||||
var hasSenderE164: Bool {return _storage._senderE164 != nil}
|
||||
/// Clears the value of `senderE164`. Subsequent reads from it will return its default value.
|
||||
mutating func clearSenderE164() {_uniqueStorage()._senderE164 = nil}
|
||||
|
||||
var senderUuid: String {
|
||||
get {return _storage._senderUuid ?? String()}
|
||||
set {_uniqueStorage()._senderUuid = newValue}
|
||||
}
|
||||
/// Returns true if `senderUuid` has been explicitly set.
|
||||
var hasSenderUuid: Bool {return _storage._senderUuid != nil}
|
||||
/// Clears the value of `senderUuid`. Subsequent reads from it will return its default value.
|
||||
mutating func clearSenderUuid() {_uniqueStorage()._senderUuid = nil}
|
||||
/// Returns true if `sender` has been explicitly set.
|
||||
var hasSender: Bool {return _storage._sender != nil}
|
||||
/// Clears the value of `sender`. Subsequent reads from it will return its default value.
|
||||
mutating func clearSender() {_storage._sender = nil}
|
||||
|
||||
/// @required
|
||||
var senderDevice: UInt32 {
|
||||
@ -151,7 +143,7 @@ struct SMKProtos_SenderCertificate {
|
||||
/// Returns true if `senderDevice` has been explicitly set.
|
||||
var hasSenderDevice: Bool {return _storage._senderDevice != nil}
|
||||
/// Clears the value of `senderDevice`. Subsequent reads from it will return its default value.
|
||||
mutating func clearSenderDevice() {_uniqueStorage()._senderDevice = nil}
|
||||
mutating func clearSenderDevice() {_storage._senderDevice = nil}
|
||||
|
||||
/// @required
|
||||
var expires: UInt64 {
|
||||
@ -161,7 +153,7 @@ struct SMKProtos_SenderCertificate {
|
||||
/// Returns true if `expires` has been explicitly set.
|
||||
var hasExpires: Bool {return _storage._expires != nil}
|
||||
/// Clears the value of `expires`. Subsequent reads from it will return its default value.
|
||||
mutating func clearExpires() {_uniqueStorage()._expires = nil}
|
||||
mutating func clearExpires() {_storage._expires = nil}
|
||||
|
||||
/// @required
|
||||
var identityKey: Data {
|
||||
@ -171,7 +163,7 @@ struct SMKProtos_SenderCertificate {
|
||||
/// Returns true if `identityKey` has been explicitly set.
|
||||
var hasIdentityKey: Bool {return _storage._identityKey != nil}
|
||||
/// Clears the value of `identityKey`. Subsequent reads from it will return its default value.
|
||||
mutating func clearIdentityKey() {_uniqueStorage()._identityKey = nil}
|
||||
mutating func clearIdentityKey() {_storage._identityKey = nil}
|
||||
|
||||
/// @required
|
||||
var signer: SMKProtos_ServerCertificate {
|
||||
@ -181,7 +173,7 @@ struct SMKProtos_SenderCertificate {
|
||||
/// Returns true if `signer` has been explicitly set.
|
||||
var hasSigner: Bool {return _storage._signer != nil}
|
||||
/// Clears the value of `signer`. Subsequent reads from it will return its default value.
|
||||
mutating func clearSigner() {_uniqueStorage()._signer = nil}
|
||||
mutating func clearSigner() {_storage._signer = nil}
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
@ -238,6 +230,7 @@ struct SMKProtos_UnidentifiedSenderMessage {
|
||||
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
|
||||
// methods supported on all messages.
|
||||
|
||||
/// @required
|
||||
var type: SMKProtos_UnidentifiedSenderMessage.Message.TypeEnum {
|
||||
get {return _storage._type ?? .prekeyMessage}
|
||||
set {_uniqueStorage()._type = newValue}
|
||||
@ -245,7 +238,7 @@ struct SMKProtos_UnidentifiedSenderMessage {
|
||||
/// Returns true if `type` has been explicitly set.
|
||||
var hasType: Bool {return _storage._type != nil}
|
||||
/// Clears the value of `type`. Subsequent reads from it will return its default value.
|
||||
mutating func clearType() {_uniqueStorage()._type = nil}
|
||||
mutating func clearType() {_storage._type = nil}
|
||||
|
||||
/// @required
|
||||
var senderCertificate: SMKProtos_SenderCertificate {
|
||||
@ -255,7 +248,7 @@ struct SMKProtos_UnidentifiedSenderMessage {
|
||||
/// Returns true if `senderCertificate` has been explicitly set.
|
||||
var hasSenderCertificate: Bool {return _storage._senderCertificate != nil}
|
||||
/// Clears the value of `senderCertificate`. Subsequent reads from it will return its default value.
|
||||
mutating func clearSenderCertificate() {_uniqueStorage()._senderCertificate = nil}
|
||||
mutating func clearSenderCertificate() {_storage._senderCertificate = nil}
|
||||
|
||||
/// @required
|
||||
var content: Data {
|
||||
@ -265,7 +258,7 @@ struct SMKProtos_UnidentifiedSenderMessage {
|
||||
/// Returns true if `content` has been explicitly set.
|
||||
var hasContent: Bool {return _storage._content != nil}
|
||||
/// Clears the value of `content`. Subsequent reads from it will return its default value.
|
||||
mutating func clearContent() {_uniqueStorage()._content = nil}
|
||||
mutating func clearContent() {_storage._content = nil}
|
||||
|
||||
var unknownFields = SwiftProtobuf.UnknownStorage()
|
||||
|
||||
@ -338,10 +331,10 @@ extension SMKProtos_ServerCertificate: SwiftProtobuf.Message, SwiftProtobuf._Mes
|
||||
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}
|
||||
func _protobuf_generated_isEqualTo(other: SMKProtos_ServerCertificate) -> Bool {
|
||||
if self._certificate != other._certificate {return false}
|
||||
if self._signature != other._signature {return false}
|
||||
if unknownFields != other.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -373,10 +366,10 @@ extension SMKProtos_ServerCertificate.Certificate: SwiftProtobuf.Message, SwiftP
|
||||
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}
|
||||
func _protobuf_generated_isEqualTo(other: SMKProtos_ServerCertificate.Certificate) -> Bool {
|
||||
if self._id != other._id {return false}
|
||||
if self._key != other._key {return false}
|
||||
if unknownFields != other.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -408,10 +401,10 @@ extension SMKProtos_SenderCertificate: SwiftProtobuf.Message, SwiftProtobuf._Mes
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: SMKProtos_SenderCertificate, rhs: SMKProtos_SenderCertificate) -> Bool {
|
||||
if lhs._certificate != rhs._certificate {return false}
|
||||
if lhs._signature != rhs._signature {return false}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
func _protobuf_generated_isEqualTo(other: SMKProtos_SenderCertificate) -> Bool {
|
||||
if self._certificate != other._certificate {return false}
|
||||
if self._signature != other._signature {return false}
|
||||
if unknownFields != other.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -419,8 +412,7 @@ extension SMKProtos_SenderCertificate: SwiftProtobuf.Message, SwiftProtobuf._Mes
|
||||
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: "senderE164"),
|
||||
6: .same(proto: "senderUuid"),
|
||||
1: .same(proto: "sender"),
|
||||
2: .same(proto: "senderDevice"),
|
||||
3: .same(proto: "expires"),
|
||||
4: .same(proto: "identityKey"),
|
||||
@ -428,8 +420,7 @@ extension SMKProtos_SenderCertificate.Certificate: SwiftProtobuf.Message, SwiftP
|
||||
]
|
||||
|
||||
fileprivate class _StorageClass {
|
||||
var _senderE164: String? = nil
|
||||
var _senderUuid: String? = nil
|
||||
var _sender: String? = nil
|
||||
var _senderDevice: UInt32? = nil
|
||||
var _expires: UInt64? = nil
|
||||
var _identityKey: Data? = nil
|
||||
@ -440,8 +431,7 @@ extension SMKProtos_SenderCertificate.Certificate: SwiftProtobuf.Message, SwiftP
|
||||
private init() {}
|
||||
|
||||
init(copying source: _StorageClass) {
|
||||
_senderE164 = source._senderE164
|
||||
_senderUuid = source._senderUuid
|
||||
_sender = source._sender
|
||||
_senderDevice = source._senderDevice
|
||||
_expires = source._expires
|
||||
_identityKey = source._identityKey
|
||||
@ -461,12 +451,11 @@ extension SMKProtos_SenderCertificate.Certificate: SwiftProtobuf.Message, SwiftP
|
||||
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
|
||||
while let fieldNumber = try decoder.nextFieldNumber() {
|
||||
switch fieldNumber {
|
||||
case 1: try decoder.decodeSingularStringField(value: &_storage._senderE164)
|
||||
case 1: try decoder.decodeSingularStringField(value: &_storage._sender)
|
||||
case 2: try decoder.decodeSingularUInt32Field(value: &_storage._senderDevice)
|
||||
case 3: try decoder.decodeSingularFixed64Field(value: &_storage._expires)
|
||||
case 4: try decoder.decodeSingularBytesField(value: &_storage._identityKey)
|
||||
case 5: try decoder.decodeSingularMessageField(value: &_storage._signer)
|
||||
case 6: try decoder.decodeSingularStringField(value: &_storage._senderUuid)
|
||||
default: break
|
||||
}
|
||||
}
|
||||
@ -475,7 +464,7 @@ extension SMKProtos_SenderCertificate.Certificate: SwiftProtobuf.Message, SwiftP
|
||||
|
||||
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
|
||||
try withExtendedLifetime(_storage) { (_storage: _StorageClass) in
|
||||
if let v = _storage._senderE164 {
|
||||
if let v = _storage._sender {
|
||||
try visitor.visitSingularStringField(value: v, fieldNumber: 1)
|
||||
}
|
||||
if let v = _storage._senderDevice {
|
||||
@ -490,29 +479,25 @@ extension SMKProtos_SenderCertificate.Certificate: SwiftProtobuf.Message, SwiftP
|
||||
if let v = _storage._signer {
|
||||
try visitor.visitSingularMessageField(value: v, fieldNumber: 5)
|
||||
}
|
||||
if let v = _storage._senderUuid {
|
||||
try visitor.visitSingularStringField(value: v, fieldNumber: 6)
|
||||
}
|
||||
}
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: SMKProtos_SenderCertificate.Certificate, rhs: SMKProtos_SenderCertificate.Certificate) -> Bool {
|
||||
if lhs._storage !== rhs._storage {
|
||||
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
|
||||
func _protobuf_generated_isEqualTo(other: SMKProtos_SenderCertificate.Certificate) -> Bool {
|
||||
if _storage !== other._storage {
|
||||
let storagesAreEqual: Bool = withExtendedLifetime((_storage, other._storage)) { (_args: (_StorageClass, _StorageClass)) in
|
||||
let _storage = _args.0
|
||||
let rhs_storage = _args.1
|
||||
if _storage._senderE164 != rhs_storage._senderE164 {return false}
|
||||
if _storage._senderUuid != rhs_storage._senderUuid {return false}
|
||||
if _storage._senderDevice != rhs_storage._senderDevice {return false}
|
||||
if _storage._expires != rhs_storage._expires {return false}
|
||||
if _storage._identityKey != rhs_storage._identityKey {return false}
|
||||
if _storage._signer != rhs_storage._signer {return false}
|
||||
let other_storage = _args.1
|
||||
if _storage._sender != other_storage._sender {return false}
|
||||
if _storage._senderDevice != other_storage._senderDevice {return false}
|
||||
if _storage._expires != other_storage._expires {return false}
|
||||
if _storage._identityKey != other_storage._identityKey {return false}
|
||||
if _storage._signer != other_storage._signer {return false}
|
||||
return true
|
||||
}
|
||||
if !storagesAreEqual {return false}
|
||||
}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
if unknownFields != other.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -549,11 +534,11 @@ extension SMKProtos_UnidentifiedSenderMessage: SwiftProtobuf.Message, SwiftProto
|
||||
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}
|
||||
func _protobuf_generated_isEqualTo(other: SMKProtos_UnidentifiedSenderMessage) -> Bool {
|
||||
if self._ephemeralPublic != other._ephemeralPublic {return false}
|
||||
if self._encryptedStatic != other._encryptedStatic {return false}
|
||||
if self._encryptedMessage != other._encryptedMessage {return false}
|
||||
if unknownFields != other.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@ -618,19 +603,19 @@ extension SMKProtos_UnidentifiedSenderMessage.Message: SwiftProtobuf.Message, Sw
|
||||
try unknownFields.traverse(visitor: &visitor)
|
||||
}
|
||||
|
||||
static func ==(lhs: SMKProtos_UnidentifiedSenderMessage.Message, rhs: SMKProtos_UnidentifiedSenderMessage.Message) -> Bool {
|
||||
if lhs._storage !== rhs._storage {
|
||||
let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in
|
||||
func _protobuf_generated_isEqualTo(other: SMKProtos_UnidentifiedSenderMessage.Message) -> Bool {
|
||||
if _storage !== other._storage {
|
||||
let storagesAreEqual: Bool = withExtendedLifetime((_storage, other._storage)) { (_args: (_StorageClass, _StorageClass)) in
|
||||
let _storage = _args.0
|
||||
let rhs_storage = _args.1
|
||||
if _storage._type != rhs_storage._type {return false}
|
||||
if _storage._senderCertificate != rhs_storage._senderCertificate {return false}
|
||||
if _storage._content != rhs_storage._content {return false}
|
||||
let other_storage = _args.1
|
||||
if _storage._type != other_storage._type {return false}
|
||||
if _storage._senderCertificate != other_storage._senderCertificate {return false}
|
||||
if _storage._content != other_storage._content {return false}
|
||||
return true
|
||||
}
|
||||
if !storagesAreEqual {return false}
|
||||
}
|
||||
if lhs.unknownFields != rhs.unknownFields {return false}
|
||||
if unknownFields != other.unknownFields {return false}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
//
|
||||
// Copyright (c) 2019 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SignalCoreKit
|
||||
|
||||
// WARNING: This code is generated. Only edit within the markers.
|
||||
|
||||
@ -21,12 +20,6 @@ public enum SMKProtoError: Error {
|
||||
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()
|
||||
@ -101,10 +94,6 @@ public enum SMKProtoError: Error {
|
||||
key: key)
|
||||
return result
|
||||
}
|
||||
|
||||
@objc public override var debugDescription: String {
|
||||
return "\(proto)"
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
@ -133,12 +122,6 @@ extension SMKProtoServerCertificateCertificate.SMKProtoServerCertificateCertific
|
||||
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()
|
||||
@ -213,10 +196,6 @@ extension SMKProtoServerCertificateCertificate.SMKProtoServerCertificateCertific
|
||||
signature: signature)
|
||||
return result
|
||||
}
|
||||
|
||||
@objc public override var debugDescription: String {
|
||||
return "\(proto)"
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
@ -241,20 +220,8 @@ extension SMKProtoServerCertificate.SMKProtoServerCertificateBuilder {
|
||||
|
||||
// MARK: - SMKProtoSenderCertificateCertificateBuilder
|
||||
|
||||
@objc public class func builder(senderDevice: UInt32, expires: UInt64, identityKey: Data, signer: SMKProtoServerCertificate) -> SMKProtoSenderCertificateCertificateBuilder {
|
||||
return SMKProtoSenderCertificateCertificateBuilder(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(senderDevice: senderDevice, expires: expires, identityKey: identityKey, signer: signer)
|
||||
if let _value = senderE164 {
|
||||
builder.setSenderE164(_value)
|
||||
}
|
||||
if let _value = senderUuid {
|
||||
builder.setSenderUuid(_value)
|
||||
}
|
||||
return builder
|
||||
@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)
|
||||
}
|
||||
|
||||
@objc public class SMKProtoSenderCertificateCertificateBuilder: NSObject {
|
||||
@ -263,21 +230,18 @@ extension SMKProtoServerCertificate.SMKProtoServerCertificateBuilder {
|
||||
|
||||
@objc fileprivate override init() {}
|
||||
|
||||
@objc fileprivate init(senderDevice: UInt32, expires: UInt64, identityKey: Data, signer: SMKProtoServerCertificate) {
|
||||
@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 setSenderE164(_ valueParam: String) {
|
||||
proto.senderE164 = valueParam
|
||||
}
|
||||
|
||||
@objc public func setSenderUuid(_ valueParam: String) {
|
||||
proto.senderUuid = valueParam
|
||||
@objc public func setSender(_ valueParam: String) {
|
||||
proto.sender = valueParam
|
||||
}
|
||||
|
||||
@objc public func setSenderDevice(_ valueParam: UInt32) {
|
||||
@ -307,6 +271,8 @@ extension SMKProtoServerCertificate.SMKProtoServerCertificateBuilder {
|
||||
|
||||
fileprivate let proto: SMKProtos_SenderCertificate.Certificate
|
||||
|
||||
@objc public let sender: String
|
||||
|
||||
@objc public let senderDevice: UInt32
|
||||
|
||||
@objc public let expires: UInt64
|
||||
@ -315,32 +281,14 @@ extension SMKProtoServerCertificate.SMKProtoServerCertificateBuilder {
|
||||
|
||||
@objc public let signer: SMKProtoServerCertificate
|
||||
|
||||
@objc public var senderE164: String? {
|
||||
guard proto.hasSenderE164 else {
|
||||
return nil
|
||||
}
|
||||
return proto.senderE164
|
||||
}
|
||||
@objc public var hasSenderE164: Bool {
|
||||
return proto.hasSenderE164
|
||||
}
|
||||
|
||||
@objc public var senderUuid: String? {
|
||||
guard proto.hasSenderUuid else {
|
||||
return nil
|
||||
}
|
||||
return proto.senderUuid
|
||||
}
|
||||
@objc public var hasSenderUuid: Bool {
|
||||
return proto.hasSenderUuid
|
||||
}
|
||||
|
||||
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
|
||||
@ -358,6 +306,11 @@ extension SMKProtoServerCertificate.SMKProtoServerCertificateBuilder {
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
@ -383,16 +336,13 @@ extension SMKProtoServerCertificate.SMKProtoServerCertificateBuilder {
|
||||
// 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
|
||||
@ -421,12 +371,6 @@ extension SMKProtoSenderCertificateCertificate.SMKProtoSenderCertificateCertific
|
||||
return SMKProtoSenderCertificateBuilder(certificate: certificate, signature: signature)
|
||||
}
|
||||
|
||||
// asBuilder() constructs a builder that reflects the proto's contents.
|
||||
@objc public func asBuilder() -> SMKProtoSenderCertificateBuilder {
|
||||
let builder = SMKProtoSenderCertificateBuilder(certificate: certificate, signature: signature)
|
||||
return builder
|
||||
}
|
||||
|
||||
@objc public class SMKProtoSenderCertificateBuilder: NSObject {
|
||||
|
||||
private var proto = SMKProtos_SenderCertificate()
|
||||
@ -501,10 +445,6 @@ extension SMKProtoSenderCertificateCertificate.SMKProtoSenderCertificateCertific
|
||||
signature: signature)
|
||||
return result
|
||||
}
|
||||
|
||||
@objc public override var debugDescription: String {
|
||||
return "\(proto)"
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
@ -550,17 +490,8 @@ extension SMKProtoSenderCertificate.SMKProtoSenderCertificateBuilder {
|
||||
|
||||
// MARK: - SMKProtoUnidentifiedSenderMessageMessageBuilder
|
||||
|
||||
@objc public class func builder(senderCertificate: SMKProtoSenderCertificate, content: Data) -> SMKProtoUnidentifiedSenderMessageMessageBuilder {
|
||||
return SMKProtoUnidentifiedSenderMessageMessageBuilder(senderCertificate: senderCertificate, content: content)
|
||||
}
|
||||
|
||||
// asBuilder() constructs a builder that reflects the proto's contents.
|
||||
@objc public func asBuilder() -> SMKProtoUnidentifiedSenderMessageMessageBuilder {
|
||||
let builder = SMKProtoUnidentifiedSenderMessageMessageBuilder(senderCertificate: senderCertificate, content: content)
|
||||
if let _value = type {
|
||||
builder.setType(_value)
|
||||
}
|
||||
return builder
|
||||
@objc public class func builder(type: SMKProtoUnidentifiedSenderMessageMessageType, senderCertificate: SMKProtoSenderCertificate, content: Data) -> SMKProtoUnidentifiedSenderMessageMessageBuilder {
|
||||
return SMKProtoUnidentifiedSenderMessageMessageBuilder(type: type, senderCertificate: senderCertificate, content: content)
|
||||
}
|
||||
|
||||
@objc public class SMKProtoUnidentifiedSenderMessageMessageBuilder: NSObject {
|
||||
@ -569,9 +500,10 @@ extension SMKProtoSenderCertificate.SMKProtoSenderCertificateBuilder {
|
||||
|
||||
@objc fileprivate override init() {}
|
||||
|
||||
@objc fileprivate init(senderCertificate: SMKProtoSenderCertificate, content: Data) {
|
||||
@objc fileprivate init(type: SMKProtoUnidentifiedSenderMessageMessageType, senderCertificate: SMKProtoSenderCertificate, content: Data) {
|
||||
super.init()
|
||||
|
||||
setType(type)
|
||||
setSenderCertificate(senderCertificate)
|
||||
setContent(content)
|
||||
}
|
||||
@ -599,32 +531,18 @@ extension SMKProtoSenderCertificate.SMKProtoSenderCertificateBuilder {
|
||||
|
||||
fileprivate let proto: SMKProtos_UnidentifiedSenderMessage.Message
|
||||
|
||||
@objc public let type: SMKProtoUnidentifiedSenderMessageMessageType
|
||||
|
||||
@objc public let senderCertificate: SMKProtoSenderCertificate
|
||||
|
||||
@objc public let content: Data
|
||||
|
||||
public var type: SMKProtoUnidentifiedSenderMessageMessageType? {
|
||||
guard proto.hasType else {
|
||||
return nil
|
||||
}
|
||||
return SMKProtoUnidentifiedSenderMessageMessage.SMKProtoUnidentifiedSenderMessageMessageTypeWrap(proto.type)
|
||||
}
|
||||
// This "unwrapped" accessor should only be used if the "has value" accessor has already been checked.
|
||||
@objc public var unwrappedType: SMKProtoUnidentifiedSenderMessageMessageType {
|
||||
if !hasType {
|
||||
// TODO: We could make this a crashing assert.
|
||||
owsFailDebug("Unsafe unwrap of missing optional: Message.type.")
|
||||
}
|
||||
return SMKProtoUnidentifiedSenderMessageMessage.SMKProtoUnidentifiedSenderMessageMessageTypeWrap(proto.type)
|
||||
}
|
||||
@objc public var hasType: Bool {
|
||||
return proto.hasType
|
||||
}
|
||||
|
||||
private init(proto: SMKProtos_UnidentifiedSenderMessage.Message,
|
||||
type: SMKProtoUnidentifiedSenderMessageMessageType,
|
||||
senderCertificate: SMKProtoSenderCertificate,
|
||||
content: Data) {
|
||||
self.proto = proto
|
||||
self.type = type
|
||||
self.senderCertificate = senderCertificate
|
||||
self.content = content
|
||||
}
|
||||
@ -640,6 +558,11 @@ extension SMKProtoSenderCertificate.SMKProtoSenderCertificateBuilder {
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
@ -655,14 +578,11 @@ extension SMKProtoSenderCertificate.SMKProtoSenderCertificateBuilder {
|
||||
// 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
|
||||
@ -691,12 +611,6 @@ extension SMKProtoUnidentifiedSenderMessageMessage.SMKProtoUnidentifiedSenderMes
|
||||
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()
|
||||
@ -786,10 +700,6 @@ extension SMKProtoUnidentifiedSenderMessageMessage.SMKProtoUnidentifiedSenderMes
|
||||
encryptedMessage: encryptedMessage)
|
||||
return result
|
||||
}
|
||||
|
||||
@objc public override var debugDescription: String {
|
||||
return "\(proto)"
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
@ -1,11 +1,7 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/NSData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSData (messagePadding)
|
||||
|
||||
- (NSData *)removePadding;
|
||||
@ -13,5 +9,3 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (NSData *)paddedMessageBody;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
10
SignalMetadataKit/src/SMK-Bridging-Header.h
Normal file
10
SignalMetadataKit/src/SMK-Bridging-Header.h
Normal file
@ -0,0 +1,10 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import <AxolotlKit/AxolotlStore.h>
|
||||
#import <Curve25519Kit/Curve25519.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <SignalCoreKit/NSObject+OWS.h>
|
||||
#import <SignalCoreKit/OWSAsserts.h>
|
||||
#import <HKDFKit/HKDFKit.h>
|
||||
@ -1,70 +0,0 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum SMKAddress {
|
||||
case both(uuid: UUID, e164: String)
|
||||
case uuid(_ uuid: UUID)
|
||||
case e164(_ e164: String)
|
||||
}
|
||||
|
||||
public extension SMKAddress {
|
||||
init(uuid: UUID?, e164: String?) throws {
|
||||
switch (uuid, e164) {
|
||||
case (.some(let uuid), .some(let e164)):
|
||||
self = .both(uuid: uuid, e164: e164)
|
||||
case (.some(let uuid), .none):
|
||||
self = .uuid(uuid)
|
||||
case (.none, .some(let e164)):
|
||||
self = .e164(e164)
|
||||
case (.none, .none):
|
||||
throw SMKError.invalidInput("had neither uuid nor e164")
|
||||
}
|
||||
}
|
||||
|
||||
var uuid: UUID? {
|
||||
switch self {
|
||||
case .both(let uuid, _):
|
||||
return uuid
|
||||
case .uuid(let uuid):
|
||||
return uuid
|
||||
case .e164:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var e164: String? {
|
||||
switch self {
|
||||
case .both(_, let e164):
|
||||
return e164
|
||||
case .uuid:
|
||||
return nil
|
||||
case .e164(let e164):
|
||||
return e164
|
||||
}
|
||||
}
|
||||
|
||||
func matches(_ other: SMKAddress) -> Bool {
|
||||
switch self {
|
||||
case .both(let uuid, let e164):
|
||||
if other.uuid == uuid || other.e164 == e164 {
|
||||
// If one matches, then the other should also match
|
||||
// if it's available
|
||||
assert(other.uuid == uuid || other.uuid == nil)
|
||||
assert(other.e164 == e164 || other.e164 == nil)
|
||||
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case .uuid(let uuid):
|
||||
return other.uuid == uuid
|
||||
case .e164(let e164):
|
||||
return other.e164 == e164
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension SMKAddress: Equatable, Hashable { }
|
||||
@ -1,19 +1,18 @@
|
||||
//
|
||||
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Curve25519Kit
|
||||
import SignalCoreKit
|
||||
import SignalClient
|
||||
|
||||
public enum SMKCertificateError: Error {
|
||||
case invalidCertificate(description: String)
|
||||
}
|
||||
|
||||
public protocol SMKCertificateValidator {
|
||||
func throwswrapped_validate(senderCertificate: SenderCertificate, validationTime: UInt64) throws
|
||||
func throwswrapped_validate(serverCertificate: ServerCertificate) throws
|
||||
@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
|
||||
@ -26,6 +25,7 @@ public protocol SMKCertificateValidator {
|
||||
// }};
|
||||
private static let kRevokedCertificateIds = Set<UInt32>()
|
||||
|
||||
//
|
||||
// private final ECPublicKey trustRoot;
|
||||
private let trustRoot: ECPublicKey
|
||||
|
||||
@ -37,10 +37,10 @@ public protocol SMKCertificateValidator {
|
||||
}
|
||||
|
||||
// public void validate(SenderCertificate certificate, long validationTime) throws InvalidCertificateException {
|
||||
public func throwswrapped_validate(senderCertificate: SenderCertificate, validationTime: UInt64) throws {
|
||||
@objc public func throwswrapped_validate(senderCertificate: SMKSenderCertificate, validationTime: UInt64) throws {
|
||||
// try {
|
||||
// ServerCertificate serverCertificate = certificate.getSigner();
|
||||
let serverCertificate = senderCertificate.serverCertificate
|
||||
let serverCertificate = senderCertificate.signer
|
||||
|
||||
// validate(serverCertificate);
|
||||
try throwswrapped_validate(serverCertificate: serverCertificate)
|
||||
@ -48,8 +48,10 @@ public protocol SMKCertificateValidator {
|
||||
// if (!Curve.verifySignature(serverCertificate.getKey(), certificate.getCertificate(), certificate.getSignature())) {
|
||||
// throw new InvalidCertificateException("Signature failed");
|
||||
// }
|
||||
guard try serverCertificate.publicKey.verifySignature(message: senderCertificate.certificateBytes,
|
||||
signature: senderCertificate.signatureBytes) else {
|
||||
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)")
|
||||
@ -59,7 +61,7 @@ public protocol SMKCertificateValidator {
|
||||
// if (validationTime > certificate.getExpiration()) {
|
||||
// throw new InvalidCertificateException("Certificate is expired");
|
||||
// }
|
||||
guard validationTime <= senderCertificate.expiration else {
|
||||
guard validationTime <= senderCertificate.expirationTimestamp else {
|
||||
let error = SMKCertificateError.invalidCertificate(description: "Certficate is expired.")
|
||||
Logger.error("\(error)")
|
||||
throw error
|
||||
@ -70,30 +72,35 @@ public protocol SMKCertificateValidator {
|
||||
// }
|
||||
}
|
||||
|
||||
// void validate(ServerCertificate certificate) throws InvalidCertificateException {
|
||||
public func throwswrapped_validate(serverCertificate: ServerCertificate) throws {
|
||||
// try {
|
||||
// if (!Curve.verifySignature(trustRoot, certificate.getCertificate(), certificate.getSignature())) {
|
||||
// throw new InvalidCertificateException("Signature failed");
|
||||
// }
|
||||
guard try trustRoot.key.verifySignature(message: serverCertificate.certificateBytes,
|
||||
signature: serverCertificate.signatureBytes) else {
|
||||
let error = SMKCertificateError.invalidCertificate(description: "Server certificate signature verification failed.")
|
||||
Logger.error("\(error)")
|
||||
throw error
|
||||
}
|
||||
// // 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()
|
||||
|
||||
// if (REVOKED.contains(certificate.getKeyId())) {
|
||||
// throw new InvalidCertificateException("Server certificate has been revoked");
|
||||
// }
|
||||
// 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);
|
||||
// }
|
||||
// } catch (InvalidKeyException e) {
|
||||
// throw new InvalidCertificateException(e);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,5 +6,4 @@ import Foundation
|
||||
|
||||
public enum SMKError: Error {
|
||||
case assertionError(description: String)
|
||||
case invalidInput(_ description: String)
|
||||
}
|
||||
|
||||
@ -1,37 +1,40 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Curve25519Kit
|
||||
import SignalCoreKit
|
||||
import SignalClient
|
||||
|
||||
public struct SecretSessionKnownSenderError: Error {
|
||||
public let senderAddress: SMKAddress
|
||||
public let senderDeviceId: UInt32
|
||||
public let cipherType: CiphertextMessage.MessageType
|
||||
public let groupId: Data?
|
||||
public let unsealedContent: Data
|
||||
public let contentHint: UnidentifiedSenderMessageContent.ContentHint
|
||||
public let underlyingError: Error
|
||||
|
||||
init(messageContent: UnidentifiedSenderMessageContent, underlyingError: Error) {
|
||||
self.senderAddress = SMKAddress(messageContent.senderCertificate.sender)
|
||||
self.senderDeviceId = messageContent.senderCertificate.sender.deviceId
|
||||
self.cipherType = messageContent.messageType
|
||||
self.groupId = messageContent.groupId.map { Data($0) }
|
||||
self.unsealedContent = Data(messageContent.contents)
|
||||
self.contentHint = messageContent.contentHint
|
||||
self.underlyingError = underlyingError
|
||||
}
|
||||
}
|
||||
|
||||
@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 {
|
||||
@ -75,35 +78,19 @@ private class SMKStaticKeys: NSObject {
|
||||
|
||||
// MARK: -
|
||||
|
||||
@objc public enum SMKMessageType: Int {
|
||||
case whisper
|
||||
case prekey
|
||||
case senderKey
|
||||
case plaintext
|
||||
}
|
||||
|
||||
@objc
|
||||
public class SMKDecryptResult: NSObject {
|
||||
|
||||
public let senderAddress: SMKAddress
|
||||
|
||||
@objc public var senderE164: String? {
|
||||
return senderAddress.e164
|
||||
}
|
||||
|
||||
@objc public var senderUuid: UUID? {
|
||||
return senderAddress.uuid
|
||||
}
|
||||
|
||||
@objc public let senderRecipientId: String
|
||||
@objc public let senderDeviceId: Int
|
||||
@objc public let paddedPayload: Data
|
||||
@objc public let messageType: SMKMessageType
|
||||
|
||||
init(senderAddress: SMKAddress,
|
||||
init(senderRecipientId: String,
|
||||
senderDeviceId: Int,
|
||||
paddedPayload: Data,
|
||||
messageType: SMKMessageType) {
|
||||
self.senderAddress = senderAddress
|
||||
self.senderRecipientId = senderRecipientId
|
||||
self.senderDeviceId = senderDeviceId
|
||||
self.paddedPayload = paddedPayload
|
||||
self.messageType = messageType
|
||||
@ -112,39 +99,6 @@ public class SMKDecryptResult: NSObject {
|
||||
|
||||
// MARK: -
|
||||
|
||||
fileprivate extension ProtocolAddress {
|
||||
convenience init(from recipientAddress: SMKAddress, deviceId: UInt32) throws {
|
||||
try self.init(name: recipientAddress.uuid?.uuidString ?? recipientAddress.e164!, deviceId: deviceId)
|
||||
}
|
||||
|
||||
convenience init(from senderAddress: SealedSenderAddress) throws {
|
||||
try self.init(name: senderAddress.uuidString, deviceId: senderAddress.deviceId)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension SMKAddress {
|
||||
init(_ address: SealedSenderAddress) {
|
||||
try! self.init(uuid: UUID(uuidString: address.uuidString), e164: address.e164)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate extension SMKMessageType {
|
||||
init(_ messageType: CiphertextMessage.MessageType) {
|
||||
switch messageType {
|
||||
case .whisper:
|
||||
self = .whisper
|
||||
case .preKey:
|
||||
self = .prekey
|
||||
case .senderKey:
|
||||
self = .senderKey
|
||||
case .plaintext:
|
||||
self = .plaintext
|
||||
default:
|
||||
fatalError("not ready for other kinds of messages yet")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc public class SMKSecretSessionCipher: NSObject {
|
||||
|
||||
private let kUDPrefixString = "UnidentifiedDelivery"
|
||||
@ -155,147 +109,355 @@ fileprivate extension SMKMessageType {
|
||||
private let preKeyStore: PreKeyStore
|
||||
private let signedPreKeyStore: SignedPreKeyStore
|
||||
private let identityStore: IdentityKeyStore
|
||||
private let senderKeyStore: SenderKeyStore
|
||||
|
||||
// public SecretSessionCipher(SignalProtocolStore signalProtocolStore) {
|
||||
public init(sessionStore: SessionStore,
|
||||
preKeyStore: PreKeyStore,
|
||||
signedPreKeyStore: SignedPreKeyStore,
|
||||
identityStore: IdentityKeyStore,
|
||||
senderKeyStore: SenderKeyStore) throws {
|
||||
@objc public init(sessionStore: SessionStore,
|
||||
preKeyStore: PreKeyStore,
|
||||
signedPreKeyStore: SignedPreKeyStore,
|
||||
identityStore: IdentityKeyStore) throws {
|
||||
|
||||
self.sessionStore = sessionStore
|
||||
self.preKeyStore = preKeyStore
|
||||
self.signedPreKeyStore = signedPreKeyStore
|
||||
self.identityStore = identityStore
|
||||
self.senderKeyStore = senderKeyStore
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
public func encryptMessage(
|
||||
recipient: SMKAddress,
|
||||
deviceId: Int32,
|
||||
paddedPlaintext: Data,
|
||||
contentHint: UnidentifiedSenderMessageContent.ContentHint,
|
||||
groupId: Data?,
|
||||
senderCertificate: SenderCertificate,
|
||||
protocolContext: StoreContext
|
||||
) throws -> Data {
|
||||
|
||||
// public byte[] encrypt(SignalProtocolAddress destinationAddress, SenderCertificate senderCertificate, byte[]
|
||||
// paddedPlaintext)
|
||||
@objc
|
||||
public func throwswrapped_encryptMessage(recipientId: String,
|
||||
deviceId: Int32,
|
||||
paddedPlaintext: Data,
|
||||
senderCertificate: SMKSenderCertificate,
|
||||
protocolContext: Any?) throws -> Data {
|
||||
guard recipientId.count > 0 else {
|
||||
throw SMKError.assertionError(description: "\(SMKSecretSessionCipher.logTag) invalid recipientId")
|
||||
}
|
||||
guard deviceId > 0 else {
|
||||
throw SMKError.assertionError(description: "\(logTag) invalid deviceId")
|
||||
throw SMKError.assertionError(description: "\(SMKSecretSessionCipher.logTag) invalid deviceId")
|
||||
}
|
||||
let recipientAddress = try ProtocolAddress(from: recipient, deviceId: UInt32(bitPattern: deviceId))
|
||||
|
||||
// CiphertextMessage message = new SessionCipher(signalProtocolStore, destinationAddress).encrypt(paddedPlaintext);
|
||||
let cipher = SessionCipher(sessionStore: sessionStore,
|
||||
preKeyStore: preKeyStore,
|
||||
signedPreKeyStore: signedPreKeyStore,
|
||||
identityKeyStore: identityStore,
|
||||
recipientId: recipientId,
|
||||
deviceId: deviceId)
|
||||
|
||||
let ciphertextMessage = try signalEncrypt(
|
||||
message: paddedPlaintext,
|
||||
for: recipientAddress,
|
||||
sessionStore: sessionStore,
|
||||
identityStore: identityStore,
|
||||
context: protocolContext)
|
||||
// CiphertextMessage message = new SessionCipher(signalProtocolStore, destinationAddress).encrypt(paddedPlaintext);
|
||||
let encryptedMessage = try cipher.encryptMessage(paddedPlaintext, protocolContext: protocolContext)
|
||||
|
||||
let usmc = try UnidentifiedSenderMessageContent(
|
||||
ciphertextMessage,
|
||||
from: senderCertificate,
|
||||
contentHint: contentHint,
|
||||
groupId: groupId ?? Data())
|
||||
guard let encryptedMessageData = encryptedMessage.serialized() else {
|
||||
throw SMKError.assertionError(description: "\(logTag) Could not serialize encrypted message.")
|
||||
}
|
||||
|
||||
let outerBytes = try sealedSenderEncrypt(
|
||||
usmc,
|
||||
for: recipientAddress,
|
||||
identityStore: identityStore,
|
||||
context: protocolContext)
|
||||
// IdentityKeyPair ourIdentity = signalProtocolStore.getIdentityKeyPair();
|
||||
guard let ourIdentityKeyPair = identityStore.identityKeyPair(protocolContext) else {
|
||||
throw SMKError.assertionError(description: "\(logTag) Missing our identity key pair.")
|
||||
}
|
||||
|
||||
return Data(outerBytes)
|
||||
// ECPublicKey theirIdentity = signalProtocolStore.getIdentity(destinationAddress).getPublicKey();
|
||||
guard let theirIdentityKeyData = identityStore.identityKey(forRecipientId: recipientId, protocolContext: protocolContext) else {
|
||||
throw SMKError.assertionError(description: "\(logTag) Missing their public identity key.")
|
||||
}
|
||||
// NOTE: we don't use ECPublicKey(serializedKeyData) since the
|
||||
// key data should not have a type byte.
|
||||
let theirIdentityKey = try ECPublicKey(keyData: theirIdentityKeyData)
|
||||
|
||||
// ECKeyPair ephemeral = Curve.generateKeyPair();
|
||||
let ephemeral = Curve25519.generateKeyPair()
|
||||
|
||||
// byte[] ephemeralSalt = ByteUtil.combine("UnidentifiedDelivery".getBytes(), theirIdentity.serialize(),
|
||||
// ephemeral.getPublicKey().serialize());
|
||||
guard let prefixData = kUDPrefixString.data(using: String.Encoding.utf8) else {
|
||||
throw SMKError.assertionError(description: "\(logTag) Could not encode prefix.")
|
||||
}
|
||||
let ephemeralSalt = NSData.join([
|
||||
prefixData,
|
||||
theirIdentityKey.serialized,
|
||||
try ephemeral.ecPublicKey().serialized
|
||||
])
|
||||
|
||||
// EphemeralKeys ephemeralKeys = calculateEphemeralKeys(theirIdentity, ephemeral.getPrivateKey(), ephemeralSalt);
|
||||
let ephemeralKeys = try throwswrapped_calculateEphemeralKeys(ephemeralPublicKey: theirIdentityKey,
|
||||
ephemeralPrivateKey: ephemeral.ecPrivateKey(),
|
||||
salt: ephemeralSalt)
|
||||
|
||||
// byte[] staticKeyCiphertext = encrypt(ephemeralKeys.cipherKey, ephemeralKeys.macKey,
|
||||
// ourIdentity.getPublicKey().getPublicKey().serialize());
|
||||
let staticKeyCipherData = try encrypt(cipherKey: ephemeralKeys.cipherKey,
|
||||
macKey: ephemeralKeys.macKey,
|
||||
plaintextData: ourIdentityKeyPair.ecPublicKey().serialized)
|
||||
|
||||
// byte[] staticSalt = ByteUtil.combine(ephemeralKeys.chainKey, staticKeyCiphertext);
|
||||
let staticSalt = NSData.join([
|
||||
ephemeralKeys.chainKey,
|
||||
staticKeyCipherData
|
||||
])
|
||||
|
||||
// StaticKeys staticKeys = calculateStaticKeys(theirIdentity, ourIdentity.getPrivateKey(), staticSalt);
|
||||
let staticKeys = try throwswrapped_calculateStaticKeys(staticPublicKey: theirIdentityKey,
|
||||
staticPrivateKey: ourIdentityKeyPair.ecPrivateKey(),
|
||||
salt: staticSalt)
|
||||
|
||||
// UnidentifiedSenderMessageContent content = new UnidentifiedSenderMessageContent(message.getType(),
|
||||
// senderCertificate, message.serialize());
|
||||
var messageType: SMKMessageType
|
||||
switch encryptedMessage.cipherMessageType {
|
||||
case .prekey:
|
||||
messageType = .prekey
|
||||
case .whisper:
|
||||
messageType = .whisper
|
||||
default:
|
||||
throw SMKError.assertionError(description: "\(logTag) Unknown cipher message type.")
|
||||
}
|
||||
let messageContent = SMKUnidentifiedSenderMessageContent(messageType: messageType,
|
||||
senderCertificate: senderCertificate,
|
||||
contentData: encryptedMessageData)
|
||||
|
||||
// byte[] messageBytes = encrypt(staticKeys.cipherKey, staticKeys.macKey, content.getSerialized());
|
||||
let messageData = try encrypt(cipherKey: staticKeys.cipherKey,
|
||||
macKey: staticKeys.macKey,
|
||||
plaintextData: try messageContent.serialized())
|
||||
|
||||
// return new UnidentifiedSenderMessage(ephemeral.getPublicKey(), staticKeyCiphertext,
|
||||
// messageBytes).getSerialized();
|
||||
let message = SMKUnidentifiedSenderMessage(ephemeralKey: try ephemeral.ecPublicKey(),
|
||||
encryptedStatic: staticKeyCipherData,
|
||||
encryptedMessage: messageData)
|
||||
return try message.serialized()
|
||||
}
|
||||
|
||||
public func groupEncryptMessage(recipients: [ProtocolAddress],
|
||||
paddedPlaintext: Data,
|
||||
senderCertificate: SenderCertificate,
|
||||
groupId: Data,
|
||||
distributionId: UUID,
|
||||
contentHint: UnidentifiedSenderMessageContent.ContentHint = .default,
|
||||
protocolContext: StoreContext?) throws -> Data {
|
||||
|
||||
let senderAddress = try ProtocolAddress(from: senderCertificate.sender)
|
||||
let ciphertext = try groupEncrypt(
|
||||
paddedPlaintext,
|
||||
from: senderAddress,
|
||||
distributionId: distributionId,
|
||||
store: senderKeyStore,
|
||||
context: protocolContext ?? NullContext())
|
||||
|
||||
let udMessageContent = try UnidentifiedSenderMessageContent(
|
||||
ciphertext,
|
||||
from: senderCertificate,
|
||||
contentHint: contentHint,
|
||||
groupId: groupId)
|
||||
|
||||
let multiRecipientMessage = try sealedSenderMultiRecipientEncrypt(
|
||||
udMessageContent,
|
||||
for: recipients,
|
||||
identityStore: identityStore,
|
||||
sessionStore: sessionStore,
|
||||
context: protocolContext ?? NullContext())
|
||||
|
||||
return Data(multiRecipientMessage)
|
||||
}
|
||||
|
||||
// public Pair<SignalProtocolAddress, byte[]> decrypt(CertificateValidator validator, byte[] ciphertext, long timestamp)
|
||||
// throws InvalidMetadataMessageException, InvalidMetadataVersionException, ProtocolInvalidMessageException, ProtocolInvalidKeyException, ProtocolNoSessionException, ProtocolLegacyMessageException, ProtocolInvalidVersionException, ProtocolDuplicateMessageException, ProtocolInvalidKeyIdException, ProtocolUntrustedIdentityException
|
||||
// public Pair<SignalProtocolAddress, byte[]> decrypt(CertificateValidator validator, byte[] ciphertext, long
|
||||
/// timestamp) /throws /InvalidMetadataMessageException, InvalidMetadataVersionException,
|
||||
// ProtocolInvalidMessageException, ProtocolInvalidKeyException,
|
||||
// ProtocolNoSessionException, ProtocolLegacyMessageException,
|
||||
// ProtocolInvalidVersionException, ProtocolDuplicateMessageException,
|
||||
// ProtocolInvalidKeyIdException, ProtocolUntrustedIdentityException
|
||||
@objc
|
||||
public func throwswrapped_decryptMessage(certificateValidator: SMKCertificateValidator,
|
||||
cipherTextData: Data,
|
||||
timestamp: UInt64,
|
||||
localE164: String?,
|
||||
localUuid: UUID?,
|
||||
localDeviceId: Int32,
|
||||
protocolContext: StoreContext?) throws -> SMKDecryptResult {
|
||||
guard timestamp > 0 else {
|
||||
throw SMKError.assertionError(description: "\(logTag) invalid timestamp")
|
||||
}
|
||||
cipherTextData: Data,
|
||||
timestamp: UInt64,
|
||||
localRecipientId: String,
|
||||
localDeviceId: Int32,
|
||||
protocolContext: Any?) throws -> SMKDecryptResult {
|
||||
|
||||
// Allow nil contexts for testing.
|
||||
let context = protocolContext ?? NullContext()
|
||||
let messageContent = try UnidentifiedSenderMessageContent(message: cipherTextData,
|
||||
identityStore: self.identityStore,
|
||||
context: context)
|
||||
guard timestamp > 0 else {
|
||||
throw SMKError.assertionError(description: "\(logTag) invalid timestamp")
|
||||
}
|
||||
|
||||
let senderAddress = messageContent.senderCertificate.sender
|
||||
let localAddress = try SMKAddress(uuid: localUuid, e164: localE164)
|
||||
// IdentityKeyPair ourIdentity = signalProtocolStore.getIdentityKeyPair();
|
||||
guard let ourIdentityKeyPair = identityStore.identityKeyPair(protocolContext) else {
|
||||
throw SMKError.assertionError(description: "\(logTag) Missing our identity key pair.")
|
||||
}
|
||||
|
||||
guard !SMKAddress(senderAddress).matches(localAddress) ||
|
||||
Int32(bitPattern: senderAddress.deviceId) != localDeviceId else {
|
||||
Logger.info("Discarding self-sent message")
|
||||
throw SMKSecretSessionCipherError.selfSentMessage
|
||||
}
|
||||
// UnidentifiedSenderMessage wrapper = new UnidentifiedSenderMessage(ciphertext);
|
||||
let wrapper = try SMKUnidentifiedSenderMessage.parse(dataAndPrefix: cipherTextData)
|
||||
|
||||
// byte[] ephemeralSalt = ByteUtil.combine("UnidentifiedDelivery".getBytes(),
|
||||
// ourIdentity.getPublicKey().getPublicKey().serialize(), wrapper.getEphemeral().serialize());
|
||||
guard let prefixData = kUDPrefixString.data(using: String.Encoding.utf8) else {
|
||||
throw SMKError.assertionError(description: "\(logTag) Could not encode prefix.")
|
||||
}
|
||||
let ephemeralSalt = NSData.join([
|
||||
prefixData,
|
||||
try ourIdentityKeyPair.ecPublicKey().serialized,
|
||||
wrapper.ephemeralKey.serialized
|
||||
])
|
||||
|
||||
// EphemeralKeys ephemeralKeys = calculateEphemeralKeys(wrapper.getEphemeral(), ourIdentity.getPrivateKey(),
|
||||
// ephemeralSalt);
|
||||
let ephemeralKeys = try throwswrapped_calculateEphemeralKeys(ephemeralPublicKey: wrapper.ephemeralKey,
|
||||
ephemeralPrivateKey: ourIdentityKeyPair.ecPrivateKey(),
|
||||
salt: ephemeralSalt)
|
||||
|
||||
// byte[] staticKeyBytes = decrypt(ephemeralKeys.cipherKey, ephemeralKeys.macKey, wrapper.getEncryptedStatic());
|
||||
let staticKeyBytes = try decrypt(cipherKey: ephemeralKeys.cipherKey,
|
||||
macKey: ephemeralKeys.macKey,
|
||||
cipherTextWithMac: wrapper.encryptedStatic)
|
||||
|
||||
// ECPublicKey staticKey = Curve.decodePoint(staticKeyBytes, 0);
|
||||
let staticKey = try ECPublicKey(serializedKeyData: staticKeyBytes)
|
||||
|
||||
// byte[] staticSalt = ByteUtil.combine(ephemeralKeys.chainKey, wrapper.getEncryptedStatic());
|
||||
let staticSalt = NSData.join([
|
||||
ephemeralKeys.chainKey,
|
||||
wrapper.encryptedStatic
|
||||
])
|
||||
|
||||
// StaticKeys staticKeys = calculateStaticKeys(staticKey, ourIdentity.getPrivateKey(), staticSalt);
|
||||
let staticKeys = try throwswrapped_calculateStaticKeys(staticPublicKey: staticKey,
|
||||
staticPrivateKey: ourIdentityKeyPair.ecPrivateKey(),
|
||||
salt: staticSalt)
|
||||
|
||||
// byte[] messageBytes = decrypt(staticKeys.cipherKey, staticKeys.macKey, wrapper.getEncryptedMessage());
|
||||
let messageBytes = try decrypt(cipherKey: staticKeys.cipherKey,
|
||||
macKey: staticKeys.macKey,
|
||||
cipherTextWithMac: wrapper.encryptedMessage)
|
||||
|
||||
// content = new UnidentifiedSenderMessageContent(messageBytes);
|
||||
let messageContent = try SMKUnidentifiedSenderMessageContent.parse(data: messageBytes)
|
||||
|
||||
guard messageContent.senderCertificate.senderRecipientId != localRecipientId ||
|
||||
messageContent.senderCertificate.senderDeviceId != localDeviceId else {
|
||||
Logger.info("Discarding self-sent message")
|
||||
throw SMKSecretSessionCipherError.selfSentMessage
|
||||
}
|
||||
|
||||
do {
|
||||
// validator.validate(content.getSenderCertificate(), timestamp);
|
||||
try certificateValidator.throwswrapped_validate(
|
||||
senderCertificate: messageContent.senderCertificate,
|
||||
validationTime: timestamp)
|
||||
try certificateValidator.throwswrapped_validate(senderCertificate: messageContent.senderCertificate,
|
||||
validationTime: timestamp)
|
||||
|
||||
let paddedMessagePlaintext = try throwswrapped_decrypt(messageContent: messageContent,
|
||||
context: context)
|
||||
// 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 {
|
||||
throw SMKError.assertionError(description: "\(logTag) Sender's certificate key does not match key used in message.")
|
||||
}
|
||||
|
||||
let paddedMessagePlaintext = try throwswrapped_decrypt(messageContent: messageContent, protocolContext: protocolContext)
|
||||
|
||||
// return new Pair<>(new SignalProtocolAddress(content.getSenderCertificate().getSender(),
|
||||
// content.getSenderCertificate().getSenderDeviceId()),
|
||||
// decrypt(content));
|
||||
// content.getSenderCertificate().getSenderDeviceId()),
|
||||
// decrypt(content));
|
||||
//
|
||||
// NOTE: We use the sender properties from the sender certificate, not from this class' properties.
|
||||
guard senderAddress.deviceId <= Int32.max else {
|
||||
let senderRecipientId = messageContent.senderCertificate.senderRecipientId
|
||||
let senderDeviceId = messageContent.senderCertificate.senderDeviceId
|
||||
guard senderDeviceId >= 0 && senderDeviceId <= INT_MAX else {
|
||||
throw SMKError.assertionError(description: "\(logTag) Invalid senderDeviceId.")
|
||||
}
|
||||
return SMKDecryptResult(senderAddress: SMKAddress(senderAddress),
|
||||
senderDeviceId: Int(senderAddress.deviceId),
|
||||
paddedPayload: Data(paddedMessagePlaintext),
|
||||
messageType: SMKMessageType(messageContent.messageType))
|
||||
} catch {
|
||||
throw SecretSessionKnownSenderError(messageContent: messageContent,
|
||||
underlyingError: error)
|
||||
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 ephemeralSecret = try Curve25519.generateSharedSecret(fromPublicKey: ephemeralPublicKey.keyData, privateKey: ephemeralPrivateKey.keyData)
|
||||
|
||||
// byte[] ephemeralDerived = new HKDFv3().deriveSecrets(ephemeralSecret, salt, new byte[0], 96);
|
||||
let kEphemeralDerivedLength: UInt = 96
|
||||
let ephemeralDerived: Data =
|
||||
try HKDFKit.deriveKey(ephemeralSecret, info: Data(), salt: salt, outputSize: Int32(kEphemeralDerivedLength))
|
||||
guard ephemeralDerived.count == kEphemeralDerivedLength else {
|
||||
throw SMKError.assertionError(description: "\(logTag) derived ephemeral has unexpected length: \(ephemeralDerived.count).")
|
||||
}
|
||||
|
||||
let ephemeralDerivedParser = OWSDataParser(data: ephemeralDerived)
|
||||
let chainKey = try ephemeralDerivedParser.nextData(length: 32, name: "chain key")
|
||||
let cipherKey = try ephemeralDerivedParser.nextData(length: 32, name: "cipher key")
|
||||
let macKey = try ephemeralDerivedParser.nextData(length: 32, name: "mac key")
|
||||
guard ephemeralDerivedParser.isEmpty else {
|
||||
throw SMKError.assertionError(description: "\(logTag) could not parse derived ephemeral.")
|
||||
}
|
||||
|
||||
return SMKEphemeralKeys(chainKey: chainKey, cipherKey: cipherKey, macKey: macKey)
|
||||
}
|
||||
|
||||
// private StaticKeys calculateStaticKeys(ECPublicKey staticPublic, ECPrivateKey staticPrivate, byte[] salt) throws
|
||||
// InvalidKeyException {
|
||||
private func throwswrapped_calculateStaticKeys(staticPublicKey: ECPublicKey,
|
||||
staticPrivateKey: ECPrivateKey,
|
||||
salt: Data) throws -> SMKStaticKeys {
|
||||
guard staticPublicKey.keyData.count > 0 else {
|
||||
throw SMKError.assertionError(description: "\(logTag) invalid staticPublicKey")
|
||||
}
|
||||
guard staticPrivateKey.keyData.count > 0 else {
|
||||
throw SMKError.assertionError(description: "\(logTag) invalid staticPrivateKey")
|
||||
}
|
||||
guard salt.count > 0 else {
|
||||
throw SMKError.assertionError(description: "\(logTag) invalid salt")
|
||||
}
|
||||
|
||||
// byte[] staticSecret = Curve.calculateAgreement(staticPublic, staticPrivate);
|
||||
//
|
||||
// See:
|
||||
// https://github.com/signalapp/libsignal-protocol-java/blob/master/java/src/main/java/org/whispersystems/libsignal/ecc/Curve.java#L30
|
||||
let staticSecret = try Curve25519.generateSharedSecret(fromPublicKey: staticPublicKey.keyData, privateKey: staticPrivateKey.keyData)
|
||||
|
||||
// byte[] staticDerived = new HKDFv3().deriveSecrets(staticSecret, salt, new byte[0], 96);
|
||||
let kStaticDerivedLength: UInt = 96
|
||||
let staticDerived: Data =
|
||||
try HKDFKit.deriveKey(staticSecret, info: Data(), salt: salt, outputSize: Int32(kStaticDerivedLength))
|
||||
guard staticDerived.count == kStaticDerivedLength else {
|
||||
throw SMKError.assertionError(description: "\(logTag) could not derive static.")
|
||||
}
|
||||
|
||||
// byte[][] staticDerivedParts = ByteUtil.split(staticDerived, 32, 32, 32);
|
||||
let staticDerivedParser = OWSDataParser(data: staticDerived)
|
||||
_ = 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
|
||||
@ -303,15 +465,16 @@ fileprivate extension SMKMessageType {
|
||||
// private byte[] decrypt(UnidentifiedSenderMessageContent message)
|
||||
// throws InvalidVersionException, InvalidMessageException, InvalidKeyException, DuplicateMessageException,
|
||||
// InvalidKeyIdException, UntrustedIdentityException, LegacyMessageException, NoSessionException
|
||||
private func throwswrapped_decrypt(messageContent: UnidentifiedSenderMessageContent,
|
||||
context: StoreContext) throws -> Data {
|
||||
private func throwswrapped_decrypt(messageContent: SMKUnidentifiedSenderMessageContent,
|
||||
protocolContext: Any?) throws -> Data {
|
||||
|
||||
// SignalProtocolAddress sender = new SignalProtocolAddress(message.getSenderCertificate().getSender(),
|
||||
// message.getSenderCertificate().getSenderDeviceId());
|
||||
//
|
||||
// NOTE: We use the sender properties from the sender certificate, not from this class' properties.
|
||||
let sender = messageContent.senderCertificate.sender
|
||||
guard sender.deviceId >= 0 && sender.deviceId <= Int32.max else {
|
||||
let senderRecipientId = messageContent.senderCertificate.senderRecipientId
|
||||
let senderDeviceId = messageContent.senderCertificate.senderDeviceId
|
||||
guard senderDeviceId >= 0 && senderDeviceId <= INT32_MAX else {
|
||||
throw SMKError.assertionError(description: "\(logTag) Invalid senderDeviceId.")
|
||||
}
|
||||
|
||||
@ -321,64 +484,82 @@ fileprivate extension SMKMessageType {
|
||||
// SessionCipher(signalProtocolStore, sender).decrypt(new PreKeySignalMessage(message.getContent())); default: throw
|
||||
// new InvalidMessageException("Unknown type: " + message.getType());
|
||||
// }
|
||||
let plaintextData: [UInt8]
|
||||
switch messageContent.messageType {
|
||||
var cipherMessage: CipherMessage
|
||||
switch (messageContent.messageType) {
|
||||
case .whisper:
|
||||
let cipherMessage = try SignalMessage(bytes: messageContent.contents)
|
||||
plaintextData = try signalDecrypt(
|
||||
message: cipherMessage,
|
||||
from: ProtocolAddress(from: sender),
|
||||
sessionStore: sessionStore,
|
||||
identityStore: identityStore,
|
||||
context: context)
|
||||
case .preKey:
|
||||
let cipherMessage = try PreKeySignalMessage(bytes: messageContent.contents)
|
||||
plaintextData = try signalDecryptPreKey(
|
||||
message: cipherMessage,
|
||||
from: ProtocolAddress(from: sender),
|
||||
sessionStore: sessionStore,
|
||||
identityStore: identityStore,
|
||||
preKeyStore: preKeyStore,
|
||||
signedPreKeyStore: signedPreKeyStore,
|
||||
context: context)
|
||||
case .senderKey:
|
||||
plaintextData = try groupDecrypt(
|
||||
messageContent.contents,
|
||||
from: ProtocolAddress(from: sender),
|
||||
store: senderKeyStore,
|
||||
context: context)
|
||||
case .plaintext:
|
||||
let plaintextMessage = try PlaintextContent(bytes: messageContent.contents)
|
||||
plaintextData = plaintextMessage.body
|
||||
case let unknownType:
|
||||
throw SMKError.assertionError(
|
||||
description: "\(logTag) Not prepared to handle this message type: \(unknownType.rawValue)")
|
||||
cipherMessage = try WhisperMessage(data: messageContent.contentData)
|
||||
case .prekey:
|
||||
cipherMessage = try PreKeyWhisperMessage(data: messageContent.contentData)
|
||||
}
|
||||
return Data(plaintextData)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Internal for testing
|
||||
|
||||
extension SMKSecretSessionCipher {
|
||||
|
||||
// Only allow nil contexts for testing
|
||||
func encryptMessage(
|
||||
recipient: SMKAddress,
|
||||
deviceId: Int32,
|
||||
paddedPlaintext: Data,
|
||||
contentHint: UnidentifiedSenderMessageContent.ContentHint = .default,
|
||||
groupId: Data? = nil,
|
||||
senderCertificate: SenderCertificate,
|
||||
protocolContext: StoreContext? = nil
|
||||
) throws -> Data {
|
||||
try encryptMessage(
|
||||
recipient: recipient,
|
||||
deviceId: deviceId,
|
||||
paddedPlaintext: paddedPlaintext,
|
||||
contentHint: contentHint,
|
||||
groupId: groupId,
|
||||
senderCertificate: senderCertificate,
|
||||
protocolContext: protocolContext ?? NullContext())
|
||||
|
||||
let cipher = SessionCipher(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
|
||||
}
|
||||
}
|
||||
|
||||
88
SignalMetadataKit/src/SMKSenderCertificate.swift
Normal file
88
SignalMetadataKit/src/SMKSenderCertificate.swift
Normal file
@ -0,0 +1,88 @@
|
||||
//
|
||||
// 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 signer: SMKServerCertificate
|
||||
@objc public let key: ECPublicKey
|
||||
@objc public let senderDeviceId: UInt32
|
||||
@objc public let senderRecipientId: String
|
||||
@objc public let expirationTimestamp: UInt64
|
||||
@objc public let signatureData: Data
|
||||
|
||||
@objc public init(signer: SMKServerCertificate,
|
||||
key: ECPublicKey,
|
||||
senderDeviceId: UInt32,
|
||||
senderRecipientId: String,
|
||||
expirationTimestamp: UInt64,
|
||||
signatureData: Data) {
|
||||
self.signer = signer
|
||||
self.key = key
|
||||
self.senderDeviceId = senderDeviceId
|
||||
self.senderRecipientId = senderRecipientId
|
||||
self.expirationTimestamp = expirationTimestamp
|
||||
self.signatureData = signatureData
|
||||
}
|
||||
|
||||
@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 certificateData = proto.certificate
|
||||
let signatureData = proto.signature
|
||||
|
||||
let certificateProto = try SMKProtoSenderCertificateCertificate.parseData(certificateData)
|
||||
|
||||
let keyData = certificateProto.identityKey
|
||||
let key = try ECPublicKey(serializedKeyData: keyData)
|
||||
let senderDeviceId = certificateProto.senderDevice
|
||||
let senderRecipientId = certificateProto.sender
|
||||
let expirationTimestamp = certificateProto.expires
|
||||
let signerProto = certificateProto.signer
|
||||
let signer = try SMKServerCertificate.parse(proto: signerProto)
|
||||
|
||||
return SMKSenderCertificate(signer: signer, key: key, senderDeviceId: senderDeviceId, senderRecipientId: senderRecipientId, expirationTimestamp: expirationTimestamp, signatureData: signatureData)
|
||||
}
|
||||
|
||||
@objc public func toProto() throws -> SMKProtoSenderCertificate {
|
||||
let certificateBuilder = SMKProtoSenderCertificateCertificate.builder(sender: senderRecipientId,
|
||||
senderDevice: senderDeviceId,
|
||||
expires: expirationTimestamp,
|
||||
identityKey: key.serialized,
|
||||
signer: try signer.toProto())
|
||||
|
||||
let builder =
|
||||
SMKProtoSenderCertificate.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? SMKSenderCertificate {
|
||||
return (signer.isEqual(other.signer) &&
|
||||
key.isEqual(other.key) &&
|
||||
senderDeviceId == other.senderDeviceId &&
|
||||
senderRecipientId == other.senderRecipientId &&
|
||||
expirationTimestamp == other.expirationTimestamp &&
|
||||
signatureData == other.signatureData)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public override var hash: Int {
|
||||
return signer.hashValue ^ key.hashValue ^ senderDeviceId.hashValue ^ senderRecipientId.hashValue ^ expirationTimestamp.hashValue ^ signatureData.hashValue
|
||||
}
|
||||
}
|
||||
64
SignalMetadataKit/src/SMKServerCertificate.swift
Normal file
64
SignalMetadataKit/src/SMKServerCertificate.swift
Normal file
@ -0,0 +1,64 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// See:
|
||||
// https://github.com/signalapp/libsignal-metadata-java/blob/cac0dde9de416a192e64a8940503982820870090/java/src/main/java/org/signal/libsignal/metadata/certificate/ServerCertificate.java
|
||||
@objc public class SMKServerCertificate: NSObject {
|
||||
|
||||
@objc public let keyId: UInt32
|
||||
@objc public let key: ECPublicKey
|
||||
@objc public let signatureData: Data
|
||||
|
||||
public init(keyId: UInt32,
|
||||
key: ECPublicKey,
|
||||
signatureData: Data) {
|
||||
self.keyId = keyId
|
||||
self.key = key
|
||||
self.signatureData = signatureData
|
||||
}
|
||||
|
||||
@objc public class func parse(data: Data) throws -> SMKServerCertificate {
|
||||
let proto = try SMKProtoServerCertificate.parseData(data)
|
||||
return try parse(proto: proto)
|
||||
}
|
||||
|
||||
@objc public class func parse(proto: SMKProtoServerCertificate) throws -> SMKServerCertificate {
|
||||
let signatureData = proto.signature
|
||||
let certificateData = proto.certificate
|
||||
let certificateProto = try SMKProtoServerCertificateCertificate.parseData(certificateData)
|
||||
let keyId = certificateProto.id
|
||||
let keyData = certificateProto.key
|
||||
let key = try ECPublicKey(serializedKeyData: keyData)
|
||||
return SMKServerCertificate(keyId: keyId, key: key, signatureData: signatureData)
|
||||
}
|
||||
|
||||
@objc public func toProto() throws -> SMKProtoServerCertificate {
|
||||
let certificateBuilder = SMKProtoServerCertificateCertificate.builder(id: keyId, key: key.serialized)
|
||||
|
||||
let builder =
|
||||
SMKProtoServerCertificate.builder(certificate: try certificateBuilder.buildSerializedData(),
|
||||
signature: signatureData)
|
||||
return try builder.build()
|
||||
}
|
||||
|
||||
@objc public func serialized() throws -> Data {
|
||||
return try toProto().serializedData()
|
||||
}
|
||||
|
||||
open override func isEqual(_ other: Any?) -> Bool {
|
||||
if let other = other as? SMKServerCertificate {
|
||||
return (keyId == other.keyId &&
|
||||
key.isEqual(other.key) &&
|
||||
(signatureData == other.signatureData))
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
public override var hash: Int {
|
||||
return keyId.hashValue ^ key.hashValue ^ signatureData.hashValue
|
||||
}
|
||||
}
|
||||
@ -23,7 +23,7 @@ public class SMKUDAccessKey: NSObject {
|
||||
// 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_DefaultIVLength))
|
||||
let initializationVector = Data(count: Int(kAESGCM256_IVLength))
|
||||
guard let keyData = Cryptography.encryptAESGCM(plainTextData: emptyPlaintext,
|
||||
initializationVector: initializationVector,
|
||||
additionalAuthenticatedData: nil,
|
||||
@ -42,30 +42,10 @@ public class SMKUDAccessKey: NSObject {
|
||||
self.keyData = Randomness.generateRandomBytes(Int32(SMKUDAccessKey.kUDAccessKeyLength))
|
||||
}
|
||||
|
||||
private init(keyData: Data) {
|
||||
self.keyData = keyData
|
||||
}
|
||||
|
||||
/// Used to compose multiple Unidentified-Access-Keys for the multiRecipient endpoint
|
||||
public static func ^(lhs: SMKUDAccessKey, rhs: SMKUDAccessKey) -> SMKUDAccessKey {
|
||||
owsAssert(lhs.keyData.count == SMKUDAccessKey.kUDAccessKeyLength)
|
||||
owsAssert(rhs.keyData.count == SMKUDAccessKey.kUDAccessKeyLength)
|
||||
|
||||
let xoredBytes = zip(lhs.keyData, rhs.keyData).map(^)
|
||||
return .init(keyData: Data(xoredBytes))
|
||||
}
|
||||
|
||||
// MARK:
|
||||
|
||||
override public func isEqual(_ object: Any?) -> Bool {
|
||||
guard let other = object as? SMKUDAccessKey else { return false }
|
||||
return self.keyData == other.keyData
|
||||
}
|
||||
|
||||
// Unrestricted UD recipients should have a zeroed access key sent to the multi-recipient endpoint
|
||||
// For a collection of mixed recipients, a zeroed key will have no effect composing keys with xor
|
||||
// For a collection of only unrestricted UD recipients, the server expects a zero access key
|
||||
public static var zeroedKey: SMKUDAccessKey {
|
||||
.init(keyData: Data(repeating: 0, count: kUDAccessKeyLength))
|
||||
}
|
||||
}
|
||||
|
||||
94
SignalMetadataKit/src/SMKUnidentifiedSenderMessage.swift
Normal file
94
SignalMetadataKit/src/SMKUnidentifiedSenderMessage.swift
Normal file
@ -0,0 +1,94 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// https://github.com/signalapp/libsignal-metadata-java/blob/master/java/src/main/java/org/signal/libsignal/metadata/protocol/UnidentifiedSenderMessage.java
|
||||
@objc public class SMKUnidentifiedSenderMessage: NSObject {
|
||||
|
||||
@objc public static let kSMKMessageCipherTextVersion: UInt = 1
|
||||
|
||||
public let cipherTextVersion: UInt
|
||||
public let ephemeralKey: ECPublicKey
|
||||
public let encryptedStatic: Data
|
||||
public let encryptedMessage: Data
|
||||
|
||||
public init(cipherTextVersion: UInt,
|
||||
ephemeralKey: ECPublicKey,
|
||||
encryptedStatic: Data,
|
||||
encryptedMessage: Data) {
|
||||
self.cipherTextVersion = cipherTextVersion
|
||||
self.ephemeralKey = ephemeralKey
|
||||
self.encryptedStatic = encryptedStatic
|
||||
self.encryptedMessage = encryptedMessage
|
||||
}
|
||||
|
||||
public init(ephemeralKey: ECPublicKey,
|
||||
encryptedStatic: Data,
|
||||
encryptedMessage: Data) {
|
||||
self.cipherTextVersion = SMKUnidentifiedSenderMessage.kSMKMessageCipherTextVersion
|
||||
self.ephemeralKey = ephemeralKey
|
||||
self.encryptedStatic = encryptedStatic
|
||||
self.encryptedMessage = encryptedMessage
|
||||
}
|
||||
|
||||
@objc public class func parse(dataAndPrefix: Data) throws -> SMKUnidentifiedSenderMessage {
|
||||
// public UnidentifiedSenderMessage(byte[] serialized)
|
||||
// throws InvalidMetadataMessageException, InvalidMetadataVersionException
|
||||
|
||||
let parser = OWSDataParser(data: dataAndPrefix)
|
||||
|
||||
// this.version = ByteUtil.highBitsToInt(serialized[0]);
|
||||
let versionByte = try parser.nextByte(name: "version byte")
|
||||
let cipherTextVersion = UInt(SerializationUtilities.highBitsToInt(fromByte: versionByte))
|
||||
|
||||
// if (version > CIPHERTEXT_VERSION) {
|
||||
// throw new InvalidMetadataVersionException("Unknown version: " + this.version);
|
||||
// }
|
||||
guard cipherTextVersion <= SMKUnidentifiedSenderMessage.kSMKMessageCipherTextVersion else {
|
||||
throw SMKError.assertionError(description: "\(logTag) unknown cipher text version: \(cipherTextVersion)")
|
||||
}
|
||||
|
||||
// SignalProtos.UnidentifiedSenderMessage unidentifiedSenderMessage =
|
||||
// SignalProtos.UnidentifiedSenderMessage.parseFrom(ByteString.copyFrom(serialized, 1, serialized.length - 1));
|
||||
let protoData = try parser.remainder(name: "proto data")
|
||||
let proto = try SMKProtoUnidentifiedSenderMessage.parseData(protoData)
|
||||
|
||||
// if (!unidentifiedSenderMessage.hasEphemeralPublic() ||
|
||||
// !unidentifiedSenderMessage.hasEncryptedStatic() ||
|
||||
// !unidentifiedSenderMessage.hasEncryptedMessage())
|
||||
// {
|
||||
// throw new InvalidMetadataMessageException("Missing fields");
|
||||
// }
|
||||
// NOTE: These fields are required in the proto schema.
|
||||
|
||||
// this.ephemeral = Curve.decodePoint(unidentifiedSenderMessage.getEphemeralPublic().toByteArray(), 0);
|
||||
let ephemeralKeyData = proto.ephemeralPublic
|
||||
let ephemeralKey = try ECPublicKey(serializedKeyData: ephemeralKeyData)
|
||||
|
||||
// this.encryptedStatic = unidentifiedSenderMessage.getEncryptedStatic().toByteArray();
|
||||
let encryptedStatic = proto.encryptedStatic
|
||||
|
||||
// this.encryptedMessage = unidentifiedSenderMessage.getEncryptedMessage().toByteArray();
|
||||
let encryptedMessage = proto.encryptedMessage
|
||||
|
||||
return SMKUnidentifiedSenderMessage(cipherTextVersion: cipherTextVersion, ephemeralKey: ephemeralKey, encryptedStatic: encryptedStatic, encryptedMessage: encryptedMessage)
|
||||
}
|
||||
|
||||
@objc public func toProto() throws -> SMKProtoUnidentifiedSenderMessage {
|
||||
let builder = SMKProtoUnidentifiedSenderMessage.builder(ephemeralPublic: ephemeralKey.serialized,
|
||||
encryptedStatic: encryptedStatic,
|
||||
encryptedMessage: encryptedMessage)
|
||||
return try builder.build()
|
||||
}
|
||||
|
||||
@objc public func serialized() throws -> Data {
|
||||
let versionByte: UInt8 = UInt8((self.cipherTextVersion << 4 | self.cipherTextVersion) & 0xFF)
|
||||
let versionBytes = [versionByte]
|
||||
let versionData = Data(bytes: versionBytes)
|
||||
let messageData = try toProto().serializedData()
|
||||
|
||||
return NSData.join([versionData, messageData])
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc public enum SMKMessageType: Int {
|
||||
case whisper
|
||||
case prekey
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
15
SignalMetadataKitTests/src/Data+SMKTests.swift
Normal file
15
SignalMetadataKitTests/src/Data+SMKTests.swift
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension Data {
|
||||
public func prependKeyType() -> Data {
|
||||
return (self as NSData).prependKeyType() as Data
|
||||
}
|
||||
|
||||
public func removeKeyType() throws -> Data {
|
||||
return try (self as NSData).removeKeyType() as Data
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,9 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import SignalMetadataKit
|
||||
import SignalCoreKit
|
||||
import Curve25519Kit
|
||||
import SignalClient
|
||||
import SignalMetadataKit
|
||||
|
||||
class SMKTest: XCTestCase {
|
||||
|
||||
@ -21,111 +18,158 @@ class SMKTest: XCTestCase {
|
||||
}
|
||||
|
||||
func testECPrivateKey() {
|
||||
let keyData = Randomness.generateRandomBytes(Int32(ECCKeyLength))
|
||||
let keyData = Randomness.generateRandomBytes(Int32(ECCKeyLength))!
|
||||
let key = try! ECPrivateKey(keyData: keyData)
|
||||
let key2 = try! ECPrivateKey(keyData: keyData)
|
||||
XCTAssertEqual(key, key2)
|
||||
}
|
||||
|
||||
func testECPublicKey() {
|
||||
let keyData = Randomness.generateRandomBytes(Int32(ECCKeyLength))
|
||||
let keyData = Randomness.generateRandomBytes(Int32(ECCKeyLength))!
|
||||
let key = try! ECPublicKey(keyData: keyData)
|
||||
XCTAssertEqual(key.keyData, keyData)
|
||||
|
||||
let parsedKey = try! ECPublicKey(serializedKeyData: key.serialized)
|
||||
let serializedData = key.serialized
|
||||
let parsedKey = try! ECPublicKey(serializedKeyData: serializedData)
|
||||
XCTAssertEqual(parsedKey.keyData, keyData)
|
||||
XCTAssertEqual(key, parsedKey)
|
||||
}
|
||||
|
||||
func testUDMessage() {
|
||||
let keyData = Randomness.generateRandomBytes(Int32(ECCKeyLength))!
|
||||
let ephemeralKey = try! ECPublicKey(keyData: keyData)
|
||||
let encryptedStatic = Randomness.generateRandomBytes(100)!
|
||||
let encryptedMessage = Randomness.generateRandomBytes(200)!
|
||||
|
||||
let message = SMKUnidentifiedSenderMessage(ephemeralKey: ephemeralKey,
|
||||
encryptedStatic: encryptedStatic,
|
||||
encryptedMessage: encryptedMessage)
|
||||
let messageData = try! message.serialized()
|
||||
let parsedMessage = try! SMKUnidentifiedSenderMessage.parse(dataAndPrefix: messageData)
|
||||
XCTAssertEqual(message.cipherTextVersion, parsedMessage.cipherTextVersion)
|
||||
XCTAssertEqual(message.ephemeralKey.keyData, parsedMessage.ephemeralKey.keyData)
|
||||
XCTAssertEqual(message.encryptedStatic, parsedMessage.encryptedStatic)
|
||||
XCTAssertEqual(message.encryptedMessage, parsedMessage.encryptedMessage)
|
||||
}
|
||||
|
||||
func testUDServerCertificate() {
|
||||
let keyId: UInt32 = 123
|
||||
let key = try! ECPublicKey(keyData: Randomness.generateRandomBytes(Int32(ECCKeyLength))!)
|
||||
let signatureData = Randomness.generateRandomBytes(100)!
|
||||
|
||||
let serverCertificate = SMKServerCertificate(keyId: keyId,
|
||||
key: key,
|
||||
signatureData: signatureData)
|
||||
let serializedData = try! serverCertificate.serialized()
|
||||
let parsed = try! SMKServerCertificate.parse(data: serializedData)
|
||||
|
||||
XCTAssertEqual(serverCertificate.keyId, parsed.keyId)
|
||||
XCTAssertEqual(serverCertificate.key, parsed.key)
|
||||
XCTAssertEqual(serverCertificate.signatureData, parsed.signatureData)
|
||||
}
|
||||
|
||||
func testUDSenderCertificate() {
|
||||
let serverCertificate = SMKServerCertificate(keyId: 123,
|
||||
key: try! ECPublicKey(keyData: Randomness.generateRandomBytes(Int32(ECCKeyLength))!),
|
||||
signatureData: Randomness.generateRandomBytes(100)!)
|
||||
|
||||
let key = try! ECPublicKey(keyData: Randomness.generateRandomBytes(Int32(ECCKeyLength))!)
|
||||
let senderDeviceId: UInt32 = 456
|
||||
let senderRecipientId = "+13213214321"
|
||||
let expirationTimestamp: UInt64 = 789
|
||||
let signatureData = Randomness.generateRandomBytes(100)!
|
||||
let senderCertificate = SMKSenderCertificate(signer: serverCertificate,
|
||||
key: key,
|
||||
senderDeviceId: senderDeviceId,
|
||||
senderRecipientId: senderRecipientId,
|
||||
expirationTimestamp: expirationTimestamp,
|
||||
signatureData: signatureData)
|
||||
let serializedData = try! senderCertificate.serialized()
|
||||
let parsed = try! SMKSenderCertificate.parse(data: serializedData)
|
||||
|
||||
XCTAssertEqual(senderCertificate.signer, parsed.signer)
|
||||
XCTAssertEqual(senderCertificate.key, parsed.key)
|
||||
XCTAssertEqual(senderCertificate.senderDeviceId, parsed.senderDeviceId)
|
||||
XCTAssertEqual(senderCertificate.senderRecipientId, parsed.senderRecipientId)
|
||||
XCTAssertEqual(senderCertificate.expirationTimestamp, parsed.expirationTimestamp)
|
||||
XCTAssertEqual(senderCertificate.signatureData, parsed.signatureData)
|
||||
}
|
||||
|
||||
func testUDMessageContent() {
|
||||
let serverCertificate = SMKServerCertificate(keyId: 123,
|
||||
key: try! ECPublicKey(keyData: Randomness.generateRandomBytes(Int32(ECCKeyLength))!),
|
||||
signatureData: Randomness.generateRandomBytes(100)!)
|
||||
let senderCertificate = SMKSenderCertificate(signer: serverCertificate,
|
||||
key: try! ECPublicKey(keyData: Randomness.generateRandomBytes(Int32(ECCKeyLength))!),
|
||||
senderDeviceId: 456,
|
||||
senderRecipientId: "+13213214321",
|
||||
expirationTimestamp: 789,
|
||||
signatureData: Randomness.generateRandomBytes(100)!)
|
||||
let contentData = Randomness.generateRandomBytes(200)!
|
||||
|
||||
let message = SMKUnidentifiedSenderMessageContent(messageType: .whisper,
|
||||
senderCertificate: senderCertificate,
|
||||
contentData: contentData)
|
||||
let messageData = try! message.serialized()
|
||||
let parsed = try! SMKUnidentifiedSenderMessageContent.parse(data: messageData)
|
||||
|
||||
XCTAssertEqual(message.messageType, parsed.messageType)
|
||||
XCTAssertEqual(message.senderCertificate, parsed.senderCertificate)
|
||||
XCTAssertEqual(message.contentData, parsed.contentData)
|
||||
}
|
||||
|
||||
func testUDSessionCipher_encrypt() {
|
||||
// NOTE: We use MockClient to ensure consistency between of our session state.
|
||||
let aliceMockClient = MockClient(address: aliceAddress, deviceId: 456, registrationId: 123)
|
||||
let bobMockClient = MockClient(address: bobAddress, deviceId: 321, registrationId: 512)
|
||||
let aliceMockClient = MockClient(recipientId: "+13213214321", deviceId: 456, registrationId: 123)
|
||||
let bobMockClient = MockClient(recipientId: "+13213214322", deviceId: 321, registrationId: 512)
|
||||
|
||||
let certificateValidator = MockCertificateValidator()
|
||||
|
||||
aliceMockClient.initializeSession(with: bobMockClient)
|
||||
let bobPrekey = bobMockClient.generateMockPreKey()
|
||||
let bobSignedPrekey = bobMockClient.generateMockSignedPreKey()
|
||||
|
||||
let bobPreKeyBundle = PreKeyBundle(registrationId: bobMockClient.registrationId,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
preKeyId: bobPrekey.id,
|
||||
preKeyPublic: try! bobPrekey.keyPair.ecPublicKey().serialized,
|
||||
signedPreKeyPublic: try! bobSignedPrekey.keyPair.ecPublicKey().serialized,
|
||||
signedPreKeyId: bobSignedPrekey.id,
|
||||
signedPreKeySignature: bobSignedPrekey.signature,
|
||||
identityKey: try! bobMockClient.identityKeyPair.ecPublicKey().serialized)!
|
||||
|
||||
let aliceToBobSessionBuilder = aliceMockClient.createSessionBuilder(forRecipient: bobMockClient)
|
||||
try! aliceToBobSessionBuilder.processPrekeyBundle(bobPreKeyBundle, protocolContext: nil)
|
||||
|
||||
let aliceToBobCipher = try! aliceMockClient.createSecretSessionCipher()
|
||||
|
||||
let plaintext = Randomness.generateRandomBytes(200)
|
||||
let paddedPlaintext = (plaintext as NSData).paddedMessageBody()
|
||||
let senderCertificate = try! SenderCertificate(buildSenderCertificateProto(senderClient: aliceMockClient).serializedData())
|
||||
let encryptedMessage = try! aliceToBobCipher.encryptMessage(recipient: bobMockClient.address,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: paddedPlaintext,
|
||||
senderCertificate: senderCertificate)
|
||||
let plaintext = Randomness.generateRandomBytes(200)!
|
||||
let paddedPlaintext = (plaintext as NSData).paddedMessageBody()!
|
||||
let serverCertificate = SMKServerCertificate(keyId: 123,
|
||||
key: try! ECPublicKey(keyData: Randomness.generateRandomBytes(Int32(ECCKeyLength))!),
|
||||
signatureData: Randomness.generateRandomBytes(100)!)
|
||||
let senderCertificate = SMKSenderCertificate(signer: serverCertificate,
|
||||
key: try! aliceMockClient.identityKeyPair.ecPublicKey(),
|
||||
senderDeviceId: UInt32(aliceMockClient.deviceId),
|
||||
senderRecipientId: aliceMockClient.recipientId,
|
||||
expirationTimestamp: 789,
|
||||
signatureData: Randomness.generateRandomBytes(100)!)
|
||||
let encryptedMessage = try! aliceToBobCipher.throwswrapped_encryptMessage(recipientId: bobMockClient.recipientId,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: paddedPlaintext, senderCertificate: senderCertificate, protocolContext: nil)
|
||||
|
||||
let messageTimestamp = NSDate.ows_millisecondTimeStamp()
|
||||
|
||||
let bobToAliceCipher = try! bobMockClient.createSecretSessionCipher()
|
||||
let decryptedMessage = try! bobToAliceCipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator,
|
||||
cipherTextData: encryptedMessage,
|
||||
timestamp: messageTimestamp,
|
||||
localE164: bobMockClient.recipientE164,
|
||||
localUuid: bobMockClient.recipientUuid,
|
||||
localDeviceId: bobMockClient.deviceId,
|
||||
protocolContext: nil)
|
||||
cipherTextData: encryptedMessage,
|
||||
timestamp: messageTimestamp,
|
||||
localRecipientId: bobMockClient.recipientId,
|
||||
localDeviceId: bobMockClient.deviceId,
|
||||
protocolContext: nil)
|
||||
let payload = (decryptedMessage.paddedPayload as NSData).removePadding()
|
||||
|
||||
XCTAssertEqual(aliceMockClient.address, decryptedMessage.senderAddress)
|
||||
XCTAssertEqual(aliceMockClient.recipientId, decryptedMessage.senderRecipientId)
|
||||
XCTAssertEqual(aliceMockClient.deviceId, Int32(decryptedMessage.senderDeviceId))
|
||||
XCTAssertEqual(plaintext, payload)
|
||||
}
|
||||
|
||||
// MARK: - Util
|
||||
|
||||
func buildServerCertificateProto() -> SMKProtoServerCertificate {
|
||||
let serverKey = try! Curve25519.generateKeyPair().ecPublicKey().serialized
|
||||
let certificateData = try! SMKProtoServerCertificateCertificate.builder(id: 123,
|
||||
key: serverKey ).buildSerializedData()
|
||||
|
||||
let signatureData = Randomness.generateRandomBytes(ECCSignatureLength)
|
||||
|
||||
let wrapperProto = SMKProtoServerCertificate.builder(certificate: certificateData,
|
||||
signature: signatureData)
|
||||
|
||||
return try! wrapperProto.build()
|
||||
}
|
||||
|
||||
func buildSenderCertificateProto(senderClient: MockClient? = nil) -> SMKProtoSenderCertificate {
|
||||
let senderAddress: SMKAddress
|
||||
let senderDevice: UInt32
|
||||
let expires = NSDate.ows_millisecondTimeStamp() + kWeekInMs
|
||||
let identityKey: IdentityKey
|
||||
let signer = buildServerCertificateProto()
|
||||
|
||||
if let senderClient = senderClient {
|
||||
senderAddress = senderClient.address
|
||||
senderDevice = UInt32(senderClient.deviceId)
|
||||
identityKey = senderClient.identityKeyPair.identityKey
|
||||
} else {
|
||||
senderAddress = .e164("+1235551234")
|
||||
senderDevice = 123
|
||||
identityKey = IdentityKeyPair.generate().identityKey
|
||||
}
|
||||
|
||||
let certificateData: Data = {
|
||||
let builder = SMKProtoSenderCertificateCertificate.builder(senderDevice: senderDevice,
|
||||
expires: expires,
|
||||
identityKey: Data(identityKey.serialize()),
|
||||
signer: signer)
|
||||
if let e164 = senderAddress.e164 {
|
||||
builder.setSenderE164(e164)
|
||||
}
|
||||
|
||||
if let uuidString = senderAddress.uuid?.uuidString {
|
||||
builder.setSenderUuid(uuidString)
|
||||
}
|
||||
|
||||
return try! builder.buildSerializedData()
|
||||
}()
|
||||
|
||||
let signatureData = Randomness.generateRandomBytes(ECCSignatureLength)
|
||||
|
||||
let wrapperProto = try! SMKProtoSenderCertificate.builder(certificate: certificateData,
|
||||
signature: signatureData).build()
|
||||
|
||||
return wrapperProto
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,450 +1,357 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import SignalMetadataKit
|
||||
@testable import SignalClient
|
||||
import Curve25519Kit
|
||||
import SignalCoreKit
|
||||
import SignalMetadataKit
|
||||
|
||||
// https://github.com/signalapp/libsignal-metadata-java/blob/master/tests/src/test/java/org/signal/libsignal/metadata/SecretSessionCipherTest.java
|
||||
// public class SecretSessionCipherTest extends TestCase {
|
||||
// public class SecretSessionCipherTest extends TestCase {
|
||||
class SMKSecretSessionCipherTest: XCTestCase {
|
||||
|
||||
// public void testEncryptDecrypt() throws UntrustedIdentityException, InvalidKeyException, InvalidCertificateException, InvalidProtocolBufferException, InvalidMetadataMessageException, ProtocolDuplicateMessageException, ProtocolUntrustedIdentityException, ProtocolLegacyMessageException, ProtocolInvalidKeyException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolInvalidMessageException, ProtocolInvalidKeyIdException, ProtocolNoSessionException, SelfSendException {
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
// public void testEncryptDecrypt() throws UntrustedIdentityException, InvalidKeyException, InvalidCertificateException, InvalidProtocolBufferException, InvalidMetadataMessageException, ProtocolDuplicateMessageException, ProtocolUntrustedIdentityException, ProtocolLegacyMessageException, ProtocolInvalidKeyException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolInvalidMessageException, ProtocolInvalidKeyIdException, ProtocolNoSessionException {
|
||||
func testEncryptDecrypt() {
|
||||
// TestInMemorySignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
|
||||
// TestInMemorySignalProtocolStore bobStore = new TestInMemorySignalProtocolStore();
|
||||
// NOTE: We use MockClient to ensure consistency between of our session state.
|
||||
let aliceMockClient = MockClient(address: aliceAddress, deviceId: 1, registrationId: 1234)
|
||||
let bobMockClient = MockClient(address: bobAddress, deviceId: 1, registrationId: 1235)
|
||||
let aliceMockClient = MockClient(recipientId: "+14159999999", deviceId: 1, registrationId: 1234)
|
||||
let bobMockClient = MockClient(recipientId: "+14158888888", deviceId: 1, registrationId: 1235)
|
||||
|
||||
// initializeSessions(aliceStore, bobStore);
|
||||
initializeSessions(aliceMockClient: aliceMockClient, bobMockClient: bobMockClient)
|
||||
|
||||
// ECKeyPair trustRoot = Curve.generateKeyPair();
|
||||
let trustRoot = IdentityKeyPair.generate()
|
||||
|
||||
// SenderCertificate senderCertificate = createCertificateFor(trustRoot, "+14151111111", 1, aliceStore.getIdentityKeyPair().getPublicKey().getPublicKey(), 31337);
|
||||
let senderCertificate = createCertificateFor(trustRoot: trustRoot,
|
||||
senderAddress: aliceMockClient.address,
|
||||
senderDeviceId: UInt32(aliceMockClient.deviceId),
|
||||
identityKey: aliceMockClient.identityKeyPair.publicKey,
|
||||
expirationTimestamp: 31337)
|
||||
|
||||
// SecretSessionCipher aliceCipher = new SecretSessionCipher(aliceStore);
|
||||
let aliceCipher: SMKSecretSessionCipher = try! aliceMockClient.createSecretSessionCipher()
|
||||
|
||||
// byte[] ciphertext = aliceCipher.encrypt(new SignalProtocolAddress("+14152222222", 1),
|
||||
// senderCertificate, "smert za smert".getBytes());
|
||||
// NOTE: The java tests don't bother padding the plaintext.
|
||||
let alicePlaintext = "smert za smert".data(using: String.Encoding.utf8)!
|
||||
let ciphertext = try! aliceCipher.encryptMessage(recipient: bobMockClient.address,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: alicePlaintext,
|
||||
senderCertificate: senderCertificate)
|
||||
|
||||
// SealedSessionCipher bobCipher = new SealedSessionCipher(bobStore, new SignalProtocolAddress("+14152222222", 1));
|
||||
let bobCipher: SMKSecretSessionCipher = try! bobMockClient.createSecretSessionCipher()
|
||||
|
||||
// Pair<SignalProtocolAddress, byte[]> plaintext = bobCipher.decrypt(new CertificateValidator(trustRoot.getPublicKey()), ciphertext, 31335);
|
||||
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: ECPublicKey(trustRoot.publicKey))
|
||||
let bobPlaintext = try! bobCipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator,
|
||||
cipherTextData: ciphertext,
|
||||
timestamp: 31335,
|
||||
localE164: bobMockClient.recipientE164,
|
||||
localUuid: bobMockClient.recipientUuid,
|
||||
localDeviceId: bobMockClient.deviceId,
|
||||
protocolContext: nil)
|
||||
|
||||
// assertEquals(new String(plaintext.second()), "smert za smert");
|
||||
// assertEquals(plaintext.first().getName(), "+14151111111");
|
||||
// assertEquals(plaintext.first().getDeviceId(), 1);
|
||||
XCTAssertEqual(String(data: bobPlaintext.paddedPayload, encoding: .utf8), "smert za smert")
|
||||
XCTAssertEqual(bobPlaintext.senderAddress, aliceMockClient.address)
|
||||
XCTAssertEqual(bobPlaintext.senderDeviceId, Int(aliceMockClient.deviceId))
|
||||
}
|
||||
|
||||
// public void testEncryptDecryptUntrusted() throws Exception {
|
||||
func testEncryptDecryptUntrusted() {
|
||||
// TestInMemorySignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
|
||||
// TestInMemorySignalProtocolStore bobStore = new TestInMemorySignalProtocolStore();
|
||||
// NOTE: We use MockClient to ensure consistency between of our session state.
|
||||
let aliceMockClient = MockClient(address: aliceAddress, deviceId: 1, registrationId: 1234)
|
||||
let bobMockClient = MockClient(address: bobAddress, deviceId: 1, registrationId: 1235)
|
||||
|
||||
// initializeSessions(aliceStore, bobStore);
|
||||
initializeSessions(aliceMockClient: aliceMockClient, bobMockClient: bobMockClient)
|
||||
|
||||
// ECKeyPair trustRoot = Curve.generateKeyPair();
|
||||
// ECKeyPair falseTrustRoot = Curve.generateKeyPair();
|
||||
let trustRoot = IdentityKeyPair.generate()
|
||||
let falseTrustRoot = IdentityKeyPair.generate()
|
||||
// SenderCertificate senderCertificate = createCertificateFor(falseTrustRoot, "+14151111111", 1, aliceStore.getIdentityKeyPair().getPublicKey().getPublicKey(), 31337);
|
||||
let senderCertificate = createCertificateFor(trustRoot: falseTrustRoot,
|
||||
senderAddress: aliceMockClient.address,
|
||||
senderDeviceId: UInt32(aliceMockClient.deviceId),
|
||||
identityKey: aliceMockClient.identityKeyPair.publicKey,
|
||||
expirationTimestamp: 31337)
|
||||
|
||||
// SecretSessionCipher aliceCipher = new SecretSessionCipher(aliceStore);
|
||||
let aliceCipher: SMKSecretSessionCipher = try! aliceMockClient.createSecretSessionCipher()
|
||||
|
||||
// byte[] ciphertext = aliceCipher.encrypt(new SignalProtocolAddress("+14152222222", 1),
|
||||
// senderCertificate, "и вот я".getBytes());
|
||||
// NOTE: The java tests don't bother padding the plaintext.
|
||||
let alicePlaintext = "и вот я".data(using: String.Encoding.utf8)!
|
||||
let aliceGroupId = Randomness.generateRandomBytes(6)
|
||||
let aliceContentHint = UnidentifiedSenderMessageContent.ContentHint.implicit
|
||||
let ciphertext = try! aliceCipher.encryptMessage(recipient: bobMockClient.address,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: alicePlaintext,
|
||||
contentHint: aliceContentHint,
|
||||
groupId: aliceGroupId,
|
||||
senderCertificate: senderCertificate)
|
||||
|
||||
// SecretSessionCipher bobCipher = new SecretSessionCipher(bobStore);
|
||||
let bobCipher: SMKSecretSessionCipher = try! bobMockClient.createSecretSessionCipher()
|
||||
|
||||
// try {
|
||||
// bobCipher.decrypt(new CertificateValidator(trustRoot.getPublicKey()), ciphertext, 31335);
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidMetadataMessageException e) {
|
||||
// // good
|
||||
// }
|
||||
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: ECPublicKey(trustRoot.publicKey))
|
||||
do {
|
||||
_ = try bobCipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator,
|
||||
cipherTextData: ciphertext,
|
||||
timestamp: 31335,
|
||||
localE164: bobMockClient.recipientE164,
|
||||
localUuid: bobMockClient.recipientUuid,
|
||||
localDeviceId: bobMockClient.deviceId,
|
||||
protocolContext: nil)
|
||||
XCTFail("Decryption should have failed.")
|
||||
} catch let knownSenderError as SecretSessionKnownSenderError {
|
||||
// Decryption is expected to fail.
|
||||
XCTAssert(knownSenderError.underlyingError is SMKCertificateError )
|
||||
XCTAssertEqual(knownSenderError.contentHint, aliceContentHint)
|
||||
XCTAssertEqual(knownSenderError.groupId, aliceGroupId)
|
||||
XCTAssertNoThrow(
|
||||
try DecryptionErrorMessage(
|
||||
originalMessageBytes: knownSenderError.unsealedContent,
|
||||
type: knownSenderError.cipherType,
|
||||
timestamp: 31335,
|
||||
originalSenderDeviceId: knownSenderError.senderDeviceId
|
||||
)
|
||||
)
|
||||
} catch {
|
||||
XCTFail("Unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
// public void testEncryptDecryptExpired() throws Exception {
|
||||
func testEncryptDecryptExpired() {
|
||||
// TestInMemorySignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
|
||||
// TestInMemorySignalProtocolStore bobStore = new TestInMemorySignalProtocolStore();
|
||||
// NOTE: We use MockClient to ensure consistency between of our session state.
|
||||
let aliceMockClient = MockClient(address: aliceAddress, deviceId: 1, registrationId: 1234)
|
||||
let bobMockClient = MockClient(address: bobAddress, deviceId: 1, registrationId: 1235)
|
||||
|
||||
// initializeSessions(aliceStore, bobStore);
|
||||
initializeSessions(aliceMockClient: aliceMockClient, bobMockClient: bobMockClient)
|
||||
|
||||
// ECKeyPair trustRoot = Curve.generateKeyPair();
|
||||
let trustRoot = IdentityKeyPair.generate()
|
||||
|
||||
// SenderCertificate senderCertificate = createCertificateFor(trustRoot, "+14151111111", 1, aliceStore.getIdentityKeyPair().getPublicKey().getPublicKey(), 31337);
|
||||
let senderCertificate = createCertificateFor(trustRoot: trustRoot,
|
||||
senderAddress: aliceMockClient.address,
|
||||
senderDeviceId: UInt32(aliceMockClient.deviceId),
|
||||
identityKey: aliceMockClient.identityKeyPair.publicKey,
|
||||
expirationTimestamp: 31337)
|
||||
|
||||
// SecretSessionCipher aliceCipher = new SecretSessionCipher(aliceStore);
|
||||
let aliceCipher: SMKSecretSessionCipher = try! aliceMockClient.createSecretSessionCipher()
|
||||
|
||||
// byte[] ciphertext = aliceCipher.encrypt(new SignalProtocolAddress("+14152222222", 1),
|
||||
// senderCertificate, "и вот я".getBytes());
|
||||
// NOTE: The java tests don't bother padding the plaintext.
|
||||
let alicePlaintext = "и вот я".data(using: String.Encoding.utf8)!
|
||||
let aliceGroupId = Randomness.generateRandomBytes(6)
|
||||
let aliceContentHint = UnidentifiedSenderMessageContent.ContentHint.resendable
|
||||
|
||||
let ciphertext = try! aliceCipher.encryptMessage(recipient: bobMockClient.address,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: alicePlaintext,
|
||||
contentHint: aliceContentHint,
|
||||
groupId: aliceGroupId,
|
||||
senderCertificate: senderCertificate)
|
||||
|
||||
// SecretSessionCipher bobCipher = new SecretSessionCipher(bobStore);
|
||||
let bobCipher: SMKSecretSessionCipher = try! bobMockClient.createSecretSessionCipher()
|
||||
|
||||
// try {
|
||||
// bobCipher.decrypt(new CertificateValidator(trustRoot.getPublicKey()), ciphertext, 31338);
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidMetadataMessageException e) {
|
||||
// // good
|
||||
// }
|
||||
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: ECPublicKey(trustRoot.publicKey))
|
||||
do {
|
||||
_ = try bobCipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator,
|
||||
cipherTextData: ciphertext,
|
||||
timestamp: 31338,
|
||||
localE164: bobMockClient.recipientE164,
|
||||
localUuid: bobMockClient.recipientUuid,
|
||||
localDeviceId: bobMockClient.deviceId,
|
||||
protocolContext: nil)
|
||||
XCTFail("Decryption should have failed.")
|
||||
} catch let knownSenderError as SecretSessionKnownSenderError {
|
||||
// Decryption is expected to fail.
|
||||
XCTAssert(knownSenderError.underlyingError is SMKCertificateError )
|
||||
XCTAssertEqual(knownSenderError.contentHint, aliceContentHint)
|
||||
XCTAssertEqual(knownSenderError.groupId, aliceGroupId)
|
||||
XCTAssertNoThrow(
|
||||
try DecryptionErrorMessage(
|
||||
originalMessageBytes: knownSenderError.unsealedContent,
|
||||
type: knownSenderError.cipherType,
|
||||
timestamp: 31338,
|
||||
originalSenderDeviceId: knownSenderError.senderDeviceId
|
||||
)
|
||||
)
|
||||
} catch {
|
||||
XCTFail("Unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
// public void testEncryptFromWrongIdentity() throws Exception {
|
||||
func testEncryptFromWrongIdentity() {
|
||||
// TestInMemorySignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
|
||||
// TestInMemorySignalProtocolStore bobStore = new TestInMemorySignalProtocolStore();
|
||||
// NOTE: We use MockClient to ensure consistency between of our session state.
|
||||
let aliceMockClient = MockClient(address: aliceAddress, deviceId: 1, registrationId: 1234)
|
||||
let bobMockClient = MockClient(address: bobAddress, deviceId: 1, registrationId: 1235)
|
||||
|
||||
// initializeSessions(aliceStore, bobStore);
|
||||
// TestInMemorySignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
|
||||
// TestInMemorySignalProtocolStore bobStore = new TestInMemorySignalProtocolStore();
|
||||
//
|
||||
// initializeSessions(aliceStore, bobStore);
|
||||
initializeSessions(aliceMockClient: aliceMockClient,
|
||||
bobMockClient: bobMockClient)
|
||||
|
||||
// ECKeyPair trustRoot = Curve.generateKeyPair();
|
||||
let trustRoot = IdentityKeyPair.generate()
|
||||
// ECKeyPair randomKeyPair = Curve.generateKeyPair();
|
||||
let randomKeyPair = IdentityKeyPair.generate()
|
||||
// SenderCertificate senderCertificate = createCertificateFor(trustRoot, "+14151111111", 1, randomKeyPair.getPublicKey(), 31337);
|
||||
// ECKeyPair trustRoot = Curve.generateKeyPair();
|
||||
let trustRoot = Curve25519.generateKeyPair()
|
||||
// SenderCertificate senderCertificate = createCertificateFor(trustRoot, "+14151111111", 1, aliceStore.getIdentityKeyPair().getPublicKey().getPublicKey(), 31337);
|
||||
let senderCertificate = createCertificateFor(trustRoot: trustRoot,
|
||||
senderAddress: aliceMockClient.address,
|
||||
senderRecipientId: aliceMockClient.recipientId,
|
||||
senderDeviceId: UInt32(aliceMockClient.deviceId),
|
||||
identityKey: randomKeyPair.publicKey,
|
||||
identityKey: try! aliceMockClient.identityKeyPair.ecPublicKey(),
|
||||
expirationTimestamp: 31337)
|
||||
// SecretSessionCipher aliceCipher = new SecretSessionCipher(aliceStore);
|
||||
|
||||
// SecretSessionCipher aliceCipher = new SecretSessionCipher(aliceStore);
|
||||
let aliceCipher: SMKSecretSessionCipher = try! aliceMockClient.createSecretSessionCipher()
|
||||
|
||||
// byte[] ciphertext = aliceCipher.encrypt(new SignalProtocolAddress("+14152222222", 1),
|
||||
// senderCertificate, "smert za smert".getBytes());
|
||||
// byte[] ciphertext = aliceCipher.encrypt(new SignalProtocolAddress("+14152222222", 1),
|
||||
// senderCertificate, "smert za smert".getBytes());
|
||||
// NOTE: The java tests don't bother padding the plaintext.
|
||||
let alicePlaintext = "smert za smert".data(using: String.Encoding.utf8)!
|
||||
let ciphertext = try! aliceCipher.encryptMessage(recipient: bobMockClient.address,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: alicePlaintext,
|
||||
senderCertificate: senderCertificate)
|
||||
let ciphertext = try! aliceCipher.throwswrapped_encryptMessage(recipientId: bobMockClient.recipientId,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: alicePlaintext,
|
||||
senderCertificate: senderCertificate,
|
||||
protocolContext: nil)
|
||||
|
||||
// SecretSessionCipher bobCipher = new SecretSessionCipher(bobStore);
|
||||
// SecretSessionCipher bobCipher = new SecretSessionCipher(bobStore);
|
||||
let bobCipher: SMKSecretSessionCipher = try! bobMockClient.createSecretSessionCipher()
|
||||
|
||||
// try {
|
||||
// bobCipher.decrypt(new CertificateValidator(trustRoot.getPublicKey()), ciphertext, 31335);
|
||||
// } catch (InvalidMetadataMessageException e) {
|
||||
// // good
|
||||
// }
|
||||
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: ECPublicKey(trustRoot.publicKey))
|
||||
// Pair<SignalProtocolAddress, byte[]> plaintext = bobCipher.decrypt(new CertificateValidator(trustRoot.getPublicKey()), ciphertext, 31335);
|
||||
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: try! trustRoot.ecPublicKey())
|
||||
let bobPlaintext = try! bobCipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator,
|
||||
cipherTextData: ciphertext,
|
||||
timestamp: 31335,
|
||||
localRecipientId: bobMockClient.recipientId,
|
||||
localDeviceId: bobMockClient.deviceId,
|
||||
protocolContext: nil)
|
||||
|
||||
// assertEquals(new String(plaintext.second()), "smert za smert");
|
||||
XCTAssertEqual(bobPlaintext.paddedPayload, alicePlaintext)
|
||||
// assertEquals(plaintext.first().getName(), "+14151111111");
|
||||
XCTAssertEqual(bobPlaintext.senderRecipientId, aliceMockClient.recipientId)
|
||||
// assertEquals(plaintext.first().getDeviceId(), 1);
|
||||
XCTAssertEqual(bobPlaintext.senderDeviceId, Int(aliceMockClient.deviceId))
|
||||
}
|
||||
|
||||
// public void testEncryptDecryptUntrusted() throws Exception {
|
||||
func testEncryptDecryptUntrusted() {
|
||||
// NOTE: We use MockClient to ensure consistency between of our session state.
|
||||
let aliceMockClient = MockClient(recipientId: "+14159999999", deviceId: 1, registrationId: 1234)
|
||||
let bobMockClient = MockClient(recipientId: "+14158888888", deviceId: 1, registrationId: 1235)
|
||||
|
||||
// TestInMemorySignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
|
||||
// TestInMemorySignalProtocolStore bobStore = new TestInMemorySignalProtocolStore();
|
||||
//
|
||||
// initializeSessions(aliceStore, bobStore);
|
||||
initializeSessions(aliceMockClient: aliceMockClient,
|
||||
bobMockClient: bobMockClient)
|
||||
|
||||
// ECKeyPair trustRoot = Curve.generateKeyPair();
|
||||
// ECKeyPair falseTrustRoot = Curve.generateKeyPair();
|
||||
let trustRoot = Curve25519.generateKeyPair()
|
||||
let falseTrustRoot = Curve25519.generateKeyPair()
|
||||
// SenderCertificate senderCertificate = createCertificateFor(falseTrustRoot, "+14151111111", 1, aliceStore.getIdentityKeyPair().getPublicKey().getPublicKey(), 31337);
|
||||
let senderCertificate = createCertificateFor(trustRoot: falseTrustRoot,
|
||||
senderRecipientId: aliceMockClient.recipientId,
|
||||
senderDeviceId: UInt32(aliceMockClient.deviceId),
|
||||
identityKey: try! aliceMockClient.identityKeyPair.ecPublicKey(),
|
||||
expirationTimestamp: 31337)
|
||||
// SecretSessionCipher aliceCipher = new SecretSessionCipher(aliceStore);
|
||||
let aliceCipher: SMKSecretSessionCipher = try! aliceMockClient.createSecretSessionCipher()
|
||||
|
||||
// byte[] ciphertext = aliceCipher.encrypt(new SignalProtocolAddress("+14152222222", 1),
|
||||
// senderCertificate, "и вот я".getBytes());
|
||||
// NOTE: The java tests don't bother padding the plaintext.
|
||||
let alicePlaintext = "и вот я".data(using: String.Encoding.utf8)!
|
||||
let ciphertext = try! aliceCipher.throwswrapped_encryptMessage(recipientId: bobMockClient.recipientId,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: alicePlaintext,
|
||||
senderCertificate: senderCertificate,
|
||||
protocolContext: nil)
|
||||
|
||||
// SecretSessionCipher bobCipher = new SecretSessionCipher(bobStore);
|
||||
let bobCipher: SMKSecretSessionCipher = try! bobMockClient.createSecretSessionCipher()
|
||||
|
||||
// try {
|
||||
// bobCipher.decrypt(new CertificateValidator(trustRoot.getPublicKey()), ciphertext, 31335);
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidMetadataMessageException e) {
|
||||
// // good
|
||||
// }
|
||||
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: try! trustRoot.ecPublicKey())
|
||||
do {
|
||||
_ = try bobCipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator,
|
||||
cipherTextData: ciphertext,
|
||||
timestamp: 31335,
|
||||
localE164: bobMockClient.recipientE164,
|
||||
localUuid: bobMockClient.recipientUuid,
|
||||
localDeviceId: bobMockClient.deviceId,
|
||||
protocolContext: nil)
|
||||
cipherTextData: ciphertext,
|
||||
timestamp: 31335,
|
||||
localRecipientId: bobMockClient.recipientId,
|
||||
localDeviceId: bobMockClient.deviceId,
|
||||
protocolContext: nil)
|
||||
XCTFail("Decryption should have failed.")
|
||||
} catch SignalError.invalidMessage(_) {
|
||||
} catch _ as SMKCertificateError {
|
||||
// Decryption is expected to fail.
|
||||
// FIXME: This particular failure doesn't get wrapped as a SecretSessionKnownSenderError
|
||||
// because it's checked before the unwrapped message is returned.
|
||||
// Why? Because it uses crypto values calculated during unwrapping to validate the sender certificate.
|
||||
} catch {
|
||||
XCTFail("unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func testGroupEncryptDecrypt_Success() {
|
||||
// Setup: Initialize sessions and sender certificate
|
||||
let aliceMockClient = MockClient(address: aliceAddress, deviceId: 1, registrationId: 1234)
|
||||
let bobMockClient = MockClient(address: bobAddress, deviceId: 1, registrationId: 1235)
|
||||
initializeSessions(aliceMockClient: aliceMockClient, bobMockClient: bobMockClient)
|
||||
|
||||
let trustRoot = IdentityKeyPair.generate()
|
||||
let senderCertificate = createCertificateFor(
|
||||
trustRoot: trustRoot,
|
||||
senderAddress: aliceMockClient.address,
|
||||
senderDeviceId: UInt32(aliceMockClient.deviceId),
|
||||
identityKey: aliceMockClient.identityKeyPair.publicKey,
|
||||
expirationTimestamp: 31337)
|
||||
|
||||
// Setup: Distribute alice's sender key to bob's key store
|
||||
let distributionId = UUID()
|
||||
let aliceSenderKeyMessage = try! SenderKeyDistributionMessage(
|
||||
from: aliceMockClient.protocolAddress,
|
||||
distributionId: distributionId,
|
||||
store: aliceMockClient.senderKeyStore,
|
||||
context: NullContext())
|
||||
|
||||
try! processSenderKeyDistributionMessage(
|
||||
aliceSenderKeyMessage,
|
||||
from: aliceMockClient.protocolAddress,
|
||||
store: bobMockClient.senderKeyStore,
|
||||
context: NullContext())
|
||||
|
||||
// Test: Alice encrypt's a message using `groupEncryptMessage`
|
||||
let aliceCipher = try! aliceMockClient.createSecretSessionCipher()
|
||||
let alicePlaintext = "beltalowda".data(using: String.Encoding.utf8)!
|
||||
let aliceCiphertext = try! aliceCipher.groupEncryptMessage(
|
||||
recipients: [bobMockClient.protocolAddress],
|
||||
paddedPlaintext: alicePlaintext,
|
||||
senderCertificate: senderCertificate,
|
||||
groupId: Data(),
|
||||
distributionId: distributionId,
|
||||
contentHint: .implicit,
|
||||
protocolContext: nil).map { $0 }
|
||||
|
||||
// This splits out irrelevant per-recipient data from the shared sender key message
|
||||
// This is only necessary in tests. The server would usually handle this.
|
||||
let singleRecipientCiphertext = try! sealedSenderMultiRecipientMessageForSingleRecipient(aliceCiphertext)
|
||||
|
||||
// Test: Bob decrypts the ciphertext
|
||||
let bobCipher = try! bobMockClient.createSecretSessionCipher()
|
||||
let bobValidator = SMKCertificateDefaultValidator(trustRoot: ECPublicKey(trustRoot.publicKey))
|
||||
let bobPlaintext = try! bobCipher.throwswrapped_decryptMessage(
|
||||
certificateValidator: bobValidator,
|
||||
cipherTextData: Data(singleRecipientCiphertext),
|
||||
timestamp: 31335,
|
||||
localE164: bobMockClient.recipientE164,
|
||||
localUuid: bobMockClient.recipientUuid,
|
||||
localDeviceId: bobMockClient.deviceId,
|
||||
protocolContext: nil)
|
||||
|
||||
// Verify
|
||||
XCTAssertEqual(String(data: bobPlaintext.paddedPayload, encoding: .utf8), "beltalowda")
|
||||
XCTAssertEqual(bobPlaintext.senderAddress, aliceMockClient.address)
|
||||
XCTAssertEqual(bobPlaintext.senderDeviceId, Int(aliceMockClient.deviceId))
|
||||
XCTAssertEqual(bobPlaintext.messageType, .senderKey)
|
||||
}
|
||||
|
||||
func testGroupEncryptDecrypt_Failure() {
|
||||
// Setup: Initialize sessions and sender certificate
|
||||
let aliceMockClient = MockClient(address: aliceAddress, deviceId: 1, registrationId: 1234)
|
||||
let bobMockClient = MockClient(address: bobAddress, deviceId: 1, registrationId: 1235)
|
||||
initializeSessions(aliceMockClient: aliceMockClient, bobMockClient: bobMockClient)
|
||||
|
||||
let trustRoot = IdentityKeyPair.generate()
|
||||
let senderCertificate = createCertificateFor(
|
||||
trustRoot: trustRoot,
|
||||
senderAddress: aliceMockClient.address,
|
||||
senderDeviceId: UInt32(aliceMockClient.deviceId),
|
||||
identityKey: aliceMockClient.identityKeyPair.publicKey,
|
||||
expirationTimestamp: 31337)
|
||||
|
||||
// Setup: Alice creates a sender key
|
||||
// Test: Bob intentionally does not process Alice's SKDM to simulate an unsent key
|
||||
let distributionId = UUID()
|
||||
let _ = try! SenderKeyDistributionMessage(
|
||||
from: aliceMockClient.protocolAddress,
|
||||
distributionId: distributionId,
|
||||
store: aliceMockClient.senderKeyStore,
|
||||
context: NullContext())
|
||||
|
||||
// Test: Alice encrypt's a message using `groupEncryptMessage`
|
||||
let aliceCipher = try! aliceMockClient.createSecretSessionCipher()
|
||||
let alicePlaintext = "beltalowda".data(using: String.Encoding.utf8)!
|
||||
let aliceCiphertext = try! aliceCipher.groupEncryptMessage(
|
||||
recipients: [bobMockClient.protocolAddress],
|
||||
paddedPlaintext: alicePlaintext,
|
||||
senderCertificate: senderCertificate,
|
||||
groupId: "inyalowda".data(using: String.Encoding.utf8)!,
|
||||
distributionId: distributionId,
|
||||
contentHint: .resendable,
|
||||
protocolContext: nil).map { $0 }
|
||||
|
||||
// This splits out irrelevant per-recipient data from the shared sender key message
|
||||
// This is only necessary in tests. The server would usually handle this.
|
||||
let singleRecipientCiphertext = try! sealedSenderMultiRecipientMessageForSingleRecipient(aliceCiphertext)
|
||||
|
||||
// Test: Bob decrypts the ciphertext
|
||||
let bobCipher = try! bobMockClient.createSecretSessionCipher()
|
||||
let bobValidator = SMKCertificateDefaultValidator(trustRoot: ECPublicKey(trustRoot.publicKey))
|
||||
do {
|
||||
_ = try bobCipher.throwswrapped_decryptMessage(
|
||||
certificateValidator: bobValidator,
|
||||
cipherTextData: Data(singleRecipientCiphertext),
|
||||
timestamp: 31335,
|
||||
localE164: bobMockClient.recipientE164,
|
||||
localUuid: bobMockClient.recipientUuid,
|
||||
localDeviceId: bobMockClient.deviceId,
|
||||
protocolContext: nil)
|
||||
XCTFail("Decryption should have failed.")
|
||||
} catch let knownSenderError as SecretSessionKnownSenderError {
|
||||
// Verify: We need to make sure that the sender, group, and contentHint are preserved
|
||||
// through decryption failures because of missing a missing sender key. This will
|
||||
// help with recovery.
|
||||
XCTAssertEqual(knownSenderError.senderAddress, aliceMockClient.address)
|
||||
XCTAssertEqual(knownSenderError.senderDeviceId, UInt32(aliceMockClient.deviceId))
|
||||
XCTAssertEqual(Data(knownSenderError.groupId!), "inyalowda".data(using: String.Encoding.utf8)!)
|
||||
XCTAssertEqual(knownSenderError.contentHint, .resendable)
|
||||
XCTAssertNoThrow(
|
||||
try DecryptionErrorMessage(
|
||||
originalMessageBytes: knownSenderError.unsealedContent,
|
||||
type: knownSenderError.cipherType,
|
||||
timestamp: 31335,
|
||||
originalSenderDeviceId: knownSenderError.senderDeviceId
|
||||
)
|
||||
)
|
||||
|
||||
if case SignalError.invalidState(_) = knownSenderError.underlyingError {
|
||||
// Expected
|
||||
} else {
|
||||
XCTFail()
|
||||
}
|
||||
} catch {
|
||||
XCTFail("Unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Utils
|
||||
// public void testEncryptDecryptExpired() throws Exception {
|
||||
func testEncryptDecryptExpired() {
|
||||
// NOTE: We use MockClient to ensure consistency between of our session state.
|
||||
let aliceMockClient = MockClient(recipientId: "+14159999999", deviceId: 1, registrationId: 1234)
|
||||
let bobMockClient = MockClient(recipientId: "+14158888888", deviceId: 1, registrationId: 1235)
|
||||
|
||||
// private SenderCertificate createCertificateFor(ECKeyPair trustRoot, String sender, int deviceId, ECPublicKey identityKey, long expires)
|
||||
// throws InvalidKeyException, InvalidCertificateException, InvalidProtocolBufferException {
|
||||
private func createCertificateFor(trustRoot: IdentityKeyPair,
|
||||
senderAddress: SMKAddress,
|
||||
senderDeviceId: UInt32,
|
||||
identityKey: PublicKey,
|
||||
expirationTimestamp: UInt64) -> SenderCertificate {
|
||||
let serverKey = IdentityKeyPair.generate()
|
||||
let serverCertificate = try! ServerCertificate(keyId: 1,
|
||||
publicKey: serverKey.publicKey,
|
||||
trustRoot: trustRoot.privateKey)
|
||||
return try! SenderCertificate(sender: SealedSenderAddress(e164: senderAddress.e164,
|
||||
uuidString: senderAddress.uuid!.uuidString,
|
||||
deviceId: senderDeviceId),
|
||||
publicKey: identityKey,
|
||||
expiration: expirationTimestamp,
|
||||
signerCertificate: serverCertificate,
|
||||
signerKey: serverKey.privateKey)
|
||||
// TestInMemorySignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
|
||||
// TestInMemorySignalProtocolStore bobStore = new TestInMemorySignalProtocolStore();
|
||||
//
|
||||
// initializeSessions(aliceStore, bobStore);
|
||||
initializeSessions(aliceMockClient: aliceMockClient,
|
||||
bobMockClient: bobMockClient)
|
||||
|
||||
// ECKeyPair trustRoot = Curve.generateKeyPair();
|
||||
let trustRoot = Curve25519.generateKeyPair()
|
||||
// SenderCertificate senderCertificate = createCertificateFor(trustRoot, "+14151111111", 1, aliceStore.getIdentityKeyPair().getPublicKey().getPublicKey(), 31337);
|
||||
let senderCertificate = createCertificateFor(trustRoot: trustRoot,
|
||||
senderRecipientId: aliceMockClient.recipientId,
|
||||
senderDeviceId: UInt32(aliceMockClient.deviceId),
|
||||
identityKey: try! aliceMockClient.identityKeyPair.ecPublicKey(),
|
||||
expirationTimestamp: 31337)
|
||||
// SecretSessionCipher aliceCipher = new SecretSessionCipher(aliceStore);
|
||||
let aliceCipher: SMKSecretSessionCipher = try! aliceMockClient.createSecretSessionCipher()
|
||||
|
||||
// byte[] ciphertext = aliceCipher.encrypt(new SignalProtocolAddress("+14152222222", 1),
|
||||
// senderCertificate, "и вот я".getBytes());
|
||||
// NOTE: The java tests don't bother padding the plaintext.
|
||||
let alicePlaintext = "и вот я".data(using: String.Encoding.utf8)!
|
||||
let ciphertext = try! aliceCipher.throwswrapped_encryptMessage(recipientId: bobMockClient.recipientId,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: alicePlaintext,
|
||||
senderCertificate: senderCertificate,
|
||||
protocolContext: nil)
|
||||
|
||||
// SecretSessionCipher bobCipher = new SecretSessionCipher(bobStore);
|
||||
let bobCipher: SMKSecretSessionCipher = try! bobMockClient.createSecretSessionCipher()
|
||||
|
||||
// try {
|
||||
// bobCipher.decrypt(new CertificateValidator(trustRoot.getPublicKey()), ciphertext, 31338);
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidMetadataMessageException e) {
|
||||
// // good
|
||||
// }
|
||||
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: try! trustRoot.ecPublicKey())
|
||||
do {
|
||||
_ = try bobCipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator,
|
||||
cipherTextData: ciphertext,
|
||||
timestamp: 31338,
|
||||
localRecipientId: bobMockClient.recipientId,
|
||||
localDeviceId: bobMockClient.deviceId,
|
||||
protocolContext: nil)
|
||||
XCTFail("Decryption should have failed.")
|
||||
} catch _ as SMKCertificateError {
|
||||
// Decryption is expected to fail.
|
||||
} catch {
|
||||
XCTFail("Unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
// private void initializeSessions(TestInMemorySignalProtocolStore aliceStore, TestInMemorySignalProtocolStore bobStore)
|
||||
// throws InvalidKeyException, UntrustedIdentityException
|
||||
private func initializeSessions(aliceMockClient: MockClient, bobMockClient: MockClient) {
|
||||
aliceMockClient.initializeSession(with: bobMockClient)
|
||||
// public void testEncryptFromWrongIdentity() throws Exception {
|
||||
func testEncryptFromWrongIdentity() {
|
||||
// NOTE: We use MockClient to ensure consistency between of our session state.
|
||||
let aliceMockClient = MockClient(recipientId: "+14159999999", deviceId: 1, registrationId: 1234)
|
||||
let bobMockClient = MockClient(recipientId: "+14158888888", deviceId: 1, registrationId: 1235)
|
||||
|
||||
// TestInMemorySignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
|
||||
// TestInMemorySignalProtocolStore bobStore = new TestInMemorySignalProtocolStore();
|
||||
//
|
||||
// initializeSessions(aliceStore, bobStore);
|
||||
initializeSessions(aliceMockClient: aliceMockClient,
|
||||
bobMockClient: bobMockClient)
|
||||
|
||||
// ECKeyPair trustRoot = Curve.generateKeyPair();
|
||||
let trustRoot = Curve25519.generateKeyPair()
|
||||
// ECKeyPair randomKeyPair = Curve.generateKeyPair();
|
||||
let randomKeyPair = Curve25519.generateKeyPair()
|
||||
// SenderCertificate senderCertificate = createCertificateFor(trustRoot, "+14151111111", 1, randomKeyPair.getPublicKey(), 31337);
|
||||
let senderCertificate = createCertificateFor(trustRoot: trustRoot,
|
||||
senderRecipientId: aliceMockClient.recipientId,
|
||||
senderDeviceId: UInt32(aliceMockClient.deviceId),
|
||||
identityKey: try! randomKeyPair.ecPublicKey(),
|
||||
expirationTimestamp: 31337)
|
||||
// SecretSessionCipher aliceCipher = new SecretSessionCipher(aliceStore);
|
||||
let aliceCipher: SMKSecretSessionCipher = try! aliceMockClient.createSecretSessionCipher()
|
||||
|
||||
// byte[] ciphertext = aliceCipher.encrypt(new SignalProtocolAddress("+14152222222", 1),
|
||||
// senderCertificate, "smert za smert".getBytes());
|
||||
// NOTE: The java tests don't bother padding the plaintext.
|
||||
let alicePlaintext = "smert za smert".data(using: String.Encoding.utf8)!
|
||||
let ciphertext = try! aliceCipher.throwswrapped_encryptMessage(recipientId: bobMockClient.recipientId,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
paddedPlaintext: alicePlaintext,
|
||||
senderCertificate: senderCertificate,
|
||||
protocolContext: nil)
|
||||
|
||||
// SecretSessionCipher bobCipher = new SecretSessionCipher(bobStore);
|
||||
let bobCipher: SMKSecretSessionCipher = try! bobMockClient.createSecretSessionCipher()
|
||||
|
||||
// try {
|
||||
// bobCipher.decrypt(new CertificateValidator(trustRoot.getPublicKey()), ciphertext, 31335);
|
||||
// } catch (InvalidMetadataMessageException e) {
|
||||
// // good
|
||||
// }
|
||||
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: try! trustRoot.ecPublicKey())
|
||||
do {
|
||||
try bobCipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator,
|
||||
cipherTextData: ciphertext,
|
||||
timestamp: 31335,
|
||||
localRecipientId: bobMockClient.recipientId,
|
||||
localDeviceId: bobMockClient.deviceId,
|
||||
protocolContext: nil)
|
||||
XCTFail("Decryption should have failed.")
|
||||
} catch {
|
||||
// Decryption is expected to fail.
|
||||
XCTAssertTrue(error is SMKError)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Utils
|
||||
|
||||
// private SenderCertificate createCertificateFor(ECKeyPair trustRoot, String sender, int deviceId, ECPublicKey identityKey, long expires)
|
||||
// throws InvalidKeyException, InvalidCertificateException, InvalidProtocolBufferException {
|
||||
private func createCertificateFor(trustRoot: ECKeyPair,
|
||||
senderRecipientId: String,
|
||||
senderDeviceId: UInt32,
|
||||
identityKey: ECPublicKey,
|
||||
expirationTimestamp: UInt64) -> SMKSenderCertificate {
|
||||
// ECKeyPair serverKey = Curve.generateKeyPair();
|
||||
let serverKey = Curve25519.generateKeyPair()
|
||||
|
||||
// byte[] serverCertificateBytes = SignalProtos.ServerCertificate.Certificate.newBuilder()
|
||||
// .setId(1)
|
||||
// .setKey(ByteString.copyFrom(serverKey.getPublicKey().serialize()))
|
||||
// .build()
|
||||
// .toByteArray();
|
||||
let keyId: UInt32 = 1
|
||||
let unsignedServerCertificateBuilder = SMKProtoServerCertificateCertificate.builder(id: keyId,
|
||||
key: try! serverKey.ecPublicKey().serialized)
|
||||
let unsignedServerCertificateData = try! unsignedServerCertificateBuilder.build().serializedData()
|
||||
|
||||
// byte[] serverCertificateSignature = Curve.calculateSignature(trustRoot.getPrivateKey(), serverCertificateBytes);
|
||||
let serverCertificateSignature = try! Ed25519.sign(unsignedServerCertificateData, with: trustRoot)
|
||||
|
||||
// ServerCertificate serverCertificate = new ServerCertificate(SignalProtos.ServerCertificate.newBuilder()
|
||||
// .setCertificate(ByteString.copyFrom(serverCertificateBytes))
|
||||
// .setSignature(ByteString.copyFrom(serverCertificateSignature))
|
||||
// .build()
|
||||
// .toByteArray());
|
||||
let signedServerCertificate = SMKServerCertificate(keyId: keyId,
|
||||
key: try! serverKey.ecPublicKey(),
|
||||
signatureData: serverCertificateSignature)
|
||||
XCTAssertEqual(try! signedServerCertificate.toProto().certificate, unsignedServerCertificateData)
|
||||
let signedServerCertificateData = try! signedServerCertificate.serialized()
|
||||
|
||||
// byte[] senderCertificateBytes = SignalProtos.SenderCertificate.Certificate.newBuilder()
|
||||
// .setSender(sender)
|
||||
// .setSenderDevice(deviceId)
|
||||
// .setIdentityKey(ByteString.copyFrom(identityKey.serialize()))
|
||||
// .setExpires(expires)
|
||||
// .setSigner(SignalProtos.ServerCertificate.parseFrom(serverCertificate.getSerialized()))
|
||||
// .build()
|
||||
// .toByteArray();
|
||||
let unsignedSenderCertificateBuilder = SMKProtoSenderCertificateCertificate.builder(sender: senderRecipientId,
|
||||
senderDevice: senderDeviceId,
|
||||
expires: expirationTimestamp,
|
||||
identityKey: identityKey.serialized,
|
||||
signer: try! signedServerCertificate.toProto())
|
||||
let unsignedSenderCertificateData = try! unsignedSenderCertificateBuilder.build().serializedData()
|
||||
|
||||
// byte[] senderCertificateSignature = Curve.calculateSignature(serverKey.getPrivateKey(), senderCertificateBytes);
|
||||
let senderCertificateSignature = try! Ed25519.sign(unsignedSenderCertificateData, with: serverKey)
|
||||
|
||||
// return new SenderCertificate(SignalProtos.SenderCertificate.newBuilder()
|
||||
// .setCertificate(ByteString.copyFrom(senderCertificateBytes))
|
||||
// .setSignature(ByteString.copyFrom(senderCertificateSignature))
|
||||
// .build()
|
||||
// .toByteArray());
|
||||
|
||||
let signedSenderCertificate = SMKSenderCertificate(signer: signedServerCertificate,
|
||||
key: identityKey,
|
||||
senderDeviceId: senderDeviceId,
|
||||
senderRecipientId: senderRecipientId,
|
||||
expirationTimestamp: expirationTimestamp,
|
||||
signatureData: senderCertificateSignature)
|
||||
XCTAssertEqual(try! signedSenderCertificate.signer.toProto().certificate, unsignedServerCertificateData)
|
||||
return signedSenderCertificate
|
||||
}
|
||||
|
||||
// private void initializeSessions(TestInMemorySignalProtocolStore aliceStore, TestInMemorySignalProtocolStore bobStore)
|
||||
// throws InvalidKeyException, UntrustedIdentityException
|
||||
// {
|
||||
private func initializeSessions(aliceMockClient: MockClient,
|
||||
bobMockClient: MockClient) {
|
||||
// ECKeyPair bobPreKey = Curve.generateKeyPair();
|
||||
let bobPreKey = bobMockClient.generatePreKey()
|
||||
// IdentityKeyPair bobIdentityKey = bobStore.getIdentityKeyPair();
|
||||
let bobIdentityKey = bobMockClient.identityKeyPair
|
||||
// SignedPreKeyRecord bobSignedPreKey = KeyHelper.generateSignedPreKey(bobIdentityKey, 2);
|
||||
let bobSignedPreKey = bobMockClient.generateSignedPreKey()
|
||||
//
|
||||
// PreKeyBundle bobBundle = new PreKeyBundle(1, 1, 1, bobPreKey.getPublicKey(), 2, bobSignedPreKey.getKeyPair().getPublicKey(), bobSignedPreKey.getSignature(), bobIdentityKey.getPublicKey());
|
||||
let bobBundle = PreKeyBundle(registrationId: bobMockClient.registrationId,
|
||||
deviceId: bobMockClient.deviceId,
|
||||
preKeyId: bobPreKey.id,
|
||||
preKeyPublic: try! bobPreKey.keyPair.ecPublicKey().serialized,
|
||||
signedPreKeyPublic: try! bobSignedPreKey.keyPair.ecPublicKey().keyData.prependKeyType(),
|
||||
signedPreKeyId: bobSignedPreKey.id,
|
||||
signedPreKeySignature: bobSignedPreKey.signature,
|
||||
identityKey: try! bobIdentityKey.ecPublicKey().keyData.prependKeyType())!
|
||||
|
||||
// SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, new SignalProtocolAddress("+14152222222", 1));
|
||||
let aliceSessionBuilder = aliceMockClient.createSessionBuilder(forRecipient: bobMockClient)
|
||||
// aliceSessionBuilder.process(bobBundle);
|
||||
try! aliceSessionBuilder.processPrekeyBundle(bobBundle, protocolContext: nil)
|
||||
|
||||
// bobStore.storeSignedPreKey(2, bobSignedPreKey);
|
||||
// bobStore.storePreKey(1, new PreKeyRecord(1, bobPreKey));
|
||||
// NOTE: These stores are taken care of in the mocks' createKey() methods above.
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,190 +1,220 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import SignalMetadataKit
|
||||
import Curve25519Kit
|
||||
import SignalClient
|
||||
|
||||
// See: https://github.com/signalapp/libsignal-metadata-java/blob/master/tests/src/test/java/org/signal/libsignal/metadata/certificate/SenderCertificateTest.java
|
||||
//
|
||||
//public class SenderCertificateTest extends TestCase {
|
||||
class SMKSenderCertificateTest: XCTestCase {
|
||||
|
||||
// private final ECKeyPair trustRoot = Curve.generateKeyPair();
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
// private final ECKeyPair trustRoot = Curve.generateKeyPair();
|
||||
let trustRoot = Curve25519.generateKeyPair()
|
||||
|
||||
// public void testSignature() throws InvalidCertificateException, InvalidKeyException {
|
||||
// public void testSignature() throws InvalidCertificateException, InvalidKeyException {
|
||||
func testSignature() {
|
||||
// ECKeyPair serverKey = Curve.generateKeyPair();
|
||||
// ECKeyPair key = Curve.generateKeyPair();
|
||||
// ECKeyPair serverKey = Curve.generateKeyPair();
|
||||
// ECKeyPair key = Curve.generateKeyPair();
|
||||
let serverKey = Curve25519.generateKeyPair()
|
||||
let key = Curve25519.generateKeyPair()
|
||||
|
||||
// byte[] certificateBytes = SignalProtos.SenderCertificate.Certificate.newBuilder()
|
||||
// .setSender("+14152222222")
|
||||
// .setSenderDevice(1)
|
||||
// .setExpires(31337)
|
||||
// .setIdentityKey(ByteString.copyFrom(key.getPublicKey().serialize()))
|
||||
// .setSigner(getServerCertificate(serverKey))
|
||||
// .build()
|
||||
// .toByteArray();
|
||||
let signer = try! getServerCertificate(serverKey: serverKey)
|
||||
let builder = try! SMKProtoSenderCertificateCertificate.builder(senderDevice: 1,
|
||||
expires: 31337,
|
||||
identityKey: key.ecPublicKey().serialized,
|
||||
signer: signer)
|
||||
builder.setSenderUuid(aliceAddress.uuid!.uuidString)
|
||||
let certificateData = try! builder.buildSerializedData()
|
||||
// byte[] certificateBytes = SignalProtos.SenderCertificate.Certificate.newBuilder()
|
||||
// .setSender("+14152222222")
|
||||
// .setSenderDevice(1)
|
||||
// .setExpires(31337)
|
||||
// .setIdentityKey(ByteString.copyFrom(key.getPublicKey().serialize()))
|
||||
// .setSigner(getServerCertificate(serverKey))
|
||||
// .build()
|
||||
// .toByteArray();
|
||||
|
||||
// byte[] certificateSignature = Curve.calculateSignature(serverKey.getPrivateKey(), certificateBytes);
|
||||
let certificateSignature = try! Ed25519.sign(certificateData, with: serverKey)
|
||||
let senderRecipientId = "+14152222222"
|
||||
let senderDeviceId: UInt32 = 1
|
||||
let expirationTimestamp: UInt64 = 31337
|
||||
|
||||
// SenderCertificate senderCertificate = new SenderCertificate(SignalProtos.SenderCertificate.newBuilder()
|
||||
// .setCertificate(ByteString.copyFrom(certificateBytes))
|
||||
// .setSignature(ByteString.copyFrom(certificateSignature))
|
||||
// .build()
|
||||
// .toByteArray());
|
||||
let senderCertificateData = try! SMKProtoSenderCertificate.builder(certificate: certificateData,
|
||||
signature: certificateSignature)
|
||||
.buildSerializedData()
|
||||
let senderCertificate = try! SenderCertificate(senderCertificateData)
|
||||
let serverCertificate = getServerCertificate(serverKey: serverKey, trustRoot: trustRoot)
|
||||
let unsignedCertificateBuilder = SMKProtoSenderCertificateCertificate.builder(sender: senderRecipientId,
|
||||
senderDevice: senderDeviceId,
|
||||
expires: expirationTimestamp,
|
||||
identityKey: try! key.ecPublicKey().serialized,
|
||||
signer: try! serverCertificate.toProto())
|
||||
unsignedCertificateBuilder.setSigner(try! serverCertificate.toProto())
|
||||
let unsignedSenderCertificateData = try! unsignedCertificateBuilder.build().serializedData()
|
||||
|
||||
// new CertificateValidator(trustRoot.getPublicKey()).validate(senderCertificate, 31336);
|
||||
// byte[] certificateSignature = Curve.calculateSignature(serverKey.getPrivateKey(), certificateBytes);
|
||||
let senderCertificateSignature = try! Ed25519.sign(unsignedSenderCertificateData, with: serverKey)
|
||||
|
||||
// SenderCertificate senderCertificate = new SenderCertificate(SignalProtos.SenderCertificate.newBuilder()
|
||||
// .setCertificate(ByteString.copyFrom(certificateBytes))
|
||||
// .setSignature(ByteString.copyFrom(certificateSignature))
|
||||
// .build()
|
||||
// .toByteArray());
|
||||
let signedSenderCertificate = SMKSenderCertificate(signer: serverCertificate,
|
||||
key: try! key.ecPublicKey(),
|
||||
senderDeviceId: senderDeviceId,
|
||||
senderRecipientId: senderRecipientId,
|
||||
expirationTimestamp: expirationTimestamp,
|
||||
signatureData: senderCertificateSignature)
|
||||
|
||||
// new CertificateValidator(trustRoot.getPublicKey()).validate(senderCertificate, 31336);
|
||||
let certificateValidator = try! SMKCertificateDefaultValidator(trustRoot: trustRoot.ecPublicKey())
|
||||
XCTAssertNoThrow(try certificateValidator.throwswrapped_validate(senderCertificate: senderCertificate,
|
||||
validationTime: 31336))
|
||||
try! certificateValidator.throwswrapped_validate(senderCertificate: signedSenderCertificate, validationTime: 31336)
|
||||
}
|
||||
|
||||
// public void testExpiredSignature() throws InvalidCertificateException, InvalidKeyException {
|
||||
// public void testExpiredSignature() throws InvalidCertificateException, InvalidKeyException {
|
||||
func testExpiredSignature() {
|
||||
// ECKeyPair serverKey = Curve.generateKeyPair();
|
||||
// ECKeyPair key = Curve.generateKeyPair();
|
||||
// ECKeyPair serverKey = Curve.generateKeyPair();
|
||||
// ECKeyPair key = Curve.generateKeyPair();
|
||||
let serverKey = Curve25519.generateKeyPair()
|
||||
let key = Curve25519.generateKeyPair()
|
||||
let key = Curve25519.generateKeyPair()
|
||||
|
||||
// byte[] certificateBytes = SignalProtos.SenderCertificate.Certificate.newBuilder()
|
||||
// .setSender("+14152222222")
|
||||
// .setSenderDevice(1)
|
||||
// .setExpires(31337)
|
||||
// .setIdentityKey(ByteString.copyFrom(key.getPublicKey().serialize()))
|
||||
// .setSigner(getServerCertificate(serverKey))
|
||||
// .build()
|
||||
// .toByteArray();
|
||||
let signer = try! getServerCertificate(serverKey: serverKey)
|
||||
let builder = try! SMKProtoSenderCertificateCertificate.builder(senderDevice: 1,
|
||||
expires: 31337,
|
||||
identityKey: key.ecPublicKey().serialized,
|
||||
signer: signer)
|
||||
builder.setSenderUuid(aliceAddress.uuid!.uuidString)
|
||||
let certificateData = try! builder.buildSerializedData()
|
||||
// byte[] certificateBytes = SignalProtos.SenderCertificate.Certificate.newBuilder()
|
||||
// .setSender("+14152222222")
|
||||
// .setSenderDevice(1)
|
||||
// .setExpires(31337)
|
||||
// .setIdentityKey(ByteString.copyFrom(key.getPublicKey().serialize()))
|
||||
// .setSigner(getServerCertificate(serverKey))
|
||||
// .build()
|
||||
// .toByteArray();
|
||||
let senderRecipientId = "+14152222222"
|
||||
let senderDeviceId: UInt32 = 1
|
||||
let expirationTimestamp: UInt64 = 31337
|
||||
|
||||
// byte[] certificateSignature = Curve.calculateSignature(serverKey.getPrivateKey(), certificateBytes);
|
||||
let certificateSignature = try! Ed25519.sign(certificateData, with: serverKey)
|
||||
let serverCertificate = getServerCertificate(serverKey: serverKey, trustRoot: trustRoot)
|
||||
let unsignedCertificateBuilder = SMKProtoSenderCertificateCertificate.builder(sender: senderRecipientId,
|
||||
senderDevice: senderDeviceId,
|
||||
expires: expirationTimestamp,
|
||||
identityKey: try! key.ecPublicKey().serialized,
|
||||
signer: try! serverCertificate.toProto())
|
||||
let unsignedSenderCertificateData = try! unsignedCertificateBuilder.build().serializedData()
|
||||
|
||||
// SenderCertificate senderCertificate = new SenderCertificate(SignalProtos.SenderCertificate.newBuilder()
|
||||
// .setCertificate(ByteString.copyFrom(certificateBytes))
|
||||
// .setSignature(ByteString.copyFrom(certificateSignature))
|
||||
// .build()
|
||||
// .toByteArray());
|
||||
let senderCertificateData = try! SMKProtoSenderCertificate.builder(certificate: certificateData,
|
||||
signature: certificateSignature)
|
||||
.buildSerializedData()
|
||||
let senderCertificate = try! SenderCertificate(senderCertificateData)
|
||||
// byte[] certificateSignature = Curve.calculateSignature(serverKey.getPrivateKey(), certificateBytes);
|
||||
let senderCertificateSignature = try! Ed25519.sign(unsignedSenderCertificateData, with: serverKey)
|
||||
|
||||
// try {
|
||||
// new CertificateValidator(trustRoot.getPublicKey()).validate(senderCertificate, 31338);
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidCertificateException e) {
|
||||
// // good
|
||||
// }
|
||||
// SenderCertificate senderCertificate = new SenderCertificate(SignalProtos.SenderCertificate.newBuilder()
|
||||
// .setCertificate(ByteString.copyFrom(certificateBytes))
|
||||
// .setSignature(ByteString.copyFrom(certificateSignature))
|
||||
// .build()
|
||||
// .toByteArray());
|
||||
let signedSenderCertificate = SMKSenderCertificate(signer: serverCertificate,
|
||||
key: try! key.ecPublicKey(),
|
||||
senderDeviceId: senderDeviceId,
|
||||
senderRecipientId: senderRecipientId,
|
||||
expirationTimestamp: expirationTimestamp,
|
||||
signatureData: senderCertificateSignature)
|
||||
|
||||
// try {
|
||||
// new CertificateValidator(trustRoot.getPublicKey()).validate(senderCertificate, 31338);
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidCertificateException e) {
|
||||
// // good
|
||||
// }
|
||||
let certificateValidator = try! SMKCertificateDefaultValidator(trustRoot: trustRoot.ecPublicKey())
|
||||
XCTAssertThrowsError(try certificateValidator.throwswrapped_validate(senderCertificate: senderCertificate, validationTime: 31338))
|
||||
XCTAssertThrowsError(try certificateValidator.throwswrapped_validate(senderCertificate: signedSenderCertificate, validationTime: 31338))
|
||||
}
|
||||
|
||||
// public void testBadSignature() throws InvalidCertificateException, InvalidKeyException {
|
||||
// public void testBadSignature() throws InvalidCertificateException, InvalidKeyException {
|
||||
func testBadSignature() {
|
||||
// ECKeyPair serverKey = Curve.generateKeyPair();
|
||||
// ECKeyPair key = Curve.generateKeyPair();
|
||||
// ECKeyPair serverKey = Curve.generateKeyPair();
|
||||
// ECKeyPair key = Curve.generateKeyPair();
|
||||
let serverKey = Curve25519.generateKeyPair()
|
||||
let key = Curve25519.generateKeyPair()
|
||||
let key = Curve25519.generateKeyPair()
|
||||
|
||||
// byte[] certificateBytes = SignalProtos.SenderCertificate.Certificate.newBuilder()
|
||||
// .setSender("+14152222222")
|
||||
// .setSenderDevice(1)
|
||||
// .setExpires(31337)
|
||||
// .setIdentityKey(ByteString.copyFrom(key.getPublicKey().serialize()))
|
||||
// .setSigner(getServerCertificate(serverKey))
|
||||
// .build()
|
||||
// .toByteArray();
|
||||
let signer = try! getServerCertificate(serverKey: serverKey)
|
||||
let builder = try! SMKProtoSenderCertificateCertificate.builder(senderDevice: 1,
|
||||
expires: 31337,
|
||||
identityKey: key.ecPublicKey().serialized,
|
||||
signer: signer)
|
||||
builder.setSenderUuid(aliceAddress.uuid!.uuidString)
|
||||
let certificateData = try! builder.buildSerializedData()
|
||||
// byte[] certificateBytes = SignalProtos.SenderCertificate.Certificate.newBuilder()
|
||||
// .setSender("+14152222222")
|
||||
// .setSenderDevice(1)
|
||||
// .setExpires(31337)
|
||||
// .setIdentityKey(ByteString.copyFrom(key.getPublicKey().serialize()))
|
||||
// .setSigner(getServerCertificate(serverKey))
|
||||
// .build()
|
||||
// .toByteArray();
|
||||
let senderRecipientId = "+14152222222"
|
||||
let senderDeviceId: UInt32 = 1
|
||||
let expirationTimestamp: UInt64 = 31337
|
||||
|
||||
// byte[] certificateSignature = Curve.calculateSignature(serverKey.getPrivateKey(), certificateBytes);
|
||||
let certificateSignature = try! Ed25519.sign(certificateData, with: serverKey)
|
||||
let serverCertificate = getServerCertificate(serverKey: serverKey, trustRoot: trustRoot)
|
||||
let unsignedCertificateBuilder = SMKProtoSenderCertificateCertificate.builder(sender: senderRecipientId,
|
||||
senderDevice: senderDeviceId,
|
||||
expires: expirationTimestamp,
|
||||
identityKey: try! key.ecPublicKey().serialized,
|
||||
signer: try! serverCertificate.toProto())
|
||||
let unsignedSenderCertificateData = try! unsignedCertificateBuilder.build().serializedData()
|
||||
|
||||
// for (int i=0;i<certificateSignature.length;i++) {
|
||||
// for (int b=0;b<8;b++) {
|
||||
for i in 0..<certificateSignature.count {
|
||||
// byte[] certificateSignature = Curve.calculateSignature(serverKey.getPrivateKey(), certificateBytes);
|
||||
let senderCertificateSignature = try! Ed25519.sign(unsignedSenderCertificateData, with: serverKey)
|
||||
|
||||
// for (int i=0;i<certificateSignature.length;i++) {
|
||||
for i in 0..<senderCertificateSignature.count {
|
||||
// for (int b=0;b<8;b++) {
|
||||
for b in 0..<8 {
|
||||
// byte[] badSignature = new byte[certificateSignature.length];
|
||||
// System.arraycopy(certificateSignature, 0, badSignature, 0, certificateSignature.length);
|
||||
//
|
||||
// badSignature[i] = (byte)(badSignature[i] ^ 1 << b);
|
||||
var badSignature = certificateSignature
|
||||
badSignature.withUnsafeMutableBytes { (bytes: UnsafeMutableRawBufferPointer) in
|
||||
bytes[i] = (bytes[i] ^ 1 << b)
|
||||
// byte[] badSignature = new byte[certificateSignature.length];
|
||||
// System.arraycopy(certificateSignature, 0, badSignature, 0, certificateSignature.length);
|
||||
var badSignature = senderCertificateSignature
|
||||
|
||||
// badSignature[i] = (byte)(badSignature[i] ^ 1 << b);
|
||||
badSignature.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) in
|
||||
bytes[i] = (UInt8)(bytes[i] ^ 1 << b)
|
||||
}
|
||||
|
||||
// SenderCertificate senderCertificate = new SenderCertificate(SignalProtos.SenderCertificate.newBuilder()
|
||||
// .setCertificate(ByteString.copyFrom(certificateBytes))
|
||||
// .setSignature(ByteString.copyFrom(badSignature))
|
||||
// .build()
|
||||
// .toByteArray());
|
||||
let serializedData = try! SMKProtoSenderCertificate.builder(certificate: certificateData,
|
||||
signature: badSignature).buildSerializedData()
|
||||
let senderCertificate = try! SenderCertificate(serializedData)
|
||||
// SenderCertificate senderCertificate = new SenderCertificate(SignalProtos.SenderCertificate.newBuilder()
|
||||
// .setCertificate(ByteString.copyFrom(certificateBytes))
|
||||
// .setSignature(ByteString.copyFrom(badSignature))
|
||||
// .build()
|
||||
// .toByteArray());
|
||||
let signedSenderCertificate = SMKSenderCertificate(signer: serverCertificate,
|
||||
key: try! key.ecPublicKey(),
|
||||
senderDeviceId: senderDeviceId,
|
||||
senderRecipientId: senderRecipientId,
|
||||
expirationTimestamp: expirationTimestamp,
|
||||
signatureData: badSignature)
|
||||
|
||||
// try {
|
||||
// new CertificateValidator(trustRoot.getPublicKey()).validate(senderCertificate, 31336);
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidCertificateException e) {
|
||||
// // good
|
||||
// }
|
||||
// try {
|
||||
// new CertificateValidator(trustRoot.getPublicKey()).validate(senderCertificate, 31336);
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidCertificateException e) {
|
||||
// // good
|
||||
// }
|
||||
let certificateValidator = try! SMKCertificateDefaultValidator(trustRoot: trustRoot.ecPublicKey())
|
||||
XCTAssertThrowsError(try certificateValidator.throwswrapped_validate(senderCertificate: senderCertificate,
|
||||
validationTime: 31336))
|
||||
XCTAssertThrowsError(try certificateValidator.throwswrapped_validate(senderCertificate: signedSenderCertificate, validationTime: 31336))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Utils
|
||||
|
||||
// private SignalProtos.ServerCertificate getServerCertificate(ECKeyPair serverKey) throws InvalidKeyException, InvalidCertificateException {
|
||||
private func getServerCertificate(serverKey: ECKeyPair) throws -> SMKProtoServerCertificate {
|
||||
// byte[] certificateBytes = SignalProtos.ServerCertificate.Certificate.newBuilder()
|
||||
// .setId(1)
|
||||
// .setKey(ByteString.copyFrom(serverKey.getPublicKey().serialize()))
|
||||
// .build()
|
||||
// .toByteArray();
|
||||
let certificateData = try! SMKProtoServerCertificateCertificate.builder(id: 1,
|
||||
key: serverKey.ecPublicKey().serialized)
|
||||
.buildSerializedData()
|
||||
// private SignalProtos.ServerCertificate getServerCertificate(ECKeyPair serverKey) throws InvalidKeyException, InvalidCertificateException {
|
||||
private func getServerCertificate(serverKey: ECKeyPair, trustRoot: ECKeyPair) -> SMKServerCertificate {
|
||||
// byte[] certificateBytes = SignalProtos.ServerCertificate.Certificate.newBuilder()
|
||||
// .setId(1)
|
||||
// .setKey(ByteString.copyFrom(serverKey.getPublicKey().serialize()))
|
||||
// .build()
|
||||
// .toByteArray();
|
||||
let keyId: UInt32 = 1
|
||||
let unsignedServerCertificateBuilder = SMKProtoServerCertificateCertificate.builder(id: keyId,
|
||||
key: try! serverKey.ecPublicKey().serialized)
|
||||
let unsignedServerCertificateData = try! unsignedServerCertificateBuilder.build().serializedData()
|
||||
|
||||
// byte[] certificateSignature = Curve.calculateSignature(trustRoot.getPrivateKey(), certificateBytes);
|
||||
let certificateSignature = try! Ed25519.sign(certificateData, with: trustRoot)
|
||||
// byte[] certificateSignature = Curve.calculateSignature(trustRoot.getPrivateKey(), certificateBytes);
|
||||
let serverCertificateSignature = try! Ed25519.sign(unsignedServerCertificateData, with: trustRoot)
|
||||
|
||||
// return SignalProtos.ServerCertificate.newBuilder()
|
||||
// .setCertificate(ByteString.copyFrom(certificateBytes))
|
||||
// .setSignature(ByteString.copyFrom(certificateSignature))
|
||||
// .build();
|
||||
return try! SMKProtoServerCertificate.builder(certificate: certificateData,
|
||||
signature: certificateSignature).build()
|
||||
// return SignalProtos.ServerCertificate.newBuilder()
|
||||
// .setCertificate(ByteString.copyFrom(certificateBytes))
|
||||
// .setSignature(ByteString.copyFrom(certificateSignature))
|
||||
// .build();
|
||||
let signedServerCertificate = SMKServerCertificate(keyId: keyId,
|
||||
key: try! serverKey.ecPublicKey(),
|
||||
signatureData: serverCertificateSignature)
|
||||
return signedServerCertificate
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,185 +1,198 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import SwiftProtobuf
|
||||
import Curve25519Kit
|
||||
import SignalMetadataKit
|
||||
import SignalClient
|
||||
|
||||
// See: https://github.com/signalapp/libsignal-metadata-java/blob/master/tests/src/test/java/org/signal/libsignal/metadata/certificate/ServerCertificateTest.java
|
||||
//
|
||||
// public class ServerCertificateTest extends TestCase {
|
||||
class SMKServerCertificateTest: XCTestCase {
|
||||
|
||||
// public void testBadFields() {
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
// public void testBadFields() {
|
||||
func testBadFields() {
|
||||
|
||||
// NOTE: We don't want to (and can't) test this.
|
||||
// Our Swift proto wrappers ensure that we never have missing fields.
|
||||
|
||||
// SignalProtos.ServerCertificate.Certificate.Builder certificate = SignalProtos.ServerCertificate.Certificate.newBuilder();
|
||||
//
|
||||
// try {
|
||||
// new ServerCertificate(SignalProtos.ServerCertificate.newBuilder().setSignature(ByteString.copyFrom(new byte[64])).build().toByteArray());
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidCertificateException e) {
|
||||
// // good
|
||||
// }
|
||||
//
|
||||
// try {
|
||||
// new ServerCertificate(SignalProtos.ServerCertificate.newBuilder().setCertificate(certificate.build().toByteString())
|
||||
// .setSignature(ByteString.copyFrom(new byte[64])).build().toByteArray());
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidCertificateException e) {
|
||||
// // good
|
||||
// }
|
||||
//
|
||||
// try {
|
||||
// new ServerCertificate(SignalProtos.ServerCertificate.newBuilder().setCertificate(certificate.setId(1).build().toByteString())
|
||||
// .setSignature(ByteString.copyFrom(new byte[64])).build().toByteArray());
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidCertificateException e) {
|
||||
// // good
|
||||
// }
|
||||
// SignalProtos.ServerCertificate.Certificate.Builder certificate = SignalProtos.ServerCertificate.Certificate.newBuilder();
|
||||
//
|
||||
// try {
|
||||
// new ServerCertificate(SignalProtos.ServerCertificate.newBuilder().setSignature(ByteString.copyFrom(new byte[64])).build().toByteArray());
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidCertificateException e) {
|
||||
// // good
|
||||
// }
|
||||
//
|
||||
// try {
|
||||
// new ServerCertificate(SignalProtos.ServerCertificate.newBuilder().setCertificate(certificate.build().toByteString())
|
||||
// .setSignature(ByteString.copyFrom(new byte[64])).build().toByteArray());
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidCertificateException e) {
|
||||
// // good
|
||||
// }
|
||||
//
|
||||
// try {
|
||||
// new ServerCertificate(SignalProtos.ServerCertificate.newBuilder().setCertificate(certificate.setId(1).build().toByteString())
|
||||
// .setSignature(ByteString.copyFrom(new byte[64])).build().toByteArray());
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidCertificateException e) {
|
||||
// // good
|
||||
// }
|
||||
}
|
||||
|
||||
// public void testSignature() throws InvalidKeyException, InvalidCertificateException {
|
||||
// public void testSignature() throws InvalidKeyException, InvalidCertificateException {
|
||||
func testSignature() {
|
||||
// ECKeyPair trustRoot = Curve.generateKeyPair();
|
||||
// ECKeyPair keyPair = Curve.generateKeyPair();
|
||||
// ECKeyPair trustRoot = Curve.generateKeyPair();
|
||||
// ECKeyPair keyPair = Curve.generateKeyPair();
|
||||
let trustRoot = Curve25519.generateKeyPair()
|
||||
let keyPair = Curve25519.generateKeyPair()
|
||||
|
||||
// SignalProtos.ServerCertificate.Certificate certificate = SignalProtos.ServerCertificate.Certificate.newBuilder()
|
||||
// .setId(1)
|
||||
// .setKey(ByteString.copyFrom(keyPair.getPublicKey().serialize()))
|
||||
// .build();
|
||||
let certificateBuilder = SMKProtoServerCertificateCertificate.builder(id: 1,
|
||||
key: try! keyPair.ecPublicKey().serialized)
|
||||
// byte[] certificateBytes = certificate.toByteArray();
|
||||
let certificateData = try! certificateBuilder.build().serializedData()
|
||||
// SignalProtos.ServerCertificate.Certificate certificate = SignalProtos.ServerCertificate.Certificate.newBuilder()
|
||||
// .setId(1)
|
||||
// .setKey(ByteString.copyFrom(keyPair.getPublicKey().serialize()))
|
||||
// .build();
|
||||
let keyId: UInt32 = 1
|
||||
let unsignedServerCertificateBuilder = SMKProtoServerCertificateCertificate.builder(id: keyId,
|
||||
key: try! keyPair.ecPublicKey().serialized)
|
||||
|
||||
// byte[] certificateSignature = Curve.calculateSignature(trustRoot.getPrivateKey(), certificateBytes);
|
||||
let certificateSignature = try! Ed25519.sign(certificateData, with: trustRoot)
|
||||
// byte[] certificateBytes = certificate.toByteArray();
|
||||
let unsignedServerCertificateData = try! unsignedServerCertificateBuilder.build().serializedData()
|
||||
|
||||
// byte[] serialized = SignalProtos.ServerCertificate.newBuilder()
|
||||
// .setCertificate(ByteString.copyFrom(certificateBytes))
|
||||
// .setSignature(ByteString.copyFrom(certificateSignature))
|
||||
// .build().toByteArray();
|
||||
//
|
||||
let serializedData = try! SMKProtoServerCertificate.builder(certificate: certificateData,
|
||||
signature: certificateSignature)
|
||||
.buildSerializedData()
|
||||
// byte[] certificateSignature = Curve.calculateSignature(trustRoot.getPrivateKey(), certificateBytes);
|
||||
let serverCertificateSignature = try! Ed25519.sign(unsignedServerCertificateData, with: trustRoot)
|
||||
|
||||
// new CertificateValidator(trustRoot.getPublicKey()).validate(new ServerCertificate(serialized));
|
||||
let serverCertificate = try! ServerCertificate(serializedData)
|
||||
// byte[] serialized = SignalProtos.ServerCertificate.newBuilder()
|
||||
// .setCertificate(ByteString.copyFrom(certificateBytes))
|
||||
// .setSignature(ByteString.copyFrom(certificateSignature))
|
||||
// .build().toByteArray();
|
||||
let signedServerCertificate = SMKServerCertificate(keyId: keyId,
|
||||
key: try! keyPair.ecPublicKey(),
|
||||
signatureData: serverCertificateSignature)
|
||||
let serializedData = try! signedServerCertificate.serialized()
|
||||
let parsed = try! SMKServerCertificate.parse(data: serializedData)
|
||||
|
||||
// new CertificateValidator(trustRoot.getPublicKey()).validate(new ServerCertificate(serialized));
|
||||
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: try! trustRoot.ecPublicKey())
|
||||
try! certificateValidator.throwswrapped_validate(serverCertificate: serverCertificate)
|
||||
try! certificateValidator.throwswrapped_validate(serverCertificate: parsed)
|
||||
}
|
||||
|
||||
// public void testBadSignature() throws Exception {
|
||||
// public void testBadSignature() throws Exception {
|
||||
func testBadSignature() {
|
||||
// ECKeyPair trustRoot = Curve.generateKeyPair();
|
||||
// ECKeyPair keyPair = Curve.generateKeyPair();
|
||||
// ECKeyPair trustRoot = Curve.generateKeyPair();
|
||||
// ECKeyPair keyPair = Curve.generateKeyPair();
|
||||
let trustRoot = Curve25519.generateKeyPair()
|
||||
let keyPair = Curve25519.generateKeyPair()
|
||||
|
||||
// SignalProtos.ServerCertificate.Certificate certificate = SignalProtos.ServerCertificate.Certificate.newBuilder()
|
||||
// .setId(1)
|
||||
// .setKey(ByteString.copyFrom(keyPair.getPublicKey().serialize()))
|
||||
// .build();
|
||||
let certificate = try! SMKProtoServerCertificateCertificate.builder(id: 1,
|
||||
key: try! keyPair.ecPublicKey().serialized)
|
||||
.build()
|
||||
// SignalProtos.ServerCertificate.Certificate certificate = SignalProtos.ServerCertificate.Certificate.newBuilder()
|
||||
// .setId(1)
|
||||
// .setKey(ByteString.copyFrom(keyPair.getPublicKey().serialize()))
|
||||
// .build();
|
||||
let keyId: UInt32 = 1
|
||||
let unsignedServerCertificateBuilder = SMKProtoServerCertificateCertificate.builder(id: keyId,
|
||||
key: try! keyPair.ecPublicKey().serialized)
|
||||
|
||||
// byte[] certificateBytes = certificate.toByteArray();
|
||||
let certificateData = try! certificate.serializedData()
|
||||
// byte[] certificateBytes = certificate.toByteArray();
|
||||
let unsignedServerCertificateData = try! unsignedServerCertificateBuilder.build().serializedData()
|
||||
|
||||
// byte[] certificateSignature = Curve.calculateSignature(trustRoot.getPrivateKey(), certificateBytes);
|
||||
let certificateSignature = try! Ed25519.sign(certificateData, with: trustRoot)
|
||||
// byte[] certificateSignature = Curve.calculateSignature(trustRoot.getPrivateKey(), certificateBytes);
|
||||
let serverCertificateSignature = try! Ed25519.sign(unsignedServerCertificateData, with: trustRoot)
|
||||
|
||||
// for (int i=0;i<certificateSignature.length;i++) {
|
||||
// for (int b=0;b<8;b++) {
|
||||
for i in 0..<certificateSignature.count {
|
||||
// for (int i=0;i<certificateSignature.length;i++) {
|
||||
for i in 0..<serverCertificateSignature.count {
|
||||
// for (int b=0;b<8;b++) {
|
||||
for b in 0..<8 {
|
||||
// byte[] badSignature = new byte[certificateSignature.length];
|
||||
// System.arraycopy(certificateSignature, 0, badSignature, 0, badSignature.length);
|
||||
//
|
||||
// badSignature[i] = (byte) (badSignature[i] ^ (1 << b));
|
||||
var badSignature = certificateSignature
|
||||
badSignature.withUnsafeMutableBytes { (bytes: UnsafeMutableRawBufferPointer) in
|
||||
bytes[i] = (bytes[i] ^ 1 << b)
|
||||
// byte[] badSignature = new byte[certificateSignature.length];
|
||||
// System.arraycopy(certificateSignature, 0, badSignature, 0, badSignature.length);
|
||||
var badSignature = serverCertificateSignature
|
||||
|
||||
// badSignature[i] = (byte) (badSignature[i] ^ (1 << b));
|
||||
badSignature.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) in
|
||||
bytes[i] = (UInt8)(bytes[i] ^ 1 << b)
|
||||
}
|
||||
|
||||
// byte[] serialized = SignalProtos.ServerCertificate.newBuilder()
|
||||
// .setCertificate(ByteString.copyFrom(certificateBytes))
|
||||
// .setSignature(ByteString.copyFrom(badSignature))
|
||||
// .build().toByteArray();
|
||||
let serializedData = try! SMKProtoServerCertificate.builder(certificate: certificateData,
|
||||
signature: badSignature)
|
||||
.buildSerializedData()
|
||||
// byte[] serialized = SignalProtos.ServerCertificate.newBuilder()
|
||||
// .setCertificate(ByteString.copyFrom(certificateBytes))
|
||||
// .setSignature(ByteString.copyFrom(badSignature))
|
||||
// .build().toByteArray();
|
||||
let signedServerCertificate = SMKServerCertificate(keyId: keyId,
|
||||
key: try! keyPair.ecPublicKey(),
|
||||
signatureData: badSignature)
|
||||
let serializedData = try! signedServerCertificate.serialized()
|
||||
let parsed = try! SMKServerCertificate.parse(data: serializedData)
|
||||
|
||||
// try {
|
||||
// new CertificateValidator(trustRoot.getPublicKey()).validate(new ServerCertificate(serialized));
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidCertificateException e) {
|
||||
// // good
|
||||
// }
|
||||
let serverCertificate = try! ServerCertificate(serializedData)
|
||||
// try {
|
||||
// new CertificateValidator(trustRoot.getPublicKey()).validate(new ServerCertificate(serialized));
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidCertificateException e) {
|
||||
// // good
|
||||
// }
|
||||
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: try! trustRoot.ecPublicKey())
|
||||
XCTAssertThrowsError(try certificateValidator.throwswrapped_validate(serverCertificate: serverCertificate))
|
||||
XCTAssertThrowsError(try certificateValidator.throwswrapped_validate(serverCertificate: parsed))
|
||||
}
|
||||
}
|
||||
|
||||
// for (int i=0;i<certificateBytes.length;i++) {
|
||||
// for (int b=0;b<8;b++) {
|
||||
for i in 0..<certificateData.count {
|
||||
// for (int i=0;i<certificateBytes.length;i++) {
|
||||
for i in 0..<unsignedServerCertificateData.count {
|
||||
// for (int b=0;b<8;b++) {
|
||||
for b in 0..<8 {
|
||||
// byte[] badCertificate = new byte[certificateBytes.length];
|
||||
// System.arraycopy(certificateBytes, 0, badCertificate, 0, badCertificate.length);
|
||||
//
|
||||
// badCertificate[i] = (byte) (badCertificate[i] ^ (1 << b));
|
||||
var badCertificate = certificateData
|
||||
badCertificate.withUnsafeMutableBytes { (bytes: UnsafeMutableRawBufferPointer) in
|
||||
bytes[i] = (bytes[i] ^ 1 << b)
|
||||
// byte[] badCertificate = new byte[certificateBytes.length];
|
||||
// System.arraycopy(certificateBytes, 0, badCertificate, 0, badCertificate.length);
|
||||
var badCertificate = unsignedServerCertificateData
|
||||
|
||||
// badCertificate[i] = (byte) (badCertificate[i] ^ (1 << b));
|
||||
badCertificate.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) in
|
||||
bytes[i] = (UInt8)(bytes[i] ^ 1 << b)
|
||||
}
|
||||
|
||||
// byte[] serialized = SignalProtos.ServerCertificate.newBuilder()
|
||||
// .setCertificate(ByteString.copyFrom(badCertificate))
|
||||
// .setSignature(ByteString.copyFrom(certificateSignature))
|
||||
// .build().toByteArray();
|
||||
let serializedData = try! SMKProtoServerCertificate.builder(certificate: badCertificate,
|
||||
signature: certificateSignature)
|
||||
.buildSerializedData()
|
||||
|
||||
// try {
|
||||
// new CertificateValidator(trustRoot.getPublicKey()).validate(new ServerCertificate(serialized));
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidCertificateException e) {
|
||||
// // good
|
||||
// }
|
||||
let serverCertificate: ServerCertificate
|
||||
// byte[] serialized = SignalProtos.ServerCertificate.newBuilder()
|
||||
// .setCertificate(ByteString.copyFrom(badCertificate))
|
||||
// .setSignature(ByteString.copyFrom(certificateSignature))
|
||||
// .build().toByteArray();
|
||||
let builder =
|
||||
SMKProtoServerCertificate.builder(certificate: badCertificate, signature: serverCertificateSignature)
|
||||
let serializedData = try! builder.buildSerializedData()
|
||||
let parsed: SMKServerCertificate
|
||||
do {
|
||||
serverCertificate = try ServerCertificate(serializedData)
|
||||
} catch SignalError.protobufError,
|
||||
SignalError.invalidMessage,
|
||||
SignalError.invalidKey {
|
||||
parsed = try SMKServerCertificate.parse(data: serializedData)
|
||||
} catch BinaryDecodingError.malformedProtobuf {
|
||||
// Some bad certificates will fail to parse.
|
||||
continue
|
||||
} catch BinaryDecodingError.truncated {
|
||||
// Some bad certificates will fail to parse.
|
||||
continue
|
||||
} catch SMKProtoError.invalidProtobuf {
|
||||
// Some bad certificates will fail to parse.
|
||||
continue
|
||||
} catch SMKError.assertionError {
|
||||
// Some bad certificates will fail to parse.
|
||||
continue
|
||||
} catch {
|
||||
XCTFail("Unexpected parsing error: \(error)")
|
||||
continue
|
||||
}
|
||||
|
||||
// try {
|
||||
// new CertificateValidator(trustRoot.getPublicKey()).validate(new ServerCertificate(serialized));
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidCertificateException e) {
|
||||
// // good
|
||||
// }
|
||||
//
|
||||
// try {
|
||||
// new CertificateValidator(trustRoot.getPublicKey()).validate(new ServerCertificate(serialized));
|
||||
// throw new AssertionError();
|
||||
// } catch (InvalidCertificateException e) {
|
||||
// // good
|
||||
// }
|
||||
// }
|
||||
let certificateValidator = SMKCertificateDefaultValidator(trustRoot: try! trustRoot.ecPublicKey())
|
||||
XCTAssertThrowsError(try certificateValidator.throwswrapped_validate(serverCertificate: serverCertificate))
|
||||
XCTAssertThrowsError(try certificateValidator.throwswrapped_validate(serverCertificate: parsed))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
333
SignalMetadataKitTests/src/SMKSessionCipherTest.swift
Normal file
333
SignalMetadataKitTests/src/SMKSessionCipherTest.swift
Normal file
@ -0,0 +1,333 @@
|
||||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import SignalMetadataKit
|
||||
|
||||
extension MutableCollection {
|
||||
/// Shuffles the contents of this collection.
|
||||
mutating func ows_shuffle() {
|
||||
let c = count
|
||||
guard c > 1 else { return }
|
||||
|
||||
for (firstUnshuffled, unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
|
||||
// Change `Int` in the next line to `IndexDistance` in < Swift 4.1
|
||||
let d: Int = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
|
||||
let i = index(firstUnshuffled, offsetBy: d)
|
||||
swapAt(firstUnshuffled, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Sequence {
|
||||
/// Returns an array with the contents of this sequence, shuffled.
|
||||
func shuffled() -> [Element] {
|
||||
var result = Array(self)
|
||||
result.ows_shuffle()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
// See: https://github.com/signalapp/libsignal-metadata-java/blob/master/tests/src/test/java/org/signal/libsignal/metadata/SessionCipherTest.java
|
||||
// public class SessionCipherTest extends TestCase {
|
||||
class SMKSessionCipherTest: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
// public void testBasicSessionV3()
|
||||
// throws InvalidKeyException, DuplicateMessageException,
|
||||
// LegacyMessageException, InvalidMessageException, NoSuchAlgorithmException, NoSessionException, UntrustedIdentityException
|
||||
func testBasicSessionV3() {
|
||||
// NOTE: We use MockClient to ensure consistency between of our session state.
|
||||
let aliceMockClient = MockClient(recipientId: "+14159999999", deviceId: 1, registrationId: 1234)
|
||||
let bobMockClient = MockClient(recipientId: "+14158888888", deviceId: 1, registrationId: 1235)
|
||||
|
||||
// SessionRecord aliceSessionRecord = new SessionRecord();
|
||||
// SessionRecord bobSessionRecord = new SessionRecord();
|
||||
let aliceSessionRecord = SessionRecord()!
|
||||
let bobSessionRecord = SessionRecord()!
|
||||
|
||||
// initializeSessionsV3(aliceSessionRecord.getSessionState(), bobSessionRecord.getSessionState());
|
||||
initializeSessionsV3(aliceSessionState: aliceSessionRecord.sessionState()!,
|
||||
bobSessionState: bobSessionRecord.sessionState()!,
|
||||
aliceMockClient: aliceMockClient,
|
||||
bobMockClient: bobMockClient)
|
||||
|
||||
// runInteraction(aliceSessionRecord, bobSessionRecord);
|
||||
runInteraction(aliceSessionRecord: aliceSessionRecord,
|
||||
bobSessionRecord: bobSessionRecord,
|
||||
aliceMockClient: aliceMockClient,
|
||||
bobMockClient: bobMockClient)
|
||||
}
|
||||
|
||||
// public void testMessageKeyLimits() throws Exception {
|
||||
func testMessageKeyLimits() {
|
||||
// NOTE: We use MockClient to ensure consistency between of our session state.
|
||||
let aliceMockClient = MockClient(recipientId: "+14159999999", deviceId: 1, registrationId: 1234)
|
||||
let bobMockClient = MockClient(recipientId: "+14158888888", deviceId: 1, registrationId: 1235)
|
||||
|
||||
// SessionRecord aliceSessionRecord = new SessionRecord();
|
||||
// SessionRecord bobSessionRecord = new SessionRecord();
|
||||
let aliceSessionRecord = SessionRecord()!
|
||||
let bobSessionRecord = SessionRecord()!
|
||||
|
||||
// initializeSessionsV3(aliceSessionRecord.getSessionState(), bobSessionRecord.getSessionState());
|
||||
initializeSessionsV3(aliceSessionState: aliceSessionRecord.sessionState()!,
|
||||
bobSessionState: bobSessionRecord.sessionState()!,
|
||||
aliceMockClient: aliceMockClient,
|
||||
bobMockClient: bobMockClient)
|
||||
|
||||
// SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
|
||||
// SignalProtocolStore bobStore = new TestInMemorySignalProtocolStore();
|
||||
//
|
||||
// aliceStore.storeSession(new SignalProtocolAddress("+14159999999", 1), aliceSessionRecord);
|
||||
// bobStore.storeSession(new SignalProtocolAddress("+14158888888", 1), bobSessionRecord);
|
||||
aliceMockClient.sessionStore.storeSession(aliceMockClient.recipientId, deviceId: aliceMockClient.deviceId, session: aliceSessionRecord, protocolContext: nil)
|
||||
bobMockClient.sessionStore.storeSession(bobMockClient.recipientId, deviceId: bobMockClient.deviceId, session: bobSessionRecord, protocolContext: nil)
|
||||
|
||||
// SessionCipher aliceCipher = new SessionCipher(aliceStore, new SignalProtocolAddress("+14159999999", 1));
|
||||
// SessionCipher bobCipher = new SessionCipher(bobStore, new SignalProtocolAddress("+14158888888", 1));
|
||||
let aliceCipher = aliceMockClient.createSessionCipher()
|
||||
let bobCipher = bobMockClient.createSessionCipher()
|
||||
|
||||
// List<CiphertextMessage> inflight = new LinkedList<>();
|
||||
var inflight = [CipherMessage]()
|
||||
|
||||
// for (int i=0;i<2010;i++) {
|
||||
// inflight.add(aliceCipher.encrypt("you've never been so hungry, you've never been so cold".getBytes()));
|
||||
// }
|
||||
for _ in 1...2010 {
|
||||
let plaintext = "you've never been so hungry, you've never been so cold".data(using: String.Encoding.utf8)!
|
||||
let message = try! aliceCipher.encryptMessage(plaintext, protocolContext: nil)
|
||||
inflight.append(message)
|
||||
}
|
||||
|
||||
// bobCipher.decrypt(new SignalMessage(inflight.get(1000).serialize()));
|
||||
// bobCipher.decrypt(new SignalMessage(inflight.get(inflight.size()-1).serialize()));
|
||||
let midpointMessage = try! bobCipher.decrypt(inflight[1000], protocolContext: nil)
|
||||
XCTAssertNotNil(midpointMessage)
|
||||
let lastMessage = try! bobCipher.decrypt(inflight.last!, protocolContext: nil)
|
||||
XCTAssertNotNil(lastMessage)
|
||||
|
||||
// TODO: Why isn't this failing?
|
||||
let firstMessage = try! bobCipher.decrypt(inflight[0], protocolContext: nil)
|
||||
XCTAssertNotNil(firstMessage)
|
||||
// try {
|
||||
// bobCipher.decrypt(new SignalMessage(inflight.get(0).serialize()));
|
||||
// throw new AssertionError("Should have failed!");
|
||||
// } catch (DuplicateMessageException dme) {
|
||||
// // good
|
||||
// }
|
||||
}
|
||||
|
||||
// MARK: - Utils
|
||||
|
||||
// private void runInteraction(SessionRecord aliceSessionRecord, SessionRecord bobSessionRecord)
|
||||
// throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, NoSuchAlgorithmException, NoSessionException, UntrustedIdentityException {
|
||||
private func runInteraction(aliceSessionRecord: SessionRecord,
|
||||
bobSessionRecord: SessionRecord,
|
||||
aliceMockClient: MockClient,
|
||||
bobMockClient: MockClient) {
|
||||
// SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
|
||||
// SignalProtocolStore bobStore = new TestInMemorySignalProtocolStore();
|
||||
|
||||
// aliceStore.storeSession(new SignalProtocolAddress("+14159999999", 1), aliceSessionRecord);
|
||||
// bobStore.storeSession(new SignalProtocolAddress("+14158888888", 1), bobSessionRecord);
|
||||
aliceMockClient.sessionStore.storeSession(aliceMockClient.recipientId, deviceId: aliceMockClient.deviceId, session: aliceSessionRecord, protocolContext: nil)
|
||||
bobMockClient.sessionStore.storeSession(bobMockClient.recipientId, deviceId: bobMockClient.deviceId, session: bobSessionRecord, protocolContext: nil)
|
||||
|
||||
// SessionCipher aliceCipher = new SessionCipher(aliceStore, new SignalProtocolAddress("+14159999999", 1));
|
||||
// SessionCipher bobCipher = new SessionCipher(bobStore, new SignalProtocolAddress("+14158888888", 1));
|
||||
let aliceCipher = aliceMockClient.createSessionCipher()
|
||||
let bobCipher = bobMockClient.createSessionCipher()
|
||||
|
||||
// byte[] alicePlaintext = "This is a plaintext message.".getBytes();
|
||||
let alicePlaintext = "This is a plaintext message.".data(using: String.Encoding.utf8)!
|
||||
// TODO: Why isn't the java test padding the plaintext?
|
||||
let alicePaddedPlaintext = (alicePlaintext as NSData).paddedMessageBody()!
|
||||
// CiphertextMessage message = aliceCipher.encrypt(alicePlaintext);
|
||||
let message = try! aliceCipher.encryptMessage(alicePaddedPlaintext, protocolContext: nil)
|
||||
// byte[] bobPlaintext = bobCipher.decrypt(new SignalMessage(message.serialize()));
|
||||
let bobPaddedPlaintext = try! bobCipher.decrypt(message, protocolContext: nil)
|
||||
let bobPlaintext = (bobPaddedPlaintext as NSData).removePadding()
|
||||
|
||||
// assertTrue(Arrays.equals(alicePlaintext, bobPlaintext));
|
||||
XCTAssertEqual(alicePlaintext, bobPlaintext)
|
||||
|
||||
// byte[] bobReply = "This is a message from Bob.".getBytes();
|
||||
let bobReply = "This is a message from Bob.".data(using: String.Encoding.utf8)!
|
||||
let bobReplyPadded = (bobReply as NSData).paddedMessageBody()!
|
||||
// CiphertextMessage reply = bobCipher.encrypt(bobReply);
|
||||
let reply = try! bobCipher.encryptMessage(bobReplyPadded, protocolContext: nil)
|
||||
// byte[] receivedReply = aliceCipher.decrypt(new SignalMessage(reply.serialize()));
|
||||
let receivedReplyPadded = try! aliceCipher.decrypt(reply, protocolContext: nil)
|
||||
let receivedReply = (receivedReplyPadded as NSData).removePadding()
|
||||
|
||||
// assertTrue(Arrays.equals(bobReply, receivedReply));
|
||||
XCTAssertEqual(bobReply, receivedReply)
|
||||
|
||||
// List<CiphertextMessage> aliceCiphertextMessages = new ArrayList<>();
|
||||
// List<byte[]> alicePlaintextMessages = new ArrayList<>();
|
||||
typealias MessageTuple = (plaintext: Data, message: CipherMessage)
|
||||
var aliceMessages = [MessageTuple]()
|
||||
|
||||
// for (int i=0;i<50;i++) {
|
||||
// alicePlaintextMessages.add(("смерть за смерть " + i).getBytes());
|
||||
// aliceCiphertextMessages.add(aliceCipher.encrypt(("смерть за смерть " + i).getBytes()));
|
||||
for i in 1...50 {
|
||||
let plaintext = "смерть за смерть \(i)".data(using: String.Encoding.utf8)!
|
||||
let message = try! aliceCipher.encryptMessage(plaintext, protocolContext: nil)
|
||||
aliceMessages.append((plaintext:plaintext, message:message))
|
||||
}
|
||||
|
||||
// long seed = System.currentTimeMillis();
|
||||
//
|
||||
// Collections.shuffle(aliceCiphertextMessages, new Random(seed));
|
||||
// Collections.shuffle(alicePlaintextMessages, new Random(seed));
|
||||
aliceMessages = aliceMessages.shuffled()
|
||||
|
||||
// for (int i=0;i<aliceCiphertextMessages.size() / 2;i++) {
|
||||
// byte[] receivedPlaintext = bobCipher.decrypt(new SignalMessage(aliceCiphertextMessages.get(i).serialize()));
|
||||
// assertTrue(Arrays.equals(receivedPlaintext, alicePlaintextMessages.get(i)));
|
||||
// }
|
||||
let alicePivot = aliceMessages.count / 2
|
||||
let aliceMessagesLeft = aliceMessages[0 ..< alicePivot]
|
||||
let aliceMessagesRight = aliceMessages[alicePivot ..< aliceMessages.count]
|
||||
for (plaintext, message) in aliceMessagesLeft {
|
||||
let receivedPlaintext = try! bobCipher.decrypt(message, protocolContext: nil)
|
||||
XCTAssertEqual(plaintext, receivedPlaintext)
|
||||
}
|
||||
|
||||
// List<CiphertextMessage> bobCiphertextMessages = new ArrayList<>();
|
||||
// List<byte[]> bobPlaintextMessages = new ArrayList<>();
|
||||
var bobMessages = [MessageTuple]()
|
||||
|
||||
// for (int i=0;i<20;i++) {
|
||||
// bobPlaintextMessages.add(("смерть за смерть " + i).getBytes());
|
||||
// bobCiphertextMessages.add(bobCipher.encrypt(("смерть за смерть " + i).getBytes()));
|
||||
// }
|
||||
for i in 1...20 {
|
||||
let plaintext = "смерть за смерть \(i)".data(using: String.Encoding.utf8)!
|
||||
let message = try! bobCipher.encryptMessage(plaintext, protocolContext: nil)
|
||||
bobMessages.append((plaintext:plaintext, message:message))
|
||||
}
|
||||
|
||||
// seed = System.currentTimeMillis();
|
||||
//
|
||||
// Collections.shuffle(bobCiphertextMessages, new Random(seed));
|
||||
// Collections.shuffle(bobPlaintextMessages, new Random(seed));
|
||||
bobMessages = bobMessages.shuffled()
|
||||
|
||||
// for (int i=0;i<bobCiphertextMessages.size() / 2;i++) {
|
||||
// byte[] receivedPlaintext = aliceCipher.decrypt(new SignalMessage(bobCiphertextMessages.get(i).serialize()));
|
||||
// assertTrue(Arrays.equals(receivedPlaintext, bobPlaintextMessages.get(i)));
|
||||
// }
|
||||
let bobPivot = bobMessages.count / 2
|
||||
let bobMessagesLeft = bobMessages[0 ..< bobPivot]
|
||||
let bobMessagesRight = bobMessages[bobPivot ..< bobMessages.count]
|
||||
for (plaintext, message) in bobMessagesLeft {
|
||||
let receivedPlaintext = try! aliceCipher.decrypt(message, protocolContext: nil)
|
||||
XCTAssertEqual(plaintext, receivedPlaintext)
|
||||
}
|
||||
|
||||
// for (int i=aliceCiphertextMessages.size()/2;i<aliceCiphertextMessages.size();i++) {
|
||||
// byte[] receivedPlaintext = bobCipher.decrypt(new SignalMessage(aliceCiphertextMessages.get(i).serialize()));
|
||||
// assertTrue(Arrays.equals(receivedPlaintext, alicePlaintextMessages.get(i)));
|
||||
// }
|
||||
for (plaintext, message) in aliceMessagesRight {
|
||||
let receivedPlaintext = try! bobCipher.decrypt(message, protocolContext: nil)
|
||||
XCTAssertEqual(plaintext, receivedPlaintext)
|
||||
}
|
||||
//
|
||||
// for (int i=bobCiphertextMessages.size() / 2;i<bobCiphertextMessages.size(); i++) {
|
||||
// byte[] receivedPlaintext = aliceCipher.decrypt(new SignalMessage(bobCiphertextMessages.get(i).serialize()));
|
||||
// assertTrue(Arrays.equals(receivedPlaintext, bobPlaintextMessages.get(i)));
|
||||
// }
|
||||
for (plaintext, message) in bobMessagesRight {
|
||||
let receivedPlaintext = try! aliceCipher.decrypt(message, protocolContext: nil)
|
||||
XCTAssertEqual(plaintext, receivedPlaintext)
|
||||
}
|
||||
}
|
||||
|
||||
// private void initializeSessionsV3(SessionState aliceSessionState, SessionState bobSessionState)
|
||||
// throws InvalidKeyException
|
||||
// {
|
||||
private func initializeSessionsV3(aliceSessionState: SessionState,
|
||||
bobSessionState: SessionState,
|
||||
aliceMockClient: MockClient,
|
||||
bobMockClient: MockClient) {
|
||||
// ECKeyPair aliceIdentityKeyPair = Curve.generateKeyPair();
|
||||
let aliceIdentityKeyPair = aliceMockClient.identityKeyPair
|
||||
// IdentityKeyPair aliceIdentityKey = new IdentityKeyPair(new IdentityKey(aliceIdentityKeyPair.getPublicKey()),
|
||||
// aliceIdentityKeyPair.getPrivateKey());
|
||||
// TODO: Is this necessary?
|
||||
let aliceIdentityKey = aliceIdentityKeyPair
|
||||
// ECKeyPair aliceBaseKey = Curve.generateKeyPair();
|
||||
let aliceBaseKey = Curve25519.generateKeyPair()
|
||||
// ECKeyPair aliceEphemeralKey = Curve.generateKeyPair();
|
||||
// NOTE: aliceEphemeralKey isn't used.
|
||||
|
||||
// ECKeyPair alicePreKey = aliceBaseKey;
|
||||
// NOTE: alicePreKey isn't used.
|
||||
|
||||
// ECKeyPair bobIdentityKeyPair = Curve.generateKeyPair();
|
||||
let bobIdentityKeyPair = bobMockClient.identityKeyPair
|
||||
// IdentityKeyPair bobIdentityKey = new IdentityKeyPair(new IdentityKey(bobIdentityKeyPair.getPublicKey()),
|
||||
// bobIdentityKeyPair.getPrivateKey());
|
||||
// TODO: Is this necessary?
|
||||
let bobIdentityKey = bobIdentityKeyPair
|
||||
// ECKeyPair bobBaseKey = Curve.generateKeyPair();
|
||||
let bobBaseKey = Curve25519.generateKeyPair()
|
||||
// ECKeyPair bobEphemeralKey = bobBaseKey;
|
||||
let bobEphemeralKey = bobBaseKey
|
||||
|
||||
// ECKeyPair bobPreKey = Curve.generateKeyPair();
|
||||
// NOTE: bobPreKey isn't used.
|
||||
|
||||
// AliceSignalProtocolParameters aliceParameters = AliceSignalProtocolParameters.newBuilder()
|
||||
// .setOurBaseKey(aliceBaseKey)
|
||||
// .setOurIdentityKey(aliceIdentityKey)
|
||||
// .setTheirOneTimePreKey(Optional.<ECPublicKey>absent())
|
||||
// .setTheirRatchetKey(bobEphemeralKey.getPublicKey())
|
||||
// .setTheirSignedPreKey(bobBaseKey.getPublicKey())
|
||||
// .setTheirIdentityKey(bobIdentityKey.getPublicKey())
|
||||
// .create();
|
||||
let aliceParameters = AliceAxolotlParameters(identityKey: aliceIdentityKey,
|
||||
theirIdentityKey: bobIdentityKey.publicKey,
|
||||
ourBaseKey: aliceBaseKey,
|
||||
theirSignedPreKey: bobBaseKey.publicKey,
|
||||
theirOneTimePreKey: nil,
|
||||
theirRatchetKey: bobEphemeralKey.publicKey)
|
||||
// BobSignalProtocolParameters bobParameters = BobSignalProtocolParameters.newBuilder()
|
||||
// .setOurRatchetKey(bobEphemeralKey)
|
||||
// .setOurSignedPreKey(bobBaseKey)
|
||||
// .setOurOneTimePreKey(Optional.<ECKeyPair>absent())
|
||||
// .setOurIdentityKey(bobIdentityKey)
|
||||
// .setTheirIdentityKey(aliceIdentityKey.getPublicKey())
|
||||
// .setTheirBaseKey(aliceBaseKey.getPublicKey())
|
||||
// .create();
|
||||
let bobParameters = BobAxolotlParameters(myIdentityKeyPair: bobIdentityKey,
|
||||
theirIdentityKey: aliceIdentityKey.publicKey,
|
||||
ourSignedPrekey: bobBaseKey,
|
||||
ourRatchetKey: bobEphemeralKey,
|
||||
ourOneTimePrekey: nil,
|
||||
theirBaseKey: aliceBaseKey.publicKey)
|
||||
|
||||
// TODO: We could expose this constant in SessionBuilder.h.
|
||||
let currentVersion: Int32 = 3
|
||||
// RatchetingSession.initializeSession(aliceSessionState, aliceParameters);
|
||||
try! RatchetingSession.initializeSession(aliceSessionState, sessionVersion: currentVersion, aliceParameters: aliceParameters)
|
||||
|
||||
// RatchetingSession.initializeSession(bobSessionState, bobParameters);
|
||||
try! RatchetingSession.initializeSession(bobSessionState, sessionVersion: currentVersion, bobParameters: bobParameters)
|
||||
}
|
||||
}
|
||||
@ -1,134 +1,89 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SignalMetadataKit
|
||||
import SignalClient
|
||||
|
||||
// Two manipulated-but-valid v1 UUIDs.
|
||||
let aliceAddress: SMKAddress = .uuid(UUID(uuidString: "aaaaaaaa-7000-11eb-b32a-33b8a8a487a6")!)
|
||||
let bobAddress: SMKAddress = .uuid(UUID(uuidString: "bbbbbbbb-7000-11eb-b32a-33b8a8a487a6")!)
|
||||
|
||||
class MockCertificateValidator: NSObject, SMKCertificateValidator {
|
||||
|
||||
public func throwswrapped_validate(senderCertificate: SenderCertificate, validationTime: UInt64) throws {
|
||||
@objc public func throwswrapped_validate(senderCertificate: SMKSenderCertificate, validationTime: UInt64) throws {
|
||||
// Do not throw
|
||||
}
|
||||
|
||||
public func throwswrapped_validate(serverCertificate: ServerCertificate) throws {
|
||||
@objc public func throwswrapped_validate(serverCertificate: SMKServerCertificate) throws {
|
||||
// Do not throw
|
||||
}
|
||||
}
|
||||
|
||||
class MockClient: NSObject {
|
||||
|
||||
var recipientUuid: UUID? {
|
||||
return address.uuid
|
||||
}
|
||||
|
||||
var recipientE164: String? {
|
||||
return address.e164
|
||||
}
|
||||
|
||||
let address: SMKAddress
|
||||
var protocolAddress: ProtocolAddress {
|
||||
try! ProtocolAddress(name: address.uuid!.uuidString, deviceId: UInt32(deviceId))
|
||||
}
|
||||
|
||||
let recipientId: String
|
||||
let deviceId: Int32
|
||||
let registrationId: Int32
|
||||
|
||||
let identityKeyPair: IdentityKeyPair
|
||||
let identityKeyPair: ECKeyPair
|
||||
|
||||
let sessionStore: InMemorySignalProtocolStore
|
||||
let preKeyStore: InMemorySignalProtocolStore
|
||||
let signedPreKeyStore: InMemorySignalProtocolStore
|
||||
let identityStore: InMemorySignalProtocolStore
|
||||
let senderKeyStore: InMemorySignalProtocolStore
|
||||
let sessionStore: SPKMockProtocolStore
|
||||
let preKeyStore: SPKMockProtocolStore
|
||||
let signedPreKeyStore: SPKMockProtocolStore
|
||||
let identityStore: SPKMockProtocolStore
|
||||
|
||||
init(address: SMKAddress, deviceId: Int32, registrationId: Int32) {
|
||||
self.address = address
|
||||
init(recipientId: String, deviceId: Int32, registrationId: Int32) {
|
||||
self.recipientId = recipientId
|
||||
self.deviceId = deviceId
|
||||
self.registrationId = registrationId
|
||||
self.identityKeyPair = IdentityKeyPair.generate()
|
||||
self.identityKeyPair = Curve25519.generateKeyPair()
|
||||
|
||||
let protocolStore = InMemorySignalProtocolStore(identity: identityKeyPair,
|
||||
registrationId: UInt32(registrationId))
|
||||
let protocolStore = SPKMockProtocolStore(identityKeyPair: identityKeyPair, localRegistrationId: registrationId)
|
||||
|
||||
sessionStore = protocolStore
|
||||
preKeyStore = protocolStore
|
||||
signedPreKeyStore = protocolStore
|
||||
identityStore = protocolStore
|
||||
senderKeyStore = protocolStore
|
||||
}
|
||||
|
||||
func createSessionCipher() -> SessionCipher {
|
||||
return SessionCipher(sessionStore: sessionStore,
|
||||
preKeyStore: preKeyStore,
|
||||
signedPreKeyStore: signedPreKeyStore,
|
||||
identityKeyStore: identityStore,
|
||||
recipientId: recipientId,
|
||||
deviceId: deviceId)
|
||||
}
|
||||
|
||||
func createSecretSessionCipher() throws -> SMKSecretSessionCipher {
|
||||
return try SMKSecretSessionCipher(sessionStore: sessionStore,
|
||||
preKeyStore: preKeyStore,
|
||||
signedPreKeyStore: signedPreKeyStore,
|
||||
identityStore: identityStore,
|
||||
senderKeyStore: senderKeyStore)
|
||||
preKeyStore: preKeyStore,
|
||||
signedPreKeyStore: signedPreKeyStore,
|
||||
identityStore: identityStore)
|
||||
}
|
||||
|
||||
func createSessionBuilder(forRecipient recipient: MockClient) -> SessionBuilder {
|
||||
return SessionBuilder(sessionStore: sessionStore,
|
||||
preKeyStore: preKeyStore,
|
||||
signedPreKeyStore: signedPreKeyStore,
|
||||
identityKeyStore: identityStore,
|
||||
recipientId: recipient.recipientId,
|
||||
deviceId: recipient.deviceId)
|
||||
}
|
||||
|
||||
func generateMockPreKey() -> PreKeyRecord {
|
||||
let preKeyId = UInt32(Int32.random(in: 0...Int32.max))
|
||||
let preKey = try! PreKeyRecord(id: preKeyId, privateKey: PrivateKey.generate())
|
||||
try! self.preKeyStore.storePreKey(preKey, id: preKeyId, context: NullContext())
|
||||
let preKeyId: Int32 = Int32(arc4random_uniform(UInt32(INT32_MAX)))
|
||||
let keyPair = Curve25519.generateKeyPair()
|
||||
let preKey = PreKeyRecord(id: preKeyId, keyPair: keyPair)!
|
||||
self.preKeyStore.storePreKey(preKeyId, preKeyRecord: preKey)
|
||||
return preKey
|
||||
}
|
||||
|
||||
func generateMockSignedPreKey() -> SignedPreKeyRecord {
|
||||
let signedPreKeyId = UInt32(Int32.random(in: 0...Int32.max))
|
||||
let keyPair = IdentityKeyPair.generate()
|
||||
let signedPreKeyId: Int32 = Int32(arc4random_uniform(UInt32(INT32_MAX)))
|
||||
let keyPair = Curve25519.generateKeyPair()
|
||||
let generatedAt = Date()
|
||||
let identityKeyPair = try! self.identityStore.identityKeyPair(context: NullContext())
|
||||
let signature = identityKeyPair.privateKey.generateSignature(message: keyPair.publicKey.serialize())
|
||||
let signedPreKey = try! SignedPreKeyRecord(id: signedPreKeyId,
|
||||
timestamp: UInt64(generatedAt.timeIntervalSince1970),
|
||||
privateKey: keyPair.privateKey,
|
||||
signature: signature)
|
||||
try! self.signedPreKeyStore.storeSignedPreKey(signedPreKey, id: signedPreKeyId, context: NullContext())
|
||||
let identityKeyPair = self.identityStore.identityKeyPair(nil)
|
||||
let signature = try! Ed25519.sign((keyPair.publicKey as NSData).prependKeyType() as Data, with: identityKeyPair)
|
||||
let signedPreKey = SignedPreKeyRecord(id: signedPreKeyId, keyPair: keyPair, signature: signature, generatedAt: generatedAt)!
|
||||
self.signedPreKeyStore.storeSignedPreKey(signedPreKeyId, signedPreKeyRecord: signedPreKey)
|
||||
return signedPreKey
|
||||
}
|
||||
|
||||
// Moved from SMKSecretSessionCipherTest.
|
||||
// private void initializeSessions(TestInMemorySignalProtocolStore aliceStore, TestInMemorySignalProtocolStore bobStore)
|
||||
// throws InvalidKeyException, UntrustedIdentityException
|
||||
func initializeSession(with bobMockClient: MockClient) {
|
||||
// ECKeyPair bobPreKey = Curve.generateKeyPair();
|
||||
let bobPreKey = bobMockClient.generateMockPreKey()
|
||||
|
||||
// IdentityKeyPair bobIdentityKey = bobStore.getIdentityKeyPair();
|
||||
let bobIdentityKey = bobMockClient.identityKeyPair
|
||||
|
||||
// SignedPreKeyRecord bobSignedPreKey = KeyHelper.generateSignedPreKey(bobIdentityKey, 2);
|
||||
let bobSignedPreKey = bobMockClient.generateMockSignedPreKey()
|
||||
|
||||
// PreKeyBundle bobBundle = new PreKeyBundle(1, 1, 1, bobPreKey.getPublicKey(), 2, bobSignedPreKey.getKeyPair().getPublicKey(), bobSignedPreKey.getSignature(), bobIdentityKey.getPublicKey());
|
||||
let bobBundle = try! PreKeyBundle(registrationId: UInt32(bitPattern: bobMockClient.registrationId),
|
||||
deviceId: UInt32(bitPattern: bobMockClient.deviceId),
|
||||
prekeyId: bobPreKey.id,
|
||||
prekey: bobPreKey.publicKey,
|
||||
signedPrekeyId: bobSignedPreKey.id,
|
||||
signedPrekey: bobSignedPreKey.publicKey,
|
||||
signedPrekeySignature: bobSignedPreKey.signature,
|
||||
identity: bobIdentityKey.identityKey)
|
||||
|
||||
// SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, new SignalProtocolAddress("+14152222222", 1));
|
||||
// aliceSessionBuilder.process(bobBundle);
|
||||
let bobProtocolAddress = try! ProtocolAddress(
|
||||
name: bobMockClient.address.uuid?.uuidString ?? bobMockClient.address.e164!,
|
||||
deviceId: UInt32(bitPattern: bobMockClient.deviceId))
|
||||
try! processPreKeyBundle(bobBundle,
|
||||
for: bobProtocolAddress,
|
||||
sessionStore: sessionStore,
|
||||
identityStore: identityStore,
|
||||
context: NullContext())
|
||||
|
||||
// bobStore.storeSignedPreKey(2, bobSignedPreKey);
|
||||
// bobStore.storePreKey(1, new PreKeyRecord(1, bobPreKey));
|
||||
// NOTE: These stores are taken care of in the mocks' createKey() methods above.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
//
|
||||
// Copyright (c) 2020 Open Whisper Systems. All rights reserved.
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import SignalMetadataKit
|
||||
import SignalCoreKit
|
||||
|
||||
class SMKUDAccessKeyTest: XCTestCase {
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
PROTOC=protoc \
|
||||
--proto_path='./'
|
||||
WRAPPER_SCRIPT=../../Signal-iOS/Scripts/ProtoWrappers.py \
|
||||
--proto-dir='./' --verbose --skip-address-helpers
|
||||
--proto-dir='./' --verbose
|
||||
|
||||
all: unidentified_delivery_protos
|
||||
|
||||
|
||||
@ -30,8 +30,8 @@ message ServerCertificate {
|
||||
|
||||
message SenderCertificate {
|
||||
message Certificate {
|
||||
optional string senderE164 = 1;
|
||||
optional string senderUuid = 6;
|
||||
// @required
|
||||
optional string sender = 1;
|
||||
// @required
|
||||
optional uint32 senderDevice = 2;
|
||||
// @required
|
||||
@ -55,6 +55,7 @@ message UnidentifiedSenderMessage {
|
||||
MESSAGE = 2;
|
||||
}
|
||||
|
||||
// @required
|
||||
optional Type type = 1;
|
||||
// @required
|
||||
optional SenderCertificate senderCertificate = 2;
|
||||
@ -68,4 +69,4 @@ message UnidentifiedSenderMessage {
|
||||
optional bytes encryptedStatic = 2;
|
||||
// @required
|
||||
optional bytes encryptedMessage = 3;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user