All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ldaptive.asn1.DERParser Maven / Gradle / Ivy

There is a newer version: 2.3.2
Show newest version
/*
  $Id: DERParser.java 3050 2014-09-04 18:24:42Z dfisher $

  Copyright (C) 2003-2014 Virginia Tech.
  All rights reserved.

  SEE LICENSE FOR MORE INFORMATION

  Author:  Middleware Services
  Email:   [email protected]
  Version: $Revision: 3050 $
  Updated: $Date: 2014-09-04 14:24:42 -0400 (Thu, 04 Sep 2014) $
*/
package org.ldaptive.asn1;

import java.nio.ByteBuffer;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class provides a SAX-like parsing facility for DER-encoded data where
 * elements of interest in the parse tree may be registered to handlers via the
 * {@link #registerHandler} methods. {@link DERPath} strings are used to map
 * handlers to elements of interest.
 *
 * @author  Middleware Services
 * @version  $Revision: 3050 $ $Date: 2014-09-04 14:24:42 -0400 (Thu, 04 Sep 2014) $
 * @see  DERPath
 */
public class DERParser
{

  /** Logger for this class. */
  protected final Logger logger = LoggerFactory.getLogger(getClass());

  /** Handlers for DER paths. */
  private final Map handlerMap =
    new HashMap();

  /** Permutations of the current path. */
  private final Queue permutations = new ArrayDeque();


  /**
   * See {@link #registerHandler(DERPath, ParseHandler)}.
   *
   * @param  path  to register
   * @param  handler  to associate with the path
   */
  public void registerHandler(final String path, final ParseHandler handler)
  {
    registerHandler(new DERPath(path), handler);
  }


  /**
   * Registers the supplied handler to fire when the supplied path is
   * encountered.
   *
   * @param  path  to register
   * @param  handler  to associate with the path
   */
  public void registerHandler(final DERPath path, final ParseHandler handler)
  {
    handlerMap.put(path, handler);
  }


  /**
   * Parse a DER-encoded data structure by calling registered handlers when
   * points of interest are encountered in the parse tree.
   *
   * @param  encoded  DER-encoded bytes.
   */
  public void parse(final ByteBuffer encoded)
  {
    parseTags(encoded);
  }


  /**
   * Reads a DER tag from a single byte at the current position of the given
   * buffer. The buffer position is naturally advanced one byte in this
   * operation.
   *
   * @param  encoded  Buffer containing DER-encoded bytes positioned at tag.
   *
   * @return  Tag or null if no universal tag or application-specific tag is
   * known that matches the byte read in.
   */
  public DERTag readTag(final ByteBuffer encoded)
  {
    if (encoded.position() >= encoded.limit()) {
      return null;
    }

    DERTag tag;
    final byte b = encoded.get();
    // CheckStyle:MagicNumber OFF
    final int tagNo = b & 0x1F;
    final boolean constructed = (b & 0x20) == 0x20;
    // Read class from first two high-order bits
    switch (b & 0xC0) {

    case UniversalDERTag.TAG_CLASS:
      tag = UniversalDERTag.fromTagNo(tagNo);
      break;

    case ApplicationDERTag.TAG_CLASS:
      tag = new ApplicationDERTag(tagNo, constructed);
      break;

    case ContextDERTag.TAG_CLASS:
      tag = new ContextDERTag(tagNo, constructed);
      break;

    default:
      // Private class (class 11b)
      throw new IllegalArgumentException("Private classes not supported.");
    }
    // CheckStyle:MagicNumber ON
    return tag;
  }


  /**
   * Reads the length of a DER-encoded value from the given byte buffer. The
   * buffer is expected to be positioned at the byte immediately following the
   * tag byte, which is where the length byte(s) begin(s). Invocation of this
   * method has two generally beneficial side effects:
   *
   * 
    *
  1. Buffer is positioned at start of value bytes.
  2. *
  3. Buffer limit is set to the end of value bytes.
  4. *
* * @param encoded buffer containing DER-encoded bytes positioned at start of * length byte(s). * * @return number of bytes occupied by tag value. */ public int readLength(final ByteBuffer encoded) { int length = 0; final byte b = encoded.get(); // CheckStyle:MagicNumber OFF if ((b & 0x80) == 0x80) { final int len = b & 0x7F; if (len > 0) { encoded.limit(encoded.position() + len); length = IntegerType.decodeUnsigned(encoded).intValue(); encoded.limit(encoded.capacity()); } } else { length = b; } return length; // CheckStyle:MagicNumber ON } /** * Reads the supplied DER encoded bytes and invokes handlers as configured * paths are encountered. * * @param encoded to parse */ private void parseTags(final ByteBuffer encoded) { int index = 0; while (encoded.position() < encoded.limit()) { final DERTag tag = readTag(encoded); if (tag != null) { addTag(tag, index++); parseTag(tag, encoded); removeTag(); } } } /** * Invokes the parse handler for the current path and advances to the next * position in the encoded bytes. * * @param tag to inspect for internal tags * @param encoded to parse */ private void parseTag(final DERTag tag, final ByteBuffer encoded) { final int end = readLength(encoded) + encoded.position(); final int start = encoded.position(); // Invoke handlers for all permutations of current path ParseHandler handler; for (DERPath p : permutations) { handler = handlerMap.get(p); if (handler != null) { encoded.position(start).limit(end); handler.handle(this, encoded); } } if (tag.isConstructed()) { parseTags(encoded); } encoded.position(end).limit(encoded.capacity()); } /** * Add the given tag at the specified index to all permutations of the current * parser path and increases the number of permutations as necessary to * satisfy the following relation: * *
size = 2^n
* *

where n is the path length.

* * @param tag to add to path. * @param index of tag relative to parent. */ private void addTag(final DERTag tag, final int index) { if (permutations.isEmpty()) { permutations.add(new DERPath().pushNode(tag.name())); permutations.add(new DERPath().pushNode(tag.name(), index)); } else { final Collection generation = new ArrayDeque( permutations.size()); for (DERPath p : permutations) { generation.add(new DERPath(p).pushNode(tag.name())); p.pushNode(tag.name(), index); } permutations.addAll(generation); } } /** * Removes the tag at the leaf position of all permutations of the current * parser path, and reduces the number of permutations as necessary to satisfy * the following relation: * *
size = 2^n
* *

where n is the path length.

*/ private void removeTag() { final int half = permutations.size() / 2; while (permutations.size() > half) { permutations.remove(); } for (DERPath p : permutations) { p.popNode(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy