jose 0.2.0 → 0.3.0
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/.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
|