jose 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +2 -2
- data/CHANGELOG.md +42 -0
- data/README.md +19 -2
- data/jose.gemspec +2 -2
- data/lib/jose.rb +13 -0
- data/lib/jose/jwa/curve25519.rb +5 -4
- data/lib/jose/jwa/curve448.rb +5 -4
- data/lib/jose/jwe.rb +27 -1
- data/lib/jose/jwe/alg.rb +8 -0
- data/lib/jose/jwe/alg_aes_gcm_kw.rb +22 -10
- data/lib/jose/jwe/alg_aes_kw.rb +20 -11
- data/lib/jose/jwe/alg_dir.rb +4 -0
- data/lib/jose/jwe/alg_ecdh_es.rb +8 -0
- data/lib/jose/jwe/alg_pbes2.rb +51 -16
- data/lib/jose/jwe/alg_rsa.rb +23 -15
- data/lib/jose/jwe/enc_aes_cbc_hmac.rb +6 -4
- data/lib/jose/jwe/enc_aes_gcm.rb +2 -0
- data/lib/jose/jwk.rb +37 -3
- data/lib/jose/jwk/kty_ec.rb +47 -8
- data/lib/jose/jwk/kty_oct.rb +45 -19
- data/lib/jose/jwk/kty_okp_ed25519.rb +8 -2
- data/lib/jose/jwk/kty_okp_ed25519ph.rb +8 -2
- data/lib/jose/jwk/kty_okp_ed448.rb +8 -2
- data/lib/jose/jwk/kty_okp_ed448ph.rb +8 -2
- data/lib/jose/jwk/kty_okp_x25519.rb +22 -5
- data/lib/jose/jwk/kty_okp_x448.rb +22 -5
- data/lib/jose/jwk/kty_rsa.rb +16 -7
- data/lib/jose/jws.rb +27 -3
- data/lib/jose/jws/alg.rb +8 -0
- data/lib/jose/jws/alg_ecdsa.rb +13 -0
- data/lib/jose/jws/alg_eddsa.rb +4 -0
- data/lib/jose/jws/alg_hmac.rb +13 -0
- data/lib/jose/jws/alg_none.rb +44 -0
- data/lib/jose/jws/alg_rsa_pkcs1_v1_5.rb +13 -0
- data/lib/jose/jws/alg_rsa_pss.rb +13 -0
- data/lib/jose/jwt.rb +20 -2
- data/lib/jose/version.rb +1 -1
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c8b3820a104b8d8b71e1e8557f70aede232595e2
|
4
|
+
data.tar.gz: 3fdaa67a5ed08f9d6f6f8e5249737b3dc22ebe89
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbe1b64dc34bad69cfb2250a54e30eeb5f7ac864431d7d39463dbeaddec4ea3e2975e7a3f09a0c4ba9d82a06e02e4ce0934e2cf97f7ae0782f8c58a78a1d56d5
|
7
|
+
data.tar.gz: 4b96226504359839de7fb9d932b929abf58cf959ccf89ac2d6b6ca045c4ab148e4cfcb7f7b8592fecb8d2313bc6b687fba7789edfddb9fe96a0ef366c4bb7d13
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.3.
|
1
|
+
2.3.1
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,47 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.3.0 (2016-05-05)
|
4
|
+
|
5
|
+
* Enhancements
|
6
|
+
* Added merge functions:
|
7
|
+
* `JOSE::JWE#merge`
|
8
|
+
* `JOSE::JWK#merge`
|
9
|
+
* `JOSE::JWS#merge`
|
10
|
+
* `JOSE::JWT#merge`
|
11
|
+
* Added block_encryptor and signer functions:
|
12
|
+
* `JOSE::JWK#block_encryptor`
|
13
|
+
* `JOSE::JWK#signer`
|
14
|
+
* Support for `"alg"`, `"enc"`, and `"use"` on keys.
|
15
|
+
|
16
|
+
Examples of new functionality:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
# Let's generate a 64 byte octet key
|
20
|
+
jwk = JOSE::JWK.generate_key([:oct, 64])
|
21
|
+
# => {"k"=>"FXSy7PufOayusvfyKQzdxCegm7yWIMp1b0LD13v57Nq2wF_B-fcr7LDOkufDikmFFsVYWLgrA2zEB--_qqDn3g", "kty"=>"oct"}
|
22
|
+
|
23
|
+
# Based on the key's size and type, a default signer (JWS) can be determined
|
24
|
+
jwk.signer
|
25
|
+
# => {"alg"=>"HS512"}
|
26
|
+
|
27
|
+
# Based on the key's size and type, a default encryptor (JWE) can be determined
|
28
|
+
jwk.block_encryptor
|
29
|
+
# => {"alg"=>"dir", "enc"=>"A256CBC-HS512"}
|
30
|
+
|
31
|
+
# Keys can be generated based on the signing algorithm (JWS)
|
32
|
+
JOSE::JWS.generate_key({'alg' => 'HS256'})
|
33
|
+
# => {"alg"=>"HS256", "k"=>"UuP3Tw2xbGV5N3BGh34cJNzzC2R1zU7i4rOnF9A8nqY", "kty"=>"oct", "use"=>"sig"}
|
34
|
+
|
35
|
+
# Keys can be generated based on the encryption algorithm (JWE)
|
36
|
+
JOSE::JWE.generate_key({'alg' => 'dir', 'enc' => 'A128GCM'})
|
37
|
+
# => {"alg"=>"dir", "enc"=>"A128GCM", "k"=>"8WNdBjXXwg6QTwrrOnvEPw", "kty"=>"oct", "use"=>"enc"}
|
38
|
+
|
39
|
+
# Example of merging a map into an existing JWS (also works with JWE, JWK, and JWT)
|
40
|
+
jws = JOSE::JWS.from({'alg' => 'HS256'})
|
41
|
+
jws.merge({'typ' => 'JWT'})
|
42
|
+
# => {"alg"=>"HS256", "typ"=>"JWT"}
|
43
|
+
```
|
44
|
+
|
3
45
|
## 0.2.0 (2016-02-25)
|
4
46
|
|
5
47
|
* Enhancements
|
data/README.md
CHANGED
@@ -24,7 +24,25 @@ Or install it yourself as:
|
|
24
24
|
|
25
25
|
## Usage
|
26
26
|
|
27
|
-
|
27
|
+
Better documentation is in progress, but the [erlang-jose documentation](https://hexdocs.pm/jose/) can provide an idea of the functionality available in this gem.
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
# Let's use our secret key "symmetric key" for use with
|
31
|
+
# the HMAC using SHA-256 (HS256) algorithm.
|
32
|
+
jwk = JOSE::JWK.from_oct('symmetric key')
|
33
|
+
|
34
|
+
# Here is the JSON format of our JWK.
|
35
|
+
jwk.to_binary
|
36
|
+
# => "{\"k\":\"c3ltbWV0cmljIGtleQ\",\"kty\":\"oct\"}"
|
37
|
+
|
38
|
+
# Now let's sign our message using HS256.
|
39
|
+
signed = jwk.sign('test', { 'alg' => 'HS256' }).compact
|
40
|
+
# => "eyJhbGciOiJIUzI1NiJ9.dGVzdA.VlZz7pJCnos0k-WUL9O9RoT9N--2kHSakNIdOg-MIro"
|
41
|
+
|
42
|
+
# We use the same key for verification.
|
43
|
+
verified, message, = jwk.verify(signed)
|
44
|
+
# => [true, "test"]
|
45
|
+
```
|
28
46
|
|
29
47
|
## Development
|
30
48
|
|
@@ -36,7 +54,6 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
36
54
|
|
37
55
|
Bug reports and pull requests are welcome on GitHub at https://github.com/potatosalad/ruby-jose. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
38
56
|
|
39
|
-
|
40
57
|
## License
|
41
58
|
|
42
59
|
The gem is available as open source under the terms of the [MPL-2.0 License](http://opensource.org/licenses/MPL-2.0).
|
data/jose.gemspec
CHANGED
@@ -29,8 +29,8 @@ Gem::Specification.new do |spec|
|
|
29
29
|
|
30
30
|
spec.add_dependency "hamster"
|
31
31
|
|
32
|
-
spec.add_development_dependency "bundler", "~> 1.
|
33
|
-
spec.add_development_dependency "rake", "~>
|
32
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
33
|
+
spec.add_development_dependency "rake", "~> 11.1"
|
34
34
|
spec.add_development_dependency "minitest"
|
35
35
|
spec.add_development_dependency "json"
|
36
36
|
spec.add_development_dependency "rbnacl-libsodium"
|
data/lib/jose.rb
CHANGED
@@ -17,6 +17,7 @@ module JOSE
|
|
17
17
|
MUTEX = Mutex.new
|
18
18
|
|
19
19
|
@__crypto_fallback__ = ENV['JOSE_CRYPTO_FALLBACK'] ? true : false
|
20
|
+
@__unsecured_signing__ = ENV['JOSE_UNSECURED_SIGNING'] ? true : false
|
20
21
|
|
21
22
|
def __crypto_fallback__
|
22
23
|
return @__crypto_fallback__
|
@@ -54,6 +55,18 @@ module JOSE
|
|
54
55
|
return JSON.dump(sort_maps(term))
|
55
56
|
end
|
56
57
|
|
58
|
+
def __unsecured_signing__
|
59
|
+
return @__unsecured_signing__
|
60
|
+
end
|
61
|
+
|
62
|
+
def __unsecured_signing__=(boolean)
|
63
|
+
boolean = !!boolean
|
64
|
+
MUTEX.synchronize {
|
65
|
+
@__unsecured_signing__ = boolean
|
66
|
+
__config_change__
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
57
70
|
def urlsafe_decode64(binary)
|
58
71
|
case binary.bytesize % 4
|
59
72
|
when 2
|
data/lib/jose/jwa/curve25519.rb
CHANGED
@@ -29,6 +29,7 @@ module JOSE::JWA::Curve25519
|
|
29
29
|
|
30
30
|
def __config_change__(lock = true)
|
31
31
|
MUTEX.lock if lock
|
32
|
+
@__implementation__ ||= nil
|
32
33
|
@__implementation__ = __pick_best_implementation__ if @__implementation__.nil? or @__implementation__.__ruby__? or not @__implementation__.__supported__?
|
33
34
|
MUTEX.unlock if lock
|
34
35
|
end
|
@@ -80,11 +81,11 @@ module JOSE::JWA::Curve25519
|
|
80
81
|
private
|
81
82
|
def __pick_best_implementation__
|
82
83
|
implementation = nil
|
83
|
-
implementation = @__implementations__.detect do |
|
84
|
-
next
|
84
|
+
implementation = @__implementations__.detect do |mod|
|
85
|
+
next mod.__supported__?
|
85
86
|
end
|
86
|
-
implementation ||= @__ruby_implementations__.detect do |
|
87
|
-
next
|
87
|
+
implementation ||= @__ruby_implementations__.detect do |mod|
|
88
|
+
next mod.__supported__?
|
88
89
|
end
|
89
90
|
implementation ||= JOSE::JWA::Curve25519_Unsupported
|
90
91
|
return implementation
|
data/lib/jose/jwa/curve448.rb
CHANGED
@@ -29,6 +29,7 @@ module JOSE::JWA::Curve448
|
|
29
29
|
|
30
30
|
def __config_change__(lock = true)
|
31
31
|
MUTEX.lock if lock
|
32
|
+
@__implementation__ ||= nil
|
32
33
|
@__implementation__ = __pick_best_implementation__ if @__implementation__.nil? or @__implementation__.__ruby__? or not @__implementation__.__supported__?
|
33
34
|
MUTEX.unlock if lock
|
34
35
|
end
|
@@ -80,11 +81,11 @@ module JOSE::JWA::Curve448
|
|
80
81
|
private
|
81
82
|
def __pick_best_implementation__
|
82
83
|
implementation = nil
|
83
|
-
implementation = @__implementations__.detect do |
|
84
|
-
next
|
84
|
+
implementation = @__implementations__.detect do |mod|
|
85
|
+
next mod.__supported__?
|
85
86
|
end
|
86
|
-
implementation ||= @__ruby_implementations__.detect do |
|
87
|
-
next
|
87
|
+
implementation ||= @__ruby_implementations__.detect do |mod|
|
88
|
+
next mod.__supported__?
|
88
89
|
end
|
89
90
|
implementation ||= JOSE::JWA::Curve448_Unsupported
|
90
91
|
return implementation
|
data/lib/jose/jwe.rb
CHANGED
@@ -138,7 +138,7 @@ module JOSE
|
|
138
138
|
else
|
139
139
|
aad_b64 = JOSE.urlsafe_encode64(aad)
|
140
140
|
concat_aad = [protected_binary, '.', aad_b64].join
|
141
|
-
cipher_text, cipher_tag = enc.block_encrypt([
|
141
|
+
cipher_text, cipher_tag = enc.block_encrypt([concat_aad, jwe.compress(plain_text)], cek, iv)
|
142
142
|
return JOSE::EncryptedMap[
|
143
143
|
'aad' => aad_b64,
|
144
144
|
'ciphertext' => JOSE.urlsafe_encode64(cipher_text),
|
@@ -199,6 +199,14 @@ module JOSE
|
|
199
199
|
end
|
200
200
|
end
|
201
201
|
|
202
|
+
def self.generate_key(object, modules = {})
|
203
|
+
return from(object, modules).generate_key
|
204
|
+
end
|
205
|
+
|
206
|
+
def generate_key
|
207
|
+
return alg.generate_key(fields, enc)
|
208
|
+
end
|
209
|
+
|
202
210
|
def key_decrypt(key, encrypted_key)
|
203
211
|
return alg.key_decrypt(key, enc, encrypted_key)
|
204
212
|
end
|
@@ -210,6 +218,24 @@ module JOSE
|
|
210
218
|
return encrypted_key, new_jwe
|
211
219
|
end
|
212
220
|
|
221
|
+
def self.merge(left, right)
|
222
|
+
return from(left).merge(right)
|
223
|
+
end
|
224
|
+
|
225
|
+
def merge(object)
|
226
|
+
object = case object
|
227
|
+
when JOSE::Map, Hash
|
228
|
+
object
|
229
|
+
when String
|
230
|
+
JOSE.decode(object)
|
231
|
+
when JOSE::JWE
|
232
|
+
object.to_map
|
233
|
+
else
|
234
|
+
raise ArgumentError, "'object' must be a Hash, String, or JOSE::JWE"
|
235
|
+
end
|
236
|
+
return JOSE::JWE.from_map(self.to_map.merge(object))
|
237
|
+
end
|
238
|
+
|
213
239
|
def next_cek(key)
|
214
240
|
return alg.next_cek(key, enc)
|
215
241
|
end
|
data/lib/jose/jwe/alg.rb
CHANGED
@@ -2,6 +2,14 @@ module JOSE::JWE::ALG
|
|
2
2
|
|
3
3
|
extend self
|
4
4
|
|
5
|
+
def generate_key(parameters, algorithm, encryption)
|
6
|
+
return JOSE::JWK.generate_key(parameters).merge({
|
7
|
+
'alg' => algorithm,
|
8
|
+
'enc' => encryption,
|
9
|
+
'use' => 'enc'
|
10
|
+
})
|
11
|
+
end
|
12
|
+
|
5
13
|
end
|
6
14
|
|
7
15
|
require 'jose/jwe/alg_aes_gcm_kw'
|
@@ -30,16 +30,7 @@ class JOSE::JWE::ALG_AES_GCM_KW < Struct.new(:cipher_name, :bits, :iv, :tag)
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def to_map(fields)
|
33
|
-
alg =
|
34
|
-
when 128
|
35
|
-
'A128GCMKW'
|
36
|
-
when 192
|
37
|
-
'A192GCMKW'
|
38
|
-
when 256
|
39
|
-
'A256GCMKW'
|
40
|
-
else
|
41
|
-
raise ArgumentError, "unhandled JOSE::JWE::ALG_AES_KW bits: #{bits.inspect}"
|
42
|
-
end
|
33
|
+
alg = algorithm
|
43
34
|
fields = fields.put('alg', alg)
|
44
35
|
if iv
|
45
36
|
fields = fields.put('iv', JOSE.urlsafe_encode64(iv))
|
@@ -52,6 +43,10 @@ class JOSE::JWE::ALG_AES_GCM_KW < Struct.new(:cipher_name, :bits, :iv, :tag)
|
|
52
43
|
|
53
44
|
# JOSE::JWE::ALG callbacks
|
54
45
|
|
46
|
+
def generate_key(fields, enc)
|
47
|
+
return JOSE::JWE::ALG.generate_key([:oct, bits.div(8)], algorithm, enc.algorithm)
|
48
|
+
end
|
49
|
+
|
55
50
|
def key_decrypt(key, enc, encrypted_key)
|
56
51
|
if iv.nil? or tag.nil?
|
57
52
|
raise ArgumentError, "missing required fields for decryption: 'iv' and 'tag'"
|
@@ -67,6 +62,7 @@ class JOSE::JWE::ALG_AES_GCM_KW < Struct.new(:cipher_name, :bits, :iv, :tag)
|
|
67
62
|
cipher.decrypt
|
68
63
|
cipher.key = derived_key
|
69
64
|
cipher.iv = iv
|
65
|
+
cipher.padding = 0
|
70
66
|
cipher.auth_data = aad
|
71
67
|
cipher.auth_tag = cipher_tag
|
72
68
|
plain_text = cipher.update(cipher_text) + cipher.final
|
@@ -85,6 +81,7 @@ class JOSE::JWE::ALG_AES_GCM_KW < Struct.new(:cipher_name, :bits, :iv, :tag)
|
|
85
81
|
cipher.encrypt
|
86
82
|
cipher.key = derived_key
|
87
83
|
cipher.iv = new_alg.iv
|
84
|
+
cipher.padding = 0
|
88
85
|
cipher.auth_data = aad
|
89
86
|
cipher_text = cipher.update(plain_text) + cipher.final
|
90
87
|
new_alg.tag = cipher.auth_tag
|
@@ -95,4 +92,19 @@ class JOSE::JWE::ALG_AES_GCM_KW < Struct.new(:cipher_name, :bits, :iv, :tag)
|
|
95
92
|
return enc.next_cek
|
96
93
|
end
|
97
94
|
|
95
|
+
# API functions
|
96
|
+
|
97
|
+
def algorithm
|
98
|
+
case bits
|
99
|
+
when 128
|
100
|
+
'A128GCMKW'
|
101
|
+
when 192
|
102
|
+
'A192GCMKW'
|
103
|
+
when 256
|
104
|
+
'A256GCMKW'
|
105
|
+
else
|
106
|
+
raise ArgumentError, "unhandled JOSE::JWE::ALG_AES_KW bits: #{bits.inspect}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
98
110
|
end
|
data/lib/jose/jwe/alg_aes_kw.rb
CHANGED
@@ -17,21 +17,15 @@ class JOSE::JWE::ALG_AES_KW < Struct.new(:bits)
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def to_map(fields)
|
20
|
-
alg
|
21
|
-
when 128
|
22
|
-
'A128KW'
|
23
|
-
when 192
|
24
|
-
'A192KW'
|
25
|
-
when 256
|
26
|
-
'A256KW'
|
27
|
-
else
|
28
|
-
raise ArgumentError, "unhandled JOSE::JWE::ALG_AES_KW bits: #{bits.inspect}"
|
29
|
-
end
|
30
|
-
return fields.put('alg', alg)
|
20
|
+
return fields.put('alg', algorithm)
|
31
21
|
end
|
32
22
|
|
33
23
|
# JOSE::JWE::ALG callbacks
|
34
24
|
|
25
|
+
def generate_key(fields, enc)
|
26
|
+
return JOSE::JWE::ALG.generate_key([:oct, bits.div(8)], algorithm, enc.algorithm)
|
27
|
+
end
|
28
|
+
|
35
29
|
def key_decrypt(key, enc, encrypted_key)
|
36
30
|
if key.is_a?(JOSE::JWK)
|
37
31
|
key = key.kty.derive_key
|
@@ -54,4 +48,19 @@ class JOSE::JWE::ALG_AES_KW < Struct.new(:bits)
|
|
54
48
|
return enc.next_cek
|
55
49
|
end
|
56
50
|
|
51
|
+
# API functions
|
52
|
+
|
53
|
+
def algorithm
|
54
|
+
case bits
|
55
|
+
when 128
|
56
|
+
'A128KW'
|
57
|
+
when 192
|
58
|
+
'A192KW'
|
59
|
+
when 256
|
60
|
+
'A256KW'
|
61
|
+
else
|
62
|
+
raise ArgumentError, "unhandled JOSE::JWE::ALG_AES_KW bits: #{bits.inspect}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
57
66
|
end
|
data/lib/jose/jwe/alg_dir.rb
CHANGED
@@ -17,6 +17,10 @@ class JOSE::JWE::ALG_dir
|
|
17
17
|
|
18
18
|
# JOSE::JWE::ALG callbacks
|
19
19
|
|
20
|
+
def generate_key(fields, enc)
|
21
|
+
return JOSE::JWE::ALG.generate_key([:oct, enc.bits.div(8)], 'dir', enc.algorithm)
|
22
|
+
end
|
23
|
+
|
20
24
|
def key_decrypt(key, enc, encrypted_key)
|
21
25
|
if key.is_a?(String)
|
22
26
|
return key
|
data/lib/jose/jwe/alg_ecdh_es.rb
CHANGED
@@ -47,6 +47,14 @@ class JOSE::JWE::ALG_ECDH_ES < Struct.new(:bits, :epk, :apu, :apv)
|
|
47
47
|
|
48
48
|
# JOSE::JWE::ALG callbacks
|
49
49
|
|
50
|
+
def generate_key(fields, enc)
|
51
|
+
if not epk.nil?
|
52
|
+
return JOSE::JWE::ALG.generate_key(epk, algorithm, enc.algorithm)
|
53
|
+
else
|
54
|
+
return JOSE::JWE::ALG.generate_key([:ec, 'P-521'], algorithm, enc.algorithm)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
50
58
|
def key_decrypt(box_keys, enc, encrypted_key)
|
51
59
|
other_public_key, my_private_key = box_keys
|
52
60
|
if my_private_key and epk and epk.to_key != other_public_key.to_key
|
data/lib/jose/jwe/alg_pbes2.rb
CHANGED
@@ -19,13 +19,17 @@ class JOSE::JWE::ALG_PBES2 < Struct.new(:hmac, :bits, :salt, :iter)
|
|
19
19
|
raise ArgumentError, "invalid 'alg' for JWE: #{fields['alg'].inspect}"
|
20
20
|
end
|
21
21
|
iter = nil
|
22
|
-
if fields
|
22
|
+
if not fields.has_key?('p2c')
|
23
|
+
iter = 1000
|
24
|
+
elsif fields['p2c'].is_a?(Integer) and fields['p2c'] >= 0
|
23
25
|
iter = fields['p2c']
|
24
26
|
else
|
25
27
|
raise ArgumentError, "invalid 'p2c' for JWE: #{fields['p2c'].inspect}"
|
26
28
|
end
|
27
29
|
salt = nil
|
28
|
-
if fields.has_key?('p2s')
|
30
|
+
if not fields.has_key?('p2s')
|
31
|
+
salt = nil
|
32
|
+
elsif fields['p2s'].is_a?(String)
|
29
33
|
salt = wrap_salt(fields['alg'], JOSE.urlsafe_decode64(fields['p2s']))
|
30
34
|
else
|
31
35
|
raise ArgumentError, "invalid 'p2s' for JWE: #{fields['p2s'].inspect}"
|
@@ -34,22 +38,29 @@ class JOSE::JWE::ALG_PBES2 < Struct.new(:hmac, :bits, :salt, :iter)
|
|
34
38
|
end
|
35
39
|
|
36
40
|
def to_map(fields)
|
37
|
-
alg =
|
38
|
-
|
39
|
-
|
40
|
-
'
|
41
|
-
elsif hmac == OpenSSL::Digest::SHA512
|
42
|
-
'PBES2-HS512+A256KW'
|
41
|
+
alg = algorithm
|
42
|
+
p2c = iter
|
43
|
+
if salt.nil?
|
44
|
+
return fields.put('alg', alg).put('p2c', p2c)
|
43
45
|
else
|
44
|
-
|
46
|
+
p2s = JOSE.urlsafe_encode64(unwrap_salt(alg, salt))
|
47
|
+
return fields.put('alg', alg).put('p2c', p2c).put('p2s', p2s)
|
45
48
|
end
|
46
|
-
p2c = iter
|
47
|
-
p2s = JOSE.urlsafe_encode64(unwrap_salt(alg, salt))
|
48
|
-
return fields.put('alg', alg).put('p2c', p2c).put('p2s', p2s)
|
49
49
|
end
|
50
50
|
|
51
51
|
# JOSE::JWE::ALG callbacks
|
52
52
|
|
53
|
+
def generate_key(fields, enc)
|
54
|
+
alg = algorithm
|
55
|
+
extra_fields = {
|
56
|
+
'p2c' => iter
|
57
|
+
}
|
58
|
+
if not salt.nil?
|
59
|
+
extra_fields['p2s'] = JOSE.urlsafe_encode64(unwrap_salt(alg, salt))
|
60
|
+
end
|
61
|
+
return JOSE::JWE::ALG.generate_key([:oct, 16], alg, enc.algorithm).merge(extra_fields)
|
62
|
+
end
|
63
|
+
|
53
64
|
def key_decrypt(key, enc, encrypted_key)
|
54
65
|
if key.is_a?(JOSE::JWK)
|
55
66
|
key = key.kty.derive_key
|
@@ -63,18 +74,34 @@ class JOSE::JWE::ALG_PBES2 < Struct.new(:hmac, :bits, :salt, :iter)
|
|
63
74
|
if key.is_a?(JOSE::JWK)
|
64
75
|
key = key.kty.derive_key
|
65
76
|
end
|
66
|
-
|
77
|
+
new_alg = self
|
78
|
+
if new_alg.salt.nil?
|
79
|
+
new_alg = JOSE::JWE::ALG_PBES2.new(hmac, bits, wrap_salt(SecureRandom.random_bytes(8)), iter)
|
80
|
+
end
|
81
|
+
derived_key = OpenSSL::PKCS5.pbkdf2_hmac(key, new_alg.salt, new_alg.iter, new_alg.bits.div(8) + (new_alg.bits % 8), new_alg.hmac.new)
|
67
82
|
encrypted_key = JOSE::JWA::AES_KW.wrap(decrypted_key, derived_key)
|
68
|
-
return encrypted_key,
|
83
|
+
return encrypted_key, new_alg
|
69
84
|
end
|
70
85
|
|
71
86
|
def next_cek(key, enc)
|
72
87
|
return enc.next_cek
|
73
88
|
end
|
74
89
|
|
75
|
-
|
90
|
+
# API functions
|
76
91
|
|
77
|
-
def
|
92
|
+
def algorithm
|
93
|
+
if hmac == OpenSSL::Digest::SHA256
|
94
|
+
'PBES2-HS256+A128KW'
|
95
|
+
elsif hmac == OpenSSL::Digest::SHA384
|
96
|
+
'PBES2-HS384+A192KW'
|
97
|
+
elsif hmac == OpenSSL::Digest::SHA512
|
98
|
+
'PBES2-HS512+A256KW'
|
99
|
+
else
|
100
|
+
raise ArgumentError, "unhandled JOSE::JWE::ALG_PBES2 hmac: #{hmac.inspect}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.unwrap_salt(algorithm, salt)
|
78
105
|
salt_s = StringIO.new(salt)
|
79
106
|
if salt_s.read(algorithm.length) != algorithm or salt_s.getbyte != 0
|
80
107
|
raise ArgumentError, "unrecognized salt value"
|
@@ -83,8 +110,16 @@ private
|
|
83
110
|
end
|
84
111
|
end
|
85
112
|
|
113
|
+
def unwrap_salt(salt)
|
114
|
+
return JOSE::JWE::ALG_PBES2.unwrap_salt(algorithm, salt)
|
115
|
+
end
|
116
|
+
|
86
117
|
def self.wrap_salt(algorithm, salt_input)
|
87
118
|
return [algorithm, 0x00, salt_input].pack('a*Ca*')
|
88
119
|
end
|
89
120
|
|
121
|
+
def wrap_salt(salt_input)
|
122
|
+
return JOSE::JWE::ALG_PBES2.wrap_salt(algorithm, salt_input)
|
123
|
+
end
|
124
|
+
|
90
125
|
end
|