#[cfg(feature = "rust_crypto")]
pub mod rust_crypto;
#[cfg(feature = "aws_lc")]
pub mod aws_lc;
#[cfg(feature = "ring")]
pub mod ring;
pub mod jws;
use std::fmt::Debug;
use std::sync::{Arc, OnceLock};
#[cfg(any(feature = "aws_lc", feature = "ring"))]
pub(crate) fn decode_pem(pem: &str) -> Result<Vec<u8>, &'static str> {
use base64::prelude::*;
let lines: Vec<&str> = pem.lines().collect();
let start = lines
.iter()
.position(|l| l.starts_with("-----BEGIN"))
.ok_or("missing BEGIN marker")?;
let end = lines
.iter()
.position(|l| l.starts_with("-----END"))
.ok_or("missing END marker")?;
if end <= start + 1 {
return Err("no content between markers");
}
let b64: String = lines[start + 1..end].concat();
BASE64_STANDARD
.decode(&b64)
.map_err(|_| "invalid base64")
}
#[cfg(any(feature = "aws_lc", feature = "ring"))]
pub(crate) fn ecdsa_raw_to_der(rs: &[u8; 64]) -> Result<Vec<u8>, CryptoError> {
use asn1_rs::{Integer, Sequence, ToDer};
let to_error = |e: asn1_rs::SerializeError| CryptoError::SigningError(e.to_string());
let r = Integer::from_const_array::<32>(rs[..32].try_into().expect("32 bytes"));
let s = Integer::from_const_array::<32>(rs[32..].try_into().expect("32 bytes"));
Sequence::from_iter_to_der([r, s].into_iter())
.map_err(to_error)?
.to_der_vec()
.map_err(to_error)
}
#[derive(thiserror::Error, Debug)]
pub enum CryptoError {
#[error("Key error: {0}")]
KeyError(String),
#[error("Signing error: {0}")]
SigningError(String),
#[error("Verification error: {0}")]
VerificationError(String),
}
pub trait P256SigningSuite: Send + Sync + Debug {
fn private_key(&self, pem: &str) -> Result<Box<dyn P256PrivateKey>, CryptoError>;
fn public_key(&self, spki_der: &[u8]) -> Result<Box<dyn P256PublicKey>, CryptoError>;
}
pub trait P256PrivateKey: Send + Sync + Debug {
fn signature(&self, message: &[u8]) -> Result<P256Signature, CryptoError>;
}
pub type P256Signature = ([u8; 64], Vec<u8>);
pub trait P256PublicKey: Send + Sync + Debug {
fn is_valid_signature(&self, signature: &[u8; 64], message: &[u8]) -> Result<(), CryptoError>;
}
#[derive(Debug, Clone)]
pub struct CryptoProvider {
pub p256_signing: &'static dyn P256SigningSuite,
}
static PROCESS_DEFAULT: OnceLock<Arc<CryptoProvider>> = OnceLock::new();
impl CryptoProvider {
pub fn install_default(self) -> Result<(), Arc<Self>> {
PROCESS_DEFAULT.set(Arc::new(self))
}
pub fn get_default() -> Option<&'static Arc<Self>> {
PROCESS_DEFAULT.get()
}
pub fn default_provider() -> &'static Arc<Self> {
PROCESS_DEFAULT.get_or_init(|| Arc::new(Self::from_crate_features()))
}
#[allow(unreachable_code)]
fn from_crate_features() -> Self {
#[cfg(feature = "rust_crypto")]
{
return rust_crypto::DEFAULT_PROVIDER;
}
#[cfg(feature = "aws_lc")]
{
return aws_lc::DEFAULT_PROVIDER;
}
#[cfg(feature = "ring")]
{
return ring::DEFAULT_PROVIDER;
}
panic!("No crypto backend. Enable 'rust_crypto', 'aws_lc' or 'ring' feature.");
}
}
#[cfg(all(test, any(feature = "aws_lc", feature = "ring")))]
mod der_tests {
use super::ecdsa_raw_to_der;
#[test]
fn der_integers_are_minimally_encoded_and_positive() {
let mut rs = [0u8; 64];
rs[0] = 0xFF; rs[32 + 31] = 0x01;
let der = ecdsa_raw_to_der(&rs).expect("encode");
assert_eq!(der[0], 0x30);
assert_eq!(der[1] as usize, der.len() - 2);
assert_eq!(der[2], 0x02);
assert_eq!(der[3], 33, "r must be padded to 33 bytes");
assert_eq!(der[4], 0x00, "pad byte keeps r positive");
assert_eq!(der[5], 0xFF);
assert_eq!(&der[der.len() - 3..], &[0x02, 0x01, 0x01], "s is one byte");
}
#[test]
fn zero_component_encodes_as_a_single_zero_byte() {
let der = ecdsa_raw_to_der(&[0u8; 64]).expect("encode");
assert_eq!(der, vec![0x30, 0x06, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00]);
}
}