node_modules/node-forge/lib/asn1.js
/**
 * Internal function to parse an asn1 object from a byte buffer in DER format.
 *
 * @param bytes the byte buffer to parse from.
 * @param remaining the number of bytes remaining for this chunk.
 * @param depth the current parsing depth.
 * @param options object with same options as fromDer().
 *
 * @return the parsed asn1 object.
 */
Internal function to parse an asn1 object from a byte buffer in DER format.
@param bytes the byte buffer to parse from.
@param remaining the number of bytes remaining for this chunk.
@param depth the current parsing depth.
@param options object with same options as fromDer().

@return the parsed asn1 object.
Internal function to parse an asn1 object from a byte buffer in DER format . @param bytes the byte buffer to parse from . @param remaining the number of bytes remaining for this chunk . @param depth the current parsing depth . @param options object with same options as fromDer () . @return the parsed asn1 object .
function _fromDer(bytes, remaining, depth, options) {
  var start;
  _checkBufferLength(bytes, remaining, 2);
  var b1 = bytes.getByte();
  remaining--;
  var tagClass = (b1 & 0xC0);
  var type = b1 & 0x1F;
  start = bytes.length();
  var length = _getValueLength(bytes, remaining);
  remaining -= start - bytes.length();
  if(length !== undefined && length > remaining) {
    if(options.strict) {
      var error = new Error('Too few bytes to read ASN.1 value.');
      error.available = bytes.length();
      error.remaining = remaining;
      error.requested = length;
      throw error;
    }
    length = remaining;
  }
  var value;
  var bitStringContents;
  var constructed = ((b1 & 0x20) === 0x20);
  if(constructed) {
    value = [];
    if(length === undefined) {
      for(;;) {
        _checkBufferLength(bytes, remaining, 2);
        if(bytes.bytes(2) === String.fromCharCode(0, 0)) {
          bytes.getBytes(2);
          remaining -= 2;
          break;
        }
        start = bytes.length();
        value.push(_fromDer(bytes, remaining, depth + 1, options));
        remaining -= start - bytes.length();
      }
    } else {
      while(length > 0) {
        start = bytes.length();
        value.push(_fromDer(bytes, length, depth + 1, options));
        remaining -= start - bytes.length();
        length -= start - bytes.length();
      }
    }
  }
  if(value === undefined && tagClass === asn1.Class.UNIVERSAL &&
    type === asn1.Type.BITSTRING) {
    bitStringContents = bytes.bytes(length);
  }
  if(value === undefined && options.decodeBitStrings &&
    tagClass === asn1.Class.UNIVERSAL &&
    (type === asn1.Type.BITSTRING ) &&
    length > 1) {
    var savedRead = bytes.read;
    var savedRemaining = remaining;
    var unused = 0;
    if(type === asn1.Type.BITSTRING) {
      _checkBufferLength(bytes, remaining, 1);
      unused = bytes.getByte();
      remaining--;
    }
    if(unused === 0) {
      try {
        start = bytes.length();
        var subOptions = {
          verbose: options.verbose,
          strict: true,
          decodeBitStrings: true
        };
        var composed = _fromDer(bytes, remaining, depth + 1, subOptions);
        var used = start - bytes.length();
        remaining -= used;
        if(type == asn1.Type.BITSTRING) {
          used++;
        }
        var tc = composed.tagClass;
        if(used === length &&
          (tc === asn1.Class.UNIVERSAL || tc === asn1.Class.CONTEXT_SPECIFIC)) {
          value = [composed];
        }
      } catch(ex) {
      }
    }
    if(value === undefined) {
      bytes.read = savedRead;
      remaining = savedRemaining;
    }
  }
  if(value === undefined) {
    if(length === undefined) {
      if(options.strict) {
        throw new Error('Non-constructed ASN.1 object of indefinite length.');
      }
      length = remaining;
    }
    if(type === asn1.Type.BMPSTRING) {
      value = '';
      for(; length > 0; length -= 2) {
        _checkBufferLength(bytes, remaining, 2);
        value += String.fromCharCode(bytes.getInt16());
        remaining -= 2;
      }
    } else {
      value = bytes.getBytes(length);
    }
  }
  var asn1Options = bitStringContents === undefined ? null : {
    bitStringContents: bitStringContents
  };
  return asn1.create(tagClass, type, constructed, value, asn1Options);
}
function _fromDer ( bytes , remaining , depth , options ) { var start ; _checkBufferLength ( bytes , remaining , 2 ) ; var b1 = bytes . getByte ( ) ; remaining -- ; var tagClass = ( b1 & 0xC0 ) ; var type = b1 & 0x1F ; start = bytes . length ( ) ; var length = _getValueLength ( bytes , remaining ) ; remaining -= start - bytes . length ( ) ; if ( length !== undefined && length > remaining ) { if ( options . strict ) { var error = new Error ( 'Too few bytes to read ASN.1 value.' ) ; error . available = bytes . length ( ) ; error . remaining = remaining ; error . requested = length ; throw error ; } length = remaining ; } var value ; var bitStringContents ; var constructed = ( ( b1 & 0x20 ) === 0x20 ) ; if ( constructed ) { value = [ ] ; if ( length === undefined ) { for ( ; ; ) { _checkBufferLength ( bytes , remaining , 2 ) ; if ( bytes . bytes ( 2 ) === String . fromCharCode ( 0 , 0 ) ) { bytes . getBytes ( 2 ) ; remaining -= 2 ; break ; } start = bytes . length ( ) ; value . push ( _fromDer ( bytes , remaining , depth + 1 , options ) ) ; remaining -= start - bytes . length ( ) ; } } else { while ( length > 0 ) { start = bytes . length ( ) ; value . push ( _fromDer ( bytes , length , depth + 1 , options ) ) ; remaining -= start - bytes . length ( ) ; length -= start - bytes . length ( ) ; } } } if ( value === undefined && tagClass === asn1 . Class . UNIVERSAL && type === asn1 . Type . BITSTRING ) { bitStringContents = bytes . bytes ( length ) ; } if ( value === undefined && options . decodeBitStrings && tagClass === asn1 . Class . UNIVERSAL && ( type === asn1 . Type . BITSTRING ) && length > 1 ) { var savedRead = bytes . read ; var savedRemaining = remaining ; var unused = 0 ; if ( type === asn1 . Type . BITSTRING ) { _checkBufferLength ( bytes , remaining , 1 ) ; unused = bytes . getByte ( ) ; remaining -- ; } if ( unused === 0 ) { try { start = bytes . length ( ) ; var subOptions = { verbose : options . verbose , strict : true , decodeBitStrings : true } ; var composed = _fromDer ( bytes , remaining , depth + 1 , subOptions ) ; var used = start - bytes . length ( ) ; remaining -= used ; if ( type == asn1 . Type . BITSTRING ) { used ++ ; } var tc = composed . tagClass ; if ( used === length && ( tc === asn1 . Class . UNIVERSAL || tc === asn1 . Class . CONTEXT_SPECIFIC ) ) { value = [ composed ] ; } } catch ( ex ) { } } if ( value === undefined ) { bytes . read = savedRead ; remaining = savedRemaining ; } } if ( value === undefined ) { if ( length === undefined ) { if ( options . strict ) { throw new Error ( 'Non-constructed ASN.1 object of indefinite length.' ) ; } length = remaining ; } if ( type === asn1 . Type . BMPSTRING ) { value = '' ; for ( ; length > 0 ; length -= 2 ) { _checkBufferLength ( bytes , remaining , 2 ) ; value += String . fromCharCode ( bytes . getInt16 ( ) ) ; remaining -= 2 ; } } else { value = bytes . getBytes ( length ) ; } } var asn1Options = bitStringContents === undefined ? null : { bitStringContents : bitStringContents } ; return asn1 . create ( tagClass , type , constructed , value , asn1Options ) ; }
Internal function to parse an asn1 object from a byte buffer in DER format.
Internal function to parse an asn1 object from a byte buffer in DER format .
// temporary storage for consumption calculations // minimum length for ASN.1 DER structure is 2 // get the first byte // consumed one byte // get the tag class // get the type (bits 1-5) // get the variable value length and adjust remaining bytes // ensure there are enough bytes to get the value // Note: be lenient with truncated values and use remaining state bytes // value storage // possible BIT STRING contents storage // constructed flag is bit 6 (32 = 0x20) of the first byte // parse child asn1 objects from the value // asn1 object of indefinite length, read until end tag // parsing asn1 object of definite length // if a BIT STRING, save the contents including padding // determine if a non-constructed value should be decoded as a composed // value that contains other ASN.1 objects. BIT STRINGs (and OCTET STRINGs) // can be used this way. // FIXME: OCTET STRINGs not yet supported here // .. other parts of forge expect to decode OCTET STRINGs manually /*|| type === asn1.Type.OCTETSTRING*/ // save read position /* The first octet gives the number of bits by which the length of the
        bit string is less than the next multiple of eight (this is called
        the "number of unused bits").

        The second and following octets give the value of the bit string
        converted to an octet string. */ // if all bits are used, maybe the BIT/OCTET STRING holds ASN.1 objs // attempt to parse child asn1 object from the value // (stored in array to signal composed value) // enforce strict mode to avoid parsing ASN.1 from plain data // if the data all decoded and the class indicates UNIVERSAL or // CONTEXT_SPECIFIC then assume we've got an encapsulated ASN.1 object // restore read position // asn1 not constructed or composed, get raw value // TODO: do DER to OID conversion and vice-versa in .toDer? // be lenient and use remaining state bytes // add BIT STRING contents if available // create and return asn1 object