CryptoUtil

This commit is contained in:
Alex Emmet 2026-07-03 16:43:29 +02:00
commit 912aa9491c
10 changed files with 153 additions and 35 deletions

View file

@ -17,6 +17,7 @@ hkdf = { version = "0.13", optional = true }
sha2 = { version = "0.11", optional = true }
zeroize = { version = "1.9", features = ["derive"] }
thiserror = "1"
base64 = "0.22"
rand_core = { version = "0.6", features = ["getrandom"] }
getrandom = "0.4.3"
mlkem-tls = { version = "0.2", optional = true }

View file

@ -28,4 +28,6 @@ pub enum CryptoError {
UnknownAlgorithm,
#[error("invalid hex encoding")]
InvalidHex,
#[error("invalid base64 encoding")]
InvalidBase64,
}

View file

@ -1,4 +1,7 @@
use std::fmt;
use base64::Engine;
use base64::engine::general_purpose;
use zeroize::{Zeroize, ZeroizeOnDrop};
// --- Private key types ---
@ -197,6 +200,16 @@ fn hex_to_bytes(s: &str) -> Result<Vec<u8>, crate::error::CryptoError> {
.collect()
}
fn bytes_to_base64(bytes: &[u8]) -> String {
general_purpose::STANDARD.encode(bytes)
}
fn base64_to_bytes(s: &str) -> Result<Vec<u8>, crate::error::CryptoError> {
general_purpose::STANDARD
.decode(s)
.map_err(|_| crate::error::CryptoError::InvalidBase64)
}
// ---
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
@ -510,6 +523,22 @@ impl Keyring {
sig_cl_secret_key: SignaturePrivateKey::new(read_key(&mut offset)?),
})
}
pub fn to_hex(&self) -> String {
bytes_to_hex(&self.to_bytes())
}
pub fn from_hex(s: &str) -> Result<Self, crate::error::CryptoError> {
Self::from_bytes(&hex_to_bytes(s)?)
}
pub fn to_base64(&self) -> String {
bytes_to_base64(&self.to_bytes())
}
pub fn from_base64(s: &str) -> Result<Self, crate::error::CryptoError> {
Self::from_bytes(&base64_to_bytes(s)?)
}
}
impl TryFrom<&[u8]> for Keyring {
@ -645,6 +674,14 @@ impl PublicKeyBundle {
sig_cl_public_key: cl,
})
}
pub fn to_base64(&self) -> String {
bytes_to_base64(&self.as_bytes())
}
pub fn from_base64(s: &str) -> Result<Self, crate::error::CryptoError> {
Self::from_bytes(&base64_to_bytes(s)?)
}
}
impl TryFrom<&[u8]> for PublicKeyBundle {
@ -760,6 +797,48 @@ mod tests {
assert_eq!(key.as_bytes(), recovered.as_bytes());
}
#[test]
fn keyring_hex_roundtrip() {
let keyring = Keyring::new(
KemPublicKey::new(vec![1u8; 16]),
KemPrivateKey::new(vec![2u8; 16]),
SignaturePqPublicKey::new(vec![3u8; 16]),
SignaturePqPrivateKey::new(vec![4u8; 16]),
SignaturePublicKey::new(vec![5u8; 16]),
SignaturePrivateKey::new(vec![6u8; 16]),
);
let hex = keyring.to_hex();
let recovered = Keyring::from_hex(&hex).unwrap();
assert_eq!(keyring.to_bytes(), recovered.to_bytes());
}
#[test]
fn keyring_base64_roundtrip() {
let keyring = Keyring::new(
KemPublicKey::new(vec![1u8; 16]),
KemPrivateKey::new(vec![2u8; 16]),
SignaturePqPublicKey::new(vec![3u8; 16]),
SignaturePqPrivateKey::new(vec![4u8; 16]),
SignaturePublicKey::new(vec![5u8; 16]),
SignaturePrivateKey::new(vec![6u8; 16]),
);
let b64 = keyring.to_base64();
let recovered = Keyring::from_base64(&b64).unwrap();
assert_eq!(keyring.to_bytes(), recovered.to_bytes());
}
#[test]
fn public_key_bundle_base64_roundtrip() {
let bundle = PublicKeyBundle::new(
KemPublicKey::new(vec![1u8; 32]),
SignaturePqPublicKey::new(vec![2u8; 64]),
SignaturePublicKey::new(vec![3u8; 32]),
);
let b64 = bundle.to_base64();
let recovered = PublicKeyBundle::from_base64(&b64).unwrap();
assert_eq!(bundle.as_bytes(), recovered.as_bytes());
}
#[test]
fn hex_invalid_returns_error() {
assert!(KemPublicKey::from_hex("xyz").is_err());