summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStuart Stock <stuart@int08h.com>2017-06-22 16:34:53 -0500
committerStuart Stock <stuart@int08h.com>2017-06-22 16:34:53 -0500
commit830e0b9782ba0181a94c436761585ba47b25b0d7 (patch)
treeaf9a229b1a8ff62d83342b4a6418bb365d38b03b
parentdc17df4885cef546ed44d14a291ba782b82d93ed (diff)
downloadroughenough-830e0b9782ba0181a94c436761585ba47b25b0d7.zip
simplistic semi-working server
-rw-r--r--Cargo.toml1
-rw-r--r--src/bin/server.rs170
-rw-r--r--src/hex.rs111
-rw-r--r--src/lib.rs4
-rw-r--r--src/message.rs12
-rw-r--r--src/tag.rs23
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<Ed25519KeyPair, error::Unspecified> {
+ // 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<Ed25519KeyPair, error::Unspecified> {
+ 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<Vec<u8>, 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<u8> = Vec::with_capacity(4);
+ let mut midp: Vec<u8> = Vec::with_capacity(8);
+
+ // TODO: populate
+ let mut nonce = vec![0u8; 64];
+
+ // one second (in microseconds)
+ radi.write_u32::<LittleEndian>(1000000).unwrap();
+
+ // current epoch time in microseconds
+ let now = (time::get_time().sec as u64) * 1000;
+ midp.write_u64::<LittleEndian>(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(&lt_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<u8>` 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<Vec<u8>, 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<Tag>,
- values: Vec<&'a [u8]>,
+ values: Vec<Vec<u8>>,
}
-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