org.ldaptive.asn1.DERParser Maven / Gradle / Ivy
/*
$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:
*
*
* - Buffer is positioned at start of value bytes.
* - Buffer limit is set to the end of value bytes.
*
*
* @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