Implement proper key types
Signed-off-by: Lee Smet <lee.smet@hotmail.com>
This commit is contained in:
		
							
								
								
									
										112
									
								
								vault/src/key/asymmetric.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								vault/src/key/asymmetric.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,112 @@
 | 
			
		||||
//! An implementation of asymmetric cryptography using SECP256k1 ECDH with ChaCha20Poly1305
 | 
			
		||||
//! for the actual encryption.
 | 
			
		||||
 | 
			
		||||
use k256::{
 | 
			
		||||
    SecretKey,
 | 
			
		||||
    ecdh::diffie_hellman,
 | 
			
		||||
};
 | 
			
		||||
use sha2::Sha256;
 | 
			
		||||
 | 
			
		||||
use crate::{error::CryptoError, key::symmetric::SymmetricKey};
 | 
			
		||||
 | 
			
		||||
/// A keypair for use in asymmetric encryption operations.
 | 
			
		||||
pub struct AsymmetricKeypair {
 | 
			
		||||
    /// Private part of the key
 | 
			
		||||
    private: SecretKey,
 | 
			
		||||
    /// Public part of the key
 | 
			
		||||
    public: k256::PublicKey,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// The public key part of an asymmetric keypair.
 | 
			
		||||
pub struct PublicKey(k256::PublicKey);
 | 
			
		||||
 | 
			
		||||
impl AsymmetricKeypair {
 | 
			
		||||
    /// Generates a new random keypair
 | 
			
		||||
    pub fn new() -> Result<Self, CryptoError> {
 | 
			
		||||
        let mut raw_private = [0u8; 32];
 | 
			
		||||
        rand::fill(&mut raw_private);
 | 
			
		||||
        let sk = SecretKey::from_slice(&raw_private)
 | 
			
		||||
            .expect("Key is provided generated with fixed valid size");
 | 
			
		||||
        let pk = sk.public_key();
 | 
			
		||||
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            private: sk,
 | 
			
		||||
            public: pk,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create a new key from existing bytes.
 | 
			
		||||
    pub(crate) fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
 | 
			
		||||
        if bytes.len() == 32 {
 | 
			
		||||
            let sk = SecretKey::from_slice(&bytes).expect("Key was checked to be a valid size");
 | 
			
		||||
            let pk = sk.public_key();
 | 
			
		||||
            Ok(Self {
 | 
			
		||||
                private: sk,
 | 
			
		||||
                public: pk,
 | 
			
		||||
            })
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(CryptoError::InvalidKeySize)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// View the raw bytes of the private key of this keypair.
 | 
			
		||||
    pub(crate) fn as_raw_private_key(&self) -> Vec<u8> {
 | 
			
		||||
        self.private.as_scalar_primitive().to_bytes().to_vec()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the public part of this keypair.
 | 
			
		||||
    pub fn public_key(&self) -> PublicKey {
 | 
			
		||||
        PublicKey(self.public.clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Encrypt data for a receiver. First a shared secret is derived using the own private key and
 | 
			
		||||
    /// the receivers public key. Then, this shared secret is used for symmetric encryption of the
 | 
			
		||||
    /// plaintext. The receiver can decrypt this by generating the same shared secret, using his
 | 
			
		||||
    /// own private key and our public key.
 | 
			
		||||
    pub fn encrypt(
 | 
			
		||||
        &self,
 | 
			
		||||
        remote_key: &PublicKey,
 | 
			
		||||
        plaintext: &[u8],
 | 
			
		||||
    ) -> Result<Vec<u8>, CryptoError> {
 | 
			
		||||
        let mut symmetric_key = [0u8; 32];
 | 
			
		||||
        diffie_hellman(self.private.to_nonzero_scalar(), remote_key.0.as_affine())
 | 
			
		||||
            .extract::<Sha256>(None)
 | 
			
		||||
            .expand(&[], &mut symmetric_key)
 | 
			
		||||
            .map_err(|_| CryptoError::InvalidKeySize)?;
 | 
			
		||||
 | 
			
		||||
        let sym_key = SymmetricKey::from_bytes(&symmetric_key)?;
 | 
			
		||||
 | 
			
		||||
        sym_key.encrypt(plaintext)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Decrypt data from a sender. The remote key must be the public key of the keypair used by
 | 
			
		||||
    /// the sender to encrypt this message.
 | 
			
		||||
    pub fn decrypt(
 | 
			
		||||
        &self,
 | 
			
		||||
        remote_key: &PublicKey,
 | 
			
		||||
        ciphertext: &[u8],
 | 
			
		||||
    ) -> Result<Vec<u8>, CryptoError> {
 | 
			
		||||
        let mut symmetric_key = [0u8; 32];
 | 
			
		||||
        diffie_hellman(self.private.to_nonzero_scalar(), remote_key.0.as_affine())
 | 
			
		||||
            .extract::<Sha256>(None)
 | 
			
		||||
            .expand(&[], &mut symmetric_key)
 | 
			
		||||
            .map_err(|_| CryptoError::InvalidKeySize)?;
 | 
			
		||||
 | 
			
		||||
        let sym_key = SymmetricKey::from_bytes(&symmetric_key)?;
 | 
			
		||||
 | 
			
		||||
        sym_key.decrypt(ciphertext)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PublicKey {
 | 
			
		||||
    /// Import a public key from raw bytes
 | 
			
		||||
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
 | 
			
		||||
        if bytes.len() == 64 {
 | 
			
		||||
            Ok(Self(
 | 
			
		||||
                k256::PublicKey::from_sec1_bytes(bytes).expect("Key is of valid size"),
 | 
			
		||||
            ))
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(CryptoError::InvalidKeySize)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										76
									
								
								vault/src/key/signature.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								vault/src/key/signature.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
			
		||||
//! An implementation of digitial signatures using secp256k1 ECDSA.
 | 
			
		||||
 | 
			
		||||
use k256::ecdsa::{
 | 
			
		||||
    Signature, SigningKey, VerifyingKey,
 | 
			
		||||
    signature::{Signer, Verifier},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::error::CryptoError;
 | 
			
		||||
 | 
			
		||||
pub struct SigningKeypair {
 | 
			
		||||
    sk: SigningKey,
 | 
			
		||||
    vk: VerifyingKey,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct PublicKey(VerifyingKey);
 | 
			
		||||
 | 
			
		||||
impl SigningKeypair {
 | 
			
		||||
    /// Generates a new random keypair
 | 
			
		||||
    pub fn new() -> Result<Self, CryptoError> {
 | 
			
		||||
        let mut raw_private = [0u8; 32];
 | 
			
		||||
        rand::fill(&mut raw_private);
 | 
			
		||||
        let sk = SigningKey::from_slice(&raw_private)
 | 
			
		||||
            .expect("Key is provided generated with fixed valid size");
 | 
			
		||||
        let vk = sk.verifying_key().to_owned();
 | 
			
		||||
 | 
			
		||||
        Ok(Self { sk, vk })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create a new key from existing bytes.
 | 
			
		||||
    pub(crate) fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
 | 
			
		||||
        if bytes.len() == 32 {
 | 
			
		||||
            let sk = SigningKey::from_slice(&bytes).expect("Key was checked to be a valid size");
 | 
			
		||||
            let vk = sk.verifying_key().to_owned();
 | 
			
		||||
            Ok(Self { sk, vk })
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(CryptoError::InvalidKeySize)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// View the raw bytes of the private key of this keypair.
 | 
			
		||||
    pub(crate) fn as_raw_private_key(&self) -> Vec<u8> {
 | 
			
		||||
        self.sk.as_nonzero_scalar().to_bytes().to_vec()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the public part of this keypair.
 | 
			
		||||
    pub fn public_key(&self) -> PublicKey {
 | 
			
		||||
        PublicKey(self.vk)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Sign data with the private key of this `SigningKeypair`. Other parties can use the public
 | 
			
		||||
    /// key to verify the signature. The generated signature is a detached signature.
 | 
			
		||||
    pub fn sign(&self, message: &[u8]) -> Result<Vec<u8>, CryptoError> {
 | 
			
		||||
        let sig: Signature = self.sk.sign(message);
 | 
			
		||||
        Ok(sig.to_vec())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PublicKey {
 | 
			
		||||
    /// Import a public key from raw bytes
 | 
			
		||||
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
 | 
			
		||||
        if bytes.len() == 64 {
 | 
			
		||||
            Ok(Self(
 | 
			
		||||
                VerifyingKey::from_sec1_bytes(bytes).expect("Key is of valid size"),
 | 
			
		||||
            ))
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(CryptoError::InvalidKeySize)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn verify_signature(&self, message: &[u8], sig: &[u8]) -> Result<(), CryptoError> {
 | 
			
		||||
        let sig = Signature::from_slice(sig).map_err(|_| CryptoError::InvalidKeySize)?;
 | 
			
		||||
        self.0
 | 
			
		||||
            .verify(message, &sig)
 | 
			
		||||
            .map_err(|_| CryptoError::SignatureFailed)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										86
									
								
								vault/src/key/symmetric.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								vault/src/key/symmetric.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
			
		||||
//! An implementation of symmetric keys for ChaCha20Poly1305 encryption.
 | 
			
		||||
//!
 | 
			
		||||
//! The ciphertext is authenticated.
 | 
			
		||||
//! The 12-byte nonce is appended to the generated ciphertext.
 | 
			
		||||
//! Keys are 32 bytes in size.
 | 
			
		||||
 | 
			
		||||
use chacha20poly1305::{ChaCha20Poly1305, KeyInit, Nonce, aead::Aead};
 | 
			
		||||
 | 
			
		||||
use crate::error::CryptoError;
 | 
			
		||||
 | 
			
		||||
pub struct SymmetricKey([u8; 32]);
 | 
			
		||||
 | 
			
		||||
/// Size of a nonce in ChaCha20Poly1305.
 | 
			
		||||
const NONCE_SIZE: usize = 12;
 | 
			
		||||
 | 
			
		||||
impl SymmetricKey {
 | 
			
		||||
    /// Generate a new random SymmetricKey.
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        let mut key = [0u8; 32];
 | 
			
		||||
        rand::fill(&mut key);
 | 
			
		||||
        Self(key)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create a new key from existing bytes.
 | 
			
		||||
    pub(crate) fn from_bytes(bytes: &[u8]) -> Result<SymmetricKey, CryptoError> {
 | 
			
		||||
        if bytes.len() == 32 {
 | 
			
		||||
            let mut key = [0u8; 32];
 | 
			
		||||
            key.copy_from_slice(bytes);
 | 
			
		||||
            Ok(SymmetricKey(key))
 | 
			
		||||
        } else {
 | 
			
		||||
            Err(CryptoError::InvalidKeySize)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// View the raw bytes of this key
 | 
			
		||||
    pub(crate) fn as_raw_bytes(&self) -> &[u8; 32] {
 | 
			
		||||
        &self.0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Encrypt a plaintext with the key. A nonce is generated and appended to the end of the
 | 
			
		||||
    /// message.
 | 
			
		||||
    pub fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>, CryptoError> {
 | 
			
		||||
        // Create cipher
 | 
			
		||||
        let cipher = ChaCha20Poly1305::new_from_slice(&self.0)
 | 
			
		||||
            .expect("Key is a fixed 32 byte array so size is always ok");
 | 
			
		||||
 | 
			
		||||
        // Generate random nonce
 | 
			
		||||
        let mut nonce_bytes = [0u8; NONCE_SIZE];
 | 
			
		||||
        rand::fill(&mut nonce_bytes);
 | 
			
		||||
        let nonce = Nonce::from_slice(&nonce_bytes);
 | 
			
		||||
 | 
			
		||||
        // Encrypt message
 | 
			
		||||
        let mut ciphertext = cipher
 | 
			
		||||
            .encrypt(nonce, plaintext)
 | 
			
		||||
            .map_err(|_| CryptoError::EncryptionFailed)?;
 | 
			
		||||
 | 
			
		||||
        // Append nonce to ciphertext
 | 
			
		||||
        ciphertext.extend_from_slice(&nonce_bytes);
 | 
			
		||||
 | 
			
		||||
        Ok(ciphertext)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Decrypts a ciphertext with appended nonce.
 | 
			
		||||
    pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, CryptoError> {
 | 
			
		||||
        // Check if ciphertext is long enough to contain a nonce
 | 
			
		||||
        if ciphertext.len() <= NONCE_SIZE {
 | 
			
		||||
            return Err(CryptoError::DecryptionFailed);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 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..];
 | 
			
		||||
 | 
			
		||||
        // Create cipher
 | 
			
		||||
        let cipher = ChaCha20Poly1305::new_from_slice(&self.0)
 | 
			
		||||
            .expect("Key is a fixed 32 byte array so size is always ok");
 | 
			
		||||
 | 
			
		||||
        let nonce = Nonce::from_slice(nonce_bytes);
 | 
			
		||||
 | 
			
		||||
        // Decrypt message
 | 
			
		||||
        cipher
 | 
			
		||||
            .decrypt(nonce, ciphertext)
 | 
			
		||||
            .map_err(|_| CryptoError::DecryptionFailed)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user