diff --git a/examples/onoff_light/src/main.rs b/examples/onoff_light/src/main.rs index 15bfc4f..15c6949 100644 --- a/examples/onoff_light/src/main.rs +++ b/examples/onoff_light/src/main.rs @@ -19,17 +19,15 @@ mod dev_att; use matter::core::{self, CommissioningData}; use matter::data_model::cluster_basic_information::BasicInfoConfig; use matter::data_model::device_types::device_type_add_on_off_light; -use rand::prelude::*; +use matter::secure_channel::spake2p::VerifierData; fn main() { env_logger::init(); - let mut comm_data = CommissioningData { + let comm_data = CommissioningData { // TODO: Hard-coded for now - passwd: 123456, + verifier: VerifierData::new_with_pw(123456), discriminator: 250, - ..Default::default() }; - rand::thread_rng().fill_bytes(&mut comm_data.salt); // vid/pid should match those in the DAC let dev_info = BasicInfoConfig { diff --git a/matter/src/core.rs b/matter/src/core.rs index c5eb5b0..9dedb6b 100644 --- a/matter/src/core.rs +++ b/matter/src/core.rs @@ -25,19 +25,15 @@ use crate::{ fabric::FabricMgr, interaction_model::InteractionModel, mdns::Mdns, - secure_channel::core::SecureChannel, + secure_channel::{core::SecureChannel, spake2p::VerifierData}, transport, }; use std::sync::Arc; -#[derive(Default)] /// Device Commissioning Data pub struct CommissioningData { - /// The commissioning salt - pub salt: [u8; 16], - /// The password for commissioning the device - // TODO: We should replace this with verifier instead of password - pub passwd: u32, + /// The data like password or verifier that is required to authenticate + pub verifier: VerifierData, /// The 12-bit discriminator used to differentiate between multiple devices pub discriminator: u16, } @@ -78,11 +74,7 @@ impl Matter { matter.transport_mgr.register_protocol(interaction_model)?; let mut secure_channel = Box::new(SecureChannel::new(matter.fabric_mgr.clone())); if open_comm_window { - secure_channel.open_comm_window( - &dev_comm.salt, - dev_comm.passwd, - dev_comm.discriminator, - )?; + secure_channel.open_comm_window(dev_comm.verifier, dev_comm.discriminator)?; } matter.transport_mgr.register_protocol(secure_channel)?; diff --git a/matter/src/lib.rs b/matter/src/lib.rs index 728c484..5d39f6b 100644 --- a/matter/src/lib.rs +++ b/matter/src/lib.rs @@ -15,7 +15,6 @@ * limitations under the License. */ - //! Native Rust Implementation of Matter (Smart-Home) //! //! This crate implements the Matter specification that can be run on embedded devices @@ -28,7 +27,7 @@ //! use matter::{Matter, CommissioningData}; //! use matter::data_model::device_types::device_type_add_on_off_light; //! use matter::data_model::cluster_basic_information::BasicInfoConfig; -//! use rand::prelude::*; +//! use matter::secure_channel::spake2p::VerifierData; //! //! # use matter::data_model::sdm::dev_att::{DataType, DevAttDataFetcher}; //! # use matter::error::Error; @@ -39,12 +38,11 @@ //! # let dev_att = Box::new(DevAtt{}); //! //! /// The commissioning data for this device -//! let mut comm_data = CommissioningData { -//! passwd: 123456, +//! let comm_data = CommissioningData { +//! verifier: VerifierData::new_with_pw(123456), //! discriminator: 250, -//! ..Default::default() +//! //! }; -//! rand::thread_rng().fill_bytes(&mut comm_data.salt); //! //! /// The basic information about this device //! let dev_info = BasicInfoConfig { diff --git a/matter/src/secure_channel/core.rs b/matter/src/secure_channel/core.rs index 86ae55b..c5f04a8 100644 --- a/matter/src/secure_channel/core.rs +++ b/matter/src/secure_channel/core.rs @@ -30,7 +30,7 @@ use log::{error, info}; use num; use rand::prelude::*; -use super::case::Case; +use super::{case::Case, spake2p::VerifierData}; /* Handle messages related to the Secure Channel */ @@ -50,15 +50,14 @@ impl SecureChannel { pub fn open_comm_window( &mut self, - salt: &[u8; 16], - passwd: u32, + verifier: VerifierData, discriminator: u16, ) -> Result<(), Error> { let name: u64 = rand::thread_rng().gen_range(0..0xFFFFFFFFFFFFFFFF); let name = format!("{:016X}", name); let mdns = Mdns::get()? .publish_service(&name, mdns::ServiceMode::Commissionable(discriminator))?; - self.pake = Some((PAKE::new(salt, passwd), mdns)); + self.pake = Some((PAKE::new(verifier), mdns)); Ok(()) } diff --git a/matter/src/secure_channel/crypto.rs b/matter/src/secure_channel/crypto.rs index 68dac8d..f9481ba 100644 --- a/matter/src/secure_channel/crypto.rs +++ b/matter/src/secure_channel/crypto.rs @@ -38,7 +38,9 @@ pub trait CryptoSpake2 { fn set_w1(&mut self, w1: &[u8]) -> Result<(), Error>; #[allow(non_snake_case)] - fn set_L(&mut self, w1s: &[u8]) -> Result<(), Error>; + fn set_L(&mut self, l: &[u8]) -> Result<(), Error>; + #[allow(non_snake_case)] + fn set_L_from_w1s(&mut self, w1s: &[u8]) -> Result<(), Error>; #[allow(non_snake_case)] fn get_pB(&mut self, pB: &mut [u8]) -> Result<(), Error>; #[allow(non_snake_case)] diff --git a/matter/src/secure_channel/crypto_mbedtls.rs b/matter/src/secure_channel/crypto_mbedtls.rs index 57eeff2..7231f63 100644 --- a/matter/src/secure_channel/crypto_mbedtls.rs +++ b/matter/src/secure_channel/crypto_mbedtls.rs @@ -114,9 +114,14 @@ impl CryptoSpake2 for CryptoMbedTLS { Ok(()) } + fn set_L(&mut self, l: &[u8]) -> Result<(), Error> { + self.L = EcPoint::from_binary(&mut self.group, l)?; + Ok(()) + } + #[allow(non_snake_case)] #[allow(dead_code)] - fn set_L(&mut self, w1s: &[u8]) -> Result<(), Error> { + fn set_L_from_w1s(&mut self, w1s: &[u8]) -> Result<(), Error> { // From the Matter spec, // L = w1 * P // where P is the generator of the underlying elliptic curve diff --git a/matter/src/secure_channel/crypto_openssl.rs b/matter/src/secure_channel/crypto_openssl.rs index 0f80f4c..84d6793 100644 --- a/matter/src/secure_channel/crypto_openssl.rs +++ b/matter/src/secure_channel/crypto_openssl.rs @@ -117,9 +117,14 @@ impl CryptoSpake2 for CryptoOpenSSL { Ok(()) } + fn set_L(&mut self, l: &[u8]) -> Result<(), Error> { + self.L = EcPoint::from_bytes(&self.group, l, &mut self.bn_ctx)?; + Ok(()) + } + #[allow(non_snake_case)] #[allow(dead_code)] - fn set_L(&mut self, w1s: &[u8]) -> Result<(), Error> { + fn set_L_from_w1s(&mut self, w1s: &[u8]) -> Result<(), Error> { // From the Matter spec, // L = w1 * P // where P is the generator of the underlying elliptic curve diff --git a/matter/src/secure_channel/pake.rs b/matter/src/secure_channel/pake.rs index 008f193..3163f56 100644 --- a/matter/src/secure_channel/pake.rs +++ b/matter/src/secure_channel/pake.rs @@ -19,12 +19,11 @@ use std::time::{Duration, SystemTime}; use super::{ common::{create_sc_status_report, SCStatusCodes}, - spake2p::Spake2P, + spake2p::{Spake2P, VerifierData}, }; use crate::{ crypto, error::Error, - sys::SPAKE2_ITERATION_COUNT, tlv::{self, get_root_node_struct, FromTLV, OctetStr, TLVElement, TLVWriter, TagType, ToTLV}, transport::{ exchange::ExchangeCtx, @@ -111,20 +110,17 @@ impl Default for PakeState { } } -#[derive(Default)] pub struct PAKE { - salt: [u8; 16], - passwd: u32, + verifier: VerifierData, state: PakeState, } impl PAKE { - pub fn new(salt: &[u8; 16], passwd: u32) -> Self { + pub fn new(verifier: VerifierData) -> Self { // TODO: Can any PBKDF2 calculation be pre-computed here PAKE { - passwd, - salt: *salt, - ..Default::default() + verifier, + state: Default::default(), } } @@ -176,8 +172,7 @@ impl PAKE { let pA = extract_pasepake_1_or_3_params(ctx.rx.as_borrow_slice())?; let mut pB: [u8; 65] = [0; 65]; let mut cB: [u8; 32] = [0; 32]; - sd.spake2p - .start_verifier(self.passwd, SPAKE2_ITERATION_COUNT, &self.salt)?; + sd.spake2p.start_verifier(&self.verifier)?; sd.spake2p.handle_pA(pA, &mut pB, &mut cB)?; let mut tw = TLVWriter::new(ctx.tx.get_writebuf()?); @@ -231,8 +226,8 @@ impl PAKE { }; if !a.has_params { let params_resp = PBKDFParamRespParams { - count: SPAKE2_ITERATION_COUNT, - salt: OctetStr(&self.salt), + count: self.verifier.count, + salt: OctetStr(&self.verifier.salt), }; resp.params = Some(params_resp); } diff --git a/matter/src/secure_channel/spake2p.rs b/matter/src/secure_channel/spake2p.rs index 3870bcd..5e82f9f 100644 --- a/matter/src/secure_channel/spake2p.rs +++ b/matter/src/secure_channel/spake2p.rs @@ -15,8 +15,13 @@ * limitations under the License. */ -use crate::crypto::{self, HmacSha256}; +use crate::{ + crypto::{self, HmacSha256}, + sys, +}; use byteorder::{ByteOrder, LittleEndian}; +use log::error; +use rand::prelude::*; use subtle::ConstantTimeEq; use crate::{ @@ -74,6 +79,10 @@ const SPAKE2P_KEY_CONFIRM_INFO: [u8; 16] = *b"ConfirmationKeys"; const SPAKE2P_CONTEXT_PREFIX: [u8; 26] = *b"CHIP PAKE V1 Commissioning"; const CRYPTO_GROUP_SIZE_BYTES: usize = 32; const CRYPTO_W_SIZE_BYTES: usize = CRYPTO_GROUP_SIZE_BYTES + 8; +const CRYPTO_PUBLIC_KEY_SIZE_BYTES: usize = (2 * CRYPTO_GROUP_SIZE_BYTES) + 1; + +const MAX_SALT_SIZE_BYTES: usize = 32; +const VERIFIER_SIZE_BYTES: usize = CRYPTO_W_SIZE_BYTES + CRYPTO_PUBLIC_KEY_SIZE_BYTES; #[cfg(feature = "crypto_openssl")] fn crypto_spake2_new() -> Result, Error> { @@ -96,6 +105,45 @@ impl Default for Spake2P { } } +pub struct VerifierData { + pub data: VerifierOption, + // For the VerifierOption::Verifier, the following fields only serve + // information purposes + pub salt: [u8; MAX_SALT_SIZE_BYTES], + pub count: u32, +} + +pub enum VerifierOption { + /// With Password + Password(u32), + /// With Verifier + Verifier([u8; VERIFIER_SIZE_BYTES]), +} + +impl VerifierData { + pub fn new_with_pw(pw: u32) -> Self { + let mut s = Self { + salt: [0; MAX_SALT_SIZE_BYTES], + count: sys::SPAKE2_ITERATION_COUNT, + data: VerifierOption::Password(pw), + }; + rand::thread_rng().fill_bytes(&mut s.salt); + s + } + + pub fn new( + verifier: [u8; VERIFIER_SIZE_BYTES], + count: u32, + salt: [u8; MAX_SALT_SIZE_BYTES], + ) -> Self { + Self { + data: VerifierOption::Verifier(verifier), + count, + salt, + } + } +} + impl Spake2P { pub fn new() -> Self { Spake2P { @@ -132,17 +180,31 @@ impl Spake2P { let _ = pbkdf2_hmac(&pw_str, iter as usize, salt, w0w1s); } - pub fn start_verifier(&mut self, pw: u32, iter: u32, salt: &[u8]) -> Result<(), Error> { - let mut w0w1s: [u8; (2 * CRYPTO_W_SIZE_BYTES)] = [0; (2 * CRYPTO_W_SIZE_BYTES)]; - Spake2P::get_w0w1s(pw, iter, salt, &mut w0w1s); - self.crypto_spake2 = Some(crypto_spake2_new()?); + pub fn start_verifier(&mut self, verifier: &VerifierData) -> Result<(), Error> { + match verifier.data { + VerifierOption::Password(pw) => { + // Derive w0 and L from the password + let mut w0w1s: [u8; (2 * CRYPTO_W_SIZE_BYTES)] = [0; (2 * CRYPTO_W_SIZE_BYTES)]; + Spake2P::get_w0w1s(pw, verifier.count, &verifier.salt, &mut w0w1s); + self.crypto_spake2 = Some(crypto_spake2_new()?); - let w0s_len = w0w1s.len() / 2; - if let Some(crypto_spake2) = &mut self.crypto_spake2 { - crypto_spake2.set_w0_from_w0s(&w0w1s[0..w0s_len])?; - crypto_spake2.set_L(&w0w1s[w0s_len..])?; + let w0s_len = w0w1s.len() / 2; + if let Some(crypto_spake2) = &mut self.crypto_spake2 { + crypto_spake2.set_w0_from_w0s(&w0w1s[0..w0s_len])?; + crypto_spake2.set_L_from_w1s(&w0w1s[w0s_len..])?; + } + } + VerifierOption::Verifier(v) => { + // Extract w0 and L from the verifier + if v.len() != CRYPTO_GROUP_SIZE_BYTES + CRYPTO_PUBLIC_KEY_SIZE_BYTES { + error!("Verifier of invalid length"); + } + if let Some(crypto_spake2) = &mut self.crypto_spake2 { + crypto_spake2.set_w0(&v[0..CRYPTO_GROUP_SIZE_BYTES])?; + crypto_spake2.set_L(&v[CRYPTO_GROUP_SIZE_BYTES..])?; + } + } } - self.mode = Spake2Mode::Verifier(Spake2VerifierState::Init); Ok(()) }