cyrtophora

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit b76d05dc5725068147856ff6fe28bd4d31cc0530
parent 3398259113b094455728406a37bbcf3a654ab5fb
Author: Jackson G. Kaindume <seestem@merely.tech>
Date:   Sun, 21 Aug 2022 11:33:02 +0200

move crypto to crypto.rs file

Diffstat:
Msrc/lib.rs | 194++-----------------------------------------------------------------------------
1 file changed, 3 insertions(+), 191 deletions(-)

diff --git a/src/lib.rs b/src/lib.rs @@ -1,191 +1,3 @@ -use anyhow::Result; -use chacha20poly1305::{ - aead::{Aead, NewAead}, - Key, XChaCha20Poly1305, XNonce, -}; -use ring::{ - digest::{self, digest, Digest}, - rand::SystemRandom, - signature::{Ed25519KeyPair, KeyPair}, -}; -use scrypt::{ - password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString}, - Scrypt, -}; - -const CREDENTIAL_LEN: usize = digest::SHA256_OUTPUT_LEN; - -/// Nonce used when encrypting the Crypto's keypair (Crypto::encrypt_keypair_document()) -const AED_NONCE: &str = "people the information "; -pub type Credential = [u8; CREDENTIAL_LEN]; - -pub struct Credentials { - /// Base64 encoded key pair serialized as a PKCS#8 document - pub keypair_doc: String, - /// Base64 encoded public key - pub public_key: String, -} - -pub struct Crypto {} - -impl Crypto { - /// Generate Ed25519 key pair, for signing - pub fn gen_ed25519_document() -> Result<Credentials> { - let rng = SystemRandom::new(); - // TODO: don't use expect() - let keypair_document = - Ed25519KeyPair::generate_pkcs8(&rng).expect("Could not generate Ed25519KeyPair"); - let keypair = Ed25519KeyPair::from_pkcs8(keypair_document.as_ref()) - .expect("Could not decode keypair from keypair document"); - let public_key_string = base64::encode(keypair.public_key().as_ref()); - let keypair_document_string = base64::encode(keypair_document.as_ref()); - let cred = Credentials { - keypair_doc: keypair_document_string, - public_key: public_key_string, - }; - - Ok(cred) - } - - /// Generate scrypt based hashed password - pub fn hash_password(password: &str) -> Result<String> { - let salt = SaltString::generate(&mut OsRng); - Ok(Scrypt - .hash_password(password.as_bytes(), &salt)? - .to_string()) - } - - /// Verify scrypt based password hash - fn verify_password_hash(attempted_password: &str, hash: &str) -> Result<bool> { - let parsed_hash = PasswordHash::new(hash)?; - - if Scrypt - .verify_password(attempted_password.as_bytes(), &parsed_hash) - .is_ok() - { - Ok(true) - } else { - Ok(false) - } - } - - /// Encrypt the keypair (ed25519) document, using XChaCha20Poly1305 - /// so that it can be safely persisted into the database. The - /// Crypto's login password is used a the secret key. - pub fn encrypt_keypair_document(keypair_document: &str, password: &str) -> Result<String> { - let secret_key = Crypto::gen_secret_key(password); - let key = Key::from_slice(&secret_key.as_ref()[..32]); // 32-bytes - let aead = XChaCha20Poly1305::new(key); - let nonce = XNonce::from_slice(AED_NONCE.as_bytes()); // 24-bytes; unique - let keypair_document_bytes = base64::decode(keypair_document)?; - let res = base64::encode( - aead.encrypt(nonce, keypair_document_bytes.as_ref()) - .expect("encryption failure!"), - ); - Ok(res) - } - - /// Decrypt the encrypted keypair (ed25519), using XChaCha20Poly1305 - fn decrypt_keypair_document(cyphertext: &str, password: &str) -> Result<String> { - let secret_key = Crypto::gen_secret_key(password); - let key = Key::from_slice(&secret_key.as_ref()[..32]); // 32-bytes - let nonce = XNonce::from_slice(AED_NONCE.as_bytes()); // 24-bytes; unique - let aead = XChaCha20Poly1305::new(key); - let cyphertext_bytes = base64::decode(cyphertext)?; - let res = base64::encode( - aead.decrypt(nonce, cyphertext_bytes.as_ref()) - .expect("decryption failure!"), - ); - Ok(res) - } - - /// Generate secret key, used to encrypt the ed25519 keypair - fn gen_secret_key(password: &str) -> Digest { - digest(&digest::SHA256, password.as_bytes()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use ring::signature::{Ed25519KeyPair, KeyPair, UnparsedPublicKey, ED25519}; - - #[test] - fn hash_password() { - let password1 = "a bad password"; - let hash1 = Crypto::hash_password(password1).unwrap(); - - let password2 = "a bad password"; - let hash2 = Crypto::hash_password(password2).unwrap(); - - // The generated hashes are all 88 bytes long - assert_eq!(hash1.len(), 88); - assert_eq!(hash2.len(), 88); - - // Even if the user passwords are the same they do not generate - // the same hashes because the username is also used as a - // seed when the hash is generated. - assert_ne!(hash1, hash2); - - // Verify password - assert!(Crypto::verify_password_hash("a bad password", &hash1).unwrap()); - assert!(!Crypto::verify_password_hash("wrong password", &hash1).unwrap()); - } - - #[test] - fn gen_keypairs() { - let cred = Crypto::gen_ed25519_document().unwrap(); - let document = base64::decode(cred.keypair_doc).unwrap(); - let key_pair = Ed25519KeyPair::from_pkcs8(&document).unwrap(); - - // Sign the message "hello, world". - const MESSAGE: &[u8] = b"hello, world"; - let sig = key_pair.sign(MESSAGE); - - // Normally an application would extract the bytes of the signature and - // send them in a protocol message to the peer(s). Here we just get the - // public key key directly from the key pair. - let peer_public_key_bytes = key_pair.public_key().as_ref(); - - // Verify the signature of the message using the public key. Normally the - // verifier of the message would parse the inputs to this code out of the - // protocol message(s) sent by the signer. - let peer_public_key = UnparsedPublicKey::new(&ED25519, peer_public_key_bytes); - peer_public_key.verify(MESSAGE, sig.as_ref()).unwrap(); - } - - #[test] - fn symmetric_encryption_decryption() { - let password = "my very weak password"; - let cred = Crypto::gen_ed25519_document().unwrap(); - let document = base64::decode(cred.keypair_doc.clone()).unwrap(); - let expected_key_pair = Ed25519KeyPair::from_pkcs8(&document).unwrap(); - - let wrong_cred = Crypto::gen_ed25519_document().unwrap(); - let wrong_document = base64::decode(wrong_cred.keypair_doc).unwrap(); - let wrong_key_pair = Ed25519KeyPair::from_pkcs8(&wrong_document).unwrap(); - - // encrypt the document - let cyphertext = Crypto::encrypt_keypair_document(&cred.keypair_doc, password).unwrap(); - let cleartext = Crypto::decrypt_keypair_document(&cyphertext, password).unwrap(); - - let cleartext_bytes = base64::decode(cleartext.clone()).unwrap(); - let key_pair = Ed25519KeyPair::from_pkcs8(cleartext_bytes.as_ref()).unwrap(); - - assert_ne!(cyphertext, cleartext); - assert_eq!( - expected_key_pair.public_key().as_ref(), - key_pair.public_key().as_ref() - ); - - assert_ne!( - expected_key_pair.public_key().as_ref(), - wrong_key_pair.public_key().as_ref() - ); - - assert_ne!( - key_pair.public_key().as_ref(), - wrong_key_pair.public_key().as_ref() - ); - } -} +mod crypto; +mod accounts; +mod validator;