mirror of
https://github.com/ZeusWPI/ZNS.git
synced 2024-10-29 21:14:27 +01:00
implement handy zns cli
This commit is contained in:
parent
c766d4631f
commit
223988741f
9 changed files with 439 additions and 10 deletions
139
Cargo.lock
generated
139
Cargo.lock
generated
|
@ -17,6 +17,55 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is_terminal_polyfill",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "asn1"
|
||||
version = "0.16.2"
|
||||
|
@ -112,6 +161,52 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
||||
|
||||
[[package]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
|
@ -536,6 +631,12 @@ version = "2.9.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
|
@ -619,6 +720,34 @@ dependencies = [
|
|||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.36.2"
|
||||
|
@ -1240,6 +1369,12 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
|
@ -1523,6 +1658,10 @@ dependencies = [
|
|||
name = "zns-cli"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"clap",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"zns",
|
||||
]
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
[workspace]
|
||||
resolver = "2"
|
||||
|
||||
members = [
|
||||
"zns",
|
||||
|
|
|
@ -3,6 +3,12 @@ name = "zns-cli"
|
|||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.13", features = ["derive"] }
|
||||
base64 = "0.22.0"
|
||||
num-bigint = "0.4"
|
||||
num-traits = "0.2"
|
||||
|
||||
|
||||
[dependencies.zns]
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -1,3 +1,285 @@
|
|||
fn main() {
|
||||
todo!();
|
||||
use base64::prelude::*;
|
||||
use num_bigint::BigUint;
|
||||
use num_traits::FromPrimitive;
|
||||
use std::error::Error;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Write;
|
||||
use std::str::from_utf8;
|
||||
use zns::{errors::ZNSError, reader::Reader};
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
/// Simple program to greet a person
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(version, about, long_about = None)]
|
||||
struct Args {
|
||||
/// Name of the person to greet
|
||||
#[arg(short, long)]
|
||||
key: String,
|
||||
|
||||
/// Name of the person to greet
|
||||
#[arg(short, long)]
|
||||
username: String,
|
||||
}
|
||||
|
||||
pub trait KeyTransformer {
|
||||
fn from_openssh(reader: &mut Reader) -> Result<Self, ZNSError>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn to_dnskey(&self, username: &str) -> (String, String);
|
||||
}
|
||||
|
||||
struct Ed25519KeyPair {
|
||||
private_payload: [u8; 32],
|
||||
public_payload: [u8; 32],
|
||||
}
|
||||
|
||||
struct RSAKeyPair {
|
||||
modulus: Vec<u8>,
|
||||
public_exponent: Vec<u8>,
|
||||
private_exponent: Vec<u8>,
|
||||
prime1: Vec<u8>,
|
||||
prime2: Vec<u8>,
|
||||
exponent1: Vec<u8>,
|
||||
exponent2: Vec<u8>,
|
||||
coefficient: Vec<u8>,
|
||||
}
|
||||
|
||||
enum KeyPair {
|
||||
ED255519(Ed25519KeyPair),
|
||||
RSA(RSAKeyPair),
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
struct OpenSSHKey {
|
||||
ciphername: String,
|
||||
kdfname: String,
|
||||
kdfoptions: String,
|
||||
keypair: KeyPair,
|
||||
}
|
||||
|
||||
fn read_string(reader: &mut Reader) -> Result<String, ZNSError> {
|
||||
let length = reader.read_u32()?;
|
||||
let data = reader.read(length as usize)?;
|
||||
let result = from_utf8(&data).map_err(|e| ZNSError::Key {
|
||||
message: format!("Wrong ciphername format: {}", e.to_string()),
|
||||
})?;
|
||||
Ok(result.to_owned())
|
||||
}
|
||||
|
||||
fn read_bytes(reader: &mut Reader) -> Result<Vec<u8>, ZNSError> {
|
||||
let length = reader.read_u32()?;
|
||||
let data = reader.read(length as usize)?;
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
impl KeyTransformer for Ed25519KeyPair {
|
||||
fn from_openssh(reader: &mut Reader) -> Result<Self, ZNSError> {
|
||||
// public key parts
|
||||
let length = reader.read_u32()?;
|
||||
reader.read(length as usize)?;
|
||||
|
||||
// private key payload
|
||||
let length = reader.read_u32()?;
|
||||
let data = reader.read(length as usize)?;
|
||||
|
||||
let private_payload = data[0..32].try_into().unwrap();
|
||||
let public_payload = data[32..].try_into().unwrap();
|
||||
|
||||
Ok(Self {
|
||||
public_payload,
|
||||
private_payload,
|
||||
})
|
||||
}
|
||||
|
||||
fn to_dnskey(&self, username: &str) -> (String, String) {
|
||||
let version: &str = "Private-key-format: v1.3";
|
||||
let algorithm: &str = "Algorithm: 15 (ED25519)";
|
||||
let private_key = format!(
|
||||
"PrivateKey: {}",
|
||||
BASE64_STANDARD.encode(self.private_payload)
|
||||
);
|
||||
let private_encoded = format!("{version}\n{algorithm}\n{private_key}");
|
||||
|
||||
let public_key = BASE64_STANDARD.encode(self.public_payload);
|
||||
let public_encoded = format!("{username}.users.zeus.gent. IN KEY 256 3 15 {public_key}");
|
||||
|
||||
(private_encoded, public_encoded)
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyTransformer for RSAKeyPair {
|
||||
fn from_openssh(reader: &mut Reader) -> Result<Self, ZNSError> {
|
||||
let mut modulus = read_bytes(reader)?;
|
||||
|
||||
if modulus[0] == 0 {
|
||||
// Remove first byte, it's a null byte for sign.
|
||||
modulus.remove(0);
|
||||
}
|
||||
|
||||
let public_exponent = read_bytes(reader)?;
|
||||
let private_exponent = read_bytes(reader)?;
|
||||
let prime1 = read_bytes(reader)?;
|
||||
let prime2 = read_bytes(reader)?;
|
||||
let coefficient = read_bytes(reader)?;
|
||||
|
||||
let d = BigUint::from_bytes_be(&private_exponent);
|
||||
let p = BigUint::from_bytes_be(&prime1);
|
||||
let q = BigUint::from_bytes_be(&prime2);
|
||||
|
||||
let pm = &d % (&p - BigUint::from_u8(1).unwrap());
|
||||
let qm = &d % (&q - BigUint::from_u8(1).unwrap());
|
||||
|
||||
let exponent1 = pm.to_bytes_be();
|
||||
let exponent2 = qm.to_bytes_be();
|
||||
|
||||
Ok(Self {
|
||||
modulus,
|
||||
public_exponent,
|
||||
private_exponent,
|
||||
prime1,
|
||||
prime2,
|
||||
exponent1,
|
||||
exponent2,
|
||||
coefficient,
|
||||
})
|
||||
}
|
||||
|
||||
fn to_dnskey(&self, username: &str) -> (String, String) {
|
||||
let modulus = BASE64_STANDARD.encode(&self.modulus);
|
||||
let pubexponent = BASE64_STANDARD.encode(&self.public_exponent);
|
||||
let privexponent = BASE64_STANDARD.encode(&self.private_exponent);
|
||||
let prime1 = BASE64_STANDARD.encode(&self.prime1);
|
||||
let prime2 = BASE64_STANDARD.encode(&self.prime2);
|
||||
let exp1 = BASE64_STANDARD.encode(&self.exponent1);
|
||||
let exp2 = BASE64_STANDARD.encode(&self.exponent2);
|
||||
let coeff = BASE64_STANDARD.encode(&self.coefficient);
|
||||
|
||||
let private_encoded = format!(
|
||||
"Private-key-format: v1.3
|
||||
Algorithm: 10 (RSASHA512)
|
||||
Modulus: {modulus}
|
||||
PublicExponent: {pubexponent}
|
||||
PrivateExponent: {privexponent}
|
||||
Prime1: {prime1}
|
||||
Prime2: {prime2}
|
||||
Exponent1: {exp1}
|
||||
Exponent2: {exp2}
|
||||
Coefficient: {coeff}
|
||||
"
|
||||
);
|
||||
|
||||
let mut public_key: Vec<u8> = vec![];
|
||||
|
||||
public_key.push(self.public_exponent.len() as u8);
|
||||
public_key.extend(&self.public_exponent);
|
||||
public_key.extend(&self.modulus);
|
||||
|
||||
let encoded_pub = BASE64_STANDARD.encode(&public_key);
|
||||
|
||||
let public_encoded = format!("{username}.users.zeus.gent. IN KEY 256 3 10 {encoded_pub}");
|
||||
|
||||
(private_encoded, public_encoded)
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyTransformer for OpenSSHKey {
|
||||
fn from_openssh(reader: &mut Reader) -> Result<Self, ZNSError> {
|
||||
// Reference Material: https://coolaj86.com/articles/the-openssh-private-key-format/
|
||||
|
||||
let buf = reader.read(14)?;
|
||||
let magic = from_utf8(&buf).map_err(|e| ZNSError::Key {
|
||||
message: format!("Not valid ASCII: {}", e.to_string()),
|
||||
})?;
|
||||
|
||||
if magic != "openssh-key-v1" {
|
||||
return Err(ZNSError::Key {
|
||||
message: String::from("ssh key does not match ASCII magic openssh-key-v1"),
|
||||
});
|
||||
}
|
||||
|
||||
reader.read_u8()?;
|
||||
|
||||
let ciphername = read_string(reader)?;
|
||||
let kdfname = read_string(reader)?;
|
||||
let kdfoptions = read_string(reader)?;
|
||||
|
||||
// Amount of keypairs
|
||||
let nkeys = reader.read_u32()?;
|
||||
|
||||
if nkeys != 1 {
|
||||
return Err(ZNSError::Key {
|
||||
message: format!(
|
||||
"Only private key file with one keypair is supported, not {} keys",
|
||||
nkeys
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
// public key
|
||||
let length = reader.read_u32()?;
|
||||
reader.read(length as usize)?;
|
||||
|
||||
// size of remaining payload
|
||||
reader.read_u32()?;
|
||||
|
||||
// salt and rounds
|
||||
reader.read(8)?;
|
||||
|
||||
// public keytype
|
||||
let keytype = read_string(reader)?;
|
||||
|
||||
let keypair = match keytype.as_str() {
|
||||
"ssh-ed25519" => Ok(KeyPair::ED255519(Ed25519KeyPair::from_openssh(reader)?)),
|
||||
"ssh-rsa" => Ok(KeyPair::RSA(RSAKeyPair::from_openssh(reader)?)),
|
||||
other => Err(ZNSError::Key {
|
||||
message: format!("Invalid public keytype {}", other),
|
||||
}),
|
||||
}?;
|
||||
|
||||
let length = reader.read_u32()?;
|
||||
reader.read(length as usize)?;
|
||||
|
||||
Ok(Self {
|
||||
ciphername,
|
||||
kdfname,
|
||||
kdfoptions,
|
||||
keypair,
|
||||
})
|
||||
}
|
||||
|
||||
fn to_dnskey(&self, username: &str) -> (String, String) {
|
||||
match &self.keypair {
|
||||
KeyPair::ED255519(keypair) => keypair.to_dnskey(username),
|
||||
KeyPair::RSA(keypair) => keypair.to_dnskey(username),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ssh_to_dnskey(file_content: String, username: &str) -> Result<(), Box<dyn Error>> {
|
||||
let bin = BASE64_STANDARD.decode(file_content.trim())?;
|
||||
let mut reader = Reader::new(&bin);
|
||||
let key = OpenSSHKey::from_openssh(&mut reader)?;
|
||||
|
||||
let mut file_private = File::create("Kdns.private")?;
|
||||
let mut file_public = File::create("Kdns.key")?;
|
||||
|
||||
let (private, public) = key.to_dnskey(username);
|
||||
file_private.write(private.as_bytes())?;
|
||||
file_public.write(public.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
match fs::read_to_string(args.key) {
|
||||
Ok(contents) => match ssh_to_dnskey(contents, &args.username) {
|
||||
Ok(()) => println!("Success"),
|
||||
Err(error) => eprintln!("{}", error),
|
||||
},
|
||||
Err(error) => eprintln!("{}", error),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ pub enum ZNSError {
|
|||
Database { message: String },
|
||||
#[error("Reader Error: {message:?}")]
|
||||
Reader { message: String },
|
||||
#[error("PublicKey Error: {message:?}")]
|
||||
PublicKey { message: String },
|
||||
#[error("Key Error: {message:?}")]
|
||||
Key { message: String },
|
||||
#[error("Reqwest error")]
|
||||
Reqwest(#[from] reqwest::Error),
|
||||
|
||||
|
@ -38,7 +38,7 @@ impl ZNSError {
|
|||
ZNSError::NotAuth { .. } => RCODE::NOTAUTH,
|
||||
ZNSError::NXDomain { .. } => RCODE::NXDOMAIN,
|
||||
ZNSError::NotImp { .. } => RCODE::NOTIMP,
|
||||
ZNSError::Refused { .. } | ZNSError::PublicKey { .. } => RCODE::REFUSED,
|
||||
ZNSError::Refused { .. } | ZNSError::Key { .. } => RCODE::REFUSED,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ pub async fn authenticate(
|
|||
connection: &mut PgConnection,
|
||||
) -> Result<bool, ZNSError> {
|
||||
if zone.len() >= Config::get().authoritative_zone.len() {
|
||||
//TODO: panic? subtract
|
||||
let username = &zone[zone.len() - Config::get().authoritative_zone.len() - 1];
|
||||
|
||||
let ssh_verified = validate_ssh(username, sig).await.is_ok_and(|b| b);
|
||||
|
|
|
@ -17,7 +17,7 @@ pub trait PublicKey {
|
|||
fn verify_ssh_type(reader: &mut Reader, key_type: &str) -> Result<(), ZNSError> {
|
||||
let type_size = reader.read_i32()?;
|
||||
let read = reader.read(type_size as usize)?;
|
||||
let algo_type = from_utf8(&read).map_err(|e| ZNSError::PublicKey {
|
||||
let algo_type = from_utf8(&read).map_err(|e| ZNSError::Key {
|
||||
message: format!(
|
||||
"Could not convert type name bytes to string: {}",
|
||||
e.to_string()
|
||||
|
@ -27,7 +27,7 @@ pub trait PublicKey {
|
|||
if algo_type == key_type {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ZNSError::PublicKey {
|
||||
Err(ZNSError::Key {
|
||||
message: String::from("ssh key type does not match identifier"),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -51,14 +51,14 @@ impl PublicKey for RsaPublicKey {
|
|||
n: asn1::BigInt::new(&self.n),
|
||||
e: asn1::BigInt::new(&self.e),
|
||||
})
|
||||
.map_err(|e| ZNSError::PublicKey {
|
||||
.map_err(|e| ZNSError::Key {
|
||||
message: format!("Verify Error: {}", e),
|
||||
})?;
|
||||
|
||||
let signature_type = match algorithm {
|
||||
Algorithm::RSASHA512 => Ok(&signature::RSA_PKCS1_2048_8192_SHA512),
|
||||
Algorithm::RSASHA256 => Ok(&signature::RSA_PKCS1_2048_8192_SHA256),
|
||||
_ => Err(ZNSError::PublicKey {
|
||||
_ => Err(ZNSError::Key {
|
||||
message: String::from("RsaPublicKey: invalid verify algorithm"),
|
||||
}),
|
||||
}?;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
mod db;
|
||||
mod handlers;
|
||||
mod message;
|
||||
mod reader;
|
||||
mod structs;
|
||||
mod utils;
|
||||
|
||||
|
@ -9,3 +8,4 @@ pub mod config;
|
|||
pub mod errors;
|
||||
pub mod parser;
|
||||
pub mod resolver;
|
||||
pub mod reader;
|
||||
|
|
Loading…
Reference in a new issue