aead 1.8.0 → 1.8.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/.travis.yml +2 -1
- data/Gemfile +0 -4
- data/README.md +2 -2
- data/VERSION +1 -1
- data/aead.gemspec +3 -1
- data/ext/openssl/cipher/aead/aead.c +23 -4
- data/lib/aead/cipher.rb +9 -10
- data/lib/aead/cipher/aes_128_gcm.rb +17 -0
- data/lib/aead/cipher/aes_256_gcm.rb +5 -33
- data/lib/aead/cipher/aes_gcm.rb +51 -0
- data/lib/aead/cipher/aes_hmac.rb +5 -2
- data/spec/aead/cipher/aes_128_gcm_spec.rb +149 -0
- data/spec/aead/cipher/aes_256_cbc_hmac_sha_256_spec.rb +7 -5
- data/spec/aead/cipher/aes_256_ctr_hmac_sha_256_spec.rb +3 -3
- data/spec/aead/cipher/aes_256_gcm_spec.rb +20 -4
- metadata +14 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec5ecf304323da8bdc09bf7d9e4c7aabff91f9d9
|
4
|
+
data.tar.gz: 78b797df1043c60fbd5a63be09a0988f981577c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f6c7ca97b9619a9c31b05d8a9ada8388e923822f2735ab68c0d8e590e59a1781e0ca69f3d79a05adcc04527ba5e3e8c71fe08c75c8c21df66c6a7e22dea946b4
|
7
|
+
data.tar.gz: fa86c9dd50321c3c0f5f15811636bab1db7976b7bc32bbd900756d3ac477d0f0e0630a2e6bb1c7ee62d4b5040f11a5ab5230e6b9de7e87b8edcb3c2f2dd2203c
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -23,8 +23,8 @@ require 'aead'
|
|
23
23
|
|
24
24
|
# currently, AES-256-GCM and AES-256-CTR-HMAC-SHA-256 are supported
|
25
25
|
mode = AEAD::Cipher.new('AES-256-GCM')
|
26
|
-
key =
|
27
|
-
nonce =
|
26
|
+
key = mode.generate_key
|
27
|
+
nonce = mode.generate_nonce
|
28
28
|
|
29
29
|
cipher = mode.new(key)
|
30
30
|
aead = cipher.encrypt(nonce, 'authentication data', 'plaintext')
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.8.
|
1
|
+
1.8.1
|
data/aead.gemspec
CHANGED
@@ -17,6 +17,8 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.executables = `git ls-files -- script/*`.split("\n").map {|e| e[7..-1] }
|
18
18
|
gem.test_files = `git ls-files -- spec/*` .split("\n")
|
19
19
|
|
20
|
+
gem.licenses = ['MIT']
|
21
|
+
|
20
22
|
gem.add_dependency 'macaddr', '~> 1'
|
21
23
|
|
22
24
|
gem.add_development_dependency 'bundler'
|
@@ -25,7 +27,7 @@ Gem::Specification.new do |gem|
|
|
25
27
|
gem.add_development_dependency 'guard-minitest'
|
26
28
|
gem.add_development_dependency 'guard-yard'
|
27
29
|
gem.add_development_dependency 'markdown'
|
28
|
-
gem.add_development_dependency 'minitest'
|
30
|
+
gem.add_development_dependency 'minitest', '~> 4'
|
29
31
|
gem.add_development_dependency 'rake'
|
30
32
|
gem.add_development_dependency 'redcarpet'
|
31
33
|
gem.add_development_dependency 'simplecov'
|
@@ -126,6 +126,24 @@ ossl_cipher_set_tag(VALUE self, VALUE data)
|
|
126
126
|
return data;
|
127
127
|
}
|
128
128
|
|
129
|
+
static VALUE
|
130
|
+
ossl_cipher_set_iv_length(VALUE self, VALUE iv_length)
|
131
|
+
{
|
132
|
+
EVP_CIPHER_CTX *ctx;
|
133
|
+
int ivlen = NUM2INT(iv_length);
|
134
|
+
|
135
|
+
GetCipher(self, ctx);
|
136
|
+
|
137
|
+
#ifndef EVP_CTRL_GCM_SET_IVLEN
|
138
|
+
ossl_raise(eCipherError, "your version of OpenSSL doesn't support GCM");
|
139
|
+
#else
|
140
|
+
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, ivlen, NULL))
|
141
|
+
ossl_raise(eCipherError, NULL);
|
142
|
+
#endif
|
143
|
+
|
144
|
+
return iv_length;
|
145
|
+
}
|
146
|
+
|
129
147
|
static VALUE
|
130
148
|
ossl_cipher_verify(VALUE self)
|
131
149
|
{
|
@@ -149,8 +167,9 @@ Init_aead(void)
|
|
149
167
|
|
150
168
|
eCipherError = rb_define_class_under(mOSSLCipher, "CipherError", eOSSLError);
|
151
169
|
|
152
|
-
rb_define_method(mOSSLCipher, "aad=",
|
153
|
-
rb_define_method(mOSSLCipher, "gcm_tag",
|
154
|
-
rb_define_method(mOSSLCipher, "gcm_tag=",
|
155
|
-
rb_define_method(mOSSLCipher, "
|
170
|
+
rb_define_method(mOSSLCipher, "aad=", ossl_cipher_set_aad, 1);
|
171
|
+
rb_define_method(mOSSLCipher, "gcm_tag", ossl_cipher_get_tag, 0);
|
172
|
+
rb_define_method(mOSSLCipher, "gcm_tag=", ossl_cipher_set_tag, 1);
|
173
|
+
rb_define_method(mOSSLCipher, "gcm_iv_len=", ossl_cipher_set_iv_length, 1);
|
174
|
+
rb_define_method(mOSSLCipher, "verify", ossl_cipher_verify, 0);
|
156
175
|
}
|
data/lib/aead/cipher.rb
CHANGED
@@ -8,6 +8,7 @@ require 'securerandom'
|
|
8
8
|
# Wraps AEAD ciphers in a simplified interface.
|
9
9
|
#
|
10
10
|
class AEAD::Cipher
|
11
|
+
autoload :AES_128_GCM, 'aead/cipher/aes_128_gcm'
|
11
12
|
autoload :AES_256_GCM, 'aead/cipher/aes_256_gcm'
|
12
13
|
autoload :AES_256_CBC_HMAC_SHA_256, 'aead/cipher/aes_256_cbc_hmac_sha_256'
|
13
14
|
autoload :AES_256_CTR_HMAC_SHA_256, 'aead/cipher/aes_256_ctr_hmac_sha_256'
|
@@ -18,7 +19,7 @@ class AEAD::Cipher
|
|
18
19
|
# @param [String] algorithm the AEAD implementation to use
|
19
20
|
# @return [Class] the cipher implementation
|
20
21
|
#
|
21
|
-
def self.new(algorithm)
|
22
|
+
def self.new(algorithm, options = {})
|
22
23
|
# run normal Class#new if we're being called from a subclass
|
23
24
|
return super unless self == AEAD::Cipher
|
24
25
|
|
@@ -110,7 +111,6 @@ class AEAD::Cipher
|
|
110
111
|
#
|
111
112
|
def encrypt(nonce, aad, plaintext)
|
112
113
|
_verify_nonce_bytesize(nonce, self.nonce_len)
|
113
|
-
_verify_plaintext_presence(plaintext)
|
114
114
|
|
115
115
|
self._encrypt(
|
116
116
|
_pad_nonce(nonce),
|
@@ -147,6 +147,8 @@ class AEAD::Cipher
|
|
147
147
|
# The secret key provided by the user.
|
148
148
|
attr_accessor :key
|
149
149
|
|
150
|
+
attr_writer :iv_len
|
151
|
+
|
150
152
|
#
|
151
153
|
# Initializes the cipher.
|
152
154
|
#
|
@@ -155,12 +157,14 @@ class AEAD::Cipher
|
|
155
157
|
# @param [String] key the encryption key supplied by the user
|
156
158
|
# @return [Cipher] the initialized Cipher
|
157
159
|
#
|
158
|
-
def initialize(algorithm, key)
|
160
|
+
def initialize(algorithm, key, options = {})
|
159
161
|
_verify_key_bytesize(key, self.key_len)
|
160
162
|
|
161
163
|
self.algorithm = algorithm.dup.freeze
|
162
164
|
self.key = key.dup.freeze
|
163
165
|
|
166
|
+
self.iv_len = options[:iv_len]
|
167
|
+
|
164
168
|
self.freeze
|
165
169
|
end
|
166
170
|
|
@@ -178,7 +182,7 @@ class AEAD::Cipher
|
|
178
182
|
# @return [Integer] the length of initialization vectors in bytes
|
179
183
|
#
|
180
184
|
def iv_len
|
181
|
-
self.class.iv_len
|
185
|
+
@iv_len || self.class.iv_len
|
182
186
|
end
|
183
187
|
|
184
188
|
#
|
@@ -201,15 +205,10 @@ class AEAD::Cipher
|
|
201
205
|
end
|
202
206
|
|
203
207
|
def _verify_nonce_bytesize(nonce, nonce_len)
|
204
|
-
raise ArgumentError, "nonce must be
|
208
|
+
raise ArgumentError, "nonce must be #{nonce_len} bytes" unless
|
205
209
|
nonce.bytesize == nonce_len
|
206
210
|
end
|
207
211
|
|
208
|
-
def _verify_plaintext_presence(plaintext)
|
209
|
-
raise ArgumentError, 'plaintext must not be empty' unless
|
210
|
-
not plaintext.nil? and not plaintext.empty?
|
211
|
-
end
|
212
|
-
|
213
212
|
def _pad_nonce(nonce)
|
214
213
|
nonce.rjust(self.iv_len, "\0")
|
215
214
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'aead/cipher'
|
2
|
+
require 'aead/cipher/aes_gcm'
|
3
|
+
|
4
|
+
#
|
5
|
+
# Encrypt plaintext using the Galois Counter Mode of AES.
|
6
|
+
#
|
7
|
+
class AEAD::Cipher::AES_128_GCM < AEAD::Cipher
|
8
|
+
include AEAD::Cipher::AES_GCM
|
9
|
+
|
10
|
+
def self.cipher_mode; 'aes-128-gcm'; end
|
11
|
+
|
12
|
+
def self.key_len; 16; end
|
13
|
+
def self.iv_len; 12; end
|
14
|
+
#def self.nonce_len; 12; end
|
15
|
+
def self.tag_len; 16; end
|
16
|
+
|
17
|
+
end
|
@@ -1,45 +1,17 @@
|
|
1
1
|
require 'aead/cipher'
|
2
|
+
require 'aead/cipher/aes_gcm'
|
2
3
|
|
3
4
|
#
|
4
5
|
# Encrypt plaintext using the Galois Counter Mode of AES.
|
5
6
|
#
|
6
7
|
class AEAD::Cipher::AES_256_GCM < AEAD::Cipher
|
8
|
+
include AEAD::Cipher::AES_GCM
|
9
|
+
|
10
|
+
def self.cipher_mode; 'aes-256-gcm'; end
|
11
|
+
|
7
12
|
def self.key_len; 32; end
|
8
13
|
def self.iv_len; 12; end
|
9
14
|
def self.nonce_len; 12; end
|
10
15
|
def self.tag_len; 16; end
|
11
16
|
|
12
|
-
#
|
13
|
-
# Instantiates the cipher with a secret key.
|
14
|
-
#
|
15
|
-
# @param [String] key a secret encryption key
|
16
|
-
#
|
17
|
-
def initialize(key)
|
18
|
-
super('aes-256-gcm', key)
|
19
|
-
end
|
20
|
-
|
21
|
-
protected
|
22
|
-
|
23
|
-
def _encrypt(nonce, aad, plaintext)
|
24
|
-
self.cipher(:encrypt) do |cipher|
|
25
|
-
cipher.key = self.key
|
26
|
-
cipher.iv = nonce
|
27
|
-
cipher.aad = aad.to_s if aad
|
28
|
-
|
29
|
-
cipher.update(plaintext) + cipher.final + cipher.gcm_tag
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def _decrypt(nonce, aad, ciphertext, tag)
|
34
|
-
self.cipher(:decrypt) do |cipher|
|
35
|
-
cipher.key = self.key
|
36
|
-
cipher.iv = nonce
|
37
|
-
cipher.gcm_tag = tag
|
38
|
-
cipher.aad = aad.to_s if aad
|
39
|
-
|
40
|
-
cipher.update(ciphertext).tap { cipher.verify }
|
41
|
-
end
|
42
|
-
rescue OpenSSL::Cipher::CipherError
|
43
|
-
raise ArgumentError, 'ciphertext failed authentication step'
|
44
|
-
end
|
45
17
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'aead/cipher'
|
2
|
+
|
3
|
+
#
|
4
|
+
# Provides the implementation details of AES + GCM, assuming the
|
5
|
+
# class including this module has defined proper class methods.
|
6
|
+
#
|
7
|
+
module AEAD::Cipher::AES_GCM
|
8
|
+
|
9
|
+
#
|
10
|
+
# Instantiates the cipher with a secret key.
|
11
|
+
#
|
12
|
+
# @param [String] key a secret encryption key
|
13
|
+
#
|
14
|
+
def initialize(key, options = {})
|
15
|
+
super(self.class.cipher_mode, key, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def nonce_len
|
19
|
+
iv_len
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def _encrypt(nonce, aad, plaintext)
|
25
|
+
self.cipher(:encrypt) do |cipher|
|
26
|
+
cipher.gcm_iv_len = self.iv_len
|
27
|
+
cipher.key = self.key
|
28
|
+
cipher.iv = nonce
|
29
|
+
cipher.aad = aad.to_s if aad
|
30
|
+
|
31
|
+
unless plaintext.nil? || plaintext.empty?
|
32
|
+
ciphertext = cipher.update(plaintext)
|
33
|
+
end
|
34
|
+
ciphertext = (ciphertext || "") + cipher.final + cipher.gcm_tag
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def _decrypt(nonce, aad, ciphertext, tag)
|
39
|
+
self.cipher(:decrypt) do |cipher|
|
40
|
+
cipher.gcm_iv_len = self.iv_len
|
41
|
+
cipher.key = self.key
|
42
|
+
cipher.iv = nonce
|
43
|
+
cipher.gcm_tag = tag
|
44
|
+
cipher.aad = aad.to_s if aad
|
45
|
+
|
46
|
+
cipher.update(ciphertext).tap { cipher.verify }
|
47
|
+
end
|
48
|
+
rescue OpenSSL::Cipher::CipherError
|
49
|
+
raise ArgumentError, 'ciphertext failed authentication step'
|
50
|
+
end
|
51
|
+
end
|
data/lib/aead/cipher/aes_hmac.rb
CHANGED
@@ -10,7 +10,7 @@ module AEAD::Cipher::AES_HMAC
|
|
10
10
|
#
|
11
11
|
# @param [String] key a secret encryption key
|
12
12
|
#
|
13
|
-
def initialize(key)
|
13
|
+
def initialize(key, options = {})
|
14
14
|
super(self.class.cipher_mode, key)
|
15
15
|
end
|
16
16
|
|
@@ -29,7 +29,10 @@ module AEAD::Cipher::AES_HMAC
|
|
29
29
|
cipher.key = self.encryption_key
|
30
30
|
cipher.iv = nonce
|
31
31
|
|
32
|
-
|
32
|
+
unless plaintext.nil? || plaintext.empty?
|
33
|
+
ciphertext = cipher.update(plaintext)
|
34
|
+
end
|
35
|
+
ciphertext = (ciphertext || "") + cipher.final
|
33
36
|
mac = hmac_generate(nonce, aad.to_s, ciphertext)
|
34
37
|
|
35
38
|
ciphertext << mac
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'aead/cipher/aes_128_gcm'
|
3
|
+
|
4
|
+
describe AEAD::Cipher::AES_128_GCM do
|
5
|
+
subject { self.cipher.new(self.key) }
|
6
|
+
|
7
|
+
let(:algo) { 'aes-128-gcm' }
|
8
|
+
let(:cipher) { AEAD::Cipher.new(algo) }
|
9
|
+
let(:key) { self.cipher.generate_key }
|
10
|
+
let(:nonce) { self.cipher.generate_nonce }
|
11
|
+
let(:aad) { SecureRandom.random_bytes }
|
12
|
+
let(:plaintext) { SecureRandom.random_bytes }
|
13
|
+
|
14
|
+
it 'must decrypt its own ciphertexts' do
|
15
|
+
ciphertext = subject.encrypt(self.nonce, self.aad, self.plaintext)
|
16
|
+
plaintext = subject.decrypt(self.nonce, self.aad, ciphertext)
|
17
|
+
|
18
|
+
plaintext.must_equal self.plaintext
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'must require a 128-bit or larger key' do
|
22
|
+
bad_keys = [ 0, 1, 15 ].map {|size| SecureRandom.random_bytes(size) }
|
23
|
+
good_keys = [ 16, 17, 256 ].map {|size| SecureRandom.random_bytes(size) }
|
24
|
+
|
25
|
+
bad_keys.each do |key|
|
26
|
+
lambda { self.cipher.new(key) }.must_raise ArgumentError
|
27
|
+
end
|
28
|
+
|
29
|
+
good_keys.each do |key|
|
30
|
+
self.cipher.new(key).must_be_kind_of AEAD::Cipher
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'must require a 12-byte nonce by default' do
|
35
|
+
bad_nonces = [0, 1, 11, 13 ].map {|size| SecureRandom.random_bytes(size) }
|
36
|
+
good_nonces = [ 12 ] .map {|size| SecureRandom.random_bytes(size) }
|
37
|
+
|
38
|
+
bad_nonces.each do |nonce|
|
39
|
+
lambda { self.subject.encrypt(nonce, self.plaintext, self.aad) }.
|
40
|
+
must_raise ArgumentError
|
41
|
+
end
|
42
|
+
|
43
|
+
good_nonces.each do |nonce|
|
44
|
+
self.subject.encrypt(nonce, self.plaintext, self.aad).
|
45
|
+
must_be_kind_of String
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'must require a correct length nonce' do
|
50
|
+
cipher = self.cipher.new(self.key, :iv_len => 16)
|
51
|
+
bad_nonces = [0, 1, 15, 17 ].map {|size| SecureRandom.random_bytes(size) }
|
52
|
+
good_nonces = [ 16 ] .map {|size| SecureRandom.random_bytes(size) }
|
53
|
+
|
54
|
+
bad_nonces.each do |nonce|
|
55
|
+
lambda { cipher.encrypt(nonce, self.plaintext, self.aad) }.
|
56
|
+
must_raise ArgumentError
|
57
|
+
end
|
58
|
+
|
59
|
+
good_nonces.each do |nonce|
|
60
|
+
cipher.encrypt(nonce, self.plaintext, self.aad).
|
61
|
+
must_be_kind_of String
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'must accept empty plaintext' do
|
66
|
+
self.subject.encrypt(nonce, self.aad, nil).must_be_kind_of String
|
67
|
+
self.subject.encrypt(nonce, self.aad, '').must_be_kind_of String
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'must encrypt plaintexts correctly' do
|
71
|
+
subject.encrypt(self.nonce, self.aad, self.plaintext).
|
72
|
+
must_equal openssl_encrypt(self.key, self.nonce, self.aad, self.plaintext)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'must decrypt ciphertexts correctly' do
|
76
|
+
ciphertext = openssl_encrypt(self.key, self.nonce, self.aad, self.plaintext)
|
77
|
+
|
78
|
+
subject.decrypt(self.nonce, self.aad, ciphertext).
|
79
|
+
must_equal openssl_decrypt(self.key, self.nonce, self.aad, ciphertext)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'must resist manipulation of the key' do
|
83
|
+
ciphertext = subject.encrypt(self.nonce, self.aad, self.plaintext)
|
84
|
+
cipher = self.cipher.new twiddle(key)
|
85
|
+
|
86
|
+
lambda { cipher.decrypt(self.nonce, self.aad, ciphertext) }.
|
87
|
+
must_raise ArgumentError
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'must resist manipulation of the nonce' do
|
91
|
+
ciphertext = subject.encrypt(self.nonce, self.aad, self.plaintext)
|
92
|
+
nonce = twiddle(self.nonce)
|
93
|
+
|
94
|
+
lambda { self.subject.decrypt(nonce, self.aad, ciphertext) }.
|
95
|
+
must_raise ArgumentError
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'must resist manipulation of the ciphertext' do
|
99
|
+
ciphertext = subject.encrypt(self.nonce, self.aad, self.plaintext)
|
100
|
+
ciphertext = twiddle(ciphertext)
|
101
|
+
|
102
|
+
lambda { self.subject.decrypt(self.nonce, self.aad, ciphertext) }.
|
103
|
+
must_raise ArgumentError
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'must resist manipulation of the aad' do
|
107
|
+
ciphertext = subject.encrypt(self.nonce, self.aad, self.plaintext)
|
108
|
+
aad = twiddle(self.aad)
|
109
|
+
|
110
|
+
lambda { self.subject.decrypt(self.nonce, aad, ciphertext) }.
|
111
|
+
must_raise ArgumentError
|
112
|
+
end
|
113
|
+
|
114
|
+
def twiddle(bytes)
|
115
|
+
# pick a random byte to change
|
116
|
+
index = SecureRandom.random_number(bytes.bytesize)
|
117
|
+
|
118
|
+
# change it by a random offset that won't loop back around to its
|
119
|
+
# original value
|
120
|
+
offset = SecureRandom.random_number(254) + 1
|
121
|
+
ord = bytes[index].ord
|
122
|
+
byte = (ord + offset).modulo(256).chr
|
123
|
+
|
124
|
+
# reconstruct the bytes with the twiddled bit inserted in place
|
125
|
+
bytes[0, index] << byte << bytes[index.succ..-1]
|
126
|
+
end
|
127
|
+
|
128
|
+
def openssl_encrypt(key, nonce, aad, plaintext)
|
129
|
+
cipher = OpenSSL::Cipher.new(self.algo).encrypt
|
130
|
+
cipher.key = key
|
131
|
+
cipher.iv = nonce
|
132
|
+
cipher.aad = aad if aad
|
133
|
+
|
134
|
+
cipher.update(plaintext) + cipher.final + cipher.gcm_tag
|
135
|
+
end
|
136
|
+
|
137
|
+
def openssl_decrypt(key, nonce, aad, ciphertext)
|
138
|
+
tag = ciphertext[ -16 .. -1 ]
|
139
|
+
ciphertext = ciphertext[ 0 .. -17 ]
|
140
|
+
|
141
|
+
cipher = OpenSSL::Cipher.new(self.algo).decrypt
|
142
|
+
cipher.key = key
|
143
|
+
cipher.iv = nonce
|
144
|
+
cipher.gcm_tag = tag
|
145
|
+
cipher.aad = aad if aad
|
146
|
+
|
147
|
+
cipher.update(ciphertext).tap { cipher.verify }
|
148
|
+
end
|
149
|
+
end if OpenSSL::Cipher.ciphers.include?('aes-128-gcm')
|
@@ -32,8 +32,10 @@ describe AEAD::Cipher::AES_256_CBC_HMAC_SHA_256 do
|
|
32
32
|
end
|
33
33
|
|
34
34
|
it 'must require a 16-byte nonce' do
|
35
|
-
bad_nonces = [ 0, 1, 15, 17 ]
|
36
|
-
|
35
|
+
bad_nonces = [ 0, 1, 15, 17 ]
|
36
|
+
.map { |size| SecureRandom.random_bytes(size) }
|
37
|
+
good_nonces = [ 16 ]
|
38
|
+
.map { |size| SecureRandom.random_bytes(size) }
|
37
39
|
|
38
40
|
bad_nonces.each do |nonce|
|
39
41
|
lambda { self.subject.encrypt(nonce, self.plaintext, self.aad) }.
|
@@ -46,9 +48,9 @@ describe AEAD::Cipher::AES_256_CBC_HMAC_SHA_256 do
|
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
49
|
-
it 'must
|
50
|
-
|
51
|
-
|
51
|
+
it 'must accept empty plaintext' do
|
52
|
+
self.subject.encrypt(nonce, self.aad, nil).must_be_kind_of String
|
53
|
+
self.subject.encrypt(nonce, self.aad, '').must_be_kind_of String
|
52
54
|
end
|
53
55
|
|
54
56
|
it 'must encrypt plaintexts correctly' do
|
@@ -46,9 +46,9 @@ describe AEAD::Cipher::AES_256_CTR_HMAC_SHA_256 do
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
it 'must
|
50
|
-
|
51
|
-
|
49
|
+
it 'must accept empty plaintext' do
|
50
|
+
self.subject.encrypt(nonce, self.aad, nil).must_be_kind_of String
|
51
|
+
self.subject.encrypt(nonce, self.aad, '').must_be_kind_of String
|
52
52
|
end
|
53
53
|
|
54
54
|
it 'must encrypt plaintexts correctly' do
|
@@ -31,7 +31,7 @@ describe AEAD::Cipher::AES_256_GCM do
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
it 'must require a 12-byte nonce' do
|
34
|
+
it 'must require a 12-byte nonce by default' do
|
35
35
|
bad_nonces = [0, 1, 11, 13 ].map {|size| SecureRandom.random_bytes(size) }
|
36
36
|
good_nonces = [ 12 ] .map {|size| SecureRandom.random_bytes(size) }
|
37
37
|
|
@@ -46,9 +46,25 @@ describe AEAD::Cipher::AES_256_GCM do
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
it 'must require a
|
50
|
-
|
51
|
-
|
49
|
+
it 'must require a correct length nonce' do
|
50
|
+
cipher = self.cipher.new(self.key, :iv_len => 16)
|
51
|
+
bad_nonces = [0, 1, 15, 17 ].map {|size| SecureRandom.random_bytes(size) }
|
52
|
+
good_nonces = [ 16 ] .map {|size| SecureRandom.random_bytes(size) }
|
53
|
+
|
54
|
+
bad_nonces.each do |nonce|
|
55
|
+
lambda { cipher.encrypt(nonce, self.plaintext, self.aad) }.
|
56
|
+
must_raise ArgumentError
|
57
|
+
end
|
58
|
+
|
59
|
+
good_nonces.each do |nonce|
|
60
|
+
cipher.encrypt(nonce, self.plaintext, self.aad).
|
61
|
+
must_be_kind_of String
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'must accept empty plaintext' do
|
66
|
+
self.subject.encrypt(nonce, self.aad, nil).must_be_kind_of String
|
67
|
+
self.subject.encrypt(nonce, self.aad, '').must_be_kind_of String
|
52
68
|
end
|
53
69
|
|
54
70
|
it 'must encrypt plaintexts correctly' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aead
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.8.
|
4
|
+
version: 1.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen Touset
|
8
8
|
autorequire:
|
9
9
|
bindir: script
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-12-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: macaddr
|
@@ -112,16 +112,16 @@ dependencies:
|
|
112
112
|
name: minitest
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - "
|
115
|
+
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
117
|
+
version: '4'
|
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: '4'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: rake
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -214,12 +214,15 @@ files:
|
|
214
214
|
- ext/openssl/cipher/aead/extconf.rb
|
215
215
|
- lib/aead.rb
|
216
216
|
- lib/aead/cipher.rb
|
217
|
+
- lib/aead/cipher/aes_128_gcm.rb
|
217
218
|
- lib/aead/cipher/aes_256_cbc_hmac_sha_256.rb
|
218
219
|
- lib/aead/cipher/aes_256_ctr_hmac_sha_256.rb
|
219
220
|
- lib/aead/cipher/aes_256_gcm.rb
|
221
|
+
- lib/aead/cipher/aes_gcm.rb
|
220
222
|
- lib/aead/cipher/aes_hmac.rb
|
221
223
|
- lib/aead/nonce.rb
|
222
224
|
- lib/openssl/cipher/.gitignore
|
225
|
+
- spec/aead/cipher/aes_128_gcm_spec.rb
|
223
226
|
- spec/aead/cipher/aes_256_cbc_hmac_sha_256_spec.rb
|
224
227
|
- spec/aead/cipher/aes_256_ctr_hmac_sha_256_spec.rb
|
225
228
|
- spec/aead/cipher/aes_256_gcm_spec.rb
|
@@ -227,7 +230,8 @@ files:
|
|
227
230
|
- spec/aead/nonce_spec.rb
|
228
231
|
- spec/spec_helper.rb
|
229
232
|
homepage: https://github.com/onelogin/aead
|
230
|
-
licenses:
|
233
|
+
licenses:
|
234
|
+
- MIT
|
231
235
|
metadata: {}
|
232
236
|
post_install_message:
|
233
237
|
rdoc_options: []
|
@@ -245,14 +249,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
245
249
|
version: '0'
|
246
250
|
requirements: []
|
247
251
|
rubyforge_project:
|
248
|
-
rubygems_version: 2.2.
|
252
|
+
rubygems_version: 2.2.2
|
249
253
|
signing_key:
|
250
254
|
specification_version: 4
|
251
255
|
summary: Ruby library to generate AEADs
|
252
256
|
test_files:
|
257
|
+
- spec/aead/cipher/aes_128_gcm_spec.rb
|
253
258
|
- spec/aead/cipher/aes_256_cbc_hmac_sha_256_spec.rb
|
254
259
|
- spec/aead/cipher/aes_256_ctr_hmac_sha_256_spec.rb
|
255
260
|
- spec/aead/cipher/aes_256_gcm_spec.rb
|
256
261
|
- spec/aead/cipher_spec.rb
|
257
262
|
- spec/aead/nonce_spec.rb
|
258
263
|
- spec/spec_helper.rb
|
264
|
+
has_rdoc:
|