diff --git a/src/controller/ExampleOperationalCredentialsIssuer.cpp b/src/controller/ExampleOperationalCredentialsIssuer.cpp index 97b704be471dbc..e0884d12cc0eac 100644 --- a/src/controller/ExampleOperationalCredentialsIssuer.cpp +++ b/src/controller/ExampleOperationalCredentialsIssuer.cpp @@ -116,61 +116,63 @@ CHIP_ERROR ExampleOperationalCredentialsIssuer::GenerateNOCChainAfterValidation( MutableByteSpan & rcac, MutableByteSpan & icac, MutableByteSpan & noc) { - ChipDN noc_dn; - // TODO: Is there a way to make this code less error-prone for consumers? - // The consumer doesn't need to know the exact OID value. - ReturnErrorOnFailure(noc_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipFabricId, fabricId)); - ReturnErrorOnFailure(noc_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipNodeId, nodeId)); - ReturnErrorOnFailure(noc_dn.AddCATs(cats)); - ChipDN icac_dn; - ReturnErrorOnFailure(icac_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipICAId, mIntermediateIssuerId)); ChipDN rcac_dn; - ReturnErrorOnFailure(rcac_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipRootId, mIssuerId)); + CHIP_ERROR err = CHIP_NO_ERROR; + uint16_t rcacBufLen = static_cast(std::min(rcac.size(), static_cast(UINT16_MAX))); + PERSISTENT_KEY_OP(mIndex, kOperationalCredentialsRootCertificateStorage, key, + err = mStorage->SyncGetKeyValue(key, rcac.data(), rcacBufLen)); + if (err == CHIP_NO_ERROR) + { + // Found root certificate in the storage. + rcac.reduce_size(rcacBufLen); + ReturnErrorOnFailure(ExtractSubjectDNFromX509Cert(rcac, rcac_dn)); + } + // If root certificate not found in the storage, generate new root certificate. + else + { + ReturnErrorOnFailure(rcac_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipRootId, mIssuerId)); - ChipLogProgress(Controller, "Generating NOC"); - X509CertRequestParams noc_request = { 1, mNow, mNow + mValidity, noc_dn, icac_dn }; - ReturnErrorOnFailure(NewNodeOperationalX509Cert(noc_request, pubkey, mIntermediateIssuer, noc)); + ChipLogProgress(Controller, "Generating RCAC"); + X509CertRequestParams rcac_request = { 0, mNow, mNow + mValidity, rcac_dn, rcac_dn }; + ReturnErrorOnFailure(NewRootX509Cert(rcac_request, mIssuer, rcac)); + + VerifyOrReturnError(CanCastTo(rcac.size()), CHIP_ERROR_INTERNAL); + PERSISTENT_KEY_OP(mIndex, kOperationalCredentialsRootCertificateStorage, key, + ReturnErrorOnFailure(mStorage->SyncSetKeyValue(key, rcac.data(), static_cast(rcac.size())))); + } + ChipDN icac_dn; uint16_t icacBufLen = static_cast(std::min(icac.size(), static_cast(UINT16_MAX))); - CHIP_ERROR err = CHIP_NO_ERROR; PERSISTENT_KEY_OP(mIndex, kOperationalCredentialsIntermediateCertificateStorage, key, err = mStorage->SyncGetKeyValue(key, icac.data(), icacBufLen)); if (err == CHIP_NO_ERROR) { - // Found root certificate in the storage. + // Found intermediate certificate in the storage. icac.reduce_size(icacBufLen); + ReturnErrorOnFailure(ExtractSubjectDNFromX509Cert(icac, icac_dn)); } + // If intermediate certificate not found in the storage, generate new intermediate certificate. else { + ReturnErrorOnFailure(icac_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipICAId, mIntermediateIssuerId)); + ChipLogProgress(Controller, "Generating ICAC"); X509CertRequestParams icac_request = { 0, mNow, mNow + mValidity, icac_dn, rcac_dn }; ReturnErrorOnFailure(NewICAX509Cert(icac_request, mIntermediateIssuer.Pubkey(), mIssuer, icac)); VerifyOrReturnError(CanCastTo(icac.size()), CHIP_ERROR_INTERNAL); PERSISTENT_KEY_OP(mIndex, kOperationalCredentialsIntermediateCertificateStorage, key, - err = mStorage->SyncSetKeyValue(key, icac.data(), static_cast(icac.size()))); + ReturnErrorOnFailure(mStorage->SyncSetKeyValue(key, icac.data(), static_cast(icac.size())))); } - uint16_t rcacBufLen = static_cast(std::min(rcac.size(), static_cast(UINT16_MAX))); - PERSISTENT_KEY_OP(mIndex, kOperationalCredentialsRootCertificateStorage, key, - err = mStorage->SyncGetKeyValue(key, rcac.data(), rcacBufLen)); - if (err == CHIP_NO_ERROR) - { - // Found root certificate in the storage. - rcac.reduce_size(rcacBufLen); - } - else - { - ChipLogProgress(Controller, "Generating RCAC"); - X509CertRequestParams rcac_request = { 0, mNow, mNow + mValidity, rcac_dn, rcac_dn }; - ReturnErrorOnFailure(NewRootX509Cert(rcac_request, mIssuer, rcac)); - - VerifyOrReturnError(CanCastTo(rcac.size()), CHIP_ERROR_INTERNAL); - PERSISTENT_KEY_OP(mIndex, kOperationalCredentialsRootCertificateStorage, key, - err = mStorage->SyncSetKeyValue(key, rcac.data(), static_cast(rcac.size()))); - } + ChipDN noc_dn; + ReturnErrorOnFailure(noc_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipFabricId, fabricId)); + ReturnErrorOnFailure(noc_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipNodeId, nodeId)); + ReturnErrorOnFailure(noc_dn.AddCATs(cats)); - return err; + ChipLogProgress(Controller, "Generating NOC"); + X509CertRequestParams noc_request = { 1, mNow, mNow + mValidity, noc_dn, icac_dn }; + return NewNodeOperationalX509Cert(noc_request, pubkey, mIntermediateIssuer, noc); } CHIP_ERROR ExampleOperationalCredentialsIssuer::GenerateNOCChain(const ByteSpan & csrElements, diff --git a/src/controller/java/AndroidOperationalCredentialsIssuer.cpp b/src/controller/java/AndroidOperationalCredentialsIssuer.cpp index 2555acfabd0f4c..6e14f5a72729b8 100644 --- a/src/controller/java/AndroidOperationalCredentialsIssuer.cpp +++ b/src/controller/java/AndroidOperationalCredentialsIssuer.cpp @@ -82,18 +82,7 @@ CHIP_ERROR AndroidOperationalCredentialsIssuer::GenerateNOCChainAfterValidation( MutableByteSpan & rcac, MutableByteSpan & icac, MutableByteSpan & noc) { - ChipDN noc_dn; - ReturnErrorOnFailure(noc_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipFabricId, fabricId)); - ReturnErrorOnFailure(noc_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipNodeId, nodeId)); - ReturnErrorOnFailure(noc_dn.AddCATs(cats)); ChipDN rcac_dn; - ReturnErrorOnFailure(rcac_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipRootId, mIssuerId)); - - ChipLogProgress(Controller, "Generating NOC"); - chip::Credentials::X509CertRequestParams noc_request = { 1, mNow, mNow + mValidity, noc_dn, rcac_dn }; - ReturnErrorOnFailure(NewNodeOperationalX509Cert(noc_request, pubkey, mIssuer, noc)); - icac.reduce_size(0); - uint16_t rcacBufLen = static_cast(std::min(rcac.size(), static_cast(UINT16_MAX))); CHIP_ERROR err = CHIP_NO_ERROR; PERSISTENT_KEY_OP(fabricId, kOperationalCredentialsRootCertificateStorage, key, @@ -102,18 +91,32 @@ CHIP_ERROR AndroidOperationalCredentialsIssuer::GenerateNOCChainAfterValidation( { // Found root certificate in the storage. rcac.reduce_size(rcacBufLen); - return CHIP_NO_ERROR; + ReturnErrorOnFailure(ExtractSubjectDNFromX509Cert(rcac, rcac_dn)); + } + // If root certificate not found in the storage, generate new root certificate. + else + { + ReturnErrorOnFailure(rcac_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipRootId, mIssuerId)); + + ChipLogProgress(Controller, "Generating RCAC"); + chip::Credentials::X509CertRequestParams rcac_request = { 0, mNow, mNow + mValidity, rcac_dn, rcac_dn }; + ReturnErrorOnFailure(NewRootX509Cert(rcac_request, mIssuer, rcac)); + + VerifyOrReturnError(CanCastTo(rcac.size()), CHIP_ERROR_INTERNAL); + PERSISTENT_KEY_OP(fabricId, kOperationalCredentialsRootCertificateStorage, key, + ReturnErrorOnFailure(mStorage->SyncSetKeyValue(key, rcac.data(), static_cast(rcac.size())))); } - ChipLogProgress(Controller, "Generating RCAC"); - chip::Credentials::X509CertRequestParams rcac_request = { 0, mNow, mNow + mValidity, rcac_dn, rcac_dn }; - ReturnErrorOnFailure(NewRootX509Cert(rcac_request, mIssuer, rcac)); + icac.reduce_size(0); - VerifyOrReturnError(CanCastTo(rcac.size()), CHIP_ERROR_INTERNAL); - PERSISTENT_KEY_OP(fabricId, kOperationalCredentialsRootCertificateStorage, key, - err = mStorage->SyncSetKeyValue(key, rcac.data(), static_cast(rcac.size()))); + ChipDN noc_dn; + ReturnErrorOnFailure(noc_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipFabricId, fabricId)); + ReturnErrorOnFailure(noc_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipNodeId, nodeId)); + ReturnErrorOnFailure(noc_dn.AddCATs(cats)); - return err; + ChipLogProgress(Controller, "Generating NOC"); + chip::Credentials::X509CertRequestParams noc_request = { 1, mNow, mNow + mValidity, noc_dn, rcac_dn }; + return NewNodeOperationalX509Cert(noc_request, pubkey, mIssuer, noc); } CHIP_ERROR AndroidOperationalCredentialsIssuer::GenerateNOCChain(const ByteSpan & csrElements, diff --git a/src/credentials/CHIPCert.cpp b/src/credentials/CHIPCert.cpp index d053bc4e087d52..0ab3b25cb2b8a1 100644 --- a/src/credentials/CHIPCert.cpp +++ b/src/credentials/CHIPCert.cpp @@ -710,6 +710,36 @@ CHIP_ERROR ChipDN::GetCertFabricId(uint64_t & fabricId) const return CHIP_NO_ERROR; } +CHIP_ERROR ChipDN::EncodeToTLV(TLVWriter & writer, Tag tag) const +{ + TLVType outerContainer; + uint8_t rdnCount = RDNCount(); + + ReturnErrorOnFailure(writer.StartContainer(tag, kTLVType_List, outerContainer)); + + for (uint8_t i = 0; i < rdnCount; i++) + { + // Derive the TLV tag number from the enum value assigned to the attribute type OID. For attributes that can be + // either UTF8String or PrintableString, use the high bit in the tag number to distinguish the two. + uint8_t tlvTagNum = GetOIDEnum(rdn[i].mAttrOID); + if (rdn[i].mAttrIsPrintableString) + { + tlvTagNum |= 0x80; + } + + if (IsChipDNAttr(rdn[i].mAttrOID)) + { + ReturnErrorOnFailure(writer.Put(ContextTag(tlvTagNum), rdn[i].mChipVal)); + } + else + { + ReturnErrorOnFailure(writer.PutString(ContextTag(tlvTagNum), rdn[i].mString)); + } + } + + return writer.EndContainer(outerContainer); +} + CHIP_ERROR ChipDN::DecodeFromTLV(TLVReader & reader) { CHIP_ERROR err; @@ -860,6 +890,100 @@ CHIP_ERROR ChipDN::EncodeToASN1(ASN1Writer & writer) const return err; } +CHIP_ERROR ChipDN::DecodeFromASN1(ASN1Reader & reader) +{ + CHIP_ERROR err; + + // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName + ASN1_PARSE_ENTER_SEQUENCE + { + while ((err = reader.Next()) == CHIP_NO_ERROR) + { + // RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue + ASN1_ENTER_SET + { + // AttributeTypeAndValue ::= SEQUENCE + ASN1_PARSE_ENTER_SEQUENCE + { + // type AttributeType + // AttributeType ::= OBJECT IDENTIFIER + OID attrOID; + ASN1_PARSE_OBJECT_ID(attrOID); + VerifyOrReturnError(GetOIDCategory(attrOID) == kOIDCategory_AttributeType, ASN1_ERROR_INVALID_ENCODING); + + // AttributeValue ::= ANY -- DEFINED BY AttributeType + ASN1_PARSE_ANY; + + uint8_t attrTag = reader.GetTag(); + + // Can only support UTF8String, PrintableString and IA5String. + VerifyOrReturnError(reader.GetClass() == kASN1TagClass_Universal && + (attrTag == kASN1UniversalTag_PrintableString || + attrTag == kASN1UniversalTag_UTF8String || attrTag == kASN1UniversalTag_IA5String), + ASN1_ERROR_UNSUPPORTED_ENCODING); + + // CHIP attributes must be UTF8Strings. + if (IsChipDNAttr(attrOID)) + { + VerifyOrReturnError(attrTag == kASN1UniversalTag_UTF8String, ASN1_ERROR_INVALID_ENCODING); + } + + // If 64-bit CHIP attribute. + if (IsChip64bitDNAttr(attrOID)) + { + uint64_t chipAttr; + VerifyOrReturnError(Encoding::UppercaseHexToUint64(reinterpret_cast(reader.GetValue()), + static_cast(reader.GetValueLen()), + chipAttr) == sizeof(uint64_t), + ASN1_ERROR_INVALID_ENCODING); + + if (attrOID == chip::ASN1::kOID_AttributeType_ChipNodeId) + { + VerifyOrReturnError(IsOperationalNodeId(chipAttr), CHIP_ERROR_WRONG_CERT_DN); + } + else if (attrOID == chip::ASN1::kOID_AttributeType_ChipFabricId) + { + VerifyOrReturnError(IsValidFabricId(chipAttr), CHIP_ERROR_WRONG_CERT_DN); + } + + ReturnErrorOnFailure(AddAttribute(attrOID, chipAttr)); + } + // If 32-bit CHIP attribute. + else if (IsChip32bitDNAttr(attrOID)) + { + CASEAuthTag chipAttr; + VerifyOrReturnError(Encoding::UppercaseHexToUint32(reinterpret_cast(reader.GetValue()), + reader.GetValueLen(), chipAttr) == sizeof(CASEAuthTag), + ASN1_ERROR_INVALID_ENCODING); + + VerifyOrReturnError(IsValidCASEAuthTag(chipAttr), CHIP_ERROR_WRONG_CERT_DN); + + ReturnErrorOnFailure(AddAttribute(attrOID, chipAttr)); + } + // Otherwise, it is a string. + else + { + ReturnErrorOnFailure(AddAttribute(attrOID, + CharSpan(Uint8::to_const_char(reader.GetValue()), reader.GetValueLen()), + attrTag == kASN1UniversalTag_PrintableString)); + } + } + ASN1_EXIT_SEQUENCE; + + // Only one AttributeTypeAndValue allowed per RDN. + err = reader.Next(); + ReturnErrorCodeIf(err == CHIP_NO_ERROR, ASN1_ERROR_UNSUPPORTED_ENCODING); + VerifyOrReturnError(err == ASN1_END, err); + } + ASN1_EXIT_SET; + } + } + ASN1_EXIT_SEQUENCE; + +exit: + return err; +} + bool ChipDN::IsEqual(const ChipDN & other) const { bool res = true; @@ -1159,5 +1283,61 @@ CHIP_ERROR ExtractSKIDFromChipCert(const ByteSpan & chipCert, CertificateKeyId & return CHIP_NO_ERROR; } +CHIP_ERROR ExtractSubjectDNFromChipCert(const ByteSpan & chipCert, ChipDN & dn) +{ + ChipCertificateSet certSet; + ChipCertificateData certData; + + ReturnErrorOnFailure(certSet.Init(&certData, 1)); + + ReturnErrorOnFailure(certSet.LoadCert(chipCert, BitFlags())); + + dn = certData.mSubjectDN; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR ExtractSubjectDNFromX509Cert(const ByteSpan & x509Cert, ChipDN & dn) +{ + CHIP_ERROR err; + ASN1Reader reader; + + VerifyOrReturnError(CanCastTo(x509Cert.size()), CHIP_ERROR_INVALID_ARGUMENT); + + reader.Init(x509Cert); + + // Certificate ::= SEQUENCE + ASN1_PARSE_ENTER_SEQUENCE + { + // tbsCertificate TBSCertificate, + // TBSCertificate ::= SEQUENCE + ASN1_PARSE_ENTER_SEQUENCE + { + // Skip version [0] EXPLICIT Version DEFAULT v1 + ASN1_PARSE_ELEMENT(kASN1TagClass_ContextSpecific, 0); + + // Skip serialNumber CertificateSerialNumber + ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_Integer); + + // Skip signature AlgorithmIdentifier + ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_Sequence); + + // Skip issuer Name + ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_Sequence); + + // Skip validity Validity, + ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_Sequence); + + // Decode subject Name, + ReturnErrorOnFailure(dn.DecodeFromASN1(reader)); + } + ASN1_SKIP_AND_EXIT_SEQUENCE; + } + ASN1_SKIP_AND_EXIT_SEQUENCE; + +exit: + return err; +} + } // namespace Credentials } // namespace chip diff --git a/src/credentials/CHIPCert.h b/src/credentials/CHIPCert.h index 872a6e59f9262d..27e4380f886937 100644 --- a/src/credentials/CHIPCert.h +++ b/src/credentials/CHIPCert.h @@ -276,6 +276,11 @@ class ChipDN **/ CHIP_ERROR GetCertFabricId(uint64_t & fabricId) const; + /** + * @brief Encode ChipDN attributes in TLV form. + **/ + CHIP_ERROR EncodeToTLV(chip::TLV::TLVWriter & writer, TLV::Tag tag) const; + /** * @brief Decode ChipDN attributes from TLV encoded format. * @@ -288,6 +293,13 @@ class ChipDN **/ CHIP_ERROR EncodeToASN1(ASN1::ASN1Writer & writer) const; + /** + * @brief Decode ChipDN attributes from ASN1 encoded format. + * + * @param reader A ASN1Reader positioned at the ChipDN ASN1 list. + **/ + CHIP_ERROR DecodeFromASN1(ASN1::ASN1Reader & reader); + bool IsEqual(const ChipDN & other) const; /** @@ -868,7 +880,7 @@ CHIP_ERROR ExtractNodeIdFabricIdFromOpCert(const ByteSpan & opcert, NodeId * nod /** * Extract Public Key from a chip certificate in ByteSpan TLV-encoded form. * This does not perform any sort of validation on the certificate structure - * structure than parsing it. + * other than parsing it. * * Can return any error that can be returned from parsing the cert. */ @@ -877,11 +889,27 @@ CHIP_ERROR ExtractPublicKeyFromChipCert(const ByteSpan & chipCert, P256PublicKey /** * Extract Subject Key Identifier from a chip certificate in ByteSpan TLV-encoded form. * This does not perform any sort of validation on the certificate structure - * structure than parsing it. + * other than parsing it. * * Can return any error that can be returned from parsing the cert. */ CHIP_ERROR ExtractSKIDFromChipCert(const ByteSpan & chipCert, CertificateKeyId & skid); +/** + * Extract Subject Distinguished Name (DN) from a chip certificate in ByteSpan TLV-encoded form. + * It is required that the certificate in the chipCert buffer stays valid while the `dn` output is used. + * + * Can return any error that can be returned from parsing the cert. + */ +CHIP_ERROR ExtractSubjectDNFromChipCert(const ByteSpan & chipCert, ChipDN & dn); + +/** + * Extract Subject Distinguished Name (DN) from a chip certificate in ByteSpan X509 DER-encoded form. + * It is required that the certificate in the chipCert buffer stays valid while the `dn` output is used. + * + * Can return any error that can be returned from converting and parsing the cert. + */ +CHIP_ERROR ExtractSubjectDNFromX509Cert(const ByteSpan & x509Cert, ChipDN & dn); + } // namespace Credentials } // namespace chip diff --git a/src/credentials/CHIPCertFromX509.cpp b/src/credentials/CHIPCertFromX509.cpp index 31c64e3ef7c36c..dc8e315b3abd45 100644 --- a/src/credentials/CHIPCertFromX509.cpp +++ b/src/credentials/CHIPCertFromX509.cpp @@ -50,136 +50,11 @@ using namespace chip::TLV; using namespace chip::Protocols; using namespace chip::Crypto; -static CHIP_ERROR ConvertDistinguishedName(ASN1Reader & reader, TLVWriter & writer, Tag tag, uint64_t & subjectOrIssuer, - Optional & fabric) +static CHIP_ERROR ConvertDistinguishedName(ASN1Reader & reader, TLVWriter & writer, Tag tag) { - CHIP_ERROR err; - TLVType outerContainer; - OID attrOID; - - err = writer.StartContainer(tag, kTLVType_List, outerContainer); - SuccessOrExit(err); - - // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName - ASN1_PARSE_ENTER_SEQUENCE - { - while ((err = reader.Next()) == CHIP_NO_ERROR) - { - // RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue - ASN1_ENTER_SET - { - // AttributeTypeAndValue ::= SEQUENCE - ASN1_PARSE_ENTER_SEQUENCE - { - // type AttributeType - // AttributeType ::= OBJECT IDENTIFIER - ASN1_PARSE_OBJECT_ID(attrOID); - VerifyOrExit(GetOIDCategory(attrOID) == kOIDCategory_AttributeType, err = ASN1_ERROR_INVALID_ENCODING); - - // AttributeValue ::= ANY -- DEFINED BY AttributeType - ASN1_PARSE_ANY; - - // Can only support UTF8String, PrintableString and IA5String. - VerifyOrExit(reader.GetClass() == kASN1TagClass_Universal && - (reader.GetTag() == kASN1UniversalTag_PrintableString || - reader.GetTag() == kASN1UniversalTag_UTF8String || - reader.GetTag() == kASN1UniversalTag_IA5String), - err = ASN1_ERROR_UNSUPPORTED_ENCODING); - - // CHIP attributes must be UTF8Strings. - if (IsChipDNAttr(attrOID)) - { - VerifyOrExit(reader.GetTag() == kASN1UniversalTag_UTF8String, err = ASN1_ERROR_INVALID_ENCODING); - } - - // Derive the TLV tag number from the enum value assigned to the attribute type OID. For attributes that can be - // either UTF8String or PrintableString, use the high bit in the tag number to distinguish the two. - uint8_t tlvTagNum = GetOIDEnum(attrOID); - if (reader.GetTag() == kASN1UniversalTag_PrintableString) - { - tlvTagNum |= 0x80; - } - - // If 64-bit CHIP attribute. - if (IsChip64bitDNAttr(attrOID)) - { - uint64_t chipAttr; - VerifyOrReturnError(Encoding::UppercaseHexToUint64(reinterpret_cast(reader.GetValue()), - static_cast(reader.GetValueLen()), - chipAttr) == sizeof(uint64_t), - err = ASN1_ERROR_INVALID_ENCODING); - - // Certificates use a combination of OIDs for Issuer and Subject. - // NOC: Issuer = kOID_AttributeType_ChipRootId or kOID_AttributeType_ChipICAId - // Subject = kOID_AttributeType_ChipNodeId - // ICA: Issuer = kOID_AttributeType_ChipRootId - // Subject = kOID_AttributeType_ChipICAId - // Root: Issuer = kOID_AttributeType_ChipRootId - // Subject = kOID_AttributeType_ChipRootId - // - // This function is called first for the Issuer DN, and later for Subject DN. - // Since the caller knows if Issuer or Subject DN is being parsed, it's left up to - // the caller to use the returned value (subjectOrIssuer) appropriately. - if (attrOID == chip::ASN1::kOID_AttributeType_ChipNodeId || - attrOID == chip::ASN1::kOID_AttributeType_ChipICAId || - attrOID == chip::ASN1::kOID_AttributeType_ChipRootId) - { - if (attrOID == chip::ASN1::kOID_AttributeType_ChipNodeId) - { - VerifyOrReturnError(IsOperationalNodeId(chipAttr), CHIP_ERROR_WRONG_CERT_DN); - } - subjectOrIssuer = chipAttr; - } - else if (attrOID == chip::ASN1::kOID_AttributeType_ChipFabricId) - { - VerifyOrReturnError(IsValidFabricId(chipAttr), CHIP_ERROR_WRONG_CERT_DN); - fabric.SetValue(chipAttr); - } - - ReturnErrorOnFailure(writer.Put(ContextTag(tlvTagNum), chipAttr)); - } - // If 32-bit CHIP attribute. - else if (IsChip32bitDNAttr(attrOID)) - { - CASEAuthTag chipAttr; - VerifyOrReturnError(Encoding::UppercaseHexToUint32(reinterpret_cast(reader.GetValue()), - reader.GetValueLen(), chipAttr) == sizeof(CASEAuthTag), - err = ASN1_ERROR_INVALID_ENCODING); - - VerifyOrReturnError(IsValidCASEAuthTag(chipAttr), CHIP_ERROR_WRONG_CERT_DN); - - ReturnErrorOnFailure(writer.Put(ContextTag(tlvTagNum), chipAttr)); - } - // Otherwise, it is a string. - else - { - ReturnErrorOnFailure( - writer.PutString(ContextTag(tlvTagNum), Uint8::to_const_char(reader.GetValue()), reader.GetValueLen())); - } - } - ASN1_EXIT_SEQUENCE; - - // Only one AttributeTypeAndValue allowed per RDN. - err = reader.Next(); - if (err == CHIP_NO_ERROR) - { - ExitNow(err = ASN1_ERROR_UNSUPPORTED_ENCODING); - } - if (err != ASN1_END) - { - ExitNow(); - } - } - ASN1_EXIT_SET; - } - } - ASN1_EXIT_SEQUENCE; - - err = writer.EndContainer(outerContainer); - SuccessOrExit(err); - -exit: - return err; + ChipDN dn; + ReturnErrorOnFailure(dn.DecodeFromASN1(reader)); + return dn.EncodeToTLV(writer, tag); } static CHIP_ERROR ConvertValidity(ASN1Reader & reader, TLVWriter & writer) @@ -553,8 +428,7 @@ CHIP_ERROR ConvertECDSASignatureDERToRaw(ASN1Reader & reader, TLVWriter & writer return err; } -static CHIP_ERROR ConvertCertificate(ASN1Reader & reader, TLVWriter & writer, Tag tag, uint64_t & issuer, uint64_t & subject, - Optional & fabric) +static CHIP_ERROR ConvertCertificate(ASN1Reader & reader, TLVWriter & writer, Tag tag) { CHIP_ERROR err; int64_t version; @@ -603,7 +477,7 @@ static CHIP_ERROR ConvertCertificate(ASN1Reader & reader, TLVWriter & writer, Ta ASN1_EXIT_SEQUENCE; // issuer Name - err = ConvertDistinguishedName(reader, writer, ContextTag(kTag_Issuer), issuer, fabric); + err = ConvertDistinguishedName(reader, writer, ContextTag(kTag_Issuer)); SuccessOrExit(err); // validity Validity, @@ -611,7 +485,7 @@ static CHIP_ERROR ConvertCertificate(ASN1Reader & reader, TLVWriter & writer, Ta SuccessOrExit(err); // subject Name, - err = ConvertDistinguishedName(reader, writer, ContextTag(kTag_Subject), subject, fabric); + err = ConvertDistinguishedName(reader, writer, ContextTag(kTag_Subject)); SuccessOrExit(err); err = ConvertSubjectPublicKeyInfo(reader, writer); @@ -686,9 +560,6 @@ CHIP_ERROR ConvertX509CertToChipCert(const ByteSpan x509Cert, MutableByteSpan & ASN1Reader reader; TLVWriter writer; - uint64_t issuer, subject; - Optional fabric; - VerifyOrReturnError(!x509Cert.empty(), CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(CanCastTo(x509Cert.size()), CHIP_ERROR_INVALID_ARGUMENT); @@ -696,7 +567,7 @@ CHIP_ERROR ConvertX509CertToChipCert(const ByteSpan x509Cert, MutableByteSpan & writer.Init(chipCert); - ReturnErrorOnFailure(ConvertCertificate(reader, writer, AnonymousTag(), issuer, subject, fabric)); + ReturnErrorOnFailure(ConvertCertificate(reader, writer, AnonymousTag())); ReturnErrorOnFailure(writer.Finalize()); diff --git a/src/credentials/tests/TestChipCert.cpp b/src/credentials/tests/TestChipCert.cpp index d532e33cba72ac..0d0953d24a7ab7 100644 --- a/src/credentials/tests/TestChipCert.cpp +++ b/src/credentials/tests/TestChipCert.cpp @@ -1421,6 +1421,99 @@ static void TestChipCert_ExtractCATsFromOpCert(nlTestSuite * inSuite, void * inC } } +static void TestChipCert_ExtractSubjectDNFromChipCert(nlTestSuite * inSuite, void * inContext) +{ + struct TestCase + { + uint8_t Cert; + ChipDN ExpectedSubjectDN; + }; + + ChipDN expectedSubjectDN_Root01; + NL_TEST_ASSERT(inSuite, + expectedSubjectDN_Root01.AddAttribute(chip::ASN1::kOID_AttributeType_ChipRootId, 0xCACACACA00000001) == + CHIP_NO_ERROR); + + ChipDN expectedSubjectDN_Root02; + NL_TEST_ASSERT(inSuite, + expectedSubjectDN_Root02.AddAttribute(chip::ASN1::kOID_AttributeType_ChipRootId, 0xCACACACA00000002) == + CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, + expectedSubjectDN_Root02.AddAttribute(chip::ASN1::kOID_AttributeType_ChipFabricId, 0xFAB000000000001D) == + CHIP_NO_ERROR); + + ChipDN expectedSubjectDN_ICA02; + NL_TEST_ASSERT(inSuite, + expectedSubjectDN_ICA02.AddAttribute(chip::ASN1::kOID_AttributeType_ChipICAId, 0xCACACACA00000004) == + CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, + expectedSubjectDN_ICA02.AddAttribute(chip::ASN1::kOID_AttributeType_ChipFabricId, 0xFAB000000000001D) == + CHIP_NO_ERROR); + + ChipDN expectedSubjectDN_Node01_01; + NL_TEST_ASSERT(inSuite, + expectedSubjectDN_Node01_01.AddAttribute(chip::ASN1::kOID_AttributeType_ChipNodeId, 0xDEDEDEDE00010001) == + CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, + expectedSubjectDN_Node01_01.AddAttribute(chip::ASN1::kOID_AttributeType_ChipFabricId, 0xFAB000000000001D) == + CHIP_NO_ERROR); + + const static char commonName_RDN[] = "TestCert02_03"; + + ChipDN expectedSubjectDN_Node02_03; + NL_TEST_ASSERT(inSuite, + expectedSubjectDN_Node02_03.AddAttribute(chip::ASN1::kOID_AttributeType_ChipNodeId, 0xDEDEDEDE00020003) == + CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, + expectedSubjectDN_Node02_03.AddAttribute(chip::ASN1::kOID_AttributeType_ChipFabricId, 0xFAB000000000001D) == + CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, + expectedSubjectDN_Node02_03.AddAttribute(chip::ASN1::kOID_AttributeType_CommonName, + CharSpan(commonName_RDN, strlen(commonName_RDN)), + false) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, + expectedSubjectDN_Node02_03.AddAttribute(chip::ASN1::kOID_AttributeType_ChipCASEAuthenticatedTag, 0xABCD0001) == + CHIP_NO_ERROR); + + // clang-format off + TestCase sTestCases[] = { + // Cert SubjectDN + // ============================================================================ + { TestCert::kRoot01, expectedSubjectDN_Root01 }, + { TestCert::kRoot02, expectedSubjectDN_Root02 }, + { TestCert::kICA02, expectedSubjectDN_ICA02 }, + { TestCert::kNode01_01, expectedSubjectDN_Node01_01 }, + { TestCert::kNode02_03, expectedSubjectDN_Node02_03 }, + }; + // clang-format on + + // Test extraction from the raw ByteSpan form. + for (auto & testCase : sTestCases) + { + ByteSpan cert; + CHIP_ERROR err = GetTestCert(testCase.Cert, sNullLoadFlag, cert); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + ChipDN subjectDN; + err = ExtractSubjectDNFromChipCert(cert, subjectDN); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, subjectDN.IsEqual(testCase.ExpectedSubjectDN)); + } + + // Test extraction from the X509 ByteSpan form. + for (auto & testCase : sTestCases) + { + ByteSpan cert; + CHIP_ERROR err = GetTestCert(testCase.Cert, TestCertLoadFlags::kDERForm, cert); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + + ChipDN subjectDN; + err = ExtractSubjectDNFromX509Cert(cert, subjectDN); + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, subjectDN.IsEqual(testCase.ExpectedSubjectDN)); + } +} + static void TestChipCert_ExtractPublicKeyAndSKID(nlTestSuite * inSuite, void * inContext) { struct TestCase @@ -1518,6 +1611,7 @@ static const nlTest sTests[] = { NL_TEST_DEF("Test extracting Node ID and Fabric ID from node certificate", TestChipCert_ExtractNodeIdFabricId), NL_TEST_DEF("Test extracting Operational Discovery ID from node and root certificate", TestChipCert_ExtractOperationalDiscoveryId), NL_TEST_DEF("Test extracting CASE Authenticated Tags from node certificate", TestChipCert_ExtractCATsFromOpCert), + NL_TEST_DEF("Test extracting Subject DN from chip certificate", TestChipCert_ExtractSubjectDNFromChipCert), NL_TEST_DEF("Test extracting PublicKey and SKID from chip certificate", TestChipCert_ExtractPublicKeyAndSKID), NL_TEST_SENTINEL() }; diff --git a/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.h b/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.h index f2d92873ebf4f8..0d038673093835 100644 --- a/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.h +++ b/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.h @@ -67,7 +67,7 @@ class CHIPOperationalCredentialsDelegate : public chip::Controller::OperationalC bool ToChipEpochTime(uint32_t offset, uint32_t & epoch); ChipP256KeypairPtr mIssuerKey; - uint32_t mIssuerId = 1234; + uint64_t mIssuerId = 1234; const uint32_t kCertificateValiditySecs = 365 * 24 * 60 * 60; const NSString * kCHIPCAKeyChainLabel = @"matter.nodeopcerts.CA:0"; diff --git a/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm b/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm index de132ee91df2a4..988dea237afc82 100644 --- a/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm +++ b/src/darwin/Framework/CHIP/CHIPOperationalCredentialsDelegate.mm @@ -87,14 +87,14 @@ static BOOL isRunningTests(void) return CHIP_ERROR_INVALID_ARGUMENT; } - char issuerIdString[16]; - uint16_t idStringLen = sizeof(issuerIdString); - if (CHIP_NO_ERROR != storage->SyncGetKeyValue(CHIP_COMMISSIONER_CA_ISSUER_ID, issuerIdString, idStringLen)) { + uint16_t issuerIdLen = sizeof(mIssuerId); + if (CHIP_NO_ERROR != storage->SyncGetKeyValue(CHIP_COMMISSIONER_CA_ISSUER_ID, &mIssuerId, issuerIdLen)) { mIssuerId = arc4random(); - CHIP_LOG_ERROR("Assigned %d certificate issuer ID to the commissioner", mIssuerId); + mIssuerId = mIssuerId << 32 | arc4random(); + CHIP_LOG_ERROR("Assigned %llx certificate issuer ID to the commissioner", mIssuerId); storage->SyncSetKeyValue(CHIP_COMMISSIONER_CA_ISSUER_ID, &mIssuerId, sizeof(mIssuerId)); } else { - CHIP_LOG_ERROR("Found %d certificate issuer ID for the commissioner", mIssuerId); + CHIP_LOG_ERROR("Found %llx certificate issuer ID for the commissioner", mIssuerId); } return CHIP_NO_ERROR; @@ -203,40 +203,37 @@ static BOOL isRunningTests(void) return CHIP_ERROR_INTERNAL; } - ChipDN noc_dn; - ReturnErrorOnFailure(noc_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipFabricId, fabricId)); - ReturnErrorOnFailure(noc_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipNodeId, nodeId)); - ReturnErrorOnFailure(noc_dn.AddCATs(cats)); ChipDN rcac_dn; - ReturnErrorOnFailure(rcac_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipRootId, mIssuerId)); - ReturnErrorOnFailure(rcac_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipFabricId, fabricId)); + if (!mGenerateRootCert) { + uint16_t rcacBufLen = static_cast(std::min(rcac.size(), static_cast(UINT16_MAX))); + PERSISTENT_KEY_OP(fabricId, kOperationalCredentialsRootCertificateStorage, key, + ReturnErrorOnFailure(mStorage->SyncGetKeyValue(key, rcac.data(), rcacBufLen))); + rcac.reduce_size(rcacBufLen); + ReturnErrorOnFailure(ExtractSubjectDNFromX509Cert(rcac, rcac_dn)); + } else { + ReturnErrorOnFailure(rcac_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipRootId, mIssuerId)); + ReturnErrorOnFailure(rcac_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipFabricId, fabricId)); - X509CertRequestParams noc_request = { 1, validityStart, validityEnd, noc_dn, rcac_dn }; - ReturnErrorOnFailure(NewNodeOperationalX509Cert(noc_request, pubkey, *mIssuerKey, noc)); - icac.reduce_size(0); + NSLog(@"Generating RCAC"); + X509CertRequestParams rcac_request = { 0, validityStart, validityEnd, rcac_dn, rcac_dn }; + ReturnErrorOnFailure(NewRootX509Cert(rcac_request, *mIssuerKey, rcac)); - uint16_t rcacBufLen = static_cast(std::min(rcac.size(), static_cast(UINT16_MAX))); - CHIP_ERROR err = CHIP_NO_ERROR; - if (!mGenerateRootCert) { + VerifyOrReturnError(CanCastTo(rcac.size()), CHIP_ERROR_INTERNAL); PERSISTENT_KEY_OP(fabricId, kOperationalCredentialsRootCertificateStorage, key, - err = mStorage->SyncGetKeyValue(key, rcac.data(), rcacBufLen)); - if (err == CHIP_NO_ERROR) { - // Found root certificate in the storage. - rcac.reduce_size(rcacBufLen); - return CHIP_NO_ERROR; - } - } + ReturnErrorOnFailure(mStorage->SyncSetKeyValue(key, rcac.data(), static_cast(rcac.size())))); - X509CertRequestParams rcac_request = { 0, validityStart, validityEnd, rcac_dn, rcac_dn }; - ReturnErrorOnFailure(NewRootX509Cert(rcac_request, *mIssuerKey, rcac)); + mGenerateRootCert = false; + } - VerifyOrReturnError(CanCastTo(rcac.size()), CHIP_ERROR_INTERNAL); - PERSISTENT_KEY_OP(fabricId, kOperationalCredentialsRootCertificateStorage, key, - err = mStorage->SyncSetKeyValue(key, rcac.data(), static_cast(rcac.size()))); + icac.reduce_size(0); - mGenerateRootCert = false; + ChipDN noc_dn; + ReturnErrorOnFailure(noc_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipFabricId, fabricId)); + ReturnErrorOnFailure(noc_dn.AddAttribute(chip::ASN1::kOID_AttributeType_ChipNodeId, nodeId)); + ReturnErrorOnFailure(noc_dn.AddCATs(cats)); - return err; + X509CertRequestParams noc_request = { 1, validityStart, validityEnd, noc_dn, rcac_dn }; + return NewNodeOperationalX509Cert(noc_request, pubkey, *mIssuerKey, noc); } CHIP_ERROR CHIPOperationalCredentialsDelegate::GenerateNOCChain(const chip::ByteSpan & csrElements, diff --git a/src/lib/asn1/ASN1Macros.h b/src/lib/asn1/ASN1Macros.h index 1a38c47303d6b8..5107515dda0425 100644 --- a/src/lib/asn1/ASN1Macros.h +++ b/src/lib/asn1/ASN1Macros.h @@ -86,18 +86,28 @@ } \ while (0) +#define ASN1_SKIP_AND_EXIT_CONSTRUCTED \ + ASN1_ERR = ASN1_READER.ExitConstructedType(); \ + SuccessOrExit(ASN1_ERR); \ + } \ + while (0) + #define ASN1_PARSE_ENTER_SEQUENCE ASN1_PARSE_ENTER_CONSTRUCTED(kASN1TagClass_Universal, kASN1UniversalTag_Sequence) #define ASN1_ENTER_SEQUENCE ASN1_ENTER_CONSTRUCTED(kASN1TagClass_Universal, kASN1UniversalTag_Sequence) #define ASN1_EXIT_SEQUENCE ASN1_EXIT_CONSTRUCTED +#define ASN1_SKIP_AND_EXIT_SEQUENCE ASN1_SKIP_AND_EXIT_CONSTRUCTED + #define ASN1_PARSE_ENTER_SET ASN1_PARSE_ENTER_CONSTRUCTED(kASN1TagClass_Universal, kASN1UniversalTag_Set) #define ASN1_ENTER_SET ASN1_ENTER_CONSTRUCTED(kASN1TagClass_Universal, kASN1UniversalTag_Set) #define ASN1_EXIT_SET ASN1_EXIT_CONSTRUCTED +#define ASN1_SKIP_AND_EXIT_SET ASN1_SKIP_AND_EXIT_CONSTRUCTED + #define ASN1_ENTER_ENCAPSULATED(CLASS, TAG) \ do \ { \