summaryrefslogtreecommitdiff
path: root/src/bin/client.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/client.rs')
-rw-r--r--src/bin/client.rs213
1 files changed, 139 insertions, 74 deletions
diff --git a/src/bin/client.rs b/src/bin/client.rs
index d9b37fc..d24315b 100644
--- a/src/bin/client.rs
+++ b/src/bin/client.rs
@@ -1,11 +1,23 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+extern crate byteorder;
+extern crate chrono;
#[macro_use]
extern crate clap;
-extern crate roughenough;
+extern crate hex;
extern crate ring;
+extern crate roughenough;
extern crate time;
-extern crate chrono;
-extern crate byteorder;
-extern crate hex;
use ring::rand;
use ring::rand::SecureRandom;
@@ -17,12 +29,12 @@ use chrono::offset::Utc;
use std::iter::Iterator;
use std::collections::HashMap;
-use std::net::{UdpSocket, ToSocketAddrs};
+use std::net::{ToSocketAddrs, UdpSocket};
-use roughenough::{RtMessage, Tag, VERSION, CERTIFICATE_CONTEXT, SIGNED_RESPONSE_CONTEXT};
+use roughenough::{RtMessage, Tag, CERTIFICATE_CONTEXT, SIGNED_RESPONSE_CONTEXT, VERSION};
use roughenough::sign::Verifier;
use roughenough::merkle::root_from_paths;
-use clap::{Arg, App};
+use clap::{App, Arg};
fn create_nonce() -> [u8; 64] {
let rng = rand::SystemRandom::new();
@@ -43,32 +55,37 @@ fn make_request(nonce: &[u8]) -> Vec<u8> {
fn receive_response(sock: &mut UdpSocket) -> RtMessage {
let mut buf = [0; 744];
let resp_len = sock.recv_from(&mut buf).unwrap().0;
+
RtMessage::from_bytes(&buf[0..resp_len]).unwrap()
}
-
struct ResponseHandler {
pub_key: Option<Vec<u8>>,
msg: HashMap<Tag, Vec<u8>>,
srep: HashMap<Tag, Vec<u8>>,
cert: HashMap<Tag, Vec<u8>>,
dele: HashMap<Tag, Vec<u8>>,
- nonce: [u8; 64]
+ nonce: [u8; 64],
}
struct ParsedResponse {
verified: bool,
midpoint: u64,
- radius: u32
+ radius: u32,
}
impl ResponseHandler {
pub fn new(pub_key: Option<Vec<u8>>, response: RtMessage, nonce: [u8; 64]) -> ResponseHandler {
let msg = response.into_hash_map();
- let srep = RtMessage::from_bytes(&msg[&Tag::SREP]).unwrap().into_hash_map();
- let cert = RtMessage::from_bytes(&msg[&Tag::CERT]).unwrap().into_hash_map();
-
- let dele = RtMessage::from_bytes(&cert[&Tag::DELE]).unwrap().into_hash_map();
+ let srep = RtMessage::from_bytes(&msg[&Tag::SREP])
+ .unwrap()
+ .into_hash_map();
+ let cert = RtMessage::from_bytes(&msg[&Tag::CERT])
+ .unwrap()
+ .into_hash_map();
+ let dele = RtMessage::from_bytes(&cert[&Tag::DELE])
+ .unwrap()
+ .into_hash_map();
ResponseHandler {
pub_key,
@@ -76,13 +93,19 @@ impl ResponseHandler {
srep,
cert,
dele,
- nonce
+ nonce,
}
}
pub fn extract_time(&self) -> ParsedResponse {
- let midpoint = self.srep[&Tag::MIDP].as_slice().read_u64::<LittleEndian>().unwrap();
- let radius = self.srep[&Tag::RADI].as_slice().read_u32::<LittleEndian>().unwrap();
+ let midpoint = self.srep[&Tag::MIDP]
+ .as_slice()
+ .read_u64::<LittleEndian>()
+ .unwrap();
+ let radius = self.srep[&Tag::RADI]
+ .as_slice()
+ .read_u32::<LittleEndian>()
+ .unwrap();
let mut verified = false;
if self.pub_key.is_some() {
@@ -96,7 +119,7 @@ impl ResponseHandler {
ParsedResponse {
verified,
midpoint,
- radius
+ radius,
}
}
@@ -104,35 +127,63 @@ impl ResponseHandler {
let mut full_cert = Vec::from(CERTIFICATE_CONTEXT.as_bytes());
full_cert.extend(&self.cert[&Tag::DELE]);
- assert!(self.validate_sig(self.pub_key.as_ref().unwrap(), &self.cert[&Tag::SIG], &full_cert),
- "Invalid signature on DELE tag!");
+ assert!(
+ self.validate_sig(
+ self.pub_key.as_ref().unwrap(),
+ &self.cert[&Tag::SIG],
+ &full_cert
+ ),
+ "Invalid signature on DELE tag!"
+ );
}
fn validate_srep(&self) {
let mut full_srep = Vec::from(SIGNED_RESPONSE_CONTEXT.as_bytes());
full_srep.extend(&self.msg[&Tag::SREP]);
- assert!(self.validate_sig(&self.dele[&Tag::PUBK], &self.msg[&Tag::SIG], &full_srep),
- "Invalid signature on SREP tag!");
+ assert!(
+ self.validate_sig(&self.dele[&Tag::PUBK], &self.msg[&Tag::SIG], &full_srep),
+ "Invalid signature on SREP tag!"
+ );
}
fn validate_merkle(&self) {
- let srep = RtMessage::from_bytes(&self.msg[&Tag::SREP]).unwrap().into_hash_map();
- let index = self.msg[&Tag::INDX].as_slice().read_u32::<LittleEndian>().unwrap();
+ let srep = RtMessage::from_bytes(&self.msg[&Tag::SREP])
+ .unwrap()
+ .into_hash_map();
+ let index = self.msg[&Tag::INDX]
+ .as_slice()
+ .read_u32::<LittleEndian>()
+ .unwrap();
let paths = &self.msg[&Tag::PATH];
- let hash = root_from_paths(index as usize, &self.nonce, paths);
-
- assert_eq!(Vec::from(hash), srep[&Tag::ROOT], "Nonce not in merkle tree!");
+ let hash = root_from_paths(index as usize, &self.nonce, paths);
+ assert_eq!(
+ Vec::from(hash),
+ srep[&Tag::ROOT],
+ "Nonce not in merkle tree!"
+ );
}
fn validate_midpoint(&self, midpoint: u64) {
- let mint = self.dele[&Tag::MINT].as_slice().read_u64::<LittleEndian>().unwrap();
- let maxt = self.dele[&Tag::MAXT].as_slice().read_u64::<LittleEndian>().unwrap();
-
- assert!(midpoint >= mint, "Response midpoint {} lies before delegation span ({}, {})");
- assert!(midpoint <= maxt, "Response midpoint {} lies after delegation span ({}, {})");
+ let mint = self.dele[&Tag::MINT]
+ .as_slice()
+ .read_u64::<LittleEndian>()
+ .unwrap();
+ let maxt = self.dele[&Tag::MAXT]
+ .as_slice()
+ .read_u64::<LittleEndian>()
+ .unwrap();
+
+ assert!(
+ midpoint >= mint,
+ "Response midpoint {} lies before delegation span ({}, {})"
+ );
+ assert!(
+ midpoint <= maxt,
+ "Response midpoint {} lies after delegation span ({}, {})"
+ );
}
fn validate_sig(&self, public_key: &[u8], sig: &[u8], data: &[u8]) -> bool {
@@ -144,56 +195,60 @@ impl ResponseHandler {
fn main() {
let matches = App::new("roughenough client")
- .version(VERSION)
- .arg(Arg::with_name("host")
- .required(true)
- .help("The Roughtime server to connect to")
- .takes_value(true))
- .arg(Arg::with_name("port")
- .required(true)
- .help("The Roughtime server port to connect to")
- .takes_value(true))
- .arg(Arg::with_name("public-key")
- .short("p")
- .long("public-key")
- .takes_value(true)
- .help("The server public key used to validate responses. If unset, no validation will be performed"))
- .arg(Arg::with_name("time-format")
- .short("f")
- .long("time-format")
- .takes_value(true)
- .help("The strftime format string used to print the time recieved from the server")
- .default_value("%b %d %Y %H:%M:%S")
- )
- .arg(Arg::with_name("num-requests")
- .short("n")
- .long("num-requests")
- .takes_value(true)
- .help("The number of requests to make to the server (each from a different source port). This is mainly useful for testing batch response handling")
- .default_value("1")
- )
- .arg(Arg::with_name("stress")
- .short("s")
- .long("stress")
- .help("Stress-tests the server by sending the same request as fast as possible. Please only use this on your own server")
- )
- .get_matches();
+ .version(VERSION)
+ .arg(Arg::with_name("host")
+ .required(true)
+ .help("The Roughtime server to connect to")
+ .takes_value(true))
+ .arg(Arg::with_name("port")
+ .required(true)
+ .help("The Roughtime server port to connect to")
+ .takes_value(true))
+ .arg(Arg::with_name("public-key")
+ .short("p")
+ .long("public-key")
+ .takes_value(true)
+ .help("The server public key used to validate responses. If unset, no validation will be performed"))
+ .arg(Arg::with_name("time-format")
+ .short("f")
+ .long("time-format")
+ .takes_value(true)
+ .help("The strftime format string used to print the time recieved from the server")
+ .default_value("%b %d %Y %H:%M:%S")
+ )
+ .arg(Arg::with_name("num-requests")
+ .short("n")
+ .long("num-requests")
+ .takes_value(true)
+ .help("The number of requests to make to the server (each from a different source port). This is mainly useful for testing batch response handling")
+ .default_value("1")
+ )
+ .arg(Arg::with_name("stress")
+ .short("s")
+ .long("stress")
+ .help("Stress-tests the server by sending the same request as fast as possible. Please only use this on your own server")
+ )
+ .get_matches();
let host = matches.value_of("host").unwrap();
let port = value_t_or_exit!(matches.value_of("port"), u16);
let num_requests = value_t_or_exit!(matches.value_of("num-requests"), u16) as usize;
- let pub_key = matches.value_of("public-key").map(|pkey| hex::decode(pkey).expect("Error parsing public key!"));
let time_format = matches.value_of("time-format").unwrap();
let stress = matches.is_present("stress");
+ let pub_key = matches
+ .value_of("public-key")
+ .map(|pkey| hex::decode(pkey).expect("Error parsing public key!"));
println!("Requesting time from: {:?}:{:?}", host, port);
let addr = (host, port).to_socket_addrs().unwrap().next().unwrap();
if stress {
-
if !addr.ip().is_loopback() {
- println!("ERROR: Cannot use non-loopback address {} for stress testing", addr.ip());
+ println!(
+ "ERROR: Cannot use non-loopback address {} for stress testing",
+ addr.ip()
+ );
return;
}
@@ -208,7 +263,6 @@ fn main() {
}
}
-
let mut requests = Vec::with_capacity(num_requests);
for _ in 0..num_requests {
@@ -226,15 +280,26 @@ fn main() {
for (nonce, _, mut socket) in requests {
let resp = receive_response(&mut socket);
- let ParsedResponse {verified, midpoint, radius} = ResponseHandler::new(pub_key.clone(), resp.clone(), nonce).extract_time();
+ let ParsedResponse {
+ verified,
+ midpoint,
+ radius,
+ } = ResponseHandler::new(pub_key.clone(), resp.clone(), nonce).extract_time();
let map = resp.into_hash_map();
- let index = map[&Tag::INDX].as_slice().read_u32::<LittleEndian>().unwrap();
+ let index = map[&Tag::INDX]
+ .as_slice()
+ .read_u32::<LittleEndian>()
+ .unwrap();
let seconds = midpoint / 10_u64.pow(6);
- let spec = Utc.timestamp(seconds as i64, ((midpoint - (seconds * 10_u64.pow(6))) * 10_u64.pow(3)) as u32);
+ let nsecs = (midpoint - (seconds * 10_u64.pow(6))) * 10_u64.pow(3);
+ let spec = Utc.timestamp(seconds as i64, nsecs as u32);
let out = spec.format(time_format).to_string();
- println!("Recieved time from server: midpoint={:?}, radius={:?} (merkle_index={}, verified={})", out, radius, index, verified);
+ println!(
+ "Received time from server: midpoint={:?}, radius={:?} (merkle_index={}, verified={})",
+ out, radius, index, verified
+ );
}
}