/*
 *
 *    Copyright (c) 2020-2022 Project CHIP Authors
 *    Copyright (c) 2019 Google LLC.
 *    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 defines data types and objects for modeling and
 *      working with CHIP certificates.
 *
 */

#pragma once

#include <cstdint>
#include <string.h>

#include <crypto/CHIPCryptoPAL.h>
#include <lib/asn1/ASN1.h>
#include <lib/core/CASEAuthTag.h>
#include <lib/core/CHIPConfig.h>
#include <lib/core/DataModelTypes.h>
#include <lib/core/PeerId.h>
#include <lib/core/TLV.h>
#include <lib/support/BitFlags.h>
#include <lib/support/DLLUtil.h>
#include <lib/support/Span.h>
#include <system/SystemClock.h>

namespace chip {
namespace Credentials {

static constexpr uint32_t kKeyIdentifierLength                 = static_cast<uint32_t>(Crypto::kSubjectKeyIdentifierLength);
static constexpr uint32_t kChip32bitAttrUTF8Length             = 8;
static constexpr uint32_t kChip64bitAttrUTF8Length             = 16;
static constexpr uint16_t kX509NoWellDefinedExpirationDateYear = 9999;

// As per specifications (6.4.5. Node Operational Credentials Certificates)
static constexpr uint32_t kMaxCHIPCertLength = 400;
static constexpr uint32_t kMaxDERCertLength  = 600;

// As per spec section 11.24 (Wi-Fi Authentication with Per-Device Credentials)
inline constexpr uint32_t kMaxCHIPCompactNetworkIdentityLength = 137;

// Length of a ASN.1 DER encoded ECPrivateKey structure (RFC 5915) for a P256 key pair.
inline constexpr uint32_t kP256ECPrivateKeyDERLength = 121;

/** Data Element Tags for the CHIP Certificate
 */
enum
{
    // ---- Context-specific Tags for ChipCertificate Structure ----
    kTag_SerialNumber            = 1,  /**< [ byte string ] Certificate serial number, in BER integer encoding. */
    kTag_SignatureAlgorithm      = 2,  /**< [ unsigned int ] Enumerated value identifying the certificate signature algorithm. */
    kTag_Issuer                  = 3,  /**< [ list ] The issuer distinguished name of the certificate. */
    kTag_NotBefore               = 4,  /**< [ unsigned int ] Certificate validity period start (certificate date format). */
    kTag_NotAfter                = 5,  /**< [ unsigned int ] Certificate validity period end (certificate date format). */
    kTag_Subject                 = 6,  /**< [ list ] The subject distinguished name of the certificate. */
    kTag_PublicKeyAlgorithm      = 7,  /**< [ unsigned int ] Identifies the algorithm with which the public key can be used. */
    kTag_EllipticCurveIdentifier = 8,  /**< [ unsigned int ] For EC certs, identifies the elliptic curve used. */
    kTag_EllipticCurvePublicKey  = 9,  /**< [ byte string ] The elliptic curve public key, in X9.62 encoded format. */
    kTag_Extensions              = 10, /**< [ list ] Certificate extensions. */
    kTag_ECDSASignature          = 11, /**< [ byte string ] The ECDSA signature for the certificate. */

    // ---- Context-specific Tags for certificate extensions ----
    kTag_BasicConstraints       = 1, /**< [ structure ] Identifies whether the subject of the certificate is a CA. */
    kTag_KeyUsage               = 2, /**< [ unsigned int ] Bits identifying key usage, per RFC5280. */
    kTag_ExtendedKeyUsage       = 3, /**< [ array ] Enumerated values giving the purposes for which the public key can be used. */
    kTag_SubjectKeyIdentifier   = 4, /**< [ byte string ] Identifier of the certificate's public key. */
    kTag_AuthorityKeyIdentifier = 5, /**< [ byte string ] Identifier of the public key used to sign the certificate. */
    kTag_FutureExtension        = 6, /**< [ byte string ] Arbitrary extension. DER encoded SEQUENCE as in X.509 form. */

    // ---- Context-specific Tags for BasicConstraints Structure ----
    kTag_BasicConstraints_IsCA = 1,              /**< [ boolean ] True if the certificate can be used to verify certificate
                                                    signatures. */
    kTag_BasicConstraints_PathLenConstraint = 2, /**< [ unsigned int ] Maximum number of subordinate intermediate certificates. */
};

/** Identifies the purpose or application of certificate
 *
 * A certificate type is a label that describes a certificate's purpose or application.
 * Certificate types are not carried as attributes of the corresponding certificates, but
 * rather are derived from the certificate's structure and/or the context in which it is used.
 * The certificate type enumeration includes a set of pre-defined values describing commonly
 * used certificate applications.
 *
 * Certificate types are primarily used in the implementation of access control policies,
 * where access to application features is influenced by the type of certificate presented
 * by a requester.
 *
 * @note Cert type is an API data type only; it should never be sent over-the-wire.
 */
enum class CertType : uint8_t
{
    kNotSpecified    = 0x00,       /**< The certificate's type has not been specified. */
    kRoot            = 0x01,       /**< A Matter Root certificate (RCAC). */
    kICA             = 0x02,       /**< A Matter Intermediate CA certificate (ICAC). */
    kNode            = 0x03,       /**< A Matter node operational certificate (NOC). */
    kFirmwareSigning = 0x04,       /**< A Matter firmware signing certificate. Note that Matter doesn't
                                        specify how firmware images are signed and implementation of
                                        firmware image signing is manufacturer-specific. The Matter
                                        certificate format supports encoding of firmware signing
                                        certificates if chosen by the manufacturer to use them. */
    kNetworkIdentity       = 0x05, /**< A Matter Network (Client) Identity. */
    kVidVerificationSigner = 0x06  /**< A Matter VendorID Verification Signer Certificate. */
};

/** X.509 Certificate Key Purpose Flags
 *
 *    The flags order must match the enumeration order of corresponding kOID_KeyPurpose values.
 */
enum class KeyPurposeFlags : uint8_t
{
    kServerAuth      = 0x01, /**< Extended key usage is server authentication. */
    kClientAuth      = 0x02, /**< Extended key usage is client authentication. */
    kCodeSigning     = 0x04, /**< Extended key usage is code signing. */
    kEmailProtection = 0x08, /**< Extended key usage is email protection. */
    kTimeStamping    = 0x10, /**< Extended key usage is time stamping. */
    kOCSPSigning     = 0x20, /**< Extended key usage is OCSP signing. */
};

/** X.509 Certificate Key Usage Flags
 */
enum class KeyUsageFlags : uint16_t
{
    kDigitalSignature = 0x0001, /**< Key usage is digital signature. */
    kNonRepudiation   = 0x0002, /**< Key usage is non-repudiation. */
    kKeyEncipherment  = 0x0004, /**< Key usage is key encipherment. */
    kDataEncipherment = 0x0008, /**< Key usage is data encipherment. */
    kKeyAgreement     = 0x0010, /**< Key usage is key agreement. */
    kKeyCertSign      = 0x0020, /**< Key usage is key cert signing. */
    kCRLSign          = 0x0040, /**< Key usage is CRL signing. */
    kEncipherOnly     = 0x0080, /**< Key usage is encipher only. */
    kDecipherOnly     = 0x0100, /**< Key usage is decipher only. */
};

/** CHIP Certificate Flags
 *
 * Contains information about a certificate that has been loaded into a ChipCertificateData object.
 */
enum class CertFlags : uint16_t
{
    kExtPresent_BasicConstraints = 0x0001, /**< Basic constraints extension is present in the certificate. */
    kExtPresent_KeyUsage         = 0x0002, /**< Key usage extension is present in the certificate. */
    kExtPresent_ExtendedKeyUsage = 0x0004, /**< Extended key usage extension is present in the certificate. */
    kExtPresent_SubjectKeyId     = 0x0008, /**< Subject key identifier extension is present in the certificate. */
    kExtPresent_AuthKeyId        = 0x0010, /**< Authority key identifier extension is present in the certificate. */
    kExtPresent_FutureIsCritical = 0x0020, /**< Future extension marked as critical is present in the certificate. */
    kPathLenConstraintPresent    = 0x0040, /**< Path length constraint is present in the certificate. */
    kIsCA                        = 0x0080, /**< Indicates that certificate is a CA certificate. */
    kIsTrustAnchor               = 0x0100, /**< Indicates that certificate is a trust anchor. */
    kTBSHashPresent              = 0x0200, /**< Indicates that TBS hash of the certificate was generated and stored. */
};

/** CHIP Certificate Decode Flags
 *
 * Contains information specifying how a certificate should be decoded.
 */
enum class CertDecodeFlags : uint8_t
{
    kGenerateTBSHash = 0x01, /**< Indicates that to-be-signed (TBS) hash of the certificate should be calculated when certificate is
                                loaded. The TBS hash is then used to validate certificate signature. Normally, all certificates
                                (except trust anchor) in the certificate validation chain require TBS hash. */
    kIsTrustAnchor = 0x02,   /**< Indicates that the corresponding certificate is trust anchor. */
};

enum
{
    kNullCertTime = 0
};

/**
 *  @struct ChipRDN
 *
 *  @brief
 *    A data structure representing a Relative Distinguished Name (RDN) in a CHIP certificate.
 */
struct ChipRDN
{
    CharSpan mString;                                         /**< Attribute value when encoded as a string. */
    uint64_t mChipVal;                                        /**< CHIP specific DN attribute value. */
    chip::ASN1::OID mAttrOID = chip::ASN1::kOID_NotSpecified; /**< DN attribute CHIP OID. */
    bool mAttrIsPrintableString;                              /**< Specifies if attribute is a printable string type. */

    bool IsEqual(const ChipRDN & other) const;
    bool IsEmpty() const { return mAttrOID == chip::ASN1::kOID_NotSpecified; }
    void Clear()
    {
        mAttrOID               = chip::ASN1::kOID_NotSpecified;
        mAttrIsPrintableString = false;
        mChipVal               = 0;
        mString                = CharSpan{};
    }
};

/**
 *  @brief
 *    A data structure representing a Distinguished Name (DN) in a CHIP certificate.
 */
class ChipDN
{
public:
    ChipDN();
    ~ChipDN();

    void Clear();

    /**
     * @brief Add CHIP-specific attribute to the DN.
     *
     * @param oid     CHIP-specific OID for DN attribute.
     * @param val     CHIP-specific DN attribute value.
     *
     * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
     **/
    CHIP_ERROR AddAttribute(chip::ASN1::OID oid, uint64_t val);

    /**
     * @brief Add CASE Authenticated Tags (CATs) attributes to the DN.
     *
     * @param cats    Array of CAT values.
     *
     * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
     **/
    CHIP_ERROR AddCATs(const chip::CATValues & cats);

    /**
     * @brief Add string attribute to the DN.
     *
     * @param oid     String OID for DN attribute.
     * @param val     A CharSpan object containing a pointer and length of the DN string attribute
     *                buffer. The value in the buffer should remain valid while the object is in use.
     * @param isPrintableString  Specifies if attribute ASN1 type is a printable string.
     *
     * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
     **/
    CHIP_ERROR AddAttribute(chip::ASN1::OID oid, CharSpan val, bool isPrintableString);

    inline CHIP_ERROR AddAttribute_CommonName(CharSpan val, bool isPrintableString)
    {
        return AddAttribute(ASN1::kOID_AttributeType_CommonName, val, isPrintableString);
    }
    inline CHIP_ERROR AddAttribute_Surname(CharSpan val, bool isPrintableString)
    {
        return AddAttribute(ASN1::kOID_AttributeType_Surname, val, isPrintableString);
    }
    inline CHIP_ERROR AddAttribute_SerialNumber(CharSpan val, bool isPrintableString)
    {
        return AddAttribute(ASN1::kOID_AttributeType_SerialNumber, val, isPrintableString);
    }
    inline CHIP_ERROR AddAttribute_CountryName(CharSpan val, bool isPrintableString)
    {
        return AddAttribute(ASN1::kOID_AttributeType_CountryName, val, isPrintableString);
    }
    inline CHIP_ERROR AddAttribute_LocalityName(CharSpan val, bool isPrintableString)
    {
        return AddAttribute(ASN1::kOID_AttributeType_LocalityName, val, isPrintableString);
    }
    inline CHIP_ERROR AddAttribute_StateOrProvinceName(CharSpan val, bool isPrintableString)
    {
        return AddAttribute(ASN1::kOID_AttributeType_StateOrProvinceName, val, isPrintableString);
    }
    inline CHIP_ERROR AddAttribute_OrganizationName(CharSpan val, bool isPrintableString)
    {
        return AddAttribute(ASN1::kOID_AttributeType_OrganizationName, val, isPrintableString);
    }
    inline CHIP_ERROR AddAttribute_OrganizationalUnitName(CharSpan val, bool isPrintableString)
    {
        return AddAttribute(ASN1::kOID_AttributeType_OrganizationalUnitName, val, isPrintableString);
    }
    inline CHIP_ERROR AddAttribute_Title(CharSpan val, bool isPrintableString)
    {
        return AddAttribute(ASN1::kOID_AttributeType_Title, val, isPrintableString);
    }
    inline CHIP_ERROR AddAttribute_Name(CharSpan val, bool isPrintableString)
    {
        return AddAttribute(ASN1::kOID_AttributeType_Name, val, isPrintableString);
    }
    inline CHIP_ERROR AddAttribute_GivenName(CharSpan val, bool isPrintableString)
    {
        return AddAttribute(ASN1::kOID_AttributeType_GivenName, val, isPrintableString);
    }
    inline CHIP_ERROR AddAttribute_Initials(CharSpan val, bool isPrintableString)
    {
        return AddAttribute(ASN1::kOID_AttributeType_Initials, val, isPrintableString);
    }
    inline CHIP_ERROR AddAttribute_GenerationQualifier(CharSpan val, bool isPrintableString)
    {
        return AddAttribute(ASN1::kOID_AttributeType_GenerationQualifier, val, isPrintableString);
    }
    inline CHIP_ERROR AddAttribute_DNQualifier(CharSpan val, bool isPrintableString)
    {
        return AddAttribute(ASN1::kOID_AttributeType_DNQualifier, val, isPrintableString);
    }
    inline CHIP_ERROR AddAttribute_Pseudonym(CharSpan val, bool isPrintableString)
    {
        return AddAttribute(ASN1::kOID_AttributeType_Pseudonym, val, isPrintableString);
    }
    inline CHIP_ERROR AddAttribute_DomainComponent(CharSpan val, bool isPrintableString)
    {
        return AddAttribute(ASN1::kOID_AttributeType_DomainComponent, val, isPrintableString);
    }
    inline CHIP_ERROR AddAttribute_MatterNodeId(uint64_t val) { return AddAttribute(ASN1::kOID_AttributeType_MatterNodeId, val); }
    inline CHIP_ERROR AddAttribute_MatterFirmwareSigningId(uint64_t val)
    {
        return AddAttribute(ASN1::kOID_AttributeType_MatterFirmwareSigningId, val);
    }
    inline CHIP_ERROR AddAttribute_MatterICACId(uint64_t val) { return AddAttribute(ASN1::kOID_AttributeType_MatterICACId, val); }
    inline CHIP_ERROR AddAttribute_MatterRCACId(uint64_t val) { return AddAttribute(ASN1::kOID_AttributeType_MatterRCACId, val); }
    inline CHIP_ERROR AddAttribute_MatterFabricId(uint64_t val)
    {
        return AddAttribute(ASN1::kOID_AttributeType_MatterFabricId, val);
    }
    inline CHIP_ERROR AddAttribute_MatterCASEAuthTag(CASEAuthTag val)
    {
        return AddAttribute(ASN1::kOID_AttributeType_MatterCASEAuthTag, val);
    }
    inline CHIP_ERROR AddAttribute_MatterVidVerificationSignerId(uint64_t val)
    {
        return AddAttribute(ASN1::kOID_AttributeType_MatterVidVerificationSignerId, val);
    }

    /**
     * @brief Determine type of a CHIP certificate.
     *        This method performs an assessment of a certificate's type based on the structure
     *        of its subject DN.
     *
     * @param certType  A reference to the certificate type value.
     *
     * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
     **/
    CHIP_ERROR GetCertType(CertType & certType) const;

    /**
     * @brief Retrieve the ID of a CHIP certificate.
     *
     * @param certId  A reference to the certificate ID value.
     *
     * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
     **/
    CHIP_ERROR GetCertChipId(uint64_t & certId) const;

    /**
     * @brief Retrieve the Fabric ID of a CHIP certificate.
     **/
    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.
     *
     * @param reader  A TLVReader positioned at the ChipDN TLV list.
     **/
    CHIP_ERROR DecodeFromTLV(chip::TLV::TLVReader & reader);

    /**
     * @brief Encode ChipDN attributes in ASN1 form.
     **/
    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;

    /**
     * @brief Determine if DN is empty (doesn't have DN attributes).
     *
     * @return true if DN is empty, false otherwise.
     **/
    bool IsEmpty() const { return RDNCount() == 0; }

    static_assert((CHIP_CONFIG_CERT_MAX_RDN_ATTRIBUTES) >= 5, "Spec requires at least 5 RDN to be supported per Matter TLV cert");
    ChipRDN rdn[CHIP_CONFIG_CERT_MAX_RDN_ATTRIBUTES];

    uint8_t RDNCount() const;
};

/**
 *  @brief  A data structure for holding a certificate key identifier, without the ownership of it.
 */
using CertificateKeyId = FixedByteSpan<kKeyIdentifierLength>;

/**
 *  @brief  A mutable `CertificateKeyId`.
 */
using MutableCertificateKeyId = FixedSpan<uint8_t, kKeyIdentifierLength>;

/**
 *  @brief  A storage type for `CertificateKeyId` and `MutableCertificateKeyId`.
 */
using CertificateKeyIdStorage = std::array<uint8_t, kKeyIdentifierLength>;

/**
 *  @brief  A data structure for holding a P256 ECDSA signature, without the ownership of it.
 */
using P256ECDSASignatureSpan = FixedByteSpan<Crypto::kP256_ECDSA_Signature_Length_Raw>;

/**
 *  @brief  A data structure for holding a P256 Public Key, without the ownership of it.
 */
using P256PublicKeySpan = FixedByteSpan<Crypto::kP256_PublicKey_Length>;

/**
 *  @brief  A data structure for holding a P256 Integer, without the ownership of it.
 */
using P256IntegerSpan = FixedByteSpan<Crypto::kP256_FE_Length>;

/**
 *  @struct ChipCertificateData
 *
 *  @brief
 *    In-memory representation of data extracted from a CHIP certificate.
 *
 *    Some of the fields in this structure are pointers to the fields in the original
 *    CHIP certificate. That CHIP certificate is stored in a separate buffer and it is
 *    required that data in that buffer remains valid while the corresponding
 *    ChipCertificateData structure is used.
 */
struct ChipCertificateData
{
    ChipCertificateData();
    ~ChipCertificateData();

    void Clear();
    bool IsEqual(const ChipCertificateData & other) const;

    ByteSpan mSerialNumber;                     /**< Certificate Serial Number. */
    ChipDN mSubjectDN;                          /**< Certificate Subject DN. */
    ChipDN mIssuerDN;                           /**< Certificate Issuer DN. */
    CertificateKeyId mSubjectKeyId;             /**< Certificate Subject public key identifier. */
    CertificateKeyId mAuthKeyId;                /**< Certificate Authority public key identifier. */
    uint32_t mNotBeforeTime;                    /**< Certificate validity: Not Before field. */
    uint32_t mNotAfterTime;                     /**< Certificate validity: Not After field. */
    P256PublicKeySpan mPublicKey;               /**< Certificate public key. */
    uint16_t mPubKeyCurveOID;                   /**< Public key Elliptic Curve CHIP OID. */
    uint16_t mPubKeyAlgoOID;                    /**< Public key algorithm CHIP OID. */
    uint16_t mSigAlgoOID;                       /**< Certificate signature algorithm CHIP OID. */
    BitFlags<CertFlags> mCertFlags;             /**< Certificate data flags. */
    BitFlags<KeyUsageFlags> mKeyUsageFlags;     /**< Certificate key usage extensions flags. */
    BitFlags<KeyPurposeFlags> mKeyPurposeFlags; /**< Certificate extended key usage extensions flags. */
    uint8_t mPathLenConstraint;                 /**< Basic constraint: path length. */
    P256ECDSASignatureSpan mSignature;          /**< Certificate signature. */

    uint8_t mTBSHash[Crypto::kSHA256_Hash_Length]; /**< Certificate TBS hash. */
};

/**
 * @brief Decode CHIP certificate.
 *        It is required that the CHIP certificate in the chipCert buffer stays valid while
 *        the certData is used.
 *
 * @param chipCert     Buffer containing CHIP certificate.
 * @param certData     Structure containing data extracted from the CHIP certificate.
 *
 * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
 **/
CHIP_ERROR DecodeChipCert(const ByteSpan chipCert, ChipCertificateData & certData, BitFlags<CertDecodeFlags> decodeFlags = {});

/**
 * @brief Decode CHIP certificate.
 *        It is required that the CHIP certificate in the reader's underlying buffer stays valid while
 *        the certData is used.
 *
 * @param reader    A TLVReader positioned at the CHIP certificate TLV structure.
 * @param certData  Structure containing data extracted from the CHIP certificate.
 *
 * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
 **/
CHIP_ERROR DecodeChipCert(chip::TLV::TLVReader & reader, ChipCertificateData & certData,
                          BitFlags<CertDecodeFlags> decodeFlags = {});

/**
 * @brief Decode CHIP Distinguished Name (DN).
 *        It is required that the CHIP DN in the reader's underlying buffer stays valid while
 *        the dn structure is used.
 *
 * @param reader  A TLVReader positioned at the CHIP DN TLV structure.
 * @param dn      Distinguished Name structure.
 *
 * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
 **/
CHIP_ERROR DecodeChipDN(chip::TLV::TLVReader & reader, ChipDN & dn);

/**
 * @brief Convert standard X.509 certificate to CHIP certificate.
 *
 * @param x509Cert        CHIP X.509 DER encoded certificate.
 * @param chipCert        Buffer to store converted certificate in CHIP format.
 *
 * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
 **/
CHIP_ERROR ConvertX509CertToChipCert(const ByteSpan x509Cert, MutableByteSpan & chipCert);

/**
 * @brief Convert CHIP certificate to the standard X.509 DER encoded certificate.
 *
 * @param chipCert        CHIP certificate in CHIP TLV encoding.
 * @param x509Cert        Buffer to store converted certificate in X.509 DER format.
 *
 * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
 **/
CHIP_ERROR ConvertChipCertToX509Cert(const ByteSpan chipCert, MutableByteSpan & x509Cert);

/**
 * @brief Verifies the signature of a certificate.
 *
 * @param cert    The certificate to be verified.
 * @param signer  The certificate containing the public key used to verify the signature.
 *
 * @return Returns a CHIP_ERROR on validation or other error, CHIP_NO_ERROR otherwise
 *
 * The certificate to be verified must have been decoded with TBS hash calculation enabled.
 *
 * Note that this function performs ONLY signature verification. No Subject and Issuer DN
 * comparison, Key Usage extension checks or similar validation is performed.
 **/
CHIP_ERROR VerifyCertSignature(const ChipCertificateData & cert, const ChipCertificateData & signer);

/**
 * Validate CHIP Root CA Certificate (RCAC) in ByteSpan TLV-encoded form.
 * This function performs RCAC parsing, checks SubjectDN validity, verifies that SubjectDN
 * and IssuerDN are equal, verifies that SKID and AKID are equal, validates certificate signature.
 *
 * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
 */
CHIP_ERROR ValidateChipRCAC(const ByteSpan & rcac);

/**
 * Validates a Network (Client) Identity in TLV-encoded form.
 * Accepts either a full certificate or the compact-pdc-identity format.
 *
 * This function parses the certificate, ensures the rigid fields have the values mandated by the
 * specification, and validates the certificate signature.
 *
 * @param cert The network identity certificate to validate.
 *
 * @return CHIP_NO_ERROR on success, CHIP_ERROR_WRONG_CERT_TYPE if the certificate does
 *         not conform to the requirements for a Network Identity, CHIP_ERROR_INVALID_SIGNATURE
 *         if the certificate has an invalid signature, or another CHIP_ERROR.
 *
 * @see section 11.24 (Wi-Fi Authentication with Per-Device Credentials) of the Matter spec
 */
CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert);

/**
 * Convenience variant of `ValidateChipNetworkIdentity` that, upon successful validation, also
 * calculates the key identifier for the Network (Client) Identity.
 * @see ValidateChipNetworkIdentity
 * @see ExtractIdentifierFromChipNetworkIdentity
 */
CHIP_ERROR ValidateChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId outKeyId);

struct FutureExtension
{
    ByteSpan OID;
    ByteSpan Extension;
};

struct X509CertRequestParams
{
    int64_t SerialNumber;
    uint32_t ValidityStart;
    uint32_t ValidityEnd;
    ChipDN SubjectDN;
    ChipDN IssuerDN;
    Optional<FutureExtension> FutureExt;
};

/**
 * @brief Generate a new X.509 DER encoded Root CA certificate
 *
 * @param requestParams   Certificate request parameters.
 * @param issuerKeypair   The certificate signing key
 * @param x509Cert        Buffer to store signed certificate in X.509 DER format.
 *
 * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
 **/
CHIP_ERROR NewRootX509Cert(const X509CertRequestParams & requestParams, const Crypto::P256Keypair & issuerKeypair,
                           MutableByteSpan & x509Cert);

/**
 * @brief Generate a new X.509 DER encoded Intermediate CA certificate
 *
 * @param requestParams   Certificate request parameters.
 * @param subjectPubkey   The public key of subject
 * @param issuerKeypair   The certificate signing key
 * @param x509Cert        Buffer to store signed certificate in X.509 DER format.
 *
 * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
 **/
CHIP_ERROR NewICAX509Cert(const X509CertRequestParams & requestParams, const Crypto::P256PublicKey & subjectPubkey,
                          const Crypto::P256Keypair & issuerKeypair, MutableByteSpan & x509Cert);

/**
 * @brief Generate a new X.509 DER encoded Node operational certificate
 *
 * @param requestParams   Certificate request parameters.
 * @param subjectPubkey   The public key of subject
 * @param issuerKeypair   The certificate signing key
 * @param x509Cert        Buffer to store signed certificate in X.509 DER format.
 *
 * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
 **/
CHIP_ERROR NewNodeOperationalX509Cert(const X509CertRequestParams & requestParams, const Crypto::P256PublicKey & subjectPubkey,
                                      const Crypto::P256Keypair & issuerKeypair, MutableByteSpan & x509Cert);

/**
 * @brief Generates a Network (Client) Identity certificate in TLV-encoded form.
 *
 * @param keypair The key pair underlying the identity.
 * @param outCompactCert Buffer to store the signed certificate in compact-pdc-identity TLV format.
 *                       Must be at least `kMaxCHIPCompactNetworkIdentityLength` bytes long.
 *
 * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
 **/
CHIP_ERROR NewChipNetworkIdentity(const Crypto::P256Keypair & keypair, MutableByteSpan & outCompactCert);

/**
 * @brief Generate a new X.509 DER encoded VendorID Verification Signer CA certificate
 *
 * @param requestParams   Certificate request parameters.
 * @param subjectPubkey   The public key of subject
 * @param issuerKeypair   The certificate signing key
 * @param x509Cert        Buffer to store signed certificate in X.509 DER format.
 *
 * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
 **/
CHIP_ERROR NewVidVerificationSignerX509Cert(const X509CertRequestParams & requestParams,
                                            const Crypto::P256PublicKey & subjectPubkey, const Crypto::P256Keypair & issuerKeypair,
                                            MutableByteSpan & x509Cert);

/**
 * @brief
 *   Convert a certificate date/time (in the form of an ASN.1 universal time structure) into a CHIP Epoch time.
 *
 * @note
 *   This function makes no attempt to verify the correct range of the input time other than year.
 *   Therefore callers must make sure the supplied values are valid prior to invocation.
 *
 * @param asn1Time   The calendar date/time to be converted.
 * @param epochTime  A reference to an integer that will receive CHIP Epoch time.
 *
 * @retval  #CHIP_NO_ERROR                      If the input time was successfully converted.
 * @retval  #ASN1_ERROR_UNSUPPORTED_ENCODING    If the input time contained a year value that could not
 *                                              be represented in a CHIP epoch UTC time value.
 **/
CHIP_ERROR ASN1ToChipEpochTime(const chip::ASN1::ASN1UniversalTime & asn1Time, uint32_t & epochTime);

/**
 * @brief
 *   Convert a CHIP epoch UTC time into an ASN.1 universal time structure.
 *
 * @param epochTime  A CHIP epoch UTC time to be converted.
 * @param asn1Time   A reference to an ASN1UniversalTime structure to receive the date/time.
 *
 * @retval  #CHIP_NO_ERROR                      If the input time was successfully converted.
 */
CHIP_ERROR ChipEpochToASN1Time(uint32_t epochTime, chip::ASN1::ASN1UniversalTime & asn1Time);

/**
 *  @return  True if the OID represents a CHIP-defined 64-bit distinguished named attribute.
 **/
inline bool IsChip64bitDNAttr(chip::ASN1::OID oid)
{
    return (oid == chip::ASN1::kOID_AttributeType_MatterNodeId || oid == chip::ASN1::kOID_AttributeType_MatterFirmwareSigningId ||
            oid == chip::ASN1::kOID_AttributeType_MatterICACId || oid == chip::ASN1::kOID_AttributeType_MatterRCACId ||
            oid == chip::ASN1::kOID_AttributeType_MatterFabricId ||
            oid == chip::ASN1::kOID_AttributeType_MatterVidVerificationSignerId);
}

/**
 *  @return  True if the OID represents a CHIP-defined 32-bit distinguished named attribute.
 **/
inline bool IsChip32bitDNAttr(chip::ASN1::OID oid)
{
    return (oid == chip::ASN1::kOID_AttributeType_MatterCASEAuthTag);
}

/**
 *  @return  True if the OID represents a CHIP-defined distinguished named attribute.
 **/
inline bool IsChipDNAttr(chip::ASN1::OID oid)
{
    return (IsChip64bitDNAttr(oid) || IsChip32bitDNAttr(oid));
}

/**
 * @brief Convert an ASN.1 DER encoded integer to a raw big-endian integer.
 *
 * @param derInt     P256 integer in ASN.1 DER encoded form.
 * @param rawInt     Buffer to store converted raw integer.
 * @param rawIntLen  The length of the converted raw integer.
 *
 * @retval  #CHIP_NO_ERROR  If the integer value was successfully converted.
 */
CHIP_ERROR ConvertIntegerDERToRaw(ByteSpan derInt, uint8_t * rawInt, const uint16_t rawIntLen);

/**
 * @brief Convert a raw CHIP signature to an ASN.1 DER encoded signature structure.
 *
 * @param[in]     rawSig  P256 ECDSA signature in raw form.
 * @param[in,out] derSig  Output buffer to receive the converted ASN.1 DER encoded signature.
 *                        `derSig` must be at least `kMax_ECDSA_Signature_Length_Der` bytes long.
 *                        The `derSig` size will be set to the actual DER encoded signature length on success.
 *
 * @retval  #CHIP_NO_ERROR  If the signature value was successfully converted.
 */
CHIP_ERROR ConvertECDSASignatureRawToDER(P256ECDSASignatureSpan rawSig, MutableByteSpan & derSig);

/**
 * @brief Convert a raw CHIP ECDSA signature to an ASN.1 DER encoded signature structure.
 *
 * @param rawSig        P256 ECDSA signature in raw form.
 * @param writer        A reference to the ASN1Writer to store ASN.1 DER encoded signature.
 *
 * @retval  #CHIP_NO_ERROR  If the signature value was successfully converted.
 */
CHIP_ERROR ConvertECDSASignatureRawToDER(P256ECDSASignatureSpan rawSig, ASN1::ASN1Writer & writer);

/**
 * @brief Convert an ASN.1 DER encoded ECDSA signature to a raw CHIP signature.
 *
 * @param reader        A reference to the ASN1Reader positioned at the beginning of the
 *                      DER encoded ECDSA signature.
 * @param writer        A reference to the TLVWriter to store TLV encoded ECDSA signature element.
 * @param tag           Tag to use for TLV encoded signature.
 *
 * @retval  #CHIP_NO_ERROR  If the signature value was successfully converted.
 */
CHIP_ERROR ConvertECDSASignatureDERToRaw(ASN1::ASN1Reader & reader, chip::TLV::TLVWriter & writer, uint64_t tag);

/**
 * @brief Convert a raw ECDSA P256 key pair to an ASN.1 DER encoded ECPrivateKey structure (RFC 5915).
 *
 * @param rawKeypair    The raw P256 key pair.
 * @param outDerKeypair Output buffer to receive the ASN.1 DER encoded key pair.
 *                      Must have a capacity of at least `kP256ECPrivateKeyDERLength` bytes.
 *
 * @retval  #CHIP_NO_ERROR If the key pair was successfully converted, or a CHIP_ERROR otherwise.
 */
CHIP_ERROR ConvertECDSAKeypairRawToDER(const Crypto::P256SerializedKeypair & rawKeypair, MutableByteSpan & outDerKeypair);

/**
 * Extract the Fabric ID from an operational certificate that has already been
 * parsed.
 *
 * This function can be used to extract Fabric ID from an ICA certificate.
 * These certificates may not contain a NodeID, so ExtractNodeIdFabricIdFromOpCert()
 * cannot be used for such certificates.
 *
 * @return CHIP_ERROR_NOT_FOUND if the passed-in cert does not have RDN
 * corresponding to FabricID.
 */
CHIP_ERROR ExtractFabricIdFromCert(const ChipCertificateData & cert, FabricId * fabricId);

/**
 * Extract Node ID and Fabric ID from an operational certificate that has already been
 * parsed.
 *
 * @return CHIP_ERROR_NOT_FOUND if the passed-in cert does not have at
 * least one NodeId RDN and one FabricId RDN in the Subject DN.  No other
 * validation (e.g. checkign that there is exactly one RDN of each type) is
 * performed.
 */
CHIP_ERROR ExtractNodeIdFabricIdFromOpCert(const ChipCertificateData & opcert, NodeId * nodeId, FabricId * fabricId);

/**
 * Extract Node ID, Fabric ID and Compressed Fabric ID from an operational
 * certificate and its associated root certificate.
 *
 * @return CHIP_ERROR on failure or CHIP_NO_ERROR otherwise.
 */
CHIP_ERROR ExtractNodeIdFabricIdCompressedFabricIdFromOpCerts(ByteSpan rcac, ByteSpan noc, CompressedFabricId & compressedFabricId,
                                                              FabricId & fabricId, NodeId & nodeId);

/**
 * Extract Node ID and Compressed Fabric ID from an operational certificate
 * and its associated root certificate.
 *
 * @return CHIP_ERROR on failure or CHIP_NO_ERROR otherwise.
 */
CHIP_ERROR ExtractNodeIdCompressedFabricIdFromOpCerts(ByteSpan rcac, ByteSpan noc, CompressedFabricId & compressedFabricId,
                                                      NodeId & nodeId);

/**
 * Extract CASE Authenticated Tags from an operational certificate in ByteSpan TLV-encoded form.
 *
 * All values in the 'cats' struct will be set either to a valid CAT value or zero (undefined) value.
 *
 * @return CHIP_ERROR_INVALID_ARGUMENT if the passed-in cert is not NOC.
 * @return CHIP_ERROR_BUFFER_TOO_SMALL if there are too many CATs in the NOC
 */
CHIP_ERROR ExtractCATsFromOpCert(const ByteSpan & opcert, CATValues & cats);

/**
 * Extract CASE Authenticated Tags from an operational certificate that has already been
 * parsed.
 *
 * All values in the 'cats' struct will be set either to a valid CAT value or to the kUndefinedCAT value.
 *
 * @return CHIP_ERROR_INVALID_ARGUMENT if the passed-in cert is not NOC.
 * @return CHIP_ERROR_BUFFER_TOO_SMALL if the passed-in CATs array is too small.
 */
CHIP_ERROR ExtractCATsFromOpCert(const ChipCertificateData & opcert, CATValues & cats);

/**
 * Extract Fabric ID from an operational certificate in ByteSpan TLV-encoded
 * form.  This does not perform any sort of validation on the certificate
 * structure other than parsing it.
 *
 * Can return any error that can be returned from parsing the cert or from the
 * ChipCertificateData* version of ExtractNodeIdFabricIdFromOpCert.
 */
CHIP_ERROR ExtractFabricIdFromCert(const ByteSpan & opcert, FabricId * fabricId);

/**
 * Extract Node ID and Fabric ID from an operational certificate in ByteSpan TLV-encoded
 * form.  This does not perform any sort of validation on the certificate
 * structure other than parsing it.
 *
 * Can return any error that can be returned from parsing the cert or from the
 * ChipCertificateData* version of ExtractNodeIdFabricIdFromOpCert.
 */
CHIP_ERROR ExtractNodeIdFabricIdFromOpCert(const ByteSpan & opcert, NodeId * nodeId, FabricId * fabricId);

/**
 * Extract Public Key from a chip certificate in ByteSpan TLV-encoded form.
 * This does not perform any sort of validation on the certificate structure
 * other than parsing it.
 *
 * Can return any error that can be returned from parsing the cert.
 */
CHIP_ERROR ExtractPublicKeyFromChipCert(const ByteSpan & chipCert, P256PublicKeySpan & publicKey);

/**
 * Extract Not Before Time from a chip certificate in ByteSpan TLV-encoded form.
 * Output format is seconds referenced from the CHIP epoch.
 *
 * This does not perform any sort of validation on the certificate structure
 * other than parsing it.
 *
 * @param chipCert CHIP certificate in TLV-encoded form
 * @param notBeforeChipEpochTime (out) certificate NotBefore time as seconds from the CHIP epoch
 * @return CHIP_NO_ERROR if certificate parsing was successful, else an appropriate CHIP_ERROR
 */
CHIP_ERROR ExtractNotBeforeFromChipCert(const ByteSpan & chipCert, chip::System::Clock::Seconds32 & notBeforeChipEpochTime);

/**
 * 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
 * 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);

/**
 * Extracts the key identifier from a Network (Client) Identity in TLV-encoded form.
 * Does NOT perform full validation of the identity certificate.
 *
 * @return CHIP_NO_ERROR on success, CHIP_ERROR_WRONG_CERT_TYPE if the certificate is
 *         not a Network (Client) Identity, or another CHIP_ERROR if parsing fails.
 */
CHIP_ERROR ExtractIdentifierFromChipNetworkIdentity(const ByteSpan & cert, MutableCertificateKeyId outKeyId);

} // namespace Credentials
} // namespace chip
