extern crate alloc;
use alloc::vec::Vec;
use crate::error::WireError;
pub mod symmetric_bit {
pub const AES128: u32 = 1 << 0;
pub const AES256: u32 = 1 << 1;
}
pub mod digital_signature_bit {
pub const RSASSA_PSS_2048_SHA256: u32 = 1 << 0;
pub const RSASSA_PKCS1_V15_2048_SHA256: u32 = 1 << 1;
pub const ECDSA_P256_SHA256: u32 = 1 << 2;
pub const ECDSA_P384_SHA384: u32 = 1 << 3;
}
pub mod key_establishment_bit {
pub const DHE_MODP_2048_256: u32 = 1 << 0;
pub const ECDHE_CEUM_P256: u32 = 1 << 1;
pub const ECDHE_CEUM_P384: u32 = 1 << 2;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AlgorithmRequirements {
pub supported: u32,
pub required: u32,
}
impl AlgorithmRequirements {
pub const WIRE_SIZE: usize = 8;
#[must_use]
pub fn to_bytes(&self, little_endian: bool) -> [u8; 8] {
let mut out = [0u8; 8];
if little_endian {
out[0..4].copy_from_slice(&self.supported.to_le_bytes());
out[4..8].copy_from_slice(&self.required.to_le_bytes());
} else {
out[0..4].copy_from_slice(&self.supported.to_be_bytes());
out[4..8].copy_from_slice(&self.required.to_be_bytes());
}
out
}
pub fn from_bytes(bytes: &[u8], little_endian: bool) -> Result<Self, WireError> {
if bytes.len() < 8 {
return Err(WireError::ValueOutOfRange {
message: "AlgorithmRequirements: < 8 bytes",
});
}
let mut s = [0u8; 4];
let mut r = [0u8; 4];
s.copy_from_slice(&bytes[0..4]);
r.copy_from_slice(&bytes[4..8]);
Ok(if little_endian {
Self {
supported: u32::from_le_bytes(s),
required: u32::from_le_bytes(r),
}
} else {
Self {
supported: u32::from_be_bytes(s),
required: u32::from_be_bytes(r),
}
})
}
#[must_use]
pub fn is_compatible_with(&self, remote_supported: u32) -> bool {
(remote_supported & self.required) == self.required
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ParticipantSecurityDigitalSignatureAlgorithmInfo {
pub trust_chain: AlgorithmRequirements,
pub message_auth: AlgorithmRequirements,
}
impl ParticipantSecurityDigitalSignatureAlgorithmInfo {
pub const WIRE_SIZE: usize = 16;
#[must_use]
pub fn spec_default() -> Self {
let mask = digital_signature_bit::RSASSA_PSS_2048_SHA256
| digital_signature_bit::ECDSA_P256_SHA256;
Self {
trust_chain: AlgorithmRequirements {
supported: mask,
required: mask,
},
message_auth: AlgorithmRequirements {
supported: mask,
required: mask,
},
}
}
#[must_use]
pub fn to_bytes(&self, little_endian: bool) -> [u8; 16] {
let mut out = [0u8; 16];
out[0..8].copy_from_slice(&self.trust_chain.to_bytes(little_endian));
out[8..16].copy_from_slice(&self.message_auth.to_bytes(little_endian));
out
}
pub fn from_bytes(bytes: &[u8], little_endian: bool) -> Result<Self, WireError> {
if bytes.len() < 16 {
return Err(WireError::ValueOutOfRange {
message: "DigitalSignatureAlgorithmInfo: < 16 bytes",
});
}
Ok(Self {
trust_chain: AlgorithmRequirements::from_bytes(&bytes[0..8], little_endian)?,
message_auth: AlgorithmRequirements::from_bytes(&bytes[8..16], little_endian)?,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ParticipantSecurityKeyEstablishmentAlgorithmInfo {
pub shared_secret: AlgorithmRequirements,
}
impl ParticipantSecurityKeyEstablishmentAlgorithmInfo {
pub const WIRE_SIZE: usize = 8;
#[must_use]
pub fn spec_default() -> Self {
let mask =
key_establishment_bit::DHE_MODP_2048_256 | key_establishment_bit::ECDHE_CEUM_P256;
Self {
shared_secret: AlgorithmRequirements {
supported: mask,
required: mask,
},
}
}
#[must_use]
pub fn to_bytes(&self, little_endian: bool) -> [u8; 8] {
self.shared_secret.to_bytes(little_endian)
}
pub fn from_bytes(bytes: &[u8], little_endian: bool) -> Result<Self, WireError> {
Ok(Self {
shared_secret: AlgorithmRequirements::from_bytes(bytes, little_endian)?,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ParticipantSecuritySymmetricCipherAlgorithmInfo {
pub supported_mask: u32,
pub builtin_endpoints_required_mask: u32,
pub builtin_kx_endpoints_required_mask: u32,
pub user_endpoints_default_required_mask: u32,
}
impl ParticipantSecuritySymmetricCipherAlgorithmInfo {
pub const WIRE_SIZE: usize = 16;
#[must_use]
pub fn spec_default() -> Self {
Self {
supported_mask: symmetric_bit::AES128 | symmetric_bit::AES256,
builtin_endpoints_required_mask: symmetric_bit::AES128,
builtin_kx_endpoints_required_mask: symmetric_bit::AES128,
user_endpoints_default_required_mask: symmetric_bit::AES128,
}
}
#[must_use]
pub fn to_bytes(&self, little_endian: bool) -> [u8; 16] {
let mut out = [0u8; 16];
let fields = [
self.supported_mask,
self.builtin_endpoints_required_mask,
self.builtin_kx_endpoints_required_mask,
self.user_endpoints_default_required_mask,
];
for (i, v) in fields.iter().enumerate() {
let bytes = if little_endian {
v.to_le_bytes()
} else {
v.to_be_bytes()
};
out[i * 4..i * 4 + 4].copy_from_slice(&bytes);
}
out
}
pub fn from_bytes(bytes: &[u8], little_endian: bool) -> Result<Self, WireError> {
if bytes.len() < 16 {
return Err(WireError::ValueOutOfRange {
message: "SymmetricCipherAlgorithmInfo: < 16 bytes",
});
}
let read = |off: usize| -> u32 {
let mut a = [0u8; 4];
a.copy_from_slice(&bytes[off..off + 4]);
if little_endian {
u32::from_le_bytes(a)
} else {
u32::from_be_bytes(a)
}
};
Ok(Self {
supported_mask: read(0),
builtin_endpoints_required_mask: read(4),
builtin_kx_endpoints_required_mask: read(8),
user_endpoints_default_required_mask: read(12),
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EndpointSecuritySymmetricCipherAlgorithmInfo {
pub required_mask: u32,
}
impl EndpointSecuritySymmetricCipherAlgorithmInfo {
pub const WIRE_SIZE: usize = 4;
#[must_use]
pub fn to_bytes(&self, little_endian: bool) -> [u8; 4] {
if little_endian {
self.required_mask.to_le_bytes()
} else {
self.required_mask.to_be_bytes()
}
}
pub fn from_bytes(bytes: &[u8], little_endian: bool) -> Result<Self, WireError> {
if bytes.len() < 4 {
return Err(WireError::ValueOutOfRange {
message: "EndpointSymmetricCipherAlgorithmInfo: < 4 bytes",
});
}
let mut a = [0u8; 4];
a.copy_from_slice(&bytes[0..4]);
Ok(Self {
required_mask: if little_endian {
u32::from_le_bytes(a)
} else {
u32::from_be_bytes(a)
},
})
}
}
#[allow(dead_code)]
fn _vec_keepalive(v: Vec<u8>) -> Vec<u8> {
v
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn algorithm_requirements_roundtrip_le() {
let a = AlgorithmRequirements {
supported: 0xCAFE_BABE,
required: 0xDEAD_BEEF,
};
let bytes = a.to_bytes(true);
let back = AlgorithmRequirements::from_bytes(&bytes, true).unwrap();
assert_eq!(a, back);
}
#[test]
fn algorithm_requirements_roundtrip_be() {
let a = AlgorithmRequirements {
supported: 0x0102_0304,
required: 0x0506_0708,
};
let bytes = a.to_bytes(false);
let back = AlgorithmRequirements::from_bytes(&bytes, false).unwrap();
assert_eq!(a, back);
}
#[test]
fn algorithm_requirements_layout_be() {
let a = AlgorithmRequirements {
supported: 0x01,
required: 0x02,
};
assert_eq!(a.to_bytes(false), [0, 0, 0, 0x01, 0, 0, 0, 0x02]);
}
#[test]
fn compatibility_check_strict() {
let local = AlgorithmRequirements {
supported: 0,
required: 0b101,
};
assert!(local.is_compatible_with(0b111));
assert!(local.is_compatible_with(0b101));
assert!(!local.is_compatible_with(0b001));
assert!(!local.is_compatible_with(0));
}
#[test]
fn sig_info_spec_default() {
let d = ParticipantSecurityDigitalSignatureAlgorithmInfo::spec_default();
let expected = digital_signature_bit::RSASSA_PSS_2048_SHA256
| digital_signature_bit::ECDSA_P256_SHA256;
assert_eq!(d.trust_chain.supported, expected);
assert_eq!(d.trust_chain.required, expected);
assert_eq!(d.message_auth.supported, expected);
assert_eq!(d.message_auth.required, expected);
}
#[test]
fn sig_info_roundtrip() {
let d = ParticipantSecurityDigitalSignatureAlgorithmInfo::spec_default();
let bytes = d.to_bytes(true);
let back =
ParticipantSecurityDigitalSignatureAlgorithmInfo::from_bytes(&bytes, true).unwrap();
assert_eq!(d, back);
}
#[test]
fn kx_info_spec_default() {
let k = ParticipantSecurityKeyEstablishmentAlgorithmInfo::spec_default();
let expected =
key_establishment_bit::DHE_MODP_2048_256 | key_establishment_bit::ECDHE_CEUM_P256;
assert_eq!(k.shared_secret.supported, expected);
assert_eq!(k.shared_secret.required, expected);
}
#[test]
fn kx_info_roundtrip() {
let k = ParticipantSecurityKeyEstablishmentAlgorithmInfo::spec_default();
let bytes = k.to_bytes(true);
let back =
ParticipantSecurityKeyEstablishmentAlgorithmInfo::from_bytes(&bytes, true).unwrap();
assert_eq!(k, back);
}
#[test]
fn sym_info_spec_default() {
let s = ParticipantSecuritySymmetricCipherAlgorithmInfo::spec_default();
assert_eq!(
s.supported_mask,
symmetric_bit::AES128 | symmetric_bit::AES256
);
assert_eq!(s.builtin_endpoints_required_mask, symmetric_bit::AES128);
assert_eq!(s.builtin_kx_endpoints_required_mask, symmetric_bit::AES128);
assert_eq!(
s.user_endpoints_default_required_mask,
symmetric_bit::AES128
);
}
#[test]
fn sym_info_roundtrip() {
let s = ParticipantSecuritySymmetricCipherAlgorithmInfo::spec_default();
let bytes = s.to_bytes(true);
let back =
ParticipantSecuritySymmetricCipherAlgorithmInfo::from_bytes(&bytes, true).unwrap();
assert_eq!(s, back);
}
#[test]
fn endpoint_sym_info_roundtrip() {
let e = EndpointSecuritySymmetricCipherAlgorithmInfo {
required_mask: symmetric_bit::AES256,
};
let bytes = e.to_bytes(true);
let back = EndpointSecuritySymmetricCipherAlgorithmInfo::from_bytes(&bytes, true).unwrap();
assert_eq!(e, back);
}
#[test]
fn truncated_buffer_rejected() {
assert!(AlgorithmRequirements::from_bytes(&[1, 2, 3], true).is_err());
assert!(
ParticipantSecurityDigitalSignatureAlgorithmInfo::from_bytes(&[0u8; 15], true).is_err()
);
assert!(
ParticipantSecuritySymmetricCipherAlgorithmInfo::from_bytes(&[0u8; 15], true).is_err()
);
assert!(EndpointSecuritySymmetricCipherAlgorithmInfo::from_bytes(&[0u8; 3], true).is_err());
}
#[test]
fn spec_bit_constants_match() {
assert_eq!(symmetric_bit::AES128, 0x01);
assert_eq!(symmetric_bit::AES256, 0x02);
assert_eq!(digital_signature_bit::RSASSA_PSS_2048_SHA256, 0x01);
assert_eq!(digital_signature_bit::RSASSA_PKCS1_V15_2048_SHA256, 0x02);
assert_eq!(digital_signature_bit::ECDSA_P256_SHA256, 0x04);
assert_eq!(digital_signature_bit::ECDSA_P384_SHA384, 0x08);
assert_eq!(key_establishment_bit::DHE_MODP_2048_256, 0x01);
assert_eq!(key_establishment_bit::ECDHE_CEUM_P256, 0x02);
assert_eq!(key_establishment_bit::ECDHE_CEUM_P384, 0x04);
}
}