blob: 9963da14c11db73f880af3a18dbc2a281bc46f1d [file] [log] [blame]
/*
*
* Copyright (c) 2020-2021 Project CHIP Authors
* Copyright (c) 2013-2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file implements methods for converting a standard X.509
* certificate to a CHIP TLV-encoded certificate.
*
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <stddef.h>
#include <credentials/CHIPCert.h>
#include <lib/asn1/ASN1.h>
#include <lib/asn1/ASN1Macros.h>
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPSafeCasts.h>
#include <lib/core/CHIPTLV.h>
#include <lib/core/Optional.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/SafeInt.h>
#include <protocols/Protocols.h>
namespace chip {
namespace Credentials {
using namespace chip::ASN1;
using namespace chip::TLV;
using namespace chip::Protocols;
using namespace chip::Crypto;
static CHIP_ERROR ParseChipAttribute(ASN1Reader & reader, uint64_t & chipAttrOut)
{
CHIP_ERROR err = CHIP_NO_ERROR;
const uint8_t * value = reader.GetValue();
uint32_t valueLen = reader.GetValueLen();
chipAttrOut = 0;
VerifyOrExit(value != nullptr, err = ASN1_ERROR_INVALID_ENCODING);
VerifyOrExit(valueLen == kChip32bitAttrUTF8Length || valueLen == kChip64bitAttrUTF8Length, err = ASN1_ERROR_INVALID_ENCODING);
for (uint32_t i = 0; i < valueLen; i++)
{
chipAttrOut <<= 4;
uint8_t ch = value[i];
if (ch >= '0' && ch <= '9')
{
chipAttrOut |= (ch - '0');
}
// CHIP Id attribute encodings only support uppercase chars.
else if (ch >= 'A' && ch <= 'F')
{
chipAttrOut |= (ch - 'A' + 10);
}
else
{
ExitNow(err = ASN1_ERROR_INVALID_ENCODING);
}
}
exit:
return err;
}
static CHIP_ERROR ConvertDistinguishedName(ASN1Reader & reader, TLVWriter & writer, Tag tag, uint64_t & subjectOrIssuer,
Optional<uint64_t> & fabric)
{
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 the attribute is a CHIP-defined attribute that contains a 64-bit or 32-bit value.
if (IsChipDNAttr(attrOID))
{
// Parse the attribute string into a CHIP attribute.
uint64_t chipAttr;
err = ParseChipAttribute(reader, chipAttr);
SuccessOrExit(err);
// Write the CHIP attribute value into the TLV.
err = writer.Put(ContextTag(tlvTagNum), chipAttr);
SuccessOrExit(err);
// 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)
{
subjectOrIssuer = chipAttr;
}
else if (attrOID == chip::ASN1::kOID_AttributeType_ChipFabricId)
{
fabric.SetValue(chipAttr);
}
}
//
else
{
err =
writer.PutString(ContextTag(tlvTagNum), Uint8::to_const_char(reader.GetValue()), reader.GetValueLen());
SuccessOrExit(err);
}
}
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;
}
static CHIP_ERROR ConvertValidity(ASN1Reader & reader, TLVWriter & writer)
{
CHIP_ERROR err;
ASN1UniversalTime asn1Time;
uint32_t chipEpochTime;
ASN1_PARSE_ENTER_SEQUENCE
{
ASN1_PARSE_TIME(asn1Time);
err = ASN1ToChipEpochTime(asn1Time, chipEpochTime);
SuccessOrExit(err);
err = writer.Put(ContextTag(kTag_NotBefore), chipEpochTime);
SuccessOrExit(err);
ASN1_PARSE_TIME(asn1Time);
err = ASN1ToChipEpochTime(asn1Time, chipEpochTime);
SuccessOrExit(err);
err = writer.Put(ContextTag(kTag_NotAfter), chipEpochTime);
SuccessOrExit(err);
}
ASN1_EXIT_SEQUENCE;
exit:
return err;
}
static CHIP_ERROR ConvertSubjectPublicKeyInfo(ASN1Reader & reader, TLVWriter & writer)
{
CHIP_ERROR err;
OID pubKeyAlgoOID, pubKeyCurveOID;
// subjectPublicKeyInfo SubjectPublicKeyInfo,
ASN1_PARSE_ENTER_SEQUENCE
{
// algorithm AlgorithmIdentifier,
// AlgorithmIdentifier ::= SEQUENCE
ASN1_PARSE_ENTER_SEQUENCE
{
// algorithm OBJECT IDENTIFIER,
ASN1_PARSE_OBJECT_ID(pubKeyAlgoOID);
// Verify that the algorithm type is supported.
VerifyOrExit(pubKeyAlgoOID == kOID_PubKeyAlgo_ECPublicKey, err = ASN1_ERROR_UNSUPPORTED_ENCODING);
err = writer.Put(ContextTag(kTag_PublicKeyAlgorithm), GetOIDEnum(pubKeyAlgoOID));
SuccessOrExit(err);
// EcpkParameters ::= CHOICE {
// ecParameters ECParameters,
// namedCurve OBJECT IDENTIFIER,
// implicitlyCA NULL }
ASN1_PARSE_ANY;
// ecParameters and implicitlyCA not supported.
if (reader.GetClass() == kASN1TagClass_Universal && reader.GetTag() == kASN1UniversalTag_Sequence)
{
ExitNow(err = ASN1_ERROR_UNSUPPORTED_ENCODING);
}
if (reader.GetClass() == kASN1TagClass_Universal && reader.GetTag() == kASN1UniversalTag_Null)
{
ExitNow(err = ASN1_ERROR_UNSUPPORTED_ENCODING);
}
ASN1_VERIFY_TAG(kASN1TagClass_Universal, kASN1UniversalTag_ObjectId);
ASN1_GET_OBJECT_ID(pubKeyCurveOID);
// Verify the curve name is recognized.
VerifyOrExit(GetOIDCategory(pubKeyCurveOID) == kOIDCategory_EllipticCurve, err = ASN1_ERROR_UNSUPPORTED_ENCODING);
err = writer.Put(ContextTag(kTag_EllipticCurveIdentifier), GetOIDEnum(pubKeyCurveOID));
SuccessOrExit(err);
}
ASN1_EXIT_SEQUENCE;
// subjectPublicKey BIT STRING
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_BitString);
// Verify public key length.
VerifyOrExit(reader.GetValueLen() > 0, err = ASN1_ERROR_INVALID_ENCODING);
// The first byte is Unused Bit Count value, which should be zero.
VerifyOrExit(reader.GetValue()[0] == 0, err = ASN1_ERROR_INVALID_ENCODING);
// Copy the X9.62 encoded EC point into the CHIP certificate as a byte string.
// Skip the first Unused Bit Count byte.
err = writer.PutBytes(ContextTag(kTag_EllipticCurvePublicKey), reader.GetValue() + 1, reader.GetValueLen() - 1);
SuccessOrExit(err);
}
ASN1_EXIT_SEQUENCE;
exit:
return err;
}
static CHIP_ERROR ConvertExtension(ASN1Reader & reader, TLVWriter & writer)
{
CHIP_ERROR err;
TLVType outerContainer;
OID extensionOID;
bool critical = false;
const uint8_t * extensionSequence;
uint32_t extensionSequenceLen;
err = reader.GetConstructedType(extensionSequence, extensionSequenceLen);
SuccessOrExit(err);
// Extension ::= SEQUENCE
ASN1_ENTER_SEQUENCE
{
// extnID OBJECT IDENTIFIER,
ASN1_PARSE_OBJECT_ID(extensionOID);
// The kOID_Unknown will be interpreted and encoded as future-extension.
if (extensionOID != kOID_Unknown)
{
VerifyOrExit(GetOIDCategory(extensionOID) == kOIDCategory_Extension, err = ASN1_ERROR_INVALID_ENCODING);
}
// critical BOOLEAN DEFAULT FALSE,
ASN1_PARSE_ANY;
if (reader.GetClass() == kASN1TagClass_Universal && reader.GetTag() == kASN1UniversalTag_Boolean)
{
ASN1_GET_BOOLEAN(critical);
VerifyOrExit(critical, err = ASN1_ERROR_INVALID_ENCODING);
ASN1_PARSE_ANY;
}
// extnValue OCTET STRING
// -- contains the DER encoding of an ASN.1 value
// -- corresponding to the extension type identified
// -- by extnID
ASN1_ENTER_ENCAPSULATED(kASN1TagClass_Universal, kASN1UniversalTag_OctetString)
{
if (extensionOID == kOID_Extension_AuthorityKeyIdentifier)
{
// This extension MUST be marked as non-critical.
VerifyOrExit(!critical, err = ASN1_ERROR_INVALID_ENCODING);
// AuthorityKeyIdentifier ::= SEQUENCE
ASN1_PARSE_ENTER_SEQUENCE
{
err = reader.Next();
SuccessOrExit(err);
// keyIdentifier [0] IMPLICIT KeyIdentifier,
// KeyIdentifier ::= OCTET STRING
VerifyOrExit(reader.GetClass() == kASN1TagClass_ContextSpecific && reader.GetTag() == 0,
err = ASN1_ERROR_INVALID_ENCODING);
VerifyOrExit(reader.IsConstructed() == false, err = ASN1_ERROR_INVALID_ENCODING);
VerifyOrExit(reader.GetValueLen() == kKeyIdentifierLength, err = ASN1_ERROR_INVALID_ENCODING);
err = writer.PutBytes(ContextTag(kTag_AuthorityKeyIdentifier), reader.GetValue(), reader.GetValueLen());
SuccessOrExit(err);
err = reader.Next();
VerifyOrExit(err == ASN1_END, err = ASN1_ERROR_INVALID_ENCODING);
}
ASN1_EXIT_SEQUENCE;
}
else if (extensionOID == kOID_Extension_SubjectKeyIdentifier)
{
// This extension MUST be marked as non-critical.
VerifyOrExit(!critical, err = ASN1_ERROR_INVALID_ENCODING);
// SubjectKeyIdentifier ::= KeyIdentifier
// KeyIdentifier ::= OCTET STRING
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_OctetString);
VerifyOrExit(reader.GetValueLen() == kKeyIdentifierLength, err = ASN1_ERROR_INVALID_ENCODING);
err = writer.PutBytes(ContextTag(kTag_SubjectKeyIdentifier), reader.GetValue(), reader.GetValueLen());
SuccessOrExit(err);
}
else if (extensionOID == kOID_Extension_KeyUsage)
{
// This extension MUST be marked as critical.
VerifyOrExit(critical, err = ASN1_ERROR_INVALID_ENCODING);
// KeyUsage ::= BIT STRING
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_BitString);
uint32_t keyUsageBits;
err = reader.GetBitString(keyUsageBits);
SuccessOrExit(err);
VerifyOrExit(CanCastTo<uint16_t>(keyUsageBits), err = ASN1_ERROR_INVALID_ENCODING);
// Check that only supported flags are set.
BitFlags<KeyUsageFlags> keyUsageFlags(static_cast<uint16_t>(keyUsageBits));
VerifyOrExit(keyUsageFlags.HasOnly(
KeyUsageFlags::kDigitalSignature, KeyUsageFlags::kNonRepudiation, KeyUsageFlags::kKeyEncipherment,
KeyUsageFlags::kDataEncipherment, KeyUsageFlags::kKeyAgreement, KeyUsageFlags::kKeyCertSign,
KeyUsageFlags::kCRLSign, KeyUsageFlags::kEncipherOnly, KeyUsageFlags::kEncipherOnly),
err = ASN1_ERROR_INVALID_ENCODING);
err = writer.Put(ContextTag(kTag_KeyUsage), keyUsageBits);
SuccessOrExit(err);
}
else if (extensionOID == kOID_Extension_BasicConstraints)
{
// This extension MUST be marked as critical.
VerifyOrExit(critical, err = ASN1_ERROR_INVALID_ENCODING);
// BasicConstraints ::= SEQUENCE
ASN1_PARSE_ENTER_SEQUENCE
{
bool isCA = false;
int64_t pathLenConstraint = -1;
// cA BOOLEAN DEFAULT FALSE
err = reader.Next();
if (err == CHIP_NO_ERROR && reader.GetClass() == kASN1TagClass_Universal &&
reader.GetTag() == kASN1UniversalTag_Boolean)
{
ASN1_GET_BOOLEAN(isCA);
VerifyOrExit(isCA, err = ASN1_ERROR_INVALID_ENCODING);
err = reader.Next();
}
// pathLenConstraint INTEGER (0..MAX) OPTIONAL
if (err == CHIP_NO_ERROR && reader.GetClass() == kASN1TagClass_Universal &&
reader.GetTag() == kASN1UniversalTag_Integer)
{
ASN1_GET_INTEGER(pathLenConstraint);
VerifyOrExit(CanCastTo<uint8_t>(pathLenConstraint), err = ASN1_ERROR_INVALID_ENCODING);
// pathLenConstraint is present only when cA is TRUE
VerifyOrExit(isCA, err = ASN1_ERROR_INVALID_ENCODING);
}
err = writer.StartContainer(ContextTag(kTag_BasicConstraints), kTLVType_Structure, outerContainer);
SuccessOrExit(err);
// Set also when cA is FALSE
err = writer.PutBoolean(ContextTag(kTag_BasicConstraints_IsCA), isCA);
SuccessOrExit(err);
if (pathLenConstraint != -1)
{
err = writer.Put(ContextTag(kTag_BasicConstraints_PathLenConstraint),
static_cast<uint8_t>(pathLenConstraint));
SuccessOrExit(err);
}
err = writer.EndContainer(outerContainer);
SuccessOrExit(err);
}
ASN1_EXIT_SEQUENCE;
}
else if (extensionOID == kOID_Extension_ExtendedKeyUsage)
{
// This extension MUST be marked as critical.
VerifyOrExit(critical, err = ASN1_ERROR_INVALID_ENCODING);
err = writer.StartContainer(ContextTag(kTag_ExtendedKeyUsage), kTLVType_Array, outerContainer);
SuccessOrExit(err);
// ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
ASN1_PARSE_ENTER_SEQUENCE
{
while ((err = reader.Next()) == CHIP_NO_ERROR)
{
// KeyPurposeId ::= OBJECT IDENTIFIER
OID keyPurposeOID;
ASN1_GET_OBJECT_ID(keyPurposeOID);
VerifyOrExit(keyPurposeOID != kOID_Unknown, err = ASN1_ERROR_UNSUPPORTED_ENCODING);
VerifyOrExit(GetOIDCategory(keyPurposeOID) == kOIDCategory_KeyPurpose, err = ASN1_ERROR_INVALID_ENCODING);
err = writer.Put(AnonymousTag, GetOIDEnum(keyPurposeOID));
SuccessOrExit(err);
}
if (err != ASN1_END)
{
SuccessOrExit(err);
}
}
ASN1_EXIT_SEQUENCE;
err = writer.EndContainer(outerContainer);
SuccessOrExit(err);
}
// Any other extension is treated as FutureExtension
else
{
err = writer.PutBytes(ContextTag(kTag_FutureExtension), extensionSequence, extensionSequenceLen);
SuccessOrExit(err);
ASN1_PARSE_ANY;
}
}
ASN1_EXIT_ENCAPSULATED;
}
ASN1_EXIT_SEQUENCE;
exit:
return err;
}
static CHIP_ERROR ConvertExtensions(ASN1Reader & reader, TLVWriter & writer)
{
CHIP_ERROR err;
TLVType containerType;
err = writer.StartContainer(ContextTag(kTag_Extensions), kTLVType_List, containerType);
SuccessOrExit(err);
// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension
ASN1_PARSE_ENTER_SEQUENCE
{
while ((err = reader.Next()) == CHIP_NO_ERROR)
{
err = ConvertExtension(reader, writer);
SuccessOrExit(err);
}
if (err != ASN1_END)
{
SuccessOrExit(err);
}
}
ASN1_EXIT_SEQUENCE;
err = writer.EndContainer(containerType);
SuccessOrExit(err);
exit:
return err;
}
CHIP_ERROR ConvertECDSASignatureDERToRaw(ASN1Reader & reader, TLVWriter & writer, Tag tag)
{
CHIP_ERROR err = CHIP_NO_ERROR;
uint8_t rawSig[kP256_ECDSA_Signature_Length_Raw];
// Per RFC3279, the ECDSA signature value is encoded in DER encapsulated in the signatureValue BIT STRING.
ASN1_ENTER_ENCAPSULATED(kASN1TagClass_Universal, kASN1UniversalTag_BitString)
{
// Ecdsa-Sig-Value ::= SEQUENCE
ASN1_PARSE_ENTER_SEQUENCE
{
// r INTEGER
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_Integer);
ReturnErrorOnFailure(
ConvertIntegerDERToRaw(ByteSpan(reader.GetValue(), reader.GetValueLen()), rawSig, kP256_FE_Length));
// s INTEGER
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_Integer);
ReturnErrorOnFailure(ConvertIntegerDERToRaw(ByteSpan(reader.GetValue(), reader.GetValueLen()), rawSig + kP256_FE_Length,
kP256_FE_Length));
}
ASN1_EXIT_SEQUENCE;
}
ASN1_EXIT_ENCAPSULATED;
ReturnErrorOnFailure(writer.PutBytes(tag, rawSig, kP256_ECDSA_Signature_Length_Raw));
exit:
return err;
}
static CHIP_ERROR ConvertCertificate(ASN1Reader & reader, TLVWriter & writer, Tag tag, uint64_t & issuer, uint64_t & subject,
Optional<uint64_t> & fabric)
{
CHIP_ERROR err;
int64_t version;
OID sigAlgoOID;
TLVType containerType;
err = writer.StartContainer(tag, kTLVType_Structure, containerType);
SuccessOrExit(err);
// Certificate ::= SEQUENCE
ASN1_PARSE_ENTER_SEQUENCE
{
// tbsCertificate TBSCertificate,
// TBSCertificate ::= SEQUENCE
ASN1_PARSE_ENTER_SEQUENCE
{
// version [0] EXPLICIT Version DEFAULT v1
ASN1_PARSE_ENTER_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0)
{
// Version ::= INTEGER { v1(0), v2(1), v3(2) }
ASN1_PARSE_INTEGER(version);
// Verify that the X.509 certificate version is v3
VerifyOrExit(version == 2, err = ASN1_ERROR_UNSUPPORTED_ENCODING);
}
ASN1_EXIT_CONSTRUCTED;
// serialNumber CertificateSerialNumber
// CertificateSerialNumber ::= INTEGER
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_Integer);
err = writer.PutBytes(ContextTag(kTag_SerialNumber), reader.GetValue(), reader.GetValueLen());
SuccessOrExit(err);
// signature AlgorithmIdentifier
// AlgorithmIdentifier ::= SEQUENCE
ASN1_PARSE_ENTER_SEQUENCE
{
// algorithm OBJECT IDENTIFIER,
ASN1_PARSE_OBJECT_ID(sigAlgoOID);
VerifyOrExit(sigAlgoOID == kOID_SigAlgo_ECDSAWithSHA256, err = ASN1_ERROR_UNSUPPORTED_ENCODING);
err = writer.Put(ContextTag(kTag_SignatureAlgorithm), GetOIDEnum(sigAlgoOID));
SuccessOrExit(err);
}
ASN1_EXIT_SEQUENCE;
// issuer Name
err = ConvertDistinguishedName(reader, writer, ContextTag(kTag_Issuer), issuer, fabric);
SuccessOrExit(err);
// validity Validity,
err = ConvertValidity(reader, writer);
SuccessOrExit(err);
// subject Name,
err = ConvertDistinguishedName(reader, writer, ContextTag(kTag_Subject), subject, fabric);
SuccessOrExit(err);
err = ConvertSubjectPublicKeyInfo(reader, writer);
SuccessOrExit(err);
err = reader.Next();
// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
// Not supported.
if (err == CHIP_NO_ERROR && reader.GetClass() == kASN1TagClass_ContextSpecific && reader.GetTag() == 1)
{
ExitNow(err = ASN1_ERROR_UNSUPPORTED_ENCODING);
}
// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
// Not supported.
if (err == CHIP_NO_ERROR && reader.GetClass() == kASN1TagClass_ContextSpecific && reader.GetTag() == 2)
{
ExitNow(err = ASN1_ERROR_UNSUPPORTED_ENCODING);
}
// extensions [3] EXPLICIT Extensions OPTIONAL
if (err == CHIP_NO_ERROR && reader.GetClass() == kASN1TagClass_ContextSpecific && reader.GetTag() == 3)
{
ASN1_ENTER_CONSTRUCTED(kASN1TagClass_ContextSpecific, 3)
{
err = ConvertExtensions(reader, writer);
SuccessOrExit(err);
}
ASN1_EXIT_CONSTRUCTED;
err = reader.Next();
}
if (err != ASN1_END)
{
ExitNow();
}
}
ASN1_EXIT_SEQUENCE;
// signatureAlgorithm AlgorithmIdentifier
// AlgorithmIdentifier ::= SEQUENCE
ASN1_PARSE_ENTER_SEQUENCE
{
OID localSigAlgoOID;
// algorithm OBJECT IDENTIFIER,
ASN1_PARSE_OBJECT_ID(localSigAlgoOID);
// Verify that the signatureAlgorithm is the same as the "signature" field in TBSCertificate.
VerifyOrExit(localSigAlgoOID == sigAlgoOID, err = ASN1_ERROR_UNSUPPORTED_ENCODING);
}
ASN1_EXIT_SEQUENCE;
// signatureValue BIT STRING
ASN1_PARSE_ELEMENT(kASN1TagClass_Universal, kASN1UniversalTag_BitString);
ReturnErrorOnFailure(ConvertECDSASignatureDERToRaw(reader, writer, ContextTag(kTag_ECDSASignature)));
}
ASN1_EXIT_SEQUENCE;
err = writer.EndContainer(containerType);
SuccessOrExit(err);
exit:
return err;
}
CHIP_ERROR ConvertX509CertToChipCert(const ByteSpan x509Cert, MutableByteSpan & chipCert)
{
ASN1Reader reader;
TLVWriter writer;
uint64_t issuer, subject;
Optional<uint64_t> fabric;
VerifyOrReturnError(!x509Cert.empty(), CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(CanCastTo<uint32_t>(x509Cert.size()), CHIP_ERROR_INVALID_ARGUMENT);
reader.Init(x509Cert);
writer.Init(chipCert);
ReturnErrorOnFailure(ConvertCertificate(reader, writer, AnonymousTag, issuer, subject, fabric));
ReturnErrorOnFailure(writer.Finalize());
chipCert.reduce_size(writer.GetLengthWritten());
return CHIP_NO_ERROR;
}
} // namespace Credentials
} // namespace chip