Merge pull request #49 from nevi-me/crypto/rustcrypto
RustCrypto backed crypto
This commit is contained in:
		
						commit
						89c02e1c7e
					
				
					 10 changed files with 799 additions and 11 deletions
				
			
		
							
								
								
									
										22
									
								
								.github/workflows/test-linux-rustcrypto.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								.github/workflows/test-linux-rustcrypto.yml
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | |||
| name: Test-Linux-RustCrypto | ||||
| 
 | ||||
| on: | ||||
|   push: | ||||
|     branches: [ main ] | ||||
|   pull_request: | ||||
|     branches: [ main ] | ||||
| 
 | ||||
| env: | ||||
|   CARGO_TERM_COLOR: always | ||||
| 
 | ||||
| jobs: | ||||
|   build_and_test: | ||||
| 
 | ||||
|     runs-on: ubuntu-latest | ||||
| 
 | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - name: Build | ||||
|       run: cd matter; cargo build --verbose --no-default-features --features crypto_rustcrypto | ||||
|     - name: Run tests | ||||
|       run: cd matter; cargo test --verbose --no-default-features --features crypto_rustcrypto -- --test-threads=1 | ||||
|  | @ -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 } | ||||
| 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" | ||||
| 
 | ||||
|  |  | |||
|  | @ -39,7 +39,7 @@ use openssl::x509::{X509NameBuilder, X509ReqBuilder, X509}; | |||
| // We directly use the hmac crate here, there was a self-referential structure
 | ||||
| // problem while using OpenSSL's Signer
 | ||||
| // TODO: Use proper OpenSSL method for this
 | ||||
| use hmac::{Hmac, Mac, NewMac}; | ||||
| use hmac::{Hmac, Mac}; | ||||
| pub struct HmacSha256 { | ||||
|     ctx: Hmac<sha2::Sha256>, | ||||
| } | ||||
|  |  | |||
							
								
								
									
										338
									
								
								matter/src/crypto/crypto_rustcrypto.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										338
									
								
								matter/src/crypto/crypto_rustcrypto.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,338 @@ | |||
| /* | ||||
|  * | ||||
|  *    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::{Signature, SigningKey, VerifyingKey}, | ||||
|     AffinePoint, EncodedPoint, PublicKey, SecretKey, | ||||
| }; | ||||
| use sha2::Digest; | ||||
| use x509_cert::{ | ||||
|     attr::AttributeType, | ||||
|     der::{asn1::BitString, Any, Encode}, | ||||
|     name::RdnSequence, | ||||
|     request::CertReq, | ||||
|     spki::{AlgorithmIdentifier, SubjectPublicKeyInfoOwned}, | ||||
| }; | ||||
| 
 | ||||
| use crate::error::Error; | ||||
| 
 | ||||
| use super::CryptoKeyPair; | ||||
| 
 | ||||
| type HmacSha256I = hmac::Hmac<sha2::Sha256>; | ||||
| type AesCcm = Ccm<Aes128, U16, U13>; | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct Sha256 { | ||||
|     hasher: sha2::Sha256, | ||||
| } | ||||
| 
 | ||||
| impl Sha256 { | ||||
|     pub fn new() -> Result<Self, Error> { | ||||
|         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<Self, Error> { | ||||
|         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<Self, Error> { | ||||
|         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<Self, Error> { | ||||
|         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<Self, Error> { | ||||
|         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<usize, Error> { | ||||
|         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<usize, Error> { | ||||
|         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<usize, Error> { | ||||
|         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<usize, Error> { | ||||
|         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::<hmac::Hmac<sha2::Sha256>>(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::<sha2::Sha256>::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<usize, Error> { | ||||
|     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<usize, Error> { | ||||
|     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) | ||||
| } | ||||
|  | @ -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)] | ||||
|  |  | |||
|  | @ -99,6 +99,13 @@ impl From<mbedtls::Error> for Error { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(feature = "crypto_rustcrypto")] | ||||
| impl From<ccm::aead::Error> for Error { | ||||
|     fn from(_e: ccm::aead::Error) -> Self { | ||||
|         Self::Crypto | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<SystemTimeError> 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 {} | ||||
|  |  | |||
							
								
								
									
										392
									
								
								matter/src/secure_channel/crypto_rustcrypto.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										392
									
								
								matter/src/secure_channel/crypto_rustcrypto.rs
									
										
									
									
									
										Normal file
									
								
							|  | @ -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<Self, Error> { | ||||
|         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<p256::EncodedPoint, Error> { | ||||
|         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()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -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; | ||||
|  |  | |||
|  | @ -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<Box<dyn CryptoSpake2>, Error> { | |||
|     Ok(Box::new(CryptoEspMbedTls::new()?)) | ||||
| } | ||||
| 
 | ||||
| #[cfg(feature = "crypto_rustcrypto")] | ||||
| fn crypto_spake2_new() -> Result<Box<dyn CryptoSpake2>, 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, | ||||
|  |  | |||
|  | @ -8,6 +8,6 @@ edition = "2021" | |||
| proc-macro = true | ||||
| 
 | ||||
| [dependencies] | ||||
| syn = { version = "*", features = ["extra-traits"]} | ||||
| quote = "*" | ||||
| proc-macro2 = "*" | ||||
| syn = { version = "1", features = ["extra-traits"]} | ||||
| quote = "1" | ||||
| proc-macro2 = "1" | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue