summaryrefslogtreecommitdiff
path: root/src/key
diff options
context:
space:
mode:
authorStuart Stock <stuart@int08h.com>2018-10-07 15:14:01 -0500
committerStuart Stock <stuart@int08h.com>2018-10-07 15:49:54 -0500
commitf6b5c2cc2aa38751ce774264a9088311ea0da412 (patch)
treeef9f331af4775e46d72142c8da6c41b6d65a560e /src/key
parent73bff435871edf500d15d63a1a486df5b56b27c0 (diff)
downloadroughenough-f6b5c2cc2aa38751ce774264a9088311ea0da412.zip
checkpoint
Diffstat (limited to 'src/key')
-rw-r--r--src/key/awskms.rs66
-rw-r--r--src/key/envelope.rs12
-rw-r--r--src/key/mod.rs30
3 files changed, 79 insertions, 29 deletions
diff --git a/src/key/awskms.rs b/src/key/awskms.rs
index 87ba4bd..bb8d215 100644
--- a/src/key/awskms.rs
+++ b/src/key/awskms.rs
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+extern crate log;
+
#[cfg(feature = "kms")]
extern crate rusoto_core;
#[cfg(feature = "kms")]
@@ -30,6 +32,8 @@ use std::fmt;
use std::fmt::Formatter;
use std::str::FromStr;
+use key::{EncryptedDEK, KmsError, KmsProvider, PlaintextDEK, DEK_SIZE_BYTES};
+
#[cfg(feature = "kms")]
pub struct AwsKms {
kms_client: KmsClient,
@@ -38,57 +42,75 @@ pub struct AwsKms {
#[cfg(feature = "kms")]
impl AwsKms {
- pub fn from_uri(uri: &str) -> Result<Self, DecryptError> {
- let parts: Vec<&str> = uri.split(':').collect();
+ pub fn from_arn(arn: &str) -> Result<Self, KmsError> {
+ let parts: Vec<&str> = arn.split(':').collect();
if parts.len() != 6 {
- return Err(DecryptError::Validation(
- "invalid KMS arn: too few parts".to_string(),
+ return Err(KmsError::InvalidConfiguration(
+ format!("invalid KMS arn: too few parts {}", parts.len())
));
}
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())),
+ Err(e) => return Err(KmsError::InvalidConfiguration(e.description().to_string())),
};
Ok(AwsKms {
kms_client: KmsClient::new(region),
- key_id: uri.to_string(),
+ key_id: arn.to_string(),
})
}
+}
- pub fn encrypt(&self, plaintext: &[u8]) -> Result<Vec<u8>, EncryptError> {
- let mut encrypt_req: EncryptRequest = Default::default();
+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()),
+ ));
+ }
+ let mut encrypt_req: EncryptRequest = Default::default();
encrypt_req.key_id = self.key_id.clone();
- encrypt_req.plaintext = Vec::from(plaintext);
+ encrypt_req.plaintext = plaintext_dek.clone();
match self.kms_client.encrypt(encrypt_req).sync() {
Ok(result) => {
- let ciphertext = result
- .ciphertext_blob
- .expect("no ciphertext despite successful response");
- Ok(ciphertext)
+ if let Some(ciphertext) = result.ciphertext_blob {
+ Ok(ciphertext)
+ } else {
+ Err(KmsError::EncryptionFailed(
+ "no ciphertext despite successful response".to_string(),
+ ))
+ }
}
- Err(e) => Err(e),
+ Err(e) => Err(KmsError::EncryptionFailed(e.description().to_string())),
}
}
- pub fn decrypt(&self, ciphertext: &[u8]) -> Result<Vec<u8>, DecryptError> {
+ fn decrypt_dek(&self, encrypted_dek: &EncryptedDEK) -> Result<PlaintextDEK, KmsError> {
let mut decrypt_req: DecryptRequest = Default::default();
-
- decrypt_req.ciphertext_blob = Vec::from(ciphertext);
+ decrypt_req.ciphertext_blob = encrypted_dek.clone();
match self.kms_client.decrypt(decrypt_req).sync() {
Ok(result) => {
- let plaintext = result
- .plaintext
- .expect("no plaintext despite successful response");
- Ok(plaintext)
+ if let Some(plaintext_dek) = result.plaintext {
+ if plaintext_dek.len() == DEK_SIZE_BYTES {
+ Ok(plaintext_dek)
+ } else {
+ Err(KmsError::InvalidKey(
+ format!("decrypted DEK wrong length: {}", plaintext_dek.len())
+ ))
+ }
+ } else {
+ Err(KmsError::DecryptionFailed(
+ "decrypted payload is empty".to_string(),
+ ))
+ }
}
- Err(e) => Err(e),
+ Err(e) => Err(KmsError::DecryptionFailed(e.description().to_string())),
}
}
}
diff --git a/src/key/envelope.rs b/src/key/envelope.rs
index 3e54255..5c869c5 100644
--- a/src/key/envelope.rs
+++ b/src/key/envelope.rs
@@ -14,18 +14,22 @@
extern crate hex;
+use ring::aead::AES_256_GCM;
use ring::rand;
use ring::rand::SecureRandom;
-use ring::aead::AES_256_GCM;
-use key::awskms::AwsKms;
+use key::KmsProvider;
+
+///
+/// 2 bytes - encrypted DEK length
+/// n bytes - encrypted DEK
+/// 2 bytes - encrypted
pub struct EnvelopeEncryption;
impl EnvelopeEncryption {
- pub fn encrypt(kms: &AwsKms, plaintext: &[u8]) -> Vec<u8> {
+ pub fn encrypt(kms: &KmsProvider, plaintext: &[u8]) -> Vec<u8> {
let rng = rand::SystemRandom::new();
let mut dek = [0u8; 16];
rng.fill(&mut dek).unwrap();
-
}
}
diff --git a/src/key/mod.rs b/src/key/mod.rs
index da18303..b269af2 100644
--- a/src/key/mod.rs
+++ b/src/key/mod.rs
@@ -19,9 +19,9 @@
extern crate hex;
extern crate log;
+mod envelope;
mod longterm;
mod online;
-mod envelope;
pub use self::longterm::LongTermKey;
pub use self::online::OnlineKey;
@@ -34,9 +34,33 @@ pub enum KeyProtection {
/// No protection, seed is in plaintext
Plaintext,
- /// Envelope encryption of seed by AWS Key Management Service
+ /// Envelope encryption with Key-Encrypting-Key (KEK) from AWS Key Management Service
AwsKmsEnvelope,
- /// Envelope encryption of seed by Google Cloud Key Management Service
+ /// Envelope encryption with Key-Encrypting-Key (KEK) from Google Cloud Key Management Service
GoogleKmsEnvelope,
}
+
+#[derive(Debug, PartialEq, Eq, PartialOrd, Hash, Clone, Copy)]
+pub enum KmsError {
+ DecryptionFailed(String),
+ EncryptionFailed(String),
+ InvalidConfiguration(String),
+ InvalidKey(String),
+}
+
+/// Size of the Data Encryption Key (DEK) in bytes
+pub const DEK_SIZE_BYTES: usize = 32;
+
+/// An unencrypted (plaintext) 256-bit Data Encryption Key (DEK).
+type PlaintextDEK = Vec<u8>;
+
+/// A Data Encryption Key (DEK) that has been encrypted (wrapped) by a Key Encryption Key (KEK).
+/// Size of the encrypted DEK is implementation specific (things like AEAD tag size, nonce size,
+/// provider metadata, and so on will cause it to vary).
+type EncryptedDEK = Vec<u8>;
+
+pub trait KmsProvider {
+ fn encrypt_dek(&self, plaintext_dek: &PlaintextDEK) -> Result<EncryptedDEK, KmsError>;
+ fn decrypt_dek(&self, encrypted_dek: &EncryptedDEK) -> Result<PlaintextDEK, KmsError>;
+}