fips-core 0.4.4

Reusable FIPS mesh, endpoint, transport, and protocol library
Documentation
#[cfg(test)]
fn validate_compressed_pubkey_hex(s: &str) -> Result<(), TransportError> {
    compressed_pubkey_from_hex(s).map(drop)
}

fn compressed_pubkey_from_hex(s: &str) -> Result<secp256k1::PublicKey, TransportError> {
    if s.len() != 66 {
        return Err(TransportError::InvalidAddress(
            "WebRTC address must be 33-byte compressed pubkey hex".into(),
        ));
    }
    let bytes = hex::decode(s).map_err(|e| TransportError::InvalidAddress(e.to_string()))?;
    if bytes.len() != 33 || !matches!(bytes[0], 0x02 | 0x03) {
        return Err(TransportError::InvalidAddress(
            "WebRTC address must be compressed secp256k1 pubkey".into(),
        ));
    }
    secp256k1::PublicKey::from_slice(&bytes)
        .map_err(|e| TransportError::InvalidAddress(e.to_string()))
}

pub(crate) fn canonical_webrtc_pubkey_hex(pubkey: secp256k1::PublicKey) -> String {
    let (xonly, _) = pubkey.x_only_public_key();
    hex::encode(xonly.public_key(secp256k1::Parity::Even).serialize())
}

pub(crate) fn canonical_webrtc_addr(
    addr: &TransportAddr,
) -> Result<TransportAddr, TransportError> {
    let Some(s) = addr.as_str() else {
        return Err(TransportError::InvalidAddress(
            "WebRTC address must be UTF-8 compressed pubkey hex".into(),
        ));
    };
    let pubkey = compressed_pubkey_from_hex(s)?;
    Ok(TransportAddr::from_string(&canonical_webrtc_pubkey_hex(
        pubkey,
    )))
}

fn xonly_from_compressed_hex(s: &str) -> Result<PublicKey, TransportError> {
    let (xonly, _) = compressed_pubkey_from_hex(s)?.x_only_public_key();
    PublicKey::from_slice(&xonly.serialize())
        .map_err(|e| TransportError::InvalidAddress(e.to_string()))
}

fn random_session_id() -> String {
    let mut bytes = [0u8; 16];
    rand::Rng::fill_bytes(&mut rand::rng(), &mut bytes);
    hex::encode(bytes)
}

fn now_ms() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map(|d| d.as_millis() as u64)
        .unwrap_or(0)
}