summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--src/bin/roughenough-kms.rs11
-rw-r--r--src/bin/roughenough-server.rs25
-rw-r--r--src/config/environment.rs8
-rw-r--r--src/config/file.rs13
-rw-r--r--src/config/mod.rs23
-rw-r--r--src/error.rs12
-rw-r--r--src/key/awskms.rs5
-rw-r--r--src/key/envelope.rs6
-rw-r--r--src/key/longterm.rs9
-rw-r--r--src/key/mod.rs49
11 files changed, 107 insertions, 55 deletions
diff --git a/.gitignore b/.gitignore
index ff6d05d..5ee4d8b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
Cargo.lock
target/
*.rs.bk
+example-kms.cfg
diff --git a/src/bin/roughenough-kms.rs b/src/bin/roughenough-kms.rs
index b660488..c8d3c74 100644
--- a/src/bin/roughenough-kms.rs
+++ b/src/bin/roughenough-kms.rs
@@ -27,6 +27,8 @@ extern crate untrusted;
use clap::{App, Arg};
use roughenough::VERSION;
+
+#[allow(unused_imports)]
use roughenough::key::EnvelopeEncryption;
#[cfg(feature = "kms")]
@@ -47,6 +49,7 @@ fn aws_kms(kms_key: &str, plaintext_seed: &[u8]) {
}
}
+#[allow(unused_variables)]
pub fn main() {
use log::Level;
@@ -54,13 +57,13 @@ pub fn main() {
let matches = App::new("Roughenough key management")
.version(VERSION)
- .arg(Arg::with_name("kms-key")
+ .arg(Arg::with_name("KEY_ID")
.short("k")
.long("kms-key")
.takes_value(true)
.required(true)
.help("Identity of the KMS key to be used"))
- .arg(Arg::with_name("seed")
+ .arg(Arg::with_name("SEED")
.short("s")
.long("seed")
.takes_value(true)
@@ -68,8 +71,8 @@ pub fn main() {
.help("Seed for the server's long-term identity"))
.get_matches();
- let kms_key = matches.value_of("kms-key").unwrap();
- let plaintext_seed = matches.value_of("seed")
+ let kms_key = matches.value_of("KEY_ID").unwrap();
+ let plaintext_seed = matches.value_of("SEED")
.map(|seed| hex::decode(seed).expect("Error parsing seed value"))
.unwrap();
diff --git a/src/bin/roughenough-server.rs b/src/bin/roughenough-server.rs
index 34e8b90..5be8620 100644
--- a/src/bin/roughenough-server.rs
+++ b/src/bin/roughenough-server.rs
@@ -55,6 +55,7 @@ use byteorder::{LittleEndian, WriteBytesExt};
use roughenough::config;
use roughenough::config::ServerConfig;
+use roughenough::key;
use roughenough::key::{LongTermKey, OnlineKey};
use roughenough::merkle::MerkleTree;
use roughenough::{Error, RtMessage, Tag};
@@ -262,21 +263,21 @@ pub fn main() {
};
let mut online_key = OnlineKey::new();
- let mut long_term_key = LongTermKey::new(config.seed());
- let cert_bytes = long_term_key.make_cert(&online_key).encode().unwrap();
+ let public_key: String;
- info!("Long-term public key : {}", long_term_key);
+ let cert_bytes = {
+ let seed = key::load_seed(&config).unwrap();
+ let mut long_term_key = LongTermKey::new(&seed);
+ public_key = hex::encode(long_term_key.public_key());
+
+ long_term_key.make_cert(&online_key).encode().unwrap()
+ };
+
+ info!("Long-term public key : {}", public_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 a4cb528..14559d1 100644
--- a/src/config/environment.rs
+++ b/src/config/environment.rs
@@ -111,8 +111,8 @@ impl ServerConfig for EnvironmentConfig {
self.port
}
- fn seed(&self) -> &[u8] {
- &self.seed
+ fn seed(&self) -> Vec<u8> {
+ self.seed.clone()
}
fn batch_size(&self) -> u8 {
@@ -131,7 +131,7 @@ impl ServerConfig for EnvironmentConfig {
}
}
- fn key_protection(&self) -> KeyProtection {
- self.key_protection
+ fn key_protection(&self) -> &KeyProtection {
+ &self.key_protection
}
}
diff --git a/src/config/file.rs b/src/config/file.rs
index a3b8b92..fd84404 100644
--- a/src/config/file.rs
+++ b/src/config/file.rs
@@ -86,6 +86,11 @@ impl FileConfig {
let val = value.as_i64().expect("status_interval value invalid");
config.status_interval = Duration::from_secs(val as u64)
}
+ "key_protection" => {
+ let val = value.as_str().unwrap().parse()
+ .expect(format!("invalid key_protection value: {:?}", value).as_ref());
+ config.key_protection = val
+ }
unknown => {
return Err(Error::InvalidConfiguration(format!(
"unknown config key: {}",
@@ -108,8 +113,8 @@ impl ServerConfig for FileConfig {
self.port
}
- fn seed(&self) -> &[u8] {
- &self.seed
+ fn seed(&self) -> Vec<u8> {
+ self.seed.clone()
}
fn batch_size(&self) -> u8 {
@@ -128,7 +133,7 @@ impl ServerConfig for FileConfig {
}
}
- fn key_protection(&self) -> KeyProtection {
- KeyProtection::Plaintext
+ fn key_protection(&self) -> &KeyProtection {
+ &self.key_protection
}
}
diff --git a/src/config/mod.rs b/src/config/mod.rs
index ac903b3..f0e2a1a 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -28,14 +28,11 @@ use std::net::SocketAddr;
use std::time::Duration;
mod file;
-
pub use self::file::FileConfig;
mod environment;
-
pub use self::environment::EnvironmentConfig;
-use key;
use Error;
use KeyProtection;
@@ -74,7 +71,7 @@ pub trait ServerConfig {
/// [Required] A 32-byte hexadecimal value used to generate the server's
/// long-term key pair. **This is a secret value and must be un-guessable**,
/// treat it with care.
- fn seed(&self) -> &[u8];
+ fn seed(&self) -> Vec<u8>;
/// [Optional] The maximum number of requests to process in one batch. All
/// nonces in a batch are used to build a Merkle tree, the root of which is signed.
@@ -89,7 +86,7 @@ pub trait ServerConfig {
fn socket_addr(&self) -> Result<SocketAddr, Error>;
/// Method used to protect the long-term key pair.
- fn key_protection(&self) -> KeyProtection;
+ fn key_protection(&self) -> &KeyProtection;
}
///
@@ -127,27 +124,19 @@ pub fn is_valid_config(cfg: &Box<ServerConfig>) -> bool {
error!("seed value is missing");
is_valid = false;
}
- if !cfg.seed().is_empty() && cfg.seed().len() != 32 {
- error!("seed value must be 32 characters long");
+ if *cfg.key_protection() == KeyProtection::Plaintext && cfg.seed().len() != 32 {
+ error!("plaintext seed value must be 32 characters long");
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/error.rs b/src/error.rs
index b681f33..971bccd 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -15,6 +15,7 @@
use std;
use tag::Tag;
+use key::KmsError;
/// Error types generated by this implementation
#[derive(Debug)]
@@ -58,3 +59,14 @@ impl From<std::io::Error> for Error {
Error::EncodingFailure(err)
}
}
+
+impl From<KmsError> for Error {
+ fn from(err: KmsError) -> Self {
+ match err {
+ KmsError::OperationFailed(m) => Error::InvalidConfiguration(m),
+ KmsError::InvalidConfiguration(m) => Error::InvalidConfiguration(m),
+ KmsError::InvalidData(m) => Error::InvalidConfiguration(m),
+ KmsError::InvalidKey(m) => Error::InvalidConfiguration(m),
+ }
+ }
+}
diff --git a/src/key/awskms.rs b/src/key/awskms.rs
index 234864b..878eff9 100644
--- a/src/key/awskms.rs
+++ b/src/key/awskms.rs
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+extern crate hex;
extern crate log;
#[cfg(feature = "kms")]
@@ -22,9 +23,7 @@ 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 self::rusoto_kms::{DecryptRequest, EncryptRequest, Kms, KmsClient};
use std::default::Default;
use std::error::Error;
diff --git a/src/key/envelope.rs b/src/key/envelope.rs
index 5128b76..ed774ae 100644
--- a/src/key/envelope.rs
+++ b/src/key/envelope.rs
@@ -42,7 +42,7 @@ const MIN_PAYLOAD_SIZE: usize = DEK_LEN_FIELD
// No input prefix to skip, consume entire buffer
const IN_PREFIX_LEN: usize = 0;
-// Trivial domain separation to guard KMS key reuse
+// Trivial domain separation to guard against KMS key reuse
static AD: &[u8; 11] = b"roughenough";
// Convenience function to create zero-filled Vec of given size
@@ -91,7 +91,7 @@ impl EnvelopeEncryption {
let dek_open_key = OpeningKey::new(&AES_256_GCM, &dek)?;
match open_in_place(&dek_open_key, &nonce, AD, IN_PREFIX_LEN, &mut encrypted_seed) {
Ok(plaintext_seed) => Ok(plaintext_seed.to_vec()),
- Err(e) => Err(KmsError::OperationFailed(
+ Err(_) => Err(KmsError::OperationFailed(
"failed to decrypt plaintext seed".to_string(),
)),
}
@@ -124,7 +124,7 @@ impl EnvelopeEncryption {
TAG_SIZE_BYTES,
) {
Ok(enc_len) => plaintext_buf[..enc_len].to_vec(),
- Err(e) => {
+ Err(_) => {
return Err(KmsError::OperationFailed(
"failed to encrypt plaintext seed".to_string(),
))
diff --git a/src/key/longterm.rs b/src/key/longterm.rs
index f06a318..ddac6ea 100644
--- a/src/key/longterm.rs
+++ b/src/key/longterm.rs
@@ -16,10 +16,6 @@
//! Represents the server's long-term identity.
//!
-use time::Timespec;
-
-use byteorder::{LittleEndian, WriteBytesExt};
-
use std::fmt;
use std::fmt::Formatter;
@@ -59,6 +55,11 @@ impl LongTermKey {
cert_msg
}
+
+ /// Return the public key for the provided seed
+ pub fn public_key(&self) -> &[u8] {
+ self.signer.public_key_bytes()
+ }
}
impl fmt::Display for LongTermKey {
diff --git a/src/key/mod.rs b/src/key/mod.rs
index 7ae2198..3fe365f 100644
--- a/src/key/mod.rs
+++ b/src/key/mod.rs
@@ -32,10 +32,10 @@ pub use self::envelope::EnvelopeEncryption;
pub use self::longterm::LongTermKey;
pub use self::online::OnlineKey;
-#[cfg(feature = "kms")]
-pub mod awskms;
+use super::error;
+use super::config::ServerConfig;
-#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Clone, Copy)]
+#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Clone)]
pub enum KeyProtection {
/// No protection, seed is in plaintext
Plaintext,
@@ -47,6 +47,16 @@ pub enum KeyProtection {
GoogleKmsEnvelope(String),
}
+impl Display for KeyProtection {
+ fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
+ match self {
+ KeyProtection::Plaintext => write!(f, "Plaintext"),
+ KeyProtection::AwsKmsEnvelope(key_id) => write!(f, "AwsKms({})", key_id),
+ KeyProtection::GoogleKmsEnvelope(key_id) => write!(f, "GoogleKms({})", key_id),
+ }
+ }
+}
+
impl FromStr for KeyProtection {
type Err = ();
@@ -75,7 +85,7 @@ impl From<std::io::Error> for KmsError {
}
impl From<ring::error::Unspecified> for KmsError {
- fn from(error: ring::error::Unspecified) -> Self {
+ fn from(_: ring::error::Unspecified) -> Self {
KmsError::OperationFailed("unspecified ring cryptographic failure".to_string())
}
}
@@ -101,3 +111,34 @@ pub trait KmsProvider {
fn encrypt_dek(&self, plaintext_dek: &PlaintextDEK) -> Result<EncryptedDEK, KmsError>;
fn decrypt_dek(&self, encrypted_dek: &EncryptedDEK) -> Result<PlaintextDEK, KmsError>;
}
+
+#[cfg(feature = "kms")]
+pub mod awskms;
+
+#[cfg(feature = "kms")]
+use key::awskms::AwsKms;
+use std::fmt::Display;
+use std::fmt::Formatter;
+
+#[cfg(feature = "kms")]
+pub fn load_seed(config: &Box<ServerConfig>) -> Result<Vec<u8>, error::Error> {
+ match config.key_protection() {
+ KeyProtection::Plaintext => Ok(config.seed()),
+ KeyProtection::AwsKmsEnvelope(key_id) => {
+ info!("Unwrapping seed via AWS KMS key '{}'", key_id);
+ let kms = AwsKms::from_arn(key_id)?;
+ let seed = EnvelopeEncryption::decrypt_seed(&kms, &config.seed())?;
+ Ok(seed)
+ }
+ _ => Err(error::Error::InvalidConfiguration("Google KMS not supported".to_string()))
+ }
+}
+
+#[cfg(not(feature = "kms"))]
+pub fn load_seed(config: &Box<ServerConfig>) -> Result<Vec<u8>, error::Error> {
+ match config.key_protection() {
+ KeyProtection::Plaintext => Ok(config.seed()),
+ v => Err(error::Error::InvalidConfiguration(
+ format!("key_protection '{}' implies KMS but server was not compiled with KMS support", v)))
+ }
+}