use crate::error::{Error as PkeError, Result as PkeResult};
use dcrypt_algorithms::hash::sha2::{Sha256, Sha384, Sha512}; use dcrypt_algorithms::kdf::hkdf::Hkdf;
use dcrypt_algorithms::kdf::KeyDerivationFunction;
#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::format;
#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::string::String;
#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::vec::Vec;
pub mod p192; pub mod p224;
pub mod p256;
pub mod p384;
pub mod p521;
pub use p192::EciesP192; pub use p224::EciesP224;
pub use p256::EciesP256;
pub use p384::EciesP384;
pub use p521::EciesP521;
pub(crate) const CHACHA20POLY1305_KEY_LEN: usize = 32;
pub(crate) const AES256GCM_KEY_LEN: usize = 32;
pub(crate) const CHACHA20POLY1305_NONCE_LEN: usize = 12;
pub(crate) const AES256GCM_NONCE_LEN: usize = 12;
pub(crate) fn derive_symmetric_key_hkdf_sha256(
shared_secret_z: &[u8], ephemeral_pk_bytes: &[u8], key_output_len: usize, info: Option<&[u8]>,
) -> PkeResult<Vec<u8>> {
let kdf = Hkdf::<Sha256>::new();
kdf.derive_key(
shared_secret_z,
Some(ephemeral_pk_bytes),
info,
key_output_len,
)
.map_err(PkeError::from)
}
pub(crate) fn derive_symmetric_key_hkdf_sha384(
shared_secret_z: &[u8],
ephemeral_pk_bytes: &[u8],
key_output_len: usize,
info: Option<&[u8]>,
) -> PkeResult<Vec<u8>> {
let kdf = Hkdf::<Sha384>::new();
kdf.derive_key(
shared_secret_z,
Some(ephemeral_pk_bytes),
info,
key_output_len,
)
.map_err(PkeError::from)
}
pub(crate) fn derive_symmetric_key_hkdf_sha512(
shared_secret_z: &[u8],
ephemeral_pk_bytes: &[u8],
key_output_len: usize,
info: Option<&[u8]>,
) -> PkeResult<Vec<u8>> {
let kdf = Hkdf::<Sha512>::new();
kdf.derive_key(
shared_secret_z,
Some(ephemeral_pk_bytes),
info,
key_output_len,
)
.map_err(PkeError::from)
}
#[derive(Clone, Debug)]
pub(crate) struct EciesCiphertextComponents {
pub ephemeral_public_key: Vec<u8>, pub aead_nonce: Vec<u8>, pub aead_ciphertext_tag: Vec<u8>, }
impl EciesCiphertextComponents {
pub fn serialize(&self) -> Vec<u8> {
let r_len = self.ephemeral_public_key.len();
let n_len = self.aead_nonce.len();
let ct_t_len = self.aead_ciphertext_tag.len();
assert!(
r_len <= u8::MAX as usize,
"Ephemeral PK too long for 1-byte length prefix"
);
assert!(
n_len <= u8::MAX as usize,
"AEAD Nonce too long for 1-byte length prefix"
);
let total_len = 1 + r_len + 1 + n_len + 4 + ct_t_len;
let mut serialized = Vec::with_capacity(total_len);
serialized.push(r_len as u8);
serialized.extend_from_slice(&self.ephemeral_public_key);
serialized.push(n_len as u8);
serialized.extend_from_slice(&self.aead_nonce);
serialized.extend_from_slice(&(ct_t_len as u32).to_be_bytes());
serialized.extend_from_slice(&self.aead_ciphertext_tag);
serialized
}
pub fn deserialize(bytes: &[u8]) -> PkeResult<Self> {
if bytes.is_empty() {
return Err(PkeError::InvalidCiphertextFormat(
"empty input for deserialization",
));
}
let mut current_pos = 0;
if bytes.len() < current_pos + 1 {
return Err(PkeError::InvalidCiphertextFormat("R length truncated"));
}
let r_len = bytes[current_pos] as usize;
current_pos += 1;
if bytes.len() < current_pos + r_len {
return Err(PkeError::InvalidCiphertextFormat("R data truncated"));
}
let ephemeral_public_key = bytes[current_pos..current_pos + r_len].to_vec();
current_pos += r_len;
if bytes.len() < current_pos + 1 {
return Err(PkeError::InvalidCiphertextFormat("Nonce length truncated"));
}
let n_len = bytes[current_pos] as usize;
current_pos += 1;
if bytes.len() < current_pos + n_len {
return Err(PkeError::InvalidCiphertextFormat("Nonce data truncated"));
}
let aead_nonce = bytes[current_pos..current_pos + n_len].to_vec();
current_pos += n_len;
if bytes.len() < current_pos + 4 {
return Err(PkeError::InvalidCiphertextFormat(
"AEAD payload length truncated",
));
}
let ct_t_len = u32::from_be_bytes(
bytes[current_pos..current_pos + 4]
.try_into()
.map_err(|_| {
PkeError::InvalidCiphertextFormat("Failed to read AEAD payload length")
})?,
) as usize;
current_pos += 4;
if bytes.len() < current_pos + ct_t_len {
return Err(PkeError::InvalidCiphertextFormat(
"AEAD payload data truncated",
));
}
let aead_ciphertext_tag = bytes[current_pos..current_pos + ct_t_len].to_vec();
current_pos += ct_t_len;
if current_pos != bytes.len() {
return Err(PkeError::InvalidCiphertextFormat(
"trailing data after deserialization",
));
}
Ok(Self {
ephemeral_public_key,
aead_nonce,
aead_ciphertext_tag,
})
}
}