cryptor 0.0.0 → 0.0.1
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/.rubocop.yml +4 -1
- data/.travis.yml +1 -1
- data/CHANGES.md +3 -0
- data/Gemfile +8 -0
- data/Guardfile +6 -0
- data/README.md +106 -5
- data/Rakefile +1 -1
- data/cryptor.gemspec +4 -2
- data/lib/cryptor.rb +4 -4
- data/lib/cryptor/cipher.rb +37 -0
- data/lib/cryptor/ciphers/message_encryptor.rb +46 -0
- data/lib/cryptor/ciphers/xsalsa20poly1305.rb +26 -0
- data/lib/cryptor/encoding.rb +27 -0
- data/lib/cryptor/secret_key.rb +83 -0
- data/lib/cryptor/symmetric_encryption.rb +51 -0
- data/lib/cryptor/version.rb +1 -1
- data/spec/cryptor/secret_key_spec.rb +38 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/symmetric_encryption_spec.rb +28 -0
- data/tasks/rspec.rake +8 -0
- data/tasks/rubocop.rake +1 -1
- metadata +55 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 28c23ac85c838a1017c2298d7237cba2f4a4cce2
|
4
|
+
data.tar.gz: 7176ba9550de9958c17cfebfdec784f66f236b5c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82907137a69c2f33963ea311ec7e9c8a632d4f91b4313b354bb69ec2b654c53c80906f9b57e8afcbe96b5f8fc6b17fab4baab48f12331d0e8db9437226b46485
|
7
|
+
data.tar.gz: 85e14622245d730c9ec036644a5f5c32f9f7d6bfcbb19c4e8060dab4bc3eab4864af79d56dcbdc0913057facb8df4dfc61677cdab6092928149f08268c3cd01f
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
data/CHANGES.md
ADDED
data/Gemfile
CHANGED
data/Guardfile
ADDED
data/README.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|

|
2
2
|
Cryptor
|
3
3
|
=======
|
4
|
+
[](http://badge.fury.io/rb/cryptor)
|
5
|
+
[](https://travis-ci.org/cryptosphere/cryptor)
|
6
|
+
[](https://codeclimate.com/github/cryptosphere/cryptor)
|
7
|
+
[](https://coveralls.io/r/cryptosphere/cryptor?branch=master)
|
4
8
|
|
5
9
|
A safe Ruby encryption library, designed to support features like multiple
|
6
10
|
active encryption keys and key rotation.
|
@@ -10,14 +14,19 @@ remains untamered with, even when it's in the hands of an attacker.
|
|
10
14
|
|
11
15
|
Cryptor supports two backends:
|
12
16
|
|
13
|
-
* [
|
17
|
+
* [RbNaCl::SimpleBox]: (default) authenticated symmetric encryption based on
|
18
|
+
XSalsa20+Poly1305 from [libsodium].
|
19
|
+
* [ActiveSupport::MessageEncryptor]: (Rails 4+) a bespoke authenticated
|
14
20
|
encryption scheme provided by Rails, based on AES-CBC and HMAC.
|
15
|
-
|
16
|
-
|
21
|
+
|
22
|
+
Cryptor uses the experimental [ORDO v0 message format][ordo] for serializing
|
23
|
+
encrypted messages.
|
17
24
|
|
18
25
|
[authenticated encryption]: https://en.wikipedia.org/wiki/Authenticated_encryption
|
19
|
-
[ActiveSupport::MessageEncryptor]: http://api.rubyonrails.org/classes/ActiveSupport/MessageEncryptor.html
|
20
26
|
[RbNaCl::SimpleBox]: https://github.com/cryptosphere/rbnacl/wiki/SimpleBox
|
27
|
+
[libsodium]: https://github.com/jedisct1/libsodium/
|
28
|
+
[ActiveSupport::MessageEncryptor]: http://api.rubyonrails.org/classes/ActiveSupport/MessageEncryptor.html
|
29
|
+
[ordo]: https://github.com/cryptosphere/ordo/wiki/Message-Format
|
21
30
|
|
22
31
|
## Installation
|
23
32
|
|
@@ -35,7 +44,99 @@ Or install it yourself as:
|
|
35
44
|
|
36
45
|
## Usage
|
37
46
|
|
38
|
-
|
47
|
+
To begin with, you must select a backend:
|
48
|
+
|
49
|
+
### RbNaCl (recommended)
|
50
|
+
|
51
|
+
RbNaCl is a Ruby FFI binding to libsodium, a portable state-of-the-art
|
52
|
+
cryptography library.
|
53
|
+
|
54
|
+
To use Cryptor with RbNaCl, add the following to your Gemfile:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
gem 'rbnacl-libsodium'
|
58
|
+
```
|
59
|
+
|
60
|
+
And in your Ruby program, require the following:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
require 'cryptor'
|
64
|
+
require 'cryptor/ciphers/xsalsa20poly1305'
|
65
|
+
```
|
66
|
+
|
67
|
+
### Rails (ActiveSupport::MessageEncryptor)
|
68
|
+
|
69
|
+
Cryptor can use ActiveSupport 4.0+'s `MessageEncryptor` class to encrypt
|
70
|
+
messages. This scheme uses AES-256 in CBC mode for encryption and HMAC-SHA1
|
71
|
+
to provide ciphertext integrity.
|
72
|
+
|
73
|
+
This option is only recommended if you have some compliance issues which
|
74
|
+
mandate the use of NIST ciphers or if you have problems installing
|
75
|
+
the rbnacl-libsodium gem or libsodium library for some reason.
|
76
|
+
|
77
|
+
To use Cryptor with ActiveSupport::MessageEncryptor, require the following
|
78
|
+
from a Rails 4.0+ app or other app with ActiveSupport 4.0+ bundled:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
require 'cryptor'
|
82
|
+
require 'cryptor/ciphers/message_encryptor'
|
83
|
+
```
|
84
|
+
|
85
|
+
### Authenticated Symmetric Encryption
|
86
|
+
|
87
|
+
To encrypt data with Cryptor, you must first make a secret key to encrypt it
|
88
|
+
under. Use the following for RbNaCl:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
# Make a RbNaCl secret key
|
92
|
+
secret_key = Cryptor::SymmetricEncryption.random_key(:xsalsa20poly1305)
|
93
|
+
```
|
94
|
+
|
95
|
+
or the following for ActiveSupport::MessageEncryptor:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
# Make an ActiveSupport secret key
|
99
|
+
secret_key = Cryptor::SymmetricEncryption.random_key(:message_encryptor)
|
100
|
+
```
|
101
|
+
|
102
|
+
Inspecting a secret key looks like this:
|
103
|
+
|
104
|
+
```
|
105
|
+
#<Cryptor::SecretKey:0x81438830 cipher=xsalsa20poly1305 fingerprint=ni:///sha-256;Wy8hx4...>
|
106
|
+
```
|
107
|
+
|
108
|
+
You can't actually see the secret key itself by calling `#inspect` or `#to_s`.
|
109
|
+
This is to prevent accidentally logging the secret key. Instead you can only
|
110
|
+
see the key's fingerprint, which is given as a [RFC 6920] hash URI of the secret
|
111
|
+
key's [ORDO secret URI].
|
112
|
+
|
113
|
+
To obtain the secret URI, use the `#to_secret_uri` method, which returns a string:
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
"secret.key:///xsalsa20poly1305;0saB1tfgKWDh_bX0oAquLWgAq-6yjG1u04mP-CtQG-4"
|
117
|
+
```
|
118
|
+
|
119
|
+
This string can be saved somewhere secret and safe then later loaded and passed into
|
120
|
+
`Cryptor::SymmetricEncryption.new`:
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
cryptor = Cryptor::SymmetricEncryption.new("secret.key:///xsalsa20poly1305;0saB...")
|
124
|
+
```
|
125
|
+
|
126
|
+
After this, you can encrypt with the `#encrypt` method:
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
ciphertext = cryptor.encrypt(plaintext)
|
130
|
+
```
|
131
|
+
|
132
|
+
and decrypt with the `#decrypt` method:
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
decrypted = cryptor.decrypt(ciphertext)
|
136
|
+
```
|
137
|
+
|
138
|
+
[RFC 6920]: http://tools.ietf.org/html/rfc6920
|
139
|
+
[ORDO secret URI]: https://github.com/cryptosphere/ordo/wiki/URI-Registry
|
39
140
|
|
40
141
|
## Contributing
|
41
142
|
|
data/Rakefile
CHANGED
data/cryptor.gemspec
CHANGED
@@ -19,9 +19,11 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.test_files = spec.files.grep(/^(test|spec|features)\//)
|
20
20
|
spec.require_paths = ['lib']
|
21
21
|
|
22
|
-
spec.add_runtime_dependency '
|
22
|
+
spec.add_runtime_dependency 'ordo', '>= 0.0.1'
|
23
23
|
|
24
|
-
spec.add_development_dependency 'bundler', '~> 1.6'
|
25
24
|
spec.add_development_dependency 'rake'
|
26
25
|
spec.add_development_dependency 'rubocop'
|
26
|
+
spec.add_development_dependency 'rspec'
|
27
|
+
spec.add_development_dependency 'rbnacl-libsodium'
|
28
|
+
spec.add_development_dependency 'activesupport', '>= 4.0.0'
|
27
29
|
end
|
data/lib/cryptor.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'cryptor/version'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
require 'cryptor/cipher'
|
4
|
+
require 'cryptor/encoding'
|
5
|
+
require 'cryptor/secret_key'
|
6
|
+
require 'cryptor/symmetric_encryption'
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Cryptor
|
2
|
+
# Base class of all Cryptor ciphers
|
3
|
+
class Cipher
|
4
|
+
REGISTRY = {}
|
5
|
+
|
6
|
+
attr_reader :algorithm, :key_bytes
|
7
|
+
|
8
|
+
def self.register(algorithm, options = {})
|
9
|
+
REGISTRY[algorithm.to_s] ||= new(algorithm, options)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.[](algorithm)
|
13
|
+
REGISTRY[algorithm.to_s] || fail(ArgumentError, "no such cipher: #{algorithm}")
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(algorithm, options = {})
|
17
|
+
@algorithm = algorithm
|
18
|
+
@key_bytes = options[:key_bytes] || fail(ArgumentError, 'key_bytes not specified')
|
19
|
+
end
|
20
|
+
|
21
|
+
def random_key
|
22
|
+
SecretKey.random_key(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def encrypt(_key, _plaintext)
|
26
|
+
#:nocov:
|
27
|
+
fail NotImplementedError, "'encrypt' method has not been implemented"
|
28
|
+
#:nocov:
|
29
|
+
end
|
30
|
+
|
31
|
+
def decrypt(_key, _ciphertext)
|
32
|
+
#:nocov:
|
33
|
+
fail NotImplementedError, "'decrypt' method has not been implemented"
|
34
|
+
#:nocov:
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'active_support/message_encryptor'
|
2
|
+
require 'active_support/message_verifier'
|
3
|
+
|
4
|
+
require 'cryptor/cipher'
|
5
|
+
|
6
|
+
module Cryptor
|
7
|
+
module Ciphers
|
8
|
+
# MessageEncryptor is a bespoke authenticated encryption scheme invented
|
9
|
+
# by rails-core. It uses AES-256-CBC and HMAC-SHA1 in an encrypt-then-MAC
|
10
|
+
# scheme with a wacky and wild semiconstant-time MAC comparison.
|
11
|
+
|
12
|
+
# Cryptor enforces the usage of independent keys for AES encryption and HMAC
|
13
|
+
# by mandating a 64-byte key (using 32-bytes for AES and 32-bytes for HMAC).
|
14
|
+
#
|
15
|
+
# This scheme is probably safe to use, but less interoperable and more
|
16
|
+
# poorly designed than xsalsa20poly1305 from RbNaCl. It does, however,
|
17
|
+
# work using only ActiveSupport and the Ruby OpenSSL extension as
|
18
|
+
# dependencies, and should be available anywhere.
|
19
|
+
#
|
20
|
+
# For the time being, this scheme is only supported for ActiveSupport 4.0+
|
21
|
+
# although support for earlier versions of ActiveSupport should be
|
22
|
+
# possible.
|
23
|
+
class MessageEncryptor < Cipher
|
24
|
+
SERIALIZER = ActiveSupport::MessageEncryptor::NullSerializer
|
25
|
+
KEY_BYTES = 64
|
26
|
+
|
27
|
+
register :message_encryptor, key_bytes: KEY_BYTES
|
28
|
+
|
29
|
+
def encrypt(key, plaintext)
|
30
|
+
encryptor(key).encrypt_and_sign(plaintext)
|
31
|
+
end
|
32
|
+
|
33
|
+
def decrypt(key, ciphertext)
|
34
|
+
encryptor(key).decrypt_and_verify(ciphertext)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def encryptor(key)
|
40
|
+
fail ArgumentError, "wrong key size: #{key.bytesize}" unless key.bytesize == KEY_BYTES
|
41
|
+
encryption_key, hmac_key = key[0, 32], key[32, 32]
|
42
|
+
ActiveSupport::MessageEncryptor.new(encryption_key, hmac_key, serializer: SERIALIZER)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rbnacl/libsodium'
|
2
|
+
|
3
|
+
require 'cryptor/cipher'
|
4
|
+
|
5
|
+
module Cryptor
|
6
|
+
module Ciphers
|
7
|
+
# XSalsa20+Poly1305 authenticated stream cipher
|
8
|
+
class XSalsa20Poly1305 < Cipher
|
9
|
+
register :xsalsa20poly1305, key_bytes: RbNaCl::SecretBoxes::XSalsa20Poly1305.key_bytes
|
10
|
+
|
11
|
+
def encrypt(key, plaintext)
|
12
|
+
box(key).encrypt(plaintext)
|
13
|
+
end
|
14
|
+
|
15
|
+
def decrypt(key, ciphertext)
|
16
|
+
box(key).decrypt(ciphertext)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def box(key)
|
22
|
+
RbNaCl::SimpleBox.new(RbNaCl::SecretBoxes::XSalsa20Poly1305.new(key))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Cryptor
|
4
|
+
# Encode and parse strings in "URL-safe" Base64 format
|
5
|
+
module Encoding
|
6
|
+
module_function
|
7
|
+
|
8
|
+
# Encode a string in unpadded URL-safe Base64
|
9
|
+
#
|
10
|
+
# @param string [String] arbitrary string to be encoded
|
11
|
+
# @return [String] URL-safe Base64 encoded string (sans '=' padding)
|
12
|
+
def encode(string)
|
13
|
+
Base64.urlsafe_encode64(string).sub(/=*$/, '')
|
14
|
+
end
|
15
|
+
|
16
|
+
# Decode an unpadded URL-safe Base64 string
|
17
|
+
#
|
18
|
+
# @param string [String] URL-safe Base64 string to be decoded (sans '=' padding)
|
19
|
+
# @return [String] decoded string
|
20
|
+
def decode(string)
|
21
|
+
padding_size = string.bytesize % 4
|
22
|
+
padded_string = padding_size > 0 ? string + '=' * (4 - padding_size) : string
|
23
|
+
|
24
|
+
Base64.urlsafe_decode64(padded_string)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'digest/sha2'
|
3
|
+
|
4
|
+
module Cryptor
|
5
|
+
# Secret key used to encrypt plaintexts
|
6
|
+
class SecretKey
|
7
|
+
attr_reader :cipher
|
8
|
+
|
9
|
+
# Generate a random secret key
|
10
|
+
#
|
11
|
+
# @param [Cryptor::Cipher, Symbol] Cryptor::Cipher or algorithm name as a symbol
|
12
|
+
#
|
13
|
+
# @return [Cryptor::SecretKey] new secret key object
|
14
|
+
def self.random_key(cipher)
|
15
|
+
cipher = Cryptor::Cipher[cipher] if cipher.is_a? Symbol
|
16
|
+
fail ArgumentError, "invalid cipher: #{cipher.inspect}" unless cipher.is_a? Cryptor::Cipher
|
17
|
+
bytes = RbNaCl::Random.random_bytes(cipher.key_bytes)
|
18
|
+
base64 = Cryptor::Encoding.encode(bytes)
|
19
|
+
|
20
|
+
new "secret.key:///#{cipher.algorithm};#{base64}"
|
21
|
+
end
|
22
|
+
|
23
|
+
# Create a new SecretKey object from a URI
|
24
|
+
#
|
25
|
+
# @param [#to_s] uri representing a secret key
|
26
|
+
#
|
27
|
+
# @raise [ArgumentError] on invalid URIs
|
28
|
+
#
|
29
|
+
# @return [Cryptor::SecretKey] new secret key object
|
30
|
+
def initialize(uri_string)
|
31
|
+
uri = URI.parse(uri_string.to_s)
|
32
|
+
fail ArgumentError, "invalid scheme: #{uri.scheme}" unless uri.scheme == 'secret.key'
|
33
|
+
|
34
|
+
components = uri.path.match(/^\/([^;]+);(.+)$/)
|
35
|
+
fail ArgumentError, "couldn't parse cipher name from secret URI" unless components
|
36
|
+
|
37
|
+
@cipher = Cryptor::Cipher[components[1]]
|
38
|
+
@secret_key = Cryptor::Encoding.decode(components[2])
|
39
|
+
end
|
40
|
+
|
41
|
+
# Serialize SecretKey object to a URI
|
42
|
+
#
|
43
|
+
# @return [String] serialized URI representing the key
|
44
|
+
def to_secret_uri
|
45
|
+
"secret.key:///#{@cipher.algorithm};#{Cryptor::Encoding.encode(@secret_key)}"
|
46
|
+
end
|
47
|
+
|
48
|
+
# Fingerprint of this key's secret URI
|
49
|
+
#
|
50
|
+
# @return [String] fingerprint as a ni:// URL
|
51
|
+
def fingerprint
|
52
|
+
digest = Digest::SHA256.digest(to_secret_uri)
|
53
|
+
"ni:///sha-256;#{Cryptor::Encoding.encode(digest)}"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Encrypt a plaintext under this key
|
57
|
+
#
|
58
|
+
# @param [String] plaintext string to be encrypted
|
59
|
+
#
|
60
|
+
# @return [String] ciphertext encrypted under this key
|
61
|
+
def encrypt(plaintext)
|
62
|
+
@cipher.encrypt(@secret_key, plaintext)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Decrypt ciphertext using this key
|
66
|
+
#
|
67
|
+
# @param [String] ciphertext string to be decrypted
|
68
|
+
#
|
69
|
+
# @return [String] plaintext decrypted from the given ciphertext
|
70
|
+
def decrypt(ciphertext)
|
71
|
+
@cipher.decrypt(@secret_key, ciphertext)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Inspect this key
|
75
|
+
#
|
76
|
+
# @return [String] a string representing this key
|
77
|
+
def inspect
|
78
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} " \
|
79
|
+
"cipher=#{cipher.algorithm} " \
|
80
|
+
"fingerprint=#{fingerprint}>"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'ordo'
|
2
|
+
|
3
|
+
require 'cryptor/version'
|
4
|
+
require 'cryptor/cipher'
|
5
|
+
|
6
|
+
module Cryptor
|
7
|
+
# Easy-to-use authenticated symmetric encryption
|
8
|
+
class SymmetricEncryption
|
9
|
+
def self.random_key(cipher)
|
10
|
+
Cipher[cipher].random_key
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(key)
|
14
|
+
@key = key
|
15
|
+
end
|
16
|
+
|
17
|
+
def encrypt(plaintext)
|
18
|
+
ciphertext = @key.encrypt(plaintext)
|
19
|
+
base64 = Base64.strict_encode64(ciphertext)
|
20
|
+
|
21
|
+
ORDO::Message.new(
|
22
|
+
base64,
|
23
|
+
'Cipher' => @key.cipher.algorithm,
|
24
|
+
'Content-Length' => base64.bytesize,
|
25
|
+
'Content-Transfer-Encoding' => 'base64',
|
26
|
+
'Key-Fingerprint' => @key.fingerprint
|
27
|
+
).to_string
|
28
|
+
end
|
29
|
+
|
30
|
+
def decrypt(ciphertext)
|
31
|
+
message = ORDO::Message.parse(ciphertext)
|
32
|
+
fingerprint = message['Key-Fingerprint']
|
33
|
+
|
34
|
+
fail ArgumentError, "no key configured for: #{fingerprint}" if @key.fingerprint != fingerprint
|
35
|
+
|
36
|
+
@key.decrypt decode(message)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def decode(message)
|
42
|
+
encoding = message['Content-Transfer-Encoding']
|
43
|
+
|
44
|
+
case encoding
|
45
|
+
when 'base64' then Base64.strict_decode64(message.body)
|
46
|
+
when 'binary' then message.body
|
47
|
+
else fail ArgumentError, "invalid message encoding: #{encoding}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/cryptor/version.rb
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cryptor::SecretKey do
|
4
|
+
let(:algorithm) { :BassOmatic }
|
5
|
+
let(:key_bytes) { 42 }
|
6
|
+
let(:cipher) { Cryptor::Cipher.new(algorithm, key_bytes: key_bytes) }
|
7
|
+
let(:secret_key) { "\xBA\x55" }
|
8
|
+
let(:secret_uri) { "secret.key:///#{algorithm};#{Cryptor::Encoding.encode(secret_key)}" }
|
9
|
+
|
10
|
+
before do
|
11
|
+
allow(Cryptor::Cipher).to receive(:[]).and_return(cipher)
|
12
|
+
end
|
13
|
+
|
14
|
+
subject { described_class.new(secret_uri) }
|
15
|
+
|
16
|
+
it 'generates random keys' do
|
17
|
+
expect(described_class.random_key(algorithm)).to be_a described_class
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'serializes to a URI' do
|
21
|
+
expect(subject.to_secret_uri).to eq secret_uri
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'serializes to a key fingerprint' do
|
25
|
+
expect(URI(subject.fingerprint).scheme).to eq 'ni'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'inspects without revealing the secret key' do
|
29
|
+
expect(subject.inspect).not_to include(secret_key)
|
30
|
+
expect(subject.inspect).not_to include(Cryptor::Encoding.encode(secret_key))
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'raises ArgumentError if given a bogus URI' do
|
34
|
+
expect do
|
35
|
+
described_class.new('http://www.google.com/')
|
36
|
+
end.to raise_exception(ArgumentError)
|
37
|
+
end
|
38
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cryptor::SymmetricEncryption do
|
4
|
+
let(:plaintext) { 'THE MAGIC WORDS ARE SQUEAMISH OSSIFRAGE' }
|
5
|
+
subject { described_class.new(secret_key) }
|
6
|
+
|
7
|
+
context 'xsalsa20poly1305' do
|
8
|
+
require 'cryptor/ciphers/xsalsa20poly1305'
|
9
|
+
|
10
|
+
let(:secret_key) { described_class.random_key(:xsalsa20poly1305) }
|
11
|
+
|
12
|
+
it 'encrypts and decrypts' do
|
13
|
+
ciphertext = subject.encrypt(plaintext)
|
14
|
+
expect(subject.decrypt(ciphertext)).to eq plaintext
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'message_encryptor' do
|
19
|
+
require 'cryptor/ciphers/message_encryptor'
|
20
|
+
|
21
|
+
let(:secret_key) { described_class.random_key(:message_encryptor) }
|
22
|
+
|
23
|
+
it 'encrypts and decrypts' do
|
24
|
+
ciphertext = subject.encrypt(plaintext)
|
25
|
+
expect(subject.decrypt(ciphertext)).to eq plaintext
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/tasks/rspec.rake
ADDED
data/tasks/rubocop.rake
CHANGED
metadata
CHANGED
@@ -1,45 +1,59 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cryptor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Arcieri
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-06-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: ordo
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.0.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
23
37
|
requirements:
|
24
38
|
- - ">="
|
25
39
|
- !ruby/object:Gem::Version
|
26
40
|
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
42
|
+
name: rubocop
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
|
-
- - "
|
45
|
+
- - ">="
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
47
|
+
version: '0'
|
34
48
|
type: :development
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
|
-
- - "
|
52
|
+
- - ">="
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
56
|
+
name: rspec
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - ">="
|
@@ -53,7 +67,7 @@ dependencies:
|
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
70
|
+
name: rbnacl-libsodium
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
73
|
- - ">="
|
@@ -66,6 +80,20 @@ dependencies:
|
|
66
80
|
- - ">="
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: activesupport
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 4.0.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 4.0.0
|
69
97
|
description: A safe Ruby encryption library, designed to support features likemultiple
|
70
98
|
active encryption keys and key rotation
|
71
99
|
email:
|
@@ -78,14 +106,26 @@ files:
|
|
78
106
|
- ".rspec"
|
79
107
|
- ".rubocop.yml"
|
80
108
|
- ".travis.yml"
|
109
|
+
- CHANGES.md
|
81
110
|
- Gemfile
|
111
|
+
- Guardfile
|
82
112
|
- LICENSE.txt
|
83
113
|
- README.md
|
84
114
|
- Rakefile
|
85
115
|
- cryptor.gemspec
|
86
116
|
- cryptosaur.png
|
87
117
|
- lib/cryptor.rb
|
118
|
+
- lib/cryptor/cipher.rb
|
119
|
+
- lib/cryptor/ciphers/message_encryptor.rb
|
120
|
+
- lib/cryptor/ciphers/xsalsa20poly1305.rb
|
121
|
+
- lib/cryptor/encoding.rb
|
122
|
+
- lib/cryptor/secret_key.rb
|
123
|
+
- lib/cryptor/symmetric_encryption.rb
|
88
124
|
- lib/cryptor/version.rb
|
125
|
+
- spec/cryptor/secret_key_spec.rb
|
126
|
+
- spec/spec_helper.rb
|
127
|
+
- spec/symmetric_encryption_spec.rb
|
128
|
+
- tasks/rspec.rake
|
89
129
|
- tasks/rubocop.rake
|
90
130
|
homepage: https://github.com/cryptosphere/cryptor
|
91
131
|
licenses:
|
@@ -111,4 +151,7 @@ rubygems_version: 2.2.2
|
|
111
151
|
signing_key:
|
112
152
|
specification_version: 4
|
113
153
|
summary: An easy-to-use library for real-world Ruby cryptography
|
114
|
-
test_files:
|
154
|
+
test_files:
|
155
|
+
- spec/cryptor/secret_key_spec.rb
|
156
|
+
- spec/spec_helper.rb
|
157
|
+
- spec/symmetric_encryption_spec.rb
|