jose 0.1.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.
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,7 @@
1
+ module JOSE::JWE::ZIP
2
+
3
+ extend self
4
+
5
+ end
6
+
7
+ require 'jose/jwe/zip_def'
@@ -0,0 +1,28 @@
1
+ class JOSE::JWE::ZIP_DEF
2
+
3
+ # JOSE::JWE callbacks
4
+
5
+ def self.from_map(fields)
6
+ case fields['zip']
7
+ when 'DEF'
8
+ return new(), fields.except('zip')
9
+ else
10
+ raise ArgumentError, "invalid 'zip' for JWE: #{fields['zip'].inspect}"
11
+ end
12
+ end
13
+
14
+ def to_map(fields)
15
+ return fields.put('zip', 'DEF')
16
+ end
17
+
18
+ # JOSE::JWE::ZIP callbacks
19
+
20
+ def compress(plain_text)
21
+ return Zlib.deflate(plain_text)
22
+ end
23
+
24
+ def uncompress(cipher_text)
25
+ return Zlib.inflate(cipher_text)
26
+ end
27
+
28
+ end
data/lib/jose/jwk.rb ADDED
@@ -0,0 +1,347 @@
1
+ module JOSE
2
+ class JWK < Struct.new(:keys, :kty, :fields)
3
+
4
+ # Decode API
5
+
6
+ def self.from(object, modules = nil, key = nil)
7
+ case object
8
+ when JOSE::Map, Hash
9
+ return from_map(object, modules, key)
10
+ when String
11
+ return from_binary(object, modules, key)
12
+ when JOSE::JWK
13
+ return object
14
+ else
15
+ raise ArgumentError, "'object' must be a Hash, String, or JOSE::JWK"
16
+ end
17
+ end
18
+
19
+ def self.from_binary(object, modules = nil, key = nil)
20
+ if (modules.is_a?(String) or modules.is_a?(JOSE::JWK)) and key.nil?
21
+ key = modules
22
+ modules = {}
23
+ end
24
+ modules ||= {}
25
+ case object
26
+ when String
27
+ if key
28
+ plain_text, jwe = JOSE::JWE.block_decrypt(key, object)
29
+ return from_binary(plain_text, modules), jwe
30
+ else
31
+ return from_map(JOSE.decode(object), modules)
32
+ end
33
+ else
34
+ raise ArgumentError, "'object' must be a String"
35
+ end
36
+ end
37
+
38
+ def self.from_file(file, modules = nil, key = nil)
39
+ return from_binary(File.binread(file), modules, key)
40
+ end
41
+
42
+ def self.from_key(object, modules = {})
43
+ kty = modules[:kty] || JOSE::JWK::KTY
44
+ return JOSE::JWK.new(nil, *kty.from_key(object))
45
+ end
46
+
47
+ def self.from_map(object, modules = nil, key = nil)
48
+ if (modules.is_a?(String) or modules.is_a?(JOSE::JWK)) and key.nil?
49
+ key = modules
50
+ modules = {}
51
+ end
52
+ modules ||= {}
53
+ case object
54
+ when JOSE::Map, Hash
55
+ if key
56
+ plain_text, jwe = JOSE::JWE.block_decrypt(key, object)
57
+ return from_binary(plain_text, modules), jwe
58
+ else
59
+ return from_fields(JOSE::JWK.new(nil, nil, JOSE::Map.new(object)), modules)
60
+ end
61
+ else
62
+ raise ArgumentError, "'object' must be a String"
63
+ end
64
+ end
65
+
66
+ def self.from_pem(object, modules = nil, password = nil)
67
+ if modules.is_a?(String) and password.nil?
68
+ password = modules
69
+ modules = {}
70
+ end
71
+ modules ||= {}
72
+ kty = modules[:kty] || JOSE::JWK::PEM
73
+ return JOSE::JWK.new(nil, *kty.from_binary(object, password))
74
+ end
75
+
76
+ def self.from_pem_file(file, modules = nil, password = nil)
77
+ return from_pem(File.binread(file), modules, password)
78
+ end
79
+
80
+ def self.from_oct(object, modules = {})
81
+ kty = modules[:kty] || JOSE::JWK::KTY_oct
82
+ return JOSE::JWK.new(nil, *kty.from_oct(object))
83
+ end
84
+
85
+ def self.from_oct_file(file, modules = {})
86
+ return from_oct(File.binread(file), modules)
87
+ end
88
+
89
+ # Encode API
90
+
91
+ def self.to_binary(jwk, key = nil, jwe = nil)
92
+ return from(jwk).to_binary(key, jwe)
93
+ end
94
+
95
+ def to_binary(key = nil, jwe = nil)
96
+ if not key.nil?
97
+ jwe ||= kty.key_encryptor(fields, key)
98
+ end
99
+ if key and jwe
100
+ return to_map(key, jwe).compact
101
+ else
102
+ return JOSE.encode(to_map)
103
+ end
104
+ end
105
+
106
+ def self.to_file(jwk, file, key = nil, jwe = nil)
107
+ return from(jwk).to_file(file, key, jwe)
108
+ end
109
+
110
+ def to_file(file, key = nil, jwe = nil)
111
+ return File.binwrite(file, to_binary(key, jwe))
112
+ end
113
+
114
+ def self.to_key(jwk)
115
+ return from(jwk).to_key
116
+ end
117
+
118
+ def to_key
119
+ return kty.to_key
120
+ end
121
+
122
+ def self.to_map(jwk, key = nil, jwe = nil)
123
+ return from(jwk).to_map(key, jwe)
124
+ end
125
+
126
+ def to_map(key = nil, jwe = nil)
127
+ if not key.nil?
128
+ jwe ||= kty.key_encryptor(fields, key)
129
+ end
130
+ if key and jwe
131
+ return JOSE::JWE.block_encrypt(key, to_binary, jwe)
132
+ else
133
+ return kty.to_map(fields)
134
+ end
135
+ end
136
+
137
+ def self.to_oct(jwk)
138
+ return from(jwk).to_oct
139
+ end
140
+
141
+ def to_oct
142
+ return kty.to_oct
143
+ end
144
+
145
+ def self.to_pem(jwk, password = nil)
146
+ return from(jwk).to_pem(password)
147
+ end
148
+
149
+ def to_pem(password = nil)
150
+ return kty.to_pem(password)
151
+ end
152
+
153
+ def self.to_public(jwk)
154
+ return from(jwk).to_public
155
+ end
156
+
157
+ def to_public
158
+ return JOSE::JWK.from_map(to_public_map)
159
+ end
160
+
161
+ def self.to_public_key(jwk)
162
+ return from(jwk).to_public_key
163
+ end
164
+
165
+ def to_public_key
166
+ return to_public.to_key
167
+ end
168
+
169
+ def self.to_public_map(jwk)
170
+ return from(jwk).to_public_map
171
+ end
172
+
173
+ def to_public_map
174
+ return kty.to_public_map(fields)
175
+ end
176
+
177
+ def self.to_thumbprint_map(jwk)
178
+ return from(jwk).to_thumbprint_map
179
+ end
180
+
181
+ def to_thumbprint_map
182
+ return kty.to_thumbprint_map(fields)
183
+ end
184
+
185
+ # API
186
+
187
+ def self.block_decrypt(jwk, encrypted)
188
+ return from(jwk).block_decrypt(encrypted)
189
+ end
190
+
191
+ def block_decrypt(encrypted)
192
+ return JOSE::JWE.block_decrypt(self, encrypted)
193
+ end
194
+
195
+ def self.block_encrypt(jwk, plain_text, jwe = nil)
196
+ return from(jwk).block_encrypt(plain_text, jwe)
197
+ end
198
+
199
+ def block_encrypt(plain_text, jwe = nil)
200
+ jwe ||= kty.block_encryptor(fields, plain_text)
201
+ return JOSE::JWE.block_encrypt(self, plain_text, jwe)
202
+ end
203
+
204
+ def self.box_decrypt(jwk, encrypted)
205
+ return from(jwk).box_decrypt(encrypted)
206
+ end
207
+
208
+ def box_decrypt(encrypted)
209
+ return JOSE::JWE.block_decrypt(self, encrypted)
210
+ end
211
+
212
+ # Generates an ephemeral private key based on other public key curve.
213
+ def box_encrypt(plain_text, my_private_jwk = nil, jwe = nil)
214
+ generated_jwk = nil
215
+ other_public_jwk = self
216
+ if my_private_jwk.nil?
217
+ generated_jwk = my_private_jwk = other_public_jwk.generate_key
218
+ end
219
+ if not my_private_jwk.is_a?(JOSE::JWK)
220
+ my_private_jwk = JOSE::JWK.from(my_private_jwk)
221
+ end
222
+ if jwe.nil?
223
+ jwe = other_public_jwk.kty.block_encryptor(fields, plain_text)
224
+ end
225
+ if jwe.is_a?(Hash)
226
+ jwe = JOSE::Map.new(jwe)
227
+ end
228
+ if jwe.is_a?(JOSE::Map)
229
+ if jwe['apu'].nil?
230
+ jwe = jwe.put('apu', my_private_jwk.fields['kid'] || my_private_jwk.thumbprint)
231
+ end
232
+ if jwe['apv'].nil?
233
+ jwe = jwe.put('apv', other_public_jwk.fields['kid'] || other_public_jwk.thumbprint)
234
+ end
235
+ if jwe['epk'].nil?
236
+ jwe = jwe.put('epk', my_private_jwk.to_public_map)
237
+ end
238
+ end
239
+ if generated_jwk
240
+ return JOSE::JWE.block_encrypt([other_public_jwk, my_private_jwk], plain_text, jwe), generated_jwk
241
+ else
242
+ return JOSE::JWE.block_encrypt([other_public_jwk, my_private_jwk], plain_text, jwe)
243
+ end
244
+ end
245
+
246
+ def derive_key(*args)
247
+ return kty.derive_key(*args)
248
+ end
249
+
250
+ def self.generate_key(params)
251
+ if params.is_a?(Array) and (params.length == 2 or params.length == 3)
252
+ case params[0]
253
+ when :ec
254
+ return JOSE::JWK.new(nil, *JOSE::JWK::KTY_EC.generate_key(params))
255
+ when :oct
256
+ return JOSE::JWK.new(nil, *JOSE::JWK::KTY_oct.generate_key(params))
257
+ when :rsa
258
+ return JOSE::JWK.new(nil, *JOSE::JWK::KTY_RSA.generate_key(params))
259
+ else
260
+ raise ArgumentError, "invalid key generation params"
261
+ end
262
+ elsif params.is_a?(JOSE::JWK)
263
+ return params.generate_key
264
+ elsif params.respond_to?(:generate_key)
265
+ return JOSE::JWK.new(nil, *params.generate_key(JOSE::Map[]))
266
+ else
267
+ raise ArgumentError, "invalid key generation params"
268
+ end
269
+ end
270
+
271
+ def generate_key
272
+ return JOSE::JWK.new(nil, *kty.generate_key(fields))
273
+ end
274
+
275
+ def self.sign(jwk, plain_text, jws = nil, header = nil)
276
+ return from(jwk).sign(plain_text, jws, header)
277
+ end
278
+
279
+ def sign(plain_text, jws = nil, header = nil)
280
+ jws ||= kty.signer(fields, plain_text)
281
+ return JOSE::JWS.sign(self, plain_text, jws, header)
282
+ end
283
+
284
+ def self.verify(signed, jwk)
285
+ return from(jwk).verify(signed)
286
+ end
287
+
288
+ def verify(signed)
289
+ return JOSE::JWS.verify(self, signed)
290
+ end
291
+
292
+ def self.verify_strict(signed, allow, jwk)
293
+ return from(jwk).verify_strict(signed, allow)
294
+ end
295
+
296
+ def verify_strict(signed, allow)
297
+ return JOSE::JWS.verify_strict(self, allow, signed)
298
+ end
299
+
300
+ # See https://tools.ietf.org/html/rfc7638
301
+ def self.thumbprint(digest_type, jwk = nil)
302
+ if jwk.nil?
303
+ jwk = digest_type
304
+ digest_type = nil
305
+ end
306
+ return from(jwk).thumbprint(digest_type)
307
+ end
308
+
309
+ def thumbprint(digest_type = nil)
310
+ digest_type ||= 'SHA256'
311
+ thumbprint_binary = JOSE.encode(to_thumbprint_map)
312
+ return JOSE.urlsafe_encode64(OpenSSL::Digest.new(digest_type).digest(thumbprint_binary))
313
+ end
314
+
315
+ private
316
+
317
+ def self.from_fields(jwk, modules)
318
+ if jwk.fields.has_key?('keys')
319
+ keys = modules[:keys] || JOSE::JWK::Set
320
+ jwk.keys, jwk.fields = keys.from_map(jwk.fields)
321
+ return from_fields(jwk, modules)
322
+ elsif jwk.fields.has_key?('kty')
323
+ kty = modules[:kty] || case jwk.fields['kty']
324
+ when 'EC'
325
+ JOSE::JWK::KTY_EC
326
+ when 'oct'
327
+ JOSE::JWK::KTY_oct
328
+ when 'RSA'
329
+ JOSE::JWK::KTY_RSA
330
+ else
331
+ raise ArgumentError, "unknown 'kty': #{jwk.fields['kty'].inspect}"
332
+ end
333
+ jwk.kty, jwk.fields = kty.from_map(jwk.fields)
334
+ return from_fields(jwk, modules)
335
+ elsif jwk.keys.nil? and jwk.kty.nil?
336
+ raise ArgumentError, "missing required keys: 'keys' or 'kty'"
337
+ else
338
+ return jwk
339
+ end
340
+ end
341
+
342
+ end
343
+ end
344
+
345
+ require 'jose/jwk/pem'
346
+ require 'jose/jwk/set'
347
+ require 'jose/jwk/kty'
@@ -0,0 +1,34 @@
1
+ module JOSE::JWK::KTY
2
+
3
+ extend self
4
+
5
+ def from_key(object)
6
+ case object
7
+ when OpenSSL::PKey::EC
8
+ return JOSE::JWK::KTY_EC.from_key(object)
9
+ when OpenSSL::PKey::RSA
10
+ return JOSE::JWK::KTY_RSA.from_key(object)
11
+ else
12
+ raise ArgumentError, "'object' is not a recognized key type"
13
+ end
14
+ end
15
+
16
+ def key_encryptor(kty, fields, key)
17
+ if key.is_a?(String)
18
+ return JOSE::Map[
19
+ 'alg' => 'PBES2-HS256+A128KW',
20
+ 'cty' => 'jwk+json',
21
+ 'enc' => 'A128GCM',
22
+ 'p2c' => 4096,
23
+ 'p2s' => JOSE.urlsafe_encode64(SecureRandom.random_bytes(16))
24
+ ]
25
+ else
26
+ raise ArgumentError, "unhandled key type for key_encryptor"
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ require 'jose/jwk/kty_ec'
33
+ require 'jose/jwk/kty_oct'
34
+ require 'jose/jwk/kty_rsa'
@@ -0,0 +1,179 @@
1
+ class JOSE::JWK::KTY_EC < Struct.new(:key)
2
+
3
+ # JOSE::JWK callbacks
4
+
5
+ def self.from_map(fields)
6
+ if fields['kty'] == 'EC' and fields['crv'].is_a?(String) and fields['x'].is_a?(String) and fields['y'].is_a?(String)
7
+ crv = case fields['crv']
8
+ when 'P-256'
9
+ 'prime256v1'
10
+ when 'P-384'
11
+ 'secp384r1'
12
+ when 'P-521'
13
+ 'secp521r1'
14
+ else
15
+ raise ArgumentError, "invalid 'EC' JWK"
16
+ end
17
+ ec = OpenSSL::PKey::EC.new(crv)
18
+ x = JOSE.urlsafe_decode64(fields['x'])
19
+ y = JOSE.urlsafe_decode64(fields['y'])
20
+ ec.public_key = OpenSSL::PKey::EC::Point.new(
21
+ OpenSSL::PKey::EC::Group.new(crv),
22
+ OpenSSL::BN.new([0x04, x, y].pack('CA*A*'), 2)
23
+ )
24
+ if fields['d'].is_a?(String)
25
+ ec.private_key = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['d']), 2)
26
+ end
27
+ return JOSE::JWK::KTY_EC.new(ec), fields.except('kty', 'crv', 'd', 'x', 'y')
28
+ else
29
+ raise ArgumentError, "invalid 'EC' JWK"
30
+ end
31
+ end
32
+
33
+ def to_key
34
+ return key
35
+ end
36
+
37
+ def to_map(fields)
38
+ ec_point = key.public_key.to_bn.to_s(2)
39
+ ec_point_x, ec_point_y = case ec_point.bytesize
40
+ when 65
41
+ ec_point.unpack('xA32A32')
42
+ when 97
43
+ ec_point.unpack('xA48A48')
44
+ when 133
45
+ ec_point.unpack('xA66A66')
46
+ else
47
+ raise ArgumentError, "unhandled EC point size: #{ec_point.bytesize.inspect}"
48
+ end
49
+ crv = case key.group.curve_name
50
+ when 'prime256v1', 'secp256r1'
51
+ 'P-256'
52
+ when 'secp384r1'
53
+ 'P-384'
54
+ when 'secp521r1'
55
+ 'P-521'
56
+ else
57
+ raise ArgumentError, "unhandled EC curve name: #{key.group.curve_name.inspect}"
58
+ end
59
+ if key.private_key?
60
+ return fields.
61
+ put('crv', crv).
62
+ put('d', JOSE.urlsafe_encode64(key.private_key.to_s(2))).
63
+ put('kty', 'EC').
64
+ put('x', JOSE.urlsafe_encode64(ec_point_x)).
65
+ put('y', JOSE.urlsafe_encode64(ec_point_y))
66
+ else
67
+ return fields.
68
+ put('crv', crv).
69
+ put('kty', 'EC').
70
+ put('x', JOSE.urlsafe_encode64(ec_point_x)).
71
+ put('y', JOSE.urlsafe_encode64(ec_point_y))
72
+ end
73
+ end
74
+
75
+ def to_public_map(fields)
76
+ return to_map(fields).except('d')
77
+ end
78
+
79
+ def to_thumbprint_map(fields)
80
+ return to_public_map(fields).slice('crv', 'kty', 'x', 'y')
81
+ end
82
+
83
+ # JOSE::JWK::KTY callbacks
84
+
85
+ def block_encryptor(fields, plain_text)
86
+ return JOSE::Map[
87
+ 'alg' => 'ECDH-ES',
88
+ 'enc' => 'A128GCM'
89
+ ]
90
+ end
91
+
92
+ def derive_key(my_private_key)
93
+ if my_private_key.is_a?(JOSE::JWK)
94
+ my_private_key = my_private_key.to_key
95
+ end
96
+ if my_private_key.private_key?
97
+ return my_private_key.dh_compute_key(key.public_key)
98
+ else
99
+ raise ArgumentError, "derive_key requires a private key as an argument"
100
+ end
101
+ end
102
+
103
+ def self.generate_key(curve_name)
104
+ if curve_name.is_a?(Array) and curve_name.length == 2 and curve_name[0] == :ec
105
+ curve_name = curve_name[1]
106
+ end
107
+ if curve_name.is_a?(String)
108
+ return from_key(OpenSSL::PKey::EC.new(curve_name).generate_key)
109
+ else
110
+ raise ArgumentError, "'curve_name' must be a String"
111
+ end
112
+ end
113
+
114
+ def generate_key(fields)
115
+ kty, other_fields = JOSE::JWK::KTY_EC.generate_key([:ec, key.group.curve_name])
116
+ return kty, fields.delete('kid').merge(other_fields)
117
+ end
118
+
119
+ def key_encryptor(fields, key)
120
+ return JOSE::JWK::KTY.key_encryptor(self, fields, key)
121
+ end
122
+
123
+ def sign(message, digest_type)
124
+ asn1_signature = key.dsa_sign_asn1(digest_type.digest(message))
125
+ asn1_sequence = OpenSSL::ASN1.decode(asn1_signature)
126
+ rbin = asn1_sequence.value[0].value.to_s(2)
127
+ sbin = asn1_sequence.value[1].value.to_s(2)
128
+ size = [rbin.bytesize, sbin.bytesize].max
129
+ rpad = pad(rbin, size)
130
+ spad = pad(sbin, size)
131
+ return rpad.concat(spad)
132
+ end
133
+
134
+ def signer(fields = nil, plain_text = nil)
135
+ if key.private_key?
136
+ return JOSE::Map['alg' => 'ES256']
137
+ else
138
+ raise ArgumentError, "signing not supported for public keys"
139
+ end
140
+ end
141
+
142
+ def verify(message, digest_type, signature)
143
+ n = signature.bytesize.div(2)
144
+ r = OpenSSL::BN.new(signature[0..(n-1)], 2)
145
+ s = OpenSSL::BN.new(signature[n..-1], 2)
146
+ asn1_sequence = OpenSSL::ASN1::Sequence.new([
147
+ OpenSSL::ASN1::Integer.new(r),
148
+ OpenSSL::ASN1::Integer.new(s)
149
+ ])
150
+ asn1_signature = asn1_sequence.to_der
151
+ return key.dsa_verify_asn1(digest_type.digest(message), asn1_signature)
152
+ end
153
+
154
+ # API functions
155
+
156
+ def self.from_key(key)
157
+ case key
158
+ when OpenSSL::PKey::EC
159
+ return JOSE::JWK::KTY_EC.new(key), JOSE::Map[]
160
+ else
161
+ raise ArgumentError, "'key' must be a OpenSSL::PKey::EC"
162
+ end
163
+ end
164
+
165
+ def to_pem(password = nil)
166
+ return JOSE::JWK::PEM.to_binary(key, password)
167
+ end
168
+
169
+ private
170
+
171
+ def pad(bin, size)
172
+ if bin.bytesize == size
173
+ return bin
174
+ else
175
+ return pad([0x00].pack('C').concat(bin), size)
176
+ end
177
+ end
178
+
179
+ end