Compare commits
10 Commits
d2e1a85554
...
ecd92d0e18
Author | SHA1 | Date |
---|---|---|
c0repwn3r | ecd92d0e18 | |
c0repwn3r | f438935954 | |
c0repwn3r | cbe8dee8a3 | |
c0repwn3r | d8ca1bbb90 | |
c0repwn3r | 2ea8a01617 | |
c0repwn3r | 5a2fd78754 | |
c0repwn3r | 79c31c29d8 | |
c0repwn3r | 6f2a0ca828 | |
c0repwn3r | 15ac941df5 | |
c0repwn3r | 3b6a8a00d7 |
|
@ -2,7 +2,10 @@
|
||||||
<module type="CPP_MODULE" version="4">
|
<module type="CPP_MODULE" version="4">
|
||||||
<component name="NewModuleRootManager">
|
<component name="NewModuleRootManager">
|
||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/certtoolpf/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/libepf/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/libepf/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/ncpf/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/netcatpf/src" isTestSource="false" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/libepf/target" />
|
<excludeFolder url="file://$MODULE_DIR$/libepf/target" />
|
||||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
</content>
|
</content>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,7 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"libepf"
|
"libepf",
|
||||||
|
"ncpf",
|
||||||
|
"netcatpf",
|
||||||
|
"certtoolpf"
|
||||||
]
|
]
|
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "certtoolpf"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
inquire = "0.6.1"
|
||||||
|
libepf = { version = "0.1.0", path = "../libepf" }
|
||||||
|
clap = { version = "4", features = ["derive", "cargo"] }
|
||||||
|
rand = "0.8.5"
|
|
@ -0,0 +1,353 @@
|
||||||
|
use clap::{Parser, Subcommand};
|
||||||
|
use inquire::{Confirm, Select, Text};
|
||||||
|
use libepf::ca_pool::load_ca_pool;
|
||||||
|
use libepf::pki::{
|
||||||
|
EPFCertificate, EPFCertificateDetails, EpfPkiCertificateOps, EpfPkiSerializable, EpfPrivateKey,
|
||||||
|
EpfPublicKey,
|
||||||
|
};
|
||||||
|
use rand::rngs::OsRng;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(author, version, about, long_about = None)]
|
||||||
|
struct Cli {
|
||||||
|
#[command(subcommand)]
|
||||||
|
command: Option<Commands>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
enum Commands {
|
||||||
|
/// Generate a public and private keypair
|
||||||
|
GenerateKeypair {
|
||||||
|
#[arg(short = 'p', long)]
|
||||||
|
out_public_key: PathBuf,
|
||||||
|
#[arg(short = 'k', long)]
|
||||||
|
out_private_key: PathBuf,
|
||||||
|
},
|
||||||
|
/// Create a new **unsigned** certificate
|
||||||
|
CreateCertificate {
|
||||||
|
#[arg(short, long)]
|
||||||
|
output: PathBuf,
|
||||||
|
#[arg(short, long)]
|
||||||
|
public_key: PathBuf,
|
||||||
|
},
|
||||||
|
/// Dump information about the certificate
|
||||||
|
DumpCertificate { cert: PathBuf },
|
||||||
|
/// Verify the certificate
|
||||||
|
VerifyCertificate { cert: PathBuf },
|
||||||
|
/// Sign the certificate using the given private key
|
||||||
|
SignCertificate {
|
||||||
|
cert: PathBuf,
|
||||||
|
#[arg(short, long)]
|
||||||
|
key: PathBuf,
|
||||||
|
#[arg(short, long)]
|
||||||
|
output: PathBuf,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = Cli::parse();
|
||||||
|
|
||||||
|
if let Some(subcommand) = args.command {
|
||||||
|
match subcommand {
|
||||||
|
Commands::GenerateKeypair {
|
||||||
|
out_private_key,
|
||||||
|
out_public_key,
|
||||||
|
} => {
|
||||||
|
let private_key = EpfPrivateKey::generate(&mut OsRng);
|
||||||
|
let public_key = private_key.verifying_key();
|
||||||
|
|
||||||
|
let private_key_pem = match private_key.as_pem() {
|
||||||
|
Ok(pem) => pem,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error while serializing private key: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let public_key_pem = match public_key.as_pem() {
|
||||||
|
Ok(pem) => pem,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error while serializing public key: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match std::fs::write(out_private_key, private_key_pem) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error saving private key: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match std::fs::write(out_public_key, public_key_pem) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error saving public key: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Commands::CreateCertificate { output, public_key } => {
|
||||||
|
// load public key
|
||||||
|
let public_key_pem = match fs::read(public_key) {
|
||||||
|
Ok(pem) => pem,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Unable to load public key: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let public_key = match EpfPublicKey::from_pem(&public_key_pem) {
|
||||||
|
Ok(k) => k,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error parsing public key: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let name = match Text::new("Certificate name?").prompt() {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error with prompt: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let options = vec![
|
||||||
|
"1 day", "1 week", "1 month", "6 months", "1 year", "2 years", "5 years",
|
||||||
|
"10 years", "Forever",
|
||||||
|
];
|
||||||
|
let expires_in =
|
||||||
|
match Select::new("How long should the certificate be valid for?", options)
|
||||||
|
.prompt()
|
||||||
|
{
|
||||||
|
Ok(expires_in) => expires_in,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error with prompt: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let expires_in = match expires_in {
|
||||||
|
"1 day" => 60 * 60 * 24,
|
||||||
|
"1 week" => 60 * 60 * 24 * 7,
|
||||||
|
"1 month" => 60 * 60 * 24 * 7 * 4,
|
||||||
|
"6 months" => 60 * 60 * 24 * 7 * 4 * 6,
|
||||||
|
"1 year" => 60 * 60 * 24 * 7 * 4 * 6 * 2,
|
||||||
|
"2 years" => 60 * 60 * 24 * 7 * 4 * 6 * 2 * 2,
|
||||||
|
"5 years" => 60 * 60 * 24 * 7 * 4 * 6 * 2 * 5,
|
||||||
|
"10 years" => 60 * 60 * 24 * 7 * 4 * 6 * 2 * 10,
|
||||||
|
"Forever" => 60 * 60 * 24 * 7 * 4 * 6 * 2 * 100000, // 100,000 years
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let add_claims = match Confirm::new(
|
||||||
|
"Would you like to add additional claims to the certificate?",
|
||||||
|
)
|
||||||
|
.with_default(false)
|
||||||
|
.prompt()
|
||||||
|
{
|
||||||
|
Ok(ac) => ac,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error with prompt: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut claims = HashMap::new();
|
||||||
|
|
||||||
|
if add_claims {
|
||||||
|
loop {
|
||||||
|
let name = match Text::new("Claim name:").prompt() {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error with prompt: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let value = match Text::new("Claim value:").prompt() {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error with prompt: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
claims.insert(name, value);
|
||||||
|
let add_another = match Confirm::new(
|
||||||
|
"Would you like to add additional claims to the certificate?",
|
||||||
|
)
|
||||||
|
.with_default(true)
|
||||||
|
.prompt()
|
||||||
|
{
|
||||||
|
Ok(ac) => ac,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error with prompt: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if !add_another {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cert = EPFCertificate {
|
||||||
|
details: EPFCertificateDetails {
|
||||||
|
name,
|
||||||
|
not_before: SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("Time went backwards")
|
||||||
|
.as_secs(),
|
||||||
|
not_after: SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("Time went backwards")
|
||||||
|
.as_secs()
|
||||||
|
+ expires_in,
|
||||||
|
public_key: public_key.to_bytes(),
|
||||||
|
issuer_public_key: [0u8; 32],
|
||||||
|
claims,
|
||||||
|
},
|
||||||
|
fingerprint: "".to_string(),
|
||||||
|
signature: [0u8; 64],
|
||||||
|
};
|
||||||
|
match cert.recalculate_fingerprint() {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error calculating certificate fingerprint: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cert_pem = match cert.as_pem() {
|
||||||
|
Ok(pem) => pem,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error serializing certificate: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match fs::write(output, cert_pem) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error saving certificate: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Commands::DumpCertificate { cert } => {
|
||||||
|
// load cert
|
||||||
|
let cert_pem = match fs::read(cert) {
|
||||||
|
Ok(pem) => pem,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Unable to load certificate: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let cert = match EPFCertificate::from_pem(&cert_pem) {
|
||||||
|
Ok(k) => k,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error parsing certificate: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
println!("{}", cert);
|
||||||
|
}
|
||||||
|
Commands::SignCertificate { cert, key, output } => {
|
||||||
|
// load cert
|
||||||
|
let cert_pem = match fs::read(cert) {
|
||||||
|
Ok(pem) => pem,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Unable to load certificate: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut cert = match EPFCertificate::from_pem(&cert_pem) {
|
||||||
|
Ok(k) => k,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error parsing certificate: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// load key
|
||||||
|
let key_pem = match fs::read(key) {
|
||||||
|
Ok(pem) => pem,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Unable to load private key: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let key = match EpfPrivateKey::from_pem(&key_pem) {
|
||||||
|
Ok(k) => k,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error parsing private key: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match cert.sign(&key) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error signing certificate: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cert_pem = match cert.as_pem() {
|
||||||
|
Ok(pem) => pem,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error serializing certificate: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match fs::write(output, cert_pem) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error saving certificate: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Commands::VerifyCertificate { cert } => {
|
||||||
|
// load cert
|
||||||
|
let cert_pem = match fs::read(cert) {
|
||||||
|
Ok(pem) => pem,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Unable to load certificate: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let cert = match EPFCertificate::from_pem(&cert_pem) {
|
||||||
|
Ok(k) => k,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error parsing certificate: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ca_pool = match load_ca_pool() {
|
||||||
|
Ok(pool) => pool,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Unable to load trusted certs pool: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match cert.verify(&ca_pool) {
|
||||||
|
Ok(trusted) => {
|
||||||
|
if !trusted {
|
||||||
|
println!("Certificate valid but not trusted");
|
||||||
|
std::process::exit(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("Certificate invalid: {}", e);
|
||||||
|
std::process::exit(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("No subcommand specified. Run with -h/--help for help.");
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,5 +14,17 @@ hex = "0.4"
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
ed25519-dalek = { version = "2.0.0-rc.2", features = ["rand_core"] }
|
ed25519-dalek = { version = "2.0.0-rc.2", features = ["rand_core"] }
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
x25519-dalek = "2.0.0-rc.2"
|
x25519-dalek = { version = "2.0.0-rc.2", features = ["static_secrets"] }
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
|
tokio = { version = "1.28", features = ["io-util"] }
|
||||||
|
async-trait = "0.1"
|
||||||
|
chacha20poly1305 = "0.10"
|
||||||
|
log = "0.4"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tokio = { version = "1.28", features = ["io-util", "macros", "rt-multi-thread", "net"] }
|
||||||
|
tcp-test = "0.1"
|
||||||
|
futures = "0.3"
|
||||||
|
simple_logger = "4.1"
|
||||||
|
hex-literal = "0.4.1"
|
||||||
|
serial_test = "2.0.0"
|
|
@ -1,5 +1,11 @@
|
||||||
use crate::pki::{EPFCertificate, EpfPublicKey};
|
use crate::pki::{EPFCertificate, EpfPkiSerializable, EpfPublicKey};
|
||||||
|
use crate::util::verifying_key;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::ffi::OsStr;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
pub struct EpfCaPool {
|
pub struct EpfCaPool {
|
||||||
pub ca_lookup_table: HashMap<EpfPublicKey, EPFCertificate>,
|
pub ca_lookup_table: HashMap<EpfPublicKey, EPFCertificate>,
|
||||||
|
@ -24,6 +30,55 @@ impl EpfCaPoolOps for EpfCaPool {
|
||||||
|
|
||||||
fn insert(&mut self, cert: &EPFCertificate) {
|
fn insert(&mut self, cert: &EPFCertificate) {
|
||||||
self.ca_lookup_table
|
self.ca_lookup_table
|
||||||
.insert(cert.details.public_key, cert.clone());
|
.insert(verifying_key(&cert.details.public_key), cert.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum EpfCaPoolLoaderError {
|
||||||
|
CertDirDoesNotExist(String),
|
||||||
|
}
|
||||||
|
impl Display for EpfCaPoolLoaderError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
EpfCaPoolLoaderError::CertDirDoesNotExist(d) => {
|
||||||
|
write!(f, "Certificate dir does not exist: {}", d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Error for EpfCaPoolLoaderError {}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[cfg(not(tarpaulin))]
|
||||||
|
pub fn load_ca_pool() -> Result<EpfCaPool, Box<dyn Error>> {
|
||||||
|
let mut cert_strings = vec![];
|
||||||
|
|
||||||
|
for entry in fs::read_dir("/etc/e3pf/certs")? {
|
||||||
|
let entry = entry?;
|
||||||
|
if entry.path().extension() == Some(OsStr::new("pem")) {
|
||||||
|
cert_strings.push(fs::read_to_string(entry.path())?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ca_pool = EpfCaPool::new();
|
||||||
|
|
||||||
|
for cert in cert_strings {
|
||||||
|
ca_pool.insert(&EPFCertificate::from_pem(cert.as_bytes())?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ca_pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::ca_pool::EpfCaPoolLoaderError;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn ca_pool_error_display_test() {
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
EpfCaPoolLoaderError::CertDirDoesNotExist("".to_string())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
use crate::pki::EpfPkiCertificateValidationError;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum EpfHandshakeError {
|
||||||
|
AlreadyTunnelled,
|
||||||
|
UnsupportedProtocolVersion(usize),
|
||||||
|
InvalidCertificate(EpfPkiCertificateValidationError),
|
||||||
|
UntrustedCertificate,
|
||||||
|
EncryptionError,
|
||||||
|
MissingKeyProof,
|
||||||
|
}
|
||||||
|
impl Display for EpfHandshakeError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
EpfHandshakeError::AlreadyTunnelled => write!(f, "Already tunneled"),
|
||||||
|
EpfHandshakeError::UnsupportedProtocolVersion(v) => {
|
||||||
|
write!(f, "Unsupported protocol version {}", v)
|
||||||
|
}
|
||||||
|
EpfHandshakeError::InvalidCertificate(e) => write!(f, "Invalid certificate: {}", e),
|
||||||
|
EpfHandshakeError::UntrustedCertificate => {
|
||||||
|
write!(f, "Certificate valid but not trusted")
|
||||||
|
}
|
||||||
|
EpfHandshakeError::EncryptionError => write!(f, "Encryption error"),
|
||||||
|
EpfHandshakeError::MissingKeyProof => write!(f, "Missing key proof"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Error for EpfHandshakeError {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::error::EpfHandshakeError;
|
||||||
|
use crate::pki::EpfPkiCertificateValidationError;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn error_display_test() {
|
||||||
|
println!("{}", EpfHandshakeError::AlreadyTunnelled);
|
||||||
|
println!("{}", EpfHandshakeError::UnsupportedProtocolVersion(0));
|
||||||
|
println!(
|
||||||
|
"{}",
|
||||||
|
EpfHandshakeError::InvalidCertificate(
|
||||||
|
EpfPkiCertificateValidationError::ValidAfterSigner
|
||||||
|
)
|
||||||
|
);
|
||||||
|
println!("{}", EpfHandshakeError::UntrustedCertificate);
|
||||||
|
println!("{}", EpfHandshakeError::EncryptionError);
|
||||||
|
println!("{}", EpfHandshakeError::MissingKeyProof);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,901 @@
|
||||||
|
use crate::ca_pool::EpfCaPool;
|
||||||
|
use crate::danger_trace;
|
||||||
|
use crate::error::EpfHandshakeError;
|
||||||
|
use crate::pki::{EPFCertificate, EpfPkiCertificateOps, EpfPrivateKey, EpfPublicKey};
|
||||||
|
use crate::protocol::{
|
||||||
|
encode_packet, recv_packet, EpfApplicationData, EpfClientHello, EpfClientState, EpfFinished,
|
||||||
|
EpfMessage, EpfServerHello, EpfServerState, PACKET_APPLICATION_DATA, PACKET_CLIENT_HELLO,
|
||||||
|
PACKET_FINISHED, PACKET_SERVER_HELLO, PROTOCOL_VERSION,
|
||||||
|
};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use chacha20poly1305::aead::{Aead, Payload};
|
||||||
|
use chacha20poly1305::{AeadCore, Key, KeyInit, XChaCha20Poly1305, XNonce};
|
||||||
|
use ed25519_dalek::SigningKey;
|
||||||
|
use log::trace;
|
||||||
|
use rand::rngs::OsRng;
|
||||||
|
use rand::Rng;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io;
|
||||||
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||||
|
use x25519_dalek::{PublicKey, StaticSecret};
|
||||||
|
|
||||||
|
///// CLIENT /////
|
||||||
|
|
||||||
|
pub struct EpfClientUpgraded<T: AsyncWriteExt + AsyncReadExt> {
|
||||||
|
inner: T,
|
||||||
|
state: EpfClientState,
|
||||||
|
client_random: [u8; 24],
|
||||||
|
server_random: [u8; 16],
|
||||||
|
client_cert: Option<EPFCertificate>,
|
||||||
|
packet_queue: Vec<EpfMessage>,
|
||||||
|
server_cert: Option<EPFCertificate>,
|
||||||
|
cipher: Option<XChaCha20Poly1305>,
|
||||||
|
private_key: EpfPrivateKey,
|
||||||
|
public_key: PublicKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ClientAuthentication {
|
||||||
|
Cert(Box<EPFCertificate>, Box<EpfPrivateKey>),
|
||||||
|
Ephemeral,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait EpfClientUpgradable {
|
||||||
|
async fn upgrade(self, auth: ClientAuthentication) -> EpfClientUpgraded<Self>
|
||||||
|
where
|
||||||
|
Self: Sized + AsyncWriteExt + AsyncReadExt + Send;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<T> EpfClientUpgradable for T
|
||||||
|
where
|
||||||
|
T: AsyncWriteExt + AsyncReadExt + Send,
|
||||||
|
{
|
||||||
|
async fn upgrade(self, auth: ClientAuthentication) -> EpfClientUpgraded<Self>
|
||||||
|
where
|
||||||
|
Self: Sized + AsyncWriteExt + AsyncReadExt + Send,
|
||||||
|
{
|
||||||
|
danger_trace!(target: "EpfClientUpgradable", "upgrade(auth: {:?})", auth);
|
||||||
|
|
||||||
|
let private_key;
|
||||||
|
let public_key;
|
||||||
|
let cert;
|
||||||
|
|
||||||
|
match auth {
|
||||||
|
ClientAuthentication::Cert(cert_d, key) => {
|
||||||
|
trace!("----!!!!! CERT AUTHENTICATION !!!!!----");
|
||||||
|
cert = Some(cert_d);
|
||||||
|
private_key = key;
|
||||||
|
public_key = PublicKey::from(&StaticSecret::from(private_key.to_bytes()));
|
||||||
|
}
|
||||||
|
ClientAuthentication::Ephemeral => {
|
||||||
|
cert = None;
|
||||||
|
let private_key_l: [u8; 32] = OsRng.gen();
|
||||||
|
let private_key_real = SigningKey::from(private_key_l);
|
||||||
|
public_key = PublicKey::from(&StaticSecret::from(private_key_real.to_bytes()));
|
||||||
|
private_key = Box::new(private_key_real);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EpfClientUpgraded {
|
||||||
|
inner: self,
|
||||||
|
state: EpfClientState::NotStarted,
|
||||||
|
client_random: OsRng.gen(),
|
||||||
|
server_random: [0u8; 16],
|
||||||
|
client_cert: cert.map(|u| *u),
|
||||||
|
server_cert: None,
|
||||||
|
packet_queue: vec![],
|
||||||
|
cipher: None,
|
||||||
|
private_key: *private_key,
|
||||||
|
public_key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait EpfClientHandshaker<S: AsyncWriteExt + AsyncReadExt + Unpin> {
|
||||||
|
async fn handshake(&mut self, cert_pool: EpfCaPool) -> Result<(), Box<dyn Error>>;
|
||||||
|
async fn upgrade(self) -> EpfClientStream<S>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<T: AsyncWriteExt + AsyncReadExt + Send + Unpin> EpfClientHandshaker<T>
|
||||||
|
for EpfClientUpgraded<T>
|
||||||
|
{
|
||||||
|
async fn handshake(&mut self, cert_pool: EpfCaPool) -> Result<(), Box<dyn Error>> {
|
||||||
|
match self.state {
|
||||||
|
EpfClientState::NotStarted => (),
|
||||||
|
_ => return Err(EpfHandshakeError::AlreadyTunnelled.into()),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 1: Send Client Hello
|
||||||
|
self.inner
|
||||||
|
.write_all(&encode_packet(
|
||||||
|
PACKET_CLIENT_HELLO,
|
||||||
|
&EpfClientHello {
|
||||||
|
protocol_version: PROTOCOL_VERSION,
|
||||||
|
client_random: self.client_random,
|
||||||
|
client_certificate: self.client_cert.clone(),
|
||||||
|
client_x25519_public_key: self.public_key.to_bytes(),
|
||||||
|
},
|
||||||
|
)?)
|
||||||
|
.await?;
|
||||||
|
self.inner.flush().await?;
|
||||||
|
|
||||||
|
trace!("---- !!!!! SENT CLIENT HELLO");
|
||||||
|
|
||||||
|
self.state = EpfClientState::WaitingForServerHello;
|
||||||
|
|
||||||
|
let server_x25519_key;
|
||||||
|
|
||||||
|
// Step 2: Wait for Server Hello
|
||||||
|
loop {
|
||||||
|
trace!("waiting for server hello");
|
||||||
|
|
||||||
|
let packet = recv_packet(&mut self.inner).await?;
|
||||||
|
|
||||||
|
if packet.packet_id != PACKET_SERVER_HELLO {
|
||||||
|
self.packet_queue.push(packet);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let server_hello: EpfServerHello = rmp_serde::from_slice(&packet.packet_data)?;
|
||||||
|
|
||||||
|
self.server_random = server_hello.server_random;
|
||||||
|
|
||||||
|
if server_hello.protocol_version != PROTOCOL_VERSION {
|
||||||
|
return Err(EpfHandshakeError::UnsupportedProtocolVersion(
|
||||||
|
server_hello.protocol_version as usize,
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.server_cert = Some(server_hello.server_certificate);
|
||||||
|
|
||||||
|
server_x25519_key = server_hello.server_x25519_public_key;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Validate Server Certificate
|
||||||
|
let cert_valid = self.server_cert.as_ref().unwrap().verify(&cert_pool);
|
||||||
|
if let Err(e) = cert_valid {
|
||||||
|
return Err(EpfHandshakeError::InvalidCertificate(e).into());
|
||||||
|
}
|
||||||
|
if let Ok(false) = cert_valid {
|
||||||
|
return Err(EpfHandshakeError::UntrustedCertificate.into());
|
||||||
|
}
|
||||||
|
// Server Cert OK
|
||||||
|
|
||||||
|
// Step 4: Build the cipher
|
||||||
|
|
||||||
|
let private_key = StaticSecret::from(self.private_key.to_bytes());
|
||||||
|
let their_public_key = PublicKey::from(server_x25519_key);
|
||||||
|
|
||||||
|
assert_ne!(
|
||||||
|
their_public_key.to_bytes(),
|
||||||
|
PublicKey::from(&private_key).to_bytes()
|
||||||
|
);
|
||||||
|
|
||||||
|
danger_trace!(
|
||||||
|
"pr: {}, their pub: {}, my pub: {}",
|
||||||
|
hex::encode(self.private_key.to_bytes()),
|
||||||
|
hex::encode(self.server_cert.as_ref().unwrap().details.public_key),
|
||||||
|
hex::encode(self.private_key.verifying_key().to_bytes())
|
||||||
|
);
|
||||||
|
|
||||||
|
let shared_key = private_key.diffie_hellman(&their_public_key).to_bytes();
|
||||||
|
|
||||||
|
trace!(
|
||||||
|
"server public key: {:x?}",
|
||||||
|
self.server_cert.as_ref().unwrap().details.public_key
|
||||||
|
);
|
||||||
|
danger_trace!("shared key: {}", hex::encode(shared_key));
|
||||||
|
|
||||||
|
let cc20p1305_key = Key::from(shared_key);
|
||||||
|
let cc20p1305 = XChaCha20Poly1305::new(&cc20p1305_key);
|
||||||
|
self.cipher = Some(cc20p1305);
|
||||||
|
|
||||||
|
let payload = Payload {
|
||||||
|
msg: &[0x42],
|
||||||
|
aad: &self.server_random,
|
||||||
|
};
|
||||||
|
|
||||||
|
let nonce = XNonce::from_slice(&self.client_random);
|
||||||
|
|
||||||
|
trace!("encrypting 0x42");
|
||||||
|
|
||||||
|
danger_trace!("aad: {:?} nonce: {:?}", payload.aad, nonce);
|
||||||
|
|
||||||
|
let encrypted_0x42 = match self.cipher.as_ref().unwrap().encrypt(nonce, payload) {
|
||||||
|
Ok(d) => d,
|
||||||
|
Err(_) => return Err(EpfHandshakeError::EncryptionError.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.inner
|
||||||
|
.write_all(&encode_packet(
|
||||||
|
PACKET_FINISHED,
|
||||||
|
&EpfFinished {
|
||||||
|
protocol_version: PROTOCOL_VERSION,
|
||||||
|
encrypted_0x42,
|
||||||
|
},
|
||||||
|
)?)
|
||||||
|
.await?;
|
||||||
|
self.inner.flush().await?;
|
||||||
|
|
||||||
|
self.state = EpfClientState::WaitingForFinished;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let packet = recv_packet(&mut self.inner).await?;
|
||||||
|
|
||||||
|
if packet.packet_id != PACKET_FINISHED {
|
||||||
|
self.packet_queue.push(packet);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let packet_finished: EpfFinished = rmp_serde::from_slice(&packet.packet_data)?;
|
||||||
|
|
||||||
|
trace!("trying to debug 0x42");
|
||||||
|
|
||||||
|
let payload = Payload {
|
||||||
|
msg: &packet_finished.encrypted_0x42,
|
||||||
|
aad: &self.server_random,
|
||||||
|
};
|
||||||
|
|
||||||
|
danger_trace!(
|
||||||
|
"ciphertext: {:?}, aad: {:?}, nonce: {:?}",
|
||||||
|
packet_finished.encrypted_0x42,
|
||||||
|
payload.aad,
|
||||||
|
nonce
|
||||||
|
);
|
||||||
|
|
||||||
|
let hopefully_0x42 = match self.cipher.as_ref().unwrap().decrypt(nonce, payload) {
|
||||||
|
Ok(d) => d,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(EpfHandshakeError::EncryptionError.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if hopefully_0x42 != vec![0x42] {
|
||||||
|
return Err(EpfHandshakeError::MissingKeyProof.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.state = EpfClientState::Transport;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn upgrade(self) -> EpfClientStream<T>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let aad = self.server_random;
|
||||||
|
let client_cert = self.client_cert.clone();
|
||||||
|
let packet_queue = self.packet_queue.clone();
|
||||||
|
let server_cert = self.server_cert.unwrap();
|
||||||
|
let cipher = self.cipher.unwrap();
|
||||||
|
let private_key = self.private_key.clone();
|
||||||
|
let public_key = self.public_key;
|
||||||
|
let raw_stream = self.inner;
|
||||||
|
EpfClientStream {
|
||||||
|
raw_stream,
|
||||||
|
aad,
|
||||||
|
client_cert,
|
||||||
|
packet_queue,
|
||||||
|
server_cert,
|
||||||
|
cipher,
|
||||||
|
private_key,
|
||||||
|
public_key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct EpfClientStream<S: AsyncReadExt + AsyncWriteExt + Unpin> {
|
||||||
|
raw_stream: S,
|
||||||
|
aad: [u8; 16],
|
||||||
|
client_cert: Option<EPFCertificate>,
|
||||||
|
packet_queue: Vec<EpfMessage>,
|
||||||
|
server_cert: EPFCertificate,
|
||||||
|
cipher: XChaCha20Poly1305,
|
||||||
|
private_key: EpfPrivateKey,
|
||||||
|
public_key: PublicKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait EpfStreamOps {
|
||||||
|
async fn write(&mut self, data: &[u8]) -> Result<(), Box<dyn Error>>;
|
||||||
|
async fn read(&mut self) -> Result<Vec<u8>, Box<dyn Error>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<S: AsyncReadExt + AsyncWriteExt + Unpin + Send> EpfStreamOps for EpfClientStream<S> {
|
||||||
|
async fn write(&mut self, data: &[u8]) -> Result<(), Box<dyn Error>> {
|
||||||
|
let nonce = XChaCha20Poly1305::generate_nonce(OsRng);
|
||||||
|
|
||||||
|
let payload = Payload {
|
||||||
|
msg: data,
|
||||||
|
aad: &self.aad,
|
||||||
|
};
|
||||||
|
|
||||||
|
let ciphertext = match self.cipher.encrypt(&nonce, payload) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(_) => return Err(io::Error::new(io::ErrorKind::Other, "Encryption error").into()),
|
||||||
|
};
|
||||||
|
let application_data = EpfApplicationData {
|
||||||
|
protocol_version: PROTOCOL_VERSION,
|
||||||
|
encrypted_application_data: ciphertext,
|
||||||
|
nonce: nonce.try_into().unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let packet = encode_packet(PACKET_APPLICATION_DATA, &application_data)?;
|
||||||
|
|
||||||
|
self.raw_stream.write_all(&packet).await?;
|
||||||
|
self.raw_stream.flush().await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read(&mut self) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
loop {
|
||||||
|
let packet = recv_packet(&mut self.raw_stream).await?;
|
||||||
|
|
||||||
|
if packet.packet_id != PACKET_APPLICATION_DATA {
|
||||||
|
self.packet_queue.push(packet);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let app_data: EpfApplicationData = rmp_serde::from_slice(&packet.packet_data)?;
|
||||||
|
|
||||||
|
let nonce = XNonce::from_slice(&app_data.nonce);
|
||||||
|
|
||||||
|
let payload = Payload {
|
||||||
|
msg: &app_data.encrypted_application_data,
|
||||||
|
aad: &self.aad,
|
||||||
|
};
|
||||||
|
|
||||||
|
let plaintext = match self.cipher.decrypt(nonce, payload) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(io::Error::new(io::ErrorKind::Other, "Decryption error").into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(plaintext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///// SERVER /////
|
||||||
|
|
||||||
|
pub struct EpfServerUpgraded<T: AsyncWriteExt + AsyncReadExt> {
|
||||||
|
inner: T,
|
||||||
|
state: EpfServerState,
|
||||||
|
client_random: [u8; 24],
|
||||||
|
server_random: [u8; 16],
|
||||||
|
client_cert: Option<EPFCertificate>,
|
||||||
|
packet_queue: Vec<EpfMessage>,
|
||||||
|
cipher: Option<XChaCha20Poly1305>,
|
||||||
|
cert: EPFCertificate,
|
||||||
|
private_key: EpfPrivateKey,
|
||||||
|
public_key: EpfPublicKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait EpfServerUpgradable {
|
||||||
|
async fn upgrade(
|
||||||
|
self,
|
||||||
|
cert: EPFCertificate,
|
||||||
|
private_key: EpfPrivateKey,
|
||||||
|
) -> EpfServerUpgraded<Self>
|
||||||
|
where
|
||||||
|
Self: Sized + AsyncWriteExt + AsyncReadExt + Send;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<T: ?Sized> EpfServerUpgradable for T
|
||||||
|
where
|
||||||
|
T: AsyncWriteExt + AsyncReadExt + Send,
|
||||||
|
{
|
||||||
|
async fn upgrade(
|
||||||
|
self,
|
||||||
|
cert: EPFCertificate,
|
||||||
|
private_key: EpfPrivateKey,
|
||||||
|
) -> EpfServerUpgraded<Self>
|
||||||
|
where
|
||||||
|
Self: Sized + AsyncWriteExt + AsyncReadExt + Send,
|
||||||
|
{
|
||||||
|
EpfServerUpgraded {
|
||||||
|
inner: self,
|
||||||
|
state: EpfServerState::WaitingForClientHello,
|
||||||
|
server_random: OsRng.gen(),
|
||||||
|
client_random: [0u8; 24],
|
||||||
|
cert,
|
||||||
|
client_cert: None,
|
||||||
|
packet_queue: vec![],
|
||||||
|
cipher: None,
|
||||||
|
private_key: private_key.clone(),
|
||||||
|
public_key: private_key.verifying_key(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait EpfServerHandshaker<S: AsyncWriteExt + AsyncReadExt + Unpin> {
|
||||||
|
async fn handshake(&mut self, cert_pool: EpfCaPool) -> Result<(), Box<dyn Error>>;
|
||||||
|
async fn upgrade(self) -> EpfServerStream<S>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<T: AsyncWriteExt + AsyncReadExt + Send + Unpin> EpfServerHandshaker<T>
|
||||||
|
for EpfServerUpgraded<T>
|
||||||
|
{
|
||||||
|
async fn handshake(&mut self, cert_pool: EpfCaPool) -> Result<(), Box<dyn Error>> {
|
||||||
|
match self.state {
|
||||||
|
EpfServerState::WaitingForClientHello => (),
|
||||||
|
_ => return Err(EpfHandshakeError::AlreadyTunnelled.into()),
|
||||||
|
}
|
||||||
|
|
||||||
|
let client_public_key;
|
||||||
|
|
||||||
|
// Step 1: Wait for Client Hello
|
||||||
|
loop {
|
||||||
|
let packet = recv_packet(&mut self.inner).await?;
|
||||||
|
|
||||||
|
if packet.packet_id != PACKET_CLIENT_HELLO {
|
||||||
|
self.packet_queue.push(packet);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace!("got client hello");
|
||||||
|
|
||||||
|
let client_hello: EpfClientHello = rmp_serde::from_slice(&packet.packet_data)?;
|
||||||
|
|
||||||
|
self.client_random = client_hello.client_random;
|
||||||
|
|
||||||
|
if client_hello.protocol_version != PROTOCOL_VERSION {
|
||||||
|
return Err(EpfHandshakeError::UnsupportedProtocolVersion(
|
||||||
|
client_hello.protocol_version as usize,
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.client_cert = client_hello.client_certificate;
|
||||||
|
|
||||||
|
client_public_key = client_hello.client_x25519_public_key;
|
||||||
|
|
||||||
|
trace!("exiting loop");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Validate Client Certificate (if present)
|
||||||
|
if let Some(client_cert) = &self.client_cert {
|
||||||
|
let cert_valid = client_cert.verify(&cert_pool);
|
||||||
|
if let Err(e) = cert_valid {
|
||||||
|
return Err(EpfHandshakeError::InvalidCertificate(e).into());
|
||||||
|
}
|
||||||
|
if let Ok(false) = cert_valid {
|
||||||
|
return Err(EpfHandshakeError::UntrustedCertificate.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Client Cert OK (if present)
|
||||||
|
|
||||||
|
trace!("client cert okay");
|
||||||
|
|
||||||
|
// Step 3: Send Server Hello
|
||||||
|
self.inner
|
||||||
|
.write_all(&encode_packet(
|
||||||
|
PACKET_SERVER_HELLO,
|
||||||
|
&EpfServerHello {
|
||||||
|
protocol_version: PROTOCOL_VERSION,
|
||||||
|
server_certificate: self.cert.clone(),
|
||||||
|
server_random: self.server_random,
|
||||||
|
server_x25519_public_key: PublicKey::from(&StaticSecret::from(
|
||||||
|
self.private_key.to_bytes(),
|
||||||
|
))
|
||||||
|
.to_bytes(),
|
||||||
|
},
|
||||||
|
)?)
|
||||||
|
.await?;
|
||||||
|
self.inner.flush().await?;
|
||||||
|
|
||||||
|
trace!("sent server hello");
|
||||||
|
|
||||||
|
self.state = EpfServerState::WaitingForFinished;
|
||||||
|
|
||||||
|
// Step 4: Build the cipher
|
||||||
|
let private_key = StaticSecret::from(self.private_key.to_bytes());
|
||||||
|
let their_public_key = PublicKey::from(client_public_key);
|
||||||
|
|
||||||
|
assert_ne!(
|
||||||
|
their_public_key.to_bytes(),
|
||||||
|
PublicKey::from(&private_key).to_bytes()
|
||||||
|
);
|
||||||
|
|
||||||
|
danger_trace!(
|
||||||
|
"pr: {}, their pub: {}, my pub: {}",
|
||||||
|
hex::encode(self.private_key.to_bytes()),
|
||||||
|
hex::encode(client_public_key),
|
||||||
|
hex::encode(self.private_key.verifying_key().to_bytes())
|
||||||
|
);
|
||||||
|
|
||||||
|
let shared_key = private_key.diffie_hellman(&their_public_key).to_bytes();
|
||||||
|
|
||||||
|
trace!("client public key: {:x?}", client_public_key);
|
||||||
|
danger_trace!("shared key: {}", hex::encode(shared_key));
|
||||||
|
|
||||||
|
let cc20p1305_key = Key::from(shared_key);
|
||||||
|
let cc20p1305 = XChaCha20Poly1305::new(&cc20p1305_key);
|
||||||
|
self.cipher = Some(cc20p1305);
|
||||||
|
|
||||||
|
let payload = Payload {
|
||||||
|
msg: &[0x42],
|
||||||
|
aad: &self.server_random,
|
||||||
|
};
|
||||||
|
|
||||||
|
let nonce = XNonce::from_slice(&self.client_random);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let packet = recv_packet(&mut self.inner).await?;
|
||||||
|
|
||||||
|
if packet.packet_id != PACKET_FINISHED {
|
||||||
|
self.packet_queue.push(packet);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let packet_finished: EpfFinished = rmp_serde::from_slice(&packet.packet_data)?;
|
||||||
|
|
||||||
|
let payload = Payload {
|
||||||
|
msg: &packet_finished.encrypted_0x42,
|
||||||
|
aad: &self.server_random,
|
||||||
|
};
|
||||||
|
|
||||||
|
trace!("trying to decrypt 0x42");
|
||||||
|
danger_trace!(
|
||||||
|
"ciphertext: {:?}, nonce: {:?}, aad: {:?}",
|
||||||
|
payload.msg,
|
||||||
|
nonce,
|
||||||
|
payload.aad
|
||||||
|
);
|
||||||
|
|
||||||
|
let hopefully_0x42 = match self.cipher.as_ref().unwrap().decrypt(nonce, payload) {
|
||||||
|
Ok(d) => d,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(EpfHandshakeError::EncryptionError.into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if hopefully_0x42 != vec![0x42] {
|
||||||
|
return Err(EpfHandshakeError::MissingKeyProof.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let encrypted_0x42 = match self.cipher.as_ref().unwrap().encrypt(nonce, payload) {
|
||||||
|
Ok(d) => d,
|
||||||
|
Err(_) => return Err(EpfHandshakeError::EncryptionError.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.inner
|
||||||
|
.write_all(&encode_packet(
|
||||||
|
PACKET_FINISHED,
|
||||||
|
&EpfFinished {
|
||||||
|
protocol_version: PROTOCOL_VERSION,
|
||||||
|
encrypted_0x42,
|
||||||
|
},
|
||||||
|
)?)
|
||||||
|
.await?;
|
||||||
|
self.inner.flush().await?;
|
||||||
|
|
||||||
|
self.state = EpfServerState::WaitingForFinished;
|
||||||
|
|
||||||
|
self.state = EpfServerState::Transport;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn upgrade(self) -> EpfServerStream<T>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
EpfServerStream {
|
||||||
|
aad: self.server_random,
|
||||||
|
server_cert: self.cert,
|
||||||
|
packet_queue: self.packet_queue,
|
||||||
|
client_cert: self.client_cert,
|
||||||
|
cipher: self.cipher.unwrap(),
|
||||||
|
private_key: self.private_key,
|
||||||
|
public_key: self.public_key,
|
||||||
|
raw_stream: self.inner,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub struct EpfServerStream<S: AsyncReadExt + AsyncWriteExt + Unpin> {
|
||||||
|
raw_stream: S,
|
||||||
|
aad: [u8; 16],
|
||||||
|
client_cert: Option<EPFCertificate>,
|
||||||
|
packet_queue: Vec<EpfMessage>,
|
||||||
|
server_cert: EPFCertificate,
|
||||||
|
cipher: XChaCha20Poly1305,
|
||||||
|
private_key: EpfPrivateKey,
|
||||||
|
public_key: EpfPublicKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<S: AsyncReadExt + AsyncWriteExt + Unpin + Send> EpfStreamOps for EpfServerStream<S> {
|
||||||
|
async fn write(&mut self, data: &[u8]) -> Result<(), Box<dyn Error>> {
|
||||||
|
let nonce = XChaCha20Poly1305::generate_nonce(OsRng);
|
||||||
|
|
||||||
|
let payload = Payload {
|
||||||
|
msg: data,
|
||||||
|
aad: &self.aad,
|
||||||
|
};
|
||||||
|
|
||||||
|
let ciphertext = match self.cipher.encrypt(&nonce, payload) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(_) => return Err(io::Error::new(io::ErrorKind::Other, "Encryption error").into()),
|
||||||
|
};
|
||||||
|
let application_data = EpfApplicationData {
|
||||||
|
protocol_version: PROTOCOL_VERSION,
|
||||||
|
encrypted_application_data: ciphertext,
|
||||||
|
nonce: nonce.try_into().unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let packet = encode_packet(PACKET_APPLICATION_DATA, &application_data)?;
|
||||||
|
|
||||||
|
self.raw_stream.write_all(&packet).await?;
|
||||||
|
self.raw_stream.flush().await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn read(&mut self) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
loop {
|
||||||
|
let packet = recv_packet(&mut self.raw_stream).await?;
|
||||||
|
|
||||||
|
if packet.packet_id != PACKET_APPLICATION_DATA {
|
||||||
|
self.packet_queue.push(packet);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let app_data: EpfApplicationData = rmp_serde::from_slice(&packet.packet_data)?;
|
||||||
|
|
||||||
|
let nonce = XNonce::from_slice(&app_data.nonce);
|
||||||
|
|
||||||
|
let payload = Payload {
|
||||||
|
msg: &app_data.encrypted_application_data,
|
||||||
|
aad: &self.aad,
|
||||||
|
};
|
||||||
|
|
||||||
|
let plaintext = match self.cipher.decrypt(nonce, payload) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(_) => {
|
||||||
|
return Err(io::Error::new(io::ErrorKind::Other, "Decryption error").into())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(plaintext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::ca_pool::{EpfCaPool, EpfCaPoolOps};
|
||||||
|
use crate::handshake_stream::{
|
||||||
|
ClientAuthentication, EpfClientHandshaker, EpfClientUpgradable, EpfClientUpgraded,
|
||||||
|
EpfServerHandshaker, EpfServerUpgradable, EpfServerUpgraded, EpfStreamOps,
|
||||||
|
};
|
||||||
|
use crate::pki::{EPFCertificate, EPFCertificateDetails, EpfPkiCertificateOps};
|
||||||
|
use ed25519_dalek::SigningKey;
|
||||||
|
use log::{debug, trace};
|
||||||
|
|
||||||
|
use serial_test::serial;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
use tokio::join;
|
||||||
|
use tokio::net::{TcpListener, TcpSocket, TcpStream};
|
||||||
|
use x25519_dalek::{PublicKey, StaticSecret};
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
pub async fn stream_test() {
|
||||||
|
//simple_logger::init().unwrap();
|
||||||
|
|
||||||
|
let tcp_listener = TcpListener::bind("0.0.0.0:36116").await.unwrap();
|
||||||
|
|
||||||
|
let tcp_client_future = TcpSocket::new_v4()
|
||||||
|
.unwrap()
|
||||||
|
.connect(SocketAddr::from_str("127.0.0.1:36116").unwrap());
|
||||||
|
|
||||||
|
let (a, b) = join![tcp_listener.accept(), tcp_client_future];
|
||||||
|
|
||||||
|
let (s, _) = a.unwrap();
|
||||||
|
let c = b.unwrap();
|
||||||
|
|
||||||
|
let server_private_key = SigningKey::from([1u8; 32]);
|
||||||
|
let client_private_key = SigningKey::from([2u8; 32]);
|
||||||
|
|
||||||
|
let mut server_cert = EPFCertificate {
|
||||||
|
details: EPFCertificateDetails {
|
||||||
|
name: "Testing Server Certificate".to_string(),
|
||||||
|
not_before: 0,
|
||||||
|
not_after: SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs()
|
||||||
|
+ 30,
|
||||||
|
public_key: server_private_key.verifying_key().to_bytes(),
|
||||||
|
issuer_public_key: [0u8; 32],
|
||||||
|
claims: Default::default(),
|
||||||
|
},
|
||||||
|
fingerprint: "".to_string(),
|
||||||
|
signature: [0u8; 64],
|
||||||
|
};
|
||||||
|
server_cert.sign(&server_private_key).unwrap();
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"{}",
|
||||||
|
hex::encode(server_private_key.verifying_key().to_bytes())
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut client_cert = EPFCertificate {
|
||||||
|
details: EPFCertificateDetails {
|
||||||
|
name: "Testing Client Certificate".to_string(),
|
||||||
|
not_before: 0,
|
||||||
|
not_after: SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs()
|
||||||
|
+ 30,
|
||||||
|
public_key: client_private_key.verifying_key().to_bytes(),
|
||||||
|
issuer_public_key: [0u8; 32],
|
||||||
|
claims: Default::default(),
|
||||||
|
},
|
||||||
|
fingerprint: "".to_string(),
|
||||||
|
signature: [0u8; 64],
|
||||||
|
};
|
||||||
|
client_cert.sign(&client_private_key).unwrap();
|
||||||
|
|
||||||
|
let mut cert_pool = EpfCaPool::new();
|
||||||
|
let mut cert_pool_2 = EpfCaPool::new();
|
||||||
|
cert_pool.insert(&server_cert);
|
||||||
|
cert_pool.insert(&client_cert);
|
||||||
|
cert_pool_2.insert(&client_cert);
|
||||||
|
cert_pool_2.insert(&server_cert);
|
||||||
|
|
||||||
|
let mut c: EpfClientUpgraded<TcpStream> = EpfClientUpgradable::upgrade(
|
||||||
|
c,
|
||||||
|
ClientAuthentication::Cert(Box::new(client_cert), Box::new(client_private_key)),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let mut s: EpfServerUpgraded<TcpStream> =
|
||||||
|
EpfServerUpgradable::upgrade(s, server_cert, server_private_key).await;
|
||||||
|
|
||||||
|
let server_handshake_accept_task = tokio::spawn(async move {
|
||||||
|
trace!("starting server handshake listener");
|
||||||
|
s.handshake(cert_pool_2).await.unwrap();
|
||||||
|
let mut upgraded = s.upgrade().await;
|
||||||
|
assert_eq!(upgraded.read().await.unwrap(), vec![0x42, 0x42]);
|
||||||
|
upgraded.write(&[0x42, 0x42]).await.unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
let client_handshake_send_task = tokio::spawn(async move {
|
||||||
|
trace!("starting client handshake sender");
|
||||||
|
c.handshake(cert_pool).await.unwrap();
|
||||||
|
let mut upgraded = EpfClientHandshaker::upgrade(c).await;
|
||||||
|
upgraded.write(&[0x42, 0x42]).await.unwrap();
|
||||||
|
assert_eq!(upgraded.read().await.unwrap(), vec![0x42, 0x42]);
|
||||||
|
});
|
||||||
|
|
||||||
|
let (a, b) = join![server_handshake_accept_task, client_handshake_send_task];
|
||||||
|
a.unwrap();
|
||||||
|
b.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
pub async fn stream_test_ephemeral() {
|
||||||
|
//simple_logger::init().unwrap();
|
||||||
|
|
||||||
|
let tcp_listener = TcpListener::bind("0.0.0.0:36117").await.unwrap();
|
||||||
|
|
||||||
|
let tcp_client_future = TcpSocket::new_v4()
|
||||||
|
.unwrap()
|
||||||
|
.connect(SocketAddr::from_str("127.0.0.1:36117").unwrap());
|
||||||
|
|
||||||
|
let (a, b) = join![tcp_listener.accept(), tcp_client_future];
|
||||||
|
|
||||||
|
let (s, _) = a.unwrap();
|
||||||
|
let c = b.unwrap();
|
||||||
|
|
||||||
|
let server_private_key = SigningKey::from([1u8; 32]);
|
||||||
|
|
||||||
|
let mut server_cert = EPFCertificate {
|
||||||
|
details: EPFCertificateDetails {
|
||||||
|
name: "Testing Server Certificate".to_string(),
|
||||||
|
not_before: 0,
|
||||||
|
not_after: SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs()
|
||||||
|
+ 30,
|
||||||
|
public_key: server_private_key.verifying_key().to_bytes(),
|
||||||
|
issuer_public_key: [0u8; 32],
|
||||||
|
claims: Default::default(),
|
||||||
|
},
|
||||||
|
fingerprint: "".to_string(),
|
||||||
|
signature: [0u8; 64],
|
||||||
|
};
|
||||||
|
server_cert.sign(&server_private_key).unwrap();
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"{}",
|
||||||
|
hex::encode(server_private_key.verifying_key().to_bytes())
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut cert_pool = EpfCaPool::new();
|
||||||
|
let mut cert_pool_2 = EpfCaPool::new();
|
||||||
|
cert_pool.insert(&server_cert);
|
||||||
|
|
||||||
|
cert_pool_2.insert(&server_cert);
|
||||||
|
|
||||||
|
let mut c: EpfClientUpgraded<TcpStream> =
|
||||||
|
EpfClientUpgradable::upgrade(c, ClientAuthentication::Ephemeral).await;
|
||||||
|
let mut s: EpfServerUpgraded<TcpStream> =
|
||||||
|
EpfServerUpgradable::upgrade(s, server_cert, server_private_key).await;
|
||||||
|
|
||||||
|
let server_handshake_accept_task = tokio::spawn(async move {
|
||||||
|
trace!("starting server handshake listener");
|
||||||
|
s.handshake(cert_pool_2).await.unwrap();
|
||||||
|
let mut upgraded = s.upgrade().await;
|
||||||
|
assert_eq!(upgraded.read().await.unwrap(), vec![0x42, 0x42])
|
||||||
|
});
|
||||||
|
|
||||||
|
let client_handshake_send_task = tokio::spawn(async move {
|
||||||
|
trace!("starting client handshake sender");
|
||||||
|
c.handshake(cert_pool).await.unwrap();
|
||||||
|
let mut upgraded = EpfClientHandshaker::upgrade(c).await;
|
||||||
|
upgraded.write(&[0x42, 0x42]).await.unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
let (a, b) = join![server_handshake_accept_task, client_handshake_send_task];
|
||||||
|
a.unwrap();
|
||||||
|
b.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn x25519_sanity_check() {
|
||||||
|
let bob_key = StaticSecret::from([1u8; 32]);
|
||||||
|
let bob_pub = PublicKey::from(&bob_key);
|
||||||
|
|
||||||
|
let alice_key = StaticSecret::from([2u8; 32]);
|
||||||
|
let alice_pub = PublicKey::from(&alice_key);
|
||||||
|
|
||||||
|
let ss_1 = bob_key.diffie_hellman(&alice_pub);
|
||||||
|
let ss_2 = alice_key.diffie_hellman(&bob_pub);
|
||||||
|
|
||||||
|
assert_eq!(ss_1.to_bytes(), ss_2.to_bytes());
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"SS: {}, B_p: {}, A_p: {}",
|
||||||
|
hex::encode(ss_1.to_bytes()),
|
||||||
|
hex::encode(bob_pub.to_bytes()),
|
||||||
|
hex::encode(alice_pub.to_bytes())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,8 @@
|
||||||
pub mod ca_pool;
|
pub mod ca_pool;
|
||||||
|
pub mod error;
|
||||||
|
pub mod handshake_stream;
|
||||||
pub mod pki;
|
pub mod pki;
|
||||||
pub mod util;
|
|
||||||
pub mod protocol;
|
pub mod protocol;
|
||||||
|
pub mod util;
|
||||||
|
#[macro_use]
|
||||||
|
pub mod log;
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! danger_trace {
|
||||||
|
// trace!(target: "my_target", key1 = 42, key2 = true; "a {} event", "log")
|
||||||
|
// trace!(target: "my_target", "a {} event", "log")
|
||||||
|
(target: $target:expr, $($arg:tt)+) => {
|
||||||
|
if std::env::var("E3PF_SUPER_DANGEROUS_VERY_VERBOSE_DEBUGGING_LOGGING") == Ok("enabled".to_string()) && cfg!(debug_assertions) {
|
||||||
|
log::trace!(target: $target, $($arg)+)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// trace!("a {} event", "log")
|
||||||
|
($($arg:tt)+) => {
|
||||||
|
if std::env::var("E3PF_SUPER_DANGEROUS_VERY_VERBOSE_DEBUGGING_LOGGING") == Ok("enabled".to_string()) && cfg!(debug_assertions) {
|
||||||
|
log::trace!($($arg)+)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ca_pool::{EpfCaPool, EpfCaPoolOps};
|
use crate::ca_pool::{EpfCaPool, EpfCaPoolOps};
|
||||||
use crate::util::{pretty_print_date, u64_to_st};
|
use crate::util::{pretty_print_date, u64_to_st, verifying_key};
|
||||||
use ed25519_dalek::{Signature, SignatureError, Signer, SigningKey, Verifier, VerifyingKey};
|
use ed25519_dalek::{Signature, SignatureError, Signer, SigningKey, Verifier, VerifyingKey};
|
||||||
use pem::Pem;
|
use pem::Pem;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -15,8 +15,8 @@ pub const EPFPKI_SIGNATURE_LENGTH: usize = 64;
|
||||||
pub const EPFPKI_SELF_SIGNED_CERTIFICATE: &str =
|
pub const EPFPKI_SELF_SIGNED_CERTIFICATE: &str =
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000";
|
"0000000000000000000000000000000000000000000000000000000000000000";
|
||||||
|
|
||||||
pub type EpfPublicKey = [u8; 32];
|
pub type EpfPublicKey = VerifyingKey;
|
||||||
pub type EpfPrivateKey = [u8; 64];
|
pub type EpfPrivateKey = SigningKey;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Eq, Clone)]
|
#[derive(Serialize, Deserialize, PartialEq, Debug, Eq, Clone)]
|
||||||
pub struct EPFCertificate {
|
pub struct EPFCertificate {
|
||||||
|
@ -43,8 +43,8 @@ pub struct EPFCertificateDetails {
|
||||||
pub trait EpfPkiSerializable {
|
pub trait EpfPkiSerializable {
|
||||||
const PEM_BANNER: &'static str;
|
const PEM_BANNER: &'static str;
|
||||||
|
|
||||||
fn as_bytes(&self) -> Result<Vec<u8>, rmp_serde::encode::Error>;
|
fn as_bytes_pki(&self) -> Result<Vec<u8>, rmp_serde::encode::Error>;
|
||||||
fn from_bytes(bytes: &[u8]) -> Result<Self, rmp_serde::decode::Error>
|
fn from_bytes_pki(bytes: &[u8]) -> Result<Self, rmp_serde::decode::Error>
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
|
|
||||||
|
@ -62,10 +62,6 @@ pub fn fingerprint(cert: &EPFCertificateDetails) -> Result<String, rmp_serde::en
|
||||||
Ok(hex::encode(hash))
|
Ok(hex::encode(hash))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_signing_key(b: &[u8; 64]) -> Result<SigningKey, SignatureError> {
|
|
||||||
SigningKey::from_keypair_bytes(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum EpfPkiCertificateValidationError {
|
pub enum EpfPkiCertificateValidationError {
|
||||||
NoLongerValid { expired_at: SystemTime },
|
NoLongerValid { expired_at: SystemTime },
|
||||||
|
@ -132,12 +128,11 @@ impl EpfPkiCertificateOps for EPFCertificate {
|
||||||
fn sign(&mut self, private_key: &EpfPrivateKey) -> Result<(), Box<dyn Error>> {
|
fn sign(&mut self, private_key: &EpfPrivateKey) -> Result<(), Box<dyn Error>> {
|
||||||
self.recalculate_fingerprint()?;
|
self.recalculate_fingerprint()?;
|
||||||
|
|
||||||
let signing_key = load_signing_key(private_key)?;
|
self.details.issuer_public_key = *private_key.verifying_key().as_bytes();
|
||||||
self.details.issuer_public_key = *signing_key.verifying_key().as_bytes();
|
|
||||||
|
|
||||||
let cert_data_bytes = rmp_serde::to_vec(&self.details)?;
|
let cert_data_bytes = rmp_serde::to_vec(&self.details)?;
|
||||||
|
|
||||||
let signature = signing_key.sign(&cert_data_bytes).to_vec();
|
let signature = private_key.sign(&cert_data_bytes).to_vec();
|
||||||
|
|
||||||
self.signature = signature.try_into().unwrap();
|
self.signature = signature.try_into().unwrap();
|
||||||
|
|
||||||
|
@ -182,26 +177,19 @@ impl EpfPkiCertificateOps for EPFCertificate {
|
||||||
|
|
||||||
let is_self_signed;
|
let is_self_signed;
|
||||||
|
|
||||||
println!(
|
let public_key = if self.details.issuer_public_key == self.details.public_key {
|
||||||
"{}: {:?} {:?}",
|
|
||||||
self.details.name, self.details.issuer_public_key, self.details.public_key
|
|
||||||
);
|
|
||||||
|
|
||||||
let verifying_key = if self.details.issuer_public_key == self.details.public_key {
|
|
||||||
// self-signed certificate
|
// self-signed certificate
|
||||||
is_self_signed = true;
|
is_self_signed = true;
|
||||||
VerifyingKey::from_bytes(&self.details.public_key)
|
verifying_key(&self.details.public_key)
|
||||||
.map_err(|e| EpfPkiCertificateValidationError::InvalidSignature { e })?
|
|
||||||
} else {
|
} else {
|
||||||
is_self_signed = false;
|
is_self_signed = false;
|
||||||
VerifyingKey::from_bytes(&self.details.issuer_public_key)
|
verifying_key(&self.details.issuer_public_key)
|
||||||
.map_err(|e| EpfPkiCertificateValidationError::InvalidSignature { e })?
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let cert_data_bytes = rmp_serde::to_vec(&self.details)
|
let cert_data_bytes = rmp_serde::to_vec(&self.details)
|
||||||
.map_err(|e| EpfPkiCertificateValidationError::InvalidCertificateData { e })?;
|
.map_err(|e| EpfPkiCertificateValidationError::InvalidCertificateData { e })?;
|
||||||
|
|
||||||
verifying_key
|
public_key
|
||||||
.verify(&cert_data_bytes, &signature)
|
.verify(&cert_data_bytes, &signature)
|
||||||
.map_err(|e| EpfPkiCertificateValidationError::InvalidSignature { e })?;
|
.map_err(|e| EpfPkiCertificateValidationError::InvalidSignature { e })?;
|
||||||
|
|
||||||
|
@ -209,12 +197,12 @@ impl EpfPkiCertificateOps for EPFCertificate {
|
||||||
|
|
||||||
// Is the signer trusted?
|
// Is the signer trusted?
|
||||||
let ca_cert = if is_self_signed {
|
let ca_cert = if is_self_signed {
|
||||||
if let Some(cert) = ca_pool.get_ca(&self.details.public_key) {
|
if let Some(cert) = ca_pool.get_ca(&verifying_key(&self.details.public_key)) {
|
||||||
cert
|
cert
|
||||||
} else {
|
} else {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
} else if let Some(cert) = ca_pool.get_ca(&self.details.issuer_public_key) {
|
} else if let Some(cert) = ca_pool.get_ca(&verifying_key(&self.details.issuer_public_key)) {
|
||||||
cert
|
cert
|
||||||
} else {
|
} else {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
|
@ -262,7 +250,7 @@ impl Display for EPFCertificate {
|
||||||
)?;
|
)?;
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
"\t\tIssuer Fingerprint: {}",
|
"\t\tIssuer Public Key: {}",
|
||||||
hex::encode(self.details.issuer_public_key)
|
hex::encode(self.details.issuer_public_key)
|
||||||
)?;
|
)?;
|
||||||
writeln!(f, "\t}}")?;
|
writeln!(f, "\t}}")?;
|
||||||
|
@ -275,18 +263,20 @@ impl Display for EPFCertificate {
|
||||||
impl EpfPkiSerializable for EPFCertificate {
|
impl EpfPkiSerializable for EPFCertificate {
|
||||||
const PEM_BANNER: &'static str = "EPF CERTIFICATE";
|
const PEM_BANNER: &'static str = "EPF CERTIFICATE";
|
||||||
|
|
||||||
fn as_bytes(&self) -> Result<Vec<u8>, rmp_serde::encode::Error> {
|
fn as_bytes_pki(&self) -> Result<Vec<u8>, rmp_serde::encode::Error> {
|
||||||
rmp_serde::to_vec(self)
|
rmp_serde::to_vec(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_bytes(bytes: &[u8]) -> Result<Self, rmp_serde::decode::Error> {
|
fn from_bytes_pki(bytes: &[u8]) -> Result<Self, rmp_serde::decode::Error> {
|
||||||
rmp_serde::from_slice(bytes)
|
rmp_serde::from_slice(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_pem(&self) -> Result<Vec<u8>, Box<dyn Error>> {
|
fn as_pem(&self) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
Ok(pem::encode(&Pem::new(Self::PEM_BANNER, self.as_bytes()?))
|
Ok(
|
||||||
.as_bytes()
|
pem::encode(&Pem::new(Self::PEM_BANNER, self.as_bytes_pki()?))
|
||||||
.to_vec())
|
.as_bytes()
|
||||||
|
.to_vec(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_pem(bytes: &[u8]) -> Result<Self, Box<dyn Error>>
|
fn from_pem(bytes: &[u8]) -> Result<Self, Box<dyn Error>>
|
||||||
|
@ -297,27 +287,29 @@ impl EpfPkiSerializable for EPFCertificate {
|
||||||
if pem.tag() != Self::PEM_BANNER {
|
if pem.tag() != Self::PEM_BANNER {
|
||||||
return Err("Not a certificate".into());
|
return Err("Not a certificate".into());
|
||||||
}
|
}
|
||||||
Ok(Self::from_bytes(pem.contents())?)
|
Ok(Self::from_bytes_pki(pem.contents())?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EpfPkiSerializable for EpfPublicKey {
|
impl EpfPkiSerializable for EpfPublicKey {
|
||||||
const PEM_BANNER: &'static str = "EPF PUBLIC KEY";
|
const PEM_BANNER: &'static str = "EPF PUBLIC KEY";
|
||||||
|
|
||||||
fn as_bytes(&self) -> Result<Vec<u8>, rmp_serde::encode::Error> {
|
fn as_bytes_pki(&self) -> Result<Vec<u8>, rmp_serde::encode::Error> {
|
||||||
Ok(self.to_vec())
|
Ok(self.as_bytes().to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_bytes(bytes: &[u8]) -> Result<Self, rmp_serde::decode::Error> {
|
fn from_bytes_pki(bytes: &[u8]) -> Result<Self, rmp_serde::decode::Error> {
|
||||||
bytes
|
bytes
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| rmp_serde::decode::Error::LengthMismatch(bytes.len() as u32))
|
.map_err(|_| rmp_serde::decode::Error::LengthMismatch(bytes.len() as u32))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_pem(&self) -> Result<Vec<u8>, Box<dyn Error>> {
|
fn as_pem(&self) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
Ok(pem::encode(&Pem::new(Self::PEM_BANNER, self.as_bytes()?))
|
Ok(
|
||||||
.as_bytes()
|
pem::encode(&Pem::new(Self::PEM_BANNER, self.as_bytes().to_vec()))
|
||||||
.to_vec())
|
.as_bytes()
|
||||||
|
.to_vec(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_pem(bytes: &[u8]) -> Result<Self, Box<dyn Error>>
|
fn from_pem(bytes: &[u8]) -> Result<Self, Box<dyn Error>>
|
||||||
|
@ -328,27 +320,33 @@ impl EpfPkiSerializable for EpfPublicKey {
|
||||||
if pem.tag() != Self::PEM_BANNER {
|
if pem.tag() != Self::PEM_BANNER {
|
||||||
return Err("Not a public key".into());
|
return Err("Not a public key".into());
|
||||||
}
|
}
|
||||||
Ok(Self::from_bytes(pem.contents())?)
|
Ok(Self::from_bytes(
|
||||||
|
pem.contents()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| -> Box<dyn Error> { "Wrong size".into() })?,
|
||||||
|
)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EpfPkiSerializable for EpfPrivateKey {
|
impl EpfPkiSerializable for EpfPrivateKey {
|
||||||
const PEM_BANNER: &'static str = "EPF PRIVATE KEY";
|
const PEM_BANNER: &'static str = "EPF PRIVATE KEY";
|
||||||
|
|
||||||
fn as_bytes(&self) -> Result<Vec<u8>, rmp_serde::encode::Error> {
|
fn as_bytes_pki(&self) -> Result<Vec<u8>, rmp_serde::encode::Error> {
|
||||||
Ok(self.to_vec())
|
Ok(self.to_keypair_bytes().to_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_bytes(bytes: &[u8]) -> Result<Self, rmp_serde::decode::Error> {
|
fn from_bytes_pki(bytes: &[u8]) -> Result<Self, rmp_serde::decode::Error> {
|
||||||
bytes
|
bytes
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| rmp_serde::decode::Error::LengthMismatch(bytes.len() as u32))
|
.map_err(|_| rmp_serde::decode::Error::LengthMismatch(bytes.len() as u32))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_pem(&self) -> Result<Vec<u8>, Box<dyn Error>> {
|
fn as_pem(&self) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
Ok(pem::encode(&Pem::new(Self::PEM_BANNER, self.as_bytes()?))
|
Ok(
|
||||||
.as_bytes()
|
pem::encode(&Pem::new(Self::PEM_BANNER, self.as_bytes_pki()?))
|
||||||
.to_vec())
|
.as_bytes()
|
||||||
|
.to_vec(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_pem(bytes: &[u8]) -> Result<Self, Box<dyn Error>>
|
fn from_pem(bytes: &[u8]) -> Result<Self, Box<dyn Error>>
|
||||||
|
@ -359,7 +357,11 @@ impl EpfPkiSerializable for EpfPrivateKey {
|
||||||
if pem.tag() != Self::PEM_BANNER {
|
if pem.tag() != Self::PEM_BANNER {
|
||||||
return Err("Incorrect PEM tag".into());
|
return Err("Incorrect PEM tag".into());
|
||||||
}
|
}
|
||||||
Ok(Self::from_bytes(pem.contents())?)
|
Ok(Self::from_keypair_bytes(
|
||||||
|
pem.contents()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| -> Box<dyn Error> { "Wrong size".into() })?,
|
||||||
|
)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,19 +373,24 @@ mod tests {
|
||||||
EpfPkiCertificateValidationError, EpfPkiSerializable, EpfPrivateKey, EpfPublicKey,
|
EpfPkiCertificateValidationError, EpfPkiSerializable, EpfPrivateKey, EpfPublicKey,
|
||||||
EPFPKI_PUBLIC_KEY_LENGTH, EPFPKI_SIGNATURE_LENGTH,
|
EPFPKI_PUBLIC_KEY_LENGTH, EPFPKI_SIGNATURE_LENGTH,
|
||||||
};
|
};
|
||||||
|
use crate::util::{signing_key, verifying_key};
|
||||||
use ed25519_dalek::{SignatureError, SigningKey};
|
use ed25519_dalek::{SignatureError, SigningKey};
|
||||||
|
use hex_literal::hex;
|
||||||
use rand::rngs::OsRng;
|
use rand::rngs::OsRng;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn certificate_serialization() {
|
pub fn certificate_serialization() {
|
||||||
assert_eq!(cert().as_bytes().unwrap(), cert_bytes())
|
assert_eq!(cert().as_bytes_pki().unwrap(), cert_bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn certificate_deserialization() {
|
pub fn certificate_deserialization() {
|
||||||
assert_eq!(EPFCertificate::from_bytes(&cert_bytes()).unwrap(), cert())
|
assert_eq!(
|
||||||
|
EPFCertificate::from_bytes_pki(&cert_bytes()).unwrap(),
|
||||||
|
cert()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -404,24 +411,33 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn pubkey_serialization() {
|
pub fn pubkey_serialization() {
|
||||||
assert_eq!(([0u8; 32]).as_bytes().unwrap(), [0u8; 32].to_vec())
|
assert_eq!(
|
||||||
|
(verifying_key(&[0u8; 32])).as_bytes_pki().unwrap(),
|
||||||
|
[0u8; 32].to_vec()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn pubkey_deserialization() {
|
pub fn pubkey_deserialization() {
|
||||||
assert_eq!(EpfPublicKey::from_bytes(&[0u8; 32]).unwrap(), [0u8; 32])
|
assert_eq!(
|
||||||
|
EpfPublicKey::from_bytes(&[0u8; 32]).unwrap(),
|
||||||
|
verifying_key(&[0u8; 32])
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn pubkey_serialization_pem() {
|
pub fn pubkey_serialization_pem() {
|
||||||
assert_eq!(([0u8; 32]).as_pem().unwrap(), null_public_key_pem())
|
assert_eq!(
|
||||||
|
(verifying_key(&[0u8; 32])).as_pem().unwrap(),
|
||||||
|
null_public_key_pem()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn pubkey_deserialization_pem() {
|
pub fn pubkey_deserialization_pem() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
EpfPublicKey::from_pem(&null_public_key_pem()).unwrap(),
|
EpfPublicKey::from_pem(&null_public_key_pem()).unwrap(),
|
||||||
[0u8; 32]
|
verifying_key(&[0u8; 32])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -433,24 +449,42 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn privkey_serialization() {
|
pub fn privkey_serialization() {
|
||||||
assert_eq!(([0u8; 64]).as_bytes().unwrap(), [0u8; 64].to_vec())
|
let priv_key_data = hex!("00000000000000000000000000000000000000000000000000000000000000003B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29");
|
||||||
|
assert_eq!(
|
||||||
|
(signing_key(&priv_key_data)).as_bytes_pki().unwrap(),
|
||||||
|
priv_key_data.to_vec()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn privkey_deserialization() {
|
pub fn privkey_deserialization() {
|
||||||
assert_eq!(EpfPrivateKey::from_bytes(&[0u8; 64]).unwrap(), [0u8; 64])
|
let priv_key = EpfPrivateKey::generate(&mut OsRng);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
priv_key.to_keypair_bytes(),
|
||||||
|
signing_key(&priv_key.to_keypair_bytes()).to_keypair_bytes()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn privkey_serialization_pem() {
|
pub fn privkey_serialization_pem() {
|
||||||
assert_eq!(([0u8; 64]).as_pem().unwrap(), null_private_key_pem())
|
let priv_key_data = hex!("00000000000000000000000000000000000000000000000000000000000000003B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
(signing_key(&priv_key_data)).as_pem().unwrap(),
|
||||||
|
null_private_key_pem()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn privkey_deserialization_pem() {
|
pub fn privkey_deserialization_pem() {
|
||||||
|
let priv_key_data = hex!("00000000000000000000000000000000000000000000000000000000000000003B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
EpfPrivateKey::from_pem(&null_private_key_pem()).unwrap(),
|
EpfPrivateKey::from_pem(&null_private_key_pem())
|
||||||
[0u8; 64]
|
.unwrap()
|
||||||
|
.to_keypair_bytes(),
|
||||||
|
signing_key(&priv_key_data).to_keypair_bytes()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,8 +492,10 @@ mod tests {
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
pub fn privkey_deserialization_pem_wrong_tag() {
|
pub fn privkey_deserialization_pem_wrong_tag() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
EpfPrivateKey::from_pem(&null_public_key_pem()).unwrap(),
|
EpfPrivateKey::from_pem(&null_public_key_pem())
|
||||||
[0u8; 64]
|
.unwrap()
|
||||||
|
.to_keypair_bytes(),
|
||||||
|
signing_key(&[0u8; 64]).to_keypair_bytes()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -513,7 +549,7 @@ mod tests {
|
||||||
|
|
||||||
assert!(ca_cert.verify(&ca_pool).is_err());
|
assert!(ca_cert.verify(&ca_pool).is_err());
|
||||||
|
|
||||||
ca_cert.sign(&private_key.to_keypair_bytes()).unwrap();
|
ca_cert.sign(&private_key).unwrap();
|
||||||
|
|
||||||
assert!(!ca_cert.verify(&ca_pool).unwrap());
|
assert!(!ca_cert.verify(&ca_pool).unwrap());
|
||||||
|
|
||||||
|
@ -545,7 +581,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(not_ca_cert.verify(&ca_pool).is_err());
|
assert!(not_ca_cert.verify(&ca_pool).is_err());
|
||||||
not_ca_cert.sign(&private_key.to_keypair_bytes()).unwrap();
|
not_ca_cert.sign(&private_key).unwrap();
|
||||||
assert!(not_ca_cert.verify(&ca_pool).unwrap());
|
assert!(not_ca_cert.verify(&ca_pool).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -579,6 +615,84 @@ mod tests {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn certificate_verification_fingerprint_does_not_match() {
|
||||||
|
let private_key = SigningKey::generate(&mut OsRng);
|
||||||
|
let public_key = private_key.verifying_key();
|
||||||
|
|
||||||
|
let mut fingerprint_does_not_match_cert = EPFCertificate {
|
||||||
|
details: EPFCertificateDetails {
|
||||||
|
name: "Testing Certificate - Fingerprint Non Matching".to_string(),
|
||||||
|
not_before: SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs()
|
||||||
|
- 20,
|
||||||
|
not_after: SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs()
|
||||||
|
+ 30,
|
||||||
|
public_key: [0u8; 32],
|
||||||
|
issuer_public_key: *public_key.as_bytes(),
|
||||||
|
claims: Default::default(),
|
||||||
|
},
|
||||||
|
fingerprint: "".to_string(),
|
||||||
|
signature: [0u8; EPFPKI_SIGNATURE_LENGTH],
|
||||||
|
};
|
||||||
|
fingerprint_does_not_match_cert.sign(&private_key).unwrap();
|
||||||
|
|
||||||
|
fingerprint_does_not_match_cert.fingerprint = "0".to_string();
|
||||||
|
|
||||||
|
let ca_pool = EpfCaPool::new();
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
fingerprint_does_not_match_cert
|
||||||
|
.verify(&ca_pool)
|
||||||
|
.unwrap_err(),
|
||||||
|
EpfPkiCertificateValidationError::FingerprintDoesNotMatch { .. }
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn certificate_verification_invalid_signature() {
|
||||||
|
let private_key = SigningKey::generate(&mut OsRng);
|
||||||
|
let public_key = private_key.verifying_key();
|
||||||
|
|
||||||
|
let mut fingerprint_does_not_match_cert = EPFCertificate {
|
||||||
|
details: EPFCertificateDetails {
|
||||||
|
name: "Testing Certificate - Fingerprint Non Matching".to_string(),
|
||||||
|
not_before: SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs()
|
||||||
|
- 20,
|
||||||
|
not_after: SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs()
|
||||||
|
+ 30,
|
||||||
|
public_key: [0u8; 32],
|
||||||
|
issuer_public_key: *public_key.as_bytes(),
|
||||||
|
claims: Default::default(),
|
||||||
|
},
|
||||||
|
fingerprint: "".to_string(),
|
||||||
|
signature: [0u8; EPFPKI_SIGNATURE_LENGTH],
|
||||||
|
};
|
||||||
|
fingerprint_does_not_match_cert.sign(&private_key).unwrap();
|
||||||
|
|
||||||
|
fingerprint_does_not_match_cert.signature = [0u8; 64];
|
||||||
|
|
||||||
|
let ca_pool = EpfCaPool::new();
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
fingerprint_does_not_match_cert
|
||||||
|
.verify(&ca_pool)
|
||||||
|
.unwrap_err(),
|
||||||
|
EpfPkiCertificateValidationError::InvalidSignature { .. }
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn certificate_verification_not_valid_yet() {
|
pub fn certificate_verification_not_valid_yet() {
|
||||||
let not_yet_valid_cert = EPFCertificate {
|
let not_yet_valid_cert = EPFCertificate {
|
||||||
|
@ -635,9 +749,7 @@ mod tests {
|
||||||
fingerprint: "".to_string(),
|
fingerprint: "".to_string(),
|
||||||
signature: [0u8; EPFPKI_SIGNATURE_LENGTH],
|
signature: [0u8; EPFPKI_SIGNATURE_LENGTH],
|
||||||
};
|
};
|
||||||
not_trusted_cert
|
not_trusted_cert.sign(&private_key).unwrap();
|
||||||
.sign(&private_key.to_keypair_bytes())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let ca_pool = EpfCaPool::new();
|
let ca_pool = EpfCaPool::new();
|
||||||
|
|
||||||
|
@ -675,7 +787,7 @@ mod tests {
|
||||||
signature: [0u8; EPFPKI_SIGNATURE_LENGTH],
|
signature: [0u8; EPFPKI_SIGNATURE_LENGTH],
|
||||||
};
|
};
|
||||||
|
|
||||||
ca_cert.sign(&private_key.to_keypair_bytes()).unwrap();
|
ca_cert.sign(&private_key).unwrap();
|
||||||
ca_pool.insert(&ca_cert);
|
ca_pool.insert(&ca_cert);
|
||||||
|
|
||||||
let mut not_ca_cert = EPFCertificate {
|
let mut not_ca_cert = EPFCertificate {
|
||||||
|
@ -700,7 +812,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(not_ca_cert.verify(&ca_pool).is_err());
|
assert!(not_ca_cert.verify(&ca_pool).is_err());
|
||||||
not_ca_cert.sign(&private_key.to_keypair_bytes()).unwrap();
|
not_ca_cert.sign(&private_key).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
not_ca_cert.verify(&ca_pool).unwrap_err(),
|
not_ca_cert.verify(&ca_pool).unwrap_err(),
|
||||||
EpfPkiCertificateValidationError::ExpiresAfterSigner
|
EpfPkiCertificateValidationError::ExpiresAfterSigner
|
||||||
|
@ -738,7 +850,7 @@ mod tests {
|
||||||
signature: [0u8; EPFPKI_SIGNATURE_LENGTH],
|
signature: [0u8; EPFPKI_SIGNATURE_LENGTH],
|
||||||
};
|
};
|
||||||
|
|
||||||
ca_cert.sign(&private_key.to_keypair_bytes()).unwrap();
|
ca_cert.sign(&private_key).unwrap();
|
||||||
ca_pool.insert(&ca_cert);
|
ca_pool.insert(&ca_cert);
|
||||||
|
|
||||||
let mut not_ca_cert = EPFCertificate {
|
let mut not_ca_cert = EPFCertificate {
|
||||||
|
@ -763,7 +875,7 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(not_ca_cert.verify(&ca_pool).is_err());
|
assert!(not_ca_cert.verify(&ca_pool).is_err());
|
||||||
not_ca_cert.sign(&private_key.to_keypair_bytes()).unwrap();
|
not_ca_cert.sign(&private_key).unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
not_ca_cert.verify(&ca_pool).unwrap_err(),
|
not_ca_cert.verify(&ca_pool).unwrap_err(),
|
||||||
EpfPkiCertificateValidationError::ValidAfterSigner
|
EpfPkiCertificateValidationError::ValidAfterSigner
|
||||||
|
@ -873,11 +985,11 @@ mod tests {
|
||||||
45, 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, 69, 80, 70, 32, 80, 82, 73, 86, 65, 84, 69,
|
45, 45, 45, 45, 45, 66, 69, 71, 73, 78, 32, 69, 80, 70, 32, 80, 82, 73, 86, 65, 84, 69,
|
||||||
32, 75, 69, 89, 45, 45, 45, 45, 45, 13, 10, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
|
32, 75, 69, 89, 45, 45, 45, 45, 45, 13, 10, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
|
||||||
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
|
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
|
||||||
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
|
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 55, 97, 105, 101, 56, 122, 114, 97, 107, 76,
|
||||||
65, 65, 65, 65, 65, 65, 65, 65, 65, 13, 10, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65,
|
87, 75, 106, 113, 78, 65, 113, 98, 119, 49, 122, 13, 10, 90, 84, 73, 86, 100, 120, 51,
|
||||||
65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 65, 61, 61, 13, 10, 45, 45, 45, 45, 45, 69, 78,
|
105, 81, 54, 89, 54, 119, 69, 105, 104, 105, 49, 110, 97, 75, 81, 61, 61, 13, 10, 45,
|
||||||
68, 32, 69, 80, 70, 32, 80, 82, 73, 86, 65, 84, 69, 32, 75, 69, 89, 45, 45, 45, 45, 45,
|
45, 45, 45, 45, 69, 78, 68, 32, 69, 80, 70, 32, 80, 82, 73, 86, 65, 84, 69, 32, 75, 69,
|
||||||
13, 10,
|
89, 45, 45, 45, 45, 45, 13, 10,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,76 +1,95 @@
|
||||||
|
use crate::pki::{EPFCertificate, EPFPKI_PUBLIC_KEY_LENGTH};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use crate::pki::EPFCertificate;
|
use std::error::Error;
|
||||||
|
use tokio::io::AsyncReadExt;
|
||||||
|
|
||||||
pub const PROTOCOL_VERSION: u32 = 1;
|
pub const PROTOCOL_VERSION: u32 = 1;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct EpfMessage {
|
pub struct EpfMessage {
|
||||||
pub packet_id: u32,
|
pub packet_id: u32,
|
||||||
pub packet_data: Vec<u8>
|
pub packet_data: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const CLIENT_HELLO: u32 = 1;
|
pub const PACKET_CLIENT_HELLO: u32 = 1;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct EpfClientHello {
|
pub struct EpfClientHello {
|
||||||
pub protocol_version: u32,
|
pub protocol_version: u32,
|
||||||
pub client_random: [u8; 16]
|
pub client_random: [u8; 24],
|
||||||
|
pub client_certificate: Option<EPFCertificate>,
|
||||||
|
pub client_x25519_public_key: [u8; EPFPKI_PUBLIC_KEY_LENGTH],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const SERVER_HELLO: u32 = 2;
|
pub const PACKET_SERVER_HELLO: u32 = 2;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct EpfServerHello {
|
pub struct EpfServerHello {
|
||||||
pub protocol_version: u32,
|
pub protocol_version: u32,
|
||||||
pub server_certificate: EPFCertificate,
|
pub server_certificate: EPFCertificate,
|
||||||
pub server_random: [u8; 16]
|
pub server_random: [u8; 16],
|
||||||
|
pub server_x25519_public_key: [u8; EPFPKI_PUBLIC_KEY_LENGTH],
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const CLIENT_KEY_EXCHANGE: u32 = 3;
|
pub const PACKET_FINISHED: u32 = 3;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct EpfClientKeyExchange {
|
|
||||||
pub protocol_version: u32,
|
|
||||||
pub encrypted_shared_secret: Vec<u8>
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const FINISHED: u32 = 4;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct EpfFinished {
|
pub struct EpfFinished {
|
||||||
pub protocol_version: u32,
|
pub protocol_version: u32,
|
||||||
pub encrypted_0x42: Vec<u8>
|
pub encrypted_0x42: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const APPLICATION_DATA: u32 = 5;
|
pub const PACKET_APPLICATION_DATA: u32 = 4;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct EpfApplicationData {
|
pub struct EpfApplicationData {
|
||||||
pub protocol_version: u32,
|
pub protocol_version: u32,
|
||||||
pub application_data: Vec<u8>
|
pub encrypted_application_data: Vec<u8>,
|
||||||
|
pub nonce: [u8; 24],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum EpfClientState {
|
pub enum EpfClientState {
|
||||||
NotStarted,
|
NotStarted,
|
||||||
WaitingForServerHello,
|
WaitingForServerHello,
|
||||||
WaitingForFinished,
|
WaitingForFinished,
|
||||||
Transport,
|
Transport,
|
||||||
Closed
|
Closed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum EpfServerState {
|
pub enum EpfServerState {
|
||||||
WaitingForClientHello,
|
WaitingForClientHello,
|
||||||
WaitingForClientKeyExchange,
|
|
||||||
WaitingForFinished,
|
WaitingForFinished,
|
||||||
Transport,
|
Transport,
|
||||||
Closed
|
Closed,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode_packet<T: Serialize>(id: u32, packet: &T) -> Result<Vec<u8>, rmp_serde::encode::Error> {
|
pub fn encode_packet<T: Serialize>(
|
||||||
|
id: u32,
|
||||||
|
packet: &T,
|
||||||
|
) -> Result<Vec<u8>, rmp_serde::encode::Error> {
|
||||||
let message_data = rmp_serde::to_vec(packet)?;
|
let message_data = rmp_serde::to_vec(packet)?;
|
||||||
let message_wrapper = EpfMessage {
|
let message_wrapper = EpfMessage {
|
||||||
packet_id: id,
|
packet_id: id,
|
||||||
packet_data: message_data,
|
packet_data: message_data,
|
||||||
};
|
};
|
||||||
rmp_serde::to_vec(&message_wrapper)
|
let mut packet_data = rmp_serde::to_vec(&message_wrapper)?;
|
||||||
|
|
||||||
|
let mut packet = (packet_data.len() as u64).to_le_bytes().to_vec();
|
||||||
|
// Packet: 8-byte little-endian length prefix, packet data
|
||||||
|
packet.append(&mut packet_data);
|
||||||
|
Ok(packet)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn recv_packet<C: AsyncReadExt + Unpin>(
|
||||||
|
stream: &mut C,
|
||||||
|
) -> Result<EpfMessage, Box<dyn Error>> {
|
||||||
|
let packet_length = stream.read_u64_le().await?;
|
||||||
|
|
||||||
|
let mut packet_data_buf = vec![0u8; packet_length as usize];
|
||||||
|
stream.read_exact(&mut packet_data_buf).await?;
|
||||||
|
let message: EpfMessage = rmp_serde::from_slice(&packet_data_buf)?;
|
||||||
|
Ok(message)
|
||||||
}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
use ed25519_dalek::{SigningKey, VerifyingKey};
|
||||||
use std::ops::Add;
|
use std::ops::Add;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
|
@ -10,3 +11,10 @@ pub fn pretty_print_date(date: &SystemTime) -> String {
|
||||||
pub fn u64_to_st(unix: u64) -> SystemTime {
|
pub fn u64_to_st(unix: u64) -> SystemTime {
|
||||||
SystemTime::UNIX_EPOCH.add(Duration::from_secs(unix))
|
SystemTime::UNIX_EPOCH.add(Duration::from_secs(unix))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn verifying_key(key: &[u8; 32]) -> VerifyingKey {
|
||||||
|
VerifyingKey::from_bytes(key).unwrap()
|
||||||
|
}
|
||||||
|
pub fn signing_key(key: &[u8; 64]) -> SigningKey {
|
||||||
|
SigningKey::from_keypair_bytes(key).unwrap()
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "ncpf"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libepf = { version = "0.1.0", path = "../libepf" }
|
||||||
|
clap = { version = "4.2", features = ["derive", "cargo"] }
|
||||||
|
tokio = { version = "1.28.0", features = ["rt-multi-thread", "macros", "net", "io-std"] }
|
|
@ -0,0 +1,90 @@
|
||||||
|
use clap::Parser;
|
||||||
|
use libepf::ca_pool::load_ca_pool;
|
||||||
|
use libepf::handshake_stream::{
|
||||||
|
ClientAuthentication, EpfClientHandshaker, EpfClientUpgradable, EpfStreamOps,
|
||||||
|
};
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::net::{IpAddr, SocketAddr};
|
||||||
|
use tokio::io::AsyncReadExt;
|
||||||
|
use tokio::net::TcpSocket;
|
||||||
|
use tokio::select;
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(author, version, about, long_about = None)]
|
||||||
|
pub struct Cli {
|
||||||
|
connect_ip: IpAddr,
|
||||||
|
connect_port: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
let bind_addr = SocketAddr::new(cli.connect_ip, cli.connect_port);
|
||||||
|
|
||||||
|
let tcp_stream = TcpSocket::new_v4()?.connect(bind_addr).await?;
|
||||||
|
|
||||||
|
let ca_pool = load_ca_pool()?;
|
||||||
|
|
||||||
|
let mut handshake_client = tcp_stream.upgrade(ClientAuthentication::Ephemeral).await;
|
||||||
|
handshake_client.handshake(ca_pool).await?;
|
||||||
|
let mut client = handshake_client.upgrade().await;
|
||||||
|
|
||||||
|
let mut stdin = tokio::io::BufReader::new(tokio::io::stdin());
|
||||||
|
let mut input = [0u8; 32767];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
select! {
|
||||||
|
packet_data = client.read() => {
|
||||||
|
match packet_data {
|
||||||
|
Ok(d) => std::io::stdout().write_all(&d).unwrap(),
|
||||||
|
Err(e) => {
|
||||||
|
match e.downcast_ref::<io::Error>() {
|
||||||
|
Some(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
|
||||||
|
std::process::exit(0);
|
||||||
|
},
|
||||||
|
Some(_) | None => {
|
||||||
|
println!("{}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
stdin_data = stdin.read(&mut input) => {
|
||||||
|
match stdin_data {
|
||||||
|
Ok(amt) => {
|
||||||
|
match client.write(&input[..amt]).await {
|
||||||
|
Ok(_) => {
|
||||||
|
input = [0u8; 32767];
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
match e.downcast_ref::<io::Error>() {
|
||||||
|
Some(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
|
||||||
|
std::process::exit(0);
|
||||||
|
},
|
||||||
|
Some(_) | None => {
|
||||||
|
println!("{}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if amt == 0 {
|
||||||
|
// EOF
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
println!("{}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "netcatpf"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libepf = { version = "0.1.0", path = "../libepf" }
|
||||||
|
clap = { version = "4.2", features = ["derive", "cargo"] }
|
||||||
|
tokio = { version = "1.28.0", features = ["rt-multi-thread", "macros", "net", "io-std"] }
|
|
@ -0,0 +1,102 @@
|
||||||
|
use clap::Parser;
|
||||||
|
use libepf::ca_pool::load_ca_pool;
|
||||||
|
use libepf::handshake_stream::{EpfServerHandshaker, EpfServerUpgradable, EpfStreamOps};
|
||||||
|
use libepf::pki::{EPFCertificate, EpfPkiSerializable, EpfPrivateKey};
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::net::{IpAddr, SocketAddr};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::{fs, io};
|
||||||
|
use tokio::io::AsyncReadExt;
|
||||||
|
use tokio::net::TcpListener;
|
||||||
|
use tokio::select;
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(author, version, about, long_about = None)]
|
||||||
|
pub struct Cli {
|
||||||
|
bind_ip: IpAddr,
|
||||||
|
bind_port: u16,
|
||||||
|
certificate: PathBuf,
|
||||||
|
key: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let cli = Cli::parse();
|
||||||
|
|
||||||
|
// load cert
|
||||||
|
let cert_data = fs::read(cli.certificate)?;
|
||||||
|
let cert = EPFCertificate::from_pem(&cert_data)?;
|
||||||
|
|
||||||
|
let private_key_data = fs::read(cli.key)?;
|
||||||
|
let private_key = EpfPrivateKey::from_pem(&private_key_data)?;
|
||||||
|
|
||||||
|
let bind_addr = SocketAddr::new(cli.bind_ip, cli.bind_port);
|
||||||
|
|
||||||
|
let listener = TcpListener::bind(bind_addr).await?;
|
||||||
|
|
||||||
|
// get client
|
||||||
|
let (tcp_stream, _) = listener.accept().await?;
|
||||||
|
|
||||||
|
let ca_pool = load_ca_pool()?;
|
||||||
|
|
||||||
|
let mut handshake_server = tcp_stream.upgrade(cert, private_key).await;
|
||||||
|
handshake_server.handshake(ca_pool).await?;
|
||||||
|
let mut client = handshake_server.upgrade().await;
|
||||||
|
|
||||||
|
let mut stdin = tokio::io::BufReader::new(tokio::io::stdin());
|
||||||
|
let mut input = [0u8; 32767];
|
||||||
|
|
||||||
|
loop {
|
||||||
|
select! {
|
||||||
|
packet_data = client.read() => {
|
||||||
|
match packet_data {
|
||||||
|
Ok(d) => std::io::stdout().write_all(&d).unwrap(),
|
||||||
|
Err(e) => {
|
||||||
|
match e.downcast_ref::<io::Error>() {
|
||||||
|
Some(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
|
||||||
|
std::process::exit(0);
|
||||||
|
},
|
||||||
|
Some(_) | None => {
|
||||||
|
println!("{}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
stdin_data = stdin.read(&mut input) => {
|
||||||
|
match stdin_data {
|
||||||
|
Ok(amt) => {
|
||||||
|
match client.write(&input[..amt]).await {
|
||||||
|
Ok(_) => {
|
||||||
|
input = [0u8; 32767];
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
match e.downcast_ref::<io::Error>() {
|
||||||
|
Some(e) if e.kind() == io::ErrorKind::UnexpectedEof => {
|
||||||
|
std::process::exit(0);
|
||||||
|
},
|
||||||
|
Some(_) | None => {
|
||||||
|
println!("{}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if amt == 0 {
|
||||||
|
// EOF
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
println!("{}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,901 @@
|
||||||
|
--
|
||||||
|
-- lua-MessagePack : <https://fperrad.frama.io/lua-MessagePack/>
|
||||||
|
--
|
||||||
|
|
||||||
|
local r, jit = pcall(require, 'jit')
|
||||||
|
if not r then
|
||||||
|
jit = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local SIZEOF_NUMBER = string.pack and #string.pack('n', 0.0) or 8
|
||||||
|
local maxinteger
|
||||||
|
local mininteger
|
||||||
|
if not jit and _VERSION < 'Lua 5.3' then
|
||||||
|
-- Lua 5.1 & 5.2
|
||||||
|
local loadstring = loadstring or load
|
||||||
|
local luac = string.dump(loadstring "a = 1")
|
||||||
|
local header = { luac:byte(1, 12) }
|
||||||
|
SIZEOF_NUMBER = header[11]
|
||||||
|
end
|
||||||
|
|
||||||
|
local assert = assert
|
||||||
|
local error = error
|
||||||
|
local pairs = pairs
|
||||||
|
local pcall = pcall
|
||||||
|
local setmetatable = setmetatable
|
||||||
|
local tostring = tostring
|
||||||
|
local type = type
|
||||||
|
local char = require'string'.char
|
||||||
|
local format = require'string'.format
|
||||||
|
local floor = require'math'.floor
|
||||||
|
local tointeger = require'math'.tointeger or floor
|
||||||
|
local frexp = require'math'.frexp or require'mathx'.frexp
|
||||||
|
local ldexp = require'math'.ldexp or require'mathx'.ldexp
|
||||||
|
local huge = require'math'.huge
|
||||||
|
local tconcat = require'table'.concat
|
||||||
|
|
||||||
|
local _ENV = nil
|
||||||
|
local m = {}
|
||||||
|
|
||||||
|
--[[ debug only
|
||||||
|
local function hexadump (s)
|
||||||
|
return (s:gsub('.', function (c) return format('%02X ', c:byte()) end))
|
||||||
|
end
|
||||||
|
m.hexadump = hexadump
|
||||||
|
--]]
|
||||||
|
|
||||||
|
local function argerror (caller, narg, extramsg)
|
||||||
|
error("bad argument #" .. tostring(narg) .. " to "
|
||||||
|
.. caller .. " (" .. extramsg .. ")")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function typeerror (caller, narg, arg, tname)
|
||||||
|
argerror(caller, narg, tname .. " expected, got " .. type(arg))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function checktype (caller, narg, arg, tname)
|
||||||
|
if type(arg) ~= tname then
|
||||||
|
typeerror(caller, narg, arg, tname)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local packers = setmetatable({}, {
|
||||||
|
__index = function (t, k)
|
||||||
|
if k == 1 then return end -- allows ipairs
|
||||||
|
error("pack '" .. k .. "' is unimplemented")
|
||||||
|
end
|
||||||
|
})
|
||||||
|
m.packers = packers
|
||||||
|
|
||||||
|
packers['nil'] = function (buffer)
|
||||||
|
buffer[#buffer+1] = char(0xC0) -- nil
|
||||||
|
end
|
||||||
|
|
||||||
|
packers['boolean'] = function (buffer, bool)
|
||||||
|
if bool then
|
||||||
|
buffer[#buffer+1] = char(0xC3) -- true
|
||||||
|
else
|
||||||
|
buffer[#buffer+1] = char(0xC2) -- false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
packers['string_compat'] = function (buffer, str)
|
||||||
|
local n = #str
|
||||||
|
if n <= 0x1F then
|
||||||
|
buffer[#buffer+1] = char(0xA0 + n) -- fixstr
|
||||||
|
elseif n <= 0xFFFF then
|
||||||
|
buffer[#buffer+1] = char(0xDA, -- str16
|
||||||
|
floor(n / 0x100),
|
||||||
|
n % 0x100)
|
||||||
|
elseif n <= 4294967295.0 then
|
||||||
|
buffer[#buffer+1] = char(0xDB, -- str32
|
||||||
|
floor(n / 0x1000000),
|
||||||
|
floor(n / 0x10000) % 0x100,
|
||||||
|
floor(n / 0x100) % 0x100,
|
||||||
|
n % 0x100)
|
||||||
|
else
|
||||||
|
error"overflow in pack 'string_compat'"
|
||||||
|
end
|
||||||
|
buffer[#buffer+1] = str
|
||||||
|
end
|
||||||
|
|
||||||
|
packers['_string'] = function (buffer, str)
|
||||||
|
local n = #str
|
||||||
|
if n <= 0x1F then
|
||||||
|
buffer[#buffer+1] = char(0xA0 + n) -- fixstr
|
||||||
|
elseif n <= 0xFF then
|
||||||
|
buffer[#buffer+1] = char(0xD9, -- str8
|
||||||
|
n)
|
||||||
|
elseif n <= 0xFFFF then
|
||||||
|
buffer[#buffer+1] = char(0xDA, -- str16
|
||||||
|
floor(n / 0x100),
|
||||||
|
n % 0x100)
|
||||||
|
elseif n <= 4294967295.0 then
|
||||||
|
buffer[#buffer+1] = char(0xDB, -- str32
|
||||||
|
floor(n / 0x1000000),
|
||||||
|
floor(n / 0x10000) % 0x100,
|
||||||
|
floor(n / 0x100) % 0x100,
|
||||||
|
n % 0x100)
|
||||||
|
else
|
||||||
|
error"overflow in pack 'string'"
|
||||||
|
end
|
||||||
|
buffer[#buffer+1] = str
|
||||||
|
end
|
||||||
|
|
||||||
|
packers['binary'] = function (buffer, str)
|
||||||
|
local n = #str
|
||||||
|
if n <= 0xFF then
|
||||||
|
buffer[#buffer+1] = char(0xC4, -- bin8
|
||||||
|
n)
|
||||||
|
elseif n <= 0xFFFF then
|
||||||
|
buffer[#buffer+1] = char(0xC5, -- bin16
|
||||||
|
floor(n / 0x100),
|
||||||
|
n % 0x100)
|
||||||
|
elseif n <= 4294967295.0 then
|
||||||
|
buffer[#buffer+1] = char(0xC6, -- bin32
|
||||||
|
floor(n / 0x1000000),
|
||||||
|
floor(n / 0x10000) % 0x100,
|
||||||
|
floor(n / 0x100) % 0x100,
|
||||||
|
n % 0x100)
|
||||||
|
else
|
||||||
|
error"overflow in pack 'binary'"
|
||||||
|
end
|
||||||
|
buffer[#buffer+1] = str
|
||||||
|
end
|
||||||
|
|
||||||
|
local set_string = function (str)
|
||||||
|
if str == 'string_compat' then
|
||||||
|
packers['string'] = packers['string_compat']
|
||||||
|
elseif str == 'string' then
|
||||||
|
packers['string'] = packers['_string']
|
||||||
|
elseif str == 'binary' then
|
||||||
|
packers['string'] = packers['binary']
|
||||||
|
else
|
||||||
|
argerror('set_string', 1, "invalid option '" .. str .."'")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
m.set_string = set_string
|
||||||
|
|
||||||
|
packers['map'] = function (buffer, tbl, n)
|
||||||
|
if n <= 0x0F then
|
||||||
|
buffer[#buffer+1] = char(0x80 + n) -- fixmap
|
||||||
|
elseif n <= 0xFFFF then
|
||||||
|
buffer[#buffer+1] = char(0xDE, -- map16
|
||||||
|
floor(n / 0x100),
|
||||||
|
n % 0x100)
|
||||||
|
elseif n <= 4294967295.0 then
|
||||||
|
buffer[#buffer+1] = char(0xDF, -- map32
|
||||||
|
floor(n / 0x1000000),
|
||||||
|
floor(n / 0x10000) % 0x100,
|
||||||
|
floor(n / 0x100) % 0x100,
|
||||||
|
n % 0x100)
|
||||||
|
else
|
||||||
|
error"overflow in pack 'map'"
|
||||||
|
end
|
||||||
|
for k, v in pairs(tbl) do
|
||||||
|
packers[type(k)](buffer, k)
|
||||||
|
packers[type(v)](buffer, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
packers['array'] = function (buffer, tbl, n)
|
||||||
|
if n <= 0x0F then
|
||||||
|
buffer[#buffer+1] = char(0x90 + n) -- fixarray
|
||||||
|
elseif n <= 0xFFFF then
|
||||||
|
buffer[#buffer+1] = char(0xDC, -- array16
|
||||||
|
floor(n / 0x100),
|
||||||
|
n % 0x100)
|
||||||
|
elseif n <= 4294967295.0 then
|
||||||
|
buffer[#buffer+1] = char(0xDD, -- array32
|
||||||
|
floor(n / 0x1000000),
|
||||||
|
floor(n / 0x10000) % 0x100,
|
||||||
|
floor(n / 0x100) % 0x100,
|
||||||
|
n % 0x100)
|
||||||
|
else
|
||||||
|
error"overflow in pack 'array'"
|
||||||
|
end
|
||||||
|
for i = 1, n do
|
||||||
|
local v = tbl[i]
|
||||||
|
packers[type(v)](buffer, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local set_array = function (array)
|
||||||
|
if array == 'without_hole' then
|
||||||
|
packers['_table'] = function (buffer, tbl)
|
||||||
|
local is_map, n, max = false, 0, 0
|
||||||
|
for k in pairs(tbl) do
|
||||||
|
if type(k) == 'number' and k > 0 then
|
||||||
|
if k > max then
|
||||||
|
max = k
|
||||||
|
end
|
||||||
|
else
|
||||||
|
is_map = true
|
||||||
|
end
|
||||||
|
n = n + 1
|
||||||
|
end
|
||||||
|
if max ~= n then -- there are holes
|
||||||
|
is_map = true
|
||||||
|
end
|
||||||
|
if is_map then
|
||||||
|
packers['map'](buffer, tbl, n)
|
||||||
|
else
|
||||||
|
packers['array'](buffer, tbl, n)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif array == 'with_hole' then
|
||||||
|
packers['_table'] = function (buffer, tbl)
|
||||||
|
local is_map, n, max = false, 0, 0
|
||||||
|
for k in pairs(tbl) do
|
||||||
|
if type(k) == 'number' and k > 0 then
|
||||||
|
if k > max then
|
||||||
|
max = k
|
||||||
|
end
|
||||||
|
else
|
||||||
|
is_map = true
|
||||||
|
end
|
||||||
|
n = n + 1
|
||||||
|
end
|
||||||
|
if is_map then
|
||||||
|
packers['map'](buffer, tbl, n)
|
||||||
|
else
|
||||||
|
packers['array'](buffer, tbl, max)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif array == 'always_as_map' then
|
||||||
|
packers['_table'] = function(buffer, tbl)
|
||||||
|
local n = 0
|
||||||
|
for k in pairs(tbl) do
|
||||||
|
n = n + 1
|
||||||
|
end
|
||||||
|
packers['map'](buffer, tbl, n)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
argerror('set_array', 1, "invalid option '" .. array .."'")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
m.set_array = set_array
|
||||||
|
|
||||||
|
packers['table'] = function (buffer, tbl)
|
||||||
|
packers['_table'](buffer, tbl)
|
||||||
|
end
|
||||||
|
|
||||||
|
packers['unsigned'] = function (buffer, n)
|
||||||
|
if n >= 0 then
|
||||||
|
if n <= 0x7F then
|
||||||
|
buffer[#buffer+1] = char(n) -- fixnum_pos
|
||||||
|
elseif n <= 0xFF then
|
||||||
|
buffer[#buffer+1] = char(0xCC, -- uint8
|
||||||
|
n)
|
||||||
|
elseif n <= 0xFFFF then
|
||||||
|
buffer[#buffer+1] = char(0xCD, -- uint16
|
||||||
|
floor(n / 0x100),
|
||||||
|
n % 0x100)
|
||||||
|
elseif n <= 4294967295.0 then
|
||||||
|
buffer[#buffer+1] = char(0xCE, -- uint32
|
||||||
|
floor(n / 0x1000000),
|
||||||
|
floor(n / 0x10000) % 0x100,
|
||||||
|
floor(n / 0x100) % 0x100,
|
||||||
|
n % 0x100)
|
||||||
|
else
|
||||||
|
buffer[#buffer+1] = char(0xCF, -- uint64
|
||||||
|
0, -- only 53 bits from double
|
||||||
|
floor(n / 0x1000000000000) % 0x100,
|
||||||
|
floor(n / 0x10000000000) % 0x100,
|
||||||
|
floor(n / 0x100000000) % 0x100,
|
||||||
|
floor(n / 0x1000000) % 0x100,
|
||||||
|
floor(n / 0x10000) % 0x100,
|
||||||
|
floor(n / 0x100) % 0x100,
|
||||||
|
n % 0x100)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if n >= -0x20 then
|
||||||
|
buffer[#buffer+1] = char(0x100 + n) -- fixnum_neg
|
||||||
|
elseif n >= -0x80 then
|
||||||
|
buffer[#buffer+1] = char(0xD0, -- int8
|
||||||
|
0x100 + n)
|
||||||
|
elseif n >= -0x8000 then
|
||||||
|
n = 0x10000 + n
|
||||||
|
buffer[#buffer+1] = char(0xD1, -- int16
|
||||||
|
floor(n / 0x100),
|
||||||
|
n % 0x100)
|
||||||
|
elseif n >= -0x80000000 then
|
||||||
|
n = 4294967296.0 + n
|
||||||
|
buffer[#buffer+1] = char(0xD2, -- int32
|
||||||
|
floor(n / 0x1000000),
|
||||||
|
floor(n / 0x10000) % 0x100,
|
||||||
|
floor(n / 0x100) % 0x100,
|
||||||
|
n % 0x100)
|
||||||
|
else
|
||||||
|
buffer[#buffer+1] = char(0xD3, -- int64
|
||||||
|
0xFF, -- only 53 bits from double
|
||||||
|
floor(n / 0x1000000000000) % 0x100,
|
||||||
|
floor(n / 0x10000000000) % 0x100,
|
||||||
|
floor(n / 0x100000000) % 0x100,
|
||||||
|
floor(n / 0x1000000) % 0x100,
|
||||||
|
floor(n / 0x10000) % 0x100,
|
||||||
|
floor(n / 0x100) % 0x100,
|
||||||
|
n % 0x100)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
packers['signed'] = function (buffer, n)
|
||||||
|
if n >= 0 then
|
||||||
|
if n <= 0x7F then
|
||||||
|
buffer[#buffer+1] = char(n) -- fixnum_pos
|
||||||
|
elseif n <= 0x7FFF then
|
||||||
|
buffer[#buffer+1] = char(0xD1, -- int16
|
||||||
|
floor(n / 0x100),
|
||||||
|
n % 0x100)
|
||||||
|
elseif n <= 0x7FFFFFFF then
|
||||||
|
buffer[#buffer+1] = char(0xD2, -- int32
|
||||||
|
floor(n / 0x1000000),
|
||||||
|
floor(n / 0x10000) % 0x100,
|
||||||
|
floor(n / 0x100) % 0x100,
|
||||||
|
n % 0x100)
|
||||||
|
else
|
||||||
|
buffer[#buffer+1] = char(0xD3, -- int64
|
||||||
|
0, -- only 53 bits from double
|
||||||
|
floor(n / 0x1000000000000) % 0x100,
|
||||||
|
floor(n / 0x10000000000) % 0x100,
|
||||||
|
floor(n / 0x100000000) % 0x100,
|
||||||
|
floor(n / 0x1000000) % 0x100,
|
||||||
|
floor(n / 0x10000) % 0x100,
|
||||||
|
floor(n / 0x100) % 0x100,
|
||||||
|
n % 0x100)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if n >= -0x20 then
|
||||||
|
buffer[#buffer+1] = char(0xE0 + 0x20 + n) -- fixnum_neg
|
||||||
|
elseif n >= -0x80 then
|
||||||
|
buffer[#buffer+1] = char(0xD0, -- int8
|
||||||
|
0x100 + n)
|
||||||
|
elseif n >= -0x8000 then
|
||||||
|
n = 0x10000 + n
|
||||||
|
buffer[#buffer+1] = char(0xD1, -- int16
|
||||||
|
floor(n / 0x100),
|
||||||
|
n % 0x100)
|
||||||
|
elseif n >= -0x80000000 then
|
||||||
|
n = 4294967296.0 + n
|
||||||
|
buffer[#buffer+1] = char(0xD2, -- int32
|
||||||
|
floor(n / 0x1000000),
|
||||||
|
floor(n / 0x10000) % 0x100,
|
||||||
|
floor(n / 0x100) % 0x100,
|
||||||
|
n % 0x100)
|
||||||
|
else
|
||||||
|
buffer[#buffer+1] = char(0xD3, -- int64
|
||||||
|
0xFF, -- only 53 bits from double
|
||||||
|
floor(n / 0x1000000000000) % 0x100,
|
||||||
|
floor(n / 0x10000000000) % 0x100,
|
||||||
|
floor(n / 0x100000000) % 0x100,
|
||||||
|
floor(n / 0x1000000) % 0x100,
|
||||||
|
floor(n / 0x10000) % 0x100,
|
||||||
|
floor(n / 0x100) % 0x100,
|
||||||
|
n % 0x100)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local set_integer = function (integer)
|
||||||
|
if integer == 'unsigned' then
|
||||||
|
packers['integer'] = packers['unsigned']
|
||||||
|
elseif integer == 'signed' then
|
||||||
|
packers['integer'] = packers['signed']
|
||||||
|
else
|
||||||
|
argerror('set_integer', 1, "invalid option '" .. integer .."'")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
m.set_integer = set_integer
|
||||||
|
|
||||||
|
packers['float'] = function (buffer, n)
|
||||||
|
local sign = 0
|
||||||
|
if n < 0.0 then
|
||||||
|
sign = 0x80
|
||||||
|
n = -n
|
||||||
|
end
|
||||||
|
local mant, expo = frexp(n)
|
||||||
|
if mant ~= mant then
|
||||||
|
buffer[#buffer+1] = char(0xCA, -- nan
|
||||||
|
0xFF, 0x88, 0x00, 0x00)
|
||||||
|
elseif mant == huge or expo > 0x80 then
|
||||||
|
if sign == 0 then
|
||||||
|
buffer[#buffer+1] = char(0xCA, -- inf
|
||||||
|
0x7F, 0x80, 0x00, 0x00)
|
||||||
|
else
|
||||||
|
buffer[#buffer+1] = char(0xCA, -- -inf
|
||||||
|
0xFF, 0x80, 0x00, 0x00)
|
||||||
|
end
|
||||||
|
elseif (mant == 0.0 and expo == 0) or expo < -0x7E then
|
||||||
|
buffer[#buffer+1] = char(0xCA, -- zero
|
||||||
|
sign, 0x00, 0x00, 0x00)
|
||||||
|
else
|
||||||
|
expo = expo + 0x7E
|
||||||
|
mant = floor((mant * 2.0 - 1.0) * ldexp(0.5, 24))
|
||||||
|
buffer[#buffer+1] = char(0xCA,
|
||||||
|
sign + floor(expo / 0x2),
|
||||||
|
(expo % 0x2) * 0x80 + floor(mant / 0x10000),
|
||||||
|
floor(mant / 0x100) % 0x100,
|
||||||
|
mant % 0x100)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
packers['double'] = function (buffer, n)
|
||||||
|
local sign = 0
|
||||||
|
if n < 0.0 then
|
||||||
|
sign = 0x80
|
||||||
|
n = -n
|
||||||
|
end
|
||||||
|
local mant, expo = frexp(n)
|
||||||
|
if mant ~= mant then
|
||||||
|
buffer[#buffer+1] = char(0xCB, -- nan
|
||||||
|
0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
|
||||||
|
elseif mant == huge or expo > 0x400 then
|
||||||
|
if sign == 0 then
|
||||||
|
buffer[#buffer+1] = char(0xCB, -- inf
|
||||||
|
0x7F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
|
||||||
|
else
|
||||||
|
buffer[#buffer+1] = char(0xCB, -- -inf
|
||||||
|
0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
|
||||||
|
end
|
||||||
|
elseif (mant == 0.0 and expo == 0) or expo < -0x3FE then
|
||||||
|
buffer[#buffer+1] = char(0xCB, -- zero
|
||||||
|
sign, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
|
||||||
|
else
|
||||||
|
expo = expo + 0x3FE
|
||||||
|
mant = floor((mant * 2.0 - 1.0) * ldexp(0.5, 53))
|
||||||
|
buffer[#buffer+1] = char(0xCB,
|
||||||
|
sign + floor(expo / 0x10),
|
||||||
|
(expo % 0x10) * 0x10 + floor(mant / 0x1000000000000),
|
||||||
|
floor(mant / 0x10000000000) % 0x100,
|
||||||
|
floor(mant / 0x100000000) % 0x100,
|
||||||
|
floor(mant / 0x1000000) % 0x100,
|
||||||
|
floor(mant / 0x10000) % 0x100,
|
||||||
|
floor(mant / 0x100) % 0x100,
|
||||||
|
mant % 0x100)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local set_number = function (number)
|
||||||
|
if number == 'float' then
|
||||||
|
packers['number'] = function (buffer, n)
|
||||||
|
if floor(n) == n and n < maxinteger and n > mininteger then
|
||||||
|
packers['integer'](buffer, n)
|
||||||
|
else
|
||||||
|
packers['float'](buffer, n)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif number == 'double' then
|
||||||
|
packers['number'] = function (buffer, n)
|
||||||
|
if floor(n) == n and n < maxinteger and n > mininteger then
|
||||||
|
packers['integer'](buffer, n)
|
||||||
|
else
|
||||||
|
packers['double'](buffer, n)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
argerror('set_number', 1, "invalid option '" .. number .."'")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
m.set_number = set_number
|
||||||
|
|
||||||
|
for k = 0, 4 do
|
||||||
|
local n = tointeger(2^k)
|
||||||
|
local fixext = 0xD4 + k
|
||||||
|
packers['fixext' .. tostring(n)] = function (buffer, tag, data)
|
||||||
|
assert(#data == n, "bad length for fixext" .. tostring(n))
|
||||||
|
buffer[#buffer+1] = char(fixext,
|
||||||
|
tag < 0 and tag + 0x100 or tag)
|
||||||
|
buffer[#buffer+1] = data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
packers['ext'] = function (buffer, tag, data)
|
||||||
|
local n = #data
|
||||||
|
if n <= 0xFF then
|
||||||
|
buffer[#buffer+1] = char(0xC7, -- ext8
|
||||||
|
n,
|
||||||
|
tag < 0 and tag + 0x100 or tag)
|
||||||
|
elseif n <= 0xFFFF then
|
||||||
|
buffer[#buffer+1] = char(0xC8, -- ext16
|
||||||
|
floor(n / 0x100),
|
||||||
|
n % 0x100,
|
||||||
|
tag < 0 and tag + 0x100 or tag)
|
||||||
|
elseif n <= 4294967295.0 then
|
||||||
|
buffer[#buffer+1] = char(0xC9, -- ext&32
|
||||||
|
floor(n / 0x1000000),
|
||||||
|
floor(n / 0x10000) % 0x100,
|
||||||
|
floor(n / 0x100) % 0x100,
|
||||||
|
n % 0x100,
|
||||||
|
tag < 0 and tag + 0x100 or tag)
|
||||||
|
else
|
||||||
|
error"overflow in pack 'ext'"
|
||||||
|
end
|
||||||
|
buffer[#buffer+1] = data
|
||||||
|
end
|
||||||
|
|
||||||
|
function m.pack (data)
|
||||||
|
local buffer = {}
|
||||||
|
packers[type(data)](buffer, data)
|
||||||
|
return tconcat(buffer)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local unpackers -- forward declaration
|
||||||
|
|
||||||
|
local function unpack_cursor (c)
|
||||||
|
local s, i, j = c.s, c.i, c.j
|
||||||
|
if i > j then
|
||||||
|
c:underflow(i)
|
||||||
|
s, i, j = c.s, c.i, c.j
|
||||||
|
end
|
||||||
|
local val = s:byte(i)
|
||||||
|
c.i = i+1
|
||||||
|
return unpackers[val](c, val)
|
||||||
|
end
|
||||||
|
m.unpack_cursor = unpack_cursor
|
||||||
|
|
||||||
|
local function unpack_str (c, n)
|
||||||
|
local s, i, j = c.s, c.i, c.j
|
||||||
|
local e = i+n-1
|
||||||
|
if e > j or n < 0 then
|
||||||
|
c:underflow(e)
|
||||||
|
s, i, j = c.s, c.i, c.j
|
||||||
|
e = i+n-1
|
||||||
|
end
|
||||||
|
c.i = i+n
|
||||||
|
return s:sub(i, e)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unpack_array (c, n)
|
||||||
|
local t = {}
|
||||||
|
for i = 1, n do
|
||||||
|
t[i] = unpack_cursor(c)
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unpack_map (c, n)
|
||||||
|
local t = {}
|
||||||
|
for i = 1, n do
|
||||||
|
local k = unpack_cursor(c)
|
||||||
|
local val = unpack_cursor(c)
|
||||||
|
if k == nil or k ~= k then
|
||||||
|
k = m.sentinel
|
||||||
|
end
|
||||||
|
if k ~= nil then
|
||||||
|
t[k] = val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unpack_float (c)
|
||||||
|
local s, i, j = c.s, c.i, c.j
|
||||||
|
if i+3 > j then
|
||||||
|
c:underflow(i+3)
|
||||||
|
s, i, j = c.s, c.i, c.j
|
||||||
|
end
|
||||||
|
local b1, b2, b3, b4 = s:byte(i, i+3)
|
||||||
|
local sign = b1 > 0x7F
|
||||||
|
local expo = (b1 % 0x80) * 0x2 + floor(b2 / 0x80)
|
||||||
|
local mant = ((b2 % 0x80) * 0x100 + b3) * 0x100 + b4
|
||||||
|
if sign then
|
||||||
|
sign = -1
|
||||||
|
else
|
||||||
|
sign = 1
|
||||||
|
end
|
||||||
|
local n
|
||||||
|
if mant == 0 and expo == 0 then
|
||||||
|
n = sign * 0.0
|
||||||
|
elseif expo == 0xFF then
|
||||||
|
if mant == 0 then
|
||||||
|
n = sign * huge
|
||||||
|
else
|
||||||
|
n = 0.0/0.0
|
||||||
|
end
|
||||||
|
else
|
||||||
|
n = sign * ldexp(1.0 + mant / 0x800000, expo - 0x7F)
|
||||||
|
end
|
||||||
|
c.i = i+4
|
||||||
|
return n
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unpack_double (c)
|
||||||
|
local s, i, j = c.s, c.i, c.j
|
||||||
|
if i+7 > j then
|
||||||
|
c:underflow(i+7)
|
||||||
|
s, i, j = c.s, c.i, c.j
|
||||||
|
end
|
||||||
|
local b1, b2, b3, b4, b5, b6, b7, b8 = s:byte(i, i+7)
|
||||||
|
local sign = b1 > 0x7F
|
||||||
|
local expo = (b1 % 0x80) * 0x10 + floor(b2 / 0x10)
|
||||||
|
local mant = ((((((b2 % 0x10) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8
|
||||||
|
if sign then
|
||||||
|
sign = -1
|
||||||
|
else
|
||||||
|
sign = 1
|
||||||
|
end
|
||||||
|
local n
|
||||||
|
if mant == 0 and expo == 0 then
|
||||||
|
n = sign * 0.0
|
||||||
|
elseif expo == 0x7FF then
|
||||||
|
if mant == 0 then
|
||||||
|
n = sign * huge
|
||||||
|
else
|
||||||
|
n = 0.0/0.0
|
||||||
|
end
|
||||||
|
else
|
||||||
|
n = sign * ldexp(1.0 + mant / 4503599627370496.0, expo - 0x3FF)
|
||||||
|
end
|
||||||
|
c.i = i+8
|
||||||
|
return n
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unpack_uint8 (c)
|
||||||
|
local s, i, j = c.s, c.i, c.j
|
||||||
|
if i > j then
|
||||||
|
c:underflow(i)
|
||||||
|
s, i, j = c.s, c.i, c.j
|
||||||
|
end
|
||||||
|
local b1 = s:byte(i)
|
||||||
|
c.i = i+1
|
||||||
|
return b1
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unpack_uint16 (c)
|
||||||
|
local s, i, j = c.s, c.i, c.j
|
||||||
|
if i+1 > j then
|
||||||
|
c:underflow(i+1)
|
||||||
|
s, i, j = c.s, c.i, c.j
|
||||||
|
end
|
||||||
|
local b1, b2 = s:byte(i, i+1)
|
||||||
|
c.i = i+2
|
||||||
|
return b1 * 0x100 + b2
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unpack_uint32 (c)
|
||||||
|
local s, i, j = c.s, c.i, c.j
|
||||||
|
if i+3 > j then
|
||||||
|
c:underflow(i+3)
|
||||||
|
s, i, j = c.s, c.i, c.j
|
||||||
|
end
|
||||||
|
local b1, b2, b3, b4 = s:byte(i, i+3)
|
||||||
|
c.i = i+4
|
||||||
|
return ((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unpack_uint64 (c)
|
||||||
|
local s, i, j = c.s, c.i, c.j
|
||||||
|
if i+7 > j then
|
||||||
|
c:underflow(i+7)
|
||||||
|
s, i, j = c.s, c.i, c.j
|
||||||
|
end
|
||||||
|
local b1, b2, b3, b4, b5, b6, b7, b8 = s:byte(i, i+7)
|
||||||
|
c.i = i+8
|
||||||
|
return ((((((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unpack_int8 (c)
|
||||||
|
local s, i, j = c.s, c.i, c.j
|
||||||
|
if i > j then
|
||||||
|
c:underflow(i)
|
||||||
|
s, i, j = c.s, c.i, c.j
|
||||||
|
end
|
||||||
|
local b1 = s:byte(i)
|
||||||
|
c.i = i+1
|
||||||
|
if b1 < 0x80 then
|
||||||
|
return b1
|
||||||
|
else
|
||||||
|
return b1 - 0x100
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unpack_int16 (c)
|
||||||
|
local s, i, j = c.s, c.i, c.j
|
||||||
|
if i+1 > j then
|
||||||
|
c:underflow(i+1)
|
||||||
|
s, i, j = c.s, c.i, c.j
|
||||||
|
end
|
||||||
|
local b1, b2 = s:byte(i, i+1)
|
||||||
|
c.i = i+2
|
||||||
|
if b1 < 0x80 then
|
||||||
|
return b1 * 0x100 + b2
|
||||||
|
else
|
||||||
|
return ((b1 - 0xFF) * 0x100 + (b2 - 0xFF)) - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unpack_int32 (c)
|
||||||
|
local s, i, j = c.s, c.i, c.j
|
||||||
|
if i+3 > j then
|
||||||
|
c:underflow(i+3)
|
||||||
|
s, i, j = c.s, c.i, c.j
|
||||||
|
end
|
||||||
|
local b1, b2, b3, b4 = s:byte(i, i+3)
|
||||||
|
c.i = i+4
|
||||||
|
if b1 < 0x80 then
|
||||||
|
return ((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4
|
||||||
|
else
|
||||||
|
return ((((b1 - 0xFF) * 0x100 + (b2 - 0xFF)) * 0x100 + (b3 - 0xFF)) * 0x100 + (b4 - 0xFF)) - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unpack_int64 (c)
|
||||||
|
local s, i, j = c.s, c.i, c.j
|
||||||
|
if i+7 > j then
|
||||||
|
c:underflow(i+7)
|
||||||
|
s, i, j = c.s, c.i, c.j
|
||||||
|
end
|
||||||
|
local b1, b2, b3, b4, b5, b6, b7, b8 = s:byte(i, i+7)
|
||||||
|
c.i = i+8
|
||||||
|
if b1 < 0x80 then
|
||||||
|
return ((((((b1 * 0x100 + b2) * 0x100 + b3) * 0x100 + b4) * 0x100 + b5) * 0x100 + b6) * 0x100 + b7) * 0x100 + b8
|
||||||
|
else
|
||||||
|
return ((((((((b1 - 0xFF) * 0x100 + (b2 - 0xFF)) * 0x100 + (b3 - 0xFF)) * 0x100 + (b4 - 0xFF)) * 0x100 + (b5 - 0xFF)) * 0x100 + (b6 - 0xFF)) * 0x100 + (b7 - 0xFF)) * 0x100 + (b8 - 0xFF)) - 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function m.build_ext (tag, data)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function unpack_ext (c, n, tag)
|
||||||
|
local s, i, j = c.s, c.i, c.j
|
||||||
|
local e = i+n-1
|
||||||
|
if e > j or n < 0 then
|
||||||
|
c:underflow(e)
|
||||||
|
s, i, j = c.s, c.i, c.j
|
||||||
|
e = i+n-1
|
||||||
|
end
|
||||||
|
c.i = i+n
|
||||||
|
return m.build_ext(tag, s:sub(i, e))
|
||||||
|
end
|
||||||
|
|
||||||
|
unpackers = setmetatable({
|
||||||
|
[0xC0] = function () return nil end,
|
||||||
|
[0xC2] = function () return false end,
|
||||||
|
[0xC3] = function () return true end,
|
||||||
|
[0xC4] = function (c) return unpack_str(c, unpack_uint8(c)) end, -- bin8
|
||||||
|
[0xC5] = function (c) return unpack_str(c, unpack_uint16(c)) end, -- bin16
|
||||||
|
[0xC6] = function (c) return unpack_str(c, unpack_uint32(c)) end, -- bin32
|
||||||
|
[0xC7] = function (c) return unpack_ext(c, unpack_uint8(c), unpack_int8(c)) end,
|
||||||
|
[0xC8] = function (c) return unpack_ext(c, unpack_uint16(c), unpack_int8(c)) end,
|
||||||
|
[0xC9] = function (c) return unpack_ext(c, unpack_uint32(c), unpack_int8(c)) end,
|
||||||
|
[0xCA] = unpack_float,
|
||||||
|
[0xCB] = unpack_double,
|
||||||
|
[0xCC] = unpack_uint8,
|
||||||
|
[0xCD] = unpack_uint16,
|
||||||
|
[0xCE] = unpack_uint32,
|
||||||
|
[0xCF] = unpack_uint64,
|
||||||
|
[0xD0] = unpack_int8,
|
||||||
|
[0xD1] = unpack_int16,
|
||||||
|
[0xD2] = unpack_int32,
|
||||||
|
[0xD3] = unpack_int64,
|
||||||
|
[0xD4] = function (c) return unpack_ext(c, 1, unpack_int8(c)) end,
|
||||||
|
[0xD5] = function (c) return unpack_ext(c, 2, unpack_int8(c)) end,
|
||||||
|
[0xD6] = function (c) return unpack_ext(c, 4, unpack_int8(c)) end,
|
||||||
|
[0xD7] = function (c) return unpack_ext(c, 8, unpack_int8(c)) end,
|
||||||
|
[0xD8] = function (c) return unpack_ext(c, 16, unpack_int8(c)) end,
|
||||||
|
[0xD9] = function (c) return unpack_str(c, unpack_uint8(c)) end,
|
||||||
|
[0xDA] = function (c) return unpack_str(c, unpack_uint16(c)) end,
|
||||||
|
[0xDB] = function (c) return unpack_str(c, unpack_uint32(c)) end,
|
||||||
|
[0xDC] = function (c) return unpack_array(c, unpack_uint16(c)) end,
|
||||||
|
[0xDD] = function (c) return unpack_array(c, unpack_uint32(c)) end,
|
||||||
|
[0xDE] = function (c) return unpack_map(c, unpack_uint16(c)) end,
|
||||||
|
[0xDF] = function (c) return unpack_map(c, unpack_uint32(c)) end,
|
||||||
|
}, {
|
||||||
|
__index = function (t, k)
|
||||||
|
if k < 0xC0 then
|
||||||
|
if k < 0x80 then
|
||||||
|
return function (c, val) return val end
|
||||||
|
elseif k < 0x90 then
|
||||||
|
return function (c, val) return unpack_map(c, val % 0x10) end
|
||||||
|
elseif k < 0xA0 then
|
||||||
|
return function (c, val) return unpack_array(c, val % 0x10) end
|
||||||
|
else
|
||||||
|
return function (c, val) return unpack_str(c, val % 0x20) end
|
||||||
|
end
|
||||||
|
elseif k > 0xDF then
|
||||||
|
return function (c, val) return val - 0x100 end
|
||||||
|
else
|
||||||
|
return function () error("unpack '" .. format('%#x', k) .. "' is unimplemented") end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
||||||
|
local function cursor_string (str)
|
||||||
|
return {
|
||||||
|
s = str,
|
||||||
|
i = 1,
|
||||||
|
j = #str,
|
||||||
|
underflow = function ()
|
||||||
|
error "missing bytes"
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local function cursor_loader (ld)
|
||||||
|
return {
|
||||||
|
s = '',
|
||||||
|
i = 1,
|
||||||
|
j = 0,
|
||||||
|
underflow = function (self, e)
|
||||||
|
self.s = self.s:sub(self.i)
|
||||||
|
e = e - self.i + 1
|
||||||
|
self.i = 1
|
||||||
|
self.j = 0
|
||||||
|
while e > self.j do
|
||||||
|
local chunk = ld()
|
||||||
|
if not chunk then
|
||||||
|
error "missing bytes"
|
||||||
|
end
|
||||||
|
self.s = self.s .. chunk
|
||||||
|
self.j = #self.s
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function m.unpack (s)
|
||||||
|
checktype('unpack', 1, s, 'string')
|
||||||
|
local cursor = cursor_string(s)
|
||||||
|
local data = unpack_cursor(cursor)
|
||||||
|
if cursor.i <= cursor.j then
|
||||||
|
error "extra bytes"
|
||||||
|
end
|
||||||
|
return data
|
||||||
|
end
|
||||||
|
|
||||||
|
function m.unpacker (src)
|
||||||
|
if type(src) == 'string' then
|
||||||
|
local cursor = cursor_string(src)
|
||||||
|
return function ()
|
||||||
|
if cursor.i <= cursor.j then
|
||||||
|
return cursor.i, unpack_cursor(cursor)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif type(src) == 'function' then
|
||||||
|
local cursor = cursor_loader(src)
|
||||||
|
return function ()
|
||||||
|
if cursor.i > cursor.j then
|
||||||
|
pcall(cursor.underflow, cursor, cursor.i)
|
||||||
|
end
|
||||||
|
if cursor.i <= cursor.j then
|
||||||
|
return true, unpack_cursor(cursor)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
argerror('unpacker', 1, "string or function expected, got " .. type(src))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
set_string'string_compat'
|
||||||
|
set_integer'unsigned'
|
||||||
|
if SIZEOF_NUMBER == 4 then
|
||||||
|
maxinteger = 16777215
|
||||||
|
mininteger = -maxinteger
|
||||||
|
m.small_lua = true
|
||||||
|
unpackers[0xCB] = nil -- double
|
||||||
|
unpackers[0xCF] = nil -- uint64
|
||||||
|
unpackers[0xD3] = nil -- int64
|
||||||
|
set_number'float'
|
||||||
|
else
|
||||||
|
maxinteger = 9007199254740991
|
||||||
|
mininteger = -maxinteger
|
||||||
|
set_number'double'
|
||||||
|
if SIZEOF_NUMBER > 8 then
|
||||||
|
m.long_double = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
set_array'without_hole'
|
||||||
|
|
||||||
|
m._VERSION = '0.5.2'
|
||||||
|
m._DESCRIPTION = "lua-MessagePack : a pure Lua implementation"
|
||||||
|
m._COPYRIGHT = "Copyright (c) 2012-2019 Francois Perrad"
|
||||||
|
return m
|
||||||
|
--
|
||||||
|
-- This library is licensed under the terms of the MIT/X11 license,
|
||||||
|
-- like Lua itself.
|
||||||
|
--
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
msgpack=require("e3pf-mp")
|
||||||
|
|
||||||
|
epf_protocol = Proto("e3pf", "e3team Protocol Framework")
|
||||||
|
|
||||||
|
packet_length = ProtoField.uint64("e3pf.packet_length", "Packet Length", base.DEC)
|
||||||
|
packet_id = ProtoField.uint8("e3pf.packet_id", "Packet ID", base.DEC)
|
||||||
|
|
||||||
|
-- Message: ClientHello
|
||||||
|
ch_protocol_version = ProtoField.uint32("e3pf.client_hello.protocol_version", "Protocol Version", base.DEC)
|
||||||
|
ch_client_random = ProtoField.string("e3pf.client_hello.client_random", "Client Random")
|
||||||
|
-- Client certificate: is a subtree
|
||||||
|
ch_client_x25519 = ProtoField.string("e3pf.client_hello.client_x25519", "Client X25519 Public Key")
|
||||||
|
|
||||||
|
-- Message: ServerHello
|
||||||
|
sh_protocol_version = ProtoField.uint32("e3pf.server_hello.protocol_version", "Protocol Version", base.DEC)
|
||||||
|
-- Server Certificate: is a subtree
|
||||||
|
sh_server_random = ProtoField.string("e3pf.server_hello.server_random", "Server Random")
|
||||||
|
sh_server_x25519 = ProtoField.string("e3pf.server_hello.server_x25519", "Server X25519 Public Key")
|
||||||
|
|
||||||
|
-- Struct: Certificate
|
||||||
|
-- Details: is a subtree
|
||||||
|
c_fingerprint = ProtoField.string("e3pf.certificate.fingerprint", "Fingerprint")
|
||||||
|
c_signature = ProtoField.string("e3pf.certificate.signature", "Signature")
|
||||||
|
|
||||||
|
-- Struct: Certificate.Details
|
||||||
|
c_d_name = ProtoField.string("e3pf.certificate.details.name", "Name")
|
||||||
|
c_d_not_before = ProtoField.string("e3pf.certificate.details.not_before", "Not Before")
|
||||||
|
c_d_not_after = ProtoField.string("e3pf.certificate.details.not_after", "Not After")
|
||||||
|
c_d_public_key = ProtoField.string("e3pf.certificate.details.public_key", "Public Key")
|
||||||
|
c_d_issuer_public_key = ProtoField.string("e3pf.certificate.details.issuer_public_key", "Issuer Public Key")
|
||||||
|
-- TODO: Claims
|
||||||
|
|
||||||
|
epf_protocol.fields = { packet_length, packet_id, ch_protocol_version, ch_client_random, ch_client_x25519, c_fingerprint, c_signature, c_d_name, c_d_not_before, c_d_not_after, c_d_public_key, c_d_issuer_public_key, sh_protocol_version, sh_server_random, sh_server_x25519 }
|
||||||
|
|
||||||
|
function dump(o)
|
||||||
|
if type(o) == 'table' then
|
||||||
|
local s = '{ '
|
||||||
|
for k,v in pairs(o) do
|
||||||
|
if type(k) ~= 'number' then k = '"'..k..'"' end
|
||||||
|
s = s .. '['..k..'] = ' .. dump(v) .. ','
|
||||||
|
end
|
||||||
|
return s .. '} '
|
||||||
|
else
|
||||||
|
return tostring(o)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function id_to_name(id)
|
||||||
|
if id == 1 then return "ClientHello"
|
||||||
|
elseif id == 2 then return "ServerHello"
|
||||||
|
elseif id == 3 then return "HandshakeFinished"
|
||||||
|
elseif id == 4 then return "ApplicationData"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function table_to_hex(ctable)
|
||||||
|
local sb=''
|
||||||
|
for _, value in pairs(ctable) do
|
||||||
|
sb=sb .. string.format("%x", value)
|
||||||
|
end
|
||||||
|
return sb
|
||||||
|
end
|
||||||
|
|
||||||
|
local function cert_to_tree(rtree, name, ctable, buffer)
|
||||||
|
local rrtree = rtree:add(epf_protocol, buffer(), name)
|
||||||
|
|
||||||
|
local tree = rrtree:add(epf_protocol, buffer(), "Details")
|
||||||
|
|
||||||
|
local details = ctable[1]
|
||||||
|
|
||||||
|
tree:add(c_d_name, details[1])
|
||||||
|
tree:add(c_d_not_before, tostring(details[2]))
|
||||||
|
tree:add(c_d_not_after, tostring(details[3]))
|
||||||
|
tree:add(c_d_public_key, table_to_hex(details[4]))
|
||||||
|
tree:add(c_d_issuer_public_key, table_to_hex(details[5]))
|
||||||
|
|
||||||
|
rrtree:add(c_fingerprint, ctable[2])
|
||||||
|
rrtree:add(c_signature, table_to_hex(ctable[3]))
|
||||||
|
end
|
||||||
|
|
||||||
|
function epf_protocol.dissector(buffer, pinfo, tree)
|
||||||
|
length = buffer:len()
|
||||||
|
if length == 0 then return end
|
||||||
|
|
||||||
|
pinfo.cols.protocol = epf_protocol.name
|
||||||
|
|
||||||
|
local subtree = tree:add(epf_protocol, buffer(), "e3team Protocol Framework Data")
|
||||||
|
local dtree = subtree:add(epf_protocol, buffer(), "Packet Data")
|
||||||
|
|
||||||
|
local packet_length_flag = tonumber(buffer(0,8):le_uint64())
|
||||||
|
|
||||||
|
subtree:add_le(packet_length, buffer(0, 8))
|
||||||
|
|
||||||
|
local data = buffer(8, packet_length_flag)
|
||||||
|
|
||||||
|
local msgpack_data=''
|
||||||
|
local bytes_message = data:bytes()
|
||||||
|
for i=0, (bytes_message:len()-1) do
|
||||||
|
value=bytes_message:get_index(i)
|
||||||
|
msgpack_data=msgpack_data .. string.char(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
local message_unpacked = msgpack.unpack(msgpack_data)
|
||||||
|
|
||||||
|
subtree:add(packet_id, message_unpacked[1]):append_text(" (" .. id_to_name(message_unpacked[1]) .. ")")
|
||||||
|
|
||||||
|
local packet_bytes = message_unpacked[2]
|
||||||
|
local packet_data=''
|
||||||
|
for _, value in pairs(packet_bytes) do
|
||||||
|
packet_data=packet_data .. string.char(value)
|
||||||
|
end
|
||||||
|
|
||||||
|
local packet = msgpack.unpack(packet_data)
|
||||||
|
|
||||||
|
if message_unpacked[1] == 1 then
|
||||||
|
-- CLIENT HELLO
|
||||||
|
dtree:add(ch_protocol_version, packet[1])
|
||||||
|
dtree:add(ch_client_random, table_to_hex(packet[2]))
|
||||||
|
dtree:add(ch_client_x25519, table_to_hex(packet[4]))
|
||||||
|
elseif message_unpacked[1] == 2 then
|
||||||
|
-- SERVER HELLO
|
||||||
|
dtree:add(sh_protocol_version, packet[1])
|
||||||
|
cert_to_tree(dtree, "Server Certificate", packet[2], buffer)
|
||||||
|
dtree:add(sh_server_random, table_to_hex(packet[3]))
|
||||||
|
dtree:add(sh_server_x25519, table_to_hex(packet[4]))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function heuristic_checker(buffer, pinfo, tree)
|
||||||
|
length = buffer:len()
|
||||||
|
if length < 12 then return false end
|
||||||
|
|
||||||
|
local header_start_flag = buffer(8,1):uint()
|
||||||
|
|
||||||
|
if header_start_flag ~= 0x92 then return false end
|
||||||
|
|
||||||
|
local packet_length_flag = buffer(0,8):le_uint64()
|
||||||
|
|
||||||
|
local packet_length = length - 8
|
||||||
|
|
||||||
|
if tostring(packet_length) ~= tostring(packet_length_flag) then return false end
|
||||||
|
|
||||||
|
local packet_id = buffer(9,1):uint()
|
||||||
|
|
||||||
|
print("packet " .. packet_id)
|
||||||
|
|
||||||
|
if packet_id == 0x01 or packet_id == 0x02 or packet_id == 0x03 or packet_id == 0x04 then
|
||||||
|
epf_protocol.dissector(buffer, pinfo, tree)
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
epf_protocol:register_heuristic("tcp", heuristic_checker)
|
Loading…
Reference in New Issue