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,282 @@
|
|
1
|
+
# encoding: US-ASCII
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
require 'krypt'
|
5
|
+
require 'base64'
|
6
|
+
require_relative './resources'
|
7
|
+
require_relative '../resources'
|
8
|
+
|
9
|
+
describe Krypt::ASN1 do
|
10
|
+
include Krypt::ASN1::Resources
|
11
|
+
|
12
|
+
let(:mod) { Krypt::ASN1 }
|
13
|
+
let(:decoder) { mod }
|
14
|
+
let(:asn1error) { mod::ASN1Error }
|
15
|
+
|
16
|
+
def create_pem_b64(b64, name)
|
17
|
+
"-----BEGIN #{name}-----\n#{b64}-----END #{name}-----\n"
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#decode" do
|
21
|
+
subject { decoder.decode(value) }
|
22
|
+
|
23
|
+
context "accepts regular DER-encoded values" do
|
24
|
+
let(:value) { "\x02\x01\x01" }
|
25
|
+
its(:tag) { should == Krypt::ASN1::INTEGER }
|
26
|
+
its(:tag_class) { should == :UNIVERSAL }
|
27
|
+
its(:value) { should == 1 }
|
28
|
+
its(:infinite_length) { should == false }
|
29
|
+
end
|
30
|
+
|
31
|
+
context "also accepts PEM-encoded values" do
|
32
|
+
let(:value) { create_pem_b64(Base64.encode64("\x02\x01\x01"), "INTEGER") }
|
33
|
+
its(:tag) { should == Krypt::ASN1::INTEGER }
|
34
|
+
its(:tag_class) { should == :UNIVERSAL }
|
35
|
+
its(:value) { should == 1 }
|
36
|
+
its(:infinite_length) { should == false }
|
37
|
+
end
|
38
|
+
|
39
|
+
context "accepts IO" do
|
40
|
+
subject do
|
41
|
+
begin
|
42
|
+
decoder.decode(io)
|
43
|
+
ensure
|
44
|
+
io.close
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "regular DER-encoded IO" do
|
49
|
+
let(:io) { Resources.certificate_io }
|
50
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
51
|
+
its(:tag_class) { should == :UNIVERSAL }
|
52
|
+
its(:infinite_length) { should == false }
|
53
|
+
its(:value) { should be_an_instance_of Array }
|
54
|
+
its(:to_der) { should == Resources.certificate }
|
55
|
+
end
|
56
|
+
|
57
|
+
context "regular PEM-encoded IO" do
|
58
|
+
let(:io) { Resources.certificate_pem_io }
|
59
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
60
|
+
its(:tag_class) { should == :UNIVERSAL }
|
61
|
+
its(:infinite_length) { should == false }
|
62
|
+
its(:value) { should be_an_instance_of Array }
|
63
|
+
its(:to_der) { should == Resources.certificate }
|
64
|
+
end
|
65
|
+
|
66
|
+
context "regular DER-encoded StringIO" do
|
67
|
+
let(:io) { StringIO.new(Resources.certificate) }
|
68
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
69
|
+
its(:tag_class) { should == :UNIVERSAL }
|
70
|
+
its(:infinite_length) { should == false }
|
71
|
+
its(:value) { should be_an_instance_of Array }
|
72
|
+
its(:to_der) { should == Resources.certificate }
|
73
|
+
end
|
74
|
+
|
75
|
+
context "regular PEM-encoded StringIO" do
|
76
|
+
let(:io) { StringIO.new(Resources.certificate_pem) }
|
77
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
78
|
+
its(:tag_class) { should == :UNIVERSAL }
|
79
|
+
its(:infinite_length) { should == false }
|
80
|
+
its(:value) { should be_an_instance_of Array }
|
81
|
+
its(:to_der) { should == Resources.certificate }
|
82
|
+
end
|
83
|
+
|
84
|
+
context "DER-encoded IO-like value that does not support rewinding" do
|
85
|
+
let(:io) do
|
86
|
+
c = Class.new do
|
87
|
+
def initialize
|
88
|
+
@io = Resources.certificate_io
|
89
|
+
end
|
90
|
+
|
91
|
+
def read(len=nil, buf=nil)
|
92
|
+
@io.read(len, buf)
|
93
|
+
end
|
94
|
+
|
95
|
+
def seek(amount, whence=IO::SEEK_SET)
|
96
|
+
raise RuntimeError.new
|
97
|
+
end
|
98
|
+
|
99
|
+
def close
|
100
|
+
@io.close
|
101
|
+
end
|
102
|
+
end
|
103
|
+
c.new
|
104
|
+
end
|
105
|
+
|
106
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
107
|
+
its(:tag_class) { should == :UNIVERSAL }
|
108
|
+
its(:infinite_length) { should == false }
|
109
|
+
its(:value) { should be_an_instance_of Array }
|
110
|
+
its(:to_der) { should == Resources.certificate }
|
111
|
+
end
|
112
|
+
|
113
|
+
context "PEM-encoded IO-like value that does not support rewinding" do
|
114
|
+
let(:io) do
|
115
|
+
c = Class.new do
|
116
|
+
def initialize
|
117
|
+
@io = Resources.certificate_pem_io
|
118
|
+
end
|
119
|
+
|
120
|
+
def read(len=nil, buf=nil)
|
121
|
+
@io.read(len, buf)
|
122
|
+
end
|
123
|
+
|
124
|
+
def seek(amount, whence=IO::SEEK_SET)
|
125
|
+
raise RuntimeError.new
|
126
|
+
end
|
127
|
+
|
128
|
+
def close
|
129
|
+
@io.close
|
130
|
+
end
|
131
|
+
end
|
132
|
+
c.new
|
133
|
+
end
|
134
|
+
|
135
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
136
|
+
its(:tag_class) { should == :UNIVERSAL }
|
137
|
+
its(:infinite_length) { should == false }
|
138
|
+
its(:value) { should be_an_instance_of Array }
|
139
|
+
its(:to_der) { should == Resources.certificate }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe "#decode_der" do
|
145
|
+
subject { decoder.decode_der(value) }
|
146
|
+
|
147
|
+
context "accepts regular DER-encoded values" do
|
148
|
+
let(:value) { "\x02\x01\x01" }
|
149
|
+
its(:tag) { should == Krypt::ASN1::INTEGER }
|
150
|
+
its(:tag_class) { should == :UNIVERSAL }
|
151
|
+
its(:value) { should == 1 }
|
152
|
+
its(:infinite_length) { should == false }
|
153
|
+
end
|
154
|
+
|
155
|
+
context "accepts IO" do
|
156
|
+
subject do
|
157
|
+
begin
|
158
|
+
decoder.decode_der(io)
|
159
|
+
ensure
|
160
|
+
io.close
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context "regular DER-encoded IO" do
|
165
|
+
let(:io) { Resources.certificate_io }
|
166
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
167
|
+
its(:tag_class) { should == :UNIVERSAL }
|
168
|
+
its(:infinite_length) { should == false }
|
169
|
+
its(:value) { should be_an_instance_of Array }
|
170
|
+
its(:to_der) { should == Resources.certificate }
|
171
|
+
end
|
172
|
+
|
173
|
+
context "regular DER-encoded StringIO" do
|
174
|
+
let(:io) { StringIO.new(Resources.certificate) }
|
175
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
176
|
+
its(:tag_class) { should == :UNIVERSAL }
|
177
|
+
its(:infinite_length) { should == false }
|
178
|
+
its(:value) { should be_an_instance_of Array }
|
179
|
+
its(:to_der) { should == Resources.certificate }
|
180
|
+
end
|
181
|
+
|
182
|
+
context "DER-encoded IO-like value that does not support rewinding" do
|
183
|
+
let(:io) do
|
184
|
+
c = Class.new do
|
185
|
+
def initialize
|
186
|
+
@io = Resources.certificate_io
|
187
|
+
end
|
188
|
+
|
189
|
+
def read(len=nil, buf=nil)
|
190
|
+
@io.read(len, buf)
|
191
|
+
end
|
192
|
+
|
193
|
+
def seek(amount, whence=IO::SEEK_SET)
|
194
|
+
raise RuntimeError.new
|
195
|
+
end
|
196
|
+
|
197
|
+
def close
|
198
|
+
@io.close
|
199
|
+
end
|
200
|
+
end
|
201
|
+
c.new
|
202
|
+
end
|
203
|
+
|
204
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
205
|
+
its(:tag_class) { should == :UNIVERSAL }
|
206
|
+
its(:infinite_length) { should == false }
|
207
|
+
its(:value) { should be_an_instance_of Array }
|
208
|
+
its(:to_der) { should == Resources.certificate }
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
describe "#decode_pem" do
|
214
|
+
subject { decoder.decode_pem(value) }
|
215
|
+
|
216
|
+
context "accepts regular PEM-encoded values" do
|
217
|
+
let(:value) { create_pem_b64(Base64.encode64("\x02\x01\x01"), "INTEGER") }
|
218
|
+
its(:tag) { should == Krypt::ASN1::INTEGER }
|
219
|
+
its(:tag_class) { should == :UNIVERSAL }
|
220
|
+
its(:value) { should == 1 }
|
221
|
+
its(:infinite_length) { should == false }
|
222
|
+
end
|
223
|
+
|
224
|
+
context "accepts IO" do
|
225
|
+
subject do
|
226
|
+
begin
|
227
|
+
decoder.decode_pem(io)
|
228
|
+
ensure
|
229
|
+
io.close
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
context "regular PEM-encoded IO" do
|
234
|
+
let(:io) { Resources.certificate_pem_io }
|
235
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
236
|
+
its(:tag_class) { should == :UNIVERSAL }
|
237
|
+
its(:infinite_length) { should == false }
|
238
|
+
its(:value) { should be_an_instance_of Array }
|
239
|
+
its(:to_der) { should == Resources.certificate }
|
240
|
+
end
|
241
|
+
|
242
|
+
context "regular PEM-encoded StringIO" do
|
243
|
+
let(:io) { StringIO.new(Resources.certificate_pem) }
|
244
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
245
|
+
its(:tag_class) { should == :UNIVERSAL }
|
246
|
+
its(:infinite_length) { should == false }
|
247
|
+
its(:value) { should be_an_instance_of Array }
|
248
|
+
its(:to_der) { should == Resources.certificate }
|
249
|
+
end
|
250
|
+
|
251
|
+
context "PEM-encoded IO-like value that does not support rewinding" do
|
252
|
+
let(:io) do
|
253
|
+
c = Class.new do
|
254
|
+
def initialize
|
255
|
+
@io = Resources.certificate_pem_io
|
256
|
+
end
|
257
|
+
|
258
|
+
def read(len=nil, buf=nil)
|
259
|
+
@io.read(len, buf)
|
260
|
+
end
|
261
|
+
|
262
|
+
def seek(amount, whence=IO::SEEK_SET)
|
263
|
+
raise RuntimeError.new
|
264
|
+
end
|
265
|
+
|
266
|
+
def close
|
267
|
+
@io.close
|
268
|
+
end
|
269
|
+
end
|
270
|
+
c.new
|
271
|
+
end
|
272
|
+
|
273
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
274
|
+
its(:tag_class) { should == :UNIVERSAL }
|
275
|
+
its(:infinite_length) { should == false }
|
276
|
+
its(:value) { should be_an_instance_of Array }
|
277
|
+
its(:to_der) { should == Resources.certificate }
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
@@ -0,0 +1,637 @@
|
|
1
|
+
# encoding: US-ASCII
|
2
|
+
|
3
|
+
require 'rspec'
|
4
|
+
require 'krypt'
|
5
|
+
require 'openssl'
|
6
|
+
require_relative './resources'
|
7
|
+
|
8
|
+
describe Krypt::ASN1::Sequence do
|
9
|
+
include Krypt::ASN1::Resources
|
10
|
+
|
11
|
+
let(:mod) { Krypt::ASN1 }
|
12
|
+
let(:klass) { mod::Sequence }
|
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::Sequence
|
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 SEQUENCE as Array' do
|
38
|
+
let(:value) { [s('hello'), i(42), s('world')] }
|
39
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
40
|
+
its(:tag_class) { should == :UNIVERSAL }
|
41
|
+
its(:value) { should == value }
|
42
|
+
its(:infinite_length) { should == false }
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'accepts SEQUENCE OF as Array' do
|
46
|
+
let(:value) { [s('hello'), s(','), s('world')] }
|
47
|
+
its(:value) { should == value }
|
48
|
+
end
|
49
|
+
|
50
|
+
context 'accepts empty Array' do
|
51
|
+
let(:value) { [] }
|
52
|
+
its(:value) { should == [] }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'gets explicit tag number as the 2nd argument' do
|
57
|
+
let(:value) { [s('hello')] }
|
58
|
+
subject { klass.new(value, tag, :PRIVATE) }
|
59
|
+
|
60
|
+
context 'accepts default tag' do
|
61
|
+
let(:tag) { Krypt::ASN1::SEQUENCE }
|
62
|
+
its(:tag) { should == tag }
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'accepts custom tag' do
|
66
|
+
let(:tag) { 14 }
|
67
|
+
its(:tag) { should == tag }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'gets tag class symbol as the 3rd argument' do
|
72
|
+
let(:value) { [s('hello')] }
|
73
|
+
subject { klass.new(value, Krypt::ASN1::SEQUENCE, tag_class) }
|
74
|
+
|
75
|
+
context 'accepts :UNIVERSAL' do
|
76
|
+
let(:tag_class) { :UNIVERSAL }
|
77
|
+
its(:tag_class) { should == tag_class }
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'accepts :APPLICATION' do
|
81
|
+
let(:tag_class) { :APPLICATION }
|
82
|
+
its(:tag_class) { should == tag_class }
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'accepts :CONTEXT_SPECIFIC' do
|
86
|
+
let(:tag_class) { :CONTEXT_SPECIFIC }
|
87
|
+
its(:tag_class) { should == tag_class }
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'accepts :PRIVATE' do
|
91
|
+
let(:tag_class) { :PRIVATE }
|
92
|
+
its(:tag_class) { should == tag_class }
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'accepts :IMPLICIT' do
|
96
|
+
let(:tag_class) { :IMPLICIT }
|
97
|
+
its(:tag_class) { should == tag_class }
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'accepts :EXPLICIT' do
|
101
|
+
let(:tag_class) { :EXPLICIT }
|
102
|
+
its(:tag_class) { should == tag_class }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'when the 2nd argument is given but 3rd argument is omitted' do
|
107
|
+
subject { klass.new([s('hello')], Krypt::ASN1::SEQUENCE) }
|
108
|
+
its(:tag_class) { should == :CONTEXT_SPECIFIC }
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe 'accessors' do
|
113
|
+
describe '#value' do
|
114
|
+
subject { o = klass.new(nil); o.value = value; o }
|
115
|
+
|
116
|
+
context 'accepts SEQUENCE as Array' do
|
117
|
+
let(:value) { [s('hello'), i(42), s('world')] }
|
118
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
119
|
+
its(:tag_class) { should == :UNIVERSAL }
|
120
|
+
its(:value) { should == value }
|
121
|
+
its(:infinite_length) { should == false }
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'accepts SEQUENCE OF as Array' do
|
125
|
+
let(:value) { [s('hello'), s(','), s('world')] }
|
126
|
+
its(:value) { should == value }
|
127
|
+
end
|
128
|
+
|
129
|
+
context 'accepts empty Array' do
|
130
|
+
let(:value) { [] }
|
131
|
+
its(:value) { should == [] }
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe '#tag' do
|
136
|
+
subject { o = klass.new(nil); o.tag = tag; o }
|
137
|
+
|
138
|
+
context 'accepts default tag' do
|
139
|
+
let(:tag) { Krypt::ASN1::SEQUENCE }
|
140
|
+
its(:tag) { should == tag }
|
141
|
+
end
|
142
|
+
|
143
|
+
context 'accepts custom tag' do
|
144
|
+
let(:tag) { 14 }
|
145
|
+
its(:tag) { should == tag }
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe '#tag_class' do
|
150
|
+
subject { o = klass.new(nil); o.tag_class = tag_class; o }
|
151
|
+
|
152
|
+
context 'accepts :UNIVERSAL' do
|
153
|
+
let(:tag_class) { :UNIVERSAL }
|
154
|
+
its(:tag_class) { should == tag_class }
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'accepts :APPLICATION' do
|
158
|
+
let(:tag_class) { :APPLICATION }
|
159
|
+
its(:tag_class) { should == tag_class }
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'accepts :CONTEXT_SPECIFIC' do
|
163
|
+
let(:tag_class) { :CONTEXT_SPECIFIC }
|
164
|
+
its(:tag_class) { should == tag_class }
|
165
|
+
end
|
166
|
+
|
167
|
+
context 'accepts :PRIVATE' do
|
168
|
+
let(:tag_class) { :PRIVATE }
|
169
|
+
its(:tag_class) { should == tag_class }
|
170
|
+
end
|
171
|
+
|
172
|
+
context 'accepts :IMPLICIT' do
|
173
|
+
let(:tag_class) { :IMPLICIT }
|
174
|
+
its(:tag_class) { should == tag_class }
|
175
|
+
end
|
176
|
+
|
177
|
+
context 'accepts :EXPLICIT' do
|
178
|
+
let(:tag_class) { :EXPLICIT }
|
179
|
+
its(:tag_class) { should == tag_class }
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
describe '#infinite_length' do
|
184
|
+
subject { o = klass.new(nil); o.infinite_length = infinite_length; o }
|
185
|
+
|
186
|
+
context 'accepts true' do
|
187
|
+
let(:infinite_length) { true }
|
188
|
+
its(:infinite_length) { should == true }
|
189
|
+
end
|
190
|
+
|
191
|
+
context 'accepts false' do
|
192
|
+
let(:infinite_length) { false }
|
193
|
+
its(:infinite_length) { should == false }
|
194
|
+
end
|
195
|
+
|
196
|
+
context 'accepts nil as false' do
|
197
|
+
let(:infinite_length) { nil }
|
198
|
+
its(:infinite_length) { should == false }
|
199
|
+
end
|
200
|
+
|
201
|
+
context 'accepts non boolean as true' do
|
202
|
+
let(:infinite_length) { Object.new }
|
203
|
+
its(:infinite_length) { should == true }
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
describe '#to_der' do
|
209
|
+
context 'encodes a given value' do
|
210
|
+
subject { klass.new(value).to_der }
|
211
|
+
|
212
|
+
context 'SEQUENCE' do
|
213
|
+
let(:value) { [s('hello'), i(42), s('world')] }
|
214
|
+
it { should == "\x30\x11\x04\x05hello\x02\x01\x2A\x04\x05world" }
|
215
|
+
end
|
216
|
+
|
217
|
+
context 'SEQUENCE OF OctetString' do
|
218
|
+
let(:value) { [s(''), s(''), s('')] }
|
219
|
+
it { should == "\x30\x06\x04\x00\x04\x00\x04\x00" }
|
220
|
+
end
|
221
|
+
|
222
|
+
context 'SEQUENCE OF Integer' do
|
223
|
+
let(:value) { [i(-1), i(0), i(1)] }
|
224
|
+
it { should == "\x30\x09\x02\x01\xFF\x02\x01\x00\x02\x01\x01" }
|
225
|
+
end
|
226
|
+
|
227
|
+
context '(empty)' do
|
228
|
+
let(:value) { [] }
|
229
|
+
it { should == "\x30\x00" }
|
230
|
+
end
|
231
|
+
|
232
|
+
context '1000 elements' do
|
233
|
+
let(:value) { [i(0)] * 1000 }
|
234
|
+
it { should == "\x30\x82\x0B\xB8" + "\x02\x01\x00" * 1000 }
|
235
|
+
end
|
236
|
+
|
237
|
+
context 'responds to :each' do
|
238
|
+
let(:value) {
|
239
|
+
o = Object.new # TODO: Discuss - BasicObject does not support respond_to?
|
240
|
+
def o.each
|
241
|
+
yield Krypt::ASN1::OctetString.new('hello')
|
242
|
+
yield Krypt::ASN1::Integer.new(42)
|
243
|
+
yield Krypt::ASN1::OctetString.new('world')
|
244
|
+
end
|
245
|
+
o
|
246
|
+
}
|
247
|
+
it { should == "\x30\x11\x04\x05hello\x02\x01\x2A\x04\x05world" }
|
248
|
+
end
|
249
|
+
|
250
|
+
context "keeps the ordering of the value in the encoding" do
|
251
|
+
context "definite length Array" do
|
252
|
+
let(:value) { [i(1), s("a"), Krypt::ASN1::Boolean.new(true)] }
|
253
|
+
it { should == "\x30\x09\x02\x01\x01\x04\x01a\x01\x01\xFF" }
|
254
|
+
end
|
255
|
+
|
256
|
+
context "definite length Enumerable" do
|
257
|
+
let(:value) {
|
258
|
+
o = Object.new
|
259
|
+
def o.each
|
260
|
+
yield Krypt::ASN1::Integer.new(1)
|
261
|
+
yield Krypt::ASN1::OctetString.new("a")
|
262
|
+
yield Krypt::ASN1::Boolean.new(true)
|
263
|
+
end
|
264
|
+
o
|
265
|
+
}
|
266
|
+
it { should == "\x30\x09\x02\x01\x01\x04\x01a\x01\x01\xFF" }
|
267
|
+
end
|
268
|
+
|
269
|
+
context "infinite length" do
|
270
|
+
subject { o = klass.new(value); o.infinite_length = true; o.to_der }
|
271
|
+
|
272
|
+
context "infinite length Array" do
|
273
|
+
let(:value) { [i(1), s("a"), Krypt::ASN1::Boolean.new(true)] }
|
274
|
+
it { should == "\x30\x80\x02\x01\x01\x04\x01a\x01\x01\xFF\x00\x00" }
|
275
|
+
end
|
276
|
+
|
277
|
+
context "infinite length Enumerable" do
|
278
|
+
let(:value) {
|
279
|
+
o = Object.new
|
280
|
+
def o.each
|
281
|
+
yield Krypt::ASN1::Integer.new(1)
|
282
|
+
yield Krypt::ASN1::OctetString.new("a")
|
283
|
+
yield Krypt::ASN1::Boolean.new(true)
|
284
|
+
end
|
285
|
+
o
|
286
|
+
}
|
287
|
+
it { should == "\x30\x80\x02\x01\x01\x04\x01a\x01\x01\xFF\x00\x00" }
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
context 'nil' do
|
293
|
+
let(:value) { nil }
|
294
|
+
it { -> { subject }.should raise_error asn1error }
|
295
|
+
end
|
296
|
+
|
297
|
+
context 'does not respond to :each' do
|
298
|
+
let(:value) { '123' }
|
299
|
+
it { -> { subject }.should raise_error asn1error }
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
context 'encodes tag number' do
|
304
|
+
let(:value) { [s(''), s(''), s('')] }
|
305
|
+
subject { klass.new(value, tag, :PRIVATE).to_der }
|
306
|
+
|
307
|
+
context 'default tag' do
|
308
|
+
let(:tag) { Krypt::ASN1::SEQUENCE }
|
309
|
+
it { should == "\xF0\x06\x04\x00\x04\x00\x04\x00" }
|
310
|
+
end
|
311
|
+
|
312
|
+
context 'custom tag' do
|
313
|
+
let(:tag) { 14 }
|
314
|
+
it { should == "\xEE\x06\x04\x00\x04\x00\x04\x00" }
|
315
|
+
end
|
316
|
+
|
317
|
+
context 'nil' do
|
318
|
+
let(:tag) { nil }
|
319
|
+
it { -> { subject }.should raise_error asn1error }
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
context 'encodes tag class' do
|
324
|
+
let(:value) { [s(''), s(''), s('')] }
|
325
|
+
subject { klass.new(value, Krypt::ASN1::SEQUENCE, tag_class).to_der }
|
326
|
+
|
327
|
+
context 'UNIVERSAL' do
|
328
|
+
let(:tag_class) { :UNIVERSAL }
|
329
|
+
it { should == "\x30\x06\x04\x00\x04\x00\x04\x00" }
|
330
|
+
end
|
331
|
+
|
332
|
+
context 'APPLICATION' do
|
333
|
+
let(:tag_class) { :APPLICATION }
|
334
|
+
it { should == "\x70\x06\x04\x00\x04\x00\x04\x00" }
|
335
|
+
end
|
336
|
+
|
337
|
+
context 'CONTEXT_SPECIFIC' do
|
338
|
+
let(:tag_class) { :CONTEXT_SPECIFIC }
|
339
|
+
it { should == "\xB0\x06\x04\x00\x04\x00\x04\x00" }
|
340
|
+
end
|
341
|
+
|
342
|
+
context 'PRIVATE' do
|
343
|
+
let(:tag_class) { :PRIVATE }
|
344
|
+
it { should == "\xF0\x06\x04\x00\x04\x00\x04\x00" }
|
345
|
+
end
|
346
|
+
|
347
|
+
context 'IMPLICIT' do
|
348
|
+
let(:tag_class) { :IMPLICIT }
|
349
|
+
it { should == "\xB0\x06\x04\x00\x04\x00\x04\x00" }
|
350
|
+
end
|
351
|
+
|
352
|
+
context 'EXPLICIT' do
|
353
|
+
let(:tag_class) { :EXPLICIT }
|
354
|
+
it { should == "\xB0\x08\x30\x06\x04\x00\x04\x00\x04\x00" }
|
355
|
+
end
|
356
|
+
|
357
|
+
context nil do
|
358
|
+
let(:tag_class) { nil }
|
359
|
+
it { -> { subject }.should raise_error asn1error } # TODO: ossl does not check nil
|
360
|
+
end
|
361
|
+
|
362
|
+
context :no_such_class do
|
363
|
+
let(:tag_class) { :no_such_class }
|
364
|
+
it { -> { subject }.should raise_error asn1error }
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
context 'encodes indefinite length packets' do
|
369
|
+
subject {
|
370
|
+
o = klass.new(nil, Krypt::ASN1::SEQUENCE, :UNIVERSAL)
|
371
|
+
o.value = value if defined? value
|
372
|
+
o.infinite_length = true
|
373
|
+
o
|
374
|
+
}
|
375
|
+
|
376
|
+
context 'with EndOfContents' do
|
377
|
+
let(:value) { [s('hello'), i(42), s('world'), eoc] }
|
378
|
+
let(:infinite_length) { true }
|
379
|
+
its(:to_der) { should == "\x30\x80\x04\x05hello\x02\x01\x2A\x04\x05world\x00\x00" }
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
context 'encodes values set via accessors' do
|
384
|
+
subject {
|
385
|
+
o = klass.new(nil)
|
386
|
+
o.value = value if defined? value
|
387
|
+
o.tag = tag if defined? tag
|
388
|
+
o.tag_class = tag_class if defined? tag_class
|
389
|
+
o.to_der
|
390
|
+
}
|
391
|
+
|
392
|
+
context 'value: SEQUENCE' do
|
393
|
+
let(:value) { [s('hello'), i(42), s('world')] }
|
394
|
+
it { should == "\x30\x11\x04\x05hello\x02\x01\x2A\x04\x05world" }
|
395
|
+
end
|
396
|
+
|
397
|
+
context 'custom tag' do
|
398
|
+
let(:value) { [s('hello'), i(42), s('world')] }
|
399
|
+
let(:tag) { 14 }
|
400
|
+
let(:tag_class) { :PRIVATE }
|
401
|
+
it { should == "\xEE\x11\x04\x05hello\x02\x01\x2A\x04\x05world" }
|
402
|
+
end
|
403
|
+
|
404
|
+
context 'tag_class' do
|
405
|
+
let(:value) { [s('hello'), i(42), s('world')] }
|
406
|
+
let(:tag_class) { :APPLICATION }
|
407
|
+
it { should == "\x70\x11\x04\x05hello\x02\x01\x2A\x04\x05world" }
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
context "encodes infinite length values" do
|
412
|
+
subject do
|
413
|
+
asn1 = klass.new(value)
|
414
|
+
asn1.infinite_length = true
|
415
|
+
asn1.to_der
|
416
|
+
end
|
417
|
+
|
418
|
+
context "with explicit EOC" do
|
419
|
+
let(:value) { [
|
420
|
+
mod::Integer.new(1),
|
421
|
+
mod::Boolean.new(true),
|
422
|
+
mod::EndOfContents.new
|
423
|
+
] }
|
424
|
+
it { subject.should == "\x30\x80\x02\x01\x01\x01\x01\xFF\x00\x00" }
|
425
|
+
end
|
426
|
+
|
427
|
+
context "without explicit EOC" do
|
428
|
+
let(:value) { [
|
429
|
+
mod::Integer.new(1),
|
430
|
+
mod::Boolean.new(true),
|
431
|
+
] }
|
432
|
+
it { subject.should == "\x30\x80\x02\x01\x01\x01\x01\xFF\x00\x00" }
|
433
|
+
end
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
describe '#encode_to' do
|
438
|
+
context 'encodes to an IO' do
|
439
|
+
subject { klass.new(value).encode_to(io); io }
|
440
|
+
|
441
|
+
context "StringIO" do
|
442
|
+
let(:value) { [s(''), s(''), s('')] }
|
443
|
+
let(:io) { string_io_object }
|
444
|
+
its(:written_bytes) { should == "\x30\x06\x04\x00\x04\x00\x04\x00" }
|
445
|
+
end
|
446
|
+
|
447
|
+
context "Object responds to :write" do
|
448
|
+
let(:value) { [s(''), s(''), s('')] }
|
449
|
+
let(:io) { writable_object }
|
450
|
+
its(:written_bytes) { should == "\x30\x06\x04\x00\x04\x00\x04\x00" }
|
451
|
+
end
|
452
|
+
|
453
|
+
context "raise IO error transparently" do
|
454
|
+
let(:value) { [s(''), s(''), s('')] }
|
455
|
+
let(:io) { io_error_object }
|
456
|
+
it { -> { subject }.should raise_error asn1error }
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
it 'returns self' do
|
461
|
+
obj = klass.new([s(''), s(''), s('')])
|
462
|
+
obj.encode_to(string_io_object).should == obj
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
describe '#each' do
|
467
|
+
subject { yielded_value_from_each(klass.new(value)) }
|
468
|
+
|
469
|
+
context "yields each value in its order" do
|
470
|
+
let(:value) { [s('hello'), i(42), s('world')] }
|
471
|
+
it { should == value }
|
472
|
+
end
|
473
|
+
|
474
|
+
context "yields nothing for empty value" do
|
475
|
+
let(:value) { [] }
|
476
|
+
it { should == value }
|
477
|
+
end
|
478
|
+
|
479
|
+
it "is Enumerable via each" do
|
480
|
+
value = [s('hello'), i(42), s('world')]
|
481
|
+
klass.new(value).map { |e| e.value }.should == ['hello', 42, 'world']
|
482
|
+
end
|
483
|
+
|
484
|
+
it "returns Enumerator for blockless call" do
|
485
|
+
value = [s('hello'), i(42), s('world')]
|
486
|
+
klass.new(value).each.next.value.should == 'hello'
|
487
|
+
end
|
488
|
+
|
489
|
+
it "yields each value for an Enumerable" do
|
490
|
+
o = Object.new
|
491
|
+
def o.each
|
492
|
+
yield Krypt::ASN1::Integer.new(1)
|
493
|
+
yield Krypt::ASN1::Integer.new(2)
|
494
|
+
yield Krypt::ASN1::Integer.new(3)
|
495
|
+
end
|
496
|
+
klass.new(o).map { |e| e.value }.should == [1, 2, 3]
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
describe 'extracted from ASN1.decode' do
|
501
|
+
subject { decoder.decode(der) }
|
502
|
+
|
503
|
+
context 'extracted value' do
|
504
|
+
context 'SEQUENCE' do
|
505
|
+
let(:der) { "\x30\x11\x04\x05hello\x02\x01\x2A\x04\x05world" }
|
506
|
+
its(:class) { should == klass }
|
507
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
508
|
+
it 'contains decoded value' do
|
509
|
+
value = subject.value
|
510
|
+
value.size.should == 3
|
511
|
+
value[0].value == 'hello'
|
512
|
+
value[1].value == 42
|
513
|
+
value[2].value == 'world'
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
context 'SEQUENCE OF Integer' do
|
518
|
+
let(:der) { "\x30\x0C\x02\x04\xFF\xFF\xFF\xFF\x02\x01\x00\x02\x01\x01" }
|
519
|
+
its(:class) { should == klass }
|
520
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
521
|
+
it 'contains decoded value' do
|
522
|
+
value = subject.value
|
523
|
+
value.size.should == 3
|
524
|
+
value[0].value == -1
|
525
|
+
value[1].value == 0
|
526
|
+
value[2].value == 1
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
context '(empty)' do
|
531
|
+
let(:der) { "\x30\x00" }
|
532
|
+
its(:class) { should == klass }
|
533
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
534
|
+
its(:value) { should == [] }
|
535
|
+
end
|
536
|
+
|
537
|
+
context '1000 elements' do
|
538
|
+
let(:der) { "\x30\x82\x0B\xB8" + "\x02\x01\x00" * 1000 }
|
539
|
+
its(:class) { should == klass }
|
540
|
+
its(:tag) { should == Krypt::ASN1::SEQUENCE }
|
541
|
+
it 'contains decoded value' do
|
542
|
+
value = subject.value
|
543
|
+
value.size == 1000
|
544
|
+
value.all? { |v| v.value == 0 }.should be_true
|
545
|
+
end
|
546
|
+
end
|
547
|
+
end
|
548
|
+
|
549
|
+
context 'extracted tag class' do
|
550
|
+
context 'UNIVERSAL' do
|
551
|
+
let(:der) { "\x30\x11\x04\x05hello\x02\x01\x2A\x04\x05world" }
|
552
|
+
its(:tag_class) { should == :UNIVERSAL }
|
553
|
+
end
|
554
|
+
|
555
|
+
context 'APPLICATION' do
|
556
|
+
let(:der) { "\x70\x11\x04\x05hello\x02\x01\x2A\x04\x05world" }
|
557
|
+
its(:tag_class) { should == :APPLICATION }
|
558
|
+
end
|
559
|
+
|
560
|
+
context 'CONTEXT_SPECIFIC' do
|
561
|
+
let(:der) { "\xB0\x11\x04\x05hello\x02\x01\x2A\x04\x05world" }
|
562
|
+
its(:tag_class) { should == :CONTEXT_SPECIFIC }
|
563
|
+
end
|
564
|
+
|
565
|
+
context 'PRIVATE' do
|
566
|
+
let(:der) { "\xF0\x11\x04\x05hello\x02\x01\x2A\x04\x05world" }
|
567
|
+
its(:tag_class) { should == :PRIVATE }
|
568
|
+
end
|
569
|
+
|
570
|
+
context "setting IMPLICIT will result in CONTEXT_SPECIFIC" do
|
571
|
+
let(:der) { "\x30\x11\x04\x05hello\x02\x01\x2A\x04\x05world" }
|
572
|
+
it do
|
573
|
+
subject.tag_class = :IMPLICIT
|
574
|
+
subject.to_der.should == "\xB0\x11\x04\x05hello\x02\x01\x2A\x04\x05world"
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
context "setting EXPLICIT will reencode as CONTEXT_SPECIFIC" do
|
579
|
+
let(:der) { "\x30\x11\x04\x05hello\x02\x01\x2A\x04\x05world" }
|
580
|
+
it do
|
581
|
+
subject.tag_class = :EXPLICIT
|
582
|
+
subject.tag = 0
|
583
|
+
subject.to_der.should == "\xA0\x13\x30\x11\x04\x05hello\x02\x01\x2A\x04\x05world"
|
584
|
+
end
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
context 'extracted infinite_length' do
|
589
|
+
context 'definite encoding' do
|
590
|
+
let(:der) { "\x30\x11\x04\x05hello\x02\x01\x2A\x04\x05world" }
|
591
|
+
its(:infinite_length) { should be_false }
|
592
|
+
end
|
593
|
+
|
594
|
+
context 'indefinite encoding' do
|
595
|
+
let(:der) { "\x30\x80\x04\x05hello\x02\x01\x2A\x04\x05world\x00\x00" }
|
596
|
+
its(:infinite_length) { should be_true }
|
597
|
+
it "drops EndOfContents as last value" do
|
598
|
+
subject.value.size.should == 3
|
599
|
+
subject.value.any? { |o| o.instance_of? Krypt::ASN1::EndOfContents }.should be_false
|
600
|
+
end
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
context "preserves the ordering present in the encoding" do
|
605
|
+
context "definite length" do
|
606
|
+
context "when immediately re-encoding" do
|
607
|
+
let(:der) { "\x30\x08\x05\x00\x04\x01a\x02\x01\x01" }
|
608
|
+
its(:to_der) { should == der }
|
609
|
+
end
|
610
|
+
|
611
|
+
context "when changing one of the values" do
|
612
|
+
let(:der) { "\x30\x08\x05\x00\x04\x01a\x02\x01\x01" }
|
613
|
+
it do
|
614
|
+
subject.value[2].value = 5
|
615
|
+
subject.to_der.should == "\x30\x08\x05\x00\x04\x01a\x02\x01\x05"
|
616
|
+
end
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
context "infinite length" do
|
621
|
+
context "when immediately re-encoding" do
|
622
|
+
let(:der) { "\x30\x80\x05\x00\x04\x01a\x02\x01\x01\x00\x00" }
|
623
|
+
its(:to_der) { should == der }
|
624
|
+
end
|
625
|
+
|
626
|
+
context "when changing one of the values" do
|
627
|
+
let(:der) { "\x30\x80\x05\x00\x04\x01a\x02\x01\x01\x00\x00" }
|
628
|
+
it do
|
629
|
+
subject.value[2].value = 5
|
630
|
+
subject.to_der.should == "\x30\x80\x05\x00\x04\x01a\x02\x01\x05\x00\x00"
|
631
|
+
end
|
632
|
+
end
|
633
|
+
end
|
634
|
+
end
|
635
|
+
end
|
636
|
+
end
|
637
|
+
|