/******************************************************************************* * Copyright (c) 2006, 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.osgi.internal.signedcontent; import java.math.BigInteger; import java.security.SignatureException; /** * This is a simple class that processes BER structures. This class * uses BER processing as outlined in X.690. */ public class BERProcessor { /** * This is the buffer that contains the BER structures that are being interrogated. */ byte buffer[]; /** * The offset into <code>buffer</code> to the start of the structure being interrogated. * If the offset is -1 that means that we have read the last structure. */ int offset; /** * The last valid offset in <code>buffer</code>. */ int lastOffset; /** * The offset into <code>buffer</code> to the start of the content of the structure * being interrogated. */ int contentOffset; /** * The length of the content of the structure being interrogated. */ int contentLength; /** * The offset into <code>buffer</code> of the end of the structure being interrogated. */ int endOffset; /** * The class of the tag of the current structure. */ int classOfTag; static final int UNIVERSAL_TAGCLASS = 0; static final int APPLICATION_TAGCLASS = 1; static final int CONTEXTSPECIFIC_TAGCLASS = 2; static final int PRIVATE_TAGCLASS = 3; static final byte BOOLTAG = 1; static final byte INTTAG = 2; static final byte OIDTAG = 6; static final byte SEQTAG = 16; static final byte SETTAG = 17; static final byte NULLTAG = 5; /** * Tagnames used in toString() */ static final String tagNames[] = {"<null>", "boolean", "int", "bitstring", "octetstring", "null", "objid", "objdesc", "external", "real", "enum", "pdv", "utf8", "relobjid", "resv", "resv", "sequence", "set", "char string"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$ //$NON-NLS-13$ //$NON-NLS-14$ //$NON-NLS-15$ //$NON-NLS-16$ //$NON-NLS-17$ //$NON-NLS-18$ //$NON-NLS-19$ /** * True if this is a structure for a constructed encoding. */ public boolean constructed; /** * The tag type. Note that X.690 specifies encodings for tags with values greater than 31, * but currently this class does not handle these kinds of tags. */ public byte tag; /** * Constructs a BERProcessor to operate on the passed buffer. The first structure in the * buffer will be processed before this method returns. * * @param buffer the buffer containing the BER structures. * @param offset the offset into <code>buffer</code> to the start of the first structure. * @param len the length of the BER structure. * @throws SignatureException */ public BERProcessor(byte buffer[], int offset, int len) throws SignatureException { this.buffer = buffer; this.offset = offset; lastOffset = len + offset; processStructure(); } /** * Parse the structure found at the current <code>offset</code> into <code>buffer</code>. * Most methods, constructor, and stepinto, will call this method automatically. If * <code>offset</code> is modified outside of those methods, this method will need to * be invoked. */ public void processStructure() throws SignatureException { // Don't process if we are at the end if (offset == -1) return; endOffset = offset; // section 8.1.2.2 classOfTag = (buffer[offset] & 0xff) >> 6; // section 8.1.2.5 constructed = (buffer[offset] & 0x20) != 0; // section 8.1.2.3 byte tagNumber = (byte) (buffer[offset] & 0x1f); if (tagNumber < 32) { tag = tagNumber; endOffset = offset + 1; } else { throw new SignatureException("Can't handle tags > 32"); //$NON-NLS-1$ } if ((buffer[endOffset] & 0x80) == 0) { // section 8.1.3.4 (doing the short form of the length) contentLength = buffer[endOffset]; endOffset++; } else { // section 8.1.3.5 (doing the long form of the length) int octetCount = buffer[endOffset] & 0x7f; if (octetCount > 3) throw new SignatureException("ContentLength octet count too large: " + octetCount); //$NON-NLS-1$ contentLength = 0; endOffset++; for (int i = 0; i < octetCount; i++) { contentLength <<= 8; contentLength |= buffer[endOffset] & 0xff; endOffset++; } // section 8.1.3.6 (doing the indefinite form if (octetCount == 0) contentLength = -1; } contentOffset = endOffset; if (contentLength != -1) endOffset += contentLength; if (endOffset > lastOffset) throw new SignatureException("Content length too large: " + endOffset + " > " + lastOffset); //$NON-NLS-1$ //$NON-NLS-2$ } /** * Returns a String representation of the current BER structure. * @return a String representation of the current BER structure. * @see java.lang.Object#toString() */ public String toString() { StringBuffer sb = new StringBuffer(); switch (classOfTag) { case UNIVERSAL_TAGCLASS : sb.append('U'); break; case APPLICATION_TAGCLASS : sb.append('A'); break; case CONTEXTSPECIFIC_TAGCLASS : sb.append('C'); break; case PRIVATE_TAGCLASS : sb.append('P'); break; } sb.append(constructed ? 'C' : 'P'); sb.append(" tag=" + tag); //$NON-NLS-1$ if (tag < tagNames.length) { sb.append("(" + tagNames[tag] + ")"); //$NON-NLS-1$ //$NON-NLS-2$ } sb.append(" len="); //$NON-NLS-1$ sb.append(contentLength); switch (tag) { case INTTAG : sb.append(" value=" + getIntValue()); //$NON-NLS-1$ break; case OIDTAG : sb.append(" value="); //$NON-NLS-1$ int oid[] = getObjId(); for (int i = 0; i < oid.length; i++) { if (i > 0) sb.append('.'); sb.append(oid[i]); } } if (tag == 12 || (tag >= 18 && tag <= 22) || (tag >= 25 && tag <= 30)) { sb.append(" value="); //$NON-NLS-1$ sb.append(getString()); } return sb.toString(); } /** * Returns a BERProcessor for the content of the current structure. * @throws SignatureException */ public BERProcessor stepInto() throws SignatureException { return new BERProcessor(buffer, contentOffset, contentLength); } public void stepOver() throws SignatureException { offset = endOffset; if (endOffset >= lastOffset) { offset = -1; return; } processStructure(); } public boolean endOfSequence() { return offset == -1; } /** * Gets the content from the current structure as a String. * @return the content from the current structure as a String. */ public String getString() { return new String(buffer, contentOffset, contentLength); } /** * Gets the content from the current structure as an int. * @return the content from the current structure as an int. */ public BigInteger getIntValue() { return new BigInteger(getBytes()); } /** * Gets the content from the current structure as an object id (int[]). * @return the content from the current structure as an object id (int[]). */ public int[] getObjId() { // First count the ids int count = 0; for (int i = 0; i < contentLength; i++) { // section 8.19.2 if ((buffer[contentOffset + i] & 0x80) == 0) count++; } count++; // section 8.19.3 int oid[] = new int[count]; int index = 0; int currentValue = 0; for (int i = 0; i < contentLength; i++) { currentValue <<= 7; currentValue |= buffer[contentOffset + i] & 0x7f; // section 8.19.2 if ((buffer[contentOffset + i] & 0x80) == 0) { if (index == 0) { // section 8.19.4 special processing oid[index++] = currentValue / 40; oid[index++] = currentValue % 40; } else { oid[index++] = currentValue; } currentValue = 0; } } return oid; } /** * Get a copy of the bytes in the content of the current structure. * @return a copy of the bytes in the content of the current structure. */ public byte[] getBytes() { byte v[] = new byte[contentLength]; System.arraycopy(buffer, contentOffset, v, 0, contentLength); return v; } }