1 unstable release
Uses old Rust 2015
0.1.0 | Mar 3, 2018 |
---|
#1732 in Cryptography
13KB
164 lines
aws-kms-crypt
Library for encrypting and decrypting secrets within the AWS ecosystem.
Features
- Interoperable - Interoperable implementations for Bash, NodeJS, Python and Rust.
- Secure - AES encryption with KMS generated data keys (details).
- Simple - Simple API for encrypting and decrypting sensitive data from all supported languages.
Installation
Shell
curl -LO https://raw.githubusercontent.com/sjakthol/aws-kms-crypt/master/shell/aws-kms-crypt.sh && chmod +x aws-kms-crypt.sh
NodeJS
npm install aws-kms-crypt
Python
pip install aws-kms-crypt
Rust
In cargo.toml
[dependencies]
aws_kms_crypt = "0.1.0"
Usage
General Prerequisites
All implementations require access to AWS credentials.
When encrypting data, the credentials must allow the following actions:
kms:GenerateDataKey
kms:GenerateRandom
When decrypting data, the credentials must allow the following actions:
kms:Decrypt
In both cases, the access can (and should in the case of kms:Decrypt
) be
further limited with IAM policy conditions (see here
for details).
Shell Scripts (Bash)
The shell script at shell/aws-kms-crypt.sh
provides an interface for shell
scripts to encrypt and decrypt data. The script needs the following commands /
tools to function:
aws
base64
cut
jq
od
openssl
sed
Encrypting Data
# No encryption context
echo -n "secretp4ssw0rd!" | ./aws-kms-crypt.sh encrypt --kms-key-id alias/common > encrypted-plan.json
# With encryption context
echo -n "secretp4ssw0rd!" | ./aws-kms-crypt.sh encrypt --kms-key-id alias/common --encryption-context type=plan,entity=admins > encrypted-plan.json
Decrypting Data
$ cat encrypted-plan.json | ./aws-kms-crypt.sh decrypt
secretp4ssw0rd!
Node
The nodejs
directory contains a Node package that implements the KMS based encryption
and decryption functionality.
A recent version of Node (>= 4) is required.
Encrypting Data
Use the encrypt()
function of the module to encrypt any stringified data:
const kmscrypt = require('aws-kms-crypt')
kmscrypt.encrypt('secretp4ssw0rd!', {
key: 'alias/common', // Change your key here
region: 'eu-west-1', // AWS SDK needs to know this
encryptionContext: { purpose: 'automation' } // optional, can be left out
}, function (err, result) {
if (err) {
return console.log('Encryption failed:', err)
}
console.log(JSON.stringify(result, null, 2))
// Console output:
// {
// "EncryptedData": "DPQ0OZ8auGY6ohQb/pypAHJTAPaQre7RrEtziIhRgB8=",
// "EncryptedDataKey": "<snip>CBZogG5a",
// "EncryptionContext": {
// "purpose": "automation"
// },
// "Iv": "6f93b293f7f77ddf7525bf43038f01c4"
// }
})
Decrypting Data
To decrypt previously encrypted data, feed the parsed JSON document
into the decrypt()
function of the module:
const kmscrypt = require('aws-kms-crypt')
kmscrypt.decrypt({
'EncryptedData': 'TSHgAb4MYkacM9qtdO5OeLQax6jze3P7+zIeUDpakC4=',
'EncryptedDataKey': '<snip>KqnVhLZY+8',
'EncryptionContext': {
'purpose': 'automation'
},
'Iv': '6cfbac80d90df12a6357a8f91b57f907'
}, { region: 'eu-west-1' }, function (err, result) {
if (err) {
return console.log('Encryption failed:', err)
}
console.log(result)
// => secretp4ssw0rd!
})
Python
The python
directory contains a Python package that implements the KMS based encryption
and decryption functionality. The module has been tested to work with both Python 2.7 and
Python 3.5.
Encrypting Data
import kmscrypt
res = kmscrypt.encrypt('secretp4ssw0rd!', key_id='alias/common', encryption_context={
'purpose': 'automation'
})
# res is now a dict of form
# {
# 'EncryptedData': 'Su00srm/ru5kd9DLDvi0EdEjjBGUrRBJ06vUmL8QHUU=',
# 'EncryptedDataKey': 'AQIDAHhyrbU/fP<snip>',
# 'EncryptionContext': {'purpose': 'automation'},
# 'Iv': 'd07acff1e2301c468cd3164b8858e477'
# }
Decrypting Data
secret = kmscrypt.decrypt(res)
print(secret) # => secretp4ssw0rd!
Rust
The rust
directory contains a rust crate that implements KMS based encryption
and decryption functionality.
Encrypting Data
extern crate aws_kms_crypt;
extern crate serde_json;
use std::collections::HashMap;
fn main() {
let mut encryption_context = HashMap::new();
encryption_context.insert("entity".to_owned(), "admin".to_owned());
let options = aws_kms_crypt::EncryptOptions {
encryption_context: encryption_context,
key: "alias/common".into(),
region: "eu-west-1".into()
};
let data = "secret".into();
let res = aws_kms_crypt::encrypt(&data, &options);
println!("{}", serde_json::to_string(&res.unwrap()).unwrap());
}
Decrypting Data
Here's a full example that uses serde_json to deserialize the JSON encoded secret into a struct:
extern crate aws_kms_crypt;
extern crate serde_json;
fn main() {
let raw = r#"{
"EncryptedData": "vRhu+D5LrwNctyhxDvUoqL51YH2LclgUKtDz/2Nxy6Y=",
"EncryptedDataKey": "<snip>KBFRpvDvpXNXu3e/tTO6Jfi",
"EncryptionContext": {
"entity": "admin"
},
"Iv": "31bf06a8e0d15a26f1325da6f4f33a9c"
}"#;
let data: aws_kms_crypt::EncryptedSecret = serde_json::from_str(raw).unwrap();
let options = aws_kms_crypt::DecryptOptions {
region: "eu-west-1".to_owned()
};
let res = aws_kms_crypt::decrypt(&data, &options);
println!("Secret is: {:?}", res.unwrap());
}
How it works?
Encrypting Data
The following steps are taken when the data is encrypted:
- A random 16 byte initialization vector is generated with the GenerateRandom KMS API call (shell) or through a platform-specific randomness API (Node, Python)
- A data encryption key for AES-128 algorithm is generated with the GenerateDataKey` KMS API call
- The input data is encrypted locally with AES-128-CBC algorithm using the plaintext version of the generated data key together with the generated IV for encryption.
- The encrypted data, encrypted data key and the initialization vector is outputted into
a JSON document of following format:
{ "EncryptedData": "<base64>", "EncryptedDataKey": "<base64>", "EncryptionContext": { "KeyName1": "1", "KeyName2": "2" }, "Iv": "<hex>" }
This JSON output can be saved to a file and stored in (semi) publicly available location as it does not reveal anything about the encrypted data.
Decrypting the Data
The decryption phase extracts the data from the JSON document the encryption phase produced and takes the following steps to decrypt the data:
- The encrypted data key is decrypted using the Decrypt KMS API call to retrieve the plaintext data encryption key
- The plaintext data key and the IV is used to decrypt the encrypted data locally.
Future Work
- Support other algorithms than AES-128-CBC
- Automated testing of interoperability
Dependencies
~20–32MB
~516K SLoC