Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved.
      3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      4  *
      5  * This code is free software; you can redistribute it and/or modify it
      6  * under the terms of the GNU General Public License version 2 only, as
      7  * published by the Free Software Foundation.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle in the LICENSE file that accompanied this code.
     10  *
     11  * This code is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  * version 2 for more details (a copy is included in the LICENSE file that
     15  * accompanied this code).
     16  *
     17  * You should have received a copy of the GNU General Public License version
     18  * 2 along with this work; if not, write to the Free Software Foundation,
     19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     20  *
     21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25 
     26 package sun.security.util;
     27 
     28 import java.io.IOException;
     29 import java.util.ArrayList;
     30 
     31 /**
     32  * A package private utility class to convert indefinite length DER
     33  * encoded byte arrays to definite length DER encoded byte arrays.
     34  *
     35  * This assumes that the basic data structure is "tag, length, value"
     36  * triplet. In the case where the length is "indefinite", terminating
     37  * end-of-contents bytes are expected.
     38  *
     39  * @author Hemma Prafullchandra
     40  */
     41 class DerIndefLenConverter {
     42 
     43     private static final int TAG_MASK            = 0x1f; // bits 5-1
     44     private static final int FORM_MASK           = 0x20; // bits 6
     45     private static final int CLASS_MASK          = 0xC0; // bits 8 and 7
     46 
     47     private static final int LEN_LONG            = 0x80; // bit 8 set
     48     private static final int LEN_MASK            = 0x7f; // bits 7 - 1
     49     private static final int SKIP_EOC_BYTES      = 2;
     50 
     51     private byte[] data, newData;
     52     private int newDataPos, dataPos, dataSize, index;
     53     private int unresolved = 0;
     54 
     55     private ArrayList<Object> ndefsList = new ArrayList<Object>();
     56 
     57     private int numOfTotalLenBytes = 0;
     58 
     59     private boolean isEOC(int tag) {
     60         return (((tag & TAG_MASK) == 0x00) &&  // EOC
     61                 ((tag & FORM_MASK) == 0x00) && // primitive
     62                 ((tag & CLASS_MASK) == 0x00)); // universal
     63     }
     64 
     65     // if bit 8 is set then it implies either indefinite length or long form
     66     static boolean isLongForm(int lengthByte) {
     67         return ((lengthByte & LEN_LONG) == LEN_LONG);
     68     }
     69 
     70     /*
     71      * Default package private constructor
     72      */
     73     DerIndefLenConverter() { }
     74 
     75     /**
     76      * Checks whether the given length byte is of the form
     77      * <em>Indefinite</em>.
     78      *
     79      * @param lengthByte the length byte from a DER encoded
     80      *        object.
     81      * @return true if the byte is of Indefinite form otherwise
     82      *         returns false.
     83      */
     84     static boolean isIndefinite(int lengthByte) {
     85         return (isLongForm(lengthByte) && ((lengthByte & LEN_MASK) == 0));
     86     }
     87 
     88     /**
     89      * Parse the tag and if it is an end-of-contents tag then
     90      * add the current position to the <code>eocList</code> vector.
     91      */
     92     private void parseTag() throws IOException {
     93         if (dataPos == dataSize)
     94             return;
     95         if (isEOC(data[dataPos]) && (data[dataPos + 1] == 0)) {
     96             int numOfEncapsulatedLenBytes = 0;
     97             Object elem = null;
     98             int index;
     99             for (index = ndefsList.size()-1; index >= 0; index--) {
    100                 // Determine the first element in the vector that does not
    101                 // have a matching EOC
    102                 elem = ndefsList.get(index);
    103                 if (elem instanceof Integer) {
    104                     break;
    105                 } else {
    106                     numOfEncapsulatedLenBytes += ((byte[])elem).length - 3;
    107                 }
    108             }
    109             if (index < 0) {
    110                 throw new IOException("EOC does not have matching " +
    111                                       "indefinite-length tag");
    112             }
    113             int sectionLen = dataPos - ((Integer)elem).intValue() +
    114                              numOfEncapsulatedLenBytes;
    115             byte[] sectionLenBytes = getLengthBytes(sectionLen);
    116             ndefsList.set(index, sectionLenBytes);
    117             unresolved--;
    118 
    119             // Add the number of bytes required to represent this section
    120             // to the total number of length bytes,
    121             // and subtract the indefinite-length tag (1 byte) and
    122             // EOC bytes (2 bytes) for this section
    123             numOfTotalLenBytes += (sectionLenBytes.length - 3);
    124         }
    125         dataPos++;
    126     }
    127 
    128     /**
    129      * Write the tag and if it is an end-of-contents tag
    130      * then skip the tag and its 1 byte length of zero.
    131      */
    132     private void writeTag() {
    133         if (dataPos == dataSize)
    134             return;
    135         int tag = data[dataPos++];
    136         if (isEOC(tag) && (data[dataPos] == 0)) {
    137             dataPos++;  // skip length
    138             writeTag();
    139         } else
    140             newData[newDataPos++] = (byte)tag;
    141     }
    142 
    143     /**
    144      * Parse the length and if it is an indefinite length then add
    145      * the current position to the <code>ndefsList</code> vector.
    146      */
    147     private int parseLength() throws IOException {
    148         int curLen = 0;
    149         if (dataPos == dataSize)
    150             return curLen;
    151         int lenByte = data[dataPos++] & 0xff;
    152         if (isIndefinite(lenByte)) {
    153             ndefsList.add(new Integer(dataPos));
    154             unresolved++;
    155             return curLen;
    156         }
    157         if (isLongForm(lenByte)) {
    158             lenByte &= LEN_MASK;
    159             if (lenByte > 4) {
    160                 throw new IOException("Too much data");
    161             }
    162             if ((dataSize - dataPos) < (lenByte + 1)) {
    163                 throw new IOException("Too little data");
    164             }
    165             for (int i = 0; i < lenByte; i++) {
    166                 curLen = (curLen << 8) + (data[dataPos++] & 0xff);
    167             }
    168             if (curLen < 0) {
    169                 throw new IOException("Invalid length bytes");
    170             }
    171         } else {
    172            curLen = (lenByte & LEN_MASK);
    173         }
    174         return curLen;
    175     }
    176 
    177     /**
    178      * Write the length and if it is an indefinite length
    179      * then calculate the definite length from the positions
    180      * of the indefinite length and its matching EOC terminator.
    181      * Then, write the value.
    182      */
    183     private void writeLengthAndValue() throws IOException {
    184         if (dataPos == dataSize)
    185            return;
    186         int curLen = 0;
    187         int lenByte = data[dataPos++] & 0xff;
    188         if (isIndefinite(lenByte)) {
    189             byte[] lenBytes = (byte[])ndefsList.get(index++);
    190             System.arraycopy(lenBytes, 0, newData, newDataPos,
    191                              lenBytes.length);
    192             newDataPos += lenBytes.length;
    193             return;
    194         }
    195         if (isLongForm(lenByte)) {
    196             lenByte &= LEN_MASK;
    197             for (int i = 0; i < lenByte; i++) {
    198                 curLen = (curLen << 8) + (data[dataPos++] & 0xff);
    199             }
    200             if (curLen < 0) {
    201                 throw new IOException("Invalid length bytes");
    202             }
    203         } else {
    204             curLen = (lenByte & LEN_MASK);
    205         }
    206         writeLength(curLen);
    207         writeValue(curLen);
    208     }
    209 
    210     private void writeLength(int curLen) {
    211         if (curLen < 128) {
    212             newData[newDataPos++] = (byte)curLen;
    213 
    214         } else if (curLen < (1 << 8)) {
    215             newData[newDataPos++] = (byte)0x81;
    216             newData[newDataPos++] = (byte)curLen;
    217 
    218         } else if (curLen < (1 << 16)) {
    219             newData[newDataPos++] = (byte)0x82;
    220             newData[newDataPos++] = (byte)(curLen >> 8);
    221             newData[newDataPos++] = (byte)curLen;
    222 
    223         } else if (curLen < (1 << 24)) {
    224             newData[newDataPos++] = (byte)0x83;
    225             newData[newDataPos++] = (byte)(curLen >> 16);
    226             newData[newDataPos++] = (byte)(curLen >> 8);
    227             newData[newDataPos++] = (byte)curLen;
    228 
    229         } else {
    230             newData[newDataPos++] = (byte)0x84;
    231             newData[newDataPos++] = (byte)(curLen >> 24);
    232             newData[newDataPos++] = (byte)(curLen >> 16);
    233             newData[newDataPos++] = (byte)(curLen >> 8);
    234             newData[newDataPos++] = (byte)curLen;
    235         }
    236     }
    237 
    238     private byte[] getLengthBytes(int curLen) {
    239         byte[] lenBytes;
    240         int index = 0;
    241 
    242         if (curLen < 128) {
    243             lenBytes = new byte[1];
    244             lenBytes[index++] = (byte)curLen;
    245 
    246         } else if (curLen < (1 << 8)) {
    247             lenBytes = new byte[2];
    248             lenBytes[index++] = (byte)0x81;
    249             lenBytes[index++] = (byte)curLen;
    250 
    251         } else if (curLen < (1 << 16)) {
    252             lenBytes = new byte[3];
    253             lenBytes[index++] = (byte)0x82;
    254             lenBytes[index++] = (byte)(curLen >> 8);
    255             lenBytes[index++] = (byte)curLen;
    256 
    257         } else if (curLen < (1 << 24)) {
    258             lenBytes = new byte[4];
    259             lenBytes[index++] = (byte)0x83;
    260             lenBytes[index++] = (byte)(curLen >> 16);
    261             lenBytes[index++] = (byte)(curLen >> 8);
    262             lenBytes[index++] = (byte)curLen;
    263 
    264         } else {
    265             lenBytes = new byte[5];
    266             lenBytes[index++] = (byte)0x84;
    267             lenBytes[index++] = (byte)(curLen >> 24);
    268             lenBytes[index++] = (byte)(curLen >> 16);
    269             lenBytes[index++] = (byte)(curLen >> 8);
    270             lenBytes[index++] = (byte)curLen;
    271         }
    272 
    273         return lenBytes;
    274     }
    275 
    276     // Returns the number of bytes needed to represent the given length
    277     // in ASN.1 notation
    278     private int getNumOfLenBytes(int len) {
    279         int numOfLenBytes = 0;
    280 
    281         if (len < 128) {
    282             numOfLenBytes = 1;
    283         } else if (len < (1 << 8)) {
    284             numOfLenBytes = 2;
    285         } else if (len < (1 << 16)) {
    286             numOfLenBytes = 3;
    287         } else if (len < (1 << 24)) {
    288             numOfLenBytes = 4;
    289         } else {
    290             numOfLenBytes = 5;
    291         }
    292         return numOfLenBytes;
    293     }
    294 
    295     /**
    296      * Parse the value;
    297      */
    298     private void parseValue(int curLen) {
    299         dataPos += curLen;
    300     }
    301 
    302     /**
    303      * Write the value;
    304      */
    305     private void writeValue(int curLen) {
    306         for (int i=0; i < curLen; i++)
    307             newData[newDataPos++] = data[dataPos++];
    308     }
    309 
    310     /**
    311      * Converts a indefinite length DER encoded byte array to
    312      * a definte length DER encoding.
    313      *
    314      * @param indefData the byte array holding the indefinite
    315      *        length encoding.
    316      * @return the byte array containing the definite length
    317      *         DER encoding.
    318      * @exception IOException on parsing or re-writing errors.
    319      */
    320     byte[] convert(byte[] indefData) throws IOException {
    321         data = indefData;
    322         dataPos=0; index=0;
    323         dataSize = data.length;
    324         int len=0;
    325         int unused = 0;
    326 
    327         // parse and set up the vectors of all the indefinite-lengths
    328         while (dataPos < dataSize) {
    329             parseTag();
    330             len = parseLength();
    331             parseValue(len);
    332             if (unresolved == 0) {
    333                 unused = dataSize - dataPos;
    334                 dataSize = dataPos;
    335                 break;
    336             }
    337         }
    338 
    339         if (unresolved != 0) {
    340             throw new IOException("not all indef len BER resolved");
    341         }
    342 
    343         newData = new byte[dataSize + numOfTotalLenBytes + unused];
    344         dataPos=0; newDataPos=0; index=0;
    345 
    346         // write out the new byte array replacing all the indefinite-lengths
    347         // and EOCs
    348         while (dataPos < dataSize) {
    349            writeTag();
    350            writeLengthAndValue();
    351         }
    352         System.arraycopy(indefData, dataSize,
    353                          newData, dataSize + numOfTotalLenBytes, unused);
    354 
    355         return newData;
    356     }
    357 }
    358