message_verifier/
lib.rs

1//! Message Verifier library compatible with Rails'
2//! [MessageVerifier](http://api.rubyonrails.org/classes/ActiveSupport/MessageVerifier.html) and
3//! [MessageEncryptor](http://api.rubyonrails.org/classes/ActiveSupport/MessageEncryptor.html).
4//!
5//! #### A Small Example
6//!
7//! Please refer to the
8//! [README](https://github.com/mikeycgto/message_verifier/blob/master/README.md)
9//! and [repo](https://github.com/mikeycgto/message_verifier) for more examples.
10//!
11//! ```
12//!  extern crate message_verifier;
13//!
14//!  use message_verifier::{Verifier, Encryptor, AesHmacEncryptor, DerivedKeyParams};
15//!
16//!  fn main() {
17//!      let key_base = "helloworld";
18//!      let salt = "test salt";
19//!      let sign_salt = "test signed salt";
20//!
21//!      let verifier = Verifier::new(key_base);
22//!
23//!      //let dkp = DerivedKeyParams::default();
24//!      //let encryptor = AesHmacEncryptor::new(key_base, salt, sign_salt, dkp).unwrap();
25//!
26//!      let message = "{\"key\":\"value\"}";
27//!
28//!      println!("{}", verifier.generate(message).expect("Verifier failed"));
29//!      //println!("{}", encryptor.encrypt_and_sign(message).expect("Encryptor failed"));
30//!  }
31//! ```
32
33#[macro_use]
34extern crate error_chain;
35
36use aes::cipher::generic_array::GenericArray;
37use base64::{engine::general_purpose::STANDARD as base64, Engine as _};
38use digest::CtOutput;
39use pbkdf2::{
40    hmac::{Hmac, Mac},
41    pbkdf2_hmac,
42};
43use rand::RngCore;
44use sha1::Sha1;
45use subtle::ConstantTimeEq;
46
47error_chain! {
48    foreign_links {
49        DecodeBase64(base64::DecodeError);
50        InvalidLength(digest::InvalidLength);
51    }
52
53    errors {
54        InvalidSignature {
55            description("Invalid message signature")
56        }
57
58        InvalidMessage {
59            description("Invalid message encoding or format")
60        }
61
62        KeyDerivationFailure {
63            description("Key Derivation Function failed to generate one or more keys")
64        }
65    }
66}
67
68/// Verifier struct; similiar to ActiveSupport::MessageVerifier.
69pub struct Verifier {
70    secret_key: Vec<u8>,
71}
72
73/// Encryption cipher key options. Only AES with 256, 192 or 128 bits is supported.
74pub enum KeySize {
75    Aes128,
76    Aes192,
77    Aes256,
78}
79
80/// Encryptor trait; similiar to ActiveSupport::MessageEncryptor. Implemented by AesHmacEncryptor
81/// and AesGcmEncryptor.
82pub trait Encryptor {
83    fn decrypt_and_verify(&self, message: &str) -> Result<Vec<u8>>;
84    fn encrypt_and_sign(&self, message: &str) -> Result<String>;
85}
86
87/// AesHmacEncryptor struct; similiar to ActiveSupport::MessageEncryptor
88pub struct AesHmacEncryptor {
89    pub key_size: KeySize,
90    secret_key: Vec<u8>,
91    verifier: Verifier,
92}
93
94/// AesGcmEncryptor struct; similiar to ActiveSupport::MessageEncryptor
95pub struct AesGcmEncryptor {
96    pub key_size: KeySize,
97    secret_key: Vec<u8>,
98}
99
100/// Key derivation parameters for PBKDF2 function.
101pub struct DerivedKeyParams {
102    size: u32,
103    iterations: u32,
104}
105
106impl Default for DerivedKeyParams {
107    /// The default mimics Rails' secure cookie setup which is
108    /// 64 bytes (512 bits) for the key size and 1000 iterations.
109    ///
110    /// ActiveSupport::KeyGenerator will default to 2^16 (65536) iterations.
111    fn default() -> DerivedKeyParams {
112        DerivedKeyParams {
113            size: 64,
114            iterations: 1000,
115        }
116    }
117}
118
119/// Create one or more PBKDF2 derived keys using a secret, some key parameters
120/// and one or more salts.
121pub fn create_derived_keys(
122    salts: &[&str],
123    secret: &str,
124    key_params: DerivedKeyParams,
125) -> Vec<Vec<u8>> {
126    salts
127        .iter()
128        .map(|salt| {
129            let mut result: Vec<u8> = vec![0; key_params.size as usize];
130            pbkdf2_hmac::<Sha1>(
131                secret.as_bytes(),
132                salt.as_bytes(),
133                key_params.iterations,
134                &mut result,
135            );
136            result
137        })
138        .collect()
139}
140
141fn random_iv(sz: usize) -> Vec<u8> {
142    let mut rng = rand::thread_rng();
143    let mut buffer: Vec<u8> = vec![0; sz];
144    rng.fill_bytes(&mut buffer);
145    buffer
146}
147
148fn split_by_n_dashes(n: usize, message: &str) -> Result<Vec<&str>> {
149    let split: Vec<&str> = message.splitn(n, "--").collect();
150
151    if split.len() == n {
152        Ok(split)
153    } else {
154        bail!(ErrorKind::InvalidMessage)
155    }
156}
157
158fn split_by_n_dashes_from_u8_slice(n: usize, slice: &[u8]) -> Result<Vec<&str>> {
159    match std::str::from_utf8(slice) {
160        Ok(string) => split_by_n_dashes(n, string),
161        Err(_) => bail!(ErrorKind::InvalidMessage),
162    }
163}
164
165impl Verifier {
166    /// Create a new Verifier object.
167    pub fn new(secret: &str) -> Verifier {
168        Verifier {
169            secret_key: secret.bytes().collect(),
170        }
171    }
172
173    /// Verify a signed message generated by a compatible verifier.
174    pub fn verify(&self, message: &str) -> Result<Vec<u8>> {
175        let msg_split = split_by_n_dashes(2, message)?;
176
177        let encoded_data = msg_split[0];
178        let signature = msg_split[1];
179
180        match self.is_valid_message(encoded_data, signature)? {
181            true => Ok(base64.decode(encoded_data)?),
182            false => bail!(ErrorKind::InvalidSignature),
183        }
184    }
185
186    /// Check if the given signature is valid for some encoded data.
187    pub fn is_valid_message(&self, encoded_data: &str, signature: &str) -> Result<bool> {
188        match hex::decode(signature) {
189            Ok(sig_bytes) => {
190                let mut mac = Hmac::<Sha1>::new_from_slice(&self.secret_key)?;
191                mac.update(encoded_data.as_bytes());
192                let sig = CtOutput::new(GenericArray::clone_from_slice(sig_bytes.as_slice()));
193                Ok(mac.finalize().ct_eq(&sig).into())
194            }
195
196            Err(_) => Ok(false),
197        }
198    }
199
200    /// Generate a signed message from the input message. This message can
201    /// be consumed and verified by a compatible verifier.
202    pub fn generate(&self, message: &str) -> Result<String> {
203        let mut mac = Hmac::<Sha1>::new_from_slice(&self.secret_key)?;
204        let encoded_data = base64.encode(message.as_bytes());
205
206        mac.update(encoded_data.as_bytes());
207
208        let signature = mac.finalize();
209        let result = format!("{}--{}", encoded_data, hex::encode(signature.into_bytes()));
210
211        Ok(result.clone())
212    }
213}
214
215impl AesHmacEncryptor {
216    /// Create a new AesHmacEncryptor object
217    pub fn new(
218        secret: &str,
219        salt: &str,
220        sign_salt: &str,
221        key_params: DerivedKeyParams,
222    ) -> Result<AesHmacEncryptor> {
223        let salts = vec![salt, sign_salt];
224        let keys = create_derived_keys(&salts, secret, key_params);
225
226        match (keys.first(), keys.last()) {
227            (Some(cipher_key), Some(sig_key)) => Ok(AesHmacEncryptor {
228                key_size: KeySize::Aes256,
229                secret_key: cipher_key.to_vec(),
230                verifier: Verifier {
231                    secret_key: sig_key.to_vec(),
232                },
233            }),
234
235            _ => bail!(ErrorKind::KeyDerivationFailure),
236        }
237    }
238}
239
240impl Encryptor for AesHmacEncryptor {
241    /// Decrypt and verify a message generated by a compatible Encryptor. The message must first be
242    /// verified before decryption is attempted.
243    fn decrypt_and_verify(&self, message: &str) -> Result<Vec<u8>> {
244        let decoded = self.verifier.verify(message)?;
245        let msg_split = split_by_n_dashes_from_u8_slice(2, &decoded)?;
246
247        let cipher_text = base64.decode(msg_split[0])?;
248        let iv = base64.decode(msg_split[1])?;
249
250        use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit};
251
252        match self.key_size {
253            KeySize::Aes128 => {
254                cbc::Decryptor::<aes::Aes128>::new_from_slices(&self.secret_key[0..16], &iv)?
255                    .decrypt_padded_vec_mut::<Pkcs7>(&cipher_text)
256            }
257            KeySize::Aes192 => {
258                cbc::Decryptor::<aes::Aes192>::new_from_slices(&self.secret_key[0..24], &iv)?
259                    .decrypt_padded_vec_mut::<Pkcs7>(&cipher_text)
260            }
261            KeySize::Aes256 => {
262                cbc::Decryptor::<aes::Aes256>::new_from_slices(&self.secret_key[0..32], &iv)?
263                    .decrypt_padded_vec_mut::<Pkcs7>(&cipher_text)
264            }
265        }
266        .or(Err(ErrorKind::InvalidMessage.into()))
267    }
268
269    /// Encrypt and sign a message from the input message. This message can be consumed by a
270    /// compatible Encryptor
271    fn encrypt_and_sign(&self, message: &str) -> Result<String> {
272        let iv = random_iv(16);
273        let message = message.as_bytes();
274
275        use aes::cipher::{block_padding::Pkcs7, BlockEncryptMut, KeyIvInit};
276
277        let cipher_result = match self.key_size {
278            KeySize::Aes128 => {
279                cbc::Encryptor::<aes::Aes128>::new_from_slices(&self.secret_key[0..16], &iv)?
280                    .encrypt_padded_vec_mut::<Pkcs7>(message)
281            }
282            KeySize::Aes192 => {
283                cbc::Encryptor::<aes::Aes192>::new_from_slices(&self.secret_key[0..24], &iv)?
284                    .encrypt_padded_vec_mut::<Pkcs7>(message)
285            }
286            KeySize::Aes256 => {
287                cbc::Encryptor::<aes::Aes256>::new_from_slices(&self.secret_key[0..32], &iv)?
288                    .encrypt_padded_vec_mut::<Pkcs7>(message)
289            }
290        };
291
292        let encoded_ctxt = base64.encode(cipher_result);
293        let encoded_iv = base64.encode(iv);
294
295        self.verifier
296            .generate(&format!("{}--{}", encoded_ctxt, encoded_iv))
297    }
298}
299
300impl AesGcmEncryptor {
301    /// Create a new AesGcmEncryptor object
302    pub fn new(secret: &str, salt: &str, key_params: DerivedKeyParams) -> Result<AesGcmEncryptor> {
303        let salts = vec![salt];
304        let keys = create_derived_keys(&salts, secret, key_params);
305
306        match keys.first() {
307            Some(cipher_key) => Ok(AesGcmEncryptor {
308                key_size: KeySize::Aes256,
309                secret_key: cipher_key.to_vec(),
310            }),
311
312            _ => bail!(ErrorKind::KeyDerivationFailure),
313        }
314    }
315}
316
317impl Encryptor for AesGcmEncryptor {
318    /// Decrypt a message, using AEAD, generated by a compatible Encryptor. The message must first
319    /// be verified before decryption is attempted.
320    fn decrypt_and_verify(&self, message: &str) -> Result<Vec<u8>> {
321        let msg_split = split_by_n_dashes(3, message)?;
322
323        let mut cipher_text = base64.decode(msg_split[0])?;
324        let iv = GenericArray::clone_from_slice(&base64.decode(msg_split[1])?);
325        let auth_tag = GenericArray::clone_from_slice(&base64.decode(msg_split[2])?);
326
327        use aes_gcm::{aead::KeyInit, AeadInPlace, AesGcm};
328        use digest::consts::U12;
329
330        let result = match self.key_size {
331            KeySize::Aes128 => {
332                let cipher = AesGcm::<aes::Aes128, U12>::new_from_slice(&self.secret_key[0..16])?;
333                cipher.decrypt_in_place_detached(&iv, &[], &mut cipher_text, &auth_tag)
334            }
335            KeySize::Aes192 => {
336                let cipher = AesGcm::<aes::Aes192, U12>::new_from_slice(&self.secret_key[0..24])?;
337                cipher.decrypt_in_place_detached(&iv, &[], &mut cipher_text, &auth_tag)
338            }
339            KeySize::Aes256 => {
340                let cipher = AesGcm::<aes::Aes256, U12>::new_from_slice(&self.secret_key[0..32])?;
341                cipher.decrypt_in_place_detached(&iv, &[], &mut cipher_text, &auth_tag)
342            }
343        };
344        if result.is_err() {
345            bail!(ErrorKind::InvalidMessage);
346        }
347        Ok(cipher_text)
348    }
349
350    /// Encrypt a message, using AEAD, from the input message. This message can be consumed by a
351    /// compatible Encryptor
352    fn encrypt_and_sign(&self, message: &str) -> Result<String> {
353        let random_iv = GenericArray::clone_from_slice(&random_iv(12));
354
355        use aes_gcm::{aead::KeyInit, AeadInPlace, AesGcm};
356        use digest::consts::U12;
357
358        let mut output: Vec<u8> = message.as_bytes().to_vec();
359        let auth_tag = match self.key_size {
360            KeySize::Aes128 => {
361                let cipher = AesGcm::<aes::Aes128, U12>::new_from_slice(&self.secret_key[0..16])?;
362                cipher.encrypt_in_place_detached(&random_iv, &[], &mut output)
363            }
364            KeySize::Aes192 => {
365                let cipher = AesGcm::<aes::Aes192, U12>::new_from_slice(&self.secret_key[0..24])?;
366                cipher.encrypt_in_place_detached(&random_iv, &[], &mut output)
367            }
368            KeySize::Aes256 => {
369                let cipher = AesGcm::<aes::Aes256, U12>::new_from_slice(&self.secret_key[0..32])?;
370                cipher.encrypt_in_place_detached(&random_iv, &[], &mut output)
371            }
372        };
373        let Ok(auth_tag) = auth_tag else {
374            bail!(ErrorKind::InvalidMessage);
375        };
376
377        let encoded_ctxt = base64.encode(output);
378        let encoded_iv = base64.encode(random_iv);
379        let encoded_tag = base64.encode(auth_tag);
380        Ok(format!("{}--{}--{}", encoded_ctxt, encoded_iv, encoded_tag))
381    }
382}
383
384#[cfg(test)]
385mod tests {
386    // assert_error_kind!(err, kind)
387    macro_rules! assert_error_kind {
388        ($err:expr, $kind:pat) => {
389            match *$err.kind() {
390                $kind => assert!(true, "{:?} is of kind {:?}", $err, stringify!($kind)),
391                _ => assert!(false, "{:?} is NOT of kind {:?}", $err, stringify!($kind)),
392            }
393        };
394    }
395
396    use crate::*;
397
398    #[test]
399    fn is_valid_message_returns_true_for_valid_signatures() {
400        let data = "eyJrZXkiOiJ2YWx1ZSJ9";
401        let sig = "fa115453dbb4a28277b1ba07ef4c7437621f5d72";
402
403        let verifier = Verifier::new("helloworld");
404
405        assert!(verifier.is_valid_message(data, sig).unwrap());
406    }
407
408    #[test]
409    fn is_valid_message_returns_false_for_invalid_signatures() {
410        let data = "eyJrZXkiOiJ2YWx1ZSJ9";
411        let sig = "05330518df0e21fb9beec7a71a5f5f951c3f5254";
412
413        let verifier = Verifier::new("helloworld");
414
415        assert!(!verifier.is_valid_message(data, sig).unwrap());
416    }
417
418    #[test]
419    fn is_valid_message_returns_false_for_invalid_messages() {
420        let data = "baddata";
421        let sig = "badsig";
422
423        let verifier = Verifier::new("helloworld");
424
425        assert!(!verifier.is_valid_message(data, sig).unwrap());
426    }
427
428    #[test]
429    fn verify_returns_decoded_message_for_valid_signatures() {
430        let msg = "eyJrZXkiOiJ2YWx1ZSJ9--fa115453dbb4a28277b1ba07ef4c7437621f5d72";
431
432        let verifier = Verifier::new("helloworld");
433
434        assert_eq!(
435            verifier.verify(msg).unwrap(),
436            "{\"key\":\"value\"}".as_bytes()
437        );
438    }
439
440    #[test]
441    fn verify_returns_invalid_signature_error_for_wrong_key() {
442        let msg = "eyJrZXkiOiJ2YWx1ZSJ9--05330518df0e21fb9beec7a71a5f5f951c3f5254";
443
444        let verifier = Verifier::new("helloworld");
445
446        assert_error_kind!(
447            verifier.verify(msg).unwrap_err(),
448            ErrorKind::InvalidSignature
449        );
450    }
451
452    #[test]
453    fn verify_returns_invalid_message_error_for_empty_message() {
454        let msg = "";
455
456        let verifier = Verifier::new("helloworld");
457
458        assert_error_kind!(verifier.verify(msg).unwrap_err(), ErrorKind::InvalidMessage);
459    }
460
461    #[test]
462    fn generate_returns_signed_and_encoded_string() {
463        let verifier = Verifier::new("helloworld");
464        let expected = "eyJrZXkiOiJ2YWx1ZSJ9--fa115453dbb4a28277b1ba07ef4c7437621f5d72";
465
466        assert_eq!(
467            verifier.generate("{\"key\":\"value\"}").unwrap(),
468            expected.to_string()
469        );
470    }
471
472    #[test]
473    fn aes_hamc_decrypt_and_verify_returns_decoded_message_for_valid_messages() {
474        let msg = "c20wSnp6Z1o1U2MyWDVjU3BPeWNNQT09LS1JOWNyR25LdDRpZUUvcmoxVTdoSTNRPT0=--a79c9522355e55bf8e4302c66d8bf5638f1a50ec";
475
476        let dkp = DerivedKeyParams::default();
477        let encryptor =
478            AesHmacEncryptor::new("helloworld", "test salt", "test signed salt", dkp).unwrap();
479
480        assert_eq!(
481            encryptor.decrypt_and_verify(msg).unwrap(),
482            "{\"key\":\"value\"}".as_bytes()
483        );
484    }
485
486    #[test]
487    fn aes_hamc_decrypt_and_verify_returns_invalid_signature_error_for_wrong_key() {
488        let msg = "SnRXQXFhOE9WSGg2QmVGUDdHdkhNZz09LS1vcjFWcm53VU40YmV0SVcwdWFlK2NRPT0=--c879b51cbd92559d4d684c406b3aaebfbc958e9d";
489
490        let dkp = DerivedKeyParams::default();
491        let encryptor =
492            AesHmacEncryptor::new("helloworld", "test salt", "test signed salt", dkp).unwrap();
493
494        assert_error_kind!(
495            encryptor.decrypt_and_verify(msg).unwrap_err(),
496            ErrorKind::InvalidSignature
497        );
498    }
499
500    #[test]
501    fn aes_hamc_decrypt_and_verify_returns_invalid_message_for_empty_message() {
502        let msg = "";
503
504        let dkp = DerivedKeyParams::default();
505        let encryptor =
506            AesHmacEncryptor::new("helloworld", "test salt", "test signed salt", dkp).unwrap();
507
508        assert_error_kind!(
509            encryptor.decrypt_and_verify(msg).unwrap_err(),
510            ErrorKind::InvalidMessage
511        );
512    }
513
514    #[test]
515    fn aes_hamc_encrypt_and_sign_returns_encrypted_and_signed_decryptable_and_verifiable_string() {
516        let dkp = DerivedKeyParams::default();
517        let encryptor =
518            AesHmacEncryptor::new("helloworld", "test salt", "test signed salt", dkp).unwrap();
519
520        let message = encryptor.encrypt_and_sign("{\"key\":\"value\"}").unwrap();
521
522        assert_eq!(
523            encryptor.decrypt_and_verify(&message).unwrap(),
524            "{\"key\":\"value\"}".as_bytes()
525        );
526    }
527
528    #[test]
529    fn aes_hamc_decrypt_and_verify_returns_decoded_message_with_non_default_cipher_for_valid_messages(
530    ) {
531        let msg = "RXFQajB4VzR3QytRQ0NpQXlGUFFTdz09LS0ycUZlcWFXNlRsb1phanMvcHlwVCtRPT0=--5d4739f859e1f730dc0ae7abfb21160c9f00dae6";
532
533        let dkp = DerivedKeyParams::default();
534        let mut encryptor =
535            AesHmacEncryptor::new("helloworld", "test salt", "test signed salt", dkp).unwrap();
536        encryptor.key_size = KeySize::Aes192;
537
538        assert_eq!(
539            encryptor.decrypt_and_verify(msg).unwrap(),
540            "{\"key\":\"value\"}".as_bytes()
541        );
542    }
543
544    #[test]
545    fn aes_hamc_encrypt_and_sign_returns_encrypted_and_signed_decryptable_and_verifiable_string_with_non_default_cipher(
546    ) {
547        let dkp = DerivedKeyParams::default();
548        let mut encryptor =
549            AesHmacEncryptor::new("helloworld", "test salt", "test signed salt", dkp).unwrap();
550        encryptor.key_size = KeySize::Aes192;
551
552        let message = encryptor.encrypt_and_sign("{\"key\":\"value\"}").unwrap();
553
554        assert_eq!(
555            encryptor.decrypt_and_verify(&message).unwrap(),
556            "{\"key\":\"value\"}".as_bytes()
557        );
558    }
559
560    #[test]
561    fn aes_gcm_decrypt_and_verify_returns_decoded_message_for_valid_messages() {
562        let msg = "H9msESjs5e8I6utXGnk0--4UI1B/xoA1MIR3A3--DHpzaZ7LMhFsWXzEbLiOCA==";
563
564        let dkp = DerivedKeyParams::default();
565        let encryptor = AesGcmEncryptor::new("helloworld", "test salt", dkp).unwrap();
566
567        assert_eq!(
568            encryptor.decrypt_and_verify(msg).unwrap(),
569            "{\"key\":\"value\"}".as_bytes()
570        );
571    }
572
573    #[test]
574    fn aes_gcm_decrypt_and_verify_returns_invalid_message_error_for_wrong_key() {
575        let msg = "Rhlx3KvutaC3AU1gi7pg--5T4OYITxIw56qdfL--pcc0hZjYYP/5xgTRYFqnkA==";
576
577        let dkp = DerivedKeyParams::default();
578        let encryptor = AesGcmEncryptor::new("helloworld", "test salt", dkp).unwrap();
579
580        assert_error_kind!(
581            encryptor.decrypt_and_verify(msg).unwrap_err(),
582            ErrorKind::InvalidMessage
583        );
584    }
585
586    #[test]
587    fn aes_gcm_decrypt_and_verify_returns_invalid_message_for_empty_message() {
588        let msg = "";
589
590        let dkp = DerivedKeyParams::default();
591        let encryptor = AesGcmEncryptor::new("helloworld", "test signed salt", dkp).unwrap();
592
593        assert_error_kind!(
594            encryptor.decrypt_and_verify(msg).unwrap_err(),
595            ErrorKind::InvalidMessage
596        );
597    }
598
599    #[test]
600    fn aes_gcm_encrypt_and_sign_returns_encrypted_and_signed_decryptable_and_verifiable_string() {
601        let dkp = DerivedKeyParams::default();
602        let encryptor = AesGcmEncryptor::new("helloworld", "test salt", dkp).unwrap();
603
604        let message = encryptor.encrypt_and_sign("{\"key\":\"value\"}").unwrap();
605
606        assert_eq!(
607            encryptor.decrypt_and_verify(&message).unwrap(),
608            "{\"key\":\"value\"}".as_bytes()
609        );
610    }
611}