sandal 0.1.0 → 0.1.1

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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ N2E4NjhhOTY5Y2JkNGU1MmU1YjE2MmIzYTY4NzUwMTg5ZTc3NTk0OA==
5
+ data.tar.gz: !binary |-
6
+ YjA1YTA1MzExMDg2YmMwNDllNWQyYTgxZjNmMmQ0MjJhNWNiNTM0Mw==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ YTBlODVkNGFiNWQwNGQzZWE2YjFkY2JmNTg4MDliMDlkN2YyYjA0NzBmYWYw
10
+ OTk5MjMzMjViYjNiODBjOGU0YmQ5MjlmMzUxODI2MzU4NzU5ZTI3ODQ1ZTk5
11
+ YmI1OThiMDFkZDNjNzRlMmViZTUxYjU5MWU0M2MwMGE4NDcxMDg=
12
+ data.tar.gz: !binary |-
13
+ ZWFhNjNlNzc4NDgwNmI5OTAwMmNhNTFlNDE1NjhjY2ViM2E2N2FlMjQ5NjM3
14
+ MDkxZDk5MmFiMzY2NGM4YTlkMjY0MzI5NDI1YTRlYWIzOWY1ZTA2MzE2NzMw
15
+ NTIwMjA1ZjQxMTQ1ZWU3Y2I2YjA3YmI5NDA3OTgxODZhMGRkMjA=
data/.gitignore CHANGED
@@ -2,19 +2,18 @@
2
2
  *.rbc
3
3
  .bundle
4
4
  .config
5
- .DS_Store
6
5
  coverage
6
+ doc
7
+ .DS_Store
7
8
  Gemfile.lock
8
9
  InstalledFiles
9
10
  lib/bundler/man
10
11
  pkg
12
+ .rbx
11
13
  rdoc
12
14
  spec/reports
13
15
  test/tmp
14
16
  test/version_tmp
15
17
  tmp
16
-
17
- # YARD artifacts
18
18
  .yardoc
19
19
  _yardoc
20
- doc/
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 0.1.1 (01 April 2013)
2
+
3
+ Features:
4
+
5
+ - Changed from json to multi_json gem for improved compatibility.
6
+ - New Claims module can add claims validation functionality to Hash-like objects.
7
+ - New ClaimError type for claim validation errors.
8
+
9
+ Bug fixes:
10
+
11
+ - Base64 decoding now ensures there is no padding before decoding.
12
+
1
13
  ## 0.1.0 (30 March 2013)
2
14
 
3
15
  The first version worth using.
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  **NOTE: This library is pretty new and still has a lot of things that aren't finished or could be improved. Expect bugs and interface changes. Pull requests or feedback very much appreciated.**
2
2
 
3
- # Sandal [![Build Status](https://travis-ci.org/gregbeech/sandal.png?branch=master)](https://travis-ci.org/gregbeech/sandal) [![Coverage Status](https://coveralls.io/repos/gregbeech/sandal/badge.png?branch=master)](https://coveralls.io/r/gregbeech/sandal)
3
+ # Sandal [![Build Status](https://travis-ci.org/gregbeech/sandal.png?branch=master)](https://travis-ci.org/gregbeech/sandal) [![Coverage Status](https://coveralls.io/repos/gregbeech/sandal/badge.png?branch=master)](https://coveralls.io/r/gregbeech/sandal) [![Code Climate](https://codeclimate.com/github/gregbeech/sandal.png)](https://codeclimate.com/github/gregbeech/sandal)
4
4
 
5
- A Ruby library for creating and reading [JSON Web Tokens (JWT) draft-06](http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06), supporting [JSON Web Signatures (JWS) draft-08](http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-08) and [JSON Web Encryption (JWE) draft-08](http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-08). See the [CHANGELOG](CHANGELOG.md) for version history.
5
+ A Ruby library for creating and reading [JSON Web Tokens (JWT) drfat-06](http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06), supporting [JSON Web Signatures (JWS) draft-08](http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-08) and [JSON Web Encryption (JWE) draft-08](http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-08). See the [CHANGELOG](CHANGELOG.md) for version history.
6
6
 
7
7
  ## Installation
8
8
 
@@ -0,0 +1,82 @@
1
+ module Sandal
2
+ # A module that can be mixed into Hash-like objects to provide claims-related functionality.
3
+ module Claims
4
+
5
+ # Validates the set of claims.
6
+ #
7
+ # @param options [Hash] The validation options (see {Sandal::DEFAULT_OPTIONS} for details).
8
+ # @return [Hash] A reference to self.
9
+ # @raise [Sandal::ClaimError] One or more claims is invalid.
10
+ def validate_claims(options)
11
+ validate_exp(options[:max_clock_skew]) if options[:validate_exp]
12
+ validate_nbf(options[:max_clock_skew]) if options[:validate_nbf]
13
+ validate_iss(options[:valid_iss])
14
+ validate_aud(options[:valid_aud])
15
+ self
16
+ end
17
+
18
+ # Validates the expires claim.
19
+ #
20
+ # @param max_clock_skew [Numeric] The maximum clock skew, in seconds.
21
+ # @return [void].
22
+ # @raise [Sandal::ClaimError] The 'exp' claim is invalid, or the token has expired.
23
+ def validate_exp(max_clock_skew)
24
+ exp = time_claim('exp')
25
+ if exp && exp <= (Time.now - max_clock_skew)
26
+ raise Sandal::ClaimError, 'The token has expired.'
27
+ end
28
+ nil
29
+ end
30
+
31
+ # Validates the not-before claim.
32
+ #
33
+ # @param max_clock_skew [Numeric] The maximum clock skew, in seconds.
34
+ # @return [void].
35
+ # @raise [Sandal::ClaimError] The 'nbf' claim is invalid, or the token is not valid yet.
36
+ def validate_nbf(max_clock_skew)
37
+ nbf = time_claim('nbf')
38
+ if nbf && nbf > (Time.now + max_clock_skew)
39
+ raise Sandal::ClaimError, 'The token is not valid yet.'
40
+ end
41
+ end
42
+
43
+ # Validates the issuer claim.
44
+ #
45
+ # @param valid_iss [Array] The valid issuers.
46
+ # @return [void].
47
+ # @raise [Sandal::ClaimError] The 'iss' claim value is not a valid issuer.
48
+ def validate_iss(valid_iss)
49
+ if valid_iss && valid_iss.length > 0
50
+ raise Sandal::ClaimError, 'The issuer is invalid.' unless valid_iss.include?(self['iss'])
51
+ end
52
+ end
53
+
54
+ # Validates the audience claim.
55
+ #
56
+ # @param valid_aud [Array] The valid audiences.
57
+ # @return [void].
58
+ # @raise [Sandal::ClaimError] The 'aud' claim value does not contain a valid audience.
59
+ def validate_aud(valid_aud)
60
+ if valid_aud && valid_aud.length > 0
61
+ aud = self['aud']
62
+ aud = [aud] unless aud.is_a?(Array)
63
+ raise Sandal::ClaimError, 'The audence is invalid.' unless (aud & valid_aud).length > 0
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ # Gets the value of a claim as a Time.
70
+ def time_claim(name)
71
+ claim = self[name]
72
+ if claim
73
+ begin
74
+ Time.at(claim)
75
+ rescue
76
+ raise ClaimError, "The '#{name}' claim is invalid."
77
+ end
78
+ end
79
+ end
80
+
81
+ end
82
+ end
@@ -35,7 +35,7 @@ module Sandal
35
35
  ciphertext = cipher.update(payload) + cipher.final
36
36
  encoded_ciphertext = Sandal::Util.base64_encode(ciphertext)
37
37
 
38
- encoded_header = Sandal::Util.base64_encode(JSON.generate(header))
38
+ encoded_header = Sandal::Util.base64_encode(MultiJson.dump(header))
39
39
  secured_input = [encoded_header, encoded_encrypted_key, encoded_iv, encoded_ciphertext].join('.')
40
40
  content_integrity_key = derive_content_key('Integrity', content_master_key, @sha_size)
41
41
  integrity_value = OpenSSL::HMAC.digest(@digest, content_integrity_key, secured_input)
data/lib/sandal/sig/es.rb CHANGED
@@ -5,11 +5,16 @@ module Sandal
5
5
 
6
6
  # Base implementation of the ECDSA-SHA family of signature algorithms.
7
7
  class ES
8
- include Sandal::Sig
9
8
 
10
- # Creates a new instance with the size of the SHA algorithm and an OpenSSL ES PKey.
9
+ # @return [String] The JWA name of the algorithm.
10
+ attr_reader :name
11
+
12
+ # Creates a new instance; it's probably easier to use one of the subclass constructors.
13
+ #
14
+ # @param sha_size [Integer] The size of the SHA algorithm.
15
+ # @param prime_size [Integer] The size of the ECDSA primes.
16
+ # @param key [OpenSSL::PKey::EC] The key to use for signing (private) or validation (public).
11
17
  def initialize(sha_size, prime_size, key)
12
- raise ArgumentError, 'A key is required.' unless key
13
18
  @name = "ES#{sha_size}"
14
19
  @digest = OpenSSL::Digest.new("sha#{sha_size}")
15
20
  @prime_size = prime_size
@@ -17,6 +22,9 @@ module Sandal
17
22
  end
18
23
 
19
24
  # Signs a payload and returns the signature.
25
+ #
26
+ # @param payload [String] The payload of the token to sign.
27
+ # @return [String] The signature.
20
28
  def sign(payload)
21
29
  hash = @digest.digest(payload)
22
30
  asn1_sig = @key.dsa_sign_asn1(hash)
@@ -24,64 +32,107 @@ module Sandal
24
32
  self.class.encode_jws_signature(r, s, @prime_size)
25
33
  end
26
34
 
27
- # Verifies a payload signature and returns whether the signature matches.
28
- def verify(signature, payload)
35
+ # Validates a payload signature and returns whether the signature matches.
36
+ #
37
+ # @param signature [String] The signature to verify.
38
+ # @param payload [String] The payload of the token.
39
+ # @return [Boolean] true if the signature is correct; otherwise false.
40
+ def valid?(signature, payload)
29
41
  hash = @digest.digest(payload)
30
42
  r, s = self.class.decode_jws_signature(signature)
31
43
  asn1_sig = self.class.encode_asn1_signature(r, s)
32
- result = @key.dsa_verify_asn1(hash, asn1_sig)
44
+ @key.dsa_verify_asn1(hash, asn1_sig)
33
45
  end
34
46
 
35
- # Decodes an ASN1 signature into a pair of BNs.
47
+ # Decodes an ASN.1 signature into a pair of BNs.
48
+ #
49
+ # @param signature [String] The ASN.1 signature.
50
+ # @return [OpenSSL::BN, OpenSSL::BN] A pair of BNs.
36
51
  def self.decode_asn1_signature(signature)
37
52
  asn_seq = OpenSSL::ASN1.decode(signature)
38
53
  return asn_seq.value[0].value, asn_seq.value[1].value
39
54
  end
40
55
 
41
- # Encodes a pair of BNs into an ASN1 signature.
56
+ # Encodes a pair of BNs into an ASN.1 signature.
57
+ #
58
+ # @param r [OpenSSL::BN] The 'r' value.
59
+ # @param s [OpenSSL::BN] The 's' value.
60
+ # @return [String] The ASN.1 signature.
42
61
  def self.encode_asn1_signature(r, s)
43
62
  items = [OpenSSL::ASN1::Integer.new(r), OpenSSL::ASN1::Integer.new(s)]
44
63
  OpenSSL::ASN1::Sequence.new(items).to_der
45
64
  end
46
65
 
47
66
  # Decodes a JWS signature into a pair of BNs.
67
+ #
68
+ # @param signature [String] The ASN.1 signature.
69
+ # @return [OpenSSL::BN, OpenSSL::BN] A pair of BNs.
48
70
  def self.decode_jws_signature(signature)
49
71
  n_length = signature.length / 2
50
- s_to_n = lambda { |s| OpenSSL::BN.new(s.unpack('H*')[0], 16) }
72
+ s_to_n = -> s { OpenSSL::BN.new(s.unpack('H*')[0], 16) }
51
73
  r = s_to_n.call(signature[0..(n_length - 1)])
52
74
  s = s_to_n.call(signature[n_length..-1])
53
75
  return r, s
54
76
  end
55
77
 
56
78
  # Encodes a pair of BNs into a JWS signature.
79
+ #
80
+ # @param r [OpenSSL::BN] The 'r' value.
81
+ # @param s [OpenSSL::BN] The 's' value.
82
+ # @param prime_size [Integer] The size of the ECDSA primes.
83
+ # @return [String] The ASN.1 signature.
57
84
  def self.encode_jws_signature(r, s, prime_size)
58
85
  byte_count = (prime_size / 8.0).ceil
59
- n_to_s = lambda { |n| [n.to_s(16)].pack('H*').rjust(byte_count, "\0") }
86
+ n_to_s = -> n { [n.to_s(16)].pack('H*').rjust(byte_count, "\0") }
60
87
  n_to_s.call(r) + n_to_s.call(s)
61
88
  end
62
89
 
90
+ protected
91
+
92
+ # Ensures that a key has a specified curve name.
93
+ #
94
+ # @param key [OpenSSL::PKey::EC] The key.
95
+ # @param curve_name [String] The curve name.
96
+ # @return [void].
97
+ # @raise [ArgumentError] The key has a different curve name.
98
+ def ensure_curve(key, curve_name)
99
+ raise ArgumentError, "The key must be in the #{curve_name} group." unless key.group.curve_name == curve_name
100
+ end
101
+
63
102
  end
64
103
 
65
104
  # The ECDSA-SHA256 signing algorithm.
66
105
  class ES256 < Sandal::Sig::ES
67
- # Creates a new instance with an OpenSSL ES PKey.
106
+ # Creates a new instance.
107
+ #
108
+ # @param key [OpenSSL::PKey::EC] The key to use for signing (private) or validation (public).
109
+ # @raise [ArgumentError] The key is not in the "prime256v1" group.
68
110
  def initialize(key)
111
+ ensure_curve(key, 'prime256v1')
69
112
  super(256, 256, key)
70
113
  end
71
114
  end
72
115
 
73
116
  # The ECDSA-SHA384 signing algorithm.
74
117
  class ES384 < Sandal::Sig::ES
75
- # Creates a new instance with an OpenSSL ES PKey.
118
+ # Creates a new instance.
119
+ #
120
+ # @param key [OpenSSL::PKey::EC] The key to use for signing (private) or validation (public).
121
+ # @raise [ArgumentError] The key is not in the "secp384r1" group.
76
122
  def initialize(key)
123
+ ensure_curve(key, 'secp384r1')
77
124
  super(384, 384, key)
78
125
  end
79
126
  end
80
127
 
81
128
  # The ECDSA-SHA512 signing algorithm.
82
129
  class ES512 < Sandal::Sig::ES
83
- # Creates a new instance with an OpenSSL ES PKey.
130
+ # Creates a new instance.
131
+ #
132
+ # @param key [OpenSSL::PKey::EC] The key to use for signing (private) or validation (public).
133
+ # @raise [ArgumentError] The key is not in the "secp521r1" group.
84
134
  def initialize(key)
135
+ ensure_curve(key, 'secp521r1')
85
136
  super(512, 521, key)
86
137
  end
87
138
  end
data/lib/sandal/sig/hs.rb CHANGED
@@ -5,23 +5,34 @@ module Sandal
5
5
 
6
6
  # Base implementation of the HMAC-SHA family of signature algorithms.
7
7
  class HS
8
- include Sandal::Sig
9
8
 
10
- # Creates a new instance with the size of the SHA algorithm and a string key.
9
+ # @return [String] The JWA name of the algorithm.
10
+ attr_reader :name
11
+
12
+ # Creates a new instance; it's probably easier to use one of the subclass constructors.
13
+ #
14
+ # @param sha_size [Integer] The size of the SHA algorithm.
15
+ # @param key [String] The key to use for signing or validation.
11
16
  def initialize(sha_size, key)
12
- raise ArgumentError, 'A key is required.' unless key
13
17
  @name = "HS#{sha_size}"
14
18
  @digest = OpenSSL::Digest.new("sha#{sha_size}")
15
19
  @key = key
16
20
  end
17
21
 
18
22
  # Signs a payload and returns the signature.
23
+ #
24
+ # @param payload [String] The payload of the token to sign.
25
+ # @return [String] The signature.
19
26
  def sign(payload)
20
27
  OpenSSL::HMAC.digest(@digest, @key, payload)
21
28
  end
22
29
 
23
- # Verifies a payload signature and returns whether the signature matches.
24
- def verify(signature, payload)
30
+ # Validates a payload signature and returns whether the signature matches.
31
+ #
32
+ # @param signature [String] The signature to verify.
33
+ # @param payload [String] The payload of the token.
34
+ # @return [Boolean] true if the signature is correct; otherwise false.
35
+ def valid?(signature, payload)
25
36
  Sandal::Util.secure_equals(sign(payload), signature)
26
37
  end
27
38
 
@@ -29,7 +40,9 @@ module Sandal
29
40
 
30
41
  # The HMAC-SHA256 signing algorithm.
31
42
  class HS256 < Sandal::Sig::HS
32
- # Creates a new instance with a string key.
43
+ # Creates a new instance.
44
+ #
45
+ # @param key [String] The key to use for signing or validation.
33
46
  def initialize(key)
34
47
  super(256, key)
35
48
  end
@@ -37,7 +50,9 @@ module Sandal
37
50
 
38
51
  # The HMAC-SHA384 signing algorithm.
39
52
  class HS384 < Sandal::Sig::HS
40
- # Creates a new instance with a string key.
53
+ # Creates a new instance.
54
+ #
55
+ # @param key [String] The key to use for signing or validation.
41
56
  def initialize(key)
42
57
  super(384, key)
43
58
  end
@@ -45,7 +60,9 @@ module Sandal
45
60
 
46
61
  # The HMAC-SHA512 signing algorithm.
47
62
  class HS512 < Sandal::Sig::HS
48
- # Creates a new instance with a string key.
63
+ # Creates a new instance.
64
+ #
65
+ # @param key [String] The key to use for signing or validation.
49
66
  def initialize(key)
50
67
  super(512, key)
51
68
  end
data/lib/sandal/sig/rs.rb CHANGED
@@ -5,27 +5,35 @@ module Sandal
5
5
 
6
6
  # Base implementation of the RSA-SHA family of signature algorithms.
7
7
  class RS
8
- include Sandal::Sig
9
8
 
10
- # Creates a new instance with the size of the SHA algorithm and an OpenSSL RSA PKey. To sign
11
- # a value this must contain a private key; to verify a signature a public key is sufficient.
12
- # Note that the size of the RSA key must be at least 2048 bits to be compliant with the
13
- # JWA specification.
9
+ # @return [String] The JWA name of the algorithm.
10
+ attr_reader :name
11
+
12
+ # Creates a new instance; it's probably easier to use one of the subclass constructors.
13
+ #
14
+ # @param sha_size [Integer] The size of the SHA algorithm.
15
+ # @param key [OpenSSL::PKey::RSA] The key to use for signing (private) or validation (public). This must
16
+ # be at least 2048 bits to be compliant with the JWA specification.
14
17
  def initialize(sha_size, key)
15
- raise ArgumentError, 'A key is required.' unless key
16
18
  @name = "RS#{sha_size}"
17
19
  @digest = OpenSSL::Digest.new("sha#{sha_size}")
18
20
  @key = key
19
21
  end
20
22
 
21
23
  # Signs a payload and returns the signature.
24
+ #
25
+ # @param payload [String] The payload of the token to sign.
26
+ # @return [String] The signature.
22
27
  def sign(payload)
23
- raise ArgumentError, 'A private key is required to sign the payload.' unless @key.private?
24
28
  @key.sign(@digest, payload)
25
29
  end
26
30
 
27
- # Verifies a payload signature and returns whether the signature matches.
28
- def verify(signature, payload)
31
+ # Validates a payload signature and returns whether the signature matches.
32
+ #
33
+ # @param signature [String] The signature to verify.
34
+ # @param payload [String] The payload of the token.
35
+ # @return [Boolean] true if the signature is correct; otherwise false.
36
+ def valid?(signature, payload)
29
37
  @key.verify(@digest, signature, payload)
30
38
  end
31
39
 
@@ -33,7 +41,10 @@ module Sandal
33
41
 
34
42
  # The RSA-SHA256 signing algorithm.
35
43
  class RS256 < Sandal::Sig::RS
36
- # Creates a new instance with an OpenSSL RSA PKey.
44
+ # Creates a new instance.
45
+ #
46
+ # @param key [OpenSSL::PKey::RSA] The key to use for signing (private) or validation (public). This must
47
+ # be at least 2048 bits to be compliant with the JWA specification.
37
48
  def initialize(key)
38
49
  super(256, key)
39
50
  end
@@ -41,7 +52,10 @@ module Sandal
41
52
 
42
53
  # The RSA-SHA384 signing algorithm.
43
54
  class RS384 < Sandal::Sig::RS
44
- # Creates a new instance with an OpenSSL RSA PKey.
55
+ # Creates a new instance.
56
+ #
57
+ # @param key [OpenSSL::PKey::RSA] The key to use for signing (private) or validation (public). This must
58
+ # be at least 2048 bits to be compliant with the JWA specification.
45
59
  def initialize(key)
46
60
  super(384, key)
47
61
  end
@@ -49,7 +63,10 @@ module Sandal
49
63
 
50
64
  # The RSA-SHA512 signing algorithm.
51
65
  class RS512 < Sandal::Sig::RS
52
- # Creates a new instance with an OpenSSL RSA PKey.
66
+ # Creates a new instance.
67
+ #
68
+ # @param key [OpenSSL::PKey::RSA] The key to use for signing (private) or validation (public). This must
69
+ # be at least 2048 bits to be compliant with the JWA specification.
53
70
  def initialize(key)
54
71
  super(512, key)
55
72
  end
data/lib/sandal/sig.rb CHANGED
@@ -1,34 +1,16 @@
1
1
  require 'singleton'
2
2
 
3
3
  module Sandal
4
- # Common signature traits.
4
+ # Contains signature (JWS) functionality.
5
5
  module Sig
6
6
 
7
- # @return [String] The JWA name of the algorithm.
8
- attr_reader :name
9
-
10
- # Signs a payload and returns the signature.
11
- #
12
- # @param payload [String] The payload of the token to sign.
13
- # @return [String] The signature.
14
- def sign(payload)
15
- raise NotImplementedError, "#{@name}.sign is not implemented."
16
- end
17
-
18
- # Verifies a payload signature and returns whether the signature matches.
19
- #
20
- # @param signature [String] The signature to verify.
21
- # @param payload [String] The payload of the token.
22
- # @return [Boolean] true if the signature is correct; otherwise false.
23
- def verify(signature, payload)
24
- raise NotImplementedError, "#{@name}.verify is not implemented."
25
- end
26
-
27
7
  # The 'none' JWA signature method.
28
8
  class None
29
- include Sandal::Sig
30
9
  include Singleton
31
10
 
11
+ # @return [String] The JWA name of the algorithm.
12
+ attr_reader :name
13
+
32
14
  # Creates a new instance.
33
15
  def initialize
34
16
  @name = 'none'
@@ -42,13 +24,13 @@ module Sandal
42
24
  ''
43
25
  end
44
26
 
45
- # Verifies that a signature is nil or empty.
27
+ # Validates that a signature is nil or empty.
46
28
  #
47
29
  # @param signature [String] The signature to verify.
48
30
  # @param payload [String] This parameter is ignored.
49
- # @return [Boolean] `true` if the signature is nil or empty; otherwise `false`.
50
- def verify(signature, payload)
51
- signature.nil? || signature.length == 0
31
+ # @return [Boolean] true if the signature is nil or empty; otherwise false.
32
+ def valid?(signature, payload)
33
+ signature.nil? || signature.empty?
52
34
  end
53
35
 
54
36
  end
data/lib/sandal/util.rb CHANGED
@@ -21,12 +21,20 @@ module Sandal
21
21
  end
22
22
 
23
23
  # Base64 encodes a string, in compliance with the JWT specification.
24
+ #
25
+ # @param s [String] The string to encode.
26
+ # @return [String] The encoded base64 string.
24
27
  def self.base64_encode(s)
25
28
  Base64.urlsafe_encode64(s).gsub(%r{=+$}, '')
26
29
  end
27
30
 
28
31
  # Base64 decodes a string, in compliance with the JWT specification.
32
+ #
33
+ # @param s [String] The base64 string to decode.
34
+ # @return [String] The decoded string.
35
+ # @raise [Sandal::TokenError] The base64 string contains padding.
29
36
  def self.base64_decode(s)
37
+ raise Sandal::TokenError, 'Base64 strings cannot contain padding.' if s.end_with?('=')
30
38
  padding_length = (4 - (s.length % 4)) % 4
31
39
  padding = '=' * padding_length
32
40
  Base64.urlsafe_decode64(s + padding)
@@ -1,4 +1,4 @@
1
1
  module Sandal
2
2
  # The semantic version of the library.
3
- VERSION = '0.1.0'
3
+ VERSION = '0.1.1'
4
4
  end
data/lib/sandal.rb CHANGED
@@ -1,12 +1,11 @@
1
1
  $:.unshift('.')
2
2
 
3
3
  require 'base64'
4
- require 'json'
4
+ require 'multi_json'
5
5
  require 'openssl'
6
-
6
+ require 'sandal/claims'
7
+ require 'sandal/util'
7
8
  require 'sandal/version'
8
- require 'sandal/sig'
9
- require 'sandal/enc'
10
9
 
11
10
  # A library for creating and reading JSON Web Tokens (JWT).
12
11
  module Sandal
@@ -14,6 +13,9 @@ module Sandal
14
13
  # The error that is raised when a token is invalid.
15
14
  class TokenError < StandardError; end
16
15
 
16
+ # The error that is raised when a claim within a token is invalid.
17
+ class ClaimError < TokenError; end
18
+
17
19
  # The default options for token handling.
18
20
  #
19
21
  # max_clock_skew:: The maximum clock skew, in seconds, when validating times.
@@ -43,23 +45,20 @@ module Sandal
43
45
 
44
46
  # Creates a signed JSON Web Token.
45
47
  #
46
- # @param payload [String/Hash] The payload of the token. If a Hash then it will be encoded as JSON.
47
- # @param signer [Sandal::Sig] The token signer, which may be nil for an unsigned token.
48
+ # @param payload [String/Hash] The payload of the token. Hashes will be encoded as JSON.
49
+ # @param signer [#name,#sign] The token signer, which may be nil for an unsigned token.
48
50
  # @param header_fields [Hash] Header fields for the token (note: do not include 'alg').
49
51
  # @return [String] A signed JSON Web Token.
50
52
  def self.encode_token(payload, signer, header_fields = nil)
51
- if header_fields && header_fields['enc']
52
- raise ArgumentError, 'The header cannot contain an "enc" parameter.'
53
- end
54
53
  signer ||= Sandal::Sig::None.instance
55
54
 
56
55
  header = {}
57
56
  header['alg'] = signer.name if signer.name != Sandal::Sig::None.instance.name
58
57
  header = header_fields.merge(header) if header_fields
59
58
 
60
- payload = JSON.generate(payload) if payload.kind_of?(Hash)
59
+ payload = MultiJson.dump(payload) unless payload.is_a?(String)
61
60
 
62
- encoded_header = Sandal::Util.base64_encode(JSON.generate(header))
61
+ encoded_header = Sandal::Util.base64_encode(MultiJson.dump(header))
63
62
  encoded_payload = Sandal::Util.base64_encode(payload)
64
63
  secured_input = [encoded_header, encoded_payload].join('.')
65
64
 
@@ -86,47 +85,29 @@ module Sandal
86
85
  # Decodes and validates a JSON Web Token.
87
86
  #
88
87
  # The block is called with the token header as the first parameter, and should return the appropriate
89
- # {Sandal::Sig} to verify the signature. It can optionally have a second options parameter which can
88
+ # {Sandal::Sig} to validate the signature. It can optionally have a second options parameter which can
90
89
  # be used to override the {DEFAULT_OPTIONS} on a per-token basis.
91
90
  #
92
91
  # @param token [String] The encoded JSON Web Token.
93
92
  # @yieldparam header [Hash] The JWT header values.
94
93
  # @yieldparam options [Hash] (Optional) A hash that can be used to override the default options.
95
- # @yieldreturn [Sandal::Sig] The signature verifier.
94
+ # @yieldreturn [#valid?] The signature validator.
96
95
  # @return [Hash/String] The payload of the token as a Hash if it was JSON, otherwise as a String.
97
- # @raise [ArgumentError] The token parameter is nil or empty, or the block has the wrong arity.
98
- # @raise [TokenError] The token format is invalid, or validation of the token failed.
99
- def self.decode_token(token, &block)
100
- raise ArgumentError, 'A token is required.' unless token && token.length > 0
96
+ # @raise [Sandal::TokenError] The token format is invalid, or validation of the token failed.
97
+ def self.decode_token(token)
101
98
  parts = token.split('.')
102
- raise TokenError, 'Invalid token format.' unless [2, 3].include?(parts.length)
103
- begin
104
- header = JSON.parse(Sandal::Util.base64_decode(parts[0]))
105
- payload = Sandal::Util.base64_decode(parts[1])
106
- signature = if parts.length > 2 then Sandal::Util.base64_decode(parts[2]) else '' end
107
- rescue
108
- raise TokenError, 'Invalid token encoding.'
109
- end
99
+ header, payload, signature = decode_jws_parts(parts)
110
100
 
111
101
  options = DEFAULT_OPTIONS.clone
112
- if block
113
- case block.arity
114
- when 1 then verifier = block.call(header)
115
- when 2 then verifier = block.call(header, options)
116
- else raise ArgumentError, 'Incorrect number of block parameters.'
117
- end
118
- end
119
- verifier ||= Sandal::Sig::None.instance
102
+ validator = yield header, options if block_given?
103
+ validator ||= Sandal::Sig::None.instance
120
104
 
121
105
  if options[:validate_signature]
122
106
  secured_input = parts.take(2).join('.')
123
- raise TokenError, 'Invalid signature.' unless verifier.verify(signature, secured_input)
107
+ raise TokenError, 'Invalid signature.' unless validator.valid?(signature, secured_input)
124
108
  end
125
109
 
126
- claims = JSON.parse(payload) rescue nil
127
- validate_claims(claims, options) if claims
128
-
129
- claims || payload
110
+ parse_and_validate(payload, header['cty'], options)
130
111
  end
131
112
 
132
113
  # Decrypts an encrypted JSON Web Token.
@@ -136,7 +117,7 @@ module Sandal
136
117
  parts = encrypted_token.split('.')
137
118
  raise ArgumentError, 'Invalid token format.' unless parts.length == 5
138
119
  begin
139
- header = JSON.parse(Sandal::Util.base64_decode(parts[0]))
120
+ header = MultiJson.load(Sandal::Util.base64_decode(parts[0]))
140
121
  encrypted_key = Sandal::Util.base64_decode(parts[1])
141
122
  iv = Sandal::Util.base64_decode(parts[2])
142
123
  ciphertext = Sandal::Util.base64_decode(parts[3])
@@ -152,54 +133,30 @@ module Sandal
152
133
 
153
134
  private
154
135
 
155
- # Validates token claims according to the options
156
- def self.validate_claims(claims, options)
157
- validate_expires(claims, options)
158
- validate_not_before(claims, options)
159
- validate_issuer(claims, options)
160
- validate_audience(claims, options)
161
- end
162
-
163
- # Validates the 'exp' claim.
164
- def self.validate_expires(claims, options)
165
- if options[:validate_exp] && claims['exp']
166
- begin
167
- exp = Time.at(claims['exp'])
168
- rescue
169
- raise TokenError, 'The "exp" claim is invalid.'
170
- end
171
- raise TokenError, 'The token has expired.' unless exp > (Time.now - options[:max_clock_skew])
172
- end
173
- end
174
-
175
- # Validates the 'nbf' claim
176
- def self.validate_not_before(claims, options)
177
- if options[:validate_nbf] && claims['nbf']
178
- begin
179
- nbf = Time.at(claims['nbf'])
180
- rescue
181
- raise TokenError, 'The "nbf" claim is invalid.'
182
- end
183
- raise TokenError, 'The token is not valid yet.' unless nbf < (Time.now + options[:max_clock_skew])
136
+ # Decodes the parts of a JWS token.
137
+ def self.decode_jws_parts(parts)
138
+ raise TokenError, 'Invalid token format.' unless [2, 3].include?(parts.length)
139
+ begin
140
+ header = MultiJson.load(Sandal::Util.base64_decode(parts[0]))
141
+ payload = Sandal::Util.base64_decode(parts[1])
142
+ signature = if parts.length > 2 then Sandal::Util.base64_decode(parts[2]) else '' end
143
+ rescue
144
+ raise TokenError, 'Invalid token encoding.'
184
145
  end
146
+ return header, payload, signature
185
147
  end
186
148
 
187
- # Validates the 'iss' claim.
188
- def self.validate_issuer(claims, options)
189
- valid_iss = options[:valid_iss]
190
- if valid_iss && valid_iss.length > 0
191
- raise TokenError, 'The issuer is invalid.' unless valid_iss.include?(claims['iss'])
149
+ # Parses the content of a token and validates the claims if is a JSON claim set.
150
+ def self.parse_and_validate(payload, content_type, options)
151
+ claims = MultiJson.load(payload) rescue nil unless content_type == 'JWT'
152
+ if claims
153
+ claims.extend(Sandal::Claims).validate_claims(options)
154
+ else
155
+ payload
192
156
  end
193
157
  end
194
158
 
195
- # Validates the 'aud' claim.
196
- def self.validate_audience(claims, options)
197
- valid_aud = options[:valid_aud]
198
- if valid_aud && valid_aud.length > 0
199
- aud = claims['aud']
200
- aud = [aud] unless aud.kind_of?(Array)
201
- raise TokenError, 'The audence is invalid.' unless (aud & valid_aud).length > 0
202
- end
203
- end
159
+ end
204
160
 
205
- end
161
+ require 'sandal/enc'
162
+ require 'sandal/sig'
data/sandal.gemspec CHANGED
@@ -17,16 +17,16 @@ Gem::Specification.new do |s|
17
17
  s.require_paths = ['lib']
18
18
  s.extra_rdoc_files = ['README.md', 'LICENSE.md']
19
19
 
20
- s.add_runtime_dependency 'json', '~> 1.7'
20
+ s.add_runtime_dependency 'multi_json', '~> 1.7'
21
21
  s.add_runtime_dependency 'jruby-openssl', '~> 0.7', '>= 0.7.3' if RUBY_PLATFORM == 'java'
22
22
 
23
- s.add_development_dependency 'bundler', '~> 1.3'
24
- s.add_development_dependency 'rake', '~> 10.0'
25
- s.add_development_dependency 'rspec', '~> 2.13'
26
- s.add_development_dependency 'coveralls', '~> 0.6'
27
- s.add_development_dependency 'yard', '~> 0.8'
28
- s.add_development_dependency 'redcarpet', '~> 2.2' unless RUBY_PLATFORM == 'java' # for yard
29
- s.add_development_dependency 'kramdown', '~> 1.0' if RUBY_PLATFORM == 'java' # for yard
23
+ s.add_development_dependency 'bundler', '>= 1.3'
24
+ s.add_development_dependency 'rake', '>= 10.0'
25
+ s.add_development_dependency 'rspec', '>= 2.13'
26
+ s.add_development_dependency 'coveralls', '>= 0.6'
27
+ s.add_development_dependency 'yard', '>= 0.8'
28
+ s.add_development_dependency 'redcarpet', '>= 2.2' unless RUBY_PLATFORM == 'java' # for yard
29
+ s.add_development_dependency 'kramdown', '>= 1.0' if RUBY_PLATFORM == 'java' # for yard
30
30
 
31
31
  s.requirements << 'openssl 1.0.1c for EC signature methods'
32
32
  end
@@ -55,8 +55,8 @@ describe Sandal::Sig::ES256 do
55
55
  signature = signer.sign(data)
56
56
  public_key = OpenSSL::PKey::EC.new(group)
57
57
  public_key.public_key = private_key.public_key
58
- verifier = Sandal::Sig::ES256.new(public_key)
59
- verifier.verify(signature, data).should == true
58
+ validator = Sandal::Sig::ES256.new(public_key)
59
+ validator.valid?(signature, data).should == true
60
60
  end
61
61
 
62
62
  it 'can verify the signature in JWS section A3.1' do
@@ -69,8 +69,8 @@ describe Sandal::Sig::ES256 do
69
69
  group = OpenSSL::PKey::EC::Group.new('prime256v1')
70
70
  public_key = OpenSSL::PKey::EC.new(group)
71
71
  public_key.public_key = make_point(group, x, y)
72
- verifier = Sandal::Sig::ES256.new(public_key)
73
- verifier.verify(signature, data).should == true
72
+ validator = Sandal::Sig::ES256.new(public_key)
73
+ validator.valid?(signature, data).should == true
74
74
  end
75
75
 
76
76
  it 'fails to verify the signature in JWS section A3.1 when the data is changed' do
@@ -83,8 +83,14 @@ describe Sandal::Sig::ES256 do
83
83
  group = OpenSSL::PKey::EC::Group.new('prime256v1')
84
84
  public_key = OpenSSL::PKey::EC.new(group)
85
85
  public_key.public_key = make_point(group, x, y)
86
- verifier = Sandal::Sig::ES256.new(public_key)
87
- verifier.verify(signature, data).should == false
86
+ validator = Sandal::Sig::ES256.new(public_key)
87
+ validator.valid?(signature, data).should == false
88
+ end
89
+
90
+ it 'raises an argument error if the key has the wrong curve' do
91
+ group = OpenSSL::PKey::EC::Group.new('secp384r1')
92
+ private_key = OpenSSL::PKey::EC.new(group).generate_key
93
+ expect { Sandal::Sig::ES256.new(private_key) }.to raise_error ArgumentError
88
94
  end
89
95
 
90
96
  end
@@ -99,8 +105,14 @@ describe Sandal::Sig::ES384 do
99
105
  signature = signer.sign(data)
100
106
  public_key = OpenSSL::PKey::EC.new(group)
101
107
  public_key.public_key = private_key.public_key
102
- verifier = Sandal::Sig::ES384.new(public_key)
103
- verifier.verify(signature, data).should == true
108
+ validator = Sandal::Sig::ES384.new(public_key)
109
+ validator.valid?(signature, data).should == true
110
+ end
111
+
112
+ it 'raises an argument error if the key has the wrong curve' do
113
+ group = OpenSSL::PKey::EC::Group.new('secp521r1')
114
+ private_key = OpenSSL::PKey::EC.new(group).generate_key
115
+ expect { Sandal::Sig::ES384.new(private_key) }.to raise_error ArgumentError
104
116
  end
105
117
 
106
118
  end
@@ -115,8 +127,8 @@ describe Sandal::Sig::ES512 do
115
127
  signature = signer.sign(data)
116
128
  public_key = OpenSSL::PKey::EC.new(group)
117
129
  public_key.public_key = private_key.public_key
118
- verifier = Sandal::Sig::ES512.new(public_key)
119
- verifier.verify(signature, data).should == true
130
+ validator = Sandal::Sig::ES512.new(public_key)
131
+ validator.valid?(signature, data).should == true
120
132
  end
121
133
 
122
134
  it 'can verify the signature in JWS section A4.1' do
@@ -129,8 +141,8 @@ describe Sandal::Sig::ES512 do
129
141
  group = OpenSSL::PKey::EC::Group.new('secp521r1')
130
142
  public_key = OpenSSL::PKey::EC.new(group)
131
143
  public_key.public_key = make_point(group, x, y)
132
- verifier = Sandal::Sig::ES512.new(public_key)
133
- verifier.verify(signature, data).should == true
144
+ validator = Sandal::Sig::ES512.new(public_key)
145
+ validator.valid?(signature, data).should == true
134
146
  end
135
147
 
136
148
  it 'fails to verify the signature in JWS section A4.1 when the data is changed' do
@@ -143,8 +155,14 @@ describe Sandal::Sig::ES512 do
143
155
  group = OpenSSL::PKey::EC::Group.new('secp521r1')
144
156
  public_key = OpenSSL::PKey::EC.new(group)
145
157
  public_key.public_key = make_point(group, x, y)
146
- verifier = Sandal::Sig::ES512.new(public_key)
147
- verifier.verify(signature, data).should == false
158
+ validator = Sandal::Sig::ES512.new(public_key)
159
+ validator.valid?(signature, data).should == false
160
+ end
161
+
162
+ it 'raises an argument error if the key has the wrong curve' do
163
+ group = OpenSSL::PKey::EC::Group.new('prime256v1')
164
+ private_key = OpenSSL::PKey::EC.new(group).generate_key
165
+ expect { Sandal::Sig::ES512.new(private_key) }.to raise_error ArgumentError
148
166
  end
149
167
 
150
168
  end
@@ -7,7 +7,7 @@ describe Sandal::Sig::HS256 do
7
7
  key = 'A secret key'
8
8
  signer = Sandal::Sig::HS256.new(key)
9
9
  signature = signer.sign(data)
10
- signer.verify(signature, data).should == true
10
+ signer.valid?(signature, data).should == true
11
11
  end
12
12
  end
13
13
 
@@ -17,7 +17,7 @@ describe Sandal::Sig::HS384 do
17
17
  key = 'Another secret key'
18
18
  signer = Sandal::Sig::HS384.new(key)
19
19
  signature = signer.sign(data)
20
- signer.verify(signature, data).should == true
20
+ signer.valid?(signature, data).should == true
21
21
  end
22
22
  end
23
23
 
@@ -27,6 +27,6 @@ describe Sandal::Sig::HS512 do
27
27
  key = 'Yet another secret key'
28
28
  signer = Sandal::Sig::HS512.new(key)
29
29
  signature = signer.sign(data)
30
- signer.verify(signature, data).should == true
30
+ signer.valid?(signature, data).should == true
31
31
  end
32
32
  end
@@ -7,8 +7,8 @@ describe Sandal::Sig::RS256 do
7
7
  private_key = OpenSSL::PKey::RSA.generate(2048)
8
8
  signer = Sandal::Sig::RS256.new(private_key)
9
9
  signature = signer.sign(data)
10
- verifier = Sandal::Sig::RS256.new(private_key.public_key)
11
- verifier.verify(signature, data).should == true
10
+ validator = Sandal::Sig::RS256.new(private_key.public_key)
11
+ validator.valid?(signature, data).should == true
12
12
  end
13
13
  end
14
14
 
@@ -18,8 +18,8 @@ describe Sandal::Sig::RS384 do
18
18
  private_key = OpenSSL::PKey::RSA.generate(2048)
19
19
  signer = Sandal::Sig::RS384.new(private_key)
20
20
  signature = signer.sign(data)
21
- verifier = Sandal::Sig::RS384.new(private_key.public_key)
22
- verifier.verify(signature, data).should == true
21
+ validator = Sandal::Sig::RS384.new(private_key.public_key)
22
+ validator.valid?(signature, data).should == true
23
23
  end
24
24
  end
25
25
 
@@ -29,7 +29,7 @@ describe Sandal::Sig::RS512 do
29
29
  private_key = OpenSSL::PKey::RSA.generate(2048)
30
30
  signer = Sandal::Sig::RS512.new(private_key)
31
31
  signature = signer.sign(data)
32
- verifier = Sandal::Sig::RS512.new(private_key.public_key)
33
- verifier.verify(signature, data).should == true
32
+ validator = Sandal::Sig::RS512.new(private_key.public_key)
33
+ validator.valid?(signature, data).should == true
34
34
  end
35
35
  end
@@ -11,6 +11,10 @@ describe Sandal::Util do
11
11
  val.should == src
12
12
  end
13
13
 
14
+ it 'raises a token error if base64 strings contain padding' do
15
+ expect { Sandal::Util.base64_decode('eyJpc3MiOiJq=') }.to raise_error Sandal::TokenError
16
+ end
17
+
14
18
  it 'compares nil strings as equal' do
15
19
  Sandal::Util.secure_equals(nil, nil).should == true
16
20
  end
data/spec/sandal_spec.rb CHANGED
@@ -27,22 +27,22 @@ describe Sandal do
27
27
 
28
28
  it 'decodes non-JSON payloads to a String' do
29
29
  token = Sandal.encode_token('not valid json', nil)
30
- Sandal.decode_token(token).class.should == String
30
+ Sandal.decode_token(token).class.should.kind_of? String
31
31
  end
32
32
 
33
33
  it 'decodes JSON payloads to a Hash' do
34
34
  token = Sandal.encode_token({ 'valid' => 'json' }, nil)
35
- Sandal.decode_token(token).class.should == Hash
35
+ Sandal.decode_token(token).class.should.kind_of? Hash
36
36
  end
37
37
 
38
- it 'raises a token error when the expiry date is far in the past' do
38
+ it 'raises a claim error when the expiry date is far in the past' do
39
39
  token = Sandal.encode_token({ 'exp' => (Time.now - 600).to_i }, nil)
40
- expect { Sandal.decode_token(token) }.to raise_error Sandal::TokenError
40
+ expect { Sandal.decode_token(token) }.to raise_error Sandal::ClaimError
41
41
  end
42
42
 
43
- it 'raises a token error when the expiry date is invalid' do
43
+ it 'raises a claim error when the expiry date is invalid' do
44
44
  token = Sandal.encode_token({ 'exp' => 'invalid value' }, nil)
45
- expect { Sandal.decode_token(token) }.to raise_error Sandal::TokenError
45
+ expect { Sandal.decode_token(token) }.to raise_error Sandal::ClaimError
46
46
  end
47
47
 
48
48
  it 'does not raise an error when the expiry date is far in the past but validation is disabled' do
@@ -60,14 +60,14 @@ describe Sandal do
60
60
  Sandal.decode_token(token)
61
61
  end
62
62
 
63
- it 'raises a token error when the not-before date is far in the future' do
63
+ it 'raises a claim error when the not-before date is far in the future' do
64
64
  token = Sandal.encode_token({ 'nbf' => (Time.now + 600).to_i }, nil)
65
- expect { Sandal.decode_token(token) }.to raise_error Sandal::TokenError
65
+ expect { Sandal.decode_token(token) }.to raise_error Sandal::ClaimError
66
66
  end
67
67
 
68
- it 'raises a token error when the not-before date is invalid' do
68
+ it 'raises a claim error when the not-before date is invalid' do
69
69
  token = Sandal.encode_token({ 'nbf' => 'invalid value' }, nil)
70
- expect { Sandal.decode_token(token) }.to raise_error Sandal::TokenError
70
+ expect { Sandal.decode_token(token) }.to raise_error Sandal::ClaimError
71
71
  end
72
72
 
73
73
  it 'does not raise an error when the not-before date is far in the future but validation is disabled' do
@@ -85,12 +85,12 @@ describe Sandal do
85
85
  Sandal.decode_token(token)
86
86
  end
87
87
 
88
- it 'raises a token error when the issuer is not valid' do
88
+ it 'raises a claim error when the issuer is not valid' do
89
89
  token = Sandal.encode_token({ 'iss' => 'example.org' }, nil)
90
90
  expect { Sandal.decode_token(token) do |header, options|
91
91
  options[:valid_iss] = ['example.net']
92
92
  nil
93
- end }.to raise_error Sandal::TokenError
93
+ end }.to raise_error Sandal::ClaimError
94
94
  end
95
95
 
96
96
  it 'does not raise an error when the issuer is valid' do
@@ -101,20 +101,20 @@ describe Sandal do
101
101
  end
102
102
  end
103
103
 
104
- it 'raises a token error when the audience string is not valid' do
104
+ it 'raises a claim error when the audience string is not valid' do
105
105
  token = Sandal.encode_token({ 'aud' => 'example.com' }, nil)
106
106
  expect { Sandal.decode_token(token) do |header, options|
107
107
  options[:valid_aud] = ['example.net']
108
108
  nil
109
- end }.to raise_error Sandal::TokenError
109
+ end }.to raise_error Sandal::ClaimError
110
110
  end
111
111
 
112
- it 'raises a token error when the audience array is not valid' do
112
+ it 'raises a claim error when the audience array is not valid' do
113
113
  token = Sandal.encode_token({ 'aud' => ['example.org', 'example.com'] }, nil)
114
114
  expect { Sandal.decode_token(token) do |header, options|
115
115
  options[:valid_aud] = ['example.net']
116
116
  nil
117
- end }.to raise_error Sandal::TokenError
117
+ end }.to raise_error Sandal::ClaimError
118
118
  end
119
119
 
120
120
  it 'does not raise an error when the audience string is valid' do
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sandal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
5
- prerelease:
4
+ version: 0.1.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Greg Beech
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-03-30 00:00:00.000000000 Z
11
+ date: 2013-04-01 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: json
14
+ name: multi_json
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ~>
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ~>
28
25
  - !ruby/object:Gem::Version
@@ -30,97 +27,85 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: bundler
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ~>
31
+ - - ! '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: '1.3'
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ~>
38
+ - - ! '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: '1.3'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rake
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ~>
45
+ - - ! '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '10.0'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ~>
52
+ - - ! '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '10.0'
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: rspec
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ~>
59
+ - - ! '>='
68
60
  - !ruby/object:Gem::Version
69
61
  version: '2.13'
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ~>
66
+ - - ! '>='
76
67
  - !ruby/object:Gem::Version
77
68
  version: '2.13'
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: coveralls
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
- - - ~>
73
+ - - ! '>='
84
74
  - !ruby/object:Gem::Version
85
75
  version: '0.6'
86
76
  type: :development
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
- - - ~>
80
+ - - ! '>='
92
81
  - !ruby/object:Gem::Version
93
82
  version: '0.6'
94
83
  - !ruby/object:Gem::Dependency
95
84
  name: yard
96
85
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
86
  requirements:
99
- - - ~>
87
+ - - ! '>='
100
88
  - !ruby/object:Gem::Version
101
89
  version: '0.8'
102
90
  type: :development
103
91
  prerelease: false
104
92
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
93
  requirements:
107
- - - ~>
94
+ - - ! '>='
108
95
  - !ruby/object:Gem::Version
109
96
  version: '0.8'
110
97
  - !ruby/object:Gem::Dependency
111
98
  name: redcarpet
112
99
  requirement: !ruby/object:Gem::Requirement
113
- none: false
114
100
  requirements:
115
- - - ~>
101
+ - - ! '>='
116
102
  - !ruby/object:Gem::Version
117
103
  version: '2.2'
118
104
  type: :development
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
- none: false
122
107
  requirements:
123
- - - ~>
108
+ - - ! '>='
124
109
  - !ruby/object:Gem::Version
125
110
  version: '2.2'
126
111
  description: A ruby library for creating and reading JSON Web Tokens (JWT), supporting
@@ -143,6 +128,7 @@ files:
143
128
  - README.md
144
129
  - Rakefile
145
130
  - lib/sandal.rb
131
+ - lib/sandal/claims.rb
146
132
  - lib/sandal/enc.rb
147
133
  - lib/sandal/enc/aescbc.rb
148
134
  - lib/sandal/enc/aesgcm.rb
@@ -162,34 +148,27 @@ files:
162
148
  homepage: http://rubygems.org/gems/sandal
163
149
  licenses:
164
150
  - MIT
151
+ metadata: {}
165
152
  post_install_message:
166
153
  rdoc_options: []
167
154
  require_paths:
168
155
  - lib
169
156
  required_ruby_version: !ruby/object:Gem::Requirement
170
- none: false
171
157
  requirements:
172
158
  - - ! '>='
173
159
  - !ruby/object:Gem::Version
174
160
  version: '0'
175
- segments:
176
- - 0
177
- hash: -3672992457221215645
178
161
  required_rubygems_version: !ruby/object:Gem::Requirement
179
- none: false
180
162
  requirements:
181
163
  - - ! '>='
182
164
  - !ruby/object:Gem::Version
183
165
  version: '0'
184
- segments:
185
- - 0
186
- hash: -3672992457221215645
187
166
  requirements:
188
167
  - openssl 1.0.1c for EC signature methods
189
168
  rubyforge_project:
190
- rubygems_version: 1.8.25
169
+ rubygems_version: 2.0.3
191
170
  signing_key:
192
- specification_version: 3
171
+ specification_version: 4
193
172
  summary: A JSON Web Token (JWT) library.
194
173
  test_files:
195
174
  - spec/helper.rb