diff --git a/BitwardenShared/Core/Auth/Models/Domain/DeviceAuthKeyMetadata.swift b/BitwardenShared/Core/Auth/Models/Domain/DeviceAuthKeyMetadata.swift index 8e88155541..ba92c65fc9 100644 --- a/BitwardenShared/Core/Auth/Models/Domain/DeviceAuthKeyMetadata.swift +++ b/BitwardenShared/Core/Auth/Models/Domain/DeviceAuthKeyMetadata.swift @@ -4,16 +4,22 @@ import Foundation /// Metadata needed for matching a request to the device passkey before decrypting the secret data. public struct DeviceAuthKeyMetadata: Codable, Equatable, Sendable { - /// The unique identifier of the cipher associated with this device auth key. - public let cipherId: String + /// The date this credential was created. + public let creationDate: Date /// The unique identifier for this credential. public let credentialId: Data - /// The relying party identifier, typically a domain name. + /// The unique identifier of the cipher associated with this device auth key. + public let recordIdentifier: String + + /// The relying party identifier, typically the Bitwarden Web Vault domain name. public let rpId: String - /// The user handle (user ID) as raw data. + /// The display name for the WebAuthn user. + public let userDisplayName: String + + /// The WebAuthn user handle (user ID) as raw data. public let userHandle: Data /// The user's username or login name. @@ -22,21 +28,27 @@ public struct DeviceAuthKeyMetadata: Codable, Equatable, Sendable { /// Creates new device auth key metadata. /// /// - Parameters: - /// - cipherId: The unique identifier of the cipher associated with this device auth key. + /// - creationDate:The date this credential was created. /// - credentialId: The unique identifier for this credential. + /// - recordIdentifier: The unique identifier of this device auth key. /// - rpId: The relying party identifier, typically a domain name. + /// - userDisplayName:The display name for the WebAuthn user. /// - userHandle: The user handle (user ID) as raw data. /// - userName: The user's username or login name. public init( - cipherId: String, + creationDate: Date, credentialId: Data, + recordIdentifier: String, rpId: String, + userDisplayName: String, userHandle: Data, userName: String, ) { - self.cipherId = cipherId + self.creationDate = creationDate self.credentialId = credentialId + self.recordIdentifier = recordIdentifier self.rpId = rpId + self.userDisplayName = userDisplayName self.userHandle = userHandle self.userName = userName } diff --git a/BitwardenShared/Core/Auth/Models/Domain/DeviceAuthKeyRecord.swift b/BitwardenShared/Core/Auth/Models/Domain/DeviceAuthKeyRecord.swift index 3f8c367f28..0ed7689107 100644 --- a/BitwardenShared/Core/Auth/Models/Domain/DeviceAuthKeyRecord.swift +++ b/BitwardenShared/Core/Auth/Models/Domain/DeviceAuthKeyRecord.swift @@ -5,62 +5,37 @@ import Foundation /// Stored key material needed to assert the device auth key passkey. public struct DeviceAuthKeyRecord: Codable, Equatable, Sendable { - /// The unique identifier of the cipher associated with this device auth key. - public let cipherId: EncString - - /// The human-readable name of the cipher. - public let cipherName: EncString - /// The signature counter value, used to detect cloned authenticators. - public let counter: EncString - - /// The date when this credential was created. - public let creationDate: Date + public let counter: UInt32 /// The unique identifier for this credential, assigned by the authenticator. - public let credentialId: EncString - - /// Whether this credential is discoverable (formerly called "resident key"). - /// A discoverable credential can be used without providing a credential ID. - public let discoverable: EncString + public let credentialId: Data /// The HMAC secret, if the credential supports the hmac-secret extension. - public let hmacSecret: EncString? + public let hmacSecret: Data - /// The algorithm used for the key (e.g., "ES256" for ECDSA with SHA-256). - public let keyAlgorithm: EncString + /// The COSE algorithm identifier used for the key (e.g., `-7` for ECDSA with SHA-256). + public let keyAlgorithm: Int64 - /// The elliptic curve used for the key (e.g., "P-256"). - public let keyCurve: EncString - - /// The type of public key credential (typically "public-key"). - public let keyType: EncString + /// The COSE elliptic curve identifier used for the key (e.g., `1` for P-256). + public let keyCurve: Int64 /// The actual key material value. - public let keyValue: EncString + public let keyValue: Data - /// The relying party identifier, typically a domain name. - public let rpId: EncString + /// The relying party identifier, typically the domain name of the Bitwarden Web Vault. + public let rpId: String /// The human-readable name of the relying party. - public let rpName: EncString? - - /// The user's human-readable display name. - public let userDisplayName: EncString? + public let rpName: String /// The user identifier for the relying party. - public let userId: EncString? - - /// The user's username or login name. - public let userName: EncString? + public let userId: Data /// Creates a new device auth key record. /// /// - Parameters: - /// - cipherId: The unique identifier of the cipher associated with this device auth key. - /// - cipherName: The human-readable name of the cipher. /// - counter: The signature counter value, used to detect cloned authenticators. - /// - creationDate: The date when this credential was created. /// - credentialId: The unique identifier for this credential, assigned by the authenticator. /// - discoverable: Whether this credential is discoverable (formerly called "resident key"). /// - hmacSecret: The HMAC secret, if the credential supports the hmac-secret extension. @@ -70,42 +45,26 @@ public struct DeviceAuthKeyRecord: Codable, Equatable, Sendable { /// - keyValue: The actual key material value. /// - rpId: The relying party identifier, typically a domain name. /// - rpName: The human-readable name of the relying party. - /// - userDisplayName: The user's human-readable display name. /// - userId: The user identifier for the relying party. - /// - userName: The user's username or login name. public init( - cipherId: EncString, - cipherName: EncString, - counter: EncString, - creationDate: Date, - credentialId: EncString, - discoverable: EncString, - hmacSecret: EncString?, - keyAlgorithm: EncString, - keyCurve: EncString, - keyType: EncString, - keyValue: EncString, - rpId: EncString, - rpName: EncString?, - userDisplayName: EncString?, - userId: EncString?, - userName: EncString?, + counter: UInt32, + credentialId: Data, + hmacSecret: Data, + keyAlgorithm: Int64, + keyCurve: Int64, + keyValue: Data, + rpId: String, + rpName: String, + userId: Data, ) { - self.cipherId = cipherId - self.cipherName = cipherName self.counter = counter - self.creationDate = creationDate self.credentialId = credentialId - self.discoverable = discoverable self.hmacSecret = hmacSecret self.keyAlgorithm = keyAlgorithm self.keyCurve = keyCurve - self.keyType = keyType self.keyValue = keyValue self.rpId = rpId self.rpName = rpName - self.userDisplayName = userDisplayName self.userId = userId - self.userName = userName } } diff --git a/BitwardenShared/Core/Auth/Services/ClientFido2Service.swift b/BitwardenShared/Core/Auth/Services/ClientFido2Service.swift index 81b97d39f6..4299cc931b 100644 --- a/BitwardenShared/Core/Auth/Services/ClientFido2Service.swift +++ b/BitwardenShared/Core/Auth/Services/ClientFido2Service.swift @@ -15,6 +15,13 @@ protocol ClientFido2Service: AnyObject { credentialStore: Fido2CredentialStore, ) -> ClientFido2ClientProtocol + + /// Returns the `ClientDeviceAuthKeyAuthenticator` to perform Fido2 authenticator tasks using the device auth key. + /// - Parameters: + /// - credentialStore: `DeviceAuthKeyStore` with necessary platform side logic related to credential storage. + /// - Returns: Returns the `ClientDeviceAuthKeyAuthenticator` to perform Fido2 authenticator tasks with the device auth key. + func deviceAuthKeyAuthenticator(credentialStore: DeviceAuthKeyStore) -> ClientDeviceAuthKeyAuthenticatorProtocol + /// Decrypts the `CipherView` Fido2 credentials but returning an array of `Fido2CredentialAutofillView` /// - Parameter cipherView: `CipherView` containing the Fido2 credentials to decrypt. /// - Returns: An array of decrypted Fido2 credentials of type `Fido2CredentialAutofillView`. @@ -41,6 +48,10 @@ extension ClientFido2: ClientFido2Service { client(userInterface: userInterface, credentialStore: credentialStore) as ClientFido2Client } + func deviceAuthKeyAuthenticator(credentialStore: DeviceAuthKeyStore) -> ClientDeviceAuthKeyAuthenticatorProtocol { + deviceAuthKeyAuthenticator(credentialStore: credentialStore) as ClientDeviceAuthKeyAuthenticator + } + func decryptFido2AutofillCredentials(cipher cipherView: CipherView) throws -> [Fido2CredentialAutofillView] { try decryptFido2AutofillCredentials(cipherView: cipherView) } diff --git a/BitwardenShared/Core/Auth/Services/DeviceAuthKeyService.swift b/BitwardenShared/Core/Auth/Services/DeviceAuthKeyService.swift index 4ec43ebe42..271926077b 100644 --- a/BitwardenShared/Core/Auth/Services/DeviceAuthKeyService.swift +++ b/BitwardenShared/Core/Auth/Services/DeviceAuthKeyService.swift @@ -21,7 +21,7 @@ protocol DeviceAuthKeyService { // sourcery: AutoMockable for request: GetAssertionRequest, recordIdentifier: String, userId: String?, - ) async throws -> GetAssertionResult? + ) async throws -> DeviceAuthKeyGetAssertionResult? /// Create device auth key with PRF encryption key. /// @@ -36,7 +36,7 @@ protocol DeviceAuthKeyService { // sourcery: AutoMockable masterPasswordHash: String, overwrite: Bool, userId: String?, - ) async throws -> DeviceAuthKeyRecord + ) async throws /// Deletes the device auth key. /// @@ -75,7 +75,7 @@ extension DeviceAuthKeyService { func assertDeviceAuthKey( for request: GetAssertionRequest, recordIdentifier: String, - ) async throws -> GetAssertionResult? { + ) async throws -> DeviceAuthKeyGetAssertionResult? { try await assertDeviceAuthKey( for: request, recordIdentifier: recordIdentifier, @@ -94,7 +94,7 @@ extension DeviceAuthKeyService { func createDeviceAuthKey( masterPasswordHash: String, overwrite: Bool, - ) async throws -> DeviceAuthKeyRecord { + ) async throws { try await createDeviceAuthKey( masterPasswordHash: masterPasswordHash, overwrite: overwrite, @@ -124,12 +124,20 @@ struct DefaultDeviceAuthKeyService: DeviceAuthKeyService { /// The provider for the active account state. private let activeAccountStateProvider: ActiveAccountStateProvider + private let clientService: ClientService + /// Repository for managing device auth keys in the keychain. private let deviceAuthKeychainRepository: DeviceAuthKeychainRepository /// A subject containing a userId and flag for the presence of the unlock passkey for logged in accounts. private let deviceAuthKeySubject = CurrentValueSubject<[String: Bool], Never>([:]) + private let environmentService: EnvironmentService + + private let stateService: StateService + + private let systemDevice: SystemDevice + // MARK: Initializers /// Creates a new instance of `DefaultDeviceAuthKeyService`. @@ -140,10 +148,18 @@ struct DefaultDeviceAuthKeyService: DeviceAuthKeyService { /// init( activeAccountStateProvider: ActiveAccountStateProvider, + clientService: ClientService, deviceAuthKeychainRepository: DeviceAuthKeychainRepository, + environmentService: EnvironmentService, + stateService: StateService, + systemDevice: SystemDevice, ) { self.activeAccountStateProvider = activeAccountStateProvider + self.clientService = clientService self.deviceAuthKeychainRepository = deviceAuthKeychainRepository + self.environmentService = environmentService + self.stateService = stateService + self.systemDevice = systemDevice } // MARK: Functions @@ -152,30 +168,57 @@ struct DefaultDeviceAuthKeyService: DeviceAuthKeyService { for request: GetAssertionRequest, recordIdentifier: String, userId: String?, - ) async throws -> GetAssertionResult? { - // TODO: PM-26177 to finish building out this stub - throw DeviceAuthKeyError.notImplemented + ) async throws -> DeviceAuthKeyGetAssertionResult? { + let resolvedUserId = try await activeAccountStateProvider.userIdOrActive(userId) + + let store = DefaultDeviceAuthKeyStore( + deviceAuthKeychainRepository: deviceAuthKeychainRepository, + userId: resolvedUserId + ) + let authenticator = try await clientService.platform().fido2().deviceAuthKeyAuthenticator( + credentialStore: store + ) + return try await authenticator.assertDeviceAuthKey(request: request) } func createDeviceAuthKey( masterPasswordHash: String, overwrite: Bool, userId: String?, - ) async throws -> DeviceAuthKeyRecord { + ) async throws { let resolvedUserId = try await activeAccountStateProvider.userIdOrActive(userId) + let account = try await stateService.getAccount(userId: resolvedUserId) + + let store = DefaultDeviceAuthKeyStore( + deviceAuthKeychainRepository: deviceAuthKeychainRepository, + userId: resolvedUserId + ) + let authenticator = try await clientService.platform().fido2().deviceAuthKeyAuthenticator( + credentialStore: store + ) + let secretVerificationRequest = SecretVerificationRequest(masterPassword: masterPasswordHash, otp: nil) + try await authenticator.createDeviceAuthKey( + clientName: "Bitwarden on \(systemDevice.modelIdentifier)", + webVaultUrl: environmentService.webVaultURL.absoluteString, + email: account.profile.email, + secretVerificationRequest: secretVerificationRequest, + kdf: account.kdf.sdkKdf, + ) var curVal = deviceAuthKeySubject.value curVal[resolvedUserId] = true deviceAuthKeySubject.send(curVal) - - // TODO: PM-26177 to finish building out this stub - throw DeviceAuthKeyError.notImplemented } func deleteDeviceAuthKey( userId: String?, ) async throws { let resolvedUserId = try await activeAccountStateProvider.userIdOrActive(userId) + + var curVal = deviceAuthKeySubject.value + curVal[resolvedUserId] = false + deviceAuthKeySubject.send(curVal) + try await deviceAuthKeychainRepository.deleteDeviceAuthKey(userId: resolvedUserId) } @@ -216,3 +259,80 @@ enum DeviceAuthKeyError: Error { /// The requested functionality has not yet been implemented. case notImplemented } + + +class DefaultDeviceAuthKeyStore: DeviceAuthKeyStore { + + private let deviceAuthKeychainRepository: DeviceAuthKeychainRepository + private let userId: String + + init(deviceAuthKeychainRepository: DeviceAuthKeychainRepository, userId: String) { + self.deviceAuthKeychainRepository = deviceAuthKeychainRepository + self.userId = userId + } + + func createRecord(record: BitwardenSdk.DeviceAuthKeyRecord) async throws { + let ourRecord = BitwardenShared.DeviceAuthKeyRecord( + counter: record.counter ?? 0, + credentialId: record.credentialId, + hmacSecret: record.hmacSecret, + keyAlgorithm: record.keyAlg, + keyCurve: record.keyCurve, + keyValue: record.key, + rpId: record.rpId, + rpName: record.rpId, + userId: record.userId, + ) + try await deviceAuthKeychainRepository.setDeviceAuthKeyRecord(record: ourRecord, userId: userId) + } + + func createMetadata(metadata: BitwardenSdk.DeviceAuthKeyMetadata) async throws { + let ourMetadata = BitwardenShared.DeviceAuthKeyMetadata( + creationDate: metadata.creationDate, + credentialId: metadata.credentialId, + recordIdentifier: metadata.recordIdentifier, + rpId: metadata.rpId, + userDisplayName: metadata.userDisplayName, + userHandle: metadata.userHandle, + userName: metadata.userName, + ) + try await deviceAuthKeychainRepository.setDeviceAuthKeyMetadata(metadata: ourMetadata, userId: userId) + } + + func getMetadata() async throws -> BitwardenSdk.DeviceAuthKeyMetadata? { + guard let ourMetadata = try await deviceAuthKeychainRepository.getDeviceAuthKeyMetadata(userId: userId) else { + return nil + } + return BitwardenSdk.DeviceAuthKeyMetadata( + recordIdentifier: ourMetadata.recordIdentifier, + creationDate: ourMetadata.creationDate, + credentialId: ourMetadata.credentialId, + rpId: ourMetadata.rpId, + userName: ourMetadata.userName, + userHandle: ourMetadata.userHandle, + userDisplayName: ourMetadata.userName, + ) + } + + func getRecord() async throws -> BitwardenSdk.DeviceAuthKeyRecord? { + guard let ourRecord = try await deviceAuthKeychainRepository.getDeviceAuthKey(userId: userId) else { + return nil + } + + return BitwardenSdk + .DeviceAuthKeyRecord( + credentialId: Data(base64Encoded: ourRecord.credentialId)!, + key: Data(base64Encoded: ourRecord.keyValue)!, + keyAlg: -7, + keyCurve: 1, + rpId: ourRecord.rpId, + userId: ourRecord.userId, + counter: UInt32(ourRecord.counter), + hmacSecret: ourRecord.hmacSecret, + ) + } + + func deleteRecordAndMetadata() async throws { + try await deviceAuthKeychainRepository.deleteDeviceAuthKey(userId: userId) + } +} diff --git a/BitwardenShared/Core/Auth/Services/DeviceAuthKeychainRepository.swift b/BitwardenShared/Core/Auth/Services/DeviceAuthKeychainRepository.swift index be1aff8a1c..37cc9a15f5 100644 --- a/BitwardenShared/Core/Auth/Services/DeviceAuthKeychainRepository.swift +++ b/BitwardenShared/Core/Auth/Services/DeviceAuthKeychainRepository.swift @@ -27,16 +27,25 @@ protocol DeviceAuthKeychainRepository { // sourcery: AutoMockable /// func getDeviceAuthKeyMetadata(userId: String) async throws -> DeviceAuthKeyMetadata? - /// Stores the device auth key and metadata for a user in the keychain. + /// Stores the device auth key metadata for a user in the keychain. /// /// - Parameters: - /// - record: The device auth key, including the secrets, to store. /// - metadata: The metadata of device auth key to store. /// - userId: The user's ID, used to get back the device auth key later on. /// - func setDeviceAuthKey( - record: DeviceAuthKeyRecord, + func setDeviceAuthKeyMetadata( metadata: DeviceAuthKeyMetadata, userId: String, ) async throws + + /// Stores the device auth key for a user in the keychain. + /// + /// - Parameters: + /// - record: The device auth key, including the secrets, to store. + /// - userId: The user's ID, used to get back the device auth key later on. + /// + func setDeviceAuthKeyRecord( + record: DeviceAuthKeyRecord, + userId: String, + ) async throws } diff --git a/BitwardenShared/Core/Auth/Services/KeychainRepository.swift b/BitwardenShared/Core/Auth/Services/KeychainRepository.swift index e70c88fa6a..1cfc27afb1 100644 --- a/BitwardenShared/Core/Auth/Services/KeychainRepository.swift +++ b/BitwardenShared/Core/Auth/Services/KeychainRepository.swift @@ -597,16 +597,19 @@ extension DefaultKeychainRepository: DeviceAuthKeychainRepository { } } - func setDeviceAuthKey( - record: DeviceAuthKeyRecord, + func setDeviceAuthKeyMetadata( metadata: DeviceAuthKeyMetadata, userId: String, ) async throws { - // We want to set metadata last because that's what's used to determine if we're in a - // consistent state. - try await setValue(record, for: .deviceAuthKey(userId: userId)) try await setValue(metadata, for: .deviceAuthKeyMetadata(userId: userId)) } + + func setDeviceAuthKeyRecord( + record: DeviceAuthKeyRecord, + userId: String, + ) async throws { + try await setValue(record, for: .deviceAuthKey(userId: userId)) + } } // MARK: UserSessionKeychainRepository diff --git a/BitwardenShared/Core/Autofill/Extensions/BitwardenSdk+Autofill.swift b/BitwardenShared/Core/Autofill/Extensions/BitwardenSdk+Autofill.swift index b2d0b0e405..a0c78675b2 100644 --- a/BitwardenShared/Core/Autofill/Extensions/BitwardenSdk+Autofill.swift +++ b/BitwardenShared/Core/Autofill/Extensions/BitwardenSdk+Autofill.swift @@ -2,6 +2,7 @@ import AuthenticationServices import BitwardenSdk +import CryptoKit // MARK: - GetAssertionRequest @@ -52,6 +53,15 @@ extension GetAssertionRequest { } } +// MARK: - ASAuthorizationPublicKeyCredentialPRFAssertionOutput + +@available(iOS 18.0, *) +extension ASAuthorizationPublicKeyCredentialPRFAssertionOutput { + init(getAssertionPrfOutput prf: GetAssertionPrfOutput) { + self.init(first: SymmetricKey(data: prf.results.first), second: prf.results.second.map(SymmetricKey.init)) + } +} + // MARK: - ASPasskeyAssertionCredential @available(iOS 17.0, *) @@ -77,7 +87,46 @@ extension ASPasskeyAssertionCredential { clientDataHash: clientDataHash, authenticatorData: assertionResult.authenticatorData, credentialID: assertionResult.credentialId, - extensionOutput: nil, // TODO: PM-26177 once SDK is updated for full PRF support we can include this + extensionOutput: ASPasskeyAssertionCredentialExtensionOutput( + getAssertionExtensionsOutput: assertionResult.extensions + ), + ) + } else { + self.init( + userHandle: assertionResult.userHandle, + relyingParty: rpId, + signature: assertionResult.signature, + clientDataHash: clientDataHash, + authenticatorData: assertionResult.authenticatorData, + credentialID: assertionResult.credentialId, + ) + } + } + + /// Creates a passkey assertion credential from a Bitwarden SDK device auth key assertion result. + /// + /// This convenience initializer creates an `ASPasskeyAssertionCredential` from the result + /// of a FIDO2 assertion operation performed by the Bitwarden SDK. It handles compatibility + /// across iOS versions, including extension output support on iOS 18+. + /// + /// - Parameters: + /// - assertionResult: The result from the Bitwarden SDK's `assertDeviceAuthKey` operation, + /// containing the signature, authenticator data, and credential ID. + /// - rpId: The relying party identifier for the credential. + /// - clientDataHash: The hash of the client data JSON from the authentication request. + /// + convenience init(assertionResult: DeviceAuthKeyGetAssertionResult, rpId: String, clientDataHash: Data) { + if #available(iOSApplicationExtension 18.0, *) { + self.init( + userHandle: assertionResult.userHandle, + relyingParty: rpId, + signature: assertionResult.signature, + clientDataHash: clientDataHash, + authenticatorData: assertionResult.authenticatorData, + credentialID: assertionResult.credentialId, + extensionOutput: ASPasskeyAssertionCredentialExtensionOutput( + getAssertionExtensionsOutput: assertionResult.extensions + ), ) } else { self.init( @@ -92,6 +141,18 @@ extension ASPasskeyAssertionCredential { } } +// MARK: - ASPasskeyAssertionCredentialOutput + +@available(iOS 18.0, *) +extension ASPasskeyAssertionCredentialExtensionOutput { + init(getAssertionExtensionsOutput extensions: GetAssertionExtensionsOutput) { + self.init( + largeBlob: nil, + prf: extensions.prf.map(ASAuthorizationPublicKeyCredentialPRFAssertionOutput.init), + ) + } +} + // MARK: - ASPasskeyCredentialIdentity @available(iOS 17.0, *) @@ -102,18 +163,29 @@ extension ASPasskeyCredentialIdentity { userName: metadata.userName, credentialID: metadata.credentialId, userHandle: metadata.userHandle, - recordIdentifier: metadata.cipherId, + recordIdentifier: metadata.recordIdentifier, ) } } +// MARK: - MakeCredentialExtensionsInput + +extension BitwardenSdk.MakeCredentialExtensionsInput: @retroactive CustomDebugStringConvertible { + public var debugDescription: String { + let eval = self.prf?.eval?.debugDescription ?? "nil" + return [ + "prf -> eval: \(eval)" + ].joined(separator: "\n") + } +} + // MARK: - MakeCredentialRequest extension BitwardenSdk.MakeCredentialRequest: @retroactive CustomDebugStringConvertible { public var debugDescription: String { let rpName = rp.name ?? "nil" let excludeList = excludeList?.description ?? "nil" - let extensions = extensions?.description ?? "nil" + let extensions = extensions?.debugDescription ?? "nil" return [ "ClientDataHash: \(clientDataHash.asHexString())", @@ -143,6 +215,28 @@ extension BitwardenSdk.MakeCredentialResult: @retroactive CustomDebugStringConve } } +// MARK: - PrfInputValues + +extension BitwardenSdk.PrfInputValues: @retroactive CustomDebugStringConvertible { + public var debugDescription: String { + return [ + "first: \(self.first.asHexString())", + "second: \(self.second?.asHexString() ?? "nil")" + ].joined(separator: "\n") + } +} + +// MARK: - PrfOutputValues + +extension BitwardenSdk.PrfOutputValues: @retroactive CustomDebugStringConvertible { + public var debugDescription: String { + return [ + "first: \(self.first.asHexString())", + "second: \(self.second?.asHexString() ?? "nil")" + ].joined(separator: "\n") + } +} + // MARK: - Uv extension BitwardenSdk.Uv { diff --git a/BitwardenShared/Core/Autofill/Services/AutofillCredentialService.swift b/BitwardenShared/Core/Autofill/Services/AutofillCredentialService.swift index 582ac5cccb..667677048c 100644 --- a/BitwardenShared/Core/Autofill/Services/AutofillCredentialService.swift +++ b/BitwardenShared/Core/Autofill/Services/AutofillCredentialService.swift @@ -435,7 +435,7 @@ class DefaultAutofillCredentialService { if await configService.getFeatureFlag(.deviceAuthKey), let recordIdentifier = credentialIdentity.recordIdentifier, let deviceAuthKeyMetadata = try await deviceAuthKeyService.getDeviceAuthKeyMetadata(userId: userId), - recordIdentifier == deviceAuthKeyMetadata.cipherId { + recordIdentifier == deviceAuthKeyMetadata.recordIdentifier { // The credential request is for the device auth key, so we serve that up. if let deviceResult = try await deviceAuthKeyService.assertDeviceAuthKey( for: request, diff --git a/BitwardenShared/Core/Platform/Services/CryptoClientProtocol+Extensions.swift b/BitwardenShared/Core/Platform/Services/CryptoClientProtocol+Extensions.swift index e51471bb2c..46e04054c3 100644 --- a/BitwardenShared/Core/Platform/Services/CryptoClientProtocol+Extensions.swift +++ b/BitwardenShared/Core/Platform/Services/CryptoClientProtocol+Extensions.swift @@ -30,6 +30,7 @@ extension CryptoClientProtocol { email: account.profile.email, accountCryptographicState: accountCryptographicState, method: method, + upgradeToken: nil, ), ) } diff --git a/BitwardenShared/Core/Platform/Services/ServiceContainer.swift b/BitwardenShared/Core/Platform/Services/ServiceContainer.swift index 23b58a635f..1530ef9ed7 100644 --- a/BitwardenShared/Core/Platform/Services/ServiceContainer.swift +++ b/BitwardenShared/Core/Platform/Services/ServiceContainer.swift @@ -913,7 +913,11 @@ public class ServiceContainer: Services { // swiftlint:disable:this type_body_le let deviceAuthKeyService = DefaultDeviceAuthKeyService( activeAccountStateProvider: stateService, + clientService: clientService, deviceAuthKeychainRepository: keychainRepository, + environmentService: environmentService, + stateService: stateService, + systemDevice: UIDevice.current, ) let credentialIdentityFactory = DefaultCredentialIdentityFactory() diff --git a/BitwardenShared/Core/Vault/Repositories/SdkCipherRepository.swift b/BitwardenShared/Core/Vault/Repositories/SdkCipherRepository.swift index e7268a5fcd..1c1b2b1879 100644 --- a/BitwardenShared/Core/Vault/Repositories/SdkCipherRepository.swift +++ b/BitwardenShared/Core/Vault/Repositories/SdkCipherRepository.swift @@ -42,10 +42,28 @@ final class SdkCipherRepository: BitwardenSdk.CipherRepository { try await cipherDataStore.deleteCipher(id: id, userId: userId) } + func removeBulk(keys: [String]) async throws { + // TODO: fake implementation stub for SDK update + for key in keys { + try await remove(id: key) + } + } + + func removeAll() async throws { + try await cipherDataStore.deleteAllCiphers(userId: userId) + } + func set(id: String, value: BitwardenSdk.Cipher) async throws { guard id == value.id else { throw BitwardenError.dataError("CipherRepository: Trying to update a cipher with mismatch IDs") } try await cipherDataStore.upsertCipher(value, userId: userId) } + + func setBulk(values: [String: BitwardenSdk.Cipher]) async throws { + // TODO: Fake implementation stub for SDK update + for (id, value) in values { + try await set(id: id, value: value) + } + } }