Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion Sources/NIOSSH/Keys And Signatures/NIOSSHPrivateKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,19 @@ public struct NIOSSHPrivateKey: Sendable {
}
#endif

/// Creates a private key backed by a signing delegate.
///
/// This initializer enables integration with SSH agents, hardware security modules,
/// or other secure key management systems by delegating signing operations to an
/// external system.
///
/// - Parameter publicKey: The public key corresponding to the private key managed by the delegate
/// - Parameter signingCallback: The delegated signing operation callback
public init(publicKey: NIOSSHPublicKey,
signingCallback: @escaping @Sendable (ByteBufferView) throws -> NIOSSHSignature) {
self.backingKey = .signingDelegate(signingCallback, publicKey)
}

// The algorithms that apply to this host key.
internal var hostKeyAlgorithms: [Substring] {
switch self.backingKey {
Expand All @@ -69,6 +82,8 @@ public struct NIOSSHPrivateKey: Sendable {
return ["ecdsa-sha2-nistp384"]
case .ecdsaP521:
return ["ecdsa-sha2-nistp521"]
case .signingDelegate(_, let publicKey):
return [String(publicKey.keyPrefix)[...]]
#if canImport(Darwin)
case .secureEnclaveP256:
return ["ecdsa-sha2-nistp256"]
Expand All @@ -79,11 +94,12 @@ public struct NIOSSHPrivateKey: Sendable {

extension NIOSSHPrivateKey {
/// The various key types that can be used with NIOSSH.
internal enum BackingKey {
internal enum BackingKey: Sendable {
case ed25519(Curve25519.Signing.PrivateKey)
case ecdsaP256(P256.Signing.PrivateKey)
case ecdsaP384(P384.Signing.PrivateKey)
case ecdsaP521(P521.Signing.PrivateKey)
case signingDelegate(@Sendable (ByteBufferView) throws -> NIOSSHSignature, NIOSSHPublicKey)

#if canImport(Darwin)
case secureEnclaveP256(SecureEnclave.P256.Signing.PrivateKey)
Expand Down Expand Up @@ -115,6 +131,11 @@ extension NIOSSHPrivateKey {
}
return NIOSSHSignature(backingSignature: .ecdsaP521(signature))

case .signingDelegate:
// Signing delegates are not supported for digest-based signing
// This method is used for host key authentication, not user auth
throw NIOSSHError.unknownPublicKey(algorithm: "signingDelegate")

#if canImport(Darwin)
case .secureEnclaveP256(let key):
let signature = try digest.withUnsafeBytes { ptr in
Expand All @@ -139,6 +160,9 @@ extension NIOSSHPrivateKey {
case .ecdsaP521(let key):
let signature = try key.signature(for: payload.bytes.readableBytesView)
return NIOSSHSignature(backingSignature: .ecdsaP521(signature))
case .signingDelegate(let sign, _):
let sshSignature = try sign(payload.bytes.readableBytesView)
return sshSignature
#if canImport(Darwin)
case .secureEnclaveP256(let key):
let signature = try key.signature(for: payload.bytes.readableBytesView)
Expand All @@ -160,6 +184,8 @@ extension NIOSSHPrivateKey {
return NIOSSHPublicKey(backingKey: .ecdsaP384(privateKey.publicKey))
case .ecdsaP521(let privateKey):
return NIOSSHPublicKey(backingKey: .ecdsaP521(privateKey.publicKey))
case .signingDelegate(_, let publicKey):
return publicKey
#if canImport(Darwin)
case .secureEnclaveP256(let privateKey):
return NIOSSHPublicKey(backingKey: .ecdsaP256(privateKey.publicKey))
Expand Down
47 changes: 47 additions & 0 deletions Sources/NIOSSH/Keys And Signatures/NIOSSHSignature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -395,3 +395,50 @@ extension P384.Signing.ECDSASignature: ECDSASignatureProtocol {
extension P521.Signing.ECDSASignature: ECDSASignatureProtocol {
static var pointSize: Int { 66 }
}

// MARK: - Public constructors for signing delegates

extension NIOSSHSignature {
/// Create an ed25519 signature from raw signature data.
///
/// This is used by signing delegates, such as SSH agents,
/// to create an an ed25519 signature from raw signature data.
/// - Parameter signature: The raw ed25519 signature data.
/// - Returns: The created NIOSSH signature.
public static func ed25519(signature: Data) -> NIOSSHSignature {
return NIOSSHSignature(backingSignature: .ed25519(.data(signature)))
}

/// Create an ECDSA P-256 signature from raw signature data.
///
/// This is used by signing delegates, such as SSH agents,
/// to create an ECDSA P-256 signature from raw signature data.
/// - Parameter signature: The raw ECDSA P-256 signature data.
/// - Returns: The created NIOSSH signature.
public static func ecdsaP256(signature: Data) throws -> NIOSSHSignature {
let ecdsaSignature = try P256.Signing.ECDSASignature(rawRepresentation: signature)
return NIOSSHSignature(backingSignature: .ecdsaP256(ecdsaSignature))
}

/// Create an ECDSA P-384 signature from raw signature data.
///
/// This is used by signing delegates, such as SSH agents,
/// to create an ECDSA P-384 signature from raw signature data.
/// - Parameter signature: The raw ECDSA P-384 signature data.
/// - Returns: The created NIOSSH signature.
public static func ecdsaP384(signature: Data) throws -> NIOSSHSignature {
let ecdsaSignature = try P384.Signing.ECDSASignature(rawRepresentation: signature)
return NIOSSHSignature(backingSignature: .ecdsaP384(ecdsaSignature))
}

/// Create an ECDSA P-521 signature from raw signature data.
///
/// This is used by signing delegates, such as SSH agents,
/// to create an ECDSA P-521 signature from raw signature data.
/// - Parameter signature: The raw ECDSA P-521 signature data.
/// - Returns: The created NIOSSH signature.
public static func ecdsaP521(signature: Data) throws -> NIOSSHSignature {
let ecdsaSignature = try P521.Signing.ECDSASignature(rawRepresentation: signature)
return NIOSSHSignature(backingSignature: .ecdsaP521(ecdsaSignature))
}
}