use std::ops::Deref;
use serde::{Deserialize, Serialize};
use tls_codec::{SecretVLBytes, TlsDeserialize, TlsSerialize, TlsSize, VLBytes};
use crate::key_store::{MlsEntity, MlsEntityId};
#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
#[repr(u16)]
pub enum AeadType {
Aes128Gcm = 0x0001,
Aes256Gcm = 0x0002,
ChaCha20Poly1305 = 0x0003,
}
impl AeadType {
pub const fn tag_size(&self) -> usize {
match self {
AeadType::Aes128Gcm => 16,
AeadType::Aes256Gcm => 16,
AeadType::ChaCha20Poly1305 => 16,
}
}
pub const fn key_size(&self) -> usize {
match self {
AeadType::Aes128Gcm => 16,
AeadType::Aes256Gcm => 32,
AeadType::ChaCha20Poly1305 => 32,
}
}
pub const fn nonce_size(&self) -> usize {
match self {
AeadType::Aes128Gcm | AeadType::Aes256Gcm | AeadType::ChaCha20Poly1305 => 12,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(u8)]
#[allow(non_camel_case_types)]
pub enum HashType {
Sha2_256 = 0x04,
Sha2_384 = 0x05,
Sha2_512 = 0x06,
}
impl HashType {
#[inline]
pub const fn size(&self) -> usize {
match self {
HashType::Sha2_256 => 32,
HashType::Sha2_384 => 48,
HashType::Sha2_512 => 64,
}
}
}
#[allow(non_camel_case_types)]
#[allow(clippy::upper_case_acronyms)]
#[derive(
Copy,
Hash,
Eq,
PartialEq,
Clone,
Debug,
Serialize,
Deserialize,
TlsSerialize,
TlsDeserialize,
TlsSize,
)]
#[repr(u16)]
pub enum SignatureScheme {
ECDSA_SECP256R1_SHA256 = 0x0403,
ECDSA_SECP384R1_SHA384 = 0x0503,
ECDSA_SECP521R1_SHA512 = 0x0603,
ED25519 = 0x0807,
ED448 = 0x0808,
}
impl TryFrom<u16> for SignatureScheme {
type Error = String;
fn try_from(value: u16) -> Result<Self, Self::Error> {
match value {
0x0403 => Ok(SignatureScheme::ECDSA_SECP256R1_SHA256),
0x0503 => Ok(SignatureScheme::ECDSA_SECP384R1_SHA384),
0x0603 => Ok(SignatureScheme::ECDSA_SECP521R1_SHA512),
0x0807 => Ok(SignatureScheme::ED25519),
0x0808 => Ok(SignatureScheme::ED448),
_ => Err(format!("Unsupported SignatureScheme: {value}")),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Error {
CryptoError(CryptoError),
InvalidSignature,
SigningError,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum CryptoError {
CryptoLibraryError,
AeadDecryptionError,
HpkeDecryptionError,
UnsupportedSignatureScheme,
KdfLabelTooLarge,
KdfSerializationError,
HkdfOutputLengthInvalid,
InsufficientRandomness,
InvalidSignature,
UnsupportedAeadAlgorithm,
UnsupportedKdf,
InvalidLength,
UnsupportedHashAlgorithm,
SignatureEncodingError,
SignatureDecodingError,
SenderSetupError,
ReceiverSetupError,
ExporterError,
UnsupportedCiphersuite,
TlsSerializationError,
}
impl std::fmt::Display for CryptoError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for CryptoError {}
#[derive(Debug)]
pub struct HpkeConfig(pub HpkeKemType, pub HpkeKdfType, pub HpkeAeadType);
#[derive(PartialEq, Eq, Copy, Clone, Debug, Serialize, Deserialize)]
#[repr(u16)]
pub enum HpkeKemType {
DhKemP256 = 0x0010,
DhKemP384 = 0x0011,
DhKemP521 = 0x0012,
DhKem25519 = 0x0020,
DhKem448 = 0x0021,
}
#[derive(PartialEq, Eq, Copy, Clone, Debug, Serialize, Deserialize)]
#[repr(u16)]
pub enum HpkeKdfType {
HkdfSha256 = 0x0001,
HkdfSha384 = 0x0002,
HkdfSha512 = 0x0003,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[repr(u16)]
pub enum HpkeAeadType {
AesGcm128 = 0x0001,
AesGcm256 = 0x0002,
ChaCha20Poly1305 = 0x0003,
Export = 0xFFFF,
}
#[derive(
Debug, PartialEq, Eq, Clone, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize,
)]
pub struct HpkeCiphertext {
pub kem_output: VLBytes,
pub ciphertext: VLBytes,
}
#[derive(
Debug, Clone, serde::Serialize, serde::Deserialize, TlsSerialize, TlsDeserialize, TlsSize,
)]
#[cfg_attr(feature = "test-utils", derive(PartialEq, Eq))]
#[serde(transparent)]
pub struct HpkePrivateKey(SecretVLBytes);
impl From<Vec<u8>> for HpkePrivateKey {
fn from(bytes: Vec<u8>) -> Self {
Self(bytes.into())
}
}
impl From<&[u8]> for HpkePrivateKey {
fn from(bytes: &[u8]) -> Self {
Self(bytes.into())
}
}
impl std::ops::Deref for HpkePrivateKey {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.0.as_slice()
}
}
impl MlsEntity for HpkePrivateKey {
const ID: MlsEntityId = MlsEntityId::HpkePrivateKey;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HpkeKeyPair {
pub private: HpkePrivateKey,
pub public: Vec<u8>,
}
pub type KemOutput = Vec<u8>;
#[derive(Clone, Debug)]
pub struct ExporterSecret(SecretVLBytes);
impl Deref for ExporterSecret {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.0.as_slice()
}
}
impl From<Vec<u8>> for ExporterSecret {
fn from(secret: Vec<u8>) -> Self {
Self(secret.into())
}
}
#[derive(
Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize,
)]
pub struct VerifiableCiphersuite(u16);
impl VerifiableCiphersuite {
pub fn new(value: u16) -> Self {
Self(value)
}
}
impl From<Ciphersuite> for VerifiableCiphersuite {
fn from(value: Ciphersuite) -> Self {
Self(value as u16)
}
}
#[allow(non_camel_case_types)]
#[allow(clippy::upper_case_acronyms)]
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
TlsDeserialize,
TlsSerialize,
TlsSize,
)]
#[repr(u16)]
pub enum Ciphersuite {
MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519 = 0x0001,
MLS_128_DHKEMP256_AES128GCM_SHA256_P256 = 0x0002,
MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 = 0x0003,
MLS_256_DHKEMX448_AES256GCM_SHA512_Ed448 = 0x0004,
MLS_256_DHKEMP521_AES256GCM_SHA512_P521 = 0x0005,
MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448 = 0x0006,
MLS_256_DHKEMP384_AES256GCM_SHA384_P384 = 0x0007,
}
impl core::fmt::Display for Ciphersuite {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl From<Ciphersuite> for u16 {
#[inline(always)]
fn from(s: Ciphersuite) -> u16 {
s as u16
}
}
impl From<&Ciphersuite> for u16 {
#[inline(always)]
fn from(s: &Ciphersuite) -> u16 {
*s as u16
}
}
impl TryFrom<u16> for Ciphersuite {
type Error = tls_codec::Error;
#[inline(always)]
fn try_from(v: u16) -> Result<Self, Self::Error> {
match v {
0x0001 => Ok(Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519),
0x0002 => Ok(Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256),
0x0003 => Ok(Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519),
0x0004 => Ok(Ciphersuite::MLS_256_DHKEMX448_AES256GCM_SHA512_Ed448),
0x0005 => Ok(Ciphersuite::MLS_256_DHKEMP521_AES256GCM_SHA512_P521),
0x0006 => Ok(Ciphersuite::MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448),
0x0007 => Ok(Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384),
_ => Err(Self::Error::DecodingError(format!(
"{v} is not a valid ciphersuite value"
))),
}
}
}
impl From<Ciphersuite> for SignatureScheme {
#[inline(always)]
fn from(ciphersuite_name: Ciphersuite) -> Self {
ciphersuite_name.signature_algorithm()
}
}
impl From<Ciphersuite> for AeadType {
#[inline(always)]
fn from(ciphersuite_name: Ciphersuite) -> Self {
ciphersuite_name.aead_algorithm()
}
}
impl From<Ciphersuite> for HpkeKemType {
#[inline(always)]
fn from(ciphersuite_name: Ciphersuite) -> Self {
ciphersuite_name.hpke_kem_algorithm()
}
}
impl From<Ciphersuite> for HpkeAeadType {
#[inline(always)]
fn from(ciphersuite_name: Ciphersuite) -> Self {
ciphersuite_name.hpke_aead_algorithm()
}
}
impl From<Ciphersuite> for HpkeKdfType {
#[inline(always)]
fn from(ciphersuite_name: Ciphersuite) -> Self {
ciphersuite_name.hpke_kdf_algorithm()
}
}
impl From<Ciphersuite> for HashType {
#[inline(always)]
fn from(ciphersuite_name: Ciphersuite) -> Self {
ciphersuite_name.hash_algorithm()
}
}
impl Ciphersuite {
#[inline]
pub const fn hash_algorithm(&self) -> HashType {
match self {
Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519
| Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256
| Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 => {
HashType::Sha2_256
}
Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 => HashType::Sha2_384,
Ciphersuite::MLS_256_DHKEMX448_AES256GCM_SHA512_Ed448
| Ciphersuite::MLS_256_DHKEMP521_AES256GCM_SHA512_P521
| Ciphersuite::MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448 => HashType::Sha2_512,
}
}
#[inline]
pub const fn signature_algorithm(&self) -> SignatureScheme {
match self {
Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519
| Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 => {
SignatureScheme::ED25519
}
Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 => {
SignatureScheme::ECDSA_SECP256R1_SHA256
}
Ciphersuite::MLS_256_DHKEMP521_AES256GCM_SHA512_P521 => {
SignatureScheme::ECDSA_SECP521R1_SHA512
}
Ciphersuite::MLS_256_DHKEMX448_AES256GCM_SHA512_Ed448
| Ciphersuite::MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448 => {
SignatureScheme::ED448
}
Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 => {
SignatureScheme::ECDSA_SECP384R1_SHA384
}
}
}
#[inline]
pub const fn aead_algorithm(&self) -> AeadType {
match self {
Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519
| Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 => AeadType::Aes128Gcm,
Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519
| Ciphersuite::MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448 => {
AeadType::ChaCha20Poly1305
}
Ciphersuite::MLS_256_DHKEMX448_AES256GCM_SHA512_Ed448
| Ciphersuite::MLS_256_DHKEMP521_AES256GCM_SHA512_P521
| Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 => AeadType::Aes256Gcm,
}
}
#[inline]
pub const fn hpke_kdf_algorithm(&self) -> HpkeKdfType {
match self {
Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519
| Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256
| Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 => {
HpkeKdfType::HkdfSha256
}
Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 => HpkeKdfType::HkdfSha384,
Ciphersuite::MLS_256_DHKEMX448_AES256GCM_SHA512_Ed448
| Ciphersuite::MLS_256_DHKEMP521_AES256GCM_SHA512_P521
| Ciphersuite::MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448 => {
HpkeKdfType::HkdfSha512
}
}
}
#[inline]
pub const fn hpke_kem_algorithm(&self) -> HpkeKemType {
match self {
Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519
| Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 => {
HpkeKemType::DhKem25519
}
Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 => HpkeKemType::DhKemP256,
Ciphersuite::MLS_256_DHKEMX448_AES256GCM_SHA512_Ed448
| Ciphersuite::MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448 => HpkeKemType::DhKem448,
Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384 => HpkeKemType::DhKemP384,
Ciphersuite::MLS_256_DHKEMP521_AES256GCM_SHA512_P521 => HpkeKemType::DhKemP521,
}
}
#[inline]
pub const fn hpke_aead_algorithm(&self) -> HpkeAeadType {
match self {
Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519
| Ciphersuite::MLS_128_DHKEMP256_AES128GCM_SHA256_P256 => HpkeAeadType::AesGcm128,
Ciphersuite::MLS_128_DHKEMX25519_CHACHA20POLY1305_SHA256_Ed25519 => {
HpkeAeadType::ChaCha20Poly1305
}
Ciphersuite::MLS_256_DHKEMX448_AES256GCM_SHA512_Ed448
| Ciphersuite::MLS_256_DHKEMP384_AES256GCM_SHA384_P384
| Ciphersuite::MLS_256_DHKEMP521_AES256GCM_SHA512_P521 => HpkeAeadType::AesGcm256,
Ciphersuite::MLS_256_DHKEMX448_CHACHA20POLY1305_SHA512_Ed448 => {
HpkeAeadType::ChaCha20Poly1305
}
}
}
#[inline]
pub const fn hpke_config(&self) -> HpkeConfig {
HpkeConfig(
self.hpke_kem_algorithm(),
self.hpke_kdf_algorithm(),
self.hpke_aead_algorithm(),
)
}
#[inline]
pub const fn hash_length(&self) -> usize {
self.hash_algorithm().size()
}
#[inline]
pub const fn mac_length(&self) -> usize {
self.aead_algorithm().tag_size()
}
#[inline]
pub const fn aead_key_length(&self) -> usize {
self.aead_algorithm().key_size()
}
#[inline]
pub const fn aead_nonce_length(&self) -> usize {
self.aead_algorithm().nonce_size()
}
}