1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package org.conscrypt.ct; 18 19 import java.io.ByteArrayInputStream; 20 import java.io.IOException; 21 import java.io.InputStream; 22 import java.io.OutputStream; 23 import java.util.ArrayList; 24 import org.conscrypt.Internal; 25 26 @Internal 27 public class Serialization { Serialization()28 private Serialization() {} 29 30 private static final int DER_TAG_MASK = 0x3f; 31 private static final int DER_TAG_OCTET_STRING = 0x4; 32 private static final int DER_LENGTH_LONG_FORM_FLAG = 0x80; 33 readDEROctetString(byte[] input)34 public static byte[] readDEROctetString(byte[] input) 35 throws SerializationException { 36 return readDEROctetString(new ByteArrayInputStream(input)); 37 } 38 readDEROctetString(InputStream input)39 public static byte[] readDEROctetString(InputStream input) 40 throws SerializationException { 41 int tag = readByte(input) & DER_TAG_MASK; 42 if (tag != DER_TAG_OCTET_STRING) { 43 throw new SerializationException("Wrong DER tag, expected OCTET STRING, got " + tag); 44 } 45 int length; 46 int width = readNumber(input, 1); 47 if ((width & DER_LENGTH_LONG_FORM_FLAG) != 0) { 48 length = readNumber(input, width & ~DER_LENGTH_LONG_FORM_FLAG); 49 } else { 50 length = width; 51 } 52 53 return readFixedBytes(input, length); 54 } 55 readList(byte[] input, int listWidth, int elemWidth)56 public static byte[][] readList(byte[] input, int listWidth, int elemWidth) 57 throws SerializationException { 58 return readList(new ByteArrayInputStream(input), listWidth, elemWidth); 59 } 60 61 /** 62 * Read a variable length vector of variable sized elements as described by RFC5246 section 4.3. 63 * The vector is prefixed by its total length, in bytes and in big endian format, 64 * so is each element contained in the vector. 65 * @param listWidth the width of the vector's length field, in bytes. 66 * @param elemWidth the width of each element's length field, in bytes. 67 * @throws SerializationException if EOF is encountered. 68 */ readList(InputStream input, int listWidth, int elemWidth)69 public static byte[][] readList(InputStream input, int listWidth, int elemWidth) 70 throws SerializationException { 71 ArrayList<byte[]> result = new ArrayList<byte[]>(); 72 byte[] data = readVariableBytes(input, listWidth); 73 input = new ByteArrayInputStream(data); 74 try { 75 while (input.available() > 0) { 76 result.add(readVariableBytes(input, elemWidth)); 77 } 78 } catch (IOException e) { 79 throw new SerializationException(e); 80 } 81 return result.toArray(new byte[result.size()][]); 82 } 83 84 /** 85 * Read a length-prefixed sequence of bytes. 86 * The length must be encoded in big endian format. 87 * @param width the width of the length prefix, in bytes. 88 * @throws SerializationException if EOF is encountered, or if {@code width} is negative or 89 * greater than 4 90 */ readVariableBytes(InputStream input, int width)91 public static byte[] readVariableBytes(InputStream input, int width) 92 throws SerializationException { 93 int length = readNumber(input, width); 94 return readFixedBytes(input, length); 95 } 96 97 /** 98 * Read a fixed number of bytes from the input stream. 99 * @param length the number of bytes to read. 100 * @throws SerializationException if EOF is encountered. 101 */ readFixedBytes(InputStream input, int length)102 public static byte[] readFixedBytes(InputStream input, int length) 103 throws SerializationException { 104 try { 105 if (length < 0) { 106 throw new SerializationException("Negative length: " + length); 107 } 108 109 byte[] data = new byte[length]; 110 int count = input.read(data); 111 if (count < length) { 112 throw new SerializationException("Premature end of input, expected " + length + 113 " bytes, only read " + count); 114 } 115 return data; 116 } catch (IOException e) { 117 throw new SerializationException(e); 118 } 119 } 120 121 /** 122 * Read a number in big endian format from the input stream. 123 * This methods only supports a width of up to 4 bytes. 124 * @param width the width of the number, in bytes. 125 * @throws SerializationException if EOF is encountered, or if {@code width} is negative or 126 * greater than 4 127 */ readNumber(InputStream input, int width)128 public static int readNumber(InputStream input, int width) throws SerializationException { 129 if (width > 4 || width < 0) { 130 throw new SerializationException("Invalid width: " + width); 131 } 132 133 int result = 0; 134 for (int i = 0; i < width; i++) { 135 result = (result << 8) | (readByte(input) & 0xFF); 136 } 137 138 return result; 139 } 140 141 /** 142 * Read a number in big endian format from the input stream. 143 * This methods supports a width of up to 8 bytes. 144 * @param width the width of the number, in bytes. 145 * @throws SerializationException if EOF is encountered. 146 * @throws IllegalArgumentException if {@code width} is negative or greater than 8 147 */ readLong(InputStream input, int width)148 public static long readLong(InputStream input, int width) throws SerializationException { 149 if (width > 8 || width < 0) { 150 throw new IllegalArgumentException("Invalid width: " + width); 151 } 152 153 long result = 0; 154 for (int i = 0; i < width; i++) { 155 result = (result << 8) | (readByte(input) & 0xFF); 156 } 157 158 return result; 159 } 160 161 /** 162 * Read a single byte from the input stream. 163 * @throws SerializationException if EOF is encountered. 164 */ readByte(InputStream input)165 public static byte readByte(InputStream input) throws SerializationException { 166 try { 167 int b = input.read(); 168 if (b == -1) { 169 throw new SerializationException("Premature end of input, could not read byte."); 170 } 171 return (byte)b; 172 } catch (IOException e) { 173 throw new SerializationException(e); 174 } 175 } 176 177 /** 178 * Write length prefixed sequence of bytes to the ouput stream. 179 * The length prefix is encoded in big endian order. 180 * @param data the data to be written. 181 * @param width the width of the length prefix, in bytes. 182 * @throws SerializationException if the length of {@code data} is too large to fit in 183 * {@code width} bytes or {@code width} is negative. 184 */ writeVariableBytes(OutputStream output, byte[] data, int width)185 public static void writeVariableBytes(OutputStream output, byte[] data, int width) 186 throws SerializationException { 187 writeNumber(output, data.length, width); 188 writeFixedBytes(output, data); 189 } 190 191 /** 192 * Write a fixed number sequence of bytes to the ouput stream. 193 * @param data the data to be written. 194 */ writeFixedBytes(OutputStream output, byte[] data)195 public static void writeFixedBytes(OutputStream output, byte[] data) 196 throws SerializationException { 197 try { 198 output.write(data); 199 } catch (IOException e) { 200 throw new SerializationException(e); 201 } 202 } 203 204 /** 205 * Write a number to the output stream. 206 * The number is encoded in big endian order. 207 * @param value the value to be written. 208 * @param width the width of the encoded number, in bytes 209 * @throws SerializationException if the number is too large to fit in {@code width} bytes or 210 * {@code width} is negative. 211 */ writeNumber(OutputStream output, long value, int width)212 public static void writeNumber(OutputStream output, long value, int width) 213 throws SerializationException { 214 if (width < 0) { 215 throw new SerializationException("Negative width: " + width); 216 } 217 if (width < 8 && value >= (1L << (8 * width))) { 218 throw new SerializationException( 219 "Number too large, " + value + " does not fit in " + width + " bytes"); 220 } 221 222 try { 223 while (width > 0) { 224 long shift = (width - 1) * 8L; 225 // Java behaves weirdly if shifting by more than the variable's size 226 if (shift < Long.SIZE) { 227 output.write((byte) ((value >> shift) & 0xFF)); 228 } else { 229 output.write(0); 230 } 231 232 width--; 233 } 234 } catch (IOException e) { 235 throw new SerializationException(e); 236 } 237 } 238 } 239 240