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 }