From 0b924cc92418f9a210a6b78cef56e427dc9c9d1d Mon Sep 17 00:00:00 2001 From: Stuart Stock Date: Sat, 6 Oct 2018 22:40:12 -0500 Subject: Land KMS support, yay! AWS KMS for now, work-in-progress --- Cargo.toml | 6 ++ src/bin/kms.rs | 64 ++++++++++++++++++ src/bin/roughenough-server.rs | 15 +++-- src/config/environment.rs | 9 +++ src/config/file.rs | 7 ++ src/config/mod.rs | 17 ++++- src/key/awskms.rs | 101 +++++++++++++++++++++++++++++ src/key/longterm.rs | 68 +++++++++++++++++++ src/key/mod.rs | 41 ++++++++++++ src/key/online.rs | 106 ++++++++++++++++++++++++++++++ src/keys.rs | 147 ------------------------------------------ src/lib.rs | 3 +- src/sign.rs | 4 +- src/tag.rs | 2 +- 14 files changed, 433 insertions(+), 157 deletions(-) create mode 100644 src/bin/kms.rs create mode 100644 src/key/awskms.rs create mode 100644 src/key/longterm.rs create mode 100644 src/key/mod.rs create mode 100644 src/key/online.rs delete mode 100644 src/keys.rs diff --git a/Cargo.toml b/Cargo.toml index c669bfd..8975b0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,10 @@ keywords = ["roughtime", "cryptography", "crypto"] [badges] travis-ci = { repository = "int08h/roughenough", branch = "master" } +[features] +default = [] +kms = ["rusoto_core", "rusoto_kms"] + [dependencies] mio = "0.6" mio-extras = "2.0" @@ -26,3 +30,5 @@ clap = "2" chrono = "0.4" hex = "0.3" +rusoto_core = { version = "0.34", optional = true } +rusoto_kms = { version = "0.34", optional = true } diff --git a/src/bin/kms.rs b/src/bin/kms.rs new file mode 100644 index 0000000..311fbb5 --- /dev/null +++ b/src/bin/kms.rs @@ -0,0 +1,64 @@ +// Copyright 2017-2018 int08h LLC +// +// 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. + +//! +//! Work with Roughenough long-term key +//! + +#[macro_use] +extern crate clap; +#[macro_use] +extern crate log; +extern crate ring; +extern crate roughenough; +extern crate simple_logger; +extern crate untrusted; + +#[cfg(feature = "kms")] +use roughenough::key::awskms::AwsKms; + +use std::default::Default; + +use clap::{App, Arg}; +use roughenough::VERSION; + +pub fn main() { + use log::Level; + + simple_logger::init_with_level(Level::Info).unwrap(); + + let matches = App::new("Roughenough key management") + .version(VERSION) + .arg( + Arg::with_name("operation") + .required(true) + .help("The operation to perform") + .takes_value(true), + ).get_matches(); + + if cfg!(feature = "kms") { + info!("KMS feature enabled"); + let client = AwsKms::from_uri( + // your key here + ).unwrap(); + + let ciphertext = client.encrypt("This is a test".as_ref()).unwrap(); + info!("Ciphertext: {:?}", ciphertext); + + let plaintext = String::from_utf8(client.decrypt(ciphertext.as_ref()).unwrap()).unwrap(); + info!("Plaintext : {:?}", plaintext); + } + + info!("Done"); +} diff --git a/src/bin/roughenough-server.rs b/src/bin/roughenough-server.rs index 13a7026..34e8b90 100644 --- a/src/bin/roughenough-server.rs +++ b/src/bin/roughenough-server.rs @@ -55,7 +55,7 @@ use byteorder::{LittleEndian, WriteBytesExt}; use roughenough::config; use roughenough::config::ServerConfig; -use roughenough::keys::{LongTermKey, OnlineKey}; +use roughenough::key::{LongTermKey, OnlineKey}; use roughenough::merkle::MerkleTree; use roughenough::{Error, RtMessage, Tag}; use roughenough::{MIN_REQUEST_LENGTH, VERSION}; @@ -256,7 +256,7 @@ pub fn main() { Err(e) => { error!("{:?}", e); process::exit(1) - }, + } Ok(ref cfg) if !config::is_valid_config(&cfg) => process::exit(1), Ok(cfg) => cfg, }; @@ -268,8 +268,15 @@ pub fn main() { info!("Long-term public key : {}", long_term_key); info!("Online public key : {}", online_key); info!("Max response batch size : {}", config.batch_size()); - info!("Status updates every : {} seconds", config.status_interval().as_secs()); - info!("Server listening on : {}:{}", config.interface(), config.port()); + info!( + "Status updates every : {} seconds", + config.status_interval().as_secs() + ); + info!( + "Server listening on : {}:{}", + config.interface(), + config.port() + ); polling_loop(&config, &mut online_key, &cert_bytes); diff --git a/src/config/environment.rs b/src/config/environment.rs index 5053517..8f91f0c 100644 --- a/src/config/environment.rs +++ b/src/config/environment.rs @@ -21,6 +21,7 @@ use std::time::Duration; use config::ServerConfig; use config::{DEFAULT_BATCH_SIZE, DEFAULT_STATUS_INTERVAL}; use Error; +use KeyProtection; /// /// Obtain a Roughenough server configuration ([ServerConfig](trait.ServerConfig.html)) @@ -33,6 +34,7 @@ use Error; /// seed | `ROUGHENOUGH_SEED` /// batch_size | `ROUGHENOUGH_BATCH_SIZE` /// status_interval | `ROUGHENOUGH_STATUS_INTERVAL` +/// key_protection | `ROUGHENOUGH_KEY_PROTECTION` /// pub struct EnvironmentConfig { port: u16, @@ -40,6 +42,7 @@ pub struct EnvironmentConfig { seed: Vec, batch_size: u8, status_interval: Duration, + key_protection: KeyProtection, } const ROUGHENOUGH_PORT: &str = "ROUGHENOUGH_PORT"; @@ -47,6 +50,7 @@ const ROUGHENOUGH_INTERFACE: &str = "ROUGHENOUGH_INTERFACE"; const ROUGHENOUGH_SEED: &str = "ROUGHENOUGH_SEED"; const ROUGHENOUGH_BATCH_SIZE: &str = "ROUGHENOUGH_BATCH_SIZE"; const ROUGHENOUGH_STATUS_INTERVAL: &str = "ROUGHENOUGH_STATUS_INTERVAL"; +const ROUGHENOUGH_KEY_PROTECTION: &str = "ROUGHENOUGH_KEY_PROTECTION"; impl EnvironmentConfig { pub fn new() -> Result { @@ -56,6 +60,7 @@ impl EnvironmentConfig { seed: Vec::new(), batch_size: DEFAULT_BATCH_SIZE, status_interval: DEFAULT_STATUS_INTERVAL, + key_protection: KeyProtection::Plaintext, }; if let Ok(port) = env::var(ROUGHENOUGH_PORT) { @@ -123,4 +128,8 @@ impl ServerConfig for EnvironmentConfig { Err(_) => Err(Error::InvalidConfiguration(addr)), } } + + fn key_protection(&self) -> KeyProtection { + self.key_protection + } } diff --git a/src/config/file.rs b/src/config/file.rs index e93ee99..a3b8b92 100644 --- a/src/config/file.rs +++ b/src/config/file.rs @@ -23,6 +23,7 @@ use yaml_rust::YamlLoader; use config::ServerConfig; use config::{DEFAULT_BATCH_SIZE, DEFAULT_STATUS_INTERVAL}; use Error; +use KeyProtection; /// /// Read a Roughenough server configuration ([ServerConfig](trait.ServerConfig.html)) @@ -42,6 +43,7 @@ pub struct FileConfig { seed: Vec, batch_size: u8, status_interval: Duration, + key_protection: KeyProtection, } impl FileConfig { @@ -67,6 +69,7 @@ impl FileConfig { seed: Vec::new(), batch_size: DEFAULT_BATCH_SIZE, status_interval: DEFAULT_STATUS_INTERVAL, + key_protection: KeyProtection::Plaintext, }; for (key, value) in cfg[0].as_hash().unwrap() { @@ -124,4 +127,8 @@ impl ServerConfig for FileConfig { Err(_) => Err(Error::InvalidConfiguration(addr)), } } + + fn key_protection(&self) -> KeyProtection { + KeyProtection::Plaintext + } } diff --git a/src/config/mod.rs b/src/config/mod.rs index 983338c..ac903b3 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -35,7 +35,9 @@ mod environment; pub use self::environment::EnvironmentConfig; +use key; use Error; +use KeyProtection; /// Maximum number of requests to process in one batch and include the the Merkle tree. pub const DEFAULT_BATCH_SIZE: u8 = 64; @@ -85,6 +87,9 @@ pub trait ServerConfig { /// Convenience function to create a `SocketAddr` from the provided `interface` and `port` fn socket_addr(&self) -> Result; + + /// Method used to protect the long-term key pair. + fn key_protection(&self) -> KeyProtection; } /// @@ -127,14 +132,22 @@ pub fn is_valid_config(cfg: &Box) -> bool { is_valid = false; } if cfg.batch_size() < 1 || cfg.batch_size() > 64 { - error!("batch_size {} is invalid; valid range 1-64", cfg.batch_size()); + error!( + "batch_size {} is invalid; valid range 1-64", + cfg.batch_size() + ); is_valid = false; } if is_valid { match cfg.socket_addr() { Err(e) => { - error!("failed to create socket {}:{} {:?}", cfg.interface(), cfg.port(), e); + error!( + "failed to create socket {}:{} {:?}", + cfg.interface(), + cfg.port(), + e + ); is_valid = false; } _ => (), diff --git a/src/key/awskms.rs b/src/key/awskms.rs new file mode 100644 index 0000000..87ba4bd --- /dev/null +++ b/src/key/awskms.rs @@ -0,0 +1,101 @@ +// Copyright 2017-2018 int08h LLC +// +// 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. + +#[cfg(feature = "kms")] +extern crate rusoto_core; +#[cfg(feature = "kms")] +extern crate rusoto_kms; + +#[cfg(feature = "kms")] +use self::rusoto_core::Region; +#[cfg(feature = "kms")] +use self::rusoto_kms::{ + DecryptError, DecryptRequest, EncryptError, EncryptRequest, Kms, KmsClient, +}; + +use std::default::Default; +use std::error::Error; +use std::fmt; +use std::fmt::Formatter; +use std::str::FromStr; + +#[cfg(feature = "kms")] +pub struct AwsKms { + kms_client: KmsClient, + key_id: String, +} + +#[cfg(feature = "kms")] +impl AwsKms { + pub fn from_uri(uri: &str) -> Result { + let parts: Vec<&str> = uri.split(':').collect(); + + if parts.len() != 6 { + return Err(DecryptError::Validation( + "invalid KMS arn: too few parts".to_string(), + )); + } + + let region_part = parts.get(3).expect("region is missing"); + let region = match Region::from_str(region_part) { + Ok(r) => r, + Err(e) => return Err(DecryptError::Validation(e.description().to_string())), + }; + + Ok(AwsKms { + kms_client: KmsClient::new(region), + key_id: uri.to_string(), + }) + } + + pub fn encrypt(&self, plaintext: &[u8]) -> Result, EncryptError> { + let mut encrypt_req: EncryptRequest = Default::default(); + + encrypt_req.key_id = self.key_id.clone(); + encrypt_req.plaintext = Vec::from(plaintext); + + match self.kms_client.encrypt(encrypt_req).sync() { + Ok(result) => { + let ciphertext = result + .ciphertext_blob + .expect("no ciphertext despite successful response"); + Ok(ciphertext) + } + Err(e) => Err(e), + } + } + + pub fn decrypt(&self, ciphertext: &[u8]) -> Result, DecryptError> { + let mut decrypt_req: DecryptRequest = Default::default(); + + decrypt_req.ciphertext_blob = Vec::from(ciphertext); + + match self.kms_client.decrypt(decrypt_req).sync() { + Ok(result) => { + let plaintext = result + .plaintext + .expect("no plaintext despite successful response"); + Ok(plaintext) + } + Err(e) => Err(e), + } + } +} + +#[cfg(feature = "kms")] +impl fmt::Display for AwsKms { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", self.key_id) + } +} diff --git a/src/key/longterm.rs b/src/key/longterm.rs new file mode 100644 index 0000000..f06a318 --- /dev/null +++ b/src/key/longterm.rs @@ -0,0 +1,68 @@ +// Copyright 2017-2018 int08h LLC +// +// 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. + +//! +//! Represents the server's long-term identity. +//! + +use time::Timespec; + +use byteorder::{LittleEndian, WriteBytesExt}; + +use std::fmt; +use std::fmt::Formatter; + +use key::OnlineKey; +use message::RtMessage; +use sign::Signer; +use tag::Tag; +use CERTIFICATE_CONTEXT; + +/// +/// Represents the server's long-term identity. +/// +pub struct LongTermKey { + signer: Signer, +} + +impl LongTermKey { + pub fn new(seed: &[u8]) -> Self { + LongTermKey { + signer: Signer::from_seed(seed), + } + } + + /// Create a CERT message with a DELE containing the provided online key + /// and a SIG of the DELE value signed by the long-term key + pub fn make_cert(&mut self, online_key: &OnlineKey) -> RtMessage { + let dele_bytes = online_key.make_dele().encode().unwrap(); + + self.signer.update(CERTIFICATE_CONTEXT.as_bytes()); + self.signer.update(&dele_bytes); + + let dele_signature = self.signer.sign(); + + let mut cert_msg = RtMessage::new(2); + cert_msg.add_field(Tag::SIG, &dele_signature).unwrap(); + cert_msg.add_field(Tag::DELE, &dele_bytes).unwrap(); + + cert_msg + } +} + +impl fmt::Display for LongTermKey { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", self.signer) + } +} diff --git a/src/key/mod.rs b/src/key/mod.rs new file mode 100644 index 0000000..a4af975 --- /dev/null +++ b/src/key/mod.rs @@ -0,0 +1,41 @@ +// Copyright 2017-2018 int08h LLC +// +// 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. + +//! +//! Representations and management of Roughtime's online and long-term Ed25519 keys +//! + +extern crate hex; +extern crate log; + +mod longterm; +mod online; + +pub use self::longterm::LongTermKey; +pub use self::online::OnlineKey; + +#[cfg(feature = "kms")] +pub mod awskms; + +#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Clone, Copy)] +pub enum KeyProtection { + /// No protection, seed is in plaintext + Plaintext, + + /// Envelope encryption of seed by AWS Key Management Service + AwsKmsEnvelope, + + /// Envelope encryption of seed by Google Cloud Key Management Service + GoogleKmsEnvelope, +} diff --git a/src/key/online.rs b/src/key/online.rs new file mode 100644 index 0000000..18c8b8f --- /dev/null +++ b/src/key/online.rs @@ -0,0 +1,106 @@ +// Copyright 2017-2018 int08h LLC +// +// 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. + +use message::RtMessage; +use sign::Signer; +use tag::Tag; +use time::Timespec; + +use byteorder::{LittleEndian, WriteBytesExt}; + +use std::fmt; +use std::fmt::Formatter; + +use SIGNED_RESPONSE_CONTEXT; + +/// +/// Represents the delegated Roughtime ephemeral online key. +/// +pub struct OnlineKey { + signer: Signer, +} + +impl OnlineKey { + pub fn new() -> Self { + OnlineKey { + signer: Signer::new(), + } + } + + /// Create a DELE message containing the public key of this online key + pub fn make_dele(&self) -> RtMessage { + let zeros = [0u8; 8]; + let max = [0xff; 8]; + let pub_key_bytes = self.signer.public_key_bytes(); + + let mut dele_msg = RtMessage::new(3); + dele_msg.add_field(Tag::PUBK, pub_key_bytes).unwrap(); + dele_msg.add_field(Tag::MINT, &zeros).unwrap(); + dele_msg.add_field(Tag::MAXT, &max).unwrap(); + + dele_msg + } + + /// Create an SREP response containing the provided time and Merkle root, + /// signed by this online key. + pub fn make_srep(&mut self, now: Timespec, merkle_root: &[u8]) -> RtMessage { + let mut radi = [0; 4]; + let mut midp = [0; 8]; + + // one second (in microseconds) + (&mut radi as &mut [u8]) + .write_u32::(1_000_000) + .unwrap(); + + // current epoch time in microseconds + let midp_time = { + let secs = (now.sec as u64) * 1_000_000; + let nsecs = (now.nsec as u64) / 1_000; + + secs + nsecs + }; + (&mut midp as &mut [u8]) + .write_u64::(midp_time) + .unwrap(); + + // Signed response SREP + let srep_bytes = { + 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, merkle_root).unwrap(); + + srep_msg.encode().unwrap() + }; + + // signature on SREP + let srep_signature = { + self.signer.update(SIGNED_RESPONSE_CONTEXT.as_bytes()); + self.signer.update(&srep_bytes); + self.signer.sign() + }; + + let mut result = RtMessage::new(2); + result.add_field(Tag::SIG, &srep_signature).unwrap(); + result.add_field(Tag::SREP, &srep_bytes).unwrap(); + + result + } +} + +impl fmt::Display for OnlineKey { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", self.signer) + } +} diff --git a/src/keys.rs b/src/keys.rs deleted file mode 100644 index 2fadb00..0000000 --- a/src/keys.rs +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2017-2018 int08h LLC -// -// 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. - -//! -//! Representations of Roughtime's online and long-term Ed25519 keys -//! - -use message::RtMessage; -use sign::Signer; -use tag::Tag; -use time::Timespec; - -use byteorder::{LittleEndian, WriteBytesExt}; - -use super::{CERTIFICATE_CONTEXT, SIGNED_RESPONSE_CONTEXT}; -use std::fmt; -use std::fmt::Formatter; - -/// -/// Represents the delegated Roughtime ephemeral online key. -/// -pub struct OnlineKey { - signer: Signer, -} - -impl OnlineKey { - pub fn new() -> Self { - OnlineKey { - signer: Signer::new(), - } - } - - /// Create a DELE message containing the public key of this online key - pub fn make_dele(&self) -> RtMessage { - let zeros = [0u8; 8]; - let max = [0xff; 8]; - let pub_key_bytes = self.signer.public_key_bytes(); - - let mut dele_msg = RtMessage::new(3); - dele_msg.add_field(Tag::PUBK, pub_key_bytes).unwrap(); - dele_msg.add_field(Tag::MINT, &zeros).unwrap(); - dele_msg.add_field(Tag::MAXT, &max).unwrap(); - - dele_msg - } - - /// Create an SREP response containing the provided time and Merkle root, - /// signed by this online key. - pub fn make_srep(&mut self, now: Timespec, merkle_root: &[u8]) -> RtMessage { - let mut radi = [0; 4]; - let mut midp = [0; 8]; - - // one second (in microseconds) - (&mut radi as &mut [u8]) - .write_u32::(1_000_000) - .unwrap(); - - // current epoch time in microseconds - let midp_time = { - let secs = (now.sec as u64) * 1_000_000; - let nsecs = (now.nsec as u64) / 1_000; - - secs + nsecs - }; - (&mut midp as &mut [u8]) - .write_u64::(midp_time) - .unwrap(); - - // Signed response SREP - let srep_bytes = { - 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, merkle_root).unwrap(); - - srep_msg.encode().unwrap() - }; - - // signature on SREP - let srep_signature = { - self.signer.update(SIGNED_RESPONSE_CONTEXT.as_bytes()); - self.signer.update(&srep_bytes); - self.signer.sign() - }; - - let mut result = RtMessage::new(2); - result.add_field(Tag::SIG, &srep_signature).unwrap(); - result.add_field(Tag::SREP, &srep_bytes).unwrap(); - - result - } -} - -impl fmt::Display for OnlineKey { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", self.signer) - } -} - -/// -/// Represents the server's long-term identity. -/// -pub struct LongTermKey { - signer: Signer, -} - -impl LongTermKey { - pub fn new(seed: &[u8]) -> Self { - LongTermKey { - signer: Signer::from_seed(seed), - } - } - - /// Create a CERT message with a DELE containing the provided online key - /// and a SIG of the DELE value signed by the long-term key - pub fn make_cert(&mut self, online_key: &OnlineKey) -> RtMessage { - let dele_bytes = online_key.make_dele().encode().unwrap(); - - self.signer.update(CERTIFICATE_CONTEXT.as_bytes()); - self.signer.update(&dele_bytes); - - let dele_signature = self.signer.sign(); - - let mut cert_msg = RtMessage::new(2); - cert_msg.add_field(Tag::SIG, &dele_signature).unwrap(); - cert_msg.add_field(Tag::DELE, &dele_bytes).unwrap(); - - cert_msg - } -} - -impl fmt::Display for LongTermKey { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", self.signer) - } -} diff --git a/src/lib.rs b/src/lib.rs index d513e4c..8a8a049 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -61,11 +61,12 @@ mod message; mod tag; pub mod config; -pub mod keys; +pub mod key; pub mod merkle; pub mod sign; pub use error::Error; +pub use key::KeyProtection; pub use message::RtMessage; pub use tag::Tag; diff --git a/src/sign.rs b/src/sign.rs index bd141b0..ead5a4c 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -20,10 +20,10 @@ extern crate hex; extern crate ring; extern crate untrusted; -use self::ring::signature; -use self::ring::signature::Ed25519KeyPair; use self::ring::rand; use self::ring::rand::SecureRandom; +use self::ring::signature; +use self::ring::signature::Ed25519KeyPair; use self::untrusted::Input; diff --git a/src/tag.rs b/src/tag.rs index 14fd39e..7996663 100644 --- a/src/tag.rs +++ b/src/tag.rs @@ -15,7 +15,7 @@ use error::Error; /// An unsigned 32-bit value (key) that maps to a byte-string (value). -#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Clone)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Clone, Copy)] 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 -- cgit v1.2.3