jdk/src/share/classes/sun/security/util/DerIndefLenConverter.java
changeset 2 90ce3da70b43
child 1093 b7d502a05abf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/security/util/DerIndefLenConverter.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,331 @@
+/*
+ * Copyright 1998-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.security.util;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * A package private utility class to convert indefinite length DER
+ * encoded byte arrays to definite length DER encoded byte arrays.
+ *
+ * This assumes that the basic data structure is "tag, length, value"
+ * triplet. In the case where the length is "indefinite", terminating
+ * end-of-contents bytes are expected.
+ *
+ * @author Hemma Prafullchandra
+ */
+class DerIndefLenConverter {
+
+    private static final int TAG_MASK            = 0x1f; // bits 5-1
+    private static final int FORM_MASK           = 0x20; // bits 6
+    private static final int CLASS_MASK          = 0xC0; // bits 8 and 7
+
+    private static final int LEN_LONG            = 0x80; // bit 8 set
+    private static final int LEN_MASK            = 0x7f; // bits 7 - 1
+    private static final int SKIP_EOC_BYTES      = 2;
+
+    private byte[] data, newData;
+    private int newDataPos, dataPos, dataSize, index;
+
+    private ArrayList<Object> ndefsList = new ArrayList<Object>();
+
+    private int numOfTotalLenBytes = 0;
+
+    private boolean isEOC(int tag) {
+        return (((tag & TAG_MASK) == 0x00) &&  // EOC
+                ((tag & FORM_MASK) == 0x00) && // primitive
+                ((tag & CLASS_MASK) == 0x00)); // universal
+    }
+
+    // if bit 8 is set then it implies either indefinite length or long form
+    static boolean isLongForm(int lengthByte) {
+        return ((lengthByte & LEN_LONG) == LEN_LONG);
+    }
+
+    /*
+     * Default package private constructor
+     */
+    DerIndefLenConverter() { }
+
+    /**
+     * Checks whether the given length byte is of the form
+     * <em>Indefinite</em>.
+     *
+     * @param lengthByte the length byte from a DER encoded
+     *        object.
+     * @return true if the byte is of Indefinite form otherwise
+     *         returns false.
+     */
+    static boolean isIndefinite(int lengthByte) {
+        return (isLongForm(lengthByte) && ((lengthByte & LEN_MASK) == 0));
+    }
+
+    /**
+     * Parse the tag and if it is an end-of-contents tag then
+     * add the current position to the <code>eocList</code> vector.
+     */
+    private void parseTag() throws IOException {
+        if (dataPos == dataSize)
+            return;
+        if (isEOC(data[dataPos]) && (data[dataPos + 1] == 0)) {
+            int numOfEncapsulatedLenBytes = 0;
+            Object elem = null;
+            int index;
+            for (index = ndefsList.size()-1; index >= 0; index--) {
+                // Determine the first element in the vector that does not
+                // have a matching EOC
+                elem = ndefsList.get(index);
+                if (elem instanceof Integer) {
+                    break;
+                } else {
+                    numOfEncapsulatedLenBytes += ((byte[])elem).length - 3;
+                }
+            }
+            if (index < 0) {
+                throw new IOException("EOC does not have matching " +
+                                      "indefinite-length tag");
+            }
+            int sectionLen = dataPos - ((Integer)elem).intValue() +
+                             numOfEncapsulatedLenBytes;
+            byte[] sectionLenBytes = getLengthBytes(sectionLen);
+            ndefsList.set(index, sectionLenBytes);
+
+            // Add the number of bytes required to represent this section
+            // to the total number of length bytes,
+            // and subtract the indefinite-length tag (1 byte) and
+            // EOC bytes (2 bytes) for this section
+            numOfTotalLenBytes += (sectionLenBytes.length - 3);
+        }
+        dataPos++;
+    }
+
+    /**
+     * Write the tag and if it is an end-of-contents tag
+     * then skip the tag and its 1 byte length of zero.
+     */
+    private void writeTag() {
+        if (dataPos == dataSize)
+            return;
+        int tag = data[dataPos++];
+        if (isEOC(tag) && (data[dataPos] == 0)) {
+            dataPos++;  // skip length
+            writeTag();
+        } else
+            newData[newDataPos++] = (byte)tag;
+    }
+
+    /**
+     * Parse the length and if it is an indefinite length then add
+     * the current position to the <code>ndefsList</code> vector.
+     */
+    private int parseLength() throws IOException {
+        int curLen = 0;
+        if (dataPos == dataSize)
+            return curLen;
+        int lenByte = data[dataPos++] & 0xff;
+        if (isIndefinite(lenByte)) {
+            ndefsList.add(new Integer(dataPos));
+            return curLen;
+        }
+        if (isLongForm(lenByte)) {
+            lenByte &= LEN_MASK;
+            if (lenByte > 4)
+                throw new IOException("Too much data");
+            if ((dataSize - dataPos) < (lenByte + 1))
+                throw new IOException("Too little data");
+            for (int i = 0; i < lenByte; i++)
+                curLen = (curLen << 8) + (data[dataPos++] & 0xff);
+        } else {
+           curLen = (lenByte & LEN_MASK);
+        }
+        return curLen;
+    }
+
+    /**
+     * Write the length and if it is an indefinite length
+     * then calculate the definite length from the positions
+     * of the indefinite length and its matching EOC terminator.
+     * Then, write the value.
+     */
+    private void writeLengthAndValue() throws IOException {
+        if (dataPos == dataSize)
+           return;
+        int curLen = 0;
+        int lenByte = data[dataPos++] & 0xff;
+        if (isIndefinite(lenByte)) {
+            byte[] lenBytes = (byte[])ndefsList.get(index++);
+            System.arraycopy(lenBytes, 0, newData, newDataPos,
+                             lenBytes.length);
+            newDataPos += lenBytes.length;
+            return;
+        }
+        if (isLongForm(lenByte)) {
+            lenByte &= LEN_MASK;
+            for (int i = 0; i < lenByte; i++)
+                curLen = (curLen << 8) + (data[dataPos++] & 0xff);
+        } else
+            curLen = (lenByte & LEN_MASK);
+        writeLength(curLen);
+        writeValue(curLen);
+    }
+
+    private void writeLength(int curLen) {
+        if (curLen < 128) {
+            newData[newDataPos++] = (byte)curLen;
+
+        } else if (curLen < (1 << 8)) {
+            newData[newDataPos++] = (byte)0x81;
+            newData[newDataPos++] = (byte)curLen;
+
+        } else if (curLen < (1 << 16)) {
+            newData[newDataPos++] = (byte)0x82;
+            newData[newDataPos++] = (byte)(curLen >> 8);
+            newData[newDataPos++] = (byte)curLen;
+
+        } else if (curLen < (1 << 24)) {
+            newData[newDataPos++] = (byte)0x83;
+            newData[newDataPos++] = (byte)(curLen >> 16);
+            newData[newDataPos++] = (byte)(curLen >> 8);
+            newData[newDataPos++] = (byte)curLen;
+
+        } else {
+            newData[newDataPos++] = (byte)0x84;
+            newData[newDataPos++] = (byte)(curLen >> 24);
+            newData[newDataPos++] = (byte)(curLen >> 16);
+            newData[newDataPos++] = (byte)(curLen >> 8);
+            newData[newDataPos++] = (byte)curLen;
+        }
+    }
+
+    private byte[] getLengthBytes(int curLen) {
+        byte[] lenBytes;
+        int index = 0;
+
+        if (curLen < 128) {
+            lenBytes = new byte[1];
+            lenBytes[index++] = (byte)curLen;
+
+        } else if (curLen < (1 << 8)) {
+            lenBytes = new byte[2];
+            lenBytes[index++] = (byte)0x81;
+            lenBytes[index++] = (byte)curLen;
+
+        } else if (curLen < (1 << 16)) {
+            lenBytes = new byte[3];
+            lenBytes[index++] = (byte)0x82;
+            lenBytes[index++] = (byte)(curLen >> 8);
+            lenBytes[index++] = (byte)curLen;
+
+        } else if (curLen < (1 << 24)) {
+            lenBytes = new byte[4];
+            lenBytes[index++] = (byte)0x83;
+            lenBytes[index++] = (byte)(curLen >> 16);
+            lenBytes[index++] = (byte)(curLen >> 8);
+            lenBytes[index++] = (byte)curLen;
+
+        } else {
+            lenBytes = new byte[5];
+            lenBytes[index++] = (byte)0x84;
+            lenBytes[index++] = (byte)(curLen >> 24);
+            lenBytes[index++] = (byte)(curLen >> 16);
+            lenBytes[index++] = (byte)(curLen >> 8);
+            lenBytes[index++] = (byte)curLen;
+        }
+
+        return lenBytes;
+    }
+
+    // Returns the number of bytes needed to represent the given length
+    // in ASN.1 notation
+    private int getNumOfLenBytes(int len) {
+        int numOfLenBytes = 0;
+
+        if (len < 128) {
+            numOfLenBytes = 1;
+        } else if (len < (1 << 8)) {
+            numOfLenBytes = 2;
+        } else if (len < (1 << 16)) {
+            numOfLenBytes = 3;
+        } else if (len < (1 << 24)) {
+            numOfLenBytes = 4;
+        } else {
+            numOfLenBytes = 5;
+        }
+        return numOfLenBytes;
+    }
+
+    /**
+     * Parse the value;
+     */
+    private void parseValue(int curLen) {
+        dataPos += curLen;
+    }
+
+    /**
+     * Write the value;
+     */
+    private void writeValue(int curLen) {
+        for (int i=0; i < curLen; i++)
+            newData[newDataPos++] = data[dataPos++];
+    }
+
+    /**
+     * Converts a indefinite length DER encoded byte array to
+     * a definte length DER encoding.
+     *
+     * @param indefData the byte array holding the indefinite
+     *        length encoding.
+     * @return the byte array containing the definite length
+     *         DER encoding.
+     * @exception IOException on parsing or re-writing errors.
+     */
+    byte[] convert(byte[] indefData) throws IOException {
+        data = indefData;
+        dataPos=0; index=0;
+        dataSize = data.length;
+        int len=0;
+
+        // parse and set up the vectors of all the indefinite-lengths
+        while (dataPos < dataSize) {
+            parseTag();
+            len = parseLength();
+            parseValue(len);
+        }
+
+        newData = new byte[dataSize + numOfTotalLenBytes];
+        dataPos=0; newDataPos=0; index=0;
+
+        // write out the new byte array replacing all the indefinite-lengths
+        // and EOCs
+        while (dataPos < dataSize) {
+           writeTag();
+           writeLengthAndValue();
+        }
+
+        return newData;
+    }
+}