krypt 0.0.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.
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,495 @@
1
+ # encoding: US-ASCII
2
+
3
+ require 'rspec'
4
+ require 'krypt'
5
+ require 'openssl'
6
+ require_relative './resources'
7
+
8
+ describe Krypt::ASN1::UTCTime do
9
+ include Krypt::ASN1::Resources
10
+
11
+ let(:mod) { Krypt::ASN1 }
12
+ let(:klass) { mod::UTCTime }
13
+ let(:decoder) { mod }
14
+ let(:asn1error) { mod::ASN1Error }
15
+
16
+ # For test against OpenSSL
17
+ #
18
+ #let(:mod) { OpenSSL::ASN1 }
19
+ #
20
+ # OpenSSL stub for signature mismatch
21
+ class OpenSSL::ASN1::UTCTime
22
+ class << self
23
+ alias old_new new
24
+ def new(*args)
25
+ if args.size > 1
26
+ args = [args[0], args[1], :IMPLICIT, args[2]]
27
+ end
28
+ old_new(*args)
29
+ end
30
+ end
31
+ end
32
+
33
+ describe '#new' do
34
+ context 'gets value for construct' do
35
+ subject { klass.new(value) }
36
+
37
+ context 'accepts Time' do
38
+ let(:value) { Time.now }
39
+ its(:tag) { should == Krypt::ASN1::UTC_TIME }
40
+ its(:tag_class) { should == :UNIVERSAL }
41
+ its(:value) { should == value }
42
+ its(:infinite_length) { should == false }
43
+ end
44
+
45
+ context 'accepts Numeric' do
46
+ let(:value) { 0 + Time.now.to_i }
47
+ its(:tag) { should == Krypt::ASN1::UTC_TIME }
48
+ its(:tag_class) { should == :UNIVERSAL }
49
+ its(:value) { should == value }
50
+ its(:infinite_length) { should == false }
51
+ end
52
+
53
+ context 'accepts String' do
54
+ let(:value) { '' + Time.now.to_i.to_s }
55
+ its(:tag) { should == Krypt::ASN1::UTC_TIME }
56
+ its(:tag_class) { should == :UNIVERSAL }
57
+ its(:value) { should == value }
58
+ its(:infinite_length) { should == false }
59
+ end
60
+
61
+ context 'accepts 0' do
62
+ let(:value) { 0 }
63
+ its(:value) { should == value }
64
+ end
65
+ end
66
+
67
+ context 'gets explicit tag number as the 2nd argument' do
68
+ subject { klass.new(Time.now, tag, :PRIVATE) }
69
+
70
+ context 'accepts default tag' do
71
+ let(:tag) { Krypt::ASN1::UTC_TIME }
72
+ its(:tag) { should == tag }
73
+ end
74
+
75
+ context 'accepts custom tag' do
76
+ let(:tag) { 14 }
77
+ its(:tag) { should == tag }
78
+ end
79
+ end
80
+
81
+ context 'gets tag class symbol as the 3rd argument' do
82
+ subject { klass.new(Time.now, Krypt::ASN1::UTC_TIME, tag_class) }
83
+
84
+ context 'accepts :UNIVERSAL' do
85
+ let(:tag_class) { :UNIVERSAL }
86
+ its(:tag_class) { should == tag_class }
87
+ end
88
+
89
+ context 'accepts :APPLICATION' do
90
+ let(:tag_class) { :APPLICATION }
91
+ its(:tag_class) { should == tag_class }
92
+ end
93
+
94
+ context 'accepts :CONTEXT_SPECIFIC' do
95
+ let(:tag_class) { :CONTEXT_SPECIFIC }
96
+ its(:tag_class) { should == tag_class }
97
+ end
98
+
99
+ context 'accepts :PRIVATE' do
100
+ let(:tag_class) { :PRIVATE }
101
+ its(:tag_class) { should == tag_class }
102
+ end
103
+
104
+ context 'accepts :IMPLICIT' do
105
+ let(:tag_class) { :IMPLICIT }
106
+ its(:tag_class) { should == tag_class }
107
+ end
108
+
109
+ context 'accepts :EXPLICIT' do
110
+ let(:tag_class) { :EXPLICIT }
111
+ its(:tag_class) { should == tag_class }
112
+ end
113
+ end
114
+
115
+ context 'when the 2nd argument is given but 3rd argument is omitted' do
116
+ subject { klass.new(Time.now, Krypt::ASN1::UTC_TIME) }
117
+ its(:tag_class) { should == :CONTEXT_SPECIFIC }
118
+ end
119
+ end
120
+
121
+ describe 'accessors' do
122
+ describe '#value' do
123
+ subject { o = klass.new(nil); o.value = value; o }
124
+
125
+ context 'accepts Time' do
126
+ let(:value) { Time.now }
127
+ its(:tag) { should == Krypt::ASN1::UTC_TIME }
128
+ its(:tag_class) { should == :UNIVERSAL }
129
+ its(:value) { should == value }
130
+ its(:infinite_length) { should == false }
131
+ end
132
+
133
+ context 'accepts Numeric' do
134
+ let(:value) { 0 + Time.now.to_i }
135
+ its(:tag) { should == Krypt::ASN1::UTC_TIME }
136
+ its(:tag_class) { should == :UNIVERSAL }
137
+ its(:value) { should == value }
138
+ its(:infinite_length) { should == false }
139
+ end
140
+
141
+ context 'accepts String' do
142
+ let(:value) { '' + Time.now.to_i.to_s }
143
+ its(:tag) { should == Krypt::ASN1::UTC_TIME }
144
+ its(:tag_class) { should == :UNIVERSAL }
145
+ its(:value) { should == value }
146
+ its(:infinite_length) { should == false }
147
+ end
148
+
149
+ context 'accepts 0' do
150
+ let(:value) { 0 }
151
+ its(:value) { should == value }
152
+ end
153
+ end
154
+
155
+ describe '#tag' do
156
+ subject { o = klass.new(nil); o.tag = tag; o }
157
+
158
+ context 'accepts default tag' do
159
+ let(:tag) { Krypt::ASN1::UTC_TIME }
160
+ its(:tag) { should == tag }
161
+ end
162
+
163
+ context 'accepts custom tag' do
164
+ let(:tag) { 14 }
165
+ its(:tag) { should == tag }
166
+ end
167
+ end
168
+
169
+ describe '#tag_class' do
170
+ subject { o = klass.new(nil); o.tag_class = tag_class; o }
171
+
172
+ context 'accepts :UNIVERSAL' do
173
+ let(:tag_class) { :UNIVERSAL }
174
+ its(:tag_class) { should == tag_class }
175
+ end
176
+
177
+ context 'accepts :APPLICATION' do
178
+ let(:tag_class) { :APPLICATION }
179
+ its(:tag_class) { should == tag_class }
180
+ end
181
+
182
+ context 'accepts :CONTEXT_SPECIFIC' do
183
+ let(:tag_class) { :CONTEXT_SPECIFIC }
184
+ its(:tag_class) { should == tag_class }
185
+ end
186
+
187
+ context 'accepts :PRIVATE' do
188
+ let(:tag_class) { :PRIVATE }
189
+ its(:tag_class) { should == tag_class }
190
+ end
191
+
192
+ context 'accepts :IMPLICIT' do
193
+ let(:tag_class) { :IMPLICIT }
194
+ its(:tag_class) { should == tag_class }
195
+ end
196
+
197
+ context 'accepts :EXPLICIT' do
198
+ let(:tag_class) { :EXPLICIT }
199
+ its(:tag_class) { should == tag_class }
200
+ end
201
+ end
202
+ end
203
+
204
+ describe '#to_der' do
205
+ context 'encodes a given value' do
206
+ subject { klass.new(value).to_der }
207
+
208
+ context 'Time' do
209
+ let(:value) { Time.utc(2012, 1, 24, 0, 0, 0) }
210
+ it { should == "\x17\x0D120124000000Z" }
211
+ end
212
+
213
+ context 'Numeric' do
214
+ let(:value) { Time.utc(2012, 1, 24, 0, 0, 0).to_i }
215
+ it { should == "\x17\x0D120124000000Z" }
216
+ end
217
+
218
+ context 'String' do
219
+ let(:value) { Time.utc(2012, 1, 24, 0, 0, 0).to_i }
220
+ it { should == "\x17\x0D120124000000Z" }
221
+ end
222
+
223
+ context 'Min time representation' do
224
+ let(:value) { Time.utc(2000, 1, 1, 0, 0, 0).to_i }
225
+ it { should == "\x17\x0D000101000000Z" }
226
+ end
227
+
228
+ context 'Max time representation' do
229
+ let(:value) { Time.utc(1999, 12, 31, 23, 59, 59).to_i }
230
+ it { should == "\x17\x0D991231235959Z" }
231
+ end
232
+
233
+ context 'timezone' do
234
+ pending 'ossl does not support this'
235
+ end
236
+
237
+ context '(empty)' do
238
+ let(:value) { '' }
239
+ it { -> { subject }.should raise_error asn1error }
240
+ end
241
+
242
+ context 'Bignum' do
243
+ let(:value) { 2**64 - 1 }
244
+ it { -> { subject }.should raise_error asn1error }
245
+ end
246
+
247
+ context 'negative Integer' do
248
+ let(:value) { -1 }
249
+ it { -> { subject }.should raise_error asn1error } # TODO: ossl does not check value
250
+ end
251
+
252
+ context 'String that Integer(str) barks' do
253
+ let(:value) { "ABC" }
254
+ it { -> { subject }.should raise_error asn1error } # TODO: ossl does not check value
255
+ end
256
+
257
+ context 'some object' do
258
+ let(:value) { Object.new }
259
+ it { -> { subject }.should raise_error asn1error }
260
+ end
261
+ end
262
+
263
+ context 'encodes tag number' do
264
+ subject { klass.new(1327330800, tag, :PRIVATE).to_der }
265
+
266
+ context 'default tag' do
267
+ let(:tag) { Krypt::ASN1::UTC_TIME }
268
+ it { should == "\xD7\x0D120123150000Z" }
269
+ end
270
+
271
+ context 'custom tag' do
272
+ let(:tag) { 14 }
273
+ it { should == "\xCE\x0D120123150000Z" }
274
+ end
275
+
276
+ context 'nil' do
277
+ let(:tag) { nil }
278
+ it { -> { subject }.should raise_error asn1error }
279
+ end
280
+ end
281
+
282
+ context 'encodes tag class' do
283
+ subject { klass.new(1327330800, Krypt::ASN1::UTC_TIME, tag_class).to_der }
284
+
285
+ context 'UNIVERSAL' do
286
+ let(:tag_class) { :UNIVERSAL }
287
+ it { should == "\x17\x0D120123150000Z" }
288
+ end
289
+
290
+ context 'APPLICATION' do
291
+ let(:tag_class) { :APPLICATION }
292
+ it { should == "\x57\x0D120123150000Z" }
293
+ end
294
+
295
+ context 'CONTEXT_SPECIFIC' do
296
+ let(:tag_class) { :CONTEXT_SPECIFIC }
297
+ it { should == "\x97\x0D120123150000Z" }
298
+ end
299
+
300
+ context 'PRIVATE' do
301
+ let(:tag_class) { :PRIVATE }
302
+ it { should == "\xD7\x0D120123150000Z" }
303
+ end
304
+
305
+ context 'IMPLICIT' do
306
+ let(:tag_class) { :IMPLICIT }
307
+ it { should == "\x97\x0D120123150000Z" }
308
+ end
309
+
310
+ context 'EXPLICIT' do
311
+ let(:tag_class) { :EXPLICIT }
312
+ it { should == "\xB7\x0F\x17\x0D120123150000Z" }
313
+ end
314
+
315
+ context nil do
316
+ let(:tag_class) { nil }
317
+ it { -> { subject }.should raise_error asn1error } # TODO: ossl does not check nil
318
+ end
319
+
320
+ context :no_such_class do
321
+ let(:tag_class) { :no_such_class }
322
+ it { -> { subject }.should raise_error asn1error }
323
+ end
324
+ end
325
+
326
+ context 'encodes values set via accessors' do
327
+ subject {
328
+ o = klass.new(nil)
329
+ o.value = value if defined? value
330
+ o.tag = tag if defined? tag
331
+ o.tag_class = tag_class if defined? tag_class
332
+ o.to_der
333
+ }
334
+
335
+ context 'value: Time' do
336
+ let(:value) { Time.utc(2012, 1, 24, 0, 0, 0) }
337
+ it { should == "\x17\x0D120124000000Z" }
338
+ end
339
+
340
+ context 'custom tag' do
341
+ let(:value) { Time.utc(2012, 1, 24, 0, 0, 0) }
342
+ let(:tag) { 14 }
343
+ let(:tag_class) { :PRIVATE }
344
+ it { should == "\xCE\x0D120124000000Z" }
345
+ end
346
+
347
+ context 'tag_class' do
348
+ let(:value) { Time.utc(2012, 1, 24, 0, 0, 0) }
349
+ let(:tag_class) { :APPLICATION }
350
+ it { should == "\x57\x0D120124000000Z" }
351
+ end
352
+ end
353
+ end
354
+
355
+ describe '#encode_to' do
356
+ context 'encodes to an IO' do
357
+ subject { klass.new(value).encode_to(io); io }
358
+
359
+ context "StringIO" do
360
+ let(:value) { 1327330800 }
361
+ let(:io) { string_io_object }
362
+ its(:written_bytes) { should == "\x17\x0D120123150000Z" }
363
+ end
364
+
365
+ context "Object responds to :write" do
366
+ let(:value) { 1327330800 }
367
+ let(:io) { writable_object }
368
+ its(:written_bytes) { should == "\x17\x0D120123150000Z" }
369
+ end
370
+
371
+ context "raise IO error transparently" do
372
+ let(:value) { 1327330800 }
373
+ let(:io) { io_error_object }
374
+ it { -> { subject }.should raise_error asn1error }
375
+ end
376
+ end
377
+
378
+ it 'returns self' do
379
+ obj = klass.new(1327330800)
380
+ obj.encode_to(string_io_object).should == obj
381
+ end
382
+ end
383
+
384
+ describe 'extracted from ASN1.decode' do
385
+ subject { decoder.decode(der) }
386
+
387
+ context 'extracted value' do
388
+ context 'Time' do
389
+ let(:der) { "\x17\x0D120123150000Z" }
390
+ its(:class) { should == klass }
391
+ its(:tag) { should == Krypt::ASN1::UTC_TIME }
392
+ its(:value) { should == Time.utc(2012, 1, 23, 15, 0, 0) }
393
+ end
394
+
395
+ context 'Min time representation' do
396
+ let(:der) { "\x17\x0D000101000000Z" }
397
+ its(:class) { should == klass }
398
+ its(:tag) { should == Krypt::ASN1::UTC_TIME }
399
+ its(:value) { should == Time.utc(2000, 1, 1, 0, 0, 0) }
400
+ end
401
+
402
+ context 'Max time representation' do
403
+ let(:der) { "\x17\x0D991231235959Z" }
404
+ its(:class) { should == klass }
405
+ its(:tag) { should == Krypt::ASN1::UTC_TIME }
406
+ its(:value) { should == Time.utc(1999, 12, 31, 23, 59, 59) }
407
+ end
408
+
409
+ context '> 69' do
410
+ let(:der) { "\x17\x0D700101000000Z" }
411
+ its(:class) { should == klass }
412
+ its(:tag) { should == Krypt::ASN1::UTC_TIME }
413
+ its(:value) { should == Time.utc(1970, 1, 1, 0, 0, 0) }
414
+ end
415
+
416
+ context 'rejects nonsensical values' do
417
+ let(:der) { "\x17\x0DABCDEFGHIJKLZ" }
418
+ it { -> { subject.value }.should raise_error asn1error }
419
+ end
420
+
421
+ context 'timezone' do
422
+ context '+' do
423
+ let(:der) { "\x17\x11000101085959+0900" }
424
+ its(:class) { should == klass }
425
+ its(:tag) { should == Krypt::ASN1::UTC_TIME }
426
+ pending 'No timezone support yet'
427
+ its(:value) { Time.utc(1999, 12, 31, 23, 59, 59) }
428
+ end
429
+
430
+ context '-' do
431
+ let(:der) { "\x17\x11991231145959-0900" }
432
+ its(:class) { should == klass }
433
+ its(:tag) { should == Krypt::ASN1::UTC_TIME }
434
+ pending 'No timezone support yet'
435
+ its(:value) { Time.utc(1999, 12, 31, 23, 59, 59) }
436
+ end
437
+
438
+ context '+0' do
439
+ let(:der) { "\x17\x11991231235959+0000" }
440
+ its(:class) { should == klass }
441
+ its(:tag) { should == Krypt::ASN1::UTC_TIME }
442
+ pending 'No timezone support yet'
443
+ its(:value) { Time.utc(1999, 12, 31, 23, 59, 59) }
444
+ end
445
+
446
+ context '-0' do
447
+ let(:der) { "\x17\x11991231235959-0000" }
448
+ its(:class) { should == klass }
449
+ its(:tag) { should == Krypt::ASN1::UTC_TIME }
450
+ pending 'No timezone support yet'
451
+ its(:value) { Time.utc(1999, 12, 31, 23, 59, 59) }
452
+ end
453
+ end
454
+ end
455
+
456
+ context 'extracted tag class' do
457
+ context 'UNIVERSAL' do
458
+ let(:der) { "\x17\x0D991231235959Z" }
459
+ its(:tag_class) { should == :UNIVERSAL }
460
+ end
461
+
462
+ context 'APPLICATION' do
463
+ let(:der) { "\x47\x0D991231235959Z" }
464
+ its(:tag_class) { should == :APPLICATION }
465
+ end
466
+
467
+ context 'CONTEXT_SPECIFIC' do
468
+ let(:der) { "\x87\x0D991231235959Z" }
469
+ its(:tag_class) { should == :CONTEXT_SPECIFIC }
470
+ end
471
+
472
+ context 'PRIVATE' do
473
+ let(:der) { "\xC7\x0D991231235959Z" }
474
+ its(:tag_class) { should == :PRIVATE }
475
+ end
476
+
477
+ context "setting IMPLICIT will result in CONTEXT_SPECIFIC" do
478
+ let(:der) { "\x17\x0D120123150000Z" }
479
+ it do
480
+ subject.tag_class = :IMPLICIT
481
+ subject.to_der.should == "\x97\x0D120123150000Z"
482
+ end
483
+ end
484
+
485
+ context "setting EXPLICIT will reencode as CONTEXT_SPECIFIC" do
486
+ let(:der) { "\x17\x0D120123150000Z" }
487
+ it do
488
+ subject.tag_class = :EXPLICIT
489
+ subject.tag = 0
490
+ subject.to_der.should == "\xA0\x0F\x17\x0D120123150000Z"
491
+ end
492
+ end
493
+ end
494
+ end
495
+ end