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