Skip to content

Commit

Permalink
feat: ✨ Add keypair generation.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jisu-Woniu committed Nov 6, 2023
1 parent 42a5764 commit a43d5c8
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 31 deletions.
14 changes: 14 additions & 0 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src-tauri/crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@ ed25519-dalek = { version = "2.0.0", features = ["rand_core"] }
rand = "0.8.5"
rsa = { version = "0.9.3", features = ["sha2", "getrandom"] }
signature = { version = "2.1.0", features = ["digest"] }
thiserror = "1.0.50"

[dependencies.tokio]
version = "1.33.0"
features = ["rt-multi-thread", "fs", "io-util", "macros"]
33 changes: 12 additions & 21 deletions src-tauri/crypto/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,20 @@
use std::io;

use ed25519::pkcs8::{self, spki};
use rsa::pkcs8::der;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum Error {
Io(io::Error),
Pkcs8(pkcs8::Error),
Spki(spki::Error),
}

pub type Result<T> = core::result::Result<T, Error>;
#[error(transparent)]
Io(#[from] io::Error),

impl From<io::Error> for Error {
fn from(value: io::Error) -> Self {
Self::Io(value)
}
#[error(transparent)]
Pkcs8(#[from] pkcs8::Error),
#[error(transparent)]
Spki(#[from] spki::Error),
#[error(transparent)]
Der(#[from] der::Error),
}

impl From<pkcs8::Error> for Error {
fn from(value: pkcs8::Error) -> Self {
Self::Pkcs8(value)
}
}

impl From<spki::Error> for Error {
fn from(value: spki::Error) -> Self {
Self::Spki(value)
}
}
pub type Result<T> = core::result::Result<T, Error>;
68 changes: 58 additions & 10 deletions src-tauri/crypto/src/keygen.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
use crate::error::Result;
use crate::{error::Result, secret_file::write_secret_file};
use std::{
fs::DirBuilder,
io::{self},
path::Path,
};

use ed25519::{
pkcs8::{EncodePrivateKey, EncodePublicKey},
KeypairBytes,
KeypairBytes, PublicKeyBytes,
};
use ed25519_dalek::SigningKey;
use rand::thread_rng;
use rsa::pkcs8::LineEnding;
use tokio::{
fs::{self, DirBuilder},
try_join,
};

pub fn write_key_pair(path: &Path) -> Result<()> {
pub async fn write_key_pair(path: impl AsRef<Path>) -> Result<()> {
let path = path.as_ref();
if let Some(parent) = path.parent() {
let name = path
.file_name()
.ok_or(io::Error::from(io::ErrorKind::InvalidInput))?;
DirBuilder::new().recursive(true).create(parent)?;
DirBuilder::new().recursive(true).create(parent).await?;
let mut priv_name = name.to_os_string();
priv_name.push("_priv.pem");

Expand All @@ -30,13 +34,57 @@ pub fn write_key_pair(path: &Path) -> Result<()> {

let key_pair =
KeypairBytes::from_bytes(&SigningKey::generate(&mut thread_rng()).to_keypair_bytes());
key_pair.write_pkcs8_pem_file(priv_key, LineEnding::default())?;
key_pair
.public_key
.unwrap()
.write_public_key_pem_file(pub_key, LineEnding::default())?;

let priv_key_pem = key_pair
.to_pkcs8_der()?
.to_pem("PRIVATE KEY", LineEnding::default())?;

let pub_key_pem = PublicKeyBytes::try_from(&key_pair)?
.to_public_key_der()?
.to_pem("PUBLIC KEY", LineEnding::default())?;

try_join!(
write_secret_file(&priv_key, priv_key_pem.as_bytes()),
fs::write(&pub_key, pub_key_pem.as_bytes())
)?;
} else {
Err(io::Error::from(io::ErrorKind::InvalidInput))?
}
Ok(())
}
#[cfg(test)]
mod tests {
use std::{env, io, path::PathBuf};

use rand::{distributions::Alphanumeric, Rng};

use tokio::fs;

use crate::error::Result;

use super::write_key_pair;

pub async fn tmp_dir(prefix: impl AsRef<str>) -> io::Result<PathBuf> {
let mut inner = env::temp_dir();
let s: String = {
// shrink scope of rng
let rng = rand::thread_rng();
rng.sample_iter(Alphanumeric)
.map(char::from)
.take(10)
.collect()
};

inner.push(&format!("{}-{}", prefix.as_ref(), s));

fs::create_dir(&inner).await?;
Ok(inner)
}

#[tokio::test]
async fn test() -> Result<()> {
let tmp = tmp_dir("keygen").await?;
write_key_pair(dbg!(tmp).join("key")).await?;
Ok(())
}
}
1 change: 1 addition & 0 deletions src-tauri/crypto/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod error;
pub mod keygen;
mod secret_file;
pub fn add(left: usize, right: usize) -> usize {
left + right
}
Expand Down
31 changes: 31 additions & 0 deletions src-tauri/crypto/src/secret_file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// use crate::error::Result;
use std::io::Result;
use std::path::Path;

#[cfg(unix)]
pub(crate) async fn write_secret_file(path: impl AsRef<Path>, data: &[u8]) -> Result<()> {
use tokio::{fs, io::AsyncWriteExt};

/// File permissions for secret data
#[cfg(unix)]
const SECRET_FILE_PERMS: u32 = 0o600;

let mut file = fs::OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.mode(SECRET_FILE_PERMS)
.open(path)
.await?;
file.write_all(data).await?;

Ok(())
}

#[cfg(not(unix))]
pub(crate) async fn write_secret_file(path: impl AsRef<Path>, data: &[u8]) -> Result<()> {
use tokio::fs;

fs::write(path, data).await?;
Ok(())
}

0 comments on commit a43d5c8

Please sign in to comment.