Compare commits
5 commits
6259840c43
...
76ebacacf2
Author | SHA1 | Date | |
---|---|---|---|
|
76ebacacf2 | ||
|
27789f94d2 | ||
|
7e9ad7c793 | ||
|
d3739d3d06 | ||
|
88d743227a |
|
@ -24,9 +24,9 @@ required-features = ["ed25519", "clap", "rpassword", "base64"]
|
|||
|
||||
[dependencies]
|
||||
rand = "0.8.4"
|
||||
clap = {version = "3.0.0-beta.5", optional = true}
|
||||
clap = {version = "3.0.0-beta.5", optional = true, features = ["derive"]}
|
||||
base64 = {version = "0.13.0", optional = true}
|
||||
bytes = {version = "1.1.0", optional = true}
|
||||
osshkeys = {path = "../rust-osshkeys", optional = true}
|
||||
osshkeys = {git = "https://github.com/noonebtw/rust-osshkeys.git", branch = "master", optional = true}
|
||||
sha2 = {version = "0.9.8", optional = true}
|
||||
rpassword = {version = "5.0.1", optional = true}
|
|
@ -1,8 +1,9 @@
|
|||
use std::io::Write;
|
||||
|
||||
#[macro_use]
|
||||
use clap::Parser;
|
||||
use libduralumin::ed25519::{generate_ed25519_keypair, randomart};
|
||||
use osshkeys::{error::OsshResult, Key, PublicParts};
|
||||
use osshkeys::{error::OsshResult, PublicParts};
|
||||
|
||||
/// program that generates ed25519 keypairs seeded by a passphrase and an optional ID.
|
||||
#[derive(Parser)]
|
||||
|
@ -16,6 +17,23 @@ struct Opts {
|
|||
file: String,
|
||||
}
|
||||
|
||||
fn fix_newline_ref(line: &mut String) {
|
||||
if line.ends_with('\n') {
|
||||
line.pop();
|
||||
|
||||
if line.ends_with('\r') {
|
||||
line.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn fix_newline(mut line: String) -> String {
|
||||
fix_newline_ref(&mut line);
|
||||
|
||||
line
|
||||
}
|
||||
|
||||
fn main() -> OsshResult<()> {
|
||||
let opts = Opts::parse();
|
||||
println!("Generating ed25519 keypair:");
|
||||
|
@ -23,12 +41,24 @@ fn main() -> OsshResult<()> {
|
|||
print!("Enter a passphrase: ");
|
||||
std::io::stdout().flush()?;
|
||||
let passphrase = rpassword::read_password()?;
|
||||
print!("Re-enter the same passphrase: ");
|
||||
std::io::stdout().flush()?;
|
||||
let passphrase2 = rpassword::read_password()?;
|
||||
|
||||
if passphrase != passphrase2 {
|
||||
println!("passphrases do not match.");
|
||||
Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
"passphrase did not match.",
|
||||
))?;
|
||||
}
|
||||
|
||||
print!("Enter an optional ID []: ");
|
||||
std::io::stdout().flush()?;
|
||||
let id = {
|
||||
let mut id = String::new();
|
||||
std::io::stdin().read_line(&mut id)?;
|
||||
fix_newline_ref(&mut id);
|
||||
|
||||
if id.is_empty() {
|
||||
None
|
||||
|
@ -45,31 +75,48 @@ fn main() -> OsshResult<()> {
|
|||
let encrypt = {
|
||||
let mut line = String::new();
|
||||
std::io::stdin().read_line(&mut line)?;
|
||||
fix_newline_ref(&mut line);
|
||||
|
||||
line.to_lowercase() == "y"
|
||||
line.to_lowercase() != "n"
|
||||
};
|
||||
|
||||
let fingerprint =
|
||||
keypair.fingerprint(osshkeys::keys::FingerprintHash::SHA256)?;
|
||||
if encrypt {
|
||||
println!("Using passphrase to encrypt key..");
|
||||
}
|
||||
|
||||
let fingerprint = keypair.fingerprint(osshkeys::keys::FingerprintHash::SHA256)?;
|
||||
|
||||
println!(
|
||||
"Your key fingerprint is: Sha256:{}",
|
||||
base64::encode(&fingerprint)
|
||||
);
|
||||
|
||||
let randomart = randomart::RandomArt::from_digest(&fingerprint)
|
||||
.render("ED25519 256", "SHA256")?;
|
||||
let randomart =
|
||||
randomart::RandomArt::from_digest(&fingerprint).render("ED25519 256", "SHA256")?;
|
||||
|
||||
println!("RandomArt:\n{}", randomart);
|
||||
|
||||
let private_path = opts.file.clone();
|
||||
let public_path = opts.file.clone() + ".pub";
|
||||
|
||||
let private_key = keypair.serialize_openssh(
|
||||
Some(&passphrase),
|
||||
encrypt.then(|| passphrase.as_str()),
|
||||
osshkeys::cipher::Cipher::Aes256_Ctr,
|
||||
)?;
|
||||
std::fs::write(&opts.file, private_key)?;
|
||||
std::fs::write(&private_path, private_key)?;
|
||||
|
||||
let public_key = keypair.serialize_publickey()?;
|
||||
std::fs::write(opts.file + ".pub", public_key)?;
|
||||
std::fs::write(&public_path, public_key)?;
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
use std::fs::Permissions;
|
||||
#[cfg(target_family = "unix")]
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
#[cfg(target_family = "unix")]
|
||||
std::fs::set_permissions(private_path, Permissions::from_mode(0o0600))?;
|
||||
#[cfg(target_family = "unix")]
|
||||
std::fs::set_permissions(public_path, Permissions::from_mode(0o0600))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#[macro_use]
|
||||
use clap::Parser;
|
||||
use libduralumin::passphrase_gen::{PassPhraseGenerator, Words};
|
||||
|
||||
|
@ -69,12 +70,8 @@ fn main() {
|
|||
.with_length(opts.words_per_passphrase());
|
||||
|
||||
let words = match opts.backend() {
|
||||
Some(Backend::UseAPassPhrase) => {
|
||||
Words::from_str(include_str!("../../useapassphrase.txt"))
|
||||
}
|
||||
Some(Backend::EnglishWords) => {
|
||||
Words::from_str(include_str!("../../words_alpha.txt"))
|
||||
}
|
||||
Some(Backend::UseAPassPhrase) => Words::from_str(include_str!("../../useapassphrase.txt")),
|
||||
Some(Backend::EnglishWords) => Words::from_str(include_str!("../../words_alpha.txt")),
|
||||
None => {
|
||||
panic!("invalid backend.")
|
||||
}
|
||||
|
|
110
src/ed25519.rs
110
src/ed25519.rs
|
@ -1,113 +1,6 @@
|
|||
use osshkeys::{error::OsshResult, keys::ed25519::Ed25519KeyPair, KeyPair};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
#[cfg(all(feature = "base64", feature = "bytes"))]
|
||||
mod asdf {
|
||||
use std::io::{Cursor, Read};
|
||||
|
||||
use bytes::Buf;
|
||||
|
||||
pub struct Ed25519PrivateKey {}
|
||||
|
||||
const OPENSSH_BEGIN: &str = "-----BEGIN OPENSSH PRIVATE KEY-----";
|
||||
const OPENSSH_END: &str = "-----END OPENSSH PRIVATE KEY-----";
|
||||
const OPENSSH_MAGIC: &str = "openssh-key-v1\0";
|
||||
|
||||
impl Ed25519PrivateKey {
|
||||
pub fn parse<S>(text: S) -> Option<()>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let mut lines = text.as_ref().lines();
|
||||
|
||||
let data = (lines.next() == Some(OPENSSH_BEGIN))
|
||||
.then(|| {
|
||||
let base64_content = lines
|
||||
.take_while(|&line| line != OPENSSH_END)
|
||||
.fold(String::new(), |mut acc, line| {
|
||||
acc.push_str(line);
|
||||
acc
|
||||
});
|
||||
|
||||
base64::decode(&base64_content).ok()
|
||||
})
|
||||
.flatten()
|
||||
.map(|data| Cursor::new(data))
|
||||
.and_then(|mut data| {
|
||||
let mut magic = vec![0u8; OPENSSH_MAGIC.len()];
|
||||
data.read_exact(&mut magic).unwrap();
|
||||
|
||||
// cypher name
|
||||
let cypher_len = data.get_u32();
|
||||
let mut cypher_name = vec![0u8; cypher_len as usize];
|
||||
data.read_exact(&mut cypher_name).unwrap();
|
||||
|
||||
// kdf name
|
||||
let kdf_name_len = data.get_u32();
|
||||
let mut kdf_name = vec![0u8; kdf_name_len as usize];
|
||||
data.read_exact(&mut kdf_name).unwrap();
|
||||
|
||||
// kdf
|
||||
let kdf_len = data.get_u32();
|
||||
let kdf = (kdf_len > 0).then(|| {
|
||||
let mut kdf = vec![0u8; kdf_len as usize];
|
||||
data.read_exact(&mut kdf).unwrap();
|
||||
kdf
|
||||
});
|
||||
|
||||
// key_count should always be `1`
|
||||
let key_count = data.get_u32();
|
||||
assert_eq!(key_count, 1);
|
||||
|
||||
// ssh public key
|
||||
|
||||
let pubkey_len = data.get_u32() as usize;
|
||||
|
||||
let keytype_len = data.get_u32() as usize;
|
||||
let mut keytype = vec![0u8; keytype_len];
|
||||
data.read_exact(&mut keytype).unwrap();
|
||||
|
||||
let pub1_len = data.get_u32() as usize;
|
||||
let mut pub1 = vec![0u8; pub1_len];
|
||||
data.read_exact(&mut pub1).unwrap();
|
||||
|
||||
let pub2_len = data.get_u32() as usize;
|
||||
let mut pub2 = vec![0u8; pub2_len];
|
||||
data.read_exact(&mut pub2).unwrap();
|
||||
|
||||
println!("magic: {}", String::from_utf8_lossy(&magic));
|
||||
println!(
|
||||
"cypher: {}",
|
||||
String::from_utf8_lossy(&cypher_name)
|
||||
);
|
||||
println!("kdf: {}", String::from_utf8_lossy(&kdf_name));
|
||||
|
||||
println!("keytype: {}", String::from_utf8_lossy(&keytype));
|
||||
println!("pub1[{}]: {:?}", pub1_len, &pub1);
|
||||
println!("pub2[{}]: {:?}", pub2_len, &pub2);
|
||||
|
||||
Some(())
|
||||
});
|
||||
|
||||
Some(())
|
||||
}
|
||||
}
|
||||
|
||||
mod tests {
|
||||
use super::Ed25519PrivateKey;
|
||||
|
||||
#[test]
|
||||
fn test_ed25519() {
|
||||
Ed25519PrivateKey::parse(include_str!("../ed25519"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ed25519_passphrased() {
|
||||
Ed25519PrivateKey::parse(include_str!("../ed25519-passphrased"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_ed25519_keypair<S1, S2>(
|
||||
passphrase: S1,
|
||||
id: Option<S2>,
|
||||
|
@ -132,8 +25,6 @@ where
|
|||
pub mod randomart {
|
||||
use std::fmt::Write;
|
||||
|
||||
use osshkeys::error::OsshResult;
|
||||
|
||||
const WIDTH: usize = 17;
|
||||
const HEIGHT: usize = 9;
|
||||
const TILES: &[char] = &[
|
||||
|
@ -229,6 +120,7 @@ pub mod randomart {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
|
|
740204
words_alpha.txt
740204
words_alpha.txt
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue