Bug #4030 » asn1_inf_length.diff
| ruby/ext/openssl/ossl_asn1.c (working copy) | ||
|---|---|---|
| }; | ||
| #endif | ||
| static VALUE join_der(VALUE enumerable); | ||
| /* | ||
|  * DATE conversion | ||
|  */ | ||
| ... | ... | |
| /* | ||
|  * ASN1 module | ||
|  */ | ||
| #define ossl_asn1_get_value(o)       rb_attr_get((o),rb_intern("@value")) | ||
| #define ossl_asn1_get_tag(o)         rb_attr_get((o),rb_intern("@tag")) | ||
| #define ossl_asn1_get_tagging(o)     rb_attr_get((o),rb_intern("@tagging")) | ||
| #define ossl_asn1_get_tag_class(o)   rb_attr_get((o),rb_intern("@tag_class")) | ||
| #define ossl_asn1_get_value(o)           rb_attr_get((o),rb_intern("@value")) | ||
| #define ossl_asn1_get_tag(o)             rb_attr_get((o),rb_intern("@tag")) | ||
| #define ossl_asn1_get_tagging(o)         rb_attr_get((o),rb_intern("@tagging")) | ||
| #define ossl_asn1_get_tag_class(o)       rb_attr_get((o),rb_intern("@tag_class")) | ||
| #define ossl_asn1_get_infinite_length(o) rb_attr_get((o),rb_intern("@infinite_length")) | ||
| #define ossl_asn1_set_value(o,v)     rb_iv_set((o),"@value",(v)) | ||
| #define ossl_asn1_set_tag(o,v)       rb_iv_set((o),"@tag",(v)) | ||
| #define ossl_asn1_set_tagging(o,v)   rb_iv_set((o),"@tagging",(v)) | ||
| #define ossl_asn1_set_tag_class(o,v) rb_iv_set((o),"@tag_class",(v)) | ||
| #define ossl_asn1_set_value(o,v)           rb_iv_set((o),"@value",(v)) | ||
| #define ossl_asn1_set_tag(o,v)             rb_iv_set((o),"@tag",(v)) | ||
| #define ossl_asn1_set_tagging(o,v)         rb_iv_set((o),"@tagging",(v)) | ||
| #define ossl_asn1_set_tag_class(o,v)       rb_iv_set((o),"@tag_class",(v)) | ||
| #define ossl_asn1_set_infinite_length(o,v) rb_iv_set((o),"@infinite_length",(v)) | ||
| VALUE mASN1; | ||
| VALUE eASN1Error; | ||
| ... | ... | |
| VALUE cASN1Primitive; | ||
| VALUE cASN1Constructive; | ||
| VALUE cASN1EndOfContent; | ||
| VALUE cASN1Boolean;                           /* BOOLEAN           */ | ||
| VALUE cASN1Integer, cASN1Enumerated;          /* INTEGER           */ | ||
| VALUE cASN1BitString;                         /* BIT STRING        */ | ||
| ... | ... | |
| } ossl_asn1_info_t; | ||
| static ossl_asn1_info_t ossl_asn1_info[] = { | ||
|     { "EOC",               NULL,                  },  /*  0 */ | ||
|     { "EOC",               &cASN1EndOfContent,    },  /*  0 */ | ||
|     { "BOOLEAN",           &cASN1Boolean,         },  /*  1 */ | ||
|     { "INTEGER",           &cASN1Integer,         },  /*  2 */ | ||
|     { "BIT_STRING",        &cASN1BitString,       },  /*  3 */ | ||
| ... | ... | |
|     ossl_asn1_set_tag(self, tag); | ||
|     ossl_asn1_set_value(self, value); | ||
|     ossl_asn1_set_tag_class(self, tag_class); | ||
|     ossl_asn1_set_infinite_length(self, Qfalse); | ||
|     return self; | ||
| } | ||
| ... | ... | |
| static VALUE | ||
| ossl_asn1data_to_der(VALUE self) | ||
| { | ||
|     VALUE value, der; | ||
|     int tag, tag_class, is_cons = 0; | ||
|     VALUE value, der, inf_length; | ||
|     int tag, tag_class, is_cons = 0, tmp_cons = 1; | ||
|     long length; | ||
|     unsigned char *p; | ||
| ... | ... | |
|     tag = ossl_asn1_tag(self); | ||
|     tag_class = ossl_asn1_tag_class(self); | ||
|     if((length = ASN1_object_size(1, RSTRING_LEN(value), tag)) <= 0) | ||
|     inf_length = ossl_asn1_get_infinite_length(self); | ||
|     if (inf_length == Qtrue) { | ||
|         is_cons = 2; | ||
|         tmp_cons = 2; | ||
|     } | ||
|     if((length = ASN1_object_size(tmp_cons, RSTRING_LEN(value), tag)) <= 0) | ||
| 	ossl_raise(eASN1Error, NULL); | ||
|     der = rb_str_new(0, length); | ||
|     p = (unsigned char *)RSTRING_PTR(der); | ||
| ... | ... | |
|     unsigned char *start, *p; | ||
|     const unsigned char *p0; | ||
|     long len, off = *offset; | ||
|     int hlen, tag, tc, j; | ||
|     int hlen, tag, tc, j, infinite = 0; | ||
|     VALUE ary, asn1data, value, tag_class; | ||
|     ary = rb_ary_new(); | ||
| ... | ... | |
| 	else | ||
| 	    tag_class = sUNIVERSAL; | ||
| 	if(j & V_ASN1_CONSTRUCTED){ | ||
| 	    /* TODO: if j == 0x21 it is indefinite length object. */ | ||
| 	    if((j == 0x21) && (len == 0)){ | ||
|                 infinite = 1; | ||
| 		long lastoff = off; | ||
| 		value = ossl_asn1_decode0(&p, length, &off, depth+1, 0, yield); | ||
| 		len = off - lastoff; | ||
| ... | ... | |
| 		    break; | ||
| 		} | ||
| 	    } | ||
| 	    asn1data = rb_funcall(klass, rb_intern("new"), 1, value); | ||
|             if (infinite && !(tag == V_ASN1_SEQUENCE || tag == V_ASN1_SET)){ | ||
|                 asn1data = rb_funcall(cASN1Constructive, | ||
|                                       rb_intern("new"), | ||
|                                       4, | ||
|                                       value, | ||
|                                       INT2NUM(tag), | ||
|                                       Qnil, | ||
|                                       ID2SYM(tag_class)); | ||
|             } | ||
|             else{ | ||
| 	        asn1data = rb_funcall(klass, rb_intern("new"), 1, value); | ||
|             } | ||
| 	    if(tag == V_ASN1_BIT_STRING){ | ||
| 		rb_iv_set(asn1data, "@unused_bits", LONG2NUM(flag)); | ||
| 	    } | ||
| ... | ... | |
| 	    asn1data = rb_funcall(cASN1Data, rb_intern("new"), 3, | ||
| 				  value, INT2NUM(tag), ID2SYM(tag_class)); | ||
| 	} | ||
|         if (infinite) | ||
|             ossl_asn1_set_infinite_length(asn1data, Qtrue); | ||
|         else | ||
|             ossl_asn1_set_infinite_length(asn1data, Qfalse); | ||
|  | ||
| 	rb_ary_push(ary, asn1data); | ||
| 	length -= len; | ||
|         if(once) break; | ||
| ... | ... | |
|     ossl_asn1_set_value(self, value); | ||
|     ossl_asn1_set_tagging(self, tagging); | ||
|     ossl_asn1_set_tag_class(self, tag_class); | ||
|     ossl_asn1_set_infinite_length(self, Qfalse); | ||
|     return self; | ||
| } | ||
| static VALUE | ||
| ossl_asn1eoc_initialize(int argc, VALUE *argv, VALUE self) { | ||
|     VALUE tag, tagging, tag_class, value; | ||
|     tag = INT2NUM(ossl_asn1_default_tag(self)); | ||
|     tagging = Qnil; | ||
|     tag_class = ID2SYM(sUNIVERSAL); | ||
|     value = rb_str_new("", 0); | ||
|     ossl_asn1_set_tag(self, tag); | ||
|     ossl_asn1_set_value(self, value); | ||
|     ossl_asn1_set_tagging(self, tagging); | ||
|     ossl_asn1_set_tag_class(self, tag_class); | ||
|     ossl_asn1_set_infinite_length(self, Qfalse); | ||
|     return self; | ||
| } | ||
| static int | ||
| ossl_i2d_ASN1_TYPE(ASN1_TYPE *a, unsigned char **pp) | ||
| { | ||
| ... | ... | |
| static VALUE | ||
| ossl_asn1cons_to_der(VALUE self) | ||
| { | ||
|     int tag, tn, tc, explicit; | ||
|     int tag, tn, tc, explicit, constructed = 1; | ||
|     int found_prim = 0; | ||
|     long seq_len, length; | ||
|     unsigned char *p; | ||
|     VALUE value, str; | ||
|     VALUE value, str, inf_length, ary, example; | ||
|     tag = ossl_asn1_default_tag(self); | ||
|     tn = NUM2INT(ossl_asn1_get_tag(self)); | ||
|     tc = ossl_asn1_tag_class(self); | ||
|     inf_length = ossl_asn1_get_infinite_length(self); | ||
|     if (inf_length == Qtrue) { | ||
|         constructed = 2; | ||
|         if (CLASS_OF(self) == cASN1Sequence || | ||
|             CLASS_OF(self) == cASN1Set) { | ||
|             tag = ossl_asn1_default_tag(self); | ||
|         } | ||
|         else { /*BIT_STRING OR OCTET_STRING*/ | ||
|             ary = ossl_asn1_get_value(self); | ||
|             /* Recursively descend until a primitive value is found. | ||
|                The overall value of the entire constructed encoding | ||
|                is of the type of the first primitive encoding to be | ||
|                found. */ | ||
|             while (!found_prim){ | ||
|                 example = rb_ary_entry(ary, 0); | ||
|                 if (rb_obj_is_kind_of(example, cASN1Primitive)){ | ||
|                     found_prim = 1; | ||
|                 } | ||
|                 else { | ||
|                     /* example is another ASN1Constructive */ | ||
|                     if (!rb_obj_is_kind_of(example, cASN1Constructive)){ | ||
|                         ossl_raise(eASN1Error, "invalid constructed encoding"); | ||
|                         return Qnil; /* dummy */ | ||
|                     } | ||
|                     ary = ossl_asn1_get_value(example); | ||
|                 } | ||
|             } | ||
|             tag = ossl_asn1_default_tag(example); | ||
|         } | ||
|     } | ||
|     else { | ||
|         tag = ossl_asn1_default_tag(self); | ||
|     } | ||
|     explicit = ossl_asn1_is_explicit(self); | ||
|     value = join_der(ossl_asn1_get_value(self)); | ||
|     seq_len = ASN1_object_size(1, RSTRING_LEN(value), tag); | ||
|     length = ASN1_object_size(1, seq_len, tn); | ||
|     seq_len = ASN1_object_size(constructed, RSTRING_LEN(value), tag); | ||
|     length = ASN1_object_size(constructed, seq_len, tn); | ||
|     str = rb_str_new(0, length); | ||
|     p = (unsigned char *)RSTRING_PTR(str); | ||
|     if(tc == V_ASN1_UNIVERSAL) | ||
| 	ASN1_put_object(&p, 1, RSTRING_LEN(value), tn, tc); | ||
|     	ASN1_put_object(&p, constructed, RSTRING_LEN(value), tn, tc); | ||
|     else{ | ||
| 	if(explicit){ | ||
| 	    ASN1_put_object(&p, 1, seq_len, tn, tc); | ||
| 	    ASN1_put_object(&p, 1, RSTRING_LEN(value), tag, V_ASN1_UNIVERSAL); | ||
|     	    ASN1_put_object(&p, constructed, seq_len, tn, tc); | ||
| 	    ASN1_put_object(&p, constructed, RSTRING_LEN(value), tag, V_ASN1_UNIVERSAL); | ||
| 	} | ||
| 	else ASN1_put_object(&p, 1, RSTRING_LEN(value), tn, tc); | ||
|         else{ | ||
|             ASN1_put_object(&p, constructed, RSTRING_LEN(value), tn, tc); | ||
|         } | ||
|     } | ||
|     memcpy(p, RSTRING_PTR(value), RSTRING_LEN(value)); | ||
|     p += RSTRING_LEN(value); | ||
| ... | ... | |
| OSSL_ASN1_IMPL_FACTORY_METHOD(GeneralizedTime) | ||
| OSSL_ASN1_IMPL_FACTORY_METHOD(Sequence) | ||
| OSSL_ASN1_IMPL_FACTORY_METHOD(Set) | ||
| OSSL_ASN1_IMPL_FACTORY_METHOD(EndOfContent) | ||
| void | ||
| Init_ossl_asn1() | ||
| ... | ... | |
|     rb_attr(cASN1Data, rb_intern("value"), 1, 1, 0); | ||
|     rb_attr(cASN1Data, rb_intern("tag"), 1, 1, 0); | ||
|     rb_attr(cASN1Data, rb_intern("tag_class"), 1, 1, 0); | ||
|     rb_attr(cASN1Data, rb_intern("infinite_length"), 1, 1, 0); | ||
|     rb_define_method(cASN1Data, "initialize", ossl_asn1data_initialize, 3); | ||
|     rb_define_method(cASN1Data, "to_der", ossl_asn1data_to_der, 0); | ||
|     cASN1Primitive = rb_define_class_under(mASN1, "Primitive", cASN1Data); | ||
|     rb_attr(cASN1Primitive, rb_intern("tagging"), 1, 1, Qtrue); | ||
|     rb_undef_method(cASN1Primitive, "infinite_length="); | ||
|     rb_define_method(cASN1Primitive, "initialize", ossl_asn1_initialize, -1); | ||
|     rb_define_method(cASN1Primitive, "to_der", ossl_asn1prim_to_der, 0); | ||
| ... | ... | |
|     OSSL_ASN1_DEFINE_CLASS(Sequence, Constructive); | ||
|     OSSL_ASN1_DEFINE_CLASS(Set, Constructive); | ||
|     OSSL_ASN1_DEFINE_CLASS(EndOfContent, Data); | ||
|     rb_define_singleton_method(cASN1ObjectId, "register", ossl_asn1obj_s_register, 3); | ||
|     rb_define_method(cASN1ObjectId, "sn", ossl_asn1obj_get_sn, 0); | ||
|     rb_define_method(cASN1ObjectId, "ln", ossl_asn1obj_get_ln, 0); | ||
| ... | ... | |
|     rb_define_alias(cASN1ObjectId, "short_name", "sn"); | ||
|     rb_define_alias(cASN1ObjectId, "long_name", "ln"); | ||
|     rb_attr(cASN1BitString, rb_intern("unused_bits"), 1, 1, 0); | ||
|     rb_define_method(cASN1EndOfContent, "initialize", ossl_asn1eoc_initialize, -1); | ||
| } | ||
| ruby/test/openssl/test_asn1.rb (working copy) | ||
|---|---|---|
|       assert_equal(v, OpenSSL::ASN1.decode(type.new(v).to_der).value) | ||
|     end | ||
|   end | ||
|   def test_primitive_cannot_set_infinite_length | ||
|     begin | ||
|       prim = OpenSSL::ASN1::Integer.new(50) | ||
|       assert_equal(false, prim.infinite_length) | ||
|       prim.infinite_length = true | ||
|       flunk('Could set infinite length on primitive value') | ||
|     rescue NoMethodError => e | ||
|       #ok | ||
|     end | ||
|   end | ||
|   def test_seq_infinite_length | ||
|     begin | ||
|       content = [ OpenSSL::ASN1::Null.new(nil), | ||
|                   OpenSSL::ASN1::EndOfContent.new() ] | ||
|       cons = OpenSSL::ASN1::Sequence.new(content) | ||
|       cons.infinite_length = true | ||
|       expected = %w{ 30 80 05 00 00 00 } | ||
|       raw = [expected.join('')].pack('H*') | ||
|       assert_equal(raw, cons.to_der) | ||
|       assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) | ||
|     end | ||
|   end | ||
|   def test_set_infinite_length | ||
|     begin | ||
|       content = [ OpenSSL::ASN1::Null.new(nil), | ||
|                   OpenSSL::ASN1::EndOfContent.new() ] | ||
|       cons = OpenSSL::ASN1::Set.new(content) | ||
|       cons.infinite_length = true | ||
|       expected = %w{ 31 80 05 00 00 00 } | ||
|       raw = [expected.join('')].pack('H*') | ||
|       assert_equal(raw, cons.to_der) | ||
|       assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) | ||
|     end | ||
|   end | ||
|   def test_octet_string_infinite_length | ||
|     begin | ||
|       octets = [ OpenSSL::ASN1::OctetString.new('aaa'), | ||
|                  OpenSSL::ASN1::EndOfContent.new() ] | ||
|       cons = OpenSSL::ASN1::Constructive.new( | ||
|         octets, | ||
|         OpenSSL::ASN1::OCTET_STRING, | ||
|         nil, | ||
|         :UNIVERSAL) | ||
|       cons.infinite_length = true | ||
|       expected = %w{ 24 80 04 03 61 61 61 00 00 } | ||
|       raw = [expected.join('')].pack('H*') | ||
|       assert_equal(raw, cons.to_der) | ||
|       assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) | ||
|     end | ||
|   end | ||
|   def test_prim_explicit_tagging | ||
|     begin | ||
|       oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT) | ||
|       expected = %w{ A0 03 04 01 61 } | ||
|       raw = [expected.join('')].pack('H*') | ||
|       assert_equal(raw, oct_str.to_der) | ||
|       assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) | ||
|     end | ||
|   end | ||
|   def test_prim_explicit_tagging_tag_class | ||
|     begin | ||
|       oct_str = OpenSSL::ASN1::OctetString.new("a", 0, :EXPLICIT) | ||
|       oct_str2 = OpenSSL::ASN1::OctetString.new( | ||
|         "a", | ||
|         0, | ||
|         :EXPLICIT, | ||
|         :CONTEXT_SPECIFIC) | ||
|       assert_equal(oct_str.to_der, oct_str2.to_der) | ||
|     end | ||
|   end | ||
|   def test_prim_implicit_tagging | ||
|     begin | ||
|       int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT) | ||
|       expected = %w{ 80 01 01 } | ||
|       raw = [expected.join('')].pack('H*') | ||
|       assert_equal(raw, int.to_der) | ||
|       assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) | ||
|     end | ||
|   end | ||
|   def test_prim_implicit_tagging_tag_class | ||
|     begin | ||
|       int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT) | ||
|       int2 = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT, :CONTEXT_SPECIFIC); | ||
|       assert_equal(int.to_der, int2.to_der) | ||
|     end | ||
|   end | ||
|   def test_cons_explicit_tagging | ||
|     begin | ||
|       content = [ OpenSSL::ASN1::PrintableString.new('abc') ] | ||
|       seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT) | ||
|       expected = %w{ A2 07 30 05 13 03 61 62 63 } | ||
|       raw = [expected.join('')].pack('H*') | ||
|       assert_equal(raw, seq.to_der) | ||
|       assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) | ||
|     end | ||
|   end | ||
|   def test_cons_explicit_tagging_inf_length | ||
|     begin | ||
|       content = [ OpenSSL::ASN1::PrintableString.new('abc') , | ||
|                   OpenSSL::ASN1::EndOfContent.new() ] | ||
|       seq = OpenSSL::ASN1::Sequence.new(content, 2, :EXPLICIT) | ||
|       seq.infinite_length = true | ||
|       expected = %w{ A2 80 30 80 13 03 61 62 63 00 00 } | ||
|       raw = [expected.join('')].pack('H*') | ||
|       assert_equal(raw, seq.to_der) | ||
|       assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) | ||
|     end | ||
|   end | ||
|   def test_cons_implicit_tagging | ||
|     begin | ||
|       content = [ OpenSSL::ASN1::Null.new(nil) ] | ||
|       seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT) | ||
|       expected = %w{ A1 02 05 00 } | ||
|       raw = [expected.join('')].pack('H*') | ||
|       assert_equal(raw, seq.to_der) | ||
|       assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) | ||
|     end | ||
|   end | ||
|   def test_cons_implicit_tagging_inf_length | ||
|     begin | ||
|       content = [ OpenSSL::ASN1::Null.new(nil), | ||
|                   OpenSSL::ASN1::EndOfContent.new() ] | ||
|       seq = OpenSSL::ASN1::Sequence.new(content, 1, :IMPLICIT) | ||
|       seq.infinite_length = true | ||
|       expected = %w{ A1 80 05 00 00 00 } | ||
|       raw = [expected.join('')].pack('H*') | ||
|       assert_equal(raw, seq.to_der) | ||
|       assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) | ||
|     end | ||
|   end | ||
|   def test_octet_string_infinite_length_explicit_tagging | ||
|     begin | ||
|       octets = [ OpenSSL::ASN1::OctetString.new('aaa'), | ||
|                  OpenSSL::ASN1::EndOfContent.new() ] | ||
|       cons = OpenSSL::ASN1::Constructive.new( | ||
|         octets, | ||
|         1, | ||
|         :EXPLICIT) | ||
|       cons.infinite_length = true | ||
|       expected = %w{ A1 80 24 80 04 03 61 61 61 00 00 } | ||
|       raw = [expected.join('')].pack('H*') | ||
|       assert_equal(raw, cons.to_der) | ||
|       assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) | ||
|     end | ||
|   end | ||
|   def test_octet_string_infinite_length_implicit_tagging | ||
|     begin | ||
|       octets = [ OpenSSL::ASN1::OctetString.new('aaa'), | ||
|                  OpenSSL::ASN1::EndOfContent.new() ] | ||
|       cons = OpenSSL::ASN1::Constructive.new( | ||
|         octets, | ||
|         0, | ||
|         :IMPLICIT) | ||
|       cons.infinite_length = true | ||
|       expected = %w{ A0 80 04 03 61 61 61 00 00 } | ||
|       raw = [expected.join('')].pack('H*') | ||
|       assert_equal(raw, cons.to_der) | ||
|       assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) | ||
|     end | ||
|   end | ||
|   def test_recursive_octet_string_infinite_length | ||
|     begin | ||
|       octets_sub1 = [ OpenSSL::ASN1::OctetString.new("\x01"), | ||
|                       OpenSSL::ASN1::EndOfContent.new() ] | ||
|       octets_sub2 = [ OpenSSL::ASN1::OctetString.new("\x02"), | ||
|                       OpenSSL::ASN1::EndOfContent.new() ] | ||
|       container1 = OpenSSL::ASN1::Constructive.new( | ||
|         octets_sub1, | ||
|         OpenSSL::ASN1::OCTET_STRING, | ||
|         nil, | ||
|         :UNIVERSAL) | ||
|       container1.infinite_length = true | ||
|       container2 = OpenSSL::ASN1::Constructive.new( | ||
|         octets_sub2, | ||
|         OpenSSL::ASN1::OCTET_STRING, | ||
|         nil, | ||
|         :UNIVERSAL) | ||
|       container2.infinite_length = true | ||
|       octets3 = OpenSSL::ASN1::OctetString.new("\x03") | ||
|       octets = [ container1, container2, octets3, | ||
|                  OpenSSL::ASN1::EndOfContent.new() ] | ||
|       cons = OpenSSL::ASN1::Constructive.new( | ||
|         octets, | ||
|         OpenSSL::ASN1::OCTET_STRING, | ||
|         nil, | ||
|         :UNIVERSAL) | ||
|       cons.infinite_length = true | ||
|       expected = %w{ 24 80 24 80 04 01 01 00 00 24 80 04 01 02 00 00 04 01 03 00 00 } | ||
|       raw = [expected.join('')].pack('H*') | ||
|       assert_equal(raw, cons.to_der) | ||
|       assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) | ||
|     end | ||
|   end | ||
|   def test_bit_string_infinite_length | ||
|     begin | ||
|       content = [ OpenSSL::ASN1::BitString.new("\x01"), | ||
|                   OpenSSL::ASN1::EndOfContent.new() ] | ||
|       cons = OpenSSL::ASN1::Constructive.new( | ||
|         content, | ||
|         OpenSSL::ASN1::BIT_STRING, | ||
|         nil, | ||
|         :UNIVERSAL) | ||
|       cons.infinite_length = true | ||
|       expected = %w{ 23 80 03 02 00 01 00 00 } | ||
|       raw = [expected.join('')].pack('H*') | ||
|       assert_equal(raw, cons.to_der) | ||
|       assert_equal(raw, OpenSSL::ASN1.decode(raw).to_der) | ||
|     end | ||
|   end | ||
|  | ||
| end if defined?(OpenSSL) | ||