mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-08-30 17:10:56 +00:00
Merge
This commit is contained in:
commit
d34d26884e
2
.github/workflows/nightly.yml
vendored
2
.github/workflows/nightly.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- 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
|
||||
env:
|
||||
RUN_ID: ${{ github.run_id }}
|
||||
|
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@ -21,9 +21,9 @@ jobs:
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- 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
|
||||
run: swift build --build-system swiftbuild --package-path Sources/Packages
|
||||
run: swift test --build-system swiftbuild --package-path Sources/Packages
|
||||
build:
|
||||
# runs-on: macOS-latest
|
||||
runs-on: macos-15
|
||||
@ -44,7 +44,7 @@ jobs:
|
||||
APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }}
|
||||
run: ./.github/scripts/signing.sh
|
||||
- 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
|
||||
env:
|
||||
TAG_NAME: ${{ github.ref }}
|
||||
|
8
.github/workflows/test.yml
vendored
8
.github/workflows/test.yml
vendored
@ -9,6 +9,8 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set Environment
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26_beta_5.app
|
||||
- name: Test
|
||||
run: swift build --build-system swiftbuild --package-path Sources/Packages
|
||||
run: sudo xcrun xcode-select -s /Applications/Xcode_26.0.app
|
||||
- name: Test Main 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
69
Package.swift
Normal 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),
|
||||
]
|
||||
}
|
@ -49,7 +49,7 @@ There's a [FAQ here](FAQ.md).
|
||||
|
||||
### 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
|
||||
|
||||
|
@ -36,47 +36,47 @@ let package = Package(
|
||||
name: "SecretKit",
|
||||
dependencies: [],
|
||||
resources: [localization],
|
||||
swiftSettings: swiftSettings
|
||||
swiftSettings: swiftSettings,
|
||||
),
|
||||
.testTarget(
|
||||
name: "SecretKitTests",
|
||||
dependencies: ["SecretKit", "SecureEnclaveSecretKit", "SmartCardSecretKit"],
|
||||
swiftSettings: swiftSettings
|
||||
swiftSettings: swiftSettings,
|
||||
),
|
||||
.target(
|
||||
name: "SecureEnclaveSecretKit",
|
||||
dependencies: ["SecretKit"],
|
||||
resources: [localization],
|
||||
swiftSettings: swiftSettings
|
||||
swiftSettings: swiftSettings,
|
||||
),
|
||||
.target(
|
||||
name: "SmartCardSecretKit",
|
||||
dependencies: ["SecretKit"],
|
||||
resources: [localization],
|
||||
swiftSettings: swiftSettings
|
||||
swiftSettings: swiftSettings,
|
||||
),
|
||||
.target(
|
||||
name: "SecretAgentKit",
|
||||
dependencies: ["SecretKit", "SecretAgentKitHeaders"],
|
||||
resources: [localization],
|
||||
swiftSettings: swiftSettings
|
||||
swiftSettings: swiftSettings,
|
||||
),
|
||||
.systemLibrary(
|
||||
name: "SecretAgentKitHeaders"
|
||||
name: "SecretAgentKitHeaders",
|
||||
),
|
||||
.testTarget(
|
||||
name: "SecretAgentKitTests",
|
||||
dependencies: ["SecretAgentKit"])
|
||||
,
|
||||
dependencies: ["SecretAgentKit"],
|
||||
),
|
||||
.target(
|
||||
name: "Brief",
|
||||
dependencies: [],
|
||||
resources: [localization],
|
||||
swiftSettings: swiftSettings
|
||||
swiftSettings: swiftSettings,
|
||||
),
|
||||
.testTarget(
|
||||
name: "BriefTests",
|
||||
dependencies: ["Brief"]
|
||||
dependencies: ["Brief"],
|
||||
),
|
||||
]
|
||||
)
|
||||
|
@ -3,7 +3,7 @@ import Foundation
|
||||
/// Type eraser for Secret.
|
||||
public struct AnySecret: Secret, @unchecked Sendable {
|
||||
|
||||
let base: Any
|
||||
public let base: Any
|
||||
private let hashable: AnyHashable
|
||||
private let _id: () -> AnyHashable
|
||||
private let _name: () -> String
|
||||
|
@ -1,8 +1,7 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
/// Type eraser for SecretStore.
|
||||
public class AnySecretStore: SecretStore, @unchecked Sendable {
|
||||
open class AnySecretStore: SecretStore, @unchecked Sendable {
|
||||
|
||||
let base: any Sendable
|
||||
private let _isAvailable: @MainActor @Sendable () -> Bool
|
||||
@ -10,7 +9,6 @@ public class AnySecretStore: SecretStore, @unchecked Sendable {
|
||||
private let _name: @MainActor @Sendable () -> String
|
||||
private let _secrets: @MainActor @Sendable () -> [AnySecret]
|
||||
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 _persistAuthentication: @Sendable (AnySecret, TimeInterval) async throws -> Void
|
||||
private let _reloadSecrets: @Sendable () async -> Void
|
||||
@ -22,7 +20,6 @@ public class AnySecretStore: SecretStore, @unchecked Sendable {
|
||||
_id = { secretStore.id }
|
||||
_secrets = { secretStore.secrets.map { AnySecret($0) } }
|
||||
_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) }
|
||||
_persistAuthentication = { try await secretStore.persistAuthentication(secret: $0.base as! SecretStoreType.SecretType, forDuration: $1) }
|
||||
_reloadSecrets = { await secretStore.reloadSecrets() }
|
||||
@ -48,10 +45,6 @@ public class AnySecretStore: SecretStore, @unchecked Sendable {
|
||||
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? {
|
||||
await _existingPersistedAuthenticationContext(secret)
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
/// Manages access to Secrets, and performs signature operations on data using those Secrets.
|
||||
public protocol SecretStore: Identifiable, Sendable {
|
||||
@ -23,14 +22,6 @@ public protocol SecretStore: Identifiable, Sendable {
|
||||
/// - Returns: The signed 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.
|
||||
/// - Parameters:
|
||||
/// - secret: The ``Secret`` to check if there is a persisted authentication for.
|
||||
|
@ -1,5 +1,4 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
import SecretKit
|
||||
|
||||
extension SecureEnclave {
|
||||
|
@ -73,41 +73,6 @@ extension SecureEnclave {
|
||||
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? {
|
||||
await persistentAuthenticationHandler.existingPersistedAuthenticationContext(secret: secret)
|
||||
}
|
||||
@ -262,9 +227,9 @@ extension SecureEnclave.Store {
|
||||
|
||||
extension SecureEnclave {
|
||||
|
||||
enum Constants {
|
||||
static let keyTag = Data("com.maxgoedjen.secretive.secureenclave.key".utf8)
|
||||
static let keyType = kSecAttrKeyTypeECSECPrimeRandom as String
|
||||
public enum Constants {
|
||||
public static let keyTag = Data("com.maxgoedjen.secretive.secureenclave.key".utf8)
|
||||
public static let keyType = kSecAttrKeyTypeECSECPrimeRandom as String
|
||||
static let unauthenticatedThreshold: TimeInterval = 0.05
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
import SecretKit
|
||||
|
||||
extension SmartCard {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import Foundation
|
||||
import Observation
|
||||
import Security
|
||||
import CryptoTokenKit
|
||||
@preconcurrency import CryptoTokenKit
|
||||
import LocalAuthentication
|
||||
import SecretKit
|
||||
|
||||
@ -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 {
|
||||
@ -34,17 +37,18 @@ extension SmartCard {
|
||||
|
||||
/// Initializes a Store.
|
||||
public init() {
|
||||
Task { @MainActor in
|
||||
if let tokenID = state.tokenID {
|
||||
state.isAvailable = true
|
||||
state.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: tokenID)
|
||||
Task {
|
||||
await MainActor.run {
|
||||
if let tokenID = smartcardTokenID {
|
||||
state.isAvailable = true
|
||||
state.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: tokenID)
|
||||
}
|
||||
loadSecrets()
|
||||
}
|
||||
loadSecrets()
|
||||
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.
|
||||
// Doing this inside a regular mainactor handler casues thread assertions in CryptoTokenKit to blow up when the handler executes.
|
||||
await state.watcher.setInsertionHandler { id in
|
||||
Task {
|
||||
self.smartcardInserted(for: id)
|
||||
await self.smartcardInserted(for: id)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -81,29 +85,6 @@ extension SmartCard {
|
||||
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? {
|
||||
nil
|
||||
}
|
||||
@ -135,12 +116,13 @@ extension SmartCard.Store {
|
||||
/// Resets the token ID and reloads secrets.
|
||||
/// - Parameter tokenID: The ID of the token that was inserted.
|
||||
@MainActor private func smartcardInserted(for tokenID: String? = nil) {
|
||||
guard let string = state.watcher.nonSecureEnclaveTokens.first else { return }
|
||||
guard state.tokenID == nil else { return }
|
||||
guard !string.contains("setoken") else { return }
|
||||
state.tokenID = string
|
||||
state.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: string)
|
||||
state.tokenID = string
|
||||
guard let string = state.watcher.nonSecureEnclaveTokens.first else { return }
|
||||
guard state.tokenID == nil else { return }
|
||||
guard !string.contains("setoken") else { return }
|
||||
state.tokenID = string
|
||||
state.watcher.addRemovalHandler(self.smartcardRemoved, forTokenID: string)
|
||||
state.tokenID = string
|
||||
reloadSecretsInternal()
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
|
||||
/// All available tokens, excluding the Secure Enclave.
|
||||
|
@ -60,18 +60,10 @@ import CryptoKit
|
||||
}
|
||||
var rs = r
|
||||
rs.append(s)
|
||||
let signature = try! P256.Signing.ECDSASignature(rawRepresentation: rs)
|
||||
let referenceValid = try! P256.Signing.PublicKey(x963Representation: Constants.Secrets.ecdsa256Secret.publicKey).isValidSignature(signature, for: dataToSign)
|
||||
let store = await list.stores.first!
|
||||
let derVerifies = try await store.verify(signature: signature.derRepresentation, for: dataToSign, with: AnySecret(Constants.Secrets.ecdsa256Secret))
|
||||
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)
|
||||
let signature = try P256.Signing.ECDSASignature(rawRepresentation: rs)
|
||||
// Correct signature
|
||||
#expect(try P256.Signing.PublicKey(x963Representation: Constants.Secrets.ecdsa256Secret.publicKey)
|
||||
.isValidSignature(signature, for: dataToSign))
|
||||
}
|
||||
|
||||
// MARK: Witness protocol
|
||||
|
@ -61,29 +61,6 @@ extension Stub {
|
||||
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? {
|
||||
nil
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import Cocoa
|
||||
import OSLog
|
||||
import Combine
|
||||
import SecretKit
|
||||
import SecureEnclaveSecretKit
|
||||
import SmartCardSecretKit
|
||||
@ -28,7 +27,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
let path = (NSHomeDirectory() as NSString).appendingPathComponent("socket.ssh") as String
|
||||
return SocketController(path: path)
|
||||
}()
|
||||
private var updateSink: AnyCancellable?
|
||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "AppDelegate")
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
import AppKit
|
||||
import SecretKit
|
||||
import Observation
|
||||
|
@ -1,5 +1,4 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
import AppKit
|
||||
|
||||
protocol JustUpdatedCheckerProtocol: Observable {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import Foundation
|
||||
import Combine
|
||||
|
||||
class PreviewAgentStatusChecker: AgentStatusCheckerProtocol {
|
||||
|
||||
|
@ -42,10 +42,6 @@ extension Preview {
|
||||
return data
|
||||
}
|
||||
|
||||
func verify(signature data: Data, for signature: Data, with secret: Preview.Secret) throws -> Bool {
|
||||
true
|
||||
}
|
||||
|
||||
func existingPersistedAuthenticationContext(secret: Preview.Secret) -> PersistedAuthenticationContext? {
|
||||
nil
|
||||
}
|
||||
@ -85,10 +81,6 @@ extension Preview {
|
||||
return data
|
||||
}
|
||||
|
||||
func verify(signature data: Data, for signature: Data, with secret: Preview.Secret) throws -> Bool {
|
||||
true
|
||||
}
|
||||
|
||||
func existingPersistedAuthenticationContext(secret: Preview.Secret) -> PersistedAuthenticationContext? {
|
||||
nil
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import SwiftUI
|
||||
import Combine
|
||||
import SecretKit
|
||||
|
||||
struct StoreListView: View {
|
||||
|
Loading…
Reference in New Issue
Block a user