#![allow(missing_docs)]
use crate::crypto::pqc::types::PqcError;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u16)]
pub enum NamedGroup {
MlKem512 = 0x0200, MlKem768 = 0x0201, MlKem1024 = 0x0202, }
impl NamedGroup {
pub const PRIMARY: Self = Self::MlKem768;
pub fn is_pqc(&self) -> bool {
true
}
pub fn is_supported(&self) -> bool {
true
}
pub fn from_u16(value: u16) -> Option<Self> {
match value {
0x0200 => Some(Self::MlKem512),
0x0201 => Some(Self::MlKem768),
0x0202 => Some(Self::MlKem1024),
_ => None, }
}
pub fn to_u16(&self) -> u16 {
*self as u16
}
pub fn name(&self) -> &'static str {
match self {
Self::MlKem512 => "ML-KEM-512",
Self::MlKem768 => "ML-KEM-768",
Self::MlKem1024 => "ML-KEM-1024",
}
}
pub fn to_bytes(&self) -> [u8; 2] {
self.to_u16().to_be_bytes()
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, PqcError> {
if bytes.len() < 2 {
return Err(PqcError::CryptoError(
"Invalid named group bytes".to_string(),
));
}
let value = u16::from_be_bytes([bytes[0], bytes[1]]);
Self::from_u16(value).ok_or_else(|| {
PqcError::NegotiationFailed(format!(
"Named group 0x{:04X} not supported - use ML-KEM-768 (0x0201)",
value
))
})
}
}
impl fmt::Display for NamedGroup {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u16)]
pub enum SignatureScheme {
MlDsa44 = 0x0904, MlDsa65 = 0x0905, MlDsa87 = 0x0906, }
impl SignatureScheme {
pub const PRIMARY: Self = Self::MlDsa65;
pub fn is_pqc(&self) -> bool {
true
}
pub fn is_supported(&self) -> bool {
true
}
pub fn from_u16(value: u16) -> Option<Self> {
match value {
0x0904 => Some(Self::MlDsa44),
0x0905 => Some(Self::MlDsa65),
0x0906 => Some(Self::MlDsa87),
_ => None, }
}
pub fn to_u16(&self) -> u16 {
*self as u16
}
pub fn name(&self) -> &'static str {
match self {
Self::MlDsa44 => "ML-DSA-44",
Self::MlDsa65 => "ML-DSA-65",
Self::MlDsa87 => "ML-DSA-87",
}
}
pub fn to_bytes(&self) -> [u8; 2] {
self.to_u16().to_be_bytes()
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self, PqcError> {
if bytes.len() < 2 {
return Err(PqcError::CryptoError(
"Invalid signature scheme bytes".to_string(),
));
}
let value = u16::from_be_bytes([bytes[0], bytes[1]]);
Self::from_u16(value).ok_or_else(|| {
PqcError::NegotiationFailed(format!(
"Signature scheme 0x{:04X} not supported - use ML-DSA-65 (0x0905)",
value
))
})
}
}
impl fmt::Display for SignatureScheme {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_named_group_primary() {
assert_eq!(NamedGroup::PRIMARY, NamedGroup::MlKem768);
assert_eq!(NamedGroup::PRIMARY.to_u16(), 0x0201);
}
#[test]
fn test_named_group_conversions() {
assert_eq!(NamedGroup::MlKem768.to_u16(), 0x0201);
assert_eq!(NamedGroup::from_u16(0x0201), Some(NamedGroup::MlKem768));
assert_eq!(NamedGroup::from_u16(0x0200), Some(NamedGroup::MlKem512));
assert_eq!(NamedGroup::from_u16(0x0202), Some(NamedGroup::MlKem1024));
assert_eq!(NamedGroup::from_u16(0x001D), None); assert_eq!(NamedGroup::from_u16(0x0017), None);
assert_eq!(NamedGroup::from_u16(0x11EC), None); assert_eq!(NamedGroup::from_u16(0x11EB), None); }
#[test]
fn test_signature_scheme_primary() {
assert_eq!(SignatureScheme::PRIMARY, SignatureScheme::MlDsa65);
assert_eq!(SignatureScheme::PRIMARY.to_u16(), 0x0905);
}
#[test]
fn test_signature_scheme_conversions() {
assert_eq!(SignatureScheme::MlDsa65.to_u16(), 0x0905);
assert_eq!(
SignatureScheme::from_u16(0x0905),
Some(SignatureScheme::MlDsa65)
);
assert_eq!(
SignatureScheme::from_u16(0x0904),
Some(SignatureScheme::MlDsa44)
);
assert_eq!(
SignatureScheme::from_u16(0x0906),
Some(SignatureScheme::MlDsa87)
);
assert_eq!(SignatureScheme::from_u16(0x0807), None); assert_eq!(SignatureScheme::from_u16(0x0403), None);
assert_eq!(SignatureScheme::from_u16(0x0920), None); assert_eq!(SignatureScheme::from_u16(0x0921), None); }
#[test]
fn test_wire_format_serialization() {
let group = NamedGroup::MlKem768;
let bytes = group.to_bytes();
assert_eq!(bytes, [0x02, 0x01]);
assert_eq!(NamedGroup::from_bytes(&bytes).unwrap(), group);
let scheme = SignatureScheme::MlDsa65;
let bytes = scheme.to_bytes();
assert_eq!(bytes, [0x09, 0x05]);
assert_eq!(SignatureScheme::from_bytes(&bytes).unwrap(), scheme);
}
#[test]
fn test_rejected_groups_error() {
let result = NamedGroup::from_bytes(&[0x00, 0x1D]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.to_string().contains("0x001D"));
assert!(err.to_string().contains("ML-KEM-768"));
let result = NamedGroup::from_bytes(&[0x11, 0xEC]);
assert!(result.is_err());
}
#[test]
fn test_is_pqc() {
assert!(NamedGroup::MlKem768.is_pqc());
assert!(NamedGroup::MlKem512.is_pqc());
assert!(NamedGroup::MlKem1024.is_pqc());
assert!(SignatureScheme::MlDsa65.is_pqc());
assert!(SignatureScheme::MlDsa44.is_pqc());
assert!(SignatureScheme::MlDsa87.is_pqc());
}
#[test]
fn test_display() {
assert_eq!(format!("{}", NamedGroup::MlKem768), "ML-KEM-768");
assert_eq!(format!("{}", SignatureScheme::MlDsa65), "ML-DSA-65");
}
}