Buckets:
localhost1978/localhost / LOCALHOST1978 /Modules /SSK_Foundation /Tests /SSK_FoundationTests /VaultKeyManagerTests.swift
| import XCTest | |
| import CryptoKit | |
| @testable import SSK_Foundation | |
| final class VaultKeyManagerTests: XCTestCase { | |
| var keyManager: VaultKeyManager! | |
| override func setUp() async throws { | |
| keyManager = VaultKeyManager() | |
| let store = GenericPasswordStore() | |
| try? store.deleteKey(account: KeychainConstants.Accounts.vaultKey, service: KeychainConstants.Services.vault) | |
| try? store.deleteKey(account: KeychainConstants.Accounts.vaultKey, service: KeychainConstants.Accounts.vaultKey) | |
| try? store.deleteKey(account: KeychainConstants.Accounts.vaultKey) | |
| } | |
| override func tearDown() async throws { | |
| if let km = keyManager { | |
| await km.scrubKeys() | |
| } | |
| let store = GenericPasswordStore() | |
| try? store.deleteKey(account: KeychainConstants.Accounts.vaultKey, service: KeychainConstants.Services.vault) | |
| try? store.deleteKey(account: KeychainConstants.Accounts.vaultKey, service: KeychainConstants.Accounts.vaultKey) | |
| try? store.deleteKey(account: KeychainConstants.Accounts.vaultKey) | |
| keyManager = nil | |
| } | |
| func testGetOrCreateSecureKeyCachesAndScrubs() async throws { | |
| // Fetch key, which should cache it | |
| let key1 = try await keyManager.getOrCreateSecureKey() | |
| let key2 = try await keyManager.getOrCreateSecureKey() | |
| // Ensure the exact same reference / values are returned due to caching | |
| let key1Bytes = key1.withUnsafeBytes { Array($0) } | |
| let key2Bytes = key2.withUnsafeBytes { Array($0) } | |
| XCTAssertEqual(key1Bytes, key2Bytes, "Subsequent calls should return the cached secure bytes.") | |
| // Scrub the keys | |
| await keyManager.scrubKeys() | |
| // After scrubbing, it should fetch from keychain again, but it should be the same key | |
| // since we didn't delete it from keychain in the middle of the test | |
| let key3 = try await keyManager.getOrCreateSecureKey() | |
| let key3Bytes = key3.withUnsafeBytes { Array($0) } | |
| XCTAssertEqual(key1Bytes, key3Bytes, "Fetching after scrub should restore from keychain.") | |
| } | |
| func testGetOrCreateVaultPasskeyString() async throws { | |
| let passkey = try await keyManager.getOrCreateVaultPasskey() | |
| XCTAssertFalse(passkey.isEmpty, "Passkey string should not be empty") | |
| // Validate it's hex by trying to decode it | |
| let isHex = passkey.allSatisfy { $0.isHexDigit } | |
| XCTAssertTrue(isHex, "Passkey should be a valid hex string") | |
| } | |
| func testPerVaultKeysAreStableAndDistinct() async throws { | |
| let firstID = VaultID() | |
| let secondID = VaultID() | |
| let firstAccount = "\(KeychainConstants.Accounts.vaultKey).\(firstID.description)" | |
| let secondAccount = "\(KeychainConstants.Accounts.vaultKey).\(secondID.description)" | |
| let store = GenericPasswordStore() | |
| defer { | |
| try? store.deleteKey( | |
| account: firstAccount, | |
| service: KeychainConstants.Services.vault | |
| ) | |
| try? store.deleteKey( | |
| account: secondAccount, | |
| service: KeychainConstants.Services.vault | |
| ) | |
| } | |
| let first = try await keyManager.getOrCreateVaultPasskey(for: firstID) | |
| let firstAgain = try await keyManager.getOrCreateVaultPasskey(for: firstID) | |
| let second = try await keyManager.getOrCreateVaultPasskey(for: secondID) | |
| XCTAssertEqual(first, firstAgain) | |
| XCTAssertNotEqual(first, second) | |
| XCTAssertEqual(first.count, 64) | |
| XCTAssertEqual(second.count, 64) | |
| } | |
| func testMissingPerVaultKeyIsNotSilentlyReplaced() async throws { | |
| let vaultID = VaultID() | |
| let account = "\(KeychainConstants.Accounts.vaultKey).\(vaultID.description)" | |
| let store = GenericPasswordStore() | |
| defer { | |
| try? store.deleteKey( | |
| account: account, | |
| service: KeychainConstants.Services.vault | |
| ) | |
| } | |
| let missingKey = try await keyManager.vaultPasskey(for: vaultID) | |
| XCTAssertNil(missingKey) | |
| _ = try await keyManager.getOrCreateVaultPasskey(for: vaultID) | |
| await keyManager.scrubKeys() | |
| let restoredKey = try await keyManager.vaultPasskey(for: vaultID) | |
| XCTAssertNotNil(restoredKey) | |
| } | |
| func testTypedRegistryDoesNotReadLegacyURLRegistry() async throws { | |
| let suffix = UUID().uuidString | |
| let service = "com.ssk.test.registry.\(suffix)" | |
| let keyAccount = "ssk.test.key.\(suffix)" | |
| let registryAccount = "ssk.test.registry.\(suffix)" | |
| let manager = VaultKeyManager( | |
| keychainAccount: keyAccount, | |
| registryAccount: registryAccount, | |
| keychainService: service, | |
| accessGroup: nil | |
| ) | |
| let store = GenericPasswordStore() | |
| defer { | |
| try? store.deleteKey(account: registryAccount, service: service) | |
| try? store.deleteKey( | |
| account: "\(registryAccount).records.v1", | |
| service: service | |
| ) | |
| } | |
| try await manager.saveKnownVaults([URL(string: "ssk://legacy-vault")!]) | |
| let typedRecords = try await manager.getVaultRecords() | |
| XCTAssertEqual(typedRecords, []) | |
| } | |
| func testTypedRegistryRoundTripPreservesMultipleVaultIdentities() async throws { | |
| let suffix = UUID().uuidString | |
| let service = "com.ssk.test.records.\(suffix)" | |
| let registryAccount = "ssk.test.records.registry.\(suffix)" | |
| let manager = VaultKeyManager( | |
| keychainAccount: "ssk.test.records.key.\(suffix)", | |
| registryAccount: registryAccount, | |
| keychainService: service, | |
| accessGroup: nil | |
| ) | |
| let store = GenericPasswordStore() | |
| defer { | |
| try? store.deleteKey( | |
| account: "\(registryAccount).records.v1", | |
| service: service | |
| ) | |
| } | |
| let records = [ | |
| VaultRecord( | |
| id: VaultID(), | |
| imageURL: URL(fileURLWithPath: "/private/tmp/one.sparsebundle"), | |
| apfsVolumeUUID: UUID(), | |
| volumeName: "One", | |
| maximumSizeGB: 20, | |
| createdAt: Date(timeIntervalSince1970: 1) | |
| ), | |
| VaultRecord( | |
| id: VaultID(), | |
| imageURL: URL(fileURLWithPath: "/private/tmp/two.sparsebundle"), | |
| apfsVolumeUUID: UUID(), | |
| volumeName: "Two", | |
| maximumSizeGB: 40, | |
| createdAt: Date(timeIntervalSince1970: 2) | |
| ), | |
| ] | |
| try await manager.saveVaultRecords(records) | |
| await manager.scrubKeys() | |
| let restoredRecords = try await manager.getVaultRecords() | |
| XCTAssertEqual(restoredRecords, records) | |
| } | |
| func testKeychainConstantsValues() { | |
| XCTAssertEqual(KeychainConstants.AccessGroups.vault, "com.sovereignsurvivalkit.vault") | |
| XCTAssertEqual(KeychainConstants.AccessGroups.prefixed(with: "ABCDE12345"), "ABCDE12345.com.sovereignsurvivalkit.vault") | |
| XCTAssertEqual(KeychainConstants.AccessGroups.prefixed(with: "ABCDE12345."), "ABCDE12345.com.sovereignsurvivalkit.vault") | |
| XCTAssertEqual(KeychainConstants.Services.vault, "com.sovereignsurvivalkit.vault") | |
| XCTAssertEqual(KeychainConstants.Services.mesh, "com.sovereignsurvivalkit.mesh") | |
| XCTAssertEqual(KeychainConstants.Services.default, "com.sovereignsurvivalkit") | |
| XCTAssertEqual(KeychainConstants.Accounts.vaultKey, "ssk.vault.key") | |
| XCTAssertEqual(KeychainConstants.Accounts.vaultRegistry, "ssk.vault.registry") | |
| XCTAssertEqual(KeychainConstants.Accounts.meshPeerRegistry, "ssk.mesh.peerregistry") | |
| XCTAssertEqual(KeychainConstants.Accounts.vpnSharedSecret, "ssk.mesh.sharedsecret") | |
| XCTAssertEqual(KeychainConstants.Accounts.tlsIdentity, "com.sovereignsurvivalkit.mesh.tls-identity") | |
| } | |
| func testGenericPasswordStoreWithConstants() throws { | |
| let store = GenericPasswordStore() | |
| let testAccount = "test.generic.account" | |
| let testService = KeychainConstants.Services.vault | |
| let testData = "secure_test_payload".data(using: .utf8)! | |
| defer { | |
| try? store.deleteKey(account: testAccount, service: testService) | |
| } | |
| try store.storeData(testData, account: testAccount, service: testService) | |
| let retrievedData = try store.readData(account: testAccount, service: testService) | |
| XCTAssertEqual(retrievedData, testData) | |
| } | |
| func testVaultKeyManagerCustomConfigurationInjection() async throws { | |
| let customKeychain = SSKFoundationConfiguration.KeychainConfig( | |
| vaultService: "com.ssk.test.custom.vault", | |
| vaultAccount: "ssk.test.custom.key", | |
| vaultRegistryAccount: "ssk.test.custom.registry" | |
| ) | |
| let customConfig = SSKFoundationConfiguration(keychain: customKeychain) | |
| let customManager = VaultKeyManager(configuration: customConfig) | |
| defer { | |
| let store = GenericPasswordStore() | |
| try? store.deleteKey(account: "ssk.test.custom.key", service: "com.ssk.test.custom.vault") | |
| try? store.deleteKey(account: "ssk.test.custom.registry", service: "com.ssk.test.custom.vault") | |
| } | |
| let passkey = try await customManager.getOrCreateVaultPasskey() | |
| XCTAssertFalse(passkey.isEmpty) | |
| let testVaults = [URL(string: "ssk://custom.vault.node")!] | |
| try await customManager.saveKnownVaults(testVaults) | |
| let loaded = try await customManager.getKnownVaults() | |
| XCTAssertEqual(loaded, testVaults) | |
| } | |
| func testVPNKeyManagerCustomConfigurationInjection() async throws { | |
| let customKeychain = SSKFoundationConfiguration.KeychainConfig( | |
| meshService: "com.ssk.test.custom.mesh", | |
| vpnSharedSecretAccount: "ssk.test.custom.vpnsecret" | |
| ) | |
| let customConfig = SSKFoundationConfiguration(keychain: customKeychain) | |
| let customVPNManager = VPNKeyManager(configuration: customConfig) | |
| defer { | |
| let store = GenericPasswordStore() | |
| try? store.deleteKey(account: "ssk.test.custom.vpnsecret", service: "com.ssk.test.custom.mesh") | |
| } | |
| let secretData = try await customVPNManager.generateAndSaveSharedSecret() | |
| XCTAssertFalse(secretData.isEmpty) | |
| let ref = try await customVPNManager.getExistingSecretReference() | |
| XCTAssertNotNil(ref) | |
| XCTAssertEqual(secretData, ref) | |
| } | |
| } | |
Xet Storage Details
- Size:
- 10.6 kB
- Xet hash:
- 8543b0e309cc091dc3b806fde2508fcffbcf9d96839a6ebd59f716fc468a1a20
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.