mirror of
https://github.com/bitwarden/ios.git
synced 2025-12-11 04:34:55 -06:00
129 lines
5.5 KiB
Swift
129 lines
5.5 KiB
Swift
import AuthenticatorBridgeKit
|
|
import AuthenticatorBridgeKitMocks
|
|
import CryptoKit
|
|
import Foundation
|
|
import XCTest
|
|
|
|
final class SharedKeychainStorageTests: BitwardenTestCase {
|
|
// MARK: Properties
|
|
|
|
let accessGroup = "group.com.example.bitwarden"
|
|
var keychainService: MockSharedKeychainService!
|
|
var subject: SharedKeychainStorage!
|
|
|
|
// MARK: Setup & Teardown
|
|
|
|
override func setUp() {
|
|
keychainService = MockSharedKeychainService()
|
|
subject = DefaultSharedKeychainStorage(
|
|
keychainService: keychainService,
|
|
sharedAppGroupIdentifier: accessGroup
|
|
)
|
|
}
|
|
|
|
override func tearDown() {
|
|
keychainService = nil
|
|
subject = nil
|
|
}
|
|
|
|
// MARK: Tests
|
|
|
|
/// Verify that `deleteValue(for:)` issues a delete with the correct search attributes specified.
|
|
///
|
|
func test_deleteValue_success() async throws {
|
|
try await subject.deleteValue(for: .authenticatorKey)
|
|
|
|
let queries = try XCTUnwrap(keychainService.deleteQueries as? [[CFString: Any]])
|
|
XCTAssertEqual(queries.count, 1)
|
|
|
|
let query = try XCTUnwrap(queries.first)
|
|
try XCTAssertEqual(XCTUnwrap(query[kSecAttrAccessGroup] as? String), accessGroup)
|
|
try XCTAssertEqual(XCTUnwrap(query[kSecAttrAccessible] as? String),
|
|
String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly))
|
|
try XCTAssertEqual(XCTUnwrap(query[kSecAttrAccount] as? String),
|
|
SharedKeychainItem.authenticatorKey.unformattedKey)
|
|
try XCTAssertEqual(XCTUnwrap(query[kSecClass] as? String),
|
|
String(kSecClassGenericPassword))
|
|
}
|
|
|
|
/// Verify that `getValue(for:)` returns a value successfully when one is set. Additionally, verify the
|
|
/// search attributes are specified correctly.
|
|
///
|
|
func test_getValue_success() async throws {
|
|
let key = SymmetricKey(size: .bits256)
|
|
let data = key.withUnsafeBytes { Data(Array($0)) }
|
|
|
|
keychainService.setSearchResultData(data)
|
|
|
|
let returnData: Data = try await subject.getValue(for: .authenticatorKey)
|
|
XCTAssertEqual(returnData, data)
|
|
|
|
let query = try XCTUnwrap(keychainService.searchQuery as? [CFString: Any])
|
|
try XCTAssertEqual(XCTUnwrap(query[kSecAttrAccessGroup] as? String), accessGroup)
|
|
try XCTAssertEqual(XCTUnwrap(query[kSecAttrAccessible] as? String),
|
|
String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly))
|
|
try XCTAssertEqual(XCTUnwrap(query[kSecAttrAccount] as? String),
|
|
SharedKeychainItem.authenticatorKey.unformattedKey)
|
|
try XCTAssertEqual(XCTUnwrap(query[kSecClass] as? String), String(kSecClassGenericPassword))
|
|
try XCTAssertEqual(XCTUnwrap(query[kSecMatchLimit] as? String), String(kSecMatchLimitOne))
|
|
try XCTAssertTrue(XCTUnwrap(query[kSecReturnAttributes] as? Bool))
|
|
try XCTAssertTrue(XCTUnwrap(query[kSecReturnData] as? Bool))
|
|
}
|
|
|
|
/// Verify that `getAuthenticatorKey()` fails with a `keyNotFound` error when an unexpected
|
|
/// result is returned instead of the key data from the keychain
|
|
///
|
|
func test_getValue_badResult() async throws {
|
|
let key = SharedKeychainItem.accountAutoLogout(userId: "1")
|
|
let error = SharedKeychainServiceError.keyNotFound(key)
|
|
keychainService.searchResult = .success([kSecValueData as String: NSObject()] as AnyObject)
|
|
|
|
await assertAsyncThrows(error: error) {
|
|
let _: Data = try await subject.getValue(for: key)
|
|
}
|
|
}
|
|
|
|
/// Verify that `getValue(for:)` fails with a `keyNotFound` error when a nil
|
|
/// result is returned instead of the key data from the keychain
|
|
///
|
|
func test_getValue_nilResult() async throws {
|
|
let key = SharedKeychainItem.accountAutoLogout(userId: "1")
|
|
let error = SharedKeychainServiceError.keyNotFound(key)
|
|
keychainService.searchResult = .success(nil)
|
|
|
|
await assertAsyncThrows(error: error) {
|
|
let _: Data = try await subject.getValue(for: key)
|
|
}
|
|
}
|
|
|
|
/// Verify that `getValue(for:)` fails with an error when the Authenticator key is not
|
|
/// present in the keychain
|
|
///
|
|
func test_getAuthenticatorKey_keyNotFound() async throws {
|
|
let error = SharedKeychainServiceError.keyNotFound(SharedKeychainItem.authenticatorKey)
|
|
keychainService.searchResult = .failure(error)
|
|
|
|
await assertAsyncThrows(error: error) {
|
|
let _: Data = try await subject.getValue(for: .authenticatorKey)
|
|
}
|
|
}
|
|
|
|
/// Verify that `setValue(_:for:)` sets a value with the correct search attributes specified.
|
|
///
|
|
func test_setAuthenticatorKey_success() async throws {
|
|
let key = SymmetricKey(size: .bits256)
|
|
let data = key.withUnsafeBytes { Data(Array($0)) }
|
|
try await subject.setValue(data, for: .authenticatorKey)
|
|
|
|
let attributes = try XCTUnwrap(keychainService.addAttributes as? [CFString: Any])
|
|
try XCTAssertEqual(XCTUnwrap(attributes[kSecAttrAccessGroup] as? String), accessGroup)
|
|
try XCTAssertEqual(XCTUnwrap(attributes[kSecAttrAccessible] as? String),
|
|
String(kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly))
|
|
try XCTAssertEqual(XCTUnwrap(attributes[kSecAttrAccount] as? String),
|
|
SharedKeychainItem.authenticatorKey.unformattedKey)
|
|
try XCTAssertEqual(XCTUnwrap(attributes[kSecClass] as? String),
|
|
String(kSecClassGenericPassword))
|
|
try XCTAssertEqual(XCTUnwrap(attributes[kSecValueData] as? Data), data)
|
|
}
|
|
}
|