summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStuart Stock <stuart@int08h.com>2018-03-17 05:28:59 -0500
committerStuart Stock <stuart@int08h.com>2018-03-17 05:36:13 -0500
commitf80c952f6f25b09946a288a51be8016849957553 (patch)
tree0e0751ce86b8c8b9dacbc420219105be387787a5
parent583bedf5e271cd2bd971118fab26c6976271afd6 (diff)
downloadroughenough-f80c952f6f25b09946a288a51be8016849957553.zip
Apply default rustfmt style to the project.
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--benches/messages.rs199
-rw-r--r--src/bin/client.rs213
-rw-r--r--src/bin/server.rs180
-rw-r--r--src/error.rs1
-rw-r--r--src/lib.rs12
-rw-r--r--src/merkle.rs26
-rw-r--r--src/message.rs32
-rw-r--r--src/sign.rs40
-rw-r--r--src/tag.rs4
10 files changed, 406 insertions, 303 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e2a8c77..789d778 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -10,7 +10,7 @@ Please open a pull request (PR) for your changes and include:
* An overall description/rationale of the PR
* Tests for any new functionality
-* Code style that matches what is already in the repo
+* Code formatted with `rustfmt` default style settings
* License (Apache 2.0) and copyright statements for your code
* A willingness to iterate and make changes ;)
diff --git a/benches/messages.rs b/benches/messages.rs
index bf24319..4523859 100644
--- a/benches/messages.rs
+++ b/benches/messages.rs
@@ -1,141 +1,138 @@
+extern crate byteorder;
#[macro_use]
extern crate criterion;
-extern crate roughenough;
extern crate ring;
-extern crate byteorder;
+extern crate roughenough;
extern crate time;
use ring::digest;
use ring::rand::SecureRandom;
use byteorder::{LittleEndian, WriteBytesExt};
-use criterion::{Criterion, black_box};
+use criterion::{black_box, Criterion};
use roughenough::{RtMessage, Tag};
use roughenough::{SIGNED_RESPONSE_CONTEXT, TREE_LEAF_TWEAK};
use roughenough::sign::Signer;
fn single_field(data: &[u8]) -> RtMessage {
- let mut msg = RtMessage::new(1);
- msg.add_field(Tag::NONC, &data[0..63]).unwrap();
- msg
+ let mut msg = RtMessage::new(1);
+ msg.add_field(Tag::NONC, &data[0..63]).unwrap();
+ msg
}
fn two_fields(data: &[u8]) -> RtMessage {
- let mut msg = RtMessage::new(2);
- msg.add_field(Tag::NONC, &data[0..63]).unwrap();
- msg.add_field(Tag::PAD, &data[64..127]).unwrap();
- msg
+ let mut msg = RtMessage::new(2);
+ msg.add_field(Tag::NONC, &data[0..63]).unwrap();
+ msg.add_field(Tag::PAD, &data[64..127]).unwrap();
+ msg
}
// Full-sized server response
fn five_fields(data: &[u8]) -> RtMessage {
- let mut response = RtMessage::new(5);
- response.add_field(Tag::SIG, &data[0..63]).unwrap();
- response.add_field(Tag::PATH, &data[64..127]).unwrap();
- response.add_field(Tag::SREP, &data[128..191]).unwrap();
- response.add_field(Tag::CERT, &data[192..255]).unwrap();
- response.add_field(Tag::INDX, &data[256..319]).unwrap();
-
- response
+ let mut response = RtMessage::new(5);
+ response.add_field(Tag::SIG, &data[0..63]).unwrap();
+ response.add_field(Tag::PATH, &data[64..127]).unwrap();
+ response.add_field(Tag::SREP, &data[128..191]).unwrap();
+ response.add_field(Tag::CERT, &data[192..255]).unwrap();
+ response.add_field(Tag::INDX, &data[256..319]).unwrap();
+
+ response
}
/// Random data for composing a response
fn make_data(len: usize) -> Vec<u8> {
- let rng = ring::rand::SystemRandom::new();
- let mut tmp = vec![1u8; len];
- rng.fill(&mut tmp).unwrap();
+ let rng = ring::rand::SystemRandom::new();
+ let mut tmp = vec![1u8; len];
+ rng.fill(&mut tmp).unwrap();
- tmp
+ tmp
}
fn message_creation(c: &mut Criterion) {
- let data = make_data(320);
-
- // TODO(stuart) .to_vec()'s below because I'm fighting the borrow-checker.
- // Tried iter_with_setup(), but haven't found the right combo of 'move'
- // and reference lifetime(s) yet.
-
- let d1 = data.to_vec();
- c.bench_function("single field",
- move |b| b.iter(|| black_box(single_field(&d1))),
- );
-
- let d2 = data.to_vec();
- c.bench_function("two fields",
- move |b| b.iter(|| black_box(two_fields(&d2))),
- );
-
- let d3 = data.to_vec();
- c.bench_function("five fields",
- move |b| b.iter(|| black_box(five_fields(&d3))),
- );
+ let data = make_data(320);
+
+ // TODO(stuart) .to_vec()'s below because I'm fighting the borrow-checker.
+ // Tried iter_with_setup(), but haven't found the right combo of 'move'
+ // and reference lifetime(s) yet.
+
+ let d1 = data.to_vec();
+ c.bench_function("single field", move |b| {
+ b.iter(|| black_box(single_field(&d1)))
+ });
+
+ let d2 = data.to_vec();
+ c.bench_function("two fields", move |b| b.iter(|| black_box(two_fields(&d2))));
+
+ let d3 = data.to_vec();
+ c.bench_function("five fields", move |b| {
+ b.iter(|| black_box(five_fields(&d3)))
+ });
}
/// TODO(stuart) straight-up copied form src/bin/server.rs, not ideal at all
fn make_response(ephemeral_key: &mut Signer, cert_bytes: &[u8], nonce: &[u8]) -> RtMessage {
- 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);
-
- // one second (in microseconds)
- radi.write_u32::<LittleEndian>(1_000_000).unwrap();
-
- // current epoch time in microseconds
- let now = {
- let tv = time::get_time();
- let secs = (tv.sec as u64) * 1_000_000;
- let nsecs = (tv.nsec as u64) / 1_000;
-
- secs + nsecs
- };
- 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 srep_signature = {
- ephemeral_key.update(SIGNED_RESPONSE_CONTEXT.as_bytes());
- ephemeral_key.update(&srep_bytes);
- ephemeral_key.sign()
- };
-
- let mut response = RtMessage::new(5);
- response.add_field(Tag::SIG, &srep_signature).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
+ 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);
+
+ // one second (in microseconds)
+ radi.write_u32::<LittleEndian>(1_000_000).unwrap();
+
+ // current epoch time in microseconds
+ let now = {
+ let tv = time::get_time();
+ let secs = (tv.sec as u64) * 1_000_000;
+ let nsecs = (tv.nsec as u64) / 1_000;
+
+ secs + nsecs
+ };
+ 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 srep_signature = {
+ ephemeral_key.update(SIGNED_RESPONSE_CONTEXT.as_bytes());
+ ephemeral_key.update(&srep_bytes);
+ ephemeral_key.sign()
+ };
+
+ let mut response = RtMessage::new(5);
+ response.add_field(Tag::SIG, &srep_signature).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 response_creation(c: &mut Criterion) {
- let nonce = make_data(64);
- let cert = make_data(152);
- let seed = make_data(32);
- let mut signer = Signer::new(&seed);
-
- c.bench_function("server response",
- move |b| b.iter(|| black_box(make_response(&mut signer, &cert, &nonce))),
- );
+ let nonce = make_data(64);
+ let cert = make_data(152);
+ let seed = make_data(32);
+ let mut signer = Signer::new(&seed);
+
+ c.bench_function("server response", move |b| {
+ b.iter(|| black_box(make_response(&mut signer, &cert, &nonce)))
+ });
}
criterion_group!(benches, message_creation, response_creation);
criterion_main!(benches);
-
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
+ );
}
}
diff --git a/src/bin/server.rs b/src/bin/server.rs
index c38066a..18ebdaf 100644
--- a/src/bin/server.rs
+++ b/src/bin/server.rs
@@ -29,7 +29,7 @@
//!
//! * **interface** - IP address or interface name for listening to client requests
//! * **port** - UDP port to listen to requests
-//! * **seed** - A 32-byte hexadecimal value used as the seed to generate the
+//! * **seed** - A 32-byte hexadecimal value used as the seed to generate the
//! server's long-term key pair. **This is a secret value**, treat it
//! with care.
//! * **batch_size** - The number of requests to process in one batch. All nonces
@@ -43,37 +43,37 @@
//! ```
extern crate byteorder;
-extern crate ring;
-extern crate roughenough;
-extern crate time;
-extern crate untrusted;
extern crate ctrlc;
-extern crate yaml_rust;
+extern crate hex;
#[macro_use]
extern crate log;
-extern crate simple_logger;
extern crate mio;
extern crate mio_extras;
-extern crate hex;
+extern crate ring;
+extern crate roughenough;
+extern crate simple_logger;
+extern crate time;
+extern crate untrusted;
+extern crate yaml_rust;
use std::env;
use std::process;
use std::fs::File;
-use std::io::{Read, ErrorKind};
+use std::io::{ErrorKind, Read};
use std::time::Duration;
use std::net::SocketAddr;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::thread;
-use mio::{Poll, Token, Ready, PollOpt, Events};
+use mio::{Events, Poll, PollOpt, Ready, Token};
use mio::net::UdpSocket;
use mio_extras::timer::Timer;
use byteorder::{LittleEndian, WriteBytesExt};
-use roughenough::{RtMessage, Tag, Error};
-use roughenough::{VERSION, CERTIFICATE_CONTEXT, MIN_REQUEST_LENGTH, SIGNED_RESPONSE_CONTEXT};
+use roughenough::{Error, RtMessage, Tag};
+use roughenough::{CERTIFICATE_CONTEXT, MIN_REQUEST_LENGTH, SIGNED_RESPONSE_CONTEXT, VERSION};
use roughenough::sign::Signer;
use roughenough::merkle::*;
@@ -109,8 +109,14 @@ fn make_key_and_cert(seed: &[u8]) -> (Signer, Vec<u8>) {
let mut long_term_key = Signer::new(seed);
let ephemeral_key = create_ephemeral_key();
- info!("Long-term public key: {}", hex::encode(long_term_key.public_key_bytes()));
- info!("Ephemeral public key: {}", hex::encode(ephemeral_key.public_key_bytes()));
+ info!(
+ "Long-term public key: {}",
+ hex::encode(long_term_key.public_key_bytes())
+ );
+ info!(
+ "Ephemeral public key: {}",
+ hex::encode(ephemeral_key.public_key_bytes())
+ );
// Make DELE and sign it with long-term key
let dele_bytes = make_dele_bytes(&ephemeral_key).unwrap();
@@ -134,26 +140,17 @@ fn make_key_and_cert(seed: &[u8]) -> (Signer, Vec<u8>) {
struct SRep {
raw_bytes: Vec<u8>,
- signature: Vec<u8>
+ signature: Vec<u8>,
}
fn make_srep(ephemeral_key: &mut Signer, root: &[u8]) -> SRep {
- // create SREP
- // sign SREP
- // create response:
- // - SIG
- // - PATH (always 0)
- // - SREP
- // - CERT (pre-created)
- // - INDX (always 0)
-
-
let mut radi = [0; 4];
let mut midp = [0; 8];
-
// one second (in microseconds)
- (&mut radi as &mut [u8]).write_u32::<LittleEndian>(1_000_000).unwrap();
+ (&mut radi as &mut [u8])
+ .write_u32::<LittleEndian>(1_000_000)
+ .unwrap();
// current epoch time in microseconds
let now = {
@@ -163,7 +160,9 @@ fn make_srep(ephemeral_key: &mut Signer, root: &[u8]) -> SRep {
secs + nsecs
};
- (&mut midp as &mut [u8]).write_u64::<LittleEndian>(now).unwrap();
+ (&mut midp as &mut [u8])
+ .write_u64::<LittleEndian>(now)
+ .unwrap();
// Signed response SREP
let srep_bytes = {
@@ -184,15 +183,15 @@ fn make_srep(ephemeral_key: &mut Signer, root: &[u8]) -> SRep {
SRep {
raw_bytes: srep_bytes,
- signature: srep_signature
+ signature: srep_signature,
}
}
fn make_response(srep: &SRep, cert_bytes: &[u8], path: &[u8], idx: u32) -> RtMessage {
-
let mut index = [0; 4];
- (&mut index as &mut [u8]).write_u32::<LittleEndian>(idx).unwrap();
-
+ (&mut index as &mut [u8])
+ .write_u32::<LittleEndian>(idx)
+ .unwrap();
let mut response = RtMessage::new(5);
response.add_field(Tag::SIG, &srep.signature).unwrap();
@@ -226,15 +225,14 @@ fn nonce_from_request(buf: &[u8], num_bytes: usize) -> Result<&[u8], Error> {
}
fn load_config(config_file: &str) -> (SocketAddr, Vec<u8>, u8) {
- let mut infile = File::open(config_file)
- .expect("failed to open config file");
+ let mut infile = File::open(config_file).expect("failed to open config file");
let mut contents = String::new();
- infile.read_to_string(&mut contents)
+ infile
+ .read_to_string(&mut contents)
.expect("could not read config file");
- let cfg = YamlLoader::load_from_str(&contents)
- .expect("could not parse config file");
+ let cfg = YamlLoader::load_from_str(&contents).expect("could not parse config file");
if cfg.len() != 1 {
panic!("empty or malformed config file");
@@ -251,7 +249,7 @@ fn load_config(config_file: &str) -> (SocketAddr, Vec<u8>, u8) {
"interface" => iface = value.as_str().unwrap().to_string(),
"seed" => seed = value.as_str().unwrap().to_string(),
"batch_size" => batch_size = value.as_i64().unwrap() as u8,
- _ => warn!("ignoring unknown config key '{}'", key.as_str().unwrap())
+ _ => warn!("ignoring unknown config key '{}'", key.as_str().unwrap()),
}
}
@@ -259,13 +257,19 @@ fn load_config(config_file: &str) -> (SocketAddr, Vec<u8>, u8) {
let sock_addr: SocketAddr = addr.parse()
.expect(&format!("could not create socket address from {}", addr));
- let binseed = hex::decode(seed)
- .expect("seed value invalid; 'seed' should be 32 byte hex value");
+ let binseed =
+ hex::decode(seed).expect("seed value invalid; 'seed' should be 32 byte hex value");
(sock_addr, binseed, batch_size)
}
-fn polling_loop(addr: &SocketAddr, mut ephemeral_key: &mut Signer, cert_bytes: &[u8], batch_size: u8, response_counter: Arc<AtomicUsize>) {
+fn polling_loop(
+ addr: &SocketAddr,
+ mut ephemeral_key: &mut Signer,
+ cert_bytes: &[u8],
+ batch_size: u8,
+ response_counter: Arc<AtomicUsize>,
+) {
let keep_running = Arc::new(AtomicBool::new(true));
let kr = keep_running.clone();
@@ -279,26 +283,27 @@ fn polling_loop(addr: &SocketAddr, mut ephemeral_key: &mut Signer, cert_bytes: &
let mut timer: Timer<()> = Timer::default();
timer.set_timeout(status_duration, ());
-
let mut buf = [0u8; 65_536];
let mut events = Events::with_capacity(32);
let mut num_bad_requests = 0u64;
let poll = Poll::new().unwrap();
- poll.register(&socket, MESSAGE, Ready::readable(), PollOpt::edge()).unwrap();
- poll.register(&timer, STATUS, Ready::readable(), PollOpt::edge()).unwrap();
+ poll.register(&socket, MESSAGE, Ready::readable(), PollOpt::edge())
+ .unwrap();
+ poll.register(&timer, STATUS, Ready::readable(), PollOpt::edge())
+ .unwrap();
let mut merkle = MerkleTree::new();
let mut requests = Vec::with_capacity(batch_size as usize);
macro_rules! check_ctrlc {
- () => {
- if !keep_running.load(Ordering::Acquire) {
- warn!("Ctrl-C caught, exiting...");
- return;
- }
+ () => {
+ if !keep_running.load(Ordering::Acquire) {
+ warn!("Ctrl-C caught, exiting...");
+ return;
}
}
+ }
loop {
check_ctrlc!();
@@ -306,10 +311,8 @@ fn polling_loop(addr: &SocketAddr, mut ephemeral_key: &mut Signer, cert_bytes: &
poll.poll(&mut events, poll_duration).expect("poll failed");
for event in events.iter() {
-
match event.token() {
MESSAGE => {
-
let mut done = false;
'process_batch: loop {
@@ -328,24 +331,31 @@ fn polling_loop(addr: &SocketAddr, mut ephemeral_key: &mut Signer, cert_bytes: &
merkle.push_leaf(nonce);
} else {
num_bad_requests += 1;
- info!("Invalid request ({} bytes) from {} (#{} in batch, resp #{})", num_bytes, src_addr, i, resp_start + i as usize);
+ info!(
+ "Invalid request ({} bytes) from {} (#{} in batch, resp #{})",
+ num_bytes, src_addr, i, resp_start + i as usize
+ );
}
- },
+ }
Err(e) => match e.kind() {
ErrorKind::WouldBlock => {
done = true;
break;
- },
+ }
_ => {
- error!("Error receiving from socket: {:?}: {:?}", e.kind(), e);
- break
+ error!(
+ "Error receiving from socket: {:?}: {:?}",
+ e.kind(),
+ e
+ );
+ break;
}
- }
+ },
};
}
if requests.is_empty() {
- break 'process_batch
+ break 'process_batch;
}
let root = merkle.compute_root();
@@ -357,24 +367,37 @@ fn polling_loop(addr: &SocketAddr, mut ephemeral_key: &mut Signer, cert_bytes: &
let resp = make_response(&srep, cert_bytes, &paths, i as u32);
let resp_bytes = resp.encode().unwrap();
- let bytes_sent = socket.send_to(&resp_bytes, &src_addr).expect("send_to failed");
+ let bytes_sent = socket
+ .send_to(&resp_bytes, &src_addr)
+ .expect("send_to failed");
let num_responses = response_counter.fetch_add(1, Ordering::SeqCst);
- info!("Responded {} bytes to {} for '{}..' (#{} in batch, resp #{})", bytes_sent, src_addr, hex::encode(&nonce[0..4]), i, num_responses);
+ info!(
+ "Responded {} bytes to {} for '{}..' (#{} in batch, resp #{})",
+ bytes_sent,
+ src_addr,
+ hex::encode(&nonce[0..4]),
+ i,
+ num_responses
+ );
}
if done {
- break 'process_batch
+ break 'process_batch;
}
}
-
}
STATUS => {
- info!("responses {}, invalid requests {}", response_counter.load(Ordering::SeqCst), num_bad_requests);
+ info!(
+ "responses {}, invalid requests {}",
+ response_counter.load(Ordering::SeqCst),
+ num_bad_requests
+ );
+
timer.set_timeout(status_duration, ());
}
- _ => unreachable!()
+ _ => unreachable!(),
}
}
}
@@ -400,26 +423,33 @@ pub fn main() {
let response_counter = Arc::new(AtomicUsize::new(0));
- if env::var("BENCH").is_ok() {
+ if env::var("BENCH").is_ok() {
log::set_max_level(log::LevelFilter::Warn);
let response_counter = response_counter.clone();
- thread::spawn(move || {
- loop {
- let old = time::get_time().sec;
- let old_reqs = response_counter.load(Ordering::SeqCst);
+ thread::spawn(move || loop {
+ let old = time::get_time().sec;
+ let old_reqs = response_counter.load(Ordering::SeqCst);
- thread::sleep(Duration::from_secs(1));
+ thread::sleep(Duration::from_secs(1));
- let new = time::get_time().sec;
- let new_reqs = response_counter.load(Ordering::SeqCst);
+ let new = time::get_time().sec;
+ let new_reqs = response_counter.load(Ordering::SeqCst);
- warn!("Processing at {:?} reqs/sec", (new_reqs - old_reqs) / (new - old) as usize);
- }
+ warn!(
+ "Processing at {:?} reqs/sec",
+ (new_reqs - old_reqs) / (new - old) as usize
+ );
});
}
- polling_loop(&addr, &mut ephemeral_key, &cert_bytes, batch_size, response_counter.clone());
+ polling_loop(
+ &addr,
+ &mut ephemeral_key,
+ &cert_bytes,
+ batch_size,
+ response_counter.clone(),
+ );
info!("Done.");
process::exit(0);
diff --git a/src/error.rs b/src/error.rs
index decc0bc..98f337e 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -22,6 +22,7 @@ pub enum Error {
/// The associated tag was added to an `RtMessage` in non-increasing order.
TagNotStrictlyIncreasing(Tag),
+ /// The associated byte sequence does not correspond to a valid Roughtime tag.
InvalidTag(Box<[u8]>),
/// Encoding failed. The associated `std::io::Error` should provide more information.
diff --git a/src/lib.rs b/src/lib.rs
index 1c3650f..4814398 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -16,13 +16,13 @@
//! An implementation of the [Roughtime](https://roughtime.googlesource.com/roughtime)
//! secure time synchronization protocol.
//!
-//! Roughtime aims to achieve rough time synchronisation in a secure way that doesn't
-//! depend on any particular time server, and in such a way that, if a time server does
-//! misbehave, clients end up with cryptographic proof of it.
+//! Roughtime aims to achieve rough time synchronisation in a secure way that doesn't
+//! depend on any particular time server, and in such a way that, if a time server does
+//! misbehave, clients end up with cryptographic proof of it.
//!
//! # Protocol
//!
-//! Roughtime messages are represetned by [`RtMessage`](struct.RtMessage.html) which
+//! Roughtime messages are represetned by [`RtMessage`](struct.RtMessage.html) which
//! implements the mapping of Roughtime `u32` [`tags`](enum.Tag.html) to byte-strings.
//!
//! # Client
@@ -37,7 +37,7 @@
//!
//! # Server
//!
-//! A Roughtime server implementation is in `src/bin/server.rs`. The server is
+//! A Roughtime server implementation is in `src/bin/server.rs`. The server is
//! configured via a yaml file:
//!
//! ```yaml
@@ -51,7 +51,7 @@
//!
//! * **interface** - IP address or interface name for listening to client requests
//! * **port** - UDP port to listen to requests
-//! * **seed** - A 32-byte hexadecimal value used as the seed to generate the
+//! * **seed** - A 32-byte hexadecimal value used as the seed to generate the
//! server's long-term key pair. **This is a secret value**, treat it
//! with care.
//! * **batch_size** - The number of requests to process in one batch. All nonces
diff --git a/src/merkle.rs b/src/merkle.rs
index 4bb4abd..d55280f 100644
--- a/src/merkle.rs
+++ b/src/merkle.rs
@@ -14,7 +14,7 @@
extern crate ring;
-use super::{TREE_LEAF_TWEAK, TREE_NODE_TWEAK, HASH_LENGTH};
+use super::{HASH_LENGTH, TREE_LEAF_TWEAK, TREE_NODE_TWEAK};
use self::ring::digest;
type Data = Vec<u8>;
@@ -27,7 +27,7 @@ pub struct MerkleTree {
impl MerkleTree {
pub fn new() -> MerkleTree {
MerkleTree {
- levels: vec![vec![]]
+ levels: vec![vec![]],
}
}
@@ -41,11 +41,7 @@ impl MerkleTree {
let mut level = 0;
while !self.levels[level].is_empty() {
- let sibling = if index % 2 == 0 {
- index + 1
- } else {
- index - 1
- };
+ let sibling = if index % 2 == 0 { index + 1 } else { index - 1 };
paths.extend(self.levels[level][sibling].clone());
level += 1;
@@ -55,10 +51,14 @@ impl MerkleTree {
}
pub fn compute_root(&mut self) -> Hash {
- assert!(self.levels[0].len() > 0, "Must have at least one leaf to hash!");
+ assert!(
+ self.levels[0].len() > 0,
+ "Must have at least one leaf to hash!"
+ );
let mut level = 0;
let mut node_count = self.levels[0].len();
+
while node_count > 1 {
level += 1;
@@ -74,10 +74,14 @@ impl MerkleTree {
node_count /= 2;
for i in 0..node_count {
- let hash = self.hash_nodes(&self.levels[level - 1][i*2], &self.levels[level - 1][(i*2)+1]);
+ let hash = self.hash_nodes(
+ &self.levels[level - 1][i * 2],
+ &self.levels[level - 1][(i * 2) + 1],
+ );
self.levels[level].push(hash);
}
}
+
assert_eq!(self.levels[level].len(), 1);
self.levels[level].pop().unwrap()
}
@@ -128,16 +132,16 @@ pub fn root_from_paths(mut index: usize, data: &[u8], paths: &[u8]) -> Hash {
ctx.update(path);
ctx.update(&hash);
}
- hash = Hash::from(ctx.finish().as_ref());
+ hash = Hash::from(ctx.finish().as_ref());
index >>= 1;
}
+
hash
}
#[cfg(test)]
mod test {
-
use merkle::*;
fn test_paths_with_num(num: usize) {
diff --git a/src/message.rs b/src/message.rs
index 8a2e1b6..11cad2b 100644
--- a/src/message.rs
+++ b/src/message.rs
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use std::io::{Read, Write, Cursor};
+use std::io::{Cursor, Read, Write};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::iter::once;
use std::collections::HashMap;
@@ -51,28 +51,27 @@ impl RtMessage {
if num_tags == 1 {
let pos = msg.position() as usize;
- let tag = Tag::from_wire(&bytes[pos..pos+4])?;
+ let tag = Tag::from_wire(&bytes[pos..pos + 4])?;
msg.set_position((pos + 4) as u64);
let mut value = Vec::new();
-
+
msg.read_to_end(&mut value).unwrap();
rt_msg.add_field(tag, &value)?;
- return Ok(rt_msg)
+ return Ok(rt_msg);
}
let mut offsets = Vec::with_capacity((num_tags - 1) as usize);
let mut tags = Vec::with_capacity(num_tags as usize);
for _ in 0..num_tags - 1 {
- let offset = msg.read_u32::<LittleEndian>()?;
+ let offset = msg.read_u32::<LittleEndian>()?;
if offset % 4 != 0 {
panic!("Invalid offset {:?} in message {:?}", offset, bytes);
}
offsets.push(offset as usize);
}
-
let mut buf = [0; 4];
for _ in 0..num_tags {
msg.read_exact(&mut buf).unwrap();
@@ -80,7 +79,7 @@ impl RtMessage {
if let Some(last_tag) = tags.last() {
if tag <= *last_tag {
- return Err(Error::TagNotStrictlyIncreasing(tag))
+ return Err(Error::TagNotStrictlyIncreasing(tag));
}
}
tags.push(tag);
@@ -96,11 +95,10 @@ impl RtMessage {
assert_eq!(offsets.len(), tags.len() - 1);
for (tag, (value_start, value_end)) in tags.into_iter().zip(
- once(&0).chain(offsets.iter()).zip(
- offsets.iter().chain(once(&msg_end))
- )
+ once(&0)
+ .chain(offsets.iter())
+ .zip(offsets.iter().chain(once(&msg_end))),
) {
-
let value = bytes[(header_end + value_start)..(header_end + value_end)].to_vec();
rt_msg.add_field(tag, &value)?;
}
@@ -111,8 +109,8 @@ impl RtMessage {
///
/// ## Arguments
///
- /// * `tag` - The [`Tag`](enum.Tag.html) to add. Tags must be added in **strictly
- /// increasing order**, violating this will result in a
+ /// * `tag` - The [`Tag`](enum.Tag.html) to add. Tags must be added in **strictly
+ /// increasing order**, violating this will result in a
/// [`Error::TagNotStrictlyIncreasing`](enum.Error.html).
///
/// * `value` - Value for the tag.
@@ -193,7 +191,7 @@ impl RtMessage {
/// Adds a PAD tag to the end of this message, with a length
/// set such that the final encoded size of this message is 1KB
- ///
+ ///
/// If the encoded size of this message is already >= 1KB,
/// this method does nothing
pub fn pad_to_kilobyte(&mut self) {
@@ -313,8 +311,10 @@ mod test {
assert_eq!(encoded.read_u32::<LittleEndian>().unwrap(), 2);
// Offset past DELE value to start of MAXT value
- assert_eq!(encoded.read_u32::<LittleEndian>().unwrap(),
- dele_value.len() as u32);
+ assert_eq!(
+ encoded.read_u32::<LittleEndian>().unwrap(),
+ dele_value.len() as u32
+ );
// DELE tag
let mut dele = [0u8; 4];
diff --git a/src/sign.rs b/src/sign.rs
index d7ef156..a990a2f 100644
--- a/src/sign.rs
+++ b/src/sign.rs
@@ -15,19 +15,19 @@
//! Ed25519 signing and verification
//!
//! `Ring` does not provide a multi-step (init-update-finish) interface
-//! for Ed25519 signatures. `Verifier` and `Signer` provide this
+//! for Ed25519 signatures. `Verifier` and `Signer` provide this
//! missing multi-step api.
+extern crate hex;
extern crate ring;
extern crate untrusted;
-extern crate hex;
use self::ring::signature;
use self::ring::signature::Ed25519KeyPair;
use self::untrusted::Input;
-/// A multi-step (init-update-finish) interface for verifying an
+/// A multi-step (init-update-finish) interface for verifying an
/// Ed25519 signature
#[derive(Debug)]
pub struct Verifier<'a> {
@@ -59,7 +59,7 @@ impl<'a> Verifier<'a> {
}
}
-/// A multi-step (init-update-finish) interface for creating an
+/// A multi-step (init-update-finish) interface for creating an
/// Ed25519 signature
pub struct Signer {
key_pair: Ed25519KeyPair,
@@ -91,17 +91,20 @@ impl Signer {
}
}
+#[cfg_attr(rustfmt, rustfmt_skip)] // rustfmt errors on the long signature strings
#[cfg(test)]
mod test {
use super::*;
#[test]
fn verify_ed25519_sig_on_empty_message() {
- let pubkey = hex::decode("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a")
- .unwrap();
+ let pubkey = hex::decode(
+ "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
+ ).unwrap();
- let signature = hex::decode("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b")
- .unwrap();
+ let signature = hex::decode(
+ "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"
+ ).unwrap();
let v = Verifier::new(&pubkey);
let result = v.verify(&signature);
@@ -110,13 +113,15 @@ mod test {
#[test]
fn verify_ed25519_sig() {
- let pubkey = hex::decode("c0dac102c4533186e25dc43128472353eaabdb878b152aeb8e001f92d90233a7")
- .unwrap();
+ let pubkey = hex::decode(
+ "c0dac102c4533186e25dc43128472353eaabdb878b152aeb8e001f92d90233a7",
+ ).unwrap();
let message = hex::decode("5f4c8989").unwrap();
- let signature = hex::decode("124f6fc6b0d100842769e71bd530664d888df8507df6c56dedfdb509aeb93416e26b918d38aa06305df3095697c18b2aa832eaa52edc0ae49fbae5a85e150c07")
- .unwrap();
+ let signature = hex::decode(
+ "124f6fc6b0d100842769e71bd530664d888df8507df6c56dedfdb509aeb93416e26b918d38aa06305df3095697c18b2aa832eaa52edc0ae49fbae5a85e150c07"
+ ).unwrap();
let mut v = Verifier::new(&pubkey);
v.update(&message);
@@ -129,8 +134,9 @@ mod test {
let seed = hex::decode("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")
.unwrap();
- let expected_sig = hex::decode("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b")
- .unwrap();
+ let expected_sig = hex::decode(
+ "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"
+ ).unwrap();
let mut s = Signer::new(&seed);
let sig = s.sign();
@@ -144,8 +150,9 @@ mod test {
let message = hex::decode("cbc77b").unwrap();
- let expected_sig = hex::decode("d9868d52c2bebce5f3fa5a79891970f309cb6591e3e1702a70276fa97c24b3a8e58606c38c9758529da50ee31b8219cba45271c689afa60b0ea26c99db19b00c")
- .unwrap();
+ let expected_sig = hex::decode(
+ "d9868d52c2bebce5f3fa5a79891970f309cb6591e3e1702a70276fa97c24b3a8e58606c38c9758529da50ee31b8219cba45271c689afa60b0ea26c99db19b00c"
+ ).unwrap();
let mut s = Signer::new(&seed);
s.update(&message);
@@ -170,5 +177,4 @@ mod test {
assert_eq!(result, true);
}
-
}
diff --git a/src/tag.rs b/src/tag.rs
index 4ec02b0..2b9c1f4 100644
--- a/src/tag.rs
+++ b/src/tag.rs
@@ -19,7 +19,7 @@ use error::Error;
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.
+ // 'NONC' is 0x434e4f4e.
//
// Tags are written here in ascending order
SIG,
@@ -75,7 +75,7 @@ impl Tag {
b"ROOT" => Ok(Tag::ROOT),
b"SIG\x00" => Ok(Tag::SIG),
b"SREP" => Ok(Tag::SREP),
- _ => Err(Error::InvalidTag(Box::from(bytes)))
+ _ => Err(Error::InvalidTag(Box::from(bytes))),
}
}
}