use hpke_rs::{Hpke, HpkePrivateKey, HpkePublicKey, Mode};
use hpke_rs_crypto::types::{AeadAlgorithm, KdfAlgorithm, KemAlgorithm};
use hpke_rs_rust_crypto::HpkeRustCrypto;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::crypto::x25519::{PublicKey, SecretKey};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct HpkeCiphertext {
#[serde(with = "serde_bytes")]
pub kem_output: Vec<u8>,
#[serde(with = "serde_bytes")]
pub ciphertext: Vec<u8>,
}
pub fn hpke_seal(
verifying_key: &PublicKey,
info: Option<&[u8]>,
aad: Option<&[u8]>,
plaintext: &[u8],
) -> Result<HpkeCiphertext, HpkeError> {
let mut hpke = Hpke::<HpkeRustCrypto>::new(
Mode::Base,
KemAlgorithm::DhKem25519,
KdfAlgorithm::HkdfSha256,
AeadAlgorithm::ChaCha20Poly1305,
);
let pk_r = HpkePublicKey::new(verifying_key.as_bytes().to_vec());
let (kem_output, ciphertext) = hpke
.seal(
&pk_r,
info.unwrap_or_default(),
aad.unwrap_or_default(),
plaintext,
None,
None,
None,
)
.map_err(HpkeError::Encryption)?;
Ok(HpkeCiphertext {
kem_output,
ciphertext,
})
}
pub fn hpke_open(
input: &HpkeCiphertext,
secret_key: &SecretKey,
info: Option<&[u8]>,
aad: Option<&[u8]>,
) -> Result<Vec<u8>, HpkeError> {
let hpke = Hpke::<HpkeRustCrypto>::new(
Mode::Base,
KemAlgorithm::DhKem25519,
KdfAlgorithm::HkdfSha256,
AeadAlgorithm::ChaCha20Poly1305,
);
let sk_r = HpkePrivateKey::new(secret_key.as_bytes().to_vec());
let plaintext = hpke
.open(
&input.kem_output,
&sk_r,
info.unwrap_or_default(),
aad.unwrap_or_default(),
&input.ciphertext,
None,
None,
None,
)
.map_err(HpkeError::Decryption)?;
Ok(plaintext)
}
#[derive(Debug, Error)]
pub enum HpkeError {
#[error("could not encrypt with hpke: {0:?}")]
Encryption(hpke_rs::HpkeError),
#[error("could not decrypt with hpke: {0:?}")]
Decryption(hpke_rs::HpkeError),
}
#[cfg(test)]
mod tests {
use crate::crypto::Rng;
use crate::crypto::x25519::SecretKey;
use super::{HpkeError, hpke_open, hpke_seal};
#[test]
fn seal_and_open() {
let rng = Rng::from_seed([1; 32]);
let secret_key = SecretKey::from_bytes(rng.random_array().unwrap());
let verifying_key = secret_key.verifying_key().unwrap();
let info = b"some info";
let aad = b"some aad";
let ciphertext =
hpke_seal(&verifying_key, Some(info), Some(aad), b"Hello, Panda!").unwrap();
let plaintext = hpke_open(&ciphertext, &secret_key, Some(info), Some(aad)).unwrap();
assert_eq!(plaintext, b"Hello, Panda!");
}
#[test]
fn decryption_failed() {
let rng = Rng::from_seed([1; 32]);
let valid_secret_key = SecretKey::from_bytes(rng.random_array().unwrap());
let verifying_key = valid_secret_key.verifying_key().unwrap();
let info = b"some info";
let aad = b"some aad";
let ciphertext =
hpke_seal(&verifying_key, Some(info), Some(aad), b"Hello, Panda!").unwrap();
let invalid_secret_key = SecretKey::from_bytes(rng.random_array().unwrap());
let result = hpke_open(&ciphertext, &invalid_secret_key, Some(info), Some(aad));
std::assert_matches!(result, Err(HpkeError::Decryption(_)));
let result = hpke_open(&ciphertext, &valid_secret_key, None, Some(aad));
std::assert_matches!(result, Err(HpkeError::Decryption(_)));
let result = hpke_open(&ciphertext, &valid_secret_key, Some(info), None);
std::assert_matches!(result, Err(HpkeError::Decryption(_)));
}
}