ssh-key 0.5.1

Pure Rust implementation of SSH key file format decoders/encoders as described in RFC4251/RFC4253 and OpenSSH key formats, as well as "sshsig" signatures and certificates (including certificate validation and certificate authority support), with further support for the `authorized_keys` and `known_hosts` file formats.
Documentation
//! `sshsig` signature tests.

#![cfg(feature = "alloc")]

use hex_literal::hex;
use ssh_key::{Algorithm, HashAlg, LineEnding, PublicKey, SshSig};

#[cfg(feature = "ed25519")]
use ssh_key::{Error, PrivateKey};

/// DSA OpenSSH-formatted private key.
#[cfg(feature = "dsa")]
const DSA_PRIVATE_KEY: &str = include_str!("examples/id_dsa_1024");

/// DSA OpenSSH-formatted public key.
#[cfg(feature = "dsa")]
const DSA_PUBLIC_KEY: &str = include_str!("examples/id_dsa_1024.pub");

/// ECDSA/P-256 OpenSSH-formatted private key.
#[cfg(feature = "p256")]
const ECDSA_P256_PRIVATE_KEY: &str = include_str!("examples/id_ecdsa_p256");

/// ECDSA/P-256 OpenSSH-formatted public key.
#[cfg(feature = "p256")]
const ECDSA_P256_PUBLIC_KEY: &str = include_str!("examples/id_ecdsa_p256.pub");

/// Ed25519 OpenSSH-formatted private key.
#[cfg(feature = "ed25519")]
const ED25519_PRIVATE_KEY: &str = include_str!("examples/id_ed25519");

/// Ed25519 OpenSSH-formatted public key.
const ED25519_PUBLIC_KEY: &str = include_str!("examples/id_ed25519.pub");

/// `sshsig`-encoded signature.
const ED25519_SIGNATURE: &str = include_str!("examples/sshsig_ed25519");

/// Bytes of the raw Ed25519 signature.
const ED25519_SIGNATURE_BYTES: [u8; 64] = hex!(
    "4f11abfeb4c18d9e8c7832eccceeb947c9505a8c29fc074900ca2396c0f2a9ac"
    "db06de2e97fafa33fd60928a4fc5a30630aa18020015094af457dc011154150f"
);

/// RSA OpenSSH-formatted private key.
#[cfg(feature = "rsa")]
const RSA_PRIVATE_KEY: &str = include_str!("examples/id_rsa_3072");

/// RSA OpenSSH-formatted public key.
#[cfg(feature = "rsa")]
const RSA_PUBLIC_KEY: &str = include_str!("examples/id_rsa_3072.pub");

/// Example message to be signed/verified.
#[cfg(feature = "ed25519")]
const MSG_EXAMPLE: &[u8] = b"testing";

/// Example domain/namespace used for the message.
const NAMESPACE_EXAMPLE: &str = "example";

#[test]
fn decode_ed25519() {
    let sshsig = ED25519_SIGNATURE.parse::<SshSig>().unwrap();
    let public_key = ED25519_PUBLIC_KEY.parse::<PublicKey>().unwrap();

    assert_eq!(sshsig.algorithm(), Algorithm::Ed25519);
    assert_eq!(sshsig.version(), 1);
    assert_eq!(sshsig.public_key(), public_key.key_data());
    assert_eq!(sshsig.namespace(), NAMESPACE_EXAMPLE);
    assert_eq!(sshsig.reserved(), &[]);
    assert_eq!(sshsig.hash_alg(), HashAlg::Sha512);
    assert_eq!(sshsig.signature_bytes(), ED25519_SIGNATURE_BYTES);
}

#[test]
fn encode_ed25519() {
    let sshsig = ED25519_SIGNATURE.parse::<SshSig>().unwrap();
    let sshsig_pem = sshsig.to_pem(LineEnding::LF).unwrap();
    assert_eq!(&sshsig_pem, ED25519_SIGNATURE);
}

#[test]
#[cfg(feature = "dsa")]
fn sign_dsa() {
    let signing_key = PrivateKey::from_openssh(DSA_PRIVATE_KEY).unwrap();
    let verifying_key = DSA_PUBLIC_KEY.parse::<PublicKey>().unwrap();

    let signature = signing_key
        .sign(NAMESPACE_EXAMPLE, HashAlg::Sha512, MSG_EXAMPLE)
        .unwrap();

    assert_eq!(
        verifying_key.verify(NAMESPACE_EXAMPLE, MSG_EXAMPLE, &signature),
        Ok(())
    );
}

#[test]
#[cfg(feature = "p256")]
fn sign_ecdsa_p256() {
    let signing_key = PrivateKey::from_openssh(ECDSA_P256_PRIVATE_KEY).unwrap();
    let verifying_key = ECDSA_P256_PUBLIC_KEY.parse::<PublicKey>().unwrap();

    let signature = signing_key
        .sign(NAMESPACE_EXAMPLE, HashAlg::Sha512, MSG_EXAMPLE)
        .unwrap();

    assert_eq!(
        verifying_key.verify(NAMESPACE_EXAMPLE, MSG_EXAMPLE, &signature),
        Ok(())
    );
}

#[test]
#[cfg(feature = "ed25519")]
fn sign_ed25519() {
    let signing_key = PrivateKey::from_openssh(ED25519_PRIVATE_KEY).unwrap();
    let signature = signing_key
        .sign(NAMESPACE_EXAMPLE, HashAlg::Sha512, MSG_EXAMPLE)
        .unwrap();

    assert_eq!(signature, ED25519_SIGNATURE.parse::<SshSig>().unwrap());
}

#[test]
#[cfg(feature = "rsa")]
fn sign_rsa() {
    let signing_key = PrivateKey::from_openssh(RSA_PRIVATE_KEY).unwrap();
    let verifying_key = RSA_PUBLIC_KEY.parse::<PublicKey>().unwrap();

    let signature = signing_key
        .sign(NAMESPACE_EXAMPLE, HashAlg::Sha512, MSG_EXAMPLE)
        .unwrap();

    assert_eq!(
        verifying_key.verify(NAMESPACE_EXAMPLE, MSG_EXAMPLE, &signature),
        Ok(())
    );
}

#[test]
#[cfg(feature = "ed25519")]
fn verify_ed25519() {
    let verifying_key = ED25519_PUBLIC_KEY.parse::<PublicKey>().unwrap();
    let signature = ED25519_SIGNATURE.parse::<SshSig>().unwrap();

    // valid
    assert_eq!(
        verifying_key.verify(NAMESPACE_EXAMPLE, MSG_EXAMPLE, &signature),
        Ok(())
    );

    // bad namespace
    assert_eq!(
        verifying_key.verify("bogus namespace", MSG_EXAMPLE, &signature),
        Err(Error::Namespace)
    );

    // invalid message
    assert_eq!(
        verifying_key.verify(NAMESPACE_EXAMPLE, b"bogus!", &signature),
        Err(Error::Crypto)
    );
}