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
17package org.conscrypt.ct;
18
19import junit.framework.TestCase;
20import java.io.ByteArrayOutputStream;
21import java.util.Arrays;
22
23public class SerializationTest extends TestCase {
24    public void test_decode_SignedCertificateTimestamp() throws Exception {
25        byte[] in = new byte[] {
26            0x00,                            // version
27            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // log id
28            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
29            0x01, 0x02, 0x03, 0x04,          // timestamp
30            0x05, 0x06, 0x07, 0x08,
31            0x00, 0x00,                      // extensions length
32            0x04, 0x03,                      // hash & signature algorithm
33            0x00, 0x04,                      // signature length
34            0x12, 0x34, 0x56, 0x78           // signature
35        };
36
37        SignedCertificateTimestamp sct
38            = SignedCertificateTimestamp.decode(in, SignedCertificateTimestamp.Origin.EMBEDDED);
39
40        assertEquals(SignedCertificateTimestamp.Version.V1, sct.getVersion());
41        assertEquals(0x0102030405060708L, sct.getTimestamp());
42        assertEquals(0, sct.getExtensions().length);
43        assertEquals(DigitallySigned.HashAlgorithm.SHA256,
44                     sct.getSignature().getHashAlgorithm());
45        assertEquals(DigitallySigned.SignatureAlgorithm.ECDSA,
46                     sct.getSignature().getSignatureAlgorithm());
47        assertTrue(Arrays.equals(new byte[] { 0x12, 0x34, 0x56, 0x78},
48                     sct.getSignature().getSignature()));
49        assertEquals(SignedCertificateTimestamp.Origin.EMBEDDED, sct.getOrigin());
50    }
51
52    public void test_decode_invalid_SignedCertificateTimestamp() throws Exception {
53        byte[] sct = new byte[] {
54            0x00,                            // version
55            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // log id
56            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
57            0x01, 0x02, 0x03, 0x04,          // timestamp
58            0x05, 0x06, 0x07, 0x08,
59            0x00, 0x00,                      // extensions length
60            0x04, 0x03,                      // hash & signature algorithm
61            0x00, 0x04,                      // signature length
62            0x12, 0x34, 0x56, 0x78           // signature
63        };
64
65        // Make sure the original decodes fine
66        SignedCertificateTimestamp.decode(sct, SignedCertificateTimestamp.Origin.EMBEDDED);
67
68        // Perform various modification to it, and make sure it throws an exception on decoding
69        try {
70            byte[] in = sct.clone();
71            in[0] = 1; // Modify version field
72            SignedCertificateTimestamp.decode(in, SignedCertificateTimestamp.Origin.EMBEDDED);
73            fail("SerializationException not thrown on unsupported version");
74        } catch (SerializationException e) {}
75
76        try {
77            byte[] in = sct.clone();
78            in[41] = 1; // Modify extensions lemgth
79            SignedCertificateTimestamp.decode(in, SignedCertificateTimestamp.Origin.EMBEDDED);
80            fail("SerializationException not thrown on invalid extensions length");
81        } catch (SerializationException e) {}
82    }
83
84    public void test_decode_DigitallySigned() throws Exception {
85        byte[] in = new byte[] {
86            0x04, 0x03,            // hash & signature algorithm
87            0x00, 0x04,            // signature length
88            0x12, 0x34, 0x56, 0x78 // signature
89        };
90
91        DigitallySigned dst = DigitallySigned.decode(in);
92        assertEquals(DigitallySigned.HashAlgorithm.SHA256, dst.getHashAlgorithm());
93        assertEquals(DigitallySigned.SignatureAlgorithm.ECDSA, dst.getSignatureAlgorithm());
94        assertEqualByteArrays(new byte[] { 0x12, 0x34, 0x56, 0x78}, dst.getSignature());
95    }
96
97    public void test_decode_invalid_DigitallySigned() throws Exception {
98        try {
99            DigitallySigned.decode(new byte[] {
100                0x07, 0x03,            // hash & signature algorithm
101                0x00, 0x04,            // signature length
102                0x12, 0x34, 0x56, 0x78 // signature
103            });
104            fail("SerializationException not thrown on invalid hash type");
105        } catch (SerializationException e) {}
106
107        try {
108            DigitallySigned.decode(new byte[] {
109                0x04, 0x04,            // hash & signature algorithm
110                0x00, 0x04,            // signature length
111                0x12, 0x34, 0x56, 0x78 // signature
112            });
113            fail("SerializationException not thrown on invalid signature type");
114        } catch (SerializationException e) {}
115
116        try {
117            DigitallySigned.decode(new byte[] {
118                0x07, 0x03,            // hash & signature algorithm
119                0x64, 0x35,            // signature length
120                0x12, 0x34, 0x56, 0x78 // signature
121            });
122            fail("SerializationException not thrown on invalid signature length");
123        } catch (SerializationException e) {}
124
125        try {
126            DigitallySigned.decode(new byte[] {
127                0x07, 0x03,            // hash & signature algorithm
128            });
129            fail("SerializationException not thrown on missing signature");
130        } catch (SerializationException e) {}
131    }
132
133    public void test_encode_CertificateEntry_X509Certificate() throws Exception {
134        // Use a dummy certificate. It doesn't matter, CertificateEntry doesn't care about the contents.
135        CertificateEntry entry = CertificateEntry.createForX509Certificate(new byte[] { 0x12, 0x34, 0x56, 0x78 });
136        ByteArrayOutputStream output = new ByteArrayOutputStream();
137        entry.encode(output);
138
139        assertEqualByteArrays(new byte[] {
140            0x00, 0x00,            // entry_type
141            0x00, 0x00, 0x04,      // x509_entry length
142            0x12, 0x34, 0x56, 0x78 // x509_entry
143        }, output.toByteArray());
144    }
145
146    public void test_encode_CertificateEntry_PreCertificate() throws Exception {
147        // Use a dummy certificate and issuer key hash. It doesn't matter,
148        // CertificateEntry doesn't care about the contents.
149        CertificateEntry entry = CertificateEntry.createForPrecertificate(new byte[] { 0x12, 0x34, 0x56, 0x78 },
150                                                          new byte[32]);
151
152        ByteArrayOutputStream output = new ByteArrayOutputStream();
153        entry.encode(output);
154
155        assertEqualByteArrays(new byte[] {
156            0x00, 0x01,                      // entry_type
157            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // issuer key hash
158            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
159            0x00, 0x00, 0x04,                // precert_entry length
160            0x12, 0x34, 0x56, 0x78           // precert_entry
161        }, output.toByteArray());
162    }
163
164    public void test_readDEROctetString() throws Exception {
165        byte[] in, expected;
166
167        in = new byte[] {
168            0x04, // TAG
169            0x06, // length
170            0x01, 0x02, 0x03, 0x04, 0x05, 0x05 // data
171        };
172        expected = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x05 };
173        assertEqualByteArrays(expected, Serialization.readDEROctetString(in));
174
175        in = new byte[203];
176        in[0] = 0x04; // TAG
177        in[1] = (byte)0x81; // long length flag
178        in[2] = (byte)200; // length
179        in[3] = 0x45; // data, the rest is just zeros
180
181        expected = new byte[200];
182        expected[0] = 0x45;
183        assertEqualByteArrays(expected, Serialization.readDEROctetString(in));
184
185        try {
186            in = new byte[] {
187                0x12, // wrong tag
188                0x06, // length
189                0x01, 0x02, 0x03, 0x04, 0x05, 0x05 // data
190            };
191            Serialization.readDEROctetString(in);
192            fail("SerializationException not thrown on invalid tag.");
193        } catch (SerializationException e) {}
194
195        try {
196            in = new byte[] {
197                0x04, // wrong tag
198                0x06, // length
199                0x01, 0x02 // data
200            };
201            Serialization.readDEROctetString(in);
202            fail("SerializationException not thrown on invalid length.");
203        } catch (SerializationException e) {}
204    }
205
206    public static void assertEqualByteArrays(byte[] expected, byte[] actual) {
207        assertEquals(Arrays.toString(expected), Arrays.toString(actual));
208    }
209}
210
211