diff options
| author | Will Dillon <william@housedillon.com> | 2025-12-04 17:07:12 +0000 |
|---|---|---|
| committer | Will Dillon <william@housedillon.com> | 2025-12-04 17:07:12 +0000 |
| commit | bf21ebaaae44b267cfefc87eee9608c09aab96b7 (patch) | |
| tree | 67234092d3c85a9c58d1fa9ca51337ac480ebfb7 /src/crypto.rs | |
| parent | Better than 90% everywhere. (diff) | |
| download | meshcore-rs-bf21ebaaae44b267cfefc87eee9608c09aab96b7.tar.gz meshcore-rs-bf21ebaaae44b267cfefc87eee9608c09aab96b7.zip | |
Getting closer to no-std being done
Diffstat (limited to 'src/crypto.rs')
| -rw-r--r-- | src/crypto.rs | 117 |
1 files changed, 73 insertions, 44 deletions
diff --git a/src/crypto.rs b/src/crypto.rs index b62fb5d..cd184dc 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -1,7 +1,3 @@ -use std::{fmt::{Debug, Display}, str::FromStr}; -use ed25519_dalek::{VerifyingKey, hazmat::ExpandedSecretKey}; -use bytes::{Buf, BufMut, Bytes, BytesMut}; -use hex::{decode, decode_to_slice, encode}; // This seems to be an absolute nightmare. GenericArray sucks // but I can't seem to figure out how to pull it out of this @@ -12,8 +8,21 @@ use curve25519_dalek::MontgomeryPoint; use aes::Aes128; use sha2::{Sha256}; use hmac::{Hmac, Mac}; +use ed25519_dalek::{VerifyingKey, hazmat::ExpandedSecretKey}; +use bytes::{Buf, BufMut, Bytes, BytesMut}; + +use crate::string_helper::NameString; + + type HmacSha256 = Hmac<Sha256>; +pub trait Keystore { + fn decrypt_and_id_p2p(&self, source: u8, _dest: u8, mac: u16, data: &Bytes) -> Option<(Bytes, u32, u32)>; + + fn decrypt_and_id_group(&self, group_hash_prefix: u8, mac: u16, data: &Bytes) -> Option<(Bytes, Option<NameString>)>; + + fn decrypt_anon(&self, dest: u8, pub_key: &PublicKey, mac: u16, data: &Bytes) -> Option<(Bytes, u32)>; +} #[derive(Debug, PartialEq)] pub enum MeshcoreCryptoError { @@ -23,10 +32,11 @@ pub enum MeshcoreCryptoError { KeyCreationError, } -impl std::error::Error for MeshcoreCryptoError {} -impl Display for MeshcoreCryptoError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl core::error::Error for MeshcoreCryptoError {} + +impl core::fmt::Display for MeshcoreCryptoError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { MeshcoreCryptoError::KeyLengthError => f.write_str("Key Length Error"), MeshcoreCryptoError::TryFromSliceError => f.write_str("Try From Slice Error"), @@ -39,9 +49,9 @@ impl Display for MeshcoreCryptoError { #[derive(PartialEq)] pub struct PrivateKey(ExpandedSecretKey); -impl std::fmt::Debug for PrivateKey { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("PrivateKey").field(&encode(self.0.scalar.as_bytes())).finish() +impl core::fmt::Debug for PrivateKey { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("PrivateKey").field(&hex::encode(self.0.scalar.as_bytes())).finish() } } @@ -74,18 +84,28 @@ impl Default for PrivateKey { } } +impl PrivateKey { + pub fn hash_prefix(&self) -> u32 { + // The has prefix is the beginning of the public key of the secret + let public_key = PublicKey::from(self); + public_key.hash_prefix() + } +} + #[derive(PartialEq, Clone)] pub struct PublicKey(ed25519_dalek::VerifyingKey); -impl std::fmt::Debug for PublicKey { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("PublicKey").field(&encode(self.0.as_bytes())).finish() +impl core::fmt::Debug for PublicKey { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("PublicKey").field(&hex::encode(self.0.as_bytes())).finish() } } impl PublicKey { - pub fn hash_prefix(&self) -> u8 { - self.0.as_bytes()[0] + pub fn hash_prefix(&self) -> u32 { + let mut bytes = Bytes::copy_from_slice(self.0.as_bytes()); + + bytes.get_u32() } } @@ -102,13 +122,13 @@ impl PartialEq for SharedSecret { } } -impl std::fmt::Debug for SharedSecret { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("SharedSecret").field(&encode(self.0.as_bytes())).finish() +impl core::fmt::Debug for SharedSecret { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("SharedSecret").field(&hex::encode(self.0.as_bytes())).finish() } } -impl FromStr for SharedSecret { +impl core::str::FromStr for SharedSecret { type Err = MeshcoreCryptoError; fn from_str(s: &str) -> Result<Self, Self::Err> { @@ -117,7 +137,7 @@ impl FromStr for SharedSecret { // The provided group secrets are only 16 bytes, // but they're zero-paded to be 32. So, we're // going to get the hex from the string and copy it in. - if decode_to_slice(s, &mut array[0..16]).is_err() { + if hex::decode_to_slice(s, &mut array[0..16]).is_err() { return Err(MeshcoreCryptoError::TryFromSliceError) } else { Ok(SharedSecret(MontgomeryPoint(array))) @@ -139,6 +159,15 @@ impl TryFrom<Bytes> for SharedSecret { } } +// This is kinda meaningless because a shared secret only works +// when it's connected to a key pair, but it needs to exist for +// Arrayvec. +impl Default for SharedSecret { + fn default() -> Self { + Self(curve25519_dalek::MontgomeryPoint([0u8; 32])) + } +} + impl SharedSecret { fn get_key(&self) -> &[u8; 16] { // Safety: The size of the slice ensures that this will never be wrong. @@ -289,11 +318,11 @@ impl TryFrom<Bytes> for PublicKey { } -impl FromStr for PublicKey { +impl core::str::FromStr for PublicKey { type Err = MeshcoreCryptoError; fn from_str(hex_str: &str) -> Result<Self, Self::Err> { - if let Ok(hex) = decode(hex_str) { + if let Ok(hex) = hex::decode(hex_str) { if let Ok(slice) = TryInto::<[u8; 32]>::try_into(hex) { if let Ok(key) = VerifyingKey::from_bytes(&slice) { Ok(PublicKey(key)) @@ -309,11 +338,11 @@ impl FromStr for PublicKey { } } -impl FromStr for PrivateKey { +impl core::str::FromStr for PrivateKey { type Err = MeshcoreCryptoError; fn from_str(hex_str: &str) -> Result<Self, Self::Err> { - if let Ok(hex) = decode(hex_str) { + if let Ok(hex) = hex::decode(hex_str) { if let Ok(bytes) = TryInto::<[u8; 64]>::try_into(hex) { Ok(PrivateKey(ExpandedSecretKey::from_bytes(&bytes))) } else { @@ -355,9 +384,11 @@ impl SharedSecret { } } +// Tests for std operations #[cfg(test)] mod tests { - use hex::{decode_to_slice, encode}; + use core::str::FromStr; + use hex::{decode_to_slice, encode, decode}; use super::*; #[test] @@ -365,7 +396,7 @@ mod tests { let mut slice = [0_u8; 32]; decode_to_slice("12349bdc1f76a0c12149bb15f791dbe42fde02c209b04a85c6f512990c8cedec", &mut slice).unwrap(); let public_key = PublicKey::from_str("12349bdc1f76a0c12149bb15f791dbe42fde02c209b04a85c6f512990c8cedec"); - assert!(Ok(PublicKey(VerifyingKey::from_bytes(&slice).unwrap())) == public_key); + assert_eq!(Ok(PublicKey(VerifyingKey::from_bytes(&slice).unwrap())), public_key); } #[test] @@ -373,7 +404,7 @@ mod tests { let private_key = PrivateKey::from_str("38DAA98490B7284697C7ADA6175FD1F8DAD12032AD7ABAE625B7EAD8FEC6444CA281C3370B97155D9C8CECD89A929FDDE0FBF3A9D5C92A1B3C24D711934CD69D").unwrap(); let public_key = PublicKey::from(&private_key); println!("Public key: {:#?}", public_key); - assert!(PublicKey::from_str("12349bdc1f76a0c12149bb15f791dbe42fde02c209b04a85c6f512990c8cedec").unwrap() == public_key); + assert_eq!(PublicKey::from_str("12349bdc1f76a0c12149bb15f791dbe42fde02c209b04a85c6f512990c8cedec").unwrap(), public_key); } #[test] @@ -385,8 +416,8 @@ mod tests { let alice_public = PublicKey::from(&alice_private); let bob_public = PublicKey::from(&bob_private); - assert!(alice_public.0.as_bytes().to_vec() == decode("34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec219c8b8f").unwrap()); - assert!( bob_public.0.as_bytes().to_vec() == decode("12349bdc1f76a0c12149bb15f791dbe42fde02c209b04a85c6f512990c8cedec").unwrap()); + assert_eq!(alice_public.0.as_bytes().to_vec(), decode("34569df1f9661916901669666fb8025eccb9ddb0499cddad4c164fec219c8b8f").unwrap()); + assert_eq!( bob_public.0.as_bytes().to_vec(), decode("12349bdc1f76a0c12149bb15f791dbe42fde02c209b04a85c6f512990c8cedec").unwrap()); println!("Alice's public key: {}", encode(&alice_public.0.to_bytes())); println!("Bob's public key: {}", encode(&bob_public.0.to_bytes())); @@ -394,12 +425,12 @@ mod tests { let left_secret = alice_private.create_secret(&bob_public); let right_secret = bob_private.create_secret(&alice_public); - assert!(left_secret.0.as_bytes().to_vec() == decode("eb7a365363bd8548ee2b54b9234247be5e42e96be9625adcdf3a55b6c1d04850").unwrap()); + assert_eq!(left_secret.0.as_bytes().to_vec(), decode("eb7a365363bd8548ee2b54b9234247be5e42e96be9625adcdf3a55b6c1d04850").unwrap()); println!("Left shared secret: {}", encode(&left_secret.0.as_bytes())); println!("Right shared secret: {}", encode(&right_secret.0.as_bytes())); - assert!(left_secret == right_secret); + assert_eq!(left_secret, right_secret); } #[test] @@ -407,12 +438,12 @@ mod tests { // Test using a group secret let group_secret = SharedSecret::new_from_group_secret(Bytes::copy_from_slice(&decode("8b3387e9c5cdea6ac9e5edbaa115cd72").unwrap())); let test_group_secret =SharedSecret::from_str("8b3387e9c5cdea6ac9e5edbaa115cd72").unwrap(); - assert!(group_secret == test_group_secret); + assert_eq!(group_secret, test_group_secret); // Test using the secret and ciphertext to make a MAC and ensure it matches an example for a group secret let sample_data = Bytes::copy_from_slice(&decode("354D619BAE9590E4D177DB7EEAF982F5BDCF78005D75157D9535FA90178F785D").unwrap()); let mac = group_secret.get_hmac(&sample_data); - assert!(0xC3C1 == mac); + assert_eq!(0xC3C1, mac); } #[test] @@ -430,7 +461,7 @@ mod tests { let cleartext = secret.decrypt(&ciphertext); let vec = cleartext.to_vec(); let string = String::from_utf8_lossy(&vec); - assert!("Hello my world!!" == string); + assert_eq!("Hello my world!!", string); } #[test] @@ -438,7 +469,7 @@ mod tests { let plaintext = Bytes::copy_from_slice("Meshcore!".as_bytes()); let secret = SharedSecret::new_from_group_secret(Bytes::copy_from_slice(&decode("44A6F78DAD2E54D73A32CDE3ECAA9E75").unwrap())); let ciphertext = secret.encrypt(plaintext); - assert!(ciphertext == decode("62374852B6A11405A081F87356C88861").unwrap()); + assert_eq!(ciphertext, decode("62374852B6A11405A081F87356C88861").unwrap()); } #[test] @@ -468,7 +499,7 @@ mod tests { println!("Result: {:#?}", encode(&text)); - assert!(text == decode("102030405060708090A0B0C0D0E0F0").unwrap()); + assert_eq!(text, decode("102030405060708090A0B0C0D0E0F0").unwrap()); } @@ -525,15 +556,15 @@ mod tests { let mac = 0xC3C1; let cleartext = group_secret.mac_then_decrypt(mac, &sample_data).unwrap(); - assert!(cleartext == decode("3757d06800f09f8cb220547265653a20e29881efb88f00000000000000000000").unwrap()); + assert_eq!(cleartext, decode("3757d06800f09f8cb220547265653a20e29881efb88f00000000000000000000").unwrap()); } #[test] fn test_error_display() { - assert!(format!("{}", MeshcoreCryptoError::KeyLengthError) == "Key Length Error"); - assert!(format!("{}", MeshcoreCryptoError::TryFromSliceError) == "Try From Slice Error"); - assert!(format!("{}", MeshcoreCryptoError::HexDecodeError) == "Hex Decode Error"); - assert!(format!("{}", MeshcoreCryptoError::KeyCreationError) == "Key Creation Error"); + assert_eq!(format!("{}", MeshcoreCryptoError::KeyLengthError), "Key Length Error"); + assert_eq!(format!("{}", MeshcoreCryptoError::TryFromSliceError), "Try From Slice Error"); + assert_eq!(format!("{}", MeshcoreCryptoError::HexDecodeError), "Hex Decode Error"); + assert_eq!(format!("{}", MeshcoreCryptoError::KeyCreationError), "Key Creation Error"); } #[test] @@ -565,9 +596,7 @@ mod tests { .first() .unwrap(); - assert!(lhs_string == cleartext, "{}", - format!("\"{}\" != \"{}\"", lhs_string, cleartext) - ); + assert_eq!(lhs_string, cleartext); } else { assert!(false, "Unable to decrypt"); } |
