Path: blob/master/src/java.base/share/classes/sun/security/util/DerIndefLenConverter.java
67773 views
/*1* Copyright (c) 1998, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.security.util;2627import java.io.IOException;28import java.io.InputStream;29import java.util.ArrayList;30import java.util.Arrays;3132/**33* A package private utility class to convert indefinite length BER34* encoded byte arrays to definite length DER encoded byte arrays.35* <p>36* Note: This class only substitute indefinite length octets to definite37* length octets. It does not update the contents even if they are not DER.38* <p>39* This assumes that the basic data structure is "tag, length, value"40* triplet. In the case where the length is "indefinite", terminating41* end-of-contents bytes are expected.42*43* @author Hemma Prafullchandra44*/45class DerIndefLenConverter {4647private static final int LEN_LONG = 0x80; // bit 8 set48private static final int LEN_MASK = 0x7f; // bits 7 - 14950private byte[] data, newData;51private int newDataPos, dataPos, dataSize, index;52private int unresolved = 0;5354// A list to store each indefinite length occurrence. Whenever an indef55// length is seen, the position after the 0x80 byte is appended to the56// list as an integer. Whenever its matching EOC is seen, we know the57// actual length and the position value is substituted with a calculated58// length octets. At the end, the new DER encoding is a concatenation of59// all existing tags, existing definite length octets, existing contents,60// and the newly created definte length octets in this list.61private ArrayList<Object> ndefsList = new ArrayList<Object>();6263// Length of extra bytes needed to convert indefinite encoding to definite.64// For each resolved indefinite length encoding, the starting 0x80 byte65// and the ending 00 00 bytes will be removed and a new definite length66// octets will be added. This value might be positive or negative.67private int numOfTotalLenBytes = 0;6869private static boolean isEOC(byte[] data, int pos) {70return data[pos] == 0 && data[pos + 1] == 0;71}7273// if bit 8 is set then it implies either indefinite length or long form74static boolean isLongForm(int lengthByte) {75return ((lengthByte & LEN_LONG) == LEN_LONG);76}7778/*79* Private constructor80*/81private DerIndefLenConverter() { }8283/**84* Checks whether the given length byte is of the form85* <em>Indefinite</em>.86*87* @param lengthByte the length byte from a DER encoded88* object.89* @return true if the byte is of Indefinite form otherwise90* returns false.91*/92static boolean isIndefinite(int lengthByte) {93return (isLongForm(lengthByte) && ((lengthByte & LEN_MASK) == 0));94}9596/**97* Consumes the tag at {@code dataPos}.98* <p>99* If it is EOC then replace the matching start position (i.e. the previous100* {@code dataPos} where an indefinite length was found by #parseLength)101* in {@code ndefsList} with a length octets for this section.102*/103private void parseTag() throws IOException {104if (isEOC(data, dataPos)) {105int numOfEncapsulatedLenBytes = 0;106Object elem = null;107int index;108for (index = ndefsList.size()-1; index >= 0; index--) {109// Determine the first element in the vector that does not110// have a matching EOC111elem = ndefsList.get(index);112if (elem instanceof Integer) {113break;114} else {115// For each existing converted part, 3 bytes (80 at the116// beginning and 00 00 at the end) are removed and a117// new length octets is added.118numOfEncapsulatedLenBytes += ((byte[])elem).length - 3;119}120}121if (index < 0) {122throw new IOException("EOC does not have matching " +123"indefinite-length tag");124}125int sectionLen = dataPos - ((Integer)elem).intValue() +126numOfEncapsulatedLenBytes;127byte[] sectionLenBytes = getLengthBytes(sectionLen);128ndefsList.set(index, sectionLenBytes);129assert unresolved > 0;130unresolved--;131132// Add the number of bytes required to represent this section133// to the total number of length bytes,134// and subtract the indefinite-length tag (1 byte) and135// EOC bytes (2 bytes) for this section136numOfTotalLenBytes += (sectionLenBytes.length - 3);137}138dataPos++;139}140141/**142* Write the tag and if it is an end-of-contents tag143* then skip the tag and its 1 byte length of zero.144*/145private void writeTag() {146while (dataPos < dataSize) {147assert dataPos + 1 < dataSize;148if (isEOC(data, dataPos)) {149dataPos += 2; // skip tag and length150} else {151newData[newDataPos++] = data[dataPos++];152break;153}154}155}156157/**158* Parse the length octets started at {@code dataPos}. After this method159* is called, {@code dataPos} is placed after the length octets except160* -1 is returned.161*162* @return a) the length of definite length data next163* b) -1, if it is a definite length data next but the length164* octets is not complete to determine the actual length165* c) 0, if it is an indefinite length. Also, append the current166* position to the {@code ndefsList} vector.167* @throws IOException if invalid data is read168*/169private int parseLength() throws IOException {170if (dataPos == dataSize) {171return 0;172}173int lenByte = data[dataPos++] & 0xff;174if (isIndefinite(lenByte)) {175ndefsList.add(dataPos);176unresolved++;177return 0;178}179int curLen = 0;180if (isLongForm(lenByte)) {181lenByte &= LEN_MASK;182if (lenByte > 4) {183throw new IOException("Too much data");184}185if ((dataSize - dataPos) < (lenByte + 1)) {186return -1;187}188for (int i = 0; i < lenByte; i++) {189curLen = (curLen << 8) + (data[dataPos++] & 0xff);190}191if (curLen < 0) {192throw new IOException("Invalid length bytes");193}194} else {195curLen = (lenByte & LEN_MASK);196}197return curLen;198}199200/**201* Write the length and value.202* <p>203* If it was definite length, just re-write the length and copy the value.204* If it was an indefinite length, copy the precalculated definite octets205* from {@code ndefsList}. There is no values here because they will be206* sub-encodings of a constructed encoding.207*/208private void writeLengthAndValue() throws IOException {209if (dataPos == dataSize) {210return;211}212int curLen = 0;213int lenByte = data[dataPos++] & 0xff;214if (isIndefinite(lenByte)) {215byte[] lenBytes = (byte[])ndefsList.get(index++);216System.arraycopy(lenBytes, 0, newData, newDataPos,217lenBytes.length);218newDataPos += lenBytes.length;219} else {220if (isLongForm(lenByte)) {221lenByte &= LEN_MASK;222for (int i = 0; i < lenByte; i++) {223curLen = (curLen << 8) + (data[dataPos++] & 0xff);224}225if (curLen < 0) {226throw new IOException("Invalid length bytes");227}228} else {229curLen = (lenByte & LEN_MASK);230}231writeLength(curLen);232writeValue(curLen);233}234}235236private void writeLength(int curLen) {237if (curLen < 128) {238newData[newDataPos++] = (byte)curLen;239240} else if (curLen < (1 << 8)) {241newData[newDataPos++] = (byte)0x81;242newData[newDataPos++] = (byte)curLen;243244} else if (curLen < (1 << 16)) {245newData[newDataPos++] = (byte)0x82;246newData[newDataPos++] = (byte)(curLen >> 8);247newData[newDataPos++] = (byte)curLen;248249} else if (curLen < (1 << 24)) {250newData[newDataPos++] = (byte)0x83;251newData[newDataPos++] = (byte)(curLen >> 16);252newData[newDataPos++] = (byte)(curLen >> 8);253newData[newDataPos++] = (byte)curLen;254255} else {256newData[newDataPos++] = (byte)0x84;257newData[newDataPos++] = (byte)(curLen >> 24);258newData[newDataPos++] = (byte)(curLen >> 16);259newData[newDataPos++] = (byte)(curLen >> 8);260newData[newDataPos++] = (byte)curLen;261}262}263264private byte[] getLengthBytes(int curLen) {265byte[] lenBytes;266int index = 0;267268if (curLen < 128) {269lenBytes = new byte[1];270lenBytes[index++] = (byte)curLen;271272} else if (curLen < (1 << 8)) {273lenBytes = new byte[2];274lenBytes[index++] = (byte)0x81;275lenBytes[index++] = (byte)curLen;276277} else if (curLen < (1 << 16)) {278lenBytes = new byte[3];279lenBytes[index++] = (byte)0x82;280lenBytes[index++] = (byte)(curLen >> 8);281lenBytes[index++] = (byte)curLen;282283} else if (curLen < (1 << 24)) {284lenBytes = new byte[4];285lenBytes[index++] = (byte)0x83;286lenBytes[index++] = (byte)(curLen >> 16);287lenBytes[index++] = (byte)(curLen >> 8);288lenBytes[index++] = (byte)curLen;289290} else {291lenBytes = new byte[5];292lenBytes[index++] = (byte)0x84;293lenBytes[index++] = (byte)(curLen >> 24);294lenBytes[index++] = (byte)(curLen >> 16);295lenBytes[index++] = (byte)(curLen >> 8);296lenBytes[index++] = (byte)curLen;297}298299return lenBytes;300}301302// Returns the number of bytes needed to represent the given length303// in ASN.1 notation304private int getNumOfLenBytes(int len) {305int numOfLenBytes = 0;306307if (len < 128) {308numOfLenBytes = 1;309} else if (len < (1 << 8)) {310numOfLenBytes = 2;311} else if (len < (1 << 16)) {312numOfLenBytes = 3;313} else if (len < (1 << 24)) {314numOfLenBytes = 4;315} else {316numOfLenBytes = 5;317}318return numOfLenBytes;319}320321/**322* Write the value;323*/324private void writeValue(int curLen) {325System.arraycopy(data, dataPos, newData, newDataPos, curLen);326dataPos += curLen;327newDataPos += curLen;328}329330/**331* Converts a indefinite length DER encoded byte array to332* a definte length DER encoding.333*334* @param indefData the byte array holding the indefinite335* length encoding.336* @return the byte array containing the definite length337* DER encoding, or null if there is not enough data.338* @exception IOException on parsing or re-writing errors.339*/340byte[] convertBytes(byte[] indefData) throws IOException {341data = indefData;342dataPos = 0;343dataSize = data.length;344345// parse and set up the vectors of all the indefinite-lengths346while (dataPos < dataSize) {347if (dataPos + 2 > dataSize) {348// There should be at least one tag and one length349return null;350}351parseTag();352int len = parseLength();353if (len < 0) {354return null;355}356dataPos += len;357if (dataPos < 0) {358// overflow359throw new IOException("Data overflow");360}361if (unresolved == 0) {362assert !ndefsList.isEmpty() && ndefsList.get(0) instanceof byte[];363break;364}365}366367if (unresolved != 0) {368return null;369}370371int unused = dataSize - dataPos;372assert unused >= 0;373dataSize = dataPos;374375newData = new byte[dataSize + numOfTotalLenBytes + unused];376dataPos = 0; newDataPos = 0; index = 0;377378// write out the new byte array replacing all the indefinite-lengths379// and EOCs380while (dataPos < dataSize) {381writeTag();382writeLengthAndValue();383}384System.arraycopy(indefData, dataSize,385newData, dataSize + numOfTotalLenBytes, unused);386387return newData;388}389390/**391* Read the input stream into a DER byte array. If an indef len BER is392* not resolved this method will try to read more data until EOF is reached.393* This may block.394*395* @param in the input stream with tag and lenByte already read396* @param tag the tag to remember397* @return a DER byte array398* @throws IOException if not all indef len BER399* can be resolved or another I/O error happens400*/401public static byte[] convertStream(InputStream in, byte tag)402throws IOException {403int offset = 2; // for tag and length bytes404int readLen = in.available();405byte[] indefData = new byte[readLen + offset];406indefData[0] = tag;407indefData[1] = (byte)0x80;408while (true) {409int bytesRead = in.readNBytes(indefData, offset, readLen);410if (bytesRead != readLen) {411readLen = bytesRead;412indefData = Arrays.copyOf(indefData, offset + bytesRead);413}414DerIndefLenConverter derIn = new DerIndefLenConverter();415byte[] result = derIn.convertBytes(indefData);416if (result == null) {417int next = in.read(); // This could block, but we need more418if (next == -1) {419throw new IOException("not enough data to resolve indef len BER");420}421int more = in.available();422// expand array to include next and more423indefData = Arrays.copyOf(indefData, offset + readLen + 1 + more);424indefData[offset + readLen] = (byte)next;425offset = offset + readLen + 1;426readLen = more;427} else {428return result;429}430}431}432}433434435