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,360 @@
|
|
1
|
+
# encoding: US-ASCII
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
require 'krypt'
|
5
|
+
require 'openssl'
|
6
|
+
require_relative './resources'
|
7
|
+
|
8
|
+
describe Krypt::ASN1::Null do
|
9
|
+
include Krypt::ASN1::Resources
|
10
|
+
|
11
|
+
let(:mod) { Krypt::ASN1 }
|
12
|
+
let(:klass) { mod::Null }
|
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::Null
|
22
|
+
class << self
|
23
|
+
alias old_new new
|
24
|
+
def new(*args)
|
25
|
+
if args.size == 1
|
26
|
+
# nothing to do
|
27
|
+
elsif args.size > 0
|
28
|
+
args = [args[0], args[1], :IMPLICIT, args[2]]
|
29
|
+
else
|
30
|
+
args = [nil]
|
31
|
+
end
|
32
|
+
old_new(*args)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#new' do
|
38
|
+
context 'constructs without value' do
|
39
|
+
subject { klass.new }
|
40
|
+
|
41
|
+
its(:tag) { should == Krypt::ASN1::NULL }
|
42
|
+
its(:tag_class) { should == :UNIVERSAL }
|
43
|
+
its(:value) { should == nil }
|
44
|
+
its(:infinite_length) { should == false }
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'gets value for construct' do
|
48
|
+
subject { klass.new(nil) }
|
49
|
+
|
50
|
+
its(:tag) { should == Krypt::ASN1::NULL }
|
51
|
+
its(:tag_class) { should == :UNIVERSAL }
|
52
|
+
its(:value) { should == nil }
|
53
|
+
its(:infinite_length) { should == false }
|
54
|
+
end
|
55
|
+
|
56
|
+
it "only accepts nil as the value argument" do
|
57
|
+
-> { klass.new(1) }.should raise_error(ArgumentError)
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'gets explicit tag number as the 2nd argument' do
|
61
|
+
subject { klass.new(nil, tag, :PRIVATE) }
|
62
|
+
|
63
|
+
context 'accepts default tag' do
|
64
|
+
let(:tag) { Krypt::ASN1::NULL }
|
65
|
+
its(:tag) { should == tag }
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'accepts custom tag' do
|
69
|
+
let(:tag) { 14 }
|
70
|
+
its(:tag) { should == tag }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'gets tag class symbol as the 3rd argument' do
|
75
|
+
subject { klass.new(nil, Krypt::ASN1::NULL, tag_class) }
|
76
|
+
|
77
|
+
context 'accepts :UNIVERSAL' do
|
78
|
+
let(:tag_class) { :UNIVERSAL }
|
79
|
+
its(:tag_class) { should == tag_class }
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'accepts :APPLICATION' do
|
83
|
+
let(:tag_class) { :APPLICATION }
|
84
|
+
its(:tag_class) { should == tag_class }
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'accepts :CONTEXT_SPECIFIC' do
|
88
|
+
let(:tag_class) { :CONTEXT_SPECIFIC }
|
89
|
+
its(:tag_class) { should == tag_class }
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'accepts :PRIVATE' do
|
93
|
+
let(:tag_class) { :PRIVATE }
|
94
|
+
its(:tag_class) { should == tag_class }
|
95
|
+
end
|
96
|
+
|
97
|
+
context 'accepts :IMPLICIT' do
|
98
|
+
let(:tag_class) { :IMPLICIT }
|
99
|
+
its(:tag_class) { should == tag_class }
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'accepts :EXPLICIT' do
|
103
|
+
let(:tag_class) { :EXPLICIT }
|
104
|
+
its(:tag_class) { should == tag_class }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'when the 2nd argument is given but 3rd argument is omitted' do
|
109
|
+
subject { klass.new(nil, Krypt::ASN1::NULL) }
|
110
|
+
its(:tag_class) { should == :CONTEXT_SPECIFIC }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe 'accessors' do
|
115
|
+
describe '#value' do
|
116
|
+
subject { o = klass.new(nil); o.value = value; o }
|
117
|
+
|
118
|
+
context 'accepts nil' do
|
119
|
+
let(:value) { nil }
|
120
|
+
its(:value) { should == nil }
|
121
|
+
end
|
122
|
+
|
123
|
+
it "only accepts nil as the value argument" do
|
124
|
+
asn1 = klass.new
|
125
|
+
asn1.value = 1
|
126
|
+
-> { asn1.to_der }.should raise_error asn1error
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe '#tag' do
|
131
|
+
subject { o = klass.new(nil); o.tag = tag; o }
|
132
|
+
|
133
|
+
context 'accepts default tag' do
|
134
|
+
let(:tag) { Krypt::ASN1::NULL }
|
135
|
+
its(:tag) { should == tag }
|
136
|
+
end
|
137
|
+
|
138
|
+
context 'accepts custom tag' do
|
139
|
+
let(:tag) { 14 }
|
140
|
+
its(:tag) { should == tag }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe '#tag_class' do
|
145
|
+
subject { o = klass.new(nil); o.tag_class = tag_class; o }
|
146
|
+
|
147
|
+
context 'accepts :UNIVERSAL' do
|
148
|
+
let(:tag_class) { :UNIVERSAL }
|
149
|
+
its(:tag_class) { should == tag_class }
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'accepts :APPLICATION' do
|
153
|
+
let(:tag_class) { :APPLICATION }
|
154
|
+
its(:tag_class) { should == tag_class }
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'accepts :CONTEXT_SPECIFIC' do
|
158
|
+
let(:tag_class) { :CONTEXT_SPECIFIC }
|
159
|
+
its(:tag_class) { should == tag_class }
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'accepts :PRIVATE' do
|
163
|
+
let(:tag_class) { :PRIVATE }
|
164
|
+
its(:tag_class) { should == tag_class }
|
165
|
+
end
|
166
|
+
|
167
|
+
context 'accepts :IMPLICIT' do
|
168
|
+
let(:tag_class) { :IMPLICIT }
|
169
|
+
its(:tag_class) { should == tag_class }
|
170
|
+
end
|
171
|
+
|
172
|
+
context 'accepts :EXPLICIT' do
|
173
|
+
let(:tag_class) { :EXPLICIT }
|
174
|
+
its(:tag_class) { should == tag_class }
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe '#to_der' do
|
180
|
+
context 'encodes a given value' do
|
181
|
+
subject { klass.new.to_der }
|
182
|
+
it { should == "\x05\x00" }
|
183
|
+
end
|
184
|
+
|
185
|
+
context 'encodes tag number' do
|
186
|
+
subject { klass.new(nil, tag, :PRIVATE).to_der }
|
187
|
+
|
188
|
+
context 'default tag' do
|
189
|
+
let(:tag) { Krypt::ASN1::NULL }
|
190
|
+
it { should == "\xC5\x00" }
|
191
|
+
end
|
192
|
+
|
193
|
+
context 'custom tag' do
|
194
|
+
let(:tag) { 14 }
|
195
|
+
it { should == "\xCE\x00" }
|
196
|
+
end
|
197
|
+
|
198
|
+
context 'nil' do
|
199
|
+
let(:tag) { nil }
|
200
|
+
it { -> { subject }.should raise_error asn1error }
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
context 'encodes tag class' do
|
205
|
+
subject { klass.new(nil, Krypt::ASN1::NULL, tag_class).to_der }
|
206
|
+
|
207
|
+
context 'UNIVERSAL' do
|
208
|
+
let(:tag_class) { :UNIVERSAL }
|
209
|
+
it { should == "\x05\x00" }
|
210
|
+
end
|
211
|
+
|
212
|
+
context 'APPLICATION' do
|
213
|
+
let(:tag_class) { :APPLICATION }
|
214
|
+
it { should == "\x45\x00" }
|
215
|
+
end
|
216
|
+
|
217
|
+
context 'CONTEXT_SPECIFIC' do
|
218
|
+
let(:tag_class) { :CONTEXT_SPECIFIC }
|
219
|
+
it { should == "\x85\x00" }
|
220
|
+
end
|
221
|
+
|
222
|
+
context 'PRIVATE' do
|
223
|
+
let(:tag_class) { :PRIVATE }
|
224
|
+
it { should == "\xC5\x00" }
|
225
|
+
end
|
226
|
+
|
227
|
+
context 'IMPLICIT' do
|
228
|
+
let(:tag_class) { :IMPLICIT }
|
229
|
+
it { should == "\x85\x00" }
|
230
|
+
end
|
231
|
+
|
232
|
+
context 'EXPLICIT' do
|
233
|
+
let(:tag_class) { :EXPLICIT }
|
234
|
+
it { should == "\xA5\x02\x05\x00" }
|
235
|
+
end
|
236
|
+
|
237
|
+
context nil do
|
238
|
+
let(:tag_class) { nil }
|
239
|
+
it { -> { subject }.should raise_error asn1error } # TODO: ossl does not check nil
|
240
|
+
end
|
241
|
+
|
242
|
+
context :no_such_class do
|
243
|
+
let(:tag_class) { :no_such_class }
|
244
|
+
it { -> { subject }.should raise_error asn1error }
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
context 'encodes values set via accessors' do
|
249
|
+
subject {
|
250
|
+
o = klass.new(nil)
|
251
|
+
o.value = value if defined? value
|
252
|
+
o.tag = tag if defined? tag
|
253
|
+
o.tag_class = tag_class if defined? tag_class
|
254
|
+
o.to_der
|
255
|
+
}
|
256
|
+
|
257
|
+
context 'value: 72' do
|
258
|
+
let(:value) { nil }
|
259
|
+
it { should == "\x05\x00" }
|
260
|
+
end
|
261
|
+
|
262
|
+
context 'custom tag' do
|
263
|
+
let(:value) { nil }
|
264
|
+
let(:tag) { 14 }
|
265
|
+
let(:tag_class) { :PRIVATE }
|
266
|
+
it { should == "\xCE\x00" }
|
267
|
+
end
|
268
|
+
|
269
|
+
context 'tag_class' do
|
270
|
+
let(:value) { nil }
|
271
|
+
let(:tag_class) { :APPLICATION }
|
272
|
+
it { should == "\x45\x00" }
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
describe '#encode_to' do
|
278
|
+
context 'encodes to an IO' do
|
279
|
+
subject { klass.new(value).encode_to(io); io }
|
280
|
+
|
281
|
+
context "StringIO" do
|
282
|
+
let(:value) { nil }
|
283
|
+
let(:io) { string_io_object }
|
284
|
+
its(:written_bytes) { should == "\x05\x00" }
|
285
|
+
end
|
286
|
+
|
287
|
+
context "Object responds to :write" do
|
288
|
+
let(:value) { nil }
|
289
|
+
let(:io) { writable_object }
|
290
|
+
its(:written_bytes) { should == "\x05\x00" }
|
291
|
+
end
|
292
|
+
|
293
|
+
context "raise IO error transparently" do
|
294
|
+
let(:value) { nil }
|
295
|
+
let(:io) { io_error_object }
|
296
|
+
it { -> { subject }.should raise_error asn1error }
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
it 'returns self' do
|
301
|
+
obj = klass.new(nil)
|
302
|
+
obj.encode_to(string_io_object).should == obj
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
describe 'extracted from ASN1.decode' do
|
307
|
+
subject { decoder.decode(der) }
|
308
|
+
|
309
|
+
context 'extracted value' do
|
310
|
+
let(:der) { "\x05\x00" }
|
311
|
+
its(:class) { should == klass }
|
312
|
+
its(:tag) { should == Krypt::ASN1::NULL }
|
313
|
+
its(:value) { should == nil }
|
314
|
+
end
|
315
|
+
|
316
|
+
context 'extracted tag class' do
|
317
|
+
context 'UNIVERSAL' do
|
318
|
+
let(:der) { "\x05\x00" }
|
319
|
+
its(:tag_class) { should == :UNIVERSAL }
|
320
|
+
end
|
321
|
+
|
322
|
+
context 'APPLICATION' do
|
323
|
+
let(:der) { "\x45\x00" }
|
324
|
+
its(:tag_class) { should == :APPLICATION }
|
325
|
+
end
|
326
|
+
|
327
|
+
context 'CONTEXT_SPECIFIC' do
|
328
|
+
let(:der) { "\x85\x00" }
|
329
|
+
its(:tag_class) { should == :CONTEXT_SPECIFIC }
|
330
|
+
end
|
331
|
+
|
332
|
+
context 'PRIVATE' do
|
333
|
+
let(:der) { "\xC5\x00" }
|
334
|
+
its(:tag_class) { should == :PRIVATE }
|
335
|
+
end
|
336
|
+
|
337
|
+
context "setting IMPLICIT will result in CONTEXT_SPECIFIC" do
|
338
|
+
let(:der) { "\x05\x00" }
|
339
|
+
it do
|
340
|
+
subject.tag_class = :IMPLICIT
|
341
|
+
subject.to_der.should == "\x85\x00"
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
context "setting EXPLICIT will reencode as CONTEXT_SPECIFIC" do
|
346
|
+
let(:der) { "\x05\x00" }
|
347
|
+
it do
|
348
|
+
subject.tag_class = :EXPLICIT
|
349
|
+
subject.tag = 0
|
350
|
+
subject.to_der.should == "\xA0\x02\x05\x00"
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
context 'rejects values with a length > 0' do
|
356
|
+
let(:der) { "\x05\x01\x00" }
|
357
|
+
it { -> { subject.value }.should raise_error asn1error }
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
@@ -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::ObjectId do
|
9
|
+
include Krypt::ASN1::Resources
|
10
|
+
|
11
|
+
let(:mod) { Krypt::ASN1 }
|
12
|
+
let(:klass) { mod::ObjectId }
|
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::ObjectId
|
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 1.0.8571.2' do
|
38
|
+
let(:value) { '1.0.8571.2' }
|
39
|
+
its(:tag) { should == Krypt::ASN1::OBJECT_ID }
|
40
|
+
its(:tag_class) { should == :UNIVERSAL }
|
41
|
+
its(:value) { should == '1.0.8571.2' }
|
42
|
+
its(:infinite_length) { should == false }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'gets explicit tag number as the 2nd argument' do
|
47
|
+
subject { klass.new('1.0.8571.2', tag, :PRIVATE) }
|
48
|
+
|
49
|
+
context 'accepts default tag' do
|
50
|
+
let(:tag) { Krypt::ASN1::OBJECT_ID }
|
51
|
+
its(:tag) { should == tag }
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'accepts custom tag' do
|
55
|
+
let(:tag) { 14 }
|
56
|
+
its(:tag) { should == tag }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'gets tag class symbol as the 3rd argument' do
|
61
|
+
subject { klass.new('1.0.8571.2', Krypt::ASN1::OBJECT_ID, tag_class) }
|
62
|
+
|
63
|
+
context 'accepts :UNIVERSAL' do
|
64
|
+
let(:tag_class) { :UNIVERSAL }
|
65
|
+
its(:tag_class) { should == tag_class }
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'accepts :APPLICATION' do
|
69
|
+
let(:tag_class) { :APPLICATION }
|
70
|
+
its(:tag_class) { should == tag_class }
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'accepts :CONTEXT_SPECIFIC' do
|
74
|
+
let(:tag_class) { :CONTEXT_SPECIFIC }
|
75
|
+
its(:tag_class) { should == tag_class }
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'accepts :PRIVATE' do
|
79
|
+
let(:tag_class) { :PRIVATE }
|
80
|
+
its(:tag_class) { should == tag_class }
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'accepts :IMPLICIT' do
|
84
|
+
let(:tag_class) { :IMPLICIT }
|
85
|
+
its(:tag_class) { should == tag_class }
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'accepts :EXPLICIT' do
|
89
|
+
let(:tag_class) { :EXPLICIT }
|
90
|
+
its(:tag_class) { should == tag_class }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'when the 2nd argument is given but 3rd argument is omitted' do
|
95
|
+
subject { klass.new('1.0.8571.2', Krypt::ASN1::OBJECT_ID) }
|
96
|
+
its(:tag_class) { should == :CONTEXT_SPECIFIC }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe 'accessors' do
|
101
|
+
describe '#value' do
|
102
|
+
subject { o = klass.new(nil); o.value = value; o }
|
103
|
+
|
104
|
+
context 'accepts 1.0.8571.2' do
|
105
|
+
let(:value) { '1.0.8571.2' }
|
106
|
+
its(:tag) { should == Krypt::ASN1::OBJECT_ID }
|
107
|
+
its(:tag_class) { should == :UNIVERSAL }
|
108
|
+
its(:value) { should == '1.0.8571.2' }
|
109
|
+
its(:infinite_length) { should == false }
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe '#tag' do
|
114
|
+
subject { o = klass.new(nil); o.tag = tag; o }
|
115
|
+
|
116
|
+
context 'accepts default tag' do
|
117
|
+
let(:tag) { Krypt::ASN1::OBJECT_ID }
|
118
|
+
its(:tag) { should == tag }
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'accepts custom tag' do
|
122
|
+
let(:tag) { 14 }
|
123
|
+
its(:tag) { should == tag }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe '#tag_class' do
|
128
|
+
subject { o = klass.new(nil); o.tag_class = tag_class; o }
|
129
|
+
|
130
|
+
context 'accepts :UNIVERSAL' do
|
131
|
+
let(:tag_class) { :UNIVERSAL }
|
132
|
+
its(:tag_class) { should == tag_class }
|
133
|
+
end
|
134
|
+
|
135
|
+
context 'accepts :APPLICATION' do
|
136
|
+
let(:tag_class) { :APPLICATION }
|
137
|
+
its(:tag_class) { should == tag_class }
|
138
|
+
end
|
139
|
+
|
140
|
+
context 'accepts :CONTEXT_SPECIFIC' do
|
141
|
+
let(:tag_class) { :CONTEXT_SPECIFIC }
|
142
|
+
its(:tag_class) { should == tag_class }
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'accepts :PRIVATE' do
|
146
|
+
let(:tag_class) { :PRIVATE }
|
147
|
+
its(:tag_class) { should == tag_class }
|
148
|
+
end
|
149
|
+
|
150
|
+
context 'accepts :IMPLICIT' do
|
151
|
+
let(:tag_class) { :IMPLICIT }
|
152
|
+
its(:tag_class) { should == tag_class }
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'accepts :EXPLICIT' do
|
156
|
+
let(:tag_class) { :EXPLICIT }
|
157
|
+
its(:tag_class) { should == tag_class }
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe '#to_der' do
|
163
|
+
context 'encodes a given value' do
|
164
|
+
subject { klass.new(value).to_der }
|
165
|
+
|
166
|
+
context '1.0.8571.2' do
|
167
|
+
let(:value) { '1.0.8571.2' }
|
168
|
+
it { should == "\x06\x04\x28\xC2\x7B\x02" }
|
169
|
+
end
|
170
|
+
|
171
|
+
context '(empty)' do
|
172
|
+
let(:value) { '' }
|
173
|
+
it { -> { subject }.should raise_error asn1error }
|
174
|
+
end
|
175
|
+
|
176
|
+
context '1' do
|
177
|
+
let(:value) { '1' }
|
178
|
+
it { -> { subject }.should raise_error asn1error }
|
179
|
+
end
|
180
|
+
|
181
|
+
# oid[0] ::= 0, 1, 2
|
182
|
+
# oid[1] ::= 0, 1, 2, 3
|
183
|
+
# v[0] ::= oid[0] * 40 + oid[1]
|
184
|
+
context '2 octets optimization' do
|
185
|
+
context '0.0' do
|
186
|
+
let(:value) { '0.0' }
|
187
|
+
it { should == "\x06\x01\x00" }
|
188
|
+
end
|
189
|
+
|
190
|
+
context '0.3' do
|
191
|
+
let(:value) { '0.3' }
|
192
|
+
it { should == "\x06\x01\x03" }
|
193
|
+
end
|
194
|
+
|
195
|
+
context '1.0' do
|
196
|
+
let(:value) { '1.0' }
|
197
|
+
it { should == "\x06\x01\x28" }
|
198
|
+
end
|
199
|
+
|
200
|
+
context '1.3' do
|
201
|
+
let(:value) { '1.3' }
|
202
|
+
it { should == "\x06\x01\x2B" }
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
context '0.0.0.....0' do
|
207
|
+
let(:value) { (['0'] * 999).join('.') }
|
208
|
+
it { should == "\x06\x82\x03\xE6\x00" + "\x00" * 997 }
|
209
|
+
end
|
210
|
+
|
211
|
+
context '1.1.1.....1' do
|
212
|
+
let(:value) { (['1'] * 1000).join('.') }
|
213
|
+
it { should == "\x06\x82\x03\xE7\x29" + "\x01" * 998 }
|
214
|
+
end
|
215
|
+
|
216
|
+
context 'nil' do
|
217
|
+
let(:value) { nil }
|
218
|
+
it { -> { subject }.should raise_error asn1error } # TODO: ossl does not check value
|
219
|
+
end
|
220
|
+
|
221
|
+
context '(empty)' do
|
222
|
+
let(:value) { '' }
|
223
|
+
it { -> { subject }.should raise_error asn1error } # TODO: ossl does not check value
|
224
|
+
end
|
225
|
+
|
226
|
+
context 'single octet' do
|
227
|
+
let(:value) { '1' }
|
228
|
+
it { -> { subject }.should raise_error asn1error } # TODO: ossl does not check value
|
229
|
+
end
|
230
|
+
|
231
|
+
context 'non OID format' do
|
232
|
+
let(:value) { '1,0:1' }
|
233
|
+
it { -> { subject }.should raise_error asn1error } # TODO: ossl does not check value
|
234
|
+
end
|
235
|
+
|
236
|
+
context 'non number id' do
|
237
|
+
let(:value) { '1.0.ABC' }
|
238
|
+
it { -> { subject }.should raise_error asn1error } # TODO: ossl does not check value
|
239
|
+
end
|
240
|
+
|
241
|
+
context 'starts with .' do
|
242
|
+
let(:value) { '.0.8571.2' }
|
243
|
+
it { -> { subject }.should raise_error asn1error } # TODO: ossl does not check value
|
244
|
+
end
|
245
|
+
|
246
|
+
context 'illegal first octet (must be 0..2)' do
|
247
|
+
let(:value) { '3.0.8571.2' }
|
248
|
+
it { -> { subject }.should raise_error asn1error } # TODO: ossl does not check value
|
249
|
+
end
|
250
|
+
|
251
|
+
context 'illegal second octet (must be 0..39)' do
|
252
|
+
let(:value) { '1.40.8571.2' }
|
253
|
+
it { -> { subject }.should raise_error asn1error } # TODO: ossl does not check value
|
254
|
+
end
|
255
|
+
|
256
|
+
context 'rejects sub identifiers in the bignum range' do
|
257
|
+
let(:value) { "1.2." + "3" * 1000 + "4.5" }
|
258
|
+
it { -> { subject }.should raise_error asn1error }
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
context 'encodes tag number' do
|
263
|
+
subject { klass.new('1.0.8571.2', tag, :PRIVATE).to_der }
|
264
|
+
|
265
|
+
context 'default tag' do
|
266
|
+
let(:tag) { 6 }
|
267
|
+
it { should == "\xC6\x04\x28\xC2\x7B\x02" }
|
268
|
+
end
|
269
|
+
|
270
|
+
context 'custom tag' do
|
271
|
+
let(:tag) { 14 }
|
272
|
+
it { should == "\xCE\x04\x28\xC2\x7B\x02" }
|
273
|
+
end
|
274
|
+
|
275
|
+
context 'nil' do
|
276
|
+
let(:tag) { nil }
|
277
|
+
it { -> { subject }.should raise_error asn1error }
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
context 'encodes tag class' do
|
282
|
+
subject { klass.new('1.0.8571.2', Krypt::ASN1::OBJECT_ID, tag_class).to_der }
|
283
|
+
|
284
|
+
context 'UNIVERSAL' do
|
285
|
+
let(:tag_class) { :UNIVERSAL }
|
286
|
+
it { should == "\x06\x04\x28\xC2\x7B\x02" }
|
287
|
+
end
|
288
|
+
|
289
|
+
context 'APPLICATION' do
|
290
|
+
let(:tag_class) { :APPLICATION }
|
291
|
+
it { should == "\x46\x04\x28\xC2\x7B\x02" }
|
292
|
+
end
|
293
|
+
|
294
|
+
context 'CONTEXT_SPECIFIC' do
|
295
|
+
let(:tag_class) { :CONTEXT_SPECIFIC }
|
296
|
+
it { should == "\x86\x04\x28\xC2\x7B\x02" }
|
297
|
+
end
|
298
|
+
|
299
|
+
context 'PRIVATE' do
|
300
|
+
let(:tag_class) { :PRIVATE }
|
301
|
+
it { should == "\xC6\x04\x28\xC2\x7B\x02" }
|
302
|
+
end
|
303
|
+
|
304
|
+
context 'IMPLICIT' do
|
305
|
+
let(:tag_class) { :IMPLICIT }
|
306
|
+
it { should == "\x86\x04\x28\xC2\x7B\x02" }
|
307
|
+
end
|
308
|
+
|
309
|
+
context 'EXPLICIT' do
|
310
|
+
let(:tag_class) { :EXPLICIT }
|
311
|
+
it { should == "\xA6\x06\x06\x04\x28\xC2\x7B\x02" }
|
312
|
+
end
|
313
|
+
|
314
|
+
context nil do
|
315
|
+
let(:tag_class) { nil }
|
316
|
+
it { -> { subject }.should raise_error asn1error } # TODO: ossl does not check nil
|
317
|
+
end
|
318
|
+
|
319
|
+
context :no_such_class do
|
320
|
+
let(:tag_class) { :no_such_class }
|
321
|
+
it { -> { subject }.should raise_error asn1error }
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
context 'encodes values set via accessors' do
|
326
|
+
subject {
|
327
|
+
o = klass.new(nil)
|
328
|
+
o.value = value if defined? value
|
329
|
+
o.tag = tag if defined? tag
|
330
|
+
o.tag_class = tag_class if defined? tag_class
|
331
|
+
o.to_der
|
332
|
+
}
|
333
|
+
|
334
|
+
context 'value: 1.0.8571.2' do
|
335
|
+
let(:value) { '1.0.8571.2' }
|
336
|
+
it { should == "\x06\x04\x28\xC2\x7B\x02" }
|
337
|
+
end
|
338
|
+
|
339
|
+
context 'custom tag' do
|
340
|
+
let(:value) { '1.0.8571.2' }
|
341
|
+
let(:tag) { 14 }
|
342
|
+
let(:tag_class) { :PRIVATE }
|
343
|
+
it { should == "\xCE\x04\x28\xC2\x7B\x02" }
|
344
|
+
end
|
345
|
+
|
346
|
+
context 'tag_class' do
|
347
|
+
let(:value) { '1.0.8571.2' }
|
348
|
+
let(:tag_class) { :APPLICATION }
|
349
|
+
it { should == "\x46\x04\x28\xC2\x7B\x02" }
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
describe '#encode_to' do
|
355
|
+
context 'encodes to an IO' do
|
356
|
+
subject { klass.new(value).encode_to(io); io }
|
357
|
+
|
358
|
+
context "StringIO" do
|
359
|
+
let(:value) { '1.0.8571.2' }
|
360
|
+
let(:io) { string_io_object }
|
361
|
+
its(:written_bytes) { should == "\x06\x04\x28\xC2\x7B\x02" }
|
362
|
+
end
|
363
|
+
|
364
|
+
context "Object responds to :write" do
|
365
|
+
let(:value) { '1.0.8571.2' }
|
366
|
+
let(:io) { writable_object }
|
367
|
+
its(:written_bytes) { should == "\x06\x04\x28\xC2\x7B\x02" }
|
368
|
+
end
|
369
|
+
|
370
|
+
context "raise IO error transparently" do
|
371
|
+
let(:value) { '1.0.8571.2' }
|
372
|
+
let(:io) { io_error_object }
|
373
|
+
it { -> { subject }.should raise_error asn1error }
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
it 'returns self' do
|
378
|
+
obj = klass.new('1.0.8571.2')
|
379
|
+
obj.encode_to(string_io_object).should == obj
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
describe 'extracted from ASN1.decode' do
|
384
|
+
subject { decoder.decode(der) }
|
385
|
+
|
386
|
+
context 'extracted value' do
|
387
|
+
context '1.0.8571.2' do
|
388
|
+
let(:der) { "\x06\x04\x28\xC2\x7B\x02" }
|
389
|
+
its(:class) { should == klass }
|
390
|
+
its(:tag) { should == Krypt::ASN1::OBJECT_ID }
|
391
|
+
its(:value) { should == '1.0.8571.2' }
|
392
|
+
end
|
393
|
+
|
394
|
+
context '2 octets optimization' do
|
395
|
+
context '0.0' do
|
396
|
+
let(:der) { "\x06\x01\x00" }
|
397
|
+
its(:class) { should == klass }
|
398
|
+
its(:tag) { should == Krypt::ASN1::OBJECT_ID }
|
399
|
+
its(:value) { should == '0.0' } # "ITU-T" in OpenSSL
|
400
|
+
end
|
401
|
+
|
402
|
+
context '0.3' do
|
403
|
+
let(:der) { "\x06\x01\x03" }
|
404
|
+
its(:class) { should == klass }
|
405
|
+
its(:tag) { should == Krypt::ASN1::OBJECT_ID }
|
406
|
+
its(:value) { should == '0.3' }
|
407
|
+
end
|
408
|
+
|
409
|
+
context '1.0' do
|
410
|
+
let(:der) { "\x06\x01\x28" }
|
411
|
+
its(:class) { should == klass }
|
412
|
+
its(:tag) { should == Krypt::ASN1::OBJECT_ID }
|
413
|
+
its(:value) { should == '1.0' } # "ISO" in OpenSSL
|
414
|
+
end
|
415
|
+
|
416
|
+
context '1.3' do
|
417
|
+
let(:der) { "\x06\x01\x2B" }
|
418
|
+
its(:class) { should == klass }
|
419
|
+
its(:tag) { should == Krypt::ASN1::OBJECT_ID }
|
420
|
+
its(:value) { should == '1.3' } # "identified-organization" in OpenSSL
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
context '0.0.0.....0' do
|
425
|
+
let(:der) { "\x06\x82\x03\xE6\x00" + "\x00" * 997 }
|
426
|
+
its(:class) { should == klass }
|
427
|
+
its(:tag) { should == Krypt::ASN1::OBJECT_ID }
|
428
|
+
its(:value) { should == (['0'] * 999).join('.') }
|
429
|
+
end
|
430
|
+
|
431
|
+
context '1.1.1.....1' do
|
432
|
+
let(:der) { "\x06\x82\x03\xE7\x29" + "\x01" * 998 }
|
433
|
+
its(:class) { should == klass }
|
434
|
+
its(:tag) { should == Krypt::ASN1::OBJECT_ID }
|
435
|
+
its(:value) { should == (['1'] * 1000).join('.') }
|
436
|
+
end
|
437
|
+
|
438
|
+
context 'Illegal first octet too large (3.50.2.3)' do
|
439
|
+
let(:der) { "\x06\x03\xAA\x02\x03" }
|
440
|
+
it { -> { subject.value }.should raise_error asn1error }
|
441
|
+
end
|
442
|
+
|
443
|
+
context 'Illegal first sub id (4.2.0.0)' do
|
444
|
+
let(:der) { "\x06\x03\xA2\x00\x00" }
|
445
|
+
it { -> { subject.value }.should raise_error asn1error }
|
446
|
+
end
|
447
|
+
|
448
|
+
describe 'We cannot prevent this mistake, so the parsed value will be different than expected' do
|
449
|
+
context 'Illegal second sub id (1.40.0.0)' do
|
450
|
+
let(:der) { "\x06\x03\x50\x00\x00" }
|
451
|
+
its(:value) { should == "2.0.0.0" }
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
context 'extracted tag class' do
|
457
|
+
context 'UNIVERSAL' do
|
458
|
+
let(:der) { "\x06\x04\x28\xC2\x7B\x02" }
|
459
|
+
its(:tag_class) { should == :UNIVERSAL }
|
460
|
+
end
|
461
|
+
|
462
|
+
context 'APPLICATION' do
|
463
|
+
let(:der) { "\x46\x04\x28\xC2\x7B\x02" }
|
464
|
+
its(:tag_class) { should == :APPLICATION }
|
465
|
+
end
|
466
|
+
|
467
|
+
context 'CONTEXT_SPECIFIC' do
|
468
|
+
let(:der) { "\x86\x04\x28\xC2\x7B\x02" }
|
469
|
+
its(:tag_class) { should == :CONTEXT_SPECIFIC }
|
470
|
+
end
|
471
|
+
|
472
|
+
context 'PRIVATE' do
|
473
|
+
let(:der) { "\xC6\x04\x28\xC2\x7B\x02" }
|
474
|
+
its(:tag_class) { should == :PRIVATE }
|
475
|
+
end
|
476
|
+
|
477
|
+
context "setting IMPLICIT will result in CONTEXT_SPECIFIC" do
|
478
|
+
let(:der) { "\x06\x04\x28\xC2\x7B\x02" }
|
479
|
+
it do
|
480
|
+
subject.tag_class = :IMPLICIT
|
481
|
+
subject.to_der.should == "\x86\x04\x28\xC2\x7B\x02"
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
context "setting EXPLICIT will reencode as CONTEXT_SPECIFIC" do
|
486
|
+
let(:der) { "\x06\x04\x28\xC2\x7B\x02" }
|
487
|
+
it do
|
488
|
+
subject.tag_class = :EXPLICIT
|
489
|
+
subject.tag = 0
|
490
|
+
subject.to_der.should == "\xA0\x06\x06\x04\x28\xC2\x7B\x02"
|
491
|
+
end
|
492
|
+
end
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|