Add tests for asymmetric keys, add public key export

Signed-off-by: Lee Smet <lee.smet@hotmail.com>
This commit is contained in:
Lee Smet 2025-05-16 15:05:45 +02:00
parent 365814b424
commit 7f55cf4fba
Signed by untrusted user who does not match committer: lee
GPG Key ID: 72CBFB5FDA7FE025
3 changed files with 67 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

@ -69,8 +69,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)