remove unneeded osshkeys dependency
This commit is contained in:
parent
70693fe768
commit
fe5245fc31
|
@ -9,7 +9,7 @@ path = "src/lib.rs"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["passphrase-gen", "password-gen", "ed25519", "clap", "rpassword", "base64"]
|
default = ["passphrase-gen", "password-gen", "ed25519", "clap", "rpassword", "base64"]
|
||||||
ed25519 = ["osshkeys", "sha2"]
|
ed25519 = ["sha2"]
|
||||||
passphrase-gen = []
|
passphrase-gen = []
|
||||||
password-gen = []
|
password-gen = []
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ rand_chacha = "0.3"
|
||||||
clap = {version = "3.0.0-beta.5", optional = true, features = ["derive"]}
|
clap = {version = "3.0.0-beta.5", optional = true, features = ["derive"]}
|
||||||
base64 = {version = "0.13", optional = true}
|
base64 = {version = "0.13", optional = true}
|
||||||
bytes = {version = "1.1", optional = true}
|
bytes = {version = "1.1", optional = true}
|
||||||
osshkeys = {git = "https://github.com/noonebtw/rust-osshkeys.git", branch = "master", optional = true}
|
|
||||||
sha2 = {version = "0.9", optional = true}
|
sha2 = {version = "0.9", optional = true}
|
||||||
rpassword = {version = "5.0", optional = true}
|
rpassword = {version = "5.0", optional = true}
|
||||||
zeroize = {version = "1.5"}
|
zeroize = {version = "1.5"}
|
||||||
|
|
|
@ -12,166 +12,6 @@ struct Opts {
|
||||||
file: String,
|
file: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod keygen {
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use osshkeys::{error::OsshResult, keys::ed25519::Ed25519KeyPair, KeyPair};
|
|
||||||
use rand::{Rng, SeedableRng};
|
|
||||||
use sha2::Digest;
|
|
||||||
use thiserror::Error;
|
|
||||||
use zeroize::Zeroizing;
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
pub enum Error {
|
|
||||||
#[error("Failed to parse")]
|
|
||||||
ParseError,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum KeyType {
|
|
||||||
SSH,
|
|
||||||
PGP,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for KeyType {
|
|
||||||
type Err = Error;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
match s.to_lowercase().as_str() {
|
|
||||||
"ssh" => Ok(Self::SSH),
|
|
||||||
"pgp" => Ok(Self::PGP),
|
|
||||||
_ => Err(Error::ParseError),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum HashType {
|
|
||||||
Sha256,
|
|
||||||
Argon2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for HashType {
|
|
||||||
type Err = Error;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
match s.to_lowercase().as_str() {
|
|
||||||
"sha256" => Ok(Self::Sha256),
|
|
||||||
"argon2" | "argon" => Ok(Self::Argon2),
|
|
||||||
_ => Err(Error::ParseError),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct SshKeyBuilder {
|
|
||||||
hash_type: HashType,
|
|
||||||
iterations: i16,
|
|
||||||
tag: Zeroizing<String>,
|
|
||||||
passphrase: Zeroizing<String>,
|
|
||||||
encrypt_key: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for SshKeyBuilder {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
hash_type: HashType::Argon2,
|
|
||||||
iterations: 4,
|
|
||||||
tag: Default::default(),
|
|
||||||
passphrase: Default::default(),
|
|
||||||
encrypt_key: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SshKeyBuilder {
|
|
||||||
pub fn with_hash_type(mut self, hash_type: HashType) -> Self {
|
|
||||||
self.hash_type = hash_type;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_encrypt(mut self, encrypt: bool) -> Self {
|
|
||||||
self.encrypt_key = encrypt;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_hash_type_and_iterations(self, hash_type: HashType, iterations: i16) -> Self {
|
|
||||||
self.with_hash_type(hash_type).with_iterations(iterations)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_iterations(mut self, iterations: i16) -> Self {
|
|
||||||
self.iterations = iterations;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_maybe_tag(self, tag: Option<String>) -> Self {
|
|
||||||
match tag {
|
|
||||||
Some(tag) => self.with_tag(tag),
|
|
||||||
None => self,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_tag(mut self, tag: String) -> Self {
|
|
||||||
self.tag = Zeroizing::new(tag);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_passphrase<Z: Into<Zeroizing<String>>>(mut self, passphrase: Z) -> Self {
|
|
||||||
self.passphrase = passphrase.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_keypair(self) -> OsshResult<((Zeroizing<String>, String), KeyPair)> {
|
|
||||||
let hash_seed = Zeroizing::new(
|
|
||||||
self.passphrase
|
|
||||||
.chars()
|
|
||||||
.chain(self.tag.chars())
|
|
||||||
.collect::<String>(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut seed = [0u8; 32];
|
|
||||||
match self.hash_type {
|
|
||||||
HashType::Sha256 => {
|
|
||||||
let mut digest = sha2::Sha256::digest(hash_seed.as_bytes());
|
|
||||||
|
|
||||||
assert!(self.iterations <= 1);
|
|
||||||
for _ in 0..(self.iterations - 1) {
|
|
||||||
digest = sha2::Sha256::digest(digest.as_slice());
|
|
||||||
}
|
|
||||||
|
|
||||||
seed.copy_from_slice(&digest[..32]);
|
|
||||||
}
|
|
||||||
HashType::Argon2 => {
|
|
||||||
// TODO: make more random salt
|
|
||||||
let salt = b"thissaltneedsupdating";
|
|
||||||
|
|
||||||
let argon = argon2::Argon2::new(
|
|
||||||
argon2::Algorithm::Argon2i,
|
|
||||||
argon2::Version::V0x13,
|
|
||||||
argon2::Params::new(65536, self.iterations as u32, 4, Some(32))
|
|
||||||
.expect("argon2 params"),
|
|
||||||
);
|
|
||||||
|
|
||||||
argon
|
|
||||||
.hash_password_into(hash_seed.as_bytes(), salt, &mut seed)
|
|
||||||
.expect("hash passphrase");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let rng = rand_chacha::ChaChaRng::from_seed(seed).gen::<[u8; 32]>();
|
|
||||||
|
|
||||||
let keypair = Ed25519KeyPair::from_seed(&rng).map(|key| Into::<KeyPair>::into(key))?;
|
|
||||||
|
|
||||||
let private_key = Zeroizing::new(keypair.serialize_openssh(
|
|
||||||
self.encrypt_key.then(|| self.passphrase.as_str()),
|
|
||||||
osshkeys::cipher::Cipher::Aes256_Ctr,
|
|
||||||
)?);
|
|
||||||
|
|
||||||
Ok(((private_key, keypair.serialize_publickey()?), keypair))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
let opts = Opts::parse();
|
let opts = Opts::parse();
|
||||||
println!("Generating ed25519 ssh keypair:");
|
println!("Generating ed25519 ssh keypair:");
|
||||||
|
|
|
@ -1,35 +1,9 @@
|
||||||
use osshkeys::{error::OsshResult, keys::ed25519::Ed25519KeyPair, KeyPair};
|
|
||||||
use sha2::{Digest, Sha256};
|
|
||||||
|
|
||||||
pub fn generate_ed25519_keypair<S1, S2>(
|
|
||||||
passphrase: S1,
|
|
||||||
id: Option<S2>,
|
|
||||||
) -> OsshResult<KeyPair>
|
|
||||||
where
|
|
||||||
S1: AsRef<str>,
|
|
||||||
S2: AsRef<str>,
|
|
||||||
{
|
|
||||||
let hash = {
|
|
||||||
let mut hasher = Sha256::new();
|
|
||||||
hasher.update(passphrase.as_ref());
|
|
||||||
if let Some(id) = id {
|
|
||||||
hasher.update(id.as_ref());
|
|
||||||
}
|
|
||||||
|
|
||||||
hasher.finalize()
|
|
||||||
};
|
|
||||||
|
|
||||||
Ed25519KeyPair::from_seed(&hash).map(|key| Into::<KeyPair>::into(key))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod randomart {
|
|
||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
const WIDTH: usize = 17;
|
const WIDTH: usize = 17;
|
||||||
const HEIGHT: usize = 9;
|
const HEIGHT: usize = 9;
|
||||||
const TILES: &[char] = &[
|
const TILES: &[char] = &[
|
||||||
' ', '.', 'o', '+', '=', '*', 'B', 'O', 'X', '@', '%', '&', '#', '/',
|
' ', '.', 'o', '+', '=', '*', 'B', 'O', 'X', '@', '%', '&', '#', '/', '^',
|
||||||
'^',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
pub struct RandomArt {
|
pub struct RandomArt {
|
||||||
|
@ -37,15 +11,10 @@ pub mod randomart {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RandomArt {
|
impl RandomArt {
|
||||||
pub fn render(
|
pub fn render(&self, title: &str, subtitle: &str) -> Result<String, std::fmt::Error> {
|
||||||
&self,
|
|
||||||
title: &str,
|
|
||||||
subtitle: &str,
|
|
||||||
) -> Result<String, std::fmt::Error> {
|
|
||||||
let mut string = String::new();
|
let mut string = String::new();
|
||||||
|
|
||||||
string
|
string.write_str(&format!("+{:-^17}+\n", format!("[{}]", title)))?;
|
||||||
.write_str(&format!("+{:-^17}+\n", format!("[{}]", title)))?;
|
|
||||||
|
|
||||||
for row in self.tiles {
|
for row in self.tiles {
|
||||||
string.write_char('|')?;
|
string.write_char('|')?;
|
||||||
|
@ -63,10 +32,7 @@ pub mod randomart {
|
||||||
string.write_char('\n')?;
|
string.write_char('\n')?;
|
||||||
}
|
}
|
||||||
|
|
||||||
string.write_str(&format!(
|
string.write_str(&format!("+{:-^17}+\n", format!("[{}]", subtitle)))?;
|
||||||
"+{:-^17}+\n",
|
|
||||||
format!("[{}]", subtitle)
|
|
||||||
))?;
|
|
||||||
|
|
||||||
Ok(string)
|
Ok(string)
|
||||||
}
|
}
|
||||||
|
@ -124,53 +90,14 @@ pub mod randomart {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
const FINGERPRINT: &str =
|
const FINGERPRINT: &str = "L5N7A2PETaGegW5P3qh/Vjd8AW6Mn4B+VB2SHK+eZCY=";
|
||||||
"L5N7A2PETaGegW5P3qh/Vjd8AW6Mn4B+VB2SHK+eZCY=";
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn render() {
|
fn render() {
|
||||||
let randomart =
|
let randomart = RandomArt::from_digest(&base64::decode(FINGERPRINT).unwrap())
|
||||||
RandomArt::from_digest(&base64::decode(FINGERPRINT).unwrap())
|
|
||||||
.render("Title", "Subtitle")
|
.render("Title", "Subtitle")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
println!("{}", randomart);
|
println!("{}", randomart);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
mod tests {
|
|
||||||
#![allow(dead_code)]
|
|
||||||
#![allow(unused_imports)]
|
|
||||||
use osshkeys::{
|
|
||||||
keys::ed25519::Ed25519KeyPair, KeyPair, PrivateParts, PublicParts,
|
|
||||||
};
|
|
||||||
use sha2::Digest;
|
|
||||||
|
|
||||||
use super::randomart::RandomArt;
|
|
||||||
|
|
||||||
const PASSPHRASE: &str = "the spice must flow";
|
|
||||||
|
|
||||||
const DATA: &str = "I’d just like to interject for a moment. What you’re refering to as Linux, is in fact, GNU/Linux, or as I’ve recently taken to calling it, GNU plus Linux. Linux is not an operating system unto itself, but rather another free component of a fully functioning GNU system made useful by the GNU corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX.";
|
|
||||||
|
|
||||||
const SIGNATURE: &[u8] = &[
|
|
||||||
115, 18, 156, 87, 192, 149, 107, 105, 13, 87, 219, 90, 26, 146, 41,
|
|
||||||
114, 20, 143, 253, 206, 216, 236, 222, 66, 252, 136, 38, 216, 184, 127,
|
|
||||||
94, 255, 68, 246, 64, 228, 141, 64, 63, 64, 236, 222, 184, 214, 3, 157,
|
|
||||||
73, 186, 73, 156, 20, 100, 76, 241, 113, 81, 38, 131, 174, 31, 103,
|
|
||||||
181, 220, 11,
|
|
||||||
];
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test() {
|
|
||||||
let mut hasher = sha2::Sha256::new();
|
|
||||||
hasher.update(PASSPHRASE);
|
|
||||||
|
|
||||||
let hash = hasher.finalize();
|
|
||||||
|
|
||||||
let keypair: KeyPair = Ed25519KeyPair::from_seed(&hash).unwrap().into();
|
|
||||||
|
|
||||||
let asdf = keypair.sign(DATA.as_bytes()).unwrap();
|
|
||||||
assert_eq!(keypair.verify(DATA.as_bytes(), SIGNATURE).unwrap(), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use russh_keys::PublicKeyBase64;
|
||||||
use sha2::Digest;
|
use sha2::Digest;
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
||||||
use crate::ed25519::randomart;
|
use crate::randomart;
|
||||||
|
|
||||||
pub mod cli {
|
pub mod cli {
|
||||||
use zeroize::Zeroizing;
|
use zeroize::Zeroizing;
|
||||||
|
|
|
@ -6,8 +6,8 @@ pub mod passphrase_gen;
|
||||||
#[cfg(feature = "password-gen")]
|
#[cfg(feature = "password-gen")]
|
||||||
pub mod password_gen;
|
pub mod password_gen;
|
||||||
|
|
||||||
#[cfg(feature = "ed25519")]
|
#[path = "ed25519.rs"]
|
||||||
pub mod ed25519;
|
pub mod randomart;
|
||||||
|
|
||||||
#[cfg(feature = "ed25519")]
|
#[cfg(feature = "ed25519")]
|
||||||
pub mod key_gen;
|
pub mod key_gen;
|
||||||
|
|
Loading…
Reference in a new issue