1use core::fmt;
4
5use bech32::primitives::decode::CheckedHrpstring;
6use chacha20poly1305::{
7 aead::{self, generic_array::typenum::Unsigned, Aead, AeadCore, KeyInit},
8 ChaCha20Poly1305,
9};
10use hkdf::Hkdf;
11use sha2::Sha256;
12
13pub fn aead_encrypt(key: &[u8; 32], plaintext: &[u8]) -> Vec<u8> {
19 let c = ChaCha20Poly1305::new(key.into());
20 c.encrypt(&[0; 12].into(), plaintext)
21 .expect("we won't overflow the ChaCha20 block counter")
22}
23
24pub fn aead_decrypt(
34 key: &[u8; 32],
35 size: usize,
36 ciphertext: &[u8],
37) -> Result<Vec<u8>, aead::Error> {
38 if ciphertext.len() != size + <ChaCha20Poly1305 as AeadCore>::TagSize::to_usize() {
39 return Err(aead::Error);
40 }
41
42 let c = ChaCha20Poly1305::new(key.into());
43 c.decrypt(&[0; 12].into(), ciphertext)
44}
45
46pub fn hkdf(salt: &[u8], label: &[u8], ikm: &[u8]) -> [u8; 32] {
52 let mut okm = [0; 32];
53 Hkdf::<Sha256>::new(Some(salt), ikm)
54 .expand(label, &mut okm)
55 .expect("okm is the correct length");
56 okm
57}
58
59#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
66enum Bech32Long {}
67impl bech32::Checksum for Bech32Long {
68 type MidstateRepr = u32;
69 const CODE_LENGTH: usize = usize::MAX;
70 const CHECKSUM_LENGTH: usize = bech32::Bech32::CHECKSUM_LENGTH;
71 const GENERATOR_SH: [u32; 5] = bech32::Bech32::GENERATOR_SH;
72 const TARGET_RESIDUE: u32 = bech32::Bech32::TARGET_RESIDUE;
73}
74
75pub fn bech32_encode(hrp: bech32::Hrp, data: &[u8]) -> String {
82 bech32::encode_lower::<Bech32Long>(hrp, data).expect("we don't enforce the Bech32 length limit")
83}
84
85pub fn bech32_encode_to_fmt(f: &mut impl fmt::Write, hrp: bech32::Hrp, data: &[u8]) -> fmt::Result {
92 bech32::encode_lower_to_fmt::<Bech32Long, _>(f, hrp, data).map_err(|e| match e {
93 bech32::EncodeError::Fmt(error) => error,
94 bech32::EncodeError::TooLong(_) => unreachable!("we don't enforce the Bech32 length limit"),
95 _ => panic!("Unexpected error: {e}"),
96 })
97}
98
99pub fn bech32_decode<E, F, G, H, T>(
106 s: &str,
107 parse_err: F,
108 hrp_filter: G,
109 data_parse: H,
110) -> Result<T, E>
111where
112 F: FnOnce(bech32::primitives::decode::CheckedHrpstringError) -> E,
113 G: FnOnce(bech32::Hrp) -> Result<(), E>,
114 H: FnOnce(bech32::Hrp, bech32::primitives::decode::ByteIter) -> Result<T, E>,
115{
116 CheckedHrpstring::new::<Bech32Long>(s)
117 .map_err(parse_err)
118 .and_then(|parsed| {
119 hrp_filter(parsed.hrp()).and_then(|()| data_parse(parsed.hrp(), parsed.byte_iter()))
120 })
121}
122
123pub fn hpke_seal<Kem: hpke::Kem, R: rand::RngCore + rand::CryptoRng>(
139 pk_recip: &Kem::PublicKey,
140 info: &[u8],
141 plaintext: &[u8],
142 rng: &mut R,
143) -> (Kem::EncappedKey, Vec<u8>) {
144 hpke::single_shot_seal::<hpke::aead::ChaCha20Poly1305, hpke::kdf::HkdfSha256, Kem, R>(
145 &hpke::OpModeS::Base,
146 pk_recip,
147 info,
148 plaintext,
149 &[],
150 rng,
151 )
152 .expect("no errors should occur with these HPKE parameters")
153}
154
155pub fn hpke_open<Kem: hpke::Kem>(
164 encapped_key: &Kem::EncappedKey,
165 sk_recip: &Kem::PrivateKey,
166 info: &[u8],
167 ciphertext: &[u8],
168) -> Result<Vec<u8>, hpke::HpkeError> {
169 hpke::single_shot_open::<hpke::aead::ChaCha20Poly1305, hpke::kdf::HkdfSha256, Kem>(
170 &hpke::OpModeR::Base,
171 sk_recip,
172 encapped_key,
173 info,
174 ciphertext,
175 &[],
176 )
177}
178
179#[cfg(test)]
180mod tests {
181 use hpke::Kem;
182 use rand::rngs::OsRng;
183
184 use super::{aead_decrypt, aead_encrypt, bech32_decode, bech32_encode, hpke_open, hpke_seal};
185
186 #[test]
187 fn aead_round_trip() {
188 let key = [14; 32];
189 let plaintext = b"12345678";
190 let encrypted = aead_encrypt(&key, plaintext);
191 let decrypted = aead_decrypt(&key, plaintext.len(), &encrypted).unwrap();
192 assert_eq!(decrypted, plaintext);
193 }
194
195 #[test]
196 fn bech32_round_trip() {
197 let hrp = bech32::Hrp::parse_unchecked("12345678");
198 let data = [14; 32];
199 let encoded = bech32_encode(hrp, &data);
200 let decoded = bech32_decode(
201 &encoded,
202 |_| (),
203 |parsed_hrp| (parsed_hrp == hrp).then_some(()).ok_or(()),
204 |_, bytes| Ok(bytes.collect::<Vec<_>>()),
205 )
206 .unwrap();
207 assert_eq!(decoded, data);
208 }
209
210 #[test]
211 fn hpke_round_trip() {
212 type Kem = hpke::kem::DhP256HkdfSha256;
213 let mut rng = OsRng;
214
215 let (sk_recip, pk_recip) = Kem::gen_keypair(&mut rng);
216
217 let info = b"foobar";
218 let plaintext = b"12345678";
219
220 let (encapped_key, ciphertext) = hpke_seal::<Kem, _>(&pk_recip, info, plaintext, &mut rng);
221 let decrypted = hpke_open::<Kem>(&encapped_key, &sk_recip, info, &ciphertext).unwrap();
222
223 assert_eq!(decrypted, plaintext);
224 }
225}