1use crate::MAX_MESSAGE_BYTES;
2use anyhow::{ensure, Result};
3use flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression};
4use openssl::{
5 bn::{BigNum, BigNumContext},
6 derive::Deriver,
7 ec::{EcGroup, EcKey, EcPoint, PointConversionForm},
8 hash::{hash, MessageDigest},
9 nid::Nid,
10 pkey::{PKey, Private},
11 rand::rand_bytes,
12 sign::{Signer, Verifier},
13 symm::{Cipher, Crypter, Mode},
14};
15use std::io::{Read, Write};
16use zeroize::Zeroize;
17
18pub fn random<const N: usize>() -> Result<[u8; N]> {
19 let mut b = [0; N];
20 rand_bytes(&mut b)?;
21 Ok(b)
22}
23pub fn sha256(b: &[u8]) -> Vec<u8> {
24 hash(MessageDigest::sha256(), b)
25 .expect("OpenSSL SHA256")
26 .to_vec()
27}
28pub fn md5(b: &[u8]) -> Vec<u8> {
29 hash(MessageDigest::md5(), b).expect("OpenSSL MD5").to_vec()
30}
31pub fn hmac(key: &[u8], input: &[u8]) -> Result<Vec<u8>> {
32 let key = PKey::hmac(key)?;
33 let mut s = Signer::new(MessageDigest::sha256(), &key)?;
34 s.update(input)?;
35 Ok(s.sign_to_vec()?)
36}
37pub fn expand(secret: &[u8], info: &[u8], size: usize) -> Result<Vec<u8>> {
38 ensure!(size > 0 && size <= 255 * 32, "Invalid HKDF size");
39 let mut last = Vec::new();
40 let mut out = Vec::new();
41 for i in 1..=255u8 {
42 last = hmac(secret, &[last.as_slice(), info, &[i]].concat())?;
43 out.extend_from_slice(&last);
44 if out.len() >= size {
45 break;
46 }
47 }
48 out.truncate(size);
49 last.zeroize();
50 Ok(out)
51}
52pub fn deflate(plain: &[u8]) -> Result<Vec<u8>> {
53 ensure!(plain.len() <= MAX_MESSAGE_BYTES, "Plaintext too large");
54 let mut e = ZlibEncoder::new(Vec::new(), Compression::default());
55 e.write_all(plain)?;
56 Ok(e.finish()?)
57}
58pub fn inflate(zipped: &[u8]) -> Result<Vec<u8>> {
59 let mut decoder = ZlibDecoder::new(zipped);
60 let mut out = Vec::new();
61 decoder
62 .by_ref()
63 .take((MAX_MESSAGE_BYTES + 1) as u64)
64 .read_to_end(&mut out)?;
65 ensure!(
66 out.len() <= MAX_MESSAGE_BYTES,
67 "Decompression limit exceeded"
68 );
69 ensure!(
70 decoder.total_in() as usize == zipped.len(),
71 "Trailing compressed bytes"
72 );
73 Ok(out)
74}
75pub fn gcm_seal(key: &[u8], iv: &[u8], aad: &[u8], plain: &[u8]) -> Result<(Vec<u8>, [u8; 16])> {
76 let cipher = match key.len() {
77 16 => Cipher::aes_128_gcm(),
78 24 => Cipher::aes_192_gcm(),
79 _ => anyhow::bail!("Invalid GCM key"),
80 };
81 ensure!(iv.len() == 12, "Invalid GCM nonce");
82 let mut c = Crypter::new(cipher, Mode::Encrypt, key, Some(iv))?;
83 c.aad_update(aad)?;
84 let mut out = vec![0; plain.len() + cipher.block_size()];
85 let n = c.update(plain, &mut out)?;
86 let m = c.finalize(&mut out[n..])?;
87 out.truncate(n + m);
88 let mut tag = [0; 16];
89 c.get_tag(&mut tag)?;
90 Ok((out, tag))
91}
92pub fn gcm_open(
93 key: &[u8],
94 iv: &[u8],
95 aad: &[u8],
96 encrypted: &[u8],
97 tag: &[u8],
98) -> Result<Vec<u8>> {
99 let cipher = match key.len() {
100 16 => Cipher::aes_128_gcm(),
101 24 => Cipher::aes_192_gcm(),
102 _ => anyhow::bail!("Invalid GCM key"),
103 };
104 ensure!(iv.len() == 12 && tag.len() == 16, "Invalid GCM envelope");
105 let mut c = Crypter::new(cipher, Mode::Decrypt, key, Some(iv))?;
106 c.aad_update(aad)?;
107 c.set_tag(tag)?;
108 let mut out = vec![0; encrypted.len() + cipher.block_size()];
109 let n = c.update(encrypted, &mut out)?;
110 let m = match c.finalize(&mut out[n..]) {
111 Ok(n) => n,
112 Err(e) => {
113 out.zeroize();
114 return Err(e.into());
115 }
116 };
117 out.truncate(n + m);
118 Ok(out)
119}
120pub fn envelope_seal(plain: &[u8], key: &[u8], aad: &[u8], iv: [u8; 12]) -> Result<Vec<u8>> {
121 ensure!(key.len() == 24, "Expected AES192 key");
122 let (body, tag) = gcm_seal(key, &iv, aad, &deflate(plain)?)?;
123 Ok([body, iv.to_vec(), tag.to_vec()].concat())
124}
125pub fn envelope_open(input: &[u8], key: &[u8], aad: &[u8]) -> Result<Vec<u8>> {
126 ensure!(
127 input.len() >= 29 && input.len() <= MAX_MESSAGE_BYTES + 65536,
128 "Invalid encrypted envelope"
129 );
130 let n = input.len();
131 let zipped = gcm_open(
132 key,
133 &input[n - 28..n - 16],
134 aad,
135 &input[..n - 28],
136 &input[n - 16..],
137 )?;
138 inflate(&zipped)
139}
140pub fn cbc(plain: &[u8], key: &[u8], mode: Mode) -> Result<Vec<u8>> {
141 ensure!(!key.is_empty() && key.len() <= 65536, "Invalid origin key");
142 ensure!(
143 plain.len() <= MAX_MESSAGE_BYTES + 65536,
144 "CBC body too large"
145 );
146 if matches!(mode, Mode::Decrypt) {
147 ensure!(
148 !plain.is_empty() && plain.len().is_multiple_of(16),
149 "Invalid CBC ciphertext"
150 );
151 }
152 let mut normalized = [0; 16];
153 let n = key.len().min(16);
154 normalized[..n].copy_from_slice(&key[..n]);
155 let result = if matches!(mode, Mode::Decrypt) {
156 openssl::symm::decrypt(Cipher::aes_128_cbc(), &normalized, Some(&normalized), plain)
157 } else {
158 openssl::symm::encrypt(Cipher::aes_128_cbc(), &normalized, Some(&normalized), plain)
159 };
160 normalized.zeroize();
161 Ok(result?)
162}
163
164pub struct Ecdh {
165 key: PKey<Private>,
166 nid: Nid,
167}
168impl Ecdh {
169 pub fn generate(nid: Nid) -> Result<Self> {
170 let group = EcGroup::from_curve_name(nid)?;
171 Ok(Self {
172 key: PKey::from_ec_key(EcKey::generate(&group)?)?,
173 nid,
174 })
175 }
176 pub fn from_private(nid: Nid, private: &[u8]) -> Result<Self> {
177 let group = EcGroup::from_curve_name(nid)?;
178 let scalar = BigNum::from_slice(private)?;
179 let mut ctx = BigNumContext::new()?;
180 let mut public = EcPoint::new(&group)?;
181 public.mul_generator2(&group, &scalar, &mut ctx)?;
182 let key = EcKey::from_private_components(&group, &scalar, &public)?;
183 key.check_key()?;
184 Ok(Self {
185 key: PKey::from_ec_key(key)?,
186 nid,
187 })
188 }
189 pub fn public(&self) -> Result<Vec<u8>> {
190 let key = self.key.ec_key()?;
191 let mut ctx = BigNumContext::new()?;
192 Ok(key
193 .public_key()
194 .to_bytes(key.group(), PointConversionForm::UNCOMPRESSED, &mut ctx)?)
195 }
196 pub fn derive(&self, point: &[u8]) -> Result<Vec<u8>> {
197 let group = EcGroup::from_curve_name(self.nid)?;
198 let mut ctx = BigNumContext::new()?;
199 let p = EcPoint::from_bytes(&group, point, &mut ctx)?;
200 let peer = EcKey::from_public_key(&group, &p)?;
201 peer.check_key()?;
202 let peer = PKey::from_ec_key(peer)?;
203 let mut d = Deriver::new(&self.key)?;
204 d.set_peer(&peer)?;
205 Ok(d.derive_to_vec()?)
206 }
207}
208pub fn verify_signature(pem: &str, data: &[u8], signature: &[u8]) -> Result<()> {
209 let key = PKey::public_key_from_pem(pem.as_bytes())?;
210 let ec = key.ec_key()?;
211 ensure!(
212 ec.group().curve_name() == Some(Nid::X9_62_PRIME256V1),
213 "Unsupported signature pin curve"
214 );
215 let mut v = Verifier::new(MessageDigest::sha256(), &key)?;
216 v.update(data)?;
217 ensure!(
218 v.verify(signature)?,
219 "Pinned server signature verification failed"
220 );
221 Ok(())
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227 #[test]
228 fn rfc5869() {
229 let prk = hex::decode("077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5")
230 .unwrap();
231 assert_eq!(
232 hex::encode(expand(&prk, &hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap(), 42).unwrap()),
233 "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865"
234 );
235 }
236 #[test]
237 fn p224_p256_shared_secrets() {
238 for nid in [Nid::SECP224R1, Nid::X9_62_PRIME256V1] {
239 let a = Ecdh::from_private(nid, &[1]).unwrap();
240 let b = Ecdh::from_private(nid, &[2]).unwrap();
241 assert_eq!(
242 a.derive(&b.public().unwrap()).unwrap(),
243 b.derive(&a.public().unwrap()).unwrap()
244 );
245 }
246 }
247 #[test]
248 fn session_known_answer() {
249 let key: Vec<u8> = (0..24).collect();
250 let nonce = std::array::from_fn(|i| i as u8);
251 let fixture=hex::decode("9e65093555920046fdfa90d437e2f0b922d46623768c7870b390381087bb0d11fa5af671c9b301db000102030405060708090a0b0d4ecbc764b639df9549f03800165ea6").unwrap();
252 assert_eq!(
253 envelope_seal(b"synthetic native session request", &key, b"", nonce).unwrap(),
254 fixture
255 );
256 assert_eq!(
257 envelope_open(&fixture, &key, b"").unwrap(),
258 b"synthetic native session request"
259 );
260 for i in [0, fixture.len() - 28, fixture.len() - 1] {
261 let mut bad = fixture.clone();
262 bad[i] ^= 1;
263 assert!(envelope_open(&bad, &key, b"").is_err());
264 }
265 }
266}