From bab8eb812431e81c59c2bb9523dc0d076529b0b6 Mon Sep 17 00:00:00 2001 From: Wakahisa Date: Sun, 2 Apr 2023 13:07:36 +0200 Subject: [PATCH] Crypto backed by RustCrypto crates Almost all crates are no_std and don't require std. Only `x509-cert` does. Manually tested with the onoff example. --- matter/Cargo.toml | 22 +- matter/src/crypto/crypto_rustcrypto.rs | 334 +++++++++++++++ matter/src/crypto/mod.rs | 5 + matter/src/error.rs | 9 + .../src/secure_channel/crypto_rustcrypto.rs | 392 ++++++++++++++++++ matter/src/secure_channel/mod.rs | 2 + matter/src/secure_channel/spake2p.rs | 12 +- 7 files changed, 769 insertions(+), 7 deletions(-) create mode 100644 matter/src/crypto/crypto_rustcrypto.rs create mode 100644 matter/src/secure_channel/crypto_rustcrypto.rs diff --git a/matter/Cargo.toml b/matter/Cargo.toml index 5a6787f..431514a 100644 --- a/matter/Cargo.toml +++ b/matter/Cargo.toml @@ -19,6 +19,7 @@ default = ["crypto_mbedtls"] crypto_openssl = ["openssl", "foreign-types", "hmac", "sha2"] crypto_mbedtls = ["mbedtls"] crypto_esp_mbedtls = ["esp-idf-sys"] +crypto_rustcrypto = ["sha2", "hmac", "pbkdf2", "hkdf", "aes", "ccm", "p256", "elliptic-curve", "crypto-bigint", "x509-cert"] [dependencies] boxslab = { path = "../boxslab" } @@ -34,11 +35,6 @@ log = { version = "0.4.17", features = ["max_level_debug", "release_max_level_de env_logger = "0.10.0" rand = "0.8.5" esp-idf-sys = { version = "0.32", features = ["binstart"], optional = true } -openssl = { git = "https://github.com/sfackler/rust-openssl", optional = true } -foreign-types = { version = "0.3.2", optional = true } -sha2 = { version = "0.9.9", optional = true } -hmac = { version = "0.11.0", optional = true } -mbedtls = { git = "https://github.com/fortanix/rust-mbedtls", optional = true } subtle = "2.4.1" colored = "2.0.0" smol = "1.3.0" @@ -47,6 +43,22 @@ safemem = "0.3.3" chrono = { version = "0.4.23", default-features = false, features = ["clock", "std"] } async-channel = "1.8" +# crypto +openssl = { git = "https://github.com/sfackler/rust-openssl", optional = true } +foreign-types = { version = "0.3.2", optional = true } +mbedtls = { git = "https://github.com/fortanix/rust-mbedtls", optional = true, default-features = false, features = ["std", "aesni"] } +sha2 = { version = "0.10", default-features = false, optional = true } +hmac = { version = "0.12", optional = true } +pbkdf2 = { version = "0.12", optional = true } +hkdf = { version = "0.12", optional = true } +aes = { version = "0.8", optional = true } +ccm = { version = "0.5", default-features = false, features = ["alloc"], optional = true } +p256 = { version = "0.13.0", default-features = false, features = ["arithmetic", "ecdh", "ecdsa"], optional = true } +elliptic-curve = { version = "0.13.2", optional = true } +crypto-bigint = { version = "0.4", default-features = false, optional = true } +# Note: requires std +x509-cert = { version = "0.2.0", default-features = false, features = ["pem", "std"], optional = true } + # to compute the check digit verhoeff = "1" diff --git a/matter/src/crypto/crypto_rustcrypto.rs b/matter/src/crypto/crypto_rustcrypto.rs new file mode 100644 index 0000000..3713844 --- /dev/null +++ b/matter/src/crypto/crypto_rustcrypto.rs @@ -0,0 +1,334 @@ +/* + * + * Copyright (c) 2020-2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use std::convert::{TryFrom, TryInto}; + +use aes::Aes128; +use ccm::{ + aead::generic_array::GenericArray, + consts::{U13, U16}, + Ccm, +}; +use elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint}; +use hmac::Mac; +use log::error; +use p256::{ecdsa::{SigningKey, VerifyingKey, Signature}, SecretKey, PublicKey, EncodedPoint, AffinePoint}; +use sha2::Digest; +use x509_cert::{ + attr::AttributeType, + der::{asn1::BitString, Any, Encode}, + spki::{AlgorithmIdentifier, SubjectPublicKeyInfoOwned}, name::RdnSequence, request::CertReq, +}; + +use crate::error::Error; + +use super::CryptoKeyPair; + +type HmacSha256I = hmac::Hmac; +type AesCcm = Ccm; + +#[derive(Clone)] +pub struct Sha256 { + hasher: sha2::Sha256, +} + +impl Sha256 { + pub fn new() -> Result { + Ok(Self { + hasher: sha2::Sha256::new(), + }) + } + + pub fn update(&mut self, data: &[u8]) -> Result<(), Error> { + self.hasher.update(data); + Ok(()) + } + + pub fn finish(self, digest: &mut [u8]) -> Result<(), Error> { + let output = self.hasher.finalize(); + digest.copy_from_slice(output.as_slice()); + Ok(()) + } +} + +pub struct HmacSha256 { + inner: HmacSha256I, +} + +impl HmacSha256 { + pub fn new(key: &[u8]) -> Result { + Ok(Self { + inner: HmacSha256I::new_from_slice(key).map_err(|e| { + error!("Error creating HmacSha256 {:?}", e); + Error::TLSStack + })?, + }) + } + + pub fn update(&mut self, data: &[u8]) -> Result<(), Error> { + self.inner.update(data); + Ok(()) + } + + pub fn finish(self, out: &mut [u8]) -> Result<(), Error> { + let result = &self.inner.finalize().into_bytes()[..]; + out.clone_from_slice(result); + Ok(()) + } +} + +pub enum KeyType { + Private(SecretKey), + Public(PublicKey), +} + +pub struct KeyPair { + key: KeyType, +} + +impl KeyPair { + pub fn new() -> Result { + let mut rng = rand::thread_rng(); + let secret_key = SecretKey::random(&mut rng); + + Ok(Self { + key: KeyType::Private(secret_key), + }) + } + + pub fn new_from_components(pub_key: &[u8], priv_key: &[u8]) -> Result { + let secret_key = SecretKey::from_slice(priv_key).unwrap(); + let encoded_point = EncodedPoint::from_bytes(pub_key).unwrap(); + let public_key = PublicKey::from_encoded_point(&encoded_point).unwrap(); + assert_eq!(public_key, secret_key.public_key()); + + Ok(Self { + key: KeyType::Private(secret_key), + }) + } + + pub fn new_from_public(pub_key: &[u8]) -> Result { + let encoded_point = EncodedPoint::from_bytes(pub_key).unwrap(); + Ok(Self { + key: KeyType::Public(PublicKey::from_encoded_point(&encoded_point).unwrap()), + }) + } + + fn public_key_point(&self) -> AffinePoint { + match &self.key { + KeyType::Private(k) => *(k.public_key().as_affine()), + KeyType::Public(k) => *(k.as_affine()), + } + } + + fn private_key(&self) -> Result<&SecretKey, Error> { + match &self.key { + KeyType::Private(key) => Ok(key), + KeyType::Public(_) => Err(Error::Crypto), + } + } +} + +impl CryptoKeyPair for KeyPair { + fn get_private_key(&self, priv_key: &mut [u8]) -> Result { + match &self.key { + KeyType::Private(key) => { + let bytes = key.to_bytes(); + let slice = bytes.as_slice(); + let len = slice.len(); + priv_key.copy_from_slice(slice); + Ok(len) + } + KeyType::Public(_) => Err(Error::Crypto), + } + } + fn get_csr<'a>(&self, out_csr: &'a mut [u8]) -> Result<&'a [u8], Error> { + use p256::ecdsa::signature::Signer; + + let subject = + RdnSequence(vec![x509_cert::name::RelativeDistinguishedName( + vec![x509_cert::attr::AttributeTypeAndValue { + // Organization name: http://www.oid-info.com/get/2.5.4.10 + oid: x509_cert::attr::AttributeType::new_unwrap("2.5.4.10"), + value: x509_cert::attr::AttributeValue::new( + x509_cert::der::Tag::Utf8String, + "CSR".as_bytes(), + ) + .unwrap(), + }] + .try_into() + .unwrap(), + )]); + let mut pubkey = [0; 65]; + self.get_public_key(&mut pubkey).unwrap(); + let info = x509_cert::request::CertReqInfo { + version: x509_cert::request::Version::V1, + subject, + public_key: SubjectPublicKeyInfoOwned { + algorithm: AlgorithmIdentifier { + // ecPublicKey(1) http://www.oid-info.com/get/1.2.840.10045.2.1 + oid: AttributeType::new_unwrap("1.2.840.10045.2.1"), + parameters: Some( + Any::new( + x509_cert::der::Tag::ObjectIdentifier, + // prime256v1 http://www.oid-info.com/get/1.2.840.10045.3.1.7 + AttributeType::new_unwrap("1.2.840.10045.3.1.7").as_bytes(), + ) + .unwrap(), + ), + }, + subject_public_key: BitString::from_bytes(&pubkey).unwrap(), + }, + attributes: Default::default(), + }; + let mut message = vec![]; + info.encode(&mut message).unwrap(); + + // Can't use self.sign_msg as the signature has to be in DER format + let private_key = self.private_key()?; + let signing_key = SigningKey::from(private_key); + let sig: Signature = signing_key.sign(&message); + let to_der = sig.to_der(); + let signature = to_der.as_bytes(); + + let cert = CertReq { + info, + algorithm: AlgorithmIdentifier { + // ecdsa-with-SHA256(2) http://www.oid-info.com/get/1.2.840.10045.4.3.2 + oid: AttributeType::new_unwrap("1.2.840.10045.4.3.2"), + parameters: None, + }, + signature: BitString::from_bytes(signature).unwrap(), + }; + let out = cert.to_der().unwrap(); + let a = &mut out_csr[0..out.len()]; + a.copy_from_slice(&out); + + Ok(a) + } + fn get_public_key(&self, pub_key: &mut [u8]) -> Result { + let point = self.public_key_point().to_encoded_point(false); + let bytes = point.as_bytes(); + let len = bytes.len(); + pub_key[..len].copy_from_slice(bytes); + Ok(len) + } + fn derive_secret(self, peer_pub_key: &[u8], secret: &mut [u8]) -> Result { + let encoded_point = EncodedPoint::from_bytes(peer_pub_key).unwrap(); + let peer_pubkey = PublicKey::from_encoded_point(&encoded_point).unwrap(); + let private_key = self.private_key()?; + let shared_secret = elliptic_curve::ecdh::diffie_hellman( + private_key.to_nonzero_scalar(), + peer_pubkey.as_affine(), + ); + let bytes = shared_secret.raw_secret_bytes(); + let bytes = bytes.as_slice(); + let len = bytes.len(); + assert_eq!(secret.len(), len); + secret.copy_from_slice(bytes); + + Ok(len) + } + fn sign_msg(&self, msg: &[u8], signature: &mut [u8]) -> Result { + use p256::ecdsa::signature::Signer; + + if signature.len() < super::EC_SIGNATURE_LEN_BYTES { + return Err(Error::NoSpace); + } + + match &self.key { + KeyType::Private(k) => { + let signing_key = SigningKey::from(k); + let sig: Signature = signing_key.sign(msg); + let bytes = sig.to_bytes().to_vec(); + let len = bytes.len(); + signature[..len].copy_from_slice(&bytes); + Ok(len) + } + KeyType::Public(_) => todo!(), + } + } + fn verify_msg(&self, msg: &[u8], signature: &[u8]) -> Result<(), Error> { + use p256::ecdsa::signature::Verifier; + + let verifying_key = VerifyingKey::from_affine(self.public_key_point()).unwrap(); + let signature = Signature::try_from(signature).unwrap(); + + verifying_key + .verify(msg, &signature) + .map_err(|_| Error::InvalidSignature)?; + + Ok(()) + } +} + +pub fn pbkdf2_hmac(pass: &[u8], iter: usize, salt: &[u8], key: &mut [u8]) -> Result<(), Error> { + pbkdf2::pbkdf2::>(pass, salt, iter as u32, key).unwrap(); + + Ok(()) +} + +pub fn hkdf_sha256(salt: &[u8], ikm: &[u8], info: &[u8], key: &mut [u8]) -> Result<(), Error> { + hkdf::Hkdf::::new(Some(salt), ikm) + .expand(info, key) + .map_err(|e| { + error!("Error with hkdf_sha256 {:?}", e); + Error::TLSStack + }) +} + +// TODO: add tests and check against mbedtls and openssl +pub fn encrypt_in_place( + key: &[u8], + nonce: &[u8], + ad: &[u8], + data: &mut [u8], + data_len: usize, +) -> Result { + use ccm::{AeadInPlace, KeyInit}; + + let key = GenericArray::from_slice(key); + let nonce = GenericArray::from_slice(nonce); + let cipher = AesCcm::new(key); + // This is probably incorrect + let mut buffer = data[0..data_len].to_vec(); + cipher.encrypt_in_place(nonce, ad, &mut buffer)?; + let len = buffer.len(); + data.clone_from_slice(&buffer[..]); + + Ok(len) +} + +pub fn decrypt_in_place( + key: &[u8], + nonce: &[u8], + ad: &[u8], + data: &mut [u8], +) -> Result { + use ccm::{AeadInPlace, KeyInit}; + + let key = GenericArray::from_slice(key); + let nonce = GenericArray::from_slice(nonce); + let cipher = AesCcm::new(key); + // This is probably incorrect + let mut buffer = data.to_vec(); + cipher.decrypt_in_place(nonce, ad, &mut buffer)?; + let len = buffer.len(); + data[..len].copy_from_slice(&buffer[..]); + + Ok(len) +} diff --git a/matter/src/crypto/mod.rs b/matter/src/crypto/mod.rs index aa707f7..2473fb0 100644 --- a/matter/src/crypto/mod.rs +++ b/matter/src/crypto/mod.rs @@ -60,6 +60,11 @@ mod crypto_openssl; #[cfg(feature = "crypto_openssl")] pub use self::crypto_openssl::*; +#[cfg(feature = "crypto_rustcrypto")] +mod crypto_rustcrypto; +#[cfg(feature = "crypto_rustcrypto")] +pub use self::crypto_rustcrypto::*; + pub mod crypto_dummy; #[cfg(test)] diff --git a/matter/src/error.rs b/matter/src/error.rs index ba07d29..b044959 100644 --- a/matter/src/error.rs +++ b/matter/src/error.rs @@ -99,6 +99,13 @@ impl From for Error { } } +#[cfg(feature = "crypto_rustcrypto")] +impl From for Error { + fn from(_e: ccm::aead::Error) -> Self { + Self::Crypto + } +} + impl From for Error { fn from(_e: SystemTimeError) -> Self { Self::SysTimeFail @@ -136,3 +143,5 @@ impl fmt::Display for Error { write!(f, "{:?}", self) } } + +impl std::error::Error for Error {} diff --git a/matter/src/secure_channel/crypto_rustcrypto.rs b/matter/src/secure_channel/crypto_rustcrypto.rs new file mode 100644 index 0000000..e8b8014 --- /dev/null +++ b/matter/src/secure_channel/crypto_rustcrypto.rs @@ -0,0 +1,392 @@ +/* + * + * Copyright (c) 2020-2022 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use crypto_bigint::Encoding; +use crypto_bigint::U384; +use elliptic_curve::ops::*; +use elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint}; +use elliptic_curve::Field; +use elliptic_curve::PrimeField; +use sha2::Digest; + +use crate::error::Error; + +use super::crypto::CryptoSpake2; + +const MATTER_M_BIN: [u8; 65] = [ + 0x04, 0x88, 0x6e, 0x2f, 0x97, 0xac, 0xe4, 0x6e, 0x55, 0xba, 0x9d, 0xd7, 0x24, 0x25, 0x79, 0xf2, + 0x99, 0x3b, 0x64, 0xe1, 0x6e, 0xf3, 0xdc, 0xab, 0x95, 0xaf, 0xd4, 0x97, 0x33, 0x3d, 0x8f, 0xa1, + 0x2f, 0x5f, 0xf3, 0x55, 0x16, 0x3e, 0x43, 0xce, 0x22, 0x4e, 0x0b, 0x0e, 0x65, 0xff, 0x02, 0xac, + 0x8e, 0x5c, 0x7b, 0xe0, 0x94, 0x19, 0xc7, 0x85, 0xe0, 0xca, 0x54, 0x7d, 0x55, 0xa1, 0x2e, 0x2d, + 0x20, +]; +const MATTER_N_BIN: [u8; 65] = [ + 0x04, 0xd8, 0xbb, 0xd6, 0xc6, 0x39, 0xc6, 0x29, 0x37, 0xb0, 0x4d, 0x99, 0x7f, 0x38, 0xc3, 0x77, + 0x07, 0x19, 0xc6, 0x29, 0xd7, 0x01, 0x4d, 0x49, 0xa2, 0x4b, 0x4f, 0x98, 0xba, 0xa1, 0x29, 0x2b, + 0x49, 0x07, 0xd6, 0x0a, 0xa6, 0xbf, 0xad, 0xe4, 0x50, 0x08, 0xa6, 0x36, 0x33, 0x7f, 0x51, 0x68, + 0xc6, 0x4d, 0x9b, 0xd3, 0x60, 0x34, 0x80, 0x8c, 0xd5, 0x64, 0x49, 0x0b, 0x1e, 0x65, 0x6e, 0xdb, + 0xe7, +]; + +#[allow(non_snake_case)] + +pub struct CryptoRustCrypto { + xy: p256::Scalar, + w0: p256::Scalar, + w1: p256::Scalar, + M: p256::EncodedPoint, + N: p256::EncodedPoint, + L: p256::EncodedPoint, + pB: p256::EncodedPoint, +} + +impl CryptoSpake2 for CryptoRustCrypto { + #[allow(non_snake_case)] + fn new() -> Result { + let M = p256::EncodedPoint::from_bytes(MATTER_M_BIN).unwrap(); + let N = p256::EncodedPoint::from_bytes(MATTER_N_BIN).unwrap(); + let L = p256::EncodedPoint::default(); + let pB = p256::EncodedPoint::default(); + + Ok(CryptoRustCrypto { + xy: p256::Scalar::ZERO, + w0: p256::Scalar::ZERO, + w1: p256::Scalar::ZERO, + M, + N, + L, + pB, + }) + } + + // Computes w0 from w0s respectively + fn set_w0_from_w0s(&mut self, w0s: &[u8]) -> Result<(), Error> { + // From the Matter Spec, + // w0 = w0s mod p + // where p is the order of the curve + let operand: [u8; 32] = [ + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, + 0xfc, 0x63, 0x25, 0x51, + ]; + let mut expanded = [0u8; 384 / 8]; + expanded[16..].copy_from_slice(&operand); + let big_operand = U384::from_be_slice(&expanded); + let mut expanded = [0u8; 384 / 8]; + expanded[8..].copy_from_slice(w0s); + let big_w0 = U384::from_be_slice(&expanded); + let w0_res = big_w0.reduce(&big_operand).unwrap(); + let mut w0_out = [0u8; 32]; + w0_out.copy_from_slice(&w0_res.to_be_bytes()[16..]); + + let w0s = p256::Scalar::from_repr( + *elliptic_curve::generic_array::GenericArray::from_slice(&w0_out), + ) + .unwrap(); + // Scalar is module the curve's order by definition, no further op needed + self.w0 = w0s; + + Ok(()) + } + + fn set_w1_from_w1s(&mut self, w1s: &[u8]) -> Result<(), Error> { + // From the Matter Spec, + // w1 = w1s mod p + // where p is the order of the curve + let operand: [u8; 32] = [ + 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, + 0xfc, 0x63, 0x25, 0x51, + ]; + let mut expanded = [0u8; 384 / 8]; + expanded[16..].copy_from_slice(&operand); + let big_operand = U384::from_be_slice(&expanded); + let mut expanded = [0u8; 384 / 8]; + expanded[8..].copy_from_slice(w1s); + let big_w1 = U384::from_be_slice(&expanded); + let w1_res = big_w1.reduce(&big_operand).unwrap(); + let mut w1_out = [0u8; 32]; + w1_out.copy_from_slice(&w1_res.to_be_bytes()[16..]); + + let w1s = p256::Scalar::from_repr( + *elliptic_curve::generic_array::GenericArray::from_slice(&w1_out), + ) + .unwrap(); + // Scalar is module the curve's order by definition, no further op needed + self.w1 = w1s; + + Ok(()) + } + + fn set_w0(&mut self, w0: &[u8]) -> Result<(), Error> { + self.w0 = + p256::Scalar::from_repr(*elliptic_curve::generic_array::GenericArray::from_slice(w0)) + .unwrap(); + Ok(()) + } + + fn set_w1(&mut self, w1: &[u8]) -> Result<(), Error> { + self.w1 = + p256::Scalar::from_repr(*elliptic_curve::generic_array::GenericArray::from_slice(w1)) + .unwrap(); + Ok(()) + } + + #[allow(non_snake_case)] + #[allow(dead_code)] + fn set_L(&mut self, l: &[u8]) -> Result<(), Error> { + self.L = p256::EncodedPoint::from_bytes(l).unwrap(); + Ok(()) + } + + 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 + self.set_w1_from_w1s(w1s)?; + self.L = (p256::AffinePoint::GENERATOR * self.w1).to_encoded_point(false); + Ok(()) + } + + #[allow(non_snake_case)] + fn get_pB(&mut self, pB: &mut [u8]) -> Result<(), Error> { + // From the SPAKE2+ spec (https://datatracker.ietf.org/doc/draft-bar-cfrg-spake2plus/) + // for y + // - select random y between 0 to p + // - Y = y*P + w0*N + // - pB = Y + let mut rng = rand::thread_rng(); + self.xy = p256::Scalar::random(&mut rng); + + let P = p256::AffinePoint::GENERATOR; + let N = p256::AffinePoint::from_encoded_point(&self.N).unwrap(); + self.pB = Self::do_add_mul(P, self.xy, N, self.w0)?; + let pB_internal = self.pB.as_bytes(); + pB.copy_from_slice(pB_internal); + + Ok(()) + } + + #[allow(non_snake_case)] + fn get_TT_as_verifier( + &mut self, + context: &[u8], + pA: &[u8], + pB: &[u8], + out: &mut [u8], + ) -> Result<(), Error> { + let mut TT = sha2::Sha256::new(); + // Context + Self::add_to_tt(&mut TT, context)?; + // 2 empty identifiers + Self::add_to_tt(&mut TT, &[])?; + Self::add_to_tt(&mut TT, &[])?; + // M + Self::add_to_tt(&mut TT, &MATTER_M_BIN)?; + // N + Self::add_to_tt(&mut TT, &MATTER_N_BIN)?; + // X = pA + Self::add_to_tt(&mut TT, pA)?; + // Y = pB + Self::add_to_tt(&mut TT, pB)?; + + let X = p256::EncodedPoint::from_bytes(pA).unwrap(); + let X = p256::AffinePoint::from_encoded_point(&X).unwrap(); + let L = p256::AffinePoint::from_encoded_point(&self.L).unwrap(); + let M = p256::AffinePoint::from_encoded_point(&self.M).unwrap(); + let (Z, V) = Self::get_ZV_as_verifier(self.w0, L, M, X, self.xy)?; + + // Z + Self::add_to_tt(&mut TT, Z.as_bytes())?; + // V + Self::add_to_tt(&mut TT, V.as_bytes())?; + // w0 + Self::add_to_tt(&mut TT, self.w0.to_bytes().to_vec().as_ref())?; + + let h = TT.finalize(); + out.copy_from_slice(h.as_slice()); + + Ok(()) + } +} + +impl CryptoRustCrypto { + fn add_to_tt(tt: &mut sha2::Sha256, buf: &[u8]) -> Result<(), Error> { + tt.update(buf.len().to_le_bytes()); + if !buf.is_empty() { + tt.update(buf); + } + Ok(()) + } + + #[inline(always)] + fn do_add_mul( + a: p256::AffinePoint, + b: p256::Scalar, + c: p256::AffinePoint, + d: p256::Scalar, + ) -> Result { + Ok(((a * b) + (c * d)).to_encoded_point(false)) + } + + #[inline(always)] + #[allow(non_snake_case)] + #[allow(dead_code)] + fn get_ZV_as_prover( + w0: p256::Scalar, + w1: p256::Scalar, + N: p256::AffinePoint, + Y: p256::AffinePoint, + x: p256::Scalar, + ) -> Result<(p256::EncodedPoint, p256::EncodedPoint), Error> { + // As per the RFC, the operation here is: + // Z = h*x*(Y - w0*N) + // V = h*w1*(Y - w0*N) + + // We will follow the same sequence as in C++ SDK, under the assumption + // that the same sequence works for all embedded platforms. So the step + // of operations is: + // tmp = x*w0 + // Z = x*Y + tmp*N (N is inverted to get the 'negative' effect) + // Z = h*Z (cofactor Mul) + + let mut tmp = x * w0; + let N_neg = N.neg(); + let Z = CryptoRustCrypto::do_add_mul(Y, x, N_neg, tmp)?; + // Cofactor for P256 is 1, so that is a No-Op + + tmp = w1 * w0; + let V = CryptoRustCrypto::do_add_mul(Y, w1, N_neg, tmp)?; + Ok((Z, V)) + } + + #[inline(always)] + #[allow(non_snake_case)] + #[allow(dead_code)] + fn get_ZV_as_verifier( + w0: p256::Scalar, + L: p256::AffinePoint, + M: p256::AffinePoint, + X: p256::AffinePoint, + y: p256::Scalar, + ) -> Result<(p256::EncodedPoint, p256::EncodedPoint), Error> { + // As per the RFC, the operation here is: + // Z = h*y*(X - w0*M) + // V = h*y*L + + // We will follow the same sequence as in C++ SDK, under the assumption + // that the same sequence works for all embedded platforms. So the step + // of operations is: + // tmp = y*w0 + // Z = y*X + tmp*M (M is inverted to get the 'negative' effect) + // Z = h*Z (cofactor Mul) + + let tmp = y * w0; + let M_neg = M.neg(); + let Z = CryptoRustCrypto::do_add_mul(X, y, M_neg, tmp)?; + // Cofactor for P256 is 1, so that is a No-Op + let V = (L * y).to_encoded_point(false); + Ok((Z, V)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use elliptic_curve::sec1::FromEncodedPoint; + + use crate::secure_channel::crypto::CryptoSpake2; + use crate::secure_channel::spake2p_test_vectors::test_vectors::*; + + #[test] + #[allow(non_snake_case)] + fn test_get_X() { + for t in RFC_T { + let mut c = CryptoRustCrypto::new().unwrap(); + let x = p256::Scalar::from_repr( + *elliptic_curve::generic_array::GenericArray::from_slice(&t.x), + ) + .unwrap(); + c.set_w0(&t.w0).unwrap(); + let P = p256::AffinePoint::GENERATOR; + let M = p256::AffinePoint::from_encoded_point(&c.M).unwrap(); + let r: p256::EncodedPoint = CryptoRustCrypto::do_add_mul(P, x, M, c.w0).unwrap(); + assert_eq!(&t.X, r.as_bytes()); + } + } + + #[test] + #[allow(non_snake_case)] + fn test_get_Y() { + for t in RFC_T { + let mut c = CryptoRustCrypto::new().unwrap(); + let y = p256::Scalar::from_repr( + *elliptic_curve::generic_array::GenericArray::from_slice(&t.y), + ) + .unwrap(); + c.set_w0(&t.w0).unwrap(); + let P = p256::AffinePoint::GENERATOR; + let N = p256::AffinePoint::from_encoded_point(&c.N).unwrap(); + let r = CryptoRustCrypto::do_add_mul(P, y, N, c.w0).unwrap(); + assert_eq!(&t.Y, r.as_bytes()); + } + } + + #[test] + #[allow(non_snake_case)] + fn test_get_ZV_as_prover() { + for t in RFC_T { + let mut c = CryptoRustCrypto::new().unwrap(); + let x = p256::Scalar::from_repr( + *elliptic_curve::generic_array::GenericArray::from_slice(&t.x), + ) + .unwrap(); + c.set_w0(&t.w0).unwrap(); + c.set_w1(&t.w1).unwrap(); + let Y = p256::EncodedPoint::from_bytes(t.Y).unwrap(); + let Y = p256::AffinePoint::from_encoded_point(&Y).unwrap(); + let N = p256::AffinePoint::from_encoded_point(&c.N).unwrap(); + let (Z, V) = CryptoRustCrypto::get_ZV_as_prover(c.w0, c.w1, N, Y, x).unwrap(); + + assert_eq!(&t.Z, Z.as_bytes()); + assert_eq!(&t.V, V.as_bytes()); + } + } + + #[test] + #[allow(non_snake_case)] + fn test_get_ZV_as_verifier() { + for t in RFC_T { + let mut c = CryptoRustCrypto::new().unwrap(); + let y = p256::Scalar::from_repr( + *elliptic_curve::generic_array::GenericArray::from_slice(&t.y), + ) + .unwrap(); + c.set_w0(&t.w0).unwrap(); + let X = p256::EncodedPoint::from_bytes(t.X).unwrap(); + let X = p256::AffinePoint::from_encoded_point(&X).unwrap(); + let L = p256::EncodedPoint::from_bytes(t.L).unwrap(); + let L = p256::AffinePoint::from_encoded_point(&L).unwrap(); + let M = p256::AffinePoint::from_encoded_point(&c.M).unwrap(); + let (Z, V) = CryptoRustCrypto::get_ZV_as_verifier(c.w0, L, M, X, y).unwrap(); + + assert_eq!(&t.Z, Z.as_bytes()); + assert_eq!(&t.V, V.as_bytes()); + } + } +} diff --git a/matter/src/secure_channel/mod.rs b/matter/src/secure_channel/mod.rs index 5a3d5b8..9328b25 100644 --- a/matter/src/secure_channel/mod.rs +++ b/matter/src/secure_channel/mod.rs @@ -23,6 +23,8 @@ pub mod crypto_esp_mbedtls; pub mod crypto_mbedtls; #[cfg(feature = "crypto_openssl")] pub mod crypto_openssl; +#[cfg(feature = "crypto_rustcrypto")] +pub mod crypto_rustcrypto; pub mod core; pub mod crypto; diff --git a/matter/src/secure_channel/spake2p.rs b/matter/src/secure_channel/spake2p.rs index ad0cac5..335d465 100644 --- a/matter/src/secure_channel/spake2p.rs +++ b/matter/src/secure_channel/spake2p.rs @@ -38,6 +38,9 @@ use super::crypto_mbedtls::CryptoMbedTLS; #[cfg(feature = "crypto_esp_mbedtls")] use super::crypto_esp_mbedtls::CryptoEspMbedTls; +#[cfg(feature = "crypto_rustcrypto")] +use super::crypto_rustcrypto::CryptoRustCrypto; + use super::{common::SCStatusCodes, crypto::CryptoSpake2}; // This file handle Spake2+ specific instructions. In itself, this file is @@ -99,6 +102,11 @@ fn crypto_spake2_new() -> Result, Error> { Ok(Box::new(CryptoEspMbedTls::new()?)) } +#[cfg(feature = "crypto_rustcrypto")] +fn crypto_spake2_new() -> Result, Error> { + Ok(Box::new(CryptoRustCrypto::new()?)) +} + impl Default for Spake2P { fn default() -> Self { Self::new() @@ -190,7 +198,7 @@ impl Spake2P { 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)]; + 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); let w0s_len = w0w1s.len() / 2; @@ -309,7 +317,7 @@ mod tests { 0x4, 0xa1, 0xd2, 0xc6, 0x11, 0xf0, 0xbd, 0x36, 0x78, 0x67, 0x79, 0x7b, 0xfe, 0x82, 0x36, 0x0, ]; - let mut w0w1s: [u8; (2 * CRYPTO_W_SIZE_BYTES)] = [0; (2 * CRYPTO_W_SIZE_BYTES)]; + let mut w0w1s: [u8; 2 * CRYPTO_W_SIZE_BYTES] = [0; 2 * CRYPTO_W_SIZE_BYTES]; Spake2P::get_w0w1s(123456, 2000, &salt, &mut w0w1s); assert_eq!( w0w1s,