Implement Session protocol
This commit is contained in:
parent
4cb17b388d
commit
2a4977d269
1
Podfile
1
Podfile
|
@ -71,6 +71,7 @@ target 'SessionMessagingKit' do
|
||||||
pod 'Reachability', :inhibit_warnings => true
|
pod 'Reachability', :inhibit_warnings => true
|
||||||
pod 'SAMKeychain', :inhibit_warnings => true
|
pod 'SAMKeychain', :inhibit_warnings => true
|
||||||
pod 'SignalCoreKit', git: 'https://github.com/signalapp/SignalCoreKit.git', :inhibit_warnings => true
|
pod 'SignalCoreKit', git: 'https://github.com/signalapp/SignalCoreKit.git', :inhibit_warnings => true
|
||||||
|
pod 'Sodium', :inhibit_warnings => true
|
||||||
pod 'SwiftProtobuf', '~> 1.5.0', :inhibit_warnings => true
|
pod 'SwiftProtobuf', '~> 1.5.0', :inhibit_warnings => true
|
||||||
pod 'YapDatabase/SQLCipher', :git => 'https://github.com/loki-project/session-ios-yap-database.git', branch: 'signal-release', :inhibit_warnings => true
|
pod 'YapDatabase/SQLCipher', :git => 'https://github.com/loki-project/session-ios-yap-database.git', branch: 'signal-release', :inhibit_warnings => true
|
||||||
end
|
end
|
||||||
|
|
|
@ -230,6 +230,6 @@ SPEC CHECKSUMS:
|
||||||
YYImage: 6db68da66f20d9f169ceb94dfb9947c3867b9665
|
YYImage: 6db68da66f20d9f169ceb94dfb9947c3867b9665
|
||||||
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
|
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb
|
||||||
|
|
||||||
PODFILE CHECKSUM: 7699c2a380fc803ef7f51157f1f75da756aa3b45
|
PODFILE CHECKSUM: 3263ab95f60e220882ca53cca4c6bdc2e7a80381
|
||||||
|
|
||||||
COCOAPODS: 1.10.0.rc.1
|
COCOAPODS: 1.10.0.rc.1
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import PromiseKit
|
import PromiseKit
|
||||||
|
import Sodium
|
||||||
|
|
||||||
extension Storage {
|
extension Storage {
|
||||||
|
|
||||||
|
@ -23,6 +24,16 @@ extension Storage {
|
||||||
public func getUserKeyPair() -> ECKeyPair? {
|
public func getUserKeyPair() -> ECKeyPair? {
|
||||||
return OWSIdentityManager.shared().identityKeyPair()
|
return OWSIdentityManager.shared().identityKeyPair()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func getUserED25519KeyPair() -> Box.KeyPair? {
|
||||||
|
let dbConnection = OWSIdentityManager.shared().dbConnection
|
||||||
|
let collection = OWSPrimaryStorageIdentityKeyStoreCollection
|
||||||
|
guard let hexEncodedPublicKey = dbConnection.object(forKey: LKED25519PublicKey, inCollection: collection) as? String,
|
||||||
|
let hexEncodedSecretKey = dbConnection.object(forKey: LKED25519SecretKey, inCollection: collection) as? String else { return nil }
|
||||||
|
let publicKey = Box.KeyPair.PublicKey(hex: hexEncodedPublicKey)
|
||||||
|
let secretKey = Box.KeyPair.SecretKey(hex: hexEncodedSecretKey)
|
||||||
|
return Box.KeyPair(publicKey: publicKey, secretKey: secretKey)
|
||||||
|
}
|
||||||
|
|
||||||
public func getUserDisplayName() -> String? {
|
public func getUserDisplayName() -> String? {
|
||||||
return SSKEnvironment.shared.profileManager.localProfileName()
|
return SSKEnvironment.shared.profileManager.localProfileName()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import CryptoSwift
|
import CryptoSwift
|
||||||
import SessionProtocolKit
|
import SessionProtocolKit
|
||||||
import SessionUtilitiesKit
|
import SessionUtilitiesKit
|
||||||
|
import Sodium
|
||||||
|
|
||||||
internal extension MessageReceiver {
|
internal extension MessageReceiver {
|
||||||
|
|
||||||
|
@ -8,7 +9,7 @@ internal extension MessageReceiver {
|
||||||
let storage = SNMessagingKitConfiguration.shared.signalStorage
|
let storage = SNMessagingKitConfiguration.shared.signalStorage
|
||||||
let certificateValidator = SNMessagingKitConfiguration.shared.certificateValidator
|
let certificateValidator = SNMessagingKitConfiguration.shared.certificateValidator
|
||||||
guard let data = envelope.content else { throw Error.noData }
|
guard let data = envelope.content else { throw Error.noData }
|
||||||
guard let userPublicKey = SNMessagingKitConfiguration.shared.storage.getUserPublicKey() else { throw Error.noUserPublicKey }
|
guard let userPublicKey = SNMessagingKitConfiguration.shared.storage.getUserPublicKey() else { throw Error.noUserX25519KeyPair }
|
||||||
let cipher = try SMKSecretSessionCipher(sessionResetImplementation: SNMessagingKitConfiguration.shared.sessionRestorationImplementation,
|
let cipher = try SMKSecretSessionCipher(sessionResetImplementation: SNMessagingKitConfiguration.shared.sessionRestorationImplementation,
|
||||||
sessionStore: storage, preKeyStore: storage, signedPreKeyStore: storage, identityStore: SNMessagingKitConfiguration.shared.identityKeyStore)
|
sessionStore: storage, preKeyStore: storage, signedPreKeyStore: storage, identityStore: SNMessagingKitConfiguration.shared.identityKeyStore)
|
||||||
let result = try cipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator, cipherTextData: data,
|
let result = try cipher.throwswrapped_decryptMessage(certificateValidator: certificateValidator, cipherTextData: data,
|
||||||
|
@ -16,6 +17,42 @@ internal extension MessageReceiver {
|
||||||
return (result.paddedPayload, result.senderRecipientId)
|
return (result.paddedPayload, result.senderRecipientId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func decryptWithSessionProtocol(envelope: SNProtoEnvelope) throws -> (plaintext: Data, senderX25519PublicKey: String) {
|
||||||
|
guard let ciphertext = envelope.content else { throw Error.noData }
|
||||||
|
let recipientX25519PrivateKey: Data
|
||||||
|
let recipientX25519PublicKey: Data
|
||||||
|
switch envelope.type {
|
||||||
|
case .unidentifiedSender:
|
||||||
|
guard let userX25519KeyPair = SNMessagingKitConfiguration.shared.storage.getUserKeyPair() else { throw Error.noUserX25519KeyPair }
|
||||||
|
recipientX25519PrivateKey = userX25519KeyPair.privateKey
|
||||||
|
recipientX25519PublicKey = Data(hex: userX25519KeyPair.hexEncodedPublicKey.removing05PrefixIfNeeded())
|
||||||
|
case .closedGroupCiphertext:
|
||||||
|
guard let hexEncodedGroupPublicKey = envelope.source, SNMessagingKitConfiguration.shared.storage.isClosedGroup(hexEncodedGroupPublicKey) else { throw Error.invalidGroupPublicKey }
|
||||||
|
guard let hexEncodedGroupPrivateKey = SNMessagingKitConfiguration.shared.storage.getClosedGroupPrivateKey(for: hexEncodedGroupPublicKey) else { throw Error.noGroupPrivateKey }
|
||||||
|
recipientX25519PrivateKey = Data(hex: hexEncodedGroupPrivateKey)
|
||||||
|
recipientX25519PublicKey = Data(hex: hexEncodedGroupPublicKey.removing05PrefixIfNeeded())
|
||||||
|
default: preconditionFailure()
|
||||||
|
}
|
||||||
|
let sodium = Sodium()
|
||||||
|
let signatureSize = sodium.sign.Bytes
|
||||||
|
let ed25519PublicKeySize = sodium.sign.PublicKeyBytes
|
||||||
|
|
||||||
|
// 1. ) Decrypt the message
|
||||||
|
guard let plaintextWithMetadata = sodium.box.open(anonymousCipherText: Bytes(ciphertext), recipientPublicKey: Box.PublicKey(Bytes(recipientX25519PublicKey)),
|
||||||
|
recipientSecretKey: Bytes(recipientX25519PrivateKey)), plaintextWithMetadata.count > (signatureSize + ed25519PublicKeySize) else { throw Error.decryptionFailed }
|
||||||
|
// 2. ) Get the message parts
|
||||||
|
let signature = Bytes(plaintextWithMetadata[plaintextWithMetadata.count - signatureSize ..< plaintextWithMetadata.count])
|
||||||
|
let senderED25519PublicKey = Bytes(plaintextWithMetadata[plaintextWithMetadata.count - (signatureSize + ed25519PublicKeySize) ..< plaintextWithMetadata.count - signatureSize])
|
||||||
|
let plaintext = Bytes(plaintextWithMetadata[0..<plaintextWithMetadata.count - (signatureSize + ed25519PublicKeySize)])
|
||||||
|
// 3. ) Verify the signature
|
||||||
|
let isValid = sodium.sign.verify(message: plaintext + senderED25519PublicKey + recipientX25519PublicKey, publicKey: senderED25519PublicKey, signature: signature)
|
||||||
|
guard isValid else { throw Error.invalidSignature }
|
||||||
|
// 4. ) Get the sender's X25519 public key
|
||||||
|
guard let senderX25519PublicKey = sodium.sign.toX25519(ed25519PublicKey: senderED25519PublicKey) else { throw Error.decryptionFailed }
|
||||||
|
|
||||||
|
return (Data(plaintext), "05" + senderX25519PublicKey.toHexString())
|
||||||
|
}
|
||||||
|
|
||||||
static func decryptWithSharedSenderKeys(envelope: SNProtoEnvelope, using transaction: Any) throws -> (plaintext: Data, senderPublicKey: String) {
|
static func decryptWithSharedSenderKeys(envelope: SNProtoEnvelope, using transaction: Any) throws -> (plaintext: Data, senderPublicKey: String) {
|
||||||
// 1. ) Check preconditions
|
// 1. ) Check preconditions
|
||||||
guard let groupPublicKey = envelope.source, SNMessagingKitConfiguration.shared.storage.isClosedGroup(groupPublicKey) else {
|
guard let groupPublicKey = envelope.source, SNMessagingKitConfiguration.shared.storage.isClosedGroup(groupPublicKey) else {
|
||||||
|
@ -36,8 +73,8 @@ internal extension MessageReceiver {
|
||||||
guard let ephemeralSharedSecret = try? Curve25519.generateSharedSecret(fromPublicKey: ephemeralPublicKey, privateKey: groupPrivateKey) else {
|
guard let ephemeralSharedSecret = try? Curve25519.generateSharedSecret(fromPublicKey: ephemeralPublicKey, privateKey: groupPrivateKey) else {
|
||||||
throw Error.sharedSecretGenerationFailed
|
throw Error.sharedSecretGenerationFailed
|
||||||
}
|
}
|
||||||
let salt = "LOKI"
|
let salt = "LOKI".data(using: String.Encoding.utf8, allowLossyConversion: true)!.bytes
|
||||||
let symmetricKey = try HMAC(key: salt.bytes, variant: .sha256).authenticate(ephemeralSharedSecret.bytes)
|
let symmetricKey = try HMAC(key: salt, variant: .sha256).authenticate(ephemeralSharedSecret.bytes)
|
||||||
let closedGroupCiphertextMessageAsData = try AESGCM.decrypt(ivAndCiphertext, with: Data(symmetricKey))
|
let closedGroupCiphertextMessageAsData = try AESGCM.decrypt(ivAndCiphertext, with: Data(symmetricKey))
|
||||||
// 4. ) Parse the closed group ciphertext message
|
// 4. ) Parse the closed group ciphertext message
|
||||||
let closedGroupCiphertextMessage = ClosedGroupCiphertextMessage(_throws_with: closedGroupCiphertextMessageAsData)
|
let closedGroupCiphertextMessage = ClosedGroupCiphertextMessage(_throws_with: closedGroupCiphertextMessageAsData)
|
||||||
|
|
|
@ -7,11 +7,14 @@ public enum MessageReceiver {
|
||||||
case invalidMessage
|
case invalidMessage
|
||||||
case unknownMessage
|
case unknownMessage
|
||||||
case unknownEnvelopeType
|
case unknownEnvelopeType
|
||||||
case noUserPublicKey
|
case noUserX25519KeyPair
|
||||||
|
case noUserED25519KeyPair
|
||||||
|
case invalidSignature
|
||||||
case noData
|
case noData
|
||||||
case senderBlocked
|
case senderBlocked
|
||||||
case noThread
|
case noThread
|
||||||
case selfSend
|
case selfSend
|
||||||
|
case decryptionFailed
|
||||||
// Shared sender keys
|
// Shared sender keys
|
||||||
case invalidGroupPublicKey
|
case invalidGroupPublicKey
|
||||||
case noGroupPrivateKey
|
case noGroupPrivateKey
|
||||||
|
@ -19,7 +22,7 @@ public enum MessageReceiver {
|
||||||
|
|
||||||
public var isRetryable: Bool {
|
public var isRetryable: Bool {
|
||||||
switch self {
|
switch self {
|
||||||
case .duplicateMessage, .invalidMessage, .unknownMessage, .unknownEnvelopeType, .noData, .senderBlocked, .selfSend: return false
|
case .duplicateMessage, .invalidMessage, .unknownMessage, .unknownEnvelopeType, .invalidSignature, .noData, .senderBlocked, .selfSend, .decryptionFailed: return false
|
||||||
default: return true
|
default: return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,15 +33,18 @@ public enum MessageReceiver {
|
||||||
case .invalidMessage: return "Invalid message."
|
case .invalidMessage: return "Invalid message."
|
||||||
case .unknownMessage: return "Unknown message type."
|
case .unknownMessage: return "Unknown message type."
|
||||||
case .unknownEnvelopeType: return "Unknown envelope type."
|
case .unknownEnvelopeType: return "Unknown envelope type."
|
||||||
case .noUserPublicKey: return "Couldn't find user key pair."
|
case .noUserX25519KeyPair: return "Couldn't find user X25519 key pair."
|
||||||
|
case .noUserED25519KeyPair: return "Couldn't find user ED25519 key pair."
|
||||||
|
case .invalidSignature: return "Invalid message signature."
|
||||||
case .noData: return "Received an empty envelope."
|
case .noData: return "Received an empty envelope."
|
||||||
case .senderBlocked: return "Received a message from a blocked user."
|
case .senderBlocked: return "Received a message from a blocked user."
|
||||||
case .noThread: return "Couldn't find thread for message."
|
case .noThread: return "Couldn't find thread for message."
|
||||||
|
case .selfSend: return "Message addressed at self."
|
||||||
|
case .decryptionFailed: return "Decryption failed."
|
||||||
// Shared sender keys
|
// Shared sender keys
|
||||||
case .invalidGroupPublicKey: return "Invalid group public key."
|
case .invalidGroupPublicKey: return "Invalid group public key."
|
||||||
case .noGroupPrivateKey: return "Missing group private key."
|
case .noGroupPrivateKey: return "Missing group private key."
|
||||||
case .sharedSecretGenerationFailed: return "Couldn't generate a shared secret."
|
case .sharedSecretGenerationFailed: return "Couldn't generate a shared secret."
|
||||||
case .selfSend: return "Message addressed at self."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,9 +65,20 @@ public enum MessageReceiver {
|
||||||
(plaintext, sender) = (envelope.content!, envelope.source!)
|
(plaintext, sender) = (envelope.content!, envelope.source!)
|
||||||
} else {
|
} else {
|
||||||
switch envelope.type {
|
switch envelope.type {
|
||||||
case .unidentifiedSender: (plaintext, sender) = try decryptWithSignalProtocol(envelope: envelope, using: transaction)
|
case .unidentifiedSender:
|
||||||
|
do {
|
||||||
|
(plaintext, sender) = try decryptWithSessionProtocol(envelope: envelope)
|
||||||
|
} catch {
|
||||||
|
// Migration
|
||||||
|
(plaintext, sender) = try decryptWithSignalProtocol(envelope: envelope, using: transaction)
|
||||||
|
}
|
||||||
case .closedGroupCiphertext:
|
case .closedGroupCiphertext:
|
||||||
(plaintext, sender) = try decryptWithSharedSenderKeys(envelope: envelope, using: transaction)
|
do {
|
||||||
|
(plaintext, sender) = try decryptWithSessionProtocol(envelope: envelope)
|
||||||
|
} catch {
|
||||||
|
// Migration
|
||||||
|
(plaintext, sender) = try decryptWithSharedSenderKeys(envelope: envelope, using: transaction)
|
||||||
|
}
|
||||||
groupPublicKey = envelope.source
|
groupPublicKey = envelope.source
|
||||||
default: throw Error.unknownEnvelopeType
|
default: throw Error.unknownEnvelopeType
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import SessionProtocolKit
|
import SessionProtocolKit
|
||||||
import SessionUtilitiesKit
|
import SessionUtilitiesKit
|
||||||
|
import Sodium
|
||||||
|
|
||||||
internal extension MessageSender {
|
internal extension MessageSender {
|
||||||
|
|
||||||
|
@ -11,12 +12,25 @@ internal extension MessageSender {
|
||||||
return try cipher.throwswrapped_encryptMessage(recipientPublicKey: publicKey, deviceID: 1, paddedPlaintext: (plaintext as NSData).paddedMessageBody(),
|
return try cipher.throwswrapped_encryptMessage(recipientPublicKey: publicKey, deviceID: 1, paddedPlaintext: (plaintext as NSData).paddedMessageBody(),
|
||||||
senderCertificate: certificate, protocolContext: transaction, useFallbackSessionCipher: true)
|
senderCertificate: certificate, protocolContext: transaction, useFallbackSessionCipher: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func encryptWithSessionProtocol(_ plaintext: Data, for recipientHexEncodedX25519PublicKey: String) throws -> Data {
|
||||||
|
guard let userED25519KeyPair = SNMessagingKitConfiguration.shared.storage.getUserED25519KeyPair() else { throw Error.noUserED25519KeyPair }
|
||||||
|
let recipientX25519PublicKey = Data(hex: recipientHexEncodedX25519PublicKey.removing05PrefixIfNeeded())
|
||||||
|
let sodium = Sodium()
|
||||||
|
|
||||||
|
let data = plaintext + Data(userED25519KeyPair.publicKey) + recipientX25519PublicKey
|
||||||
|
guard let signature = sodium.sign.signature(message: Bytes(data), secretKey: userED25519KeyPair.secretKey) else { throw Error.signingFailed }
|
||||||
|
guard let ciphertext = sodium.box.seal(message: Bytes(plaintext + Data(userED25519KeyPair.publicKey)
|
||||||
|
+ Data(signature)), recipientPublicKey: Bytes(recipientX25519PublicKey)) else { throw Error.encryptionFailed }
|
||||||
|
|
||||||
|
return Data(ciphertext)
|
||||||
|
}
|
||||||
|
|
||||||
static func encryptWithSharedSenderKeys(_ plaintext: Data, for groupPublicKey: String, using transaction: Any) throws -> Data {
|
static func encryptWithSharedSenderKeys(_ plaintext: Data, for groupPublicKey: String, using transaction: Any) throws -> Data {
|
||||||
// 1. ) Encrypt the data with the user's sender key
|
// 1. ) Encrypt the data with the user's sender key
|
||||||
guard let userPublicKey = SNMessagingKitConfiguration.shared.storage.getUserPublicKey() else {
|
guard let userPublicKey = SNMessagingKitConfiguration.shared.storage.getUserPublicKey() else {
|
||||||
SNLog("Couldn't find user key pair.")
|
SNLog("Couldn't find user key pair.")
|
||||||
throw Error.noUserPublicKey
|
throw Error.noUserX25519KeyPair
|
||||||
}
|
}
|
||||||
let (ivAndCiphertext, keyIndex) = try SharedSenderKeys.encrypt((plaintext as NSData).paddedMessageBody(), for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
|
let (ivAndCiphertext, keyIndex) = try SharedSenderKeys.encrypt((plaintext as NSData).paddedMessageBody(), for: groupPublicKey, senderPublicKey: userPublicKey, using: transaction)
|
||||||
let encryptedMessage = ClosedGroupCiphertextMessage(_throws_withIVAndCiphertext: ivAndCiphertext, senderPublicKey: Data(hex: userPublicKey), keyIndex: UInt32(keyIndex))
|
let encryptedMessage = ClosedGroupCiphertextMessage(_throws_withIVAndCiphertext: ivAndCiphertext, senderPublicKey: Data(hex: userPublicKey), keyIndex: UInt32(keyIndex))
|
||||||
|
|
|
@ -10,7 +10,10 @@ public final class MessageSender : NSObject {
|
||||||
case invalidMessage
|
case invalidMessage
|
||||||
case protoConversionFailed
|
case protoConversionFailed
|
||||||
case proofOfWorkCalculationFailed
|
case proofOfWorkCalculationFailed
|
||||||
case noUserPublicKey
|
case noUserX25519KeyPair
|
||||||
|
case noUserED25519KeyPair
|
||||||
|
case signingFailed
|
||||||
|
case encryptionFailed
|
||||||
// Closed groups
|
// Closed groups
|
||||||
case noThread
|
case noThread
|
||||||
case noPrivateKey
|
case noPrivateKey
|
||||||
|
@ -18,7 +21,7 @@ public final class MessageSender : NSObject {
|
||||||
|
|
||||||
internal var isRetryable: Bool {
|
internal var isRetryable: Bool {
|
||||||
switch self {
|
switch self {
|
||||||
case .invalidMessage, .protoConversionFailed, .proofOfWorkCalculationFailed, .invalidClosedGroupUpdate: return false
|
case .invalidMessage, .protoConversionFailed, .proofOfWorkCalculationFailed, .invalidClosedGroupUpdate, .signingFailed, .encryptionFailed: return false
|
||||||
default: return true
|
default: return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +31,10 @@ public final class MessageSender : NSObject {
|
||||||
case .invalidMessage: return "Invalid message."
|
case .invalidMessage: return "Invalid message."
|
||||||
case .protoConversionFailed: return "Couldn't convert message to proto."
|
case .protoConversionFailed: return "Couldn't convert message to proto."
|
||||||
case .proofOfWorkCalculationFailed: return "Proof of work calculation failed."
|
case .proofOfWorkCalculationFailed: return "Proof of work calculation failed."
|
||||||
case .noUserPublicKey: return "Couldn't find user key pair."
|
case .noUserX25519KeyPair: return "Couldn't find user X25519 key pair."
|
||||||
|
case .noUserED25519KeyPair: return "Couldn't find user ED25519 key pair."
|
||||||
|
case .signingFailed: return "Couldn't sign message."
|
||||||
|
case .encryptionFailed: return "Couldn't encrypt message."
|
||||||
// Closed groups
|
// Closed groups
|
||||||
case .noThread: return "Couldn't find a thread associated with the given group public key."
|
case .noThread: return "Couldn't find a thread associated with the given group public key."
|
||||||
case .noPrivateKey: return "Couldn't find a private key associated with the given group public key."
|
case .noPrivateKey: return "Couldn't find a private key associated with the given group public key."
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import SessionProtocolKit
|
import SessionProtocolKit
|
||||||
import PromiseKit
|
import PromiseKit
|
||||||
|
import Sodium
|
||||||
|
|
||||||
public protocol SessionMessagingKitStorageProtocol {
|
public protocol SessionMessagingKitStorageProtocol {
|
||||||
|
|
||||||
|
@ -15,6 +16,7 @@ public protocol SessionMessagingKitStorageProtocol {
|
||||||
|
|
||||||
func getUserPublicKey() -> String?
|
func getUserPublicKey() -> String?
|
||||||
func getUserKeyPair() -> ECKeyPair?
|
func getUserKeyPair() -> ECKeyPair?
|
||||||
|
func getUserED25519KeyPair() -> Box.KeyPair?
|
||||||
func getUserDisplayName() -> String?
|
func getUserDisplayName() -> String?
|
||||||
func getUserProfileKey() -> Data?
|
func getUserProfileKey() -> Data?
|
||||||
func getUserProfilePictureURL() -> String?
|
func getUserProfilePictureURL() -> String?
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public extension String {
|
public extension String {
|
||||||
|
|
||||||
var digitsOnly: String {
|
var digitsOnly: String {
|
||||||
return (self as NSString).digitsOnly()
|
return (self as NSString).digitsOnly()
|
||||||
}
|
}
|
||||||
|
|
|
@ -500,7 +500,6 @@ CGFloat ScaleFromIPhone5(CGFloat iPhone5Value)
|
||||||
|
|
||||||
- (UIView *)addBorderViewWithColor:(UIColor *)color strokeWidth:(CGFloat)strokeWidth cornerRadius:(CGFloat)cornerRadius
|
- (UIView *)addBorderViewWithColor:(UIColor *)color strokeWidth:(CGFloat)strokeWidth cornerRadius:(CGFloat)cornerRadius
|
||||||
{
|
{
|
||||||
|
|
||||||
UIView *borderView = [UIView new];
|
UIView *borderView = [UIView new];
|
||||||
borderView.userInteractionEnabled = NO;
|
borderView.userInteractionEnabled = NO;
|
||||||
borderView.backgroundColor = UIColor.clearColor;
|
borderView.backgroundColor = UIColor.clearColor;
|
||||||
|
|
|
@ -244,6 +244,7 @@
|
||||||
B8566C63256F55930045A0B9 /* OWSLinkPreview+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8566C62256F55930045A0B9 /* OWSLinkPreview+Conversion.swift */; };
|
B8566C63256F55930045A0B9 /* OWSLinkPreview+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8566C62256F55930045A0B9 /* OWSLinkPreview+Conversion.swift */; };
|
||||||
B8566C6C256F60F50045A0B9 /* OWSUserProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2D1255B6DAF007E1867 /* OWSUserProfile.m */; };
|
B8566C6C256F60F50045A0B9 /* OWSUserProfile.m in Sources */ = {isa = PBXBuildFile; fileRef = C38EF2D1255B6DAF007E1867 /* OWSUserProfile.m */; };
|
||||||
B8566C7D256F62030045A0B9 /* OWSUserProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2D3255B6DAF007E1867 /* OWSUserProfile.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
B8566C7D256F62030045A0B9 /* OWSUserProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = C38EF2D3255B6DAF007E1867 /* OWSUserProfile.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
|
B866CE112581C1A900535CC4 /* Sodium+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E7134E251C867C009649BB /* Sodium+Conversion.swift */; };
|
||||||
B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; };
|
B86BD08423399ACF000F5AE3 /* Modal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08323399ACF000F5AE3 /* Modal.swift */; };
|
||||||
B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; };
|
B86BD08623399CEF000F5AE3 /* SeedModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = B86BD08523399CEF000F5AE3 /* SeedModal.swift */; };
|
||||||
B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */; };
|
B8783E9E23EB948D00404FB8 /* UILabel+Interaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */; };
|
||||||
|
@ -914,7 +915,6 @@
|
||||||
C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */; };
|
C3DAB3242480CB2B00725F25 /* SRCopyableLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DAB3232480CB2A00725F25 /* SRCopyableLabel.swift */; };
|
||||||
C3DFFAC623E96F0D0058DAF8 /* Sheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */; };
|
C3DFFAC623E96F0D0058DAF8 /* Sheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3DFFAC523E96F0D0058DAF8 /* Sheet.swift */; };
|
||||||
C3E5C2FA251DBABB0040DFFC /* EditClosedGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */; };
|
C3E5C2FA251DBABB0040DFFC /* EditClosedGroupVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E5C2F9251DBABB0040DFFC /* EditClosedGroupVC.swift */; };
|
||||||
C3E7134F251C867C009649BB /* Sodium+Conversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E7134E251C867C009649BB /* Sodium+Conversion.swift */; };
|
|
||||||
C3ECBF7B257056B700EA7FCE /* Threading.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3ECBF7A257056B700EA7FCE /* Threading.swift */; };
|
C3ECBF7B257056B700EA7FCE /* Threading.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3ECBF7A257056B700EA7FCE /* Threading.swift */; };
|
||||||
C3F0A530255C80BC007BE2A3 /* NoopNotificationsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F0A52F255C80BC007BE2A3 /* NoopNotificationsManager.swift */; };
|
C3F0A530255C80BC007BE2A3 /* NoopNotificationsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3F0A52F255C80BC007BE2A3 /* NoopNotificationsManager.swift */; };
|
||||||
D2179CFC16BB0B3A0006F3AB /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2179CFB16BB0B3A0006F3AB /* CoreTelephony.framework */; };
|
D2179CFC16BB0B3A0006F3AB /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D2179CFB16BB0B3A0006F3AB /* CoreTelephony.framework */; };
|
||||||
|
@ -2603,7 +2603,6 @@
|
||||||
C31FFE56254A5FFE00F19441 /* KeyPairUtilities.swift */,
|
C31FFE56254A5FFE00F19441 /* KeyPairUtilities.swift */,
|
||||||
B84664F4235022F30083A1CD /* MentionUtilities.swift */,
|
B84664F4235022F30083A1CD /* MentionUtilities.swift */,
|
||||||
B886B4A82398BA1500211ABE /* QRCode.swift */,
|
B886B4A82398BA1500211ABE /* QRCode.swift */,
|
||||||
C3E7134E251C867C009649BB /* Sodium+Conversion.swift */,
|
|
||||||
B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */,
|
B8783E9D23EB948D00404FB8 /* UILabel+Interaction.swift */,
|
||||||
B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */,
|
B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */,
|
||||||
C31A6C59247F214E001123EF /* UIView+Glow.swift */,
|
C31A6C59247F214E001123EF /* UIView+Glow.swift */,
|
||||||
|
@ -3372,6 +3371,7 @@
|
||||||
C33FDB91255A581200E217F9 /* ProtoUtils.h */,
|
C33FDB91255A581200E217F9 /* ProtoUtils.h */,
|
||||||
C33FDA6C255A57FA00E217F9 /* ProtoUtils.m */,
|
C33FDA6C255A57FA00E217F9 /* ProtoUtils.m */,
|
||||||
C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */,
|
C38EEF09255B49A8007E1867 /* SNProtoEnvelope+Conversion.swift */,
|
||||||
|
C3E7134E251C867C009649BB /* Sodium+Conversion.swift */,
|
||||||
C33FDB31255A580A00E217F9 /* SSKEnvironment.h */,
|
C33FDB31255A580A00E217F9 /* SSKEnvironment.h */,
|
||||||
C33FDAF4255A580600E217F9 /* SSKEnvironment.m */,
|
C33FDAF4255A580600E217F9 /* SSKEnvironment.m */,
|
||||||
C33FDB32255A580A00E217F9 /* SSKIncrementingIdFinder.swift */,
|
C33FDB32255A580A00E217F9 /* SSKIncrementingIdFinder.swift */,
|
||||||
|
@ -5253,6 +5253,7 @@
|
||||||
C3D9E3BE25676AD70040E4F3 /* TSAttachmentPointer.m in Sources */,
|
C3D9E3BE25676AD70040E4F3 /* TSAttachmentPointer.m in Sources */,
|
||||||
C3ECBF7B257056B700EA7FCE /* Threading.swift in Sources */,
|
C3ECBF7B257056B700EA7FCE /* Threading.swift in Sources */,
|
||||||
C32C5AAB256DBE8F003C73A2 /* TSIncomingMessage+Conversion.swift in Sources */,
|
C32C5AAB256DBE8F003C73A2 /* TSIncomingMessage+Conversion.swift in Sources */,
|
||||||
|
B866CE112581C1A900535CC4 /* Sodium+Conversion.swift in Sources */,
|
||||||
C32C5A88256DBCF9003C73A2 /* MessageReceiver+Handling.swift in Sources */,
|
C32C5A88256DBCF9003C73A2 /* MessageReceiver+Handling.swift in Sources */,
|
||||||
C32C5C1B256DC9E0003C73A2 /* General.swift in Sources */,
|
C32C5C1B256DC9E0003C73A2 /* General.swift in Sources */,
|
||||||
C32C5A02256DB658003C73A2 /* MessageSender+Convenience.swift in Sources */,
|
C32C5A02256DB658003C73A2 /* MessageSender+Convenience.swift in Sources */,
|
||||||
|
@ -5522,7 +5523,6 @@
|
||||||
34D1F0831F8678AA0066283D /* ConversationInputTextView.m in Sources */,
|
34D1F0831F8678AA0066283D /* ConversationInputTextView.m in Sources */,
|
||||||
340FC8B6204DAC8D007AEB0F /* OWSQRCodeScanningViewController.m in Sources */,
|
340FC8B6204DAC8D007AEB0F /* OWSQRCodeScanningViewController.m in Sources */,
|
||||||
4CB5F26920F7D060004D1B42 /* MessageActions.swift in Sources */,
|
4CB5F26920F7D060004D1B42 /* MessageActions.swift in Sources */,
|
||||||
C3E7134F251C867C009649BB /* Sodium+Conversion.swift in Sources */,
|
|
||||||
340FC8B5204DAC8D007AEB0F /* AboutTableViewController.m in Sources */,
|
340FC8B5204DAC8D007AEB0F /* AboutTableViewController.m in Sources */,
|
||||||
C33100082558FF6D00070591 /* NewConversationButtonSet.swift in Sources */,
|
C33100082558FF6D00070591 /* NewConversationButtonSet.swift in Sources */,
|
||||||
B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */,
|
B8BB82A5238F627000BA5194 /* HomeVC.swift in Sources */,
|
||||||
|
|
Loading…
Reference in New Issue