10
0
Fork 0
mirror of https://github.com/ZeusWPI/ZNS.git synced 2024-10-30 05:24:26 +01:00

Add rsa support

This commit is contained in:
Xander Bil 2024-06-18 23:50:12 +02:00
parent c6437e6901
commit a166ec562b
No known key found for this signature in database
GPG key ID: EC9706B54A278598
9 changed files with 224 additions and 34 deletions

21
Cargo.lock generated
View file

@ -17,6 +17,26 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "asn1"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "532ceda058281b62096b2add4ab00ab3a453d30dee28b8890f62461a0109ebbd"
dependencies = [
"asn1_derive",
]
[[package]]
name = "asn1_derive"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6076d38cc17cc22b0f65f31170a2ee1975e6b07f0012893aefd86ce19c987"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "atomic-waker" name = "atomic-waker"
version = "1.1.2" version = "1.1.2"
@ -1296,6 +1316,7 @@ dependencies = [
name = "zeusns" name = "zeusns"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"asn1",
"base64", "base64",
"diesel", "diesel",
"dotenvy", "dotenvy",

View file

@ -11,3 +11,4 @@ tokio = {version = "1.36.0", features = ["macros","rt-multi-thread","net"], defa
ring = "0.17.8" ring = "0.17.8"
base64 = "0.22.0" base64 = "0.22.0"
reqwest = {version = "0.12.4", features = ["json","default"]} reqwest = {version = "0.12.4", features = ["json","default"]}
asn1 = "0.16.2"

View file

@ -1,4 +1,4 @@
use reqwest::Error; use base64::prelude::*;
use crate::{ use crate::{
config::Config, config::Config,
@ -9,7 +9,11 @@ use crate::{
structs::{Class, RRClass, RRType, Type}, structs::{Class, RRClass, RRType, Type},
}; };
use super::{dnskey::DNSKeyRData, sig::Sig}; use super::{
dnskey::DNSKeyRData,
pubkeys::{Ed25519PublicKey, PublicKey, PublicKeyError, RsaPublicKey, SSH_ED25519, SSH_RSA},
sig::Sig,
};
pub(super) async fn authenticate( pub(super) async fn authenticate(
sig: &Sig, sig: &Sig,
@ -32,7 +36,7 @@ pub(super) async fn authenticate(
} }
} }
async fn validate_ssh(username: &String, sig: &Sig) -> Result<bool, Error> { async fn validate_ssh(username: &String, sig: &Sig) -> Result<bool, PublicKeyError> {
Ok(reqwest::get(format!( Ok(reqwest::get(format!(
"{}/users/keys/{}", "{}/users/keys/{}",
Config::get().zauth_url, Config::get().zauth_url,
@ -44,11 +48,13 @@ async fn validate_ssh(username: &String, sig: &Sig) -> Result<bool, Error> {
.iter() .iter()
.any(|key| { .any(|key| {
let key_split: Vec<&str> = key.split_ascii_whitespace().collect(); let key_split: Vec<&str> = key.split_ascii_whitespace().collect();
match key_split.len() { let bin = BASE64_STANDARD.decode(key_split[1]).unwrap();
3 => match key_split[0] { match key_split[0] {
"ssh-ed25519" => sig.verify_ssh_ed25519(key_split[1]), //TODO: do something with error, debugging?
_ => false, SSH_ED25519 => {
}, Ed25519PublicKey::from_openssh(&bin).is_ok_and(|pubkey| sig.verify(pubkey))
}
SSH_RSA => RsaPublicKey::from_openssh(&bin).is_ok_and(|pubkey| sig.verify(pubkey)),
_ => false, _ => false,
} }
})) }))
@ -67,3 +73,19 @@ async fn validate_dnskey(zone: &Vec<String>, sig: &Sig) -> Result<bool, Database
}), }),
) )
} }
impl From<reqwest::Error> for PublicKeyError {
fn from(value: reqwest::Error) -> Self {
PublicKeyError {
message: format!("Reqwest Error: {}", value.to_string()),
}
}
}
impl From<PublicKeyError> for AuthenticationError {
fn from(value: PublicKeyError) -> Self {
AuthenticationError {
message: value.to_string(),
}
}
}

View file

@ -1,8 +1,9 @@
use base64::prelude::*;
use crate::{errors::ParseError, parser::FromBytes, reader::Reader}; use crate::{errors::ParseError, parser::FromBytes, reader::Reader};
use super::sig::Sig; use super::{
pubkeys::{Ed25519PublicKey, PublicKey, RsaPublicKey},
sig::Sig,
};
/// https://datatracker.ietf.org/doc/html/rfc4034#section-2 /// https://datatracker.ietf.org/doc/html/rfc4034#section-2
#[derive(Debug)] #[derive(Debug)]
@ -27,9 +28,12 @@ impl FromBytes for DNSKeyRData {
impl DNSKeyRData { impl DNSKeyRData {
pub fn verify(&self, sig: &Sig) -> bool { pub fn verify(&self, sig: &Sig) -> bool {
let encoded = BASE64_STANDARD.encode(&self.public_key);
match self.algorithm { match self.algorithm {
15 => sig.verify_ed25519(&encoded), 10 => {
RsaPublicKey::from_dnskey(&self.public_key).is_ok_and(|pubkey| sig.verify(pubkey))
}
15 => Ed25519PublicKey::from_dnskey(&self.public_key)
.is_ok_and(|pubkey| sig.verify(pubkey)),
_ => false, _ => false,
} }
} }

View file

@ -11,6 +11,7 @@ use super::ResponseHandler;
mod authenticate; mod authenticate;
mod dnskey; mod dnskey;
mod pubkeys;
mod sig; mod sig;
pub(super) struct UpdateHandler {} pub(super) struct UpdateHandler {}

View file

@ -0,0 +1,36 @@
use ring::signature;
use crate::reader::Reader;
use super::{PublicKey, PublicKeyError, SSH_ED25519};
pub struct Ed25519PublicKey {
data: Vec<u8>,
}
impl PublicKey for Ed25519PublicKey {
fn from_openssh(key: &[u8]) -> Result<Self, PublicKeyError>
where
Self: Sized,
{
let mut reader = Reader::new(key);
Ed25519PublicKey::verify_ssh_type(&mut reader, SSH_ED25519)?;
reader.read_i32()?;
Ok(Ed25519PublicKey {
data: reader.read(reader.unread_bytes())?,
})
}
fn from_dnskey(key: &[u8]) -> Result<Self, PublicKeyError>
where
Self: Sized,
{
Ok(Ed25519PublicKey { data: key.to_vec() })
}
fn verify(&self, data: &[u8], signature: &[u8]) -> Result<bool, PublicKeyError> {
let pkey = ring::signature::UnparsedPublicKey::new(&signature::ED25519, &self.data);
Ok(pkey.verify(data, signature).is_ok())
}
}

View file

@ -0,0 +1,64 @@
mod ed25519;
mod rsa;
use core::fmt;
use std::str::from_utf8;
use base64::prelude::*;
use crate::{errors::ReaderError, reader::Reader};
pub use self::ed25519::Ed25519PublicKey;
pub use self::rsa::RsaPublicKey;
#[derive(Debug)]
pub struct PublicKeyError {
pub message: String,
}
impl fmt::Display for PublicKeyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Public Key Error: {}", self.message)
}
}
impl From<ReaderError> for PublicKeyError {
fn from(value: ReaderError) -> Self {
PublicKeyError {
message: value.to_string(),
}
}
}
pub const SSH_ED25519: &str = "ssh-ed25519";
pub const SSH_RSA: &str = "ssh-rsa";
pub trait PublicKey {
fn verify_ssh_type(reader: &mut Reader, key_type: &str) -> Result<(), PublicKeyError> {
let type_size = reader.read_i32()?;
let read = reader.read(type_size as usize)?;
let algo_type = from_utf8(&read).map_err(|e| PublicKeyError {
message: format!(
"Could not convert type name bytes to string: {}",
e.to_string()
),
})?;
if algo_type == key_type {
Ok(())
} else {
Err(PublicKeyError {
message: String::from("ssh key type does not match identifier"),
})
}
}
fn from_openssh(key: &[u8]) -> Result<Self, PublicKeyError>
where
Self: Sized;
fn from_dnskey(key: &[u8]) -> Result<Self, PublicKeyError>
where
Self: Sized;
fn verify(&self, data: &[u8], signature: &[u8]) -> Result<bool, PublicKeyError>;
}

View file

@ -0,0 +1,57 @@
use ring::signature;
use crate::reader::Reader;
use super::{PublicKey, PublicKeyError, SSH_RSA};
pub struct RsaPublicKey {
e: Vec<u8>,
n: Vec<u8>,
}
#[derive(asn1::Asn1Write)]
struct RsaAsn1<'a> {
n: Option<asn1::BigInt<'a>>,
e: Option<asn1::BigInt<'a>>,
}
impl PublicKey for RsaPublicKey {
fn from_openssh(key: &[u8]) -> Result<Self, PublicKeyError>
where
Self: Sized,
{
let mut reader = Reader::new(key);
RsaPublicKey::verify_ssh_type(&mut reader, SSH_RSA)?;
let e_size = reader.read_i32()?;
let e = reader.read(e_size as usize)?;
let n_size = reader.read_i32()?;
let n = reader.read(n_size as usize)?;
Ok(RsaPublicKey { e, n })
}
fn verify(&self, data: &[u8], signature: &[u8]) -> Result<bool, PublicKeyError> {
let result = asn1::write_single(&RsaAsn1 {
n: asn1::BigInt::new(&self.n),
e: asn1::BigInt::new(&self.e),
})
.map_err(|e| PublicKeyError {
message: format!("Verify Error: {}", e),
})?;
let pkey =
ring::signature::UnparsedPublicKey::new(&signature::RSA_PKCS1_2048_8192_SHA512, result);
Ok(pkey.verify(data, signature).is_ok())
}
fn from_dnskey(key: &[u8]) -> Result<Self, PublicKeyError>
where
Self: Sized,
{
let mut reader = Reader::new(key);
let e_len = reader.read_u8()?;
let e = reader.read(e_len as usize)?;
let n = reader.read(reader.unread_bytes())?;
Ok(RsaPublicKey { e, n })
}
}

View file

@ -1,5 +1,3 @@
use base64::prelude::*;
use crate::{ use crate::{
errors::ParseError, errors::ParseError,
parser::FromBytes, parser::FromBytes,
@ -7,6 +5,8 @@ use crate::{
structs::{KeyRData, RR}, structs::{KeyRData, RR},
}; };
use super::pubkeys::PublicKey;
pub(super) struct Sig { pub(super) struct Sig {
raw_data: Vec<u8>, raw_data: Vec<u8>,
key_rdata: KeyRData, key_rdata: KeyRData,
@ -29,24 +29,8 @@ impl Sig {
}) })
} }
pub fn verify_ed25519(&self, key: &str) -> bool { pub fn verify(&self, key: impl PublicKey) -> bool {
let blob = BASE64_STANDARD.decode(key).unwrap(); key.verify(&self.raw_data, &self.key_rdata.signature)
.is_ok_and(|b| b)
let pkey = ring::signature::UnparsedPublicKey::new(&ring::signature::ED25519, &blob);
pkey.verify(&self.raw_data, &self.key_rdata.signature)
.is_ok()
}
pub fn verify_ssh_ed25519(&self, key: &str) -> bool {
let blob = BASE64_STANDARD.decode(key).unwrap();
let pkey = ring::signature::UnparsedPublicKey::new(
&ring::signature::ED25519,
&blob.as_slice()[19..],
);
pkey.verify(&self.raw_data, &self.key_rdata.signature)
.is_ok()
} }
} }