sandal 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/sandal.rb +26 -12
- data/lib/sandal/sig.rb +16 -2
- data/lib/sandal/sig/es.rb +14 -17
- data/lib/sandal/version.rb +1 -1
- metadata +3 -2
data/lib/sandal.rb
CHANGED
@@ -11,37 +11,49 @@ require 'sandal/enc'
|
|
11
11
|
# A library for creating and reading JSON Web Tokens (JWT).
|
12
12
|
module Sandal
|
13
13
|
|
14
|
-
# Creates a signed
|
15
|
-
|
14
|
+
# Creates a signed JSON Web Token.
|
15
|
+
#
|
16
|
+
# @param payload [String] The payload of the token.
|
17
|
+
# @param signer [Sandal::Sig] The token signer, which may be nil for an unsigned token.
|
18
|
+
# @param header_fields [Hash] Header fields for the token (note: do not include 'alg').
|
19
|
+
# @return [String] A signed JSON Web Token.
|
20
|
+
def self.encode_token(payload, signer, header_fields = nil)
|
16
21
|
if header_fields && header_fields['enc']
|
17
22
|
throw ArgumentError.new('The header cannot contain an "enc" parameter.')
|
18
23
|
end
|
19
|
-
|
24
|
+
signer ||= Sandal::Sig::None.instance
|
20
25
|
|
21
26
|
header = {}
|
22
|
-
header['alg'] =
|
27
|
+
header['alg'] = signer.name if signer.name != Sandal::Sig::None.instance.name
|
23
28
|
header = header_fields.merge(header) if header_fields
|
24
29
|
|
25
30
|
encoded_header = Sandal::Util.base64_encode(JSON.generate(header))
|
26
31
|
encoded_payload = Sandal::Util.base64_encode(payload)
|
27
32
|
secured_input = [encoded_header, encoded_payload].join('.')
|
28
33
|
|
29
|
-
signature =
|
34
|
+
signature = signer.sign(secured_input)
|
30
35
|
encoded_signature = Sandal::Util.base64_encode(signature)
|
31
36
|
[secured_input, encoded_signature].join('.')
|
32
37
|
end
|
33
38
|
|
34
|
-
# Creates an encrypted
|
35
|
-
|
39
|
+
# Creates an encrypted JSON Web Token.
|
40
|
+
#
|
41
|
+
# @param payload [String] The payload of the token.
|
42
|
+
# @param encrypted [Sandal::Enc] The token encrypter.
|
43
|
+
# @param header_fields [Hash] Header fields for the token (note: do not include 'alg' or 'enc').
|
44
|
+
# @return [String] An encrypted JSON Web Token.
|
45
|
+
def self.encrypt_token(payload, encrypter, header_fields = nil)
|
36
46
|
header = {}
|
37
|
-
header['enc'] =
|
38
|
-
header['alg'] =
|
47
|
+
header['enc'] = encrypter.name
|
48
|
+
header['alg'] = encrypter.alg_name
|
39
49
|
header = header_fields.merge(header) if header_fields
|
40
50
|
|
41
|
-
|
51
|
+
encrypter.encrypt(header, payload)
|
42
52
|
end
|
43
53
|
|
44
|
-
# Decodes a
|
54
|
+
# Decodes a JSON Web Token, verifying the signature as necessary.
|
55
|
+
#
|
56
|
+
# **NOTE: This method is likely to change, to allow more validation options**
|
45
57
|
def self.decode_token(token, &sig_finder)
|
46
58
|
parts = token.split('.')
|
47
59
|
throw ArgumentError.new('Invalid token format.') unless [2, 3].include?(parts.length)
|
@@ -65,7 +77,9 @@ module Sandal
|
|
65
77
|
payload
|
66
78
|
end
|
67
79
|
|
68
|
-
# Decrypts
|
80
|
+
# Decrypts an encrypted JSON Web Token.
|
81
|
+
#
|
82
|
+
# **NOTE: This method is likely to change, to allow more validation options**
|
69
83
|
def self.decrypt_token(encrypted_token, &enc_finder)
|
70
84
|
parts = encrypted_token.split('.')
|
71
85
|
throw ArgumentError.new('Invalid token format.') unless parts.length == 5
|
data/lib/sandal/sig.rb
CHANGED
@@ -4,15 +4,22 @@ module Sandal
|
|
4
4
|
# Common signature traits.
|
5
5
|
module Sig
|
6
6
|
|
7
|
-
# The JWA name of the algorithm.
|
7
|
+
# @return [String] The JWA name of the algorithm.
|
8
8
|
attr_reader :name
|
9
9
|
|
10
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.
|
11
14
|
def sign(payload)
|
12
15
|
throw NotImplementedError.new("#{@name}.sign is not implemented.")
|
13
16
|
end
|
14
17
|
|
15
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.
|
16
23
|
def verify(signature, payload)
|
17
24
|
throw NotImplementedError.new("#{@name}.verify is not implemented.")
|
18
25
|
end
|
@@ -28,11 +35,18 @@ module Sandal
|
|
28
35
|
end
|
29
36
|
|
30
37
|
# Returns an empty signature.
|
38
|
+
#
|
39
|
+
# @param payload [String] This parameter is ignored.
|
40
|
+
# @return [String] An empty string.
|
31
41
|
def sign(payload)
|
32
42
|
''
|
33
43
|
end
|
34
44
|
|
35
|
-
# Verifies that
|
45
|
+
# Verifies that a signature is nil or empty.
|
46
|
+
#
|
47
|
+
# @param signature [String] The signature to verify.
|
48
|
+
# @param payload [String] This parameter is ignored.
|
49
|
+
# @return [Boolean] `true` if the signature is nil or empty; otherwise `false`.
|
36
50
|
def verify(signature, payload)
|
37
51
|
signature.nil? || signature.length == 0
|
38
52
|
end
|
data/lib/sandal/sig/es.rb
CHANGED
@@ -8,10 +8,11 @@ module Sandal
|
|
8
8
|
include Sandal::Sig
|
9
9
|
|
10
10
|
# Creates a new instance with the size of the SHA algorithm and an OpenSSL ES PKey.
|
11
|
-
def initialize(sha_size, key)
|
11
|
+
def initialize(sha_size, prime_size, key)
|
12
12
|
throw ArgumentError.new('A key is required.') unless key
|
13
13
|
@name = "ES#{sha_size}"
|
14
14
|
@digest = OpenSSL::Digest.new("sha#{sha_size}")
|
15
|
+
@prime_size = prime_size
|
15
16
|
@key = key
|
16
17
|
end
|
17
18
|
|
@@ -20,7 +21,7 @@ module Sandal
|
|
20
21
|
hash = @digest.digest(payload)
|
21
22
|
asn1_sig = @key.dsa_sign_asn1(hash)
|
22
23
|
r, s = self.class.decode_asn1_signature(asn1_sig)
|
23
|
-
self.class.encode_jws_signature(r, s)
|
24
|
+
self.class.encode_jws_signature(r, s, @prime_size)
|
24
25
|
end
|
25
26
|
|
26
27
|
# Verifies a payload signature and returns whether the signature matches.
|
@@ -45,22 +46,18 @@ module Sandal
|
|
45
46
|
|
46
47
|
# Decodes a JWS signature into a pair of BNs.
|
47
48
|
def self.decode_jws_signature(signature)
|
48
|
-
|
49
|
-
|
50
|
-
r =
|
51
|
-
s =
|
49
|
+
n_length = signature.length / 2
|
50
|
+
s_to_n = lambda { |s| OpenSSL::BN.new(s.unpack('H*')[0], 16) }
|
51
|
+
r = s_to_n.call(signature[0..(n_length - 1)])
|
52
|
+
s = s_to_n.call(signature[n_length..-1])
|
52
53
|
return r, s
|
53
54
|
end
|
54
55
|
|
55
56
|
# Encodes a pair of BNs into a JWS signature.
|
56
|
-
def self.encode_jws_signature(r, s)
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
s_str = [s.to_s(16)].pack('H*')
|
61
|
-
s_str = "\x00" + s_str if s_str.length % 2 != 0
|
62
|
-
binary_string = r_str + s_str
|
63
|
-
Sandal::Util.base64_encode(binary_string)
|
57
|
+
def self.encode_jws_signature(r, s, prime_size)
|
58
|
+
byte_count = (prime_size / 8.0).ceil
|
59
|
+
n_to_s = lambda { |n| [n.to_s(16)].pack('H*').rjust(byte_count, "\0") }
|
60
|
+
n_to_s.call(r) + n_to_s.call(s)
|
64
61
|
end
|
65
62
|
|
66
63
|
end
|
@@ -69,7 +66,7 @@ module Sandal
|
|
69
66
|
class ES256 < Sandal::Sig::ES
|
70
67
|
# Creates a new instance with an OpenSSL ES PKey.
|
71
68
|
def initialize(key)
|
72
|
-
super(256, key)
|
69
|
+
super(256, 256, key)
|
73
70
|
end
|
74
71
|
end
|
75
72
|
|
@@ -77,7 +74,7 @@ module Sandal
|
|
77
74
|
class ES384 < Sandal::Sig::ES
|
78
75
|
# Creates a new instance with an OpenSSL ES PKey.
|
79
76
|
def initialize(key)
|
80
|
-
super(384, key)
|
77
|
+
super(384, 384, key)
|
81
78
|
end
|
82
79
|
end
|
83
80
|
|
@@ -85,7 +82,7 @@ module Sandal
|
|
85
82
|
class ES512 < Sandal::Sig::ES
|
86
83
|
# Creates a new instance with an OpenSSL ES PKey.
|
87
84
|
def initialize(key)
|
88
|
-
super(512, key)
|
85
|
+
super(512, 521, key)
|
89
86
|
end
|
90
87
|
end
|
91
88
|
|
data/lib/sandal/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sandal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-03-
|
12
|
+
date: 2013-03-29 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: A ruby library for creating and reading JSON Web Tokens (JWT), supporting
|
15
15
|
JSON Web Signatures (JWS) and JSON Web Encryption (JWE).
|
@@ -55,3 +55,4 @@ signing_key:
|
|
55
55
|
specification_version: 3
|
56
56
|
summary: A JSON Web Token (JWT) library.
|
57
57
|
test_files: []
|
58
|
+
has_rdoc:
|