From 2994861f45071bb9493cde3c27cca9e7383f34e6 Mon Sep 17 00:00:00 2001 From: Max Goedjen Date: Sat, 6 Sep 2025 14:19:40 -0700 Subject: [PATCH] WIP --- .../Sources/SecretAgentKit/Agent.swift | 12 ++------ .../OpenSSHCertificateHandler.swift | 28 +------------------ .../OpenSSHReader.swift | 2 +- .../SecretAgentKit/SSHAgentInputParser.swift | 26 ++++++++++++++++- 4 files changed, 29 insertions(+), 39 deletions(-) rename Sources/Packages/Sources/{SecretKit/OpenSSH => SecretAgentKit}/OpenSSHCertificateHandler.swift (73%) rename Sources/Packages/Sources/{SecretKit/OpenSSH => SecretAgentKit}/OpenSSHReader.swift (97%) diff --git a/Sources/Packages/Sources/SecretAgentKit/Agent.swift b/Sources/Packages/Sources/SecretAgentKit/Agent.swift index c287470..e1fbf00 100644 --- a/Sources/Packages/Sources/SecretAgentKit/Agent.swift +++ b/Sources/Packages/Sources/SecretAgentKit/Agent.swift @@ -92,16 +92,8 @@ extension Agent { /// - provenance: A ``SecretKit.SigningRequestProvenance`` object describing the origin of the request. /// - Returns: An OpenSSH formatted Data payload containing the signed data response. func sign(data: Data, keyBlob: Data, provenance: SigningRequestProvenance) async throws -> Data { - // Check if hash is actually an openssh certificate and reconstruct the public key if it is - let resolvedBlob: Data - if let certificatePublicKey = await certificateHandler.publicKeyHash(from: keyBlob) { - resolvedBlob = certificatePublicKey - } else { - resolvedBlob = keyBlob - } - - guard let (secret, store) = await secret(matching: resolvedBlob) else { - logger.debug("Agent did not have a key matching \(resolvedBlob as NSData)") + guard let (secret, store) = await secret(matching: keyBlob) else { + logger.debug("Agent did not have a key matching \(keyBlob as NSData)") throw NoMatchingKeyError() } diff --git a/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHCertificateHandler.swift b/Sources/Packages/Sources/SecretAgentKit/OpenSSHCertificateHandler.swift similarity index 73% rename from Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHCertificateHandler.swift rename to Sources/Packages/Sources/SecretAgentKit/OpenSSHCertificateHandler.swift index a5af72c..5451e49 100644 --- a/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHCertificateHandler.swift +++ b/Sources/Packages/Sources/SecretAgentKit/OpenSSHCertificateHandler.swift @@ -1,5 +1,6 @@ import Foundation import OSLog +import SecretKit /// Manages storage and lookup for OpenSSH certificates. public actor OpenSSHCertificateHandler: Sendable { @@ -25,33 +26,6 @@ public actor OpenSSHCertificateHandler: Sendable { } } - /// Reconstructs a public key from a ``Data``, if that ``Data`` contains an OpenSSH certificate hash. Currently only ecdsa certificates are supported - /// - Parameter certBlock: The openssh certificate to extract the public key from - /// - Returns: A ``Data`` object containing the public key in OpenSSH wire format if the ``Data`` is an OpenSSH certificate hash, otherwise nil. - public func publicKeyHash(from hash: Data) -> Data? { - let reader = OpenSSHReader(data: hash) - do { - let certType = String(decoding: try reader.readNextChunk(), as: UTF8.self) - switch certType { - case "ecdsa-sha2-nistp256-cert-v01@openssh.com", - "ecdsa-sha2-nistp384-cert-v01@openssh.com", - "ecdsa-sha2-nistp521-cert-v01@openssh.com": - _ = try reader.readNextChunk() // nonce - let curveIdentifier = try reader.readNextChunk() - let publicKey = try reader.readNextChunk() - - let openSSHIdentifier = certType.replacingOccurrences(of: "-cert-v01@openssh.com", with: "") - return openSSHIdentifier.lengthAndData + - curveIdentifier.lengthAndData + - publicKey.lengthAndData - default: - return nil - } - } catch { - return nil - } - } - /// Attempts to find an OpenSSH Certificate that corresponds to a ``Secret`` /// - Parameter secret: The secret to search for a certificate with /// - Returns: A (``Data``, ``Data``) tuple containing the certificate and certificate name, respectively. diff --git a/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHReader.swift b/Sources/Packages/Sources/SecretAgentKit/OpenSSHReader.swift similarity index 97% rename from Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHReader.swift rename to Sources/Packages/Sources/SecretAgentKit/OpenSSHReader.swift index 22417cb..63107f9 100644 --- a/Sources/Packages/Sources/SecretKit/OpenSSH/OpenSSHReader.swift +++ b/Sources/Packages/Sources/SecretAgentKit/OpenSSHReader.swift @@ -1,7 +1,7 @@ import Foundation /// Reads OpenSSH protocol data. -public final class OpenSSHReader { +final class OpenSSHReader { var remaining: Data diff --git a/Sources/Packages/Sources/SecretAgentKit/SSHAgentInputParser.swift b/Sources/Packages/Sources/SecretAgentKit/SSHAgentInputParser.swift index 29fba64..df74c8a 100644 --- a/Sources/Packages/Sources/SecretAgentKit/SSHAgentInputParser.swift +++ b/Sources/Packages/Sources/SecretAgentKit/SSHAgentInputParser.swift @@ -59,11 +59,35 @@ extension SSHAgentInputParser { func signatureRequestContext(from data: Data) throws -> SSHAgent.Request.SignatureRequestContext { let reader = OpenSSHReader(data: data) - let keyBlob = try reader.readNextChunk() + let rawKeyBlob = try reader.readNextChunk() + let keyBlob = certificatePublicKeyBlob(from: rawKeyBlob) ?? rawKeyBlob let dataToSign = try reader.readNextChunk() return SSHAgent.Request.SignatureRequestContext(keyBlob: keyBlob, dataToSign: dataToSign) } + func certificatePublicKeyBlob(from hash: Data) -> Data? { + let reader = OpenSSHReader(data: hash) + do { + let certType = String(decoding: try reader.readNextChunk(), as: UTF8.self) + switch certType { + case "ecdsa-sha2-nistp256-cert-v01@openssh.com", + "ecdsa-sha2-nistp384-cert-v01@openssh.com", + "ecdsa-sha2-nistp521-cert-v01@openssh.com": + _ = try reader.readNextChunk() // nonce + let curveIdentifier = try reader.readNextChunk() + let publicKey = try reader.readNextChunk() + let openSSHIdentifier = certType.replacingOccurrences(of: "-cert-v01@openssh.com", with: "") + return openSSHIdentifier.lengthAndData + + curveIdentifier.lengthAndData + + publicKey.lengthAndData + default: + return nil + } + } catch { + return nil + } + } + }