rasn1 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,70 @@
1
+ module RASN1
2
+ module Types
3
+
4
+ # ASN.1 Bit String
5
+ # @author Sylvain Daubert
6
+ class BitString < Primitive
7
+ TAG = 0x03
8
+
9
+ # @return [Integer] bit length of bit string
10
+ attr_accessor :bit_length
11
+
12
+ # @param [Symbol, String] name name for this tag in grammar
13
+ # @param [Hash] options
14
+ # @option options [Symbol] :class ASN.1 tag class. Default value is +:universal+
15
+ # @option options [::Boolean] :optional define this tag as optional. Default
16
+ # is +false+
17
+ # @option options [Object] :default default value for DEFAULT tag
18
+ # @option options [Object] :bit_length default bit_length value. Should be
19
+ # present if +:default+ is set
20
+ def initialize(name, options={})
21
+ super
22
+ if @default
23
+ if options[:bit_length].nil?
24
+ raise ASN1Error, "TAG #@name: default bit length is not defined"
25
+ end
26
+ @default_bit_length = options[:bit_length]
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def build_tag?
33
+ !(!@default.nil? and (@value.nil? or @value == @default and
34
+ @bit_length == @default_bit_length)) and
35
+ !(optional? and @value.nil?)
36
+ end
37
+
38
+ def value_to_der
39
+ raise ASN1Error, "TAG #@name: bit length is not set" if @bit_length.nil?
40
+
41
+ while @value.length * 8 < @bit_length
42
+ @value << "\x00"
43
+ end
44
+ @value.force_encoding('BINARY')
45
+
46
+ if @value.length * 8 > @bit_length
47
+ max_len = @bit_length / 8 + (@bit_length % 8 > 0 ? 1 : 0)
48
+ @value = @value[0, max_len]
49
+ end
50
+
51
+ unused = @value.length * 8 - @bit_length
52
+ der = [unused, @value].pack('CA*')
53
+
54
+ if unused > 0
55
+ last_byte = @value[-1].unpack('C').first
56
+ last_byte &= (0xff >> unused) << unused
57
+ der[-1] = [last_byte].pack('C')
58
+ end
59
+
60
+ der
61
+ end
62
+
63
+ def der_to_value(der, ber:false)
64
+ unused, @value = der.unpack('CA*')
65
+ @bit_length = @value.length * 8 - unused
66
+ end
67
+ end
68
+ end
69
+ end
70
+
@@ -0,0 +1,36 @@
1
+ module RASN1
2
+ module Types
3
+
4
+ # ASN.1 Boolean
5
+ # @author Sylvain Daubert
6
+ class Boolean < Primitive
7
+ TAG = 0x01
8
+
9
+ private
10
+
11
+ def value_to_der
12
+ [@value ? 0xff : 0x00].pack('C')
13
+ end
14
+
15
+ def der_to_value(der, ber: false)
16
+ unless der.size == 1
17
+ raise ASN1Error, "tag #@name: BOOLEAN should have a length of 1"
18
+ end
19
+
20
+ bool = der.unpack('C').first
21
+ case bool
22
+ when 0
23
+ @value = false
24
+ when 0xff
25
+ @value = true
26
+ else
27
+ if ber
28
+ @value = true
29
+ else
30
+ raise ASN1Error, "tag #@name: bad value 0x%02x for BOOLEAN" % bool
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,97 @@
1
+ module RASN1
2
+ module Types
3
+
4
+ # A ASN.1 CHOICE is a choice between different types.
5
+ #
6
+ # == Create a CHOICE
7
+ # A CHOICE is defined this way:
8
+ # choice = Choice.new(:a_choice)
9
+ # choice.value = [Integer.new(:int1, implicit: 0, class: :context),
10
+ # Integer.new(:int2, implicit: 1, class: :context),
11
+ # OctetString.new(:os, implicit: 2, class: :context)]
12
+ # The chosen type may be set this way:
13
+ # choice.chosen = 0 # choose :int1
14
+ # The chosen value may be set these ways:
15
+ # choise.value[choice.chosen].value = 1
16
+ # choise.set_chosen_value 1
17
+ # The chosen value may be got these ways:
18
+ # choise.value[choice.chosen].value # => 1
19
+ # choice.chosen_value # => 1
20
+ #
21
+ # == Encode a CHOICE
22
+ # {#to_der} only encodes the chosen value:
23
+ # choise.to_der # => "\x80\x01\x01"
24
+ #
25
+ # == Parse a CHOICE
26
+ # Parsing a CHOICE set {#chosen} and set value to chosen type. If parsed string does
27
+ # not contain a type from CHOICE, a {RASN1::ASN1Error} is raised.
28
+ # str = "\x04\x03abc"
29
+ # choice.parse! str
30
+ # choice.chosen # => 2
31
+ # choice.chosen_value # => "abc"
32
+ # @author Sylvain Daubert
33
+ class Choice < Base
34
+
35
+ # Chosen type
36
+ # @return [Integer] index of type in choice value
37
+ attr_accessor :chosen
38
+
39
+ # Set chosen value.
40
+ # @note {#chosen} MUST be set before calling this method
41
+ # @param [Object] value
42
+ # @return [Object] value
43
+ # @raise [ChoiceError] {#chosen} not set
44
+ def set_chosen_value(value)
45
+ check_chosen
46
+ @value[@chosen].value = value
47
+ end
48
+
49
+ # Get chosen value
50
+ # @note {#chosen} MUST be set before calling this method
51
+ # @return [Object] value
52
+ # @raise [ChoiceError] {#chosen} not set
53
+ def chosen_value
54
+ check_chosen
55
+ @value[@chosen].value
56
+ end
57
+
58
+ # @note {#chosen} MUST be set before calling this method
59
+ # @return [String] DER-formated string
60
+ # @raise [ChoiceError] {#chosen} not set
61
+ def to_der
62
+ check_chosen
63
+ @value[@chosen].to_der
64
+ end
65
+
66
+ # Parse a DER string. This method updates object by setting {#chosen} and
67
+ # chosen value.
68
+ # @param [String] der DER string
69
+ # @param [Boolean] ber if +true+, accept BER encoding
70
+ # @return [Integer] total number of parsed bytes
71
+ # @raise [ASN1Error] error on parsing
72
+ def parse!(der, ber: false)
73
+ parsed = false
74
+ @value.each_with_index do |element, i|
75
+ begin
76
+ @chosen = i
77
+ nb_bytes = element.parse!(der)
78
+ parsed = true
79
+ break nb_bytes
80
+ rescue ASN1Error
81
+ @chosen = nil
82
+ next
83
+ end
84
+ end
85
+ raise ASN1Error, "CHOICE #@name: no type matching #{der.inspect}" unless parsed
86
+ end
87
+
88
+ private
89
+
90
+ def check_chosen
91
+ raise ChoiceError if @chosen.nil?
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+
@@ -0,0 +1,13 @@
1
+ module RASN1
2
+ module Types
3
+
4
+ # @abstract This class SHOULD be used as base class for all ASN.1 primitive
5
+ # types.
6
+ # Base class for all ASN.1 constructed types
7
+ # @author Sylvain Daubert
8
+ class Constructed < Base
9
+ # Constructed value
10
+ ASN1_PC = 0x20
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,119 @@
1
+ module RASN1
2
+ module Types
3
+
4
+ # ASN.1 Enumerated
5
+ #
6
+ # An enumerated type permits to assign names to integer values. It may be defined
7
+ # different ways:
8
+ # enum = RASN1::Types::Enumerated.new(:name, enum: { 'a' => 0, 'b' => 1, 'c' => 2 })
9
+ # enum = RASN1::Types::Enumerated.new(:name, enum: { a: 0, b: 1, c: 2 })
10
+ # Its value should be setting as an Integer or a String/symbol:
11
+ # enum.value = :b
12
+ # enum.value = 1 # equivalent to :b
13
+ # But its value is always stored as named integer:
14
+ # enum.value = :b
15
+ # enum.value # => :b
16
+ # enum.value = 0
17
+ # enum.value # => :a
18
+ # A {EnumeratedError} is raised when set value is not in enumeration.
19
+ # @author Sylvain Daubert
20
+ class Enumerated < Integer
21
+
22
+ # @param [Symbol, String] name name for this tag in grammar
23
+ # @param [Hash] options
24
+ # @option options [Symbol] :class ASN.1 tag class. Default value is +:universal+
25
+ # @option options [::Boolean] :optional define this tag as optional. Default
26
+ # is +false+
27
+ # @option options [Object] :default default value for DEFAULT tag
28
+ # @option options [Hash] :enum enumeration hash. Keys are names, and values
29
+ # are integers. This key is mandatory.
30
+ # @raise [EnumeratedError] +:enum+ key is not present
31
+ # @raise [EnumeratedError] +:default+ value is unknown
32
+ def initialize(name, options={})
33
+ super
34
+ raise EnumeratedError, 'no enumeration given' unless options.has_key? :enum
35
+ @enum = options[:enum]
36
+
37
+ case @default
38
+ when String,Symbol
39
+ unless @enum.has_key? @default
40
+ raise EnumeratedError, "TAG #@name: unknwon enumerated default value #@default"
41
+ end
42
+ when ::Integer
43
+ if @enum.has_value? @default
44
+ @default = @enum.key(@default)
45
+ else
46
+ raise EnumeratedError, "TAG #@name: default value #@defalt not in enumeration"
47
+ end
48
+ when nil
49
+ else
50
+ raise TypeError, "TAG #@name: #{@value.class} not handled as default value"
51
+ end
52
+ end
53
+
54
+ # @param [Integer,String,Symbol,nil] v
55
+ # @return [String,Symbol,nil]
56
+ def value=(v)
57
+ case v
58
+ when String,Symbol
59
+ unless @enum.has_key? v
60
+ raise EnumeratedError, "TAG #@name: unknwon enumerated value #{v}"
61
+ end
62
+ @value = v
63
+ when ::Integer
64
+ unless @enum.has_value? v
65
+ raise EnumeratedError, "TAG #@name: #{v} not in enumeration"
66
+ end
67
+ @value = @enum.key(v)
68
+ when nil
69
+ @value = nil
70
+ else
71
+ raise EnumeratedError, "TAG #@name: not in enumeration"
72
+ end
73
+ end
74
+
75
+ # @return [Integer]
76
+ def to_i
77
+ case @value
78
+ when String, Symbol
79
+ @enum[@value]
80
+ when ::Integer
81
+ super
82
+ when nil
83
+ if @default
84
+ @enum[@default]
85
+ else
86
+ 0
87
+ end
88
+ else
89
+ raise TypeError, "TAG #@name: #{@value.class} not handled"
90
+ end
91
+ end
92
+
93
+ # @return [Hash]
94
+ def to_h
95
+ @enum
96
+ end
97
+
98
+ private
99
+
100
+ def value_to_der
101
+ case @value
102
+ when String, Symbol
103
+ super @enum[@value]
104
+ when ::Integer
105
+ super
106
+ else
107
+ raise TypeError, "TAG #@name: #{@value.class} not handled"
108
+ end
109
+ end
110
+
111
+ def der_to_value(der, ber:false)
112
+ super
113
+ v = @value
114
+ @value = @enum.key(v)
115
+ raise EnumeratedError, "TAG #@name: value #{v} not in enumeration" if @value.nil?
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,42 @@
1
+ module RASN1
2
+ module Types
3
+
4
+ # ASN.1 Integer
5
+ # @author Sylvain Daubert
6
+ class Integer < Primitive
7
+ TAG = 0x02
8
+
9
+ # @return [Integer]
10
+ def to_i
11
+ @value || @default || 0
12
+ end
13
+
14
+ private
15
+
16
+ def value_to_der(value=nil)
17
+ v = value || @value
18
+ size = v.bit_length / 8 + (v.bit_length % 8 > 0 ? 1 : 0)
19
+ size = 1 if size == 0
20
+ comp_value = if v > 0
21
+ # If MSB is 1, increment size to set initial octet
22
+ # to 0 to amrk it as a positive integer
23
+ size += 1 if v >> (size * 8 - 1) == 1
24
+ v
25
+ else
26
+ ~(v.abs) + 1
27
+ end
28
+ ary = []
29
+ size.times { ary << (comp_value & 0xff); comp_value >>= 8 }
30
+ ary.reverse.pack('C*')
31
+ end
32
+
33
+ def der_to_value(der, ber: false)
34
+ ary = der.unpack('C*')
35
+ @value = ary.reduce(0) { |len, b| (len << 8) | b }
36
+ if ary[0] & 0x80 == 0x80
37
+ @value = -((~@value & ((1 << @value.bit_length) - 1)) + 1)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,19 @@
1
+ module RASN1
2
+ module Types
3
+
4
+ # ASN.1 Null
5
+ # @author Sylvain Daubert
6
+ class Null < Primitive
7
+ TAG = 0x05
8
+
9
+ def value_to_der
10
+ ''
11
+ end
12
+
13
+ def der_to_value(der, ber: false)
14
+ raise ASN1Error, "NULL TAG should not have content!" if der.length > 0
15
+ @value = nil
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,79 @@
1
+ module RASN1
2
+ module Types
3
+
4
+ # ASN.1 Object ID
5
+ # @author Sylvain Daubert
6
+ class ObjectId < Primitive
7
+ TAG = 6
8
+
9
+ private
10
+
11
+ def value_to_der
12
+ ids = @value.split('.').map! { |str| str.to_i }
13
+
14
+ if ids[0] > 2
15
+ raise ASN1Error, 'OBJECT ID #@name: first subidentifier should be less than 3'
16
+ end
17
+ if ids[0] < 2 and ids[1] > 39
18
+ raise ASN1Error, 'OBJECT ID #@name: second subidentifier should be less than 40'
19
+ end
20
+
21
+ ids[0, 2] = ids[0] * 40 + ids[1]
22
+ ids.map! do |v|
23
+ next v if v < 128
24
+
25
+ ary = []
26
+ while v > 0
27
+ ary.unshift (v & 0x7f) | 0x80
28
+ v >>= 7
29
+ end
30
+ ary[-1] &= 0x7f
31
+ ary
32
+ end
33
+ ids.flatten.pack('C*')
34
+ end
35
+
36
+ def der_to_value(der, ber: false)
37
+ bytes = der.unpack('C*')
38
+ ids = if bytes[0] < 80
39
+ remove = 1
40
+ [ bytes[0] / 40, bytes[0] % 40]
41
+ elsif bytes[0] < 128
42
+ remove = 1
43
+ [2, bytes[0] - 80]
44
+ else
45
+ remove = 1
46
+ second_id = bytes[0] & 0x7f
47
+ bytes[1..-1].each do |byte|
48
+ remove += 1
49
+ second_id <<= 7
50
+ if byte < 128
51
+ second_id |= byte
52
+ break
53
+ else
54
+ second_id |= byte & 0x7f
55
+ end
56
+ end
57
+ [2, second_id - 80]
58
+ end
59
+
60
+ id = 0
61
+ bytes.shift(remove)
62
+ bytes.each do |byte|
63
+ if byte < 128
64
+ if id == 0
65
+ ids << byte
66
+ else
67
+ ids << ((id << 7) | byte)
68
+ id = 0
69
+ end
70
+ else
71
+ id = (id << 7) | (byte & 0x7f)
72
+ end
73
+ end
74
+
75
+ @value = ids.join('.')
76
+ end
77
+ end
78
+ end
79
+ end