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