text: try {
    var asn1 = require('asn1.js');
  } catch (e) {
    var asn1 = require('../..');
  }
  
  var CRLReason = asn1.define('CRLReason', function() {
    this.enum({
      0: 'unspecified',
      1: 'keyCompromise',
      2: 'CACompromise',
      3: 'affiliationChanged',
      4: 'superseded',
      5: 'cessationOfOperation',
      6: 'certificateHold',
      8: 'removeFromCRL',
      9: 'privilegeWithdrawn',
      10: 'AACompromise'
    });
  });
  exports.CRLReason = CRLReason;
  
  var AlgorithmIdentifier = asn1.define('AlgorithmIdentifier', function() {
    this.seq().obj(
      this.key('algorithm').objid(),
      this.key('parameters').optional().any()
    );
  });
  exports.AlgorithmIdentifier = AlgorithmIdentifier;
  
  var Certificate = asn1.define('Certificate', function() {
    this.seq().obj(
      this.key('tbsCertificate').use(TBSCertificate),
      this.key('signatureAlgorithm').use(AlgorithmIdentifier),
      this.key('signature').bitstr()
    );
  });
  exports.Certificate = Certificate;
  
  var TBSCertificate = asn1.define('TBSCertificate', function() {
    this.seq().obj(
      this.key('version').def('v1').explicit(0).use(Version),
      this.key('serialNumber').use(CertificateSerialNumber),
      this.key('signature').use(AlgorithmIdentifier),
      this.key('issuer').use(Name),
      this.key('validity').use(Validity),
      this.key('subject').use(Name),
      this.key('subjectPublicKeyInfo').use(SubjectPublicKeyInfo),
  
      // TODO(indutny): validate that version is v2 or v3
      this.key('issuerUniqueID').optional().explicit(1).use(UniqueIdentifier),
      this.key('subjectUniqueID').optional().explicit(2).use(UniqueIdentifier),
  
      // TODO(indutny): validate that version is v3
      this.key('extensions').optional().explicit(3).use(Extensions)
    );
  });
  exports.TBSCertificate = TBSCertificate;
  
  var Version = asn1.define('Version', function() {
    this.int({
      0: 'v1',
      1: 'v2',
      2: 'v3'
    });
  });
  exports.Version = Version;
  
  var CertificateSerialNumber = asn1.define('CertificateSerialNumber',
                                            function() {
    this.int();
  });
  exports.CertificateSerialNumber = CertificateSerialNumber;
  
  var Validity = asn1.define('Validity', function() {
    this.seq().obj(
      this.key('notBefore').use(Time),
      this.key('notAfter').use(Time)
    );
  });
  exports.Validity = Validity;
  
  var Time = asn1.define('Time', function() {
    this.choice({
      utcTime: this.utctime(),
      genTime: this.gentime()
    });
  });
  exports.Time = Time;
  
  var UniqueIdentifier = asn1.define('UniqueIdentifier', function() {
    this.bitstr();
  });
  exports.UniqueIdentifier = UniqueIdentifier;
  
  var SubjectPublicKeyInfo = asn1.define('SubjectPublicKeyInfo', function() {
    this.seq().obj(
      this.key('algorithm').use(AlgorithmIdentifier),
      this.key('subjectPublicKey').bitstr()
    );
  });
  exports.SubjectPublicKeyInfo = SubjectPublicKeyInfo;
  
  var Extensions = asn1.define('Extensions', function() {
    this.seqof(Extension);
  });
  exports.Extensions = Extensions;
  
  var Extension = asn1.define('Extension', function() {
    this.seq().obj(
      this.key('extnID').objid(),
      this.key('critical').bool().def(false),
      this.key('extnValue').octstr()
    );
  });
  exports.Extension = Extension;
  
  var Name = asn1.define('Name', function() {
    this.choice({
      rdn: this.use(RDNSequence)
    });
  });
  exports.Name = Name;
  
  var RDNSequence = asn1.define('RDNSequence', function() {
    this.seqof(RelativeDistinguishedName);
  });
  exports.RDNSequence = RDNSequence;
  
  var RelativeDistinguishedName = asn1.define('RelativeDistinguishedName',
                                              function() {
    this.setof(AttributeTypeAndValue);
  });
  exports.RelativeDistinguishedName = RelativeDistinguishedName;
  
  var AttributeTypeAndValue = asn1.define('AttributeTypeAndValue', function() {
    this.seq().obj(
      this.key('type').use(AttributeType),
      this.key('value').use(AttributeValue)
    );
  });
  exports.AttributeTypeAndValue = AttributeTypeAndValue;
  
  var AttributeType = asn1.define('AttributeType', function() {
    this.objid();
  });
  exports.AttributeType = AttributeType;
  
  var AttributeValue = asn1.define('AttributeValue', function() {
    this.any();
  });
  exports.AttributeValue = AttributeValue;
  