summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bin/kms.rs64
-rw-r--r--src/bin/roughenough-server.rs15
-rw-r--r--src/config/environment.rs9
-rw-r--r--src/config/file.rs7
-rw-r--r--src/config/mod.rs17
-rw-r--r--src/key/awskms.rs101
-rw-r--r--src/key/longterm.rs68
-rw-r--r--src/key/mod.rs41
-rw-r--r--src/key/online.rs (renamed from src/keys.rs)45
-rw-r--r--src/lib.rs3
-rw-r--r--src/sign.rs4
-rw-r--r--src/tag.rs2
12 files changed, 323 insertions, 53 deletions
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<u8>,
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<Self, Error> {
@@ -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<u8>,
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<SocketAddr, Error>;
+
+ /// 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<ServerConfig>) -> 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<Self, DecryptError> {
+ 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<Vec<u8>, 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<Vec<u8>, 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/keys.rs b/src/key/online.rs
index 2fadb00..18c8b8f 100644
--- a/src/keys.rs
+++ b/src/key/online.rs
@@ -12,10 +12,6 @@
// 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;
@@ -23,10 +19,11 @@ use time::Timespec;
use byteorder::{LittleEndian, WriteBytesExt};
-use super::{CERTIFICATE_CONTEXT, SIGNED_RESPONSE_CONTEXT};
use std::fmt;
use std::fmt::Formatter;
+use SIGNED_RESPONSE_CONTEXT;
+
///
/// Represents the delegated Roughtime ephemeral online key.
///
@@ -107,41 +104,3 @@ impl fmt::Display for OnlineKey {
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