• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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