/src/mozilla-central/security/certverifier/tests/gtest/CTSerializationTest.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "CTSerialization.h" |
8 | | #include "CTTestUtils.h" |
9 | | #include "gtest/gtest.h" |
10 | | #include "mozilla/Move.h" |
11 | | |
12 | | namespace mozilla { namespace ct { |
13 | | |
14 | | using namespace pkix; |
15 | | |
16 | | class CTSerializationTest : public ::testing::Test |
17 | | { |
18 | | public: |
19 | | void SetUp() override |
20 | 0 | { |
21 | 0 | mTestDigitallySigned = GetTestDigitallySigned(); |
22 | 0 | mTestSignatureData = GetTestDigitallySignedData(); |
23 | 0 | } |
24 | | |
25 | | protected: |
26 | | Buffer mTestDigitallySigned; |
27 | | Buffer mTestSignatureData; |
28 | | }; |
29 | | |
30 | | TEST_F(CTSerializationTest, DecodesDigitallySigned) |
31 | 0 | { |
32 | 0 | Input digitallySigned = InputForBuffer(mTestDigitallySigned); |
33 | 0 | Reader digitallySignedReader(digitallySigned); |
34 | 0 |
|
35 | 0 | DigitallySigned parsed; |
36 | 0 | ASSERT_EQ(Success, |
37 | 0 | DecodeDigitallySigned(digitallySignedReader, parsed)); |
38 | 0 | EXPECT_TRUE(digitallySignedReader.AtEnd()); |
39 | 0 |
|
40 | 0 | EXPECT_EQ(DigitallySigned::HashAlgorithm::SHA256, |
41 | 0 | parsed.hashAlgorithm); |
42 | 0 | EXPECT_EQ(DigitallySigned::SignatureAlgorithm::ECDSA, |
43 | 0 | parsed.signatureAlgorithm); |
44 | 0 | EXPECT_EQ(mTestSignatureData, parsed.signatureData); |
45 | 0 | } |
46 | | |
47 | | TEST_F(CTSerializationTest, FailsToDecodePartialDigitallySigned) |
48 | 0 | { |
49 | 0 | Input partial; |
50 | 0 | ASSERT_EQ(Success, |
51 | 0 | partial.Init(mTestDigitallySigned.begin(), |
52 | 0 | mTestDigitallySigned.length() - 5)); |
53 | 0 | Reader partialReader(partial); |
54 | 0 |
|
55 | 0 | DigitallySigned parsed; |
56 | 0 |
|
57 | 0 | EXPECT_NE(Success, DecodeDigitallySigned(partialReader, parsed)); |
58 | 0 | } |
59 | | |
60 | | TEST_F(CTSerializationTest, EncodesDigitallySigned) |
61 | 0 | { |
62 | 0 | DigitallySigned digitallySigned; |
63 | 0 | digitallySigned.hashAlgorithm = |
64 | 0 | DigitallySigned::HashAlgorithm::SHA256; |
65 | 0 | digitallySigned.signatureAlgorithm = |
66 | 0 | DigitallySigned::SignatureAlgorithm::ECDSA; |
67 | 0 | digitallySigned.signatureData = cloneBuffer(mTestSignatureData); |
68 | 0 |
|
69 | 0 | Buffer encoded; |
70 | 0 |
|
71 | 0 | ASSERT_EQ(Success, EncodeDigitallySigned(digitallySigned, encoded)); |
72 | 0 | EXPECT_EQ(mTestDigitallySigned, encoded); |
73 | 0 | } |
74 | | |
75 | | TEST_F(CTSerializationTest, EncodesLogEntryForX509Cert) |
76 | 0 | { |
77 | 0 | LogEntry entry; |
78 | 0 | GetX509CertLogEntry(entry); |
79 | 0 |
|
80 | 0 | Buffer encoded; |
81 | 0 | ASSERT_EQ(Success, EncodeLogEntry(entry, encoded)); |
82 | 0 | EXPECT_EQ((718U + 5U), encoded.length()); |
83 | 0 | // First two bytes are log entry type. Next, length: |
84 | 0 | // Length is 718 which is 512 + 206, which is 0x2ce |
85 | 0 | Buffer expectedPrefix; |
86 | 0 | MOZ_RELEASE_ASSERT(expectedPrefix.append("\0\0\0\x2\xCE", 5)); |
87 | 0 | Buffer encodedPrefix; |
88 | 0 | MOZ_RELEASE_ASSERT(encodedPrefix. |
89 | 0 | append(encoded.begin(), encoded.begin() + 5)); |
90 | 0 | EXPECT_EQ(expectedPrefix, encodedPrefix); |
91 | 0 | } |
92 | | |
93 | | TEST_F(CTSerializationTest, EncodesLogEntryForPrecert) |
94 | 0 | { |
95 | 0 | LogEntry entry; |
96 | 0 | GetPrecertLogEntry(entry); |
97 | 0 |
|
98 | 0 | Buffer encoded; |
99 | 0 | ASSERT_EQ(Success, EncodeLogEntry(entry, encoded)); |
100 | 0 | // log entry type + issuer key + length + tbsCertificate |
101 | 0 | EXPECT_EQ((2U + 32U + 3U + entry.tbsCertificate.length()), encoded.length()); |
102 | 0 |
|
103 | 0 | // First two bytes are log entry type. |
104 | 0 | Buffer expectedPrefix; |
105 | 0 | MOZ_RELEASE_ASSERT(expectedPrefix.append("\0\x1", 2)); |
106 | 0 | Buffer encodedPrefix; |
107 | 0 | MOZ_RELEASE_ASSERT(encodedPrefix. |
108 | 0 | append(encoded.begin(), encoded.begin() + 2)); |
109 | 0 | EXPECT_EQ(expectedPrefix, encodedPrefix); |
110 | 0 |
|
111 | 0 | // Next is the issuer key (32 bytes). |
112 | 0 | Buffer encodedKeyHash; |
113 | 0 | MOZ_RELEASE_ASSERT(encodedKeyHash. |
114 | 0 | append(encoded.begin() + 2, encoded.begin() + 2 + 32)); |
115 | 0 | EXPECT_EQ(GetDefaultIssuerKeyHash(), encodedKeyHash); |
116 | 0 | } |
117 | | |
118 | | TEST_F(CTSerializationTest, EncodesV1SCTSignedData) |
119 | 0 | { |
120 | 0 | uint64_t timestamp = UINT64_C(0x139fe353cf5); |
121 | 0 | const uint8_t DUMMY_BYTES[] = { 0x61, 0x62, 0x63 }; // abc |
122 | 0 | Input dummyEntry(DUMMY_BYTES); |
123 | 0 | Input emptyExtensions; |
124 | 0 | Buffer encoded; |
125 | 0 | ASSERT_EQ(Success, EncodeV1SCTSignedData( |
126 | 0 | timestamp, dummyEntry, emptyExtensions, encoded)); |
127 | 0 | EXPECT_EQ((size_t) 15, encoded.length()); |
128 | 0 |
|
129 | 0 | const uint8_t EXPECTED_BYTES[] = { |
130 | 0 | 0x00, // version |
131 | 0 | 0x00, // signature type |
132 | 0 | 0x00, 0x00, 0x01, 0x39, 0xFE, 0x35, 0x3C, 0xF5, // timestamp |
133 | 0 | 0x61, 0x62, 0x63, // log signature |
134 | 0 | 0x00, 0x00 // extensions (empty) |
135 | 0 | }; |
136 | 0 | Buffer expectedBuffer; |
137 | 0 | MOZ_RELEASE_ASSERT( |
138 | 0 | expectedBuffer.append(EXPECTED_BYTES, sizeof(EXPECTED_BYTES))); |
139 | 0 | EXPECT_EQ(expectedBuffer, encoded); |
140 | 0 | } |
141 | | |
142 | | TEST_F(CTSerializationTest, DecodesSCTList) |
143 | 0 | { |
144 | 0 | // Two items in the list: "abc", "def" |
145 | 0 | const uint8_t ENCODED[] = { |
146 | 0 | 0x00, 0x0a, 0x00, 0x03, 0x61, 0x62, 0x63, 0x00, 0x03, 0x64, 0x65, 0x66 |
147 | 0 | }; |
148 | 0 | const uint8_t DECODED_1[] = { 0x61, 0x62, 0x63 }; |
149 | 0 | const uint8_t DECODED_2[] = { 0x64, 0x65, 0x66 }; |
150 | 0 |
|
151 | 0 | Reader listReader; |
152 | 0 | ASSERT_EQ(Success, DecodeSCTList(Input(ENCODED), listReader)); |
153 | 0 |
|
154 | 0 | Input decoded1; |
155 | 0 | ASSERT_EQ(Success, ReadSCTListItem(listReader, decoded1)); |
156 | 0 |
|
157 | 0 | Input decoded2; |
158 | 0 | ASSERT_EQ(Success, ReadSCTListItem(listReader, decoded2)); |
159 | 0 |
|
160 | 0 | EXPECT_TRUE(listReader.AtEnd()); |
161 | 0 | EXPECT_TRUE(InputsAreEqual(decoded1, Input(DECODED_1))); |
162 | 0 | EXPECT_TRUE(InputsAreEqual(decoded2, Input(DECODED_2))); |
163 | 0 | } |
164 | | |
165 | | TEST_F(CTSerializationTest, FailsDecodingInvalidSCTList) |
166 | 0 | { |
167 | 0 | // A list with one item that's too short (the second one) |
168 | 0 | const uint8_t ENCODED[] = { |
169 | 0 | 0x00, 0x0a, 0x00, 0x03, 0x61, 0x62, 0x63, 0x00, 0x05, 0x64, 0x65, 0x66 |
170 | 0 | }; |
171 | 0 |
|
172 | 0 | Reader listReader; |
173 | 0 | ASSERT_EQ(Success, DecodeSCTList(Input(ENCODED), listReader)); |
174 | 0 | Input decoded1; |
175 | 0 | EXPECT_EQ(Success, ReadSCTListItem(listReader, decoded1)); |
176 | 0 | Input decoded2; |
177 | 0 | EXPECT_NE(Success, ReadSCTListItem(listReader, decoded2)); |
178 | 0 | } |
179 | | |
180 | | TEST_F(CTSerializationTest, EncodesSCTList) |
181 | 0 | { |
182 | 0 | const uint8_t SCT_1[] = { 0x61, 0x62, 0x63 }; |
183 | 0 | const uint8_t SCT_2[] = { 0x64, 0x65, 0x66 }; |
184 | 0 |
|
185 | 0 | Vector<Input> list; |
186 | 0 | ASSERT_TRUE(list.append(Input(SCT_1))); |
187 | 0 | ASSERT_TRUE(list.append(Input(SCT_2))); |
188 | 0 |
|
189 | 0 | Buffer encodedList; |
190 | 0 | ASSERT_EQ(Success, EncodeSCTList(list, encodedList)); |
191 | 0 |
|
192 | 0 | Reader listReader; |
193 | 0 | ASSERT_EQ(Success, DecodeSCTList(InputForBuffer(encodedList), listReader)); |
194 | 0 |
|
195 | 0 | Input decoded1; |
196 | 0 | ASSERT_EQ(Success, ReadSCTListItem(listReader, decoded1)); |
197 | 0 | EXPECT_TRUE(InputsAreEqual(decoded1, Input(SCT_1))); |
198 | 0 |
|
199 | 0 | Input decoded2; |
200 | 0 | ASSERT_EQ(Success, ReadSCTListItem(listReader, decoded2)); |
201 | 0 | EXPECT_TRUE(InputsAreEqual(decoded2, Input(SCT_2))); |
202 | 0 |
|
203 | 0 | EXPECT_TRUE(listReader.AtEnd()); |
204 | 0 | } |
205 | | |
206 | | TEST_F(CTSerializationTest, DecodesSignedCertificateTimestamp) |
207 | 0 | { |
208 | 0 | Buffer encodedSctBuffer = GetTestSignedCertificateTimestamp(); |
209 | 0 | Input encodedSctInput = InputForBuffer(encodedSctBuffer); |
210 | 0 | Reader encodedSctReader(encodedSctInput); |
211 | 0 |
|
212 | 0 | SignedCertificateTimestamp sct; |
213 | 0 | ASSERT_EQ(Success, |
214 | 0 | DecodeSignedCertificateTimestamp(encodedSctReader, sct)); |
215 | 0 | EXPECT_EQ(SignedCertificateTimestamp::Version::V1, sct.version); |
216 | 0 | EXPECT_EQ(GetTestPublicKeyId(), sct.logId); |
217 | 0 | const uint64_t expectedTime = 1365181456089; |
218 | 0 | EXPECT_EQ(expectedTime, sct.timestamp); |
219 | 0 | const size_t expectedSignatureLength = 71; |
220 | 0 | EXPECT_EQ(expectedSignatureLength, sct.signature.signatureData.length()); |
221 | 0 | EXPECT_TRUE(sct.extensions.empty()); |
222 | 0 | } |
223 | | |
224 | | TEST_F(CTSerializationTest, FailsDecodingInvalidSignedCertificateTimestamp) |
225 | 0 | { |
226 | 0 | SignedCertificateTimestamp sct; |
227 | 0 |
|
228 | 0 | // Invalid version |
229 | 0 | const uint8_t INVALID_VERSION_BYTES[] = { 0x02, 0x00 }; |
230 | 0 | Input invalidVersionSctInput(INVALID_VERSION_BYTES); |
231 | 0 | Reader invalidVersionSctReader(invalidVersionSctInput); |
232 | 0 | EXPECT_EQ(pkix::Result::ERROR_BAD_DER, |
233 | 0 | DecodeSignedCertificateTimestamp(invalidVersionSctReader, sct)); |
234 | 0 |
|
235 | 0 | // Valid version, invalid length (missing data) |
236 | 0 | const uint8_t INVALID_LENGTH_BYTES[] = { 0x00, 0x0a, 0x0b, 0x0c }; |
237 | 0 | Input invalidLengthSctInput(INVALID_LENGTH_BYTES); |
238 | 0 | Reader invalidLengthSctReader(invalidLengthSctInput); |
239 | 0 | EXPECT_EQ(pkix::Result::ERROR_BAD_DER, |
240 | 0 | DecodeSignedCertificateTimestamp(invalidLengthSctReader, sct)); |
241 | 0 | } |
242 | | |
243 | | TEST_F(CTSerializationTest, EncodesValidSignedTreeHead) |
244 | 0 | { |
245 | 0 | SignedTreeHead signedTreeHead; |
246 | 0 | GetSampleSignedTreeHead(signedTreeHead); |
247 | 0 |
|
248 | 0 | Buffer encoded; |
249 | 0 | ASSERT_EQ(Success, |
250 | 0 | EncodeTreeHeadSignature(signedTreeHead, encoded)); |
251 | 0 | // Expected size is 50 bytes: |
252 | 0 | // Byte 0 is version, byte 1 is signature type |
253 | 0 | // Bytes 2-9 are timestamp |
254 | 0 | // Bytes 10-17 are tree size |
255 | 0 | // Bytes 18-49 are sha256 root hash |
256 | 0 | ASSERT_EQ(50u, encoded.length()); |
257 | 0 | const uint8_t EXPECTED_BYTES_PREFIX[] = { |
258 | 0 | 0x00, // version |
259 | 0 | 0x01, // signature type |
260 | 0 | 0x00, 0x00, 0x01, 0x45, 0x3c, 0x5f, 0xb8, 0x35, // timestamp |
261 | 0 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15 // tree size |
262 | 0 | // sha256 root hash should follow |
263 | 0 | }; |
264 | 0 | Buffer expectedBuffer; |
265 | 0 | MOZ_RELEASE_ASSERT(expectedBuffer.append(EXPECTED_BYTES_PREFIX, 18)); |
266 | 0 | Buffer hash = GetSampleSTHSHA256RootHash(); |
267 | 0 | MOZ_RELEASE_ASSERT(expectedBuffer.append(hash.begin(), hash.length())); |
268 | 0 | EXPECT_EQ(expectedBuffer, encoded); |
269 | 0 | } |
270 | | } } // namespace mozilla::ct |