jose 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +20 -0
  3. data/.gitignore +9 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +4 -0
  7. data/CODE_OF_CONDUCT.md +13 -0
  8. data/Gemfile +20 -0
  9. data/LICENSE.txt +373 -0
  10. data/README.md +41 -0
  11. data/Rakefile +10 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +7 -0
  14. data/jose.gemspec +36 -0
  15. data/lib/jose.rb +61 -0
  16. data/lib/jose/jwa.rb +21 -0
  17. data/lib/jose/jwa/aes_kw.rb +105 -0
  18. data/lib/jose/jwa/concat_kdf.rb +50 -0
  19. data/lib/jose/jwa/pkcs1.rb +269 -0
  20. data/lib/jose/jwa/pkcs7.rb +23 -0
  21. data/lib/jose/jwe.rb +290 -0
  22. data/lib/jose/jwe/alg.rb +12 -0
  23. data/lib/jose/jwe/alg_aes_gcm_kw.rb +98 -0
  24. data/lib/jose/jwe/alg_aes_kw.rb +57 -0
  25. data/lib/jose/jwe/alg_dir.rb +40 -0
  26. data/lib/jose/jwe/alg_ecdh_es.rb +123 -0
  27. data/lib/jose/jwe/alg_pbes2.rb +90 -0
  28. data/lib/jose/jwe/alg_rsa.rb +63 -0
  29. data/lib/jose/jwe/enc.rb +8 -0
  30. data/lib/jose/jwe/enc_aes_cbc_hmac.rb +80 -0
  31. data/lib/jose/jwe/enc_aes_gcm.rb +68 -0
  32. data/lib/jose/jwe/zip.rb +7 -0
  33. data/lib/jose/jwe/zip_def.rb +28 -0
  34. data/lib/jose/jwk.rb +347 -0
  35. data/lib/jose/jwk/kty.rb +34 -0
  36. data/lib/jose/jwk/kty_ec.rb +179 -0
  37. data/lib/jose/jwk/kty_oct.rb +104 -0
  38. data/lib/jose/jwk/kty_rsa.rb +185 -0
  39. data/lib/jose/jwk/pem.rb +19 -0
  40. data/lib/jose/jwk/set.rb +2 -0
  41. data/lib/jose/jws.rb +242 -0
  42. data/lib/jose/jws/alg.rb +10 -0
  43. data/lib/jose/jws/alg_ecdsa.rb +41 -0
  44. data/lib/jose/jws/alg_hmac.rb +41 -0
  45. data/lib/jose/jws/alg_rsa_pkcs1_v1_5.rb +41 -0
  46. data/lib/jose/jws/alg_rsa_pss.rb +41 -0
  47. data/lib/jose/jwt.rb +145 -0
  48. data/lib/jose/version.rb +3 -0
  49. metadata +162 -0
@@ -0,0 +1,104 @@
1
+ class JOSE::JWK::KTY_oct < Struct.new(:oct)
2
+
3
+ # JOSE::JWK callbacks
4
+
5
+ def self.from_map(fields)
6
+ if fields['kty'] == 'oct' and fields['k'].is_a?(String)
7
+ return JOSE::JWK::KTY_oct.new(JOSE.urlsafe_decode64(fields['k'])), fields.except('kty', 'k')
8
+ else
9
+ raise ArgumentError, "invalid 'oct' JWK"
10
+ end
11
+ end
12
+
13
+ def to_key
14
+ return oct
15
+ end
16
+
17
+ def to_map(fields)
18
+ return fields.put('k', JOSE.urlsafe_encode64(oct)).put('kty', 'oct')
19
+ end
20
+
21
+ def to_public_map(fields)
22
+ return to_map(fields)
23
+ end
24
+
25
+ def to_thumbprint_map(fields)
26
+ return to_public_map(fields).slice('k', 'kty')
27
+ end
28
+
29
+ # JOSE::JWK::KTY callbacks
30
+
31
+ def block_encryptor(fields, plain_text)
32
+ enc = case (oct.bytesize * 8)
33
+ when 128
34
+ 'A128GCM'
35
+ when 192
36
+ 'A192GCM'
37
+ when 256
38
+ 'A256GCM'
39
+ when 384
40
+ 'A192CBC-HS384'
41
+ when 512
42
+ 'A256CBC-HS512'
43
+ else
44
+ raise ArgumentError, "oct of size #{oct.bytesize * 8} has no default block encryptor"
45
+ end
46
+ return JOSE::Map[
47
+ 'alg' => 'dir',
48
+ 'enc' => enc
49
+ ]
50
+ end
51
+
52
+ def derive_key
53
+ return oct
54
+ end
55
+
56
+ def self.generate_key(size)
57
+ if size.is_a?(Array) and size.length == 2 and size[0] == :oct
58
+ size = size[1]
59
+ end
60
+ case size
61
+ when Integer
62
+ return from_oct(SecureRandom.random_bytes(size))
63
+ else
64
+ raise ArgumentError, "'size' must be an Integer"
65
+ end
66
+ end
67
+
68
+ def generate_key(fields)
69
+ kty, other_fields = JOSE::JWK::KTY_oct.generate_key(oct.bytesize)
70
+ return kty, fields.delete('kid').merge(other_fields)
71
+ end
72
+
73
+ def key_encryptor(fields, key)
74
+ return JOSE::JWK::KTY.key_encryptor(self, fields, key)
75
+ end
76
+
77
+ def sign(message, digest_type)
78
+ return OpenSSL::HMAC.digest(digest_type.new, oct, message)
79
+ end
80
+
81
+ def signer(fields = nil, plain_text = nil)
82
+ return JOSE::Map['alg' => 'HS256']
83
+ end
84
+
85
+ def verify(message, digest_type, signature)
86
+ return JOSE::JWA.constant_time_compare(signature, sign(message, digest_type))
87
+ end
88
+
89
+ # API functions
90
+
91
+ def self.from_oct(binary)
92
+ case binary
93
+ when String
94
+ return JOSE::JWK::KTY_oct.new(binary), JOSE::Map[]
95
+ else
96
+ raise ArgumentError, "'binary' must be a String"
97
+ end
98
+ end
99
+
100
+ def to_oct
101
+ return oct
102
+ end
103
+
104
+ end
@@ -0,0 +1,185 @@
1
+ class JOSE::JWK::KTY_RSA < Struct.new(:key)
2
+
3
+ # JOSE::JWK callbacks
4
+
5
+ def self.from_map(fields)
6
+ if fields['kty'] == 'RSA' and fields['e'].is_a?(String) and fields['n'].is_a?(String)
7
+ if fields['oth'].is_a?(Array)
8
+ raise ArgumentError, "multi-prime RSA keys are not supported"
9
+ elsif fields['d'].is_a?(String)
10
+ if fields['dp'].is_a?(String) and fields['dq'].is_a?(String) and fields['p'].is_a?(String) and fields['q'].is_a?(String) and fields['qi'].is_a?(String)
11
+ rsa = OpenSSL::PKey::RSA.new
12
+ rsa.d = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['d']), 2)
13
+ rsa.dmp1 = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['dp']), 2)
14
+ rsa.dmq1 = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['dq']), 2)
15
+ rsa.e = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['e']), 2)
16
+ rsa.n = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['n']), 2)
17
+ rsa.p = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['p']), 2)
18
+ rsa.q = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['q']), 2)
19
+ rsa.iqmp = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['qi']), 2)
20
+ return JOSE::JWK::KTY_RSA.new(rsa), fields.except('kty', 'd', 'dp', 'dq', 'e', 'n', 'p', 'q', 'qi')
21
+ else
22
+ raise ArgumentError, "invalid 'RSA' JWK"
23
+ end
24
+ else
25
+ rsa = OpenSSL::PKey::RSA.new
26
+ rsa.e = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['e']), 2)
27
+ rsa.n = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['n']), 2)
28
+ return JOSE::JWK::KTY_RSA.new(rsa), fields.except('kty', 'e', 'n')
29
+ end
30
+ else
31
+ raise ArgumentError, "invalid 'RSA' JWK"
32
+ end
33
+ end
34
+
35
+ def to_key
36
+ return key
37
+ end
38
+
39
+ def to_map(fields)
40
+ if key.private?
41
+ return fields.
42
+ put('d', JOSE.urlsafe_encode64(key.d.to_s(2))).
43
+ put('dp', JOSE.urlsafe_encode64(key.dmp1.to_s(2))).
44
+ put('dq', JOSE.urlsafe_encode64(key.dmq1.to_s(2))).
45
+ put('e', JOSE.urlsafe_encode64(key.e.to_s(2))).
46
+ put('kty', 'RSA').
47
+ put('n', JOSE.urlsafe_encode64(key.n.to_s(2))).
48
+ put('p', JOSE.urlsafe_encode64(key.p.to_s(2))).
49
+ put('q', JOSE.urlsafe_encode64(key.q.to_s(2))).
50
+ put('qi', JOSE.urlsafe_encode64(key.iqmp.to_s(2))).
51
+ put('q', JOSE.urlsafe_encode64(key.q.to_s(2)))
52
+ else
53
+ return fields.
54
+ put('e', JOSE.urlsafe_encode64(key.e.to_s(2))).
55
+ put('kty', 'RSA').
56
+ put('n', JOSE.urlsafe_encode64(key.n.to_s(2)))
57
+ end
58
+ end
59
+
60
+ def to_public_map(fields)
61
+ return to_map(fields).except('d', 'dp', 'dq', 'p', 'q', 'qi', 'oth')
62
+ end
63
+
64
+ def to_thumbprint_map(fields)
65
+ return to_public_map(fields).slice('e', 'kty', 'n')
66
+ end
67
+
68
+ # JOSE::JWK::KTY callbacks
69
+
70
+ def block_encryptor(fields, plain_text)
71
+ return JOSE::Map[
72
+ 'alg' => 'RSA-OAEP',
73
+ 'enc' => 'A128GCM'
74
+ ]
75
+ end
76
+
77
+ def decrypt_private(cipher_text, rsa_padding: :rsa_pkcs1_padding, rsa_oaep_md: nil)
78
+ case rsa_padding
79
+ when :rsa_pkcs1_padding
80
+ return key.private_decrypt(cipher_text, OpenSSL::PKey::RSA::PKCS1_PADDING)
81
+ when :rsa_pkcs1_oaep_padding
82
+ rsa_oaep_md ||= OpenSSL::Digest::SHA1
83
+ if rsa_oaep_md == OpenSSL::Digest::SHA1
84
+ return key.private_decrypt(cipher_text, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
85
+ elsif rsa_oaep_md == OpenSSL::Digest::SHA256
86
+ return JOSE::JWA::PKCS1.rsaes_oaep_decrypt(rsa_oaep_md, cipher_text, key)
87
+ else
88
+ raise ArgumentError, "unsupported RSA OAEP md: #{rsa_oaep_md.inspect}"
89
+ end
90
+ else
91
+ raise ArgumentError, "unsupported RSA padding: #{rsa_padding.inspect}"
92
+ end
93
+ end
94
+
95
+ def encrypt_public(plain_text, rsa_padding: :rsa_pkcs1_padding, rsa_oaep_md: nil)
96
+ case rsa_padding
97
+ when :rsa_pkcs1_padding
98
+ return key.public_encrypt(plain_text, OpenSSL::PKey::RSA::PKCS1_PADDING)
99
+ when :rsa_pkcs1_oaep_padding
100
+ rsa_oaep_md ||= OpenSSL::Digest::SHA1
101
+ if rsa_oaep_md == OpenSSL::Digest::SHA1
102
+ return key.public_encrypt(plain_text, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
103
+ elsif rsa_oaep_md == OpenSSL::Digest::SHA256
104
+ return JOSE::JWA::PKCS1.rsaes_oaep_encrypt(rsa_oaep_md, plain_text, key)
105
+ else
106
+ raise ArgumentError, "unsupported RSA OAEP md: #{rsa_oaep_md.inspect}"
107
+ end
108
+ else
109
+ raise ArgumentError, "unsupported RSA padding: #{rsa_padding.inspect}"
110
+ end
111
+ end
112
+
113
+ def self.generate_key(modulus_size, exponent_size = nil)
114
+ if modulus_size.is_a?(Array)
115
+ if modulus_size.length == 2 and modulus_size[0] == :rsa
116
+ modulus_size = modulus_size[1]
117
+ elsif modulus_size.length == 3 and modulus_size[0] == :rsa
118
+ exponent_size = modulus_size[2]
119
+ modulus_size = modulus_size[1]
120
+ end
121
+ end
122
+ if modulus_size.is_a?(Integer) and (exponent_size.nil? or exponent_size.is_a?(Integer))
123
+ return from_key(OpenSSL::PKey::RSA.generate(modulus_size, exponent_size))
124
+ else
125
+ raise ArgumentError, "'modulus_size' must be an Integer and 'exponent_size' must be nil or an Integer"
126
+ end
127
+ end
128
+
129
+ def generate_key(fields)
130
+ kty, other_fields = JOSE::JWK::KTY_RSA.generate_key([:rsa, key.n.num_bits, key.e.to_i])
131
+ return kty, fields.delete('kid').merge(other_fields)
132
+ end
133
+
134
+ def key_encryptor(fields, key)
135
+ return JOSE::JWK::KTY.key_encryptor(self, fields, key)
136
+ end
137
+
138
+ def sign(message, digest_type, padding: :rsa_pkcs1_padding)
139
+ case padding
140
+ when :rsa_pkcs1_padding
141
+ return key.sign(digest_type.new, message)
142
+ when :rsa_pkcs1_pss_padding
143
+ return JOSE::JWA::PKCS1.rsassa_pss_sign(digest_type, message, key)
144
+ else
145
+ raise ArgumentError, "unsupported RSA padding: #{padding.inspect}"
146
+ end
147
+ end
148
+
149
+ def signer(fields = nil, plain_text = nil)
150
+ if key.private?
151
+ return JOSE::Map['alg' => 'RS256']
152
+ else
153
+ raise ArgumentError, "signing not supported for public keys"
154
+ end
155
+ end
156
+
157
+ def verify(message, digest_type, signature, padding: :rsa_pkcs1_padding)
158
+ case padding
159
+ when :rsa_pkcs1_padding
160
+ return key.verify(digest_type.new, signature, message)
161
+ when :rsa_pkcs1_pss_padding
162
+ return JOSE::JWA::PKCS1.rsassa_pss_verify(digest_type, message, signature, key)
163
+ else
164
+ raise ArgumentError, "unsupported RSA padding: #{padding.inspect}"
165
+ end
166
+ rescue OpenSSL::PKey::PKeyError # jruby raises this error if the signature is invalid
167
+ false
168
+ end
169
+
170
+ # API functions
171
+
172
+ def self.from_key(key)
173
+ case key
174
+ when OpenSSL::PKey::RSA
175
+ return JOSE::JWK::KTY_RSA.new(key), JOSE::Map[]
176
+ else
177
+ raise ArgumentError, "'key' must be a OpenSSL::PKey::RSA"
178
+ end
179
+ end
180
+
181
+ def to_pem(password = nil)
182
+ return JOSE::JWK::PEM.to_binary(key, password)
183
+ end
184
+
185
+ end
@@ -0,0 +1,19 @@
1
+ module JOSE::JWK::PEM
2
+
3
+ extend self
4
+
5
+ def from_binary(object, password = nil)
6
+ pkey = OpenSSL::PKey.read(object, password)
7
+ return JOSE::JWK::KTY.from_key(pkey)
8
+ end
9
+
10
+ def to_binary(key, password = nil)
11
+ if password
12
+ cipher = OpenSSL::Cipher.new('DES-EDE3-CBC')
13
+ return key.to_pem(cipher, password)
14
+ else
15
+ return key.to_pem
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,2 @@
1
+ class JOSE::JWK::Set
2
+ end
data/lib/jose/jws.rb ADDED
@@ -0,0 +1,242 @@
1
+ module JOSE
2
+
3
+ class SignedBinary < ::String
4
+ def expand
5
+ return JOSE::JWS.expand(self)
6
+ end
7
+ end
8
+
9
+ class SignedMap < JOSE::Map
10
+ def compact
11
+ return JOSE::JWS.compact(self)
12
+ end
13
+ end
14
+
15
+ class JWS < Struct.new(:alg, :b64, :fields)
16
+
17
+ # Decode API
18
+
19
+ def self.from(object, modules = {})
20
+ case object
21
+ when JOSE::Map, Hash
22
+ return from_map(object, modules)
23
+ when String
24
+ return from_binary(object, modules)
25
+ when JOSE::JWS
26
+ return object
27
+ else
28
+ raise ArgumentError, "'object' must be a Hash, String, or JOSE::JWS"
29
+ end
30
+ end
31
+
32
+ def self.from_binary(object, modules = {})
33
+ case object
34
+ when String
35
+ return from_map(JOSE.decode(object), modules)
36
+ else
37
+ raise ArgumentError, "'object' must be a String"
38
+ end
39
+ end
40
+
41
+ def self.from_file(file, modules = {})
42
+ return from_binary(File.binread(file), modules)
43
+ end
44
+
45
+ def self.from_map(object, modules = {})
46
+ case object
47
+ when JOSE::Map, Hash
48
+ return from_fields(JOSE::JWS.new(nil, nil, JOSE::Map.new(object)), modules)
49
+ else
50
+ raise ArgumentError, "'object' must be a Hash"
51
+ end
52
+ end
53
+
54
+ # Encode API
55
+
56
+ def self.to_binary(jws)
57
+ return from(jws).to_binary
58
+ end
59
+
60
+ def to_binary
61
+ return JOSE.encode(to_map)
62
+ end
63
+
64
+ def self.to_file(jws, file)
65
+ return from(jws).to_file(file)
66
+ end
67
+
68
+ def to_file(file)
69
+ return File.binwrite(file, to_binary)
70
+ end
71
+
72
+ def self.to_map(jws)
73
+ return from(jws).to_map
74
+ end
75
+
76
+ def to_map
77
+ return alg.to_map(fields)
78
+ end
79
+
80
+ # API
81
+
82
+ def self.compact(map)
83
+ if map.is_a?(Hash) or map.is_a?(JOSE::Map)
84
+ return JOSE::SignedBinary.new([
85
+ map['protected'] || '',
86
+ '.',
87
+ map['payload'] || '',
88
+ '.',
89
+ map['signature'] || ''
90
+ ].join)
91
+ else
92
+ raise ArgumentError, "'map' must be a Hash or a JOSE::Map"
93
+ end
94
+ end
95
+
96
+ def self.expand(binary)
97
+ if binary.is_a?(String)
98
+ parts = binary.split('.')
99
+ if parts.length == 3
100
+ protected_binary, payload, signature = parts
101
+ return JOSE::SignedMap[
102
+ 'payload' => payload,
103
+ 'protected' => protected_binary,
104
+ 'signature' => signature
105
+ ]
106
+ else
107
+ raise ArgumentError, "'binary' is not a valid signed String"
108
+ end
109
+ else
110
+ raise ArgumentError, "'binary' must be a String"
111
+ end
112
+ end
113
+
114
+ def self.peek_payload(signed)
115
+ if signed.is_a?(String)
116
+ signed = expand(signed)
117
+ end
118
+ return JOSE.urlsafe_decode64(signed['payload'])
119
+ end
120
+
121
+ def self.peek_protected(signed)
122
+ if signed.is_a?(String)
123
+ signed = expand(signed)
124
+ end
125
+ return JOSE::Map.new(JOSE.decode(JOSE.urlsafe_decode64(signed['protected'])))
126
+ end
127
+
128
+ def self.sign(key, plain_text, jws, header = nil)
129
+ return from(jws).sign(key, plain_text, header)
130
+ end
131
+
132
+ def sign(key, plain_text, header = nil)
133
+ protected_binary = JOSE.urlsafe_encode64(to_binary)
134
+ payload = JOSE.urlsafe_encode64(plain_text)
135
+ signing_input = signing_input(plain_text, protected_binary)
136
+ signature = JOSE.urlsafe_encode64(alg.sign(key, signing_input))
137
+ return signature_to_map(payload, protected_binary, header, key, signature)
138
+ end
139
+
140
+ # See https://tools.ietf.org/html/draft-ietf-jose-jws-signing-input-options-04
141
+ def signing_input(payload, protected_binary = JOSE.urlsafe_encode64(to_binary))
142
+ if b64 == true or b64.nil?
143
+ payload = JOSE.urlsafe_encode64(payload)
144
+ end
145
+ return [protected_binary, '.', payload].join
146
+ end
147
+
148
+ def self.verify(key, signed)
149
+ if signed.is_a?(String)
150
+ signed = JOSE::JWS.expand(signed)
151
+ end
152
+ if signed.is_a?(Hash)
153
+ signed = JOSE::SignedMap.new(signed)
154
+ end
155
+ if signed.is_a?(JOSE::Map) and signed['payload'].is_a?(String) and signed['protected'].is_a?(String) and signed['signature'].is_a?(String)
156
+ jws = from_binary(JOSE.urlsafe_decode64(signed['protected']))
157
+ signature = JOSE.urlsafe_decode64(signed['signature'])
158
+ plain_text = JOSE.urlsafe_decode64(signed['payload'])
159
+ return jws.verify(key, plain_text, signature, signed['protected'])
160
+ else
161
+ raise ArgumentError, "'signed' is not a valid signed String, Hash, or JOSE::Map"
162
+ end
163
+ end
164
+
165
+ def verify(key, plain_text, signature, protected_binary = JOSE.urlsafe_encode64(to_binary))
166
+ payload = JOSE.urlsafe_encode64(plain_text)
167
+ signing_input = signing_input(plain_text, protected_binary)
168
+ return alg.verify(key, signing_input, signature), plain_text, self
169
+ end
170
+
171
+ def self.verify_strict(key, allow, signed)
172
+ if signed.is_a?(String)
173
+ signed = JOSE::JWS.expand(signed)
174
+ end
175
+ if signed.is_a?(Hash)
176
+ signed = JOSE::SignedMap.new(signed)
177
+ end
178
+ if signed.is_a?(JOSE::Map) and signed['payload'].is_a?(String) and signed['protected'].is_a?(String) and signed['signature'].is_a?(String)
179
+ protected_map = JOSE.decode(JOSE.urlsafe_decode64(signed['protected']))
180
+ plain_text = JOSE.urlsafe_decode64(signed['payload'])
181
+ if allow.member?(protected_map['alg'])
182
+ jws = from_map(protected_map)
183
+ signature = JOSE.urlsafe_decode64(signed['signature'])
184
+ return jws.verify(key, plain_text, signature, signed['protected'])
185
+ else
186
+ return false, plain_text, protected_map
187
+ end
188
+ else
189
+ raise ArgumentError, "'signed' is not a valid signed String, Hash, or JOSE::Map"
190
+ end
191
+ end
192
+
193
+ private
194
+
195
+ def self.from_fields(jws, modules)
196
+ if jws.fields.has_key?('b64')
197
+ jws.b64 = jws.fields['b64']
198
+ jws.fields = jws.fields.delete('b64')
199
+ return from_fields(jws, modules)
200
+ elsif jws.fields.has_key?('alg') and jws.fields['alg'].is_a?(String)
201
+ alg = modules[:alg] || case
202
+ when jws.fields['alg'].start_with?('ES')
203
+ JOSE::JWS::ALG_ECDSA
204
+ when jws.fields['alg'].start_with?('HS')
205
+ JOSE::JWS::ALG_HMAC
206
+ when jws.fields['alg'].start_with?('PS')
207
+ JOSE::JWS::ALG_RSA_PSS
208
+ when jws.fields['alg'].start_with?('RS')
209
+ JOSE::JWS::ALG_RSA_PKCS1_V1_5
210
+ when jws.fields['alg'] == 'none'
211
+ JOSE::JWS::ALG_none
212
+ else
213
+ raise ArgumentError, "unknown 'alg': #{jws.fields['alg'].inspect}"
214
+ end
215
+ jws.alg, jws.fields = alg.from_map(jws.fields)
216
+ return from_fields(jws, modules)
217
+ elsif jws.alg.nil?
218
+ raise ArgumentError, "missing required keys: 'alg'"
219
+ else
220
+ return jws
221
+ end
222
+ end
223
+
224
+ def signature_to_map(payload, protected_binary, header, key, signature)
225
+ if header and header.is_a?(Hash)
226
+ header = JOSE::Map.new(header)
227
+ end
228
+ header ||= JOSE::Map[]
229
+ if key.is_a?(JOSE::JWK) and key.fields['kid'].is_a?(String)
230
+ header = header.put('kid', key.fields['kid'])
231
+ end
232
+ if header.size == 0
233
+ return JOSE::SignedMap['payload' => payload, 'protected' => protected_binary, 'signature' => signature]
234
+ else
235
+ return JOSE::SignedMap['header' => header.to_hash, 'payload' => payload, 'protected' => protected_binary, 'signature' => signature]
236
+ end
237
+ end
238
+
239
+ end
240
+ end
241
+
242
+ require 'jose/jws/alg'