diff --git a/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift b/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift index 2b96dbd..39708d9 100644 --- a/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift +++ b/Sources/Packages/Sources/SmartCardSecretKit/SmartCardStore.swift @@ -23,6 +23,9 @@ extension SmartCard { public var isAvailable: Bool { state.isAvailable } + @MainActor public var smartcardTokenID: String? { + state.tokenID + } public let id = UUID() @MainActor public var name: String { @@ -35,7 +38,7 @@ extension SmartCard { /// Initializes a Store. public init() { Task { @MainActor in - if let tokenID = state.tokenID { + if let tokenID = smartcardTokenID{ state.isAvailable = true state.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: tokenID) } @@ -172,88 +175,6 @@ extension SmartCard.Store { } - -// MARK: Smart Card specific encryption/decryption/verification -extension SmartCard.Store { - - /// Encrypts a payload with a specified key. - /// - Parameters: - /// - data: The payload to encrypt. - /// - secret: The secret to encrypt with. - /// - Returns: The encrypted data. - /// - Warning: Encryption functions are deliberately only exposed on a library level, and are not exposed in Secretive itself to prevent users from data loss. Any pull requests which expose this functionality in the app will not be merged. - public func encrypt(data: Data, with secret: SecretType) throws -> Data { - let context = LAContext() - context.localizedReason = String(localized: .authContextRequestEncryptDescription(secretName: secret.name)) - context.localizedCancelTitle = String(localized: .authContextRequestDenyButton) - let attributes = KeychainDictionary([ - kSecAttrKeyType: secret.algorithm.secAttrKeyType, - kSecAttrKeySizeInBits: secret.keySize, - kSecAttrKeyClass: kSecAttrKeyClassPublic, - kSecUseAuthenticationContext: context - ]) - var encryptError: SecurityError? - let untyped: CFTypeRef? = SecKeyCreateWithData(secret.publicKey as CFData, attributes, &encryptError) - guard let untypedSafe = untyped else { - throw KeychainError(statusCode: errSecSuccess) - } - let key = untypedSafe as! SecKey - guard let signature = SecKeyCreateEncryptedData(key, encryptionAlgorithm(for: secret), data as CFData, &encryptError) else { - throw SigningError(error: encryptError) - } - return signature as Data - } - - /// Decrypts a payload with a specified key. - /// - Parameters: - /// - data: The payload to decrypt. - /// - secret: The secret to decrypt with. - /// - Returns: The decrypted data. - /// - Warning: Encryption functions are deliberately only exposed on a library level, and are not exposed in Secretive itself to prevent users from data loss. Any pull requests which expose this functionality in the app will not be merged. - public func decrypt(data: Data, with secret: SecretType) async throws -> Data { - guard let tokenID = await state.tokenID else { fatalError() } - let context = LAContext() - context.localizedReason = String(localized: .authContextRequestDecryptDescription(secretName: secret.name)) - context.localizedCancelTitle = String(localized: .authContextRequestDenyButton) - let attributes = KeychainDictionary([ - kSecClass: kSecClassKey, - kSecAttrKeyClass: kSecAttrKeyClassPrivate, - kSecAttrApplicationLabel: secret.id as CFData, - kSecAttrTokenID: tokenID, - kSecUseAuthenticationContext: context, - kSecReturnRef: true - ]) - var untyped: CFTypeRef? - let status = SecItemCopyMatching(attributes, &untyped) - if status != errSecSuccess { - throw KeychainError(statusCode: status) - } - guard let untypedSafe = untyped else { - throw KeychainError(statusCode: errSecSuccess) - } - let key = untypedSafe as! SecKey - var encryptError: SecurityError? - guard let signature = SecKeyCreateDecryptedData(key, encryptionAlgorithm(for: secret), data as CFData, &encryptError) else { - throw SigningError(error: encryptError) - } - return signature as Data - } - - private func encryptionAlgorithm(for secret: SecretType) -> SecKeyAlgorithm { - switch (secret.algorithm, secret.keySize) { - case (.ellipticCurve, 256): - return .eciesEncryptionCofactorVariableIVX963SHA256AESGCM - case (.ellipticCurve, 384): - return .eciesEncryptionCofactorVariableIVX963SHA384AESGCM - case (.rsa, 1024), (.rsa, 2048): - return .rsaEncryptionOAEPSHA512AESGCM - default: - fatalError() - } - } - -} - extension TKTokenWatcher { /// All available tokens, excluding the Secure Enclave.