blob: e340c9e2a9522f11bcb315f4040682fa3267bddb [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.
#![feature(test)]
#![feature(drain_filter)]
#![deny(warnings)]
// Remove once Cipher and AKM *_bits() were replaced with *_len() calls.
#![allow(deprecated)]
use failure::{self, Fail};
// TODO(hahnr): Limit exports and rearrange modules.
pub mod akm;
pub mod auth;
pub mod cipher;
mod crypto_utils;
mod integrity;
pub mod key;
mod key_data;
mod keywrap;
mod pmkid;
pub mod rsna;
pub mod rsne;
mod state_machine;
pub mod suite_selector;
use crate::key::exchange::{
self,
handshake::fourway::{self, MessageNumber},
handshake::group_key,
};
use crate::rsna::esssa::EssSa;
use crate::rsna::{Role, UpdateSink};
use std::sync::{Arc, Mutex};
pub use crate::auth::psk;
pub use crate::crypto_utils::nonce;
pub use crate::key::gtk;
pub use crate::key::gtk::GtkProvider;
pub use crate::rsna::NegotiatedRsne;
pub use crate::suite_selector::OUI;
#[derive(Debug, PartialEq)]
pub struct Supplicant {
esssa: EssSa,
}
impl Supplicant {
/// WPA2-PSK CCMP-128 Supplicant which supports 4-Way- and Group-Key Handshakes.
pub fn new_wpa2psk_ccmp128(
nonce_rdr: Arc<nonce::NonceReader>,
psk: psk::Psk,
s_addr: [u8; 6],
s_rsne: rsne::Rsne,
a_addr: [u8; 6],
a_rsne: rsne::Rsne,
) -> Result<Supplicant, failure::Error> {
let negotiated_rsne = NegotiatedRsne::from_rsne(&s_rsne)?;
let akm = negotiated_rsne.akm.clone();
let group_data = negotiated_rsne.group_data.clone();
let esssa = EssSa::new(
Role::Supplicant,
negotiated_rsne,
auth::Config::ComputedPsk(psk),
exchange::Config::FourWayHandshake(fourway::Config::new(
Role::Supplicant,
s_addr,
s_rsne,
a_addr,
a_rsne,
nonce_rdr,
None,
)?),
Some(exchange::Config::GroupKeyHandshake(group_key::Config {
role: Role::Supplicant,
akm,
cipher: group_data,
})),
)?;
Ok(Supplicant { esssa })
}
/// Starts the Supplicant. A Supplicant must be started after its creation and everytime it was
/// reset.
pub fn start(&mut self) -> Result<(), failure::Error> {
// The Supplicant always waits for Authenticator to initiate and does not yet support EAPOL
// request frames. Thus, all updates can be ignored.
let mut dead_update_sink = vec![];
self.esssa.initiate(&mut dead_update_sink)
}
/// Resets all established Security Associations and invalidates all derived keys.
/// The Supplicant must be reset or destroyed when the underlying 802.11 association terminates.
pub fn reset(&mut self) {
self.esssa.reset();
}
/// Entry point for all incoming EAPOL frames. Incoming frames can be corrupted, invalid or of
/// unsupported types; the Supplicant will filter and drop all unexpected frames.
/// Outbound EAPOL frames, status and key updates will be pushed into the `update_sink`.
/// The method will return an `Error` if the frame was invalid.
pub fn on_eapol_frame(
&mut self,
update_sink: &mut UpdateSink,
frame: &eapol::Frame,
) -> Result<(), failure::Error> {
self.esssa.on_eapol_frame(update_sink, frame)
}
}
#[derive(Debug, PartialEq)]
pub struct Authenticator {
esssa: EssSa,
}
impl Authenticator {
/// WPA2-PSK CCMP-128 Authenticator which supports 4-Way Handshake.
/// The Authenticator does not support GTK rotations.
pub fn new_wpa2psk_ccmp128(
nonce_rdr: Arc<nonce::NonceReader>,
gtk_provider: Arc<Mutex<gtk::GtkProvider>>,
psk: psk::Psk,
s_addr: [u8; 6],
s_rsne: rsne::Rsne,
a_addr: [u8; 6],
a_rsne: rsne::Rsne,
) -> Result<Authenticator, failure::Error> {
let negotiated_rsne = NegotiatedRsne::from_rsne(&s_rsne)?;
let esssa = EssSa::new(
Role::Authenticator,
negotiated_rsne,
auth::Config::ComputedPsk(psk),
exchange::Config::FourWayHandshake(fourway::Config::new(
Role::Authenticator,
s_addr,
s_rsne,
a_addr,
a_rsne,
nonce_rdr,
Some(gtk_provider),
)?),
// Group-Key Handshake does not support Authenticator role yet.
None,
)?;
Ok(Authenticator { esssa })
}
pub fn get_negotiated_rsne(&self) -> &NegotiatedRsne {
&self.esssa.negotiated_rsne
}
/// Resets all established Security Associations and invalidates all derived keys.
/// The Authenticator must be reset or destroyed when the underlying 802.11 association
/// terminates.
pub fn reset(&mut self) {
self.esssa.reset();
}
/// `initiate(...)` must be called when the Authenticator should start establishing a
/// security association with a client.
/// The Authenticator must always initiate the security association in the current system as
/// EAPOL request frames from clients are not yet supported.
/// This method can be called multiple times to re-initiate the security association, however,
/// calling this method will invalidate all established security associations and their derived
/// keys.
pub fn initiate(&mut self, update_sink: &mut UpdateSink) -> Result<(), failure::Error> {
self.esssa.initiate(update_sink)
}
/// Entry point for all incoming EAPOL frames. Incoming frames can be corrupted, invalid or of
/// unsupported types; the Authenticator will filter and drop all unexpected frames.
/// Outbound EAPOL frames, status and key updates will be pushed into the `update_sink`.
/// The method will return an `Error` if the frame was invalid.
pub fn on_eapol_frame(
&mut self,
update_sink: &mut UpdateSink,
frame: &eapol::Frame,
) -> Result<(), failure::Error> {
self.esssa.on_eapol_frame(update_sink, frame)
}
}
#[derive(Debug, Fail)]
pub enum Error {
#[fail(display = "unexpected IO error: {}", _0)]
UnexpectedIoError(#[cause] std::io::Error),
#[fail(display = "invalid OUI length; expected 3 bytes but received {}", _0)]
InvalidOuiLength(usize),
#[fail(display = "invalid PMKID length; expected 16 bytes but received {}", _0)]
InvalidPmkidLength(usize),
#[fail(display = "invalid ssid length: {}", _0)]
InvalidSsidLen(usize),
#[fail(display = "invalid passphrase length: {}", _0)]
InvalidPassphraseLen(usize),
#[fail(display = "passphrase contains invalid character: {:x}", _0)]
InvalidPassphraseChar(u8),
#[fail(display = "the config `{:?}` is incompatible with the auth method `{:?}`", _0, _1)]
IncompatibleConfig(auth::Config, String),
#[fail(display = "invalid bit size; must be a multiple of 8 but was {}", _0)]
InvalidBitSize(usize),
#[fail(display = "nonce could not be generated")]
NonceError,
#[fail(display = "error deriving PTK; invalid PMK")]
PtkHierarchyInvalidPmkError,
#[fail(display = "error deriving PTK; unsupported AKM suite")]
PtkHierarchyUnsupportedAkmError,
#[fail(display = "error deriving PTK; unsupported cipher suite")]
PtkHierarchyUnsupportedCipherError,
#[fail(display = "error deriving GTK; unsupported cipher suite")]
GtkHierarchyUnsupportedCipherError,
#[fail(display = "error invalid key size for AES keywrap: {}", _0)]
InvalidAesKeywrapKeySize(usize),
#[fail(
display = "error data must be a multiple of 64-bit blocks and at least 128 bits: {}",
_0
)]
InvalidAesKeywrapDataLength(usize),
#[fail(display = "error wrong key for AES Keywrap unwrapping")]
WrongAesKeywrapKey,
#[fail(
display = "invalid key data length; must be at least 16 bytes and a multiple of 8: {}",
_0
)]
InvaidKeyDataLength(usize),
#[fail(display = "invalid key data; error code: {:?}", _0)]
InvalidKeyData(nom::IError),
#[fail(display = "unknown authentication method")]
UnknownAuthenticationMethod,
#[fail(display = "no AKM negotiated")]
InvalidNegotiatedAkm,
#[fail(display = "unknown key exchange method")]
UnknownKeyExchange,
#[fail(display = "cannot initiate Fourway Handshake as Supplicant")]
UnexpectedInitiationRequest,
#[fail(display = "unsupported Key Descriptor Type: {:?}", _0)]
UnsupportedKeyDescriptor(u8),
#[fail(display = "unexpected Key Descriptor Type {:?}; expected {:?}", _0, _1)]
InvalidKeyDescriptor(u8, eapol::KeyDescriptor),
#[fail(display = "unsupported Key Descriptor Version: {:?}", _0)]
UnsupportedKeyDescriptorVersion(u16),
#[fail(display = "only PTK and GTK derivation is supported")]
UnsupportedKeyDerivation,
#[fail(display = "unexpected message: {:?}", _0)]
Unexpected4WayHandshakeMessage(MessageNumber),
#[fail(display = "invalid install bit value; message: {:?}", _0)]
InvalidInstallBitValue(MessageNumber),
#[fail(display = "error, install bit set for Group-/SMK-Handshake")]
InvalidInstallBitGroupSmkHandshake,
#[fail(display = "invalid key_ack bit value; message: {:?}", _0)]
InvalidKeyAckBitValue(MessageNumber),
#[fail(display = "invalid key_mic bit value; message: {:?}", _0)]
InvalidKeyMicBitValue(MessageNumber),
#[fail(display = "invalid key_mic bit value; message: {:?}", _0)]
InvalidSecureBitValue(MessageNumber),
#[fail(display = "error, secure bit set by Authenticator before PTK is known")]
SecureBitWithUnknownPtk,
#[fail(display = "error, secure bit set must be set by Supplicant once PTK and GTK are known")]
SecureBitNotSetWithKnownPtkGtk,
#[fail(display = "invalid error bit value; message: {:?}", _0)]
InvalidErrorBitValue(MessageNumber),
#[fail(display = "invalid request bit value; message: {:?}", _0)]
InvalidRequestBitValue(MessageNumber),
#[fail(display = "error, Authenticator set request bit")]
InvalidRequestBitAuthenticator,
#[fail(display = "error, Authenticator set error bit")]
InvalidErrorBitAuthenticator,
#[fail(display = "error, Supplicant set key_ack bit")]
InvalidKeyAckBitSupplicant,
#[fail(display = "invalid encrypted_key_data bit value")]
InvalidEncryptedKeyDataBitValue(MessageNumber),
#[fail(display = "invalid key length {:?}; expected {:?}", _0, _1)]
InvalidKeyLength(u16, u16),
#[fail(display = "unsupported cipher suite")]
UnsupportedCipherSuite,
#[fail(display = "unsupported AKM suite")]
UnsupportedAkmSuite,
#[fail(display = "invalid MIC size")]
InvalidMicSize,
#[fail(display = "invalid Nonce; expected to be non-zero")]
InvalidNonce(MessageNumber),
#[fail(display = "invalid RSC; expected to be zero")]
InvalidRsc(MessageNumber),
#[fail(display = "invalid key data; must not be zero")]
EmptyKeyData(MessageNumber),
#[fail(display = "invalid key data")]
InvalidKeyDataContent,
#[fail(display = "invalid key data length; doesn't match with key data")]
InvalidKeyDataLength,
#[fail(display = "cannot validate MIC; PTK not yet derived")]
UnexpectedMic,
#[fail(display = "invalid MIC")]
InvalidMic,
#[fail(display = "cannot decrypt key data; PTK not yet derived")]
UnexpectedEncryptedKeyData,
#[fail(display = "invalid key replay counter {:?}; expected counter to be > {:?}", _0, _1)]
InvalidKeyReplayCounter(u64, u64),
#[fail(display = "invalid nonce; nonce must match nonce from 1st message")]
ErrorNonceDoesntMatch,
#[fail(display = "invalid IV; EAPOL protocol version: {:?}; message: {:?}", _0, _1)]
InvalidIv(u8, MessageNumber),
#[fail(display = "PMKSA was not yet established")]
PmksaNotEstablished,
#[fail(display = "invalid nonce size; expected 32 bytes, found: {:?}", _0)]
InvalidNonceSize(usize),
#[fail(display = "invalid key data; expected negotiated RSNE")]
InvalidKeyDataRsne,
#[fail(display = "buffer too small; required: {}, available: {}", _0, _1)]
BufferTooSmall(usize, usize),
#[fail(display = "error, SMK-Handshake is not supported")]
SmkHandshakeNotSupported,
#[fail(display = "error, negotiated RSNE is invalid")]
InvalidNegotiatedRsne,
}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Error::UnexpectedIoError(e)
}
}