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:
M | src/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;