rasn1 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -1
- data/lib/rasn1/model.rb +140 -63
- data/lib/rasn1/types.rb +1 -0
- data/lib/rasn1/types/any.rb +29 -0
- data/lib/rasn1/types/base.rb +28 -16
- data/lib/rasn1/types/enumerated.rb +6 -0
- data/lib/rasn1/types/sequence.rb +21 -5
- data/lib/rasn1/types/sequence_of.rb +60 -17
- data/lib/rasn1/version.rb +1 -1
- data/rasn1.gemspec +2 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 379d5a61021e3b58ffb46317f72905e9e3972044
|
4
|
+
data.tar.gz: 60bb0d280cccd6c999744045fb45f334863d46f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: beb7960cfdef060b22f81dea31d7b93ece7781afad2b0a7db92606b860e8ff00ab1ae7991a86d5594b97b18b53c2888d6d83f11e4fe6e84abb33cfdddb491d75
|
7
|
+
data.tar.gz: 2fe8288bb99e4c21ead959855e3b0611c182abc6f2e23a8a60ff0babe7ebe62ef9a7795ec8f0512d50792f2e02278b192e8d62879c6f7815836ebb3c2c3287a0
|
data/README.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/rasn1.svg)](https://badge.fury.io/rb/rasn1)
|
2
|
+
|
1
3
|
# Rasn1
|
2
4
|
|
3
5
|
Rasn1 will be a ruby ASN.1 library to encode, parse and decode ASN.1 data in DER format.
|
@@ -47,7 +49,7 @@ class ComplexRecord < RASN1::Model
|
|
47
49
|
sequence :cplx_record,
|
48
50
|
content: [boolean(:bool),
|
49
51
|
octet_string(:data, explicit: 0),
|
50
|
-
Record]
|
52
|
+
model(:a_record, Record)]
|
51
53
|
end
|
52
54
|
```
|
53
55
|
|
data/lib/rasn1/model.rb
CHANGED
@@ -40,41 +40,94 @@ module RASN1
|
|
40
40
|
# class Record2 < RASN1::Model
|
41
41
|
# sequence(:record2,
|
42
42
|
# content: [boolean(:rented, default: false),
|
43
|
-
# Record])
|
43
|
+
# model(:a_record, Record)])
|
44
44
|
# end
|
45
45
|
# Set values like this:
|
46
46
|
# record2 = Record2.new
|
47
47
|
# record2[:rented] = true
|
48
|
-
# record2[:
|
49
|
-
# record2[:
|
48
|
+
# record2[:a_record][:id] = 65537
|
49
|
+
# record2[:a_record][:room] = 43
|
50
50
|
# or like this:
|
51
|
-
# record2 = Record2.new(rented: true,
|
51
|
+
# record2 = Record2.new(rented: true, a_record: { id: 65537, room: 43 })
|
52
52
|
# @author Sylvain Daubert
|
53
53
|
class Model
|
54
54
|
|
55
55
|
class << self
|
56
56
|
|
57
|
-
#
|
58
|
-
# @param [
|
59
|
-
# @
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
57
|
+
# Use another model in this model
|
58
|
+
# @param [String,Symbol] name
|
59
|
+
# @param [Class] model_klass
|
60
|
+
def model(name, model_klass)
|
61
|
+
@root = [name, model_klass]
|
62
|
+
end
|
63
|
+
|
64
|
+
# @method sequence(name, options)
|
65
|
+
# @see Types::Sequence#initialize
|
66
|
+
# @method set(name, options)
|
67
|
+
# @see Types::Set#initialize
|
68
|
+
# @method choice(name, options)
|
69
|
+
# @see Types::Choice#initialize
|
70
|
+
%w(sequence set choice).each do |type|
|
71
|
+
class_eval "def #{type}(name, options={})\n" \
|
72
|
+
" proc = Proc.new do\n" \
|
73
|
+
" Types::#{type.capitalize}.new(name, options)\n" \
|
74
|
+
" end\n" \
|
75
|
+
" @root = [name, proc]\n" \
|
76
|
+
" @root << options[:content] unless options[:content].nil?\n" \
|
77
|
+
" @root\n" \
|
78
|
+
"end"
|
69
79
|
end
|
70
80
|
|
71
|
-
#
|
81
|
+
# @method sequence_of(name, type, options)
|
82
|
+
# @see Types::SequenceOf#initialize
|
83
|
+
# @method set_of(name, type, options)
|
84
|
+
# @see Types::SetOf#initialize
|
85
|
+
%w(sequence set).each do |type|
|
86
|
+
klass_name = "Types::#{type.capitalize}Of"
|
87
|
+
class_eval "def #{type}_of(name, type, options={})\n" \
|
88
|
+
" proc = Proc.new do\n" \
|
89
|
+
" #{klass_name}.new(name, type, options)\n" \
|
90
|
+
" end\n" \
|
91
|
+
" @root = [name, proc]\n" \
|
92
|
+
"end"
|
93
|
+
end
|
94
|
+
|
95
|
+
# @method boolean(name, options)
|
96
|
+
# @see Types::Boolean#initialize
|
97
|
+
# @method integer(name, options)
|
98
|
+
# @see Types::Integer#initialize
|
99
|
+
# @method bit_string(name, options)
|
100
|
+
# @see Types::BitString#initialize
|
101
|
+
# @method octet_string(name, options)
|
102
|
+
# @see Types::OctetString#initialize
|
103
|
+
# @method null(name, options)
|
104
|
+
# @see Types::Null#initialize
|
105
|
+
# @method enumerated(name, options)
|
106
|
+
# @see Types::Enumerated#initialize
|
107
|
+
# @method utf8_string(name, options)
|
108
|
+
# @see Types::Utf8String#initialize
|
72
109
|
Types.primitives.each do |prim|
|
110
|
+
next if prim == Types::ObjectId
|
73
111
|
class_eval "def #{prim.type.downcase.gsub(/\s+/, '_')}(name, options={})\n" \
|
74
|
-
" Proc.new { #{prim.to_s}.new(name, options) }\n" \
|
112
|
+
" proc = Proc.new { #{prim.to_s}.new(name, options) }\n" \
|
113
|
+
" @root = [name, proc]\n" \
|
75
114
|
"end"
|
76
115
|
end
|
77
116
|
|
117
|
+
# @note This method is named +objectid+ and not +object_id+ to not override
|
118
|
+
# +Object#object_id+.
|
119
|
+
# @see Types::ObjectId#initialize
|
120
|
+
def objectid(name, options={})
|
121
|
+
proc = Proc.new { Types::ObjectId.new(name, options) }
|
122
|
+
@root = [name, proc]
|
123
|
+
end
|
124
|
+
|
125
|
+
# @see Types::Any#initialize
|
126
|
+
def any(name, options={})
|
127
|
+
proc = Proc.new { Types::Any.new(name, options) }
|
128
|
+
@root = [name, proc]
|
129
|
+
end
|
130
|
+
|
78
131
|
# Parse a DER/BER encoded string
|
79
132
|
# @param [String] str
|
80
133
|
# @param [Boolean] ber accept BER encoding or not
|
@@ -89,7 +142,8 @@ module RASN1
|
|
89
142
|
# Create a new instance of a {Model}
|
90
143
|
# @param [Hash] args
|
91
144
|
def initialize(args={})
|
92
|
-
|
145
|
+
root = generate_root
|
146
|
+
set_elements *root
|
93
147
|
initialize_elements self, args
|
94
148
|
end
|
95
149
|
|
@@ -106,6 +160,12 @@ module RASN1
|
|
106
160
|
@elements[@root].name
|
107
161
|
end
|
108
162
|
|
163
|
+
# Get elements names
|
164
|
+
# @return [Array<Symbol,String>]
|
165
|
+
def keys
|
166
|
+
@elements.keys
|
167
|
+
end
|
168
|
+
|
109
169
|
# Return a hash image of model
|
110
170
|
# @return [Hash]
|
111
171
|
def to_h
|
@@ -117,46 +177,56 @@ module RASN1
|
|
117
177
|
@elements[@root].to_der
|
118
178
|
end
|
119
179
|
|
180
|
+
# Get root element from model
|
181
|
+
# @return [Types::Base,Model]
|
182
|
+
def root
|
183
|
+
@elements[@root]
|
184
|
+
end
|
185
|
+
|
120
186
|
# Parse a DER/BER encoded string, and modify object in-place.
|
121
187
|
# @param [String] str
|
122
188
|
# @param [Boolean] ber accept BER encoding or not
|
123
189
|
# @return [Integer] number of parsed bytes
|
124
190
|
def parse!(str, ber: false)
|
125
|
-
@elements[@root].parse!(str, ber: ber)
|
191
|
+
@elements[@root].parse!(str.dup.force_encoding('BINARY'), ber: ber)
|
126
192
|
end
|
127
193
|
|
128
194
|
private
|
129
195
|
|
130
|
-
def
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
196
|
+
def is_composed?(el)
|
197
|
+
[Types::Sequence, Types::Set].include? el.class
|
198
|
+
end
|
199
|
+
|
200
|
+
def is_of?(el)
|
201
|
+
[Types::SequenceOf, Types::SetOf].include? el.class
|
202
|
+
end
|
203
|
+
|
204
|
+
def get_type(proc_or_class, name=nil)
|
205
|
+
case proc_or_class
|
206
|
+
when Proc
|
207
|
+
proc_or_class.call
|
208
|
+
when Class
|
209
|
+
proc_or_class.new
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def generate_root
|
214
|
+
root = self.class.class_eval { @root }
|
215
|
+
@root = root[0]
|
216
|
+
@elements = {}
|
217
|
+
@elements[@root] = get_type(root[1])
|
218
|
+
root
|
219
|
+
end
|
220
|
+
|
221
|
+
def set_elements(name, el, content=nil)
|
222
|
+
if content.is_a? Array
|
223
|
+
@elements[name].value = content.map do |name2, proc_or_class, content2|
|
224
|
+
subel = get_type(proc_or_class, name2)
|
225
|
+
@elements[name2] = subel
|
226
|
+
if is_composed?(subel) and content2.is_a? Array
|
227
|
+
set_elements(name2, proc_or_class, content2)
|
147
228
|
end
|
148
|
-
|
149
|
-
else
|
150
|
-
element.value.map! do |subel|
|
151
|
-
se = case subel
|
152
|
-
when Proc
|
153
|
-
subel.call
|
154
|
-
when Class
|
155
|
-
subel.new
|
156
|
-
end
|
157
|
-
@elements[se.name] = se
|
158
|
-
set_elements se if se.is_a? Types::Sequence
|
159
|
-
se
|
229
|
+
subel
|
160
230
|
end
|
161
231
|
end
|
162
232
|
end
|
@@ -168,7 +238,7 @@ module RASN1
|
|
168
238
|
if obj[name].is_a? Model
|
169
239
|
initialize_elements obj[name], value
|
170
240
|
else
|
171
|
-
raise ArgumentError, "element #{name}: may only pass a Hash for
|
241
|
+
raise ArgumentError, "element #{name}: may only pass a Hash for Model elements"
|
172
242
|
end
|
173
243
|
else
|
174
244
|
obj[name].value = value
|
@@ -178,20 +248,27 @@ module RASN1
|
|
178
248
|
end
|
179
249
|
|
180
250
|
def private_to_h(element)
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
251
|
+
if element.value.is_a? Array
|
252
|
+
h = {}
|
253
|
+
element.value.each do |subel|
|
254
|
+
case subel
|
255
|
+
when Types::Sequence, Types::Set
|
256
|
+
h[subel.name] = private_to_h(subel)
|
257
|
+
when Model
|
258
|
+
h[@elements.key(subel)] = subel.to_h[subel.name]
|
259
|
+
when Hash, Array
|
260
|
+
# Array of Hash for SequenceOf and SetOf
|
261
|
+
# Array of Array of... of Hash are nested SequenceOf or SetOf
|
262
|
+
return element.value
|
263
|
+
else
|
264
|
+
next if subel.value.nil? and subel.optional?
|
265
|
+
h[subel.name] = subel.value
|
266
|
+
end
|
267
|
+
end
|
268
|
+
h
|
269
|
+
else
|
270
|
+
element.value
|
192
271
|
end
|
193
|
-
|
194
|
-
h
|
195
272
|
end
|
196
273
|
end
|
197
274
|
end
|
data/lib/rasn1/types.rb
CHANGED
@@ -0,0 +1,29 @@
|
|
1
|
+
module RASN1
|
2
|
+
module Types
|
3
|
+
|
4
|
+
# ASN.1 ANY: accpets any types
|
5
|
+
# @author Sylvain Daubert
|
6
|
+
class Any < Base
|
7
|
+
|
8
|
+
# @return [String] DER-formated string
|
9
|
+
def to_der
|
10
|
+
case @value
|
11
|
+
when Base, Model
|
12
|
+
@value.to_der
|
13
|
+
else
|
14
|
+
@value.to_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Parse a DER string. This method updates object: {#value} will a DER string.
|
19
|
+
# @param [String] der DER string
|
20
|
+
# @param [Boolean] ber if +true+, accept BER encoding
|
21
|
+
# @return [Integer] total number of parsed bytes
|
22
|
+
def parse!(der, ber: false)
|
23
|
+
total_length, = get_data(der, ber)
|
24
|
+
@value = der[0, total_length]
|
25
|
+
total_length
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/rasn1/types/base.rb
CHANGED
@@ -36,6 +36,9 @@ module RASN1
|
|
36
36
|
# ctype = RASN1::Types::Integer.new(:ctype, explicit: 0) # with explicit, default #asn1_class is :context
|
37
37
|
# atype = RASN1::Types::Integer.new(:atype, explicit: 1, class: :application)
|
38
38
|
# ptype = RASN1::Types::Integer.new(:ptype, explicit: 2, class: :private)
|
39
|
+
# Sometimes, an EXPLICIT type should be CONSTRUCTED. To do that, use +:constructed+
|
40
|
+
# option:
|
41
|
+
# ptype = RASN1::Types::Integer.new(:ptype, explicit: 2, class: :private, constructed: true)
|
39
42
|
#
|
40
43
|
# Implicit tagged values may also be defined:
|
41
44
|
# ctype_implicit = RASN1::Types::Integer.new(:ctype, implicit: 0)
|
@@ -56,7 +59,7 @@ module RASN1
|
|
56
59
|
INDEFINITE_LENGTH = 0x80
|
57
60
|
|
58
61
|
# @return [Symbol, String]
|
59
|
-
|
62
|
+
attr_accessor :name
|
60
63
|
# @return [Symbol]
|
61
64
|
attr_reader :asn1_class
|
62
65
|
# @return [Object,nil] default value, if defined
|
@@ -74,13 +77,19 @@ module RASN1
|
|
74
77
|
|
75
78
|
# @param [Symbol, String] name name for this tag in grammar
|
76
79
|
# @param [Hash] options
|
77
|
-
# @option options [Symbol] :class ASN.1 tag class. Default value is +:universal
|
80
|
+
# @option options [Symbol] :class ASN.1 tag class. Default value is +:universal+.
|
81
|
+
# If +:explicit+ or +:implicit:+ is defined, default value is +:context+.
|
78
82
|
# @option options [::Boolean] :optional define this tag as optional. Default
|
79
83
|
# is +false+
|
80
84
|
# @option options [Object] :default default value for DEFAULT tag
|
81
85
|
# @option options [Object] :value value to set
|
86
|
+
# @option options [::Integer] :implicit define an IMPLICIT tagged type
|
87
|
+
# @option options [::Integer] :explicit define an EXPLICIT tagged type
|
88
|
+
# @option options [::Boolean] :constructed if +true+, set type as constructed.
|
89
|
+
# May only be used when +:explicit+ is defined, else it is discarded.
|
82
90
|
def initialize(name, options={})
|
83
91
|
@name = name
|
92
|
+
@constructed = nil
|
84
93
|
|
85
94
|
set_options options
|
86
95
|
end
|
@@ -137,20 +146,12 @@ module RASN1
|
|
137
146
|
|
138
147
|
# @return [::Boolean] +true+ if this is a primitive type
|
139
148
|
def primitive?
|
140
|
-
|
141
|
-
true
|
142
|
-
else
|
143
|
-
false
|
144
|
-
end
|
149
|
+
(self.class < Primitive) && !@constructed
|
145
150
|
end
|
146
151
|
|
147
152
|
# @return [::Boolean] +true+ if this is a constructed type
|
148
153
|
def constructed?
|
149
|
-
|
150
|
-
true
|
151
|
-
else
|
152
|
-
false
|
153
|
-
end
|
154
|
+
!!((self.class < Constructed) || @constructed)
|
154
155
|
end
|
155
156
|
|
156
157
|
# Get ASN.1 type
|
@@ -162,7 +163,12 @@ module RASN1
|
|
162
163
|
# Get tag value
|
163
164
|
# @return [Integer]
|
164
165
|
def tag
|
165
|
-
|
166
|
+
pc = if @constructed.nil?
|
167
|
+
self.class::ASN1_PC
|
168
|
+
else
|
169
|
+
Constructed::ASN1_PC
|
170
|
+
end
|
171
|
+
(@tag_value || self.class::TAG) | CLASSES[@asn1_class] | pc
|
166
172
|
end
|
167
173
|
|
168
174
|
# @abstract This method SHOULD be partly implemented by subclasses to parse
|
@@ -177,7 +183,8 @@ module RASN1
|
|
177
183
|
|
178
184
|
total_length, data = get_data(der, ber)
|
179
185
|
if explicit?
|
180
|
-
type
|
186
|
+
# Delegate to #explicit type to generate sub-tag
|
187
|
+
type = explicit_type
|
181
188
|
type.parse!(data)
|
182
189
|
@value = type.value
|
183
190
|
else
|
@@ -225,6 +232,7 @@ module RASN1
|
|
225
232
|
if options[:explicit]
|
226
233
|
@tag = :explicit
|
227
234
|
@tag_value = options[:explicit]
|
235
|
+
@constructed = options[:constructed]
|
228
236
|
elsif options[:implicit]
|
229
237
|
@tag = :implicit
|
230
238
|
@tag_value = options[:implicit]
|
@@ -241,7 +249,7 @@ module RASN1
|
|
241
249
|
def build_tag
|
242
250
|
if build_tag?
|
243
251
|
if explicit?
|
244
|
-
v =
|
252
|
+
v = explicit_type
|
245
253
|
v.value = @value
|
246
254
|
encoded_value = v.to_der
|
247
255
|
else
|
@@ -325,8 +333,12 @@ module RASN1
|
|
325
333
|
[total_length, data]
|
326
334
|
end
|
327
335
|
|
336
|
+
def explicit_type
|
337
|
+
self.class.new(@name)
|
338
|
+
end
|
339
|
+
|
328
340
|
def raise_tag_error(expected_tag, tag)
|
329
|
-
msg = "Expected
|
341
|
+
msg = "Expected #{tag2name(expected_tag)} but get #{tag2name(tag)}"
|
330
342
|
msg << " for #@name"
|
331
343
|
raise ASN1Error, msg
|
332
344
|
end
|
@@ -18,6 +18,8 @@ module RASN1
|
|
18
18
|
# A {EnumeratedError} is raised when set value is not in enumeration.
|
19
19
|
# @author Sylvain Daubert
|
20
20
|
class Enumerated < Integer
|
21
|
+
# @return [Hash]
|
22
|
+
attr_reader :enum
|
21
23
|
|
22
24
|
# @param [Symbol, String] name name for this tag in grammar
|
23
25
|
# @param [Hash] options
|
@@ -114,6 +116,10 @@ module RASN1
|
|
114
116
|
@value = @enum.key(v)
|
115
117
|
raise EnumeratedError, "TAG #@name: value #{v} not in enumeration" if @value.nil?
|
116
118
|
end
|
119
|
+
|
120
|
+
def explicit_type
|
121
|
+
self.class.new(@name, enum: self.enum)
|
122
|
+
end
|
117
123
|
end
|
118
124
|
end
|
119
125
|
end
|
data/lib/rasn1/types/sequence.rb
CHANGED
@@ -18,6 +18,11 @@ module RASN1
|
|
18
18
|
# RASN1::Types::Integer(:id, explicit: 0, optional: true),
|
19
19
|
# RASN1::Types::Integer(:id, implicit: 1, default: 0)
|
20
20
|
# ]
|
21
|
+
#
|
22
|
+
# A sequence may also be used without value to not parse sequence content:
|
23
|
+
# seq = RASN1::Types::Sequence.new(:seq)
|
24
|
+
# seq.parse!(der_string)
|
25
|
+
# seq.value # => String
|
21
26
|
# @author Sylvain Daubert
|
22
27
|
class Sequence < Constructed
|
23
28
|
TAG = 0x10
|
@@ -25,14 +30,25 @@ module RASN1
|
|
25
30
|
private
|
26
31
|
|
27
32
|
def value_to_der
|
28
|
-
@value
|
33
|
+
case @value
|
34
|
+
when Array
|
35
|
+
@value.map { |element| element.to_der }.join
|
36
|
+
else
|
37
|
+
@value.to_s
|
38
|
+
end
|
29
39
|
end
|
30
40
|
|
31
41
|
def der_to_value(der, ber:false)
|
32
|
-
|
33
|
-
|
34
|
-
nb_bytes
|
35
|
-
|
42
|
+
case @value
|
43
|
+
when Array
|
44
|
+
nb_bytes = 0
|
45
|
+
@value.each do |element|
|
46
|
+
nb_bytes += element.parse!(der[nb_bytes..-1])
|
47
|
+
end
|
48
|
+
else
|
49
|
+
@value = der
|
50
|
+
der.length
|
51
|
+
end
|
36
52
|
end
|
37
53
|
end
|
38
54
|
end
|
@@ -5,6 +5,7 @@ module RASN1
|
|
5
5
|
#
|
6
6
|
# A SEQUENCE OF is an array of one ASN.1 type.
|
7
7
|
#
|
8
|
+
# == Use with {Primitive} types
|
8
9
|
# To encode this ASN.1 example:
|
9
10
|
# Integers ::= SEQUENCE OF INTEGER
|
10
11
|
# do:
|
@@ -13,6 +14,7 @@ module RASN1
|
|
13
14
|
# # Set integer values
|
14
15
|
# seqof.value = [1, 2, 3, 4]
|
15
16
|
#
|
17
|
+
# == Use with {Constructed} types
|
16
18
|
# SEQUENCE OF may be used to create sequence of composed types. For example:
|
17
19
|
# composed_type = RASN1::Sequence.new(:comp)
|
18
20
|
# commposed_type.value = [RASN1::Types::Integer(:id),
|
@@ -20,38 +22,54 @@ module RASN1
|
|
20
22
|
# seqof = RASN1::SequenceOf.new(:comp, composed_type)
|
21
23
|
# seqof.value << [0, 'data0']
|
22
24
|
# seqof.value << [1, 'data1']
|
25
|
+
#
|
26
|
+
# == Use with {Model}
|
27
|
+
# SEQUENCE OF may also be used with a Model type:
|
28
|
+
# class MyModel < RASN1::Model
|
29
|
+
# sequence :seq,
|
30
|
+
# content: [boolean(:bool), integer(:int)]
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# seqof = RASN1::Types::SequenceOf.new(:record, MyModel)
|
34
|
+
# # set values
|
35
|
+
# seqof.value << { bool: true, int: 12 }
|
36
|
+
# seqof.value << { bool: false, int: 65535 }
|
37
|
+
# # Generate DER string
|
38
|
+
# der = seqof.to_der # => String
|
39
|
+
# # parse
|
40
|
+
# seqof.parse! der
|
23
41
|
# @author Sylvain Daubert
|
24
42
|
class SequenceOf < Constructed
|
25
43
|
TAG = Sequence::TAG
|
26
44
|
|
27
45
|
# @return [Class, Base]
|
28
|
-
attr_reader :
|
46
|
+
attr_reader :of_type
|
29
47
|
|
30
48
|
# @param [Symbol, String] name name for this tag in grammar
|
31
|
-
# @param [Class, Base]
|
49
|
+
# @param [Class, Base] of_type base type for sequence of
|
32
50
|
# @see Base#initialize
|
33
|
-
def initialize(name,
|
51
|
+
def initialize(name, of_type, options={})
|
34
52
|
super(name, options)
|
35
|
-
@
|
53
|
+
@of_type = of_type
|
36
54
|
@value = []
|
37
55
|
end
|
38
56
|
|
39
57
|
private
|
40
58
|
|
41
|
-
def
|
42
|
-
|
59
|
+
def of_type_class
|
60
|
+
@of_type.is_a?(Class) ? @of_type : @of_type.class
|
43
61
|
end
|
44
62
|
|
45
|
-
def
|
46
|
-
[Sequence, Set].include?
|
63
|
+
def composed_of_type?
|
64
|
+
[Sequence, Set].include? of_type_class
|
47
65
|
end
|
48
66
|
|
49
67
|
def value_to_der
|
50
|
-
if
|
68
|
+
if composed_of_type?
|
51
69
|
@value.map do |ary|
|
52
|
-
s =
|
70
|
+
s = of_type_class.new(@of_type.name)
|
53
71
|
sval = []
|
54
|
-
@
|
72
|
+
@of_type.value.each_with_index do |type, i|
|
55
73
|
type2 = type.dup
|
56
74
|
type2.value = ary[i]
|
57
75
|
sval << type2
|
@@ -59,8 +77,21 @@ module RASN1
|
|
59
77
|
s.value = sval
|
60
78
|
s.to_der
|
61
79
|
end.join
|
80
|
+
elsif of_type_class < Model
|
81
|
+
if of_type_class.new.root.is_a? SequenceOf
|
82
|
+
@value.map do |ary|
|
83
|
+
model = of_type_class.new
|
84
|
+
model.root.value = ary
|
85
|
+
model.to_der
|
86
|
+
end.join
|
87
|
+
else
|
88
|
+
@value.map do |hsh|
|
89
|
+
model = of_type_class.new(hsh)
|
90
|
+
model.to_der
|
91
|
+
end.join
|
92
|
+
end
|
62
93
|
else
|
63
|
-
@value.map { |v|
|
94
|
+
@value.map { |v| of_type_class.new(:t, value: v).to_der }.join
|
64
95
|
end
|
65
96
|
end
|
66
97
|
|
@@ -69,16 +100,28 @@ module RASN1
|
|
69
100
|
nb_bytes = 0
|
70
101
|
|
71
102
|
while nb_bytes < der.length
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
103
|
+
type = if composed_of_type?
|
104
|
+
@of_type.dup
|
105
|
+
elsif of_type_class < Model
|
106
|
+
of_type_class.new
|
107
|
+
else
|
108
|
+
of_type_class.new(:t)
|
109
|
+
end
|
110
|
+
nb_bytes += type.parse!(der[nb_bytes, der.length])
|
111
|
+
value = if composed_of_type?
|
112
|
+
type.value.map { |obj| obj.value }
|
113
|
+
elsif of_type_class < Model
|
114
|
+
type.to_h[type.to_h.keys.first]
|
76
115
|
else
|
77
|
-
|
116
|
+
type.value
|
78
117
|
end
|
79
118
|
@value << value
|
80
119
|
end
|
81
120
|
end
|
121
|
+
|
122
|
+
def explicit_type
|
123
|
+
self.class.new(@name, self.of_type)
|
124
|
+
end
|
82
125
|
end
|
83
126
|
end
|
84
127
|
end
|
data/lib/rasn1/version.rb
CHANGED
data/rasn1.gemspec
CHANGED
@@ -20,6 +20,8 @@ Gem::Specification.new do |spec|
|
|
20
20
|
#spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
21
|
spec.require_paths = ['lib']
|
22
22
|
|
23
|
+
spec.required_ruby_version = '>= 2.1.0'
|
24
|
+
|
23
25
|
spec.add_dependency 'yard', '~>0.9'
|
24
26
|
|
25
27
|
spec.add_development_dependency 'bundler', '~> 1.13'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rasn1
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sylvain Daubert
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-08-
|
11
|
+
date: 2017-08-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: yard
|
@@ -95,6 +95,7 @@ files:
|
|
95
95
|
- lib/rasn1.rb
|
96
96
|
- lib/rasn1/model.rb
|
97
97
|
- lib/rasn1/types.rb
|
98
|
+
- lib/rasn1/types/any.rb
|
98
99
|
- lib/rasn1/types/base.rb
|
99
100
|
- lib/rasn1/types/bit_string.rb
|
100
101
|
- lib/rasn1/types/boolean.rb
|
@@ -125,7 +126,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
125
126
|
requirements:
|
126
127
|
- - ">="
|
127
128
|
- !ruby/object:Gem::Version
|
128
|
-
version:
|
129
|
+
version: 2.1.0
|
129
130
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
131
|
requirements:
|
131
132
|
- - ">="
|