/src/mozilla-central/security/pkix/test/lib/pkixtestutil.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 code is made available to you under your choice of the following sets |
4 | | * of licensing terms: |
5 | | */ |
6 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
7 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
8 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
9 | | */ |
10 | | /* Copyright 2013 Mozilla Contributors |
11 | | * |
12 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
13 | | * you may not use this file except in compliance with the License. |
14 | | * You may obtain a copy of the License at |
15 | | * |
16 | | * http://www.apache.org/licenses/LICENSE-2.0 |
17 | | * |
18 | | * Unless required by applicable law or agreed to in writing, software |
19 | | * distributed under the License is distributed on an "AS IS" BASIS, |
20 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
21 | | * See the License for the specific language governing permissions and |
22 | | * limitations under the License. |
23 | | */ |
24 | | |
25 | | #include "pkixtestutil.h" |
26 | | |
27 | | #include <cerrno> |
28 | | #include <cstdio> |
29 | | #include <limits> |
30 | | #include <new> |
31 | | #include <sstream> |
32 | | #include <cstdlib> |
33 | | |
34 | | #include "pkixder.h" |
35 | | #include "pkixutil.h" |
36 | | |
37 | | #include "mozilla/Unused.h" |
38 | | |
39 | | using namespace std; |
40 | | |
41 | | namespace mozilla { namespace pkix { namespace test { |
42 | | |
43 | | namespace { |
44 | | |
45 | | inline void |
46 | 0 | fclose_void(FILE* file) { |
47 | 0 | (void) fclose(file); |
48 | 0 | } |
49 | | |
50 | | typedef mozilla::pkix::ScopedPtr<FILE, fclose_void> ScopedFILE; |
51 | | |
52 | | FILE* |
53 | | OpenFile(const string& dir, const string& filename, const string& mode) |
54 | 0 | { |
55 | 0 | string path = dir + '/' + filename; |
56 | 0 |
|
57 | 0 | ScopedFILE file; |
58 | | #ifdef _MSC_VER |
59 | | { |
60 | | FILE* rawFile; |
61 | | errno_t error = fopen_s(&rawFile, path.c_str(), mode.c_str()); |
62 | | if (error) { |
63 | | // TODO: map error to NSPR error code |
64 | | rawFile = nullptr; |
65 | | } |
66 | | file.reset(rawFile); |
67 | | } |
68 | | #else |
69 | | file.reset(fopen(path.c_str(), mode.c_str())); |
70 | 0 | #endif |
71 | 0 | return file.release(); |
72 | 0 | } |
73 | | |
74 | | } // namespace |
75 | | |
76 | | bool |
77 | | InputEqualsByteString(Input input, const ByteString& bs) |
78 | 0 | { |
79 | 0 | Input bsInput; |
80 | 0 | if (bsInput.Init(bs.data(), bs.length()) != Success) { |
81 | 0 | // Init can only fail if it is given a bad pointer or if the input is too |
82 | 0 | // long, which won't ever happen. Plus, if it does, it is ok to call abort |
83 | 0 | // since this is only test code. |
84 | 0 | abort(); |
85 | 0 | } |
86 | 0 | return InputsAreEqual(input, bsInput); |
87 | 0 | } |
88 | | |
89 | | ByteString |
90 | | InputToByteString(Input input) |
91 | 0 | { |
92 | 0 | ByteString result; |
93 | 0 | Reader reader(input); |
94 | 0 | for (;;) { |
95 | 0 | uint8_t b; |
96 | 0 | if (reader.Read(b) != Success) { |
97 | 0 | return result; |
98 | 0 | } |
99 | 0 | result.push_back(b); |
100 | 0 | } |
101 | 0 | } |
102 | | |
103 | | Result |
104 | | TamperOnce(/*in/out*/ ByteString& item, const ByteString& from, |
105 | | const ByteString& to) |
106 | 0 | { |
107 | 0 | if (from.length() < 8) { |
108 | 0 | return Result::FATAL_ERROR_INVALID_ARGS; |
109 | 0 | } |
110 | 0 | if (from.length() != to.length()) { |
111 | 0 | return Result::FATAL_ERROR_INVALID_ARGS; |
112 | 0 | } |
113 | 0 | size_t pos = item.find(from); |
114 | 0 | if (pos == string::npos) { |
115 | 0 | return Result::FATAL_ERROR_INVALID_ARGS; // No matches. |
116 | 0 | } |
117 | 0 | if (item.find(from, pos + from.length()) != string::npos) { |
118 | 0 | return Result::FATAL_ERROR_INVALID_ARGS; // More than once match. |
119 | 0 | } |
120 | 0 | item.replace(pos, from.length(), to); |
121 | 0 | return Success; |
122 | 0 | } |
123 | | |
124 | | // Given a tag and a value, generates a DER-encoded tag-length-value item. |
125 | | ByteString |
126 | | TLV(uint8_t tag, size_t length, const ByteString& value) |
127 | 4.32k | { |
128 | 4.32k | ByteString result; |
129 | 4.32k | result.push_back(tag); |
130 | 4.32k | |
131 | 4.32k | if (value.length() < 128) { |
132 | 4.32k | result.push_back(static_cast<uint8_t>(length)); |
133 | 4.32k | } else if (value.length() < 256) { |
134 | 0 | result.push_back(0x81u); |
135 | 0 | result.push_back(static_cast<uint8_t>(length)); |
136 | 3 | } else if (value.length() < 65536) { |
137 | 3 | result.push_back(0x82u); |
138 | 3 | result.push_back(static_cast<uint8_t>(length / 256)); |
139 | 3 | result.push_back(static_cast<uint8_t>(length % 256)); |
140 | 3 | } else { |
141 | 0 | // It is MUCH more convenient for TLV to be infallible than for it to have |
142 | 0 | // "proper" error handling. |
143 | 0 | abort(); |
144 | 0 | } |
145 | 4.32k | result.append(value); |
146 | 4.32k | return result; |
147 | 4.32k | } |
148 | | |
149 | | OCSPResponseExtension::OCSPResponseExtension() |
150 | | : id() |
151 | | , critical(false) |
152 | | , value() |
153 | | , next(nullptr) |
154 | 0 | { |
155 | 0 | } |
156 | | |
157 | | OCSPResponseContext::OCSPResponseContext(const CertID& aCertID, time_t time) |
158 | | : certID(aCertID) |
159 | | , responseStatus(successful) |
160 | | , skipResponseBytes(false) |
161 | | , producedAt(time) |
162 | | , singleExtensions(nullptr) |
163 | | , responseExtensions(nullptr) |
164 | | , includeEmptyExtensions(false) |
165 | | , signatureAlgorithm(sha256WithRSAEncryption()) |
166 | | , badSignature(false) |
167 | | , certs(nullptr) |
168 | | |
169 | | , certStatus(good) |
170 | | , revocationTime(0) |
171 | | , thisUpdate(time) |
172 | | , nextUpdate(time + static_cast<time_t>(Time::ONE_DAY_IN_SECONDS)) |
173 | | , includeNextUpdate(true) |
174 | 0 | { |
175 | 0 | } |
176 | | |
177 | | static ByteString ResponseBytes(OCSPResponseContext& context); |
178 | | static ByteString BasicOCSPResponse(OCSPResponseContext& context); |
179 | | static ByteString ResponseData(OCSPResponseContext& context); |
180 | | static ByteString ResponderID(OCSPResponseContext& context); |
181 | | static ByteString KeyHash(const ByteString& subjectPublicKeyInfo); |
182 | | static ByteString SingleResponse(OCSPResponseContext& context); |
183 | | static ByteString CertID(OCSPResponseContext& context); |
184 | | static ByteString CertStatus(OCSPResponseContext& context); |
185 | | |
186 | | static ByteString |
187 | | SHA1(const ByteString& toHash) |
188 | 0 | { |
189 | 0 | uint8_t digestBuf[20]; |
190 | 0 | Input input; |
191 | 0 | if (input.Init(toHash.data(), toHash.length()) != Success) { |
192 | 0 | abort(); |
193 | 0 | } |
194 | 0 | Result rv = TestDigestBuf(input, DigestAlgorithm::sha1, digestBuf, |
195 | 0 | sizeof(digestBuf)); |
196 | 0 | if (rv != Success) { |
197 | 0 | abort(); |
198 | 0 | } |
199 | 0 | return ByteString(digestBuf, sizeof(digestBuf)); |
200 | 0 | } |
201 | | |
202 | | static ByteString |
203 | | HashedOctetString(const ByteString& bytes) |
204 | 0 | { |
205 | 0 | ByteString digest(SHA1(bytes)); |
206 | 0 | if (ENCODING_FAILED(digest)) { |
207 | 0 | return ByteString(); |
208 | 0 | } |
209 | 0 | return TLV(der::OCTET_STRING, digest); |
210 | 0 | } |
211 | | |
212 | | static ByteString |
213 | | BitString(const ByteString& rawBytes, bool corrupt) |
214 | 0 | { |
215 | 0 | ByteString prefixed; |
216 | 0 | // We have to add a byte at the beginning indicating no unused bits. |
217 | 0 | // TODO: add ability to have bit strings of bit length not divisible by 8, |
218 | 0 | // resulting in unused bits in the bitstring encoding |
219 | 0 | prefixed.push_back(0); |
220 | 0 | prefixed.append(rawBytes); |
221 | 0 | if (corrupt) { |
222 | 0 | assert(prefixed.length() > 8); |
223 | 0 | prefixed[8]++; |
224 | 0 | } |
225 | 0 | return TLV(der::BIT_STRING, prefixed); |
226 | 0 | } |
227 | | |
228 | | ByteString |
229 | | Boolean(bool value) |
230 | 21 | { |
231 | 21 | ByteString encodedValue; |
232 | 21 | encodedValue.push_back(value ? 0xffu : 0x00u); |
233 | 21 | return TLV(der::BOOLEAN, encodedValue); |
234 | 21 | } |
235 | | |
236 | | ByteString |
237 | | Integer(long value) |
238 | 3 | { |
239 | 3 | if (value < 0 || value > 127) { |
240 | 0 | // TODO: add encoding of larger values |
241 | 0 | // It is MUCH more convenient for Integer to be infallible than for it to |
242 | 0 | // have "proper" error handling. |
243 | 0 | abort(); |
244 | 0 | } |
245 | 3 | |
246 | 3 | ByteString encodedValue; |
247 | 3 | encodedValue.push_back(static_cast<uint8_t>(value)); |
248 | 3 | return TLV(der::INTEGER, encodedValue); |
249 | 3 | } |
250 | | |
251 | | enum TimeEncoding { UTCTime = 0, GeneralizedTime = 1 }; |
252 | | |
253 | | // Windows doesn't provide gmtime_r, but it provides something very similar. |
254 | | #if defined(WIN32) && (!defined(_POSIX_C_SOURCE) || !defined(_POSIX_THREAD_SAFE_FUNCTIONS)) |
255 | | static tm* |
256 | | gmtime_r(const time_t* t, /*out*/ tm* exploded) |
257 | | { |
258 | | if (gmtime_s(exploded, t) != 0) { |
259 | | return nullptr; |
260 | | } |
261 | | return exploded; |
262 | | } |
263 | | #endif |
264 | | |
265 | | // http://tools.ietf.org/html/rfc5280#section-4.1.2.5 |
266 | | // UTCTime: YYMMDDHHMMSSZ (years 1950-2049 only) |
267 | | // GeneralizedTime: YYYYMMDDHHMMSSZ |
268 | | // |
269 | | // This assumes that time/time_t are POSIX-compliant in that time() returns |
270 | | // the number of seconds since the Unix epoch. |
271 | | static ByteString |
272 | | TimeToEncodedTime(time_t time, TimeEncoding encoding) |
273 | 0 | { |
274 | 0 | assert(encoding == UTCTime || encoding == GeneralizedTime); |
275 | 0 |
|
276 | 0 | tm exploded; |
277 | 0 | if (!gmtime_r(&time, &exploded)) { |
278 | 0 | return ByteString(); |
279 | 0 | } |
280 | 0 | |
281 | 0 | if (exploded.tm_sec >= 60) { |
282 | 0 | // round down for leap seconds |
283 | 0 | exploded.tm_sec = 59; |
284 | 0 | } |
285 | 0 |
|
286 | 0 | // exploded.tm_year is the year offset by 1900. |
287 | 0 | int year = exploded.tm_year + 1900; |
288 | 0 |
|
289 | 0 | if (encoding == UTCTime && (year < 1950 || year >= 2050)) { |
290 | 0 | return ByteString(); |
291 | 0 | } |
292 | 0 | |
293 | 0 | ByteString value; |
294 | 0 |
|
295 | 0 | if (encoding == GeneralizedTime) { |
296 | 0 | value.push_back(static_cast<uint8_t>('0' + (year / 1000))); |
297 | 0 | value.push_back(static_cast<uint8_t>('0' + ((year % 1000) / 100))); |
298 | 0 | } |
299 | 0 |
|
300 | 0 | value.push_back(static_cast<uint8_t>('0' + ((year % 100) / 10))); |
301 | 0 | value.push_back(static_cast<uint8_t>('0' + (year % 10))); |
302 | 0 | value.push_back(static_cast<uint8_t>('0' + ((exploded.tm_mon + 1) / 10))); |
303 | 0 | value.push_back(static_cast<uint8_t>('0' + ((exploded.tm_mon + 1) % 10))); |
304 | 0 | value.push_back(static_cast<uint8_t>('0' + (exploded.tm_mday / 10))); |
305 | 0 | value.push_back(static_cast<uint8_t>('0' + (exploded.tm_mday % 10))); |
306 | 0 | value.push_back(static_cast<uint8_t>('0' + (exploded.tm_hour / 10))); |
307 | 0 | value.push_back(static_cast<uint8_t>('0' + (exploded.tm_hour % 10))); |
308 | 0 | value.push_back(static_cast<uint8_t>('0' + (exploded.tm_min / 10))); |
309 | 0 | value.push_back(static_cast<uint8_t>('0' + (exploded.tm_min % 10))); |
310 | 0 | value.push_back(static_cast<uint8_t>('0' + (exploded.tm_sec / 10))); |
311 | 0 | value.push_back(static_cast<uint8_t>('0' + (exploded.tm_sec % 10))); |
312 | 0 | value.push_back('Z'); |
313 | 0 |
|
314 | 0 | return TLV(encoding == GeneralizedTime ? der::GENERALIZED_TIME : der::UTCTime, |
315 | 0 | value); |
316 | 0 | } |
317 | | |
318 | | static ByteString |
319 | | TimeToGeneralizedTime(time_t time) |
320 | 0 | { |
321 | 0 | return TimeToEncodedTime(time, GeneralizedTime); |
322 | 0 | } |
323 | | |
324 | | // http://tools.ietf.org/html/rfc5280#section-4.1.2.5: "CAs conforming to this |
325 | | // profile MUST always encode certificate validity dates through the year 2049 |
326 | | // as UTCTime; certificate validity dates in 2050 or later MUST be encoded as |
327 | | // GeneralizedTime." (This is a special case of the rule that we must always |
328 | | // use the shortest possible encoding.) |
329 | | static ByteString |
330 | | TimeToTimeChoice(time_t time) |
331 | 0 | { |
332 | 0 | tm exploded; |
333 | 0 | if (!gmtime_r(&time, &exploded)) { |
334 | 0 | return ByteString(); |
335 | 0 | } |
336 | 0 | TimeEncoding encoding = (exploded.tm_year + 1900 >= 1950 && |
337 | 0 | exploded.tm_year + 1900 < 2050) |
338 | 0 | ? UTCTime |
339 | 0 | : GeneralizedTime; |
340 | 0 |
|
341 | 0 | return TimeToEncodedTime(time, encoding); |
342 | 0 | } |
343 | | |
344 | | Time |
345 | | YMDHMS(uint16_t year, uint16_t month, uint16_t day, |
346 | | uint16_t hour, uint16_t minutes, uint16_t seconds) |
347 | 12 | { |
348 | 12 | assert(year <= 9999); |
349 | 12 | assert(month >= 1); |
350 | 12 | assert(month <= 12); |
351 | 12 | assert(day >= 1); |
352 | 12 | assert(hour < 24); |
353 | 12 | assert(minutes < 60); |
354 | 12 | assert(seconds < 60); |
355 | 12 | |
356 | 12 | uint64_t days = DaysBeforeYear(year); |
357 | 12 | |
358 | 12 | { |
359 | 12 | static const int16_t DAYS_IN_MONTH[] = { |
360 | 12 | 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 |
361 | 12 | }; |
362 | 12 | |
363 | 12 | int16_t i = 1; |
364 | 144 | for (;;) { |
365 | 144 | int16_t daysInMonth = DAYS_IN_MONTH[i - 1]; |
366 | 144 | if (i == 2 && |
367 | 144 | ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)))) { |
368 | 3 | // Add leap day |
369 | 3 | ++daysInMonth; |
370 | 3 | } |
371 | 144 | if (i == month) { |
372 | 12 | assert(day <= daysInMonth); |
373 | 12 | break; |
374 | 12 | } |
375 | 132 | days += daysInMonth; |
376 | 132 | ++i; |
377 | 132 | } |
378 | 12 | } |
379 | 12 | |
380 | 12 | days += (day - 1); |
381 | 12 | |
382 | 12 | uint64_t totalSeconds = days * Time::ONE_DAY_IN_SECONDS; |
383 | 12 | totalSeconds += hour * 60 * 60; |
384 | 12 | totalSeconds += minutes * 60; |
385 | 12 | totalSeconds += seconds; |
386 | 12 | return TimeFromElapsedSecondsAD(totalSeconds); |
387 | 12 | } |
388 | | |
389 | | static ByteString |
390 | | SignedData(const ByteString& tbsData, |
391 | | const TestKeyPair& keyPair, |
392 | | const TestSignatureAlgorithm& signatureAlgorithm, |
393 | | bool corrupt, /*optional*/ const ByteString* certs) |
394 | 0 | { |
395 | 0 | ByteString signature; |
396 | 0 | if (keyPair.SignData(tbsData, signatureAlgorithm, signature) != Success) { |
397 | 0 | return ByteString(); |
398 | 0 | } |
399 | 0 | |
400 | 0 | // TODO: add ability to have signatures of bit length not divisible by 8, |
401 | 0 | // resulting in unused bits in the bitstring encoding |
402 | 0 | ByteString signatureNested(BitString(signature, corrupt)); |
403 | 0 | if (ENCODING_FAILED(signatureNested)) { |
404 | 0 | return ByteString(); |
405 | 0 | } |
406 | 0 | |
407 | 0 | ByteString certsNested; |
408 | 0 | if (certs) { |
409 | 0 | ByteString certsSequenceValue; |
410 | 0 | while (!(*certs).empty()) { |
411 | 0 | certsSequenceValue.append(*certs); |
412 | 0 | ++certs; |
413 | 0 | } |
414 | 0 | ByteString certsSequence(TLV(der::SEQUENCE, certsSequenceValue)); |
415 | 0 | certsNested = TLV(der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0, |
416 | 0 | certsSequence); |
417 | 0 | } |
418 | 0 |
|
419 | 0 | ByteString value; |
420 | 0 | value.append(tbsData); |
421 | 0 | value.append(signatureAlgorithm.algorithmIdentifier); |
422 | 0 | value.append(signatureNested); |
423 | 0 | value.append(certsNested); |
424 | 0 | return TLV(der::SEQUENCE, value); |
425 | 0 | } |
426 | | |
427 | | // Extension ::= SEQUENCE { |
428 | | // extnID OBJECT IDENTIFIER, |
429 | | // critical BOOLEAN DEFAULT FALSE, |
430 | | // extnValue OCTET STRING |
431 | | // -- contains the DER encoding of an ASN.1 value |
432 | | // -- corresponding to the extension type identified |
433 | | // -- by extnID |
434 | | // } |
435 | | static ByteString |
436 | | Extension(Input extnID, Critical critical, const ByteString& extnValueBytes) |
437 | 0 | { |
438 | 0 | ByteString encoded; |
439 | 0 |
|
440 | 0 | encoded.append(ByteString(extnID.UnsafeGetData(), extnID.GetLength())); |
441 | 0 |
|
442 | 0 | if (critical == Critical::Yes) { |
443 | 0 | encoded.append(Boolean(true)); |
444 | 0 | } |
445 | 0 |
|
446 | 0 | ByteString extnValueSequence(TLV(der::SEQUENCE, extnValueBytes)); |
447 | 0 | ByteString extnValue(TLV(der::OCTET_STRING, extnValueSequence)); |
448 | 0 | encoded.append(extnValue); |
449 | 0 | return TLV(der::SEQUENCE, encoded); |
450 | 0 | } |
451 | | |
452 | | static ByteString |
453 | | EmptyExtension(Input extnID, Critical critical) |
454 | 0 | { |
455 | 0 | ByteString encoded(extnID.UnsafeGetData(), extnID.GetLength()); |
456 | 0 |
|
457 | 0 | if (critical == Critical::Yes) { |
458 | 0 | encoded.append(Boolean(true)); |
459 | 0 | } |
460 | 0 |
|
461 | 0 | ByteString extnValue(TLV(der::OCTET_STRING, ByteString())); |
462 | 0 | encoded.append(extnValue); |
463 | 0 | return TLV(der::SEQUENCE, encoded); |
464 | 0 | } |
465 | | |
466 | | std::string |
467 | | GetEnv(const char* name) |
468 | 0 | { |
469 | 0 | std::string result; |
470 | 0 |
|
471 | 0 | #ifndef _MSC_VER |
472 | 0 | // XXX: Not thread safe. |
473 | 0 | const char* value = getenv(name); |
474 | 0 | if (value) { |
475 | 0 | result = value; |
476 | 0 | } |
477 | | #else |
478 | | char* value = nullptr; |
479 | | size_t valueLength = 0; |
480 | | if (_dupenv_s(&value, &valueLength, name) != 0) { |
481 | | abort(); |
482 | | } |
483 | | if (value) { |
484 | | result = value; |
485 | | free(value); |
486 | | } |
487 | | #endif |
488 | | return result; |
489 | 0 | } |
490 | | |
491 | | void |
492 | | MaybeLogOutput(const ByteString& result, const char* suffix) |
493 | 0 | { |
494 | 0 | assert(suffix); |
495 | 0 |
|
496 | 0 | // This allows us to more easily debug the generated output, by creating a |
497 | 0 | // file in the directory given by MOZILLA_PKIX_TEST_LOG_DIR for each |
498 | 0 | // NOT THREAD-SAFE!!! |
499 | 0 | std::string logPath(GetEnv("MOZILLA_PKIX_TEST_LOG_DIR")); |
500 | 0 | if (!logPath.empty()) { |
501 | 0 | static int counter = 0; |
502 | 0 |
|
503 | 0 | std::ostringstream counterStream; |
504 | 0 | counterStream << counter; |
505 | 0 | if (!counterStream) { |
506 | 0 | assert(false); |
507 | 0 | return; |
508 | 0 | } |
509 | 0 | string filename = counterStream.str() + '-' + suffix + ".der"; |
510 | 0 |
|
511 | 0 | ++counter; |
512 | 0 | ScopedFILE file(OpenFile(logPath, filename, "wb")); |
513 | 0 | if (file) { |
514 | 0 | Unused << fwrite(result.data(), result.length(), 1, file.get()); |
515 | 0 | } |
516 | 0 | } |
517 | 0 | } |
518 | | |
519 | | /////////////////////////////////////////////////////////////////////////////// |
520 | | // Certificates |
521 | | |
522 | | static ByteString TBSCertificate(long version, const ByteString& serialNumber, |
523 | | const ByteString& signature, |
524 | | const ByteString& issuer, |
525 | | time_t notBefore, time_t notAfter, |
526 | | const ByteString& subject, |
527 | | const ByteString& subjectPublicKeyInfo, |
528 | | /*optional*/ const ByteString* extensions); |
529 | | |
530 | | // Certificate ::= SEQUENCE { |
531 | | // tbsCertificate TBSCertificate, |
532 | | // signatureAlgorithm AlgorithmIdentifier, |
533 | | // signatureValue BIT STRING } |
534 | | ByteString |
535 | | CreateEncodedCertificate(long version, |
536 | | const TestSignatureAlgorithm& signature, |
537 | | const ByteString& serialNumber, |
538 | | const ByteString& issuerNameDER, |
539 | | time_t notBefore, time_t notAfter, |
540 | | const ByteString& subjectNameDER, |
541 | | const TestKeyPair& subjectKeyPair, |
542 | | /*optional*/ const ByteString* extensions, |
543 | | const TestKeyPair& issuerKeyPair, |
544 | | const TestSignatureAlgorithm& signatureAlgorithm) |
545 | 0 | { |
546 | 0 | ByteString tbsCertificate(TBSCertificate(version, serialNumber, |
547 | 0 | signature.algorithmIdentifier, |
548 | 0 | issuerNameDER, notBefore, |
549 | 0 | notAfter, subjectNameDER, |
550 | 0 | subjectKeyPair.subjectPublicKeyInfo, |
551 | 0 | extensions)); |
552 | 0 | if (ENCODING_FAILED(tbsCertificate)) { |
553 | 0 | return ByteString(); |
554 | 0 | } |
555 | 0 | |
556 | 0 | ByteString result(SignedData(tbsCertificate, issuerKeyPair, |
557 | 0 | signatureAlgorithm, false, nullptr)); |
558 | 0 | if (ENCODING_FAILED(result)) { |
559 | 0 | return ByteString(); |
560 | 0 | } |
561 | 0 | |
562 | 0 | MaybeLogOutput(result, "cert"); |
563 | 0 |
|
564 | 0 | return result; |
565 | 0 | } |
566 | | |
567 | | // TBSCertificate ::= SEQUENCE { |
568 | | // version [0] Version DEFAULT v1, |
569 | | // serialNumber CertificateSerialNumber, |
570 | | // signature AlgorithmIdentifier, |
571 | | // issuer Name, |
572 | | // validity Validity, |
573 | | // subject Name, |
574 | | // subjectPublicKeyInfo SubjectPublicKeyInfo, |
575 | | // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, |
576 | | // -- If present, version MUST be v2 or v3 |
577 | | // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, |
578 | | // -- If present, version MUST be v2 or v3 |
579 | | // extensions [3] Extensions OPTIONAL |
580 | | // -- If present, version MUST be v3 -- } |
581 | | static ByteString |
582 | | TBSCertificate(long versionValue, |
583 | | const ByteString& serialNumber, const ByteString& signature, |
584 | | const ByteString& issuer, time_t notBeforeTime, |
585 | | time_t notAfterTime, const ByteString& subject, |
586 | | const ByteString& subjectPublicKeyInfo, |
587 | | /*optional*/ const ByteString* extensions) |
588 | 0 | { |
589 | 0 | ByteString value; |
590 | 0 |
|
591 | 0 | if (versionValue != static_cast<long>(der::Version::v1)) { |
592 | 0 | ByteString versionInteger(Integer(versionValue)); |
593 | 0 | ByteString version(TLV(der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 0, |
594 | 0 | versionInteger)); |
595 | 0 | value.append(version); |
596 | 0 | } |
597 | 0 |
|
598 | 0 | value.append(serialNumber); |
599 | 0 | value.append(signature); |
600 | 0 | value.append(issuer); |
601 | 0 |
|
602 | 0 | // Validity ::= SEQUENCE { |
603 | 0 | // notBefore Time, |
604 | 0 | // notAfter Time } |
605 | 0 | ByteString validity; |
606 | 0 | { |
607 | 0 | ByteString notBefore(TimeToTimeChoice(notBeforeTime)); |
608 | 0 | if (ENCODING_FAILED(notBefore)) { |
609 | 0 | return ByteString(); |
610 | 0 | } |
611 | 0 | ByteString notAfter(TimeToTimeChoice(notAfterTime)); |
612 | 0 | if (ENCODING_FAILED(notAfter)) { |
613 | 0 | return ByteString(); |
614 | 0 | } |
615 | 0 | ByteString validityValue; |
616 | 0 | validityValue.append(notBefore); |
617 | 0 | validityValue.append(notAfter); |
618 | 0 | validity = TLV(der::SEQUENCE, validityValue); |
619 | 0 | if (ENCODING_FAILED(validity)) { |
620 | 0 | return ByteString(); |
621 | 0 | } |
622 | 0 | } |
623 | 0 | value.append(validity); |
624 | 0 |
|
625 | 0 | value.append(subject); |
626 | 0 |
|
627 | 0 | value.append(subjectPublicKeyInfo); |
628 | 0 |
|
629 | 0 | if (extensions) { |
630 | 0 | ByteString extensionsValue; |
631 | 0 | while (!(*extensions).empty()) { |
632 | 0 | extensionsValue.append(*extensions); |
633 | 0 | ++extensions; |
634 | 0 | } |
635 | 0 | ByteString extensionsSequence(TLV(der::SEQUENCE, extensionsValue)); |
636 | 0 | if (ENCODING_FAILED(extensionsSequence)) { |
637 | 0 | return ByteString(); |
638 | 0 | } |
639 | 0 | ByteString extensionsWrapped( |
640 | 0 | TLV(der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 3, extensionsSequence)); |
641 | 0 | if (ENCODING_FAILED(extensionsWrapped)) { |
642 | 0 | return ByteString(); |
643 | 0 | } |
644 | 0 | value.append(extensionsWrapped); |
645 | 0 | } |
646 | 0 |
|
647 | 0 | return TLV(der::SEQUENCE, value); |
648 | 0 | } |
649 | | |
650 | | // AttributeTypeAndValue ::= SEQUENCE { |
651 | | // type AttributeType, |
652 | | // value AttributeValue } |
653 | | // |
654 | | // AttributeType ::= OBJECT IDENTIFIER |
655 | | // |
656 | | // AttributeValue ::= ANY -- DEFINED BY AttributeType |
657 | | // |
658 | | // DirectoryString ::= CHOICE { |
659 | | // teletexString TeletexString (SIZE (1..MAX)), |
660 | | // printableString PrintableString (SIZE (1..MAX)), |
661 | | // universalString UniversalString (SIZE (1..MAX)), |
662 | | // utf8String UTF8String (SIZE (1..MAX)), |
663 | | // bmpString BMPString (SIZE (1..MAX)) } |
664 | | template <size_t N> |
665 | | static ByteString |
666 | | AVA(const uint8_t (&type)[N], uint8_t directoryStringType, |
667 | | const ByteString& value) |
668 | 570 | { |
669 | 570 | ByteString wrappedValue(TLV(directoryStringType, value)); |
670 | 570 | ByteString ava; |
671 | 570 | ava.append(type, N); |
672 | 570 | ava.append(wrappedValue); |
673 | 570 | return TLV(der::SEQUENCE, ava); |
674 | 570 | } pkixtestutil.cpp:std::__1::basic_string<unsigned char, std::__1::char_traits<unsigned char>, std::__1::allocator<unsigned char> > mozilla::pkix::test::AVA<5ul>(unsigned char const (&) [5ul], unsigned char, std::__1::basic_string<unsigned char, std::__1::char_traits<unsigned char>, std::__1::allocator<unsigned char> > const&) Line | Count | Source | 668 | 564 | { | 669 | 564 | ByteString wrappedValue(TLV(directoryStringType, value)); | 670 | 564 | ByteString ava; | 671 | 564 | ava.append(type, N); | 672 | 564 | ava.append(wrappedValue); | 673 | 564 | return TLV(der::SEQUENCE, ava); | 674 | 564 | } |
pkixtestutil.cpp:std::__1::basic_string<unsigned char, std::__1::char_traits<unsigned char>, std::__1::allocator<unsigned char> > mozilla::pkix::test::AVA<11ul>(unsigned char const (&) [11ul], unsigned char, std::__1::basic_string<unsigned char, std::__1::char_traits<unsigned char>, std::__1::allocator<unsigned char> > const&) Line | Count | Source | 668 | 6 | { | 669 | 6 | ByteString wrappedValue(TLV(directoryStringType, value)); | 670 | 6 | ByteString ava; | 671 | 6 | ava.append(type, N); | 672 | 6 | ava.append(wrappedValue); | 673 | 6 | return TLV(der::SEQUENCE, ava); | 674 | 6 | } |
|
675 | | |
676 | | ByteString |
677 | | CN(const ByteString& value, uint8_t encodingTag) |
678 | 429 | { |
679 | 429 | // id-at OBJECT IDENTIFIER ::= { joint-iso-ccitt(2) ds(5) 4 } |
680 | 429 | // id-at-commonName AttributeType ::= { id-at 3 } |
681 | 429 | // python DottedOIDToCode.py --tlv id-at-commonName 2.5.4.3 |
682 | 429 | static const uint8_t tlv_id_at_commonName[] = { |
683 | 429 | 0x06, 0x03, 0x55, 0x04, 0x03 |
684 | 429 | }; |
685 | 429 | return AVA(tlv_id_at_commonName, encodingTag, value); |
686 | 429 | } |
687 | | |
688 | | ByteString |
689 | | OU(const ByteString& value, uint8_t encodingTag) |
690 | 135 | { |
691 | 135 | // id-at OBJECT IDENTIFIER ::= { joint-iso-ccitt(2) ds(5) 4 } |
692 | 135 | // id-at-organizationalUnitName AttributeType ::= { id-at 11 } |
693 | 135 | // python DottedOIDToCode.py --tlv id-at-organizationalUnitName 2.5.4.11 |
694 | 135 | static const uint8_t tlv_id_at_organizationalUnitName[] = { |
695 | 135 | 0x06, 0x03, 0x55, 0x04, 0x0b |
696 | 135 | }; |
697 | 135 | |
698 | 135 | return AVA(tlv_id_at_organizationalUnitName, encodingTag, value); |
699 | 135 | } |
700 | | |
701 | | ByteString |
702 | | emailAddress(const ByteString& value) |
703 | 6 | { |
704 | 6 | // id-emailAddress AttributeType ::= { pkcs-9 1 } |
705 | 6 | // python DottedOIDToCode.py --tlv id-emailAddress 1.2.840.113549.1.9.1 |
706 | 6 | static const uint8_t tlv_id_emailAddress[] = { |
707 | 6 | 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01 |
708 | 6 | }; |
709 | 6 | |
710 | 6 | return AVA(tlv_id_emailAddress, der::IA5String, value); |
711 | 6 | } |
712 | | |
713 | | // RelativeDistinguishedName ::= |
714 | | // SET SIZE (1..MAX) OF AttributeTypeAndValue |
715 | | // |
716 | | ByteString |
717 | | RDN(const ByteString& avas) |
718 | 510 | { |
719 | 510 | return TLV(der::SET, avas); |
720 | 510 | } |
721 | | |
722 | | // Name ::= CHOICE { -- only one possibility for now -- |
723 | | // rdnSequence RDNSequence } |
724 | | // |
725 | | // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName |
726 | | // |
727 | | ByteString |
728 | | Name(const ByteString& rdns) |
729 | 57 | { |
730 | 57 | return TLV(der::SEQUENCE, rdns); |
731 | 57 | } |
732 | | |
733 | | ByteString |
734 | | CreateEncodedSerialNumber(long serialNumberValue) |
735 | 0 | { |
736 | 0 | return Integer(serialNumberValue); |
737 | 0 | } |
738 | | |
739 | | // BasicConstraints ::= SEQUENCE { |
740 | | // cA BOOLEAN DEFAULT FALSE, |
741 | | // pathLenConstraint INTEGER (0..MAX) OPTIONAL } |
742 | | ByteString |
743 | | CreateEncodedBasicConstraints(bool isCA, |
744 | | /*optional in*/ const long* pathLenConstraintValue, |
745 | | Critical critical) |
746 | 0 | { |
747 | 0 | ByteString value; |
748 | 0 |
|
749 | 0 | if (isCA) { |
750 | 0 | ByteString cA(Boolean(true)); |
751 | 0 | value.append(cA); |
752 | 0 | } |
753 | 0 |
|
754 | 0 | if (pathLenConstraintValue) { |
755 | 0 | ByteString pathLenConstraint(Integer(*pathLenConstraintValue)); |
756 | 0 | value.append(pathLenConstraint); |
757 | 0 | } |
758 | 0 |
|
759 | 0 | // python DottedOIDToCode.py --tlv id-ce-basicConstraints 2.5.29.19 |
760 | 0 | static const uint8_t tlv_id_ce_basicConstraints[] = { |
761 | 0 | 0x06, 0x03, 0x55, 0x1d, 0x13 |
762 | 0 | }; |
763 | 0 | return Extension(Input(tlv_id_ce_basicConstraints), critical, value); |
764 | 0 | } |
765 | | |
766 | | // ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId |
767 | | // KeyPurposeId ::= OBJECT IDENTIFIER |
768 | | ByteString |
769 | | CreateEncodedEKUExtension(Input ekuOID, Critical critical) |
770 | 0 | { |
771 | 0 | ByteString value(ekuOID.UnsafeGetData(), ekuOID.GetLength()); |
772 | 0 |
|
773 | 0 | // python DottedOIDToCode.py --tlv id-ce-extKeyUsage 2.5.29.37 |
774 | 0 | static const uint8_t tlv_id_ce_extKeyUsage[] = { |
775 | 0 | 0x06, 0x03, 0x55, 0x1d, 0x25 |
776 | 0 | }; |
777 | 0 |
|
778 | 0 | return Extension(Input(tlv_id_ce_extKeyUsage), critical, value); |
779 | 0 | } |
780 | | |
781 | | // python DottedOIDToCode.py --tlv id-ce-subjectAltName 2.5.29.17 |
782 | | static const uint8_t tlv_id_ce_subjectAltName[] = { |
783 | | 0x06, 0x03, 0x55, 0x1d, 0x11 |
784 | | }; |
785 | | |
786 | | ByteString |
787 | | CreateEncodedSubjectAltName(const ByteString& names) |
788 | 0 | { |
789 | 0 | return Extension(Input(tlv_id_ce_subjectAltName), Critical::No, names); |
790 | 0 | } |
791 | | |
792 | | ByteString |
793 | | CreateEncodedEmptySubjectAltName() |
794 | 0 | { |
795 | 0 | return EmptyExtension(Input(tlv_id_ce_subjectAltName), Critical::No); |
796 | 0 | } |
797 | | |
798 | | /////////////////////////////////////////////////////////////////////////////// |
799 | | // OCSP responses |
800 | | |
801 | | ByteString |
802 | | CreateEncodedOCSPResponse(OCSPResponseContext& context) |
803 | 0 | { |
804 | 0 | if (!context.skipResponseBytes) { |
805 | 0 | if (!context.signerKeyPair) { |
806 | 0 | return ByteString(); |
807 | 0 | } |
808 | 0 | } |
809 | 0 | |
810 | 0 | // OCSPResponse ::= SEQUENCE { |
811 | 0 | // responseStatus OCSPResponseStatus, |
812 | 0 | // responseBytes [0] EXPLICIT ResponseBytes OPTIONAL } |
813 | 0 | |
814 | 0 | // OCSPResponseStatus ::= ENUMERATED { |
815 | 0 | // successful (0), -- Response has valid confirmations |
816 | 0 | // malformedRequest (1), -- Illegal confirmation request |
817 | 0 | // internalError (2), -- Internal error in issuer |
818 | 0 | // tryLater (3), -- Try again later |
819 | 0 | // -- (4) is not used |
820 | 0 | // sigRequired (5), -- Must sign the request |
821 | 0 | // unauthorized (6) -- Request unauthorized |
822 | 0 | // } |
823 | 0 | ByteString reponseStatusValue; |
824 | 0 | reponseStatusValue.push_back(context.responseStatus); |
825 | 0 | ByteString responseStatus(TLV(der::ENUMERATED, reponseStatusValue)); |
826 | 0 |
|
827 | 0 | ByteString responseBytesNested; |
828 | 0 | if (!context.skipResponseBytes) { |
829 | 0 | ByteString responseBytes(ResponseBytes(context)); |
830 | 0 | if (ENCODING_FAILED(responseBytes)) { |
831 | 0 | return ByteString(); |
832 | 0 | } |
833 | 0 | |
834 | 0 | responseBytesNested = TLV(der::CONSTRUCTED | der::CONTEXT_SPECIFIC, |
835 | 0 | responseBytes); |
836 | 0 | } |
837 | 0 |
|
838 | 0 | ByteString value; |
839 | 0 | value.append(responseStatus); |
840 | 0 | value.append(responseBytesNested); |
841 | 0 | ByteString result(TLV(der::SEQUENCE, value)); |
842 | 0 |
|
843 | 0 | MaybeLogOutput(result, "ocsp"); |
844 | 0 |
|
845 | 0 | return result; |
846 | 0 | } |
847 | | |
848 | | // ResponseBytes ::= SEQUENCE { |
849 | | // responseType OBJECT IDENTIFIER, |
850 | | // response OCTET STRING } |
851 | | ByteString |
852 | | ResponseBytes(OCSPResponseContext& context) |
853 | 0 | { |
854 | 0 | // Includes tag and length |
855 | 0 | static const uint8_t id_pkix_ocsp_basic_encoded[] = { |
856 | 0 | 0x06, 0x09, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01 |
857 | 0 | }; |
858 | 0 | ByteString response(BasicOCSPResponse(context)); |
859 | 0 | if (ENCODING_FAILED(response)) { |
860 | 0 | return ByteString(); |
861 | 0 | } |
862 | 0 | ByteString responseNested = TLV(der::OCTET_STRING, response); |
863 | 0 |
|
864 | 0 | ByteString value; |
865 | 0 | value.append(id_pkix_ocsp_basic_encoded, |
866 | 0 | sizeof(id_pkix_ocsp_basic_encoded)); |
867 | 0 | value.append(responseNested); |
868 | 0 | return TLV(der::SEQUENCE, value); |
869 | 0 | } |
870 | | |
871 | | // BasicOCSPResponse ::= SEQUENCE { |
872 | | // tbsResponseData ResponseData, |
873 | | // signatureAlgorithm AlgorithmIdentifier, |
874 | | // signature BIT STRING, |
875 | | // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL } |
876 | | ByteString |
877 | | BasicOCSPResponse(OCSPResponseContext& context) |
878 | 0 | { |
879 | 0 | ByteString tbsResponseData(ResponseData(context)); |
880 | 0 | if (ENCODING_FAILED(tbsResponseData)) { |
881 | 0 | return ByteString(); |
882 | 0 | } |
883 | 0 | |
884 | 0 | return SignedData(tbsResponseData, *context.signerKeyPair, |
885 | 0 | context.signatureAlgorithm, context.badSignature, |
886 | 0 | context.certs); |
887 | 0 | } |
888 | | |
889 | | // Extension ::= SEQUENCE { |
890 | | // id OBJECT IDENTIFIER, |
891 | | // critical BOOLEAN DEFAULT FALSE |
892 | | // value OCTET STRING |
893 | | // } |
894 | | static ByteString |
895 | | OCSPExtension(OCSPResponseExtension& extension) |
896 | 0 | { |
897 | 0 | ByteString encoded; |
898 | 0 | encoded.append(extension.id); |
899 | 0 | if (extension.critical) { |
900 | 0 | encoded.append(Boolean(true)); |
901 | 0 | } |
902 | 0 | ByteString value(TLV(der::OCTET_STRING, extension.value)); |
903 | 0 | encoded.append(value); |
904 | 0 | return TLV(der::SEQUENCE, encoded); |
905 | 0 | } |
906 | | |
907 | | // Extensions ::= [1] { |
908 | | // SEQUENCE OF Extension |
909 | | // } |
910 | | static ByteString |
911 | | OCSPExtensions(OCSPResponseExtension* extensions) |
912 | 0 | { |
913 | 0 | ByteString value; |
914 | 0 | for (OCSPResponseExtension* extension = extensions; |
915 | 0 | extension; extension = extension->next) { |
916 | 0 | ByteString extensionEncoded(OCSPExtension(*extension)); |
917 | 0 | if (ENCODING_FAILED(extensionEncoded)) { |
918 | 0 | return ByteString(); |
919 | 0 | } |
920 | 0 | value.append(extensionEncoded); |
921 | 0 | } |
922 | 0 | ByteString sequence(TLV(der::SEQUENCE, value)); |
923 | 0 | return TLV(der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 1, sequence); |
924 | 0 | } |
925 | | |
926 | | // ResponseData ::= SEQUENCE { |
927 | | // version [0] EXPLICIT Version DEFAULT v1, |
928 | | // responderID ResponderID, |
929 | | // producedAt GeneralizedTime, |
930 | | // responses SEQUENCE OF SingleResponse, |
931 | | // responseExtensions [1] EXPLICIT Extensions OPTIONAL } |
932 | | ByteString |
933 | | ResponseData(OCSPResponseContext& context) |
934 | 0 | { |
935 | 0 | ByteString responderID(ResponderID(context)); |
936 | 0 | if (ENCODING_FAILED(responderID)) { |
937 | 0 | return ByteString(); |
938 | 0 | } |
939 | 0 | ByteString producedAtEncoded(TimeToGeneralizedTime(context.producedAt)); |
940 | 0 | if (ENCODING_FAILED(producedAtEncoded)) { |
941 | 0 | return ByteString(); |
942 | 0 | } |
943 | 0 | ByteString response(SingleResponse(context)); |
944 | 0 | if (ENCODING_FAILED(response)) { |
945 | 0 | return ByteString(); |
946 | 0 | } |
947 | 0 | ByteString responses(TLV(der::SEQUENCE, response)); |
948 | 0 | ByteString responseExtensions; |
949 | 0 | if (context.responseExtensions || context.includeEmptyExtensions) { |
950 | 0 | responseExtensions = OCSPExtensions(context.responseExtensions); |
951 | 0 | } |
952 | 0 |
|
953 | 0 | ByteString value; |
954 | 0 | value.append(responderID); |
955 | 0 | value.append(producedAtEncoded); |
956 | 0 | value.append(responses); |
957 | 0 | value.append(responseExtensions); |
958 | 0 | return TLV(der::SEQUENCE, value); |
959 | 0 | } |
960 | | |
961 | | // ResponderID ::= CHOICE { |
962 | | // byName [1] Name, |
963 | | // byKey [2] KeyHash } |
964 | | // } |
965 | | ByteString |
966 | | ResponderID(OCSPResponseContext& context) |
967 | 0 | { |
968 | 0 | ByteString contents; |
969 | 0 | uint8_t responderIDType; |
970 | 0 | if (!context.signerNameDER.empty()) { |
971 | 0 | contents = context.signerNameDER; |
972 | 0 | responderIDType = 1; // byName |
973 | 0 | } else { |
974 | 0 | contents = KeyHash(context.signerKeyPair->subjectPublicKey); |
975 | 0 | if (ENCODING_FAILED(contents)) { |
976 | 0 | return ByteString(); |
977 | 0 | } |
978 | 0 | responderIDType = 2; // byKey |
979 | 0 | } |
980 | 0 |
|
981 | 0 | // XXX: MSVC 2015 wrongly warns about signed/unsigned conversion without the |
982 | 0 | // static_cast. |
983 | 0 | uint8_t tag = static_cast<uint8_t>(der::CONSTRUCTED | der::CONTEXT_SPECIFIC | |
984 | 0 | responderIDType); |
985 | 0 | return TLV(tag, contents); |
986 | 0 | } |
987 | | |
988 | | // KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key |
989 | | // -- (i.e., the SHA-1 hash of the value of the |
990 | | // -- BIT STRING subjectPublicKey [excluding |
991 | | // -- the tag, length, and number of unused |
992 | | // -- bits] in the responder's certificate) |
993 | | ByteString |
994 | | KeyHash(const ByteString& subjectPublicKey) |
995 | 0 | { |
996 | 0 | return HashedOctetString(subjectPublicKey); |
997 | 0 | } |
998 | | |
999 | | // SingleResponse ::= SEQUENCE { |
1000 | | // certID CertID, |
1001 | | // certStatus CertStatus, |
1002 | | // thisUpdate GeneralizedTime, |
1003 | | // nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL, |
1004 | | // singleExtensions [1] EXPLICIT Extensions OPTIONAL } |
1005 | | ByteString |
1006 | | SingleResponse(OCSPResponseContext& context) |
1007 | 0 | { |
1008 | 0 | ByteString certID(CertID(context)); |
1009 | 0 | if (ENCODING_FAILED(certID)) { |
1010 | 0 | return ByteString(); |
1011 | 0 | } |
1012 | 0 | ByteString certStatus(CertStatus(context)); |
1013 | 0 | if (ENCODING_FAILED(certStatus)) { |
1014 | 0 | return ByteString(); |
1015 | 0 | } |
1016 | 0 | ByteString thisUpdateEncoded(TimeToGeneralizedTime(context.thisUpdate)); |
1017 | 0 | if (ENCODING_FAILED(thisUpdateEncoded)) { |
1018 | 0 | return ByteString(); |
1019 | 0 | } |
1020 | 0 | ByteString nextUpdateEncodedNested; |
1021 | 0 | if (context.includeNextUpdate) { |
1022 | 0 | ByteString nextUpdateEncoded(TimeToGeneralizedTime(context.nextUpdate)); |
1023 | 0 | if (ENCODING_FAILED(nextUpdateEncoded)) { |
1024 | 0 | return ByteString(); |
1025 | 0 | } |
1026 | 0 | nextUpdateEncodedNested = TLV(der::CONSTRUCTED | der::CONTEXT_SPECIFIC | 0, |
1027 | 0 | nextUpdateEncoded); |
1028 | 0 | } |
1029 | 0 | ByteString singleExtensions; |
1030 | 0 | if (context.singleExtensions || context.includeEmptyExtensions) { |
1031 | 0 | singleExtensions = OCSPExtensions(context.singleExtensions); |
1032 | 0 | } |
1033 | 0 |
|
1034 | 0 | ByteString value; |
1035 | 0 | value.append(certID); |
1036 | 0 | value.append(certStatus); |
1037 | 0 | value.append(thisUpdateEncoded); |
1038 | 0 | value.append(nextUpdateEncodedNested); |
1039 | 0 | value.append(singleExtensions); |
1040 | 0 | return TLV(der::SEQUENCE, value); |
1041 | 0 | } |
1042 | | |
1043 | | // CertID ::= SEQUENCE { |
1044 | | // hashAlgorithm AlgorithmIdentifier, |
1045 | | // issuerNameHash OCTET STRING, -- Hash of issuer's DN |
1046 | | // issuerKeyHash OCTET STRING, -- Hash of issuer's public key |
1047 | | // serialNumber CertificateSerialNumber } |
1048 | | ByteString |
1049 | | CertID(OCSPResponseContext& context) |
1050 | 0 | { |
1051 | 0 | ByteString issuerName(context.certID.issuer.UnsafeGetData(), |
1052 | 0 | context.certID.issuer.GetLength()); |
1053 | 0 | ByteString issuerNameHash(HashedOctetString(issuerName)); |
1054 | 0 | if (ENCODING_FAILED(issuerNameHash)) { |
1055 | 0 | return ByteString(); |
1056 | 0 | } |
1057 | 0 | |
1058 | 0 | ByteString issuerKeyHash; |
1059 | 0 | { |
1060 | 0 | // context.certID.issuerSubjectPublicKeyInfo is the entire |
1061 | 0 | // SubjectPublicKeyInfo structure, but we need just the subjectPublicKey |
1062 | 0 | // part. |
1063 | 0 | Reader input(context.certID.issuerSubjectPublicKeyInfo); |
1064 | 0 | Reader contents; |
1065 | 0 | if (der::ExpectTagAndGetValue(input, der::SEQUENCE, contents) != Success) { |
1066 | 0 | return ByteString(); |
1067 | 0 | } |
1068 | 0 | // Skip AlgorithmIdentifier |
1069 | 0 | if (der::ExpectTagAndSkipValue(contents, der::SEQUENCE) != Success) { |
1070 | 0 | return ByteString(); |
1071 | 0 | } |
1072 | 0 | Input subjectPublicKey; |
1073 | 0 | if (der::BitStringWithNoUnusedBits(contents, subjectPublicKey) |
1074 | 0 | != Success) { |
1075 | 0 | return ByteString(); |
1076 | 0 | } |
1077 | 0 | issuerKeyHash = KeyHash(ByteString(subjectPublicKey.UnsafeGetData(), |
1078 | 0 | subjectPublicKey.GetLength())); |
1079 | 0 | if (ENCODING_FAILED(issuerKeyHash)) { |
1080 | 0 | return ByteString(); |
1081 | 0 | } |
1082 | 0 | } |
1083 | 0 | |
1084 | 0 | ByteString serialNumberValue(context.certID.serialNumber.UnsafeGetData(), |
1085 | 0 | context.certID.serialNumber.GetLength()); |
1086 | 0 | ByteString serialNumber(TLV(der::INTEGER, serialNumberValue)); |
1087 | 0 |
|
1088 | 0 | // python DottedOIDToCode.py --alg id-sha1 1.3.14.3.2.26 |
1089 | 0 | static const uint8_t alg_id_sha1[] = { |
1090 | 0 | 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a |
1091 | 0 | }; |
1092 | 0 |
|
1093 | 0 | ByteString value; |
1094 | 0 | value.append(alg_id_sha1, sizeof(alg_id_sha1)); |
1095 | 0 | value.append(issuerNameHash); |
1096 | 0 | value.append(issuerKeyHash); |
1097 | 0 | value.append(serialNumber); |
1098 | 0 | return TLV(der::SEQUENCE, value); |
1099 | 0 | } |
1100 | | |
1101 | | // CertStatus ::= CHOICE { |
1102 | | // good [0] IMPLICIT NULL, |
1103 | | // revoked [1] IMPLICIT RevokedInfo, |
1104 | | // unknown [2] IMPLICIT UnknownInfo } |
1105 | | // |
1106 | | // RevokedInfo ::= SEQUENCE { |
1107 | | // revocationTime GeneralizedTime, |
1108 | | // revocationReason [0] EXPLICIT CRLReason OPTIONAL } |
1109 | | // |
1110 | | // UnknownInfo ::= NULL |
1111 | | // |
1112 | | ByteString |
1113 | | CertStatus(OCSPResponseContext& context) |
1114 | 0 | { |
1115 | 0 | switch (context.certStatus) { |
1116 | 0 | // Both good and unknown are ultimately represented as NULL - the only |
1117 | 0 | // difference is in the tag that identifies them. |
1118 | 0 | case 0: |
1119 | 0 | case 2: |
1120 | 0 | { |
1121 | 0 | // XXX: MSVC 2015 wrongly warns about signed/unsigned conversion without |
1122 | 0 | // the static cast. |
1123 | 0 | return TLV(static_cast<uint8_t>(der::CONTEXT_SPECIFIC | |
1124 | 0 | context.certStatus), ByteString()); |
1125 | 0 | } |
1126 | 0 | case 1: |
1127 | 0 | { |
1128 | 0 | ByteString revocationTime(TimeToGeneralizedTime(context.revocationTime)); |
1129 | 0 | if (ENCODING_FAILED(revocationTime)) { |
1130 | 0 | return ByteString(); |
1131 | 0 | } |
1132 | 0 | // TODO(bug 980536): add support for revocationReason |
1133 | 0 | return TLV(der::CONTEXT_SPECIFIC | der::CONSTRUCTED | 1, revocationTime); |
1134 | 0 | } |
1135 | 0 | default: |
1136 | 0 | assert(false); |
1137 | 0 | // fall through |
1138 | 0 | } |
1139 | 0 | return ByteString(); |
1140 | 0 | } |
1141 | | |
1142 | | static const ByteString NO_UNUSED_BITS(1, 0x00); |
1143 | | |
1144 | | // The SubjectPublicKeyInfo syntax is specified in RFC 5280 Section 4.1. |
1145 | | TestKeyPair::TestKeyPair(const TestPublicKeyAlgorithm& aPublicKeyAlg, |
1146 | | const ByteString& spk) |
1147 | | : publicKeyAlg(aPublicKeyAlg) |
1148 | | , subjectPublicKeyInfo(TLV(der::SEQUENCE, |
1149 | | aPublicKeyAlg.algorithmIdentifier + |
1150 | | TLV(der::BIT_STRING, NO_UNUSED_BITS + spk))) |
1151 | | , subjectPublicKey(spk) |
1152 | 0 | { |
1153 | 0 | } |
1154 | | |
1155 | | } } } // namespace mozilla::pkix::test |