Skip to content

Commit

Permalink
continue work on kms
Browse files Browse the repository at this point in the history
  • Loading branch information
int08h committed Oct 10, 2018
1 parent bab728c commit c66513b
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 62 deletions.
59 changes: 30 additions & 29 deletions src/bin/roughenough-kms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
//! Work with Roughenough long-term key
//!
#[macro_use]
extern crate clap;
#[macro_use]
extern crate log;
Expand All @@ -26,31 +25,21 @@ extern crate roughenough;
extern crate simple_logger;
extern crate untrusted;

use std::default::Default;

use clap::{App, Arg};
use roughenough::key::{EnvelopeEncryption, KmsProvider};
use roughenough::VERSION;
use roughenough::key::EnvelopeEncryption;

#[cfg(feature = "kms")]
use roughenough::key::awskms::AwsKms;

#[cfg(feature = "kms")]
fn aws_kms() {
let client = AwsKms::from_arn(
"arn:aws:kms:us-east-2:927891522318:key/1c96fb2c-d417-48f4-bf24-8e7173a587f5",
).unwrap();
fn aws_kms(kms_key: &str, plaintext_seed: &[u8]) {
let client = AwsKms::from_arn(kms_key).unwrap();

let plaintext_seed = [b'a'; 32];
match EnvelopeEncryption::encrypt_seed(&client, &plaintext_seed) {
Ok(bundle) => {
info!("Bundle len={}", bundle.len());
info!("{}", hex::encode(&bundle));

match EnvelopeEncryption::decrypt_seed(&client, &bundle) {
Ok(plaintext) => info!("Result is {}", hex::encode(plaintext)),
Err(e) => error!("Nope, {:?}", e),
};
Ok(encrypted_blob) => {
println!("key_protection: \"{}\"", kms_key);
println!("seed: {}", hex::encode(&encrypted_blob));
}
Err(e) => {
error!("Error: {:?}", e);
Expand All @@ -65,22 +54,34 @@ pub fn main() {

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();
.arg(Arg::with_name("kms-key")
.short("k")
.long("kms-key")
.takes_value(true)
.required(true)
.help("Identity of the KMS key to be used"))
.arg(Arg::with_name("seed")
.short("s")
.long("seed")
.takes_value(true)
.required(true)
.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")
.map(|seed| hex::decode(seed).expect("Error parsing seed value"))
.unwrap();

if plaintext_seed.len() != 32 {
error!("Seed must be 32 bytes long; provided seed is {}", plaintext_seed.len());
return;
}

if cfg!(feature = "kms") {
info!("KMS feature enabled");
#[cfg(feature = "kms")]
{
aws_kms();
}
aws_kms(kms_key, &plaintext_seed);
} else {
warn!("KMS not enabled, nothing to do");
}

info!("Done");
}
14 changes: 8 additions & 6 deletions src/config/environment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,8 @@ impl EnvironmentConfig {
};

if let Ok(seed) = env::var(ROUGHENOUGH_SEED) {
cfg.seed = hex::decode(&seed).expect(
format!(
"invalid seed value: {}\n'seed' should be 32 byte hex value",
seed
).as_ref(),
);
cfg.seed = hex::decode(&seed)
.expect("invalid seed value; 'seed' should be a hex value");
};

if let Ok(batch_size) = env::var(ROUGHENOUGH_BATCH_SIZE) {
Expand All @@ -96,6 +92,12 @@ impl EnvironmentConfig {
cfg.status_interval = Duration::from_secs(val as u64);
};

if let Ok(key_protection) = env::var(ROUGHENOUGH_KEY_PROTECTION) {
cfg.key_protection = key_protection
.parse()
.expect(format!("invalid key_protection value: {}", key_protection).as_ref());
}

Ok(cfg)
}
}
Expand Down
41 changes: 18 additions & 23 deletions src/key/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,14 @@ const MIN_PAYLOAD_SIZE: usize = DEK_LEN_FIELD
+ MIN_SEED_LENGTH as usize
+ TAG_SIZE_BYTES;

// Domain separation in case KMS key is reused
// No input prefix to skip, consume entire buffer
const IN_PREFIX_LEN: usize = 0;

// Trivial domain separation to guard KMS key reuse
static AD: &[u8; 11] = b"roughenough";

// Convenience function to create zero-filled Vec of given size
fn zero_filled(len: usize) -> Vec<u8> {
fn vec_zero_filled(len: usize) -> Vec<u8> {
let mut v = Vec::with_capacity(len);
for _ in 0..len {
v.push(0);
Expand All @@ -63,31 +66,30 @@ impl EnvelopeEncryption {
)));
}

info!("--- decrypt ---");
info!("blob {}", hex::encode(ciphertext_blob));
let mut tmp = Cursor::new(ciphertext_blob);

// Read the lengths of the wrapped DEK and of the nonce
let dek_len = tmp.read_u16::<LittleEndian>()?;
let nonce_len = tmp.read_u16::<LittleEndian>()?;

let mut encrypted_dek = zero_filled(dek_len as usize);
// Consume the wrapped DEK
let mut encrypted_dek = vec_zero_filled(dek_len as usize);
tmp.read_exact(&mut encrypted_dek)?;

let mut nonce = zero_filled(nonce_len as usize);
// Consume the nonce
let mut nonce = vec_zero_filled(nonce_len as usize);
tmp.read_exact(&mut nonce)?;

// Consume the encrypted seed + tag
let mut encrypted_seed = Vec::new();
tmp.read_to_end(&mut encrypted_seed)?;

info!("dek len {}", dek_len);
info!("nonce len {}", nonce_len);
info!("enc dec {}", hex::encode(&encrypted_dek));
info!("nonce {}", hex::encode(&nonce));
info!("blob {}", hex::encode(&encrypted_seed));

// Invoke KMS to decrypt the DEK
let dek = kms.decrypt_dek(&encrypted_dek)?;
let dek_open_key = OpeningKey::new(&AES_256_GCM, &dek)?;

match open_in_place(&dek_open_key, &nonce, AD, 0, &mut encrypted_seed) {
// Decrypt the seed value using the DEK
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(
"failed to decrypt plaintext seed".to_string(),
Expand All @@ -112,7 +114,7 @@ impl EnvelopeEncryption {
plaintext_buf.push(0);
}

// Encrypt the plaintext seed
// Encrypt the plaintext seed using the DEK
let dek_seal_key = SealingKey::new(&AES_256_GCM, &dek)?;
let encrypted_seed = match seal_in_place(
&dek_seal_key,
Expand All @@ -129,7 +131,7 @@ impl EnvelopeEncryption {
}
};

// Wrap the DEK
// Use the KMS to wrap the DEK
let wrapped_dek = kms.encrypt_dek(&dek.to_vec())?;

// And coalesce everything together
Expand All @@ -140,13 +142,6 @@ impl EnvelopeEncryption {
output.write_all(&nonce)?;
output.write_all(&encrypted_seed)?;

info!("--- encrypt ---");
info!("seed {}", hex::encode(plaintext_seed));
info!("dek {}", hex::encode(&dek));
info!("enc dek {}", hex::encode(&wrapped_dek));
info!("nonce {}", hex::encode(&nonce));
info!("blob {}", hex::encode(&encrypted_seed));

Ok(output)
}
}
22 changes: 18 additions & 4 deletions src/key/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ mod longterm;
mod online;

use std::error::Error;
use std::str::FromStr;

pub use self::envelope::EnvelopeEncryption;
pub use self::longterm::LongTermKey;
Expand All @@ -39,11 +40,24 @@ pub enum KeyProtection {
/// No protection, seed is in plaintext
Plaintext,

/// Envelope encryption with Key-Encrypting-Key (KEK) from AWS Key Management Service
AwsKmsEnvelope,
/// Envelope encryption using AWS Key Management Service
AwsKmsEnvelope(String),

/// Envelope encryption with Key-Encrypting-Key (KEK) from Google Cloud Key Management Service
GoogleKmsEnvelope,
/// Envelope encryption using Google Cloud Key Management Service
GoogleKmsEnvelope(String),
}

impl FromStr for KeyProtection {
type Err = ();

fn from_str(s: &str) -> Result<KeyProtection, ()> {
match s {
"plaintext" => Ok(KeyProtection::Plaintext),
s if s.starts_with("arn") => Ok(KeyProtection::AwsKmsEnvelope(s.to_string())),
s if s.starts_with("gcp") => Ok(KeyProtection::GoogleKmsEnvelope(s.to_string())),
_ => Err(())
}
}
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Clone)]
Expand Down

0 comments on commit c66513b

Please sign in to comment.