Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
Improvements to keystore
Alexis Sellier committed 3 years ago
commit f193d7d6ccb8e1176723448d17dda91fc2a584ad
parent 2f1ea7f8dae064dc873256e425cdf8e926cd674b
7 files changed +34 -13
modified radicle-cli/src/commands/auth.rs
@@ -80,7 +80,7 @@ pub fn init(options: Options) -> anyhow::Result<()> {
    let home = profile::home()?;
    let passphrase = term::read_passphrase(options.stdin, true)?;
    let spinner = term::spinner("Creating your 🌱 Ed25519 keypair...");
-
    let profile = Profile::init(home, passphrase.as_str())?;
+
    let profile = Profile::init(home, passphrase)?;
    spinner.finish();

    term::success!(
modified radicle-cli/tests/commands.rs
@@ -37,7 +37,7 @@ fn profile(home: &Path) -> Profile {
    // Set debug mode, to make test output more predictable.
    env::set_var("RAD_DEBUG", "1");
    // Setup a new user.
-
    Profile::init(home, "radicle").unwrap()
+
    Profile::init(home, "radicle".to_owned()).unwrap()
}

#[test]
modified radicle-crypto/src/lib.rs
@@ -220,6 +220,12 @@ impl From<ed25519::SecretKey> for SecretKey {
    }
}

+
impl From<SecretKey> for ed25519::SecretKey {
+
    fn from(other: SecretKey) -> Self {
+
        other.0
+
    }
+
}
+

impl Deref for SecretKey {
    type Target = ed25519::SecretKey;

modified radicle-crypto/src/ssh.rs
@@ -13,6 +13,8 @@ use radicle_ssh::encoding::Reader;
use crate as crypto;
use crate::PublicKey;

+
pub use keystore::{Keystore, Passphrase};
+

pub mod fmt {
    use radicle_ssh::encoding::Encoding as _;

modified radicle-crypto/src/ssh/keystore.rs
@@ -5,7 +5,7 @@ use std::{fs, io};
use thiserror::Error;
use zeroize::Zeroizing;

-
use crate::{keypair, PublicKey, SecretKey, SharedSecret, Signature, Signer, SignerError};
+
use crate::{keypair, KeyPair, PublicKey, SecretKey, SharedSecret, Signature, Signer, SignerError};

/// A secret key passphrase.
pub type Passphrase = Zeroizing<String>;
@@ -46,12 +46,25 @@ impl Keystore {
    ///
    /// The `comment` is associated with the private key.
    /// The `passphrase` is used to encrypt the private key.
-
    pub fn init(&self, comment: &str, passphrase: &str) -> Result<PublicKey, Error> {
-
        let pair = keypair::generate();
-
        let ssh_pair = ssh_key::private::Ed25519Keypair::from_bytes(&pair)?;
+
    pub fn init(
+
        &self,
+
        comment: &str,
+
        passphrase: impl Into<Passphrase>,
+
    ) -> Result<PublicKey, Error> {
+
        self.store(keypair::generate(), comment, passphrase)
+
    }
+

+
    /// Store a keypair on disk. Returns an error if the key already exists.
+
    pub fn store(
+
        &self,
+
        keypair: KeyPair,
+
        comment: &str,
+
        passphrase: impl Into<Passphrase>,
+
    ) -> Result<PublicKey, Error> {
+
        let ssh_pair = ssh_key::private::Ed25519Keypair::from_bytes(&keypair)?;
        let ssh_pair = ssh_key::private::KeypairData::Ed25519(ssh_pair);
        let secret = ssh_key::PrivateKey::new(ssh_pair, comment)?;
-
        let secret = secret.encrypt(ssh_key::rand_core::OsRng, passphrase)?;
+
        let secret = secret.encrypt(ssh_key::rand_core::OsRng, passphrase.into())?;
        let public = secret.public_key();
        let path = self.path.join("radicle");

@@ -67,7 +80,7 @@ impl Keystore {
        secret.write_openssh_file(&path, ssh_key::LineEnding::default())?;
        public.write_openssh_file(&path.with_extension("pub"))?;

-
        Ok(pair.pk.into())
+
        Ok(keypair.pk.into())
    }

    /// Load the public key from the store. Returns `None` if it wasn't found.
@@ -195,7 +208,7 @@ mod tests {
        let tmp = tempfile::tempdir().unwrap();
        let store = Keystore::new(&tmp.path());

-
        let public = store.init("test", "hunter").unwrap();
+
        let public = store.init("test", "hunter".to_owned()).unwrap();
        assert_eq!(public, store.public_key().unwrap().unwrap());

        let secret = store
@@ -212,7 +225,7 @@ mod tests {
        let tmp = tempfile::tempdir().unwrap();
        let store = Keystore::new(&tmp.path());

-
        let public = store.init("test", "hunter").unwrap();
+
        let public = store.init("test", "hunter".to_owned()).unwrap();
        let signer = MemorySigner::load(&store, "hunter".to_owned().into()).unwrap();

        assert_eq!(public, *signer.public_key());
modified radicle-tools/src/rad-auth.rs
@@ -4,7 +4,7 @@ use radicle::profile::{Error, Profile};
fn main() -> anyhow::Result<()> {
    let profile = match Profile::load() {
        Ok(profile) => profile,
-
        Err(Error::NotFound(_)) => Profile::init(profile::home()?, "radicle")?,
+
        Err(Error::NotFound(_)) => Profile::init(profile::home()?, "radicle".to_owned())?,
        Err(err) => anyhow::bail!(err),
    };

modified radicle/src/profile.rs
@@ -16,7 +16,7 @@ use std::path::{Path, PathBuf};
use thiserror::Error;

use crate::crypto::ssh::agent::{Agent, AgentSigner};
-
use crate::crypto::ssh::keystore::Keystore;
+
use crate::crypto::ssh::{Keystore, Passphrase};
use crate::crypto::PublicKey;
use crate::node;
use crate::storage::git::transport;
@@ -57,7 +57,7 @@ pub struct Profile {
}

impl Profile {
-
    pub fn init(home: impl AsRef<Path>, passphrase: &str) -> Result<Self, Error> {
+
    pub fn init(home: impl AsRef<Path>, passphrase: impl Into<Passphrase>) -> Result<Self, Error> {
        let home = home.as_ref().to_path_buf();
        let storage = Storage::open(home.join("storage"))?;
        let keystore = Keystore::new(&home.join("keys"));