blob: 88ebcdf21ee626d54c734a02c053a10a50287157 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use akm::Akm;
use bytes::Bytes;
use cipher::Cipher;
use crypto_utils::prf;
use std::cmp::{max, min};
use {Error, Result};
/// A PTK is derived from a PMK and provides access to the PTK's key-hierarchy which yields a KEK,
/// KCK, and TK, used for EAPOL frame protection, integrity check and unicast frame protection
/// respectively.
#[derive(Debug)]
pub struct Ptk {
ptk: Vec<u8>,
kck_len: usize,
kek_len: usize,
tk_len: usize,
// TODO(hahnr): Add TKIP Tx/Rx MIC support (IEEE 802.11-2016, 12.8.1).
}
impl Ptk {
pub fn kck(&self) -> &[u8] {
&self.ptk[0..self.kck_len]
}
pub fn kek(&self) -> &[u8] {
let start = self.kck_len;
&self.ptk[start..start + self.kek_len]
}
pub fn tk(&self) -> &[u8] {
let start = self.kck_len + self.kek_len;
&self.ptk[start..start + self.tk_len]
}
}
// IEEE 802.11-2016, 12.7.1.3
pub fn new(
pmk: &[u8], aa: &[u8; 6], spa: &[u8; 6], anonce: &[u8; 32], snonce: &[u8; 32], akm: &Akm,
cipher: &Cipher,
) -> Result<Ptk> {
let pmk_bits = akm.pmk_bits()
.ok_or_else(|| Error::PtkHierarchyUnsupportedAkmError)?;
if pmk.len() != (pmk_bits / 8) as usize {
return Err(Error::PtkHierarchyInvalidPmkError);
}
let kck_bits = akm.kck_bits()
.ok_or_else(|| Error::PtkHierarchyUnsupportedAkmError)?;
let kek_bits = akm.kek_bits()
.ok_or_else(|| Error::PtkHierarchyUnsupportedAkmError)?;
let tk_bits = cipher
.tk_bits()
.ok_or_else(|| Error::PtkHierarchyUnsupportedCipherError)?;
let prf_bits = kck_bits + kek_bits + tk_bits;
// data length = 6 (aa) + 6 (spa) + 32 (anonce) + 32 (snonce)
let mut data: [u8; 76] = [0; 76];
data[0..6].copy_from_slice(&min(aa, spa)[..]);
data[6..12].copy_from_slice(&max(aa, spa)[..]);
data[12..44].copy_from_slice(&min(anonce, snonce)[..]);
data[44..].copy_from_slice(&max(anonce, snonce)[..]);
// Use PRF to derive the PTK from the PMK while grants access to the KEK, KCK and TK.
let ptk_bytes = prf(pmk, "Pairwise key expansion", &data, prf_bits as usize)?;
let ptk = Ptk {
ptk: ptk_bytes,
kck_len: (kck_bits / 8) as usize,
kek_len: (kek_bits / 8) as usize,
tk_len: (tk_bits / 8) as usize,
};
Ok(ptk)
}
#[cfg(test)]
mod tests {
use super::*;
use akm::{Akm, PSK, SAE};
use cipher::{CCMP_128, Cipher, WEP_40, TKIP};
use hex::FromHex;
use suite_selector::{Factory, OUI};
struct TestData {
pmk: Vec<u8>,
aa: [u8; 6],
spa: [u8; 6],
anonce: [u8; 32],
snonce: [u8; 32],
}
// IEEE Std 802.11-2016, J.7.1, Table J-13
fn ieee_test_data() -> TestData {
let pmk = Vec::from_hex("0dc0d6eb90555ed6419756b9a15ec3e3209b63df707dd508d14581f8982721af")
.unwrap();
let aa = <[u8; 6]>::from_hex("a0a1a1a3a4a5").unwrap();
let spa = <[u8; 6]>::from_hex("b0b1b2b3b4b5").unwrap();
let anonce = <[u8; 32]>::from_hex(
"e0e1e2e3e4e5e6e7e8e9f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405",
).unwrap();
let snonce = <[u8; 32]>::from_hex(
"c0c1c2c3c4c5c6c7c8c9d0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5",
).unwrap();
TestData {
pmk,
aa,
spa,
anonce,
snonce,
}
}
fn new_ptk(data: &TestData, akm_suite: u8, cipher_suite: u8) -> Result<Ptk> {
let akm = Akm::new(Bytes::from(&OUI[..]), akm_suite).unwrap();
let cipher = Cipher::new(Bytes::from(&OUI[..]), cipher_suite).unwrap();
new(
&data.pmk[..],
&data.aa,
&data.spa,
&data.anonce,
&data.snonce,
&akm,
&cipher,
)
}
// IEEE Std 802.11-2016, J.7.1 & J.7.2
#[test]
fn test_pairwise_key_hierarchy_ccmp() {
let data = ieee_test_data();
let ptk_result = new_ptk(&data, PSK, CCMP_128);
assert_eq!(ptk_result.is_ok(), true);
// IEEE Std 802.11-2016, J.7.2, Table J-14
let expected_kck = Vec::from_hex("379f9852d0199236b94e407ce4c00ec8").unwrap();
let expected_kek = Vec::from_hex("47c9edc01c2c6e5b4910caddfb3e51a7").unwrap();
let expected_tk = Vec::from_hex("b2360c79e9710fdd58bea93deaf06599").unwrap();
let ptk = ptk_result.unwrap();
assert_eq!(ptk.kck(), &expected_kck[..]);
assert_eq!(ptk.kek(), &expected_kek[..]);
assert_eq!(ptk.tk(), &expected_tk[..]);
}
// IEEE Std 802.11-2016, J.7.1 & J.7.3
#[test]
fn test_pairwise_key_hierarchy_tkip() {
let data = ieee_test_data();
let ptk_result = new_ptk(&data, PSK, TKIP);
assert_eq!(ptk_result.is_ok(), true);
// IEEE Std 802.11-2016, J.7.3, Table J-15
let expected_kck = Vec::from_hex("379f9852d0199236b94e407ce4c00ec8").unwrap();
let expected_kek = Vec::from_hex("47c9edc01c2c6e5b4910caddfb3e51a7").unwrap();
let expected_tk = Vec::from_hex(
"b2360c79e9710fdd58bea93deaf06599db980afbc29c152855740a6ce5ae3827",
).unwrap();
let ptk = ptk_result.unwrap();
assert_eq!(ptk.kck(), &expected_kck[..]);
assert_eq!(ptk.kek(), &expected_kek[..]);
assert_eq!(ptk.tk(), &expected_tk[..]);
}
#[test]
fn test_pairwise_key_hierarchy_invalid_pmk() {
let mut data = ieee_test_data();
data.pmk.remove(0); // Invalidate PMK.
let ptk_result = new_ptk(&data, PSK, CCMP_128);
assert_eq!(ptk_result.is_err(), true);
}
#[test]
fn test_pairwise_key_hierarchy_unsupported_akm() {
let data = ieee_test_data();
let ptk_result = new_ptk(&data, 200, CCMP_128);
assert_eq!(ptk_result.is_err(), true);
}
#[test]
fn test_pairwise_key_hierarchy_unsupported_cipher() {
let data = ieee_test_data();
let ptk_result = new_ptk(&data, PSK, 200);
assert_eq!(ptk_result.is_err(), true);
}
}