Skip to content

Commit bab8eb8

Browse files
committed
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.
1 parent 7bde97d commit bab8eb8

File tree

7 files changed

+769
-7
lines changed

7 files changed

+769
-7
lines changed

matter/Cargo.toml

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ default = ["crypto_mbedtls"]
1919
crypto_openssl = ["openssl", "foreign-types", "hmac", "sha2"]
2020
crypto_mbedtls = ["mbedtls"]
2121
crypto_esp_mbedtls = ["esp-idf-sys"]
22+
crypto_rustcrypto = ["sha2", "hmac", "pbkdf2", "hkdf", "aes", "ccm", "p256", "elliptic-curve", "crypto-bigint", "x509-cert"]
2223

2324
[dependencies]
2425
boxslab = { path = "../boxslab" }
@@ -34,11 +35,6 @@ log = { version = "0.4.17", features = ["max_level_debug", "release_max_level_de
3435
env_logger = "0.10.0"
3536
rand = "0.8.5"
3637
esp-idf-sys = { version = "0.32", features = ["binstart"], optional = true }
37-
openssl = { git = "https://github.com/sfackler/rust-openssl", optional = true }
38-
foreign-types = { version = "0.3.2", optional = true }
39-
sha2 = { version = "0.9.9", optional = true }
40-
hmac = { version = "0.11.0", optional = true }
41-
mbedtls = { git = "https://github.com/fortanix/rust-mbedtls", optional = true }
4238
subtle = "2.4.1"
4339
colored = "2.0.0"
4440
smol = "1.3.0"
@@ -47,6 +43,22 @@ safemem = "0.3.3"
4743
chrono = { version = "0.4.23", default-features = false, features = ["clock", "std"] }
4844
async-channel = "1.8"
4945

46+
# crypto
47+
openssl = { git = "https://github.com/sfackler/rust-openssl", optional = true }
48+
foreign-types = { version = "0.3.2", optional = true }
49+
mbedtls = { git = "https://github.com/fortanix/rust-mbedtls", optional = true, default-features = false, features = ["std", "aesni"] }
50+
sha2 = { version = "0.10", default-features = false, optional = true }
51+
hmac = { version = "0.12", optional = true }
52+
pbkdf2 = { version = "0.12", optional = true }
53+
hkdf = { version = "0.12", optional = true }
54+
aes = { version = "0.8", optional = true }
55+
ccm = { version = "0.5", default-features = false, features = ["alloc"], optional = true }
56+
p256 = { version = "0.13.0", default-features = false, features = ["arithmetic", "ecdh", "ecdsa"], optional = true }
57+
elliptic-curve = { version = "0.13.2", optional = true }
58+
crypto-bigint = { version = "0.4", default-features = false, optional = true }
59+
# Note: requires std
60+
x509-cert = { version = "0.2.0", default-features = false, features = ["pem", "std"], optional = true }
61+
5062
# to compute the check digit
5163
verhoeff = "1"
5264

Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
/*
2+
*
3+
* Copyright (c) 2020-2022 Project CHIP Authors
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
use std::convert::{TryFrom, TryInto};
19+
20+
use aes::Aes128;
21+
use ccm::{
22+
aead::generic_array::GenericArray,
23+
consts::{U13, U16},
24+
Ccm,
25+
};
26+
use elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint};
27+
use hmac::Mac;
28+
use log::error;
29+
use p256::{ecdsa::{SigningKey, VerifyingKey, Signature}, SecretKey, PublicKey, EncodedPoint, AffinePoint};
30+
use sha2::Digest;
31+
use x509_cert::{
32+
attr::AttributeType,
33+
der::{asn1::BitString, Any, Encode},
34+
spki::{AlgorithmIdentifier, SubjectPublicKeyInfoOwned}, name::RdnSequence, request::CertReq,
35+
};
36+
37+
use crate::error::Error;
38+
39+
use super::CryptoKeyPair;
40+
41+
type HmacSha256I = hmac::Hmac<sha2::Sha256>;
42+
type AesCcm = Ccm<Aes128, U16, U13>;
43+
44+
#[derive(Clone)]
45+
pub struct Sha256 {
46+
hasher: sha2::Sha256,
47+
}
48+
49+
impl Sha256 {
50+
pub fn new() -> Result<Self, Error> {
51+
Ok(Self {
52+
hasher: sha2::Sha256::new(),
53+
})
54+
}
55+
56+
pub fn update(&mut self, data: &[u8]) -> Result<(), Error> {
57+
self.hasher.update(data);
58+
Ok(())
59+
}
60+
61+
pub fn finish(self, digest: &mut [u8]) -> Result<(), Error> {
62+
let output = self.hasher.finalize();
63+
digest.copy_from_slice(output.as_slice());
64+
Ok(())
65+
}
66+
}
67+
68+
pub struct HmacSha256 {
69+
inner: HmacSha256I,
70+
}
71+
72+
impl HmacSha256 {
73+
pub fn new(key: &[u8]) -> Result<Self, Error> {
74+
Ok(Self {
75+
inner: HmacSha256I::new_from_slice(key).map_err(|e| {
76+
error!("Error creating HmacSha256 {:?}", e);
77+
Error::TLSStack
78+
})?,
79+
})
80+
}
81+
82+
pub fn update(&mut self, data: &[u8]) -> Result<(), Error> {
83+
self.inner.update(data);
84+
Ok(())
85+
}
86+
87+
pub fn finish(self, out: &mut [u8]) -> Result<(), Error> {
88+
let result = &self.inner.finalize().into_bytes()[..];
89+
out.clone_from_slice(result);
90+
Ok(())
91+
}
92+
}
93+
94+
pub enum KeyType {
95+
Private(SecretKey),
96+
Public(PublicKey),
97+
}
98+
99+
pub struct KeyPair {
100+
key: KeyType,
101+
}
102+
103+
impl KeyPair {
104+
pub fn new() -> Result<Self, Error> {
105+
let mut rng = rand::thread_rng();
106+
let secret_key = SecretKey::random(&mut rng);
107+
108+
Ok(Self {
109+
key: KeyType::Private(secret_key),
110+
})
111+
}
112+
113+
pub fn new_from_components(pub_key: &[u8], priv_key: &[u8]) -> Result<Self, Error> {
114+
let secret_key = SecretKey::from_slice(priv_key).unwrap();
115+
let encoded_point = EncodedPoint::from_bytes(pub_key).unwrap();
116+
let public_key = PublicKey::from_encoded_point(&encoded_point).unwrap();
117+
assert_eq!(public_key, secret_key.public_key());
118+
119+
Ok(Self {
120+
key: KeyType::Private(secret_key),
121+
})
122+
}
123+
124+
pub fn new_from_public(pub_key: &[u8]) -> Result<Self, Error> {
125+
let encoded_point = EncodedPoint::from_bytes(pub_key).unwrap();
126+
Ok(Self {
127+
key: KeyType::Public(PublicKey::from_encoded_point(&encoded_point).unwrap()),
128+
})
129+
}
130+
131+
fn public_key_point(&self) -> AffinePoint {
132+
match &self.key {
133+
KeyType::Private(k) => *(k.public_key().as_affine()),
134+
KeyType::Public(k) => *(k.as_affine()),
135+
}
136+
}
137+
138+
fn private_key(&self) -> Result<&SecretKey, Error> {
139+
match &self.key {
140+
KeyType::Private(key) => Ok(key),
141+
KeyType::Public(_) => Err(Error::Crypto),
142+
}
143+
}
144+
}
145+
146+
impl CryptoKeyPair for KeyPair {
147+
fn get_private_key(&self, priv_key: &mut [u8]) -> Result<usize, Error> {
148+
match &self.key {
149+
KeyType::Private(key) => {
150+
let bytes = key.to_bytes();
151+
let slice = bytes.as_slice();
152+
let len = slice.len();
153+
priv_key.copy_from_slice(slice);
154+
Ok(len)
155+
}
156+
KeyType::Public(_) => Err(Error::Crypto),
157+
}
158+
}
159+
fn get_csr<'a>(&self, out_csr: &'a mut [u8]) -> Result<&'a [u8], Error> {
160+
use p256::ecdsa::signature::Signer;
161+
162+
let subject =
163+
RdnSequence(vec![x509_cert::name::RelativeDistinguishedName(
164+
vec![x509_cert::attr::AttributeTypeAndValue {
165+
// Organization name: http://www.oid-info.com/get/2.5.4.10
166+
oid: x509_cert::attr::AttributeType::new_unwrap("2.5.4.10"),
167+
value: x509_cert::attr::AttributeValue::new(
168+
x509_cert::der::Tag::Utf8String,
169+
"CSR".as_bytes(),
170+
)
171+
.unwrap(),
172+
}]
173+
.try_into()
174+
.unwrap(),
175+
)]);
176+
let mut pubkey = [0; 65];
177+
self.get_public_key(&mut pubkey).unwrap();
178+
let info = x509_cert::request::CertReqInfo {
179+
version: x509_cert::request::Version::V1,
180+
subject,
181+
public_key: SubjectPublicKeyInfoOwned {
182+
algorithm: AlgorithmIdentifier {
183+
// ecPublicKey(1) http://www.oid-info.com/get/1.2.840.10045.2.1
184+
oid: AttributeType::new_unwrap("1.2.840.10045.2.1"),
185+
parameters: Some(
186+
Any::new(
187+
x509_cert::der::Tag::ObjectIdentifier,
188+
// prime256v1 http://www.oid-info.com/get/1.2.840.10045.3.1.7
189+
AttributeType::new_unwrap("1.2.840.10045.3.1.7").as_bytes(),
190+
)
191+
.unwrap(),
192+
),
193+
},
194+
subject_public_key: BitString::from_bytes(&pubkey).unwrap(),
195+
},
196+
attributes: Default::default(),
197+
};
198+
let mut message = vec![];
199+
info.encode(&mut message).unwrap();
200+
201+
// Can't use self.sign_msg as the signature has to be in DER format
202+
let private_key = self.private_key()?;
203+
let signing_key = SigningKey::from(private_key);
204+
let sig: Signature = signing_key.sign(&message);
205+
let to_der = sig.to_der();
206+
let signature = to_der.as_bytes();
207+
208+
let cert = CertReq {
209+
info,
210+
algorithm: AlgorithmIdentifier {
211+
// ecdsa-with-SHA256(2) http://www.oid-info.com/get/1.2.840.10045.4.3.2
212+
oid: AttributeType::new_unwrap("1.2.840.10045.4.3.2"),
213+
parameters: None,
214+
},
215+
signature: BitString::from_bytes(signature).unwrap(),
216+
};
217+
let out = cert.to_der().unwrap();
218+
let a = &mut out_csr[0..out.len()];
219+
a.copy_from_slice(&out);
220+
221+
Ok(a)
222+
}
223+
fn get_public_key(&self, pub_key: &mut [u8]) -> Result<usize, Error> {
224+
let point = self.public_key_point().to_encoded_point(false);
225+
let bytes = point.as_bytes();
226+
let len = bytes.len();
227+
pub_key[..len].copy_from_slice(bytes);
228+
Ok(len)
229+
}
230+
fn derive_secret(self, peer_pub_key: &[u8], secret: &mut [u8]) -> Result<usize, Error> {
231+
let encoded_point = EncodedPoint::from_bytes(peer_pub_key).unwrap();
232+
let peer_pubkey = PublicKey::from_encoded_point(&encoded_point).unwrap();
233+
let private_key = self.private_key()?;
234+
let shared_secret = elliptic_curve::ecdh::diffie_hellman(
235+
private_key.to_nonzero_scalar(),
236+
peer_pubkey.as_affine(),
237+
);
238+
let bytes = shared_secret.raw_secret_bytes();
239+
let bytes = bytes.as_slice();
240+
let len = bytes.len();
241+
assert_eq!(secret.len(), len);
242+
secret.copy_from_slice(bytes);
243+
244+
Ok(len)
245+
}
246+
fn sign_msg(&self, msg: &[u8], signature: &mut [u8]) -> Result<usize, Error> {
247+
use p256::ecdsa::signature::Signer;
248+
249+
if signature.len() < super::EC_SIGNATURE_LEN_BYTES {
250+
return Err(Error::NoSpace);
251+
}
252+
253+
match &self.key {
254+
KeyType::Private(k) => {
255+
let signing_key = SigningKey::from(k);
256+
let sig: Signature = signing_key.sign(msg);
257+
let bytes = sig.to_bytes().to_vec();
258+
let len = bytes.len();
259+
signature[..len].copy_from_slice(&bytes);
260+
Ok(len)
261+
}
262+
KeyType::Public(_) => todo!(),
263+
}
264+
}
265+
fn verify_msg(&self, msg: &[u8], signature: &[u8]) -> Result<(), Error> {
266+
use p256::ecdsa::signature::Verifier;
267+
268+
let verifying_key = VerifyingKey::from_affine(self.public_key_point()).unwrap();
269+
let signature = Signature::try_from(signature).unwrap();
270+
271+
verifying_key
272+
.verify(msg, &signature)
273+
.map_err(|_| Error::InvalidSignature)?;
274+
275+
Ok(())
276+
}
277+
}
278+
279+
pub fn pbkdf2_hmac(pass: &[u8], iter: usize, salt: &[u8], key: &mut [u8]) -> Result<(), Error> {
280+
pbkdf2::pbkdf2::<hmac::Hmac<sha2::Sha256>>(pass, salt, iter as u32, key).unwrap();
281+
282+
Ok(())
283+
}
284+
285+
pub fn hkdf_sha256(salt: &[u8], ikm: &[u8], info: &[u8], key: &mut [u8]) -> Result<(), Error> {
286+
hkdf::Hkdf::<sha2::Sha256>::new(Some(salt), ikm)
287+
.expand(info, key)
288+
.map_err(|e| {
289+
error!("Error with hkdf_sha256 {:?}", e);
290+
Error::TLSStack
291+
})
292+
}
293+
294+
// TODO: add tests and check against mbedtls and openssl
295+
pub fn encrypt_in_place(
296+
key: &[u8],
297+
nonce: &[u8],
298+
ad: &[u8],
299+
data: &mut [u8],
300+
data_len: usize,
301+
) -> Result<usize, Error> {
302+
use ccm::{AeadInPlace, KeyInit};
303+
304+
let key = GenericArray::from_slice(key);
305+
let nonce = GenericArray::from_slice(nonce);
306+
let cipher = AesCcm::new(key);
307+
// This is probably incorrect
308+
let mut buffer = data[0..data_len].to_vec();
309+
cipher.encrypt_in_place(nonce, ad, &mut buffer)?;
310+
let len = buffer.len();
311+
data.clone_from_slice(&buffer[..]);
312+
313+
Ok(len)
314+
}
315+
316+
pub fn decrypt_in_place(
317+
key: &[u8],
318+
nonce: &[u8],
319+
ad: &[u8],
320+
data: &mut [u8],
321+
) -> Result<usize, Error> {
322+
use ccm::{AeadInPlace, KeyInit};
323+
324+
let key = GenericArray::from_slice(key);
325+
let nonce = GenericArray::from_slice(nonce);
326+
let cipher = AesCcm::new(key);
327+
// This is probably incorrect
328+
let mut buffer = data.to_vec();
329+
cipher.decrypt_in_place(nonce, ad, &mut buffer)?;
330+
let len = buffer.len();
331+
data[..len].copy_from_slice(&buffer[..]);
332+
333+
Ok(len)
334+
}

matter/src/crypto/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ mod crypto_openssl;
6060
#[cfg(feature = "crypto_openssl")]
6161
pub use self::crypto_openssl::*;
6262

63+
#[cfg(feature = "crypto_rustcrypto")]
64+
mod crypto_rustcrypto;
65+
#[cfg(feature = "crypto_rustcrypto")]
66+
pub use self::crypto_rustcrypto::*;
67+
6368
pub mod crypto_dummy;
6469

6570
#[cfg(test)]

0 commit comments

Comments
 (0)