Skip to content

Commit

Permalink
Test: added a basic tsig flow test in tig_integration_tests
Browse files Browse the repository at this point in the history
  • Loading branch information
EstudianteGenerico047 committed Jul 14, 2024
1 parent ec6fb88 commit 2594500
Showing 1 changed file with 113 additions and 12 deletions.
125 changes: 113 additions & 12 deletions tests/tsig_integration_tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::{net::IpAddr, str::FromStr, thread, net::UdpSocket, time::Duration};
use dns_rust::{async_resolver::{config::ResolverConfig, AsyncResolver}, client::client_error::ClientError, domain_name::DomainName, message::{rdata::Rdata,class_qclass::Qclass, type_qtype, resource_record::ResourceRecord, header::Header, DnsMessage},tsig::{self, TsigAlgorithm}};
use std::{collections::HashMap, net::{IpAddr, UdpSocket}, str::FromStr, thread, time::Duration};
use dns_rust::{async_resolver::{config::ResolverConfig, AsyncResolver}, client::client_error::ClientError, domain_name::DomainName, message::{class_qclass::Qclass, header::Header, rdata::{tsig_rdata::TSigRdata, Rdata}, resource_record::ResourceRecord, type_qtype, DnsMessage},tsig::{self, get_digest_request, process_tsig, sign_tsig, string_to_tsig_alg, TsigAlgorithm, TsigErrorCode}};

///RFC 8945 TSIG tests
/*This tests verifies section 5.3:
Expand All @@ -21,28 +21,59 @@ use dns_rust::{async_resolver::{config::ResolverConfig, AsyncResolver}, client::
Request MAC
DNS Message (response)
TSIG Variables (response) */
TSIG Variables (response)
DISCLAIMER: Como no hay un "NameServer" implementado, se probó la firma y verififcaciónde TSIG utilizando
threads y scokets. La idea central es tener un thread recibiendo datos de localhost, el cual tiene en alguna
parte guardados pares (key, name) que serían utilizados para la autenticación (en este caso, se guardaron los
pares en un hash_map. Como se guarden no es relevante para tsig, depende de la implementación del servidor.
Lo único importante es que podamos buscar la llave asociada a un cierto nombre, de acuerdo a la solicitud que
recibamos).
En este test se verifica a nivel macro el correcto flujo de TSIG: primero se envia a un host en localhost
una query firmada, el cual verifica la integridad de la query y responde con un respuesta firmada que será verificada.
*/
#[tokio::test]
async fn tsig_signature() {
// global test variables
// the key to test tsig flow. The server should had the same key
let key = b"1234567890";
// global test variables
let alg_name = TsigAlgorithm::HmacSha1;
let fudge = 0;
let time_signed = 0;
let fudge = 100;
let time_signed = 12345678;
let id = 6502;
let name = "nictest.cl";
let mut dns_query_message =
DnsMessage::new_query_message(
DomainName::new_from_string("nictest.cl".to_string()),
DomainName::new_from_string(name.to_string()),
type_qtype::Qtype::A,
Qclass::IN,
0,
false,
id);

//Lanzamiento de threads
//Se lanza el servidor. Recibe un mensaje sin firmar, lo firma y lo reenvía
//lista de algoritmos disponibles. En este caso, ambos host tendrán la misma
let mut a_algs :Vec<(String, bool)> = vec![];
a_algs.push((String::from("hmac-sha1"),true));
a_algs.push((String::from("hmac-sha256"),true));


//Código para el servidor. Recibe un mensaje firmado, lo verifica y envía una repuesta autenticada, según lo descrito en el
//RFC 8945. Este servidor tiene su propia lista de algoritmos disponibles y llaves asociadas a nombres de dominio
fn host(){
println!("I am a host");
//la lista de algoritmos del host
let mut list :Vec<(String, bool)> = vec![];
list.push((String::from("hmac-sha1"),true));
list.push((String::from("hmac-sha256"),true));

// se crean las llaves del servidor
let key1 = b"1234567890";
let key2 = b"1034567692";
// se mapean las llaves anteriores a un nombre. Acá deberemos buscar el nombre de lo que se reciba para utilizar la llave correcta
let mut keys = HashMap::new();
keys.insert(DomainName::new_from_string("nictest.cl".to_string()), key1);
keys.insert(DomainName::new_from_string("example.cl".to_string()), key2);

//se recibiran datos de otro thread a través de un socket UDP
let udp_socket = UdpSocket::bind("127.0.0.1:8002").expect("Failed to bind to address");
let mut buf = [0; 512];

Expand All @@ -52,9 +83,42 @@ async fn tsig_signature() {
println!("Received {} bytes from {}", size, source);
let mut data = DnsMessage::from_bytes(&buf[0..size]).unwrap();
println!("The data is {:?}", data);
let key_name = "".to_string();
tsig::sign_tsig(&mut data, b"1234567890",TsigAlgorithm::HmacSha1,0,0, key_name);
let mut addit = data.get_additional();
let rr = addit.pop().expect("No tsigrr");
let mut tsig_rd = TSigRdata::new();
let mut can_sign = false;

match rr.get_rdata() {
Rdata::TSIG(data) =>{
tsig_rd = data;
}
_ => {
can_sign = true;
println!("error: no TSIG rdata found!");
}
}
//se extraen las variables TSIG necesarias.
let alg_name = tsig_rd.get_algorithm_name().get_name();
let time =tsig_rd.get_time_signed();
let fudge = tsig_rd.get_fudge();
let mac = tsig_rd.get_mac();
let name = rr.get_name();
let key_name = name.clone().get_name();
// se extrae la llave necesaria
let key_found = keys[&name];

//el servidor verifica la estructura del tsig recibido. Sumamos un pequeño delay al time para simular retraso
let (answer,error) = process_tsig(&data, key_found, key_name.clone(), time + 50, list, vec![]);
//se setea el aditional sin el ultimo resource record, para que sign_tsig lo regenere
data.set_additional(addit);
data.update_header_counters();
// se firma el mensaje recibido con el digest de la respuesta. Notar que el vector final ahora no está vacío
sign_tsig(&mut data, key_found,string_to_tsig_alg(alg_name),fudge,time, key_name, mac);
let response = &DnsMessage::to_bytes(&data);
//se verifica que la request haya pasado proces_tsig
assert_eq!(error,TsigErrorCode::NOERR);

// se envia la respuesta si lo anterior resultó ser correcto
udp_socket
.send_to(&response, source)
.expect("Failed to send response");
Expand All @@ -67,18 +131,55 @@ async fn tsig_signature() {
}

}

//Lanzamiento de threads
println!("Starting server");
let server_handle = thread::spawn(|| {
host();

});
thread::sleep(Duration::from_secs(2));

// se instancia un socket cliente que enviará y mensajes
let client_sock = UdpSocket::bind("127.0.0.1:8001").expect("Nothing");
// El cliente firma el mensaje para enviar al servidor. Se guarda el mac de la firma
let mac = sign_tsig(&mut dns_query_message, key, alg_name, fudge, time_signed, name.to_string(), vec![]);
let buf = dns_query_message.to_bytes();
client_sock.send_to(&buf,"127.0.0.1:8002").unwrap();
println!("Mensaje enviado");
server_handle.join().unwrap();
let mut buf = [0; 512];

// Ahora el cliente verifica la respuesta recibida del servidor
match client_sock.recv_from(&mut buf) {

Ok((size, source)) => {
println!("Received {} bytes from {}", size, source);
let data = DnsMessage::from_bytes(&buf[0..size]).unwrap();
println!("The data is {:?}", data);
let mut additionals = data.get_additional();
let tsig_rr = additionals.pop().expect("No tsigrr");
let mut tsig_rd= TSigRdata::new();
match tsig_rr.get_rdata() {
Rdata::TSIG(data) =>{
tsig_rd = data;
}
_ => {
println!("error: no TSIG rdata found!");
}
}

// El cliente procesa la respuesta
let (answer, error ) = process_tsig(&data, key, name.to_string(), time_signed, a_algs, mac);
// se verifica que el mensaje haya pasado process_tsig
assert!(answer);
assert_eq!(error,TsigErrorCode::NOERR);
}
Err(e) => {
eprintln!("Error receiving data: {}", e);

}
}


}
Expand Down

0 comments on commit 2594500

Please sign in to comment.