Return created key.

This commit is contained in:
Max Goedjen 2025-08-26 22:31:53 -07:00
parent c37d0c0cba
commit 225a610801
No known key found for this signature in database
3 changed files with 18 additions and 8 deletions

View File

@ -61,20 +61,21 @@ open class AnySecretStore: SecretStore, @unchecked Sendable {
public final class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable, @unchecked Sendable { public final class AnySecretStoreModifiable: AnySecretStore, SecretStoreModifiable, @unchecked Sendable {
private let _create: @Sendable (String, Attributes) async throws -> Void private let _create: @Sendable (String, Attributes) async throws -> SecretType
private let _delete: @Sendable (AnySecret) async throws -> Void private let _delete: @Sendable (AnySecret) async throws -> Void
private let _update: @Sendable (AnySecret, String, Attributes) async throws -> Void private let _update: @Sendable (AnySecret, String, Attributes) async throws -> Void
private let _supportedKeyTypes: @Sendable () -> [KeyType] private let _supportedKeyTypes: @Sendable () -> [KeyType]
public init<SecretStoreType>(_ secretStore: SecretStoreType) where SecretStoreType: SecretStoreModifiable { public init<SecretStoreType>(_ secretStore: SecretStoreType) where SecretStoreType: SecretStoreModifiable {
_create = { try await secretStore.create(name: $0, attributes: $1) } _create = { try await secretStore.create(name: $0, attributes: $1) as! SecretType }
_delete = { try await secretStore.delete(secret: $0.base as! SecretStoreType.SecretType) } _delete = { try await secretStore.delete(secret: $0.base as! SecretStoreType.SecretType) }
_update = { try await secretStore.update(secret: $0.base as! SecretStoreType.SecretType, name: $1, attributes: $2) } _update = { try await secretStore.update(secret: $0.base as! SecretStoreType.SecretType, name: $1, attributes: $2) }
_supportedKeyTypes = { secretStore.supportedKeyTypes } _supportedKeyTypes = { secretStore.supportedKeyTypes }
super.init(secretStore) super.init(secretStore)
} }
public func create(name: String, attributes: Attributes) async throws { @discardableResult
public func create(name: String, attributes: Attributes) async throws -> SecretType {
try await _create(name, attributes) try await _create(name, attributes)
} }

View File

@ -46,8 +46,9 @@ public protocol SecretStoreModifiable<SecretType>: SecretStore {
/// Creates a new ``Secret`` in the store. /// Creates a new ``Secret`` in the store.
/// - Parameters: /// - Parameters:
/// - name: The user-facing name for the ``Secret``. /// - name: The user-facing name for the ``Secret``.
/// - attributes: A struct describing the options for creating the key. /// - attributes: A struct describing the options for creating the key.'
func create(name: String, attributes: Attributes) async throws @discardableResult
func create(name: String, attributes: Attributes) async throws -> SecretType
/// Deletes a Secret in the store. /// Deletes a Secret in the store.
/// - Parameters: /// - Parameters:

View File

@ -121,21 +121,26 @@ extension SecureEnclave {
let dataRep: Data let dataRep: Data
switch (attributes.keyType.algorithm, attributes.keyType.size) { switch (attributes.keyType.algorithm, attributes.keyType.size) {
case (.ecdsa, 256): case (.ecdsa, 256):
let publicKey: Data
let created = try CryptoKit.SecureEnclave.P256.Signing.PrivateKey(accessControl: access!) let created = try CryptoKit.SecureEnclave.P256.Signing.PrivateKey(accessControl: access!)
dataRep = created.dataRepresentation dataRep = created.dataRepresentation
case (.mldsa, 65): case (.mldsa, 65):
publicKey = created.publicKey.x963Representation
guard #available(macOS 26.0, *) else { throw Attributes.UnsupportedOptionError() } guard #available(macOS 26.0, *) else { throw Attributes.UnsupportedOptionError() }
let created = try CryptoKit.SecureEnclave.MLDSA65.PrivateKey(accessControl: access!) let created = try CryptoKit.SecureEnclave.MLDSA65.PrivateKey(accessControl: access!)
dataRep = created.dataRepresentation dataRep = created.dataRepresentation
case (.mldsa, 87): case (.mldsa, 87):
publicKey = created.publicKey.rawRepresentation
guard #available(macOS 26.0, *) else { throw Attributes.UnsupportedOptionError() } guard #available(macOS 26.0, *) else { throw Attributes.UnsupportedOptionError() }
let created = try CryptoKit.SecureEnclave.MLDSA87.PrivateKey(accessControl: access!) let created = try CryptoKit.SecureEnclave.MLDSA87.PrivateKey(accessControl: access!)
dataRep = created.dataRepresentation dataRep = created.dataRepresentation
publicKey = created.publicKey.rawRepresentation
default: default:
throw Attributes.UnsupportedOptionError() throw Attributes.UnsupportedOptionError()
} }
try saveKey(dataRep, name: name, attributes: attributes) let id = try saveKey(dataRep, name: name, attributes: attributes)
await reloadSecrets() await reloadSecrets()
return Secret(id: id, name: name, publicKey: publicKey, attributes: attributes)
} }
public func delete(secret: Secret) async throws { public func delete(secret: Secret) async throws {
@ -249,14 +254,16 @@ extension SecureEnclave.Store {
/// - name: A user-facing name for the key. /// - name: A user-facing name for the key.
/// - attributes: Attributes of the key. /// - attributes: Attributes of the key.
/// - Note: Despite the name, the "Data" of the key is _not_ actual key material. This is an opaque data representation that the SEP can manipulate. /// - Note: Despite the name, the "Data" of the key is _not_ actual key material. This is an opaque data representation that the SEP can manipulate.
func saveKey(_ key: Data, name: String, attributes: Attributes) throws { @discardableResult
func saveKey(_ key: Data, name: String, attributes: Attributes) throws -> String {
let attributes = try JSONEncoder().encode(attributes) let attributes = try JSONEncoder().encode(attributes)
let id = UUID().uuidString
let keychainAttributes = KeychainDictionary([ let keychainAttributes = KeychainDictionary([
kSecClass: Constants.keyClass, kSecClass: Constants.keyClass,
kSecAttrService: Constants.keyTag, kSecAttrService: Constants.keyTag,
kSecUseDataProtectionKeychain: true, kSecUseDataProtectionKeychain: true,
kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly, kSecAttrAccessible: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
kSecAttrAccount: UUID().uuidString, kSecAttrAccount: id,
kSecValueData: key, kSecValueData: key,
kSecAttrLabel: name, kSecAttrLabel: name,
kSecAttrGeneric: attributes kSecAttrGeneric: attributes
@ -265,6 +272,7 @@ extension SecureEnclave.Store {
if status != errSecSuccess { if status != errSecSuccess {
throw KeychainError(statusCode: status) throw KeychainError(statusCode: status)
} }
return id
} }
} }