master-crypt 0.0.1 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +79 -3
- data/lib/master_crypt.rb +66 -8
- data/lib/master_crypt/version.rb +2 -2
- metadata +31 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a11aff2304c6483147f3e7cc3d57996af1a4fd0ba730f10d3e130fec9730562
|
4
|
+
data.tar.gz: 86bfd58ddc3e941d344ea0ccb83ae608305ba86e5118243e19e47d9e306f1a20
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 47d07d1d36a509892ee5cc4e05794233ef6d37c6f866896133a89abc92a0ed5af30dc2c1a7640925b71ff2f0dba5c52d378c66e3926d9c85cf5249df87ea663a
|
7
|
+
data.tar.gz: a8e56a738b7afc220129eb7bd7dd5762b10a8a8cf09285327917a83e68b2b167ee18725c903c2a232b4f4b67c16849aae64e9937817a11513ff9548d4a0a6bbb
|
data/README.md
CHANGED
@@ -1,12 +1,88 @@
|
|
1
|
-
#
|
1
|
+
# Master Crypt
|
2
|
+
|
3
|
+
[](https://circleci.com/gh/cianmce/master-crypt)
|
4
|
+
|
5
|
+
Master Key is a gem for encrypting data with a [master keying](https://en.wikipedia.org/wiki/Master_keying) approach
|
6
|
+
|
7
|
+
This allows you to have a master key to decrypt the full set of data while also creating keys that can only decrypt a subset of data. These keys can then be safely distributed to relevant actors who will be only able to access their permitted data
|
8
|
+
|
9
|
+
You can encrypt data with as many keys as needed, all of which will be able to decrypt the data while only causing a small encrypted data size increase of 129 bytes for each extra key
|
10
|
+
|
11
|
+
|
12
|
+
## Installatio
|
13
|
+
### Installing RbNaCl
|
14
|
+
https://github.com/RubyCrypto/rbnacl#installation
|
15
|
+
|
16
|
+
#### OS X users
|
17
|
+
```sh
|
18
|
+
brew install libsodium
|
19
|
+
```
|
20
|
+
|
21
|
+
#### FreeBSD users
|
22
|
+
```sh
|
23
|
+
pkg install libsodium
|
24
|
+
```
|
25
|
+
|
26
|
+
#### APT users
|
27
|
+
|
28
|
+
```sh
|
29
|
+
apt install libsodium-dev
|
30
|
+
```
|
31
|
+
|
32
|
+
### Installing MasterCrypt
|
33
|
+
```sh
|
34
|
+
gem install master_crypt
|
35
|
+
```
|
36
|
+
|
37
|
+
## Usage
|
38
|
+
### Encrypting data with a master key
|
39
|
+
```ruby
|
40
|
+
require "master_crypt"
|
41
|
+
|
42
|
+
master_key = "Very secure & random master k3y"
|
43
|
+
other_secret_key = "Another very secure & random other k3y"
|
44
|
+
plaintext = "Secret data..."
|
45
|
+
master_crypt = MasterCrypt.new(master_key)
|
46
|
+
|
47
|
+
encrypted_data = master_crypt.master_key_encrypt(plaintext, [other_secret_key])
|
48
|
+
# encrypted_data can be decrypted with either the master_key or other_secret_key
|
49
|
+
```
|
50
|
+
|
51
|
+
### Decrypting data with a master key
|
52
|
+
```ruby
|
53
|
+
master_key = "Very secure & random master k3y"
|
54
|
+
encrypted_data = "...."
|
55
|
+
master_crypt = MasterCrypt.new(master_key)
|
56
|
+
|
57
|
+
plaintext = master_crypt.master_key_decrypt(encrypted_data)
|
58
|
+
```
|
59
|
+
|
60
|
+
### Encrypting data with an array of keys
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
secret_keys = ["array", "of", "secret", "keys"]
|
64
|
+
encrypted_data = MasterCrypt.encrypt(plaintext, secret_keys)
|
65
|
+
```
|
66
|
+
|
67
|
+
### Decrypting data with a specific key
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
MasterCrypt.decrypt(encrypted_data, secret_keys[0])
|
71
|
+
```
|
2
72
|
|
3
73
|
## Development
|
4
74
|
```sh
|
5
75
|
bundle install
|
6
76
|
```
|
7
77
|
|
8
|
-
###
|
9
|
-
|
78
|
+
### Run all specs + standardrb
|
79
|
+
|
10
80
|
```sh
|
81
|
+
bundle exec rake
|
82
|
+
```
|
11
83
|
|
84
|
+
### Run specs using guard
|
85
|
+
|
86
|
+
```sh
|
87
|
+
bundle exec guard
|
12
88
|
```
|
data/lib/master_crypt.rb
CHANGED
@@ -3,11 +3,59 @@ require "rbnacl"
|
|
3
3
|
require "base64"
|
4
4
|
require "digest"
|
5
5
|
|
6
|
-
|
6
|
+
class MasterCrypt
|
7
|
+
class CryptoError < StandardError; end
|
8
|
+
|
9
|
+
# Create a new MasterCrypt object with a master key which
|
10
|
+
# is used in `master_key_encrypt` and `master_key_decrypt`
|
11
|
+
#
|
12
|
+
# @param [String] Master key
|
13
|
+
#
|
14
|
+
# @return [MasterCrypt] A MasterCrypt object with a stored master key
|
15
|
+
def initialize(master_key)
|
16
|
+
raise ArgumentError, "Master key must not be blank" if master_key.nil? || master_key.empty?
|
17
|
+
@master_key = master_key
|
18
|
+
end
|
19
|
+
|
20
|
+
# Encrypts plaintext data with the master key and an optional
|
21
|
+
# list of additional secret keys
|
22
|
+
#
|
23
|
+
# @param plaintext [String] Plaintext data to be encrypted
|
24
|
+
# @param secret_keys [Array<String>] Optional list of
|
25
|
+
# additional secret keys to be used for encrypting data
|
26
|
+
#
|
27
|
+
# @return [String] Base64 representation of encrypted data
|
28
|
+
#
|
29
|
+
# @raise [ArgumentError] When secret keys are missing or blank
|
30
|
+
def master_key_encrypt(plaintext, secret_keys = [])
|
31
|
+
self.class.encrypt(plaintext, [@master_key] + Array(secret_keys))
|
32
|
+
end
|
33
|
+
|
34
|
+
# Decrypts encrypted data with the master key
|
35
|
+
#
|
36
|
+
# @param encrypted_data [String] Base64 representation of encrypted data
|
37
|
+
#
|
38
|
+
# @return [String] Decrypted plaintext data
|
39
|
+
#
|
40
|
+
# @raise [MasterCrypt::CryptoError] When master key or encrypted data is invalid
|
41
|
+
def master_key_decrypt(encrypted_data)
|
42
|
+
self.class.decrypt(encrypted_data, @master_key)
|
43
|
+
end
|
44
|
+
|
7
45
|
class << self
|
8
|
-
|
9
|
-
|
10
|
-
|
46
|
+
# Encrypts plaintext data with a list of secret keys
|
47
|
+
#
|
48
|
+
# @param plaintext [String] Plaintext data to be encrypted
|
49
|
+
# @param secret_keys [Array<String>] A list of secret keys to be used for encrypting data
|
50
|
+
#
|
51
|
+
# @return [String] Base64 representation of encrypted data
|
52
|
+
#
|
53
|
+
# @raise [ArgumentError] When secret keys are missing or blank
|
54
|
+
def encrypt(plaintext, secret_keys)
|
55
|
+
raise ArgumentError, "At least 1 secret key is required" if !secret_keys.is_a?(Array) || secret_keys.empty?
|
56
|
+
raise ArgumentError, "Secret keys must not be blank" unless secret_keys.select(&:empty?).empty?
|
57
|
+
# there's no point in using the same key multiple times
|
58
|
+
secret_keys.uniq!
|
11
59
|
|
12
60
|
random_key = RbNaCl::Random.random_bytes(RbNaCl::SecretBox.key_bytes)
|
13
61
|
# encrypt data with random_key
|
@@ -17,7 +65,7 @@ module MasterCrypt
|
|
17
65
|
encrypted_data64 = Base64.strict_encode64(encrypted_data)
|
18
66
|
|
19
67
|
# encrypt random_kets with each secret
|
20
|
-
encrypted_random_keys64 =
|
68
|
+
encrypted_random_keys64 = secret_keys.collect do |secret|
|
21
69
|
key = generate_key_from_secret(secret)
|
22
70
|
box = RbNaCl::SimpleBox.from_secret_key(key)
|
23
71
|
|
@@ -29,16 +77,26 @@ module MasterCrypt
|
|
29
77
|
Base64.strict_encode64(encrypted_data64 + "|" + encrypted_random_keys64.join(":"))
|
30
78
|
end
|
31
79
|
|
32
|
-
|
80
|
+
# Decrypts encrypted data with a provided secret key
|
81
|
+
#
|
82
|
+
# @param encrypted_data [String] Base64 representation of encrypted data
|
83
|
+
# @param secret_key [String] Secret key to be used to decrypt data
|
84
|
+
#
|
85
|
+
# @return [String] Decrypted plaintext data
|
86
|
+
#
|
87
|
+
# @raise [MasterCrypt::CryptoError] When secret key or encrypted data is invalid
|
88
|
+
def decrypt(encrypted_data, secret_key)
|
33
89
|
encrypted_data64, encrypted_random_keys64_joined = Base64.strict_decode64(encrypted_data).split("|", 2)
|
34
90
|
|
35
91
|
encrypted_random_keys64 = encrypted_random_keys64_joined.split(":")
|
36
|
-
key = find_key(encrypted_random_keys64,
|
92
|
+
key = find_key(encrypted_random_keys64, secret_key)
|
37
93
|
|
38
94
|
box = RbNaCl::SimpleBox.from_secret_key(key)
|
39
95
|
|
40
96
|
ciphertext = Base64.strict_decode64(encrypted_data64)
|
41
97
|
box.decrypt(ciphertext).force_encoding(Encoding::UTF_8)
|
98
|
+
rescue RbNaCl::CryptoError => e
|
99
|
+
raise CryptoError, e.message
|
42
100
|
end
|
43
101
|
|
44
102
|
private
|
@@ -55,7 +113,7 @@ module MasterCrypt
|
|
55
113
|
rescue RbNaCl::CryptoError
|
56
114
|
end
|
57
115
|
end
|
58
|
-
raise "
|
116
|
+
raise CryptoError, "Invalid secret key '#{secret}'"
|
59
117
|
end
|
60
118
|
|
61
119
|
def generate_key_from_secret(secret)
|
data/lib/master_crypt/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = "0.0.
|
1
|
+
class MasterCrypt
|
2
|
+
VERSION = "0.0.5"
|
3
3
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: master-crypt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cian McElhinney
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-08-
|
11
|
+
date: 2021-08-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: standardrb
|
@@ -28,16 +28,16 @@ dependencies:
|
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '1'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '1'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -70,86 +70,86 @@ dependencies:
|
|
70
70
|
name: pry
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
75
|
+
version: 0.13.0
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - "
|
80
|
+
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
82
|
+
version: 0.13.0
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: pry-byebug
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
89
|
+
version: '3.9'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
96
|
+
version: '3.9'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: pry-doc
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
103
|
+
version: '1.1'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
110
|
+
version: '1.1'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: guard
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - "
|
115
|
+
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
117
|
+
version: '2.18'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - "
|
122
|
+
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
124
|
+
version: '2.18'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: guard-rspec
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- - "
|
129
|
+
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version:
|
131
|
+
version: 4.7.3
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- - "
|
136
|
+
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version:
|
138
|
+
version: 4.7.3
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: rbnacl
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
|
-
- -
|
143
|
+
- - "~>"
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version: 7.
|
145
|
+
version: '7.0'
|
146
146
|
type: :runtime
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
|
-
- -
|
150
|
+
- - "~>"
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
version: 7.
|
152
|
+
version: '7.0'
|
153
153
|
description: Allows for encrypting a single piece of data with multiple keys which
|
154
154
|
can each be used to decrypt it
|
155
155
|
email:
|
@@ -176,7 +176,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
176
176
|
requirements:
|
177
177
|
- - ">="
|
178
178
|
- !ruby/object:Gem::Version
|
179
|
-
version:
|
179
|
+
version: 2.3.0
|
180
180
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
181
181
|
requirements:
|
182
182
|
- - ">="
|