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
// Copyright 2020 The Tink-Rust Authors
//
// 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.
//
////////////////////////////////////////////////////////////////////////////////

//! Provides an implementation of PRF using AES-CMAC.

use aes::{Aes128, Aes192, Aes256};
use cmac::{Cmac, Mac, NewMac};
use std::{
    cmp::min,
    ops::DerefMut,
    sync::{Arc, Mutex},
};
use tink_core::TinkError;

const RECOMMENDED_KEY_SIZE: usize = 32;
const AES_BLOCK_SIZE_IN_BYTES: usize = 16;

enum AesCmacVariant {
    Aes128(Box<Cmac<Aes128>>),
    Aes192(Box<Cmac<Aes192>>),
    Aes256(Box<Cmac<Aes256>>),
}

/// `AesCmacPrf` is a type that can be used to compute several CMACs with the same key material.
#[derive(Clone)]
pub struct AesCmacPrf {
    mac: Arc<Mutex<AesCmacVariant>>,
}

impl AesCmacPrf {
    /// Create a new `AesCmacPrf` object and initialize it with the correct key material.
    pub fn new(key: &[u8]) -> Result<AesCmacPrf, TinkError> {
        let aes_cmac = match key.len() {
            16 => AesCmacVariant::Aes128(Box::new(
                Cmac::<Aes128>::new_varkey(key)
                    .map_err(|_| TinkError::new("failed to create key"))?,
            )),
            24 => AesCmacVariant::Aes192(Box::new(
                Cmac::<Aes192>::new_varkey(key)
                    .map_err(|_| TinkError::new("failed to create key"))?,
            )),
            32 => AesCmacVariant::Aes256(Box::new(
                Cmac::<Aes256>::new_varkey(key)
                    .map_err(|_| TinkError::new("failed to create key"))?,
            )),
            _ => return Err("AesCmacPrf: invalid key length for AES".into()),
        };
        Ok(AesCmacPrf {
            mac: Arc::new(Mutex::new(aes_cmac)),
        })
    }
}

/// Check that the key is the recommended size for AES-CMAC.
pub fn validate_aes_cmac_prf_params(key_size: usize) -> Result<(), TinkError> {
    if key_size != RECOMMENDED_KEY_SIZE {
        Err(format!(
            "Recommended key size for AES-CMAC is {}, but {} given",
            RECOMMENDED_KEY_SIZE, key_size
        )
        .into())
    } else {
        Ok(())
    }
}

impl tink_core::Prf for AesCmacPrf {
    /// Compute the AES-CMAC for the given key and data, returning `output_length` bytes.
    /// The timing of this function will only depend on `data.len()`, and not leak any additional
    /// information about the key or the data.
    fn compute_prf(&self, data: &[u8], output_length: usize) -> Result<Vec<u8>, TinkError> {
        if output_length > AES_BLOCK_SIZE_IN_BYTES {
            return Err(format!(
                "AesCmacPrf: output_length must be between 0 and {}",
                AES_BLOCK_SIZE_IN_BYTES
            )
            .into());
        }
        Ok(
            match self
                .mac
                .lock()
                .expect("internal lock corrupted") // safe: lock
                .deref_mut()
            {
                AesCmacVariant::Aes128(mac) => {
                    mac.update(data);
                    let result = mac.finalize_reset().into_bytes();
                    result[..min(result.len(), output_length)].to_vec()
                }
                AesCmacVariant::Aes192(mac) => {
                    mac.update(data);
                    let result = mac.finalize_reset().into_bytes();
                    result[..min(result.len(), output_length)].to_vec()
                }
                AesCmacVariant::Aes256(mac) => {
                    mac.update(data);
                    let result = mac.finalize_reset().into_bytes();
                    result[..min(result.len(), output_length)].to_vec()
                }
            },
        )
    }
}