wolfcose 0.1.0

Safe Rust API for wolfSSL wolfCOSE.
use crate::{Algorithm, CoseKey, Curve, Error, KeyType, Result};
use alloc::vec::Vec;
use core::fmt;

/// Owned symmetric key material with optional COSE metadata.
#[derive(Clone, Eq, PartialEq)]
pub struct SymmetricKey {
    material: Vec<u8>,
    algorithm: Option<Algorithm>,
    kid: Option<Vec<u8>>,
}

impl SymmetricKey {
    /// Create owned symmetric key material.
    pub fn new(material: impl AsRef<[u8]>) -> Self {
        Self {
            material: material.as_ref().to_vec(),
            algorithm: None,
            kid: None,
        }
    }

    /// Attach an algorithm hint.
    pub fn with_algorithm(mut self, algorithm: Algorithm) -> Self {
        self.algorithm = Some(algorithm);
        self
    }

    /// Attach a key identifier.
    pub fn with_kid(mut self, kid: impl AsRef<[u8]>) -> Self {
        self.kid = Some(kid.as_ref().to_vec());
        self
    }

    /// Borrow key material.
    pub fn material(&self) -> &[u8] {
        &self.material
    }

    /// Algorithm hint.
    pub fn algorithm(&self) -> Option<Algorithm> {
        self.algorithm
    }

    /// Key identifier.
    pub fn kid(&self) -> Option<&[u8]> {
        self.kid.as_deref()
    }

    /// Convert to a `CoseKey`.
    pub fn into_cose_key(self) -> Result<CoseKey> {
        let mut builder = CoseKeyBuilder::symmetric(self.material);
        if let Some(algorithm) = self.algorithm {
            builder = builder.algorithm(algorithm);
        }
        if let Some(kid) = self.kid {
            builder = builder.kid(kid);
        }
        builder.build()
    }
}

impl fmt::Debug for SymmetricKey {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("SymmetricKey")
            .field("material_len", &self.material.len())
            .field("algorithm", &self.algorithm)
            .field("kid", &self.kid)
            .finish_non_exhaustive()
    }
}

/// Builder for high-level `CoseKey` construction.
#[derive(Clone, Default)]
pub struct CoseKeyBuilder {
    key_type: Option<KeyType>,
    algorithm: Option<Algorithm>,
    curve: Option<Curve>,
    kid: Option<Vec<u8>>,
    symmetric: Option<Vec<u8>>,
    has_private: Option<bool>,
}

impl fmt::Debug for CoseKeyBuilder {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("CoseKeyBuilder")
            .field("key_type", &self.key_type)
            .field("algorithm", &self.algorithm)
            .field("curve", &self.curve)
            .field("kid", &self.kid)
            .field("symmetric_len", &self.symmetric.as_ref().map(Vec::len))
            .field("has_private", &self.has_private)
            .finish_non_exhaustive()
    }
}

impl CoseKeyBuilder {
    /// Start an empty key descriptor builder.
    pub fn new() -> Self {
        Self::default()
    }

    /// Start a symmetric key builder.
    pub fn symmetric(material: impl AsRef<[u8]>) -> Self {
        Self::new()
            .key_type(KeyType::SYMMETRIC)
            .symmetric_material(material)
    }

    /// Set key type.
    pub fn key_type(mut self, key_type: KeyType) -> Self {
        self.key_type = Some(key_type);
        self
    }

    /// Set algorithm.
    pub fn algorithm(mut self, algorithm: Algorithm) -> Self {
        self.algorithm = Some(algorithm);
        self
    }

    /// Set curve.
    pub fn curve(mut self, curve: Curve) -> Self {
        self.curve = Some(curve);
        self
    }

    /// Set key identifier.
    pub fn kid(mut self, kid: impl AsRef<[u8]>) -> Self {
        self.kid = Some(kid.as_ref().to_vec());
        self
    }

    /// Set owned symmetric material.
    pub fn symmetric_material(mut self, material: impl AsRef<[u8]>) -> Self {
        self.symmetric = Some(material.as_ref().to_vec());
        self
    }

    /// Mark private key material as present.
    pub fn has_private(mut self, has_private: bool) -> Self {
        self.has_private = Some(has_private);
        self
    }

    /// Build the key descriptor.
    pub fn build(self) -> Result<CoseKey> {
        if let (Some(algorithm), Some(material)) = (self.algorithm, self.symmetric.as_deref()) {
            validate_symmetric_len(algorithm, material.len())?;
        }

        let mut key = CoseKey::new()?;
        if let Some(material) = self.symmetric {
            key.set_symmetric(material)?;
        }
        if let Some(key_type) = self.key_type {
            key.set_key_type(key_type);
        }
        if let Some(algorithm) = self.algorithm {
            key.set_algorithm(algorithm);
        }
        if let Some(curve) = self.curve {
            key.set_curve(curve);
        }
        if let Some(kid) = self.kid {
            key.set_kid(kid);
        }
        if let Some(has_private) = self.has_private {
            key.set_has_private(has_private);
        }
        Ok(key)
    }
}

/// Typed view over public `CoseKey` metadata.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CoseKeyView {
    /// COSE key type.
    pub key_type: KeyType,
    /// COSE algorithm.
    pub algorithm: Algorithm,
    /// COSE curve.
    pub curve: Curve,
    /// Key identifier.
    pub kid: Vec<u8>,
    /// Length of owned symmetric key material, if this Rust value owns it.
    pub symmetric_len: Option<usize>,
    /// Whether private key material is marked as present.
    pub has_private: bool,
}

impl CoseKeyView {
    /// Build a view from a key descriptor.
    pub fn from_key(key: &CoseKey) -> Self {
        let raw = key.as_raw();
        Self {
            key_type: KeyType::from_id(raw.kty),
            algorithm: Algorithm::from_id(raw.alg),
            curve: Curve::from_id(raw.crv),
            kid: key.kid().unwrap_or_default().to_vec(),
            symmetric_len: key.symmetric_material().map(<[u8]>::len),
            has_private: key.has_private(),
        }
    }
}

fn validate_symmetric_len(algorithm: Algorithm, len: usize) -> Result<()> {
    if let Some(bits) = algorithm.key_bits_hint() {
        if len * 8 != bits {
            return Err(Error::CoseKeyType);
        }
    }
    Ok(())
}