mirror of
https://github.com/ZeusWPI/ZNS.git
synced 2024-11-24 06:11:10 +01:00
Add rsa support
This commit is contained in:
parent
c6437e6901
commit
a166ec562b
9 changed files with 224 additions and 34 deletions
21
Cargo.lock
generated
21
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {}
|
||||||
|
|
36
src/handlers/update/pubkeys/ed25519.rs
Normal file
36
src/handlers/update/pubkeys/ed25519.rs
Normal 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())
|
||||||
|
}
|
||||||
|
}
|
64
src/handlers/update/pubkeys/mod.rs
Normal file
64
src/handlers/update/pubkeys/mod.rs
Normal 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>;
|
||||||
|
}
|
57
src/handlers/update/pubkeys/rsa.rs
Normal file
57
src/handlers/update/pubkeys/rsa.rs
Normal 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 })
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue