uselesskey-ecdsa 0.7.0

ECDSA P-256/P-384 key fixtures (PKCS#8/SPKI, PEM/DER) for tests.
Documentation
/// ECDSA algorithm specification.
///
/// # Examples
///
/// ```
/// use uselesskey_ecdsa::EcdsaSpec;
///
/// let es256 = EcdsaSpec::es256();
/// assert_eq!(es256.alg_name(), "ES256");
/// assert_eq!(es256.curve_name(), "P-256");
///
/// let es384 = EcdsaSpec::es384();
/// assert_eq!(es384.alg_name(), "ES384");
/// assert_eq!(es384.curve_name(), "P-384");
/// ```
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum EcdsaSpec {
    /// P-256 / secp256r1 / prime256v1 (for ES256 JWT signing).
    Es256,
    /// P-384 / secp384r1 (for ES384 JWT signing).
    Es384,
}

impl EcdsaSpec {
    /// Spec suitable for ES256 JWT signing.
    ///
    /// # Examples
    ///
    /// ```
    /// use uselesskey_ecdsa::EcdsaSpec;
    ///
    /// let spec = EcdsaSpec::es256();
    /// assert_eq!(spec.alg_name(), "ES256");
    /// assert_eq!(spec.curve_name(), "P-256");
    /// ```
    pub fn es256() -> Self {
        Self::Es256
    }

    /// Spec suitable for ES384 JWT signing.
    ///
    /// # Examples
    ///
    /// ```
    /// use uselesskey_ecdsa::EcdsaSpec;
    ///
    /// let spec = EcdsaSpec::es384();
    /// assert_eq!(spec.alg_name(), "ES384");
    /// assert_eq!(spec.curve_name(), "P-384");
    /// ```
    pub fn es384() -> Self {
        Self::Es384
    }

    /// Returns the JWT algorithm name.
    ///
    /// # Examples
    ///
    /// ```
    /// use uselesskey_ecdsa::EcdsaSpec;
    ///
    /// assert_eq!(EcdsaSpec::es256().alg_name(), "ES256");
    /// assert_eq!(EcdsaSpec::es384().alg_name(), "ES384");
    /// ```
    pub fn alg_name(&self) -> &'static str {
        match self {
            Self::Es256 => "ES256",
            Self::Es384 => "ES384",
        }
    }

    /// Returns the curve name.
    ///
    /// # Examples
    ///
    /// ```
    /// use uselesskey_ecdsa::EcdsaSpec;
    ///
    /// assert_eq!(EcdsaSpec::es256().curve_name(), "P-256");
    /// assert_eq!(EcdsaSpec::es384().curve_name(), "P-384");
    /// ```
    pub fn curve_name(&self) -> &'static str {
        match self {
            Self::Es256 => "P-256",
            Self::Es384 => "P-384",
        }
    }

    /// Returns the expected coordinate length in bytes for uncompressed points.
    ///
    /// # Examples
    ///
    /// ```
    /// use uselesskey_ecdsa::EcdsaSpec;
    ///
    /// assert_eq!(EcdsaSpec::es256().coordinate_len_bytes(), 32);
    /// assert_eq!(EcdsaSpec::es384().coordinate_len_bytes(), 48);
    /// ```
    pub fn coordinate_len_bytes(&self) -> usize {
        match self {
            Self::Es256 => 32,
            Self::Es384 => 48,
        }
    }

    /// Stable encoding for cache keys / deterministic derivation.
    ///
    /// If you change this, bump the derivation version in `uselesskey-core`.
    ///
    /// # Examples
    ///
    /// ```
    /// use uselesskey_ecdsa::EcdsaSpec;
    ///
    /// let bytes = EcdsaSpec::es256().stable_bytes();
    /// assert_eq!(bytes.len(), 4);
    /// ```
    pub fn stable_bytes(&self) -> [u8; 4] {
        match self {
            Self::Es256 => [0, 0, 0, 1],
            Self::Es384 => [0, 0, 0, 2],
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn alg_and_curve_names_match_specs() {
        let es256 = EcdsaSpec::es256();
        assert_eq!(es256.alg_name(), "ES256");
        assert_eq!(es256.curve_name(), "P-256");
        assert_eq!(es256.coordinate_len_bytes(), 32);

        let es384 = EcdsaSpec::es384();
        assert_eq!(es384.alg_name(), "ES384");
        assert_eq!(es384.curve_name(), "P-384");
        assert_eq!(es384.coordinate_len_bytes(), 48);
    }

    #[test]
    fn stable_bytes_are_unique() {
        let es256 = EcdsaSpec::es256().stable_bytes();
        let es384 = EcdsaSpec::es384().stable_bytes();
        assert_ne!(es256, es384);
    }
}