jose 0.3.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -22,7 +22,7 @@ class JOSE::JWS::ALG_none < Struct.new(:none)
22
22
  end
23
23
 
24
24
  def sign(jwk, message)
25
- if JOSE.__unsecured_signing__
25
+ if JOSE.unsecured_signing
26
26
  return ''
27
27
  else
28
28
  raise NotImplementedError
@@ -30,7 +30,7 @@ class JOSE::JWS::ALG_none < Struct.new(:none)
30
30
  end
31
31
 
32
32
  def verify(jwk, message, signature)
33
- if JOSE.__unsecured_signing__
33
+ if JOSE.unsecured_signing
34
34
  if signature == ''
35
35
  return true
36
36
  else
@@ -1,9 +1,82 @@
1
1
  module JOSE
2
-
2
+ # JWT stands for JSON Web Token which is defined in [RFC 7519](https://tools.ietf.org/html/rfc7519).
3
+ #
4
+ # ## Encryption Examples
5
+ #
6
+ # All of the example keys generated below can be found here: [https://gist.github.com/potatosalad/dd140560b2bdbdab886d](https://gist.github.com/potatosalad/dd140560b2bdbdab886d)
7
+ #
8
+ # See {JOSE::JWE JOSE::JWE} for more Encryption examples.
9
+ #
10
+ # ### A128GCM
11
+ #
12
+ # !!!ruby
13
+ # jwk_oct128 = JOSE::JWK.from_oct(([0]*16).pack('C*'))
14
+ # jwt = { "test" => true }
15
+ #
16
+ # # A128GCM
17
+ # encrypted_a128gcm = JOSE::JWT.encrypt(jwk_oct128, { "alg" => "dir", "enc" => "A128GCM" }, jwt).compact
18
+ # # => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIiwidHlwIjoiSldUIn0..yKs3KxBPBsp60bVv.lYrRQrT8GQQMG2OFCA.Z6GQkHT6K6VWxkOJBLFt3g"
19
+ # JOSE::JWT.decrypt(jwk_oct128, encrypted_a128gcm)
20
+ # # => [#<struct JOSE::JWT fields=JOSE::Map["test" => true]>,
21
+ # # #<struct JOSE::JWE
22
+ # # alg=#<JOSE::JWE::ALG_dir:0x007fd81c1023d0>,
23
+ # # enc=#<struct JOSE::JWE::ENC_AES_GCM cipher_name="aes-128-gcm", bits=128, cek_len=16, iv_len=12>,
24
+ # # zip=nil,
25
+ # # fields=JOSE::Map["typ" => "JWT"]>]
26
+ #
27
+ # ## Signature Examples
28
+ #
29
+ # All of the example keys generated below can be found here: [https://gist.github.com/potatosalad/925a8b74d85835e285b9](https://gist.github.com/potatosalad/925a8b74d85835e285b9)
30
+ #
31
+ # See {JOSE::JWS JOSE::JWS} for more Signature examples. For security purposes, {JOSE::JWT.verify_strict JOSE::JWT.verify_strict} is recommended over {JOSE::JWT.verify JOSE::JWT.verify}.
32
+ #
33
+ # ### HS256
34
+ #
35
+ # !!!ruby
36
+ # # let's generate the key we'll use below and define our jwt
37
+ # jwk_hs256 = JOSE::JWK.generate_key([:oct, 16])
38
+ # jwt = { "test" => true }
39
+ #
40
+ # # HS256
41
+ # signed_hs256 = JOSE::JWT.sign(jwk_hs256, { "alg" => "HS256" }, jwt).compact
42
+ # # => "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0Ijp0cnVlfQ.XYsFJDhfBZCAKnEZjR0WWd1l1ZPDD4bYpZYMHizexfQ"
43
+ #
44
+ # # verify_strict is recommended over verify
45
+ # JOSE::JWT.verify_strict(jwk_hs256, ["HS256"], signed_hs256)
46
+ # # => [true,
47
+ # # #<struct JOSE::JWT fields=JOSE::Map["test" => true]>,
48
+ # # #<struct JOSE::JWS
49
+ # # alg=#<struct JOSE::JWS::ALG_HMAC hmac=OpenSSL::Digest::SHA256>,
50
+ # # b64=nil,
51
+ # # fields=JOSE::Map["typ" => "JWT"]>]
52
+ #
53
+ # # verify returns the same thing without "alg" whitelisting
54
+ # JOSE::JWT.verify(jwk_hs256, signed_hs256)
55
+ # # => [true,
56
+ # # #<struct JOSE::JWT fields=JOSE::Map["test" => true]>,
57
+ # # #<struct JOSE::JWS
58
+ # # alg=#<struct JOSE::JWS::ALG_HMAC hmac=OpenSSL::Digest::SHA256>,
59
+ # # b64=nil,
60
+ # # fields=JOSE::Map["typ" => "JWT"]>]
61
+ #
62
+ # # the default signing algorithm is also "HS256" based on the type of jwk used
63
+ # signed_hs256 == JOSE::JWT.sign(jwk_hs256, jwt).compact
64
+ # # => true
3
65
  class JWT < Struct.new(:fields)
4
66
 
5
67
  # Decode API
6
68
 
69
+ # Converts a binary or map into a {JOSE::JWT JOSE::JWT}.
70
+ #
71
+ # !!!ruby
72
+ # JOSE::JWT.from({ "test" => true })
73
+ # # => #<struct JOSE::JWT fields=JOSE::Map["test" => true]>
74
+ # JOSE::JWT.from("{\"test\":true}")
75
+ # # => #<struct JOSE::JWT fields=JOSE::Map["test" => true]>
76
+ #
77
+ # @param [JOSE::Map, Hash, String, JOSE::JWT, Array<JOSE::Map, Hash, String, JOSE::JWT>] object
78
+ # @param [Hash] modules
79
+ # @return [JOSE::JWT, Array<JOSE::JWT>]
7
80
  def self.from(object, modules = {})
8
81
  case object
9
82
  when JOSE::Map, Hash
@@ -12,70 +85,135 @@ module JOSE
12
85
  return from_binary(object, modules)
13
86
  when JOSE::JWT
14
87
  return object
88
+ when Array
89
+ return object.map { |obj| from(obj, modules) }
15
90
  else
16
- raise ArgumentError, "'object' must be a Hash, String, or JOSE::JWT"
91
+ raise ArgumentError, "'object' must be a Hash, String, JOSE::JWT, or Array"
17
92
  end
18
93
  end
19
94
 
95
+ # Converts a binary into a {JOSE::JWT JOSE::JWT}.
96
+ # @param [String, Array<String>] object
97
+ # @param [Hash] modules
98
+ # @return [JOSE::JWT, Array<JOSE::JWT>]
20
99
  def self.from_binary(object, modules = {})
21
100
  case object
22
101
  when String
23
102
  return from_map(JOSE.decode(object), modules)
103
+ when Array
104
+ return object.map { |obj| from_binary(obj, modules) }
24
105
  else
25
- raise ArgumentError, "'object' must be a String"
106
+ raise ArgumentError, "'object' must be a String or Array"
26
107
  end
27
108
  end
28
109
 
110
+ # Reads file and calls {.from_binary} to convert into a {JOSE::JWT JOSE::JWT}.
111
+ # @param [String] file
112
+ # @param [Hash] modules
113
+ # @return [JOSE::JWT]
29
114
  def self.from_file(file, modules = {})
30
115
  return from_binary(File.binread(file), modules)
31
116
  end
32
117
 
118
+ # Converts a map into a {JOSE::JWT JOSE::JWT}.
119
+ # @param [JOSE::Map, Hash, Array<JOSE::Map, Hash>] object
120
+ # @param [Hash] modules
121
+ # @return [JOSE::JWT, Array<JOSE::JWT>]
33
122
  def self.from_map(object, modules = {})
34
123
  case object
35
124
  when JOSE::Map, Hash
36
125
  return from_fields(JOSE::JWT.new(JOSE::Map.new(object)), modules)
126
+ when Array
127
+ return object.map { |obj| from_map(obj, modules) }
37
128
  else
38
- raise ArgumentError, "'object' must be a Hash"
129
+ raise ArgumentError, "'object' must be a Hash or Array"
39
130
  end
40
131
  end
41
132
 
42
133
  # Encode API
43
134
 
135
+ # Converts a {JOSE::JWT JOSE::JWT} into a binary.
136
+ # @param [JOSE::Map, Hash, String, JOSE::JWT, Array<JOSE::Map, Hash, String, JOSE::JWT>] jwt
137
+ # @return [String, Array<String>]
44
138
  def self.to_binary(jwt)
45
- return from(jwt).to_binary
139
+ if jwt.is_a?(Array)
140
+ return from(jwt).map { |obj| obj.to_binary }
141
+ else
142
+ return from(jwt).to_binary
143
+ end
46
144
  end
47
145
 
146
+ # Converts a {JOSE::JWT JOSE::JWT} into a binary.
147
+ # @return [String]
48
148
  def to_binary
49
149
  return JOSE.encode(to_map)
50
150
  end
51
151
 
152
+ # Calls {.to_binary} on a {JOSE::JWT JOSE::JWT} and then writes the binary to `file`.
153
+ # @param [JOSE::Map, Hash, String, JOSE::JWT] jwt
154
+ # @param [String] file
155
+ # @return [Fixnum] bytes written
52
156
  def self.to_file(jwt, file)
53
157
  return from(jwt).to_file(file)
54
158
  end
55
159
 
160
+ # Calls {#to_binary} on a {JOSE::JWT JOSE::JWT} and then writes the binary to `file`.
161
+ # @param [String] file
162
+ # @return [Fixnum] bytes written
56
163
  def to_file(file)
57
164
  return File.binwrite(file, to_binary)
58
165
  end
59
166
 
167
+ # Converts a {JOSE::JWT JOSE::JWT} into a map.
168
+ # @param [JOSE::Map, Hash, String, JOSE::JWT, Array<JOSE::Map, Hash, String, JOSE::JWT>] jwt
169
+ # @return [JOSE::Map, Array<JOSE::Map>]
60
170
  def self.to_map(jwt)
61
- return from(jwt).to_map
171
+ if jwt.is_a?(Array)
172
+ return from(jwt).map { |obj| obj.to_map }
173
+ else
174
+ return from(jwt).to_map
175
+ end
62
176
  end
63
177
 
178
+ # Converts a {JOSE::JWT JOSE::JWT} into a map.
179
+ # @return [JOSE::Map]
64
180
  def to_map
65
181
  return fields
66
182
  end
67
183
 
68
184
  # API
69
185
 
186
+ # Decrypts an encrypted {JOSE::JWT JOSE::JWT} using the `jwk`.
187
+ # @see JOSE::JWE.block_decrypt
188
+ # @param [JOSE::JWK] jwk
189
+ # @param [JOSE::EncryptedBinary, JOSE::EncryptedMap] encrypted
190
+ # @return [[JOSE::JWT, JOSE::JWE]]
70
191
  def self.decrypt(jwk, encrypted)
71
192
  decrypted, jwe = JOSE::JWK.block_decrypt(jwk, encrypted)
72
193
  return from_binary(decrypted), jwe
73
194
  end
74
195
 
75
- def self.encrypt(jwt, jwk, jwe = nil)
196
+ # Encrypts a {JOSE::JWT JOSE::JWT} using the `jwk` and the default block encryptor algorithm `jwe` for the key type.
197
+ # @see JOSE::JWT#encrypt
198
+ # @param [JOSE::JWK] jwk
199
+ # @param [JOSE::JWE, JOSE::JWT] jwe
200
+ # @param [JOSE::JWT] jwt
201
+ # @return [JOSE::EncryptedMap]
202
+ def self.encrypt(jwk, jwe, jwt = nil)
203
+ if jwt.nil?
204
+ jwt = jwe
205
+ jwe = nil
206
+ end
76
207
  return from(jwt).encrypt(jwk, jwe)
77
208
  end
78
209
 
210
+ # Encrypts a {JOSE::JWT JOSE::JWT} using the `jwk` and the `jwe` algorithm.
211
+ #
212
+ # If `"typ"` is not specified in the `jwe`, `{ "typ" => "JWT" }` will be added.
213
+ # @see JOSE::JWK.block_encrypt
214
+ # @param [JOSE::JWK] jwk
215
+ # @param [JOSE::JWE] jwe
216
+ # @return [JOSE::EncryptedMap]
79
217
  def encrypt(jwk, jwe = nil)
80
218
  plain_text = to_binary
81
219
  if jwe.nil?
@@ -91,10 +229,17 @@ module JOSE
91
229
  return JOSE::JWK.block_encrypt(jwk, plain_text, jwe)
92
230
  end
93
231
 
232
+ # Merges map on right into map on left.
233
+ # @param [JOSE::Map, Hash, String, JOSE::JWT] left
234
+ # @param [JOSE::Map, Hash, String, JOSE::JWT] right
235
+ # @return [JOSE::JWT]
94
236
  def self.merge(left, right)
95
237
  return from(left).merge(right)
96
238
  end
97
239
 
240
+ # Merges object into current map.
241
+ # @param [JOSE::Map, Hash, String, JOSE::JWT] object
242
+ # @return [JOSE::JWT]
98
243
  def merge(object)
99
244
  object = case object
100
245
  when JOSE::Map, Hash
@@ -109,41 +254,90 @@ module JOSE
109
254
  return JOSE::JWT.from_map(self.to_map.merge(object))
110
255
  end
111
256
 
257
+ # Returns the decoded payload portion of a signed binary or map without verifying the signature.
258
+ #
259
+ # !!!ruby
260
+ # JOSE::JWT.peek_payload("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0Ijp0cnVlfQ.XYsFJDhfBZCAKnEZjR0WWd1l1ZPDD4bYpZYMHizexfQ")
261
+ # # => JOSE::Map["test" => true]
262
+ #
263
+ # @see JOSE::JWS.peek_payload
264
+ # @param [JOSE::SignedBinary, String] signed
265
+ # @return [JOSE::Map]
112
266
  def self.peek_payload(signed)
113
267
  return JOSE::Map.new(JOSE.decode(JOSE::JWS.peek_payload(signed)))
114
268
  end
115
269
 
270
+ # Returns the decoded protected portion of a signed binary or map without verifying the signature.
271
+ # @see JOSE::JWS.peek_protected
272
+ # @param [JOSE::SignedBinary, String] signed
273
+ # @return [String]
116
274
  def self.peek_protected(signed)
117
275
  return JOSE::JWS.peek_protected(signed)
118
276
  end
119
277
 
120
- def self.sign(jwt, jwk, jws = nil, header = nil)
121
- return from(jwt).sign(jwk, jws)
278
+ # Returns the decoded signature portion of a signed binary or map without verifying the signature.
279
+ # @see JOSE::JWS.peek_signature
280
+ # @param [JOSE::SignedBinary, String] signed
281
+ # @return [String]
282
+ def self.peek_signature(signed)
283
+ return JOSE::JWS.peek_signature(signed)
122
284
  end
123
285
 
286
+ # Signs a {JOSE::JWT JOSE::JWT} using the `jwk` and the default signer algorithm `jws` for the key type.
287
+ # @see JOSE::JWT#sign
288
+ # @see JOSE::JWS.sign
289
+ # @param [JOSE::JWK] jwk
290
+ # @param [JOSE::JWS, JOSE::JWT] jws
291
+ # @param [JOSE::JWT] jwt
292
+ # @param [JOSE::Map, Hash] header
293
+ # @return [JOSE::SignedMap]
294
+ def self.sign(jwk, jws, jwt = nil, header = nil)
295
+ if jwt.nil?
296
+ jwt = jws
297
+ jws = nil
298
+ end
299
+ return from(jwt).sign(jwk, jws, header)
300
+ end
301
+
302
+ # Signs a {JOSE::JWT JOSE::JWT} using the `jwk` and the `jws` algorithm.
303
+ # @see JOSE::JWT#sign
304
+ # @see JOSE::JWS.sign
305
+ # @param [JOSE::JWK] jwk
306
+ # @param [JOSE::JWS] jws
307
+ # @param [JOSE::Map, Hash] header
308
+ # @return [JOSE::SignedMap]
124
309
  def sign(jwk, jws = nil, header = nil)
125
310
  plain_text = to_binary
126
311
  if jws.nil?
127
312
  jwk = JOSE::JWK.from(jwk)
128
313
  jws = jwk.signer
129
314
  end
130
- if jws.is_a?(Hash)
131
- jws = JOSE::Map.new(jws)
132
- end
133
- if jws.is_a?(JOSE::Map) and not jws.has_key?('typ')
315
+ jws = JOSE::JWS.from(jws).to_map
316
+ if not jws.has_key?('typ')
134
317
  jws = jws.put('typ', 'JWT')
135
318
  end
136
319
  return JOSE::JWK.sign(jwk, plain_text, jws, header)
137
320
  end
138
321
 
322
+ # Verifies the `signed` using the `jwk` and calls {JOSE::JWT.from JOST::JWT.from} on the payload.
323
+ # @see JOSE::JWS.verify
324
+ # @param [JOSE::JWK] jwk
325
+ # @param [JOSE::SignedBinary, JOSE::SignedMap] signed
326
+ # @return [[Boolean, JOSE::JWT, JOSE::JWS]]
139
327
  def self.verify(jwk, signed)
140
328
  verified, payload, jws = JOSE::JWK.verify(signed, jwk)
141
329
  jwt = from_binary(payload)
142
330
  return verified, jwt, jws
143
331
  end
144
332
 
333
+ # Verifies the `signed` using the `jwk`, whitelists the `"alg"` using `allow`, and calls {JOSE::JWT.from JOST::JWT.from} on the payload.
334
+ # @see JOSE::JWS.verify_strict
335
+ # @param [JOSE::JWK] jwk
336
+ # @param [Array<String>] allow
337
+ # @param [JOSE::SignedBinary, JOSE::SignedMap] signed
338
+ # @return [[Boolean, (JOSE::JWT, String), (JOSE::JWS, JOSE::Map)]]
145
339
  def self.verify_strict(jwk, allow, signed)
146
- verified, payload, jws = JOSE::JWK.verify(signed, allow, jwk)
340
+ verified, payload, jws = JOSE::JWK.verify_strict(signed, allow, jwk)
147
341
  jwt = payload
148
342
  if verified
149
343
  jwt = from_binary(payload)
@@ -1,3 +1,3 @@
1
1
  module JOSE
2
- VERSION = "0.3.1"
2
+ VERSION = "1.0.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jose
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Bennett
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-05-05 00:00:00.000000000 Z
11
+ date: 2016-05-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hamster
@@ -106,14 +106,19 @@ files:
106
106
  - ".ruby-gemset"
107
107
  - ".ruby-version"
108
108
  - ".travis.yml"
109
+ - ".yardopts"
109
110
  - CHANGELOG.md
110
111
  - CODE_OF_CONDUCT.md
111
112
  - Gemfile
112
- - LICENSE.txt
113
+ - LICENSE.md
113
114
  - README.md
114
115
  - Rakefile
115
116
  - bin/console
116
117
  - bin/setup
118
+ - docs/EncryptionAlgorithms.md
119
+ - docs/GettingStarted.md
120
+ - docs/KeyGeneration.md
121
+ - docs/SignatureAlgorithms.md
117
122
  - jose.gemspec
118
123
  - lib/jose.rb
119
124
  - lib/jose/jwa.rb
@@ -161,6 +166,7 @@ files:
161
166
  - lib/jose/jwk/kty_okp_x25519.rb
162
167
  - lib/jose/jwk/kty_okp_x448.rb
163
168
  - lib/jose/jwk/kty_rsa.rb
169
+ - lib/jose/jwk/openssh_key.rb
164
170
  - lib/jose/jwk/pem.rb
165
171
  - lib/jose/jwk/set.rb
166
172
  - lib/jose/jws.rb