This commit is contained in:
Max Goedjen 2025-08-23 20:55:38 -07:00
commit d34d26884e
No known key found for this signature in database
21 changed files with 119 additions and 246 deletions

View File

@ -20,7 +20,7 @@ jobs:
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
run: ./.github/scripts/signing.sh run: ./.github/scripts/signing.sh
- name: Set Environment - name: Set Environment
run: sudo xcrun xcode-select -s /Applications/Xcode_26_beta_5.app run: sudo xcrun xcode-select -s /Applications/Xcode_26.0.app
- name: Update Build Number - name: Update Build Number
env: env:
RUN_ID: ${{ github.run_id }} RUN_ID: ${{ github.run_id }}

View File

@ -21,9 +21,9 @@ jobs:
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
run: ./.github/scripts/signing.sh run: ./.github/scripts/signing.sh
- name: Set Environment - name: Set Environment
run: sudo xcrun xcode-select -s /Applications/Xcode_26_beta_5.app run: sudo xcrun xcode-select -s /Applications/Xcode_26.0.app
- name: Test - name: Test
run: swift build --build-system swiftbuild --package-path Sources/Packages run: swift test --build-system swiftbuild --package-path Sources/Packages
build: build:
# runs-on: macOS-latest # runs-on: macOS-latest
runs-on: macos-15 runs-on: macos-15
@ -44,7 +44,7 @@ jobs:
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
run: ./.github/scripts/signing.sh run: ./.github/scripts/signing.sh
- name: Set Environment - name: Set Environment
run: sudo xcrun xcode-select -s /Applications/Xcode_26_beta_5.app run: sudo xcrun xcode-select -s /Applications/Xcode_26.0.app
- name: Update Build Number - name: Update Build Number
env: env:
TAG_NAME: ${{ github.ref }} TAG_NAME: ${{ github.ref }}

View File

@ -9,6 +9,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
- name: Set Environment - name: Set Environment
run: sudo xcrun xcode-select -s /Applications/Xcode_26_beta_5.app run: sudo xcrun xcode-select -s /Applications/Xcode_26.0.app
- name: Test - name: Test Main Packages
run: swift build --build-system swiftbuild --package-path Sources/Packages run: swift test --build-system swiftbuild --package-path Sources/Packages
- name: Test SecretKit Packages
run: swift test --build-system swiftbuild

69
Package.swift Normal file
View File

@ -0,0 +1,69 @@
// swift-tools-version:6.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
// This is basically the same package as `Sources/Packages/Package.swift`, but thinned slightly.
// Ideally this would be the same package, but SPM requires it to be at the root of the project,
// and Xcode does _not_ like that, so they're separate.
let package = Package(
name: "SecretKit",
defaultLocalization: "en",
platforms: [
.macOS(.v14)
],
products: [
.library(
name: "SecretKit",
targets: ["SecretKit"]),
.library(
name: "SecureEnclaveSecretKit",
targets: ["SecureEnclaveSecretKit"]),
.library(
name: "SmartCardSecretKit",
targets: ["SmartCardSecretKit"]),
],
dependencies: [
],
targets: [
.target(
name: "SecretKit",
dependencies: [],
path: "Sources/Packages/Sources/SecretKit",
resources: [localization],
swiftSettings: swiftSettings
),
.testTarget(
name: "SecretKitTests",
dependencies: ["SecretKit", "SecureEnclaveSecretKit", "SmartCardSecretKit"],
path: "Sources/Packages/Tests/SecretKitTests",
swiftSettings: swiftSettings
),
.target(
name: "SecureEnclaveSecretKit",
dependencies: ["SecretKit"],
path: "Sources/Packages/Sources/SecureEnclaveSecretKit",
resources: [localization],
swiftSettings: swiftSettings
),
.target(
name: "SmartCardSecretKit",
dependencies: ["SecretKit"],
path: "Sources/Packages/Sources/SmartCardSecretKit",
resources: [localization],
swiftSettings: swiftSettings
),
]
)
var localization: Resource {
.process("../../Localizable.xcstrings")
}
var swiftSettings: [PackageDescription.SwiftSetting] {
[
.swiftLanguageMode(.v6),
// This freaks out Xcode in a dependency context.
// .treatAllWarnings(as: .error),
]
}

View File

@ -49,7 +49,7 @@ There's a [FAQ here](FAQ.md).
### Auditable Build Process ### Auditable Build Process
Builds are produced by GitHub Actions with an auditable build and release generation process. Each build has a "Document SHAs" step, which will output SHA checksums for the build produced by the GitHub Action, so you can verify that the source code for a given build corresponds to any given release. Builds are produced by GitHub Actions with an auditable build and release generation process. Starting with Secretive 3.0, builds are attested using [GitHub Artifact Attestation](https://docs.github.com/en/actions/concepts/security/artifact-attestations). Attestations are viewable in the build log for a build, and also on the [main attestation page](https://github.com/maxgoedjen/secretive/attestations).
### A Note Around Code Signing and Keychains ### A Note Around Code Signing and Keychains

View File

@ -36,47 +36,47 @@ let package = Package(
name: "SecretKit", name: "SecretKit",
dependencies: [], dependencies: [],
resources: [localization], resources: [localization],
swiftSettings: swiftSettings swiftSettings: swiftSettings,
), ),
.testTarget( .testTarget(
name: "SecretKitTests", name: "SecretKitTests",
dependencies: ["SecretKit", "SecureEnclaveSecretKit", "SmartCardSecretKit"], dependencies: ["SecretKit", "SecureEnclaveSecretKit", "SmartCardSecretKit"],
swiftSettings: swiftSettings swiftSettings: swiftSettings,
), ),
.target( .target(
name: "SecureEnclaveSecretKit", name: "SecureEnclaveSecretKit",
dependencies: ["SecretKit"], dependencies: ["SecretKit"],
resources: [localization], resources: [localization],
swiftSettings: swiftSettings swiftSettings: swiftSettings,
), ),
.target( .target(
name: "SmartCardSecretKit", name: "SmartCardSecretKit",
dependencies: ["SecretKit"], dependencies: ["SecretKit"],
resources: [localization], resources: [localization],
swiftSettings: swiftSettings swiftSettings: swiftSettings,
), ),
.target( .target(
name: "SecretAgentKit", name: "SecretAgentKit",
dependencies: ["SecretKit", "SecretAgentKitHeaders"], dependencies: ["SecretKit", "SecretAgentKitHeaders"],
resources: [localization], resources: [localization],
swiftSettings: swiftSettings swiftSettings: swiftSettings,
), ),
.systemLibrary( .systemLibrary(
name: "SecretAgentKitHeaders" name: "SecretAgentKitHeaders",
), ),
.testTarget( .testTarget(
name: "SecretAgentKitTests", name: "SecretAgentKitTests",
dependencies: ["SecretAgentKit"]) dependencies: ["SecretAgentKit"],
, ),
.target( .target(
name: "Brief", name: "Brief",
dependencies: [], dependencies: [],
resources: [localization], resources: [localization],
swiftSettings: swiftSettings swiftSettings: swiftSettings,
), ),
.testTarget( .testTarget(
name: "BriefTests", name: "BriefTests",
dependencies: ["Brief"] dependencies: ["Brief"],
), ),
] ]
) )

View File

@ -3,7 +3,7 @@ import Foundation
/// Type eraser for Secret. /// Type eraser for Secret.
public struct AnySecret: Secret, @unchecked Sendable { public struct AnySecret: Secret, @unchecked Sendable {
let base: Any public let base: Any
private let hashable: AnyHashable private let hashable: AnyHashable
private let _id: () -> AnyHashable private let _id: () -> AnyHashable
private let _name: () -> String private let _name: () -> String

View File

@ -1,8 +1,7 @@
import Foundation import Foundation
import Combine
/// Type eraser for SecretStore. /// Type eraser for SecretStore.
public class AnySecretStore: SecretStore, @unchecked Sendable { open class AnySecretStore: SecretStore, @unchecked Sendable {
let base: any Sendable let base: any Sendable
private let _isAvailable: @MainActor @Sendable () -> Bool private let _isAvailable: @MainActor @Sendable () -> Bool
@ -10,7 +9,6 @@ public class AnySecretStore: SecretStore, @unchecked Sendable {
private let _name: @MainActor @Sendable () -> String private let _name: @MainActor @Sendable () -> String
private let _secrets: @MainActor @Sendable () -> [AnySecret] private let _secrets: @MainActor @Sendable () -> [AnySecret]
private let _sign: @Sendable (Data, AnySecret, SigningRequestProvenance) async throws -> Data private let _sign: @Sendable (Data, AnySecret, SigningRequestProvenance) async throws -> Data
private let _verify: @Sendable (Data, Data, AnySecret) async throws -> Bool
private let _existingPersistedAuthenticationContext: @Sendable (AnySecret) async -> PersistedAuthenticationContext? private let _existingPersistedAuthenticationContext: @Sendable (AnySecret) async -> PersistedAuthenticationContext?
private let _persistAuthentication: @Sendable (AnySecret, TimeInterval) async throws -> Void private let _persistAuthentication: @Sendable (AnySecret, TimeInterval) async throws -> Void
private let _reloadSecrets: @Sendable () async -> Void private let _reloadSecrets: @Sendable () async -> Void
@ -22,7 +20,6 @@ public class AnySecretStore: SecretStore, @unchecked Sendable {
_id = { secretStore.id } _id = { secretStore.id }
_secrets = { secretStore.secrets.map { AnySecret($0) } } _secrets = { secretStore.secrets.map { AnySecret($0) } }
_sign = { try await secretStore.sign(data: $0, with: $1.base as! SecretStoreType.SecretType, for: $2) } _sign = { try await secretStore.sign(data: $0, with: $1.base as! SecretStoreType.SecretType, for: $2) }
_verify = { try await secretStore.verify(signature: $0, for: $1, with: $2.base as! SecretStoreType.SecretType) }
_existingPersistedAuthenticationContext = { await secretStore.existingPersistedAuthenticationContext(secret: $0.base as! SecretStoreType.SecretType) } _existingPersistedAuthenticationContext = { await secretStore.existingPersistedAuthenticationContext(secret: $0.base as! SecretStoreType.SecretType) }
_persistAuthentication = { try await secretStore.persistAuthentication(secret: $0.base as! SecretStoreType.SecretType, forDuration: $1) } _persistAuthentication = { try await secretStore.persistAuthentication(secret: $0.base as! SecretStoreType.SecretType, forDuration: $1) }
_reloadSecrets = { await secretStore.reloadSecrets() } _reloadSecrets = { await secretStore.reloadSecrets() }
@ -48,10 +45,6 @@ public class AnySecretStore: SecretStore, @unchecked Sendable {
try await _sign(data, secret, provenance) try await _sign(data, secret, provenance)
} }
public func verify(signature: Data, for data: Data, with secret: AnySecret) async throws -> Bool {
try await _verify(signature, data, secret)
}
public func existingPersistedAuthenticationContext(secret: AnySecret) async -> PersistedAuthenticationContext? { public func existingPersistedAuthenticationContext(secret: AnySecret) async -> PersistedAuthenticationContext? {
await _existingPersistedAuthenticationContext(secret) await _existingPersistedAuthenticationContext(secret)
} }

View File

@ -1,5 +1,4 @@
import Foundation import Foundation
import Combine
/// Manages access to Secrets, and performs signature operations on data using those Secrets. /// Manages access to Secrets, and performs signature operations on data using those Secrets.
public protocol SecretStore: Identifiable, Sendable { public protocol SecretStore: Identifiable, Sendable {
@ -23,14 +22,6 @@ public protocol SecretStore: Identifiable, Sendable {
/// - Returns: The signed data. /// - Returns: The signed data.
func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) async throws -> Data func sign(data: Data, with secret: SecretType, for provenance: SigningRequestProvenance) async throws -> Data
/// Verifies that a signature is valid over a specified payload.
/// - Parameters:
/// - signature: The signature over the data.
/// - data: The data to verify the signature of.
/// - secret: The secret whose signature to verify.
/// - Returns: Whether the signature was verified.
func verify(signature: Data, for data: Data, with secret: SecretType) async throws -> Bool
/// Checks to see if there is currently a valid persisted authentication for a given secret. /// Checks to see if there is currently a valid persisted authentication for a given secret.
/// - Parameters: /// - Parameters:
/// - secret: The ``Secret`` to check if there is a persisted authentication for. /// - secret: The ``Secret`` to check if there is a persisted authentication for.

View File

@ -1,5 +1,4 @@
import Foundation import Foundation
import Combine
import SecretKit import SecretKit
extension SecureEnclave { extension SecureEnclave {

View File

@ -73,41 +73,6 @@ extension SecureEnclave {
return signature as Data return signature as Data
} }
public func verify(signature: Data, for data: Data, with secret: Secret) throws -> Bool {
let context = LAContext()
context.localizedReason = String(localized: .authContextRequestVerifyDescription(secretName: secret.name))
context.localizedCancelTitle = String(localized: .authContextRequestDenyButton)
let attributes = KeychainDictionary([
kSecClass: kSecClassKey,
kSecAttrKeyClass: kSecAttrKeyClassPrivate,
kSecAttrApplicationLabel: secret.id as CFData,
kSecAttrKeyType: Constants.keyType,
kSecAttrTokenID: kSecAttrTokenIDSecureEnclave,
kSecAttrApplicationTag: Constants.keyTag,
kSecUseAuthenticationContext: context,
kSecReturnRef: true
])
var verifyError: SecurityError?
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
let verified = SecKeyVerifySignature(key, .ecdsaSignatureMessageX962SHA256, data as CFData, signature as CFData, &verifyError)
if !verified, let verifyError {
if verifyError.takeUnretainedValue() ~= .verifyError {
return false
} else {
throw SigningError(error: verifyError)
}
}
return verified
}
public func existingPersistedAuthenticationContext(secret: Secret) async -> PersistedAuthenticationContext? { public func existingPersistedAuthenticationContext(secret: Secret) async -> PersistedAuthenticationContext? {
await persistentAuthenticationHandler.existingPersistedAuthenticationContext(secret: secret) await persistentAuthenticationHandler.existingPersistedAuthenticationContext(secret: secret)
} }
@ -262,9 +227,9 @@ extension SecureEnclave.Store {
extension SecureEnclave { extension SecureEnclave {
enum Constants { public enum Constants {
static let keyTag = Data("com.maxgoedjen.secretive.secureenclave.key".utf8) public static let keyTag = Data("com.maxgoedjen.secretive.secureenclave.key".utf8)
static let keyType = kSecAttrKeyTypeECSECPrimeRandom as String public static let keyType = kSecAttrKeyTypeECSECPrimeRandom as String
static let unauthenticatedThreshold: TimeInterval = 0.05 static let unauthenticatedThreshold: TimeInterval = 0.05
} }

View File

@ -1,5 +1,4 @@
import Foundation import Foundation
import Combine
import SecretKit import SecretKit
extension SmartCard { extension SmartCard {

View File

@ -1,7 +1,7 @@
import Foundation import Foundation
import Observation import Observation
import Security import Security
import CryptoTokenKit @preconcurrency import CryptoTokenKit
import LocalAuthentication import LocalAuthentication
import SecretKit import SecretKit
@ -23,6 +23,9 @@ extension SmartCard {
public var isAvailable: Bool { public var isAvailable: Bool {
state.isAvailable state.isAvailable
} }
@MainActor public var smartcardTokenID: String? {
state.tokenID
}
public let id = UUID() public let id = UUID()
@MainActor public var name: String { @MainActor public var name: String {
@ -34,17 +37,18 @@ extension SmartCard {
/// Initializes a Store. /// Initializes a Store.
public init() { public init() {
Task { @MainActor in Task {
if let tokenID = state.tokenID { await MainActor.run {
state.isAvailable = true if let tokenID = smartcardTokenID {
state.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: tokenID) state.isAvailable = true
state.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: tokenID)
}
loadSecrets()
} }
loadSecrets() // Doing this inside a regular mainactor handler casues thread assertions in CryptoTokenKit to blow up when the handler executes.
state.watcher.setInsertionHandler { id in await state.watcher.setInsertionHandler { id in
// Setting insertion handler will cause it to be called immediately.
// Make a thread jump so we don't hit a recursive lock attempt.
Task { Task {
self.smartcardInserted(for: id) await self.smartcardInserted(for: id)
} }
} }
} }
@ -81,29 +85,6 @@ extension SmartCard {
return signature as Data return signature as Data
} }
public func verify(signature: Data, for data: Data, with secret: Secret) throws -> Bool {
let attributes = KeychainDictionary([
kSecAttrKeyType: secret.keyType.secAttrKeyType as Any,
kSecAttrKeySizeInBits: secret.keyType.size,
kSecAttrKeyClass: kSecAttrKeyClassPublic
])
var verifyError: SecurityError?
let untyped: CFTypeRef? = SecKeyCreateWithData(secret.publicKey as CFData, attributes, &verifyError)
guard let untypedSafe = untyped else {
throw KeychainError(statusCode: errSecSuccess)
}
let key = untypedSafe as! SecKey
let verified = SecKeyVerifySignature(key, signatureAlgorithm(for: secret, allowRSA: true), data as CFData, signature as CFData, &verifyError)
if !verified, let verifyError {
if verifyError.takeUnretainedValue() ~= .verifyError {
return false
} else {
throw SigningError(error: verifyError)
}
}
return verified
}
public func existingPersistedAuthenticationContext(secret: Secret) -> PersistedAuthenticationContext? { public func existingPersistedAuthenticationContext(secret: Secret) -> PersistedAuthenticationContext? {
nil nil
} }
@ -135,12 +116,13 @@ extension SmartCard.Store {
/// Resets the token ID and reloads secrets. /// Resets the token ID and reloads secrets.
/// - Parameter tokenID: The ID of the token that was inserted. /// - Parameter tokenID: The ID of the token that was inserted.
@MainActor private func smartcardInserted(for tokenID: String? = nil) { @MainActor private func smartcardInserted(for tokenID: String? = nil) {
guard let string = state.watcher.nonSecureEnclaveTokens.first else { return } guard let string = state.watcher.nonSecureEnclaveTokens.first else { return }
guard state.tokenID == nil else { return } guard state.tokenID == nil else { return }
guard !string.contains("setoken") else { return } guard !string.contains("setoken") else { return }
state.tokenID = string state.tokenID = string
state.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: string) state.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: string)
state.tokenID = string state.tokenID = string
reloadSecretsInternal()
} }
/// Resets the token ID and reloads secrets. /// Resets the token ID and reloads secrets.
@ -188,88 +170,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.keyType.secAttrKeyType as Any,
kSecAttrKeySizeInBits: secret.keyType.size,
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.keyType.algorithm, secret.keyType.size) {
case (.ecdsa, 256):
return .eciesEncryptionCofactorVariableIVX963SHA256AESGCM
case (.ecdsa, 384):
return .eciesEncryptionCofactorVariableIVX963SHA384AESGCM
case (.rsa, 1024), (.rsa, 2048):
return .rsaEncryptionOAEPSHA512AESGCM
default:
fatalError()
}
}
}
extension TKTokenWatcher { extension TKTokenWatcher {
/// All available tokens, excluding the Secure Enclave. /// All available tokens, excluding the Secure Enclave.

View File

@ -60,18 +60,10 @@ import CryptoKit
} }
var rs = r var rs = r
rs.append(s) rs.append(s)
let signature = try! P256.Signing.ECDSASignature(rawRepresentation: rs) let signature = try P256.Signing.ECDSASignature(rawRepresentation: rs)
let referenceValid = try! P256.Signing.PublicKey(x963Representation: Constants.Secrets.ecdsa256Secret.publicKey).isValidSignature(signature, for: dataToSign) // Correct signature
let store = await list.stores.first! #expect(try P256.Signing.PublicKey(x963Representation: Constants.Secrets.ecdsa256Secret.publicKey)
let derVerifies = try await store.verify(signature: signature.derRepresentation, for: dataToSign, with: AnySecret(Constants.Secrets.ecdsa256Secret)) .isValidSignature(signature, for: dataToSign))
let invalidRandomSignature = try await store.verify(signature: "invalid".data(using: .utf8)!, for: dataToSign, with: AnySecret(Constants.Secrets.ecdsa256Secret))
let invalidRandomData = try await store.verify(signature: signature.derRepresentation, for: "invalid".data(using: .utf8)!, with: AnySecret(Constants.Secrets.ecdsa256Secret))
let invalidWrongKey = try await store.verify(signature: signature.derRepresentation, for: dataToSign, with: AnySecret(Constants.Secrets.ecdsa384Secret))
#expect(referenceValid)
#expect(derVerifies)
#expect(invalidRandomSignature == false)
#expect(invalidRandomData == false)
#expect(invalidWrongKey == false)
} }
// MARK: Witness protocol // MARK: Witness protocol

View File

@ -61,29 +61,6 @@ extension Stub {
return SecKeyCreateSignature(privateKey, signatureAlgorithm(for: secret), data as CFData, nil)! as Data return SecKeyCreateSignature(privateKey, signatureAlgorithm(for: secret), data as CFData, nil)! as Data
} }
public func verify(signature: Data, for data: Data, with secret: Stub.Secret) throws -> Bool {
let attributes = KeychainDictionary([
kSecAttrKeyType: secret.algorithm.secAttrKeyType,
kSecAttrKeySizeInBits: secret.keySize,
kSecAttrKeyClass: kSecAttrKeyClassPublic
])
var verifyError: Unmanaged<CFError>?
let untyped: CFTypeRef? = SecKeyCreateWithData(secret.publicKey as CFData, attributes, &verifyError)
guard let untypedSafe = untyped else {
throw NSError(domain: "test", code: 0, userInfo: nil)
}
let key = untypedSafe as! SecKey
let verified = SecKeyVerifySignature(key, signatureAlgorithm(for: secret), data as CFData, signature as CFData, &verifyError)
if let verifyError {
if verifyError.takeUnretainedValue() ~= .verifyError {
return false
} else {
throw NSError(domain: "test", code: 0, userInfo: nil)
}
}
return verified
}
public func existingPersistedAuthenticationContext(secret: Stub.Secret) -> PersistedAuthenticationContext? { public func existingPersistedAuthenticationContext(secret: Stub.Secret) -> PersistedAuthenticationContext? {
nil nil
} }

View File

@ -1,6 +1,5 @@
import Cocoa import Cocoa
import OSLog import OSLog
import Combine
import SecretKit import SecretKit
import SecureEnclaveSecretKit import SecureEnclaveSecretKit
import SmartCardSecretKit import SmartCardSecretKit
@ -28,7 +27,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let path = (NSHomeDirectory() as NSString).appendingPathComponent("socket.ssh") as String let path = (NSHomeDirectory() as NSString).appendingPathComponent("socket.ssh") as String
return SocketController(path: path) return SocketController(path: path)
}() }()
private var updateSink: AnyCancellable?
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "AppDelegate") private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "AppDelegate")
func applicationDidFinishLaunching(_ aNotification: Notification) { func applicationDidFinishLaunching(_ aNotification: Notification) {

View File

@ -1,5 +1,4 @@
import Foundation import Foundation
import Combine
import AppKit import AppKit
import SecretKit import SecretKit
import Observation import Observation

View File

@ -1,5 +1,4 @@
import Foundation import Foundation
import Combine
import AppKit import AppKit
protocol JustUpdatedCheckerProtocol: Observable { protocol JustUpdatedCheckerProtocol: Observable {

View File

@ -1,5 +1,4 @@
import Foundation import Foundation
import Combine
class PreviewAgentStatusChecker: AgentStatusCheckerProtocol { class PreviewAgentStatusChecker: AgentStatusCheckerProtocol {

View File

@ -42,10 +42,6 @@ extension Preview {
return data return data
} }
func verify(signature data: Data, for signature: Data, with secret: Preview.Secret) throws -> Bool {
true
}
func existingPersistedAuthenticationContext(secret: Preview.Secret) -> PersistedAuthenticationContext? { func existingPersistedAuthenticationContext(secret: Preview.Secret) -> PersistedAuthenticationContext? {
nil nil
} }
@ -85,10 +81,6 @@ extension Preview {
return data return data
} }
func verify(signature data: Data, for signature: Data, with secret: Preview.Secret) throws -> Bool {
true
}
func existingPersistedAuthenticationContext(secret: Preview.Secret) -> PersistedAuthenticationContext? { func existingPersistedAuthenticationContext(secret: Preview.Secret) -> PersistedAuthenticationContext? {
nil nil
} }

View File

@ -1,5 +1,4 @@
import SwiftUI import SwiftUI
import Combine
import SecretKit import SecretKit
struct StoreListView: View { struct StoreListView: View {