Compare commits

...

2 Commits

Author SHA1 Message Date
Lee Smet
e5a4a1b634
Add tests for symmetric keys
Some checks failed
Rhai Tests / Run Rhai Tests (pull_request) Has been cancelled
Signed-off-by: Lee Smet <lee.smet@hotmail.com>
2025-05-16 15:19:45 +02:00
Lee Smet
7f55cf4fba
Add tests for asymmetric keys, add public key export
Signed-off-by: Lee Smet <lee.smet@hotmail.com>
2025-05-16 15:05:45 +02:00
3 changed files with 114 additions and 12 deletions

View File

@ -101,3 +101,9 @@ impl From<k256::ecdsa::Error> for CryptoError {
Self::InvalidKey Self::InvalidKey
} }
} }
impl From<k256::elliptic_curve::Error> for CryptoError {
fn from(_: k256::elliptic_curve::Error) -> Self {
Self::InvalidKey
}
}

View File

@ -1,10 +1,7 @@
//! An implementation of asymmetric cryptography using SECP256k1 ECDH with ChaCha20Poly1305 //! An implementation of asymmetric cryptography using SECP256k1 ECDH with ChaCha20Poly1305
//! for the actual encryption. //! for the actual encryption.
use k256::{ use k256::{SecretKey, ecdh::diffie_hellman, elliptic_curve::sec1::ToEncodedPoint};
SecretKey,
ecdh::diffie_hellman,
};
use sha2::Sha256; use sha2::Sha256;
use crate::{error::CryptoError, key::symmetric::SymmetricKey}; use crate::{error::CryptoError, key::symmetric::SymmetricKey};
@ -18,6 +15,7 @@ pub struct AsymmetricKeypair {
} }
/// The public key part of an asymmetric keypair. /// The public key part of an asymmetric keypair.
#[derive(Debug, PartialEq, Eq)]
pub struct PublicKey(k256::PublicKey); pub struct PublicKey(k256::PublicKey);
impl AsymmetricKeypair { impl AsymmetricKeypair {
@ -101,12 +99,63 @@ impl AsymmetricKeypair {
impl PublicKey { impl PublicKey {
/// Import a public key from raw bytes /// Import a public key from raw bytes
pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> { pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
if bytes.len() == 64 { Ok(Self(k256::PublicKey::from_sec1_bytes(bytes)?))
Ok(Self( }
k256::PublicKey::from_sec1_bytes(bytes).expect("Key is of valid size"),
)) /// Get the raw bytes of this `PublicKey`, which can be transferred to another party.
} else { ///
Err(CryptoError::InvalidKeySize) /// The public key is SEC-1 encoded and compressed.
} pub fn as_bytes(&self) -> Box<[u8]> {
self.0.to_encoded_point(true).to_bytes()
}
}
#[cfg(test)]
mod tests {
/// Export a public key and import it later
#[test]
fn import_public_key() {
let kp = super::AsymmetricKeypair::new().expect("Can generate new keypair");
let pk1 = kp.public_key();
let pk_bytes = pk1.as_bytes();
let pk2 = super::PublicKey::from_bytes(&pk_bytes).expect("Can import public key");
assert_eq!(pk1, pk2);
}
/// Make sure 2 random keypairs derive the same shared secret (and thus encryption key), by
/// encrypting a random message, decrypting it, and verifying it matches.
#[test]
fn encrypt_and_decrypt() {
let kp1 = super::AsymmetricKeypair::new().expect("Can generate new keypair");
let kp2 = super::AsymmetricKeypair::new().expect("Can generate new keypair");
let pk1 = kp1.public_key();
let pk2 = kp2.public_key();
let message = b"this is a random message to encrypt and decrypt";
let enc = kp1.encrypt(&pk2, message).expect("Can encrypt message");
let dec = kp2.decrypt(&pk1, &enc).expect("Can decrypt message");
assert_eq!(message.as_slice(), dec.as_slice());
}
/// Use a different public key for decrypting than the expected one, this should fail the
/// decryption process as we use AEAD encryption with the symmetric key.
#[test]
fn decrypt_with_wrong_key() {
let kp1 = super::AsymmetricKeypair::new().expect("Can generate new keypair");
let kp2 = super::AsymmetricKeypair::new().expect("Can generate new keypair");
let kp3 = super::AsymmetricKeypair::new().expect("Can generate new keypair");
let pk2 = kp2.public_key();
let pk3 = kp3.public_key();
let message = b"this is a random message to encrypt and decrypt";
let enc = kp1.encrypt(&pk2, message).expect("Can encrypt message");
let dec = kp2.decrypt(&pk3, &enc);
assert!(dec.is_err());
} }
} }

View File

@ -8,6 +8,7 @@ use chacha20poly1305::{ChaCha20Poly1305, KeyInit, Nonce, aead::Aead};
use crate::error::CryptoError; use crate::error::CryptoError;
#[derive(Debug, PartialEq, Eq)]
pub struct SymmetricKey([u8; 32]); pub struct SymmetricKey([u8; 32]);
/// Size of a nonce in ChaCha20Poly1305. /// Size of a nonce in ChaCha20Poly1305.
@ -69,8 +70,8 @@ impl SymmetricKey {
// Extract nonce from the end of ciphertext // Extract nonce from the end of ciphertext
let ciphertext_len = ciphertext.len() - NONCE_SIZE; let ciphertext_len = ciphertext.len() - NONCE_SIZE;
let ciphertext = &ciphertext[0..ciphertext_len];
let nonce_bytes = &ciphertext[ciphertext_len..]; let nonce_bytes = &ciphertext[ciphertext_len..];
let ciphertext = &ciphertext[0..ciphertext_len];
// Create cipher // Create cipher
let cipher = ChaCha20Poly1305::new_from_slice(&self.0) let cipher = ChaCha20Poly1305::new_from_slice(&self.0)
@ -102,3 +103,49 @@ impl SymmetricKey {
Self(key) Self(key)
} }
} }
#[cfg(test)]
mod tests {
/// Using the same password derives the same key
#[test]
fn same_password_derives_same_key() {
const EXPECTED_KEY: [u8; 32] = [
4, 179, 233, 202, 225, 70, 211, 200, 7, 73, 115, 1, 85, 149, 90, 42, 160, 68, 16, 106,
136, 19, 197, 195, 153, 145, 179, 21, 37, 13, 37, 90,
];
const PASSWORD: &str = "test123";
let key = super::SymmetricKey::derive_from_password(PASSWORD);
assert_eq!(key.0, EXPECTED_KEY);
}
/// Make sure an encrypted value with some key can be decrypted with the same key
#[test]
fn can_decrypt() {
let key = super::SymmetricKey::new();
let message = b"this is a message to decrypt";
let enc = key.encrypt(message).expect("Can encrypt message");
let dec = key.decrypt(&enc).expect("Can decrypt message");
assert_eq!(message.as_slice(), dec.as_slice());
}
/// Make sure a value encrypted with one key can't be decrypted with a different key. Since we
/// use AEAD encryption we will notice this when trying to decrypt
#[test]
fn different_key_cant_decrypt() {
let key1 = super::SymmetricKey::new();
let key2 = super::SymmetricKey::new();
let message = b"this is a message to decrypt";
let enc = key1.encrypt(message).expect("Can encrypt message");
let dec = key2.decrypt(&enc);
assert!(dec.is_err());
}
}