From e07085f358ce3d6905724ad567a4f60cd85cb525 Mon Sep 17 00:00:00 2001 From: Jisu-Woniu <31986081+Jisu-Woniu@users.noreply.github.com> Date: Mon, 13 Nov 2023 00:37:57 +0800 Subject: [PATCH] refactor: :recycle: Add docs and move keygen functions to module `key_pair`. --- .vscode/settings.json | 11 +++++ src-tauri/crypto/src/error.rs | 6 +++ src-tauri/crypto/src/key_pair.rs | 60 +++++++++++++++++++++++++- src-tauri/crypto/src/keygen.rs | 73 +++++++------------------------- src-tauri/crypto/src/lib.rs | 5 +++ src-tauri/crypto/src/signing.rs | 12 ++++-- 6 files changed, 105 insertions(+), 62 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f21cf14 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "cSpell.words": [ + "smallvec", + "Subpacket", + "subpackets", + "thiserror", + "unhashed", + "zeroize", + "Zeroizing" + ] +} diff --git a/src-tauri/crypto/src/error.rs b/src-tauri/crypto/src/error.rs index e55b25a..50d89b9 100644 --- a/src-tauri/crypto/src/error.rs +++ b/src-tauri/crypto/src/error.rs @@ -1,15 +1,21 @@ +//! Error types wrapper + use std::io; use thiserror::Error; +/// Error type #[derive(Debug, Error)] #[non_exhaustive] pub enum Error { + /// Error propagated from [`io::Error`] #[error("IO error: {0}")] Io(#[from] io::Error), + /// Error propagated from [`pgp::errors::Error`] #[error("PGP error: {0}")] Pgp(#[from] pgp::errors::Error), } +/// Result type for use in the crate. pub type Result = std::result::Result; diff --git a/src-tauri/crypto/src/key_pair.rs b/src-tauri/crypto/src/key_pair.rs index 6f57de7..04926a1 100644 --- a/src-tauri/crypto/src/key_pair.rs +++ b/src-tauri/crypto/src/key_pair.rs @@ -1,4 +1,14 @@ -use pgp::{SignedPublicKey, SignedSecretKey}; +use std::path::Path; + +use pgp::{ + crypto::{hash::HashAlgorithm, sym::SymmetricKeyAlgorithm}, + types::{CompressionAlgorithm, SecretKeyTrait as _}, + Deserializable, KeyType, SecretKeyParamsBuilder, SignedPublicKey, SignedSecretKey, +}; +use smallvec::smallvec; +use tokio::fs; + +use crate::Result; pub(crate) struct KeyPair { secret_key: SignedSecretKey, @@ -6,6 +16,42 @@ pub(crate) struct KeyPair { } impl KeyPair { + pub(crate) fn generate(name: &str, email: &str) -> Result { + let secret_key = SecretKeyParamsBuilder::default() + // Set keygen params. + // TODO: Allow user to choose between RSA, EdDSA and more. + .key_type(KeyType::EdDSA) + .primary_user_id(format!("{} <{}>", name, email)) + .preferred_symmetric_algorithms(smallvec![ + SymmetricKeyAlgorithm::AES256, + SymmetricKeyAlgorithm::AES192, + SymmetricKeyAlgorithm::AES128, + SymmetricKeyAlgorithm::TripleDES, + ]) + .preferred_hash_algorithms(smallvec![ + HashAlgorithm::SHA2_512, + HashAlgorithm::SHA2_384, + HashAlgorithm::SHA2_256, + HashAlgorithm::SHA2_224, + HashAlgorithm::SHA1 + ]) + .preferred_compression_algorithms(smallvec![ + CompressionAlgorithm::ZLIB, + CompressionAlgorithm::BZip2, + CompressionAlgorithm::ZIP + ]) + .can_sign(true) + .build() + .expect("msg") + .generate()?; + let passwd_fn = String::new; + let signed_secret_key = secret_key.sign(passwd_fn)?; + let public_key = signed_secret_key.public_key(); + let signed_public_key = public_key.sign(&signed_secret_key, passwd_fn)?; + + Ok(KeyPair::from_keys(signed_secret_key, signed_public_key)) + } + pub(crate) fn from_keys(secret_key: SignedSecretKey, public_key: SignedPublicKey) -> Self { Self { secret_key, @@ -20,3 +66,15 @@ impl KeyPair { &self.public_key } } + +#[allow(dead_code)] +pub(crate) async fn secret_key_from_file(path: impl AsRef) -> Result { + let input = fs::read_to_string(path).await?; + Ok(SignedSecretKey::from_string(&input)?.0) +} + +#[allow(dead_code)] +pub(crate) async fn public_key_from_file(path: impl AsRef) -> Result { + let input = fs::read_to_string(path).await?; + Ok(SignedPublicKey::from_string(&input)?.0) +} diff --git a/src-tauri/crypto/src/keygen.rs b/src-tauri/crypto/src/keygen.rs index 636dd5f..9b78029 100644 --- a/src-tauri/crypto/src/keygen.rs +++ b/src-tauri/crypto/src/keygen.rs @@ -1,90 +1,52 @@ +//! Utilities for key generation. + use std::path::Path; -use pgp::{ - crypto::{hash::HashAlgorithm, sym::SymmetricKeyAlgorithm}, - types::{CompressionAlgorithm, KeyTrait, SecretKeyTrait as _}, - KeyType, SecretKeyParamsBuilder, -}; -use smallvec::smallvec; +use pgp::types::KeyTrait; use tokio::{ fs::{self, DirBuilder}, try_join, }; use zeroize::Zeroizing; -use crate::{error::Result, key_pair::KeyPair, secret_file::write_secret_file}; +use crate::{key_pair::KeyPair, secret_file::write_secret_file, Result}; +/// Write a key pair generated from `name` and `email` to a given path, in armored form as RFC 4880. pub async fn write_key_pair(name: &str, email: &str, path: impl AsRef) -> Result<()> { let path = path.as_ref(); // Create output directory if not exist. DirBuilder::new().recursive(true).create(path).await?; - let key_pair = gen_key_pair(name, email)?; + let key_pair = KeyPair::generate(name, email)?; let signed_secret_key = key_pair.secret_key(); let signed_public_key = key_pair.public_key(); - let keyid = &hex::encode_upper(&signed_secret_key.key_id().as_ref()[4..]); + let key_id = &hex::encode_upper(&signed_secret_key.key_id().as_ref()[4..]); - let secret_key = path.join(format!("{}_0x{}_SECRET.asc", name, keyid)); - let public_key = path.join(format!("{}_0x{}_public.asc", name, keyid)); + let secret_key = path.join(format!("{}_0x{}_SECRET.asc", name, key_id)); + let public_key = path.join(format!("{}_0x{}_public.asc", name, key_id)); - let secret_key_pem = Zeroizing::new(signed_secret_key.to_armored_bytes(None)?); - let pub_key_pem = signed_public_key.to_armored_bytes(None)?; + let secret_key_armored = Zeroizing::new(signed_secret_key.to_armored_bytes(None)?); + let public_key_armored = signed_public_key.to_armored_bytes(None)?; try_join!( - write_secret_file(&secret_key, &secret_key_pem), - fs::write(&public_key, &pub_key_pem) + write_secret_file(&secret_key, &secret_key_armored), + fs::write(&public_key, &public_key_armored) )?; Ok(()) } -pub(crate) fn gen_key_pair(name: &str, email: &str) -> Result { - let secret_key = SecretKeyParamsBuilder::default() - // Set keygen params. - .key_type(KeyType::EdDSA) - .primary_user_id(format!("{} <{}>", name, email)) - .preferred_symmetric_algorithms(smallvec![ - SymmetricKeyAlgorithm::AES256, - SymmetricKeyAlgorithm::AES192, - SymmetricKeyAlgorithm::AES128, - SymmetricKeyAlgorithm::TripleDES, - ]) - .preferred_hash_algorithms(smallvec![ - HashAlgorithm::SHA2_512, - HashAlgorithm::SHA2_384, - HashAlgorithm::SHA2_256, - HashAlgorithm::SHA2_224, - HashAlgorithm::SHA1 - ]) - .preferred_compression_algorithms(smallvec![ - CompressionAlgorithm::ZLIB, - CompressionAlgorithm::BZip2, - CompressionAlgorithm::ZIP - ]) - .can_sign(true) - .build() - .expect("msg") - .generate() - .expect("Failed to generate a plain key."); - let passwd_fn = String::new; - let signed_secret_key = secret_key.sign(passwd_fn)?; - let public_key = signed_secret_key.public_key(); - let signed_public_key = public_key.sign(&signed_secret_key, passwd_fn)?; - - Ok(KeyPair::from_keys(signed_secret_key, signed_public_key)) -} - #[cfg(test)] mod tests { use pgp::{types::KeyTrait, Deserializable, SignedSecretKey}; - use super::{gen_key_pair, Result}; + use crate::{key_pair::KeyPair, Result}; #[tokio::test] #[ignore = "Manual testing for file generation."] async fn test() -> Result<()> { - let key_pair = gen_key_pair("DS", "ds@example.com")?; + let key_pair = KeyPair::generate("DS", "ds@example.com")?; let (secret_key, public_key) = (key_pair.secret_key(), key_pair.public_key()); println!("{}", secret_key.to_armored_string(None)?); println!("{}", public_key.to_armored_string(None)?); @@ -95,18 +57,15 @@ mod tests { #[tokio::test] #[ignore = "Manual testing for file parsing."] async fn extract_key_info() -> Result<()> { - let secret_key_str = gen_key_pair("DS", "ds@example.com")? + let secret_key_str = KeyPair::generate("DS", "ds@example.com")? .secret_key() .to_armored_string(None)?; let secret_key = SignedSecretKey::from_string(&secret_key_str)?.0; dbg!(&secret_key); let key_id = secret_key.key_id(); - dbg!(&key_id); - dbg!(&hex::encode_upper(&key_id.as_ref()[4..])); - Ok(()) } } diff --git a/src-tauri/crypto/src/lib.rs b/src-tauri/crypto/src/lib.rs index ab0cf50..d930f60 100644 --- a/src-tauri/crypto/src/lib.rs +++ b/src-tauri/crypto/src/lib.rs @@ -1,5 +1,10 @@ +#![warn(missing_docs)] +//! Cryptographic functions for the digital signature app. + pub mod error; mod key_pair; pub mod keygen; mod secret_file; pub mod signing; + +pub use error::Result; diff --git a/src-tauri/crypto/src/signing.rs b/src-tauri/crypto/src/signing.rs index 94d1384..c6703b1 100644 --- a/src-tauri/crypto/src/signing.rs +++ b/src-tauri/crypto/src/signing.rs @@ -1,3 +1,5 @@ +//! Utilities for signing files. + use chrono::Utc; use pgp::{ packet::{SignatureConfigBuilder, SignatureType, Subpacket, SubpacketData}, @@ -5,8 +7,9 @@ use pgp::{ Signature, }; -use crate::error::Result; +use crate::Result; +/// Generate a signature of the given data. pub fn sign(data: &[u8], secret_key: &impl SecretKeyTrait) -> Result { let now = Utc::now(); let sig_conf = SignatureConfigBuilder::default() @@ -23,6 +26,7 @@ pub fn sign(data: &[u8], secret_key: &impl SecretKeyTrait) -> Result Ok(sig_conf.sign(secret_key, String::new, data)?) } +/// Verify a signature of the given data. pub fn verify(data: &[u8], public_key: &impl PublicKeyTrait, signature: &Signature) -> Result<()> { Ok(signature.verify(public_key, data)?) } @@ -30,11 +34,11 @@ pub fn verify(data: &[u8], public_key: &impl PublicKeyTrait, signature: &Signatu #[cfg(test)] mod tests { use super::{sign, verify, Result}; - use crate::keygen::gen_key_pair; + use crate::key_pair::KeyPair; #[test] fn test_sign() -> Result<()> { - let key_pair = gen_key_pair("DS", "ds@example.com")?; + let key_pair = KeyPair::generate("DS", "ds@example.com")?; let (secret_key, public_key) = (key_pair.secret_key(), key_pair.public_key()); let signature = sign(b"Hello", &secret_key)?; verify(b"Hello", &public_key, &signature)?; @@ -43,7 +47,7 @@ mod tests { #[test] fn test_sign_error() -> Result<()> { - let key_pair = gen_key_pair("DS", "ds@example.com")?; + let key_pair = KeyPair::generate("DS", "ds@example.com")?; let (secret_key, public_key) = (key_pair.secret_key(), key_pair.public_key()); let signature = sign(b"Hello", &secret_key)?; eprintln!(