10
0
Fork 0
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:
Xander Bil 2024-08-10 23:47:39 +02:00
parent c766d4631f
commit 223988741f
No known key found for this signature in database
GPG key ID: EC9706B54A278598
9 changed files with 439 additions and 10 deletions

139
Cargo.lock generated
View file

@ -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",
]

View file

@ -1,4 +1,5 @@
[workspace]
resolver = "2"
members = [
"zns",

View file

@ -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"

View file

@ -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),
}
}

View file

@ -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,
}
}
}

View file

@ -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);

View file

@ -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"),
})
}

View file

@ -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"),
}),
}?;

View file

@ -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;