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
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
|