ssi_ssh/
lib.rs

1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
2
3use sshkeys::PublicKeyKind;
4use ssi_jwk::{Base64urlUInt, Params as JWKParams, JWK};
5use thiserror::Error;
6
7#[derive(Error, Debug)]
8pub enum SSHKeyToJWKError {
9    #[error("SSH key: {0}")]
10    SSHKey(#[from] sshkeys::Error),
11    #[error("Unsupported ssh-dsa key")]
12    UnsupportedDsaKey,
13    #[error("P-256 parse error: {0}")]
14    P256Parse(String),
15    #[error("Unsupported ECDSA key type: {0}")]
16    UnsupportedEcdsaKey(String),
17    #[error("Missing features: {0}")]
18    MissingFeatures(&'static str),
19}
20
21fn pk_to_jwk_rsa(pk: &sshkeys::RsaPublicKey) -> JWK {
22    JWK::from(JWKParams::RSA(ssi_jwk::RSAParams::new_public(&pk.e, &pk.n)))
23}
24
25fn pk_to_jwk_ecdsa(pk: &sshkeys::EcdsaPublicKey) -> Result<JWK, SSHKeyToJWKError> {
26    match pk.curve.kind {
27        sshkeys::CurveKind::Nistp256 => {
28            #[cfg(not(feature = "secp256r1"))]
29            {
30                Err(SSHKeyToJWKError::MissingFeatures("secp256r1"))
31            }
32            #[cfg(feature = "secp256r1")]
33            {
34                ssi_jwk::p256_parse(&pk.key).map_err(|e| SSHKeyToJWKError::P256Parse(e.to_string()))
35            }
36        }
37        _ => Err(SSHKeyToJWKError::UnsupportedEcdsaKey(
38            pk.curve.identifier.to_string(),
39        )),
40    }
41}
42
43fn pk_to_jwk_ed25519(pk: &sshkeys::Ed25519PublicKey) -> JWK {
44    JWK::from(JWKParams::OKP(ssi_jwk::OctetParams {
45        curve: "Ed25519".to_string(),
46        public_key: Base64urlUInt(pk.key.clone()),
47        private_key: None,
48    }))
49}
50
51/// Convert a SSH public key to a JWK.
52pub fn ssh_pkk_to_jwk(pkk: &PublicKeyKind) -> Result<JWK, SSHKeyToJWKError> {
53    // https://datatracker.ietf.org/doc/html/rfc4253#section-6.6
54    // https://datatracker.ietf.org/doc/html/rfc5656#section-3.1
55    // https://datatracker.ietf.org/doc/html/draft-ietf-curdle-ssh-ed25519-02#section-4
56    let jwk = match pkk {
57        PublicKeyKind::Rsa(pk) => pk_to_jwk_rsa(pk),
58        PublicKeyKind::Dsa(_) => return Err(SSHKeyToJWKError::UnsupportedDsaKey),
59        PublicKeyKind::Ecdsa(pk) => pk_to_jwk_ecdsa(pk)?,
60        PublicKeyKind::Ed25519(pk) => pk_to_jwk_ed25519(pk),
61    };
62    Ok(jwk)
63}