blob: cc100f28928d922f4591c05e14ecf1c4c1981058 [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 Error;
use akm::Akm;
use bytes::Bytes;
use bytes::BytesMut;
use crypto_utils::nonce::NonceReader;
use eapol;
use failure;
use integrity;
use key::exchange::Key;
use key::exchange::handshake::fourway::{self, FourwayHandshakeFrame};
use key::gtk::Gtk;
use key::ptk::Ptk;
use key_data;
use rsna::{SecAssocResult, SecAssocUpdate, VerifiedKeyFrame};
use rsne::Rsne;
#[derive(Debug, Default, PartialEq)]
struct PtkInitState {}
#[derive(Debug, PartialEq)]
struct GtkInitState {}
impl PtkInitState {
// IEEE Std 802.1X-2010, 12.7.6.2
fn on_message_1(&self, shared: &mut SharedState, msg1: FourwayHandshakeFrame)
-> Result<(eapol::KeyFrame, Ptk), failure::Error>
{
let anonce = &msg1.get().key_nonce;
let snonce = shared.nonce_rdr.next()?;
let rsne = &shared.cfg.s_rsne;
let akm = &rsne.akm_suites[0];
let cipher = &rsne.pairwise_cipher_suites[0];
let ptk = Ptk::new(
&shared.pmk[..],
&shared.cfg.a_addr,
&shared.cfg.s_addr,
&anonce[..],
&snonce[..],
akm,
cipher,
)?;
shared.anonce.copy_from_slice(&anonce[..]);
shared.kek = ptk.kek().to_vec();
shared.kck = ptk.kck().to_vec();
let msg2 = self.create_message_2(shared, msg1.get(), &snonce[..])?;
Ok((msg2, ptk))
}
// IEEE Std 802.1X-2010, 12.7.6.3
fn create_message_2(
&self,
shared: &SharedState,
msg1: &eapol::KeyFrame,
snonce: &[u8],
) -> Result<eapol::KeyFrame, failure::Error> {
let mut key_info = eapol::KeyInformation(0);
key_info.set_key_descriptor_version(msg1.key_info.key_descriptor_version());
key_info.set_key_type(msg1.key_info.key_type());
key_info.set_key_mic(true);
let mut key_data = vec![];
shared.cfg.s_rsne.as_bytes(&mut key_data);
let mut msg2 = eapol::KeyFrame {
version: msg1.version,
packet_type: eapol::PacketType::Key as u8,
packet_body_len: 0, // Updated afterwards
descriptor_type: eapol::KeyDescriptor::Ieee802dot11 as u8,
key_info: key_info,
key_len: 0,
key_replay_counter: msg1.key_replay_counter,
key_mic: Bytes::from(vec![0u8; msg1.key_mic.len()]),
key_rsc: 0,
key_iv: [0u8; 16],
key_nonce: eapol::to_array(snonce),
key_data_len: key_data.len() as u16,
key_data: Bytes::from(key_data),
};
msg2.update_packet_body_len();
// Verified before that Supplicant's RSNE holds one AKM Suite.
let akm = &shared.cfg.s_rsne.akm_suites[0];
let integrity_alg = akm.integrity_algorithm().ok_or(Error::UnsupportedAkmSuite)?;
let mic_len = akm.mic_bytes().ok_or(Error::UnsupportedAkmSuite)?;
update_mic(&shared.kck[..], mic_len, integrity_alg, &mut msg2)?;
Ok(msg2)
}
}
impl GtkInitState {
// IEEE Std 802.1X-2010, 12.7.6.4
fn on_message_3(&self, shared: &mut SharedState, msg3: FourwayHandshakeFrame)
-> Result<(eapol::KeyFrame, Gtk), failure::Error>
{
let mut gtk: Option<key_data::kde::Gtk> = None;
let mut rsne: Option<Rsne> = None;
let mut _second_rsne: Option<Rsne> = None;
let elements = key_data::extract_elements(&msg3.key_data_plaintext()[..])?;
for ele in elements {
match (ele, rsne.as_ref()) {
(key_data::Element::Gtk(_, e), _) => gtk = Some(e),
(key_data::Element::Rsne(e), None) => rsne = Some(e),
(key_data::Element::Rsne(e), Some(_)) => _second_rsne = Some(e),
_ => (),
}
}
// Proceed if key data held a GTK and RSNE and RSNE is the Authenticator's announced one.
match (gtk, rsne) {
(Some(gtk), Some(rsne)) => {
ensure!(&rsne == &shared.cfg.a_rsne, Error::InvalidKeyDataRsne);
let msg4 = self.create_message_4(shared, msg3.get())?;
Ok((msg4, Gtk::from_gtk(gtk.gtk, gtk.info.key_id())))
}
_ => bail!(Error::InvalidKeyDataContent),
}
}
// IEEE Std 802.1X-2010, 12.7.6.5
fn create_message_4(&self, shared: &SharedState, msg3: &eapol::KeyFrame)
-> Result<eapol::KeyFrame, failure::Error>
{
let mut key_info = eapol::KeyInformation(0);
key_info.set_key_descriptor_version(msg3.key_info.key_descriptor_version());
key_info.set_key_type(msg3.key_info.key_type());
key_info.set_key_mic(true);
key_info.set_secure(true);
let mut msg4 = eapol::KeyFrame {
version: msg3.version,
packet_type: eapol::PacketType::Key as u8,
packet_body_len: 0, // Updated afterwards
descriptor_type: eapol::KeyDescriptor::Ieee802dot11 as u8,
key_info: key_info,
key_len: 0,
key_replay_counter: msg3.key_replay_counter,
key_mic: Bytes::from(vec![0u8; msg3.key_mic.len()]),
key_rsc: 0,
key_iv: [0u8; 16],
key_nonce: [0u8; 32],
key_data_len: 0,
key_data: Bytes::from(vec![]),
};
msg4.update_packet_body_len();
// Verified before that Supplicant's RSNE holds one AKM Suite.
let akm = &shared.cfg.s_rsne.akm_suites[0];
let integrity_alg = akm.integrity_algorithm().ok_or(Error::UnsupportedAkmSuite)?;
let mic_len = akm.mic_bytes().ok_or(Error::UnsupportedAkmSuite)?;
update_mic(&shared.kck[..], mic_len, integrity_alg, &mut msg4)?;
Ok(msg4)
}
}
#[derive(Debug, PartialEq)]
enum State {
PtkInit(PtkInitState),
GtkInit(GtkInitState),
Completed,
}
impl State {
pub fn on_eapol_key_frame(self, shared: &mut SharedState, frame: FourwayHandshakeFrame)
-> Result<(State, Option<Vec<SecAssocUpdate>>), failure::Error>
{
match fourway::message_number(frame.get()) {
// Only process first and third message of the Handshake.
fourway::MessageNumber::Message1 => self.on_message_1(shared, frame),
fourway::MessageNumber::Message3 => self.on_message_3(shared, frame),
// Drop any other message with an error.
unexpected_msg => bail!(Error::Unexpected4WayHandshakeMessage(unexpected_msg)),
}
}
fn on_message_1(self, shared: &mut SharedState, msg1: FourwayHandshakeFrame)
-> Result<(State, Option<Vec<SecAssocUpdate>>), failure::Error>
{
// Always reset Handshake when first message was received.
match self {
// If the Handshake already completed, simply drop the message.
State::Completed => return Ok((self, None)),
// If the Handshake is already advanced further, restart the entire Handshake.
State::GtkInit(_) => return Ok((State::PtkInit(PtkInitState {}), None)),
// Else, if the message was expected, proceed.
_ => (),
};
// Only the PTK-Init state processes the first message.
match self {
State::PtkInit(ptk_init) => {
let (msg2, ptk) = ptk_init.on_message_1(shared, msg1)?;
// If the first message was processed successfully the PTK is known and the GTK
// can be exchanged with the Authenticator. Move the state machine forward.
Ok((
State::GtkInit(GtkInitState {}),
Some(vec![
SecAssocUpdate::TxEapolKeyFrame(msg2),
SecAssocUpdate::Key(Key::Ptk(ptk)),
]),
))
}
// This should never happen.
_ => panic!("tried to process first message of 4-Way Handshake in illegal state"),
}
}
fn on_message_3(self, shared: &mut SharedState, msg3: FourwayHandshakeFrame)
-> Result<(State, Option<Vec<SecAssocUpdate>>), failure::Error>
{
// Third message of Handshake is only processed once to prevent replay attacks such as
// KRACK. A replayed third message will be dropped and has no effect on the Supplicant.
// TODO(hahnr): Decide whether this client side fix should be kept, which will reduce
// reliability, or if instead the Supplicant should trust the Authenticator to be patched.
match self {
State::GtkInit(gtk_init) => {
let (msg4, gtk) = gtk_init.on_message_3(shared, msg3)?;
// The third message was successfully processed and the handshake completed.
Ok((
State::Completed,
Some(vec![
SecAssocUpdate::TxEapolKeyFrame(msg4),
SecAssocUpdate::Key(Key::Gtk(gtk)),
])
))
}
// At this point keys are either already installed and this message is a replay of a
// previous one, or, the message was received before the first message. In any case,
// drop the frame.
_ => Ok((self, None)),
}
}
}
#[derive(Debug)]
struct SharedState {
anonce: [u8; 32],
pmk: Vec<u8>,
kek: Vec<u8>,
kck: Vec<u8>,
cfg: fourway::Config,
nonce_rdr: NonceReader,
}
impl PartialEq for SharedState {
fn eq(&self, other: &SharedState) -> bool {
// Exclude nonce generator from comparison. This code will soon change once the nonce
// generator moved further up the stack.
self.anonce == other.anonce &&
self.pmk == other.pmk &&
self.kek == other.kek &&
self.kck == other.kck &&
self.cfg == other.cfg
}
}
#[derive(Debug, PartialEq)]
pub struct Supplicant {
shared: SharedState,
state: Option<State>,
}
impl Supplicant {
pub fn new(cfg: fourway::Config, pmk: Vec<u8>) -> Result<Self, failure::Error> {
let nonce_rdr = NonceReader::new(&cfg.s_addr[..])?;
Ok(Supplicant {
state: Some(State::PtkInit(PtkInitState {})),
shared: SharedState {
anonce: [0u8; 32],
pmk: pmk,
kek: vec![],
kck: vec![],
cfg,
nonce_rdr,
},
})
}
pub fn anonce(&self) -> &[u8] {
&self.shared.anonce[..]
}
pub fn on_eapol_key_frame(&mut self, frame: FourwayHandshakeFrame) -> SecAssocResult {
self.state.take().map_or_else(|| Ok(vec![]), |state| {
let (state, updates) = state.on_eapol_key_frame(&mut self.shared, frame)?;
self.state = Some(state);
Ok(updates.unwrap_or_default())
})
}
pub fn destroy(self) -> fourway::Config {
self.shared.cfg
}
}
fn update_mic(
kck: &[u8],
mic_len: u16,
alg: Box<integrity::Algorithm>,
frame: &mut eapol::KeyFrame,
) -> Result<(), failure::Error> {
let mut buf = Vec::with_capacity(frame.len());
frame.as_bytes(true, &mut buf);
let written = buf.len();
buf.truncate(written);
let mic = alg.compute(kck, &buf[..])?;
frame.key_mic = Bytes::from(&mic[..mic_len as usize]);
Ok(())
}