diff --git a/vault/src/error.rs b/vault/src/error.rs index 60ec0fd..87ff0d4 100644 --- a/vault/src/error.rs +++ b/vault/src/error.rs @@ -101,3 +101,9 @@ impl From for CryptoError { Self::InvalidKey } } + +impl From for CryptoError { + fn from(_: k256::elliptic_curve::Error) -> Self { + Self::InvalidKey + } +} diff --git a/vault/src/key/asymmetric.rs b/vault/src/key/asymmetric.rs index b58e5ab..ea89740 100644 --- a/vault/src/key/asymmetric.rs +++ b/vault/src/key/asymmetric.rs @@ -1,10 +1,7 @@ //! An implementation of asymmetric cryptography using SECP256k1 ECDH with ChaCha20Poly1305 //! for the actual encryption. -use k256::{ - SecretKey, - ecdh::diffie_hellman, -}; +use k256::{SecretKey, ecdh::diffie_hellman, elliptic_curve::sec1::ToEncodedPoint}; use sha2::Sha256; use crate::{error::CryptoError, key::symmetric::SymmetricKey}; @@ -18,6 +15,7 @@ pub struct AsymmetricKeypair { } /// The public key part of an asymmetric keypair. +#[derive(Debug, PartialEq, Eq)] pub struct PublicKey(k256::PublicKey); impl AsymmetricKeypair { @@ -101,12 +99,63 @@ impl AsymmetricKeypair { impl PublicKey { /// Import a public key from raw bytes pub fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() == 64 { - Ok(Self( - k256::PublicKey::from_sec1_bytes(bytes).expect("Key is of valid size"), - )) - } else { - Err(CryptoError::InvalidKeySize) - } + Ok(Self(k256::PublicKey::from_sec1_bytes(bytes)?)) + } + + /// Get the raw bytes of this `PublicKey`, which can be transferred to another party. + /// + /// 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()); } } diff --git a/vault/src/key/symmetric.rs b/vault/src/key/symmetric.rs index c73a31b..1caff50 100644 --- a/vault/src/key/symmetric.rs +++ b/vault/src/key/symmetric.rs @@ -69,8 +69,8 @@ impl SymmetricKey { // Extract nonce from the end of ciphertext let ciphertext_len = ciphertext.len() - NONCE_SIZE; - let ciphertext = &ciphertext[0..ciphertext_len]; let nonce_bytes = &ciphertext[ciphertext_len..]; + let ciphertext = &ciphertext[0..ciphertext_len]; // Create cipher let cipher = ChaCha20Poly1305::new_from_slice(&self.0)