mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-08-28 16:10:56 +00:00
parent
8f4d0b8eda
commit
c52728a050
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -98,7 +98,7 @@ extension SecureEnclave {
|
|||||||
|
|
||||||
// MARK: SecretStoreModifiable
|
// MARK: SecretStoreModifiable
|
||||||
|
|
||||||
public func create(name: String, attributes: Attributes) async throws {
|
public func create(name: String, attributes: Attributes) async throws -> Secret {
|
||||||
var accessError: SecurityError?
|
var accessError: SecurityError?
|
||||||
let flags: SecAccessControlCreateFlags = switch attributes.authentication {
|
let flags: SecAccessControlCreateFlags = switch attributes.authentication {
|
||||||
case .notRequired:
|
case .notRequired:
|
||||||
@ -119,23 +119,28 @@ extension SecureEnclave {
|
|||||||
throw error.takeRetainedValue() as Error
|
throw error.takeRetainedValue() as Error
|
||||||
}
|
}
|
||||||
let dataRep: Data
|
let dataRep: Data
|
||||||
|
let publicKey: Data
|
||||||
switch attributes.keyType {
|
switch attributes.keyType {
|
||||||
case .ecdsa256:
|
case .ecdsa256:
|
||||||
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
|
||||||
|
publicKey = created.publicKey.x963Representation
|
||||||
case .mldsa65:
|
case .mldsa65:
|
||||||
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
|
||||||
|
publicKey = created.publicKey.rawRepresentation
|
||||||
case .mldsa87:
|
case .mldsa87:
|
||||||
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 {
|
||||||
@ -253,14 +258,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
|
||||||
@ -269,6 +276,7 @@ extension SecureEnclave.Store {
|
|||||||
if status != errSecSuccess {
|
if status != errSecSuccess {
|
||||||
throw KeychainError(statusCode: status)
|
throw KeychainError(statusCode: status)
|
||||||
}
|
}
|
||||||
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user