Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate public/private keys and signatures into protocol conformances #158

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
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
138 changes: 138 additions & 0 deletions Sources/NIOSSH/Keys And Signatures/CryptoKit+PrivateKeyProtocols.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2023 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import Foundation
import NIOCore
import Crypto

struct Curve25519Signature: NIOSSHSignatureProtocol {
static let signaturePrefix = "ssh-ed25519"

let rawRepresentation: Data

func write(to buffer: inout ByteBuffer) -> Int {
buffer.writeEd25519Signature(signature: self)
}

static func read(from buffer: inout ByteBuffer) throws -> Self {
guard var rawRepresentation = buffer.readSSHString() else {
throw NIOSSHError.invalidSSHSignature
}

return Self(rawRepresentation: rawRepresentation.readData(length: rawRepresentation.readableBytes)!)
}
}

extension Curve25519.Signing.PrivateKey: NIOSSHPrivateKeyProtocol {
public static var keyPrefix: String { "ssh-ed25519" }

public var sshPublicKey: NIOSSHPublicKeyProtocol { publicKey }

public func sshSignature<D: DataProtocol>(for data: D) throws -> NIOSSHSignature {
let signature = try Curve25519Signature(rawRepresentation: self.signature(for: data))
return NIOSSHSignature(backingSignature: .ed25519(signature))
}
}

extension P256.Signing.ECDSASignature: NIOSSHSignatureProtocol {
public static var signaturePrefix: String { "ecdsa-sha2-nistp256" }

public func write(to buffer: inout ByteBuffer) -> Int {
buffer.writeECDSAP256Signature(baseSignature: self)
}

public static func read(from buffer: inout ByteBuffer) throws -> Self {
guard let rawRepresentation = buffer.readSSHString() else {
throw NIOSSHError.invalidSSHSignature
}

return try Self(rawRepresentation: rawRepresentation.readableBytesView)
}
}

extension P256.Signing.PrivateKey: NIOSSHPrivateKeyProtocol {
public static var keyPrefix: String { "ecdsa-sha2-nistp256" }

public var sshPublicKey: NIOSSHPublicKeyProtocol { publicKey }

public func sshSignature<D: DataProtocol>(for data: D) throws -> NIOSSHSignature {
return try NIOSSHSignature(backingSignature: .p256(self.signature(for: data)))
}
}

extension P384.Signing.ECDSASignature: NIOSSHSignatureProtocol {
public static var signaturePrefix: String { "ecdsa-sha2-nistp384" }

public func write(to buffer: inout ByteBuffer) -> Int {
buffer.writeECDSAP384Signature(baseSignature: self)
}

public static func read(from buffer: inout ByteBuffer) throws -> Self {
guard let rawRepresentation = buffer.readSSHString() else {
throw NIOSSHError.invalidSSHSignature
}

return try Self(rawRepresentation: rawRepresentation.readableBytesView)
}
}

extension P384.Signing.PrivateKey: NIOSSHPrivateKeyProtocol {
public static var keyPrefix: String { "ecdsa-sha2-nistp384" }

public var sshPublicKey: NIOSSHPublicKeyProtocol { publicKey }

public func sshSignature<D: DataProtocol>(for data: D) throws -> NIOSSHSignature {
return try NIOSSHSignature(backingSignature: .p384(self.signature(for: data)))
}
}

extension P521.Signing.ECDSASignature: NIOSSHSignatureProtocol {
public static var signaturePrefix: String { "ecdsa-sha2-nistp521" }

public func write(to buffer: inout ByteBuffer) -> Int {
buffer.writeECDSAP521Signature(baseSignature: self)
}

public static func read(from buffer: inout ByteBuffer) throws -> Self {
guard let rawRepresentation = buffer.readSSHString() else {
throw NIOSSHError.invalidSSHSignature
}

return try Self(rawRepresentation: rawRepresentation.readableBytesView)
}
}

extension P521.Signing.PrivateKey: NIOSSHPrivateKeyProtocol {
public static var keyPrefix: String {
"ecdsa-sha2-nistp521"
}

public var sshPublicKey: NIOSSHPublicKeyProtocol { publicKey }

public func sshSignature<D: DataProtocol>(for data: D) throws -> NIOSSHSignature {
return try NIOSSHSignature(backingSignature: .p521(self.signature(for: data)))
}
}

#if canImport(Darwin)
extension SecureEnclave.P256.Signing.PrivateKey: NIOSSHPrivateKeyProtocol {
public static var keyPrefix: String { P256.Signing.PrivateKey.keyPrefix }

public var sshPublicKey: NIOSSHPublicKeyProtocol { publicKey }

public func sshSignature<D: DataProtocol>(for data: D) throws -> NIOSSHSignature {
return try NIOSSHSignature(backingSignature: .p256(self.signature(for: data)))
}
}
#endif
179 changes: 179 additions & 0 deletions Sources/NIOSSH/Keys And Signatures/CryptoKit+PublicKeyProtocols.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2023 Apple Inc. and the SwiftNIO project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import Foundation
import NIOCore
import Crypto

extension Curve25519.Signing.PublicKey: NIOSSHPublicKeyProtocol {
internal static var prefix: String { "ssh-ed25519" }
public static var publicKeyPrefix: String? { Self.prefix }
public var publicKeyPrefix: String { Self.prefix }

public func isValidSignature<D: DataProtocol>(_ signature: NIOSSHSignature, for data: D) -> Bool {
guard case .ed25519(let signature) = signature._backingSignature else {
return false
}

return self.isValidSignature(signature.rawRepresentation, for: data)
}

public func write(to buffer: inout ByteBuffer) -> Int {
buffer.writeEd25519PublicKey(baseKey: self)
}

public func writeHostKey(to buffer: inout ByteBuffer) -> Int {
var writtenBytes = 0
writtenBytes += buffer.writeSSHString(self.publicKeyPrefix.utf8)
writtenBytes += self.write(to: &buffer)
return writtenBytes
}

public static func read(from buffer: inout ByteBuffer) throws -> Self? {
guard let qBytes = buffer.readSSHString() else {
return nil
}

return try Curve25519.Signing.PublicKey(rawRepresentation: qBytes.readableBytesView)
}
}

extension P256.Signing.PublicKey: NIOSSHPublicKeyProtocol {
internal static var prefix: String { "ecdsa-sha2-nistp256" }
public static var publicKeyPrefix: String? { Self.prefix }
public var publicKeyPrefix: String { Self.prefix }

public func isValidSignature<D: DataProtocol>(_ signature: NIOSSHSignature, for data: D) -> Bool {
guard case .p256(let signature) = signature._backingSignature else {
return false
}

return self.isValidSignature(signature, for: data)
}

public func write(to buffer: inout ByteBuffer) -> Int {
buffer.writeECDSAP256PublicKey(baseKey: self)
}

public func writeHostKey(to buffer: inout ByteBuffer) -> Int {
var writtenBytes = 0
writtenBytes += buffer.writeSSHString(self.publicKeyPrefix.utf8)
writtenBytes += self.write(to: &buffer)
return writtenBytes
}

public static func read(from buffer: inout ByteBuffer) throws -> Self? {
// For ECDSA-P256, the key format is the string "nistp256" followed by the
// the public point Q.
guard var domainParameter = buffer.readSSHString() else {
return nil
}
guard domainParameter.readableBytesView.elementsEqual("nistp256".utf8) else {
let unexpectedParameter = domainParameter.readString(length: domainParameter.readableBytes) ?? "<unknown domain parameter>"
throw NIOSSHError.invalidDomainParametersForKey(parameters: unexpectedParameter)
}

guard let qBytes = buffer.readSSHString() else {
return nil
}

return try P256.Signing.PublicKey(x963Representation: qBytes.readableBytesView)
}
}

extension P384.Signing.PublicKey: NIOSSHPublicKeyProtocol {
internal static var prefix: String { "ecdsa-sha2-nistp384" }
public static var publicKeyPrefix: String? { Self.prefix }
public var publicKeyPrefix: String { Self.prefix }

public func isValidSignature<D: DataProtocol>(_ signature: NIOSSHSignature, for data: D) -> Bool {
guard case .p384(let signature) = signature._backingSignature else {
return false
}

return self.isValidSignature(signature, for: data)
}

public func write(to buffer: inout ByteBuffer) -> Int {
buffer.writeECDSAP384PublicKey(baseKey: self)
}

public func writeHostKey(to buffer: inout ByteBuffer) -> Int {
var writtenBytes = 0
writtenBytes += buffer.writeSSHString(self.publicKeyPrefix.utf8)
writtenBytes += self.write(to: &buffer)
return writtenBytes
}

public static func read(from buffer: inout ByteBuffer) throws -> Self? {
// For ECDSA-P384, the key format is the string "nistp384" followed by the
// the public point Q.
guard var domainParameter = buffer.readSSHString() else {
return nil
}
guard domainParameter.readableBytesView.elementsEqual("nistp384".utf8) else {
let unexpectedParameter = domainParameter.readString(length: domainParameter.readableBytes) ?? "<unknown domain parameter>"
throw NIOSSHError.invalidDomainParametersForKey(parameters: unexpectedParameter)
}

guard let qBytes = buffer.readSSHString() else {
return nil
}

return try P384.Signing.PublicKey(x963Representation: qBytes.readableBytesView)
}
}

extension P521.Signing.PublicKey: NIOSSHPublicKeyProtocol {
internal static var prefix: String { "ecdsa-sha2-nistp521" }
public static var publicKeyPrefix: String? { Self.prefix }
public var publicKeyPrefix: String { Self.prefix }

public func isValidSignature<D: DataProtocol>(_ signature: NIOSSHSignature, for data: D) -> Bool {
guard case .p521(let signature) = signature._backingSignature else {
return false
}

return self.isValidSignature(signature, for: data)
}

public func write(to buffer: inout ByteBuffer) -> Int {
buffer.writeECDSAP521PublicKey(baseKey: self)
}

public func writeHostKey(to buffer: inout ByteBuffer) -> Int {
var writtenBytes = 0
writtenBytes += buffer.writeSSHString(self.publicKeyPrefix.utf8)
writtenBytes += self.write(to: &buffer)
return writtenBytes
}

public static func read(from buffer: inout ByteBuffer) throws -> Self? {
// For ECDSA-P521, the key format is the string "nistp521" followed by the
// the public point Q.
guard var domainParameter = buffer.readSSHString() else {
return nil
}
guard domainParameter.readableBytesView.elementsEqual("nistp521".utf8) else {
let unexpectedParameter = domainParameter.readString(length: domainParameter.readableBytes) ?? "<unknown domain parameter>"
throw NIOSSHError.invalidDomainParametersForKey(parameters: unexpectedParameter)
}

guard let qBytes = buffer.readSSHString() else {
return nil
}

return try P521.Signing.PublicKey(x963Representation: qBytes.readableBytesView)
}
}
Loading