rasn1 0.6.8 → 0.9.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.
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  module Types
3
-
4
5
  # @abstract This is base class for all ASN.1 types.
5
6
  #
6
7
  # Subclasses SHOULD define:
7
- # * a TAG constant defining ASN.1 tag number,
8
+ # * an ID constant defining ASN.1 BER/DER identification number,
8
9
  # * a private method {#value_to_der} converting its {#value} to DER,
9
10
  # * a private method {#der_to_value} converting DER into {#value}.
10
11
  #
@@ -12,15 +13,15 @@ module RASN1
12
13
  # An optional value may be defined using +:optional+ key from {#initialize}:
13
14
  # Integer.new(:int, optional: true)
14
15
  # An optional value implies:
15
- # * while parsing, if decoded tag is not optional expected tag, no {ASN1Error}
16
- # is raised, and parser tries net tag,
16
+ # * while parsing, if decoded ID is not optional expected ID, no {ASN1Error}
17
+ # is raised, and parser tries next field,
17
18
  # * while encoding, if {#value} is +nil+, this value is not encoded.
18
19
  # ==Define a default value
19
20
  # A default value may be defined using +:default+ key from {#initialize}:
20
21
  # Integer.new(:int, default: 0)
21
22
  # A default value implies:
22
- # * while parsing, if decoded tag is not expected tag, no {ASN1Error} is raised
23
- # and parser sets default value to this tag. Then parser tries nex tag,
23
+ # * while parsing, if decoded ID is not expected one, no {ASN1Error} is raised
24
+ # and parser sets default value to this ID. Then parser tries next field,
24
25
  # * while encoding, if {#value} is equal to default value, this value is not
25
26
  # encoded.
26
27
  # ==Define a tagged value
@@ -43,17 +44,21 @@ module RASN1
43
44
  # Implicit tagged values may also be defined:
44
45
  # ctype_implicit = RASN1::Types::Integer.new(implicit: 0)
45
46
  # @author Sylvain Daubert
46
- class Base
47
- # Allowed ASN.1 tag classes
47
+ class Base # rubocop:disable Metrics/ClassLength
48
+ # Allowed ASN.1 classes
48
49
  CLASSES = {
49
- universal: 0x00,
50
- application: 0x40,
51
- context: 0x80,
52
- private: 0xc0
53
- }
50
+ universal: 0x00,
51
+ application: 0x40,
52
+ context: 0x80,
53
+ private: 0xc0
54
+ }.freeze
54
55
 
55
- # Maximum ASN.1 tag number
56
- MAX_TAG = 0x1e
56
+ # Binary mask to get class
57
+ # @private
58
+ CLASS_MASK = 0xc0
59
+
60
+ # @private first octet identifier for multi-octets identifier
61
+ MULTI_OCTETS_ID = 0x1f
57
62
 
58
63
  # Length value for indefinite length
59
64
  INDEFINITE_LENGTH = 0x80
@@ -64,19 +69,18 @@ module RASN1
64
69
  attr_reader :asn1_class
65
70
  # @return [Object,nil] default value, if defined
66
71
  attr_reader :default
67
- # @return [Object]
68
- attr_writer :value
69
72
 
70
73
  # Get ASN.1 type
71
74
  # @return [String]
72
75
  def self.type
73
76
  return @type if defined? @type
77
+
74
78
  @type = self.to_s.gsub(/.*::/, '').gsub(/([a-z0-9])([A-Z])/, '\1 \2').upcase
75
79
  end
76
80
 
77
81
  # Get ASN.1 type used to encode this one
78
82
  # @return [String]
79
- def self.encode_type
83
+ def self.encoded_type
80
84
  type
81
85
  end
82
86
 
@@ -91,68 +95,50 @@ module RASN1
91
95
  obj
92
96
  end
93
97
 
94
-
95
- # @overload initialize(options={})
96
- # @param [Hash] options
97
- # @option options [Symbol] :class ASN.1 tag class. Default value is +:universal+.
98
- # If +:explicit+ or +:implicit:+ is defined, default value is +:context+.
99
- # @option options [::Boolean] :optional define this tag as optional. Default
100
- # is +false+
101
- # @option options [Object] :default default value for DEFAULT tag
102
- # @option options [Object] :value value to set
103
- # @option options [::Integer] :implicit define an IMPLICIT tagged type
104
- # @option options [::Integer] :explicit define an EXPLICIT tagged type
105
- # @option options [::Boolean] :constructed if +true+, set type as constructed.
106
- # May only be used when +:explicit+ is defined, else it is discarded.
107
- # @option options [::String] :name name for this node
108
- # @overload initialize(value, options={})
109
- # @param [Object] value value to set for this ASN.1 object
110
- # @param [Hash] options
111
- # @option options [Symbol] :class ASN.1 tag class. Default value is +:universal+.
112
- # If +:explicit+ or +:implicit:+ is defined, default value is +:context+.
113
- # @option options [::Boolean] :optional define this tag as optional. Default
114
- # is +false+
115
- # @option options [Object] :default default value for DEFAULT tag
116
- # @option options [::Integer] :implicit define an IMPLICIT tagged type
117
- # @option options [::Integer] :explicit define an EXPLICIT tagged type
118
- # @option options [::Boolean] :constructed if +true+, set type as constructed.
119
- # May only be used when +:explicit+ is defined, else it is discarded.
120
- # @option options [::String] :name name for this node
121
- def initialize(value_or_options={}, options={})
98
+ # @param [Hash] options
99
+ # @option options [Symbol] :class ASN.1 class. Default value is +:universal+.
100
+ # If +:explicit+ or +:implicit:+ is defined, default value is +:context+.
101
+ # @option options [::Boolean] :optional define this tag as optional. Default
102
+ # is +false+
103
+ # @option options [Object] :default default value (ASN.1 DEFAULT)
104
+ # @option options [Object] :value value to set
105
+ # @option options [::Integer] :implicit define an IMPLICIT tagged type
106
+ # @option options [::Integer] :explicit define an EXPLICIT tagged type
107
+ # @option options [::Boolean] :constructed if +true+, set type as constructed.
108
+ # May only be used when +:explicit+ is defined, else it is discarded.
109
+ # @option options [::String] :name name for this node
110
+ def initialize(options={})
122
111
  @constructed = nil
123
- if value_or_options.is_a? Hash
124
- set_options value_or_options
125
- else
126
- set_options options
127
- @value = value_or_options
128
- end
112
+ set_options options
129
113
  end
130
114
 
131
- # Used by +#dup+ and +#clone+. Deep copy @value.
132
- def initialize_copy(other)
133
- @value = case
134
- when NilClass, TrueClass, FalseClass, Integer
135
- @value
136
- else
137
- @value.dup
138
- end
139
- @default = case
140
- when NilClass, TrueClass, FalseClass, Integer
141
- @default
142
- else
143
- @default.dup
144
- end
115
+ # Used by +#dup+ and +#clone+. Deep copy @value and @default.
116
+ def initialize_copy(_other)
117
+ @value = @value.dup
118
+ @no_value = @no_value.dup
119
+ @default = @default.dup
145
120
  end
146
121
 
147
122
  # Get value or default value
148
123
  def value
149
- if @value.nil?
150
- @default
151
- else
124
+ if value?
152
125
  @value
126
+ else
127
+ @default
153
128
  end
154
129
  end
155
130
 
131
+ # Set value. If +val+ is +nil+, unset value
132
+ # @param [Object,nil] val
133
+ def value=(val)
134
+ set_value(val)
135
+ end
136
+
137
+ # @abstract Define 'void' value (i.e. 'value' when no value was set)
138
+ def void_value
139
+ ''
140
+ end
141
+
156
142
  # @return [::Boolean]
157
143
  def optional?
158
144
  @optional
@@ -168,21 +154,21 @@ module RASN1
168
154
  # @return [::Boolean,nil] return +nil+ if not tagged, return +true+
169
155
  # if explicit, else +false+
170
156
  def explicit?
171
- !defined?(@tag) ? nil : @tag == :explicit
157
+ defined?(@tag) ? @tag == :explicit : nil
172
158
  end
173
159
 
174
160
  # Say if a tagged type is implicit
175
161
  # @return [::Boolean,nil] return +nil+ if not tagged, return +true+
176
162
  # if implicit, else +false+
177
163
  def implicit?
178
- !defined?(@tag) ? nil : @tag == :implicit
164
+ defined?(@tag) ? @tag == :implicit : nil
179
165
  end
180
166
 
181
167
  # @abstract This method SHOULD be partly implemented by subclasses, which
182
168
  # SHOULD respond to +#value_to_der+.
183
169
  # @return [String] DER-formated string
184
170
  def to_der
185
- build_tag
171
+ build
186
172
  end
187
173
 
188
174
  # @return [::Boolean] +true+ if this is a primitive type
@@ -192,7 +178,7 @@ module RASN1
192
178
 
193
179
  # @return [::Boolean] +true+ if this is a constructed type
194
180
  def constructed?
195
- !!((self.class < Constructed) || @constructed)
181
+ (self.class < Constructed) || !!@constructed
196
182
  end
197
183
 
198
184
  # Get ASN.1 type
@@ -201,17 +187,10 @@ module RASN1
201
187
  self.class.type
202
188
  end
203
189
 
204
- # Get tag value
190
+ # Get identifier value
205
191
  # @return [Integer]
206
- def tag
207
- pc = if @constructed.nil?
208
- self.class::ASN1_PC
209
- elsif @constructed # true
210
- Constructed::ASN1_PC
211
- else # false
212
- 0
213
- end
214
- tag_value | CLASSES[@asn1_class] | pc
192
+ def id
193
+ id_value
215
194
  end
216
195
 
217
196
  # @abstract This method SHOULD be partly implemented by subclasses to parse
@@ -222,13 +201,15 @@ module RASN1
222
201
  # @return [Integer] total number of parsed bytes
223
202
  # @raise [ASN1Error] error on parsing
224
203
  def parse!(der, ber: false)
225
- return 0 unless check_tag(der)
204
+ return 0 unless check_id(der)
226
205
 
227
- total_length, data = get_data(der, ber)
206
+ id_size = Types.decode_identifier_octets(der).last
207
+ total_length, data = get_data(der[id_size..-1], ber)
208
+ total_length += id_size
209
+ @no_value = false
228
210
  if explicit?
229
- # Delegate to #explicit type to generate sub-tag
211
+ # Delegate to #explicit type to generate sub-value
230
212
  type = explicit_type
231
- type.value = @value
232
213
  type.parse!(data)
233
214
  @value = type.value
234
215
  else
@@ -247,11 +228,9 @@ module RASN1
247
228
  # @param [Integer] level
248
229
  # @return [String]
249
230
  def inspect(level=0)
250
- str = ''
251
- str << ' ' * level if level > 0
252
- str << "#{@name} " unless @name.nil?
253
- str << "#{type}: #{value.inspect}"
254
- str << " OPTIONAL" if optional?
231
+ str = common_inspect(level)
232
+ str << ' ' << inspect_value
233
+ str << ' OPTIONAL' if optional?
255
234
  str << " DEFAULT #{@default}" unless @default.nil?
256
235
  str
257
236
  end
@@ -265,6 +244,34 @@ module RASN1
265
244
 
266
245
  private
267
246
 
247
+ def pc_bit
248
+ if @constructed.nil?
249
+ self.class::ASN1_PC
250
+ elsif @constructed # true
251
+ Constructed::ASN1_PC
252
+ else # false
253
+ Primitive::ASN1_PC
254
+ end
255
+ end
256
+
257
+ def common_inspect(level)
258
+ lvl = level >= 0 ? level : 0
259
+ str = ' ' * lvl
260
+ str << "#{@name} " unless @name.nil?
261
+ str << asn1_class.to_s.upcase << ' ' unless asn1_class == :universal
262
+ str << "[#{id}] EXPLICIT " if explicit?
263
+ str << "[#{id}] IMPLICIT " if implicit?
264
+ str << "#{type}:"
265
+ end
266
+
267
+ def inspect_value
268
+ if value?
269
+ value.inspect
270
+ else
271
+ '(NO VALUE)'
272
+ end
273
+ end
274
+
268
275
  def value_to_der
269
276
  case @value
270
277
  when Base
@@ -274,68 +281,82 @@ module RASN1
274
281
  end
275
282
  end
276
283
 
277
- def der_to_value(der, ber:false)
284
+ def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
278
285
  @value = der
279
286
  end
280
287
 
281
- def set_options(options)
288
+ def set_options(options) # rubocop:disable Naming/AccessorMethodName
282
289
  set_class options[:class]
283
290
  set_optional options[:optional]
284
291
  set_default options[:default]
285
292
  set_tag options
286
- @value = options[:value]
293
+ set_value options[:value]
287
294
  @name = options[:name]
295
+ @options = options
288
296
  end
289
297
 
290
- def set_class(asn1_class)
298
+ def set_class(asn1_class) # rubocop:disable Naming/AccessorMethodName
291
299
  case asn1_class
292
300
  when nil
293
301
  @asn1_class = :universal
294
302
  when Symbol
295
- if CLASSES.keys.include? asn1_class
296
- @asn1_class = asn1_class
297
- else
298
- raise ClassError
299
- end
303
+ raise ClassError unless CLASSES.key? asn1_class
304
+
305
+ @asn1_class = asn1_class
300
306
  else
301
307
  raise ClassError
302
308
  end
303
309
  end
304
310
 
305
- def set_optional(optional)
311
+ def set_optional(optional) # rubocop:disable Naming/AccessorMethodName
306
312
  @optional = !!optional
307
313
  end
308
314
 
309
- def set_default(default)
315
+ def set_default(default) # rubocop:disable Naming/AccessorMethodName
310
316
  @default = default
311
317
  end
312
318
 
313
319
  # handle undocumented option +:tag_value+, used internally by
314
320
  # {RASN1.parse} to parse non-universal class tags.
315
- def set_tag(options)
321
+ def set_tag(options) # rubocop:disable Naming/AccessorMethodName
316
322
  if options[:explicit]
317
323
  @tag = :explicit
318
- @tag_value = options[:explicit]
324
+ @id_value = options[:explicit]
319
325
  @constructed = options[:constructed]
320
326
  elsif options[:implicit]
321
327
  @tag = :implicit
322
- @tag_value = options[:implicit]
328
+ @id_value = options[:implicit]
323
329
  @constructed = options[:constructed]
324
330
  elsif options[:tag_value]
325
- @tag_value = options[:tag_value]
331
+ @id_value = options[:tag_value]
326
332
  @constructed = options[:constructed]
327
333
  end
328
334
 
329
335
  @asn1_class = :context if defined?(@tag) && (@asn1_class == :universal)
330
336
  end
331
337
 
332
- def build_tag?
333
- !(!@default.nil? and (@value.nil? or @value == @default)) and
334
- !(optional? and @value.nil?)
338
+ def set_value(value) # rubocop:disable Naming/AccessorMethodName
339
+ if value.nil?
340
+ @no_value = true
341
+ @value = void_value
342
+ else
343
+ @no_value = false
344
+ @value = value
345
+ end
346
+ value
347
+ end
348
+
349
+ def value?
350
+ !@no_value
351
+ end
352
+
353
+ def can_build?
354
+ (@default.nil? || (value? && (@value != @default))) &&
355
+ (!optional? || value?)
335
356
  end
336
357
 
337
- def build_tag
338
- if build_tag?
358
+ def build
359
+ if can_build?
339
360
  if explicit?
340
361
  v = explicit_type
341
362
  v.value = @value
@@ -343,35 +364,52 @@ module RASN1
343
364
  else
344
365
  encoded_value = value_to_der
345
366
  end
346
- encode_tag << encode_size(encoded_value.size) << encoded_value
367
+ encode_identifier_octets << encode_size(encoded_value.size) << encoded_value
347
368
  else
348
369
  ''
349
370
  end
350
371
  end
351
372
 
352
- def tag_value
353
- return @tag_value if defined? @tag_value
373
+ def id_value
374
+ return @id_value if defined? @id_value
354
375
 
355
- self.class::TAG
376
+ self.class::ID
356
377
  end
357
378
 
358
- def encode_tag
359
- if tag_value <= MAX_TAG
360
- [tag].pack('C')
379
+ def encode_identifier_octets
380
+ id2octets.pack('C*')
381
+ end
382
+
383
+ def id2octets
384
+ first_octet = CLASSES[asn1_class] | pc_bit
385
+ if id < MULTI_OCTETS_ID
386
+ [first_octet | id]
361
387
  else
362
- raise ASN1Error, 'multi-byte tag value are not supported'
388
+ [first_octet | MULTI_OCTETS_ID] + unsigned_to_chained_octets(id)
363
389
  end
364
390
  end
365
391
 
392
+ # Encode an unsigned integer on multiple octets.
393
+ # Value is encoded on bit 6-0 of each octet, bit 7(MSB) indicates wether
394
+ # further octets follow.
395
+ def unsigned_to_chained_octets(value)
396
+ ary = []
397
+ while value.positive?
398
+ ary.unshift(value & 0x7f | 0x80)
399
+ value >>= 7
400
+ end
401
+ ary[-1] &= 0x7f
402
+ ary
403
+ end
404
+
366
405
  def encode_size(size)
367
406
  if size >= INDEFINITE_LENGTH
368
407
  bytes = []
369
408
  while size > 255
370
- bytes << (size & 0xff)
409
+ bytes.unshift(size & 0xff)
371
410
  size >>= 8
372
411
  end
373
- bytes << size
374
- bytes.reverse!
412
+ bytes.unshift(size)
375
413
  bytes.unshift(INDEFINITE_LENGTH | bytes.size)
376
414
  bytes.pack('C*')
377
415
  else
@@ -379,84 +417,92 @@ module RASN1
379
417
  end
380
418
  end
381
419
 
382
- def check_tag(der)
383
- tag = der[0, 1]
384
- if tag != encode_tag
385
- if optional?
386
- @value = nil
387
- elsif !@default.nil?
388
- @value = @default
389
- else
390
- raise_tag_error(tag)
391
- end
392
- false
420
+ def check_id(der)
421
+ expected_id = encode_identifier_octets
422
+ real_id = der[0, expected_id.size]
423
+ return true if real_id == expected_id
424
+
425
+ if optional?
426
+ @no_value = true
427
+ @value = void_value
428
+ elsif !@default.nil?
429
+ @value = @default
393
430
  else
394
- true
431
+ raise_id_error(der)
395
432
  end
433
+ false
396
434
  end
397
435
 
398
436
  def get_data(der, ber)
399
- length = der[1, 1].unpack('C').first
437
+ length = der.unpack1('C').to_i
400
438
  length_length = 0
401
439
 
402
440
  if length == INDEFINITE_LENGTH
403
- if primitive?
404
- raise ASN1Error, "malformed #{type} TAG: indefinite length " \
405
- "forbidden for primitive types"
406
- else
407
- if ber
408
- raise NotImplementedError, "TAG: indefinite length not " \
409
- "supported yet"
410
- else
411
- raise ASN1Error, "TAG: indefinite length forbidden in DER " \
412
- "encoding"
413
- end
414
- end
415
- elsif length < INDEFINITE_LENGTH
416
- data = der[2, length]
417
- else
441
+ raise_on_indefinite_length(ber)
442
+ elsif length > INDEFINITE_LENGTH
418
443
  length_length = length & 0x7f
419
- length = der[2, length_length].unpack('C*').
420
- reduce(0) { |len, b| (len << 8) | b }
421
- data = der[2 + length_length, length]
444
+ length = der[1, length_length].unpack('C*')
445
+ .reduce(0) { |len, b| (len << 8) | b }
422
446
  end
447
+ data = der[1 + length_length, length]
423
448
 
424
- total_length = 2 + length
425
- total_length += length_length if length_length > 0
449
+ total_length = 1 + length
450
+ total_length += length_length if length_length.positive?
426
451
 
427
452
  [total_length, data]
428
453
  end
429
454
 
455
+ def raise_on_indefinite_length(ber)
456
+ if primitive?
457
+ raise ASN1Error, "malformed #{type}: indefinite length " \
458
+ 'forbidden for primitive types'
459
+ elsif ber
460
+ raise NotImplementedError, 'indefinite length not supported'
461
+ else
462
+ raise ASN1Error, 'indefinite length forbidden in DER encoding'
463
+ end
464
+ end
465
+
430
466
  def explicit_type
431
467
  self.class.new
432
468
  end
433
469
 
434
- def raise_tag_error(tag)
435
- msg = name.nil? ? '' : "#{name}: "
436
- msg << "Expected #{self2name} but get #{tag2name(tag)}"
470
+ def raise_id_error(der)
471
+ msg = name.nil? ? +'' : +"#{name}: "
472
+ msg << "Expected #{self2name} but get #{der2name(der)}"
437
473
  raise ASN1Error, msg
438
474
  end
439
475
 
476
+ def class_from_numeric_id(id)
477
+ CLASSES.key(id & CLASS_MASK)
478
+ end
479
+
440
480
  def self2name
441
- name = CLASSES.key(tag & 0xc0).to_s.upcase
442
- name << " #{tag & Constructed::ASN1_PC > 0 ? 'CONSTRUCTED' : 'PRIMITIVE'}"
481
+ name = +"#{asn1_class.to_s.upcase} #{constructed? ? 'CONSTRUCTED' : 'PRIMITIVE'}"
443
482
  if implicit? || explicit?
444
- name << ' 0x%02X (0x%02X)' % [tag & 0x1f, tag]
483
+ name << ' 0x%X (0x%s)' % [id, bin2hex(encode_identifier_octets)]
445
484
  else
446
485
  name << ' ' << self.class.type
447
486
  end
448
487
  end
449
488
 
450
- def tag2name(tag)
451
- return 'no tag' if tag.nil? or tag.empty?
489
+ def der2name(der)
490
+ return 'no ID' if der.nil? || der.empty?
491
+
492
+ asn1_class, pc, id, id_size = Types.decode_identifier_octets(der)
493
+ name = +"#{asn1_class.to_s.upcase} #{pc.to_s.upcase}"
494
+ type = find_type(id)
495
+ name << " #{type.nil? ? '0x%X (0x%s)' % [id, bin2hex(der[0...id_size])] : type.encoded_type}"
496
+ end
497
+
498
+ def find_type(id)
499
+ Types.constants.map { |c| Types.const_get(c) }
500
+ .select { |klass| klass < Primitive || klass < Constructed }
501
+ .find { |klass| klass::ID == id }
502
+ end
452
503
 
453
- itag = tag.unpack('C').first
454
- name = CLASSES.key(itag & 0xc0).to_s.upcase
455
- name << " #{itag & Constructed::ASN1_PC > 0 ? 'CONSTRUCTED' : 'PRIMITIVE'}"
456
- type = Types.constants.map { |c| Types.const_get(c) }.
457
- select { |klass| klass < Primitive || klass < Constructed }.
458
- find { |klass| klass::TAG == itag & 0x1f }
459
- name << " #{type.nil? ? "0x%02X (0x%02X)" % [itag & 0x1f, itag] : type.encode_type }"
504
+ def bin2hex(str)
505
+ str.unpack1('H*')
460
506
  end
461
507
  end
462
508
  end