3 * Copyright (c) 2020-2021 Project CHIP Authors
4 * Copyright (c) 2013-2017 Nest Labs, Inc.
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
22 * This file implements methods for converting a standard X.509
23 * certificate to a CHIP TLV-encoded certificate.
27 #ifndef __STDC_LIMIT_MACROS
28 #define __STDC_LIMIT_MACROS
33 #include <asn1/ASN1.h>
34 #include <asn1/ASN1Macros.h>
35 #include <core/CHIPCore.h>
36 #include <core/CHIPSafeCasts.h>
37 #include <core/CHIPTLV.h>
38 #include <credentials/CHIPCert.h>
39 #include <protocols/Protocols.h>
40 #include <support/CodeUtils.h>
43 namespace Credentials {
45 using namespace chip::ASN1;
46 using namespace chip::TLV;
47 using namespace chip::Protocols;
49 static ASN1_ERROR ParseChipIdAttribute(ASN1Reader & reader, uint64_t & chipIdOut)
51 ASN1_ERROR err = ASN1_NO_ERROR;
52 const uint8_t * value = nullptr;
54 VerifyOrExit(reader.GetValueLen() == kChipIdUTF8Length, err = ASN1_ERROR_INVALID_ENCODING);
56 value = reader.GetValue();
57 VerifyOrExit(value != nullptr, err = ASN1_ERROR_INVALID_ENCODING);
61 for (uint32_t i = 0; i < kChipIdUTF8Length; i++)
64 uint8_t ch = value[i];
65 if (ch >= '0' && ch <= '9')
67 chipIdOut |= (ch - '0');
69 // CHIP Id attribute encodings only support uppercase chars.
70 else if (ch >= 'A' && ch <= 'F')
72 chipIdOut |= (ch - 'A' + 10);
76 ExitNow(err = ASN1_ERROR_INVALID_ENCODING);
84 static CHIP_ERROR ConvertDistinguishedName(ASN1Reader & reader, TLVWriter & writer, uint64_t tag)
87 TLVType outerContainer;
90 err = writer.StartContainer(tag, kTLVType_List, outerContainer);
93 // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
94 ASN1_PARSE_ENTER_SEQUENCE
96 while ((err = reader.Next()) == ASN1_NO_ERROR)
98 // RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
101 // AttributeTypeAndValue ::= SEQUENCE
102 ASN1_PARSE_ENTER_SEQUENCE
104 // type AttributeType
105 // AttributeType ::= OBJECT IDENTIFIER
106 ASN1_PARSE_OBJECT_ID(attrOID);
107 VerifyOrExit(GetOIDCategory(attrOID) == kOIDCategory_AttributeType, err = ASN1_ERROR_INVALID_ENCODING);
109 // AttributeValue ::= ANY -- DEFINED BY AttributeType
112 // Can only support UTF8String, PrintableString and IA5String.
113 VerifyOrExit(reader.GetClass() == kASN1TagClass_Universal &&
114 (reader.GetTag() == kASN1UniversalTag_PrintableString ||
115 reader.GetTag() == kASN1UniversalTag_UTF8String ||
116 reader.GetTag() == kASN1UniversalTag_IA5String),
117 err = ASN1_ERROR_UNSUPPORTED_ENCODING);
119 // CHIP id attributes must be UTF8Strings.
120 if (IsChipIdX509Attr(attrOID))
122 VerifyOrExit(reader.GetTag() == kASN1UniversalTag_UTF8String, err = ASN1_ERROR_INVALID_ENCODING);
125 // Derive the TLV tag number from the enum value assigned to the attribute type OID. For attributes that can be
126 // either UTF8String or PrintableString, use the high bit in the tag number to distinguish the two.
127 uint8_t tlvTagNum = GetOIDEnum(attrOID);
128 if (reader.GetTag() == kASN1UniversalTag_PrintableString)
133 // If the attribute is a CHIP-defined attribute that contains a 64-bit CHIP id...
134 if (IsChipIdX509Attr(attrOID))
136 // Parse the attribute string into a 64-bit CHIP id.
138 err = ParseChipIdAttribute(reader, chipId);
141 // Write the CHIP id into the TLV.
142 err = writer.Put(ContextTag(tlvTagNum), chipId);
150 writer.PutString(ContextTag(tlvTagNum), Uint8::to_const_char(reader.GetValue()), reader.GetValueLen());
156 // Only one AttributeTypeAndValue allowed per RDN.
158 if (err == ASN1_NO_ERROR)
160 ExitNow(err = ASN1_ERROR_UNSUPPORTED_ENCODING);
172 err = writer.EndContainer(outerContainer);
179 static CHIP_ERROR ConvertValidity(ASN1Reader & reader, TLVWriter & writer)
182 ASN1UniversalTime asn1Time;
183 uint32_t chipEpochTime;
185 ASN1_PARSE_ENTER_SEQUENCE
187 ASN1_PARSE_TIME(asn1Time);
189 err = ASN1ToChipEpochTime(asn1Time, chipEpochTime);
192 err = writer.Put(ContextTag(kTag_NotBefore), chipEpochTime);
195 ASN1_PARSE_TIME(asn1Time);
197 err = ASN1ToChipEpochTime(asn1Time, chipEpochTime);
200 err = writer.Put(ContextTag(kTag_NotAfter), chipEpochTime);
209 static CHIP_ERROR ConvertSubjectPublicKeyInfo(ASN1Reader & reader, TLVWriter & writer)
212 OID pubKeyAlgoOID, pubKeyCurveOID;
214 // subjectPublicKeyInfo SubjectPublicKeyInfo,
215 ASN1_PARSE_ENTER_SEQUENCE
217 // algorithm AlgorithmIdentifier,
218 // AlgorithmIdentifier ::= SEQUENCE
219 ASN1_PARSE_ENTER_SEQUENCE
221 // algorithm OBJECT IDENTIFIER,
222 ASN1_PARSE_OBJECT_ID(pubKeyAlgoOID);
224 // Verify that the algorithm type is supported.
225 VerifyOrExit(pubKeyAlgoOID == kOID_PubKeyAlgo_ECPublicKey, err = ASN1_ERROR_UNSUPPORTED_ENCODING);
227 err = writer.Put(ContextTag(kTag_PublicKeyAlgorithm), GetOIDEnum(pubKeyAlgoOID));
230 // EcpkParameters ::= CHOICE {
231 // ecParameters ECParameters,
232 // namedCurve OBJECT IDENTIFIER,
233 // implicitlyCA NULL }
236 // ecParameters and implicitlyCA not supported.
237 if (reader.GetClass() == kASN1TagClass_Universal && reader.GetTag() == kASN1UniversalTag_Sequence)
239 ExitNow(err = ASN1_ERROR_UNSUPPORTED_ENCODING);
241 if (reader.GetClass() == kASN1TagClass_Universal && reader.GetTag() == kASN1UniversalTag_Null)
243 ExitNow(err = ASN1_ERROR_UNSUPPORTED_ENCODING);
246 ASN1_VERIFY_TAG(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId);
248 ASN1_GET_OBJECT_ID(pubKeyCurveOID);
250 // Verify the curve name is recognized.
251 VerifyOrExit(GetOIDCategory(pubKeyCurveOID) == kOIDCategory_EllipticCurve, err = ASN1_ERROR_UNSUPPORTED_ENCODING);
253 err = writer.Put(ContextTag(kTag_EllipticCurveIdentifier), GetOIDEnum(pubKeyCurveOID));
258 // subjectPublicKey BIT STRING
259 ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_BitString);
261 // Verify public key length.
262 VerifyOrExit(reader.GetValueLen() > 0, err = ASN1_ERROR_INVALID_ENCODING);
264 // The first byte is Unused Bit Count value, which should be zero.
265 VerifyOrExit(reader.GetValue()[0] == 0, err = ASN1_ERROR_INVALID_ENCODING);
267 // Copy the X9.62 encoded EC point into the CHIP certificate as a byte string.
268 // Skip the first Unused Bit Count byte.
269 err = writer.PutBytes(ContextTag(kTag_EllipticCurvePublicKey), reader.GetValue() + 1, reader.GetValueLen() - 1);
278 static CHIP_ERROR ConvertExtension(ASN1Reader & reader, TLVWriter & writer)
281 TLVType outerContainer;
283 bool critical = false;
285 // Extension ::= SEQUENCE
288 // extnID OBJECT IDENTIFIER,
289 ASN1_PARSE_OBJECT_ID(extensionOID);
291 VerifyOrExit(extensionOID != kOID_Unknown, err = ASN1_ERROR_UNSUPPORTED_ENCODING);
292 VerifyOrExit(GetOIDCategory(extensionOID) == kOIDCategory_Extension, err = ASN1_ERROR_INVALID_ENCODING);
294 // critical BOOLEAN DEFAULT FALSE,
296 if (reader.GetClass() == kASN1TagClass_Universal && reader.GetTag() == kASN1UniversalTag_Boolean)
298 ASN1_GET_BOOLEAN(critical);
300 VerifyOrExit(critical, err = ASN1_ERROR_INVALID_ENCODING);
305 // extnValue OCTET STRING
306 // -- contains the DER encoding of an ASN.1 value
307 // -- corresponding to the extension type identified
309 ASN1_ENTER_ENCAPSULATED(kASN1TagClass_Universal, kASN1UniversalTag_OctetString)
311 if (extensionOID == kOID_Extension_AuthorityKeyIdentifier)
313 // This extension MUST be marked as non-critical.
314 VerifyOrExit(!critical, err = ASN1_ERROR_INVALID_ENCODING);
316 // AuthorityKeyIdentifier ::= SEQUENCE
317 ASN1_PARSE_ENTER_SEQUENCE
320 VerifyOrExit(err == ASN1_NO_ERROR, err = ASN1_ERROR_INVALID_ENCODING);
322 // keyIdentifier [0] IMPLICIT KeyIdentifier,
323 // KeyIdentifier ::= OCTET STRING
324 VerifyOrExit(reader.GetClass() == kASN1TagClass_ContextSpecific && reader.GetTag() == 0,
325 err = ASN1_ERROR_INVALID_ENCODING);
327 VerifyOrExit(reader.IsConstructed() == false, err = ASN1_ERROR_INVALID_ENCODING);
328 VerifyOrExit(reader.GetValueLen() == kKeyIdentifierLength, err = ASN1_ERROR_INVALID_ENCODING);
330 err = writer.PutBytes(ContextTag(kTag_AuthorityKeyIdentifier), reader.GetValue(), reader.GetValueLen());
334 VerifyOrExit(err == ASN1_END, err = ASN1_ERROR_INVALID_ENCODING);
338 else if (extensionOID == kOID_Extension_SubjectKeyIdentifier)
340 // This extension MUST be marked as non-critical.
341 VerifyOrExit(!critical, err = ASN1_ERROR_INVALID_ENCODING);
343 // SubjectKeyIdentifier ::= KeyIdentifier
344 // KeyIdentifier ::= OCTET STRING
345 ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_OctetString);
347 VerifyOrExit(reader.GetValueLen() == kKeyIdentifierLength, err = ASN1_ERROR_INVALID_ENCODING);
349 err = writer.PutBytes(ContextTag(kTag_SubjectKeyIdentifier), reader.GetValue(), reader.GetValueLen());
352 else if (extensionOID == kOID_Extension_KeyUsage)
354 // This extension MUST be marked as critical.
355 VerifyOrExit(critical, err = ASN1_ERROR_INVALID_ENCODING);
357 // KeyUsage ::= BIT STRING
358 ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_BitString);
360 uint32_t keyUsageBits;
361 err = reader.GetBitString(keyUsageBits);
363 VerifyOrExit(keyUsageBits <= UINT16_MAX, err = ASN1_ERROR_INVALID_ENCODING);
365 // Check that only supported flags are set.
366 BitFlags<KeyUsageFlags> keyUsageFlags(static_cast<uint16_t>(keyUsageBits));
367 VerifyOrExit(keyUsageFlags.HasOnly(
368 KeyUsageFlags::kDigitalSignature, KeyUsageFlags::kNonRepudiation, KeyUsageFlags::kKeyEncipherment,
369 KeyUsageFlags::kDataEncipherment, KeyUsageFlags::kKeyAgreement, KeyUsageFlags::kKeyCertSign,
370 KeyUsageFlags::kCRLSign, KeyUsageFlags::kEncipherOnly, KeyUsageFlags::kEncipherOnly),
371 err = ASN1_ERROR_INVALID_ENCODING);
373 err = writer.Put(ContextTag(kTag_KeyUsage), keyUsageBits);
376 else if (extensionOID == kOID_Extension_BasicConstraints)
378 // This extension MUST be marked as critical.
379 VerifyOrExit(critical, err = ASN1_ERROR_INVALID_ENCODING);
381 // BasicConstraints ::= SEQUENCE
382 ASN1_PARSE_ENTER_SEQUENCE
385 int64_t pathLenConstraint = -1;
387 // cA BOOLEAN DEFAULT FALSE
389 if (err == ASN1_NO_ERROR && reader.GetClass() == kASN1TagClass_Universal &&
390 reader.GetTag() == kASN1UniversalTag_Boolean)
392 ASN1_GET_BOOLEAN(isCA);
394 VerifyOrExit(isCA, err = ASN1_ERROR_INVALID_ENCODING);
399 // pathLenConstraint INTEGER (0..MAX) OPTIONAL
400 if (err == ASN1_NO_ERROR && reader.GetClass() == kASN1TagClass_Universal &&
401 reader.GetTag() == kASN1UniversalTag_Integer)
403 ASN1_GET_INTEGER(pathLenConstraint);
405 VerifyOrExit(pathLenConstraint <= UINT8_MAX, err = ASN1_ERROR_INVALID_ENCODING);
406 VerifyOrExit(pathLenConstraint >= 0, err = ASN1_ERROR_INVALID_ENCODING);
408 // pathLenConstraint is present only when cA is TRUE
409 VerifyOrExit(isCA, err = ASN1_ERROR_INVALID_ENCODING);
412 err = writer.StartContainer(ContextTag(kTag_BasicConstraints), kTLVType_Structure, outerContainer);
415 // Set also when cA is FALSE
416 err = writer.PutBoolean(ContextTag(kTag_BasicConstraints_IsCA), isCA);
419 if (pathLenConstraint != -1)
421 err = writer.Put(ContextTag(kTag_BasicConstraints_PathLenConstraint),
422 static_cast<uint8_t>(pathLenConstraint));
426 err = writer.EndContainer(outerContainer);
431 else if (extensionOID == kOID_Extension_ExtendedKeyUsage)
433 // This extension MUST be marked as critical.
434 VerifyOrExit(critical, err = ASN1_ERROR_INVALID_ENCODING);
436 err = writer.StartContainer(ContextTag(kTag_ExtendedKeyUsage), kTLVType_Array, outerContainer);
439 // ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
440 ASN1_PARSE_ENTER_SEQUENCE
442 while ((err = reader.Next()) == ASN1_NO_ERROR)
444 // KeyPurposeId ::= OBJECT IDENTIFIER
446 ASN1_GET_OBJECT_ID(keyPurposeOID);
448 VerifyOrExit(keyPurposeOID != kOID_Unknown, err = ASN1_ERROR_UNSUPPORTED_ENCODING);
449 VerifyOrExit(GetOIDCategory(keyPurposeOID) == kOIDCategory_KeyPurpose, err = ASN1_ERROR_INVALID_ENCODING);
451 err = writer.Put(AnonymousTag, GetOIDEnum(keyPurposeOID));
461 err = writer.EndContainer(outerContainer);
466 ExitNow(err = ASN1_ERROR_UNSUPPORTED_ENCODING);
469 ASN1_EXIT_ENCAPSULATED;
477 static CHIP_ERROR ConvertExtensions(ASN1Reader & reader, TLVWriter & writer)
481 // Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
482 ASN1_PARSE_ENTER_SEQUENCE
484 while ((err = reader.Next()) == ASN1_NO_ERROR)
486 err = ConvertExtension(reader, writer);
501 static CHIP_ERROR ConvertCertificate(ASN1Reader & reader, TLVWriter & writer)
506 TLVType containerType;
508 err = writer.StartContainer(ProfileTag(kProtocol_OpCredentials, kTag_ChipCertificate), kTLVType_Structure, containerType);
511 // Certificate ::= SEQUENCE
512 ASN1_PARSE_ENTER_SEQUENCE
514 // tbsCertificate TBSCertificate,
515 // TBSCertificate ::= SEQUENCE
516 ASN1_PARSE_ENTER_SEQUENCE
518 // version [0] EXPLICIT Version DEFAULT v1
519 ASN1_PARSE_ENTER_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0)
521 // Version ::= INTEGER { v1(0), v2(1), v3(2) }
522 ASN1_PARSE_INTEGER(version);
524 // Verify that the X.509 certificate version is v3
525 VerifyOrExit(version == 2, err = ASN1_ERROR_UNSUPPORTED_ENCODING);
527 ASN1_EXIT_CONSTRUCTED;
529 // serialNumber CertificateSerialNumber
530 // CertificateSerialNumber ::= INTEGER
531 ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_Integer);
532 err = writer.PutBytes(ContextTag(kTag_SerialNumber), reader.GetValue(), reader.GetValueLen());
535 // signature AlgorithmIdentifier
536 // AlgorithmIdentifier ::= SEQUENCE
537 ASN1_PARSE_ENTER_SEQUENCE
539 // algorithm OBJECT IDENTIFIER,
540 ASN1_PARSE_OBJECT_ID(sigAlgoOID);
542 VerifyOrExit(sigAlgoOID == kOID_SigAlgo_ECDSAWithSHA256, err = ASN1_ERROR_UNSUPPORTED_ENCODING);
544 err = writer.Put(ContextTag(kTag_SignatureAlgorithm), GetOIDEnum(sigAlgoOID));
550 err = ConvertDistinguishedName(reader, writer, ContextTag(kTag_Issuer));
553 // validity Validity,
554 err = ConvertValidity(reader, writer);
558 err = ConvertDistinguishedName(reader, writer, ContextTag(kTag_Subject));
561 err = ConvertSubjectPublicKeyInfo(reader, writer);
566 // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
568 if (err == ASN1_NO_ERROR && reader.GetClass() == kASN1TagClass_ContextSpecific && reader.GetTag() == 1)
570 ExitNow(err = ASN1_ERROR_UNSUPPORTED_ENCODING);
573 // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
575 if (err == ASN1_NO_ERROR && reader.GetClass() == kASN1TagClass_ContextSpecific && reader.GetTag() == 2)
577 ExitNow(err = ASN1_ERROR_UNSUPPORTED_ENCODING);
580 // extensions [3] EXPLICIT Extensions OPTIONAL
581 if (err == ASN1_NO_ERROR && reader.GetClass() == kASN1TagClass_ContextSpecific && reader.GetTag() == 3)
583 ASN1_ENTER_CONSTRUCTED(kASN1TagClass_ContextSpecific, 3)
585 err = ConvertExtensions(reader, writer);
588 ASN1_EXIT_CONSTRUCTED;
600 // signatureAlgorithm AlgorithmIdentifier
601 // AlgorithmIdentifier ::= SEQUENCE
602 ASN1_PARSE_ENTER_SEQUENCE
606 // algorithm OBJECT IDENTIFIER,
607 ASN1_PARSE_OBJECT_ID(localSigAlgoOID);
609 // Verify that the signatureAlgorithm is the same as the "signature" field in TBSCertificate.
610 VerifyOrExit(localSigAlgoOID == sigAlgoOID, err = ASN1_ERROR_UNSUPPORTED_ENCODING);
614 // signatureValue BIT STRING
615 ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_BitString);
617 // Per RFC3279, the ECDSA signature value is encoded in DER encapsulated in the signatureValue BIT STRING.
618 ASN1_ENTER_ENCAPSULATED(kASN1TagClass_Universal, kASN1UniversalTag_BitString)
620 TLVType outerContainer;
622 err = writer.StartContainer(ContextTag(kTag_ECDSASignature), kTLVType_Structure, outerContainer);
625 // Ecdsa-Sig-Value ::= SEQUENCE
626 ASN1_PARSE_ENTER_SEQUENCE
629 ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_Integer);
630 err = writer.PutBytes(ContextTag(kTag_ECDSASignature_r), reader.GetValue(), reader.GetValueLen());
634 ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_Integer);
635 err = writer.PutBytes(ContextTag(kTag_ECDSASignature_s), reader.GetValue(), reader.GetValueLen());
640 err = writer.EndContainer(outerContainer);
643 ASN1_EXIT_ENCAPSULATED;
647 err = writer.EndContainer(containerType);
654 DLL_EXPORT CHIP_ERROR ConvertX509CertToChipCert(const uint8_t * x509Cert, uint32_t x509CertLen, uint8_t * chipCertBuf,
655 uint32_t chipCertBufSize, uint32_t & chipCertLen)
661 reader.Init(x509Cert, x509CertLen);
663 writer.Init(chipCertBuf, chipCertBufSize);
665 err = ConvertCertificate(reader, writer);
668 err = writer.Finalize();
671 chipCertLen = writer.GetLengthWritten();
677 } // namespace Credentials