1010
1111module OpenSSL
1212 module ASN1
13+ INT_MAX = begin
14+ n_bytes = [ 42 ] . pack ( 'i' ) . size
15+ n_bits = n_bytes * 16
16+ 2 ** ( n_bits - 2 ) - 1
17+ end
18+
19+ V_ASN1_UNIVERSAL = 0x00
20+ V_ASN1_APPLICATION = 0x40
21+ V_ASN1_CONTEXT_SPECIFIC = 0x80
22+ V_ASN1_PRIVATE = 0xc0
23+ V_ASN1_CONSTRUCTED = 0x20
24+ V_ASN1_PRIMITIVE_TAG = 0x1f
25+
26+
1327 class ASN1Data
1428 #
1529 # Carries the value of a ASN.1 type.
@@ -71,6 +85,101 @@ def initialize(value, tag, tag_class)
7185 @tag_class = tag_class
7286 @indefinite_length = false
7387 end
88+
89+ def to_der
90+ if @value . is_a? ( Array )
91+ cons_to_der
92+ elsif @indefinite_length
93+ raise ASN1Error , "indefinite length form cannot be used " \
94+ "with primitive encoding"
95+ else
96+ prim_to_der
97+ end
98+ end
99+
100+ private
101+
102+ def cons_to_der
103+ ary = @value . to_a
104+ str = "" . b
105+
106+ @value . each_with_index do |item , idx |
107+ if @indefinite_length && item . is_a? ( EndOfContent )
108+ if idx != ary . size - 1
109+ raise ASN1Error , "illegal EOC octets in value"
110+ end
111+
112+ break
113+ end
114+
115+ item = item . to_der if item . respond_to? ( :to_der )
116+
117+ str << item
118+ end
119+
120+ to_der_internal ( str , true )
121+ end
122+
123+ def prim_to_der
124+ return to_der_internal ( @value ) unless ASN1 . take_default_tag ( self . class )
125+
126+ # TODO: how to translate this?
127+ asn1 = ossl_asn1_get_asn1type ( self )
128+ alllen = i2d_ASN1_TYPE ( asn1 , NULL )
129+
130+ if ( alllen < 0 )
131+ ASN1_TYPE_free ( asn1 )
132+ raise ASN1Error , "i2d_ASN1_TYPE"
133+ end
134+
135+ str = String . new ( capacity : alllen )
136+
137+ p0 = p1 = str ;
138+ if ( i2d_ASN1_TYPE ( asn1 , &p0 ) < 0 )
139+ ASN1_TYPE_free ( asn1 ) ;
140+ ossl_raise ( eASN1Error , "i2d_ASN1_TYPE" ) ;
141+ end
142+ ASN1_TYPE_free ( asn1 ) ;
143+ ossl_str_adjust ( str , p0 ) ;
144+
145+ j = ASN1_get_object ( p1 , bodylen , tag , tc , alllen )
146+ if j & 0x80
147+ ossl_raise ( eASN1Error , "ASN1_get_object" ) ; # should not happen
148+ end
149+
150+ to_der_internal ( str [ ( alllen - bodylen ) ..-1 ] )
151+ end
152+
153+ def to_der_internal ( body , constructed = false )
154+ default_tag = ASN1 . take_default_tag ( self . class )
155+ body_len = body . size
156+
157+ if @tagging == :EXPLICIT
158+ raise ASN1Error , "explicit tagging of unknown tag" unless default_tag
159+
160+ inner_len = ASN1 . object_size ( constructed && @indefinite_length , body_len , default_tag )
161+ total_len = ASN1 . object_size ( @indefinite_length , inner_len , @tag )
162+
163+ # Put explicit tag
164+ str = ASN1 . put_object ( constructed , @indefinite_length , inner_len , @tag , @tag_class ) <<
165+ # Append inner object
166+ ASN1 . put_object ( constructed , @indefinite_length , body_len , default_tag , :UNIVERSAL )
167+
168+ str << body
169+ if @indefinite_length
170+ str << "\x00 \x00 \x00 \x00 "
171+ end
172+ else
173+ total_length = ASN1 . object_size ( constructed && @indefinite_length , body_len , @tag )
174+ str = ASN1 . put_object ( constructed , @indefinite_length , body_len , @tag , @tag_class )
175+ str << body
176+ if @indefinite_length
177+ str << "\x00 \x00 "
178+ end
179+ end
180+
181+ str
182+ end
74183 end
75184
76185 module TaggedASN1Data
@@ -172,8 +281,110 @@ def initialize
172281 end
173282 end
174283
284+ module_function
285+
286+ # ruby port of ASN1_object_size
287+ def object_size ( indefinite_length , length , tag )
288+ ret = 1
289+
290+ return -1 if length < 0
291+
292+ if tag >= 31
293+ while tag > 0
294+ tag >>= 7
295+ ret += 1
296+ end
297+ end
298+ if indefinite_length
299+ ret += 3
300+ else
301+ ret += 1
302+ if length > 127
303+ tmplen = length
304+ while tmplen > 0
305+ tmplen >>= 8
306+ ret +=1
307+ end
308+ end
309+ end
310+
311+ return -1 if ( ret >= INT_MAX - length )
312+
313+
314+ ret + length
315+ end
316+
317+ # ruby port of openssl ASN1_put_object
318+ def put_object ( constructed , indefinite_length , length , tag , tag_class )
319+ str = "" . b
320+ xclass = take_asn1_tag_class ( tag_class )
321+
322+ i = constructed ? V_ASN1_CONSTRUCTED : 0
323+ i |= ( xclass & V_ASN1_PRIVATE )
324+
325+ if tag < 31
326+ str << ( i | ( tag & V_ASN1_PRIMITIVE_TAG ) ) . chr
327+
328+ else
329+ str << ( i | V_ASN1_PRIMITIVE_TAG ) . chr
330+
331+ i = 0
332+ ttag = tag
333+
334+ while ttag > 0
335+ i += 1
336+ ttag >>= 7
337+ end
338+
339+ ttag = i
340+
341+ while i > 0
342+ i -= 1
343+ tag_str = tag & 0x7f
344+ if ( i != ( ttag - 1 ) )
345+ tag_str |= 0x80
346+ end
347+ str . insert ( 1 , tag_str . chr )
348+ tag >>= 7
349+ end
350+ end
351+
352+ if constructed && indefinite_length
353+ str << 0x80 . chr
354+ else
355+ str << put_length ( length )
356+ end
357+ str
358+ end
359+
360+
361+ def put_length ( length )
362+ raise ASN1Error , "invalid length" if length < 0
363+
364+ if length < 0x80
365+ length . chr
366+ else
367+ i = length
368+
369+ if i >= 0
370+ done = 0
371+ else
372+ done = -1
373+ end
374+
375+ octets = "" . b
376+ begin
377+ octets = ( i & 0xff ) . chr << octets
378+ i = i >> 8
379+ end until i == done
380+ octets
381+
382+ ( octets . size | 0x80 ) . chr << octets
383+ end
384+ end
385+
175386 # :nodoc:
176- def self . take_default_tag ( klass )
387+ def take_default_tag ( klass )
177388 tag = CLASS_TAG_MAP [ klass ]
178389
179390 return tag if tag
@@ -184,5 +395,17 @@ def self.take_default_tag(klass)
184395
185396 take_default_tag ( sklass )
186397 end
398+
399+ # from ossl_asn1.c : ossl_asn1_tag_class
400+ def take_asn1_tag_class ( tag_class )
401+ case tag_class
402+ when :UNIVERSAL , nil then V_ASN1_UNIVERSAL
403+ when :APPLICATION then V_ASN1_APPLICATION
404+ when :CONTEXT_SPECIFIC then V_ASN1_CONTEXT_SPECIFIC
405+ when :PRIVATE then V_ASN1_PRIVATE
406+ else
407+ raise ASN1Error , "invalid tag class"
408+ end
409+ end
187410 end
188411end
0 commit comments