jose 0.3.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +5 -0
- data/.yardopts +4 -0
- data/CHANGELOG.md +7 -0
- data/{LICENSE.txt → LICENSE.md} +0 -0
- data/README.md +10 -3
- data/docs/EncryptionAlgorithms.md +55 -0
- data/docs/GettingStarted.md +137 -0
- data/docs/KeyGeneration.md +263 -0
- data/docs/SignatureAlgorithms.md +35 -0
- data/lib/jose.rb +86 -22
- data/lib/jose/jwa.rb +293 -0
- data/lib/jose/jwa/concat_kdf.rb +2 -0
- data/lib/jose/jwa/curve25519_ruby.rb +1 -1
- data/lib/jose/jwa/curve448_ruby.rb +1 -1
- data/lib/jose/jwa/ed448.rb +8 -8
- data/lib/jose/jwe.rb +819 -11
- data/lib/jose/jwe/alg_aes_gcm_kw.rb +1 -1
- data/lib/jose/jwe/alg_aes_kw.rb +1 -1
- data/lib/jose/jwe/alg_dir.rb +4 -4
- data/lib/jose/jwe/alg_ecdh_es.rb +45 -18
- data/lib/jose/jwe/alg_pbes2.rb +3 -3
- data/lib/jose/jwe/alg_rsa.rb +1 -1
- data/lib/jose/jwk.rb +639 -48
- data/lib/jose/jwk/kty_ec.rb +22 -4
- data/lib/jose/jwk/kty_oct.rb +16 -0
- data/lib/jose/jwk/kty_okp_ed25519.rb +36 -0
- data/lib/jose/jwk/kty_okp_ed25519ph.rb +36 -0
- data/lib/jose/jwk/kty_okp_ed448.rb +36 -0
- data/lib/jose/jwk/kty_okp_ed448ph.rb +36 -0
- data/lib/jose/jwk/kty_okp_x25519.rb +28 -0
- data/lib/jose/jwk/kty_okp_x448.rb +28 -0
- data/lib/jose/jwk/kty_rsa.rb +8 -0
- data/lib/jose/jwk/openssh_key.rb +278 -0
- data/lib/jose/jws.rb +486 -21
- data/lib/jose/jws/alg_none.rb +2 -2
- data/lib/jose/jwt.rb +208 -14
- data/lib/jose/version.rb +1 -1
- metadata +9 -3
@@ -0,0 +1,35 @@
|
|
1
|
+
# @title Signature Algorithms
|
2
|
+
|
3
|
+
# Signature Algorithms
|
4
|
+
|
5
|
+
The basic parameters for a {JOSE::JWS JOSE::JWS} header are:
|
6
|
+
|
7
|
+
- `"alg"` **(required)** - Cryptographic Algorithm used to secure the JWS.
|
8
|
+
|
9
|
+
See [RFC 7515](https://tools.ietf.org/html/rfc7515#section-4.1) for more information about other header parameters.
|
10
|
+
|
11
|
+
### `alg` Header Parameter
|
12
|
+
|
13
|
+
Here are the supported options for the `alg` parameter, grouped by similar funcionality:
|
14
|
+
|
15
|
+
- Elliptic Curve Digital Signature Algorithm (ECDSA)
|
16
|
+
- [`ES256`](http://www.rubydoc.info/gems/jose/JOSE/JWS#ECDSA-group)
|
17
|
+
- [`ES384`](http://www.rubydoc.info/gems/jose/JOSE/JWS#ECDSA-group)
|
18
|
+
- [`ES512`](http://www.rubydoc.info/gems/jose/JOSE/JWS#ECDSA-group)
|
19
|
+
- Edwards-curve Digital Signature Algorithm (EdDSA)
|
20
|
+
- [`Ed25519`](http://www.rubydoc.info/gems/jose/JOSE/JWS#EdDSA-25519-group)
|
21
|
+
- [`Ed25519ph`](http://www.rubydoc.info/gems/jose/JOSE/JWS#EdDSA-25519-group)
|
22
|
+
- [`Ed448`](http://www.rubydoc.info/gems/jose/JOSE/JWS#EdDSA-448-group)
|
23
|
+
- [`Ed448ph`](http://www.rubydoc.info/gems/jose/JOSE/JWS#EdDSA-448-group)
|
24
|
+
- HMAC using SHA-2
|
25
|
+
- [`HS256`](http://www.rubydoc.info/gems/jose/JOSE/JWS#HMACSHA2-group)
|
26
|
+
- [`HS384`](http://www.rubydoc.info/gems/jose/JOSE/JWS#HMACSHA2-group)
|
27
|
+
- [`HS512`](http://www.rubydoc.info/gems/jose/JOSE/JWS#HMACSHA2-group)
|
28
|
+
- RSASSA PSS using SHA-2 and MGF1 with SHA-2
|
29
|
+
- [`PS256`](http://www.rubydoc.info/gems/jose/JOSE/JWS#RSASSAPSS-group)
|
30
|
+
- [`PS384`](http://www.rubydoc.info/gems/jose/JOSE/JWS#RSASSAPSS-group)
|
31
|
+
- [`PS512`](http://www.rubydoc.info/gems/jose/JOSE/JWS#RSASSAPSS-group)
|
32
|
+
- RSASSA PKCS#1.5 using SHA-2
|
33
|
+
- [`RS256`](http://www.rubydoc.info/gems/jose/JOSE/JWS#RSASSAPKCS1_5-group)
|
34
|
+
- [`RS384`](http://www.rubydoc.info/gems/jose/JOSE/JWS#RSASSAPKCS1_5-group)
|
35
|
+
- [`RS512`](http://www.rubydoc.info/gems/jose/JOSE/JWS#RSASSAPKCS1_5-group)
|
data/lib/jose.rb
CHANGED
@@ -6,68 +6,129 @@ require 'json'
|
|
6
6
|
require 'openssl'
|
7
7
|
require 'thread'
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
# JOSE stands for JSON Object Signing and Encryption which is a is a set of
|
10
|
+
# standards established by the [JOSE Working Group](https://datatracker.ietf.org/wg/jose).
|
11
|
+
#
|
12
|
+
# JOSE is split into 5 main components:
|
13
|
+
#
|
14
|
+
# * {JOSE::JWA JOSE::JWA} - JSON Web Algorithms (JWA) {https://tools.ietf.org/html/rfc7518 RFC 7518}
|
15
|
+
# * {JOSE::JWE JOSE::JWE} - JSON Web Encryption (JWE) {https://tools.ietf.org/html/rfc7516 RFC 7516}
|
16
|
+
# * {JOSE::JWK JOSE::JWK} - JSON Web Key (JWK) {https://tools.ietf.org/html/rfc7517 RFC 7517}
|
17
|
+
# * {JOSE::JWS JOSE::JWS} - JSON Web Signature (JWS) {https://tools.ietf.org/html/rfc7515 RFC 7515}
|
18
|
+
# * {JOSE::JWT JOSE::JWT} - JSON Web Token (JWT) {https://tools.ietf.org/html/rfc7519 RFC 7519}
|
19
|
+
#
|
20
|
+
# Additional specifications and drafts implemented:
|
21
|
+
#
|
22
|
+
# * JSON Web Key (JWK) Thumbprint [RFC 7638](https://tools.ietf.org/html/rfc7638)
|
23
|
+
# * JWS Unencoded Payload Option [draft-ietf-jose-jws-signing-input-options-04](https://tools.ietf.org/html/draft-ietf-jose-jws-signing-input-options-04)
|
13
24
|
module JOSE
|
14
25
|
|
15
|
-
|
16
|
-
|
26
|
+
# @!visibility private
|
17
27
|
MUTEX = Mutex.new
|
18
28
|
|
29
|
+
# Immutable Map structure based on `Hamster::Hash`.
|
30
|
+
class Map < Hamster::Hash; end
|
31
|
+
|
19
32
|
@__crypto_fallback__ = ENV['JOSE_CRYPTO_FALLBACK'] ? true : false
|
20
33
|
@__unsecured_signing__ = ENV['JOSE_UNSECURED_SIGNING'] ? true : false
|
21
34
|
|
22
|
-
|
35
|
+
# Gets the current Cryptographic Algorithm Fallback state, defaults to `false`.
|
36
|
+
# @return [Boolean]
|
37
|
+
def self.crypto_fallback
|
23
38
|
return @__crypto_fallback__
|
24
39
|
end
|
25
40
|
|
26
|
-
|
41
|
+
# Sets the current Cryptographic Algorithm Fallback state.
|
42
|
+
# @param [Boolean] boolean
|
43
|
+
# @return [Boolean]
|
44
|
+
def self.crypto_fallback=(boolean)
|
27
45
|
boolean = !!boolean
|
28
46
|
MUTEX.synchronize {
|
29
47
|
@__crypto_fallback__ = boolean
|
30
48
|
__config_change__
|
31
49
|
}
|
50
|
+
return boolean
|
32
51
|
end
|
33
52
|
|
34
|
-
|
53
|
+
# Gets the current Curve25519 module used by {JOSE::JWA::Curve25519 JOSE::JWA::Curve25519}, see {.curve25519_module=} for default.
|
54
|
+
# @return [Module]
|
55
|
+
def self.curve25519_module
|
35
56
|
return JOSE::JWA::Curve25519.__implementation__
|
36
57
|
end
|
37
58
|
|
38
|
-
|
39
|
-
|
59
|
+
# Sets the current Curve25519 module used by {JOSE::JWA::Curve25519 JOSE::JWA::Curve25519}.
|
60
|
+
#
|
61
|
+
# Currently supported Curve25519 modules (first found is used as default):
|
62
|
+
#
|
63
|
+
# * {https://github.com/cryptosphere/rbnacl `RbNaCl`}
|
64
|
+
# * {JOSE::JWA::Curve25519_Ruby JOSE::JWA::Curve25519_Ruby} - only supported when {.crypto_fallback} is `true`
|
65
|
+
#
|
66
|
+
# Additional modules that implement the functions specified in {JOSE::JWA::Curve25519 JOSE::JWA::Curve25519} may also be used.
|
67
|
+
# @param [Module] mod
|
68
|
+
# @return [Module]
|
69
|
+
def self.curve25519_module=(mod)
|
70
|
+
JOSE::JWA::Curve25519.__implementation__ = mod
|
40
71
|
end
|
41
72
|
|
42
|
-
|
73
|
+
# Gets the current Curve448 module used by {JOSE::JWA::Curve448 JOSE::JWA::Curve448}, see {.curve25519_module=} for default.
|
74
|
+
# @return [Module]
|
75
|
+
def self.curve448_module
|
43
76
|
return JOSE::JWA::Curve448.__implementation__
|
44
77
|
end
|
45
78
|
|
46
|
-
|
47
|
-
|
79
|
+
# Sets the current Curve448 module used by {JOSE::JWA::Curve448 JOSE::JWA::Curve448}.
|
80
|
+
#
|
81
|
+
# Currently supported Curve448 modules (first found is used as default):
|
82
|
+
#
|
83
|
+
# * {JOSE::JWA::Curve448_Ruby JOSE::JWA::Curve448_Ruby} - only supported when {.crypto_fallback} is `true`
|
84
|
+
#
|
85
|
+
# Additional modules that implement the functions specified in {JOSE::JWA::Curve448 JOSE::JWA::Curve448} may also be used.
|
86
|
+
# @param [Module] mod
|
87
|
+
# @return [Module]
|
88
|
+
def self.curve448_module=(mod)
|
89
|
+
JOSE::JWA::Curve448.__implementation__ = mod
|
48
90
|
end
|
49
91
|
|
50
|
-
|
92
|
+
# Decode JSON binary to a term.
|
93
|
+
# @param [String] binary
|
94
|
+
# @return [Object]
|
95
|
+
def self.decode(binary)
|
51
96
|
return JSON.load(binary)
|
52
97
|
end
|
53
98
|
|
54
|
-
|
99
|
+
# Encode a term to JSON binary and sorts `Hash` and {JOSE::Map JOSE::Map} keys.
|
100
|
+
# @param [Object] term
|
101
|
+
# @return [Object]
|
102
|
+
def self.encode(term)
|
55
103
|
return JSON.dump(sort_maps(term))
|
56
104
|
end
|
57
105
|
|
58
|
-
|
106
|
+
# Gets the current Unsecured Signing state, defaults to `false`.
|
107
|
+
# @return [Boolean]
|
108
|
+
def self.unsecured_signing
|
59
109
|
return @__unsecured_signing__
|
60
110
|
end
|
61
111
|
|
62
|
-
|
112
|
+
# Sets the current Unsecured Signing state.
|
113
|
+
#
|
114
|
+
# Enables/disables the `"none"` algorithm used for signing and verifying.
|
115
|
+
#
|
116
|
+
# See {https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/ Critical vulnerabilities in JSON Web Token libraries} for more information.
|
117
|
+
# @param [Boolean] boolean
|
118
|
+
# @return [Boolean]
|
119
|
+
def self.unsecured_signing=(boolean)
|
63
120
|
boolean = !!boolean
|
64
121
|
MUTEX.synchronize {
|
65
122
|
@__unsecured_signing__ = boolean
|
66
123
|
__config_change__
|
67
124
|
}
|
125
|
+
return boolean
|
68
126
|
end
|
69
127
|
|
70
|
-
|
128
|
+
# Returns the Base64Url decoded version of `binary` without padding.
|
129
|
+
# @param [String] binary
|
130
|
+
# @return [String]
|
131
|
+
def self.urlsafe_decode64(binary)
|
71
132
|
case binary.bytesize % 4
|
72
133
|
when 2
|
73
134
|
binary += '=='
|
@@ -77,18 +138,21 @@ module JOSE
|
|
77
138
|
return Base64.urlsafe_decode64(binary)
|
78
139
|
end
|
79
140
|
|
80
|
-
|
141
|
+
# Returns the Base64Url encoded version of `binary` without padding.
|
142
|
+
# @param [String] binary
|
143
|
+
# @return [String]
|
144
|
+
def self.urlsafe_encode64(binary)
|
81
145
|
return Base64.urlsafe_encode64(binary).tr('=', '')
|
82
146
|
end
|
83
147
|
|
84
148
|
private
|
85
149
|
|
86
|
-
def __config_change__
|
150
|
+
def self.__config_change__
|
87
151
|
JOSE::JWA::Curve25519.__config_change__
|
88
152
|
JOSE::JWA::Curve448.__config_change__
|
89
153
|
end
|
90
154
|
|
91
|
-
def sort_maps(term)
|
155
|
+
def self.sort_maps(term)
|
92
156
|
case term
|
93
157
|
when Hash, JOSE::Map
|
94
158
|
return term.keys.sort.each_with_object(Hash.new) do |key, hash|
|
data/lib/jose/jwa.rb
CHANGED
@@ -1,4 +1,19 @@
|
|
1
1
|
module JOSE
|
2
|
+
# JWA stands for JSON Web Algorithms which is defined in [RFC 7518](https://tools.ietf.org/html/rfc7518).
|
3
|
+
#
|
4
|
+
# ## Cryptographic Algorithm Fallback
|
5
|
+
#
|
6
|
+
# Native implementations of all cryptographic and public key algorithms
|
7
|
+
# required by the JWA specifications are not present in current versions
|
8
|
+
# of Ruby.
|
9
|
+
#
|
10
|
+
# JOSE will detect whether a specific algorithm is natively supported or not
|
11
|
+
# and, by default, it will mark the algorithm as unsupported if a native
|
12
|
+
# implementation is not found.
|
13
|
+
#
|
14
|
+
# However, JOSE also has pure Ruby versions of many of the missing algorithms
|
15
|
+
# which can be used as a fallback by calling {JOSE.crypto_fallback= JOSE.crypto_fallback=} and
|
16
|
+
# passing `true`.
|
2
17
|
module JWA
|
3
18
|
|
4
19
|
extend self
|
@@ -6,6 +21,10 @@ module JOSE
|
|
6
21
|
UCHAR_PACK = 'C*'.freeze
|
7
22
|
ZERO_PAD = [0].pack('C').force_encoding('BINARY').freeze
|
8
23
|
|
24
|
+
# Performs a constant time comparison between two binaries to help avoid [timing attacks](https://en.wikipedia.org/wiki/Timing_attack).
|
25
|
+
# @param [String] a
|
26
|
+
# @param [String] b
|
27
|
+
# @return [Boolean]
|
9
28
|
def constant_time_compare(a, b)
|
10
29
|
return false if a.empty? || b.empty? || a.bytesize != b.bytesize
|
11
30
|
l = a.unpack "C#{a.bytesize}"
|
@@ -15,6 +34,7 @@ module JOSE
|
|
15
34
|
return res == 0
|
16
35
|
end
|
17
36
|
|
37
|
+
# @!visibility private
|
18
38
|
def exor(a, b)
|
19
39
|
a = a.to_bn if a.respond_to?(:to_bn)
|
20
40
|
b = b.to_bn if b.respond_to?(:to_bn)
|
@@ -29,6 +49,279 @@ module JOSE
|
|
29
49
|
end.reverse.pack(UCHAR_PACK), 2)
|
30
50
|
end
|
31
51
|
|
52
|
+
# Returns the current listing of supported JOSE algorithms.
|
53
|
+
#
|
54
|
+
# !!!ruby
|
55
|
+
# JOSE::JWA.supports
|
56
|
+
# # => {:jwe=>
|
57
|
+
# # {:alg=>
|
58
|
+
# # ["A128GCMKW",
|
59
|
+
# # "A192GCMKW",
|
60
|
+
# # "A256GCMKW",
|
61
|
+
# # "A128KW",
|
62
|
+
# # "A192KW",
|
63
|
+
# # "A256KW",
|
64
|
+
# # "ECDH-ES",
|
65
|
+
# # "ECDH-ES+A128KW",
|
66
|
+
# # "ECDH-ES+A192KW",
|
67
|
+
# # "ECDH-ES+A256KW",
|
68
|
+
# # "PBES2-HS256+A128KW",
|
69
|
+
# # "PBES2-HS384+A192KW",
|
70
|
+
# # "PBES2-HS512+A256KW",
|
71
|
+
# # "RSA1_5",
|
72
|
+
# # "RSA-OAEP",
|
73
|
+
# # "RSA-OAEP-256",
|
74
|
+
# # "dir"],
|
75
|
+
# # :enc=>
|
76
|
+
# # ["A128GCM",
|
77
|
+
# # "A192GCM",
|
78
|
+
# # "A256GCM",
|
79
|
+
# # "A128CBC-HS256",
|
80
|
+
# # "A192CBC-HS384",
|
81
|
+
# # "A256CBC-HS512"],
|
82
|
+
# # :zip=>
|
83
|
+
# # ["DEF"]},
|
84
|
+
# # :jwk=>
|
85
|
+
# # {:kty=>
|
86
|
+
# # ["EC",
|
87
|
+
# # "OKP",
|
88
|
+
# # "RSA",
|
89
|
+
# # "oct"],
|
90
|
+
# # :kty_EC_crv=>
|
91
|
+
# # ["P-256",
|
92
|
+
# # "P-384",
|
93
|
+
# # "P-521"],
|
94
|
+
# # :kty_OKP_crv=>
|
95
|
+
# # ["Ed25519",
|
96
|
+
# # "Ed25519ph",
|
97
|
+
# # "Ed448",
|
98
|
+
# # "Ed448ph",
|
99
|
+
# # "X25519",
|
100
|
+
# # "X448"]},
|
101
|
+
# # :jws=>
|
102
|
+
# # {:alg=>
|
103
|
+
# # ["Ed25519",
|
104
|
+
# # "Ed25519ph",
|
105
|
+
# # "Ed448",
|
106
|
+
# # "Ed448ph",
|
107
|
+
# # "ES256",
|
108
|
+
# # "ES384",
|
109
|
+
# # "ES512",
|
110
|
+
# # "HS256",
|
111
|
+
# # "HS384",
|
112
|
+
# # "HS512",
|
113
|
+
# # "PS256",
|
114
|
+
# # "PS384",
|
115
|
+
# # "PS512",
|
116
|
+
# # "RS256",
|
117
|
+
# # "RS384",
|
118
|
+
# # "RS512",
|
119
|
+
# # "none"]}}
|
120
|
+
#
|
121
|
+
# @return [Hash]
|
122
|
+
def supports
|
123
|
+
jwe_enc = __jwe_enc_support_check__([
|
124
|
+
'A128GCM',
|
125
|
+
'A192GCM',
|
126
|
+
'A256GCM',
|
127
|
+
'A128CBC-HS256',
|
128
|
+
'A192CBC-HS384',
|
129
|
+
'A256CBC-HS512'
|
130
|
+
])
|
131
|
+
jwe_alg = __jwe_alg_support_check__([
|
132
|
+
['A128GCMKW', :block],
|
133
|
+
['A192GCMKW', :block],
|
134
|
+
['A256GCMKW', :block],
|
135
|
+
['A128KW', :block],
|
136
|
+
['A192KW', :block],
|
137
|
+
['A256KW', :block],
|
138
|
+
['ECDH-ES', :box],
|
139
|
+
['ECDH-ES+A128KW', :box],
|
140
|
+
['ECDH-ES+A192KW', :box],
|
141
|
+
['ECDH-ES+A256KW', :box],
|
142
|
+
['PBES2-HS256+A128KW', :block],
|
143
|
+
['PBES2-HS384+A192KW', :block],
|
144
|
+
['PBES2-HS512+A256KW', :block],
|
145
|
+
['RSA1_5', :rsa],
|
146
|
+
['RSA-OAEP', :rsa],
|
147
|
+
['RSA-OAEP-256', :rsa],
|
148
|
+
['dir', :direct]
|
149
|
+
], jwe_enc)
|
150
|
+
jwe_zip = __jwe_zip_support_check__([
|
151
|
+
'DEF'
|
152
|
+
], jwe_enc)
|
153
|
+
jwk_kty, jwk_kty_EC_crv, jwk_kty_OKP_crv = __jwk_kty_support_check__([
|
154
|
+
['EC', ['P-256', 'P-384', 'P-521']],
|
155
|
+
['OKP', ['Ed25519', 'Ed25519ph', 'Ed448', 'Ed448ph', 'X25519', 'X448']],
|
156
|
+
'RSA',
|
157
|
+
'oct'
|
158
|
+
])
|
159
|
+
jws_alg = __jws_alg_support_check__([
|
160
|
+
'Ed25519',
|
161
|
+
'Ed25519ph',
|
162
|
+
'Ed448',
|
163
|
+
'Ed448ph',
|
164
|
+
'ES256',
|
165
|
+
'ES384',
|
166
|
+
'ES512',
|
167
|
+
'HS256',
|
168
|
+
'HS384',
|
169
|
+
'HS512',
|
170
|
+
'PS256',
|
171
|
+
'PS384',
|
172
|
+
'PS512',
|
173
|
+
'RS256',
|
174
|
+
'RS384',
|
175
|
+
'RS512',
|
176
|
+
'X25519',
|
177
|
+
'X448',
|
178
|
+
'none'
|
179
|
+
])
|
180
|
+
return {
|
181
|
+
jwe: {
|
182
|
+
alg: jwe_alg,
|
183
|
+
enc: jwe_enc,
|
184
|
+
zip: jwe_zip
|
185
|
+
},
|
186
|
+
jwk: {
|
187
|
+
kty: jwk_kty,
|
188
|
+
kty_EC_crv: jwk_kty_EC_crv,
|
189
|
+
kty_OKP_crv: jwk_kty_OKP_crv
|
190
|
+
},
|
191
|
+
jws: {
|
192
|
+
alg: jws_alg
|
193
|
+
}
|
194
|
+
}
|
195
|
+
end
|
196
|
+
|
197
|
+
private
|
198
|
+
|
199
|
+
def __jwe_enc_support_check__(encryption_algorithms)
|
200
|
+
plain_text = SecureRandom.random_bytes(16)
|
201
|
+
return encryption_algorithms.select do |enc|
|
202
|
+
begin
|
203
|
+
jwe = JOSE::JWE.from({ 'alg' => 'dir', 'enc' => enc })
|
204
|
+
jwk = jwe.generate_key
|
205
|
+
cipher_text = jwk.block_encrypt(plain_text).compact
|
206
|
+
next jwk.block_decrypt(cipher_text).first == plain_text
|
207
|
+
rescue StandardError, NotImplementedError
|
208
|
+
next false
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def __jwe_alg_support_check__(key_algorithms, encryption_algorithms)
|
214
|
+
return [] if encryption_algorithms.empty?
|
215
|
+
plain_text = SecureRandom.random_bytes(16)
|
216
|
+
enc = encryption_algorithms[0]
|
217
|
+
rsa = nil
|
218
|
+
return key_algorithms.select do |(alg, strategy)|
|
219
|
+
begin
|
220
|
+
if strategy == :block
|
221
|
+
jwe = JOSE::JWE.from({ 'alg' => alg, 'enc' => enc })
|
222
|
+
jwk = jwe.generate_key
|
223
|
+
cipher_text = jwk.block_encrypt(plain_text).compact
|
224
|
+
next jwk.block_decrypt(cipher_text).first == plain_text
|
225
|
+
elsif strategy == :box
|
226
|
+
jwe = JOSE::JWE.from({ 'alg' => alg, 'enc' => enc })
|
227
|
+
send_jwk = jwe.generate_key
|
228
|
+
recv_jwk = jwe.generate_key
|
229
|
+
cipher_text = recv_jwk.box_encrypt(plain_text, send_jwk).compact
|
230
|
+
next recv_jwk.box_decrypt(cipher_text).first == plain_text
|
231
|
+
elsif strategy == :rsa
|
232
|
+
rsa ||= JOSE::JWK.generate_key([:rsa, 1024])
|
233
|
+
cipher_text = rsa.block_encrypt(plain_text, { 'alg' => alg, 'enc' => enc }).compact
|
234
|
+
next rsa.block_decrypt(cipher_text).first == plain_text
|
235
|
+
elsif strategy == :direct
|
236
|
+
next true
|
237
|
+
else
|
238
|
+
next false
|
239
|
+
end
|
240
|
+
rescue StandardError, NotImplementedError
|
241
|
+
next false
|
242
|
+
end
|
243
|
+
end.transpose.first
|
244
|
+
end
|
245
|
+
|
246
|
+
def __jwe_zip_support_check__(zip_algorithms, encryption_algorithms)
|
247
|
+
return [] if encryption_algorithms.empty?
|
248
|
+
plain_text = SecureRandom.random_bytes(16)
|
249
|
+
alg = 'dir'
|
250
|
+
enc = encryption_algorithms[0]
|
251
|
+
return zip_algorithms.select do |zip|
|
252
|
+
begin
|
253
|
+
jwe = JOSE::JWE.from({ 'alg' => alg, 'enc' => enc, 'zip' => zip })
|
254
|
+
jwk = jwe.generate_key
|
255
|
+
cipher_text = jwk.block_encrypt(plain_text, jwe).compact
|
256
|
+
next jwk.block_decrypt(cipher_text).first == plain_text
|
257
|
+
rescue StandardError, NotImplementedError
|
258
|
+
next false
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def __jwk_kty_support_check__(key_types)
|
264
|
+
kty = []
|
265
|
+
kty_EC_crv = []
|
266
|
+
kty_OKP_crv = []
|
267
|
+
key_types.each do |(key_type, curves)|
|
268
|
+
case key_type
|
269
|
+
when 'EC'
|
270
|
+
curves.each do |curve|
|
271
|
+
begin
|
272
|
+
JOSE::JWK.generate_key([:ec, curve])
|
273
|
+
kty_EC_crv.push(curve)
|
274
|
+
rescue StandardError, NotImplementedError
|
275
|
+
next
|
276
|
+
end
|
277
|
+
end
|
278
|
+
kty.push(key_type) if not kty_EC_crv.empty?
|
279
|
+
when 'OKP'
|
280
|
+
curves.each do |curve|
|
281
|
+
begin
|
282
|
+
JOSE::JWK.generate_key([:okp, curve.to_sym])
|
283
|
+
kty_OKP_crv.push(curve)
|
284
|
+
rescue StandardError, NotImplementedError
|
285
|
+
next
|
286
|
+
end
|
287
|
+
end
|
288
|
+
kty.push(key_type) if not kty_OKP_crv.empty?
|
289
|
+
when 'RSA'
|
290
|
+
begin
|
291
|
+
JOSE::JWK.generate_key([:rsa, 256])
|
292
|
+
kty.push(key_type)
|
293
|
+
rescue StandardError, NotImplementedError
|
294
|
+
# do nothing
|
295
|
+
end
|
296
|
+
when 'oct'
|
297
|
+
begin
|
298
|
+
JOSE::JWK.generate_key([:oct, 8])
|
299
|
+
kty.push(key_type)
|
300
|
+
rescue StandardError, NotImplementedError
|
301
|
+
# do nothing
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
return kty, kty_EC_crv, kty_OKP_crv
|
306
|
+
end
|
307
|
+
|
308
|
+
def __jws_alg_support_check__(signature_algorithms)
|
309
|
+
plain_text = SecureRandom.random_bytes(16)
|
310
|
+
rsa = nil
|
311
|
+
return signature_algorithms.select do |alg|
|
312
|
+
begin
|
313
|
+
jwk = nil
|
314
|
+
jwk ||= JOSE::JWK.generate_key([:oct, 0]).merge({ 'alg' => alg, 'use' => 'sig' }) if alg == 'none'
|
315
|
+
jwk ||= (rsa ||= JOSE::JWK.generate_key([:rsa, 1024])).merge({ 'alg' => alg, 'use' => 'sig' }) if alg.start_with?('RS') or alg.start_with?('PS')
|
316
|
+
jwk ||= JOSE::JWS.generate_key({ 'alg' => alg })
|
317
|
+
signed_text = jwk.sign(plain_text).compact
|
318
|
+
next jwk.verify_strict(signed_text, [alg]).first
|
319
|
+
rescue StandardError, NotImplementedError
|
320
|
+
next false
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
32
325
|
end
|
33
326
|
end
|
34
327
|
|