[Add] Key de-/serialization
This commit is contained in:
parent
15cc1d4c5e
commit
daf2f940d5
4 changed files with 505 additions and 126 deletions
37
Cargo.lock
generated
37
Cargo.lock
generated
|
|
@ -49,7 +49,7 @@ dependencies = [
|
||||||
"nom",
|
"nom",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"rusticata-macros",
|
"rusticata-macros",
|
||||||
"thiserror",
|
"thiserror 2.0.18",
|
||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -1020,7 +1020,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quinn",
|
"quinn",
|
||||||
"rustls",
|
"rustls",
|
||||||
"thiserror",
|
"thiserror 2.0.18",
|
||||||
"wtransport",
|
"wtransport",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -1038,6 +1038,7 @@ dependencies = [
|
||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
"serde",
|
"serde",
|
||||||
"sha2 0.11.0",
|
"sha2 0.11.0",
|
||||||
|
"thiserror 1.0.69",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -1331,7 +1332,7 @@ dependencies = [
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"rustls",
|
"rustls",
|
||||||
"socket2",
|
"socket2",
|
||||||
"thiserror",
|
"thiserror 2.0.18",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"web-time",
|
"web-time",
|
||||||
|
|
@ -1353,7 +1354,7 @@ dependencies = [
|
||||||
"rustls",
|
"rustls",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"slab",
|
"slab",
|
||||||
"thiserror",
|
"thiserror 2.0.18",
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
"tracing",
|
"tracing",
|
||||||
"web-time",
|
"web-time",
|
||||||
|
|
@ -1852,13 +1853,33 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl 1.0.69",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.18"
|
version = "2.0.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl 2.0.18",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -2377,7 +2398,7 @@ dependencies = [
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"sha2 0.11.0",
|
"sha2 0.11.0",
|
||||||
"socket2",
|
"socket2",
|
||||||
"thiserror",
|
"thiserror 2.0.18",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|
@ -2394,7 +2415,7 @@ checksum = "d5867c629e4252f7439d82315923daaf27f4fa442410d51b78ab93ef4c432a11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"httlib-huffman",
|
"httlib-huffman",
|
||||||
"octets",
|
"octets",
|
||||||
"thiserror",
|
"thiserror 2.0.18",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -2425,7 +2446,7 @@ dependencies = [
|
||||||
"oid-registry",
|
"oid-registry",
|
||||||
"ring",
|
"ring",
|
||||||
"rusticata-macros",
|
"rusticata-macros",
|
||||||
"thiserror",
|
"thiserror 2.0.18",
|
||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ ed25519-dalek = { version = "2.2", optional = true, features = [
|
||||||
hkdf = { version = "0.13", optional = true }
|
hkdf = { version = "0.13", optional = true }
|
||||||
sha2 = { version = "0.11", optional = true }
|
sha2 = { version = "0.11", optional = true }
|
||||||
zeroize = { version = "1.9", features = ["derive"] }
|
zeroize = { version = "1.9", features = ["derive"] }
|
||||||
|
thiserror = "1"
|
||||||
rand_core = { version = "0.6", features = ["getrandom"] }
|
rand_core = { version = "0.6", features = ["getrandom"] }
|
||||||
getrandom = "0.4.3"
|
getrandom = "0.4.3"
|
||||||
mlkem-tls = { version = "0.2", optional = true }
|
mlkem-tls = { version = "0.2", optional = true }
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,31 @@
|
||||||
use std::fmt;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Error, Debug, Clone)]
|
||||||
pub enum CryptoError {
|
pub enum CryptoError {
|
||||||
|
#[error("encryption failed")]
|
||||||
EncryptionFailed,
|
EncryptionFailed,
|
||||||
|
#[error("decryption failed")]
|
||||||
DecryptionFailed,
|
DecryptionFailed,
|
||||||
|
#[error("invalid key length")]
|
||||||
InvalidKeyLength,
|
InvalidKeyLength,
|
||||||
|
#[error("invalid nonce length")]
|
||||||
InvalidNonceLength,
|
InvalidNonceLength,
|
||||||
|
#[error("invalid signature")]
|
||||||
InvalidSignature,
|
InvalidSignature,
|
||||||
|
#[error("signing failed")]
|
||||||
SigningFailed,
|
SigningFailed,
|
||||||
|
#[error("verification failed")]
|
||||||
VerificationFailed,
|
VerificationFailed,
|
||||||
|
#[error("key generation failed")]
|
||||||
KeyGenerationFailed,
|
KeyGenerationFailed,
|
||||||
|
#[error("KDF error")]
|
||||||
KdfError,
|
KdfError,
|
||||||
|
#[error("KEM encapsulation failed")]
|
||||||
KemEncapsulationFailed,
|
KemEncapsulationFailed,
|
||||||
|
#[error("KEM decapsulation failed")]
|
||||||
KemDecapsulationFailed,
|
KemDecapsulationFailed,
|
||||||
|
#[error("unknown algorithm")]
|
||||||
UnknownAlgorithm,
|
UnknownAlgorithm,
|
||||||
|
#[error("invalid hex encoding")]
|
||||||
|
InvalidHex,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CryptoError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
CryptoError::EncryptionFailed => write!(f, "encryption failed"),
|
|
||||||
CryptoError::DecryptionFailed => write!(f, "decryption failed"),
|
|
||||||
CryptoError::InvalidKeyLength => write!(f, "invalid key length"),
|
|
||||||
CryptoError::InvalidNonceLength => write!(f, "invalid nonce length"),
|
|
||||||
CryptoError::InvalidSignature => write!(f, "invalid signature"),
|
|
||||||
CryptoError::SigningFailed => write!(f, "signing failed"),
|
|
||||||
CryptoError::VerificationFailed => write!(f, "verification failed"),
|
|
||||||
CryptoError::KeyGenerationFailed => write!(f, "key generation failed"),
|
|
||||||
CryptoError::KdfError => write!(f, "KDF error"),
|
|
||||||
CryptoError::KemEncapsulationFailed => write!(f, "KEM encapsulation failed"),
|
|
||||||
CryptoError::KemDecapsulationFailed => write!(f, "KEM decapsulation failed"),
|
|
||||||
CryptoError::UnknownAlgorithm => write!(f, "unknown algorithm"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for CryptoError {}
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
|
use std::fmt;
|
||||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
|
// --- Private key types ---
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
#[derive(Zeroize, ZeroizeOnDrop)]
|
#[derive(Zeroize, ZeroizeOnDrop)]
|
||||||
|
|
@ -9,12 +12,26 @@ impl EncryptionPrivateKey {
|
||||||
pub fn new(bytes: Vec<u8>) -> Self {
|
pub fn new(bytes: Vec<u8>) -> Self {
|
||||||
Self(bytes)
|
Self(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for EncryptionPrivateKey {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("EncryptionPrivateKey")
|
||||||
|
.field("len", &self.0.len())
|
||||||
|
.field("data", &"[REDACTED]")
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for EncryptionPrivateKey {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Vec<u8>> for EncryptionPrivateKey {
|
impl From<Vec<u8>> for EncryptionPrivateKey {
|
||||||
fn from(bytes: Vec<u8>) -> Self {
|
fn from(bytes: Vec<u8>) -> Self {
|
||||||
Self(bytes)
|
Self(bytes)
|
||||||
|
|
@ -27,6 +44,8 @@ impl From<&[u8]> for EncryptionPrivateKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
#[derive(Zeroize, ZeroizeOnDrop)]
|
#[derive(Zeroize, ZeroizeOnDrop)]
|
||||||
|
|
@ -36,12 +55,26 @@ impl SignaturePrivateKey {
|
||||||
pub fn new(bytes: Vec<u8>) -> Self {
|
pub fn new(bytes: Vec<u8>) -> Self {
|
||||||
Self(bytes)
|
Self(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for SignaturePrivateKey {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("SignaturePrivateKey")
|
||||||
|
.field("len", &self.0.len())
|
||||||
|
.field("data", &"[REDACTED]")
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for SignaturePrivateKey {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Vec<u8>> for SignaturePrivateKey {
|
impl From<Vec<u8>> for SignaturePrivateKey {
|
||||||
fn from(bytes: Vec<u8>) -> Self {
|
fn from(bytes: Vec<u8>) -> Self {
|
||||||
Self(bytes)
|
Self(bytes)
|
||||||
|
|
@ -54,59 +87,7 @@ impl From<&[u8]> for SignaturePrivateKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
// ---
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct EncryptionPublicKey(Vec<u8>);
|
|
||||||
|
|
||||||
impl EncryptionPublicKey {
|
|
||||||
pub fn new(bytes: Vec<u8>) -> Self {
|
|
||||||
Self(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<u8>> for EncryptionPublicKey {
|
|
||||||
fn from(bytes: Vec<u8>) -> Self {
|
|
||||||
Self(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&[u8]> for EncryptionPublicKey {
|
|
||||||
fn from(bytes: &[u8]) -> Self {
|
|
||||||
Self(bytes.to_vec())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SignaturePublicKey(Vec<u8>);
|
|
||||||
|
|
||||||
impl SignaturePublicKey {
|
|
||||||
pub fn new(bytes: Vec<u8>) -> Self {
|
|
||||||
Self(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<u8>> for SignaturePublicKey {
|
|
||||||
fn from(bytes: Vec<u8>) -> Self {
|
|
||||||
Self(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&[u8]> for SignaturePublicKey {
|
|
||||||
fn from(bytes: &[u8]) -> Self {
|
|
||||||
Self(bytes.to_vec())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
|
|
@ -117,12 +98,26 @@ impl KemPrivateKey {
|
||||||
pub fn new(bytes: Vec<u8>) -> Self {
|
pub fn new(bytes: Vec<u8>) -> Self {
|
||||||
Self(bytes)
|
Self(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for KemPrivateKey {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("KemPrivateKey")
|
||||||
|
.field("len", &self.0.len())
|
||||||
|
.field("data", &"[REDACTED]")
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for KemPrivateKey {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Vec<u8>> for KemPrivateKey {
|
impl From<Vec<u8>> for KemPrivateKey {
|
||||||
fn from(bytes: Vec<u8>) -> Self {
|
fn from(bytes: Vec<u8>) -> Self {
|
||||||
Self(bytes)
|
Self(bytes)
|
||||||
|
|
@ -135,6 +130,179 @@ impl From<&[u8]> for KemPrivateKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
|
#[derive(Zeroize, ZeroizeOnDrop)]
|
||||||
|
pub struct SignaturePqPrivateKey(Vec<u8>);
|
||||||
|
|
||||||
|
impl SignaturePqPrivateKey {
|
||||||
|
pub fn new(bytes: Vec<u8>) -> Self {
|
||||||
|
Self(bytes)
|
||||||
|
}
|
||||||
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for SignaturePqPrivateKey {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("SignaturePqPrivateKey")
|
||||||
|
.field("len", &self.0.len())
|
||||||
|
.field("data", &"[REDACTED]")
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for SignaturePqPrivateKey {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for SignaturePqPrivateKey {
|
||||||
|
fn from(bytes: Vec<u8>) -> Self {
|
||||||
|
Self(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&[u8]> for SignaturePqPrivateKey {
|
||||||
|
fn from(bytes: &[u8]) -> Self {
|
||||||
|
Self(bytes.to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Public key types ---
|
||||||
|
|
||||||
|
fn bytes_to_hex(bytes: &[u8]) -> String {
|
||||||
|
bytes
|
||||||
|
.iter()
|
||||||
|
.fold(String::with_capacity(bytes.len() * 2), |mut s, b| {
|
||||||
|
use fmt::Write;
|
||||||
|
let _ = write!(s, "{:02x}", b);
|
||||||
|
s
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hex_to_bytes(s: &str) -> Result<Vec<u8>, crate::error::CryptoError> {
|
||||||
|
if s.len() % 2 != 0 {
|
||||||
|
return Err(crate::error::CryptoError::InvalidHex);
|
||||||
|
}
|
||||||
|
(0..s.len())
|
||||||
|
.step_by(2)
|
||||||
|
.map(|i| {
|
||||||
|
u8::from_str_radix(&s[i..i + 2], 16).map_err(|_| crate::error::CryptoError::InvalidHex)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct EncryptionPublicKey(Vec<u8>);
|
||||||
|
|
||||||
|
impl EncryptionPublicKey {
|
||||||
|
pub fn new(bytes: Vec<u8>) -> Self {
|
||||||
|
Self(bytes)
|
||||||
|
}
|
||||||
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
pub fn to_hex(&self) -> String {
|
||||||
|
bytes_to_hex(&self.0)
|
||||||
|
}
|
||||||
|
pub fn from_hex(s: &str) -> Result<Self, crate::error::CryptoError> {
|
||||||
|
hex_to_bytes(s).map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for EncryptionPublicKey {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "EncryptionPublicKey({})", self.to_hex())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for EncryptionPublicKey {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for EncryptionPublicKey {
|
||||||
|
fn from(bytes: Vec<u8>) -> Self {
|
||||||
|
Self(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&[u8]> for EncryptionPublicKey {
|
||||||
|
fn from(bytes: &[u8]) -> Self {
|
||||||
|
Self(bytes.to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&EncryptionPublicKey> for Vec<u8> {
|
||||||
|
fn from(key: &EncryptionPublicKey) -> Vec<u8> {
|
||||||
|
key.0.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SignaturePublicKey(Vec<u8>);
|
||||||
|
|
||||||
|
impl SignaturePublicKey {
|
||||||
|
pub fn new(bytes: Vec<u8>) -> Self {
|
||||||
|
Self(bytes)
|
||||||
|
}
|
||||||
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
pub fn to_hex(&self) -> String {
|
||||||
|
bytes_to_hex(&self.0)
|
||||||
|
}
|
||||||
|
pub fn from_hex(s: &str) -> Result<Self, crate::error::CryptoError> {
|
||||||
|
hex_to_bytes(s).map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for SignaturePublicKey {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "SignaturePublicKey({})", self.to_hex())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for SignaturePublicKey {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for SignaturePublicKey {
|
||||||
|
fn from(bytes: Vec<u8>) -> Self {
|
||||||
|
Self(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&[u8]> for SignaturePublicKey {
|
||||||
|
fn from(bytes: &[u8]) -> Self {
|
||||||
|
Self(bytes.to_vec())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&SignaturePublicKey> for Vec<u8> {
|
||||||
|
fn from(key: &SignaturePublicKey) -> Vec<u8> {
|
||||||
|
key.0.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
@ -144,10 +312,27 @@ impl KemPublicKey {
|
||||||
pub fn new(bytes: Vec<u8>) -> Self {
|
pub fn new(bytes: Vec<u8>) -> Self {
|
||||||
Self(bytes)
|
Self(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
pub fn to_hex(&self) -> String {
|
||||||
|
bytes_to_hex(&self.0)
|
||||||
|
}
|
||||||
|
pub fn from_hex(s: &str) -> Result<Self, crate::error::CryptoError> {
|
||||||
|
hex_to_bytes(s).map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for KemPublicKey {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "KemPublicKey({})", self.to_hex())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for KemPublicKey {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<u8>> for KemPublicKey {
|
impl From<Vec<u8>> for KemPublicKey {
|
||||||
|
|
@ -162,6 +347,14 @@ impl From<&[u8]> for KemPublicKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&KemPublicKey> for Vec<u8> {
|
||||||
|
fn from(key: &KemPublicKey) -> Vec<u8> {
|
||||||
|
key.0.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
@ -171,10 +364,27 @@ impl SignaturePqPublicKey {
|
||||||
pub fn new(bytes: Vec<u8>) -> Self {
|
pub fn new(bytes: Vec<u8>) -> Self {
|
||||||
Self(bytes)
|
Self(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
|
pub fn to_hex(&self) -> String {
|
||||||
|
bytes_to_hex(&self.0)
|
||||||
|
}
|
||||||
|
pub fn from_hex(s: &str) -> Result<Self, crate::error::CryptoError> {
|
||||||
|
hex_to_bytes(s).map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for SignaturePqPublicKey {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "SignaturePqPublicKey({})", self.to_hex())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<[u8]> for SignaturePqPublicKey {
|
||||||
|
fn as_ref(&self) -> &[u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<u8>> for SignaturePqPublicKey {
|
impl From<Vec<u8>> for SignaturePqPublicKey {
|
||||||
|
|
@ -189,32 +399,13 @@ impl From<&[u8]> for SignaturePqPublicKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
impl From<&SignaturePqPublicKey> for Vec<u8> {
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
fn from(key: &SignaturePqPublicKey) -> Vec<u8> {
|
||||||
#[derive(Zeroize, ZeroizeOnDrop)]
|
key.0.clone()
|
||||||
pub struct SignaturePqPrivateKey(Vec<u8>);
|
|
||||||
|
|
||||||
impl SignaturePqPrivateKey {
|
|
||||||
pub fn new(bytes: Vec<u8>) -> Self {
|
|
||||||
Self(bytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
|
||||||
&self.0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Vec<u8>> for SignaturePqPrivateKey {
|
// --- Keyring ---
|
||||||
fn from(bytes: Vec<u8>) -> Self {
|
|
||||||
Self(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&[u8]> for SignaturePqPrivateKey {
|
|
||||||
fn from(bytes: &[u8]) -> Self {
|
|
||||||
Self(bytes.to_vec())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(ZeroizeOnDrop)]
|
#[derive(ZeroizeOnDrop)]
|
||||||
|
|
@ -252,12 +443,9 @@ impl Keyring {
|
||||||
#[cfg(all(feature = "mlkem-tls", feature = "ml-dsa", feature = "ed25519-dalek"))]
|
#[cfg(all(feature = "mlkem-tls", feature = "ml-dsa", feature = "ed25519-dalek"))]
|
||||||
pub fn generate() -> Self {
|
pub fn generate() -> Self {
|
||||||
let (kem_sk, kem_pk) = crate::kem::HybridKem::generate_keypair();
|
let (kem_sk, kem_pk) = crate::kem::HybridKem::generate_keypair();
|
||||||
|
|
||||||
let (ed_signer, sig_cl_sk, sig_cl_pk) = crate::sign::Ed25519Signer::generate();
|
let (ed_signer, sig_cl_sk, sig_cl_pk) = crate::sign::Ed25519Signer::generate();
|
||||||
let (_ml_signer, sig_pq_sk, sig_pq_pk) = crate::sign::MlDsaSigner::generate();
|
let (_ml_signer, sig_pq_sk, sig_pq_pk) = crate::sign::MlDsaSigner::generate();
|
||||||
|
|
||||||
drop(ed_signer);
|
drop(ed_signer);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
kem_public_key: kem_pk,
|
kem_public_key: kem_pk,
|
||||||
kem_secret_key: kem_sk,
|
kem_secret_key: kem_sk,
|
||||||
|
|
@ -267,8 +455,98 @@ impl Keyring {
|
||||||
sig_cl_secret_key: sig_cl_sk,
|
sig_cl_secret_key: sig_cl_sk,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn public_key_bundle(&self) -> PublicKeyBundle {
|
||||||
|
PublicKeyBundle {
|
||||||
|
kem_public_key: self.kem_public_key.clone(),
|
||||||
|
sig_pq_public_key: self.sig_pq_public_key.clone(),
|
||||||
|
sig_cl_public_key: self.sig_cl_public_key.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
let fields: &[&[u8]] = &[
|
||||||
|
self.kem_public_key.as_bytes(),
|
||||||
|
self.kem_secret_key.as_bytes(),
|
||||||
|
self.sig_pq_public_key.as_bytes(),
|
||||||
|
self.sig_pq_secret_key.as_bytes(),
|
||||||
|
self.sig_cl_public_key.as_bytes(),
|
||||||
|
self.sig_cl_secret_key.as_bytes(),
|
||||||
|
];
|
||||||
|
let mut out = Vec::new();
|
||||||
|
for f in fields {
|
||||||
|
out.extend_from_slice(&(f.len() as u16).to_be_bytes());
|
||||||
|
out.extend_from_slice(f);
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self, crate::error::CryptoError> {
|
||||||
|
use crate::error::CryptoError;
|
||||||
|
let mut offset = 0;
|
||||||
|
let read_key = |offset: &mut usize| -> Result<Vec<u8>, CryptoError> {
|
||||||
|
let len = u16::from_be_bytes(
|
||||||
|
bytes
|
||||||
|
.get(*offset..*offset + 2)
|
||||||
|
.ok_or(CryptoError::InvalidKeyLength)?
|
||||||
|
.try_into()
|
||||||
|
.expect("slice is 2 bytes, verified above"),
|
||||||
|
) as usize;
|
||||||
|
*offset += 2;
|
||||||
|
let key = bytes
|
||||||
|
.get(*offset..*offset + len)
|
||||||
|
.ok_or(CryptoError::InvalidKeyLength)?
|
||||||
|
.to_vec();
|
||||||
|
*offset += len;
|
||||||
|
Ok(key)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
kem_public_key: KemPublicKey::new(read_key(&mut offset)?),
|
||||||
|
kem_secret_key: KemPrivateKey::new(read_key(&mut offset)?),
|
||||||
|
sig_pq_public_key: SignaturePqPublicKey::new(read_key(&mut offset)?),
|
||||||
|
sig_pq_secret_key: SignaturePqPrivateKey::new(read_key(&mut offset)?),
|
||||||
|
sig_cl_public_key: SignaturePublicKey::new(read_key(&mut offset)?),
|
||||||
|
sig_cl_secret_key: SignaturePrivateKey::new(read_key(&mut offset)?),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&[u8]> for Keyring {
|
||||||
|
type Error = crate::error::CryptoError;
|
||||||
|
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||||
|
Self::from_bytes(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Keyring> for Vec<u8> {
|
||||||
|
fn from(keyring: &Keyring) -> Vec<u8> {
|
||||||
|
keyring.to_bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Keyring {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("Keyring")
|
||||||
|
.field("kem_public_key", &self.kem_public_key)
|
||||||
|
.field("kem_secret_key", &self.kem_secret_key)
|
||||||
|
.field("sig_pq_public_key", &self.sig_pq_public_key)
|
||||||
|
.field("sig_pq_secret_key", &self.sig_pq_secret_key)
|
||||||
|
.field("sig_cl_public_key", &self.sig_cl_public_key)
|
||||||
|
.field("sig_cl_secret_key", &self.sig_cl_secret_key)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- PublicKeyBundle ---
|
||||||
|
|
||||||
|
/// Ed25519 public key is always 32 bytes.
|
||||||
|
pub const SIG_CL_PUBLIC_KEY_LEN: usize = 32;
|
||||||
|
/// ML-DSA-65 public key is always 1952 bytes.
|
||||||
|
pub const SIG_PQ_PUBLIC_KEY_LEN: usize = 1952;
|
||||||
|
/// Hybrid X25519 + ML-KEM-768 public key (32 + 1184 bytes).
|
||||||
|
pub const KEM_PUBLIC_KEY_LEN: usize = 1216;
|
||||||
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PublicKeyBundle {
|
pub struct PublicKeyBundle {
|
||||||
|
|
@ -290,6 +568,21 @@ impl PublicKeyBundle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "ed25519-dalek", feature = "ml-dsa", feature = "mlkem-tls"))]
|
||||||
|
pub fn validate(&self) -> Result<(), crate::error::CryptoError> {
|
||||||
|
use crate::error::CryptoError;
|
||||||
|
if self.sig_cl_public_key.as_bytes().len() != SIG_CL_PUBLIC_KEY_LEN {
|
||||||
|
return Err(CryptoError::InvalidKeyLength);
|
||||||
|
}
|
||||||
|
if self.sig_pq_public_key.as_bytes().len() != SIG_PQ_PUBLIC_KEY_LEN {
|
||||||
|
return Err(CryptoError::InvalidKeyLength);
|
||||||
|
}
|
||||||
|
if self.kem_public_key.as_bytes().len() != KEM_PUBLIC_KEY_LEN {
|
||||||
|
return Err(CryptoError::InvalidKeyLength);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_bytes(&self) -> Vec<u8> {
|
pub fn as_bytes(&self) -> Vec<u8> {
|
||||||
let kem = self.kem_public_key.as_bytes();
|
let kem = self.kem_public_key.as_bytes();
|
||||||
let pq = self.sig_pq_public_key.as_bytes();
|
let pq = self.sig_pq_public_key.as_bytes();
|
||||||
|
|
@ -354,66 +647,137 @@ impl PublicKeyBundle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Keyring {
|
impl TryFrom<&[u8]> for PublicKeyBundle {
|
||||||
pub fn public_key_bundle(&self) -> PublicKeyBundle {
|
type Error = crate::error::CryptoError;
|
||||||
PublicKeyBundle {
|
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||||
kem_public_key: self.kem_public_key.clone(),
|
Self::from_bytes(bytes)
|
||||||
sig_pq_public_key: self.sig_pq_public_key.clone(),
|
}
|
||||||
sig_cl_public_key: self.sig_cl_public_key.clone(),
|
}
|
||||||
}
|
|
||||||
}
|
impl From<&PublicKeyBundle> for Vec<u8> {
|
||||||
|
fn from(bundle: &PublicKeyBundle) -> Vec<u8> {
|
||||||
/*
|
bundle.as_bytes()
|
||||||
* Serialize the full keyring (all six keys) into a byte vector.
|
}
|
||||||
*
|
}
|
||||||
* Format: for each key, a 2-byte length prefix followed by the key bytes,
|
|
||||||
* in the order: kem_pk, kem_sk, sig_pq_pk, sig_pq_sk, sig_cl_pk, sig_cl_sk.
|
impl fmt::Debug for PublicKeyBundle {
|
||||||
*/
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
pub fn to_bytes(&self) -> Vec<u8> {
|
f.debug_struct("PublicKeyBundle")
|
||||||
let fields: &[&[u8]] = &[
|
.field("kem_public_key", &self.kem_public_key)
|
||||||
self.kem_public_key.as_bytes(),
|
.field("sig_pq_public_key", &self.sig_pq_public_key)
|
||||||
self.kem_secret_key.as_bytes(),
|
.field("sig_cl_public_key", &self.sig_cl_public_key)
|
||||||
self.sig_pq_public_key.as_bytes(),
|
.finish()
|
||||||
self.sig_pq_secret_key.as_bytes(),
|
}
|
||||||
self.sig_cl_public_key.as_bytes(),
|
}
|
||||||
self.sig_cl_secret_key.as_bytes(),
|
|
||||||
];
|
#[cfg(test)]
|
||||||
let mut out = Vec::new();
|
mod tests {
|
||||||
for f in fields {
|
use super::*;
|
||||||
out.extend_from_slice(&(f.len() as u16).to_be_bytes());
|
|
||||||
out.extend_from_slice(f);
|
#[test]
|
||||||
}
|
fn public_key_bundle_roundtrip() {
|
||||||
out
|
let kem = KemPublicKey::new(vec![1u8; 32]);
|
||||||
}
|
let pq = SignaturePqPublicKey::new(vec![2u8; 64]);
|
||||||
|
let cl = SignaturePublicKey::new(vec![3u8; 32]);
|
||||||
/// Deserialize a full keyring from bytes produced by [`Keyring::to_bytes`].
|
|
||||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, crate::error::CryptoError> {
|
let bundle = PublicKeyBundle::new(kem, pq, cl);
|
||||||
use crate::error::CryptoError;
|
let bytes = bundle.as_bytes();
|
||||||
let mut offset = 0;
|
let recovered = PublicKeyBundle::from_bytes(&bytes).unwrap();
|
||||||
let read_key = |offset: &mut usize| -> Result<Vec<u8>, CryptoError> {
|
|
||||||
let len = u16::from_be_bytes(
|
assert_eq!(
|
||||||
bytes
|
bundle.kem_public_key.as_bytes(),
|
||||||
.get(*offset..*offset + 2)
|
recovered.kem_public_key.as_bytes()
|
||||||
.ok_or(CryptoError::InvalidKeyLength)?
|
);
|
||||||
.try_into()
|
assert_eq!(
|
||||||
.expect("slice is 2 bytes, verified above"),
|
bundle.sig_pq_public_key.as_bytes(),
|
||||||
) as usize;
|
recovered.sig_pq_public_key.as_bytes()
|
||||||
*offset += 2;
|
);
|
||||||
let key = bytes
|
assert_eq!(
|
||||||
.get(*offset..*offset + len)
|
bundle.sig_cl_public_key.as_bytes(),
|
||||||
.ok_or(CryptoError::InvalidKeyLength)?
|
recovered.sig_cl_public_key.as_bytes()
|
||||||
.to_vec();
|
);
|
||||||
*offset += len;
|
}
|
||||||
Ok(key)
|
|
||||||
};
|
#[test]
|
||||||
|
fn public_key_bundle_try_from_roundtrip() {
|
||||||
Ok(Self {
|
let bundle = PublicKeyBundle::new(
|
||||||
kem_public_key: KemPublicKey::new(read_key(&mut offset)?),
|
KemPublicKey::new(vec![0xABu8; 48]),
|
||||||
kem_secret_key: KemPrivateKey::new(read_key(&mut offset)?),
|
SignaturePqPublicKey::new(vec![0xCDu8; 96]),
|
||||||
sig_pq_public_key: SignaturePqPublicKey::new(read_key(&mut offset)?),
|
SignaturePublicKey::new(vec![0xEFu8; 32]),
|
||||||
sig_pq_secret_key: SignaturePqPrivateKey::new(read_key(&mut offset)?),
|
);
|
||||||
sig_cl_public_key: SignaturePublicKey::new(read_key(&mut offset)?),
|
let bytes: Vec<u8> = Vec::from(&bundle);
|
||||||
sig_cl_secret_key: SignaturePrivateKey::new(read_key(&mut offset)?),
|
let recovered = PublicKeyBundle::try_from(bytes.as_slice()).unwrap();
|
||||||
})
|
assert_eq!(bundle.as_bytes(), recovered.as_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn keyring_roundtrip() {
|
||||||
|
let keyring = Keyring::new(
|
||||||
|
KemPublicKey::new(vec![1u8; 32]),
|
||||||
|
KemPrivateKey::new(vec![2u8; 32]),
|
||||||
|
SignaturePqPublicKey::new(vec![3u8; 64]),
|
||||||
|
SignaturePqPrivateKey::new(vec![4u8; 64]),
|
||||||
|
SignaturePublicKey::new(vec![5u8; 32]),
|
||||||
|
SignaturePrivateKey::new(vec![6u8; 32]),
|
||||||
|
);
|
||||||
|
let bytes = keyring.to_bytes();
|
||||||
|
let recovered = Keyring::from_bytes(&bytes).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
keyring.kem_public_key.as_bytes(),
|
||||||
|
recovered.kem_public_key.as_bytes()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
keyring.kem_secret_key.as_bytes(),
|
||||||
|
recovered.kem_secret_key.as_bytes()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
keyring.sig_cl_public_key.as_bytes(),
|
||||||
|
recovered.sig_cl_public_key.as_bytes()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn keyring_try_from_roundtrip() {
|
||||||
|
let keyring = Keyring::new(
|
||||||
|
KemPublicKey::new(vec![0u8; 16]),
|
||||||
|
KemPrivateKey::new(vec![1u8; 16]),
|
||||||
|
SignaturePqPublicKey::new(vec![2u8; 16]),
|
||||||
|
SignaturePqPrivateKey::new(vec![3u8; 16]),
|
||||||
|
SignaturePublicKey::new(vec![4u8; 16]),
|
||||||
|
SignaturePrivateKey::new(vec![5u8; 16]),
|
||||||
|
);
|
||||||
|
let bytes: Vec<u8> = Vec::from(&keyring);
|
||||||
|
let recovered = Keyring::try_from(bytes.as_slice()).unwrap();
|
||||||
|
assert_eq!(keyring.to_bytes(), recovered.to_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hex_roundtrip() {
|
||||||
|
let key = KemPublicKey::new(vec![0xDE, 0xAD, 0xBE, 0xEF]);
|
||||||
|
let hex = key.to_hex();
|
||||||
|
assert_eq!(hex, "deadbeef");
|
||||||
|
let recovered = KemPublicKey::from_hex(&hex).unwrap();
|
||||||
|
assert_eq!(key.as_bytes(), recovered.as_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hex_invalid_returns_error() {
|
||||||
|
assert!(KemPublicKey::from_hex("xyz").is_err());
|
||||||
|
assert!(KemPublicKey::from_hex("abc").is_err()); // odd length
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn debug_private_key_redacted() {
|
||||||
|
let key = KemPrivateKey::new(vec![0u8; 32]);
|
||||||
|
let s = format!("{:?}", key);
|
||||||
|
assert!(s.contains("REDACTED"));
|
||||||
|
assert!(!s.contains("00"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn debug_public_key_shows_hex() {
|
||||||
|
let key = KemPublicKey::new(vec![0xABu8; 4]);
|
||||||
|
let s = format!("{:?}", key);
|
||||||
|
assert!(s.contains("abababab"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue