blaze_ssl/
crypto.rs

1use crypto::digest::Digest;
2use crypto::md5::Md5;
3use crypto::sha1::Sha1;
4
5/// Structure for storing cryptographic keys and
6/// state that may be required
7pub struct CryptographicState {
8    pub(crate) alg: HashAlgorithm,
9    pub(crate) master_key: [u8; 48],
10    pub(crate) client_write_secret: Vec<u8>,
11    pub(crate) server_write_secret: Vec<u8>,
12    pub(crate) client_write_key: [u8; 16],
13    pub(crate) server_write_key: [u8; 16],
14}
15
16#[derive(Debug, Clone, Copy)]
17pub enum HashAlgorithm {
18    Md5,
19    Sha1,
20}
21
22impl HashAlgorithm {
23    /// Returns the hash length for the algorithm
24    pub fn hash_length(&self) -> usize {
25        match self {
26            Self::Md5 => 16,
27            Self::Sha1 => 20,
28        }
29    }
30
31    /// Compares the provided mac bytes with a mac generated
32    /// from the same expected data.
33    pub fn compare_mac(
34        &self,
35        mac: &[u8],
36        write_secret: &[u8],
37        ty: u8,
38        message: &[u8],
39        seq: &u64,
40    ) -> bool {
41        match self {
42            Self::Md5 => compute_mac_md5(write_secret, ty, message, seq).eq(mac),
43            Self::Sha1 => compute_mac_sha(write_secret, ty, message, seq).eq(mac),
44        }
45    }
46
47    /// Computes and appends the mac to the provided payload
48    pub fn append_mac(&self, payload: &mut Vec<u8>, write_secret: &[u8], ty: u8, seq: &u64) {
49        match self {
50            Self::Md5 => {
51                let mac = compute_mac_md5(write_secret, ty, &payload, seq);
52                payload.extend_from_slice(&mac);
53            }
54            Self::Sha1 => {
55                let mac = compute_mac_sha(write_secret, ty, &payload, seq);
56                payload.extend_from_slice(&mac);
57            }
58        }
59    }
60}
61
62/// Creates the cryptographic state from the provided pre-master secret client random
63/// and server random
64pub fn create_crypto_state(
65    pm_key: &[u8],
66    alg: HashAlgorithm,
67    cr: &[u8; 32],
68    sr: &[u8; 32],
69) -> CryptographicState {
70    let mut master_key = [0u8; 48];
71    generate_key_block(&mut master_key, &pm_key, cr, sr);
72
73    // Generate key block 80 bytes long (20x2 for write secrets + 16x2 for write keys) only 72 bytes used
74    let mut key_block = [0u8; 80];
75    generate_key_block(&mut key_block, &master_key, sr, cr);
76
77    let hash_length = alg.hash_length();
78    let (client_write_secret, key_block) = key_block.split_at(hash_length);
79    let (server_write_secret, key_block) = key_block.split_at(hash_length);
80
81    let mut client_write_key = [0u8; 16];
82    client_write_key.copy_from_slice(&key_block[0..16]);
83    let mut server_write_key = [0u8; 16];
84    server_write_key.copy_from_slice(&key_block[16..32]);
85
86    CryptographicState {
87        alg,
88        master_key,
89        client_write_secret: client_write_secret.to_vec(),
90        server_write_secret: server_write_secret.to_vec(),
91        client_write_key,
92        server_write_key,
93    }
94}
95
96pub fn generate_key_block(out: &mut [u8], pm: &[u8], rand_1: &[u8; 32], rand_2: &[u8; 32]) {
97    // The digest use for the outer hash
98    let mut outer = Md5::new();
99    // The digest used for the inner hash
100    let mut inner = Sha1::new();
101
102    let mut randoms = [0u8; 64];
103    randoms[..32].copy_from_slice(rand_1);
104    randoms[32..].copy_from_slice(rand_2);
105
106    let mut inner_value = [0u8; 20];
107
108    let salts = ["A", "BB", "CCC", "DDDD", "EEEEE"].iter();
109
110    for (chunk, salt) in out.chunks_mut(16).zip(salts) {
111        inner.input(salt.as_bytes());
112        inner.input(pm);
113        inner.input(&randoms);
114        inner.result(&mut inner_value);
115        inner.reset();
116
117        outer.input(pm);
118        outer.input(&inner_value);
119        outer.result(chunk);
120        outer.reset();
121    }
122}
123
124pub fn compute_mac_sha(write_secret: &[u8], ty: u8, message: &[u8], seq: &u64) -> [u8; 20] {
125    let mut digest = Sha1::new();
126    let mut out = [0u8; 20];
127    let pad1 = [0x36; 40];
128    let pad2 = [0x5c; 40];
129    // A = hash(MAC_write_secret + pad_1 + seq_num + SSLCompressed.type + SSLCompressed.length + SSLCompressed.fragment)
130    digest.input(write_secret);
131    digest.input(&pad1);
132    digest.input(&seq.to_be_bytes());
133    digest.input(&[ty]);
134    let length = u16::to_be_bytes(message.len() as u16);
135    digest.input(&length);
136    digest.input(message);
137    digest.result(&mut out);
138    digest.reset();
139
140    // hash(MAC_write_secret + pad_2 + A);
141    digest.input(write_secret);
142    digest.input(&pad2);
143    digest.input(&out);
144    digest.result(&mut out);
145    out
146}
147
148pub fn compute_mac_md5(write_secret: &[u8], ty: u8, message: &[u8], seq: &u64) -> [u8; 16] {
149    let mut digest = Md5::new();
150    let mut out = [0u8; 16];
151    let pad1 = [0x36; 48];
152    let pad2 = [0x5c; 48];
153    // A = hash(MAC_write_secret + pad_1 + seq_num + SSLCompressed.type + SSLCompressed.length + SSLCompressed.fragment)
154    digest.input(write_secret);
155    digest.input(&pad1);
156    digest.input(&seq.to_be_bytes());
157    digest.input(&[ty]);
158    let length = u16::to_be_bytes(message.len() as u16);
159    digest.input(&length);
160    digest.input(message);
161    digest.result(&mut out);
162    digest.reset();
163
164    // hash(MAC_write_secret + pad_2 + A);
165    digest.input(write_secret);
166    digest.input(&pad2);
167    digest.input(&out);
168    digest.result(&mut out);
169    out
170}
171
172pub enum FinishedSender {
173    Client,
174    Server,
175}
176
177impl FinishedSender {
178    pub fn value(&self) -> [u8; 4] {
179        let value: u32 = match self {
180            FinishedSender::Client => 0x434C4E54,
181            FinishedSender::Server => 0x53525652,
182        };
183        value.to_be_bytes()
184    }
185}
186
187pub fn compute_finished_md5(
188    master_secret: &[u8],
189    sender: &FinishedSender,
190    transcript: &[u8],
191) -> [u8; 16] {
192    let mut digest = Md5::new();
193    let mut out = [0u8; 16];
194    let pad1 = [0x36; 48];
195    let pad2 = [0x5c; 48];
196    digest.input(transcript);
197    digest.input(&sender.value());
198    digest.input(master_secret);
199    digest.input(&pad1);
200    digest.result(&mut out);
201    digest.reset();
202
203    digest.input(master_secret);
204    digest.input(&pad2);
205    digest.input(&out);
206    digest.result(&mut out);
207    out
208}
209
210pub fn compute_finished_sha(
211    master_secret: &[u8],
212    sender: &FinishedSender,
213    transcript: &[u8],
214) -> [u8; 20] {
215    let mut digest = Sha1::new();
216    let mut out = [0u8; 20];
217
218    let pad1 = [0x36; 40];
219    let pad2 = [0x5c; 40];
220    digest.input(transcript);
221    digest.input(&sender.value());
222    digest.input(master_secret);
223    digest.input(&pad1);
224    digest.result(&mut out);
225    digest.reset();
226
227    digest.input(master_secret);
228    digest.input(&pad2);
229    digest.input(&out);
230    digest.result(&mut out);
231    out
232}