hydan 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.pre-commit-config.yaml +6 -0
- data/Dockerfile.tpl +10 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +48 -0
- data/README.md +145 -0
- data/Rakefile +16 -0
- data/bin/console +14 -0
- data/bin/hydan +5 -0
- data/bin/setup +8 -0
- data/circle.yml +29 -0
- data/hydan.gemspec +40 -0
- data/lib/hydan/cli.rb +130 -0
- data/lib/hydan/crypto/decrypt.rb +51 -0
- data/lib/hydan/crypto/encrypt.rb +56 -0
- data/lib/hydan/crypto/kms/decrypt.rb +45 -0
- data/lib/hydan/crypto/kms/encrypt.rb +56 -0
- data/lib/hydan/crypto/kms.rb +56 -0
- data/lib/hydan/crypto.rb +5 -0
- data/lib/hydan/io.rb +41 -0
- data/lib/hydan/path_types.rb +8 -0
- data/lib/hydan/s3.rb +91 -0
- data/lib/hydan/version.rb +3 -0
- data/lib/hydan.rb +19 -0
- metadata +199 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b9a7ad17b289c67ab39aa32e25df5571fa67fc18
|
4
|
+
data.tar.gz: 7521911f20b2c469fca0684cca905bc49bd55012
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 183654b57e053c76526841781086fbb3bf77ba85a2ce8c0afc264aafeff51de1a1e2312f21a013b56b2adf641f0f516ec6ca5ebc5b25f5138435a6455768eaeb
|
7
|
+
data.tar.gz: 7233ecf3068a7d1ae6ecd584000a83c6b361c1e154bb5e1d9d1c00b0fe55200e3b1a0043172b67472ea45d49ca3db1d3b8c0e0709099338c6816596d69a24fde
|
data/.gitignore
ADDED
data/Dockerfile.tpl
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
hydan (0.1.1)
|
5
|
+
aws-sdk (~> 2)
|
6
|
+
gibberish
|
7
|
+
thor
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
aws-sdk (2.2.30)
|
13
|
+
aws-sdk-resources (= 2.2.30)
|
14
|
+
aws-sdk-core (2.2.30)
|
15
|
+
jmespath (~> 1.0)
|
16
|
+
aws-sdk-resources (2.2.30)
|
17
|
+
aws-sdk-core (= 2.2.30)
|
18
|
+
coderay (1.1.1)
|
19
|
+
gibberish (2.0.0)
|
20
|
+
jmespath (1.1.3)
|
21
|
+
method_source (0.8.2)
|
22
|
+
minitest (5.8.4)
|
23
|
+
pry (0.10.3)
|
24
|
+
coderay (~> 1.1.0)
|
25
|
+
method_source (~> 0.8.1)
|
26
|
+
slop (~> 3.4)
|
27
|
+
pry-doc (0.8.0)
|
28
|
+
pry (~> 0.9)
|
29
|
+
yard (~> 0.8)
|
30
|
+
rake (10.5.0)
|
31
|
+
slop (3.6.0)
|
32
|
+
thor (0.19.1)
|
33
|
+
yard (0.8.7.6)
|
34
|
+
|
35
|
+
PLATFORMS
|
36
|
+
ruby
|
37
|
+
|
38
|
+
DEPENDENCIES
|
39
|
+
bundler (~> 1.11)
|
40
|
+
hydan!
|
41
|
+
minitest (~> 5.0)
|
42
|
+
pry (~> 0.10)
|
43
|
+
pry-doc (>= 0.6.0)
|
44
|
+
rake (~> 10.0)
|
45
|
+
yard
|
46
|
+
|
47
|
+
BUNDLED WITH
|
48
|
+
1.11.2
|
data/README.md
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
# Hydan
|
2
|
+
|
3
|
+
Hydan is a command-line utility for encrypting and decrypting text and/or files. In addition to local crypto operations, Hydan can also defer to Amazon KMS for symmetric master keys. S3 uploads/downloads are also supported, and can leverage KMS for encryption/decryption.
|
4
|
+
|
5
|
+
## TODO:
|
6
|
+
|
7
|
+
- [ ] Support multi-part uploads for S3
|
8
|
+
- [ ] Support SSE for S3 uploads
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
gem 'hydan'
|
16
|
+
```
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install hydan
|
25
|
+
|
26
|
+
## Disclaimer
|
27
|
+
|
28
|
+
Hydan is open-source software - **USE AT YOUR OWN RISK!** The authors and all affiliates assume no responsibility for issues encountered with the use of of this software.
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
|
32
|
+
### Local text encryption
|
33
|
+
|
34
|
+
Local text encryption returns a JSON object containing the cihpertext and randomly-generated data key. Plaintext can be piped into STDIN or passed on the CLI via the `--text` flag.
|
35
|
+
|
36
|
+
```
|
37
|
+
# We'll grab 256 random bits for the master key
|
38
|
+
KEY=$(head -c 32 /dev/urandom | base64)
|
39
|
+
echo 'A secret on STDIN' | hydan encrypt --master-key $KEY
|
40
|
+
{
|
41
|
+
"ciphertext": {
|
42
|
+
"v": 1,
|
43
|
+
"adata": "",
|
44
|
+
"ks": 256,
|
45
|
+
"ct": "8VmGeR6+rtznK6Lu8vmr99PFmbxfu6FcyzWyiU17",
|
46
|
+
"ts": 96,
|
47
|
+
"mode": "gcm",
|
48
|
+
"cipher": "aes",
|
49
|
+
"iter": 100000,
|
50
|
+
"iv": "8Pj1RuenaRqV5BRc",
|
51
|
+
"salt": "mXfzMTw89lE="
|
52
|
+
},
|
53
|
+
"data_key": "eyJ2IjoxLCJhZGF0YSI6IiIsImtzIjoyNTYsImN0IjoiVlVDclRLUTV2WVpBMGFuTW9NTUQzWS9OQ0NYUkF0SWFpc3JYcFI1THVraHptUE1WeTVqRzBsbFJsM1k9IiwidHMiOjk2LCJtb2RlIjoiZ2NtIiwiY2lwaGVyIjoiYWVzIiwiaXRlciI6MTAwMDAwLCJpdiI6ImRUeTBPVUt2a3plaThkdnEiLCJzYWx0Ijoib1BQTXF2U1ZCN2c9In0="
|
54
|
+
}
|
55
|
+
```
|
56
|
+
|
57
|
+
### Local decryption
|
58
|
+
```
|
59
|
+
KEY=$(head -c 32 /dev/urandom | base64)
|
60
|
+
echo 'The Krabby Patty secret recipe IS...' | hydan encrypt --master-key $KEY | hydan decrypt --master-key $KEY
|
61
|
+
The Krabby Patty secret recipe IS...
|
62
|
+
|
63
|
+
hydan encrypt --master-key $KEY --text 'This also works' | hydan decrypt --master-key $KEY
|
64
|
+
This also works
|
65
|
+
```
|
66
|
+
|
67
|
+
#### Env files
|
68
|
+
A common use case (actually, the reason that this utility was even written) for setting up credentials is through environment variables. To accomodate this, `hydan` supports `--env-formatted` text input. When this flag is passed in, K/V pairs are parsed from each line, and the value (V) is encrypted. This allows for a file to be partially encrypted in a way that hides sensitive information, while still allowing an administrator or operator to understand what the file is for.
|
69
|
+
|
70
|
+
```
|
71
|
+
KEY=$(head -c 32 /dev/urandom | base64)
|
72
|
+
cat test.env
|
73
|
+
FOO=bar
|
74
|
+
BAR=baz
|
75
|
+
BAZ=bat
|
76
|
+
A=B
|
77
|
+
ENV=ENV
|
78
|
+
K=V
|
79
|
+
|
80
|
+
hydan encrypt --env-formatted --master-key $KEY < test.env
|
81
|
+
FOO={"ciphertext":{"v":1,"adata":"","ks":256,"ct":"fnu8Ulyr5JLQArNp5rLz","ts":96,"mode":"gcm","cipher":"aes","iter":100000,"iv":"MOZzAZ/9qtHMCr/t","salt":"37WkVaQQxDA="},"data_key":"eyJ2IjoxLCJhZGF0YSI6IiIsImtzIjoyNTYsImN0IjoiMjFjZ21kM21PbUxBL20xdUt1bDJHNXV5VUhkaXFLQlZhSGlCQ2NzVnczbG5mUnY0Y05SSHRRMFFWVUE9IiwidHMiOjk2LCJtb2RlIjoiZ2NtIiwiY2lwaGVyIjoiYWVzIiwiaXRlciI6MTAwMDAwLCJpdiI6IkNRSkNJanlKVW9pR0kzbHQiLCJzYWx0IjoibS94a2trSm85Nzg9In0="}
|
82
|
+
BAR={"ciphertext":{"v":1,"adata":"","ks":256,"ct":"F9oTm34xCj5Ke68hB+rL","ts":96,"mode":"gcm","cipher":"aes","iter":100000,"iv":"uXP2BC9wyCyJiFy6","salt":"DLDWXIxsbv8="},"data_key":"eyJ2IjoxLCJhZGF0YSI6IiIsImtzIjoyNTYsImN0IjoiYWRRdC9reFQzS2FwSG9FWFlzclJ6bmhqdlcxYVVGdWtQd0xRZXJZTUdTMDF0WFY1RSsyRkRBZndndDA9IiwidHMiOjk2LCJtb2RlIjoiZ2NtIiwiY2lwaGVyIjoiYWVzIiwiaXRlciI6MTAwMDAwLCJpdiI6Ii9SbnFWWkZTR0EwQkw0UEEiLCJzYWx0IjoiSHYxeEd0eTZLcTQ9In0="}
|
83
|
+
BAZ={"ciphertext":{"v":1,"adata":"","ks":256,"ct":"VZ3qtRrRYVRIhJ3qIyku","ts":96,"mode":"gcm","cipher":"aes","iter":100000,"iv":"Ib19BVMCP3VqoeQ+","salt":"9DRsMkbtRvo="},"data_key":"eyJ2IjoxLCJhZGF0YSI6IiIsImtzIjoyNTYsImN0IjoieDcrdklNazBCYThnbWlMMGZ3WHE0TGpMVkNUYVBjaFhZWDFSUUkrL2oraGlBR2V0b3BxS0w2ZG5CbUU9IiwidHMiOjk2LCJtb2RlIjoiZ2NtIiwiY2lwaGVyIjoiYWVzIiwiaXRlciI6MTAwMDAwLCJpdiI6IkgzTXlEcXB4SXUrMGNoVTgiLCJzYWx0IjoiN0RSYzZmWkZWcFE9In0="}
|
84
|
+
A={"ciphertext":{"v":1,"adata":"","ks":256,"ct":"6Hqk69PLuImnpTWSjw==","ts":96,"mode":"gcm","cipher":"aes","iter":100000,"iv":"oy5lea/k99nsZz8/","salt":"yYrMK6MbXfQ="},"data_key":"eyJ2IjoxLCJhZGF0YSI6IiIsImtzIjoyNTYsImN0IjoiN3gySUZIZmk2NjB6VnpqWDd4MjJqTUlHeXdZclRuY3E1cGdOcHU1RTB2R2I3MWMvbVp3ZjR0cUxtSk09IiwidHMiOjk2LCJtb2RlIjoiZ2NtIiwiY2lwaGVyIjoiYWVzIiwiaXRlciI6MTAwMDAwLCJpdiI6IkJ1a1lBRGJhaHVvcDJuM2YiLCJzYWx0IjoiKzhVOWhiaUtCRmc9In0="}
|
85
|
+
ENV={"ciphertext":{"v":1,"adata":"","ks":256,"ct":"FQfnNaMl936fhZo3Ykrx","ts":96,"mode":"gcm","cipher":"aes","iter":100000,"iv":"12QqZqCxkvMbagzo","salt":"A3YypAcXUIQ="},"data_key":"eyJ2IjoxLCJhZGF0YSI6IiIsImtzIjoyNTYsImN0IjoiL2xpM0NnaG5BUmJCM3NmMlA5aTNLRXd3eFBITk9HQVFTdFBzVHFWUXRIZGRBRHNOL0wzV1Jrc3dIbGM9IiwidHMiOjk2LCJtb2RlIjoiZ2NtIiwiY2lwaGVyIjoiYWVzIiwiaXRlciI6MTAwMDAwLCJpdiI6ImFEK1dIZEwwWnlzbmJ5d0QiLCJzYWx0IjoiZ3hlL3dYVU9GRmM9In0="}
|
86
|
+
K={"ciphertext":{"v":1,"adata":"","ks":256,"ct":"wpSRhDud1zqxhlvqdQ==","ts":96,"mode":"gcm","cipher":"aes","iter":100000,"iv":"9WnyuuOx2q6J/KCR","salt":"kZNxqsQE8o0="},"data_key":"eyJ2IjoxLCJhZGF0YSI6IiIsImtzIjoyNTYsImN0IjoiWHpsSWEzZEVoeWNHQ0lDQTJvU2xZem93SFZUeU1CVGUyb1dwNkVZcm9JamR4azg4SnVXci9USlIxaUE9IiwidHMiOjk2LCJtb2RlIjoiZ2NtIiwiY2lwaGVyIjoiYWVzIiwiaXRlciI6MTAwMDAwLCJpdiI6IkUxdGNNT0E0YkpBSzlEdGMiLCJzYWx0IjoibzRZcjQ5U3NhVU09In0="}
|
87
|
+
|
88
|
+
# --env-formatted plays nicely with pipes too
|
89
|
+
hydan encrypt --env-formatted --master-key $KEY < test.env | hydan decrypt --env-formatted --master-key $KEY
|
90
|
+
FOO=bar
|
91
|
+
BAR=baz
|
92
|
+
BAZ=bat
|
93
|
+
A=B
|
94
|
+
ENV=ENV
|
95
|
+
K=V
|
96
|
+
|
97
|
+
# This can easily be used to construct export statements to be evaluated via eval
|
98
|
+
hydan encrypt --env-formatted --master-key $KEY < test.env | hydan decrypt --env-formatted --master-key $KEY | awk '{ print "export " $1 }'
|
99
|
+
export FOO=bar
|
100
|
+
export BAR=baz
|
101
|
+
export BAZ=bat
|
102
|
+
export A=B
|
103
|
+
export ENV=ENV
|
104
|
+
export K=V
|
105
|
+
```
|
106
|
+
|
107
|
+
#### Large files
|
108
|
+
When files are too large to read into memory, use the `--in` and `--out` flags. This will prompt `hydan` to use logic that reads and encrypts the file line-by-line, as opposed to "slurping" the file beforehand. This also implies that it's not worth Base64 encoding the encrypted output, since it will also be large. Because of this, encryption that uses the `--in` flag saves it's output in binary instead of Base64.
|
109
|
+
|
110
|
+
```
|
111
|
+
# Make a huge file
|
112
|
+
mkfile -n 10g ~/Desktop/LargeTestFile
|
113
|
+
|
114
|
+
hydan encrypt --in ~/Desktop/LargeTestFile --out ~/Desktop/LargeTestFile.bin --master-key $KEY
|
115
|
+
Data key (Base64): U2FsdGVkX18NG/y8sEFGyop79njj0R9omH+/UZ3Ijy1c8nYACE2UQt1NMvSL2Rh9GH4p+TSEZxSMqqWqzg7/Kw==
|
116
|
+
|
117
|
+
# We use the encrypted data key generated by the program in order to decrypt the file
|
118
|
+
# If `--key-out` is specified, the key will be saved to the supplied path instead of
|
119
|
+
# being printed to STDOUT.
|
120
|
+
hydan decrypt --in ~/Desktop/LargeTestFile.bin --out ~/Desktop/LargeTestFile.decrypted --master-key $KEY --data-key U2FsdGVkX18NG/y8sEFGyop79njj0R9omH+/UZ3Ijy1c8nYACE2UQt1NMvSL2Rh9GH4p+TSEZxSMqqWqzg7/Kw==
|
121
|
+
|
122
|
+
# Original
|
123
|
+
shasum -a 256 ~/Desktop/LargeTestFile
|
124
|
+
732377e7f4a2abdc13ddfa1eb4c9c497fd2a2b294674d056cf51581b47dd586d /Users/roberto/Desktop/LargeTestFile
|
125
|
+
|
126
|
+
# Decrypted
|
127
|
+
shasum -a 256 ~/Desktop/LargeTestFile.decrypted
|
128
|
+
732377e7f4a2abdc13ddfa1eb4c9c497fd2a2b294674d056cf51581b47dd586d /Users/roberto/Desktop/LargeTestFile.decrypted
|
129
|
+
```
|
130
|
+
|
131
|
+
## Development
|
132
|
+
|
133
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
134
|
+
|
135
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
136
|
+
|
137
|
+
## Contributing
|
138
|
+
|
139
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/hydan.
|
140
|
+
|
141
|
+
|
142
|
+
## License
|
143
|
+
|
144
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
145
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rake/testtask"
|
3
|
+
|
4
|
+
Rake::TestTask.new(:test) do |t|
|
5
|
+
t.libs << "test"
|
6
|
+
t.libs << "lib"
|
7
|
+
t.test_files = FileList['test/**/*_test.rb']
|
8
|
+
end
|
9
|
+
|
10
|
+
Rake::TestTask.new(:ci_test) do |t|
|
11
|
+
t.libs << "test"
|
12
|
+
t.libs << "lib"
|
13
|
+
t.test_files = FileList['test/**/*ci_test.rb']
|
14
|
+
end
|
15
|
+
|
16
|
+
task :default => :spec
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "hydan"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/hydan
ADDED
data/bin/setup
ADDED
data/circle.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
machine:
|
2
|
+
environment:
|
3
|
+
TARGETS: '2.1 2.2 2.3'
|
4
|
+
services:
|
5
|
+
- docker
|
6
|
+
|
7
|
+
dependencies:
|
8
|
+
override:
|
9
|
+
- gem install rubocop
|
10
|
+
- | # Build required images
|
11
|
+
for v in ${TARGETS[*]}; do
|
12
|
+
echo "Building version $v..."
|
13
|
+
sed "s/#VERSION#/$v/g" Dockerfile.tpl > Dockerfile
|
14
|
+
docker build -t $REGISTRY_URL/seibelsbi/docker-hydan:$v .
|
15
|
+
done
|
16
|
+
|
17
|
+
test:
|
18
|
+
override:
|
19
|
+
- docker run -it --entrypoint bash $REGISTRY_URL/seibelsbi/docker-hydan:2.1 -c 'rake ci_test'
|
20
|
+
- docker run -it --entrypoint bash $REGISTRY_URL/seibelsbi/docker-hydan:2.2 -c 'rake ci_test'
|
21
|
+
- docker run -it --entrypoint bash $REGISTRY_URL/seibelsbi/docker-hydan:2.3 -c 'rake ci_test'
|
22
|
+
|
23
|
+
deployment:
|
24
|
+
master:
|
25
|
+
branch: master
|
26
|
+
commands:
|
27
|
+
- docker login -e=$REGISTRY_EMAIL -u=$REGISTRY_USER -p=$REGISTRY_PASSWORD $REGISTRY_URL
|
28
|
+
- docker tag $REGISTRY_URL/seibelsbi/docker-hydan:2.3 $REGISTRY_URL/seibelsbi/docker-hydan:latest
|
29
|
+
- docker push $REGISTRY_URL/seibelsbi/docker-hydan:latest
|
data/hydan.gemspec
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'hydan/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "hydan"
|
8
|
+
spec.version = Hydan::VERSION
|
9
|
+
spec.authors = ["Roberto Acevedo"]
|
10
|
+
spec.email = ["rxacevedo@fastmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{A Ruby gem for KMS and local envelope encryption.}
|
13
|
+
spec.description = %q{Hide your secrets.}
|
14
|
+
spec.homepage = "https://github.com/seibelsbi/hydan"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
18
|
+
# delete this section to allow pushing this gem to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
21
|
+
else
|
22
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
23
|
+
end
|
24
|
+
|
25
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
26
|
+
spec.bindir = "bin"
|
27
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
28
|
+
spec.require_paths = ["lib"]
|
29
|
+
|
30
|
+
spec.add_development_dependency "bundler", "~> 1.11"
|
31
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
32
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
33
|
+
spec.add_development_dependency "pry", "~> 0.10"
|
34
|
+
spec.add_development_dependency "pry-doc", ">= 0.6.0"
|
35
|
+
spec.add_development_dependency "yard"
|
36
|
+
|
37
|
+
spec.add_dependency "thor"
|
38
|
+
spec.add_dependency "aws-sdk", "~> 2"
|
39
|
+
spec.add_dependency "gibberish"
|
40
|
+
end
|
data/lib/hydan/cli.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
module Hydan
|
2
|
+
class CLIBase < Thor
|
3
|
+
def self.shared_options
|
4
|
+
method_option(
|
5
|
+
:master_key,
|
6
|
+
:type => :string,
|
7
|
+
:desc => 'The symmetric master key used to encrypt the exported data key',
|
8
|
+
:required => true
|
9
|
+
)
|
10
|
+
end
|
11
|
+
def self.shared_text_options
|
12
|
+
method_option(
|
13
|
+
:env_formatted,
|
14
|
+
:type => :boolean,
|
15
|
+
:desc => 'Indicates that the input is .env formatted (K=V). Results in K=encrypt(V) output.'
|
16
|
+
)
|
17
|
+
method_option(
|
18
|
+
:text,
|
19
|
+
:type => :array,
|
20
|
+
:desc => 'The plaintext to be encrypted'
|
21
|
+
)
|
22
|
+
end
|
23
|
+
def self.shared_file_options
|
24
|
+
method_option(
|
25
|
+
:in,
|
26
|
+
:type => :string,
|
27
|
+
:desc => 'The file being encrypted'
|
28
|
+
)
|
29
|
+
method_option(
|
30
|
+
:out,
|
31
|
+
:type => :string,
|
32
|
+
:desc => 'Where the encrypted content should be written'
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class CLI < CLIBase
|
38
|
+
|
39
|
+
include Hydan::IO
|
40
|
+
LOGGER = Logger.new(STDOUT)
|
41
|
+
LOGGER.level = Logger::INFO
|
42
|
+
|
43
|
+
desc 'encrypt', 'Encrypt a string or file'
|
44
|
+
shared_options
|
45
|
+
shared_text_options
|
46
|
+
# This is only here to support branching over to encrypt-file
|
47
|
+
method_option(
|
48
|
+
:in,
|
49
|
+
:type => :string,
|
50
|
+
:desc => 'The file being encrypted'
|
51
|
+
)
|
52
|
+
def encrypt(*args)
|
53
|
+
if options[:in]
|
54
|
+
invoke :encrypt_file
|
55
|
+
else
|
56
|
+
master_key = Base64.strict_decode64(options[:master_key])
|
57
|
+
client = Hydan::Crypto::EncryptionHelper.new(master_key)
|
58
|
+
data = handle_input(options)
|
59
|
+
json = client.encrypt(data) unless options[:env_formatted]
|
60
|
+
json = client.encrypt_env_formatted(data) if options[:env_formatted]
|
61
|
+
handle_output(json)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
desc 'encrypt-file', 'Encrypt a file'
|
66
|
+
shared_options
|
67
|
+
shared_file_options
|
68
|
+
# Only used for file encryption
|
69
|
+
method_option(
|
70
|
+
:key_out,
|
71
|
+
:type => :string,
|
72
|
+
:desc => 'Where the encrypted data key should be written'
|
73
|
+
)
|
74
|
+
def encrypt_file(*args)
|
75
|
+
master_key = Base64.strict_decode64(options[:master_key])
|
76
|
+
client = Hydan::Crypto::EncryptionHelper.new(master_key)
|
77
|
+
encrypted_data_key_blob = client.encrypt_file(
|
78
|
+
options[:in],
|
79
|
+
options[:out]
|
80
|
+
)
|
81
|
+
handle_key_output(encrypted_data_key_blob, options[:key_out])
|
82
|
+
end
|
83
|
+
|
84
|
+
desc 'decrypt', 'Decrypt a string or file'
|
85
|
+
shared_options
|
86
|
+
shared_text_options
|
87
|
+
# This is only here to support branching over to encrypt-file
|
88
|
+
method_option(
|
89
|
+
:in,
|
90
|
+
:type => :string,
|
91
|
+
:desc => 'The file being encrypted'
|
92
|
+
)
|
93
|
+
def decrypt(*args)
|
94
|
+
if options[:in]
|
95
|
+
invoke :decrypt_file
|
96
|
+
else
|
97
|
+
key = Base64.strict_decode64(options[:master_key])
|
98
|
+
client = Hydan::Crypto::DecryptionHelper.new(key)
|
99
|
+
data = handle_input(options)
|
100
|
+
plaintext = client.decrypt(data) unless options[:env_formatted]
|
101
|
+
plaintext = client.decrypt_env_file(data) if options[:env_formatted]
|
102
|
+
handle_output(plaintext)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
desc 'decrypt-file', 'Decrypt a file'
|
107
|
+
shared_options
|
108
|
+
shared_file_options
|
109
|
+
method_option(
|
110
|
+
:data_key,
|
111
|
+
:type => :string,
|
112
|
+
:desc => 'The data key used to decrypt encypted data',
|
113
|
+
:required => true
|
114
|
+
)
|
115
|
+
def decrypt_file(*args)
|
116
|
+
master_key = Base64.strict_decode64(options[:master_key])
|
117
|
+
data_key = Base64.strict_decode64(options[:data_key])
|
118
|
+
# TODO: Clear keys
|
119
|
+
client = Hydan::Crypto::DecryptionHelper.new(master_key)
|
120
|
+
client.decrypt_file(options[:in], options[:out], data_key)
|
121
|
+
end
|
122
|
+
|
123
|
+
desc 's3', 'Use the S3 API'
|
124
|
+
subcommand 's3', Hydan::S3::S3Cmd
|
125
|
+
|
126
|
+
desc 'kms', 'Use the KMS API for encryption/decryption'
|
127
|
+
subcommand 'kms', Hydan::Crypto::KMS::KMSCmd
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Hydan
|
2
|
+
module Crypto
|
3
|
+
class DecryptionHelper
|
4
|
+
|
5
|
+
def initialize(symmetric_key)
|
6
|
+
@master_key = symmetric_key
|
7
|
+
@generator = OpenSSL::Cipher.new(Crypto::DEFAULT_CIPHER)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Decrypts a JSON object
|
11
|
+
# @return [String]
|
12
|
+
def decrypt(json)
|
13
|
+
input_hash = JSON.parse(json)
|
14
|
+
data_key = Base64.strict_decode64(input_hash['data_key'])
|
15
|
+
key_cipher = Gibberish::AES.new(@master_key)
|
16
|
+
plaintext_key = key_cipher.decrypt(data_key)
|
17
|
+
data_cipher = Gibberish::AES.new(plaintext_key)
|
18
|
+
plaintext = data_cipher.decrypt(JSON.generate(input_hash['ciphertext']))
|
19
|
+
plaintext
|
20
|
+
end
|
21
|
+
|
22
|
+
# Decrypts a file
|
23
|
+
def decrypt_file(in_file, out_file, key)
|
24
|
+
key_cipher = Gibberish::AES::CBC.new(@master_key)
|
25
|
+
# The return value for this is Base64-encoded by default,
|
26
|
+
# we're overriding it here to later #strict_encode64 it.
|
27
|
+
data_key = key_cipher.decrypt(key, binary: true)
|
28
|
+
data_cipher = Gibberish::AES::CBC.new(data_key)
|
29
|
+
data_key = nil # Scrub from memory as soon as feasible
|
30
|
+
data_cipher.decrypt_file(in_file, out_file)
|
31
|
+
end
|
32
|
+
|
33
|
+
# # Decrypts an env-formatted text string.
|
34
|
+
# # A file is considered to be env-formatted when:
|
35
|
+
# # - Each line consists of K=V pairs
|
36
|
+
# # - Each V is a JSON string that contains a Gibberish
|
37
|
+
# # payload (ciphertext, IV, salt, etc) and an encrypted
|
38
|
+
# # data key that was used to encrypt the ciphertext
|
39
|
+
# # @return [String]
|
40
|
+
def decrypt_env_file(env_body)
|
41
|
+
new_text = []
|
42
|
+
env_body.each_line do |l|
|
43
|
+
k, v = l.match(Hydan::IO::ENV_LINE_REGEX).captures
|
44
|
+
dec_v = decrypt(v)
|
45
|
+
new_text << "#{k}=#{dec_v}"
|
46
|
+
end
|
47
|
+
new_text
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# Class to simplify envelope encryption
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module Hydan
|
6
|
+
module Crypto
|
7
|
+
class EncryptionHelper
|
8
|
+
|
9
|
+
def initialize(master_key)
|
10
|
+
@master_key = master_key
|
11
|
+
@generator = OpenSSL::Cipher.new(Crypto::DEFAULT_CIPHER)
|
12
|
+
@generator.encrypt
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns a JSON string containing the ciphertext (Base64 encoded)
|
16
|
+
# and the encrypted data key used to encrypt it
|
17
|
+
def encrypt(plaintext)
|
18
|
+
data_key = @generator.random_key
|
19
|
+
key_cipher = Gibberish::AES.new(@master_key)
|
20
|
+
data_cipher = Gibberish::AES.new(data_key)
|
21
|
+
output = {
|
22
|
+
'ciphertext' => JSON.parse(data_cipher.encrypt(plaintext)),
|
23
|
+
'data_key' => Base64.strict_encode64(key_cipher.encrypt(data_key))
|
24
|
+
}
|
25
|
+
JSON.pretty_generate output
|
26
|
+
end
|
27
|
+
|
28
|
+
# Encrypts a file and returns the encrypted data key
|
29
|
+
# that was generated for the file. File encryption
|
30
|
+
# uses a different library method that is basically
|
31
|
+
# line-by-line read-encrypt-write mechanism.
|
32
|
+
def encrypt_file(in_file, out_file)
|
33
|
+
data_key = @generator.random_key
|
34
|
+
key_cipher = Gibberish::AES::CBC.new(@master_key)
|
35
|
+
data_cipher = Gibberish::AES::CBC.new(data_key)
|
36
|
+
# The return value for this is Base64-encoded by default,
|
37
|
+
# we're overriding it here to later #strict_encode64 it.
|
38
|
+
encrypted_data_key = key_cipher.encrypt(data_key, binary: true)
|
39
|
+
data_key = nil # Scrub from memory as soon as feasible
|
40
|
+
data_cipher.encrypt_file(in_file, out_file)
|
41
|
+
encrypted_data_key
|
42
|
+
end
|
43
|
+
# TODO: This may be better suited for the plaintext
|
44
|
+
# encryption entrypoint (STDIN or --plaintext flag)
|
45
|
+
def encrypt_env_formatted(plaintext)
|
46
|
+
new_text = []
|
47
|
+
plaintext.each_line do |l|
|
48
|
+
k, v = l.match(Hydan::IO::ENV_LINE_REGEX).captures
|
49
|
+
enc_v = JSON.generate(JSON.parse(encrypt(v)))
|
50
|
+
new_text << "#{k}=#{enc_v}"
|
51
|
+
end
|
52
|
+
new_text
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Class to simplify KMS decryption interface
|
2
|
+
|
3
|
+
module Hydan
|
4
|
+
module Crypto
|
5
|
+
module KMS
|
6
|
+
class DecryptionHelper
|
7
|
+
|
8
|
+
include Hydan::Crypto
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@kms = Aws::KMS::Client.new
|
12
|
+
end
|
13
|
+
|
14
|
+
# Decrypts a JSON object
|
15
|
+
# @return [String]
|
16
|
+
def decrypt(json)
|
17
|
+
input_hash = JSON.parse(json)
|
18
|
+
data_key = Base64.strict_decode64(input_hash['data_key'])
|
19
|
+
plaintext_key = @kms.decrypt(:ciphertext_blob => data_key).plaintext
|
20
|
+
cipher = Gibberish::AES.new(plaintext_key)
|
21
|
+
plaintext = cipher.decrypt(JSON.generate(input_hash['ciphertext']))
|
22
|
+
plaintext
|
23
|
+
end
|
24
|
+
|
25
|
+
# Decrypts an env-formatted text string.
|
26
|
+
# A file is considered to be env-formatted when:
|
27
|
+
# - Each line consists of K=V pairs
|
28
|
+
# - Each V is a JSON string that contains a Gibberish
|
29
|
+
# payload (ciphertext, IV, salt, etc) and an encrypted
|
30
|
+
# data key that was used to encrypt the ciphertext
|
31
|
+
# @return [String]
|
32
|
+
def decrypt_env_file(env_body)
|
33
|
+
new_text = []
|
34
|
+
env_body.each_line do |l|
|
35
|
+
k, v = l.match(Hydan::IO::ENV_LINE_REGEX).captures
|
36
|
+
dec_v = decrypt(v)
|
37
|
+
new_text << "#{k}=#{dec_v}"
|
38
|
+
end
|
39
|
+
new_text
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# Class to simplify KMS encryption interface
|
2
|
+
|
3
|
+
module Hydan
|
4
|
+
module Crypto
|
5
|
+
module KMS
|
6
|
+
class EncryptionHelper
|
7
|
+
|
8
|
+
include Hydan::Crypto
|
9
|
+
|
10
|
+
# Initializes the EncryptionHelper object with an
|
11
|
+
# Aws::KMS::Client.
|
12
|
+
def initialize
|
13
|
+
@kms = Aws::KMS::Client.new
|
14
|
+
end
|
15
|
+
|
16
|
+
# TODO: Should this be private?
|
17
|
+
# Returns the KMS key ID for a given alias
|
18
|
+
def get_kms_key_id(kms_key_alias)
|
19
|
+
unless @kms.nil?
|
20
|
+
aliases = @kms.list_aliases.aliases
|
21
|
+
kms_key = aliases.find { |a| a.alias_name == kms_key_alias }
|
22
|
+
kms_key_id = kms_key.target_key_id
|
23
|
+
kms_key_id
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns a JSON string containing the ciphertext (Base64 encoded)
|
28
|
+
# and the encrypted data key used to encrypt it
|
29
|
+
def encrypt(plaintext, kms_key_id, &block)
|
30
|
+
unwrapped = block.call(plaintext) if block
|
31
|
+
resp = @kms.generate_data_key(
|
32
|
+
key_id: kms_key_id,
|
33
|
+
key_spec: 'AES_256'
|
34
|
+
)
|
35
|
+
cipher = Gibberish::AES.new(resp[:plaintext])
|
36
|
+
output = {
|
37
|
+
'ciphertext' => JSON.parse(cipher.encrypt(unwrapped || plaintext)),
|
38
|
+
'data_key' => Base64.strict_encode64(resp[:ciphertext_blob])
|
39
|
+
}
|
40
|
+
JSON.pretty_generate output
|
41
|
+
end
|
42
|
+
|
43
|
+
def encrypt_env_file(plaintext, kms_key_id)
|
44
|
+
new_text = []
|
45
|
+
plaintext.each_line do |l|
|
46
|
+
k, v = l.match(Hydan::IO::ENV_LINE_REGEX).captures
|
47
|
+
enc_v = JSON.generate(JSON.parse(encrypt(v, kms_key_id)))
|
48
|
+
new_text << "#{k}=#{enc_v}"
|
49
|
+
end
|
50
|
+
new_text
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Hydan
|
2
|
+
module Crypto
|
3
|
+
module KMS
|
4
|
+
# I think there are some path resolution issues going on here
|
5
|
+
require 'hydan/io'
|
6
|
+
class KMSCmd < Thor
|
7
|
+
|
8
|
+
include Hydan::IO
|
9
|
+
desc 'encrypt', 'Encrypt a string or file'
|
10
|
+
method_option :env_formatted, :type => :boolean
|
11
|
+
method_option :file, :type => :string
|
12
|
+
method_option :key_alias, :type => :string, :required => true
|
13
|
+
method_option :out, :type => :string
|
14
|
+
method_option :text, :type => :array
|
15
|
+
def encrypt(*args)
|
16
|
+
client = Hydan::Crypto::KMS::EncryptionHelper.new
|
17
|
+
kms_key_id = client.get_kms_key_id options[:key_alias]
|
18
|
+
|
19
|
+
# TODO: Implement encrypt-file here, same as local enc/dec
|
20
|
+
if options[:file]
|
21
|
+
file = File.open(options[:file], 'r')
|
22
|
+
json = client.encrypt(file, kms_key_id) { |f, k| f.read } unless options[:env_formatted]
|
23
|
+
json = client.encrypt_env_file(file, kms_key_id) { |f, k| f.read } if options[:env_formatted]
|
24
|
+
handle_output(json)
|
25
|
+
else
|
26
|
+
text = handle_input(options)
|
27
|
+
json = client.encrypt(text, kms_key_id) unless options[:env_formatted]
|
28
|
+
json = client.encrypt_env_file(text, kms_key_id) if options[:env_formatted]
|
29
|
+
handle_output(json)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
desc 'decrypt', 'Decrypts a string or file'
|
34
|
+
method_option :file, :type => :string
|
35
|
+
method_option :env_formatted, :type => :boolean
|
36
|
+
method_option :out, :type => :string
|
37
|
+
def decrypt(*args)
|
38
|
+
|
39
|
+
client = Hydan::Crypto::KMS::DecryptionHelper.new
|
40
|
+
|
41
|
+
if options[:file]
|
42
|
+
file = File.open(options[:file], 'r')
|
43
|
+
plaintext = client.decrypt(file.read) unless options[:env_formatted]
|
44
|
+
plaintext = client.decrypt_env_file(file.read) if options[:env_formatted]
|
45
|
+
handle_output(plaintext)
|
46
|
+
else
|
47
|
+
data = handle_input(options)
|
48
|
+
plaintext = client.decrypt(data) unless options[:env_formatted]
|
49
|
+
plaintext = client.decrypt_env_file(data) if options[:env_formatted]
|
50
|
+
handle_output(plaintext)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
data/lib/hydan/crypto.rb
ADDED
data/lib/hydan/io.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
module Hydan
|
2
|
+
module IO
|
3
|
+
ENV_LINE_REGEX = /(.*?)=(.*)/
|
4
|
+
|
5
|
+
# Reads text from STDIN, or uses the value supplied with
|
6
|
+
# --plaintext, if any. Returns the text.
|
7
|
+
# @return [String]
|
8
|
+
def handle_input(options)
|
9
|
+
text = options[:text].join ' ' if options[:text]
|
10
|
+
unless options[:text]
|
11
|
+
text = ''
|
12
|
+
text << $LAST_READ_LINE while $stdin.gets # unless $stdin.tty?
|
13
|
+
end
|
14
|
+
# raise ArgumentError.new('No plaintext specified') if text.empty?
|
15
|
+
text
|
16
|
+
end
|
17
|
+
|
18
|
+
# Output phase of the encryption process, prints output
|
19
|
+
# to STDOUT or uses the value supplied with --out to write
|
20
|
+
# output to a file, if any.
|
21
|
+
# @return [Nil]
|
22
|
+
def handle_output(json)
|
23
|
+
File.open(options[:out], 'w') { |f| f.write json } if options[:out]
|
24
|
+
puts json unless options[:out]
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
# Handles the output phase for file encryption/decryption.
|
29
|
+
# This only concerns the encrypted data key, since file
|
30
|
+
# encryption automatically assumes that an output file is being
|
31
|
+
# used (by the library). The input encrypted_data_key is expected
|
32
|
+
# to be a binary key, *not* Base64 encoded.
|
33
|
+
def handle_key_output(encrypted_data_key_blob, out_key_file = nil)
|
34
|
+
File.open(out_key_file, 'wb') {
|
35
|
+
|f| f.write encrypted_data_key_blob
|
36
|
+
} if out_key_file
|
37
|
+
puts "Data key (Base64): #{Base64.strict_encode64(encrypted_data_key_blob)}" unless out_key_file
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
data/lib/hydan/s3.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Hydan
|
4
|
+
module S3
|
5
|
+
# S3 command class
|
6
|
+
class S3Cmd < Thor
|
7
|
+
# TODO: This still doesn't account for the S3EncryptedClient's upload/download functions, which read/write binary
|
8
|
+
# encoded files (not the JSON/base64 stuff that I'm doing here).
|
9
|
+
desc 'cp', 'Use the S3 API to copy files'
|
10
|
+
method_option :decrypt, :type => :boolean
|
11
|
+
method_option :key_alias, :type => :string
|
12
|
+
# Copies src to dest (args[0] and args[1]). Handles S3->local (download)
|
13
|
+
# and local->S3 (upload).
|
14
|
+
def cp (*args)
|
15
|
+
src, dest = args.take 2
|
16
|
+
src_type, dest_type = [S3Helper.parse_path(src), S3Helper.parse_path(dest)]
|
17
|
+
|
18
|
+
event = if src_type == PathTypes::UNIX && dest_type == PathTypes::S3
|
19
|
+
:upload
|
20
|
+
elsif src_type == PathTypes::S3 && dest_type == PathTypes::UNIX
|
21
|
+
:download
|
22
|
+
else
|
23
|
+
:invalid
|
24
|
+
end
|
25
|
+
|
26
|
+
if event == :upload
|
27
|
+
bucket, key = S3Helper.parse_s3_path dest
|
28
|
+
kms_client = Hydan::Crypto::KMS::EncryptionHelper.new
|
29
|
+
kms_key_id = kms_client.get_kms_key_id(options[:key_alias]) if options[:key_alias]
|
30
|
+
client = S3Helper.new(kms_key_id)
|
31
|
+
client.upload(bucket, key, File.open(src, 'r').read)
|
32
|
+
elsif event == :download
|
33
|
+
bucket, key = S3Helper.parse_s3_path src
|
34
|
+
client = S3Helper.new(options[:decrypt])
|
35
|
+
client.download(bucket, key, dest)
|
36
|
+
else puts "Invalid"
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
# Wrapper class surrounding common S3 functions
|
44
|
+
class S3Helper
|
45
|
+
S3_PATH_REGEX = %r{s3:\/\/(.*?)\/(.*)}
|
46
|
+
|
47
|
+
# Initialize the S3Helper object. When a kms_key_id is given, the
|
48
|
+
# client uses an Aws::S3::Encrpytion::Client. If omitted, a regular
|
49
|
+
# Aws::S3::Client is used instead.
|
50
|
+
def initialize(kms_key_id = nil)
|
51
|
+
@s3 = Aws::S3::Client.new unless kms_key_id
|
52
|
+
@s3 = Aws::S3::Encryption::Client.new(
|
53
|
+
kms_key_id: kms_key_id
|
54
|
+
) if kms_key_id
|
55
|
+
end
|
56
|
+
|
57
|
+
# Performs a PutObject operation for content at s3://bucket/key
|
58
|
+
def upload(bucket, key, body)
|
59
|
+
@s3.put_object(
|
60
|
+
bucket: bucket,
|
61
|
+
key: key,
|
62
|
+
body: body
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Downloads the object located at s3://bucket/prefix and writes
|
67
|
+
# the content to dest
|
68
|
+
def download(bucket, prefix, dest = nil)
|
69
|
+
resp = @s3.get_object(bucket: bucket, key: prefix)
|
70
|
+
content = resp.body.string
|
71
|
+
File.open(dest, 'w') { |f| f.write content } if dest
|
72
|
+
puts content unless dest
|
73
|
+
end
|
74
|
+
|
75
|
+
# Parses a given string path and returns the type
|
76
|
+
# (constants in Hydan::PathTypes)
|
77
|
+
def self.parse_path(path)
|
78
|
+
# If we "parse" it as an S3 path, we'll let the S3 client throw
|
79
|
+
# an error if it's a non-existant path
|
80
|
+
return PathTypes::S3 if path.start_with? 's3://'
|
81
|
+
return PathTypes::UNIX unless path.start_with? 's3://'
|
82
|
+
end
|
83
|
+
|
84
|
+
# Parses an S3 path and returns regex capture
|
85
|
+
# groups [bucket, key]
|
86
|
+
def self.parse_s3_path(s3_path)
|
87
|
+
s3_path.match(S3_PATH_REGEX).captures
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/hydan.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'aws-sdk'
|
2
|
+
require 'base64'
|
3
|
+
require 'English'
|
4
|
+
require 'gibberish'
|
5
|
+
require 'logger'
|
6
|
+
require 'thor'
|
7
|
+
require 'hydan/crypto/kms'
|
8
|
+
require 'hydan/crypto/kms/encrypt'
|
9
|
+
require 'hydan/crypto/kms/decrypt'
|
10
|
+
require 'hydan/path_types'
|
11
|
+
require 'hydan/io'
|
12
|
+
require 'hydan/crypto'
|
13
|
+
require 'hydan/crypto/encrypt'
|
14
|
+
require 'hydan/crypto/decrypt'
|
15
|
+
require 'hydan/s3'
|
16
|
+
require 'hydan/cli'
|
17
|
+
require 'hydan/version'
|
18
|
+
|
19
|
+
module Hydan end
|
metadata
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hydan
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Roberto Acevedo
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-05-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.11'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.11'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.10'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.10'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry-doc
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.6.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.6.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: yard
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: thor
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: aws-sdk
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '2'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '2'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: gibberish
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description: Hide your secrets.
|
140
|
+
email:
|
141
|
+
- rxacevedo@fastmail.com
|
142
|
+
executables:
|
143
|
+
- console
|
144
|
+
- hydan
|
145
|
+
- setup
|
146
|
+
extensions: []
|
147
|
+
extra_rdoc_files: []
|
148
|
+
files:
|
149
|
+
- ".gitignore"
|
150
|
+
- ".pre-commit-config.yaml"
|
151
|
+
- Dockerfile.tpl
|
152
|
+
- Gemfile
|
153
|
+
- Gemfile.lock
|
154
|
+
- README.md
|
155
|
+
- Rakefile
|
156
|
+
- bin/console
|
157
|
+
- bin/hydan
|
158
|
+
- bin/setup
|
159
|
+
- circle.yml
|
160
|
+
- hydan.gemspec
|
161
|
+
- lib/hydan.rb
|
162
|
+
- lib/hydan/cli.rb
|
163
|
+
- lib/hydan/crypto.rb
|
164
|
+
- lib/hydan/crypto/decrypt.rb
|
165
|
+
- lib/hydan/crypto/encrypt.rb
|
166
|
+
- lib/hydan/crypto/kms.rb
|
167
|
+
- lib/hydan/crypto/kms/decrypt.rb
|
168
|
+
- lib/hydan/crypto/kms/encrypt.rb
|
169
|
+
- lib/hydan/io.rb
|
170
|
+
- lib/hydan/path_types.rb
|
171
|
+
- lib/hydan/s3.rb
|
172
|
+
- lib/hydan/version.rb
|
173
|
+
homepage: https://github.com/seibelsbi/hydan
|
174
|
+
licenses:
|
175
|
+
- MIT
|
176
|
+
metadata:
|
177
|
+
allowed_push_host: https://rubygems.org
|
178
|
+
post_install_message:
|
179
|
+
rdoc_options: []
|
180
|
+
require_paths:
|
181
|
+
- lib
|
182
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - ">="
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: '0'
|
187
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
188
|
+
requirements:
|
189
|
+
- - ">="
|
190
|
+
- !ruby/object:Gem::Version
|
191
|
+
version: '0'
|
192
|
+
requirements: []
|
193
|
+
rubyforge_project:
|
194
|
+
rubygems_version: 2.4.8
|
195
|
+
signing_key:
|
196
|
+
specification_version: 4
|
197
|
+
summary: A Ruby gem for KMS and local envelope encryption.
|
198
|
+
test_files: []
|
199
|
+
has_rdoc:
|