p2panda_encryption/crypto/
hpke.rs1use hpke_rs::{Hpke, HpkePrivateKey, HpkePublicKey, Mode};
8use hpke_rs_crypto::types::{AeadAlgorithm, KdfAlgorithm, KemAlgorithm};
9use hpke_rs_rust_crypto::HpkeRustCrypto;
10use serde::{Deserialize, Serialize};
11use thiserror::Error;
12
13use crate::crypto::x25519::{PublicKey, SecretKey};
14
15#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
16pub struct HpkeCiphertext {
17 #[serde(with = "serde_bytes")]
19 pub kem_output: Vec<u8>,
20
21 #[serde(with = "serde_bytes")]
23 pub ciphertext: Vec<u8>,
24}
25
26pub fn hpke_seal(
36 verifying_key: &PublicKey,
37 info: Option<&[u8]>,
38 aad: Option<&[u8]>,
39 plaintext: &[u8],
40) -> Result<HpkeCiphertext, HpkeError> {
41 let mut hpke = Hpke::<HpkeRustCrypto>::new(
44 Mode::Base,
45 KemAlgorithm::DhKem25519,
46 KdfAlgorithm::HkdfSha256,
47 AeadAlgorithm::ChaCha20Poly1305,
48 );
49 let pk_r = HpkePublicKey::new(verifying_key.as_bytes().to_vec());
50 let (kem_output, ciphertext) = hpke
51 .seal(
52 &pk_r,
53 info.unwrap_or_default(),
54 aad.unwrap_or_default(),
55 plaintext,
56 None,
57 None,
58 None,
59 )
60 .map_err(HpkeError::Encryption)?;
61 Ok(HpkeCiphertext {
62 kem_output,
63 ciphertext,
64 })
65}
66
67pub fn hpke_open(
73 input: &HpkeCiphertext,
74 secret_key: &SecretKey,
75 info: Option<&[u8]>,
76 aad: Option<&[u8]>,
77) -> Result<Vec<u8>, HpkeError> {
78 let hpke = Hpke::<HpkeRustCrypto>::new(
79 Mode::Base,
80 KemAlgorithm::DhKem25519,
81 KdfAlgorithm::HkdfSha256,
82 AeadAlgorithm::ChaCha20Poly1305,
83 );
84 let sk_r = HpkePrivateKey::new(secret_key.as_bytes().to_vec());
85 let plaintext = hpke
86 .open(
87 &input.kem_output,
88 &sk_r,
89 info.unwrap_or_default(),
90 aad.unwrap_or_default(),
91 &input.ciphertext,
92 None,
93 None,
94 None,
95 )
96 .map_err(HpkeError::Decryption)?;
97 Ok(plaintext)
98}
99
100#[derive(Debug, Error)]
101pub enum HpkeError {
102 #[error("could not encrypt with hpke: {0:?}")]
103 Encryption(hpke_rs::HpkeError),
104
105 #[error("could not decrypt with hpke: {0:?}")]
106 Decryption(hpke_rs::HpkeError),
107}
108
109#[cfg(test)]
110mod tests {
111 use crate::crypto::Rng;
112 use crate::crypto::x25519::SecretKey;
113
114 use super::{HpkeError, hpke_open, hpke_seal};
115
116 #[test]
117 fn seal_and_open() {
118 let rng = Rng::from_seed([1; 32]);
119
120 let secret_key = SecretKey::from_bytes(rng.random_array().unwrap());
121 let verifying_key = secret_key.verifying_key().unwrap();
122
123 let info = b"some info";
124 let aad = b"some aad";
125 let ciphertext =
126 hpke_seal(&verifying_key, Some(info), Some(aad), b"Hello, Panda!").unwrap();
127 let plaintext = hpke_open(&ciphertext, &secret_key, Some(info), Some(aad)).unwrap();
128
129 assert_eq!(plaintext, b"Hello, Panda!");
130 }
131
132 #[test]
133 fn decryption_failed() {
134 let rng = Rng::from_seed([1; 32]);
135
136 let valid_secret_key = SecretKey::from_bytes(rng.random_array().unwrap());
137 let verifying_key = valid_secret_key.verifying_key().unwrap();
138
139 let info = b"some info";
140 let aad = b"some aad";
141 let ciphertext =
142 hpke_seal(&verifying_key, Some(info), Some(aad), b"Hello, Panda!").unwrap();
143
144 let invalid_secret_key = SecretKey::from_bytes(rng.random_array().unwrap());
146 let result = hpke_open(&ciphertext, &invalid_secret_key, Some(info), Some(aad));
147 std::assert_matches!(result, Err(HpkeError::Decryption(_)));
148
149 let result = hpke_open(&ciphertext, &valid_secret_key, None, Some(aad));
151 std::assert_matches!(result, Err(HpkeError::Decryption(_)));
152
153 let result = hpke_open(&ciphertext, &valid_secret_key, Some(info), None);
155 std::assert_matches!(result, Err(HpkeError::Decryption(_)));
156 }
157}