Traversio

Public-Key Authentication

Ed25519, RSA, and ECDSA public-key authentication, including raw-key, OpenSSH key-loading, and OpenSSH key-generation paths.

Public-Key Methods

Traversio exposes Ed25519, RSA, plus ECDSA P-256, P-384, and P-521 public-key authentication:

.ed25519PrivateKey(rawRepresentation: [UInt8])
.rsaPrivateKey(pkcs1DERRepresentation: [UInt8])
.ecdsaP256PrivateKey(rawRepresentation: [UInt8])
.ecdsaP384PrivateKey(rawRepresentation: [UInt8])
.ecdsaP521PrivateKey(rawRepresentation: [UInt8])
try .ed25519PrivateKey(openSSHPrivateKey: String, passphrase: String? = nil)
try .ed25519PrivateKey(contentsOfOpenSSHPrivateKeyFile: String, passphrase: String? = nil)
try .rsaPrivateKey(openSSHPrivateKey: String, passphrase: String? = nil)
try .rsaPrivateKey(contentsOfOpenSSHPrivateKeyFile: String, passphrase: String? = nil)
try .ecdsaPrivateKey(openSSHPrivateKey: String, passphrase: String? = nil)
try .ecdsaPrivateKey(contentsOfOpenSSHPrivateKeyFile: String, passphrase: String? = nil)

The Ed25519 raw-value case expects a 32-byte private-key seed. The RSA raw-value case expects PKCS#1 DER private-key bytes. The ECDSA raw-value cases expect the corresponding curve-specific raw private-key representation. The throwing helpers load OpenSSH openssh-key-v1 Ed25519, RSA, or ECDSA private keys and return the matching auth case. Pass passphrase: when the file is encrypted with the OpenSSH bcrypt KDF and a supported AES cipher.

OpenSSH Key Generation

Traversio also exposes a small OpenSSH key-generation surface:

SSHOpenSSHKeyPair.generate(
    algorithm: .ed25519,
    comment: String = "traversio",
    encryption: SSHOpenSSHPrivateKeyEncryption? = nil
)

Supported key-pair algorithms:

  • .ed25519
  • .ecdsaP256
  • .ecdsaP384
  • .ecdsaP521
  • .rsa(bitCount: Int)

SSHOpenSSHPrivateKeyEncryption(passphrase:cipher:rounds:) supports:

  • aes128-ctr
  • aes192-ctr
  • aes256-ctr
  • aes128-cbc
  • aes192-cbc
  • aes256-cbc

The default encrypted export matches the OpenSSH baseline: bcrypt with aes256-ctr and 24 rounds.

For a full workflow guide with file-writing examples, permission handling, and direct use of the generated authenticationMethod, see Key Generation.

Generation Example

import Foundation
import Traversio

@available(macOS 26.0, iOS 26.0, *)
func makeDeploymentKey() throws -> SSHAuthenticationMethod {
    let keyPair = try SSHOpenSSHKeyPair.generate(
        algorithm: .ed25519,
        comment: "[email protected]",
        encryption: SSHOpenSSHPrivateKeyEncryption(
            passphrase: "correct horse battery staple"
        )
    )

    let sshDirectory = FileManager.default.homeDirectoryForCurrentUser
        .appendingPathComponent(".ssh", isDirectory: true)
    let privateKeyURL = sshDirectory.appendingPathComponent("id_traversio")
    let publicKeyURL = sshDirectory.appendingPathComponent("id_traversio.pub")

    try keyPair.privateKeyPEM.write(to: privateKeyURL, atomically: true, encoding: .utf8)
    try keyPair.authorizedKeyLine.write(to: publicKeyURL, atomically: true, encoding: .utf8)
    try FileManager.default.setAttributes(
        [.posixPermissions: 0o600],
        ofItemAtPath: privateKeyURL.path
    )

    return keyPair.authenticationMethod
}

End-to-End Example

import Traversio

@available(macOS 26.0, iOS 26.0, *)
func connectWithECDSAKeyFile() async throws {
    let configuration = SSHClientConfiguration(
        host: "example.com",
        username: "deploy",
        authentication: try .ecdsaPrivateKey(
            contentsOfOpenSSHPrivateKeyFile: "/Users/me/.ssh/id_ecdsa",
            passphrase: "correct horse battery staple"
        ),
        hostKeyPolicy: .knownHostsFile("/Users/me/.ssh/known_hosts")
    )

    try await SSHClient.withConnection(configuration: configuration) { connection in
        let result = try await connection.execute("whoami")
        print(String(decoding: result.standardOutput, as: UTF8.self))
    }
}

If you already manage key material in memory, the raw cases remain available for Ed25519, RSA, and each supported ECDSA curve. SSHOpenSSHKeyPair.generate(...) covers the matching OpenSSH file-generation workflow.

Request Flow

For the OpenSSH-compatible path, Traversio:

  1. constructs the method-specific public-key request for ssh-ed25519, rsa-sha2-512 / rsa-sha2-256, optional legacy ssh-rsa, or ecdsa-sha2-nistp*
  2. handles the method-specific SSH_MSG_USERAUTH_PK_OK confirmation step
  3. signs the session-identifier-bound authentication payload
  4. retries with the signed request

For RSA keys, Traversio prefers rsa-sha2-512 and falls back to rsa-sha2-256 when the server's server-sig-algs extension only advertises that SHA-256 path.

If you enable SSHLegacyAlgorithmOptions.sshRSA on SSHClientConfiguration or one SSHProxyJumpHost, Traversio also enables the legacy RSA/SHA-1 path for that specific connection or hop. It appends ssh-rsa to the preferred host-key list, then retries RSA public-key authentication with ssh-rsa only after the server declines the SHA-2 attempt and still offers publickey.

RSA and ECDSA public-key authentication are already live-validated against OpenSSH. Ed25519, RSA, and ECDSA request and signature handling is also covered by deterministic protocol tests. Generated OpenSSH private keys are smoke-tested against the system ssh-keygen -y reader on macOS for the supported algorithms.

Deferred Areas

The public-key feature set stays focused on direct key loading and OpenSSH file compatibility:

  • SSH agent integration
  • keychain-backed credential loading

Good Fits

Use the public-key API when:

  • you want Ed25519, RSA/SHA-2, or ECDSA authentication
  • you want optional explicit legacy ssh-rsa compatibility for an older endpoint and can enable it on that specific connection or jump-host hop
  • an OpenSSH private key file is acceptable, with passphrase: supplied when it is encrypted, or you already control the key material yourself
  • you want the most direct production-oriented path in the current public surface

SSH agent support and keychain-backed credentials remain outside the current public surface.

On this page