Skip to main content

wechat_protocol/
crypto.rs

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}