When someone sends a message to your PNI, your responses (from your ACI) must include a PNI signature, and the sealed sender certificate you use during this period should include your phone number. This confirms to the other user that your ACI is associated with your PNI. This commit adds the state tracking that and ensures that both TSOutgoingMessage and OWSUDManager check that state when building 1:1 messages and choosing certificates, respectively. Later commits will set and clear this flag as needed.
415 lines
19 KiB
Swift
415 lines
19 KiB
Swift
//
|
|
// Copyright (c) 2022 Open Whisper Systems. All rights reserved.
|
|
//
|
|
|
|
import XCTest
|
|
import Foundation
|
|
import Curve25519Kit
|
|
import SignalCoreKit
|
|
import LibSignalClient
|
|
@testable import SignalServiceKit
|
|
|
|
class OWSUDManagerTest: SSKBaseTestSwift {
|
|
|
|
private var udManagerImpl: OWSUDManagerImpl {
|
|
return SSKEnvironment.shared.udManager as! OWSUDManagerImpl
|
|
}
|
|
|
|
// MARK: - Setup/Teardown
|
|
|
|
let aliceE164 = "+13213214321"
|
|
let aliceUuid = UUID()
|
|
let trustRoot = IdentityKeyPair.generate()
|
|
lazy var aliceAddress = SignalServiceAddress(uuid: aliceUuid, phoneNumber: aliceE164)
|
|
lazy var defaultSenderCert = buildSenderCertificate(uuidOnly: false)
|
|
lazy var uuidOnlySenderCert = buildSenderCertificate(uuidOnly: true)
|
|
|
|
override func setUp() {
|
|
super.setUp()
|
|
|
|
tsAccountManager.registerForTests(withLocalNumber: aliceE164, uuid: aliceUuid)
|
|
(MockSSKEnvironment.shared as! MockSSKEnvironment).setContactsManagerForMock(FakeContactsManager())
|
|
|
|
// Configure UDManager
|
|
self.write { transaction in
|
|
self.profileManager.setProfileKeyData(OWSAES256Key.generateRandom().keyData,
|
|
for: self.aliceAddress,
|
|
userProfileWriter: .tests,
|
|
transaction: transaction)
|
|
}
|
|
|
|
udManagerImpl.trustRoot = ECPublicKey(trustRoot.publicKey)
|
|
udManagerImpl.setSenderCertificate(uuidOnly: true, certificateData: Data(uuidOnlySenderCert.serialize()))
|
|
udManagerImpl.setSenderCertificate(uuidOnly: false, certificateData: Data(defaultSenderCert.serialize()))
|
|
}
|
|
|
|
override func tearDown() {
|
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
|
super.tearDown()
|
|
}
|
|
|
|
// MARK: - Tests
|
|
|
|
func testMode_self() {
|
|
XCTAssert(udManagerImpl.hasSenderCertificates())
|
|
|
|
XCTAssert(tsAccountManager.isRegistered)
|
|
guard let localAddress = tsAccountManager.localAddress else {
|
|
XCTFail("localAddress was unexpectedly nil")
|
|
return
|
|
}
|
|
XCTAssert(localAddress.isValid)
|
|
|
|
do {
|
|
let udAccess = udManagerImpl.udAccess(forAddress: localAddress, requireSyncAccess: false)!
|
|
XCTAssertNotNil(udAccess)
|
|
XCTAssertEqual(.enabled, udAccess.udAccessMode)
|
|
XCTAssertFalse(udAccess.isRandomKey)
|
|
}
|
|
|
|
do {
|
|
udManagerImpl.setUnidentifiedAccessMode(.unknown, address: aliceAddress)
|
|
let udAccess = udManagerImpl.udAccess(forAddress: localAddress, requireSyncAccess: false)!
|
|
XCTAssertNotNil(udAccess)
|
|
XCTAssertEqual(.unknown, udAccess.udAccessMode)
|
|
XCTAssertFalse(udAccess.isRandomKey)
|
|
}
|
|
|
|
do {
|
|
udManagerImpl.setUnidentifiedAccessMode(.disabled, address: aliceAddress)
|
|
let udAccess = udManagerImpl.udAccess(forAddress: localAddress, requireSyncAccess: false)
|
|
XCTAssertNil(udAccess)
|
|
}
|
|
|
|
do {
|
|
udManagerImpl.setUnidentifiedAccessMode(.enabled, address: aliceAddress)
|
|
let udAccess = udManagerImpl.udAccess(forAddress: localAddress, requireSyncAccess: false)!
|
|
XCTAssertNotNil(udAccess)
|
|
XCTAssertEqual(.enabled, udAccess.udAccessMode)
|
|
XCTAssertFalse(udAccess.isRandomKey)
|
|
}
|
|
|
|
do {
|
|
udManagerImpl.setUnidentifiedAccessMode(.unrestricted, address: aliceAddress)
|
|
let udAccess = udManagerImpl.udAccess(forAddress: localAddress, requireSyncAccess: false)!
|
|
XCTAssertNotNil(udAccess)
|
|
XCTAssertEqual(.unrestricted, udAccess.udAccessMode)
|
|
XCTAssert(udAccess.isRandomKey)
|
|
}
|
|
}
|
|
|
|
func testMode_noProfileKey() {
|
|
XCTAssert(udManagerImpl.hasSenderCertificates())
|
|
|
|
XCTAssert(tsAccountManager.isRegistered)
|
|
guard let localAddress = tsAccountManager.localAddress else {
|
|
XCTFail("localAddress was unexpectedly nil")
|
|
return
|
|
}
|
|
XCTAssert(localAddress.isValid)
|
|
|
|
// Ensure UD is enabled by setting our own access level to enabled.
|
|
udManagerImpl.setUnidentifiedAccessMode(.enabled, address: localAddress)
|
|
|
|
let bobRecipientAddress = SignalServiceAddress(phoneNumber: "+13213214322")
|
|
XCTAssertFalse(bobRecipientAddress.isLocalAddress)
|
|
self.read { transaction in
|
|
XCTAssertNil(self.profileManager.profileKeyData(for: bobRecipientAddress, transaction: transaction))
|
|
}
|
|
|
|
do {
|
|
let udAccess = udManagerImpl.udAccess(forAddress: bobRecipientAddress, requireSyncAccess: false)!
|
|
XCTAssertNotNil(udAccess)
|
|
XCTAssertEqual(.unknown, udAccess.udAccessMode)
|
|
XCTAssert(udAccess.isRandomKey)
|
|
}
|
|
|
|
do {
|
|
udManagerImpl.setUnidentifiedAccessMode(.unknown, address: bobRecipientAddress)
|
|
let udAccess = udManagerImpl.udAccess(forAddress: bobRecipientAddress, requireSyncAccess: false)!
|
|
XCTAssertNotNil(udAccess)
|
|
XCTAssertEqual(.unknown, udAccess.udAccessMode)
|
|
XCTAssert(udAccess.isRandomKey)
|
|
}
|
|
|
|
do {
|
|
udManagerImpl.setUnidentifiedAccessMode(.disabled, address: bobRecipientAddress)
|
|
let udAccess = udManagerImpl.udAccess(forAddress: bobRecipientAddress, requireSyncAccess: false)
|
|
XCTAssertNil(udAccess)
|
|
}
|
|
|
|
do {
|
|
udManagerImpl.setUnidentifiedAccessMode(.enabled, address: bobRecipientAddress)
|
|
let udAccess = udManagerImpl.udAccess(forAddress: bobRecipientAddress, requireSyncAccess: false)
|
|
XCTAssertNil(udAccess)
|
|
}
|
|
|
|
do {
|
|
// Bob should work in unrestricted mode, even if he doesn't have a profile key.
|
|
udManagerImpl.setUnidentifiedAccessMode(.unrestricted, address: bobRecipientAddress)
|
|
let udAccess = udManagerImpl.udAccess(forAddress: bobRecipientAddress, requireSyncAccess: false)!
|
|
XCTAssertNotNil(udAccess)
|
|
XCTAssertEqual(.unrestricted, udAccess.udAccessMode)
|
|
XCTAssert(udAccess.isRandomKey)
|
|
}
|
|
}
|
|
|
|
func testMode_withProfileKey() {
|
|
XCTAssert(udManagerImpl.hasSenderCertificates())
|
|
|
|
XCTAssert(tsAccountManager.isRegistered)
|
|
guard let localAddress = tsAccountManager.localAddress else {
|
|
XCTFail("localAddress was unexpectedly nil")
|
|
return
|
|
}
|
|
XCTAssert(localAddress.isValid)
|
|
|
|
// Ensure UD is enabled by setting our own access level to enabled.
|
|
udManagerImpl.setUnidentifiedAccessMode(.enabled, address: localAddress)
|
|
|
|
let bobRecipientAddress = SignalServiceAddress(phoneNumber: "+13213214322")
|
|
XCTAssertFalse(bobRecipientAddress.isLocalAddress)
|
|
self.write { transaction in
|
|
self.profileManager.setProfileKeyData(OWSAES256Key.generateRandom().keyData,
|
|
for: bobRecipientAddress,
|
|
userProfileWriter: .tests,
|
|
transaction: transaction)
|
|
}
|
|
|
|
do {
|
|
let udAccess = udManagerImpl.udAccess(forAddress: bobRecipientAddress, requireSyncAccess: false)!
|
|
XCTAssertNotNil(udAccess)
|
|
XCTAssertEqual(.unknown, udAccess.udAccessMode)
|
|
XCTAssertFalse(udAccess.isRandomKey)
|
|
}
|
|
|
|
do {
|
|
udManagerImpl.setUnidentifiedAccessMode(.unknown, address: bobRecipientAddress)
|
|
let udAccess = udManagerImpl.udAccess(forAddress: bobRecipientAddress, requireSyncAccess: false)!
|
|
XCTAssertNotNil(udAccess)
|
|
XCTAssertEqual(.unknown, udAccess.udAccessMode)
|
|
XCTAssertFalse(udAccess.isRandomKey)
|
|
|
|
}
|
|
|
|
do {
|
|
udManagerImpl.setUnidentifiedAccessMode(.disabled, address: bobRecipientAddress)
|
|
let udAccess = udManagerImpl.udAccess(forAddress: bobRecipientAddress, requireSyncAccess: false)
|
|
XCTAssertNil(udAccess)
|
|
}
|
|
|
|
do {
|
|
udManagerImpl.setUnidentifiedAccessMode(.enabled, address: bobRecipientAddress)
|
|
let udAccess = udManagerImpl.udAccess(forAddress: bobRecipientAddress, requireSyncAccess: false)!
|
|
XCTAssertNotNil(udAccess)
|
|
XCTAssertEqual(.enabled, udAccess.udAccessMode)
|
|
XCTAssertFalse(udAccess.isRandomKey)
|
|
}
|
|
|
|
do {
|
|
udManagerImpl.setUnidentifiedAccessMode(.unrestricted, address: bobRecipientAddress)
|
|
let udAccess = udManagerImpl.udAccess(forAddress: bobRecipientAddress, requireSyncAccess: false)!
|
|
XCTAssertNotNil(udAccess)
|
|
XCTAssertEqual(.unrestricted, udAccess.udAccessMode)
|
|
XCTAssert(udAccess.isRandomKey)
|
|
}
|
|
}
|
|
|
|
func test_senderAccess() {
|
|
XCTAssert(udManagerImpl.hasSenderCertificates())
|
|
|
|
XCTAssert(tsAccountManager.isRegistered)
|
|
guard let localAddress = tsAccountManager.localAddress else {
|
|
XCTFail("localAddress was unexpectedly nil")
|
|
return
|
|
}
|
|
XCTAssert(localAddress.isValid)
|
|
|
|
// Ensure UD is enabled by setting our own access level to enabled.
|
|
udManagerImpl.setUnidentifiedAccessMode(.enabled, address: localAddress)
|
|
|
|
let bobRecipientAddress = SignalServiceAddress(phoneNumber: "+13213214322")
|
|
XCTAssertFalse(bobRecipientAddress.isLocalAddress)
|
|
write { transaction in
|
|
self.profileManager.setProfileKeyData(OWSAES256Key.generateRandom().keyData,
|
|
for: bobRecipientAddress,
|
|
userProfileWriter: .tests,
|
|
transaction: transaction)
|
|
}
|
|
|
|
firstly {
|
|
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict)
|
|
}.done { senderCertificates in
|
|
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress,
|
|
requireSyncAccess: false,
|
|
senderCertificates: senderCertificates)!
|
|
XCTAssertEqual(.unknown, sendingAccess.udAccess.udAccessMode)
|
|
XCTAssertFalse(sendingAccess.udAccess.isRandomKey)
|
|
XCTAssertEqual(sendingAccess.senderCertificate.serialize(),
|
|
self.defaultSenderCert.serialize())
|
|
}.expect(timeout: 1.0)
|
|
|
|
// Turn off phone number sharing.
|
|
write { transaction in
|
|
udManagerImpl.setPhoneNumberSharingMode(.nobody,
|
|
updateStorageService: false,
|
|
transaction: transaction.unwrapGrdbWrite)
|
|
}
|
|
|
|
firstly {
|
|
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict)
|
|
}.done { senderCertificates in
|
|
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress,
|
|
requireSyncAccess: false,
|
|
senderCertificates: senderCertificates)!
|
|
XCTAssertEqual(.unknown, sendingAccess.udAccess.udAccessMode)
|
|
XCTAssertFalse(sendingAccess.udAccess.isRandomKey)
|
|
XCTAssertEqual(sendingAccess.senderCertificate.serialize(),
|
|
self.uuidOnlySenderCert.serialize())
|
|
}.expect(timeout: 1.0)
|
|
|
|
// Contacts-only sharing?
|
|
write { transaction in
|
|
udManagerImpl.setPhoneNumberSharingMode(.contactsOnly,
|
|
updateStorageService: false,
|
|
transaction: transaction.unwrapGrdbWrite)
|
|
}
|
|
|
|
firstly {
|
|
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict)
|
|
}.done { senderCertificates in
|
|
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress,
|
|
requireSyncAccess: false,
|
|
senderCertificates: senderCertificates)!
|
|
XCTAssertEqual(.unknown, sendingAccess.udAccess.udAccessMode)
|
|
XCTAssertFalse(sendingAccess.udAccess.isRandomKey)
|
|
XCTAssertEqual(sendingAccess.senderCertificate.serialize(),
|
|
self.uuidOnlySenderCert.serialize())
|
|
}.expect(timeout: 1.0)
|
|
|
|
// Add Bob to our contacts.
|
|
(contactsManager as! FakeContactsManager).systemContacts.append(bobRecipientAddress)
|
|
|
|
firstly {
|
|
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict)
|
|
}.done { senderCertificates in
|
|
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress,
|
|
requireSyncAccess: false,
|
|
senderCertificates: senderCertificates)!
|
|
XCTAssertEqual(.unknown, sendingAccess.udAccess.udAccessMode)
|
|
XCTAssertFalse(sendingAccess.udAccess.isRandomKey)
|
|
XCTAssertEqual(sendingAccess.senderCertificate.serialize(),
|
|
self.defaultSenderCert.serialize())
|
|
}.expect(timeout: 1.0)
|
|
}
|
|
|
|
func test_certificateChoiceWithPhoneNumberShared() {
|
|
XCTAssert(udManagerImpl.hasSenderCertificates())
|
|
|
|
XCTAssert(tsAccountManager.isRegistered)
|
|
guard let localAddress = tsAccountManager.localAddress else {
|
|
XCTFail("localAddress was unexpectedly nil")
|
|
return
|
|
}
|
|
XCTAssert(localAddress.isValid)
|
|
|
|
// Ensure UD is enabled by setting our own access level to enabled.
|
|
udManagerImpl.setUnidentifiedAccessMode(.enabled, address: localAddress)
|
|
|
|
let bobRecipientAddress = SignalServiceAddress(uuid: UUID(), phoneNumber: "+13213214322")
|
|
XCTAssertFalse(bobRecipientAddress.isLocalAddress)
|
|
write { transaction in
|
|
self.profileManager.setProfileKeyData(OWSAES256Key.generateRandom().keyData,
|
|
for: bobRecipientAddress,
|
|
userProfileWriter: .tests,
|
|
transaction: transaction)
|
|
identityManager.setShouldSharePhoneNumber(with: bobRecipientAddress, transaction: transaction)
|
|
}
|
|
|
|
firstly {
|
|
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict)
|
|
}.done { senderCertificates in
|
|
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress,
|
|
requireSyncAccess: false,
|
|
senderCertificates: senderCertificates)!
|
|
XCTAssertEqual(sendingAccess.senderCertificate.serialize(),
|
|
self.defaultSenderCert.serialize())
|
|
}.expect(timeout: 1.0)
|
|
|
|
// Turn off phone number sharing.
|
|
write { transaction in
|
|
udManagerImpl.setPhoneNumberSharingMode(.nobody,
|
|
updateStorageService: false,
|
|
transaction: transaction.unwrapGrdbWrite)
|
|
}
|
|
|
|
firstly {
|
|
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict)
|
|
}.done { senderCertificates in
|
|
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress,
|
|
requireSyncAccess: false,
|
|
senderCertificates: senderCertificates)!
|
|
XCTAssertEqual(sendingAccess.senderCertificate.serialize(),
|
|
self.defaultSenderCert.serialize())
|
|
}.expect(timeout: 1.0)
|
|
|
|
// Contacts-only sharing?
|
|
write { transaction in
|
|
udManagerImpl.setPhoneNumberSharingMode(.contactsOnly,
|
|
updateStorageService: false,
|
|
transaction: transaction.unwrapGrdbWrite)
|
|
}
|
|
|
|
firstly {
|
|
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict)
|
|
}.done { senderCertificates in
|
|
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress,
|
|
requireSyncAccess: false,
|
|
senderCertificates: senderCertificates)!
|
|
XCTAssertEqual(sendingAccess.senderCertificate.serialize(),
|
|
self.defaultSenderCert.serialize())
|
|
}.expect(timeout: 1.0)
|
|
|
|
// Make sure it resets on clear.
|
|
write { transaction in
|
|
self.profileManager.setProfileKeyData(OWSAES256Key.generateRandom().keyData,
|
|
for: bobRecipientAddress,
|
|
userProfileWriter: .tests,
|
|
transaction: transaction)
|
|
identityManager.clearShouldSharePhoneNumber(with: bobRecipientAddress, transaction: transaction)
|
|
}
|
|
|
|
firstly {
|
|
udManagerImpl.ensureSenderCertificates(certificateExpirationPolicy: .strict)
|
|
}.done { senderCertificates in
|
|
let sendingAccess = self.udManagerImpl.udSendingAccess(forAddress: bobRecipientAddress,
|
|
requireSyncAccess: false,
|
|
senderCertificates: senderCertificates)!
|
|
XCTAssertEqual(sendingAccess.senderCertificate.serialize(),
|
|
self.uuidOnlySenderCert.serialize())
|
|
}.expect(timeout: 1.0)
|
|
}
|
|
|
|
// MARK: - Util
|
|
|
|
func buildSenderCertificate(uuidOnly: Bool) -> SenderCertificate {
|
|
let serverKeys = IdentityKeyPair.generate()
|
|
let serverCert = try! ServerCertificate(keyId: 1,
|
|
publicKey: serverKeys.publicKey,
|
|
trustRoot: trustRoot.privateKey)
|
|
|
|
var senderAddress = try! SealedSenderAddress(e164: nil, uuidString: aliceUuid.uuidString, deviceId: 1)
|
|
if !uuidOnly {
|
|
senderAddress.e164 = aliceE164
|
|
}
|
|
|
|
let expires = NSDate.ows_millisecondTimeStamp() + kWeekInMs
|
|
let senderKeys = IdentityKeyPair.generate()
|
|
return try! SenderCertificate(sender: senderAddress,
|
|
publicKey: senderKeys.publicKey,
|
|
expiration: expires,
|
|
signerCertificate: serverCert,
|
|
signerKey: serverKeys.privateKey)
|
|
}
|
|
}
|