krypt 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/LICENSE +20 -0
  2. data/README.md +82 -0
  3. data/lib/krypt.rb +49 -0
  4. data/lib/krypt/asn1.rb +3 -0
  5. data/lib/krypt/asn1/common.rb +96 -0
  6. data/lib/krypt/asn1/template.rb +257 -0
  7. data/lib/krypt/codec.rb +57 -0
  8. data/lib/krypt/codec/base64.rb +140 -0
  9. data/lib/krypt/codec/base_codec.rb +36 -0
  10. data/lib/krypt/codec/hex.rb +122 -0
  11. data/lib/krypt/digest.rb +112 -0
  12. data/lib/krypt/hmac.rb +69 -0
  13. data/lib/krypt/pkcs5.rb +1 -0
  14. data/lib/krypt/pkcs5/pbkdf2.rb +41 -0
  15. data/lib/krypt/provider.rb +35 -0
  16. data/lib/krypt/x509.rb +3 -0
  17. data/lib/krypt/x509/certificate.rb +36 -0
  18. data/lib/krypt/x509/common.rb +41 -0
  19. data/lib/krypt/x509/crl.rb +33 -0
  20. data/lib/krypt_missing.rb +32 -0
  21. data/spec/krypt-core/MEMO.txt +85 -0
  22. data/spec/krypt-core/asn1/asn1_bit_string_spec.rb +475 -0
  23. data/spec/krypt-core/asn1/asn1_boolean_spec.rb +392 -0
  24. data/spec/krypt-core/asn1/asn1_constants_spec.rb +71 -0
  25. data/spec/krypt-core/asn1/asn1_data_spec.rb +1153 -0
  26. data/spec/krypt-core/asn1/asn1_end_of_contents_spec.rb +133 -0
  27. data/spec/krypt-core/asn1/asn1_enumerated_spec.rb +458 -0
  28. data/spec/krypt-core/asn1/asn1_generalized_time_spec.rb +492 -0
  29. data/spec/krypt-core/asn1/asn1_integer_spec.rb +557 -0
  30. data/spec/krypt-core/asn1/asn1_null_spec.rb +360 -0
  31. data/spec/krypt-core/asn1/asn1_object_id_spec.rb +495 -0
  32. data/spec/krypt-core/asn1/asn1_octet_string_spec.rb +456 -0
  33. data/spec/krypt-core/asn1/asn1_parser_spec.rb +503 -0
  34. data/spec/krypt-core/asn1/asn1_pem_spec.rb +282 -0
  35. data/spec/krypt-core/asn1/asn1_sequence_spec.rb +637 -0
  36. data/spec/krypt-core/asn1/asn1_set_spec.rb +795 -0
  37. data/spec/krypt-core/asn1/asn1_utc_time_spec.rb +495 -0
  38. data/spec/krypt-core/asn1/asn1_utf8_string_spec.rb +404 -0
  39. data/spec/krypt-core/asn1/resources.rb +53 -0
  40. data/spec/krypt-core/base64/base64_spec.rb +97 -0
  41. data/spec/krypt-core/digest/digest_spec.rb +707 -0
  42. data/spec/krypt-core/hex/hex_spec.rb +102 -0
  43. data/spec/krypt-core/pem/pem_decode_spec.rb +235 -0
  44. data/spec/krypt-core/resources.rb +1 -0
  45. data/spec/krypt-core/template/template_choice_parse_spec.rb +289 -0
  46. data/spec/krypt-core/template/template_dsl_spec.rb +351 -0
  47. data/spec/krypt-core/template/template_seq_of_parse_spec.rb +64 -0
  48. data/spec/krypt-core/template/template_seq_parse_spec.rb +1241 -0
  49. data/spec/krypt/codec/base64_decoder_spec.rb +94 -0
  50. data/spec/krypt/codec/base64_encoder_spec.rb +94 -0
  51. data/spec/krypt/codec/base64_mixed_spec.rb +16 -0
  52. data/spec/krypt/codec/hex_decoder_spec.rb +94 -0
  53. data/spec/krypt/codec/hex_encoder_spec.rb +94 -0
  54. data/spec/krypt/codec/hex_mixed_spec.rb +17 -0
  55. data/spec/krypt/codec/identity_shared.rb +119 -0
  56. data/spec/krypt/hmac/hmac_spec.rb +311 -0
  57. data/spec/krypt/pkcs5/pbkdf2_spec.rb +79 -0
  58. data/spec/krypt/provider/provider_spec.rb +83 -0
  59. data/spec/res/ca-bundle.crt +11758 -0
  60. data/spec/res/certificate.cer +0 -0
  61. data/spec/res/certificate.pem +20 -0
  62. data/spec/res/multiple_certs.pem +60 -0
  63. data/spec/resources.rb +66 -0
  64. data/test/helper.rb +8 -0
  65. data/test/res/certificate.cer +0 -0
  66. data/test/resources.rb +48 -0
  67. data/test/scratch.rb +28 -0
  68. data/test/test_krypt_asn1.rb +119 -0
  69. data/test/test_krypt_parser.rb +331 -0
  70. metadata +134 -0
@@ -0,0 +1,503 @@
1
+ # encoding: US-ASCII
2
+
3
+ require 'rspec'
4
+ require 'krypt'
5
+ require_relative '../resources'
6
+ require 'stringio'
7
+
8
+ def do_and_close(io)
9
+ yield io
10
+ io.close
11
+ end
12
+
13
+ describe Krypt::ASN1::Parser do
14
+
15
+ it "can be instantiated with default constructor" do
16
+ Krypt::ASN1::Parser.new.should be_an_instance_of Krypt::ASN1::Parser
17
+ end
18
+
19
+ it "takes no arguments in its constructor" do
20
+ lambda { Krypt::ASN1::Parser.new(Object.new) }.should raise_error(ArgumentError)
21
+ end
22
+
23
+ it "should be reusable for several IOs" do
24
+ parser = Krypt::ASN1::Parser.new
25
+ io = Resources.certificate_io
26
+ do_and_close(io) { |io| parser.next(io).should_not be_nil }
27
+ io = Resources.certificate_io
28
+ do_and_close(io) { |io| parser.next(io).should_not be_nil }
29
+ end
30
+
31
+ end
32
+
33
+ describe Krypt::ASN1::Parser, "#next" do
34
+
35
+ subject { Krypt::ASN1::Parser.new }
36
+
37
+ it "returns a Header when called on an IO representing an ASN.1" do
38
+ io = Resources.certificate_io
39
+ do_and_close(io) { |io| parse_next io }
40
+ parse_next(StringIO.new(Resources.certificate))
41
+ end
42
+
43
+ it "raises an ArgumentError when called on a String. A String does not
44
+ provide internal state that would be needed for parsing." do
45
+ lambda { parse_next(Resources.certificate) }.should raise_error(ArgumentError)
46
+ end
47
+
48
+ def parse_next(io)
49
+ subject.next(io).should be_an_instance_of Krypt::ASN1::Header
50
+ end
51
+
52
+ it "raises ArgumentError if anything other than an IO is passed. Rejection
53
+ is handled depending on the presence of a #read method." do
54
+ lambda { subject.next(:sym) }.should raise_error(ArgumentError)
55
+
56
+ c = Class.new do
57
+ def initialize
58
+ @io = Resources.certificate_io
59
+ end
60
+
61
+ def read(len=nil, buf=nil)
62
+ @io.read(len, buf)
63
+ end
64
+ end
65
+
66
+ parse_next(c.new)
67
+ end
68
+
69
+ it "does not close the underlying IO after reading it" do
70
+ io = Resources.certificate_io
71
+ subject.next(io).skip_value
72
+ subject.next(io).should be_nil
73
+ io.closed?.should be_false
74
+ io.close
75
+ io.closed?.should be_true
76
+ end
77
+
78
+ it "reads nested Headers when called subsequently for constructed values" do
79
+ num_headers = 0
80
+ io = Resources.certificate_io
81
+ while header = subject.next(io)
82
+ num_headers += 1
83
+ next if header.constructed?
84
+ header.skip_value #need to consume the values
85
+ end
86
+ num_headers.should > 1
87
+ io.close
88
+ end
89
+
90
+ it "also reads singular, non-constructed values" do
91
+ io = Resources.bytes_to_io( %w{02 01 01} )
92
+ header = subject.next(io)
93
+ header.tag.should == 2
94
+ header.size.should == 1
95
+ header.header_size.should == 2
96
+ header.tag_class.should == :UNIVERSAL
97
+ header.constructed?.should be_false
98
+ header.infinite?.should be_false
99
+ header.value.should == "\x01"
100
+ end
101
+
102
+ it "yields the original contents for the header and the value of the
103
+ initial sequence" do
104
+ cert = Resources.certificate
105
+ io = StringIO.new cert
106
+ header = subject.next io
107
+ value = header.value
108
+ (header.header_size + value.size).should == cert.size
109
+ ("" << header.bytes << value).should == cert
110
+ end
111
+
112
+ it "yields the original contents for the nested headers and their values" do
113
+ cert = Resources.certificate
114
+ io = StringIO.new cert
115
+ out = ""
116
+ while header = subject.next(io)
117
+ out << header.bytes
118
+ next if header.constructed?
119
+ val = header.value
120
+ out << val unless val == nil
121
+ end
122
+ out.should == cert
123
+ end
124
+
125
+ end
126
+
127
+ describe Krypt::ASN1::Header do
128
+
129
+ it "cannot be instantiated" do
130
+ lambda { Krypt::ASN1::Header.new }.should raise_error
131
+ end
132
+
133
+ end
134
+
135
+ describe Krypt::ASN1::Header, "#tag" do
136
+
137
+ subject { Krypt::ASN1::Parser.new }
138
+
139
+ it "yields the tag of an ASN1 value, both for simple and complex tags" do
140
+ simple = Resources.bytes_to_io( %w{05 00} )
141
+ header = subject.next(simple)
142
+ header.tag.should == 5
143
+ end
144
+
145
+ it "yields the tag for complex tags with a single octet" do
146
+ complex_single_octet = %w{df 2a 01}
147
+ complex_single_octet_v = Resources.bytes_to_io(Array.new(complex_single_octet) << '00')
148
+ header = subject.next(complex_single_octet_v)
149
+ header.tag.should == 42
150
+ end
151
+
152
+ it "yields the tag for complext tags with multiple octets" do
153
+ complex_two_octets = %w{5f 82 2c 01}
154
+ complex_two_octets_v = Resources.bytes_to_io(Array.new(complex_two_octets) << '00')
155
+ header = subject.next(complex_two_octets_v)
156
+ header.tag.should == 300
157
+ end
158
+
159
+ end
160
+
161
+ describe Krypt::ASN1::Header, "#tag_class" do
162
+
163
+ it "recognizes UNIVERSAL, CONTEXT_SPEICIFIC, APPLICATION and PRIVATE" do
164
+ subject = Krypt::ASN1::Parser.new
165
+ universal = Resources.bytes_to_io( %w{05 00} )
166
+ header = subject.next(universal)
167
+ header.tag_class.should == :UNIVERSAL
168
+ context_specific = Resources.bytes_to_io( %w{81 00} )
169
+ header = subject.next(context_specific)
170
+ header.tag_class.should == :CONTEXT_SPECIFIC
171
+ private_tc = Resources.bytes_to_io( %w{df 2a 01} )
172
+ header = subject.next(private_tc)
173
+ header.tag_class.should == :PRIVATE
174
+ application = Resources.bytes_to_io( %w{5f 82 2c 01} )
175
+ header = subject.next(application)
176
+ header.tag_class.should == :APPLICATION
177
+ end
178
+
179
+ end
180
+
181
+ describe Krypt::ASN1::Header, "#constructed?" do
182
+
183
+ subject { Krypt::ASN1::Parser.new }
184
+
185
+ it "returns false for primitive values" do
186
+ evaluate(%w{05 00}, false)
187
+ evaluate(%w{81 00}, false)
188
+ evaluate(%w{df 2a 01}, false)
189
+ evaluate(%w{5f 82 2c 01}, false)
190
+ end
191
+
192
+ it "returns true for constructed values" do
193
+ evaluate(%w{30 03 02 01 01}, true)
194
+ evaluate(%w{31 03 02 01 01}, true)
195
+ evaluate(%w{30 80 02 01 01 00 00}, true)
196
+ evaluate(%w{24 80 04 01 01 00 00}, true)
197
+ end
198
+
199
+ def evaluate(bytes, expectation)
200
+ io = Resources.bytes_to_io bytes
201
+ header = subject.next io
202
+ header.constructed?.should == expectation
203
+ end
204
+
205
+ end
206
+
207
+ describe Krypt::ASN1::Header, "#infinite?" do
208
+
209
+ subject { Krypt::ASN1::Parser.new }
210
+
211
+ it "returns false for definite length values" do
212
+ evaluate(%w{05 00}, false)
213
+ evaluate(%w{81 00}, false)
214
+ evaluate(%w{df 2a 01}, false)
215
+ evaluate(%w{5f 82 2c 01}, false)
216
+ evaluate(%w{30 03 02 01 01}, false)
217
+ evaluate(%w{31 03 02 01 01}, false)
218
+ end
219
+
220
+ it "returns true for infinite length values" do
221
+ evaluate(%w{30 80 02 01 01 00 00}, true)
222
+ evaluate(%w{31 80 02 01 01 00 00}, true)
223
+ evaluate(%w{24 80 04 01 01 00 00}, true)
224
+ end
225
+
226
+ it "raises an error for infinite length primitive values" do
227
+ lambda { evaluate(%w{04 80 04 01 01 00 00}, true) }.should raise_error(Krypt::ASN1::ParseError)
228
+ end
229
+
230
+ def evaluate(bytes, expectation)
231
+ io = Resources.bytes_to_io bytes
232
+ header = subject.next io
233
+ header.infinite?.should == expectation
234
+ end
235
+
236
+ end
237
+
238
+ describe Krypt::ASN1::Header, "#size" do
239
+
240
+ subject { Krypt::ASN1::Parser.new }
241
+
242
+ it "returns the size of the value for single octet lengths" do
243
+ simple = Resources.bytes_to_io( %w{02 01 01} )
244
+ header = subject.next(simple)
245
+ header.size.should == 1
246
+ end
247
+
248
+ it "returns the size of the value for multiple octet lengths" do
249
+ complex = Resources.bytes_to_io( %w{04 82 03 e8} << ('a' * 1000))
250
+ header = subject.next complex
251
+ header.size.should == 1000
252
+ end
253
+
254
+ it "returns 0 for missing values" do
255
+ null = %w{05 00}
256
+ eoc = %w{00 00}
257
+ subject.next(Resources.bytes_to_io(null)).size.should == 0
258
+ subject.next(Resources.bytes_to_io(eoc)).size.should == 0
259
+ end
260
+
261
+ it "returns 0 for infinite length values" do
262
+ inf = %w{30 80 02 01 01 00 00}
263
+ subject.next(Resources.bytes_to_io(inf)).size.should == 0
264
+ end
265
+
266
+ end
267
+
268
+ describe Krypt::ASN1::Header, "#header_size" do
269
+
270
+ subject { Krypt::ASN1::Parser.new }
271
+
272
+ it "returns the size of the sum of tag plus length encoding
273
+ for simple tags" do
274
+ simple = Resources.bytes_to_io( %w{05 00} )
275
+ header = subject.next(simple)
276
+ header.header_size.should == 2
277
+ end
278
+
279
+ it "returns the size of the sum of tag plus length encoding
280
+ for complex tags with a single octet" do
281
+ complex_single_octet = %w{df 2a 01}
282
+ complex_single_octet_v = Resources.bytes_to_io(Array.new(complex_single_octet) << '00')
283
+ header = subject.next(complex_single_octet_v)
284
+ header.header_size.should == 3
285
+ end
286
+
287
+ it "returns the size of the sum of tag plus length encoding
288
+ for complex tags with multiple octets" do
289
+ complex_two_octets = %w{5f 82 2c 01}
290
+ complex_two_octets_v = Resources.bytes_to_io(Array.new(complex_two_octets) << '00')
291
+ header = subject.next(complex_two_octets_v)
292
+ header.header_size.should == 4
293
+ end
294
+
295
+ it "returns the size of the header for multiple octet lengths" do
296
+ complex = Resources.bytes_to_io( %w{04 82 03 e8} << ('a' * 1000))
297
+ header = subject.next complex
298
+ header.header_size.should == 4
299
+ end
300
+
301
+ end
302
+
303
+ describe Krypt::ASN1::Header, "#skip_value" do
304
+
305
+ it "skips to the end of the file when asked to skip the value of a
306
+ starting constructed value" do
307
+ skip_value(StringIO.new(Resources.certificate))
308
+ skip_value Resources.certificate_io
309
+ end
310
+
311
+ def skip_value(io)
312
+ parser = Krypt::ASN1::Parser.new
313
+ header = parser.next(io)
314
+ header.skip_value
315
+ parser.next(io).should be_nil
316
+ end
317
+
318
+ end
319
+
320
+ describe Krypt::ASN1::Header, "#value" do
321
+
322
+ subject { Krypt::ASN1::Parser.new }
323
+
324
+ it "caches the value of a header once it was read" do
325
+ io = Resources.certificate_io
326
+ begin
327
+ header = subject.next(io)
328
+ header.value.should == header.value
329
+ ensure
330
+ io.close
331
+ end
332
+ end
333
+
334
+ it "returns nil for missing values" do
335
+ null = %w{05 00}
336
+ eoc = %w{00 00}
337
+ subject.next(Resources.bytes_to_io(null)).value.should be_nil
338
+ subject.next(Resources.bytes_to_io(eoc)).value.should be_nil
339
+ end
340
+
341
+ it "has Encoding::BINARY" do
342
+ io = Resources.certificate_io
343
+ begin
344
+ subject.next(io).value.encoding.should == Encoding::BINARY
345
+ ensure
346
+ io.close
347
+ end
348
+ end
349
+
350
+ end
351
+
352
+ describe Krypt::ASN1::Header, "#value_io" do
353
+
354
+ subject { Krypt::ASN1::Parser.new }
355
+
356
+ it "returns an IO that reads the entire value of a definite sequence" do
357
+ cert = Resources.certificate_io
358
+ header = subject.next cert
359
+ (header.bytes << header.value_io.read).should == Resources.certificate
360
+ cert.close
361
+ end
362
+
363
+ it "returns an IO whose content is in Encoding::BINARY" do
364
+ cert = Resources.certificate_io
365
+ header = subject.next cert
366
+ header.value_io.read.encoding.should == Encoding::BINARY
367
+ cert.close
368
+ end
369
+
370
+ it "reads the values excluding the headers for an infinite length primitive
371
+ value by default" do
372
+ io = Resources.bytes_to_io( %w{24 80 04 01 01 04 01 02 00 00} )
373
+ expected = [%w{01 02}.join('')].pack('H*')
374
+ header = subject.next io
375
+ header.value_io.read.should == expected
376
+ end
377
+
378
+ it "reads the values including the headers for an infinite length primitive
379
+ value when passed false as a parameter" do
380
+ io = Resources.bytes_to_io( %w{24 80 04 01 01 04 01 02 00 00} )
381
+ expected = [%w{04 01 01 04 01 02 00 00}.join('')].pack('H*')
382
+ header = subject.next io
383
+ header.value_io(false).read.should == expected
384
+ end
385
+
386
+ it "reads the values excluding the headers for an infinite length sequence
387
+ by default" do
388
+ io = Resources.bytes_to_io( %w{30 80 04 01 01 04 01 02 00 00} )
389
+ expected = [%w{01 02}.join('')].pack('H*')
390
+ header = subject.next io
391
+ header.value_io.read.should == expected
392
+ end
393
+
394
+ it "reads the values including the headers for an infinite length sequence
395
+ when passed false as a parameter" do
396
+ io = Resources.bytes_to_io( %w{30 80 04 01 01 04 01 02 00 00} )
397
+ expected = [%w{04 01 01 04 01 02 00 00}.join('')].pack('H*')
398
+ header = subject.next io
399
+ header.value_io(false).read.should == expected
400
+ end
401
+
402
+ it "caches an IO if requested for a header more than once" do
403
+ io = Resources.certificate_io
404
+ header = subject.next io
405
+ header.value_io
406
+ lambda { header.value_io }.should_not raise_error
407
+ io.close
408
+ end
409
+
410
+ it "raises an error if the value of a header is requested after requesting
411
+ an IO" do
412
+ io = Resources.certificate_io
413
+ header = subject.next io
414
+ header.value_io
415
+ lambda { header.value }.should raise_error(Krypt::ASN1::ParseError)
416
+ io.close
417
+ end
418
+
419
+ it "raises an error if an IO after reading the value of the header" do
420
+ io = Resources.certificate_io
421
+ header = subject.next io
422
+ header.value
423
+ lambda { header.value_io }.should raise_error(Krypt::ASN1::ParseError)
424
+ io.close
425
+ end
426
+
427
+ it "is stateful wrt to the amount of data already read" do
428
+ io = Resources.certificate_io
429
+ header = subject.next io
430
+ header.value_io.read.should_not == ""
431
+ header.value_io.read.should == ""
432
+ io.close
433
+ end
434
+
435
+ it "returns an 'empty' IO for missing values" do
436
+ null = %w{05 00}
437
+ eoc = %w{00 00}
438
+ subject.next(Resources.bytes_to_io(null)).value_io.read.should == ""
439
+ subject.next(Resources.bytes_to_io(eoc)).value_io.read.should == ""
440
+ end
441
+
442
+ end
443
+
444
+ describe Krypt::ASN1::Header, "#bytes" do
445
+
446
+ subject { Krypt::ASN1::Parser.new }
447
+
448
+ it "returns the encoding of a parsed header" do
449
+ complex_two_octets = %w{5f 82 2c 01}
450
+ complex_two_octets_v = Resources.bytes_to_io(Array.new(complex_two_octets) << '00')
451
+ header = subject.next(complex_two_octets_v)
452
+ header.bytes.should == Resources.bytes(complex_two_octets)
453
+ end
454
+
455
+ it "returns the encoding of a simple tag" do
456
+ raw = %w{05 00}
457
+ simple = Resources.bytes_to_io(raw)
458
+ header = subject.next(simple)
459
+ header.bytes.should == Resources.bytes(raw)
460
+ end
461
+
462
+ it "returns the encoding for complex tags with a single octet" do
463
+ complex_single_octet = %w{df 2a 01}
464
+ complex_single_octet_v = Resources.bytes_to_io(Array.new(complex_single_octet) << '00')
465
+ header = subject.next(complex_single_octet_v)
466
+ header.bytes.should == Resources.bytes(complex_single_octet)
467
+ end
468
+
469
+ it "returns the encoding for complex tags with multiple octets" do
470
+ complex_two_octets = %w{5f 82 2c 01}
471
+ complex_two_octets_v = Resources.bytes_to_io(Array.new(complex_two_octets) << '00')
472
+ header = subject.next(complex_two_octets_v)
473
+ header.bytes.should == Resources.bytes(complex_two_octets)
474
+ end
475
+
476
+ it "returns the encoding for multiple octet lengths" do
477
+ raw = %w{04 82 03 e8}
478
+ complex = Resources.bytes_to_io( Array.new(raw) << ('a' * 1000))
479
+ header = subject.next complex
480
+ header.bytes.should == Resources.bytes(raw)
481
+ end
482
+
483
+ it "has Encoding::BINARY" do
484
+ raw = %w{05 00}
485
+ io = Resources.bytes_to_io(raw)
486
+ subject.next(io).bytes.encoding.should == Encoding::BINARY
487
+ end
488
+
489
+ end
490
+
491
+ describe Krypt::ASN1::Header, "#encode_to" do
492
+
493
+ it "encodes the header to an IO" do
494
+ null = %w{05 00}
495
+ io = Resources.bytes_to_io(null)
496
+ header = Krypt::ASN1::Parser.new.next io
497
+ out = StringIO.new
498
+ header.encode_to(out)
499
+ out.string.should == header.bytes
500
+ end
501
+
502
+ end
503
+