Skip to content
This repository was archived by the owner on Oct 29, 2019. It is now read-only.

Commit 216ab12

Browse files
committed
Initial commit and public release
0 parents  commit 216ab12

33 files changed

+2782
-0
lines changed

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.DS_Store
2+
/venv
3+
*.py[cdo]
4+
*.swp
5+
.idea/
6+
MANIFEST
7+
build/
8+
dist/
9+
docs/_build
10+
.cache
11+
.tox/
12+
.eggs/
13+
*.egg-info/

HISTORY.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.. :changelog:
2+
3+
Release History
4+
---------------
5+
6+
1.0.0 (2017-01-25)
7+
++++++++++++++++++
8+
9+
- Initial public release
10+

LICENSE

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Copyright 2017 AOL Inc.
2+
3+
Licensed under the Apache License, Version 2.0 (the "License");
4+
you may not use this file except in compliance with the License.
5+
You may obtain a copy of the License at
6+
7+
http://www.apache.org/licenses/LICENSE-2.0
8+
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include README.md HISTORY.rst requirements.txt

README.rst

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
mrcrypt: Multi-Region Encryption
2+
================================
3+
4+
mrcrypt is a command-line tool that allows you to encrypt secrets in
5+
multiple AWS regions using KMS keys using a technique called `Envelope
6+
Encryption <http://docs.aws.amazon.com/kms/latest/developerguide/workflow.html>`__.
7+
It is intended to be used with the `AWS Encryption SDK for
8+
Java <https://github.com/awslabs/aws-encryption-sdk-java>`__, but could
9+
be used on its own.
10+
11+
Compatability with the AWS Encryption SDK
12+
'''''''''''''''''''''''''''''''''''''''''
13+
14+
**All files encrypted with mrcrypt can be decrypted with the AWS
15+
Encryption SDK.** But not all files encrypted with the AWS Encryption
16+
SDK can be decrypted by mrcrypt.
17+
18+
Currently, mrcrypt only supports the AWS Encryption SDK's default (and
19+
most secure) cryptographic algorithm:
20+
21+
- Content Type: Framed
22+
- Frame size: 4096
23+
- Algorithm: ALG\_AES\_256\_GCM\_IV12\_TAG16\_HKDF\_SHA384\_ECDSA\_P384
24+
25+
Support for the remaining algorithms are planned, but files encrypted
26+
with the AWS Encryption SDK using one of the other algorithms are
27+
currently not supported in mrcrypt.
28+
29+
Also, the AWS Encryption SDK creates files using elliptic curve point
30+
compression. Files created with mrcrypt do not use point compression
31+
because they are not currently supported in
32+
`Cryptography <https://github.com/pyca/cryptography>`__, a Python
33+
package mrcrypt uses. The uncompressed points are just as secure as the
34+
compressed points, but files are a few bytes larger. The AWS Encryption
35+
SDK can decrypt files that use uncompressed points, meaning all files
36+
created with mrcrypt are compatible with the AWS Encryption SDK.
37+
38+
Installation
39+
------------
40+
41+
To install mrcrypt simply clone the repo, and run ``pip install .``
42+
inside of the directory:
43+
44+
::
45+
46+
git clone ssh://git@stash.ops.aol.com:2022/identity_services/mrcrypt.git
47+
cd mrcrypt
48+
pip install .
49+
50+
**Note:** mrcrypt uses the Python package
51+
`Cryptography <https://github.com/pyca/cryptography>`__ which depends on
52+
``libffi``. You may need to install it on your system if
53+
``pip install .`` fails. For more specific instructions for your OS:
54+
https://cryptography.io/en/latest/installation/
55+
56+
Usage
57+
-----
58+
59+
::
60+
61+
usage: mrcrypt [-h] [-p PROFILE] [-e ENCRYPTION_CONTEXT] [-d] [-o OUTFILE]
62+
{encrypt,decrypt} ...
63+
64+
Multi Region Encryption. A tool for managing secrets across multiple AWS
65+
regions.
66+
67+
positional arguments:
68+
{encrypt,decrypt}
69+
70+
optional arguments:
71+
-h, --help show this help message and exit
72+
-p PROFILE, --profile PROFILE
73+
The profile to use
74+
-e ENCRYPTION_CONTEXT, --encryption_context ENCRYPTION_CONTEXT
75+
An encryption context to use. (Cannot have whitespace)
76+
-d, --debug Enable more output for debugging
77+
-o OUTFILE, --outfile OUTFILE
78+
The file to write the results to
79+
80+
Both the encrypt, and decrypt commands can encrypt and decrypt files in
81+
directories recursively.
82+
83+
Named Profiles
84+
''''''''''''''
85+
86+
If you have multiple named profiles in your ``~/.aws/credentials`` file,
87+
you can specify one using the ``-p`` argument.
88+
89+
::
90+
91+
mrcrypt -p my_profile encrypt alias/master-key secrets.txt
92+
93+
Encryption Context
94+
''''''''''''''''''
95+
96+
You can specify an `encryption
97+
context <http://docs.aws.amazon.com/kms/latest/developerguide/encryption-context.html>`__
98+
using the ``-e`` argument. This flag takes a JSON object with no spaces:
99+
100+
::
101+
102+
# encrypt
103+
mrcrypt -e '{"key":"value","key2":"value2"}' encrypt alias/master-key secrets.txt
104+
105+
# decrypt
106+
mrcrypt -e '{"key":"value","key2":"value2"}' decrypt secrets.txt.encrypted
107+
108+
Output file name
109+
''''''''''''''''
110+
111+
If you want to specify the output filename, you can use the ``-o``
112+
argument.
113+
114+
``# Encrypt 'file.txt' writing the output into 'encrypted-file.txt' mrcrypt -o encrypted-file.txt encrypt alias/master-key file.txt``
115+
116+
By default, when encrypting, mrcrypt will create a file with the same
117+
file name as the input file with ``.encrypted`` appended to the end.
118+
When decrypting, if the file ends with ``.encrypted`` it will write the
119+
plaintext output to a file of the same name but without the
120+
``.encrypted``.
121+
122+
Encryption
123+
----------
124+
125+
::
126+
127+
usage: mrcrypt encrypt [-h] [-r REGIONS [REGIONS ...]] [-e ENCRYPTION_CONTEXT]
128+
key_id filename
129+
130+
Encrypts a file or directory recursively
131+
132+
positional arguments:
133+
key_id An identifier for a customer master key.
134+
filename The file or directory to encrypt. Use a - to read from
135+
stdin
136+
137+
optional arguments:
138+
-h, --help show this help message and exit
139+
-r REGIONS [REGIONS ...], --regions REGIONS [REGIONS ...]
140+
A list of regions to encrypt with KMS. End the list
141+
with --
142+
-e ENCRYPTION_CONTEXT, --encryption_context ENCRYPTION_CONTEXT
143+
An encryption context to use
144+
145+
**Example:** Encrypt ``secrets.txt`` with the key alias
146+
``alias/master-key`` in the regions ``us-east-1`` and ``us-west-2``:
147+
148+
``mrcrypt encrypt -r us-east-1 us-west-2 -- alias/master-key secrets.txt``
149+
150+
Decryption
151+
----------
152+
153+
::
154+
155+
usage: mrcrypt decrypt [-h] filename
156+
157+
Decrypts a file
158+
159+
positional arguments:
160+
filename The file or directory to decrypt. Use a - to read from stdin
161+
162+
optional arguments:
163+
-h, --help show this help message and exit
164+
165+
**Example:** To decrypt ``secrets.txt.encrypted``:
166+
167+
::
168+
169+
mrcrypt decrypt secrets.txt.encrypted
170+
171+
**Note:** Be careful when decrypting a directory. If the directory
172+
contains files that are not encrypted, it will fail.
173+
174+
Testing
175+
'''''''
176+
177+
Running tests for mrcrypt is easy if you have ``tox`` installed. Simply
178+
run ``tox`` at the project's root.

mrcrypt/__init__.py

Whitespace-only changes.

mrcrypt/algorithms.py

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
"""
2+
mrcrypt.algorithms
3+
~~~~~~~~~~~~~~~~~~
4+
5+
Contains named tuples that describe different algorithms supported by this tool.
6+
"""
7+
from collections import namedtuple
8+
9+
#: Max unsigned 16 bit number
10+
GCM_MAX_CONTENT_LENGTH_BITS = (1 << 16) - 1
11+
12+
#: All lengths are in bytes, unless stated otherwise.
13+
AlgorithmProfile = namedtuple('AlgorithmProfile',
14+
'block_size_bits, iv_length, tag_length, max_content_length_bits, '
15+
'key_algorithm, key_length, id, data_key_algorithm, '
16+
'data_key_length, trailing_signature_algorithm, '
17+
'trailing_signature_length_bits')
18+
19+
alg_aes_128_gcm_iv12_tag16_no_kdf = AlgorithmProfile(
20+
block_size_bits=128,
21+
iv_length=12,
22+
tag_length=16,
23+
max_content_length_bits=GCM_MAX_CONTENT_LENGTH_BITS,
24+
key_algorithm='AES',
25+
key_length=16,
26+
id=0x0014,
27+
data_key_algorithm='AES',
28+
data_key_length=16,
29+
trailing_signature_algorithm=None,
30+
trailing_signature_length_bits=None)
31+
32+
alg_aes_192_gcm_iv12_tag16_no_kdf = AlgorithmProfile(
33+
block_size_bits=128,
34+
iv_length=12,
35+
tag_length=16,
36+
max_content_length_bits=GCM_MAX_CONTENT_LENGTH_BITS,
37+
key_algorithm='AES',
38+
key_length=24,
39+
id=0x0046,
40+
data_key_algorithm='AES',
41+
data_key_length=24,
42+
trailing_signature_algorithm=None,
43+
trailing_signature_length_bits=None)
44+
45+
alg_aes_256_gcm_iv12_tag16_no_kdf = AlgorithmProfile(
46+
block_size_bits=128,
47+
iv_length=12,
48+
tag_length=16,
49+
max_content_length_bits=GCM_MAX_CONTENT_LENGTH_BITS,
50+
key_algorithm='AES',
51+
key_length=32,
52+
id=0x0078,
53+
data_key_algorithm='AES',
54+
data_key_length=32,
55+
trailing_signature_algorithm=None,
56+
trailing_signature_length_bits=None)
57+
58+
alg_aes_128_gcm_iv12_tag16_hkdf_sha256 = AlgorithmProfile(
59+
block_size_bits=128,
60+
iv_length=12,
61+
tag_length=16,
62+
max_content_length_bits=GCM_MAX_CONTENT_LENGTH_BITS,
63+
key_algorithm='AES',
64+
key_length=16,
65+
id=0x0114,
66+
data_key_algorithm='HkdfSHA256',
67+
data_key_length=16,
68+
trailing_signature_algorithm=None,
69+
trailing_signature_length_bits=None)
70+
71+
alg_aes_192_gcm_iv12_tag16_hkdf_sha256 = AlgorithmProfile(
72+
block_size_bits=128,
73+
iv_length=12,
74+
tag_length=16,
75+
max_content_length_bits=GCM_MAX_CONTENT_LENGTH_BITS,
76+
key_algorithm='AES',
77+
key_length=24,
78+
id=0x0146,
79+
data_key_algorithm='HkdfSHA256',
80+
data_key_length=24,
81+
trailing_signature_algorithm=None,
82+
trailing_signature_length_bits=None)
83+
84+
alg_aes_256_gcm_iv12_tag16_hkdf_sha256 = AlgorithmProfile(
85+
block_size_bits=128,
86+
iv_length=12,
87+
tag_length=16,
88+
max_content_length_bits=GCM_MAX_CONTENT_LENGTH_BITS,
89+
key_algorithm='AES',
90+
key_length=32,
91+
id=0x0178,
92+
data_key_algorithm='HkdfSHA256',
93+
data_key_length=32,
94+
trailing_signature_algorithm=None,
95+
trailing_signature_length_bits=None)
96+
97+
alg_aes_128_gcm_iv12_tag16_hkdf_sha256_ecdsa_p256 = AlgorithmProfile(
98+
block_size_bits=128,
99+
iv_length=12,
100+
tag_length=16,
101+
max_content_length_bits=GCM_MAX_CONTENT_LENGTH_BITS,
102+
key_algorithm='AES',
103+
key_length=16,
104+
id=0x0214,
105+
data_key_algorithm='HkdfSHA256',
106+
data_key_length=16,
107+
trailing_signature_algorithm='SHA256withECDSA',
108+
trailing_signature_length_bits=72)
109+
110+
alg_aes_192_gcm_iv12_tag16_hkdf_sha384_ecdsa_p384 = AlgorithmProfile(
111+
block_size_bits=128,
112+
iv_length=12,
113+
tag_length=16,
114+
max_content_length_bits=GCM_MAX_CONTENT_LENGTH_BITS,
115+
key_algorithm='AES',
116+
key_length=24,
117+
id=0x0346,
118+
data_key_algorithm='HkdfSHA384',
119+
data_key_length=24,
120+
trailing_signature_algorithm='SHA384withECDSA',
121+
trailing_signature_length_bits=104)
122+
123+
alg_aes_256_gcm_iv12_tag16_hkdf_sha384_ecdsa_p384 = AlgorithmProfile(
124+
block_size_bits=128,
125+
iv_length=12,
126+
tag_length=16,
127+
max_content_length_bits=GCM_MAX_CONTENT_LENGTH_BITS,
128+
key_algorithm='AES',
129+
key_length=32,
130+
id=0x0378,
131+
data_key_algorithm='HkdfSHA384',
132+
data_key_length=32,
133+
trailing_signature_algorithm='SHA384withECDSA',
134+
trailing_signature_length_bits=104)
135+
136+
137+
def algorithm_from_id(algorithm_id):
138+
"""Retrieves an :class:`AlgorithmProfile` from ``algorithm_id``."""
139+
mapping = _get_mapping()
140+
try:
141+
return mapping[algorithm_id]
142+
except KeyError:
143+
raise ValueError('The number {} does not map to an algorithm'.format(algorithm_id))
144+
145+
146+
def _get_mapping():
147+
"""Builds a dictionary, mapping IDs to their corresponding :class:`AlgorithmProfile`."""
148+
return dict((v.id, v) for v in globals().values() if type(v) is AlgorithmProfile)
149+
150+
151+
def default_algorithm():
152+
return alg_aes_256_gcm_iv12_tag16_hkdf_sha384_ecdsa_p384

mrcrypt/cli/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)