rasn1 0.1.0 → 0.2.0
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.
- 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
|
+
[](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
|
- - ">="
|