blob: 0907d8ed623907fa3fd4cd01bdf3059a9edf44e6 [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 crate::akm::Akm;
use crate::cipher::{Cipher, GROUP_CIPHER_SUITE, TKIP};
use crate::key::exchange::Key;
use crate::rsne::{RsnCapabilities, Rsne};
use crate::Error;
use eapol;
use failure::{self, bail, ensure};
pub mod esssa;
#[cfg(test)]
pub mod test_util;
#[derive(Debug, Clone, PartialEq)]
pub struct NegotiatedRsne {
pub group_data: Cipher,
pub pairwise: Cipher,
pub akm: Akm,
pub mic_size: u16,
// Some networks carry RSN capabilities.
// To construct a valid RSNE, these capabilities must be tracked.
caps: Option<RsnCapabilities>,
}
impl NegotiatedRsne {
pub fn from_rsne(rsne: &Rsne) -> Result<NegotiatedRsne, failure::Error> {
ensure!(rsne.group_data_cipher_suite.is_some(), Error::InvalidNegotiatedRsne);
let group_data = rsne.group_data_cipher_suite.as_ref().unwrap();
ensure!(rsne.pairwise_cipher_suites.len() == 1, Error::InvalidNegotiatedRsne);
let pairwise = &rsne.pairwise_cipher_suites[0];
ensure!(rsne.akm_suites.len() == 1, Error::InvalidNegotiatedRsne);
let akm = &rsne.akm_suites[0];
let mic_size = akm.mic_bytes();
ensure!(mic_size.is_some(), Error::InvalidNegotiatedRsne);
let mic_size = mic_size.unwrap();
Ok(NegotiatedRsne {
group_data: group_data.clone(),
pairwise: pairwise.clone(),
akm: akm.clone(),
mic_size,
caps: rsne.rsn_capabilities.clone(),
})
}
pub fn to_full_rsne(&self) -> Rsne {
let mut s_rsne = Rsne::new();
s_rsne.group_data_cipher_suite = Some(self.group_data.clone());
s_rsne.pairwise_cipher_suites = vec![self.pairwise.clone()];
s_rsne.akm_suites = vec![self.akm.clone()];
s_rsne.rsn_capabilities = self.caps.clone();
s_rsne
}
}
pub struct EncryptedKeyData<'a> {
key_data: &'a [u8],
}
impl<'a> EncryptedKeyData<'a> {
pub fn decrypt(&self, kek: &[u8], akm: &Akm) -> Result<Vec<u8>, failure::Error> {
Ok(akm.keywrap_algorithm().ok_or(Error::UnsupportedAkmSuite)?.unwrap(kek, self.key_data)?)
}
}
pub struct UnverifiedMic<'a> {
frame: &'a eapol::KeyFrame,
}
impl<'a> UnverifiedMic<'a> {
pub fn verify_mic(&self, kck: &[u8], akm: &Akm) -> Result<&'a eapol::KeyFrame, failure::Error> {
// IEEE Std 802.11-2016, 12.7.2 h)
// IEEE Std 802.11-2016, 12.7.2 b.6)
let mic_bytes = akm.mic_bytes().ok_or(Error::UnsupportedAkmSuite)?;
ensure!(self.frame.key_mic.len() == mic_bytes as usize, Error::InvalidMicSize);
// If a MIC is set but the PTK was not yet derived, the MIC cannot be verified.
let mut buf = Vec::with_capacity(self.frame.len());
self.frame.as_bytes(true, &mut buf);
let valid_mic = akm.integrity_algorithm().ok_or(Error::UnsupportedAkmSuite)?.verify(
kck,
&buf[..],
&self.frame.key_mic[..],
);
ensure!(valid_mic, Error::InvalidMic);
Ok(self.frame)
}
}
pub enum KeyFrameKeyDataState<'a> {
Encrypted(EncryptedKeyData<'a>),
Unencrypted(&'a [u8]),
}
impl<'a> KeyFrameKeyDataState<'a> {
pub fn from_frame(frame: &'a eapol::KeyFrame) -> KeyFrameKeyDataState<'a> {
if frame.key_info.encrypted_key_data() {
KeyFrameKeyDataState::Encrypted(EncryptedKeyData { key_data: &frame.key_data[..] })
} else {
KeyFrameKeyDataState::Unencrypted(&frame.key_data[..])
}
}
}
pub enum KeyFrameState<'a> {
UnverifiedMic(UnverifiedMic<'a>),
NoMic(&'a eapol::KeyFrame),
}
impl<'a> KeyFrameState<'a> {
pub fn from_frame(frame: &'a eapol::KeyFrame) -> KeyFrameState<'a> {
if frame.key_info.key_mic() {
KeyFrameState::UnverifiedMic(UnverifiedMic { frame })
} else {
KeyFrameState::NoMic(frame)
}
}
/// CAUTION: Returns the underlying frame without verifying its MIC if one is present.
/// Only use this if you know what you are doing.
pub fn unsafe_get_raw(&self) -> &'a eapol::KeyFrame {
match self {
KeyFrameState::UnverifiedMic(UnverifiedMic { frame }) => frame,
KeyFrameState::NoMic(frame) => frame,
}
}
}
// EAPOL Key frames carried in this struct comply with IEEE Std 802.11-2016, 12.7.2.
#[derive(Debug, Clone, PartialEq)]
pub struct VerifiedKeyFrame<'a> {
frame: &'a eapol::KeyFrame,
}
impl<'a> VerifiedKeyFrame<'a> {
pub fn from_key_frame(
frame: &'a eapol::KeyFrame,
role: &Role,
rsne: &NegotiatedRsne,
key_replay_counter: u64,
) -> Result<VerifiedKeyFrame<'a>, failure::Error> {
let sender = match role {
Role::Supplicant => Role::Authenticator,
Role::Authenticator => Role::Supplicant,
};
// IEEE Std 802.11-2016, 12.7.2 a)
// IEEE Std 802.1X-2010, 11.9
let key_descriptor = match eapol::KeyDescriptor::from_u8(frame.descriptor_type) {
Some(eapol::KeyDescriptor::Ieee802dot11) => eapol::KeyDescriptor::Ieee802dot11,
// Use of RC4 is deprecated.
Some(_) => bail!(Error::InvalidKeyDescriptor(
frame.descriptor_type,
eapol::KeyDescriptor::Ieee802dot11,
)),
// Invalid value.
None => bail!(Error::UnsupportedKeyDescriptor(frame.descriptor_type)),
};
// IEEE Std 802.11-2016, 12.7.2 b.1)
let expected_version = derive_key_descriptor_version(key_descriptor, rsne);
ensure!(
frame.key_info.key_descriptor_version() == expected_version,
Error::UnsupportedKeyDescriptorVersion(frame.key_info.key_descriptor_version())
);
// IEEE Std 802.11-2016, 12.7.2 b.2)
// IEEE Std 802.11-2016, 12.7.2 b.4)
match frame.key_info.key_type() {
eapol::KEY_TYPE_PAIRWISE => {}
eapol::KEY_TYPE_GROUP_SMK => {
// IEEE Std 802.11-2016, 12.7.2 b.4 ii)
ensure!(!frame.key_info.install(), Error::InvalidInstallBitGroupSmkHandshake);
}
_ => bail!(Error::UnsupportedKeyDerivation),
};
// IEEE Std 802.11-2016, 12.7.2 b.5)
if let Role::Supplicant = sender {
ensure!(!frame.key_info.key_ack(), Error::InvalidKeyAckBitSupplicant);
}
// IEEE Std 802.11-2016, 12.7.2 b.6)
// IEEE Std 802.11-2016, 12.7.2 b.7)
// MIC and Secure bit depend on specific key-exchange methods and can not be verified now.
// More specifically, there are frames which can carry a MIC or secure bit but are required
// to compute the PTK and/or GTK and thus cannot be verified up-front.
// IEEE Std 802.11-2016, 12.7.2 b.8)
if let Role::Authenticator = sender {
ensure!(!frame.key_info.error(), Error::InvalidErrorBitAuthenticator);
}
// IEEE Std 802.11-2016, 12.7.2 b.9)
if let Role::Authenticator = sender {
ensure!(!frame.key_info.request(), Error::InvalidRequestBitAuthenticator);
}
// IEEE Std 802.11-2016, 12.7.2 b.10)
// Encrypted key data is validated at the end once all other validations succeeded.
// IEEE Std 802.11-2016, 12.7.2 b.11)
ensure!(!frame.key_info.smk_message(), Error::SmkHandshakeNotSupported);
// IEEE Std 802.11-2016, 12.7.2 c)
match frame.key_info.key_type() {
eapol::KEY_TYPE_PAIRWISE => match sender {
// IEEE is somewhat vague on what is expected from the frame's key_len field.
// IEEE Std 802.11-2016, 12.7.2 c) requires the key_len to match the PTK's
// length, while all handshakes defined in IEEE such as
// 4-Way Handshake (12.7.6.3) and Group Key Handshake (12.7.7.3) explicitly require
// a value of 0 for frames sent by the Supplicant.
// Not all vendors follow the latter requirement, such as Apple with iOS.
// To improve interoperability, a value of 0 or the pairwise temporal key length is
// allowed for frames sent by the Supplicant.
Role::Supplicant if frame.key_len != 0 => {
let tk_bits = rsne.pairwise.tk_bits().ok_or(Error::UnsupportedCipherSuite)?;
let tk_len = tk_bits / 8;
ensure!(
frame.key_len == tk_len,
Error::InvalidKeyLength(frame.key_len, tk_len)
);
}
// Authenticator must use the pairwise cipher's key length.
Role::Authenticator => {
let tk_bits = rsne.pairwise.tk_bits().ok_or(Error::UnsupportedCipherSuite)?;
let tk_len = tk_bits / 8;
ensure!(
frame.key_len == tk_len,
Error::InvalidKeyLength(frame.key_len, tk_len)
);
}
_ => {}
},
// IEEE Std 802.11-2016, 12.7.2 c) does not specify the expected value for frames
// involved in exchanging the GTK. Thus, leave validation and enforcement of this
// requirement to the selected key exchange method.
eapol::KEY_TYPE_GROUP_SMK => {}
_ => bail!(Error::UnsupportedKeyDerivation),
};
// IEEE Std 802.11-2016, 12.7.2, d)
if key_replay_counter > 0 {
match sender {
// Supplicant responds to messages from the Authenticator with the same
// key replay counter.
Role::Supplicant => {
ensure!(
frame.key_replay_counter >= key_replay_counter,
Error::InvalidKeyReplayCounter(
frame.key_replay_counter,
key_replay_counter
)
);
}
// Authenticator must send messages with a strictly larger key replay counter.
Role::Authenticator => {
ensure!(
frame.key_replay_counter > key_replay_counter,
Error::InvalidKeyReplayCounter(
frame.key_replay_counter,
key_replay_counter
)
);
}
}
}
// IEEE Std 802.11-2016, 12.7.2, e)
// Validation is specific for the selected key exchange method.
// IEEE Std 802.11-2016, 12.7.2, f)
// Validation is specific for the selected key exchange method.
// IEEE Std 802.11-2016, 12.7.2, g)
// Validation is specific for the selected key exchange method.
// IEEE Std 802.11-2016, 12.7.2 h)
// IEEE Std 802.11-2016, 12.7.2 b.6)
// See explanation for IEEE Std 802.11-2016, 12.7.2 b.7) why the MIC cannot be verified
// here.
// IEEE Std 802.11-2016, 12.7.2 i) & j)
// IEEE Std 802.11-2016, 12.7.2 b.10)
ensure!(frame.key_data_len as usize == frame.key_data.len(), Error::InvalidKeyDataLength);
Ok(VerifiedKeyFrame { frame })
}
pub fn get(&self) -> KeyFrameState<'a> {
KeyFrameState::from_frame(self.frame)
}
pub fn get_key_data(&self) -> KeyFrameKeyDataState<'a> {
KeyFrameKeyDataState::from_frame(self.frame)
}
}
// IEEE Std 802.11-2016, 12.7.2 b.1)
// Key Descriptor Version is based on the negotiated AKM, Pairwise- and Group Cipher suite.
pub fn derive_key_descriptor_version(
key_descriptor_type: eapol::KeyDescriptor,
rsne: &NegotiatedRsne,
) -> u16 {
let akm = &rsne.akm;
let pairwise = &rsne.pairwise;
if !akm.has_known_algorithm() || !pairwise.has_known_usage() {
return 0;
}
match akm.suite_type {
1 | 2 => match key_descriptor_type {
eapol::KeyDescriptor::Rc4 => match pairwise.suite_type {
TKIP | GROUP_CIPHER_SUITE => 1,
_ => 0,
},
eapol::KeyDescriptor::Ieee802dot11
if pairwise.is_enhanced() || rsne.group_data.is_enhanced() =>
{
2
}
_ => 0,
},
// Interestingly, IEEE 802.11 does not specify any pairwise- or group cipher
// requirements for these AKMs.
3...6 => 3,
_ => 0,
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Role {
Authenticator,
Supplicant,
}
#[derive(Debug, PartialEq)]
pub enum SecAssocStatus {
// TODO(hahnr): Rather than reporting wrong password as a status, report it as an error.
WrongPassword,
EssSaEstablished,
}
#[derive(Debug, PartialEq)]
pub enum SecAssocUpdate {
TxEapolKeyFrame(eapol::KeyFrame),
Key(Key),
Status(SecAssocStatus),
}
pub type UpdateSink = Vec<SecAssocUpdate>;
#[cfg(test)]
mod tests {
use super::*;
use crate::akm;
use crate::cipher;
use crate::rsna::{test_util, NegotiatedRsne, Role};
use crate::rsne::Rsne;
use crate::suite_selector::OUI;
use bytes::Bytes;
#[test]
fn test_negotiated_rsne_from_rsne() {
let rsne = make_rsne(Some(cipher::GCMP_256), vec![cipher::CCMP_128], vec![akm::PSK]);
NegotiatedRsne::from_rsne(&rsne).expect("error, could not create negotiated RSNE");
let rsne = make_rsne(None, vec![cipher::CCMP_128], vec![akm::PSK]);
NegotiatedRsne::from_rsne(&rsne).expect_err("error, created negotiated RSNE");
let rsne = make_rsne(Some(cipher::CCMP_128), vec![], vec![akm::PSK]);
NegotiatedRsne::from_rsne(&rsne).expect_err("error, created negotiated RSNE");
let rsne = make_rsne(Some(cipher::CCMP_128), vec![cipher::CCMP_128], vec![]);
NegotiatedRsne::from_rsne(&rsne).expect_err("error, created negotiated RSNE");
}
// IEEE requires the key length to be zeroed in the 4-Way Handshake but some vendors send the
// pairwise cipher's key length instead. The requirement was relaxed to improve
// interoperability,
#[test]
fn test_supplicant_sends_zeroed_and_non_zeroed_key_length() {
let rsne = NegotiatedRsne::from_rsne(&test_util::get_s_rsne())
.expect("could not derive negotiated RSNE");
let mut env = test_util::FourwayTestEnv::new();
// Use arbitrarily chosen key_replay_counter.
let msg1 = env.initiate(12);
let (mut msg2, s_ptk) = env.send_msg1_to_supplicant(msg1, 12);
// Use CCMP-128 key length.
msg2.key_len = 16;
msg2 = test_util::finalize_key_frame(msg2, Some(s_ptk.kck()));
let result = VerifiedKeyFrame::from_key_frame(&msg2, &Role::Authenticator, &rsne, 12);
assert!(result.is_ok(), "failed verifying message: {}", result.unwrap_err());
msg2.key_len = 0;
msg2 = test_util::finalize_key_frame(msg2, Some(s_ptk.kck()));
let result = VerifiedKeyFrame::from_key_frame(&msg2, &Role::Authenticator, &rsne, 12);
assert!(result.is_ok(), "failed verifying message: {}", result.unwrap_err());
}
// Fuchsia requires EAPOL frames sent from the Supplicant to contain a key length of either 0 or
// the PTK's length.
#[test]
fn test_supplicant_sends_random_key_length() {
let mut env = test_util::FourwayTestEnv::new();
// Use arbitrarily chosen key_replay_counter.
let msg1 = env.initiate(12);
let (mut msg2, s_ptk) = env.send_msg1_to_supplicant(msg1, 12);
msg2.key_len = 29;
msg2 = test_util::finalize_key_frame(msg2, Some(s_ptk.kck()));
let rsne = NegotiatedRsne::from_rsne(&test_util::get_s_rsne())
.expect("could not derive negotiated RSNE");
let result = VerifiedKeyFrame::from_key_frame(&msg2, &Role::Authenticator, &rsne, 12);
assert!(result.is_err(), "successfully verified illegal message");
}
#[test]
fn test_to_rsne() {
let rsne = make_rsne(Some(cipher::CCMP_128), vec![cipher::CCMP_128], vec![akm::PSK]);
let negotiated_rsne = NegotiatedRsne::from_rsne(&rsne)
.expect("error, could not create negotiated RSNE")
.to_full_rsne();
assert_eq!(negotiated_rsne, rsne);
}
fn make_cipher(suite_type: u8) -> cipher::Cipher {
cipher::Cipher { oui: Bytes::from(&OUI[..]), suite_type }
}
fn make_akm(suite_type: u8) -> akm::Akm {
akm::Akm { oui: Bytes::from(&OUI[..]), suite_type }
}
fn make_rsne(data: Option<u8>, pairwise: Vec<u8>, akms: Vec<u8>) -> Rsne {
let mut rsne = Rsne::new();
rsne.group_data_cipher_suite = data.map(make_cipher);
rsne.pairwise_cipher_suites = pairwise.into_iter().map(make_cipher).collect();
rsne.akm_suites = akms.into_iter().map(make_akm).collect();
rsne
}
}