summaryrefslogtreecommitdiff
path: root/src/key
diff options
context:
space:
mode:
authorStuart Stock <stuart@int08h.com>2018-10-07 21:44:53 -0500
committerStuart Stock <stuart@int08h.com>2018-10-07 21:44:53 -0500
commita072d3c67efdcaf4a9d969150268ed0151febb60 (patch)
treee70233e8291aba17e01596b6b0a4f76f7568729c /src/key
parentf6b5c2cc2aa38751ce774264a9088311ea0da412 (diff)
downloadroughenough-a072d3c67efdcaf4a9d969150268ed0151febb60.zip
wip checkpoint; nearly round-trip kms
Diffstat (limited to 'src/key')
-rw-r--r--src/key/awskms.rs29
-rw-r--r--src/key/envelope.rs115
-rw-r--r--src/key/mod.rs29
3 files changed, 150 insertions, 23 deletions
diff --git a/src/key/awskms.rs b/src/key/awskms.rs
index bb8d215..234864b 100644
--- a/src/key/awskms.rs
+++ b/src/key/awskms.rs
@@ -46,9 +46,10 @@ impl AwsKms {
let parts: Vec<&str> = arn.split(':').collect();
if parts.len() != 6 {
- return Err(KmsError::InvalidConfiguration(
- format!("invalid KMS arn: too few parts {}", parts.len())
- ));
+ return Err(KmsError::InvalidConfiguration(format!(
+ "invalid KMS arn: too few parts {}",
+ parts.len()
+ )));
}
let region_part = parts.get(3).expect("region is missing");
@@ -67,9 +68,10 @@ impl AwsKms {
impl KmsProvider for AwsKms {
fn encrypt_dek(&self, plaintext_dek: &PlaintextDEK) -> Result<EncryptedDEK, KmsError> {
if plaintext_dek.len() != DEK_SIZE_BYTES {
- return Err(KmsError::InvalidKey(
- format!("provided DEK wrong length: {}", plaintext_dek.len()),
- ));
+ return Err(KmsError::InvalidKey(format!(
+ "provided DEK wrong length: {}",
+ plaintext_dek.len()
+ )));
}
let mut encrypt_req: EncryptRequest = Default::default();
@@ -81,12 +83,12 @@ impl KmsProvider for AwsKms {
if let Some(ciphertext) = result.ciphertext_blob {
Ok(ciphertext)
} else {
- Err(KmsError::EncryptionFailed(
+ Err(KmsError::OperationFailed(
"no ciphertext despite successful response".to_string(),
))
}
}
- Err(e) => Err(KmsError::EncryptionFailed(e.description().to_string())),
+ Err(e) => Err(KmsError::OperationFailed(e.description().to_string())),
}
}
@@ -100,17 +102,18 @@ impl KmsProvider for AwsKms {
if plaintext_dek.len() == DEK_SIZE_BYTES {
Ok(plaintext_dek)
} else {
- Err(KmsError::InvalidKey(
- format!("decrypted DEK wrong length: {}", plaintext_dek.len())
- ))
+ Err(KmsError::InvalidKey(format!(
+ "decrypted DEK wrong length: {}",
+ plaintext_dek.len()
+ )))
}
} else {
- Err(KmsError::DecryptionFailed(
+ Err(KmsError::OperationFailed(
"decrypted payload is empty".to_string(),
))
}
}
- Err(e) => Err(KmsError::DecryptionFailed(e.description().to_string())),
+ Err(e) => Err(KmsError::OperationFailed(e.description().to_string())),
}
}
}
diff --git a/src/key/envelope.rs b/src/key/envelope.rs
index 5c869c5..1bcc077 100644
--- a/src/key/envelope.rs
+++ b/src/key/envelope.rs
@@ -14,22 +14,123 @@
extern crate hex;
-use ring::aead::AES_256_GCM;
+use std::io::{Cursor, Read, Write};
+
+use ring::aead::{open_in_place, seal_in_place, OpeningKey, SealingKey, AES_256_GCM};
use ring::rand;
use ring::rand::SecureRandom;
-use key::KmsProvider;
+use super::super::MIN_SEED_LENGTH;
+use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
+use key::{KmsError, KmsProvider, DEK_SIZE_BYTES, NONCE_SIZE_BYTES, TAG_SIZE_BYTES};
-///
/// 2 bytes - encrypted DEK length
+/// 2 bytes - nonce length
/// n bytes - encrypted DEK
-/// 2 bytes - encrypted
+/// n bytes - nonce
+/// n bytes - opaque (encrypted seed + tag)
pub struct EnvelopeEncryption;
+static AD: &[u8; 11] = b"roughenough";
+
+const DEK_LEN_FIELD: usize = 2;
+const NONCE_LEN_FIELD: usize = 2;
+
+fn zero_filled(len: usize) -> Vec<u8> {
+ let mut v = Vec::with_capacity(len);
+ for _ in 0..len {
+ v.push(0);
+ }
+ return v;
+}
+
impl EnvelopeEncryption {
- pub fn encrypt(kms: &KmsProvider, plaintext: &[u8]) -> Vec<u8> {
+ pub fn decrypt_seed(kms: &KmsProvider, ciphertext_blob: &[u8]) -> Result<Vec<u8>, KmsError> {
+ let min_size = DEK_LEN_FIELD
+ + NONCE_LEN_FIELD
+ + DEK_SIZE_BYTES
+ + NONCE_SIZE_BYTES
+ + TAG_SIZE_BYTES
+ + MIN_SEED_LENGTH as usize;
+
+ if ciphertext_blob.len() < min_size {
+ return Err(KmsError::InvalidData(
+ format!("ciphertext too short: min {}, found {}", min_size, ciphertext_blob.len())
+ ));
+ }
+ /// 2 bytes - encrypted DEK length
+ /// 2 bytes - nonce length
+ /// n bytes - encrypted DEK
+ /// n bytes - nonce
+ /// n bytes - encrypted seed
+
+ let mut tmp = Cursor::new(ciphertext_blob.clone());
+ let dek_len = tmp.read_u16::<LittleEndian>()?;
+ let nonce_len = tmp.read_u16::<LittleEndian>()?;
+
+ let mut encrypted_dek = zero_filled(usize::from(dek_len));
+ tmp.read_exact(&mut encrypted_dek)?;
+
+ let mut nonce = zero_filled(usize::from(nonce_len));
+ tmp.read_exact(&mut nonce)?;
+
+ let mut encrypted_blob = zero_filled(ciphertext_blob.len() - tmp.position() as usize);
+ tmp.read_to_end(&mut encrypted_blob)?;
+
+ 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_blob));
+
+ Ok(Vec::new())
+ }
+
+ pub fn encrypt_seed(kms: &KmsProvider, plaintext_seed: &[u8]) -> Result<Vec<u8>, KmsError> {
+ // Generate random DEK and nonce
let rng = rand::SystemRandom::new();
- let mut dek = [0u8; 16];
- rng.fill(&mut dek).unwrap();
+ let mut dek = [0u8; DEK_SIZE_BYTES];
+ let mut nonce = [0u8; NONCE_SIZE_BYTES];
+ rng.fill(&mut dek)?;
+ rng.fill(&mut nonce)?;
+
+ // ring will overwrite plaintext with ciphertext in this buffer
+ let mut plaintext_buf = plaintext_seed.to_vec();
+
+ // reserve space for the authentication tag which will be appended after the ciphertext
+ plaintext_buf.reserve(TAG_SIZE_BYTES);
+ for _ in 0..TAG_SIZE_BYTES {
+ plaintext_buf.push(0);
+ }
+
+ // Encrypt the plaintext seed
+ let dek_seal_key = SealingKey::new(&AES_256_GCM, &dek)?;
+ let encrypted_seed =
+ match seal_in_place(&dek_seal_key, &nonce, AD, &mut plaintext_buf, TAG_SIZE_BYTES) {
+ Ok(enc_len) => plaintext_buf[..enc_len].to_vec(),
+ Err(e) => {
+ return Err(KmsError::OperationFailed(
+ "failed to encrypt plaintext seed".to_string(),
+ ))
+ }
+ };
+
+ // Wrap the DEK
+ let wrapped_dek = kms.encrypt_dek(&dek.to_vec())?;
+
+ // And coalesce everything together
+ let mut output = Vec::new();
+ output.write_u16::<LittleEndian>(wrapped_dek.len() as u16)?;
+ output.write_u16::<LittleEndian>(nonce.len() as u16)?;
+ output.write_all(&wrapped_dek)?;
+ output.write_all(&nonce)?;
+ output.write_all(&encrypted_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)
}
}
diff --git a/src/key/mod.rs b/src/key/mod.rs
index b269af2..62c37a9 100644
--- a/src/key/mod.rs
+++ b/src/key/mod.rs
@@ -18,11 +18,16 @@
extern crate hex;
extern crate log;
+extern crate ring;
+extern crate std;
mod envelope;
mod longterm;
mod online;
+use std::error::Error;
+
+pub use self::envelope::EnvelopeEncryption;
pub use self::longterm::LongTermKey;
pub use self::online::OnlineKey;
@@ -41,17 +46,35 @@ pub enum KeyProtection {
GoogleKmsEnvelope,
}
-#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Clone, Copy)]
+#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Clone)]
pub enum KmsError {
- DecryptionFailed(String),
- EncryptionFailed(String),
+ OperationFailed(String),
InvalidConfiguration(String),
+ InvalidData(String),
InvalidKey(String),
}
+impl From<std::io::Error> for KmsError {
+ fn from(error: std::io::Error) -> Self {
+ KmsError::OperationFailed(error.description().to_string())
+ }
+}
+
+impl From<ring::error::Unspecified> for KmsError {
+ fn from(error: ring::error::Unspecified) -> Self {
+ KmsError::OperationFailed("unspecified ring cryptographic failure".to_string())
+ }
+}
+
/// Size of the Data Encryption Key (DEK) in bytes
pub const DEK_SIZE_BYTES: usize = 32;
+/// Size of the AEAD nonce in bytes
+pub const NONCE_SIZE_BYTES: usize = 12;
+
+/// Size of the AEAD authentication tag in bytes
+pub const TAG_SIZE_BYTES: usize = 16;
+
/// An unencrypted (plaintext) 256-bit Data Encryption Key (DEK).
type PlaintextDEK = Vec<u8>;