From 830e0b9782ba0181a94c436761585ba47b25b0d7 Mon Sep 17 00:00:00 2001 From: Stuart Stock Date: Thu, 22 Jun 2017 16:34:53 -0500 Subject: simplistic semi-working server --- Cargo.toml | 1 + src/bin/server.rs | 170 +++++++++++++++++++++++++++++++++++++++++------------- src/hex.rs | 111 ----------------------------------- src/lib.rs | 4 +- src/message.rs | 12 ++-- src/tag.rs | 23 ++------ 6 files changed, 144 insertions(+), 177 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 01e9df3..e0caede 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,4 @@ license = "Apache-2.0" byteorder = "1" ring = "0.11.0" untrusted = "0.5.0" +time = "0.1" diff --git a/src/bin/server.rs b/src/bin/server.rs index f62e4c2..ef1f46c 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -2,72 +2,77 @@ //! Roughtime server //! +extern crate byteorder; extern crate core; extern crate ring; -extern crate untrusted; extern crate roughenough; +extern crate time; +extern crate untrusted; + +use std::io; -use core::ptr; +use std::net::UdpSocket; +use std::time::Duration; use untrusted::Input; + use roughenough::{RtMessage, Tag, Error}; use roughenough::hex::*; +use roughenough::{CERTIFICATE_CONTEXT, SIGNED_RESPONSE_CONTEXT, TREE_LEAF_TWEAK}; -use ring::{digest, rand}; +use ring::{digest, error, rand}; use ring::rand::SecureRandom; use ring::signature::Ed25519KeyPair; -/// Zero all bytes in dst -#[inline] -pub fn zero(dst: &mut [u8]) { - unsafe { - ptr::write_bytes(dst.as_mut_ptr(), 0u8, dst.len()); - } -} - -fn main() { - // Read long-term key - let long_term_key = { - let mut seed = [b'x'; 32]; - - let lt_key = Ed25519KeyPair::from_seed_unchecked(Input::from(&seed)).unwrap(); - println!("Long-term public key: {}", lt_key.public_key_bytes().to_hex()); - - lt_key - }; +use byteorder::{LittleEndian, WriteBytesExt}; - // Create DELE - let ephemeral_key = { - let rng = rand::SystemRandom::new(); - let mut seed = [0u8; 32]; - rng.fill(&mut seed).unwrap(); +fn get_long_term_key() -> Result { + // TODO: read from config + let seed = [b'x'; 32]; + Ed25519KeyPair::from_seed_unchecked(Input::from(&seed)) +} - let eph_key = Ed25519KeyPair::from_seed_unchecked(Input::from(&seed)).unwrap(); - println!("Ephemeral public key: {}", eph_key.public_key_bytes().to_hex()); +fn make_ephemeral_key() -> Result { + let rng = rand::SystemRandom::new(); + let mut seed = [0u8; 32]; + rng.fill(&mut seed).unwrap(); - eph_key - }; + Ed25519KeyPair::from_seed_unchecked(Input::from(&seed)) +} +fn make_dele_bytes(ephemeral_key: &Ed25519KeyPair) -> Result, Error> { let zeros = [0u8; 8]; let max = [0xff; 8]; let mut dele_msg = RtMessage::new(3); - dele_msg.add_field(Tag::PUBK, &ephemeral_key.public_key_bytes()).unwrap(); - dele_msg.add_field(Tag::MINT, &zeros).unwrap(); - dele_msg.add_field(Tag::MAXT, &max).unwrap(); + dele_msg.add_field(Tag::PUBK, &ephemeral_key.public_key_bytes())?; + dele_msg.add_field(Tag::MINT, &zeros)?; + dele_msg.add_field(Tag::MAXT, &max)?; - let dele_bytes = dele_msg.encode().unwrap(); + dele_msg.encode() +} + +fn make_cert(long_term_key: &Ed25519KeyPair, ephemeral_key: &Ed25519KeyPair) -> RtMessage { + // Make DELE and sign it with long-term key + let dele_bytes = make_dele_bytes(&ephemeral_key).unwrap(); + let dele_sig = { + let mut sha_ctx = digest::Context::new(&digest::SHA512); + sha_ctx.update(CERTIFICATE_CONTEXT.as_bytes()); + sha_ctx.update(&dele_bytes); + let digest = sha_ctx.finish(); - println!("{}", dele_bytes.to_hex()); + long_term_key.sign(digest.as_ref()) + }; - // Sign it with long-term key // Create CERT + let mut cert_msg = RtMessage::new(2); + cert_msg.add_field(Tag::SIG, dele_sig.as_ref()).unwrap(); + cert_msg.add_field(Tag::DELE, &dele_bytes).unwrap(); - // Wipe long-term key + cert_msg +} - // loop: - // read request - // validate request or goto loop +fn make_response(ephemeral_key: &Ed25519KeyPair, cert_bytes: &[u8], request: &[u8]) -> RtMessage { // create SREP // sign SREP // create response: @@ -76,6 +81,91 @@ fn main() { // - SREP // - CERT (pre-created) // - INDX (always 0) + + let path = [0u8; 0]; + let zeros = [0u8; 4]; + + let mut radi: Vec = Vec::with_capacity(4); + let mut midp: Vec = Vec::with_capacity(8); + + // TODO: populate + let mut nonce = vec![0u8; 64]; + + // one second (in microseconds) + radi.write_u32::(1000000).unwrap(); + + // current epoch time in microseconds + let now = (time::get_time().sec as u64) * 1000; + midp.write_u64::(now).unwrap(); + + // Signed response SREP + let srep_bytes = { + // hash request nonce + let mut ctx = digest::Context::new(&digest::SHA512); + ctx.update(&TREE_LEAF_TWEAK); + ctx.update(&nonce); + let digest = ctx.finish(); + + let mut srep_msg = RtMessage::new(3); + srep_msg.add_field(Tag::RADI, &radi).unwrap(); + srep_msg.add_field(Tag::MIDP, &midp).unwrap(); + srep_msg.add_field(Tag::ROOT, digest.as_ref()).unwrap(); + + srep_msg.encode().unwrap() + }; + + // signature on SREP + let signature = { + let mut sha_ctx = digest::Context::new(&digest::SHA512); + sha_ctx.update(SIGNED_RESPONSE_CONTEXT.as_bytes()); + sha_ctx.update(&srep_bytes); + let digest = sha_ctx.finish(); + + ephemeral_key.sign(digest.as_ref()) + }; + + let mut response = RtMessage::new(5); + response.add_field(Tag::SIG, signature.as_ref()).unwrap(); + response.add_field(Tag::PATH, &path).unwrap(); + response.add_field(Tag::SREP, &srep_bytes).unwrap(); + response.add_field(Tag::CERT, cert_bytes).unwrap(); + response.add_field(Tag::INDX, &zeros).unwrap(); + + response +} + +fn main() { + let lt_key = get_long_term_key().expect("failed to obtain long-term key"); + let ephemeral_key = make_ephemeral_key().expect("failed to create ephemeral key"); + + println!("Long-term public key: {}", lt_key.public_key_bytes().to_hex()); + println!("Ephemeral public key: {}", ephemeral_key.public_key_bytes().to_hex()); + + let cert_msg = make_cert(<_key, &ephemeral_key); + let cert_bytes = cert_msg.encode().unwrap(); + + let mut socket = UdpSocket::bind("127.0.0.1:8686").expect("failed to bind to socket"); + socket.set_read_timeout(Some(Duration::from_secs(1))).expect("could not set read timeout"); + + let mut buf = [0u8; 65536]; + let mut loops = 0u64; + + loop { + match socket.recv_from(&mut buf) { + Ok((num_bytes, src_addr)) => { + println!("{} bytes from {}", num_bytes, src_addr); + let resp = make_response(&ephemeral_key, &cert_bytes, &buf[..num_bytes]); + println!("response {:?}", resp.encode().unwrap().to_hex()); + }, + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => loops += 1, + Err(ref e) => println!("Error {:?}: {:?}", e.kind(), e) + } + } + + + // loop: + // read request + // validate request or goto loop // send response } diff --git a/src/hex.rs b/src/hex.rs index c2b49e9..6e9ec9b 100644 --- a/src/hex.rs +++ b/src/hex.rs @@ -26,20 +26,6 @@ const CHARS: &'static [u8] = b"0123456789abcdef"; impl ToHex for [u8] { /// Turn a vector of `u8` bytes into a hexadecimal string. - /// - /// # Examples - /// - /// ``` - /// #![feature(rustc_private)] - /// - /// extern crate serialize; - /// use serialize::hex::ToHex; - /// - /// fn main () { - /// let str = [52,32].to_hex(); - /// println!("{}", str); - /// } - /// ``` fn to_hex(&self) -> String { let mut v = Vec::with_capacity(self.len() * 2); for &byte in self { @@ -96,25 +82,6 @@ impl FromHex for str { /// You can use the `String::from_utf8` function to turn a /// `Vec` into a string with characters corresponding to those values. /// - /// # Examples - /// - /// This converts a string literal to hexadecimal and back. - /// - /// ``` - /// #![feature(rustc_private)] - /// - /// extern crate serialize; - /// use serialize::hex::{FromHex, ToHex}; - /// - /// fn main () { - /// let hello_str = "Hello, World".as_bytes().to_hex(); - /// println!("{}", hello_str); - /// let bytes = hello_str.from_hex().unwrap(); - /// println!("{:?}", bytes); - /// let result_str = String::from_utf8(bytes).unwrap(); - /// println!("{}", result_str); - /// } - /// ``` fn from_hex(&self) -> Result, FromHexError> { // This may be an overestimate if there is any whitespace let mut b = Vec::with_capacity(self.len() / 2); @@ -152,81 +119,3 @@ impl FromHex for str { } } -#[cfg(test)] -mod tests { - extern crate test; - use self::test::Bencher; - use hex::{FromHex, ToHex}; - - #[test] - pub fn test_to_hex() { - assert_eq!("foobar".as_bytes().to_hex(), "666f6f626172"); - } - - #[test] - pub fn test_from_hex_okay() { - assert_eq!("666f6f626172".from_hex().unwrap(), - b"foobar"); - assert_eq!("666F6F626172".from_hex().unwrap(), - b"foobar"); - } - - #[test] - pub fn test_from_hex_odd_len() { - assert!("666".from_hex().is_err()); - assert!("66 6".from_hex().is_err()); - } - - #[test] - pub fn test_from_hex_invalid_char() { - assert!("66y6".from_hex().is_err()); - } - - #[test] - pub fn test_from_hex_ignores_whitespace() { - assert_eq!("666f 6f6\r\n26172 ".from_hex().unwrap(), - b"foobar"); - } - - #[test] - pub fn test_to_hex_all_bytes() { - for i in 0..256 { - assert_eq!([i as u8].to_hex(), format!("{:02x}", i as usize)); - } - } - - #[test] - pub fn test_from_hex_all_bytes() { - for i in 0..256 { - let ii: &[u8] = &[i as u8]; - assert_eq!(format!("{:02x}", i as usize).from_hex() - .unwrap(), - ii); - assert_eq!(format!("{:02X}", i as usize).from_hex() - .unwrap(), - ii); - } - } - - #[bench] - pub fn bench_to_hex(b: &mut Bencher) { - let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \ - ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン"; - b.iter(|| { - s.as_bytes().to_hex(); - }); - b.bytes = s.len() as u64; - } - - #[bench] - pub fn bench_from_hex(b: &mut Bencher) { - let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \ - ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン"; - let sb = s.as_bytes().to_hex(); - b.iter(|| { - sb.from_hex().unwrap(); - }); - b.bytes = sb.len() as u64; - } -} - diff --git a/src/lib.rs b/src/lib.rs index 4a6d2fa..96cbd46 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,7 +45,7 @@ pub use message::RtMessage; pub const SIGNED_RESPONSE_CONTEXT: &str = "RoughTime v1 response signature\x00"; /// Value prepended to leaves prior to hashing - pub const TREE_LEAF_TWEAK: u8 = 0x00; + pub const TREE_LEAF_TWEAK: &[u8] = &[0x00]; /// Value prepended to nodes prior to hashing - pub const TREE_NODE_TWEAK: u8 = 0x01; + pub const TREE_NODE_TWEAK: &[u8] = &[0x01]; diff --git a/src/message.rs b/src/message.rs index 73db76a..996550f 100644 --- a/src/message.rs +++ b/src/message.rs @@ -9,12 +9,12 @@ use error::Error; /// A Roughtime protocol message; a map of u32 tags to arbitrary byte-strings. /// #[derive(Debug)] -pub struct RtMessage<'a> { +pub struct RtMessage { tags: Vec, - values: Vec<&'a [u8]>, + values: Vec>, } -impl<'a> RtMessage<'a> { +impl RtMessage { /// Construct a new RtMessage /// @@ -38,7 +38,7 @@ impl<'a> RtMessage<'a> { /// /// * `value` - Value for the tag. /// - pub fn add_field(&mut self, tag: Tag, value: &'a [u8]) -> Result<(), Error> { + pub fn add_field(&mut self, tag: Tag, value: &[u8]) -> Result<(), Error> { if let Some(last_tag) = self.tags.last() { if tag <= *last_tag { return Err(Error::TagNotStrictlyIncreasing(tag)); @@ -46,7 +46,7 @@ impl<'a> RtMessage<'a> { } self.tags.push(tag); - self.values.push(value); + self.values.push(value.to_vec()); Ok(()) } @@ -95,7 +95,7 @@ impl<'a> RtMessage<'a> { let num_tags = self.tags.len(); let tags_size = 4 * num_tags; let offsets_size = if num_tags < 2 { 0 } else { 4 * (num_tags - 1) }; - let values_size: usize = self.values.iter().map(|&v| v.len()).sum(); + let values_size: usize = self.values.iter().map(|ref v| v.len()).sum(); 4 + tags_size + offsets_size + values_size } diff --git a/src/tag.rs b/src/tag.rs index b50f5f5..4e3a8f1 100644 --- a/src/tag.rs +++ b/src/tag.rs @@ -4,26 +4,13 @@ pub enum Tag { // Enforcement of the "tags in strictly increasing order" rule is done using the // little-endian encoding of the ASCII tag value; e.g. 'SIG\x00' is 0x00474953 and - // 'NONC' is 0x434e4f4e. The tags are listed here is reverse lexical order which - // matches the little-endian comparison order. - SREP, - SIG, - ROOT, - RADI, - PUBK, - PATH, - PAD, - NONC, - MINT, - MIDP, - MAXT, - INDX, - DELE, - CERT, + // 'NONC' is 0x434e4f4e. + + SIG, NONC, DELE, PATH, RADI, PUBK, MIDP, SREP, MINT, ROOT, CERT, MAXT, INDX, PAD } -static PAD_VALUE: [u8; 4] = [b'P', b'A', b'D', 0x00]; -static SIG_VALUE: [u8; 4] = [b'S', b'I', b'G', 0xff]; +static PAD_VALUE: [u8; 4] = [b'P', b'A', b'D', 0xff]; +static SIG_VALUE: [u8; 4] = [b'S', b'I', b'G', 0x00]; impl Tag { /// Translates a tag into its on-the-wire representation -- cgit v1.2.3