Skip to main content

auths_crypto/
ssh.rs

1//! OpenSSH public key parsing for Ed25519 and P-256 keys.
2
3use ssh_key::PublicKey;
4
5/// Errors from parsing an OpenSSH public key.
6#[derive(Debug, Clone, thiserror::Error, PartialEq, Eq)]
7#[non_exhaustive]
8pub enum SshKeyError {
9    #[error("Malformed or invalid OpenSSH public key: {0}")]
10    InvalidFormat(String),
11
12    #[error("Unsupported key type: expected ssh-ed25519 or ecdsa-sha2-nistp256")]
13    UnsupportedKeyType,
14}
15
16impl crate::AuthsErrorInfo for SshKeyError {
17    fn error_code(&self) -> &'static str {
18        match self {
19            Self::InvalidFormat(_) => "AUTHS-E1301",
20            Self::UnsupportedKeyType => "AUTHS-E1302",
21        }
22    }
23
24    fn suggestion(&self) -> Option<&'static str> {
25        match self {
26            Self::InvalidFormat(_) => Some("Check that the public key is a valid OpenSSH format"),
27            Self::UnsupportedKeyType => {
28                Some("Supported key types: ssh-ed25519, ecdsa-sha2-nistp256")
29            }
30        }
31    }
32}
33
34/// Parse an OpenSSH public key line and return the curve type and raw key bytes.
35///
36/// Supports `ssh-ed25519` (returns 32-byte key) and `ecdsa-sha2-nistp256`
37/// (returns 33-byte compressed SEC1 point).
38///
39/// Args:
40/// * `openssh_pub`: A full OpenSSH public key line, e.g. `"ssh-ed25519 AAAA... comment"`.
41///
42/// Usage:
43/// ```ignore
44/// let (curve, raw) = openssh_pub_to_raw("ssh-ed25519 AAAA...")?;
45/// assert_eq!(curve, CurveType::Ed25519);
46/// assert_eq!(raw.len(), 32);
47/// ```
48pub fn openssh_pub_to_raw(openssh_pub: &str) -> Result<(crate::CurveType, Vec<u8>), SshKeyError> {
49    let public_key = PublicKey::from_openssh(openssh_pub)
50        .map_err(|e| SshKeyError::InvalidFormat(e.to_string()))?;
51
52    if let Some(ed) = public_key.key_data().ed25519() {
53        return Ok((crate::CurveType::Ed25519, ed.0.to_vec()));
54    }
55
56    if let Some(ssh_key::public::EcdsaPublicKey::NistP256(point)) = public_key.key_data().ecdsa() {
57        return Ok((crate::CurveType::P256, point.as_ref().to_vec()));
58    }
59
60    Err(SshKeyError::UnsupportedKeyType)
61}