1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
// Copyright 2018 The Exonum Team // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Utilities for constructing `Cipher` from an unauthenticated symmetric cipher and a MAC. use constant_time_eq::constant_time_eq; use core::marker::PhantomData; use crate::{alloc::Vec, Cipher, CipherOutput, MacMismatch}; /// Symmetric cipher without built-in authentication. pub trait UnauthenticatedCipher: 'static { /// Byte size of a key for this cipher. const KEY_LEN: usize; /// Byte size of a nonce (aka initialization vector, IV) for this cipher. const NONCE_LEN: usize; /// Encrypts or decrypts `message` in place, given the `nonce` and `key`. /// /// # Safety /// /// When used within [`PwBox`](crate::PwBox), `nonce` and `key` are guaranteed /// to have correct sizes. fn seal_or_open(message: &mut [u8], nonce: &[u8], key: &[u8]); } /// Message authentication code. pub trait Mac: 'static { /// Byte size of a MAC key. const KEY_LEN: usize; /// Byte size of the MAC output. const MAC_LEN: usize; /// Digests a message under the specified key. /// /// The output of this method **must** have size `MAC_LEN`. /// /// # Safety /// /// When used within [`PwBox`](crate::PwBox), `key` is guaranteed to have the correct size. fn digest(key: &[u8], message: &[u8]) -> Vec<u8>; } /// Authenticated cipher constructed from an ordinary symmetric cipher and a MAC construction. /// /// See [`Cipher` implementation] for details how this implementation works. /// /// [`Cipher` implementation]: #impl-Cipher #[derive(Debug)] pub struct CipherWithMac<C, M> { _cipher: PhantomData<C>, _mac: PhantomData<M>, } impl<C, M> Cipher for CipherWithMac<C, M> where C: UnauthenticatedCipher, M: Mac, { /// Equals to the sum of key sizes for the cipher and MAC. const KEY_LEN: usize = C::KEY_LEN + M::KEY_LEN; const NONCE_LEN: usize = C::NONCE_LEN; const MAC_LEN: usize = M::MAC_LEN; /// Works as follows: /// /// 1. Split the key into `cipher_key` (first bytes of the key) and `mac_key` /// (remaining bytes). /// 2. Encrypt the `message` using the cipher under `cipher_key` and `nonce`. /// 3. Compute MAC over the ciphertext with `mac_key`. fn seal(message: &[u8], nonce: &[u8], key: &[u8]) -> CipherOutput { let (cipher_key, mac_key) = (&key[..C::KEY_LEN], &key[C::KEY_LEN..]); let mut ciphertext = message.to_vec(); C::seal_or_open(&mut ciphertext, nonce, cipher_key); CipherOutput { mac: M::digest(mac_key, &ciphertext), ciphertext, } } /// Works as follows: /// /// 1. Split the key into `cipher_key` (first bytes of the key) and `mac_key` /// (remaining bytes). /// 2. Compute MAC over the ciphertext with `mac_key`. If MAC is not equal to /// the supplied one, return `None`. /// 3. Decrypt the ciphertext under the `cipher_key` and `nonce`. fn open( output: &mut [u8], enc: &CipherOutput, nonce: &[u8], key: &[u8], ) -> Result<(), MacMismatch> { debug_assert_eq!(key.len(), Self::KEY_LEN); debug_assert_eq!(enc.mac.len(), Self::MAC_LEN); debug_assert_eq!(output.len(), enc.ciphertext.len()); let (cipher_key, mac_key) = (&key[..C::KEY_LEN], &key[C::KEY_LEN..]); if !constant_time_eq(&M::digest(mac_key, &enc.ciphertext), &enc.mac) { return Err(MacMismatch); } output.copy_from_slice(&enc.ciphertext); C::seal_or_open(output, nonce, cipher_key); Ok(()) } }