Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (c) 1998, 2008, 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             if ((dataSize - dataPos) < (lenByte + 1))
    162                 throw new IOException("Too little data");
    163             for (int i = 0; i < lenByte; i++)
    164                 curLen = (curLen << 8) + (data[dataPos++] & 0xff);
    165         } else {
    166            curLen = (lenByte & LEN_MASK);
    167         }
    168         return curLen;
    169     }
    170 
    171     /**
    172      * Write the length and if it is an indefinite length
    173      * then calculate the definite length from the positions
    174      * of the indefinite length and its matching EOC terminator.
    175      * Then, write the value.
    176      */
    177     private void writeLengthAndValue() throws IOException {
    178         if (dataPos == dataSize)
    179            return;
    180         int curLen = 0;
    181         int lenByte = data[dataPos++] & 0xff;
    182         if (isIndefinite(lenByte)) {
    183             byte[] lenBytes = (byte[])ndefsList.get(index++);
    184             System.arraycopy(lenBytes, 0, newData, newDataPos,
    185                              lenBytes.length);
    186             newDataPos += lenBytes.length;
    187             return;
    188         }
    189         if (isLongForm(lenByte)) {
    190             lenByte &= LEN_MASK;
    191             for (int i = 0; i < lenByte; i++)
    192                 curLen = (curLen << 8) + (data[dataPos++] & 0xff);
    193         } else
    194             curLen = (lenByte & LEN_MASK);
    195         writeLength(curLen);
    196         writeValue(curLen);
    197     }
    198 
    199     private void writeLength(int curLen) {
    200         if (curLen < 128) {
    201             newData[newDataPos++] = (byte)curLen;
    202 
    203         } else if (curLen < (1 << 8)) {
    204             newData[newDataPos++] = (byte)0x81;
    205             newData[newDataPos++] = (byte)curLen;
    206 
    207         } else if (curLen < (1 << 16)) {
    208             newData[newDataPos++] = (byte)0x82;
    209             newData[newDataPos++] = (byte)(curLen >> 8);
    210             newData[newDataPos++] = (byte)curLen;
    211 
    212         } else if (curLen < (1 << 24)) {
    213             newData[newDataPos++] = (byte)0x83;
    214             newData[newDataPos++] = (byte)(curLen >> 16);
    215             newData[newDataPos++] = (byte)(curLen >> 8);
    216             newData[newDataPos++] = (byte)curLen;
    217 
    218         } else {
    219             newData[newDataPos++] = (byte)0x84;
    220             newData[newDataPos++] = (byte)(curLen >> 24);
    221             newData[newDataPos++] = (byte)(curLen >> 16);
    222             newData[newDataPos++] = (byte)(curLen >> 8);
    223             newData[newDataPos++] = (byte)curLen;
    224         }
    225     }
    226 
    227     private byte[] getLengthBytes(int curLen) {
    228         byte[] lenBytes;
    229         int index = 0;
    230 
    231         if (curLen < 128) {
    232             lenBytes = new byte[1];
    233             lenBytes[index++] = (byte)curLen;
    234 
    235         } else if (curLen < (1 << 8)) {
    236             lenBytes = new byte[2];
    237             lenBytes[index++] = (byte)0x81;
    238             lenBytes[index++] = (byte)curLen;
    239 
    240         } else if (curLen < (1 << 16)) {
    241             lenBytes = new byte[3];
    242             lenBytes[index++] = (byte)0x82;
    243             lenBytes[index++] = (byte)(curLen >> 8);
    244             lenBytes[index++] = (byte)curLen;
    245 
    246         } else if (curLen < (1 << 24)) {
    247             lenBytes = new byte[4];
    248             lenBytes[index++] = (byte)0x83;
    249             lenBytes[index++] = (byte)(curLen >> 16);
    250             lenBytes[index++] = (byte)(curLen >> 8);
    251             lenBytes[index++] = (byte)curLen;
    252 
    253         } else {
    254             lenBytes = new byte[5];
    255             lenBytes[index++] = (byte)0x84;
    256             lenBytes[index++] = (byte)(curLen >> 24);
    257             lenBytes[index++] = (byte)(curLen >> 16);
    258             lenBytes[index++] = (byte)(curLen >> 8);
    259             lenBytes[index++] = (byte)curLen;
    260         }
    261 
    262         return lenBytes;
    263     }
    264 
    265     // Returns the number of bytes needed to represent the given length
    266     // in ASN.1 notation
    267     private int getNumOfLenBytes(int len) {
    268         int numOfLenBytes = 0;
    269 
    270         if (len < 128) {
    271             numOfLenBytes = 1;
    272         } else if (len < (1 << 8)) {
    273             numOfLenBytes = 2;
    274         } else if (len < (1 << 16)) {
    275             numOfLenBytes = 3;
    276         } else if (len < (1 << 24)) {
    277             numOfLenBytes = 4;
    278         } else {
    279             numOfLenBytes = 5;
    280         }
    281         return numOfLenBytes;
    282     }
    283 
    284     /**
    285      * Parse the value;
    286      */
    287     private void parseValue(int curLen) {
    288         dataPos += curLen;
    289     }
    290 
    291     /**
    292      * Write the value;
    293      */
    294     private void writeValue(int curLen) {
    295         for (int i=0; i < curLen; i++)
    296             newData[newDataPos++] = data[dataPos++];
    297     }
    298 
    299     /**
    300      * Converts a indefinite length DER encoded byte array to
    301      * a definte length DER encoding.
    302      *
    303      * @param indefData the byte array holding the indefinite
    304      *        length encoding.
    305      * @return the byte array containing the definite length
    306      *         DER encoding.
    307      * @exception IOException on parsing or re-writing errors.
    308      */
    309     byte[] convert(byte[] indefData) throws IOException {
    310         data = indefData;
    311         dataPos=0; index=0;
    312         dataSize = data.length;
    313         int len=0;
    314         int unused = 0;
    315 
    316         // parse and set up the vectors of all the indefinite-lengths
    317         while (dataPos < dataSize) {
    318             parseTag();
    319             len = parseLength();
    320             parseValue(len);
    321             if (unresolved == 0) {
    322                 unused = dataSize - dataPos;
    323                 dataSize = dataPos;
    324                 break;
    325             }
    326         }
    327 
    328         if (unresolved != 0) {
    329             throw new IOException("not all indef len BER resolved");
    330         }
    331 
    332         newData = new byte[dataSize + numOfTotalLenBytes + unused];
    333         dataPos=0; newDataPos=0; index=0;
    334 
    335         // write out the new byte array replacing all the indefinite-lengths
    336         // and EOCs
    337         while (dataPos < dataSize) {
    338            writeTag();
    339            writeLengthAndValue();
    340         }
    341         System.arraycopy(indefData, dataSize,
    342                          newData, dataSize + numOfTotalLenBytes, unused);
    343 
    344         return newData;
    345     }
    346 }
    347