Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021-2022 Project CHIP Authors
4 : * All rights reserved.
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : /**
20 : * @file
21 : * This file implements methods for generating CHIP X.509 certificate.
22 : *
23 : */
24 :
25 : #include <algorithm>
26 : #include <initializer_list>
27 : #include <inttypes.h>
28 : #include <stddef.h>
29 :
30 : #include <credentials/CHIPCert_Internal.h>
31 : #include <lib/asn1/ASN1.h>
32 : #include <lib/asn1/ASN1Macros.h>
33 : #include <lib/core/CHIPCore.h>
34 : #include <lib/core/CHIPSafeCasts.h>
35 : #include <lib/support/CodeUtils.h>
36 : #include <lib/support/DLLUtil.h>
37 : #include <protocols/Protocols.h>
38 :
39 : namespace chip {
40 : namespace Credentials {
41 :
42 : using namespace chip::ASN1;
43 : using namespace chip::Crypto;
44 : using namespace chip::Protocols;
45 :
46 : namespace {
47 :
48 : enum IsCACert
49 : {
50 : kCACert,
51 : kNotCACert,
52 : };
53 :
54 253 : CHIP_ERROR EncodeSubjectPublicKeyInfo(const Crypto::P256PublicKey & pubkey, ASN1Writer & writer)
55 : {
56 253 : CHIP_ERROR err = CHIP_NO_ERROR;
57 :
58 253 : ASN1_START_SEQUENCE
59 : {
60 253 : ASN1_START_SEQUENCE
61 : {
62 253 : ASN1_ENCODE_OBJECT_ID(kOID_PubKeyAlgo_ECPublicKey);
63 253 : ASN1_ENCODE_OBJECT_ID(kOID_EllipticCurve_prime256v1);
64 : }
65 253 : ASN1_END_SEQUENCE;
66 :
67 253 : ReturnErrorOnFailure(writer.PutBitString(0, pubkey, static_cast<uint8_t>(pubkey.Length())));
68 : }
69 253 : ASN1_END_SEQUENCE;
70 :
71 253 : exit:
72 253 : return err;
73 : }
74 :
75 248 : CHIP_ERROR EncodeAuthorityKeyIdentifierExtension(const Crypto::P256PublicKey & pubkey, ASN1Writer & writer)
76 : {
77 248 : CHIP_ERROR err = CHIP_NO_ERROR;
78 :
79 248 : ASN1_START_SEQUENCE
80 : {
81 248 : ASN1_ENCODE_OBJECT_ID(kOID_Extension_AuthorityKeyIdentifier);
82 :
83 248 : ASN1_START_OCTET_STRING_ENCAPSULATED
84 : {
85 248 : ASN1_START_SEQUENCE
86 : {
87 : uint8_t keyid[kSHA1_Hash_Length];
88 248 : ReturnErrorOnFailure(Crypto::Hash_SHA1(pubkey, pubkey.Length(), keyid));
89 :
90 248 : ReturnErrorOnFailure(
91 : writer.PutOctetString(kASN1TagClass_ContextSpecific, 0, keyid, static_cast<uint8_t>(sizeof(keyid))));
92 : }
93 248 : ASN1_END_SEQUENCE;
94 : }
95 248 : ASN1_END_ENCAPSULATED;
96 : }
97 248 : ASN1_END_SEQUENCE;
98 :
99 248 : exit:
100 248 : return err;
101 : }
102 :
103 248 : CHIP_ERROR EncodeSubjectKeyIdentifierExtension(const Crypto::P256PublicKey & pubkey, ASN1Writer & writer)
104 : {
105 248 : CHIP_ERROR err = CHIP_NO_ERROR;
106 :
107 248 : ASN1_START_SEQUENCE
108 : {
109 248 : ASN1_ENCODE_OBJECT_ID(kOID_Extension_SubjectKeyIdentifier);
110 :
111 248 : ASN1_START_OCTET_STRING_ENCAPSULATED
112 : {
113 : uint8_t keyid[kSHA1_Hash_Length];
114 248 : ReturnErrorOnFailure(Crypto::Hash_SHA1(pubkey, pubkey.Length(), keyid));
115 :
116 248 : ReturnErrorOnFailure(writer.PutOctetString(keyid, static_cast<uint8_t>(sizeof(keyid))));
117 : }
118 248 : ASN1_END_ENCAPSULATED;
119 : }
120 248 : ASN1_END_SEQUENCE;
121 :
122 248 : exit:
123 248 : return err;
124 : }
125 :
126 111 : CHIP_ERROR EncodeExtKeyUsageExtension(std::initializer_list<OID> keyPurposeOIDs, ASN1Writer & writer)
127 : {
128 111 : CHIP_ERROR err = CHIP_NO_ERROR;
129 111 : ASN1_START_SEQUENCE
130 : {
131 111 : ASN1_ENCODE_OBJECT_ID(kOID_Extension_ExtendedKeyUsage);
132 :
133 : // ExtKeyUsage extension MUST be marked as critical.
134 111 : ASN1_ENCODE_BOOLEAN(true);
135 111 : ASN1_START_OCTET_STRING_ENCAPSULATED
136 : {
137 111 : ASN1_START_SEQUENCE
138 : {
139 333 : for (auto && oid : keyPurposeOIDs)
140 : {
141 222 : ASN1_ENCODE_OBJECT_ID(oid);
142 : }
143 : }
144 111 : ASN1_END_SEQUENCE;
145 : }
146 111 : ASN1_END_ENCAPSULATED;
147 : }
148 111 : ASN1_END_SEQUENCE;
149 :
150 111 : exit:
151 111 : return err;
152 : }
153 :
154 253 : CHIP_ERROR EncodeKeyUsageExtension(BitFlags<KeyUsageFlags> keyUsageFlags, ASN1Writer & writer)
155 : {
156 253 : CHIP_ERROR err = CHIP_NO_ERROR;
157 253 : ASN1_START_SEQUENCE
158 : {
159 253 : ASN1_ENCODE_OBJECT_ID(kOID_Extension_KeyUsage);
160 :
161 : // KeyUsage extension MUST be marked as critical.
162 253 : ASN1_ENCODE_BOOLEAN(true);
163 253 : ASN1_START_OCTET_STRING_ENCAPSULATED
164 : {
165 253 : ASN1_ENCODE_BIT_STRING(keyUsageFlags.Raw());
166 : }
167 253 : ASN1_END_ENCAPSULATED;
168 : }
169 253 : ASN1_END_SEQUENCE;
170 :
171 253 : exit:
172 253 : return err;
173 : }
174 :
175 253 : CHIP_ERROR EncodeIsCAExtension(IsCACert isCA, ASN1Writer & writer)
176 : {
177 253 : CHIP_ERROR err = CHIP_NO_ERROR;
178 253 : ASN1_START_SEQUENCE
179 : {
180 253 : ASN1_ENCODE_OBJECT_ID(kOID_Extension_BasicConstraints);
181 :
182 : // BasicConstraints extension MUST be marked as critical.
183 253 : ASN1_ENCODE_BOOLEAN(true);
184 :
185 253 : ASN1_START_OCTET_STRING_ENCAPSULATED
186 : {
187 253 : ASN1_START_SEQUENCE
188 : {
189 : // cA BOOLEAN
190 253 : if (isCA == kCACert)
191 : {
192 : // Encode the boolean only if isCA is true
193 138 : ASN1_ENCODE_BOOLEAN(true);
194 : }
195 : }
196 253 : ASN1_END_SEQUENCE;
197 : }
198 253 : ASN1_END_ENCAPSULATED;
199 : }
200 253 : ASN1_END_SEQUENCE;
201 :
202 253 : exit:
203 253 : return err;
204 : }
205 :
206 138 : CHIP_ERROR EncodeCASpecificExtensions(ASN1Writer & writer)
207 : {
208 138 : ReturnErrorOnFailure(EncodeIsCAExtension(kCACert, writer));
209 138 : ReturnErrorOnFailure(
210 : EncodeKeyUsageExtension(BitFlags<KeyUsageFlags>(KeyUsageFlags::kKeyCertSign, KeyUsageFlags::kCRLSign), writer));
211 138 : return CHIP_NO_ERROR;
212 : }
213 :
214 106 : CHIP_ERROR EncodeNOCSpecificExtensions(ASN1Writer & writer)
215 : {
216 106 : ReturnErrorOnFailure(EncodeIsCAExtension(kNotCACert, writer));
217 106 : ReturnErrorOnFailure(EncodeKeyUsageExtension(KeyUsageFlags::kDigitalSignature, writer));
218 106 : ReturnErrorOnFailure(EncodeExtKeyUsageExtension({ kOID_KeyPurpose_ClientAuth, kOID_KeyPurpose_ServerAuth }, writer));
219 106 : return CHIP_NO_ERROR;
220 : }
221 :
222 4 : CHIP_ERROR EncodeVendorIdVerificationSignerSpecificExtensions(ASN1Writer & writer)
223 : {
224 4 : ReturnErrorOnFailure(EncodeIsCAExtension(kNotCACert, writer));
225 4 : ReturnErrorOnFailure(EncodeKeyUsageExtension(KeyUsageFlags::kDigitalSignature, writer));
226 4 : return CHIP_NO_ERROR;
227 : }
228 :
229 248 : CHIP_ERROR EncodeFutureExtension(const Optional<FutureExtension> & futureExt, ASN1Writer & writer)
230 : {
231 248 : CHIP_ERROR err = CHIP_NO_ERROR;
232 :
233 248 : VerifyOrReturnError(futureExt.HasValue(), CHIP_NO_ERROR);
234 :
235 16 : ASN1_START_SEQUENCE
236 : {
237 16 : ReturnErrorOnFailure(writer.PutObjectId(futureExt.Value().OID.data(), static_cast<uint16_t>(futureExt.Value().OID.size())));
238 :
239 16 : ASN1_START_OCTET_STRING_ENCAPSULATED
240 : {
241 16 : ReturnErrorOnFailure(writer.PutOctetString(futureExt.Value().Extension.data(),
242 : static_cast<uint16_t>(futureExt.Value().Extension.size())));
243 : }
244 16 : ASN1_END_ENCAPSULATED;
245 : }
246 16 : ASN1_END_SEQUENCE;
247 :
248 16 : exit:
249 16 : return err;
250 : }
251 :
252 248 : CHIP_ERROR EncodeExtensions(CertType certType, const Crypto::P256PublicKey & SKI, const Crypto::P256PublicKey & AKI,
253 : const Optional<FutureExtension> & futureExt, ASN1Writer & writer)
254 : {
255 248 : CHIP_ERROR err = CHIP_NO_ERROR;
256 :
257 248 : ASN1_START_CONSTRUCTED(kASN1TagClass_ContextSpecific, 3)
258 : {
259 248 : ASN1_START_SEQUENCE
260 : {
261 248 : switch (certType)
262 : {
263 138 : case CertType::kICA:
264 : case CertType::kRoot: {
265 138 : ReturnErrorOnFailure(EncodeCASpecificExtensions(writer));
266 138 : break;
267 : }
268 106 : case CertType::kNode: {
269 106 : ReturnErrorOnFailure(EncodeNOCSpecificExtensions(writer));
270 106 : break;
271 : }
272 4 : case CertType::kVidVerificationSigner: {
273 4 : ReturnErrorOnFailure(EncodeVendorIdVerificationSignerSpecificExtensions(writer));
274 4 : break;
275 : }
276 0 : case CertType::kFirmwareSigning:
277 : case CertType::kNetworkIdentity: {
278 : // Nothing to encode extra for those.
279 0 : break;
280 : }
281 0 : default: {
282 : // Unknown/invalid certificate type should not happen.
283 0 : return CHIP_ERROR_INVALID_ARGUMENT;
284 : }
285 : }
286 248 : ReturnErrorOnFailure(EncodeSubjectKeyIdentifierExtension(SKI, writer));
287 :
288 248 : ReturnErrorOnFailure(EncodeAuthorityKeyIdentifierExtension(AKI, writer));
289 :
290 248 : ReturnErrorOnFailure(EncodeFutureExtension(futureExt, writer));
291 : }
292 248 : ASN1_END_SEQUENCE;
293 : }
294 248 : ASN1_END_CONSTRUCTED;
295 :
296 248 : exit:
297 248 : return err;
298 : }
299 :
300 253 : CHIP_ERROR EncodeValidity(uint32_t validityStart, uint32_t validityEnd, ASN1Writer & writer)
301 : {
302 253 : CHIP_ERROR err = CHIP_NO_ERROR;
303 : ASN1UniversalTime asn1Time;
304 :
305 253 : ASN1_START_SEQUENCE
306 : {
307 253 : ReturnErrorOnFailure(ChipEpochToASN1Time(validityStart, asn1Time));
308 253 : ASN1_ENCODE_TIME(asn1Time);
309 :
310 253 : ReturnErrorOnFailure(ChipEpochToASN1Time(validityEnd, asn1Time));
311 253 : ASN1_ENCODE_TIME(asn1Time);
312 : }
313 253 : ASN1_END_SEQUENCE;
314 :
315 253 : exit:
316 253 : return err;
317 : }
318 :
319 124 : CHIP_ERROR EncodeChipECDSASignature(Crypto::P256ECDSASignature & signature, ASN1Writer & writer)
320 : {
321 124 : CHIP_ERROR err = CHIP_NO_ERROR;
322 :
323 124 : ASN1_START_BIT_STRING_ENCAPSULATED
324 : {
325 : // Convert RAW signature to DER when generating X509 certs.
326 124 : P256ECDSASignatureSpan raw_sig(signature.Bytes());
327 124 : ReturnErrorOnFailure(ConvertECDSASignatureRawToDER(raw_sig, writer));
328 : }
329 124 : ASN1_END_ENCAPSULATED;
330 :
331 124 : exit:
332 124 : return err;
333 : }
334 :
335 251 : CHIP_ERROR EncodeTBSCert(const X509CertRequestParams & requestParams, const Crypto::P256PublicKey & subjectPubkey,
336 : const Crypto::P256PublicKey & issuerPubkey, ASN1Writer & writer)
337 : {
338 251 : CHIP_ERROR err = CHIP_NO_ERROR;
339 : CertType certType;
340 :
341 251 : VerifyOrReturnError(requestParams.SerialNumber >= 0, CHIP_ERROR_INVALID_ARGUMENT);
342 248 : VerifyOrReturnError(requestParams.ValidityEnd == kNullCertTime || requestParams.ValidityEnd >= requestParams.ValidityStart,
343 : CHIP_ERROR_INVALID_ARGUMENT);
344 :
345 248 : ReturnErrorOnFailure(requestParams.SubjectDN.GetCertType(certType));
346 :
347 248 : ASN1_START_SEQUENCE
348 : {
349 : // version [0] EXPLICIT Version DEFAULT v1
350 248 : ASN1_START_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0)
351 : {
352 : // Version ::= INTEGER { v1(0), v2(1), v3(2) }
353 248 : ASN1_ENCODE_INTEGER(2);
354 : }
355 248 : ASN1_END_CONSTRUCTED;
356 :
357 248 : ReturnErrorOnFailure(writer.PutInteger(requestParams.SerialNumber));
358 :
359 248 : ASN1_START_SEQUENCE
360 : {
361 248 : ASN1_ENCODE_OBJECT_ID(kOID_SigAlgo_ECDSAWithSHA256);
362 : }
363 248 : ASN1_END_SEQUENCE;
364 :
365 : // issuer Name
366 248 : ReturnErrorOnFailure(requestParams.IssuerDN.EncodeToASN1(writer));
367 :
368 : // validity Validity,
369 248 : ReturnErrorOnFailure(EncodeValidity(requestParams.ValidityStart, requestParams.ValidityEnd, writer));
370 :
371 : // subject Name
372 248 : ReturnErrorOnFailure(requestParams.SubjectDN.EncodeToASN1(writer));
373 :
374 248 : ReturnErrorOnFailure(EncodeSubjectPublicKeyInfo(subjectPubkey, writer));
375 :
376 : // certificate extensions
377 248 : ReturnErrorOnFailure(EncodeExtensions(certType, subjectPubkey, issuerPubkey, requestParams.FutureExt, writer));
378 : }
379 248 : ASN1_END_SEQUENCE;
380 :
381 248 : exit:
382 248 : return err;
383 : }
384 :
385 : } // namespace
386 :
387 5 : CHIP_ERROR EncodeNetworkIdentityTBSCert(const P256PublicKey & pubkey, ASN1Writer & writer)
388 : {
389 5 : CHIP_ERROR err = CHIP_NO_ERROR;
390 5 : ChipDN issuerAndSubject;
391 5 : InitNetworkIdentitySubject(issuerAndSubject);
392 :
393 5 : ASN1_START_SEQUENCE
394 : {
395 : // version [0] EXPLICIT Version DEFAULT v1
396 5 : ASN1_START_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0)
397 : {
398 5 : ASN1_ENCODE_INTEGER(2); // Version ::= INTEGER { v1(0), v2(1), v3(2) }
399 : }
400 5 : ASN1_END_CONSTRUCTED;
401 :
402 5 : ReturnErrorOnFailure(writer.PutInteger(kNetworkIdentitySerialNumber));
403 :
404 5 : ASN1_START_SEQUENCE
405 : {
406 5 : ASN1_ENCODE_OBJECT_ID(kOID_SigAlgo_ECDSAWithSHA256);
407 : }
408 5 : ASN1_END_SEQUENCE;
409 :
410 : // issuer Name
411 5 : ReturnErrorOnFailure(issuerAndSubject.EncodeToASN1(writer));
412 :
413 : // validity Validity,
414 5 : ReturnErrorOnFailure(EncodeValidity(kNetworkIdentityNotBeforeTime, kNetworkIdentityNotAfterTime, writer));
415 :
416 : // subject Name
417 5 : ReturnErrorOnFailure(issuerAndSubject.EncodeToASN1(writer));
418 :
419 5 : ReturnErrorOnFailure(EncodeSubjectPublicKeyInfo(pubkey, writer));
420 :
421 : // certificate extensions
422 5 : ASN1_START_CONSTRUCTED(kASN1TagClass_ContextSpecific, 3)
423 : {
424 5 : ASN1_START_SEQUENCE
425 : {
426 5 : TEMPORARY_RETURN_IGNORED EncodeIsCAExtension(kNotCACert, writer);
427 5 : TEMPORARY_RETURN_IGNORED EncodeKeyUsageExtension(KeyUsageFlags::kDigitalSignature, writer);
428 5 : TEMPORARY_RETURN_IGNORED EncodeExtKeyUsageExtension({ kOID_KeyPurpose_ClientAuth, kOID_KeyPurpose_ServerAuth },
429 : writer);
430 : }
431 5 : ASN1_END_SEQUENCE;
432 : }
433 5 : ASN1_END_CONSTRUCTED;
434 : }
435 5 : ASN1_END_SEQUENCE;
436 :
437 5 : exit:
438 5 : return err;
439 5 : }
440 :
441 127 : CHIP_ERROR NewChipX509Cert(const X509CertRequestParams & requestParams, const Crypto::P256PublicKey & subjectPubkey,
442 : const Crypto::P256Keypair & issuerKeypair, MutableByteSpan & x509Cert)
443 : {
444 127 : CHIP_ERROR err = CHIP_NO_ERROR;
445 : ASN1Writer writer;
446 127 : writer.Init(x509Cert);
447 :
448 127 : ReturnErrorOnFailure(EncodeTBSCert(requestParams, subjectPubkey, issuerKeypair.Pubkey(), writer));
449 :
450 124 : Crypto::P256ECDSASignature signature;
451 124 : ReturnErrorOnFailure(issuerKeypair.ECDSA_sign_msg(x509Cert.data(), writer.GetLengthWritten(), signature));
452 :
453 124 : writer.Init(x509Cert);
454 :
455 124 : ASN1_START_SEQUENCE
456 : {
457 124 : ReturnErrorOnFailure(EncodeTBSCert(requestParams, subjectPubkey, issuerKeypair.Pubkey(), writer));
458 :
459 124 : ASN1_START_SEQUENCE
460 : {
461 124 : ASN1_ENCODE_OBJECT_ID(kOID_SigAlgo_ECDSAWithSHA256);
462 : }
463 124 : ASN1_END_SEQUENCE;
464 :
465 124 : ReturnErrorOnFailure(EncodeChipECDSASignature(signature, writer));
466 : }
467 124 : ASN1_END_SEQUENCE;
468 :
469 124 : x509Cert.reduce_size(writer.GetLengthWritten());
470 :
471 124 : exit:
472 124 : return err;
473 124 : }
474 :
475 42 : DLL_EXPORT CHIP_ERROR NewRootX509Cert(const X509CertRequestParams & requestParams, const Crypto::P256Keypair & issuerKeypair,
476 : MutableByteSpan & x509Cert)
477 : {
478 : CertType certType;
479 :
480 42 : ReturnErrorOnFailure(requestParams.SubjectDN.GetCertType(certType));
481 42 : VerifyOrReturnError(certType == CertType::kRoot, CHIP_ERROR_INVALID_ARGUMENT);
482 41 : VerifyOrReturnError(requestParams.SubjectDN.IsEqual(requestParams.IssuerDN), CHIP_ERROR_INVALID_ARGUMENT);
483 :
484 40 : return NewChipX509Cert(requestParams, issuerKeypair.Pubkey(), issuerKeypair, x509Cert);
485 : }
486 :
487 32 : DLL_EXPORT CHIP_ERROR NewICAX509Cert(const X509CertRequestParams & requestParams, const Crypto::P256PublicKey & subjectPubkey,
488 : const Crypto::P256Keypair & issuerKeypair, MutableByteSpan & x509Cert)
489 : {
490 : CertType certType;
491 :
492 32 : ReturnErrorOnFailure(requestParams.SubjectDN.GetCertType(certType));
493 32 : VerifyOrReturnError(certType == CertType::kICA, CHIP_ERROR_INVALID_ARGUMENT);
494 :
495 31 : ReturnErrorOnFailure(requestParams.IssuerDN.GetCertType(certType));
496 31 : VerifyOrReturnError(certType == CertType::kRoot, CHIP_ERROR_INVALID_ARGUMENT);
497 :
498 31 : return NewChipX509Cert(requestParams, subjectPubkey, issuerKeypair, x509Cert);
499 : }
500 :
501 5 : CHIP_ERROR NewVidVerificationSignerX509Cert(const X509CertRequestParams & requestParams,
502 : const Crypto::P256PublicKey & subjectPubkey, const Crypto::P256Keypair & issuerKeypair,
503 : MutableByteSpan & x509Cert)
504 : {
505 : CertType certType;
506 :
507 5 : ReturnErrorOnFailure(requestParams.SubjectDN.GetCertType(certType));
508 4 : VerifyOrReturnError(certType == CertType::kVidVerificationSigner, CHIP_ERROR_INVALID_ARGUMENT);
509 :
510 3 : ReturnErrorOnFailure(requestParams.IssuerDN.GetCertType(certType));
511 3 : VerifyOrReturnError((certType == CertType::kICA) || (certType == CertType::kRoot), CHIP_ERROR_INVALID_ARGUMENT);
512 :
513 3 : return NewChipX509Cert(requestParams, subjectPubkey, issuerKeypair, x509Cert);
514 : }
515 :
516 56 : DLL_EXPORT CHIP_ERROR NewNodeOperationalX509Cert(const X509CertRequestParams & requestParams,
517 : const Crypto::P256PublicKey & subjectPubkey,
518 : const Crypto::P256Keypair & issuerKeypair, MutableByteSpan & x509Cert)
519 : {
520 : CertType certType;
521 :
522 56 : ReturnErrorOnFailure(requestParams.SubjectDN.GetCertType(certType));
523 55 : VerifyOrReturnError(certType == CertType::kNode, CHIP_ERROR_INVALID_ARGUMENT);
524 :
525 54 : ReturnErrorOnFailure(requestParams.IssuerDN.GetCertType(certType));
526 54 : VerifyOrReturnError(certType == CertType::kICA || certType == CertType::kRoot, CHIP_ERROR_INVALID_ARGUMENT);
527 :
528 53 : return NewChipX509Cert(requestParams, subjectPubkey, issuerKeypair, x509Cert);
529 : }
530 :
531 : } // namespace Credentials
532 : } // namespace chip
|