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.
- data/LICENSE +20 -0
- data/README.md +82 -0
- data/lib/krypt.rb +49 -0
- data/lib/krypt/asn1.rb +3 -0
- data/lib/krypt/asn1/common.rb +96 -0
- data/lib/krypt/asn1/template.rb +257 -0
- data/lib/krypt/codec.rb +57 -0
- data/lib/krypt/codec/base64.rb +140 -0
- data/lib/krypt/codec/base_codec.rb +36 -0
- data/lib/krypt/codec/hex.rb +122 -0
- data/lib/krypt/digest.rb +112 -0
- data/lib/krypt/hmac.rb +69 -0
- data/lib/krypt/pkcs5.rb +1 -0
- data/lib/krypt/pkcs5/pbkdf2.rb +41 -0
- data/lib/krypt/provider.rb +35 -0
- data/lib/krypt/x509.rb +3 -0
- data/lib/krypt/x509/certificate.rb +36 -0
- data/lib/krypt/x509/common.rb +41 -0
- data/lib/krypt/x509/crl.rb +33 -0
- data/lib/krypt_missing.rb +32 -0
- data/spec/krypt-core/MEMO.txt +85 -0
- data/spec/krypt-core/asn1/asn1_bit_string_spec.rb +475 -0
- data/spec/krypt-core/asn1/asn1_boolean_spec.rb +392 -0
- data/spec/krypt-core/asn1/asn1_constants_spec.rb +71 -0
- data/spec/krypt-core/asn1/asn1_data_spec.rb +1153 -0
- data/spec/krypt-core/asn1/asn1_end_of_contents_spec.rb +133 -0
- data/spec/krypt-core/asn1/asn1_enumerated_spec.rb +458 -0
- data/spec/krypt-core/asn1/asn1_generalized_time_spec.rb +492 -0
- data/spec/krypt-core/asn1/asn1_integer_spec.rb +557 -0
- data/spec/krypt-core/asn1/asn1_null_spec.rb +360 -0
- data/spec/krypt-core/asn1/asn1_object_id_spec.rb +495 -0
- data/spec/krypt-core/asn1/asn1_octet_string_spec.rb +456 -0
- data/spec/krypt-core/asn1/asn1_parser_spec.rb +503 -0
- data/spec/krypt-core/asn1/asn1_pem_spec.rb +282 -0
- data/spec/krypt-core/asn1/asn1_sequence_spec.rb +637 -0
- data/spec/krypt-core/asn1/asn1_set_spec.rb +795 -0
- data/spec/krypt-core/asn1/asn1_utc_time_spec.rb +495 -0
- data/spec/krypt-core/asn1/asn1_utf8_string_spec.rb +404 -0
- data/spec/krypt-core/asn1/resources.rb +53 -0
- data/spec/krypt-core/base64/base64_spec.rb +97 -0
- data/spec/krypt-core/digest/digest_spec.rb +707 -0
- data/spec/krypt-core/hex/hex_spec.rb +102 -0
- data/spec/krypt-core/pem/pem_decode_spec.rb +235 -0
- data/spec/krypt-core/resources.rb +1 -0
- data/spec/krypt-core/template/template_choice_parse_spec.rb +289 -0
- data/spec/krypt-core/template/template_dsl_spec.rb +351 -0
- data/spec/krypt-core/template/template_seq_of_parse_spec.rb +64 -0
- data/spec/krypt-core/template/template_seq_parse_spec.rb +1241 -0
- data/spec/krypt/codec/base64_decoder_spec.rb +94 -0
- data/spec/krypt/codec/base64_encoder_spec.rb +94 -0
- data/spec/krypt/codec/base64_mixed_spec.rb +16 -0
- data/spec/krypt/codec/hex_decoder_spec.rb +94 -0
- data/spec/krypt/codec/hex_encoder_spec.rb +94 -0
- data/spec/krypt/codec/hex_mixed_spec.rb +17 -0
- data/spec/krypt/codec/identity_shared.rb +119 -0
- data/spec/krypt/hmac/hmac_spec.rb +311 -0
- data/spec/krypt/pkcs5/pbkdf2_spec.rb +79 -0
- data/spec/krypt/provider/provider_spec.rb +83 -0
- data/spec/res/ca-bundle.crt +11758 -0
- data/spec/res/certificate.cer +0 -0
- data/spec/res/certificate.pem +20 -0
- data/spec/res/multiple_certs.pem +60 -0
- data/spec/resources.rb +66 -0
- data/test/helper.rb +8 -0
- data/test/res/certificate.cer +0 -0
- data/test/resources.rb +48 -0
- data/test/scratch.rb +28 -0
- data/test/test_krypt_asn1.rb +119 -0
- data/test/test_krypt_parser.rb +331 -0
- metadata +134 -0
@@ -0,0 +1,1153 @@
|
|
1
|
+
# encoding: US-ASCII
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
require 'krypt'
|
5
|
+
require 'openssl'
|
6
|
+
require_relative './resources'
|
7
|
+
require_relative '../resources'
|
8
|
+
|
9
|
+
describe Krypt::ASN1::ASN1Data do
|
10
|
+
include Krypt::ASN1::Resources
|
11
|
+
|
12
|
+
let(:mod) { Krypt::ASN1 }
|
13
|
+
let(:klass) { mod::ASN1Data }
|
14
|
+
let(:decoder) { mod }
|
15
|
+
let(:asn1error) { mod::ASN1Error }
|
16
|
+
|
17
|
+
# For test against OpenSSL
|
18
|
+
#
|
19
|
+
#let(:mod) { OpenSSL::ASN1 }
|
20
|
+
#
|
21
|
+
# OpenSSL stub for signature mismatch
|
22
|
+
class OpenSSL::ASN1::ASN1Data
|
23
|
+
class << self
|
24
|
+
alias old_new new
|
25
|
+
def new(*args)
|
26
|
+
if args.size > 1
|
27
|
+
args = [args[0], args[1], :IMPLICIT, args[2]]
|
28
|
+
end
|
29
|
+
old_new(*args)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#new" do
|
35
|
+
context "requires exactly 3 arguments" do
|
36
|
+
subject { klass.new(value, tag, tag_class) }
|
37
|
+
|
38
|
+
context "accepts any object as value" do
|
39
|
+
let(:value) { Object.new }
|
40
|
+
let(:tag) { 14 }
|
41
|
+
let(:tag_class) { :UNIVERSAL }
|
42
|
+
its(:tag) { should == 14 }
|
43
|
+
its(:tag_class) { should == :UNIVERSAL }
|
44
|
+
its(:value) { should == value }
|
45
|
+
its(:infinite_length) { should == false }
|
46
|
+
end
|
47
|
+
|
48
|
+
context "accepts nil as value" do
|
49
|
+
let(:value) { nil }
|
50
|
+
let(:tag) { 14 }
|
51
|
+
let(:tag_class) { :UNIVERSAL }
|
52
|
+
its(:tag) { should == 14 }
|
53
|
+
its(:tag_class) { should == :UNIVERSAL }
|
54
|
+
its(:value) { should == nil }
|
55
|
+
its(:infinite_length) { should == false }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "raises ArgumentError for more or less arguments" do
|
60
|
+
it { -> { klass.new }.should raise_error ArgumentError }
|
61
|
+
it { -> { klass.new(5) }.should raise_error ArgumentError }
|
62
|
+
it { -> { klass.new(5, 2) }.should raise_error ArgumentError }
|
63
|
+
it { -> { klass.new(5, 2, :UNIVERSAL, 17) }.should raise_error ArgumentError }
|
64
|
+
end
|
65
|
+
|
66
|
+
context "gets explicit tag number as the 2nd argument" do
|
67
|
+
subject { klass.new(nil, tag, :PRIVATE) }
|
68
|
+
|
69
|
+
context "accepts tags in the UNIVERSAL range" do
|
70
|
+
let(:tag) { Krypt::ASN1::BOOLEAN }
|
71
|
+
its(:tag) { should == tag }
|
72
|
+
end
|
73
|
+
|
74
|
+
context "accepts custom tags" do
|
75
|
+
let(:tag) { 42 }
|
76
|
+
its(:tag) { should == tag }
|
77
|
+
end
|
78
|
+
|
79
|
+
context "does not accept nil as tag" do
|
80
|
+
let(:tag) { nil }
|
81
|
+
it { -> { subject }.should raise_error asn1error }
|
82
|
+
end
|
83
|
+
|
84
|
+
context "does not accept a non-Number as tag" do
|
85
|
+
let(:tag) { Object.new }
|
86
|
+
it { -> { subject }.should raise_error asn1error }
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
context "gets tag class symbol as the 3rd argument" do
|
92
|
+
subject { klass.new(true, 14, tag_class) }
|
93
|
+
|
94
|
+
context "accepts :UNIVERSAL" do
|
95
|
+
let(:tag_class) { :UNIVERSAL }
|
96
|
+
its(:tag_class) { should == tag_class }
|
97
|
+
end
|
98
|
+
|
99
|
+
context "accepts :APPLICATION" do
|
100
|
+
let(:tag_class) { :APPLICATION }
|
101
|
+
its(:tag_class) { should == tag_class }
|
102
|
+
end
|
103
|
+
|
104
|
+
context "accepts :CONTEXT_SPECIFIC" do
|
105
|
+
let(:tag_class) { :CONTEXT_SPECIFIC }
|
106
|
+
its(:tag_class) { should == tag_class }
|
107
|
+
end
|
108
|
+
|
109
|
+
context "accepts :PRIVATE" do
|
110
|
+
let(:tag_class) { :PRIVATE }
|
111
|
+
its(:tag_class) { should == tag_class }
|
112
|
+
end
|
113
|
+
|
114
|
+
context "accepts :IMPLICIT" do
|
115
|
+
let(:tag_class) { :IMPLICIT }
|
116
|
+
its(:tag_class) { should == tag_class }
|
117
|
+
end
|
118
|
+
|
119
|
+
context "does not accept :EXPLICIT" do
|
120
|
+
let(:tag_class) { :EXPLICIT }
|
121
|
+
it { -> { subject }.should raise_error asn1error }
|
122
|
+
end
|
123
|
+
|
124
|
+
context "does not accept unknown tag classes" do
|
125
|
+
let(:tag_class) { :IMAGINARY }
|
126
|
+
it { -> { subject }.should raise_error asn1error }
|
127
|
+
end
|
128
|
+
|
129
|
+
context "does not accept non-Symbols as tag class" do
|
130
|
+
let(:tag_class) { 7 }
|
131
|
+
it { -> { subject }.should raise_error asn1error }
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "rejects :EXPLICIT when set via accessor" do
|
136
|
+
it "primitive UNIVERSAL" do
|
137
|
+
asn1 = klass.new(42, Krypt::ASN1::INTEGER, :UNIVERSAL)
|
138
|
+
-> { asn1.tag_class = :EXPLICIT }.should raise_error asn1error
|
139
|
+
end
|
140
|
+
|
141
|
+
it "primitive CONTEXT_SPECIFIC" do
|
142
|
+
asn1 = klass.new("42", 0, :CONTEXT_SPECIFIC)
|
143
|
+
-> { asn1.tag_class = :EXPLICIT }.should raise_error asn1error
|
144
|
+
end
|
145
|
+
|
146
|
+
it "primitive APPLICATION" do
|
147
|
+
asn1 = klass.new("42", 0, :APPLICATION)
|
148
|
+
-> { asn1.tag_class = :EXPLICIT }.should raise_error asn1error
|
149
|
+
end
|
150
|
+
|
151
|
+
it "primitive PRIVATE" do
|
152
|
+
asn1 = klass.new("42", 0, :PRIVATE)
|
153
|
+
-> { asn1.tag_class = :EXPLICIT }.should raise_error asn1error
|
154
|
+
end
|
155
|
+
|
156
|
+
it "constructive UNIVERSAL" do
|
157
|
+
asn1 = klass.new([Krypt::ASN1::Null.new], Krypt::ASN1::SEQUENCE, :UNIVERSAL)
|
158
|
+
-> { asn1.tag_class = :EXPLICIT }.should raise_error asn1error
|
159
|
+
end
|
160
|
+
|
161
|
+
it "constructive CONTEXT_SPECIFIC" do
|
162
|
+
asn1 = klass.new([Krypt::ASN1::Null.new], Krypt::ASN1::SEQUENCE, :CONTEXT_SPECIFIC)
|
163
|
+
-> { asn1.tag_class = :EXPLICIT }.should raise_error asn1error
|
164
|
+
end
|
165
|
+
|
166
|
+
it "constructive APPLICATION" do
|
167
|
+
asn1 = klass.new([Krypt::ASN1::Null.new], Krypt::ASN1::SEQUENCE, :APPLICATION)
|
168
|
+
-> { asn1.tag_class = :EXPLICIT }.should raise_error asn1error
|
169
|
+
end
|
170
|
+
|
171
|
+
it "constructive PRIVATE" do
|
172
|
+
asn1 = klass.new([Krypt::ASN1::Null.new], Krypt::ASN1::SEQUENCE, :PRIVATE)
|
173
|
+
-> { asn1.tag_class = :EXPLICIT }.should raise_error asn1error
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
context "returns an instance of ASN1Data, despite of an
|
178
|
+
UNIVERSAL tag and tag class" do
|
179
|
+
subject { klass.new("test", Krypt::ASN1::OCTET_STRING, :UNIVERSAL) }
|
180
|
+
it { subject.should be_an_instance_of klass }
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe Krypt::ASN1::Constructive, "#new" do
|
185
|
+
let(:klazz) { mod::Constructive }
|
186
|
+
|
187
|
+
context "requires exactly 3 arguments" do
|
188
|
+
subject { klazz.new(value, tag, tag_class) }
|
189
|
+
|
190
|
+
context "accepts any object as value" do
|
191
|
+
let(:value) { Object.new }
|
192
|
+
let(:tag) { 14 }
|
193
|
+
let(:tag_class) { :UNIVERSAL }
|
194
|
+
its(:tag) { should == 14 }
|
195
|
+
its(:tag_class) { should == :UNIVERSAL }
|
196
|
+
its(:value) { should == value }
|
197
|
+
its(:infinite_length) { should == false }
|
198
|
+
end
|
199
|
+
|
200
|
+
context "accepts nil as value" do
|
201
|
+
let(:value) { nil }
|
202
|
+
let(:tag) { 14 }
|
203
|
+
let(:tag_class) { :UNIVERSAL }
|
204
|
+
its(:tag) { should == 14 }
|
205
|
+
its(:tag_class) { should == :UNIVERSAL }
|
206
|
+
its(:value) { should == nil }
|
207
|
+
its(:infinite_length) { should == false }
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
context "rejects :EXPLICIT" do
|
212
|
+
it { -> { klazz.new(42, 3, :EXPLICIT) }.should raise_error asn1error }
|
213
|
+
|
214
|
+
describe "when set via accessor" do
|
215
|
+
it "constructive UNIVERSAL" do
|
216
|
+
asn1 = klazz.new([Krypt::ASN1::Null.new], Krypt::ASN1::SEQUENCE, :UNIVERSAL)
|
217
|
+
-> { asn1.tag_class = :EXPLICIT }.should raise_error asn1error
|
218
|
+
end
|
219
|
+
|
220
|
+
it "constructive CONTEXT_SPECIFIC" do
|
221
|
+
asn1 = klazz.new([Krypt::ASN1::Null.new], Krypt::ASN1::SEQUENCE, :CONTEXT_SPECIFIC)
|
222
|
+
-> { asn1.tag_class = :EXPLICIT }.should raise_error asn1error
|
223
|
+
end
|
224
|
+
|
225
|
+
it "constructive APPLICATION" do
|
226
|
+
asn1 = klazz.new([Krypt::ASN1::Null.new], Krypt::ASN1::SEQUENCE, :APPLICATION)
|
227
|
+
-> { asn1.tag_class = :EXPLICIT }.should raise_error asn1error
|
228
|
+
end
|
229
|
+
|
230
|
+
it "constructive PRIVATE" do
|
231
|
+
asn1 = klazz.new([Krypt::ASN1::Null.new], Krypt::ASN1::SEQUENCE, :PRIVATE)
|
232
|
+
-> { asn1.tag_class = :EXPLICIT }.should raise_error asn1error
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
context "raises ArgumentError for more or less arguments" do
|
238
|
+
it { -> { klazz.new }.should raise_error ArgumentError }
|
239
|
+
it { -> { klazz.new(5) }.should raise_error ArgumentError }
|
240
|
+
it { -> { klazz.new(5, 2) }.should raise_error ArgumentError }
|
241
|
+
it { -> { klazz.new(5, 2, :UNIVERSAL, 17) }.should raise_error ArgumentError }
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
describe Krypt::ASN1::Primitive, "#new" do
|
246
|
+
let(:klazz) { mod::Primitive }
|
247
|
+
|
248
|
+
context "requires exactly 3 arguments" do
|
249
|
+
subject { klazz.new(value, tag, tag_class) }
|
250
|
+
|
251
|
+
context "accepts any object as value" do
|
252
|
+
let(:value) { Object.new }
|
253
|
+
let(:tag) { 14 }
|
254
|
+
let(:tag_class) { :UNIVERSAL }
|
255
|
+
its(:tag) { should == 14 }
|
256
|
+
its(:tag_class) { should == :UNIVERSAL }
|
257
|
+
its(:value) { should == value }
|
258
|
+
its(:infinite_length) { should == false }
|
259
|
+
end
|
260
|
+
|
261
|
+
context "accepts nil as value" do
|
262
|
+
let(:value) { nil }
|
263
|
+
let(:tag) { 14 }
|
264
|
+
let(:tag_class) { :UNIVERSAL }
|
265
|
+
its(:tag) { should == 14 }
|
266
|
+
its(:tag_class) { should == :UNIVERSAL }
|
267
|
+
its(:value) { should == nil }
|
268
|
+
its(:infinite_length) { should == false }
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
context "rejects :EXPLICIT" do
|
273
|
+
it { -> { klazz.new(42, 3, :EXPLICIT) }.should raise_error asn1error }
|
274
|
+
end
|
275
|
+
|
276
|
+
describe "rejects :EXPLICIT when set via accessor" do
|
277
|
+
it "primitive UNIVERSAL" do
|
278
|
+
asn1 = klazz.new(42, Krypt::ASN1::INTEGER, :UNIVERSAL)
|
279
|
+
-> { asn1.tag_class = :EXPLICIT }.should raise_error asn1error
|
280
|
+
end
|
281
|
+
|
282
|
+
it "primitive CONTEXT_SPECIFIC" do
|
283
|
+
asn1 = klazz.new("42", 0, :CONTEXT_SPECIFIC)
|
284
|
+
-> { asn1.tag_class = :EXPLICIT }.should raise_error asn1error
|
285
|
+
end
|
286
|
+
|
287
|
+
it "primitive APPLICATION" do
|
288
|
+
asn1 = klazz.new("42", 0, :APPLICATION)
|
289
|
+
-> { asn1.tag_class = :EXPLICIT }.should raise_error asn1error
|
290
|
+
end
|
291
|
+
|
292
|
+
it "primitive PRIVATE" do
|
293
|
+
asn1 = klazz.new("42", 0, :PRIVATE)
|
294
|
+
-> { asn1.tag_class = :EXPLICIT }.should raise_error asn1error
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
context "raises ArgumentError for more or less arguments" do
|
299
|
+
it { -> { klazz.new }.should raise_error ArgumentError }
|
300
|
+
it { -> { klazz.new(5) }.should raise_error ArgumentError }
|
301
|
+
it { -> { klazz.new(5, 2) }.should raise_error ArgumentError }
|
302
|
+
it { -> { klazz.new(5, 2, :UNIVERSAL, 17) }.should raise_error ArgumentError }
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
describe "#to_der" do
|
307
|
+
context "encodes UNIVERSAL tag values as if the equivalent primitive
|
308
|
+
class was used" do
|
309
|
+
subject { klass.new(value, tag, :UNIVERSAL).to_der }
|
310
|
+
|
311
|
+
context "END_OF_CONTENTS" do
|
312
|
+
let(:tag) { Krypt::ASN1::END_OF_CONTENTS }
|
313
|
+
let(:value) { nil }
|
314
|
+
it { should == Krypt::ASN1::EndOfContents.new().to_der }
|
315
|
+
end
|
316
|
+
|
317
|
+
context "BOOLEAN" do
|
318
|
+
let(:tag) { Krypt::ASN1::BOOLEAN }
|
319
|
+
let(:value) { true }
|
320
|
+
it { should == Krypt::ASN1::Boolean.new(true).to_der }
|
321
|
+
end
|
322
|
+
|
323
|
+
context "PRINTABLE_STRING" do
|
324
|
+
let(:tag) { Krypt::ASN1::PRINTABLE_STRING }
|
325
|
+
let(:value) { "test" }
|
326
|
+
it { should == Krypt::ASN1::PrintableString.new("test").to_der }
|
327
|
+
end
|
328
|
+
|
329
|
+
context "SEQUENCE" do
|
330
|
+
let(:tag) { Krypt::ASN1::SEQUENCE }
|
331
|
+
let(:value) { [Krypt::ASN1::Integer.new(1)] }
|
332
|
+
it { should == Krypt::ASN1::Sequence.new([Krypt::ASN1::Integer.new(1)]).to_der }
|
333
|
+
end
|
334
|
+
|
335
|
+
context "SET" do
|
336
|
+
let(:tag) { Krypt::ASN1::SET }
|
337
|
+
let(:value) { [Krypt::ASN1::Integer.new(1), Krypt::ASN1::Boolean.new(true)] }
|
338
|
+
it { should == Krypt::ASN1::Set.new([Krypt::ASN1::Integer.new(1), Krypt::ASN1::Boolean.new(true)]).to_der }
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
context "expects raw byte strings for non-universal tags" do
|
343
|
+
subject { klass.new(value, tag, tag_class).to_der }
|
344
|
+
|
345
|
+
context ":PRIVATE tag class" do
|
346
|
+
let(:tag_class) { :PRIVATE }
|
347
|
+
let(:value) { "\xC0\xFF\xEE\xBA\xBE" }
|
348
|
+
|
349
|
+
context "tag < 30" do
|
350
|
+
let(:tag) { Krypt::ASN1::BOOLEAN }
|
351
|
+
it { should == "\xC1\x05\xC0\xFF\xEE\xBA\xBE" }
|
352
|
+
end
|
353
|
+
|
354
|
+
context "tag > 30" do
|
355
|
+
let(:tag) { 42 }
|
356
|
+
it { should == "\xDF\x2A\x05\xC0\xFF\xEE\xBA\xBE" }
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
context ":APPLICATION tag class" do
|
361
|
+
let(:tag_class) { :APPLICATION }
|
362
|
+
let(:value) { "\xC0\xFF\xEE\xBA\xBE" }
|
363
|
+
|
364
|
+
context "tag < 30" do
|
365
|
+
let(:tag) { Krypt::ASN1::BOOLEAN }
|
366
|
+
it { should == "\x41\x05\xC0\xFF\xEE\xBA\xBE" }
|
367
|
+
end
|
368
|
+
|
369
|
+
context "tag > 30" do
|
370
|
+
let(:tag) { 42 }
|
371
|
+
it { should == "\x5F\x2A\x05\xC0\xFF\xEE\xBA\xBE" }
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
context ":CONTEXT_SPECIFIC tag class" do
|
376
|
+
let(:tag_class) { :CONTEXT_SPECIFIC }
|
377
|
+
let(:value) { "\xC0\xFF\xEE\xBA\xBE" }
|
378
|
+
|
379
|
+
context "tag < 30" do
|
380
|
+
let(:tag) { Krypt::ASN1::BOOLEAN }
|
381
|
+
it { should == "\x81\x05\xC0\xFF\xEE\xBA\xBE" }
|
382
|
+
end
|
383
|
+
|
384
|
+
context "tag > 30" do
|
385
|
+
let(:tag) { 42 }
|
386
|
+
it { should == "\x9F\x2A\x05\xC0\xFF\xEE\xBA\xBE" }
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
context ":IMPLICIT tag class" do
|
391
|
+
let(:tag_class) { :IMPLICIT }
|
392
|
+
let(:value) { "\xC0\xFF\xEE\xBA\xBE" }
|
393
|
+
|
394
|
+
context "tag < 30" do
|
395
|
+
let(:tag) { Krypt::ASN1::BOOLEAN }
|
396
|
+
it { should == "\x81\x05\xC0\xFF\xEE\xBA\xBE" }
|
397
|
+
end
|
398
|
+
|
399
|
+
context "tag > 30" do
|
400
|
+
let(:tag) { 42 }
|
401
|
+
it { should == "\x9F\x2A\x05\xC0\xFF\xEE\xBA\xBE" }
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
context "decides dynamically whether to encode non-UNIVERSAL data
|
407
|
+
as as constructed or a primitive encoding" do
|
408
|
+
it "when constructing an implicitly 0-tagged BOOLEAN" do
|
409
|
+
klass.new("\xFF", 0, :CONTEXT_SPECIFIC).to_der.should == "\x80\x01\xFF"
|
410
|
+
end
|
411
|
+
|
412
|
+
it "when constructing an explicitly 0-tagged BOOLEAN" do
|
413
|
+
asn1 = klass.new([Krypt::ASN1::Boolean.new(true)], 0, :CONTEXT_SPECIFIC)
|
414
|
+
asn1.to_der.should == "\xA0\x03\x01\x01\xFF"
|
415
|
+
end
|
416
|
+
|
417
|
+
it "when setting the value from a primitive value to an Enumerable" do
|
418
|
+
asn1 = klass.new("\xFF", 0, :CONTEXT_SPECIFIC)
|
419
|
+
asn1.to_der.should == "\x80\x01\xFF"
|
420
|
+
asn1.value = [Krypt::ASN1::Boolean.new(true)]
|
421
|
+
asn1.to_der.should == "\xA0\x03\x01\x01\xFF"
|
422
|
+
end
|
423
|
+
|
424
|
+
it "when setting the value from an Enumerable to a primitive value" do
|
425
|
+
asn1 = klass.new([Krypt::ASN1::Boolean.new(true)], 0, :CONTEXT_SPECIFIC)
|
426
|
+
asn1.to_der.should == "\xA0\x03\x01\x01\xFF"
|
427
|
+
asn1.value = "\xFF"
|
428
|
+
asn1.to_der.should == "\x80\x01\xFF"
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
context "decides dynamically how to encode UNIVERSAL data" do
|
433
|
+
subject { klass.new("\x01", 0, :CONTEXT_SPECIFIC) }
|
434
|
+
|
435
|
+
it "when resetting tag and value with a Primitive" do
|
436
|
+
subject.to_der.should == "\x80\x01\x01"
|
437
|
+
subject.tag = Krypt::ASN1::INTEGER
|
438
|
+
subject.to_der.should == "\x82\x01\x01"
|
439
|
+
subject.tag_class = :UNIVERSAL
|
440
|
+
subject.value = 1
|
441
|
+
subject.to_der.should == "\x02\x01\x01"
|
442
|
+
end
|
443
|
+
|
444
|
+
it "when resetting tag and value with a Constructive" do
|
445
|
+
subject.to_der.should == "\x80\x01\x01"
|
446
|
+
subject.tag = Krypt::ASN1::SEQUENCE
|
447
|
+
subject.to_der.should == "\x90\x01\x01"
|
448
|
+
subject.tag_class = :UNIVERSAL
|
449
|
+
subject.value = [ Krypt::ASN1::EndOfContents.new ]
|
450
|
+
subject.to_der.should == "\x30\x02\x00\x00"
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
context "encodes infinite length tagged values" do
|
455
|
+
subject do
|
456
|
+
asn1 = klass.new(value, 0, :CONTEXT_SPECIFIC)
|
457
|
+
asn1.infinite_length = true
|
458
|
+
asn1.to_der
|
459
|
+
end
|
460
|
+
|
461
|
+
context "SEQUENCE" do
|
462
|
+
let(:value) { [mod::Integer.new(1), mod::EndOfContents.new] }
|
463
|
+
it { subject.should == "\xA0\x80\x02\x01\x01\x00\x00" }
|
464
|
+
end
|
465
|
+
|
466
|
+
context "OCTET STRING" do
|
467
|
+
let(:value) { [mod::OctetString.new("\x00"), mod::OctetString.new("\x01"), mod::EndOfContents.new] }
|
468
|
+
it { subject.should == "\xA0\x80\x04\x01\x00\x04\x01\x01\x00\x00" }
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
context "encodes infinite length constructed values" do
|
473
|
+
subject do
|
474
|
+
asn1 = klass.new(value, tag, tag_class)
|
475
|
+
asn1.infinite_length = true
|
476
|
+
asn1.to_der
|
477
|
+
end
|
478
|
+
|
479
|
+
context "UNIVERSAL primitive with explicit EOC" do
|
480
|
+
let(:value) { [
|
481
|
+
mod::OctetString.new("\x01"),
|
482
|
+
mod::OctetString.new("\x02"),
|
483
|
+
mod::EndOfContents.new
|
484
|
+
] }
|
485
|
+
let(:tag) { Krypt::ASN1::OCTET_STRING }
|
486
|
+
let(:tag_class) { :UNIVERSAL }
|
487
|
+
it { subject.should == "\x24\x80\x04\x01\x01\x04\x01\x02\x00\x00" }
|
488
|
+
end
|
489
|
+
|
490
|
+
context "UNIVERSAL primitive without explicit EOC" do
|
491
|
+
let(:value) { [
|
492
|
+
mod::OctetString.new("\x01"),
|
493
|
+
mod::OctetString.new("\x02"),
|
494
|
+
] }
|
495
|
+
let(:tag) { Krypt::ASN1::OCTET_STRING }
|
496
|
+
let(:tag_class) { :UNIVERSAL }
|
497
|
+
it { subject.should == "\x24\x80\x04\x01\x01\x04\x01\x02\x00\x00" }
|
498
|
+
end
|
499
|
+
|
500
|
+
context "UNIVERSAL constructed with explicit EOC" do
|
501
|
+
let(:value) { [
|
502
|
+
mod::Integer.new(1),
|
503
|
+
mod::Boolean.new(true),
|
504
|
+
mod::EndOfContents.new
|
505
|
+
] }
|
506
|
+
let(:tag) { Krypt::ASN1::SEQUENCE }
|
507
|
+
let(:tag_class) { :UNIVERSAL }
|
508
|
+
it { subject.should == "\x30\x80\x02\x01\x01\x01\x01\xFF\x00\x00" }
|
509
|
+
end
|
510
|
+
|
511
|
+
context "UNIVERSAL constructed without explicit EOC" do
|
512
|
+
let(:value) { [
|
513
|
+
mod::Integer.new(1),
|
514
|
+
mod::Boolean.new(true),
|
515
|
+
] }
|
516
|
+
let(:tag) { Krypt::ASN1::SEQUENCE }
|
517
|
+
let(:tag_class) { :UNIVERSAL }
|
518
|
+
it { subject.should == "\x30\x80\x02\x01\x01\x01\x01\xFF\x00\x00" }
|
519
|
+
end
|
520
|
+
|
521
|
+
context "implicitly 0-tagged constructed with explicit EOC" do
|
522
|
+
let(:value) { [
|
523
|
+
mod::Integer.new(1),
|
524
|
+
mod::Boolean.new(true),
|
525
|
+
mod::EndOfContents.new
|
526
|
+
] }
|
527
|
+
let(:tag) { 0 }
|
528
|
+
let(:tag_class) { :CONTEXT_SPECIFIC }
|
529
|
+
it { subject.should == "\xA0\x80\x02\x01\x01\x01\x01\xFF\x00\x00" }
|
530
|
+
end
|
531
|
+
|
532
|
+
context "implicitly 0-tagged constructed without explicit EOC" do
|
533
|
+
let(:value) { [
|
534
|
+
mod::Integer.new(1),
|
535
|
+
mod::Boolean.new(true),
|
536
|
+
mod::EndOfContents.new
|
537
|
+
] }
|
538
|
+
let(:tag) { 0 }
|
539
|
+
let(:tag_class) { :CONTEXT_SPECIFIC }
|
540
|
+
it { subject.should == "\xA0\x80\x02\x01\x01\x01\x01\xFF\x00\x00" }
|
541
|
+
end
|
542
|
+
|
543
|
+
context "nested with explicit EOC" do
|
544
|
+
let(:value) do
|
545
|
+
inner = mod::OctetString.new([
|
546
|
+
mod::OctetString.new("\x01"),
|
547
|
+
mod::OctetString.new("\x02"),
|
548
|
+
mod::EndOfContents.new
|
549
|
+
])
|
550
|
+
inner.infinite_length = true
|
551
|
+
[
|
552
|
+
inner,
|
553
|
+
mod::Integer.new(1),
|
554
|
+
mod::EndOfContents.new
|
555
|
+
]
|
556
|
+
end
|
557
|
+
let(:tag) { mod::SEQUENCE }
|
558
|
+
let(:tag_class) { :UNIVERSAL }
|
559
|
+
it { subject.should == "\x30\x80\x24\x80\x04\x01\x01\x04\x01\x02\x00\x00\x02\x01\x01\x00\x00" }
|
560
|
+
end
|
561
|
+
|
562
|
+
context "nested without explicit EOC" do
|
563
|
+
let(:value) do
|
564
|
+
inner = mod::OctetString.new([
|
565
|
+
mod::OctetString.new("\x01"),
|
566
|
+
mod::OctetString.new("\x02"),
|
567
|
+
])
|
568
|
+
inner.infinite_length = true
|
569
|
+
[
|
570
|
+
inner,
|
571
|
+
mod::Integer.new(1),
|
572
|
+
]
|
573
|
+
end
|
574
|
+
let(:tag) { mod::SEQUENCE }
|
575
|
+
let(:tag_class) { :UNIVERSAL }
|
576
|
+
it { subject.should == "\x30\x80\x24\x80\x04\x01\x01\x04\x01\x02\x00\x00\x02\x01\x01\x00\x00" }
|
577
|
+
end
|
578
|
+
|
579
|
+
context "enumerable with explicit EOC" do
|
580
|
+
let(:value) do
|
581
|
+
o = Object.new
|
582
|
+
def o.each
|
583
|
+
yield Krypt::ASN1::OctetString.new("\x01")
|
584
|
+
yield Krypt::ASN1::OctetString.new("\x02")
|
585
|
+
yield Krypt::ASN1::EndOfContents.new
|
586
|
+
end
|
587
|
+
o
|
588
|
+
end
|
589
|
+
let(:tag) { mod::SEQUENCE }
|
590
|
+
let(:tag_class) { :UNIVERSAL }
|
591
|
+
it { subject.should == "\x30\x80\x04\x01\x01\x04\x01\x02\x00\x00" }
|
592
|
+
end
|
593
|
+
|
594
|
+
context "enumerable without explicit EOC" do
|
595
|
+
let(:value) do
|
596
|
+
o = Object.new
|
597
|
+
def o.each
|
598
|
+
yield Krypt::ASN1::OctetString.new("\x01")
|
599
|
+
yield Krypt::ASN1::OctetString.new("\x02")
|
600
|
+
end
|
601
|
+
o
|
602
|
+
end
|
603
|
+
let(:tag) { mod::SEQUENCE }
|
604
|
+
let(:tag_class) { :UNIVERSAL }
|
605
|
+
it { subject.should == "\x30\x80\x04\x01\x01\x04\x01\x02\x00\x00" }
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
context "all STRING classes except BIT STRING and UTF8 STRING behave like OCTET STRING" do
|
610
|
+
subject { decoder.decode(klazz.new("test").to_der).value == "test" }
|
611
|
+
|
612
|
+
context "OCTET STRING" do
|
613
|
+
let(:klazz) { mod::OctetString }
|
614
|
+
it { should == true }
|
615
|
+
end
|
616
|
+
|
617
|
+
context "NUMERIC STRING" do
|
618
|
+
let(:klazz) { mod::NumericString }
|
619
|
+
it { should == true }
|
620
|
+
end
|
621
|
+
|
622
|
+
context "PRINTABLE STRING" do
|
623
|
+
let(:klazz) { mod::PrintableString }
|
624
|
+
it { should == true }
|
625
|
+
end
|
626
|
+
|
627
|
+
context "T61 STRING" do
|
628
|
+
let(:klazz) { mod::T61String }
|
629
|
+
it { should == true }
|
630
|
+
end
|
631
|
+
|
632
|
+
context "VIDEOTEX STRING" do
|
633
|
+
let(:klazz) { mod::VideotexString }
|
634
|
+
it { should == true }
|
635
|
+
end
|
636
|
+
|
637
|
+
context "IA5 STRING" do
|
638
|
+
let(:klazz) { mod::IA5String }
|
639
|
+
it { should == true }
|
640
|
+
end
|
641
|
+
|
642
|
+
context "GRAPHIC STRING" do
|
643
|
+
let(:klazz) { mod::GraphicString }
|
644
|
+
it { should == true }
|
645
|
+
end
|
646
|
+
|
647
|
+
context "ISO64 STRING" do
|
648
|
+
let(:klazz) { mod::ISO64String }
|
649
|
+
it { should == true }
|
650
|
+
end
|
651
|
+
|
652
|
+
context "GENERAL STRING" do
|
653
|
+
let(:klazz) { mod::GeneralString }
|
654
|
+
it { should == true }
|
655
|
+
end
|
656
|
+
|
657
|
+
context "UNIVERSAL STRING" do
|
658
|
+
let(:klazz) { mod::UniversalString }
|
659
|
+
it { should == true }
|
660
|
+
end
|
661
|
+
|
662
|
+
context "BMP STRING" do
|
663
|
+
let(:klazz) { mod::BMPString }
|
664
|
+
it { should == true }
|
665
|
+
end
|
666
|
+
end
|
667
|
+
|
668
|
+
context "rejects UNIVERSAL tags > 30" do
|
669
|
+
it { -> { klass.new("\xFF", 31, :UNIVERSAL).to_der }.should raise_error asn1error }
|
670
|
+
end
|
671
|
+
|
672
|
+
it "rejects constructed primitive values that are not infinite length" do
|
673
|
+
asn1 = mod::OctetString.new [mod::OctetString.new("\x00"), mod::EndOfContents.new]
|
674
|
+
-> { asn1.to_der }.should raise_error asn1error
|
675
|
+
end
|
676
|
+
|
677
|
+
it "allows to encode constructed primitive values that are infinite length" do
|
678
|
+
asn1 = mod::OctetString.new [mod::OctetString.new("\x00"), mod::EndOfContents.new]
|
679
|
+
asn1.infinite_length = true
|
680
|
+
asn1.to_der.should == "\x24\x80\x04\x01\x00\x00\x00"
|
681
|
+
end
|
682
|
+
|
683
|
+
it "rejects primitive SEQUENCE values" do
|
684
|
+
asn1 = mod::Sequence.new(1)
|
685
|
+
-> { asn1.to_der }.should raise_error asn1error
|
686
|
+
end
|
687
|
+
|
688
|
+
it "rejects primitive SET values" do
|
689
|
+
asn1 = mod::Set.new(1)
|
690
|
+
-> { asn1.to_der }.should raise_error asn1error
|
691
|
+
end
|
692
|
+
end
|
693
|
+
|
694
|
+
describe "#encode_to" do
|
695
|
+
context "encodes to an IO" do
|
696
|
+
subject { klass.new("\xFF", 0, :CONTEXT_SPECIFIC).encode_to(io); io }
|
697
|
+
|
698
|
+
context "StringIO" do
|
699
|
+
let(:io) { string_io_object }
|
700
|
+
its(:written_bytes) { should == "\x80\x01\xFF" }
|
701
|
+
end
|
702
|
+
|
703
|
+
context "Object responds to :write" do
|
704
|
+
let(:io) { writable_object }
|
705
|
+
its(:written_bytes) { should == "\x80\x01\xFF" }
|
706
|
+
end
|
707
|
+
|
708
|
+
it "encodes to File IO" do
|
709
|
+
#io = File.open(IO::NULL, "wb") # not defined in JRuby yet
|
710
|
+
io = File.open("/dev/null", "wb")
|
711
|
+
begin
|
712
|
+
klass.new("\xFF", 0, :CONTEXT_SPECIFIC).encode_to(io)
|
713
|
+
ensure
|
714
|
+
io.close
|
715
|
+
end
|
716
|
+
end if File.exists?("/dev/null")
|
717
|
+
|
718
|
+
context "raise IO error transparently" do
|
719
|
+
let(:io) { io_error_object }
|
720
|
+
it { -> { subject }.should raise_error asn1error }
|
721
|
+
end
|
722
|
+
end
|
723
|
+
|
724
|
+
it "returns self" do
|
725
|
+
obj = klass.new(nil, Krypt::ASN1::END_OF_CONTENTS, :UNIVERSAL)
|
726
|
+
obj.encode_to(string_io_object).should == obj
|
727
|
+
end
|
728
|
+
end
|
729
|
+
|
730
|
+
describe "<=>" do
|
731
|
+
context "determines equality based on equality of the DER encoding" do
|
732
|
+
context "when equal" do
|
733
|
+
let(:v1) { decoder.decode("\x30\x03\x02\x01\x01") }
|
734
|
+
let(:v2) { decoder.decode("\x30\x03\x02\x01\x01") }
|
735
|
+
it { v1.should == v2 && v1.eql?(v2).should == false }
|
736
|
+
end
|
737
|
+
|
738
|
+
context "finds a value encoded and reparsed to be equal to itself" do
|
739
|
+
let(:v1) { decoder.decode("\x30\x03\x02\x01\x01") }
|
740
|
+
it { v1.should == (decoder.decode(v1.to_der)) }
|
741
|
+
end
|
742
|
+
|
743
|
+
context "when not equal" do
|
744
|
+
let(:v1) { decoder.decode("\x30\x03\x03\x01\x01") }
|
745
|
+
let(:v2) { decoder.decode("\x30\x03\x02\x01\x01") }
|
746
|
+
it { v1.should_not == v2 }
|
747
|
+
end
|
748
|
+
|
749
|
+
context "when equal in terms of DER but not BER" do
|
750
|
+
let(:v1) { decoder.decode("\x30\x83\x00\x00\x03\x02\x01\x01") }
|
751
|
+
let(:v2) { decoder.decode("\x30\x03\x02\x01\x01") }
|
752
|
+
it { v1.should_not == v2 }
|
753
|
+
end
|
754
|
+
end
|
755
|
+
|
756
|
+
context "orders the values by tag" do
|
757
|
+
let(:v1) { decoder.decode("\x02\x01\x01") }
|
758
|
+
let(:v2) { decoder.decode("\x05\x00") }
|
759
|
+
let(:v3) { decoder.decode("\x13\x03abc") }
|
760
|
+
it { v1.should < v2 }
|
761
|
+
it { v2.should < v3 }
|
762
|
+
it { v1.should < v3 } #transitivity
|
763
|
+
end
|
764
|
+
|
765
|
+
context "orders the values in lexographical bit order when tag is equal" do
|
766
|
+
let(:v1) { decoder.decode("\x13\x01\x01") }
|
767
|
+
let(:v2) { decoder.decode("\x13\x03\x00\x00\x00") }
|
768
|
+
let(:v3) { decoder.decode("\x13\x05\x00\x00\x00\x00\x00") }
|
769
|
+
it { v1.should < v2 }
|
770
|
+
it { v2.should < v3 }
|
771
|
+
it { v1.should < v3 }
|
772
|
+
end
|
773
|
+
end
|
774
|
+
|
775
|
+
describe "extracted from ASN1" do
|
776
|
+
subject { decoder.send(method, "#{tag}#{length}#{value}") }
|
777
|
+
|
778
|
+
[:decode, :decode_der].each do |m|
|
779
|
+
let(:method) { m }
|
780
|
+
|
781
|
+
context "for all non-UNIVERSAL primitive values" do
|
782
|
+
let(:length) { "\x01" }
|
783
|
+
let(:value) { "\xFF" }
|
784
|
+
|
785
|
+
context ":PRIVATE" do
|
786
|
+
let(:tag) { "\xC0" }
|
787
|
+
its(:tag) { should == 0 }
|
788
|
+
its(:tag_class) { should == :PRIVATE }
|
789
|
+
its(:value) { should == value }
|
790
|
+
it { subject.should be_an_instance_of klass }
|
791
|
+
end
|
792
|
+
|
793
|
+
context ":APPLICATION" do
|
794
|
+
let(:tag) { "\x40" }
|
795
|
+
its(:tag) { should == 0 }
|
796
|
+
its(:tag_class) { should == :APPLICATION }
|
797
|
+
its(:value) { should == value }
|
798
|
+
it { subject.should be_an_instance_of klass }
|
799
|
+
end
|
800
|
+
|
801
|
+
context ":CONTEXT_SPECIFIC" do
|
802
|
+
let(:tag) { "\x80" }
|
803
|
+
its(:tag) { should == 0 }
|
804
|
+
its(:tag_class) { should == :CONTEXT_SPECIFIC }
|
805
|
+
its(:value) { should == value }
|
806
|
+
it { subject.should be_an_instance_of klass }
|
807
|
+
end
|
808
|
+
end
|
809
|
+
|
810
|
+
context "ASN1Constructive is returned for all non-UNIVERSAL constructed values" do
|
811
|
+
context "implicitly 0-tagged sequence" do
|
812
|
+
let(:tag) { "\xA0" }
|
813
|
+
let(:length) { "\x03" }
|
814
|
+
let(:value) { "\x02\x01\x00" }
|
815
|
+
its(:tag) { should == 0 }
|
816
|
+
its(:tag_class) { should == :CONTEXT_SPECIFIC }
|
817
|
+
it "" do
|
818
|
+
subject.value.should respond_to :each
|
819
|
+
subject.value.size.should == 1
|
820
|
+
int = subject.value.first
|
821
|
+
int.tag.should == Krypt::ASN1::INTEGER
|
822
|
+
int.tag_class.should == :UNIVERSAL
|
823
|
+
int.value.should == 0
|
824
|
+
subject.should be_an_instance_of mod::Constructive
|
825
|
+
end
|
826
|
+
end
|
827
|
+
|
828
|
+
context "explicitly 1-tagged integer" do
|
829
|
+
let(:tag) { "\xA1" }
|
830
|
+
let(:length) { "\x03" }
|
831
|
+
let(:value) { "\x02\x01\x00" }
|
832
|
+
its(:tag) { should == 1 }
|
833
|
+
its(:tag_class) { should == :CONTEXT_SPECIFIC }
|
834
|
+
it "" do
|
835
|
+
subject.should be_an_instance_of mod::Constructive
|
836
|
+
subject.value.should respond_to :each
|
837
|
+
subject.value.size.should == 1
|
838
|
+
int = subject.value.first
|
839
|
+
int.tag.should == Krypt::ASN1::INTEGER
|
840
|
+
int.tag_class.should == :UNIVERSAL
|
841
|
+
int.value.should == 0
|
842
|
+
end
|
843
|
+
end
|
844
|
+
|
845
|
+
context "infinite-length 0-tagged octet string" do
|
846
|
+
let(:tag) { "\xA0" }
|
847
|
+
let(:length) { "\x80" }
|
848
|
+
let(:value) { "\x04\x01\x00\x04\x01\x01\x00\x00" }
|
849
|
+
its(:tag) { should == 0 }
|
850
|
+
its(:tag_class) { should == :CONTEXT_SPECIFIC }
|
851
|
+
its(:infinite_length) { should be_true }
|
852
|
+
it do
|
853
|
+
subject.should be_an_instance_of mod::Constructive
|
854
|
+
subject.value.should respond_to :each
|
855
|
+
subject.value.size.should == 2
|
856
|
+
oct1 = subject.value[0]
|
857
|
+
oct2 = subject.value[1]
|
858
|
+
[oct1, oct2].each do |oct|
|
859
|
+
oct.tag.should == Krypt::ASN1::OCTET_STRING
|
860
|
+
oct.tag_class.should == :UNIVERSAL
|
861
|
+
end
|
862
|
+
oct1.value.should == "\x00"
|
863
|
+
oct2.value.should == "\x01"
|
864
|
+
end
|
865
|
+
end
|
866
|
+
end
|
867
|
+
|
868
|
+
context "drops the EOC when parsing" do
|
869
|
+
context"and encoding again" do
|
870
|
+
let(:tag) { "\x30" }
|
871
|
+
let(:length) { "\x80" }
|
872
|
+
let(:value) { "\x02\x01\x01\x00\x00" }
|
873
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
874
|
+
its(:tag_class) { should == :UNIVERSAL }
|
875
|
+
its(:infinite_length) { should be_true }
|
876
|
+
its(:to_der) { should == "\x30\x80\x02\x01\x01\x00\x00" }
|
877
|
+
end
|
878
|
+
|
879
|
+
context"and inspecting the values" do
|
880
|
+
let(:tag) { "\x30" }
|
881
|
+
let(:length) { "\x80" }
|
882
|
+
let(:value) { "\x02\x01\x01\x00\x00" }
|
883
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
884
|
+
its(:tag_class) { should == :UNIVERSAL }
|
885
|
+
its(:infinite_length) { should be_true }
|
886
|
+
it do
|
887
|
+
subject.value.should respond_to :each
|
888
|
+
subject.value.size.should == 1
|
889
|
+
int = subject.value[0]
|
890
|
+
int.tag.should == Krypt::ASN1::INTEGER
|
891
|
+
int.tag_class.should == :UNIVERSAL
|
892
|
+
int.infinite_length.should be_false
|
893
|
+
subject.to_der.should == "\x30\x80\x02\x01\x01\x00\x00"
|
894
|
+
end
|
895
|
+
end
|
896
|
+
|
897
|
+
context "rejects an encoding without EOC" do
|
898
|
+
let(:tag) { "\x30" }
|
899
|
+
let(:length) { "\x80" }
|
900
|
+
let(:value) { "\x02\x01\x01" }
|
901
|
+
it { -> { subject.value }.should raise_error asn1error }
|
902
|
+
end
|
903
|
+
end
|
904
|
+
|
905
|
+
context "accepts BER redundant length encodings" do
|
906
|
+
let(:tag) { "\x04" }
|
907
|
+
let(:length) { "\x81\x01" }
|
908
|
+
let(:value) { "\x02" }
|
909
|
+
its(:tag) { should == Krypt::ASN1::OCTET_STRING }
|
910
|
+
its(:tag_class) { should == :UNIVERSAL }
|
911
|
+
its(:infinite_length) { should == false }
|
912
|
+
its(:to_der) { should == "#{tag}#{length}#{value}" }
|
913
|
+
it do
|
914
|
+
subject.value = "\x03" #forces re-encoding in DER
|
915
|
+
subject.to_der.should == "\x04\x01\x03"
|
916
|
+
end
|
917
|
+
end
|
918
|
+
|
919
|
+
context "accepts BER redundant length encodings multiple octets" do
|
920
|
+
context do
|
921
|
+
let(:tag) { "\x04" }
|
922
|
+
let(:length) { "\x8A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01" }
|
923
|
+
let(:value) { "\x01" }
|
924
|
+
its(:tag) { should == Krypt::ASN1::OCTET_STRING }
|
925
|
+
its(:tag_class) { should == :UNIVERSAL }
|
926
|
+
its(:infinite_length) { should == false }
|
927
|
+
its(:to_der) { should == "#{tag}#{length}#{value}" }
|
928
|
+
it do
|
929
|
+
subject.value = "\x02" #forces re-encoding in DER
|
930
|
+
subject.to_der.should == "\x04\x01\x02"
|
931
|
+
end
|
932
|
+
end
|
933
|
+
|
934
|
+
context do
|
935
|
+
let(:tag) { "\x80" }
|
936
|
+
let(:length) { "\x8A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" }
|
937
|
+
let(:value) { "" }
|
938
|
+
its(:tag) { should == 0 }
|
939
|
+
its(:tag_class) { should == :CONTEXT_SPECIFIC }
|
940
|
+
its(:infinite_length) { should == false }
|
941
|
+
its(:to_der) { should == "#{tag}#{length}" }
|
942
|
+
it do
|
943
|
+
subject.value = nil #forces re-encoding in DER
|
944
|
+
subject.to_der.should == "\x80\x00"
|
945
|
+
end
|
946
|
+
end
|
947
|
+
end
|
948
|
+
|
949
|
+
context "rejects length encodings that exceed max Fixnum size" do
|
950
|
+
let(:tag) { "\x04" }
|
951
|
+
let(:length) { "\x8A\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" }
|
952
|
+
let(:value) { "\x01" }
|
953
|
+
it { -> { subject }.should raise_error asn1error }
|
954
|
+
end
|
955
|
+
|
956
|
+
context "rejects reserved initial octet 11111111 for long definite length
|
957
|
+
encodings" do
|
958
|
+
let(:tag) { "\x80" }
|
959
|
+
let(:length) { "\xFF\x01\x00" }
|
960
|
+
let(:value) { "" }
|
961
|
+
it { -> { subject }.should raise_error asn1error }
|
962
|
+
end
|
963
|
+
|
964
|
+
context "accepts BER redundant tag encodings single octet" do
|
965
|
+
let(:tag) { "\x1F\x02" }
|
966
|
+
let(:length) { "\x01" }
|
967
|
+
let(:value) { "\x01" }
|
968
|
+
its(:tag) { should == Krypt::ASN1::INTEGER }
|
969
|
+
its(:tag_class) { should == :UNIVERSAL }
|
970
|
+
its(:infinite_length) { should == false }
|
971
|
+
its(:to_der) { should == "#{tag}#{length}#{value}" }
|
972
|
+
it do
|
973
|
+
subject.value = 2 # does not force re-encoding in DER, since tag isn't changed
|
974
|
+
subject.to_der.should == "#{tag}#{length}\x02"
|
975
|
+
subject.tag_class = :CONTEXT_SPECIFIC
|
976
|
+
subject.to_der.should == "\x82\x01\x02"
|
977
|
+
end
|
978
|
+
end
|
979
|
+
|
980
|
+
context "rejects BER complex tag encodings where the first octet has
|
981
|
+
bits 7 to 1 set to 0" do
|
982
|
+
let(:tag) { "\x1F\x80\x80\x02" }
|
983
|
+
let(:length) { "\x01" }
|
984
|
+
let(:value) { "\x01" }
|
985
|
+
it { -> { subject }.should raise_error asn1error }
|
986
|
+
end
|
987
|
+
|
988
|
+
context "rejects complex tag encodings where the tag exceeds max Fixnum
|
989
|
+
value" do
|
990
|
+
let(:tag) { "\x1F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x7F" }
|
991
|
+
let(:length) { "\x00" }
|
992
|
+
let(:value) { "" }
|
993
|
+
it { -> { subject }.should raise_error asn1error }
|
994
|
+
end
|
995
|
+
|
996
|
+
context "rejects infinite length primitive values" do
|
997
|
+
let(:tag) { "\x80" }
|
998
|
+
let(:length) { "\x80" }
|
999
|
+
let(:value) { "\x01\x01\xFF\x00\x00" }
|
1000
|
+
it { -> { subject }.should raise_error asn1error }
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
context "rejects UNIVERSAL tags > 30" do
|
1004
|
+
let(:tag) { "\x1F\x42" }
|
1005
|
+
let(:length) { "\x01" }
|
1006
|
+
let(:value) { "\x00" }
|
1007
|
+
it { -> { subject }.should raise_error asn1error }
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
context "raises ParseError if premature EOF is detected" do
|
1011
|
+
let(:tag) { "\x02" }
|
1012
|
+
let(:length) { "\x02" }
|
1013
|
+
let(:value) { "\x00" }
|
1014
|
+
it { -> { subject }.should raise_error asn1error }
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
context "raises ParseError if header ends prematurely" do
|
1018
|
+
let(:tag) { "" }
|
1019
|
+
let(:length) { "" }
|
1020
|
+
let(:value) { "" }
|
1021
|
+
it { -> { subject }.should raise_error asn1error }
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
it "decodes arbitrary objects that respond to #to_der" do
|
1025
|
+
o = Object.new
|
1026
|
+
def o.to_der
|
1027
|
+
"\x02\x01\x01"
|
1028
|
+
end
|
1029
|
+
asn1 = decoder.decode(o)
|
1030
|
+
asn1.tag.should == mod::INTEGER
|
1031
|
+
asn1.value.should == 1
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
it "decodes files" do
|
1035
|
+
io = Resources.certificate_io
|
1036
|
+
begin
|
1037
|
+
asn1 = decoder.decode(io)
|
1038
|
+
asn1.tag.should == mod::SEQUENCE
|
1039
|
+
asn1.to_der.should == Resources.certificate
|
1040
|
+
ensure
|
1041
|
+
io.close
|
1042
|
+
end
|
1043
|
+
end
|
1044
|
+
|
1045
|
+
context "handles 'unknown' tag number (13) as binary content" do
|
1046
|
+
context "primitive" do
|
1047
|
+
let(:tag) { "\x0D" }
|
1048
|
+
let(:length) { "\x01" }
|
1049
|
+
let(:value) { "\x01" }
|
1050
|
+
its(:tag) { should == 13 }
|
1051
|
+
its(:tag_class) { should == :UNIVERSAL }
|
1052
|
+
its(:value) { should == "\x01" }
|
1053
|
+
end
|
1054
|
+
|
1055
|
+
context "constructive" do
|
1056
|
+
let(:tag) { "\x2D" }
|
1057
|
+
let(:length) { "\x03" }
|
1058
|
+
let(:value) { "\x02\x01\x01" }
|
1059
|
+
its(:tag) { should == 13 }
|
1060
|
+
its(:tag_class) { should == :UNIVERSAL }
|
1061
|
+
its(:value) { should be_an_instance_of Array }
|
1062
|
+
it do
|
1063
|
+
content = subject.value
|
1064
|
+
content.size.should == 1
|
1065
|
+
int = content[0]
|
1066
|
+
int.tag.should == Krypt::ASN1::INTEGER
|
1067
|
+
int.tag_class.should == :UNIVERSAL
|
1068
|
+
int.value.should == 1
|
1069
|
+
end
|
1070
|
+
end
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
it "should parse indefinite length constructive" do
|
1074
|
+
raw = "\x30\x80\x02\x01\x01\x80\x01\x02\x00\x00"
|
1075
|
+
asn1 = decoder.decode(raw)
|
1076
|
+
asn1.value.size.should == 2
|
1077
|
+
asn1.value.any? { |o| o.instance_of? Krypt::ASN1::EndOfContents }.should be_false
|
1078
|
+
end
|
1079
|
+
end
|
1080
|
+
|
1081
|
+
it "should handle IO as an IO" do
|
1082
|
+
io = StringIO.new(
|
1083
|
+
[
|
1084
|
+
Krypt::ASN1::Null.new,
|
1085
|
+
Krypt::ASN1::Integer.new(0)
|
1086
|
+
].map { |e| e.to_der }.join
|
1087
|
+
)
|
1088
|
+
decoder.decode_der(io).should be_an_instance_of Krypt::ASN1::Null
|
1089
|
+
decoder.decode_der(io).should be_an_instance_of Krypt::ASN1::Integer
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
# TODO: Fails for JRuby - bug?
|
1093
|
+
it "should handle generic IOs as an IO" do
|
1094
|
+
stringio = StringIO.new(
|
1095
|
+
[
|
1096
|
+
Krypt::ASN1::Null.new,
|
1097
|
+
Krypt::ASN1::Integer.new(0)
|
1098
|
+
].map { |e| e.to_der }.join
|
1099
|
+
)
|
1100
|
+
c = Class.new do
|
1101
|
+
def initialize(io)
|
1102
|
+
@io = io
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
def read(*args)
|
1106
|
+
@io.read(*args)
|
1107
|
+
end
|
1108
|
+
end
|
1109
|
+
generic = c.new(stringio)
|
1110
|
+
decoder.decode_der(generic).should be_an_instance_of Krypt::ASN1::Null
|
1111
|
+
decoder.decode_der(generic).should be_an_instance_of Krypt::ASN1::Integer
|
1112
|
+
end
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
describe "extracted from ASN1.decode_pem" do
|
1116
|
+
subject { decoder.decode_pem(value) }
|
1117
|
+
|
1118
|
+
context "PEM certificate" do
|
1119
|
+
let(:value) { Resources.certificate_pem }
|
1120
|
+
its(:to_der) { should == Resources.certificate }
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
context "rejects non PEM values" do
|
1124
|
+
let(:value) { Resources.certificate }
|
1125
|
+
it { -> { subject }.should raise_error asn1error }
|
1126
|
+
end
|
1127
|
+
|
1128
|
+
it "decodes files" do
|
1129
|
+
io = Resources.certificate_pem_io
|
1130
|
+
begin
|
1131
|
+
decoder.decode_pem(io).to_der.should == Resources.certificate
|
1132
|
+
ensure
|
1133
|
+
io.close
|
1134
|
+
end
|
1135
|
+
end
|
1136
|
+
|
1137
|
+
context "decodes StringIO" do
|
1138
|
+
let(:value) { StringIO.new(Resources.certificate_pem) }
|
1139
|
+
its(:to_der) { should == Resources.certificate }
|
1140
|
+
end
|
1141
|
+
|
1142
|
+
context "decodes arbitrary objects that respond to #to_pem" do
|
1143
|
+
let(:value) do
|
1144
|
+
o = Object.new
|
1145
|
+
def o.to_pem
|
1146
|
+
Resources.certificate_pem
|
1147
|
+
end
|
1148
|
+
o
|
1149
|
+
end
|
1150
|
+
its(:to_der) { should == Resources.certificate }
|
1151
|
+
end
|
1152
|
+
end
|
1153
|
+
end
|