1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/test/revocation_builder.h"
6
7 #include "base/functional/callback.h"
8 #include "base/hash/sha1.h"
9 #include "base/strings/string_piece.h"
10 #include "base/strings/string_util.h"
11 #include "base/test/bind.h"
12 #include "net/cert/asn1_util.h"
13 #include "net/cert/time_conversions.h"
14 #include "net/cert/x509_util.h"
15 #include "net/test/cert_builder.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "third_party/boringssl/src/include/openssl/bytestring.h"
18 #include "third_party/boringssl/src/include/openssl/mem.h"
19 #include "third_party/boringssl/src/pki/input.h"
20
21 namespace net {
22
23 namespace {
24
Sha1()25 std::string Sha1() {
26 // SEQUENCE { OBJECT_IDENTIFIER { 1.3.14.3.2.26 } }
27 const uint8_t kSHA1[] = {0x30, 0x07, 0x06, 0x05, 0x2b,
28 0x0e, 0x03, 0x02, 0x1a};
29 return std::string(std::begin(kSHA1), std::end(kSHA1));
30 }
31
32 // Adds bytes (specified as a StringPiece) to the given CBB.
33 // The argument ordering follows the boringssl CBB_* api style.
CBBAddBytes(CBB * cbb,base::StringPiece bytes)34 bool CBBAddBytes(CBB* cbb, base::StringPiece bytes) {
35 return CBB_add_bytes(cbb, reinterpret_cast<const uint8_t*>(bytes.data()),
36 bytes.size());
37 }
38
39 // Adds bytes (from fixed size array) to the given CBB.
40 // The argument ordering follows the boringssl CBB_* api style.
41 template <size_t N>
CBBAddBytes(CBB * cbb,const uint8_t (& data)[N])42 bool CBBAddBytes(CBB* cbb, const uint8_t (&data)[N]) {
43 return CBB_add_bytes(cbb, data, N);
44 }
45
46 // Adds a GeneralizedTime value to the given CBB.
47 // The argument ordering follows the boringssl CBB_* api style.
CBBAddGeneralizedTime(CBB * cbb,const base::Time & time)48 bool CBBAddGeneralizedTime(CBB* cbb, const base::Time& time) {
49 bssl::der::GeneralizedTime generalized_time;
50 if (!EncodeTimeAsGeneralizedTime(time, &generalized_time)) {
51 return false;
52 }
53 CBB time_cbb;
54 uint8_t out[bssl::der::kGeneralizedTimeLength];
55 if (!bssl::der::EncodeGeneralizedTime(generalized_time, out) ||
56 !CBB_add_asn1(cbb, &time_cbb, CBS_ASN1_GENERALIZEDTIME) ||
57 !CBBAddBytes(&time_cbb, out) || !CBB_flush(cbb)) {
58 return false;
59 }
60 return true;
61 }
62
63 // Finalizes the CBB to a std::string.
FinishCBB(CBB * cbb)64 std::string FinishCBB(CBB* cbb) {
65 size_t cbb_len;
66 uint8_t* cbb_bytes;
67
68 if (!CBB_finish(cbb, &cbb_bytes, &cbb_len)) {
69 ADD_FAILURE() << "CBB_finish() failed";
70 return std::string();
71 }
72
73 bssl::UniquePtr<uint8_t> delete_bytes(cbb_bytes);
74 return std::string(reinterpret_cast<char*>(cbb_bytes), cbb_len);
75 }
76
PKeyToSPK(const EVP_PKEY * pkey)77 std::string PKeyToSPK(const EVP_PKEY* pkey) {
78 bssl::ScopedCBB cbb;
79 if (!CBB_init(cbb.get(), 64) || !EVP_marshal_public_key(cbb.get(), pkey)) {
80 ADD_FAILURE();
81 return std::string();
82 }
83 std::string spki = FinishCBB(cbb.get());
84
85 base::StringPiece spk;
86 if (!asn1::ExtractSubjectPublicKeyFromSPKI(spki, &spk)) {
87 ADD_FAILURE();
88 return std::string();
89 }
90
91 // ExtractSubjectPublicKeyFromSPKI() includes the unused bit count. For this
92 // application, the unused bit count must be zero, and is not included in the
93 // result.
94 if (spk.empty() || spk[0] != '\0') {
95 ADD_FAILURE();
96 return std::string();
97 }
98 spk.remove_prefix(1);
99
100 return std::string(spk);
101 }
102
103 // Returns a DER-encoded bssl::OCSPResponse with the given |response_status|.
104 // |response_type| and |response| are optional and may be empty.
EncodeOCSPResponse(bssl::OCSPResponse::ResponseStatus response_status,bssl::der::Input response_type,std::string response)105 std::string EncodeOCSPResponse(
106 bssl::OCSPResponse::ResponseStatus response_status,
107 bssl::der::Input response_type,
108 std::string response) {
109 // RFC 6960 section 4.2.1:
110 //
111 // bssl::OCSPResponse ::= SEQUENCE {
112 // responseStatus OCSPResponseStatus,
113 // responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
114 //
115 // OCSPResponseStatus ::= ENUMERATED {
116 // successful (0), -- Response has valid confirmations
117 // malformedRequest (1), -- Illegal confirmation request
118 // internalError (2), -- Internal error in issuer
119 // tryLater (3), -- Try again later
120 // -- (4) is not used
121 // sigRequired (5), -- Must sign the request
122 // unauthorized (6) -- Request unauthorized
123 // }
124 //
125 // The value for responseBytes consists of an OBJECT IDENTIFIER and a
126 // response syntax identified by that OID encoded as an OCTET STRING.
127 //
128 // ResponseBytes ::= SEQUENCE {
129 // responseType OBJECT IDENTIFIER,
130 // response OCTET STRING }
131 bssl::ScopedCBB cbb;
132 CBB ocsp_response, ocsp_response_status, ocsp_response_bytes,
133 ocsp_response_bytes_sequence, ocsp_response_type,
134 ocsp_response_octet_string;
135
136 if (!CBB_init(cbb.get(), 64 + response_type.Length() + response.size()) ||
137 !CBB_add_asn1(cbb.get(), &ocsp_response, CBS_ASN1_SEQUENCE) ||
138 !CBB_add_asn1(&ocsp_response, &ocsp_response_status,
139 CBS_ASN1_ENUMERATED) ||
140 !CBB_add_u8(&ocsp_response_status,
141 static_cast<uint8_t>(response_status))) {
142 ADD_FAILURE();
143 return std::string();
144 }
145
146 if (response_type.Length()) {
147 if (!CBB_add_asn1(&ocsp_response, &ocsp_response_bytes,
148 CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
149 !CBB_add_asn1(&ocsp_response_bytes, &ocsp_response_bytes_sequence,
150 CBS_ASN1_SEQUENCE) ||
151 !CBB_add_asn1(&ocsp_response_bytes_sequence, &ocsp_response_type,
152 CBS_ASN1_OBJECT) ||
153 !CBBAddBytes(&ocsp_response_type, response_type.AsStringView()) ||
154 !CBB_add_asn1(&ocsp_response_bytes_sequence,
155 &ocsp_response_octet_string, CBS_ASN1_OCTETSTRING) ||
156 !CBBAddBytes(&ocsp_response_octet_string, response)) {
157 ADD_FAILURE();
158 return std::string();
159 }
160 }
161
162 return FinishCBB(cbb.get());
163 }
164
165 // Adds a DER-encoded OCSP SingleResponse to |responses_cbb|.
166 // |issuer_name_hash| and |issuer_key_hash| should be binary SHA1 hashes.
AddOCSPSingleResponse(CBB * responses_cbb,const OCSPBuilderSingleResponse & response,const std::string & issuer_name_hash,const std::string & issuer_key_hash)167 bool AddOCSPSingleResponse(CBB* responses_cbb,
168 const OCSPBuilderSingleResponse& response,
169 const std::string& issuer_name_hash,
170 const std::string& issuer_key_hash) {
171 // RFC 6960 section 4.2.1:
172 //
173 // SingleResponse ::= SEQUENCE {
174 // certID CertID,
175 // certStatus CertStatus,
176 // thisUpdate GeneralizedTime,
177 // nextUpdate [0] EXPLICIT GeneralizedTime OPTIONAL,
178 // singleExtensions [1] EXPLICIT Extensions OPTIONAL }
179 //
180 // CertStatus ::= CHOICE {
181 // good [0] IMPLICIT NULL,
182 // revoked [1] IMPLICIT RevokedInfo,
183 // unknown [2] IMPLICIT UnknownInfo }
184 //
185 // RevokedInfo ::= SEQUENCE {
186 // revocationTime GeneralizedTime,
187 // revocationReason [0] EXPLICIT CRLReason OPTIONAL }
188 //
189 // UnknownInfo ::= NULL
190 //
191 // RFC 6960 section 4.1.1:
192 // CertID ::= SEQUENCE {
193 // hashAlgorithm AlgorithmIdentifier,
194 // issuerNameHash OCTET STRING, -- Hash of issuer's DN
195 // issuerKeyHash OCTET STRING, -- Hash of issuer's public key
196 // serialNumber CertificateSerialNumber }
197 //
198 // The contents of CertID include the following fields:
199 //
200 // o hashAlgorithm is the hash algorithm used to generate the
201 // issuerNameHash and issuerKeyHash values.
202 //
203 // o issuerNameHash is the hash of the issuer's distinguished name
204 // (DN). The hash shall be calculated over the DER encoding of the
205 // issuer's name field in the certificate being checked.
206 //
207 // o issuerKeyHash is the hash of the issuer's public key. The hash
208 // shall be calculated over the value (excluding tag and length) of
209 // the subject public key field in the issuer's certificate.
210 //
211 // o serialNumber is the serial number of the certificate for which
212 // status is being requested.
213
214 CBB single_response, issuer_name_hash_cbb, issuer_key_hash_cbb, cert_id;
215 if (!CBB_add_asn1(responses_cbb, &single_response, CBS_ASN1_SEQUENCE) ||
216 !CBB_add_asn1(&single_response, &cert_id, CBS_ASN1_SEQUENCE) ||
217 !CBBAddBytes(&cert_id, Sha1()) ||
218 !CBB_add_asn1(&cert_id, &issuer_name_hash_cbb, CBS_ASN1_OCTETSTRING) ||
219 !CBBAddBytes(&issuer_name_hash_cbb, issuer_name_hash) ||
220 !CBB_add_asn1(&cert_id, &issuer_key_hash_cbb, CBS_ASN1_OCTETSTRING) ||
221 !CBBAddBytes(&issuer_key_hash_cbb, issuer_key_hash) ||
222 !CBB_add_asn1_uint64(&cert_id, response.serial)) {
223 ADD_FAILURE();
224 return false;
225 }
226
227 unsigned int cert_status_tag_number;
228 switch (response.cert_status) {
229 case bssl::OCSPRevocationStatus::GOOD:
230 cert_status_tag_number = CBS_ASN1_CONTEXT_SPECIFIC | 0;
231 break;
232 case bssl::OCSPRevocationStatus::REVOKED:
233 cert_status_tag_number =
234 CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 1;
235 break;
236 case bssl::OCSPRevocationStatus::UNKNOWN:
237 cert_status_tag_number = CBS_ASN1_CONTEXT_SPECIFIC | 2;
238 break;
239 }
240
241 CBB cert_status_cbb;
242 if (!CBB_add_asn1(&single_response, &cert_status_cbb,
243 cert_status_tag_number)) {
244 ADD_FAILURE();
245 return false;
246 }
247 if (response.cert_status == bssl::OCSPRevocationStatus::REVOKED &&
248 !CBBAddGeneralizedTime(&cert_status_cbb, response.revocation_time)) {
249 ADD_FAILURE();
250 return false;
251 }
252
253 CBB next_update_cbb;
254 if (!CBBAddGeneralizedTime(&single_response, response.this_update) ||
255 !CBB_add_asn1(&single_response, &next_update_cbb,
256 CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
257 !CBBAddGeneralizedTime(&next_update_cbb, response.next_update)) {
258 ADD_FAILURE();
259 return false;
260 }
261
262 return CBB_flush(responses_cbb);
263 }
264
265 } // namespace
266
BuildOCSPResponseError(bssl::OCSPResponse::ResponseStatus response_status)267 std::string BuildOCSPResponseError(
268 bssl::OCSPResponse::ResponseStatus response_status) {
269 DCHECK_NE(response_status, bssl::OCSPResponse::ResponseStatus::SUCCESSFUL);
270 return EncodeOCSPResponse(response_status, bssl::der::Input(), std::string());
271 }
272
BuildOCSPResponse(const std::string & responder_subject,EVP_PKEY * responder_key,base::Time produced_at,const std::vector<OCSPBuilderSingleResponse> & responses)273 std::string BuildOCSPResponse(
274 const std::string& responder_subject,
275 EVP_PKEY* responder_key,
276 base::Time produced_at,
277 const std::vector<OCSPBuilderSingleResponse>& responses) {
278 std::string responder_name_hash = base::SHA1HashString(responder_subject);
279 std::string responder_key_hash =
280 base::SHA1HashString(PKeyToSPK(responder_key));
281
282 // RFC 6960 section 4.2.1:
283 //
284 // ResponseData ::= SEQUENCE {
285 // version [0] EXPLICIT Version DEFAULT v1,
286 // responderID ResponderID,
287 // producedAt GeneralizedTime,
288 // responses SEQUENCE OF SingleResponse,
289 // responseExtensions [1] EXPLICIT Extensions OPTIONAL }
290 //
291 // ResponderID ::= CHOICE {
292 // byName [1] Name,
293 // byKey [2] KeyHash }
294 //
295 // KeyHash ::= OCTET STRING -- SHA-1 hash of responder's public key
296 // (excluding the tag and length fields)
297 bssl::ScopedCBB tbs_cbb;
298 CBB response_data, responder_id, responder_id_by_key, responses_cbb;
299 if (!CBB_init(tbs_cbb.get(), 64) ||
300 !CBB_add_asn1(tbs_cbb.get(), &response_data, CBS_ASN1_SEQUENCE) ||
301 // Version is the default v1, so it is not encoded.
302 !CBB_add_asn1(&response_data, &responder_id,
303 CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 2) ||
304 !CBB_add_asn1(&responder_id, &responder_id_by_key,
305 CBS_ASN1_OCTETSTRING) ||
306 !CBBAddBytes(&responder_id_by_key, responder_key_hash) ||
307 !CBBAddGeneralizedTime(&response_data, produced_at) ||
308 !CBB_add_asn1(&response_data, &responses_cbb, CBS_ASN1_SEQUENCE)) {
309 ADD_FAILURE();
310 return std::string();
311 }
312
313 for (const auto& response : responses) {
314 if (!AddOCSPSingleResponse(&responses_cbb, response, responder_name_hash,
315 responder_key_hash)) {
316 return std::string();
317 }
318 }
319
320 // responseExtensions not currently supported.
321
322 return BuildOCSPResponseWithResponseData(responder_key,
323 FinishCBB(tbs_cbb.get()));
324 }
325
BuildOCSPResponseWithResponseData(EVP_PKEY * responder_key,const std::string & tbs_response_data,absl::optional<bssl::SignatureAlgorithm> signature_algorithm)326 std::string BuildOCSPResponseWithResponseData(
327 EVP_PKEY* responder_key,
328 const std::string& tbs_response_data,
329 absl::optional<bssl::SignatureAlgorithm> signature_algorithm) {
330 // For a basic OCSP responder, responseType will be id-pkix-ocsp-basic.
331 //
332 // id-pkix-ocsp OBJECT IDENTIFIER ::= { id-ad-ocsp }
333 // id-pkix-ocsp-basic OBJECT IDENTIFIER ::= { id-pkix-ocsp 1 }
334 //
335 // The value for response SHALL be the DER encoding of
336 // BasicOCSPResponse.
337 //
338 // BasicOCSPResponse ::= SEQUENCE {
339 // tbsResponseData ResponseData,
340 // signatureAlgorithm AlgorithmIdentifier,
341 // signature BIT STRING,
342 // certs [0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
343 //
344 // The value for signature SHALL be computed on the hash of the DER
345 // encoding of ResponseData. The responder MAY include certificates in
346 // the certs field of BasicOCSPResponse that help the OCSP client verify
347 // the responder's signature. If no certificates are included, then
348 // certs SHOULD be absent.
349 //
350 bssl::ScopedCBB basic_ocsp_response_cbb;
351 CBB basic_ocsp_response, signature;
352 if (!responder_key) {
353 ADD_FAILURE();
354 return std::string();
355 }
356 if (!signature_algorithm)
357 signature_algorithm =
358 CertBuilder::DefaultSignatureAlgorithmForKey(responder_key);
359 if (!signature_algorithm) {
360 ADD_FAILURE();
361 return std::string();
362 }
363 std::string signature_algorithm_tlv =
364 CertBuilder::SignatureAlgorithmToDer(*signature_algorithm);
365 if (signature_algorithm_tlv.empty() ||
366 !CBB_init(basic_ocsp_response_cbb.get(), 64 + tbs_response_data.size()) ||
367 !CBB_add_asn1(basic_ocsp_response_cbb.get(), &basic_ocsp_response,
368 CBS_ASN1_SEQUENCE) ||
369 !CBBAddBytes(&basic_ocsp_response, tbs_response_data) ||
370 !CBBAddBytes(&basic_ocsp_response, signature_algorithm_tlv) ||
371 !CBB_add_asn1(&basic_ocsp_response, &signature, CBS_ASN1_BITSTRING) ||
372 !CBB_add_u8(&signature, 0 /* no unused bits */) ||
373 !CertBuilder::SignData(*signature_algorithm, tbs_response_data,
374 responder_key, &signature)) {
375 ADD_FAILURE();
376 return std::string();
377 }
378
379 // certs field not currently supported.
380
381 return EncodeOCSPResponse(bssl::OCSPResponse::ResponseStatus::SUCCESSFUL,
382 bssl::der::Input(bssl::kBasicOCSPResponseOid),
383 FinishCBB(basic_ocsp_response_cbb.get()));
384 }
385
BuildCrlWithSigner(const std::string & crl_issuer_subject,EVP_PKEY * crl_issuer_key,const std::vector<uint64_t> & revoked_serials,const std::string & signature_algorithm_tlv,base::OnceCallback<bool (std::string,CBB *)> signer)386 std::string BuildCrlWithSigner(
387 const std::string& crl_issuer_subject,
388 EVP_PKEY* crl_issuer_key,
389 const std::vector<uint64_t>& revoked_serials,
390 const std::string& signature_algorithm_tlv,
391 base::OnceCallback<bool(std::string, CBB*)> signer) {
392 if (!crl_issuer_key) {
393 ADD_FAILURE();
394 return std::string();
395 }
396 // TBSCertList ::= SEQUENCE {
397 // version Version OPTIONAL,
398 // -- if present, MUST be v2
399 // signature AlgorithmIdentifier,
400 // issuer Name,
401 // thisUpdate Time,
402 // nextUpdate Time OPTIONAL,
403 // revokedCertificates SEQUENCE OF SEQUENCE {
404 // userCertificate CertificateSerialNumber,
405 // revocationDate Time,
406 // crlEntryExtensions Extensions OPTIONAL
407 // -- if present, version MUST be v2
408 // } OPTIONAL,
409 // crlExtensions [0] EXPLICIT Extensions OPTIONAL
410 // -- if present, version MUST be v2
411 // }
412 bssl::ScopedCBB tbs_cbb;
413 CBB tbs_cert_list, revoked_serials_cbb;
414 if (!CBB_init(tbs_cbb.get(), 10) ||
415 !CBB_add_asn1(tbs_cbb.get(), &tbs_cert_list, CBS_ASN1_SEQUENCE) ||
416 !CBB_add_asn1_uint64(&tbs_cert_list, 1 /* V2 */) ||
417 !CBBAddBytes(&tbs_cert_list, signature_algorithm_tlv) ||
418 !CBBAddBytes(&tbs_cert_list, crl_issuer_subject) ||
419 !x509_util::CBBAddTime(&tbs_cert_list,
420 base::Time::Now() - base::Days(1)) ||
421 !x509_util::CBBAddTime(&tbs_cert_list,
422 base::Time::Now() + base::Days(6))) {
423 ADD_FAILURE();
424 return std::string();
425 }
426 if (!revoked_serials.empty()) {
427 if (!CBB_add_asn1(&tbs_cert_list, &revoked_serials_cbb,
428 CBS_ASN1_SEQUENCE)) {
429 ADD_FAILURE();
430 return std::string();
431 }
432 for (const int64_t revoked_serial : revoked_serials) {
433 CBB revoked_serial_cbb;
434 if (!CBB_add_asn1(&revoked_serials_cbb, &revoked_serial_cbb,
435 CBS_ASN1_SEQUENCE) ||
436 !CBB_add_asn1_uint64(&revoked_serial_cbb, revoked_serial) ||
437 !x509_util::CBBAddTime(&revoked_serial_cbb,
438 base::Time::Now() - base::Days(1)) ||
439 !CBB_flush(&revoked_serials_cbb)) {
440 ADD_FAILURE();
441 return std::string();
442 }
443 }
444 }
445
446 std::string tbs_tlv = FinishCBB(tbs_cbb.get());
447
448 // CertificateList ::= SEQUENCE {
449 // tbsCertList TBSCertList,
450 // signatureAlgorithm AlgorithmIdentifier,
451 // signatureValue BIT STRING }
452 bssl::ScopedCBB crl_cbb;
453 CBB cert_list, signature;
454 if (!CBB_init(crl_cbb.get(), 10) ||
455 !CBB_add_asn1(crl_cbb.get(), &cert_list, CBS_ASN1_SEQUENCE) ||
456 !CBBAddBytes(&cert_list, tbs_tlv) ||
457 !CBBAddBytes(&cert_list, signature_algorithm_tlv) ||
458 !CBB_add_asn1(&cert_list, &signature, CBS_ASN1_BITSTRING) ||
459 !CBB_add_u8(&signature, 0 /* no unused bits */) ||
460 !std::move(signer).Run(tbs_tlv, &signature)) {
461 ADD_FAILURE();
462 return std::string();
463 }
464 return FinishCBB(crl_cbb.get());
465 }
466
BuildCrl(const std::string & crl_issuer_subject,EVP_PKEY * crl_issuer_key,const std::vector<uint64_t> & revoked_serials,absl::optional<bssl::SignatureAlgorithm> signature_algorithm)467 std::string BuildCrl(
468 const std::string& crl_issuer_subject,
469 EVP_PKEY* crl_issuer_key,
470 const std::vector<uint64_t>& revoked_serials,
471 absl::optional<bssl::SignatureAlgorithm> signature_algorithm) {
472 if (!signature_algorithm) {
473 signature_algorithm =
474 CertBuilder::DefaultSignatureAlgorithmForKey(crl_issuer_key);
475 }
476 if (!signature_algorithm) {
477 ADD_FAILURE();
478 return std::string();
479 }
480 std::string signature_algorithm_tlv =
481 CertBuilder::SignatureAlgorithmToDer(*signature_algorithm);
482 if (signature_algorithm_tlv.empty()) {
483 ADD_FAILURE();
484 return std::string();
485 }
486
487 auto signer =
488 base::BindLambdaForTesting([&](std::string tbs_tlv, CBB* signature) {
489 return CertBuilder::SignData(*signature_algorithm, tbs_tlv,
490 crl_issuer_key, signature);
491 });
492 return BuildCrlWithSigner(crl_issuer_subject, crl_issuer_key, revoked_serials,
493 signature_algorithm_tlv, signer);
494 }
495
BuildCrlWithAlgorithmTlvAndDigest(const std::string & crl_issuer_subject,EVP_PKEY * crl_issuer_key,const std::vector<uint64_t> & revoked_serials,const std::string & signature_algorithm_tlv,const EVP_MD * digest)496 std::string BuildCrlWithAlgorithmTlvAndDigest(
497 const std::string& crl_issuer_subject,
498 EVP_PKEY* crl_issuer_key,
499 const std::vector<uint64_t>& revoked_serials,
500 const std::string& signature_algorithm_tlv,
501 const EVP_MD* digest) {
502 auto signer =
503 base::BindLambdaForTesting([&](std::string tbs_tlv, CBB* signature) {
504 return CertBuilder::SignDataWithDigest(digest, tbs_tlv, crl_issuer_key,
505 signature);
506 });
507 return BuildCrlWithSigner(crl_issuer_subject, crl_issuer_key, revoked_serials,
508 signature_algorithm_tlv, signer);
509 }
510
511 } // namespace net
512