Init_ossl_asn1(void)
{
    VALUE ary;
    int i;

#if 0
    mOSSL = rb_define_module("OpenSSL");
    eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
#endif

    sym_UNIVERSAL = ID2SYM(rb_intern_const("UNIVERSAL"));
    sym_CONTEXT_SPECIFIC = ID2SYM(rb_intern_const("CONTEXT_SPECIFIC"));
    sym_APPLICATION = ID2SYM(rb_intern_const("APPLICATION"));
    sym_PRIVATE = ID2SYM(rb_intern_const("PRIVATE"));
    sym_EXPLICIT = ID2SYM(rb_intern_const("EXPLICIT"));
    sym_IMPLICIT = ID2SYM(rb_intern_const("IMPLICIT"));

    sivVALUE = rb_intern("@value");
    sivTAG = rb_intern("@tag");
    sivTAGGING = rb_intern("@tagging");
    sivTAG_CLASS = rb_intern("@tag_class");
    sivINFINITE_LENGTH = rb_intern("@infinite_length");
    sivUNUSED_BITS = rb_intern("@unused_bits");

    /*
     * Document-module: OpenSSL::ASN1
     *
     * Abstract Syntax Notation One (or ASN.1) is a notation syntax to
     * describe data structures and is defined in ITU-T X.680. ASN.1 itself
     * does not mandate any encoding or parsing rules, but usually ASN.1 data
     * structures are encoded using the Distinguished Encoding Rules (DER) or
     * less often the Basic Encoding Rules (BER) described in ITU-T X.690. DER
     * and BER encodings are binary Tag-Length-Value (TLV) encodings that are
     * quite concise compared to other popular data description formats such
     * as XML, JSON etc.
     * ASN.1 data structures are very common in cryptographic applications,
     * e.g. X.509 public key certificates or certificate revocation lists
     * (CRLs) are all defined in ASN.1 and DER-encoded. ASN.1, DER and BER are
     * the building blocks of applied cryptography.
     * The ASN1 module provides the necessary classes that allow generation
     * of ASN.1 data structures and the methods to encode them using a DER
     * encoding. The decode method allows parsing arbitrary BER-/DER-encoded
     * data to a Ruby object that can then be modified and re-encoded at will.
     *
     * == ASN.1 class hierarchy
     *
     * The base class representing ASN.1 structures is ASN1Data. ASN1Data offers
     * attributes to read and set the +tag+, the +tag_class+ and finally the
     * +value+ of a particular ASN.1 item. Upon parsing, any tagged values
     * (implicit or explicit) will be represented by ASN1Data instances because
     * their "real type" can only be determined using out-of-band information
     * from the ASN.1 type declaration. Since this information is normally
     * known when encoding a type, all sub-classes of ASN1Data offer an
     * additional attribute +tagging+ that allows to encode a value implicitly
     * (+:IMPLICIT+) or explicitly (+:EXPLICIT+).
     *
     * === Constructive
     *
     * Constructive is, as its name implies, the base class for all
     * constructed encodings, i.e. those that consist of several values,
     * opposed to "primitive" encodings with just one single value.
     * Primitive values that are encoded with "infinite length" are typically
     * constructed (their values come in multiple chunks) and are therefore
     * represented by instances of Constructive. The value of an Constructive
     * is always an Array.
     *
     * ==== ASN1::Set and ASN1::Sequence
     *
     * The most common constructive encodings are SETs and SEQUENCEs, which is
     * why there are two sub-classes of Constructive representing each of
     * them.
     *
     * === Primitive
     *
     * This is the super class of all primitive values. Primitive
     * itself is not used when parsing ASN.1 data, all values are either
     * instances of a corresponding sub-class of Primitive or they are
     * instances of ASN1Data if the value was tagged implicitly or explicitly.
     * Please cf. Primitive documentation for details on sub-classes and
     * their respective mappings of ASN.1 data types to Ruby objects.
     *
     * == Possible values for +tagging+
     *
     * When constructing an ASN1Data object the ASN.1 type definition may
     * require certain elements to be either implicitly or explicitly tagged.
     * This can be achieved by setting the +tagging+ attribute manually for
     * sub-classes of ASN1Data. Use the symbol +:IMPLICIT+ for implicit
     * tagging and +:EXPLICIT+ if the element requires explicit tagging.
     *
     * == Possible values for +tag_class+
     *
     * It is possible to create arbitrary ASN1Data objects that also support
     * a PRIVATE or APPLICATION tag class. Possible values for the +tag_class+
     * attribute are:
     * * +:UNIVERSAL+ (the default for untagged values)
     * * +:CONTEXT_SPECIFIC+ (the default for tagged values)
     * * +:APPLICATION+
     * * +:PRIVATE+
     *
     * == Tag constants
     *
     * There is a constant defined for each universal tag:
     * * OpenSSL::ASN1::EOC (0)
     * * OpenSSL::ASN1::BOOLEAN (1)
     * * OpenSSL::ASN1::INTEGER (2)
     * * OpenSSL::ASN1::BIT_STRING (3)
     * * OpenSSL::ASN1::OCTET_STRING (4)
     * * OpenSSL::ASN1::NULL (5)
     * * OpenSSL::ASN1::OBJECT (6)
     * * OpenSSL::ASN1::ENUMERATED (10)
     * * OpenSSL::ASN1::UTF8STRING (12)
     * * OpenSSL::ASN1::SEQUENCE (16)
     * * OpenSSL::ASN1::SET (17)
     * * OpenSSL::ASN1::NUMERICSTRING (18)
     * * OpenSSL::ASN1::PRINTABLESTRING (19)
     * * OpenSSL::ASN1::T61STRING (20)
     * * OpenSSL::ASN1::VIDEOTEXSTRING (21)
     * * OpenSSL::ASN1::IA5STRING (22)
     * * OpenSSL::ASN1::UTCTIME (23)
     * * OpenSSL::ASN1::GENERALIZEDTIME (24)
     * * OpenSSL::ASN1::GRAPHICSTRING (25)
     * * OpenSSL::ASN1::ISO64STRING (26)
     * * OpenSSL::ASN1::GENERALSTRING (27)
     * * OpenSSL::ASN1::UNIVERSALSTRING (28)
     * * OpenSSL::ASN1::BMPSTRING (30)
     *
     * == UNIVERSAL_TAG_NAME constant
     *
     * An Array that stores the name of a given tag number. These names are
     * the same as the name of the tag constant that is additionally defined,
     * e.g. UNIVERSAL_TAG_NAME[2] = "INTEGER" and OpenSSL::ASN1::INTEGER = 2.
     *
     * == Example usage
     *
     * === Decoding and viewing a DER-encoded file
     *   require 'openssl'
     *   require 'pp'
     *   der = File.binread('data.der')
     *   asn1 = OpenSSL::ASN1.decode(der)
     *   pp der
     *
     * === Creating an ASN.1 structure and DER-encoding it
     *   require 'openssl'
     *   version = OpenSSL::ASN1::Integer.new(1)
     *   # Explicitly 0-tagged implies context-specific tag class
     *   serial = OpenSSL::ASN1::Integer.new(12345, 0, :EXPLICIT, :CONTEXT_SPECIFIC)
     *   name = OpenSSL::ASN1::PrintableString.new('Data 1')
     *   sequence = OpenSSL::ASN1::Sequence.new( [ version, serial, name ] )
     *   der = sequence.to_der
     */
    mASN1 = rb_define_module_under(mOSSL, "ASN1");

    /* Document-class: OpenSSL::ASN1::ASN1Error
     *
     * Generic error class for all errors raised in ASN1 and any of the
     * classes defined in it.
     */
    eASN1Error = rb_define_class_under(mASN1, "ASN1Error", eOSSLError);
    rb_define_module_function(mASN1, "traverse", ossl_asn1_traverse, 1);
    rb_define_module_function(mASN1, "decode", ossl_asn1_decode, 1);
    rb_define_module_function(mASN1, "decode_all", ossl_asn1_decode_all, 1);
    ary = rb_ary_new();

    /*
     * Array storing tag names at the tag's index.
     */
    rb_define_const(mASN1, "UNIVERSAL_TAG_NAME", ary);
    for(i = 0; i < ossl_asn1_info_size; i++){
	if(ossl_asn1_info[i].name[0] == '[') continue;
	rb_define_const(mASN1, ossl_asn1_info[i].name, INT2NUM(i));
	rb_ary_store(ary, i, rb_str_new2(ossl_asn1_info[i].name));
    }

    /* Document-class: OpenSSL::ASN1::ASN1Data
     *
     * The top-level class representing any ASN.1 object. When parsed by
     * ASN1.decode, tagged values are always represented by an instance
     * of ASN1Data.
     *
     * == The role of ASN1Data for parsing tagged values
     *
     * When encoding an ASN.1 type it is inherently clear what original
     * type (e.g. INTEGER, OCTET STRING etc.) this value has, regardless
     * of its tagging.
     * But opposed to the time an ASN.1 type is to be encoded, when parsing
     * them it is not possible to deduce the "real type" of tagged
     * values. This is why tagged values are generally parsed into ASN1Data
     * instances, but with a different outcome for implicit and explicit
     * tagging.
     *
     * === Example of a parsed implicitly tagged value
     *
     * An implicitly 1-tagged INTEGER value will be parsed as an
     * ASN1Data with
     * * +tag+ equal to 1
     * * +tag_class+ equal to +:CONTEXT_SPECIFIC+
     * * +value+ equal to a +String+ that carries the raw encoding
     *   of the INTEGER.
     * This implies that a subsequent decoding step is required to
     * completely decode implicitly tagged values.
     *
     * === Example of a parsed explicitly tagged value
     *
     * An explicitly 1-tagged INTEGER value will be parsed as an
     * ASN1Data with
     * * +tag+ equal to 1
     * * +tag_class+ equal to +:CONTEXT_SPECIFIC+
     * * +value+ equal to an +Array+ with one single element, an
     *   instance of OpenSSL::ASN1::Integer, i.e. the inner element
     *   is the non-tagged primitive value, and the tagging is represented
     *   in the outer ASN1Data
     *
     * == Example - Decoding an implicitly tagged INTEGER
     *   int = OpenSSL::ASN1::Integer.new(1, 0, :IMPLICIT) # implicit 0-tagged
     *   seq = OpenSSL::ASN1::Sequence.new( [int] )
     *   der = seq.to_der
     *   asn1 = OpenSSL::ASN1.decode(der)
     *   # pp asn1 => #<OpenSSL::ASN1::Sequence:0x87326e0
     *   #              @infinite_length=false,
     *   #              @tag=16,
     *   #              @tag_class=:UNIVERSAL,
     *   #              @tagging=nil,
     *   #              @value=
     *   #                [#<OpenSSL::ASN1::ASN1Data:0x87326f4
     *   #                   @infinite_length=false,
     *   #                   @tag=0,
     *   #                   @tag_class=:CONTEXT_SPECIFIC,
     *   #                   @value="\x01">]>
     *   raw_int = asn1.value[0]
     *   # manually rewrite tag and tag class to make it an UNIVERSAL value
     *   raw_int.tag = OpenSSL::ASN1::INTEGER
     *   raw_int.tag_class = :UNIVERSAL
     *   int2 = OpenSSL::ASN1.decode(raw_int)
     *   puts int2.value # => 1
     *
     * == Example - Decoding an explicitly tagged INTEGER
     *   int = OpenSSL::ASN1::Integer.new(1, 0, :EXPLICIT) # explicit 0-tagged
     *   seq = OpenSSL::ASN1::Sequence.new( [int] )
     *   der = seq.to_der
     *   asn1 = OpenSSL::ASN1.decode(der)
     *   # pp asn1 => #<OpenSSL::ASN1::Sequence:0x87326e0
     *   #              @infinite_length=false,
     *   #              @tag=16,
     *   #              @tag_class=:UNIVERSAL,
     *   #              @tagging=nil,
     *   #              @value=
     *   #                [#<OpenSSL::ASN1::ASN1Data:0x87326f4
     *   #                   @infinite_length=false,
     *   #                   @tag=0,
     *   #                   @tag_class=:CONTEXT_SPECIFIC,
     *   #                   @value=
     *   #                     [#<OpenSSL::ASN1::Integer:0x85bf308
     *   #                        @infinite_length=false,
     *   #                        @tag=2,
     *   #                        @tag_class=:UNIVERSAL
     *   #                        @tagging=nil,
     *   #                        @value=1>]>]>
     *   int2 = asn1.value[0].value[0]
     *   puts int2.value # => 1
     */
    cASN1Data = rb_define_class_under(mASN1, "ASN1Data", rb_cObject);
    /*
     * Carries the value of a ASN.1 type.
     * Please confer Constructive and Primitive for the mappings between
     * ASN.1 data types and Ruby classes.
     */
    rb_attr(cASN1Data, rb_intern("value"), 1, 1, 0);
    /*
     * A +Number+ representing the tag number of this ASN1Data. Never +nil+.
     */
    rb_attr(cASN1Data, rb_intern("tag"), 1, 1, 0);
    /*
     * A +Symbol+ representing the tag class of this ASN1Data. Never +nil+.
     * See ASN1Data for possible values.
     */
    rb_attr(cASN1Data, rb_intern("tag_class"), 1, 1, 0);
    /*
     * Never +nil+. A +Boolean+ indicating whether the encoding was infinite
     * length (in the case of parsing) or whether an infinite length encoding
     * shall be used (in the encoding case).
     * In DER, every value has a finite length associated with it. But in
     * scenarios where large amounts of data need to be transferred it
     * might be desirable to have some kind of streaming support available.
     * For example, huge OCTET STRINGs are preferably sent in smaller-sized
     * chunks, each at a time.
     * This is possible in BER by setting the length bytes of an encoding
     * to zero and by this indicating that the following value will be
     * sent in chunks. Infinite length encodings are always constructed.
     * The end of such a stream of chunks is indicated by sending a EOC
     * (End of Content) tag. SETs and SEQUENCEs may use an infinite length
     * encoding, but also primitive types such as e.g. OCTET STRINGS or
     * BIT STRINGS may leverage this functionality (cf. ITU-T X.690).
     */
    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);

    /* Document-class: OpenSSL::ASN1::Primitive
     *
     * The parent class for all primitive encodings. Attributes are the same as
     * for ASN1Data, with the addition of +tagging+.
     * Primitive values can never be infinite length encodings, thus it is not
     * possible to set the +infinite_length+ attribute for Primitive and its
     * sub-classes.
     *
     * == Primitive sub-classes and their mapping to Ruby classes
     * * OpenSSL::ASN1::EndOfContent    <=> +value+ is always +nil+
     * * OpenSSL::ASN1::Boolean         <=> +value+ is a +Boolean+
     * * OpenSSL::ASN1::Integer         <=> +value+ is a +Number+
     * * OpenSSL::ASN1::BitString       <=> +value+ is a +String+
     * * OpenSSL::ASN1::OctetString     <=> +value+ is a +String+
     * * OpenSSL::ASN1::Null            <=> +value+ is always +nil+
     * * OpenSSL::ASN1::Object          <=> +value+ is a +String+
     * * OpenSSL::ASN1::Enumerated      <=> +value+ is a +Number+
     * * OpenSSL::ASN1::UTF8String      <=> +value+ is a +String+
     * * OpenSSL::ASN1::NumericString   <=> +value+ is a +String+
     * * OpenSSL::ASN1::PrintableString <=> +value+ is a +String+
     * * OpenSSL::ASN1::T61String       <=> +value+ is a +String+
     * * OpenSSL::ASN1::VideotexString  <=> +value+ is a +String+
     * * OpenSSL::ASN1::IA5String       <=> +value+ is a +String+
     * * OpenSSL::ASN1::UTCTime         <=> +value+ is a +Time+
     * * OpenSSL::ASN1::GeneralizedTime <=> +value+ is a +Time+
     * * OpenSSL::ASN1::GraphicString   <=> +value+ is a +String+
     * * OpenSSL::ASN1::ISO64String     <=> +value+ is a +String+
     * * OpenSSL::ASN1::GeneralString   <=> +value+ is a +String+
     * * OpenSSL::ASN1::UniversalString <=> +value+ is a +String+
     * * OpenSSL::ASN1::BMPString       <=> +value+ is a +String+
     *
     * == OpenSSL::ASN1::BitString
     *
     * === Additional attributes
     * +unused_bits+: if the underlying BIT STRING's
     * length is a multiple of 8 then +unused_bits+ is 0. Otherwise
     * +unused_bits+ indicates the number of bits that are to be ignored in
     * the final octet of the +BitString+'s +value+.
     *
     * == OpenSSL::ASN1::ObjectId
     *
     * NOTE: While OpenSSL::ASN1::ObjectId.new will allocate a new ObjectId,
     * it is not typically allocated this way, but rather that are received from
     * parsed ASN1 encodings.
     *
     * === Additional attributes
     * * +sn+: the short name as defined in <openssl/objects.h>.
     * * +ln+: the long name as defined in <openssl/objects.h>.
     * * +oid+: the object identifier as a +String+, e.g. "1.2.3.4.5"
     * * +short_name+: alias for +sn+.
     * * +long_name+: alias for +ln+.
     *
     * == Examples
     * With the Exception of OpenSSL::ASN1::EndOfContent, each Primitive class
     * constructor takes at least one parameter, the +value+.
     *
     * === Creating EndOfContent
     *   eoc = OpenSSL::ASN1::EndOfContent.new
     *
     * === Creating any other Primitive
     *   prim = <class>.new(value) # <class> being one of the sub-classes except EndOfContent
     *   prim_zero_tagged_implicit = <class>.new(value, 0, :IMPLICIT)
     *   prim_zero_tagged_explicit = <class>.new(value, 0, :EXPLICIT)
     */
    cASN1Primitive = rb_define_class_under(mASN1, "Primitive", cASN1Data);
    /*
     * May be used as a hint for encoding a value either implicitly or
     * explicitly by setting it either to +:IMPLICIT+ or to +:EXPLICIT+.
     * +tagging+ is not set when a ASN.1 structure is parsed using
     * OpenSSL::ASN1.decode.
     */
    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);

    /* Document-class: OpenSSL::ASN1::Constructive
     *
     * The parent class for all constructed encodings. The +value+ attribute
     * of a Constructive is always an +Array+. Attributes are the same as
     * for ASN1Data, with the addition of +tagging+.
     *
     * == SET and SEQUENCE
     *
     * Most constructed encodings come in the form of a SET or a SEQUENCE.
     * These encodings are represented by one of the two sub-classes of
     * Constructive:
     * * OpenSSL::ASN1::Set
     * * OpenSSL::ASN1::Sequence
     * Please note that tagged sequences and sets are still parsed as
     * instances of ASN1Data. Find further details on tagged values
     * there.
     *
     * === Example - constructing a SEQUENCE
     *   int = OpenSSL::ASN1::Integer.new(1)
     *   str = OpenSSL::ASN1::PrintableString.new('abc')
     *   sequence = OpenSSL::ASN1::Sequence.new( [ int, str ] )
     *
     * === Example - constructing a SET
     *   int = OpenSSL::ASN1::Integer.new(1)
     *   str = OpenSSL::ASN1::PrintableString.new('abc')
     *   set = OpenSSL::ASN1::Set.new( [ int, str ] )
     *
     * == Infinite length primitive values
     *
     * The only case where Constructive is used directly is for infinite
     * length encodings of primitive values. These encodings are always
     * constructed, with the contents of the +value+ +Array+ being either
     * UNIVERSAL non-infinite length partial encodings of the actual value
     * or again constructive encodings with infinite length (i.e. infinite
     * length primitive encodings may be constructed recursively with another
     * infinite length value within an already infinite length value). Each
     * partial encoding must be of the same UNIVERSAL type as the overall
     * encoding. The value of the overall encoding consists of the
     * concatenation of each partial encoding taken in sequence. The +value+
     * array of the outer infinite length value must end with a
     * OpenSSL::ASN1::EndOfContent instance.
     *
     * Please note that it is not possible to encode Constructive without
     * the +infinite_length+ attribute being set to +true+, use
     * OpenSSL::ASN1::Sequence or OpenSSL::ASN1::Set in these cases instead.
     *
     * === Example - Infinite length OCTET STRING
     *   partial1 = OpenSSL::ASN1::OctetString.new("\x01")
     *   partial2 = OpenSSL::ASN1::OctetString.new("\x02")
     *   inf_octets = OpenSSL::ASN1::Constructive.new( [ partial1,
     *                                                   partial2,
     *                                                   OpenSSL::ASN1::EndOfContent.new ],
     *                                                 OpenSSL::ASN1::OCTET_STRING,
     *                                                 nil,
     *                                                 :UNIVERSAL )
     *   # The real value of inf_octets is "\x01\x02", i.e. the concatenation
     *   # of partial1 and partial2
     *   inf_octets.infinite_length = true
     *   der = inf_octets.to_der
     *   asn1 = OpenSSL::ASN1.decode(der)
     *   puts asn1.infinite_length # => true
     */
    cASN1Constructive = rb_define_class_under(mASN1,"Constructive", cASN1Data);
    rb_include_module(cASN1Constructive, rb_mEnumerable);
    /*
     * May be used as a hint for encoding a value either implicitly or
     * explicitly by setting it either to +:IMPLICIT+ or to +:EXPLICIT+.
     * +tagging+ is not set when a ASN.1 structure is parsed using
     * OpenSSL::ASN1.decode.
     */
    rb_attr(cASN1Constructive, rb_intern("tagging"), 1, 1, Qtrue);
    rb_define_method(cASN1Constructive, "initialize", ossl_asn1_initialize, -1);
    rb_define_method(cASN1Constructive, "to_der", ossl_asn1cons_to_der, 0);
    rb_define_method(cASN1Constructive, "each", ossl_asn1cons_each, 0);

#define OSSL_ASN1_DEFINE_CLASS(name, super) \
do{\
    cASN1##name = rb_define_class_under(mASN1, #name, cASN1##super);\
    rb_define_module_function(mASN1, #name, ossl_asn1_##name, -1);\
}while(0)

    OSSL_ASN1_DEFINE_CLASS(Boolean, Primitive);
    OSSL_ASN1_DEFINE_CLASS(Integer, Primitive);
    OSSL_ASN1_DEFINE_CLASS(Enumerated, Primitive);
    OSSL_ASN1_DEFINE_CLASS(BitString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(OctetString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(UTF8String, Primitive);
    OSSL_ASN1_DEFINE_CLASS(NumericString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(PrintableString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(T61String, Primitive);
    OSSL_ASN1_DEFINE_CLASS(VideotexString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(IA5String, Primitive);
    OSSL_ASN1_DEFINE_CLASS(GraphicString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(ISO64String, Primitive);
    OSSL_ASN1_DEFINE_CLASS(GeneralString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(UniversalString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(BMPString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(Null, Primitive);
    OSSL_ASN1_DEFINE_CLASS(ObjectId, Primitive);
    OSSL_ASN1_DEFINE_CLASS(UTCTime, Primitive);
    OSSL_ASN1_DEFINE_CLASS(GeneralizedTime, Primitive);

    OSSL_ASN1_DEFINE_CLASS(Sequence, Constructive);
    OSSL_ASN1_DEFINE_CLASS(Set, Constructive);

    OSSL_ASN1_DEFINE_CLASS(EndOfContent, Data);


    /* Document-class: OpenSSL::ASN1::ObjectId
     *
     * Represents the primitive object id for OpenSSL::ASN1
     */
#if 0
    cASN1ObjectId = rb_define_class_under(mASN1, "ObjectId", cASN1Primitive);  /* let rdoc know */
#endif
    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_method(cASN1ObjectId, "oid", ossl_asn1obj_get_oid, 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, 0);

    class_tag_map = rb_hash_new();
    rb_hash_aset(class_tag_map, cASN1EndOfContent, INT2NUM(V_ASN1_EOC));
    rb_hash_aset(class_tag_map, cASN1Boolean, INT2NUM(V_ASN1_BOOLEAN));
    rb_hash_aset(class_tag_map, cASN1Integer, INT2NUM(V_ASN1_INTEGER));
    rb_hash_aset(class_tag_map, cASN1BitString, INT2NUM(V_ASN1_BIT_STRING));
    rb_hash_aset(class_tag_map, cASN1OctetString, INT2NUM(V_ASN1_OCTET_STRING));
    rb_hash_aset(class_tag_map, cASN1Null, INT2NUM(V_ASN1_NULL));
    rb_hash_aset(class_tag_map, cASN1ObjectId, INT2NUM(V_ASN1_OBJECT));
    rb_hash_aset(class_tag_map, cASN1Enumerated, INT2NUM(V_ASN1_ENUMERATED));
    rb_hash_aset(class_tag_map, cASN1UTF8String, INT2NUM(V_ASN1_UTF8STRING));
    rb_hash_aset(class_tag_map, cASN1Sequence, INT2NUM(V_ASN1_SEQUENCE));
    rb_hash_aset(class_tag_map, cASN1Set, INT2NUM(V_ASN1_SET));
    rb_hash_aset(class_tag_map, cASN1NumericString, INT2NUM(V_ASN1_NUMERICSTRING));
    rb_hash_aset(class_tag_map, cASN1PrintableString, INT2NUM(V_ASN1_PRINTABLESTRING));
    rb_hash_aset(class_tag_map, cASN1T61String, INT2NUM(V_ASN1_T61STRING));
    rb_hash_aset(class_tag_map, cASN1VideotexString, INT2NUM(V_ASN1_VIDEOTEXSTRING));
    rb_hash_aset(class_tag_map, cASN1IA5String, INT2NUM(V_ASN1_IA5STRING));
    rb_hash_aset(class_tag_map, cASN1UTCTime, INT2NUM(V_ASN1_UTCTIME));
    rb_hash_aset(class_tag_map, cASN1GeneralizedTime, INT2NUM(V_ASN1_GENERALIZEDTIME));
    rb_hash_aset(class_tag_map, cASN1GraphicString, INT2NUM(V_ASN1_GRAPHICSTRING));
    rb_hash_aset(class_tag_map, cASN1ISO64String, INT2NUM(V_ASN1_ISO64STRING));
    rb_hash_aset(class_tag_map, cASN1GeneralString, INT2NUM(V_ASN1_GENERALSTRING));
    rb_hash_aset(class_tag_map, cASN1UniversalString, INT2NUM(V_ASN1_UNIVERSALSTRING));
    rb_hash_aset(class_tag_map, cASN1BMPString, INT2NUM(V_ASN1_BMPSTRING));
    rb_global_variable(&class_tag_map);
}
Init_ossl_asn1(void)
{
    VALUE ary;
    int i;

#if 0
    mOSSL = rb_define_module("OpenSSL");
    eOSSLError = rb_define_class_under(mOSSL, "OpenSSLError", rb_eStandardError);
#endif

    sym_UNIVERSAL = ID2SYM(rb_intern_const("UNIVERSAL"));
    sym_CONTEXT_SPECIFIC = ID2SYM(rb_intern_const("CONTEXT_SPECIFIC"));
    sym_APPLICATION = ID2SYM(rb_intern_const("APPLICATION"));
    sym_PRIVATE = ID2SYM(rb_intern_const("PRIVATE"));
    sym_EXPLICIT = ID2SYM(rb_intern_const("EXPLICIT"));
    sym_IMPLICIT = ID2SYM(rb_intern_const("IMPLICIT"));

    sivVALUE = rb_intern("@value");
    sivTAG = rb_intern("@tag");
    sivTAGGING = rb_intern("@tagging");
    sivTAG_CLASS = rb_intern("@tag_class");
    sivINFINITE_LENGTH = rb_intern("@infinite_length");
    sivUNUSED_BITS = rb_intern("@unused_bits");

     
    mASN1 = rb_define_module_under(mOSSL, "ASN1");

     
    eASN1Error = rb_define_class_under(mASN1, "ASN1Error", eOSSLError);
    rb_define_module_function(mASN1, "traverse", ossl_asn1_traverse, 1);
    rb_define_module_function(mASN1, "decode", ossl_asn1_decode, 1);
    rb_define_module_function(mASN1, "decode_all", ossl_asn1_decode_all, 1);
    ary = rb_ary_new();

     
    rb_define_const(mASN1, "UNIVERSAL_TAG_NAME", ary);
    for(i = 0; i < ossl_asn1_info_size; i++){
	if(ossl_asn1_info[i].name[0] == '[') continue;
	rb_define_const(mASN1, ossl_asn1_info[i].name, INT2NUM(i));
	rb_ary_store(ary, i, rb_str_new2(ossl_asn1_info[i].name));
    }

     
    cASN1Data = rb_define_class_under(mASN1, "ASN1Data", rb_cObject);
     
    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);

     
    cASN1Constructive = rb_define_class_under(mASN1,"Constructive", cASN1Data);
    rb_include_module(cASN1Constructive, rb_mEnumerable);
     
    rb_attr(cASN1Constructive, rb_intern("tagging"), 1, 1, Qtrue);
    rb_define_method(cASN1Constructive, "initialize", ossl_asn1_initialize, -1);
    rb_define_method(cASN1Constructive, "to_der", ossl_asn1cons_to_der, 0);
    rb_define_method(cASN1Constructive, "each", ossl_asn1cons_each, 0);

#define OSSL_ASN1_DEFINE_CLASS(name, super) \
do{\
    cASN1##name = rb_define_class_under(mASN1, #name, cASN1##super);\
    rb_define_module_function(mASN1, #name, ossl_asn1_##name, -1);\
}while(0)

    OSSL_ASN1_DEFINE_CLASS(Boolean, Primitive);
    OSSL_ASN1_DEFINE_CLASS(Integer, Primitive);
    OSSL_ASN1_DEFINE_CLASS(Enumerated, Primitive);
    OSSL_ASN1_DEFINE_CLASS(BitString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(OctetString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(UTF8String, Primitive);
    OSSL_ASN1_DEFINE_CLASS(NumericString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(PrintableString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(T61String, Primitive);
    OSSL_ASN1_DEFINE_CLASS(VideotexString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(IA5String, Primitive);
    OSSL_ASN1_DEFINE_CLASS(GraphicString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(ISO64String, Primitive);
    OSSL_ASN1_DEFINE_CLASS(GeneralString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(UniversalString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(BMPString, Primitive);
    OSSL_ASN1_DEFINE_CLASS(Null, Primitive);
    OSSL_ASN1_DEFINE_CLASS(ObjectId, Primitive);
    OSSL_ASN1_DEFINE_CLASS(UTCTime, Primitive);
    OSSL_ASN1_DEFINE_CLASS(GeneralizedTime, Primitive);

    OSSL_ASN1_DEFINE_CLASS(Sequence, Constructive);
    OSSL_ASN1_DEFINE_CLASS(Set, Constructive);

    OSSL_ASN1_DEFINE_CLASS(EndOfContent, Data);


     
#if 0
    cASN1ObjectId = rb_define_class_under(mASN1, "ObjectId", cASN1Primitive);   
#endif
    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_method(cASN1ObjectId, "oid", ossl_asn1obj_get_oid, 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, 0);

    class_tag_map = rb_hash_new();
    rb_hash_aset(class_tag_map, cASN1EndOfContent, INT2NUM(V_ASN1_EOC));
    rb_hash_aset(class_tag_map, cASN1Boolean, INT2NUM(V_ASN1_BOOLEAN));
    rb_hash_aset(class_tag_map, cASN1Integer, INT2NUM(V_ASN1_INTEGER));
    rb_hash_aset(class_tag_map, cASN1BitString, INT2NUM(V_ASN1_BIT_STRING));
    rb_hash_aset(class_tag_map, cASN1OctetString, INT2NUM(V_ASN1_OCTET_STRING));
    rb_hash_aset(class_tag_map, cASN1Null, INT2NUM(V_ASN1_NULL));
    rb_hash_aset(class_tag_map, cASN1ObjectId, INT2NUM(V_ASN1_OBJECT));
    rb_hash_aset(class_tag_map, cASN1Enumerated, INT2NUM(V_ASN1_ENUMERATED));
    rb_hash_aset(class_tag_map, cASN1UTF8String, INT2NUM(V_ASN1_UTF8STRING));
    rb_hash_aset(class_tag_map, cASN1Sequence, INT2NUM(V_ASN1_SEQUENCE));
    rb_hash_aset(class_tag_map, cASN1Set, INT2NUM(V_ASN1_SET));
    rb_hash_aset(class_tag_map, cASN1NumericString, INT2NUM(V_ASN1_NUMERICSTRING));
    rb_hash_aset(class_tag_map, cASN1PrintableString, INT2NUM(V_ASN1_PRINTABLESTRING));
    rb_hash_aset(class_tag_map, cASN1T61String, INT2NUM(V_ASN1_T61STRING));
    rb_hash_aset(class_tag_map, cASN1VideotexString, INT2NUM(V_ASN1_VIDEOTEXSTRING));
    rb_hash_aset(class_tag_map, cASN1IA5String, INT2NUM(V_ASN1_IA5STRING));
    rb_hash_aset(class_tag_map, cASN1UTCTime, INT2NUM(V_ASN1_UTCTIME));
    rb_hash_aset(class_tag_map, cASN1GeneralizedTime, INT2NUM(V_ASN1_GENERALIZEDTIME));
    rb_hash_aset(class_tag_map, cASN1GraphicString, INT2NUM(V_ASN1_GRAPHICSTRING));
    rb_hash_aset(class_tag_map, cASN1ISO64String, INT2NUM(V_ASN1_ISO64STRING));
    rb_hash_aset(class_tag_map, cASN1GeneralString, INT2NUM(V_ASN1_GENERALSTRING));
    rb_hash_aset(class_tag_map, cASN1UniversalString, INT2NUM(V_ASN1_UNIVERSALSTRING));
    rb_hash_aset(class_tag_map, cASN1BMPString, INT2NUM(V_ASN1_BMPSTRING));
    rb_global_variable(&class_tag_map);
}