jose 0.3.1 → 1.0.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/.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
data/lib/jose/jws.rb
CHANGED
@@ -1,21 +1,254 @@
|
|
1
1
|
module JOSE
|
2
2
|
|
3
3
|
class SignedBinary < ::String
|
4
|
+
# Expands a compacted signed binary or list of signed binaries into a map.
|
5
|
+
# @see JOSE::JWS.expand
|
4
6
|
def expand
|
5
7
|
return JOSE::JWS.expand(self)
|
6
8
|
end
|
9
|
+
|
10
|
+
# Returns the decoded payload portion of a signed binary or map without verifying the signature.
|
11
|
+
# @see JOSE::JWS.peek_payload
|
12
|
+
def peek_payload
|
13
|
+
return JOSE::JWS.peek_payload(self)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the decoded protected portion of a signed binary or map without verifying the signature.
|
17
|
+
# @see JOSE::JWS.peek_protected
|
18
|
+
def peek_protected
|
19
|
+
return JOSE::JWS.peek_protected(self)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the decoded signature portion of a signed binary or map without verifying the signature.
|
23
|
+
# @see JOSE::JWS.peek_signature
|
24
|
+
def peek_signature
|
25
|
+
return JOSE::JWS.peek_signature(self)
|
26
|
+
end
|
7
27
|
end
|
8
28
|
|
29
|
+
# Immutable signed Map structure based on {JOSE::Map JOSE::Map}.
|
9
30
|
class SignedMap < JOSE::Map
|
31
|
+
# Compacts an expanded signed map or signed list into a binary.
|
32
|
+
# @see JOSE::JWS.compact
|
10
33
|
def compact
|
11
34
|
return JOSE::JWS.compact(self)
|
12
35
|
end
|
13
36
|
end
|
14
37
|
|
38
|
+
# JWS stands for JSON Web Signature which is defined in [RFC 7515](https://tools.ietf.org/html/rfc7515).
|
39
|
+
#
|
40
|
+
# ## Unsecured Signing Vulnerability
|
41
|
+
#
|
42
|
+
# The [`"none"`](https://tools.ietf.org/html/rfc7515#appendix-A.5) signing
|
43
|
+
# algorithm is disabled by default to prevent accidental verification of empty
|
44
|
+
# signatures (read about the vulnerability [here](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/)).
|
45
|
+
#
|
46
|
+
# You may also enable the `"none"` algorithm by setting the `JOSE_UNSECURED_SIGNING`
|
47
|
+
# environment variable or by using {JOSE.unsecured_signing= JOSE.unsecured_signing=}.
|
48
|
+
#
|
49
|
+
# ## Strict Verification Recommended
|
50
|
+
#
|
51
|
+
# {JOSE::JWS.verify_strict JOSE::JWS.verify_strict} is recommended over {JOSE::JWS.verify JOSE::JWS.verify} so that
|
52
|
+
# signing algorithms may be whitelisted during verification of signed input.
|
53
|
+
#
|
54
|
+
# ## Algorithms
|
55
|
+
#
|
56
|
+
# The following algorithms are currently supported by {JOSE::JWS JOSE::JWS} (some may need the {JOSE.crypto_fallback= JOSE.crypto_fallback=} option to be enabled):
|
57
|
+
#
|
58
|
+
# * `"Ed25519"`
|
59
|
+
# * `"Ed25519ph"`
|
60
|
+
# * `"Ed448"`
|
61
|
+
# * `"Ed448ph"`
|
62
|
+
# * `"ES256"`
|
63
|
+
# * `"ES384"`
|
64
|
+
# * `"ES512"`
|
65
|
+
# * `"HS256"`
|
66
|
+
# * `"HS384"`
|
67
|
+
# * `"HS512"`
|
68
|
+
# * `"PS256"`
|
69
|
+
# * `"PS384"`
|
70
|
+
# * `"PS512"`
|
71
|
+
# * `"RS256"`
|
72
|
+
# * `"RS384"`
|
73
|
+
# * `"RS512"`
|
74
|
+
# * `"none"` (disabled by default, enable with {JOSE.unsecured_signing= JOSE.unsecured_signing=})
|
75
|
+
#
|
76
|
+
# ## Examples
|
77
|
+
#
|
78
|
+
# All of the example keys generated below can be found here: [https://gist.github.com/potatosalad/925a8b74d85835e285b9](https://gist.github.com/potatosalad/925a8b74d85835e285b9)
|
79
|
+
#
|
80
|
+
# ### <a name="EdDSA-25519-group">Ed25519 and Ed25519ph</a>
|
81
|
+
#
|
82
|
+
# !!!ruby
|
83
|
+
# # let's generate the 2 keys we'll use below
|
84
|
+
# jwk_ed25519 = JOSE::JWK.generate_key([:okp, :Ed25519])
|
85
|
+
# jwk_ed25519ph = JOSE::JWK.generate_key([:okp, :Ed25519ph])
|
86
|
+
#
|
87
|
+
# # Ed25519
|
88
|
+
# signed_ed25519 = JOSE::JWS.sign(jwk_ed25519, "{}", { "alg" => "Ed25519" }).compact
|
89
|
+
# # => "eyJhbGciOiJFZDI1NTE5In0.e30.xyg2LTblm75KbLFJtROZRhEgAFJdlqH9bhx8a9LO1yvLxNLhO9fLqnFuU3ojOdbObr8bsubPkPqUfZlPkGHXCQ"
|
90
|
+
# JOSE::JWS.verify(jwk_ed25519, signed_ed25519).first
|
91
|
+
# # => true
|
92
|
+
#
|
93
|
+
# # Ed25519ph
|
94
|
+
# signed_ed25519ph = JOSE::JWS.sign(jwk_ed25519ph, "{}", { "alg" => "Ed25519ph" }).compact
|
95
|
+
# # => "eyJhbGciOiJFZDI1NTE5cGgifQ.e30.R3je4TTxQvoBOupIKkel_b8eW-G8KaWmXuC14NMGSCcHCTalURtMmVqX2KbcIpFBeI-OKP3BLHNIpt1keKveDg"
|
96
|
+
# JOSE::JWS.verify(jwk_ed25519ph, signed_ed25519ph).first
|
97
|
+
# # => true
|
98
|
+
#
|
99
|
+
# ### <a name="EdDSA-448-group">Ed448 and Ed448ph</a>
|
100
|
+
#
|
101
|
+
# !!!ruby
|
102
|
+
# # let's generate the 2 keys we'll use below
|
103
|
+
# jwk_ed448 = JOSE::JWK.generate_key([:okp, :Ed448])
|
104
|
+
# jwk_ed448ph = JOSE::JWK.generate_key([:okp, :Ed448ph])
|
105
|
+
#
|
106
|
+
# # Ed448
|
107
|
+
# signed_ed448 = JOSE::JWS.sign(jwk_ed448, "{}", { "alg" => "Ed448" }).compact
|
108
|
+
# # => "eyJhbGciOiJFZDQ0OCJ9.e30.UlqTx962FvZP1G5pZOrScRXlAB0DJI5dtZkknNTm1E70AapkONi8vzpvKd355czflQdc7uyOzTeAz0-eLvffCKgWm_zebLly7L3DLBliynQk14qgJgz0si-60mBFYOIxRghk95kk5hCsFpxpVE45jRIA"
|
109
|
+
# JOSE::JWS.verify(jwk_ed448, signed_ed448).first
|
110
|
+
# # => true
|
111
|
+
#
|
112
|
+
# # Ed448ph
|
113
|
+
# signed_ed448ph = JOSE::JWS.sign(jwk_ed448ph, "{}", { "alg" => "Ed448ph" }).compact
|
114
|
+
# # => "eyJhbGciOiJFZDQ0OHBoIn0.e30._7wxQF8Am-Fg3E-KgREXBv3Gr2vqLM6ja_7hs6kA5EakCrJVQ2QiAHrr4NriLABmiPbVd7F7IiaAApyR3Ud4ak3lGcHVxSyksjJjvBUbKnSB_xkT6v_QMmx27hV08JlxskUkfvjAG0-yKGC8BXoT9R0A"
|
115
|
+
# JOSE::JWS.verify(jwk_ed448ph, signed_ed448ph).first
|
116
|
+
# # => true
|
117
|
+
#
|
118
|
+
# ### <a name="ECDSA-group">ES256, ES384, and ES512</a>
|
119
|
+
#
|
120
|
+
# !!!ruby
|
121
|
+
# # let's generate the 3 keys we'll use below
|
122
|
+
# jwk_es256 = JOSE::JWK.generate_key([:ec, "P-256"])
|
123
|
+
# jwk_es384 = JOSE::JWK.generate_key([:ec, "P-384"])
|
124
|
+
# jwk_es512 = JOSE::JWK.generate_key([:ec, "P-521"])
|
125
|
+
#
|
126
|
+
# # ES256
|
127
|
+
# signed_es256 = JOSE::JWS.sign(jwk_es256, "{}", { "alg" => "ES256" }).compact
|
128
|
+
# # => "eyJhbGciOiJFUzI1NiJ9.e30.nb7cEQQuIi2NgcP5A468FHGG8UZg8gWZjloISyVIwNh3X6FiTTFZsvc0mL3RnulWoNJzKF6xwhae3botI1LbRg"
|
129
|
+
# JOSE::JWS.verify(jwk_es256, signed_es256).first
|
130
|
+
# # => true
|
131
|
+
#
|
132
|
+
# # ES384
|
133
|
+
# signed_es384 = JOSE::JWS.sign(jwk_es384, "{}", { "alg" => "ES384" }).compact
|
134
|
+
# # => "eyJhbGciOiJFUzM4NCJ9.e30.-2kZkNe66y2SprhgvvtMa0qBrSb2imPhMYkbi_a7vx-vpEHuVKsxCpUyNVLe5_CXaHWhHyc2rNi4uEfU73c8XQB3e03rg_JOj0H5XGIGS5G9f4RmNMSCiYGwqshLSDFI"
|
135
|
+
# JOSE::JWS.verify(jwk_es384, signed_es384).first
|
136
|
+
# # => true
|
137
|
+
#
|
138
|
+
# # ES512
|
139
|
+
# signed_es512 = JOSE::JWS.sign(jwk_es512, "{}", { "alg" => "ES512" }).compact
|
140
|
+
# # => "eyJhbGciOiJFUzUxMiJ9.e30.AOIw4KTq5YDu6QNrAYKtFP8R5IljAbhqXuPK1dUARPqlfc5F3mM0kmSh5KOVNHDmdCdapBv0F3b6Hl6glFDPlxpiASuSWtvvs9K8_CRfSkEzvToj8wf3WLGOarQHDwYXtlZoki1zMPGeWABwafTZNQaItNSpqYd_P9GtN0XM3AALdua0"
|
141
|
+
# JOSE::JWS.verify(jwk_es512, signed_es512).first
|
142
|
+
# # => true
|
143
|
+
#
|
144
|
+
# ### <a name="HMACSHA2-group">HS256, HS384, and HS512</a>
|
145
|
+
#
|
146
|
+
# !!!ruby
|
147
|
+
# # let's generate the 3 keys we'll use below
|
148
|
+
# jwk_hs256 = JOSE::JWK.generate_key([:oct, 16])
|
149
|
+
# jwk_hs384 = JOSE::JWK.generate_key([:oct, 24])
|
150
|
+
# jwk_hs512 = JOSE::JWK.generate_key([:oct, 32])
|
151
|
+
#
|
152
|
+
# # HS256
|
153
|
+
# signed_hs256 = JOSE::JWS.sign(jwk_hs256, "{}", { "alg" => "HS256" }).compact
|
154
|
+
# # => "eyJhbGciOiJIUzI1NiJ9.e30.r2JwwMFHECoDZlrETLT-sgFT4qN3w0MLee9MrgkDwXs"
|
155
|
+
# JOSE::JWS.verify(jwk_hs256, signed_hs256).first
|
156
|
+
# # => true
|
157
|
+
#
|
158
|
+
# # HS384
|
159
|
+
# signed_hs384 = JOSE::JWS.sign(jwk_hs384, "{}", { "alg" => "HS384" }).compact
|
160
|
+
# # => "eyJhbGciOiJIUzM4NCJ9.e30.brqQFXXM0XtMWDdKf0foEQcvK18swcoDkxBqCPeed_IO317_tisr60H2mz79SlNR"
|
161
|
+
# JOSE::JWS.verify(jwk_hs384, signed_hs384).first
|
162
|
+
# # => true
|
163
|
+
#
|
164
|
+
# # HS512
|
165
|
+
# signed_hs512 = JOSE::JWS.sign(jwk_hs512, "{}", { "alg" => "HS512" }).compact
|
166
|
+
# # => "eyJhbGciOiJIUzUxMiJ9.e30.ge1JYomO8Fyl6sgxLbc4g3AMPbaMHLmeTl0jrUYAJZSloN9j4VyhjucX8d-RWIlMjzdG0xyklw53k1-kaTlRVQ"
|
167
|
+
# JOSE::JWS.verify(jwk_hs512, signed_hs512).first
|
168
|
+
# # => true
|
169
|
+
#
|
170
|
+
# ### <a name="RSASSAPSS-group">PS256, PS384, and PS512</a>
|
171
|
+
#
|
172
|
+
# !!!ruby
|
173
|
+
# # let's generate the 3 keys we'll use below (cutkey must be installed as a dependency)
|
174
|
+
# jwk_ps256 = JOSE::JWK.generate_key([:rsa, 2048])
|
175
|
+
# jwk_ps384 = JOSE::JWK.generate_key([:rsa, 4096])
|
176
|
+
# jwk_ps512 = JOSE::JWK.generate_key([:rsa, 8192]) # this may take a few seconds
|
177
|
+
#
|
178
|
+
# # PS256
|
179
|
+
# signed_ps256 = JOSE::JWS.sign(jwk_ps256, "{}", { "alg" => "PS256" }).compact
|
180
|
+
# # => "eyJhbGciOiJQUzI1NiJ9.e30.RY5A3rG2TjmdlARE57eSSSFE6plkuQPKLKsyqz3WrqKRWZgSrvROACRTzoGyrx1sNvQEZJLZ-xVhrFvP-80Q14XzQbPfYLubvn-2wcMNCmih3OVQNVtFdFjA5U2NG-sF-SWAUmm9V_DvMShFGG0qHxLX7LqT83lAIgEulgsytb0xgOjtJObBru5jLjN_uEnc7fCfnxi3my1GAtnrs9NiKvMfuIVlttvOORDFBTO2aFiCv1F-S6Xgj16rc0FGImG0x3amQcmFAD9g41KY0_KsCXgUfoiVpC6CqO6saRC4UDykks91B7Nuoxjsm3nKWa_4vKh9QJy-V8Sf0gHxK58j8Q"
|
181
|
+
# JOSE::JWS.verify(jwk_ps256, signed_ps256).first
|
182
|
+
# # => true
|
183
|
+
#
|
184
|
+
# # PS384
|
185
|
+
# signed_ps384 = JOSE::JWS.sign(jwk_ps384, "{}", { "alg" => "PS384" }).compact
|
186
|
+
# # => "eyJhbGciOiJQUzM4NCJ9.e30.xmYVenIhi75hDMy3bnL6WVpVlTzYmO1ejOZeq9AkSjkp_STrdIp6uUEs9H_y7CLD9LrGYYHDNDl9WmoH6cn95WZT9KJgAVNFFYd8owY6JUHGKU1jUbLkptAgvdphVpWZ1C5fVCRt4vmp8K9f6jy3er9jCBNjl9gSBdmToFwYdXI26ZKSBjfoVm2tFFQIOThye4YQWCWHbzSho6J7d5ATje72L30zDvWXavJ-XNvof5Tkju4WQQB-ukFoqTw4yV8RVwCa-DX61I1hNrq-Zr75_iWmHak3GqNkg5ACBEjDtvtyxJizqy9KINKSlbB9jGztiWoEiXZ6wJ5sSJ6ZrSFJuQVEmns_dLqzpSHEFkWfczEV_gj9Eu_EXwMp9YQlQ3GktfXaz-mzH_jUaLmudEUskQGCiR92gK9KR6_ROQPJfD54Tkqdh6snwg6y17k8GdlTc5qMM3V84q3R6zllmhrRhV1Dlduc0MEqKcsQSX_IX21-sfiVMIcUsW73dIPXVZI2jsNlEHKqwMjWdSfjYUf3YApxSGERU3u4lRS3F0yRrZur8KWS3ToilApjg0cNg9jKas8g8C8ZPgGFYM6StVxUnXRmsJILDnsZMIPjbUDAPHhB0DwLwOB7OqGUBcItX-zwur1OVnHR7aIh1DbfWfyTIml8VIhYfGfazgXfgQVcGEM"
|
187
|
+
# JOSE::JWS.verify(jwk_ps384, signed_ps384).first
|
188
|
+
# # => true
|
189
|
+
#
|
190
|
+
# # PS512
|
191
|
+
# signed_ps512 = JOSE::JWS.sign(jwk_ps512, "{}", { "alg" => "PS512" }).compact
|
192
|
+
# # => "eyJhbGciOiJQUzUxMiJ9.e30.fJe52-PF3I7UrpQamLCnmVAGkBhP0HVeJi48qZqaFc1-_tQEiYTfxuwQBDlt01GQWpjTZRb097bZF6RcrKWwRHyAo3otOZdR32emWfOHddWLL3qotj_fTaDR2-OhLixwce6mFjnHqppHH1zjCmgbKPG8S2cAadNd5w10VR-IS6LdnFRhNZOahuuB7dzCEJaSjkGfm3_9xdj3I0ZRl4fauR_LO9NQIyvMMeCFevowz1sVGG1G-I2njPrEXvxhAMp7y2mao5Yik8UUORXRjcn2Wai3umy8Yh4nHYU5qqruHjLjDwudCPNDjxjg294z1uAUpt7S0v7VbrkgUvgutTFAT-bcHywFODiycajQuqIpFp1TCUAq3Xe2yk4DTRduvPIKcPkJQnFrVkClJAU9A4D4602xpdK-z2uCgWsBVHVokf5-9ba5EqVb8BJx2xYZUIA5CdrIiTBfoe_cI5Jh92uprcWC_llio2ZJvGdQpPgwCgca7-RQ94LAmIA4u3mAndrZj_z48T2GjHbaKzl18FOPQH0XEvK_W5oypUe5NOGlz9mMGZigbFqBY2lM-7oVVYc4ZA3VFy8Dv1nWhU6DGb2NnDnQUyChllyBREuZbwrkOTQEvqqdV-6lM6VwXNu1gqc3YHly9W6u5CmsnxtvlIxsUVg679HiqdtdWxLSaIJObd9Xji56-eEkWMEA08SNy9p-F9AgHOxzoZqgrAQDEwqyEwqoAW681xLc5Vck580AQDxO9Ha4IqLIPirpO5EODQjOd8-S_SlAP5o_wz1Oh38MC5T5V13PqPuZ70dbggB4bUgVaHYC4FE4XHCqP7W3xethaPc68cY9-g9f1RUvthmnEYXSRpvyaMY3iX0txZazWIS_Jg7pNTCEaWr9JCLTZd1MiLbFowPvKYGM-z-39K31OUbq5PIScy0I9OOz9joecm8KsCesA2ysPph1E7cL7Etiw5tGhCFzcdQwm8Gm6SDwj8vCEcZUkXeZJfhlS1cJtZk1sNu3KZNndevtZjRWaXi2m4WNKVxVE-nuaF7V3GWfDemh9RXxyFK8OC8aYLIqcc2pAKJM47ANVty2ll1xaCIB3q3CKdnk5fmsnzKkQI9SjKy70p9TWT-NNoYU682KG_mZo-ByEs5CvJ8w7qysmX8Xpb2I6oSJf7S3qjbqkqtXQcV5MuQ232vk7-g42CcQGL82xvRc09TuvwnmykpKHmjUaJ4U9k9zTN3g2iTdpkvl6vbnND9uG1SBaieVeFYWCT-6VdhovEiD9bvIdA7D_R7NZO8YHBt_lfBQRle_jDyLzHSlkP6kt9dYRhrc2SNMzF_4i3iEUAihbaQYvbNsGwWrHqyGofnva20pRXwc4GxOlw"
|
193
|
+
# JOSE::JWS.verify(jwk_ps512, signed_ps512).first
|
194
|
+
# # => true
|
195
|
+
#
|
196
|
+
# ### <a name="RSASSAPKCS1_5-group">RS256, RS384, and RS512</a>
|
197
|
+
#
|
198
|
+
# !!!ruby
|
199
|
+
# # let's generate the 3 keys we'll use below
|
200
|
+
# jwk_rs256 = JOSE::JWK.generate_key([:rsa, 1024])
|
201
|
+
# jwk_rs384 = JOSE::JWK.generate_key([:rsa, 2048])
|
202
|
+
# jwk_rs512 = JOSE::JWK.generate_key([:rsa, 4096])
|
203
|
+
#
|
204
|
+
# # RS256
|
205
|
+
# signed_rs256 = JOSE::JWS.sign(jwk_rs256, "{}", { "alg" => "RS256" }).compact
|
206
|
+
# # => "eyJhbGciOiJSUzI1NiJ9.e30.C0J8v5R-sEe9-g_s0SMgPorCh8VDdaZ9gLpWNm1Tn1Cv2xRph1Xn9Rzm10ZCEs84sj7kxA4v28fVShQ_P1AHN83yQ2mvstkKwsuwXxr-cludx_NLQL5CKKQtTR0ITD_pxUowjfAkBYuJv0677jUj-8lGKs1P5e2dbwW9IqFe4uE"
|
207
|
+
# JOSE::JWS.verify(jwk_rs256, signed_rs256).first
|
208
|
+
# # => true
|
209
|
+
#
|
210
|
+
# # RS384
|
211
|
+
# signed_rs384 = JOSE::JWS.sign(jwk_rs384, "{}", { "alg" => "RS384" }).compact
|
212
|
+
# # => "eyJhbGciOiJSUzM4NCJ9.e30.fvPxeNhO0oitOsdqFmrBgpGE7Gn_NdJ1J8F5ArKon54pdHB2v30hua9wbG4V2Hr-hNAyflaBJtoGAwIpKVkfHn-IW7d06hKw_Hv0ecG-VvZr60cK2IJnHS149Htz_652egThZh1GIKRZN1IrRVlraLMozFcWP0Ojc-L-g5XjcTFafesmV0GFGfFubAiQWEiWIgNV3822L-wPe7ZGeFe5yYsZ70WMHQQ1tSuNsm5QUOUVInOThAhJ30FRTCNFgv46l4TEF9aaI9443cKAbwzd_EavD0FpvgpwEhGyNTVx0sxiCZIYUE_jN53aSaHXB82d0xwIr2-GXlr3Y-dLwERIMw"
|
213
|
+
# JOSE::JWS.verify(jwk_rs384, signed_rs384).first
|
214
|
+
# # => true
|
215
|
+
#
|
216
|
+
# # RS512
|
217
|
+
# signed_rs512 = JOSE::JWS.sign(jwk_rs512, "{}", { "alg" => "RS512" }).compact
|
218
|
+
# # => "eyJhbGciOiJSUzUxMiJ9.e30.le2_kCnmj6Y02bl16Hh5EPqmLsFkB3YZpiEfvmA6xfdg9I3QJ5uSgOejs_HpuIbItuMFUdcqtkfW45_6YKlI7plB49iWiNnWY0PLxsvbiZaSmT4R4dOUWx9KlO_Ui5SE94XkigUoFanDTHTr9bh4NpvoIaNdi_xLdC7FYA-AqZspegRcgY-QZQv4kbD3NQJtxsEiAXk8-C8CX3lF6haRlh7s4pyAmgj7SJeElsPjhPNVZ7EduhTLZfVwiLrRmzLKQ6dJ_PrZDig1lgl9jf2NjzcsFpt6lvfrMsDdIQEGyJoh53-zXiD_ltyAZGS3pX-_tHRxoAZ1SyAPkkC4cCra6wc-03sBQPoUa26xyyhrgf4h7E2l-JqhKPXT7pJv6AbRPgKUH4prEH636gpoWQrRc-JxbDIJHR0ShdL8ssf5e-rKpcVVAZKnRI64NbSKXTg-JtDxhU9QG8JVEkHqOxSeo-VSXOoExdmm8lCfqylrw7qmDxjEwOq7TGjhINyjVaK1Op_64BWVuCzgooea6G2ZvCTIEl0-k8wY8s9VC7hxSrsgCAnpWeKpIcbLQoDIoyasG-6Qb5OuSLR367eg9NAQ8WMTbrrQkm-KLNCYvMFaxmlWzBFST2JDmIr0VH9BzXRAdfG81SymuyFA7_FdpiVYwAwEGR4Q5HYEpequ38tHu3Y"
|
219
|
+
# JOSE::JWS.verify(jwk_rs512, signed_rs512).first
|
220
|
+
# # => true
|
15
221
|
class JWS < Struct.new(:alg, :b64, :fields)
|
16
222
|
|
17
223
|
# Decode API
|
18
224
|
|
225
|
+
# Converts a binary or map into a {JOSE::JWS JOSE::JWS}.
|
226
|
+
#
|
227
|
+
# !!!ruby
|
228
|
+
# JOSE::JWS.from({ "alg" => "HS256" })
|
229
|
+
# # => #<struct JOSE::JWS
|
230
|
+
# # alg=#<struct JOSE::JWS::ALG_HMAC hmac=OpenSSL::Digest::SHA256>,
|
231
|
+
# # b64=nil,
|
232
|
+
# # fields=JOSE::Map[]>
|
233
|
+
# JOSE::JWS.from("{\"alg\":\"HS256\"}")
|
234
|
+
# # => #<struct JOSE::JWS
|
235
|
+
# # alg=#<struct JOSE::JWS::ALG_HMAC hmac=OpenSSL::Digest::SHA256>,
|
236
|
+
# # b64=nil,
|
237
|
+
# # fields=JOSE::Map[]>
|
238
|
+
#
|
239
|
+
# Support for custom algorithms may be added by specifying `:alg` under `modules`:
|
240
|
+
#
|
241
|
+
# !!!ruby
|
242
|
+
# JOSE::JWS.from({ "alg" => "custom" }, { alg: MyCustomAlgorithm })
|
243
|
+
# # => #<struct JOSE::JWS
|
244
|
+
# # alg=#<MyCustomAlgorithm:0x007f8c5419ff68>,
|
245
|
+
# # b64=nil,
|
246
|
+
# # fields=JOSE::Map[]>
|
247
|
+
#
|
248
|
+
# *Note:* `MyCustomAlgorithm` must implement the methods mentioned in other alg modules.
|
249
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWS, Array<JOSE::Map, Hash, String, JOSE::JWS>] object
|
250
|
+
# @param [Hash] modules
|
251
|
+
# @return [JOSE::JWS, Array<JOSE::JWS>]
|
19
252
|
def self.from(object, modules = {})
|
20
253
|
case object
|
21
254
|
when JOSE::Map, Hash
|
@@ -24,61 +257,121 @@ module JOSE
|
|
24
257
|
return from_binary(object, modules)
|
25
258
|
when JOSE::JWS
|
26
259
|
return object
|
260
|
+
when Array
|
261
|
+
return object.map { |obj| from(obj, modules) }
|
27
262
|
else
|
28
|
-
raise ArgumentError, "'object' must be a Hash, String,
|
263
|
+
raise ArgumentError, "'object' must be a Hash, String, JOSE::JWS, or Array"
|
29
264
|
end
|
30
265
|
end
|
31
266
|
|
267
|
+
# Converts a binary into a {JOSE::JWS JOSE::JWS}.
|
268
|
+
# @param [String, Array<String>] object
|
269
|
+
# @param [Hash] modules
|
270
|
+
# @return [JOSE::JWS, Array<JOSE::JWS>]
|
32
271
|
def self.from_binary(object, modules = {})
|
33
272
|
case object
|
34
273
|
when String
|
35
274
|
return from_map(JOSE.decode(object), modules)
|
275
|
+
when Array
|
276
|
+
return object.map { |obj| from_binary(obj, modules) }
|
36
277
|
else
|
37
|
-
raise ArgumentError, "'object' must be a String"
|
278
|
+
raise ArgumentError, "'object' must be a String or Array"
|
38
279
|
end
|
39
280
|
end
|
40
281
|
|
282
|
+
# Reads file and calls {.from_binary} to convert into a {JOSE::JWS JOSE::JWS}.
|
283
|
+
# @param [String] file
|
284
|
+
# @param [Hash] modules
|
285
|
+
# @return [JOSE::JWS]
|
41
286
|
def self.from_file(file, modules = {})
|
42
287
|
return from_binary(File.binread(file), modules)
|
43
288
|
end
|
44
289
|
|
290
|
+
# Converts a map into a {JOSE::JWS JOSE::JWS}.
|
291
|
+
# @param [JOSE::Map, Hash, Array<JOSE::Map, Hash>] object
|
292
|
+
# @param [Hash] modules
|
293
|
+
# @return [JOSE::JWS, Array<JOSE::JWS>]
|
45
294
|
def self.from_map(object, modules = {})
|
46
295
|
case object
|
47
296
|
when JOSE::Map, Hash
|
48
297
|
return from_fields(JOSE::JWS.new(nil, nil, JOSE::Map.new(object)), modules)
|
298
|
+
when Array
|
299
|
+
return object.map { |obj| from_map(obj, modules) }
|
49
300
|
else
|
50
|
-
raise ArgumentError, "'object' must be a Hash"
|
301
|
+
raise ArgumentError, "'object' must be a Hash or Array"
|
51
302
|
end
|
52
303
|
end
|
53
304
|
|
54
305
|
# Encode API
|
55
306
|
|
307
|
+
# Converts a {JOSE::JWS JOSE::JWS} into a binary.
|
308
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWS, Array<JOSE::Map, Hash, String, JOSE::JWS>] jws
|
309
|
+
# @return [String, Array<String>]
|
56
310
|
def self.to_binary(jws)
|
57
|
-
|
311
|
+
if jws.is_a?(Array)
|
312
|
+
return from(jws).map { |obj| obj.to_binary }
|
313
|
+
else
|
314
|
+
return from(jws).to_binary
|
315
|
+
end
|
58
316
|
end
|
59
317
|
|
318
|
+
# Converts a {JOSE::JWS JOSE::JWS} into a binary.
|
319
|
+
# @return [String]
|
60
320
|
def to_binary
|
61
321
|
return JOSE.encode(to_map)
|
62
322
|
end
|
63
323
|
|
324
|
+
# Calls {.to_binary} on a {JOSE::JWS JOSE::JWS} and then writes the binary to `file`.
|
325
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWS] jws
|
326
|
+
# @param [String] file
|
327
|
+
# @return [Fixnum] bytes written
|
64
328
|
def self.to_file(jws, file)
|
65
329
|
return from(jws).to_file(file)
|
66
330
|
end
|
67
331
|
|
332
|
+
# Calls {#to_binary} on a {JOSE::JWS JOSE::JWS} and then writes the binary to `file`.
|
333
|
+
# @param [String] file
|
334
|
+
# @return [Fixnum] bytes written
|
68
335
|
def to_file(file)
|
69
336
|
return File.binwrite(file, to_binary)
|
70
337
|
end
|
71
338
|
|
339
|
+
# Converts a {JOSE::JWS JOSE::JWS} into a map.
|
340
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWS, Array<JOSE::Map, Hash, String, JOSE::JWS>] jws
|
341
|
+
# @return [JOSE::Map, Array<JOSE::Map>]
|
72
342
|
def self.to_map(jws)
|
73
|
-
|
343
|
+
if jws.is_a?(Array)
|
344
|
+
return from(jws).map { |obj| obj.to_map }
|
345
|
+
else
|
346
|
+
return from(jws).to_map
|
347
|
+
end
|
74
348
|
end
|
75
349
|
|
350
|
+
# Converts a {JOSE::JWS JOSE::JWS} into a map.
|
351
|
+
# @return [JOSE::Map]
|
76
352
|
def to_map
|
77
|
-
|
353
|
+
map = alg.to_map(fields)
|
354
|
+
if b64 == false or b64 == true
|
355
|
+
map = map.put('b64', b64)
|
356
|
+
end
|
357
|
+
return map
|
78
358
|
end
|
79
359
|
|
80
360
|
# API
|
81
361
|
|
362
|
+
# Compacts an expanded signed map or signed list into a binary.
|
363
|
+
#
|
364
|
+
# !!!ruby
|
365
|
+
# JOSE::JWS.compact({
|
366
|
+
# "payload" => "e30",
|
367
|
+
# "protected" => "eyJhbGciOiJIUzI1NiJ9",
|
368
|
+
# "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"
|
369
|
+
# })
|
370
|
+
# # => "eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"
|
371
|
+
#
|
372
|
+
# @see JOSE::JWS.expand
|
373
|
+
# @param [JOSE::SignedMap, JOSE::Map, Hash] map
|
374
|
+
# @return [JOSE::SignedBinary]
|
82
375
|
def self.compact(map)
|
83
376
|
if map.is_a?(Hash) or map.is_a?(JOSE::Map)
|
84
377
|
return JOSE::SignedBinary.new([
|
@@ -93,6 +386,18 @@ module JOSE
|
|
93
386
|
end
|
94
387
|
end
|
95
388
|
|
389
|
+
# Expands a compacted signed binary or list of signed binaries into a map.
|
390
|
+
#
|
391
|
+
# !!!ruby
|
392
|
+
# JOSE::JWS.expand("eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU")
|
393
|
+
# # => JOSE::SignedMap[
|
394
|
+
# # "protected" => "eyJhbGciOiJIUzI1NiJ9",
|
395
|
+
# # "payload" => "e30",
|
396
|
+
# # "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"]
|
397
|
+
#
|
398
|
+
# @see JOSE::JWS.compact
|
399
|
+
# @param [JOSE::SignedBinary, String] binary
|
400
|
+
# @return [JOSE::SignedMap]
|
96
401
|
def self.expand(binary)
|
97
402
|
if binary.is_a?(String)
|
98
403
|
if binary.count('.') == 2 and (parts = binary.split('.', 3)).length == 3
|
@@ -110,18 +415,46 @@ module JOSE
|
|
110
415
|
end
|
111
416
|
end
|
112
417
|
|
113
|
-
|
114
|
-
|
418
|
+
# Generates a new {JOSE::JWK JOSE::JWK} based on the algorithms of the specified {JOSE::JWS JOSE::JWS}.
|
419
|
+
#
|
420
|
+
# !!!ruby
|
421
|
+
# JOSE::JWS.generate_key({"alg" => "HS256"})
|
422
|
+
# # => #<struct JOSE::JWK
|
423
|
+
# # keys=nil,
|
424
|
+
# # kty=
|
425
|
+
# # #<struct JOSE::JWK::KTY_oct
|
426
|
+
# # oct="\x96G\x1DO\xE4 \xDA\x04o\xFA\xD4\x81\xE2\xADV\xCDH0bdBDq\r+<z\xF8\xB3,\x8C\x18">,
|
427
|
+
# # fields=JOSE::Map["alg" => "HS256", "use" => "sig"]>
|
428
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWS, Array<JOSE::Map, Hash, String, JOSE::JWS>] jws
|
429
|
+
# @param [Hash] modules
|
430
|
+
# @return [JOSE::JWK, Array<JOSE::JWK>]
|
431
|
+
def self.generate_key(jws, modules = {})
|
432
|
+
if jws.is_a?(Array)
|
433
|
+
return from(jws, modules).map { |obj| obj.generate_key }
|
434
|
+
else
|
435
|
+
return from(jws, modules).generate_key
|
436
|
+
end
|
115
437
|
end
|
116
438
|
|
439
|
+
# Generates a new {JOSE::JWK JOSE::JWK} based on the algorithms of the specified {JOSE::JWS JOSE::JWS}.
|
440
|
+
#
|
441
|
+
# @see JOSE::JWS.generate_key
|
442
|
+
# @return [JOSE::JWK]
|
117
443
|
def generate_key
|
118
444
|
return alg.generate_key(fields)
|
119
445
|
end
|
120
446
|
|
447
|
+
# Merges map on right into map on left.
|
448
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWS] left
|
449
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWS] right
|
450
|
+
# @return [JOSE::JWS]
|
121
451
|
def self.merge(left, right)
|
122
452
|
return from(left).merge(right)
|
123
453
|
end
|
124
454
|
|
455
|
+
# Merges object into current map.
|
456
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWS] object
|
457
|
+
# @return [JOSE::JWS]
|
125
458
|
def merge(object)
|
126
459
|
object = case object
|
127
460
|
when JOSE::Map, Hash
|
@@ -136,6 +469,14 @@ module JOSE
|
|
136
469
|
return JOSE::JWS.from_map(self.to_map.merge(object))
|
137
470
|
end
|
138
471
|
|
472
|
+
# Returns the decoded payload portion of a signed binary or map without verifying the signature.
|
473
|
+
#
|
474
|
+
# !!!ruby
|
475
|
+
# JOSE::JWS.peek_payload("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.dMAojPMVbFvvkouYUSI9AxIRBxgqretQMCvNF7KmTHU")
|
476
|
+
# # => "{}"
|
477
|
+
#
|
478
|
+
# @param [JOSE::SignedBinary, String] signed
|
479
|
+
# @return [String]
|
139
480
|
def self.peek_payload(signed)
|
140
481
|
if signed.is_a?(String)
|
141
482
|
signed = expand(signed)
|
@@ -143,6 +484,14 @@ module JOSE
|
|
143
484
|
return JOSE.urlsafe_decode64(signed['payload'])
|
144
485
|
end
|
145
486
|
|
487
|
+
# Returns the decoded protected portion of a signed binary or map without verifying the signature.
|
488
|
+
#
|
489
|
+
# !!!ruby
|
490
|
+
# JOSE::JWS.peek_protected("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.dMAojPMVbFvvkouYUSI9AxIRBxgqretQMCvNF7KmTHU")
|
491
|
+
# # => JOSE::Map["alg" => "HS256", "typ" => "JWT"]
|
492
|
+
#
|
493
|
+
# @param [JOSE::SignedBinary, String] signed
|
494
|
+
# @return [JOSE::Map]
|
146
495
|
def self.peek_protected(signed)
|
147
496
|
if signed.is_a?(String)
|
148
497
|
signed = expand(signed)
|
@@ -150,27 +499,112 @@ module JOSE
|
|
150
499
|
return JOSE::Map.new(JOSE.decode(JOSE.urlsafe_decode64(signed['protected'])))
|
151
500
|
end
|
152
501
|
|
153
|
-
|
154
|
-
|
502
|
+
# Returns the decoded signature portion of a signed binary or map without verifying the signature.
|
503
|
+
#
|
504
|
+
# !!!ruby
|
505
|
+
# JOSE::JWS.peek_signature("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.dMAojPMVbFvvkouYUSI9AxIRBxgqretQMCvNF7KmTHU")
|
506
|
+
# # => "t\xC0(\x8C\xF3\x15l[\xEF\x92\x8B\x98Q\"=\x03\x12\x11\a\x18*\xAD\xEBP0+\xCD\x17\xB2\xA6Lu"
|
507
|
+
#
|
508
|
+
# @param [JOSE::SignedBinary, String] signed
|
509
|
+
# @return [String]
|
510
|
+
def self.peek_signature(signed)
|
511
|
+
if signed.is_a?(String)
|
512
|
+
signed = expand(signed)
|
513
|
+
end
|
514
|
+
return JOSE.urlsafe_decode64(signed['signature'])
|
515
|
+
end
|
516
|
+
|
517
|
+
# Signs the `plain_text` using the `jwk` and algorithm specified by the `jws`.
|
518
|
+
#
|
519
|
+
# !!!ruby
|
520
|
+
# jwk = JOSE::JWK.from({"k" => "qUg4Yw", "kty" => "oct"})
|
521
|
+
# # => #<struct JOSE::JWK keys=nil, kty=#<struct JOSE::JWK::KTY_oct oct="\xA9H8c">, fields=JOSE::Map[]>
|
522
|
+
# JOSE::JWS.sign(jwk, "{}", { "alg" => "HS256" })
|
523
|
+
# # => JOSE::SignedMap[
|
524
|
+
# # "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU",
|
525
|
+
# # "protected" => "eyJhbGciOiJIUzI1NiJ9",
|
526
|
+
# # "payload" => "e30"]
|
527
|
+
#
|
528
|
+
# If the `jwk` has a `"kid"` assigned, it will be added to the `"header"` on the signed map:
|
529
|
+
#
|
530
|
+
# !!!ruby
|
531
|
+
# jwk = JOSE::JWK.from({"k" => "qUg4Yw", "kid" => "eyHC48MN26DvoBpkaudvOVXuI5Sy8fKMxQMYiRWmjFw", "kty" => "oct"})
|
532
|
+
# # => #<struct JOSE::JWK
|
533
|
+
# # keys=nil,
|
534
|
+
# # kty=#<struct JOSE::JWK::KTY_oct oct="\xA9H8c">,
|
535
|
+
# # fields=JOSE::Map["kid" => "eyHC48MN26DvoBpkaudvOVXuI5Sy8fKMxQMYiRWmjFw"]>
|
536
|
+
# JOSE::JWS.sign(jwk, "test", { "alg" => "HS256" })
|
537
|
+
# # => JOSE::SignedMap[
|
538
|
+
# # "signature" => "ZEBxtZ4SAW5hYyT7CKxH8dqynTAg-Y24QjkudQMaA_M",
|
539
|
+
# # "header" => {"kid"=>"eyHC48MN26DvoBpkaudvOVXuI5Sy8fKMxQMYiRWmjFw"},
|
540
|
+
# # "protected" => "eyJhbGciOiJIUzI1NiJ9",
|
541
|
+
# # "payload" => "dGVzdA"]
|
542
|
+
#
|
543
|
+
# *Note:* Signed maps with a `"header"` or other fields will have data loss when used with {JOSE::JWS.compact JOSE::JWS.compact}.
|
544
|
+
# @param [JOSE::JWK] jwk
|
545
|
+
# @param [String] plain_text
|
546
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWS] jws
|
547
|
+
# @param [JOSE::Map, Hash] header
|
548
|
+
# @return [JOSE::SignedMap]
|
549
|
+
def self.sign(jwk, plain_text, jws, header = nil)
|
550
|
+
return from(jws).sign(jwk, plain_text, header)
|
155
551
|
end
|
156
552
|
|
157
|
-
|
553
|
+
# Signs the `plain_text` using the `jwk` and algorithm specified by the `jws`.
|
554
|
+
# @see JOSE::JWS.sign
|
555
|
+
# @param [JOSE::JWK] jwk
|
556
|
+
# @param [String] plain_text
|
557
|
+
# @param [JOSE::Map, Hash] header
|
558
|
+
# @return [JOSE::SignedMap]
|
559
|
+
def sign(jwk, plain_text, header = nil)
|
158
560
|
protected_binary = JOSE.urlsafe_encode64(to_binary)
|
159
561
|
payload = JOSE.urlsafe_encode64(plain_text)
|
160
562
|
signing_input = signing_input(plain_text, protected_binary)
|
161
|
-
signature = JOSE.urlsafe_encode64(alg.sign(
|
162
|
-
return signature_to_map(payload, protected_binary, header,
|
563
|
+
signature = JOSE.urlsafe_encode64(alg.sign(jwk, signing_input))
|
564
|
+
return signature_to_map(payload, protected_binary, header, jwk, signature)
|
565
|
+
end
|
566
|
+
|
567
|
+
# Combines `payload` and `protected_binary` based on the `"b64"` setting on the `jws` for the signing input used by {JOSE::JWS.sign JOSE::JWS.sign}.
|
568
|
+
#
|
569
|
+
# If `"b64"` is set to `false` on the `jws`, the raw `payload` will be used:
|
570
|
+
#
|
571
|
+
# !!!ruby
|
572
|
+
# JOSE::JWS.signing_input("{}", { "alg" => "HS256" })
|
573
|
+
# # => "eyJhbGciOiJIUzI1NiJ9.e30"
|
574
|
+
# JOSE::JWS.signing_input("{}", { "alg" => "HS256", "b64" => false })
|
575
|
+
# # => "eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2V9.{}"
|
576
|
+
#
|
577
|
+
# @see https://tools.ietf.org/html/draft-ietf-jose-jws-signing-input-options-04 JWS Unencoded Payload Option
|
578
|
+
# @param [String] payload
|
579
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWS] jws
|
580
|
+
# @param [String] protected_binary
|
581
|
+
# @return [String]
|
582
|
+
def self.signing_input(payload, jws, protected_binary = nil)
|
583
|
+
return from(jws).signing_input(payload, protected_binary)
|
163
584
|
end
|
164
585
|
|
165
|
-
#
|
166
|
-
|
586
|
+
# Combines `payload` and `protected_binary` based on the `"b64"` setting on the `jws` for the signing input used by {JOSE::JWS.sign JOSE::JWS.sign}.
|
587
|
+
# @see JOSE::JWS.signing_input
|
588
|
+
def signing_input(payload, protected_binary = nil)
|
167
589
|
if b64 == true or b64.nil?
|
168
590
|
payload = JOSE.urlsafe_encode64(payload)
|
169
591
|
end
|
592
|
+
protected_binary ||= JOSE.urlsafe_encode64(to_binary)
|
170
593
|
return [protected_binary, '.', payload].join
|
171
594
|
end
|
172
595
|
|
173
|
-
|
596
|
+
# Verifies the `signed` using the `jwk`.
|
597
|
+
#
|
598
|
+
# !!!ruby
|
599
|
+
# jwk = JOSE::JWK.from({"k" => "qUg4Yw", "kty" => "oct"})
|
600
|
+
# # => #<struct JOSE::JWK keys=nil, kty=#<struct JOSE::JWK::KTY_oct oct="\xA9H8c">, fields=JOSE::Map[]>
|
601
|
+
# JOSE::JWS.verify(jwk, "eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU")
|
602
|
+
# # => => [true, "{}", #<struct JOSE::JWS alg=#<struct JOSE::JWS::ALG_HMAC hmac=OpenSSL::Digest::SHA256>, b64=nil, fields=JOSE::Map[]>]
|
603
|
+
#
|
604
|
+
# @param [JOSE::JWK] jwk
|
605
|
+
# @param [JOSE::SignedBinary, JOSE::SignedMap, Hash, String] signed
|
606
|
+
# @return [[Boolean, String, JOSE::JWS]]
|
607
|
+
def self.verify(jwk, signed)
|
174
608
|
if signed.is_a?(String)
|
175
609
|
signed = JOSE::JWS.expand(signed)
|
176
610
|
end
|
@@ -181,18 +615,49 @@ module JOSE
|
|
181
615
|
jws = from_binary(JOSE.urlsafe_decode64(signed['protected']))
|
182
616
|
signature = JOSE.urlsafe_decode64(signed['signature'])
|
183
617
|
plain_text = JOSE.urlsafe_decode64(signed['payload'])
|
184
|
-
return jws.verify(
|
618
|
+
return jws.verify(jwk, plain_text, signature, signed['protected'])
|
185
619
|
else
|
186
620
|
raise ArgumentError, "'signed' is not a valid signed String, Hash, or JOSE::Map"
|
187
621
|
end
|
188
622
|
end
|
189
623
|
|
190
|
-
|
624
|
+
# Verifies the `signature` using the `jwk`, `plain_text`, and `protected_binary`.
|
625
|
+
# @see JOSE::JWS.verify
|
626
|
+
# @see JOSE::JWS.verify_strict
|
627
|
+
# @param [JOSE::JWK] jwk
|
628
|
+
# @param [String] plain_text
|
629
|
+
# @param [String] signature
|
630
|
+
# @param [String] protected_binary
|
631
|
+
# @return [[Boolean, String, JOSE::JWS]]
|
632
|
+
def verify(jwk, plain_text, signature, protected_binary = nil)
|
633
|
+
protected_binary ||= JOSE.urlsafe_encode64(to_binary)
|
191
634
|
signing_input = signing_input(plain_text, protected_binary)
|
192
|
-
return alg.verify(
|
635
|
+
return alg.verify(jwk, signing_input, signature), plain_text, self
|
193
636
|
end
|
194
637
|
|
195
|
-
|
638
|
+
# Same as {JOSE::JWS.verify JOSE::JWS.verify}, but uses `allow` as a whitelist for `"alg"` which are allowed to verify against.
|
639
|
+
#
|
640
|
+
# If the detected algorithm is not present in `allow`, then `false` is returned.
|
641
|
+
#
|
642
|
+
# !!!ruby
|
643
|
+
# jwk = JOSE::JWK.from({"k" => "qUg4Yw", "kty" => "oct"})
|
644
|
+
# # => #<struct JOSE::JWK keys=nil, kty=#<struct JOSE::JWK::KTY_oct oct="\xA9H8c">, fields=JOSE::Map[]>
|
645
|
+
# signed_hs256 = JOSE::JWS.sign(jwk, "{}", { "alg" => "HS256" }).compact
|
646
|
+
# # => "eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"
|
647
|
+
# signed_hs512 = JOSE::JWS.sign(jwk, "{}", { "alg" => "HS512" }).compact
|
648
|
+
# # => "eyJhbGciOiJIUzUxMiJ9.e30.DN_JCks5rzQiDJJ15E6uJFskAMw-KcasGINKK_4S8xKo7W6tZ-a00ZL8UWOWgE7oHpcFrYnvSpNRldAMp19iyw"
|
649
|
+
# JOSE::JWS.verify_strict(jwk, ["HS256"], signed_hs256).first
|
650
|
+
# # => true
|
651
|
+
# JOSE::JWS.verify_strict(jwk, ["HS256"], signed_hs512).first
|
652
|
+
# # => false
|
653
|
+
# JOSE::JWS.verify_strict(jwk, ["HS256", "HS512"], signed_hs512).first
|
654
|
+
# # => true
|
655
|
+
#
|
656
|
+
# @param [JOSE::JWK] jwk
|
657
|
+
# @param [Array<String>] allow
|
658
|
+
# @param [JOSE::SignedBinary, JOSE::SignedMap, Hash, String] signed
|
659
|
+
# @return [[Boolean, String, (JOSE::JWS, JOSE::Map)]]
|
660
|
+
def self.verify_strict(jwk, allow, signed)
|
196
661
|
if signed.is_a?(String)
|
197
662
|
signed = JOSE::JWS.expand(signed)
|
198
663
|
end
|
@@ -205,7 +670,7 @@ module JOSE
|
|
205
670
|
if allow.member?(protected_map['alg'])
|
206
671
|
jws = from_map(protected_map)
|
207
672
|
signature = JOSE.urlsafe_decode64(signed['signature'])
|
208
|
-
return jws.verify(
|
673
|
+
return jws.verify(jwk, plain_text, signature, signed['protected'])
|
209
674
|
else
|
210
675
|
return false, plain_text, protected_map
|
211
676
|
end
|