mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-08-31 01:20:57 +00:00
Cleanup
This commit is contained in:
parent
7d6223a327
commit
698b7571c8
@ -9,7 +9,8 @@ public final class Agent: Sendable {
|
|||||||
|
|
||||||
private let storeList: SecretStoreList
|
private let storeList: SecretStoreList
|
||||||
private let witness: SigningWitness?
|
private let witness: SigningWitness?
|
||||||
private let writer = OpenSSHKeyWriter()
|
private let publicKeyWriter = OpenSSHPublicKeyWriter()
|
||||||
|
private let signatureWriter = OpenSSHSignatureWriter()
|
||||||
private let requestTracer = SigningRequestTracer()
|
private let requestTracer = SigningRequestTracer()
|
||||||
private let certificateHandler = OpenSSHCertificateHandler()
|
private let certificateHandler = OpenSSHCertificateHandler()
|
||||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "Agent")
|
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "Agent")
|
||||||
@ -43,7 +44,7 @@ extension Agent {
|
|||||||
guard data.count > 4 else { return false}
|
guard data.count > 4 else { return false}
|
||||||
let requestTypeInt = data[4]
|
let requestTypeInt = data[4]
|
||||||
guard let requestType = SSHAgent.RequestType(rawValue: requestTypeInt) else {
|
guard let requestType = SSHAgent.RequestType(rawValue: requestTypeInt) else {
|
||||||
writer.write(OpenSSHKeyWriter().lengthAndData(of: SSHAgent.ResponseType.agentFailure.data))
|
writer.write(SSHAgent.ResponseType.agentFailure.data.lengthAndData)
|
||||||
logger.debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)")
|
logger.debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -75,8 +76,7 @@ extension Agent {
|
|||||||
response.append(SSHAgent.ResponseType.agentFailure.data)
|
response.append(SSHAgent.ResponseType.agentFailure.data)
|
||||||
logger.debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)")
|
logger.debug("Agent returned \(SSHAgent.ResponseType.agentFailure.debugDescription)")
|
||||||
}
|
}
|
||||||
let full = OpenSSHKeyWriter().lengthAndData(of: response)
|
return response.lengthAndData
|
||||||
return full
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -92,14 +92,14 @@ extension Agent {
|
|||||||
var keyData = Data()
|
var keyData = Data()
|
||||||
|
|
||||||
for secret in secrets {
|
for secret in secrets {
|
||||||
let keyBlob = writer.data(secret: secret)
|
let keyBlob = publicKeyWriter.data(secret: secret)
|
||||||
let curveData = Data(writer.curveType(for: secret.keyType).utf8)
|
let curveData = publicKeyWriter.openSSHIdentifier(for: secret.keyType)
|
||||||
keyData.append(writer.lengthAndData(of: keyBlob))
|
keyData.append(keyBlob.lengthAndData)
|
||||||
keyData.append(writer.lengthAndData(of: curveData))
|
keyData.append(curveData.lengthAndData)
|
||||||
|
|
||||||
if let (certificateData, name) = try? await certificateHandler.keyBlobAndName(for: secret) {
|
if let (certificateData, name) = try? await certificateHandler.keyBlobAndName(for: secret) {
|
||||||
keyData.append(writer.lengthAndData(of: certificateData))
|
keyData.append(certificateData.lengthAndData)
|
||||||
keyData.append(writer.lengthAndData(of: name))
|
keyData.append(name.lengthAndData)
|
||||||
count += 1
|
count += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,7 +137,7 @@ extension Agent {
|
|||||||
let dataToSign = reader.readNextChunk()
|
let dataToSign = reader.readNextChunk()
|
||||||
let rawRepresentation = try await store.sign(data: dataToSign, with: secret, for: provenance)
|
let rawRepresentation = try await store.sign(data: dataToSign, with: secret, for: provenance)
|
||||||
|
|
||||||
let curveData = Data(writer.curveType(for: secret.keyType).utf8)
|
let curveData = publicKeyWriter.openSSHIdentifier(for: secret.keyType)
|
||||||
|
|
||||||
let signedData: Data
|
let signedData: Data
|
||||||
if secret.keyType.algorithm == .ecdsa {
|
if secret.keyType.algorithm == .ecdsa {
|
||||||
@ -155,20 +155,20 @@ extension Agent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var signatureChunk = Data()
|
var signatureChunk = Data()
|
||||||
signatureChunk.append(writer.lengthAndData(of: r))
|
signatureChunk.append(r.lengthAndData)
|
||||||
signatureChunk.append(writer.lengthAndData(of: s))
|
signatureChunk.append(s.lengthAndData)
|
||||||
var mutSignedData = Data()
|
var mutSignedData = Data()
|
||||||
var sub = Data()
|
var sub = Data()
|
||||||
sub.append(writer.lengthAndData(of: curveData))
|
sub.append(curveData.lengthAndData)
|
||||||
sub.append(writer.lengthAndData(of: signatureChunk))
|
sub.append(signatureChunk.lengthAndData)
|
||||||
mutSignedData.append(writer.lengthAndData(of: sub))
|
mutSignedData.append(sub.lengthAndData)
|
||||||
signedData = mutSignedData
|
signedData = mutSignedData
|
||||||
} else {
|
} else {
|
||||||
var mutSignedData = Data()
|
var mutSignedData = Data()
|
||||||
var sub = Data()
|
var sub = Data()
|
||||||
sub.append(writer.lengthAndData(of: Data("rsa-sha2-512".utf8)))
|
sub.append("rsa-sha2-512".lengthAndData)
|
||||||
sub.append(writer.lengthAndData(of: rawRepresentation))
|
sub.append(rawRepresentation.lengthAndData)
|
||||||
mutSignedData.append(writer.lengthAndData(of: sub))
|
mutSignedData.append(sub.lengthAndData)
|
||||||
|
|
||||||
signedData = mutSignedData
|
signedData = mutSignedData
|
||||||
}
|
}
|
||||||
@ -203,7 +203,7 @@ extension Agent {
|
|||||||
func secret(matching hash: Data) async -> (AnySecretStore, AnySecret)? {
|
func secret(matching hash: Data) async -> (AnySecretStore, AnySecret)? {
|
||||||
for store in await storeList.stores {
|
for store in await storeList.stores {
|
||||||
let allMatching = await store.secrets.filter { secret in
|
let allMatching = await store.secrets.filter { secret in
|
||||||
hash == writer.data(secret: secret)
|
hash == publicKeyWriter.data(secret: secret)
|
||||||
}
|
}
|
||||||
if let matching = allMatching.first {
|
if let matching = allMatching.first {
|
||||||
return (store, matching)
|
return (store, matching)
|
||||||
|
@ -22,7 +22,7 @@ SecretKit is a collection of protocols describing secrets and stores.
|
|||||||
|
|
||||||
### OpenSSH
|
### OpenSSH
|
||||||
|
|
||||||
- ``OpenSSHKeyWriter``
|
- ``OpenSSHPublicKeyWriter``
|
||||||
- ``OpenSSHReader``
|
- ``OpenSSHReader``
|
||||||
|
|
||||||
### Signing Process
|
### Signing Process
|
||||||
|
@ -52,16 +52,16 @@ public extension SecretStore {
|
|||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - secret: The secret which will be used for signing.
|
/// - secret: The secret which will be used for signing.
|
||||||
/// - Returns: The appropriate algorithm.
|
/// - Returns: The appropriate algorithm.
|
||||||
func signatureAlgorithm(for secret: SecretType) -> SecKeyAlgorithm {
|
func signatureAlgorithm(for secret: SecretType) -> SecKeyAlgorithm? {
|
||||||
switch (secret.keyType.algorithm, secret.keyType.size) {
|
switch (secret.keyType.algorithm, secret.keyType.size) {
|
||||||
case (.ecdsa, 256):
|
case (.ecdsa, 256):
|
||||||
return .ecdsaSignatureMessageX962SHA256
|
.ecdsaSignatureMessageX962SHA256
|
||||||
case (.ecdsa, 384):
|
case (.ecdsa, 384):
|
||||||
return .ecdsaSignatureMessageX962SHA384
|
.ecdsaSignatureMessageX962SHA384
|
||||||
case (.rsa, 2048):
|
case (.rsa, 2048):
|
||||||
return .rsaSignatureMessagePKCS1v15SHA512
|
.rsaSignatureMessagePKCS1v15SHA512
|
||||||
default:
|
default:
|
||||||
fatalError()
|
nil
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension Data {
|
||||||
|
|
||||||
|
/// Creates an OpenSSH protocol style data object, which has a length header, followed by the data payload.
|
||||||
|
/// - Returns: OpenSSH data.
|
||||||
|
package var lengthAndData: Data {
|
||||||
|
let rawLength = UInt32(count)
|
||||||
|
var endian = rawLength.bigEndian
|
||||||
|
return Data(bytes: &endian, count: UInt32.bitWidth/8) + self
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
|
||||||
|
/// Creates an OpenSSH protocol style data object, which has a length header, followed by the data payload.
|
||||||
|
/// - Returns: OpenSSH data.
|
||||||
|
package var lengthAndData: Data {
|
||||||
|
Data(utf8).lengthAndData
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -6,7 +6,7 @@ public actor OpenSSHCertificateHandler: Sendable {
|
|||||||
|
|
||||||
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: NSHomeDirectory())
|
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: NSHomeDirectory())
|
||||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "OpenSSHCertificateHandler")
|
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "OpenSSHCertificateHandler")
|
||||||
private let writer = OpenSSHKeyWriter()
|
private let writer = OpenSSHPublicKeyWriter()
|
||||||
private var keyBlobsAndNames: [AnySecret: (Data, Data)] = [:]
|
private var keyBlobsAndNames: [AnySecret: (Data, Data)] = [:]
|
||||||
|
|
||||||
/// Initializes an OpenSSHCertificateHandler.
|
/// Initializes an OpenSSHCertificateHandler.
|
||||||
@ -40,10 +40,10 @@ public actor OpenSSHCertificateHandler: Sendable {
|
|||||||
let curveIdentifier = reader.readNextChunk()
|
let curveIdentifier = reader.readNextChunk()
|
||||||
let publicKey = reader.readNextChunk()
|
let publicKey = reader.readNextChunk()
|
||||||
|
|
||||||
let curveType = Data(certType.replacingOccurrences(of: "-cert-v01@openssh.com", with: "").utf8)
|
let openSSHIdentifier = certType.replacingOccurrences(of: "-cert-v01@openssh.com", with: "")
|
||||||
return writer.lengthAndData(of: curveType) +
|
return openSSHIdentifier.lengthAndData +
|
||||||
writer.lengthAndData(of: curveIdentifier) +
|
curveIdentifier.lengthAndData +
|
||||||
writer.lengthAndData(of: publicKey)
|
publicKey.lengthAndData
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import CryptoKit
|
import CryptoKit
|
||||||
|
|
||||||
/// Generates OpenSSH representations of Secrets.
|
/// Generates OpenSSH representations of the public key sof secrets.
|
||||||
public struct OpenSSHKeyWriter: Sendable {
|
public struct OpenSSHPublicKeyWriter: Sendable {
|
||||||
|
|
||||||
/// Initializes the writer.
|
/// Initializes the writer.
|
||||||
public init() {
|
public init() {
|
||||||
@ -13,15 +13,18 @@ public struct OpenSSHKeyWriter: Sendable {
|
|||||||
public func data<SecretType: Secret>(secret: SecretType) -> Data {
|
public func data<SecretType: Secret>(secret: SecretType) -> Data {
|
||||||
switch secret.keyType.algorithm {
|
switch secret.keyType.algorithm {
|
||||||
case .ecdsa:
|
case .ecdsa:
|
||||||
lengthAndData(of: Data(curveType(for: secret.keyType).utf8)) +
|
// https://datatracker.ietf.org/doc/html/rfc5656#section-3.1
|
||||||
lengthAndData(of: Data(curveIdentifier(for: secret.keyType).utf8)) +
|
openSSHIdentifier(for: secret.keyType).lengthAndData +
|
||||||
lengthAndData(of: secret.publicKey)
|
("nistp" + String(describing: secret.keyType.size)).lengthAndData +
|
||||||
|
secret.publicKey.lengthAndData
|
||||||
case .mldsa:
|
case .mldsa:
|
||||||
lengthAndData(of: Data(curveType(for: secret.keyType).utf8)) +
|
// https://www.ietf.org/archive/id/draft-sfluhrer-ssh-mldsa-04.txt
|
||||||
lengthAndData(of: secret.publicKey)
|
openSSHIdentifier(for: secret.keyType).lengthAndData +
|
||||||
|
secret.publicKey.lengthAndData
|
||||||
case .rsa:
|
case .rsa:
|
||||||
lengthAndData(of: Data(curveType(for: secret.keyType).utf8)) +
|
// https://datatracker.ietf.org/doc/html/rfc4253#section-6.6
|
||||||
rsa(secret: secret)
|
openSSHIdentifier(for: secret.keyType).lengthAndData +
|
||||||
|
rsaPublicKeyBlob(secret: secret)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +42,7 @@ public struct OpenSSHKeyWriter: Sendable {
|
|||||||
.replacingOccurrences(of: " ", with: "-")
|
.replacingOccurrences(of: " ", with: "-")
|
||||||
resolvedComment = "\(dashedKeyName)@\(dashedHostName)"
|
resolvedComment = "\(dashedKeyName)@\(dashedHostName)"
|
||||||
}
|
}
|
||||||
return [curveType(for: secret.keyType), data(secret: secret).base64EncodedString(), resolvedComment]
|
return [openSSHIdentifier(for: secret.keyType), data(secret: secret).base64EncodedString(), resolvedComment]
|
||||||
.compactMap { $0 }
|
.compactMap { $0 }
|
||||||
.joined(separator: " ")
|
.joined(separator: " ")
|
||||||
}
|
}
|
||||||
@ -64,63 +67,43 @@ public struct OpenSSHKeyWriter: Sendable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension OpenSSHKeyWriter {
|
extension OpenSSHPublicKeyWriter {
|
||||||
|
|
||||||
/// Creates an OpenSSH protocol style data object, which has a length header, followed by the data payload.
|
|
||||||
/// - Parameter data: The data payload.
|
|
||||||
/// - Returns: OpenSSH data.
|
|
||||||
public func lengthAndData(of data: Data) -> Data {
|
|
||||||
let rawLength = UInt32(data.count)
|
|
||||||
var endian = rawLength.bigEndian
|
|
||||||
return Data(bytes: &endian, count: UInt32.bitWidth/8) + data
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The fully qualified OpenSSH identifier for the algorithm.
|
/// The fully qualified OpenSSH identifier for the algorithm.
|
||||||
/// - Parameters:
|
/// - Parameters:
|
||||||
/// - algorithm: The algorithm to identify.
|
/// - algorithm: The algorithm to identify.
|
||||||
/// - length: The key length of the algorithm.
|
/// - length: The key length of the algorithm.
|
||||||
/// - Returns: The OpenSSH identifier for the algorithm.
|
/// - Returns: The OpenSSH identifier for the algorithm.
|
||||||
public func curveType(for keyType: KeyType) -> String {
|
public func openSSHIdentifier(for keyType: KeyType) -> String {
|
||||||
switch (keyType.algorithm, keyType.size) {
|
switch (keyType.algorithm, keyType.size) {
|
||||||
case (.ecdsa, 256), (.ecdsa, 384):
|
case (.ecdsa, 256), (.ecdsa, 384):
|
||||||
"ecdsa-sha2-nistp" + String(describing: keyType.size)
|
"ecdsa-sha2-nistp" + String(describing: keyType.size)
|
||||||
case (.mldsa, 65), (.mldsa, 87):
|
case (.mldsa, 65), (.mldsa, 87):
|
||||||
"ssh-mldsa" + String(describing: keyType.size)
|
"ssh-mldsa-" + String(describing: keyType.size)
|
||||||
case (.rsa, _):
|
case (.rsa, _):
|
||||||
"ssh-rsa"
|
"ssh-rsa"
|
||||||
default:
|
default:
|
||||||
"unknown"
|
"unknown"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The OpenSSH identifier for an algorithm.
|
}
|
||||||
/// - Parameters:
|
|
||||||
/// - algorithm: The algorithm to identify.
|
|
||||||
/// - length: The key length of the algorithm.
|
|
||||||
/// - Returns: The OpenSSH identifier for the algorithm.
|
|
||||||
private func curveIdentifier(for keyType: KeyType) -> String {
|
|
||||||
switch keyType.algorithm {
|
|
||||||
case .ecdsa:
|
|
||||||
"nistp" + String(describing: keyType.size)
|
|
||||||
case .mldsa:
|
|
||||||
"mldsa" + String(describing: keyType.size)
|
|
||||||
default:
|
|
||||||
fatalError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func rsa<SecretType: Secret>(secret: SecretType) -> Data {
|
extension OpenSSHPublicKeyWriter {
|
||||||
|
|
||||||
|
public func rsaPublicKeyBlob<SecretType: Secret>(secret: SecretType) -> Data {
|
||||||
// Cheap way to pull out e and n as defined in https://datatracker.ietf.org/doc/html/rfc4253
|
// Cheap way to pull out e and n as defined in https://datatracker.ietf.org/doc/html/rfc4253
|
||||||
// Keychain stores it as a thin ASN.1 wrapper with this format:
|
// Keychain stores it as a thin ASN.1 wrapper with this format:
|
||||||
// [4 byte prefix][2 byte prefix][n][2 byte prefix][e]
|
// [4 byte prefix][2 byte prefix][n][2 byte prefix][e]
|
||||||
// Rather than parse out the whole ASN.1 blob, we know how this should be formatted, so pull values directly.
|
// Rather than parse out the whole ASN.1 blob, we'll cheat and pull values directly since
|
||||||
|
// we only support one key type, and the keychain always gives it in a specific format.
|
||||||
let keySize = secret.keyType.size
|
let keySize = secret.keyType.size
|
||||||
guard secret.keyType.algorithm == .rsa && keySize == 2048 else { fatalError() }
|
guard secret.keyType.algorithm == .rsa && keySize == 2048 else { fatalError() }
|
||||||
let length = secret.keyType.size/8
|
let length = secret.keyType.size/8
|
||||||
let data = secret.publicKey
|
let data = secret.publicKey
|
||||||
let n = Data(data[8..<(9+length)])
|
let n = Data(data[8..<(9+length)])
|
||||||
let e = Data(data[(2+9+length)...])
|
let e = Data(data[(2+9+length)...])
|
||||||
return lengthAndData(of: e) + lengthAndData(of: n)
|
return e.lengthAndData + n.lengthAndData
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
import Foundation
|
||||||
|
import CryptoKit
|
||||||
|
|
||||||
|
/// Generates OpenSSH representations of Secrets.
|
||||||
|
public struct OpenSSHSignatureWriter: Sendable {
|
||||||
|
|
||||||
|
/// Initializes the writer.
|
||||||
|
public init() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates an OpenSSH data payload identifying the secret.
|
||||||
|
/// - Returns: OpenSSH data payload identifying the secret.
|
||||||
|
public func data<SecretType: Secret>(secret: SecretType, signature: Data) -> Data {
|
||||||
|
switch secret.keyType.algorithm {
|
||||||
|
case .ecdsa:
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc5656#section-3.1
|
||||||
|
fatalError()
|
||||||
|
case .mldsa:
|
||||||
|
// https://www.ietf.org/archive/id/draft-sfluhrer-ssh-mldsa-04.txt
|
||||||
|
fatalError()
|
||||||
|
case .rsa:
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc4253#section-6.6
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension OpenSSHSignatureWriter {
|
||||||
|
|
||||||
|
|
||||||
|
/// The fully qualified OpenSSH identifier for the algorithm.
|
||||||
|
/// - Parameters:
|
||||||
|
/// - algorithm: The algorithm to identify.
|
||||||
|
/// - length: The key length of the algorithm.
|
||||||
|
/// - Returns: The OpenSSH identifier for the algorithm.
|
||||||
|
public func openSSHIdentifier(for keyType: KeyType) -> String {
|
||||||
|
switch (keyType.algorithm, keyType.size) {
|
||||||
|
case (.ecdsa, 256), (.ecdsa, 384):
|
||||||
|
"ecdsa-sha2-nistp" + String(describing: keyType.size)
|
||||||
|
case (.mldsa, 65), (.mldsa, 87):
|
||||||
|
"ssh-mldsa-" + String(describing: keyType.size)
|
||||||
|
case (.rsa, _):
|
||||||
|
"ssh-rsa"
|
||||||
|
default:
|
||||||
|
"unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension OpenSSHSignatureWriter {
|
||||||
|
|
||||||
|
public func rsaPublicKeyBlob<SecretType: Secret>(secret: SecretType) -> Data {
|
||||||
|
// Cheap way to pull out e and n as defined in https://datatracker.ietf.org/doc/html/rfc4253
|
||||||
|
// Keychain stores it as a thin ASN.1 wrapper with this format:
|
||||||
|
// [4 byte prefix][2 byte prefix][n][2 byte prefix][e]
|
||||||
|
// Rather than parse out the whole ASN.1 blob, we'll cheat and pull values directly since
|
||||||
|
// we only support one key type, and the keychain always gives it in a specific format.
|
||||||
|
let keySize = secret.keyType.size
|
||||||
|
guard secret.keyType.algorithm == .rsa && keySize == 2048 else { fatalError() }
|
||||||
|
let length = secret.keyType.size/8
|
||||||
|
let data = secret.publicKey
|
||||||
|
let n = Data(data[8..<(9+length)])
|
||||||
|
let e = Data(data[(2+9+length)...])
|
||||||
|
return e.lengthAndData + n.lengthAndData
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -6,7 +6,7 @@ public final class PublicKeyFileStoreController: Sendable {
|
|||||||
|
|
||||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "PublicKeyFileStoreController")
|
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "PublicKeyFileStoreController")
|
||||||
private let directory: String
|
private let directory: String
|
||||||
private let keyWriter = OpenSSHKeyWriter()
|
private let keyWriter = OpenSSHPublicKeyWriter()
|
||||||
|
|
||||||
/// Initializes a PublicKeyFileStoreController.
|
/// Initializes a PublicKeyFileStoreController.
|
||||||
public init(homeDirectory: String) {
|
public init(homeDirectory: String) {
|
||||||
|
@ -79,7 +79,8 @@ extension SmartCard {
|
|||||||
}
|
}
|
||||||
let key = untypedSafe as! SecKey
|
let key = untypedSafe as! SecKey
|
||||||
var signError: SecurityError?
|
var signError: SecurityError?
|
||||||
guard let signature = SecKeyCreateSignature(key, signatureAlgorithm(for: secret), data as CFData, &signError) else {
|
guard let algorithm = signatureAlgorithm(for: secret) else { throw UnsupportKeyType() }
|
||||||
|
guard let signature = SecKeyCreateSignature(key, algorithm, data as CFData, &signError) else {
|
||||||
throw SigningError(error: signError)
|
throw SigningError(error: signError)
|
||||||
}
|
}
|
||||||
return signature as Data
|
return signature as Data
|
||||||
@ -153,7 +154,7 @@ extension SmartCard.Store {
|
|||||||
var untyped: CFTypeRef?
|
var untyped: CFTypeRef?
|
||||||
SecItemCopyMatching(attributes, &untyped)
|
SecItemCopyMatching(attributes, &untyped)
|
||||||
guard let typed = untyped as? [[CFString: Any]] else { return }
|
guard let typed = untyped as? [[CFString: Any]] else { return }
|
||||||
let wrapped = typed.map {
|
let wrapped: [SecretType] = typed.compactMap {
|
||||||
let name = $0[kSecAttrLabel] as? String ?? String(localized: .unnamedSecret)
|
let name = $0[kSecAttrLabel] as? String ?? String(localized: .unnamedSecret)
|
||||||
let tokenID = $0[kSecAttrApplicationLabel] as! Data
|
let tokenID = $0[kSecAttrApplicationLabel] as! Data
|
||||||
let algorithmSecAttr = $0[kSecAttrKeyType] as! NSNumber
|
let algorithmSecAttr = $0[kSecAttrKeyType] as! NSNumber
|
||||||
@ -163,7 +164,9 @@ extension SmartCard.Store {
|
|||||||
let publicKeyAttributes = SecKeyCopyAttributes(publicKeySecRef) as! [CFString: Any]
|
let publicKeyAttributes = SecKeyCopyAttributes(publicKeySecRef) as! [CFString: Any]
|
||||||
let publicKey = publicKeyAttributes[kSecValueData] as! Data
|
let publicKey = publicKeyAttributes[kSecValueData] as! Data
|
||||||
let attributes = Attributes(keyType: KeyType(secAttr: algorithmSecAttr, size: keySize)!, authentication: .unknown)
|
let attributes = Attributes(keyType: KeyType(secAttr: algorithmSecAttr, size: keySize)!, authentication: .unknown)
|
||||||
return SmartCard.Secret(id: tokenID, name: name, publicKey: publicKey, attributes: attributes)
|
let secret = SmartCard.Secret(id: tokenID, name: name, publicKey: publicKey, attributes: attributes)
|
||||||
|
guard signatureAlgorithm(for: secret) != nil else { return nil }
|
||||||
|
return secret
|
||||||
}
|
}
|
||||||
state.secrets.append(contentsOf: wrapped)
|
state.secrets.append(contentsOf: wrapped)
|
||||||
}
|
}
|
||||||
@ -178,3 +181,9 @@ extension TKTokenWatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension SmartCard {
|
||||||
|
|
||||||
|
public struct UnsupportKeyType: Error {}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -45,7 +45,7 @@ extension Stub {
|
|||||||
let privateData = (privateAttributes[kSecValueData] as! Data)
|
let privateData = (privateAttributes[kSecValueData] as! Data)
|
||||||
let secret = Secret(keySize: size, publicKey: publicData, privateKey: privateData)
|
let secret = Secret(keySize: size, publicKey: publicData, privateKey: privateData)
|
||||||
print(secret)
|
print(secret)
|
||||||
print("Public Key OpenSSH: \(OpenSSHKeyWriter().openSSHString(secret: secret))")
|
print("Public Key OpenSSH: \(OpenSSHPublicKeyWriter().openSSHString(secret: secret))")
|
||||||
}
|
}
|
||||||
|
|
||||||
public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) throws -> Data {
|
public func sign(data: Data, with secret: Secret, for provenance: SigningRequestProvenance) throws -> Data {
|
||||||
|
@ -4,11 +4,12 @@ import Testing
|
|||||||
@testable import SecureEnclaveSecretKit
|
@testable import SecureEnclaveSecretKit
|
||||||
@testable import SmartCardSecretKit
|
@testable import SmartCardSecretKit
|
||||||
|
|
||||||
|
|
||||||
@Suite struct AnySecretTests {
|
@Suite struct AnySecretTests {
|
||||||
|
|
||||||
@Test func eraser() {
|
@Test func eraser() {
|
||||||
let data = Data(UUID().uuidString.utf8)
|
let data = Data(UUID().uuidString.utf8)
|
||||||
let secret = SmartCard.Secret(id: data, name: "Name", publicKey: data, attributes: Attributes(keyType: KeyType(algorithm: .ecdsa, size: 256)))
|
let secret = SmartCard.Secret(id: data, name: "Name", publicKey: data, attributes: Attributes(keyType: KeyType(algorithm: .ecdsa, size: 256), authentication: .notRequired))
|
||||||
let erased = AnySecret(secret)
|
let erased = AnySecret(secret)
|
||||||
#expect(erased.id == secret.id as AnyHashable)
|
#expect(erased.id == secret.id as AnyHashable)
|
||||||
#expect(erased.name == secret.name)
|
#expect(erased.name == secret.name)
|
||||||
|
@ -4,9 +4,9 @@ import Testing
|
|||||||
@testable import SecureEnclaveSecretKit
|
@testable import SecureEnclaveSecretKit
|
||||||
@testable import SmartCardSecretKit
|
@testable import SmartCardSecretKit
|
||||||
|
|
||||||
@Suite struct OpenSSHWriterTests {
|
@Suite struct OpenSSHPublicKeyWriterTests {
|
||||||
|
|
||||||
let writer = OpenSSHKeyWriter()
|
let writer = OpenSSHPublicKeyWriter()
|
||||||
|
|
||||||
@Test func ecdsa256MD5Fingerprint() {
|
@Test func ecdsa256MD5Fingerprint() {
|
||||||
#expect(writer.openSSHMD5Fingerprint(secret: Constants.ecdsa256Secret) == "dc:60:4d:ff:c2:d9:18:8b:2f:24:40:b5:7f:43:47:e5")
|
#expect(writer.openSSHMD5Fingerprint(secret: Constants.ecdsa256Secret) == "dc:60:4d:ff:c2:d9:18:8b:2f:24:40:b5:7f:43:47:e5")
|
||||||
@ -44,11 +44,11 @@ import Testing
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension OpenSSHWriterTests {
|
extension OpenSSHPublicKeyWriterTests {
|
||||||
|
|
||||||
enum Constants {
|
enum Constants {
|
||||||
static let ecdsa256Secret = SmartCard.Secret(id: Data(), name: "Test Key (ECDSA 256)", publicKey: Data(base64Encoded: "BOVEjgAA5PHqRgwykjN5qM21uWCHFSY/Sqo5gkHAkn+e1MMQKHOLga7ucB9b3mif33MBid59GRK9GEPVlMiSQwo=")!, attributes: Attributes(keyType: KeyType(algorithm: .ecdsa, size: 256), publicKeyAttribution: "test@example.com"))
|
static let ecdsa256Secret = SmartCard.Secret(id: Data(), name: "Test Key (ECDSA 256)", publicKey: Data(base64Encoded: "BOVEjgAA5PHqRgwykjN5qM21uWCHFSY/Sqo5gkHAkn+e1MMQKHOLga7ucB9b3mif33MBid59GRK9GEPVlMiSQwo=")!, attributes: Attributes(keyType: KeyType(algorithm: .ecdsa, size: 256), authentication: .notRequired, publicKeyAttribution: "test@example.com"))
|
||||||
static let ecdsa384Secret = SmartCard.Secret(id: Data(), name: "Test Key (ECDSA 384)", publicKey: Data(base64Encoded: "BG2MNc/C5OTHFE2tBvbZCVcpOGa8vBMquiTLkH4lwkeqOPxhi+PyYUfQZMTRJNPiTyWPoMBqNiCIFRVv60yPN/AHufHaOgbdTP42EgMlMMImkAjYUEv9DESHTVIs2PW1yQ==")!, attributes: Attributes(keyType: KeyType(algorithm: .ecdsa, size: 384), publicKeyAttribution: "test@example.com"))
|
static let ecdsa384Secret = SmartCard.Secret(id: Data(), name: "Test Key (ECDSA 384)", publicKey: Data(base64Encoded: "BG2MNc/C5OTHFE2tBvbZCVcpOGa8vBMquiTLkH4lwkeqOPxhi+PyYUfQZMTRJNPiTyWPoMBqNiCIFRVv60yPN/AHufHaOgbdTP42EgMlMMImkAjYUEv9DESHTVIs2PW1yQ==")!, attributes: Attributes(keyType: KeyType(algorithm: .ecdsa, size: 384), authentication: .notRequired, publicKeyAttribution: "test@example.com"))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ struct SecretDetailView<SecretType: Secret>: View {
|
|||||||
|
|
||||||
let secret: SecretType
|
let secret: SecretType
|
||||||
|
|
||||||
private let keyWriter = OpenSSHKeyWriter()
|
private let keyWriter = OpenSSHPublicKeyWriter()
|
||||||
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: NSHomeDirectory().replacingOccurrences(of: Bundle.main.hostBundleID, with: Bundle.main.agentBundleID))
|
private let publicKeyFileStoreController = PublicKeyFileStoreController(homeDirectory: NSHomeDirectory().replacingOccurrences(of: Bundle.main.hostBundleID, with: Bundle.main.agentBundleID))
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
Loading…
Reference in New Issue
Block a user