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 public_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(public_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 public_key = secret_key.public_key().unwrap();
122
123 let info = b"some info";
124 let aad = b"some aad";
125 let ciphertext = hpke_seal(&public_key, Some(info), Some(aad), b"Hello, Panda!").unwrap();
126 let plaintext = hpke_open(&ciphertext, &secret_key, Some(info), Some(aad)).unwrap();
127
128 assert_eq!(plaintext, b"Hello, Panda!");
129 }
130
131 #[test]
132 fn decryption_failed() {
133 let rng = Rng::from_seed([1; 32]);
134
135 let valid_secret_key = SecretKey::from_bytes(rng.random_array().unwrap());
136 let public_key = valid_secret_key.public_key().unwrap();
137
138 let info = b"some info";
139 let aad = b"some aad";
140 let ciphertext = hpke_seal(&public_key, Some(info), Some(aad), b"Hello, Panda!").unwrap();
141
142 let invalid_secret_key = SecretKey::from_bytes(rng.random_array().unwrap());
144 let result = hpke_open(&ciphertext, &invalid_secret_key, Some(info), Some(aad));
145 assert!(matches!(result, Err(HpkeError::Decryption(_))));
146
147 let result = hpke_open(&ciphertext, &valid_secret_key, None, Some(aad));
149 assert!(matches!(result, Err(HpkeError::Decryption(_))));
150
151 let result = hpke_open(&ciphertext, &valid_secret_key, Some(info), None);
153 assert!(matches!(result, Err(HpkeError::Decryption(_))));
154 }
155}