From 237017f6baf44ab149e5cc2de6a49b08583582ec Mon Sep 17 00:00:00 2001 From: Chirag Bansal Date: Mon, 13 Feb 2023 21:32:40 +0530 Subject: [PATCH 1/6] Added the platform chip_crypto to SiWx917 platform --- src/platform/silabs/SiWx917/BUILD.gn | 16 + src/platform/silabs/SiWx917/CHIPCryptoPAL.h | 1703 ++++++++++++++++ .../silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp | 1772 +++++++++++++++++ src/platform/silabs/SiWx917/wifi_args.gni | 2 +- 4 files changed, 3492 insertions(+), 1 deletion(-) create mode 100644 src/platform/silabs/SiWx917/CHIPCryptoPAL.h create mode 100644 src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp diff --git a/src/platform/silabs/SiWx917/BUILD.gn b/src/platform/silabs/SiWx917/BUILD.gn index 7f361f313cd771..8f5bc8ff90b1fe 100644 --- a/src/platform/silabs/SiWx917/BUILD.gn +++ b/src/platform/silabs/SiWx917/BUILD.gn @@ -27,6 +27,9 @@ if (chip_enable_openthread) { import("//build_overrides/openthread.gni") } +if (chip_crypto == "platform") { + import("//build_overrides/mbedtls.gni") +} static_library("SiWx917") { sources = [ "${silabs_platform_dir}/BLEManagerImpl.h", @@ -67,6 +70,19 @@ static_library("SiWx917") { public_deps = [ "${chip_root}/src/platform:platform_base" ] + # Add platform crypto implementation + if (chip_crypto == "platform") { + sources += [ + "CHIPCryptoPALTinyCrypt.cpp", + "CHIPCryptoPAL.h", + + ] + public_deps += [ + "${chip_root}/src/crypto", + "${mbedtls_root}:mbedtls", + ] + } + if (chip_enable_wifi) { sources += [ "${silabs_platform_dir}/ConnectivityManagerImpl_WIFI.cpp", diff --git a/src/platform/silabs/SiWx917/CHIPCryptoPAL.h b/src/platform/silabs/SiWx917/CHIPCryptoPAL.h new file mode 100644 index 00000000000000..7a5cb1f191d706 --- /dev/null +++ b/src/platform/silabs/SiWx917/CHIPCryptoPAL.h @@ -0,0 +1,1703 @@ +/* + * + * Copyright (c) 2020-2022 Project CHIP Authors + * + * 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 + * Header that exposes the platform agnostic CHIP crypto primitives + */ + +#pragma once + +#if CHIP_HAVE_CONFIG_H +#include +#endif // CHIP_HAVE_CONFIG_H + +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace chip { +namespace Crypto { + +constexpr size_t kMax_x509_Certificate_Length = 600; + +constexpr size_t kP256_FE_Length = 32; +constexpr size_t kP256_ECDSA_Signature_Length_Raw = (2 * kP256_FE_Length); +constexpr size_t kP256_Point_Length = (2 * kP256_FE_Length + 1); +constexpr size_t kSHA256_Hash_Length = 32; +constexpr size_t kSHA1_Hash_Length = 20; +constexpr size_t kSubjectKeyIdentifierLength = kSHA1_Hash_Length; +constexpr size_t kAuthorityKeyIdentifierLength = kSHA1_Hash_Length; + +constexpr size_t CHIP_CRYPTO_GROUP_SIZE_BYTES = kP256_FE_Length; +constexpr size_t CHIP_CRYPTO_PUBLIC_KEY_SIZE_BYTES = kP256_Point_Length; + +constexpr size_t CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES = 16; +constexpr size_t CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES = 16; + +constexpr size_t kMax_ECDH_Secret_Length = kP256_FE_Length; +constexpr size_t kMax_ECDSA_Signature_Length = kP256_ECDSA_Signature_Length_Raw; +constexpr size_t kMAX_FE_Length = kP256_FE_Length; +constexpr size_t kMAX_Point_Length = kP256_Point_Length; +constexpr size_t kMAX_Hash_Length = kSHA256_Hash_Length; + +// Max CSR length should be relatively small since it's a single P256 key and +// no metadata is expected to be honored by the CA. +constexpr size_t kMAX_CSR_Length = 255; + +constexpr size_t CHIP_CRYPTO_HASH_LEN_BYTES = kSHA256_Hash_Length; + +constexpr size_t kSpake2p_Min_PBKDF_Salt_Length = 16; +constexpr size_t kSpake2p_Max_PBKDF_Salt_Length = 32; +constexpr uint32_t kSpake2p_Min_PBKDF_Iterations = 1000; +constexpr uint32_t kSpake2p_Max_PBKDF_Iterations = 100000; + +constexpr size_t kP256_PrivateKey_Length = CHIP_CRYPTO_GROUP_SIZE_BYTES; +constexpr size_t kP256_PublicKey_Length = CHIP_CRYPTO_PUBLIC_KEY_SIZE_BYTES; + +constexpr size_t kAES_CCM128_Key_Length = 128u / 8u; +constexpr size_t kAES_CCM128_Block_Length = kAES_CCM128_Key_Length; +constexpr size_t kAES_CCM128_Nonce_Length = 13; +constexpr size_t kAES_CCM128_Tag_Length = 16; + +/* These sizes are hardcoded here to remove header dependency on underlying crypto library + * in a public interface file. The validity of these sizes is verified by static_assert in + * the implementation files. + */ +constexpr size_t kMAX_Spake2p_Context_Size = 1024; +constexpr size_t kMAX_P256Keypair_Context_Size = 512; + +constexpr size_t kEmitDerIntegerWithoutTagOverhead = 1; // 1 sign stuffer +constexpr size_t kEmitDerIntegerOverhead = 3; // Tag + Length byte + 1 sign stuffer + +constexpr size_t kMAX_Hash_SHA256_Context_Size = CHIP_CONFIG_SHA256_CONTEXT_SIZE; + +constexpr size_t kSpake2p_WS_Length = kP256_FE_Length + 8; +constexpr size_t kSpake2p_VerifierSerialized_Length = kP256_FE_Length + kP256_Point_Length; + +constexpr char kVIDPrefixForCNEncoding[] = "Mvid:"; +constexpr char kPIDPrefixForCNEncoding[] = "Mpid:"; +constexpr size_t kVIDandPIDHexLength = sizeof(uint16_t) * 2; +constexpr size_t kMax_CommonNameAttr_Length = 64; + +/* + * Overhead to encode a raw ECDSA signature in X9.62 format in ASN.1 DER + * + * Ecdsa-Sig-Value ::= SEQUENCE { + * r INTEGER, + * s INTEGER + * } + * + * --> SEQUENCE, universal constructed tag (0x30), length over 2 bytes, up to 255 (to support future larger sizes up to 512 bits) + * -> SEQ_OVERHEAD = 3 bytes + * --> INTEGER, universal primitive tag (0x02), length over 1 byte, one extra byte worst case + * over max for 0x00 when MSB is set. + * -> INT_OVERHEAD = 3 bytes + * + * There is 1 sequence of 2 integers. Overhead is SEQ_OVERHEAD + (2 * INT_OVERHEAD) = 3 + (2 * 3) = 9. + */ +constexpr size_t kMax_ECDSA_X9Dot62_Asn1_Overhead = 9; +constexpr size_t kMax_ECDSA_Signature_Length_Der = kMax_ECDSA_Signature_Length + kMax_ECDSA_X9Dot62_Asn1_Overhead; + +static_assert(kMax_ECDH_Secret_Length >= kP256_FE_Length, "ECDH shared secret is too short for crypto suite"); +static_assert(kMax_ECDSA_Signature_Length >= kP256_ECDSA_Signature_Length_Raw, + "ECDSA signature buffer length is too short for crypto suite"); + +constexpr size_t kCompressedFabricIdentifierSize = 8; + +/** + * Spake2+ parameters for P256 + * Defined in https://www.ietf.org/id/draft-bar-cfrg-spake2plus-01.html#name-ciphersuites + */ +const uint8_t spake2p_M_p256[65] = { + 0x04, 0x88, 0x6e, 0x2f, 0x97, 0xac, 0xe4, 0x6e, 0x55, 0xba, 0x9d, 0xd7, 0x24, 0x25, 0x79, 0xf2, 0x99, + 0x3b, 0x64, 0xe1, 0x6e, 0xf3, 0xdc, 0xab, 0x95, 0xaf, 0xd4, 0x97, 0x33, 0x3d, 0x8f, 0xa1, 0x2f, 0x5f, + 0xf3, 0x55, 0x16, 0x3e, 0x43, 0xce, 0x22, 0x4e, 0x0b, 0x0e, 0x65, 0xff, 0x02, 0xac, 0x8e, 0x5c, 0x7b, + 0xe0, 0x94, 0x19, 0xc7, 0x85, 0xe0, 0xca, 0x54, 0x7d, 0x55, 0xa1, 0x2e, 0x2d, 0x20, +}; +const uint8_t spake2p_N_p256[65] = { + 0x04, 0xd8, 0xbb, 0xd6, 0xc6, 0x39, 0xc6, 0x29, 0x37, 0xb0, 0x4d, 0x99, 0x7f, 0x38, 0xc3, 0x77, 0x07, + 0x19, 0xc6, 0x29, 0xd7, 0x01, 0x4d, 0x49, 0xa2, 0x4b, 0x4f, 0x98, 0xba, 0xa1, 0x29, 0x2b, 0x49, 0x07, + 0xd6, 0x0a, 0xa6, 0xbf, 0xad, 0xe4, 0x50, 0x08, 0xa6, 0x36, 0x33, 0x7f, 0x51, 0x68, 0xc6, 0x4d, 0x9b, + 0xd3, 0x60, 0x34, 0x80, 0x8c, 0xd5, 0x64, 0x49, 0x0b, 0x1e, 0x65, 0x6e, 0xdb, 0xe7, +}; + +/** + * Spake2+ state machine to ensure proper execution of the protocol. + */ +enum class CHIP_SPAKE2P_STATE : uint8_t +{ + PREINIT = 0, // Before any initialization + INIT, // First initialization + STARTED, // Prover & Verifier starts + R1, // Round one complete + R2, // Round two complete + KC, // Key confirmation complete +}; + +/** + * Spake2+ role. + */ +enum class CHIP_SPAKE2P_ROLE : uint8_t +{ + VERIFIER = 0, // Accessory + PROVER = 1, // Commissioner +}; + +enum class SupportedECPKeyTypes : uint8_t +{ + ECP256R1 = 0, +}; + +enum class ECPKeyTarget : uint8_t +{ + ECDH = 0, + ECDSA = 1, +}; + +/** @brief Safely clears the first `len` bytes of memory area `buf`. + * @param buf Pointer to a memory buffer holding secret data that must be cleared. + * @param len Specifies secret data size in bytes. + **/ +void ClearSecretData(uint8_t * buf, size_t len); + +/** + * Helper for clearing a C array which auto-deduces the size. + */ +template +void ClearSecretData(uint8_t (&buf)[N]) +{ + ClearSecretData(buf, N); +} + +/** + * @brief Constant-time buffer comparison + * + * This function implements constant time memcmp. It's good practice + * to use constant time functions for cryptographic functions. + * + * @param a Pointer to first buffer + * @param b Pointer to Second buffer + * @param n Number of bytes to compare + * @return true if `n` first bytes of both buffers are equal, false otherwise + */ +bool IsBufferContentEqualConstantTime(const void * a, const void * b, size_t n); + +template +class ECPKey +{ +public: + virtual ~ECPKey() {} + virtual SupportedECPKeyTypes Type() const = 0; + virtual size_t Length() const = 0; + virtual bool IsUncompressed() const = 0; + virtual operator const uint8_t *() const = 0; + virtual operator uint8_t *() = 0; + virtual const uint8_t * ConstBytes() const = 0; + virtual uint8_t * Bytes() = 0; + + virtual bool Matches(const ECPKey & other) const + { + return (this->Length() == other.Length()) && + IsBufferContentEqualConstantTime(this->ConstBytes(), other.ConstBytes(), this->Length()); + } + + virtual CHIP_ERROR ECDSA_validate_msg_signature(const uint8_t * msg, const size_t msg_length, const Sig & signature) const = 0; + virtual CHIP_ERROR ECDSA_validate_hash_signature(const uint8_t * hash, const size_t hash_length, + const Sig & signature) const = 0; +}; + +/** + * @brief Helper class for holding sensitive data that should be erased from memory after use. + * + * The sensitive data buffer is a variable-length, fixed-capacity buffer class that securely erases + * the contents of a buffer when the buffer is destroyed. + */ +template +class SensitiveDataBuffer +{ +public: + ~SensitiveDataBuffer() + { + // Sanitize after use + ClearSecretData(mBytes); + } + + SensitiveDataBuffer & operator=(const SensitiveDataBuffer & other) + { + // Guard self assignment + if (this == &other) + return *this; + + ClearSecretData(mBytes); + SetLength(other.Length()); + ::memcpy(Bytes(), other.ConstBytes(), other.Length()); + return *this; + } + + /** + * @brief Set current length of the buffer + * @return Error if new length is exceeds capacity of the buffer + */ + CHIP_ERROR SetLength(size_t length) + { + VerifyOrReturnError(length <= kCapacity, CHIP_ERROR_INVALID_ARGUMENT); + mLength = length; + return CHIP_NO_ERROR; + } + + /** + * @brief Returns current length of the buffer + */ + size_t Length() const { return mLength; } + + /** + * @brief Returns non-const pointer to start of the underlying buffer + */ + uint8_t * Bytes() { return &mBytes[0]; } + + /** + * @brief Returns const pointer to start of the underlying buffer + */ + const uint8_t * ConstBytes() const { return &mBytes[0]; } + + /** + * @brief Constructs span from the underlying buffer + */ + ByteSpan Span() const { return ByteSpan(ConstBytes(), Length()); } + + /** + * @brief Returns capacity of the buffer + */ + static constexpr size_t Capacity() { return kCapacity; } + +private: + uint8_t mBytes[kCapacity]; + size_t mLength = 0; +}; + +/** + * @brief Helper class for holding fixed-sized sensitive data that should be erased from memory after use. + * + * The sensitive data buffer is a fixed-length, fixed-capacity buffer class that securely erases + * the contents of a buffer when the buffer is destroyed. + */ +template +class SensitiveDataFixedBuffer +{ +public: + SensitiveDataFixedBuffer() = default; + + constexpr explicit SensitiveDataFixedBuffer(const uint8_t (&rawValue)[kCapacity]) + { + memcpy(&mBytes[0], &rawValue[0], kCapacity); + } + + constexpr explicit SensitiveDataFixedBuffer(const FixedByteSpan & value) + { + memcpy(&mBytes[0], value.data(), kCapacity); + } + + ~SensitiveDataFixedBuffer() + { + // Sanitize after use + ClearSecretData(mBytes); + } + + /** + * @brief Returns fixed length of the buffer + */ + constexpr size_t Length() const { return kCapacity; } + + /** + * @brief Returns non-const pointer to start of the underlying buffer + */ + uint8_t * Bytes() { return &mBytes[0]; } + + /** + * @brief Returns const pointer to start of the underlying buffer + */ + const uint8_t * ConstBytes() const { return &mBytes[0]; } + + /** + * @brief Constructs fixed span from the underlying buffer + */ + FixedByteSpan Span() const { return FixedByteSpan(mBytes); } + +private: + uint8_t mBytes[kCapacity]; +}; + +using P256ECDSASignature = SensitiveDataBuffer; +using P256ECDHDerivedSecret = SensitiveDataBuffer; + +using IdentityProtectionKey = SensitiveDataFixedBuffer; +using IdentityProtectionKeySpan = FixedByteSpan; + +class P256PublicKey : public ECPKey +{ +public: + P256PublicKey() {} + + template + constexpr P256PublicKey(const uint8_t (&raw_value)[N]) + { + static_assert(N == kP256_PublicKey_Length, "Can only array-initialize from proper bounds"); + memcpy(&bytes[0], &raw_value[0], N); + } + + template + constexpr P256PublicKey(const FixedByteSpan & value) + { + static_assert(N == kP256_PublicKey_Length, "Can only initialize from proper sized byte span"); + memcpy(&bytes[0], value.data(), N); + } + + template + P256PublicKey & operator=(const FixedByteSpan & value) + { + static_assert(N == kP256_PublicKey_Length, "Can only initialize from proper sized byte span"); + memcpy(&bytes[0], value.data(), N); + return *this; + } + + SupportedECPKeyTypes Type() const override { return SupportedECPKeyTypes::ECP256R1; } + size_t Length() const override { return kP256_PublicKey_Length; } + operator uint8_t *() override { return bytes; } + operator const uint8_t *() const override { return bytes; } + const uint8_t * ConstBytes() const override { return &bytes[0]; } + uint8_t * Bytes() override { return &bytes[0]; } + bool IsUncompressed() const override + { + constexpr uint8_t kUncompressedPointMarker = 0x04; + // SEC1 definition of an uncompressed point is (0x04 || X || Y) where X and Y are + // raw zero-padded big-endian large integers of the group size. + return (Length() == ((kP256_FE_Length * 2) + 1)) && (ConstBytes()[0] == kUncompressedPointMarker); + } + + CHIP_ERROR ECDSA_validate_msg_signature(const uint8_t * msg, size_t msg_length, + const P256ECDSASignature & signature) const override; + CHIP_ERROR ECDSA_validate_hash_signature(const uint8_t * hash, size_t hash_length, + const P256ECDSASignature & signature) const override; + +private: + uint8_t bytes[kP256_PublicKey_Length]; +}; + +template +class ECPKeypair +{ +public: + virtual ~ECPKeypair() {} + + /** @brief Generate a new Certificate Signing Request (CSR). + * @param csr Newly generated CSR in DER format + * @param csr_length The caller provides the length of input buffer (csr). The function returns the actual length of generated + *CSR. + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR NewCertificateSigningRequest(uint8_t * csr, size_t & csr_length) const = 0; + + /** + * @brief A function to sign a msg using ECDSA + * @param msg Message that needs to be signed + * @param msg_length Length of message + * @param out_signature Buffer that will hold the output signature. The signature consists of: 2 EC elements (r and s), + * in raw point form (see SEC1). + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR ECDSA_sign_msg(const uint8_t * msg, size_t msg_length, Sig & out_signature) const = 0; + + /** @brief A function to derive a shared secret using ECDH + * @param remote_public_key Public key of remote peer with which we are trying to establish secure channel. remote_public_key is + * ASN.1 DER encoded as padded big-endian field elements as described in SEC 1: Elliptic Curve Cryptography + * [https://www.secg.org/sec1-v2.pdf] + * @param out_secret Buffer to write out secret into. This is a byte array representing the x coordinate of the shared secret. + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR ECDH_derive_secret(const PK & remote_public_key, Secret & out_secret) const = 0; + + virtual const PK & Pubkey() const = 0; +}; + +struct alignas(size_t) P256KeypairContext +{ + uint8_t mBytes[kMAX_P256Keypair_Context_Size]; +}; + +using P256SerializedKeypair = SensitiveDataBuffer; + +class P256KeypairBase : public ECPKeypair +{ +public: + /** + * @brief Initialize the keypair. + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR Initialize(ECPKeyTarget key_target) = 0; + + /** + * @brief Serialize the keypair. + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR Serialize(P256SerializedKeypair & output) const = 0; + + /** + * @brief Deserialize the keypair. + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR Deserialize(P256SerializedKeypair & input) = 0; +}; + +class P256Keypair : public P256KeypairBase +{ +public: + P256Keypair() {} + ~P256Keypair() override; + + /** + * @brief Initialize the keypair. + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + CHIP_ERROR Initialize(ECPKeyTarget key_target) override; + + /** + * @brief Serialize the keypair. + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + CHIP_ERROR Serialize(P256SerializedKeypair & output) const override; + + /** + * @brief Deserialize the keypair. + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + CHIP_ERROR Deserialize(P256SerializedKeypair & input) override; + + /** + * @brief Generate a new Certificate Signing Request (CSR). + * @param csr Newly generated CSR in DER format + * @param csr_length The caller provides the length of input buffer (csr). The function returns the actual length of generated + *CSR. + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + CHIP_ERROR NewCertificateSigningRequest(uint8_t * csr, size_t & csr_length) const override; + + /** + * @brief A function to sign a msg using ECDSA + * @param msg Message that needs to be signed + * @param msg_length Length of message + * @param out_signature Buffer that will hold the output signature. The signature consists of: 2 EC elements (r and s), + * in raw point form (see SEC1). + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + CHIP_ERROR ECDSA_sign_msg(const uint8_t * msg, size_t msg_length, P256ECDSASignature & out_signature) const override; + + /** + * @brief A function to derive a shared secret using ECDH + * + * This implements the CHIP_Crypto_ECDH(PrivateKey myPrivateKey, PublicKey theirPublicKey) cryptographic primitive + * from the specification, using this class's private key from `mKeypair` as `myPrivateKey` and the remote + * public key from `remote_public_key` as `theirPublicKey`. + * + * @param remote_public_key Public key of remote peer with which we are trying to establish secure channel. remote_public_key is + * ASN.1 DER encoded as padded big-endian field elements as described in SEC 1: Elliptic Curve Cryptography + * [https://www.secg.org/sec1-v2.pdf] + * @param out_secret Buffer to write out secret into. This is a byte array representing the x coordinate of the shared secret. + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + CHIP_ERROR ECDH_derive_secret(const P256PublicKey & remote_public_key, P256ECDHDerivedSecret & out_secret) const override; + + /** @brief Return public key for the keypair. + **/ + const P256PublicKey & Pubkey() const override { return mPublicKey; } + + /** Release resources associated with this key pair */ + void Clear(); + +protected: + P256PublicKey mPublicKey; + mutable P256KeypairContext mKeypair; + bool mInitialized = false; +}; + +/** + * @brief Convert a raw ECDSA signature to ASN.1 signature (per X9.62) as used by TLS libraries. + * + * Errors are: + * - CHIP_ERROR_INVALID_ARGUMENT on any argument being invalid (e.g. nullptr), wrong sizes, + * wrong or unsupported format, + * - CHIP_ERROR_BUFFER_TOO_SMALL on running out of space at runtime. + * - CHIP_ERROR_INTERNAL on any unexpected processing error. + * + * @param[in] fe_length_bytes Field Element length in bytes (e.g. 32 for P256 curve) + * @param[in] raw_sig Raw signature of concatenated + * @param[out] out_asn1_sig ASN.1 DER signature format output buffer. Size must have space for at least + * kMax_ECDSA_X9Dot62_Asn1_Overhead. On CHIP_NO_ERROR, the out_asn1_sig buffer will be re-assigned + * to have the correct size based on variable-length output. + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + */ +CHIP_ERROR EcdsaRawSignatureToAsn1(size_t fe_length_bytes, const ByteSpan & raw_sig, MutableByteSpan & out_asn1_sig); + +/** + * @brief Convert an ASN.1 DER signature (per X9.62) as used by TLS libraries to SEC1 raw format + * + * Errors are: + * - CHIP_ERROR_INVALID_ARGUMENT on any argument being invalid (e.g. nullptr), wrong sizes, + * wrong or unsupported format, + * - CHIP_ERROR_BUFFER_TOO_SMALL on running out of space at runtime. + * - CHIP_ERROR_INTERNAL on any unexpected processing error. + * + * @param[in] fe_length_bytes Field Element length in bytes (e.g. 32 for P256 curve) + * @param[in] asn1_sig ASN.1 DER signature input + * @param[out] out_raw_sig Raw signature of concatenated format output buffer. Size must be at + * least >= `2 * fe_length_bytes`. On CHIP_NO_ERROR, the out_raw_sig buffer will be re-assigned + * to have the correct size (2 * fe_length_bytes). + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + */ +CHIP_ERROR EcdsaAsn1SignatureToRaw(size_t fe_length_bytes, const ByteSpan & asn1_sig, MutableByteSpan & out_raw_sig); + +/** + * @brief Utility to emit a DER-encoded INTEGER given a raw unsigned large integer + * in big-endian order. The `out_der_integer` span is updated to reflect the final + * variable length, including tag and length, and must have at least `kEmitDerIntegerOverhead` + * extra space in addition to the `raw_integer.size()`. + * @param[in] raw_integer Bytes of a large unsigned integer in big-endian, possibly including leading zeroes + * @param[out] out_der_integer Buffer to receive the DER-encoded integer + * @return Returns CHIP_ERROR_INVALID_ARGUMENT or CHIP_ERROR_BUFFER_TOO_SMALL on error, CHIP_NO_ERROR otherwise. + */ +CHIP_ERROR ConvertIntegerRawToDer(const ByteSpan & raw_integer, MutableByteSpan & out_der_integer); + +/** + * @brief Utility to emit a DER-encoded INTEGER given a raw unsigned large integer + * in big-endian order. The `out_der_integer` span is updated to reflect the final + * variable length, excluding tag and length, and must have at least `kEmitDerIntegerWithoutTagOverhead` + * extra space in addition to the `raw_integer.size()`. + * @param[in] raw_integer Bytes of a large unsigned integer in big-endian, possibly including leading zeroes + * @param[out] out_der_integer Buffer to receive the DER-encoded integer + * @return Returns CHIP_ERROR_INVALID_ARGUMENT or CHIP_ERROR_BUFFER_TOO_SMALL on error, CHIP_NO_ERROR otherwise. + */ +CHIP_ERROR ConvertIntegerRawToDerWithoutTag(const ByteSpan & raw_integer, MutableByteSpan & out_der_integer); + +/** + * @brief A function that implements AES-CCM encryption + * + * This implements the CHIP_Crypto_AEAD_GenerateEncrypt() cryptographic primitive + * from the specification. For an empty plaintext, the user of the API can provide + * an empty string, or a nullptr, and provide plaintext_length as 0. The output buffer, + * ciphertext can also be an empty string, or a nullptr for this case. + * + * @param plaintext Plaintext to encrypt + * @param plaintext_length Length of plain_text + * @param aad Additional authentication data + * @param aad_length Length of additional authentication data + * @param key Encryption key + * @param key_length Length of encryption key (in bytes) + * @param nonce Encryption nonce + * @param nonce_length Length of encryption nonce + * @param ciphertext Buffer to write ciphertext into. Caller must ensure this is large enough to hold the ciphertext + * @param tag Buffer to write tag into. Caller must ensure this is large enough to hold the tag + * @param tag_length Expected length of tag + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + * */ +CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, const uint8_t * aad, size_t aad_length, + const uint8_t * key, size_t key_length, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, + uint8_t * tag, size_t tag_length); + +/** + * @brief A function that implements AES-CCM decryption + * + * This implements the CHIP_Crypto_AEAD_DecryptVerify() cryptographic primitive + * from the specification. For an empty ciphertext, the user of the API can provide + * an empty string, or a nullptr, and provide ciphertext_length as 0. The output buffer, + * plaintext can also be an empty string, or a nullptr for this case. + * + * @param ciphertext Ciphertext to decrypt + * @param ciphertext_length Length of ciphertext + * @param aad Additional authentical data. + * @param aad_length Length of additional authentication data + * @param tag Tag to use to decrypt + * @param tag_length Length of tag + * @param key Decryption key + * @param key_length Length of Decryption key (in bytes) + * @param nonce Encryption nonce + * @param nonce_length Length of encryption nonce + * @param plaintext Buffer to write plaintext into + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ +CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_length, const uint8_t * aad, size_t aad_length, + const uint8_t * tag, size_t tag_length, const uint8_t * key, size_t key_length, const uint8_t * nonce, + size_t nonce_length, uint8_t * plaintext); + +/** + * @brief A function that implements AES-CTR encryption/decryption + * + * This implements the AES-CTR-Encrypt/Decrypt() cryptographic primitives per sections + * 3.7.1 and 3.7.2 of the specification. For an empty input, the user of the API + * can provide an empty string, or a nullptr, and provide input as 0. + * The output buffer can also be an empty string, or a nullptr for this case. + * + * @param input Input text to encrypt/decrypt + * @param input_length Length of ciphertext + * @param key Decryption key + * @param key_length Length of Decryption key (in bytes) + * @param nonce Encryption nonce + * @param nonce_length Length of encryption nonce + * @param output Buffer to write output into + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ +CHIP_ERROR AES_CTR_crypt(const uint8_t * input, size_t input_length, const uint8_t * key, size_t key_length, const uint8_t * nonce, + size_t nonce_length, uint8_t * output); + +/** + * @brief Generate a PKCS#10 CSR, usable for Matter, from a P256Keypair. + * + * This uses first principles ASN.1 encoding to avoid relying on the CHIPCryptoPAL backend + * itself, other than to provide an implementation of a P256Keypair * that supports + * at least `::Pubkey()` and `::ECDSA_sign_msg`. This allows using it with + * OS/Platform-bridged private key handling, without requiring a specific + * implementation of other bits like ASN.1. + * + * The CSR will have subject OU set to `CSA`. This is needed since omiting + * subject altogether often trips CSR parsing code. The profile at the CA can + * be configured to ignore CSR requested subject. + * + * @param keypair The key pair for which a CSR should be generated. Must not be null. + * @param csr_span Span to hold the resulting CSR. Must be at least kMAX_CSR_Length. Otherwise returns CHIP_ERROR_BUFFER_TOO_SMALL. + * It will get resized to actual size needed on success. + + * @return Returns a CHIP_ERROR from P256Keypair or ASN.1 backend on error, CHIP_NO_ERROR otherwise + **/ +CHIP_ERROR GenerateCertificateSigningRequest(const P256Keypair * keypair, MutableByteSpan & csr_span); + +/** + * @brief Common code to validate ASN.1 format/size of a CSR, used by VerifyCertificateSigningRequest. + * + * Ensures it's not obviously malformed and doesn't have trailing garbage. + * + * @param csr CSR in DER format + * @param csr_length The length of the CSR buffer + * @return CHIP_ERROR_UNSUPPORTED_CERT_FORMAT on invalid format, CHIP_NO_ERROR otherwise. + */ +CHIP_ERROR VerifyCertificateSigningRequestFormat(const uint8_t * csr, size_t csr_length); + +/** + * @brief Verify the Certificate Signing Request (CSR). If successfully verified, it outputs the public key from the CSR. + * + * The CSR is valid if the format is correct, the signature validates with the embedded public + * key, and there is no trailing garbage data. + * + * @param csr CSR in DER format + * @param csr_length The length of the CSR + * @param pubkey The public key from the verified CSR + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ +CHIP_ERROR VerifyCertificateSigningRequest(const uint8_t * csr, size_t csr_length, P256PublicKey & pubkey); + +/** + * @brief A function that implements SHA-256 hash + * + * This implements the CHIP_Crypto_Hash() cryptographic primitive + * in the the specification. + * + * @param data The data to hash + * @param data_length Length of the data + * @param out_buffer Pointer to buffer to write output into + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + +CHIP_ERROR Hash_SHA256(const uint8_t * data, size_t data_length, uint8_t * out_buffer); + +/** + * @brief A function that implements SHA-1 hash + * @param data The data to hash + * @param data_length Length of the data + * @param out_buffer Pointer to buffer to write output into + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + +CHIP_ERROR Hash_SHA1(const uint8_t * data, size_t data_length, uint8_t * out_buffer); + +/** + * @brief A class that defines stream based implementation of SHA-256 hash + * It's expected that the object of this class can be safely copied. + * All implementations must check for std::is_trivially_copyable. + **/ + +struct alignas(size_t) HashSHA256OpaqueContext +{ + uint8_t mOpaque[kMAX_Hash_SHA256_Context_Size]; +}; + +class Hash_SHA256_stream +{ +public: + Hash_SHA256_stream(); + ~Hash_SHA256_stream(); + + /** + * @brief Re-initialize digest computation to an empty context. + * + * @return CHIP_ERROR_INTERNAL on failure to initialize the context, + * CHIP_NO_ERROR otherwise. + */ + CHIP_ERROR Begin(); + + /** + * @brief Add some data to the digest computation, updating internal state. + * + * @param[in] data The span of bytes to include in the digest update process. + * + * @return CHIP_ERROR_INTERNAL on failure to ingest the data, CHIP_NO_ERROR otherwise. + */ + CHIP_ERROR AddData(const ByteSpan data); + + /** + * @brief Get the intermediate padded digest for the current state of the stream. + * + * More data can be added before finish is called. + * + * @param[in,out] out_buffer Output buffer to receive the digest. `out_buffer` must + * be at least `kSHA256_Hash_Length` bytes long. The `out_buffer` size + * will be set to `kSHA256_Hash_Length` on success. + * + * @return CHIP_ERROR_INTERNAL on failure to compute the digest, CHIP_ERROR_BUFFER_TOO_SMALL + * if out_buffer is too small, CHIP_NO_ERROR otherwise. + */ + CHIP_ERROR GetDigest(MutableByteSpan & out_buffer); + + /** + * @brief Finalize the stream digest computation, getting the final digest. + * + * @param[in,out] out_buffer Output buffer to receive the digest. `out_buffer` must + * be at least `kSHA256_Hash_Length` bytes long. The `out_buffer` size + * will be set to `kSHA256_Hash_Length` on success. + * + * @return CHIP_ERROR_INTERNAL on failure to compute the digest, CHIP_ERROR_BUFFER_TOO_SMALL + * if out_buffer is too small, CHIP_NO_ERROR otherwise. + */ + CHIP_ERROR Finish(MutableByteSpan & out_buffer); + + /** + * @brief Clear-out internal digest data to avoid lingering the state. + */ + void Clear(); + +private: + HashSHA256OpaqueContext mContext; +}; + +class HKDF_sha +{ +public: + HKDF_sha() {} + virtual ~HKDF_sha() {} + + /** + * @brief A function that implements SHA-256 based HKDF + * + * This implements the CHIP_Crypto_KDF() cryptographic primitive + * in the the specification. + * + * Error values are: + * - CHIP_ERROR_INVALID_ARGUMENT: for any bad arguments or nullptr input on + * any pointer. + * - CHIP_ERROR_INTERNAL: for any unexpected error arising in the underlying + * cryptographic layers. + * + * @param secret The secret to use as the key to the HKDF + * @param secret_length Length of the secret + * @param salt Optional salt to use as input to the HKDF + * @param salt_length Length of the salt + * @param info Optional info to use as input to the HKDF + * @param info_length Length of the info + * @param out_buffer Pointer to buffer to write output into. + * @param out_length Size of the underlying out_buffer. That length of output key material will be generated in out_buffer. + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + + virtual CHIP_ERROR HKDF_SHA256(const uint8_t * secret, size_t secret_length, const uint8_t * salt, size_t salt_length, + const uint8_t * info, size_t info_length, uint8_t * out_buffer, size_t out_length); +}; + +class HMAC_sha +{ +public: + HMAC_sha() {} + virtual ~HMAC_sha() {} + + /** + * @brief A function that implements SHA-256 based HMAC per FIPS1981. + * + * This implements the CHIP_Crypto_HMAC() cryptographic primitive + * in the the specification. + * + * The `out_length` must be at least kSHA256_Hash_Length, and only + * kSHA256_Hash_Length bytes are written to out_buffer. + * + * Error values are: + * - CHIP_ERROR_INVALID_ARGUMENT: for any bad arguments or nullptr input on + * any pointer. + * - CHIP_ERROR_INTERNAL: for any unexpected error arising in the underlying + * cryptographic layers. + * + * @param key The key to use for the HMAC operation + * @param key_length Length of the key + * @param message Message over which to compute the HMAC + * @param message_length Length of the message over which to compute the HMAC + * @param out_buffer Pointer to buffer into which to write the output. + * @param out_length Underlying size of the `out_buffer`. + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + + virtual CHIP_ERROR HMAC_SHA256(const uint8_t * key, size_t key_length, const uint8_t * message, size_t message_length, + uint8_t * out_buffer, size_t out_length); +}; + +/** + * @brief A cryptographically secure random number generator based on NIST SP800-90A + * @param out_buffer Buffer into which to write random bytes + * @param out_length Number of random bytes to generate + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ +CHIP_ERROR DRBG_get_bytes(uint8_t * out_buffer, size_t out_length); + +/** @brief Entropy callback function + * @param data Callback-specific data pointer + * @param output Output data to fill + * @param len Length of output buffer + * @param olen The actual amount of data that was written to output buffer + * @return 0 if success + */ +typedef int (*entropy_source)(void * data, uint8_t * output, size_t len, size_t * olen); + +/** @brief A function to add entropy sources to crypto library + * @param fn_source Function pointer to the entropy source + * @param p_source Data that should be provided when fn_source is called + * @param threshold Minimum required from source before entropy is released + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ +CHIP_ERROR add_entropy_source(entropy_source fn_source, void * p_source, size_t threshold); + +class PBKDF2_sha256 +{ +public: + PBKDF2_sha256() {} + virtual ~PBKDF2_sha256() {} + + /** @brief Function to derive key using password. SHA256 hashing algorithm is used for calculating hmac. + * @param password password used for key derivation + * @param plen length of buffer containing password + * @param salt salt to use as input to the KDF + * @param slen length of salt + * @param iteration_count number of iterations to run + * @param key_length length of output key + * @param output output buffer where the key will be written + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR pbkdf2_sha256(const uint8_t * password, size_t plen, const uint8_t * salt, size_t slen, + unsigned int iteration_count, uint32_t key_length, uint8_t * output); +}; + +/** + * The below class implements the draft 01 version of the Spake2+ protocol as + * defined in https://www.ietf.org/id/draft-bar-cfrg-spake2plus-01.html. + * + * The following describes the protocol flows: + * + * Commissioner Accessory + * ------------ --------- + * + * Init + * BeginProver + * ComputeRoundOne -------------> + * Init + * BeginVerifier + * /- ComputeRoundOne + * <------------- ComputeRoundTwo + * ComputeRoundTwo -------------> + * KeyConfirm KeyConfirm + * GetKeys GetKeys + * + **/ +class Spake2p +{ +public: + Spake2p(size_t fe_size, size_t point_size, size_t hash_size); + virtual ~Spake2p() {} + + /** + * @brief Initialize Spake2+ with some context specific information. + * + * @param context The context is arbitrary but should include information about the + * protocol being run, contain the transcript for negotiation, include + * the PKBDF parameters, etc. + * @param context_len The length of the context. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR Init(const uint8_t * context, size_t context_len); + + /** + * @brief Free Spake2+ underlying objects. + **/ + virtual void Clear() = 0; + + /** + * @brief Start the Spake2+ process as a verifier (i.e. an accessory being provisioned). + * + * @param my_identity The verifier identity. May be NULL if identities are not established. + * @param my_identity_len The verifier identity length. + * @param peer_identity The peer identity. May be NULL if identities are not established. + * @param peer_identity_len The peer identity length. + * @param w0in The input w0 (a parameter baked into the device or computed with ComputeW0). + * @param w0in_len The input w0 length. + * @param Lin The input L (a parameter baked into the device or computed with ComputeL). + * @param Lin_len The input L length. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR BeginVerifier(const uint8_t * my_identity, size_t my_identity_len, const uint8_t * peer_identity, + size_t peer_identity_len, const uint8_t * w0in, size_t w0in_len, const uint8_t * Lin, + size_t Lin_len); + + /** + * @brief Start the Spake2+ process as a prover (i.e. a commissioner). + * + * @param my_identity The prover identity. May be NULL if identities are not established. + * @param my_identity_len The prover identity length. + * @param peer_identity The peer identity. May be NULL if identities are not established. + * @param peer_identity_len The peer identity length. + * @param w0in The input w0 (an output from the PBKDF). + * @param w0in_len The input w0 length. + * @param w1in The input w1 (an output from the PBKDF). + * @param w1in_len The input w1 length. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR BeginProver(const uint8_t * my_identity, size_t my_identity_len, const uint8_t * peer_identity, + size_t peer_identity_len, const uint8_t * w0in, size_t w0in_len, const uint8_t * w1in, + size_t w1in_len); + + /** + * @brief Compute the first round of the protocol. + * + * @param pab X value from commissioner. + * @param pab_len X length. + * @param out The output first round Spake2+ contribution. + * @param out_len The output first round Spake2+ contribution length. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR ComputeRoundOne(const uint8_t * pab, size_t pab_len, uint8_t * out, size_t * out_len); + + /** + * @brief Compute the second round of the protocol. + * + * @param in The peer first round Spake2+ contribution. + * @param in_len The peer first round Spake2+ contribution length. + * @param out The output second round Spake2+ contribution. + * @param out_len The output second round Spake2+ contribution length. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR ComputeRoundTwo(const uint8_t * in, size_t in_len, uint8_t * out, size_t * out_len); + + /** + * @brief Confirm that each party computed the same keys. + * + * @param in The peer second round Spake2+ contribution. + * @param in_len The peer second round Spake2+ contribution length. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR KeyConfirm(const uint8_t * in, size_t in_len); + + /** + * @brief Return the shared secret. + * + * @param out The output secret. + * @param out_len The output secret length. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + CHIP_ERROR GetKeys(uint8_t * out, size_t * out_len); + + CHIP_ERROR InternalHash(const uint8_t * in, size_t in_len); + CHIP_ERROR WriteMN(); + CHIP_ERROR GenerateKeys(); + + /** + * @brief Load a field element. + * + * @param in The input big endian field element. + * @param in_len The size of the input buffer in bytes. + * @param fe A pointer to an initialized implementation dependant field element. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR FELoad(const uint8_t * in, size_t in_len, void * fe) = 0; + + /** + * @brief Write a field element in big-endian format. + * + * @param fe The field element to write. + * @param out The output buffer. + * @param out_len The length of the output buffer. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR FEWrite(const void * fe, uint8_t * out, size_t out_len) = 0; + + /** + * @brief Generate a field element. + * + * @param fe A pointer to an initialized implementation dependant field element. + * + * @note The implementation must generate a random element from [0, q) where q is the curve order. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR FEGenerate(void * fe) = 0; + + /** + * @brief Multiply two field elements, fer = fe1 * fe2. + * + * @param fer A pointer to an initialized implementation dependant field element. + * @param fe1 A pointer to an initialized implementation dependant field element. + * @param fe2 A pointer to an initialized implementation dependant field element. + * + * @note The result must be a field element (i.e. reduced by the curve order). + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR FEMul(void * fer, const void * fe1, const void * fe2) = 0; + + /** + * @brief Load a point from 0x04 || X || Y format + * + * @param in Input buffer + * @param in_len Input buffer length + * @param R A pointer to an initialized implementation dependant point. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR PointLoad(const uint8_t * in, size_t in_len, void * R) = 0; + + /** + * @brief Write a point in 0x04 || X || Y format + * + * @param R A pointer to an initialized implementation dependant point. + * @param out Output buffer + * @param out_len Length of the output buffer + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR PointWrite(const void * R, uint8_t * out, size_t out_len) = 0; + + /** + * @brief Scalar multiplication, R = fe1 * P1. + * + * @param R Resultant point + * @param P1 Input point + * @param fe1 Input field element. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR PointMul(void * R, const void * P1, const void * fe1) = 0; + + /** + * @brief Scalar multiplication with addition, R = fe1 * P1 + fe2 * P2. + * + * @param R Resultant point + * @param P1 Input point + * @param fe1 Input field element. + * @param P2 Input point + * @param fe2 Input field element. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR PointAddMul(void * R, const void * P1, const void * fe1, const void * P2, const void * fe2) = 0; + + /** + * @brief Point inversion. + * + * @param R Input/Output point to point_invert + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR PointInvert(void * R) = 0; + + /** + * @brief Multiply a point by the curve cofactor. + * + * @param R Input/Output point to point_invert + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR PointCofactorMul(void * R) = 0; + + /* + * @synopsis Check if a point is on the curve. + * + * @param R Input point to check. + * + * @return CHIP_NO_ERROR if the point is valid, CHIP_ERROR otherwise. + */ + virtual CHIP_ERROR PointIsValid(void * R) = 0; + + /* + * @synopsis Compute w0sin mod p + * + * @param w0out Output field element (modulo p) + * @param w0_len Output field element length + * @param w1sin Input field element + * @param w1sin_len Input field element length + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR ComputeW0(uint8_t * w0out, size_t * w0_len, const uint8_t * w0sin, size_t w0sin_len) = 0; + + /* + * @synopsis Compute w1in*G + * + * @param Lout Output point in 0x04 || X || Y format. + * @param L_len Output point length + * @param w1in Input field element + * @param w1in_len Input field element size + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR ComputeL(uint8_t * Lout, size_t * L_len, const uint8_t * w1in, size_t w1in_len) = 0; + + void * M; + void * N; + const void * G; + void * X; + void * Y; + void * L; + void * Z; + void * V; + void * w0; + void * w1; + void * xy; + void * order; + void * tempbn; + +protected: + /** + * @brief Initialize underlying implementation curve, points, field elements, etc. + * + * @details The implementation needs to: + * 1. Initialize each of the points below and set the relevant pointers on the class: + * a. M + * b. N + * c. G + * d. X + * e. Y + * f. L + * g. Z + * h. V + * + * As an example: + * this.M = implementation_alloc_point(); + * 2. Initialize each of the field elements below and set the relevant pointers on the class: + * a. w0 + * b. w1 + * c. xy + * d. tempbn + * 3. The hashing context should be initialized + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR InitImpl() = 0; + + /** + * @brief Hash in_len bytes of in into the internal hash context. + * + * @param in The input buffer. + * @param in_len Size of the input buffer in bytes. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR Hash(const uint8_t * in, size_t in_len) = 0; + + /** + * @brief Return the hash. + * + * @param out_span Output buffer. The size available must be >= the hash size. It gets resized + * to hash size on success. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR HashFinalize(MutableByteSpan & out_span) = 0; + + /** + * @brief Generate a message authentication code. + * + * @param key The MAC key buffer. + * @param key_len The size of the MAC key in bytes. + * @param in The input buffer. + * @param in_len The size of the input data to MAC in bytes. + * @param out_span The output MAC buffer span. Size must be >= the hash_size. Output size is updated to fit on success. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + virtual CHIP_ERROR Mac(const uint8_t * key, size_t key_len, const uint8_t * in, size_t in_len, MutableByteSpan & out_span) = 0; + + /** + * @brief Verify a message authentication code. + * + * @param key The MAC key buffer. + * @param key_len The size of the MAC key in bytes. + * @param mac The input MAC buffer. + * @param mac_len The size of the MAC in bytes. + * @param in The input buffer to verify. + * @param in_len The size of the input data to verify in bytes. + * + * @return Returns a CHIP_ERROR when the MAC doesn't validate, CHIP_NO_ERROR otherwise. + **/ + virtual CHIP_ERROR MacVerify(const uint8_t * key, size_t key_len, const uint8_t * mac, size_t mac_len, const uint8_t * in, + size_t in_len) = 0; + + /** + * @brief Derive an key of length out_len. + * + * @param ikm The input key material buffer. + * @param ikm_len The input key material length. + * @param salt The optional salt. This may be NULL. + * @param salt_len The size of the salt in bytes. + * @param info The info. + * @param info_len The size of the info in bytes. + * @param out The output key + * @param out_len The output key length + * + * @return Returns a CHIP_ERROR when the MAC doesn't validate, CHIP_NO_ERROR otherwise. + **/ + virtual CHIP_ERROR KDF(const uint8_t * ikm, size_t ikm_len, const uint8_t * salt, size_t salt_len, const uint8_t * info, + size_t info_len, uint8_t * out, size_t out_len) = 0; + + CHIP_SPAKE2P_ROLE role; + CHIP_SPAKE2P_STATE state = CHIP_SPAKE2P_STATE::PREINIT; + size_t fe_size; + size_t hash_size; + size_t point_size; + uint8_t Kcab[kMAX_Hash_Length]; + uint8_t Kae[kMAX_Hash_Length]; + uint8_t * Kca; + uint8_t * Kcb; + uint8_t * Ka; + uint8_t * Ke; +}; + +struct alignas(size_t) Spake2pOpaqueContext +{ + uint8_t mOpaque[kMAX_Spake2p_Context_Size]; +}; + +class Spake2p_P256_SHA256_HKDF_HMAC : public Spake2p +{ +public: + Spake2p_P256_SHA256_HKDF_HMAC() : Spake2p(kP256_FE_Length, kP256_Point_Length, kSHA256_Hash_Length) + { + memset(&mSpake2pContext, 0, sizeof(mSpake2pContext)); + } + + ~Spake2p_P256_SHA256_HKDF_HMAC() override { Spake2p_P256_SHA256_HKDF_HMAC::Clear(); } + + void Clear() override; + CHIP_ERROR Mac(const uint8_t * key, size_t key_len, const uint8_t * in, size_t in_len, MutableByteSpan & out_span) override; + CHIP_ERROR MacVerify(const uint8_t * key, size_t key_len, const uint8_t * mac, size_t mac_len, const uint8_t * in, + size_t in_len) override; + CHIP_ERROR FELoad(const uint8_t * in, size_t in_len, void * fe) override; + CHIP_ERROR FEWrite(const void * fe, uint8_t * out, size_t out_len) override; + CHIP_ERROR FEGenerate(void * fe) override; + CHIP_ERROR FEMul(void * fer, const void * fe1, const void * fe2) override; + + CHIP_ERROR PointLoad(const uint8_t * in, size_t in_len, void * R) override; + CHIP_ERROR PointWrite(const void * R, uint8_t * out, size_t out_len) override; + CHIP_ERROR PointMul(void * R, const void * P1, const void * fe1) override; + CHIP_ERROR PointAddMul(void * R, const void * P1, const void * fe1, const void * P2, const void * fe2) override; + CHIP_ERROR PointInvert(void * R) override; + CHIP_ERROR PointCofactorMul(void * R) override; + CHIP_ERROR PointIsValid(void * R) override; + + CHIP_ERROR ComputeW0(uint8_t * w0out, size_t * w0_len, const uint8_t * w0sin, size_t w0sin_len) override; + CHIP_ERROR ComputeL(uint8_t * Lout, size_t * L_len, const uint8_t * w1in, size_t w1in_len) override; + +protected: + CHIP_ERROR InitImpl() override; + CHIP_ERROR Hash(const uint8_t * in, size_t in_len) override; + CHIP_ERROR HashFinalize(MutableByteSpan & out_span) override; + CHIP_ERROR KDF(const uint8_t * secret, size_t secret_length, const uint8_t * salt, size_t salt_length, const uint8_t * info, + size_t info_length, uint8_t * out, size_t out_length) override; + +private: + CHIP_ERROR InitInternal(); + Hash_SHA256_stream sha256_hash_ctx; + + Spake2pOpaqueContext mSpake2pContext; +}; + +/** + * @brief Class used for verifying PASE secure sessions. + **/ +class Spake2pVerifier +{ +public: + uint8_t mW0[kP256_FE_Length]; + uint8_t mL[kP256_Point_Length]; + + CHIP_ERROR Serialize(MutableByteSpan & outSerialized) const; + CHIP_ERROR Deserialize(const ByteSpan & inSerialized); + + /** + * @brief Generate the Spake2+ verifier. + * + * @param pbkdf2IterCount Iteration count for PBKDF2 function + * @param salt Salt to be used for Spake2+ operation + * @param setupPin Provided setup PIN (passcode) + * + * @return CHIP_ERROR The result of Spake2+ verifier generation + */ + CHIP_ERROR Generate(uint32_t pbkdf2IterCount, const ByteSpan & salt, uint32_t setupPin); + + /** + * @brief Compute the initiator values (w0, w1) used for PAKE input. + * + * @param pbkdf2IterCount Iteration count for PBKDF2 function + * @param salt Salt to be used for Spake2+ operation + * @param setupPin Provided setup PIN (passcode) + * @param ws The output pair (w0, w1) stored sequentially + * @param ws_len The output length + * + * @return CHIP_ERROR The result from running PBKDF2 + */ + static CHIP_ERROR ComputeWS(uint32_t pbkdf2IterCount, const ByteSpan & salt, uint32_t setupPin, uint8_t * ws, uint32_t ws_len); +}; + +/** + * @brief Serialized format of the Spake2+ Verifier components. + * + * This is used when the Verifier should be presented in a serialized form. + * For example, when it is generated using PBKDF function, when stored in the + * memory or when sent over the wire. + * The serialized format is concatentation of 'W0' and 'L' verifier components: + * { Spake2pVerifier.mW0[kP256_FE_Length], Spake2pVerifier.mL[kP256_Point_Length] } + **/ +typedef uint8_t Spake2pVerifierSerialized[kSpake2p_VerifierSerialized_Length]; + +/** + * @brief Compute the compressed fabric identifier used for operational discovery service + * records from a Node's root public key and Fabric ID. On success, out_compressed_fabric_id + * will have a size of exactly kCompressedFabricIdentifierSize. + * + * Errors are: + * - CHIP_ERROR_INVALID_ARGUMENT if root_public_key is invalid + * - CHIP_ERROR_BUFFER_TOO_SMALL if out_compressed_fabric_id is too small for serialization + * - CHIP_ERROR_INTERNAL on any unexpected crypto or data conversion errors. + * + * @param[in] root_public_key The root public key associated with the node's fabric + * @param[in] fabric_id The fabric ID associated with the node's fabric + * @param[out] out_compressed_fabric_id Span where output will be written. Its size must be >= kCompressedFabricIdentifierSize. + * @returns a CHIP_ERROR (see above) on failure or CHIP_NO_ERROR otherwise. + */ +CHIP_ERROR GenerateCompressedFabricId(const Crypto::P256PublicKey & root_public_key, uint64_t fabric_id, + MutableByteSpan & out_compressed_fabric_id); + +/** + * @brief Compute the compressed fabric identifier used for operational discovery service + * records from a Node's root public key and Fabric ID. This is a conveniance + * overload that writes to a uint64_t (CompressedFabricId) type. + * + * @param[in] rootPublicKey The root public key associated with the node's fabric + * @param[in] fabricId The fabric ID associated with the node's fabric + * @param[out] compressedFabricId output location for compressed fabric ID + * @returns a CHIP_ERROR on failure or CHIP_NO_ERROR otherwise. + */ +CHIP_ERROR GenerateCompressedFabricId(const Crypto::P256PublicKey & rootPublicKey, uint64_t fabricId, + uint64_t & compressedFabricId); + +enum class CertificateChainValidationResult +{ + kSuccess = 0, + + kRootFormatInvalid = 100, + kRootArgumentInvalid = 101, + + kICAFormatInvalid = 200, + kICAArgumentInvalid = 201, + + kLeafFormatInvalid = 300, + kLeafArgumentInvalid = 301, + + kChainInvalid = 400, + + kNoMemory = 500, + + kInternalFrameworkError = 600, +}; + +CHIP_ERROR ValidateCertificateChain(const uint8_t * rootCertificate, size_t rootCertificateLen, const uint8_t * caCertificate, + size_t caCertificateLen, const uint8_t * leafCertificate, size_t leafCertificateLen, + CertificateChainValidationResult & result); + +enum class AttestationCertType +{ + kPAA = 0, + kPAI = 1, + kDAC = 2, +}; + +CHIP_ERROR VerifyAttestationCertificateFormat(const ByteSpan & cert, AttestationCertType certType); + +/** + * @brief Validate notBefore timestamp of a certificate (candidateCertificate) against validity period of the + * issuer certificate (issuerCertificate). + * + * Errors are: + * - CHIP_ERROR_CERT_EXPIRED if the candidateCertificate timestamp does not satisfy the issuerCertificate's timestamp. + * - CHIP_ERROR_INVALID_ARGUMENT when passing an invalid argument. + * - CHIP_ERROR_INTERNAL on any unexpected crypto or data conversion errors. + * + * @param candidateCertificate A DER Certificate ByteSpan those notBefore timestamp to be evaluated. + * @param issuerCertificate A DER Certificate ByteSpan used to evaluate validity timestamp of the candidateCertificate. + * + * @returns a CHIP_ERROR (see above) on failure or CHIP_NO_ERROR otherwise. + **/ +CHIP_ERROR IsCertificateValidAtIssuance(const ByteSpan & candidateCertificate, const ByteSpan & issuerCertificate); + +/** + * @brief Validate a certificate's validity date against current time. + * + * Errors are: + * - CHIP_ERROR_CERT_EXPIRED if the certificate has expired. + * - CHIP_ERROR_INVALID_ARGUMENT when passing an invalid argument. + * - CHIP_ERROR_INTERNAL on any unexpected crypto or data conversion errors. + * + * @param certificate A DER Certificate ByteSpan used as the validity reference to be checked against current time. + * + * @returns a CHIP_ERROR (see above) on failure or CHIP_NO_ERROR otherwise. + **/ +CHIP_ERROR IsCertificateValidAtCurrentTime(const ByteSpan & certificate); + +CHIP_ERROR ExtractPubkeyFromX509Cert(const ByteSpan & certificate, Crypto::P256PublicKey & pubkey); + +/** + * @brief Extracts the Subject Key Identifier from an X509 Certificate. + **/ +CHIP_ERROR ExtractSKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan & skid); + +/** + * @brief Extracts the Authority Key Identifier from an X509 Certificate. + **/ +CHIP_ERROR ExtractAKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan & akid); + +/** + * @brief Checks for resigned version of the certificate in the list and returns it. + * + * The following conditions SHOULD be satisfied for the certificate to qualify as + * a resigned version of a reference certificate: + * - SKID of the candidate and the reference certificate should match. + * - SubjectDN of the candidate and the reference certificate should match. + * + * If no resigned version is found then reference certificate itself is returned. + * + * @param referenceCertificate A DER certificate. + * @param candidateCertificates A pointer to the list of DER Certificates, which should be searched + * for the resigned version of `referenceCertificate`. + * @param candidateCertificatesCount Number of certificates in the `candidateCertificates` list. + * @param outCertificate A reference to the certificate or it's resigned version if found. + * Note that it points to either `referenceCertificate` or one of + * `candidateCertificates`, but it doesn't copy data. + * + * @returns error if there is certificate parsing/format issue or CHIP_NO_ERROR otherwise. + **/ +CHIP_ERROR ReplaceCertIfResignedCertFound(const ByteSpan & referenceCertificate, const ByteSpan * candidateCertificates, + size_t candidateCertificatesCount, ByteSpan & outCertificate); + +/** + * Defines DN attribute types that can include endocing of VID/PID parameters. + */ +enum class DNAttrType +{ + kUnspecified = 0, + kCommonName = 1, + kMatterVID = 2, + kMatterPID = 3, +}; + +/** + * @struct AttestationCertVidPid + * + * @brief + * A data structure representing Attestation Certificate VID and PID attributes. + */ +struct AttestationCertVidPid +{ + Optional mVendorId; + Optional mProductId; + + bool Initialized() const { return (mVendorId.HasValue() || mProductId.HasValue()); } +}; + +/** + * @brief Extracts VID and PID attributes from the DN Attribute string. + * If attribute is not present the corresponding output value stays uninitialized. + * + * @return CHIP_ERROR_INVALID_ARGUMENT if wrong input is provided. + * CHIP_ERROR_WRONG_CERT_DN if encoding of kMatterVID and kMatterPID attributes is wrong. + * CHIP_NO_ERROR otherwise. + **/ +CHIP_ERROR ExtractVIDPIDFromAttributeString(DNAttrType attrType, const ByteSpan & attr, + AttestationCertVidPid & vidpidFromMatterAttr, AttestationCertVidPid & vidpidFromCNAttr); + +/** + * @brief Extracts VID and PID attributes from the Subject DN of an X509 Certificate. + * If attribute is not present the corresponding output value stays uninitialized. + **/ +CHIP_ERROR ExtractVIDPIDFromX509Cert(const ByteSpan & x509Cert, AttestationCertVidPid & vidpid); + +/** + * @brief The set of credentials needed to operate group message security with symmetric keys. + */ +typedef struct GroupOperationalCredentials +{ + /// Validity start time in microseconds since 2000-01-01T00:00:00 UTC ("the Epoch") + uint64_t start_time; + /// Session Id + uint16_t hash; + /// Operational group key + uint8_t encryption_key[Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES]; + /// Privacy key + uint8_t privacy_key[Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES]; +} GroupOperationalCredentials; + +/** + * @brief Opaque context used to protect a symmetric key. The key operations must + * be performed without exposing the protected key value. + */ +class SymmetricKeyContext +{ +public: + /** + * @brief Returns the symmetric key hash + * + * TODO: Replace GetKeyHash() with DeriveGroupSessionId(SymmetricKeyContext &, uint16_t & session_id) + * + * @return Group Key Hash + */ + virtual uint16_t GetKeyHash() = 0; + + virtual ~SymmetricKeyContext() = default; + /** + * @brief Perform the message encryption as described in 4.7.2. (Security Processing of Outgoing Messages) + * @param[in] plaintext Outgoing message payload. + * @param[in] aad Additional data (message header contents) + * @param[in] nonce Nonce (Security Flags | Message Counter | Source Node ID) + * @param[out] mic Outgoing Message Integrity Check + * @param[out] ciphertext Outgoing encrypted payload. Must be at least as big as plaintext. The same buffer may be used both + * for ciphertext, and plaintext. + * @return CHIP_ERROR + */ + virtual CHIP_ERROR MessageEncrypt(const ByteSpan & plaintext, const ByteSpan & aad, const ByteSpan & nonce, + MutableByteSpan & mic, MutableByteSpan & ciphertext) const = 0; + /** + * @brief Perform the message decryption as described in 4.7.3.(Security Processing of Incoming Messages) + * @param[in] ciphertext Incoming encrypted payload + * @param[in] aad Additional data (message header contents) + * @param[in] nonce Nonce (Security Flags | Message Counter | Source Node ID) + * @param[in] mic Incoming Message Integrity Check + * @param[out] plaintext Incoming message payload. Must be at least as big as ciphertext. The same buffer may be used both + * for plaintext, and ciphertext. + * @return CHIP_ERROR + */ + virtual CHIP_ERROR MessageDecrypt(const ByteSpan & ciphertext, const ByteSpan & aad, const ByteSpan & nonce, + const ByteSpan & mic, MutableByteSpan & plaintext) const = 0; + + /** + * @brief Perform privacy encoding as described in 4.8.2. (Privacy Processing of Outgoing Messages) + * @param[in] input Message header to privacy encrypt + * @param[in] nonce Privacy Nonce = session_id | mic + * @param[out] output Message header obfuscated + * @return CHIP_ERROR + */ + virtual CHIP_ERROR PrivacyEncrypt(const ByteSpan & input, const ByteSpan & nonce, MutableByteSpan & output) const = 0; + + /** + * @brief Perform privacy decoding as described in 4.8.3. (Privacy Processing of Incoming Messages) + * @param[in] input Message header to privacy decrypt + * @param[in] nonce Privacy Nonce = session_id | mic + * @param[out] output Message header deobfuscated + * @return CHIP_ERROR + */ + virtual CHIP_ERROR PrivacyDecrypt(const ByteSpan & input, const ByteSpan & nonce, MutableByteSpan & output) const = 0; + + /** + * @brief Release resources such as dynamic memory used to allocate this instance of the SymmetricKeyContext + */ + virtual void Release() = 0; +}; + +/** + * @brief Derives the Operational Group Key using the Key Derivation Function (KDF) from the given epoch key. + * @param[in] epoch_key The epoch key. Must be CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES bytes length. + * @param[in] compressed_fabric_id The compressed fabric ID for the fabric (big endian byte string) + * @param[out] out_key Symmetric key used as the encryption key during message processing for group communication. + The buffer size must be at least CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES bytes length. + * @return Returns a CHIP_NO_ERROR on succcess, or CHIP_ERROR_INTERNAL if the provided key is invalid. + **/ +CHIP_ERROR DeriveGroupOperationalKey(const ByteSpan & epoch_key, const ByteSpan & compressed_fabric_id, MutableByteSpan & out_key); + +/** + * @brief Derives the Group Session ID from a given operational group key using + * the Key Derivation Function (Group Key Hash) + * @param[in] operational_key The operational group key. Must be CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES bytes length. + * @param[out] session_id Output of the Group Key Hash + * @return Returns a CHIP_NO_ERROR on succcess, or CHIP_ERROR_INVALID_ARGUMENT if the provided key is invalid. + **/ +CHIP_ERROR DeriveGroupSessionId(const ByteSpan & operational_key, uint16_t & session_id); + +/** + * @brief Derives the Privacy Group Key using the Key Derivation Function (KDF) from the given epoch key. + * @param[in] epoch_key The epoch key. Must be CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES bytes length. + * @param[out] out_key Symmetric key used as the privacy key during message processing for group communication. + * The buffer size must be at least CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES bytes length. + * @return Returns a CHIP_NO_ERROR on succcess, or CHIP_ERROR_INTERNAL if the provided key is invalid. + **/ +CHIP_ERROR DeriveGroupPrivacyKey(const ByteSpan & epoch_key, MutableByteSpan & out_key); + +/** + * @brief Derives the complete set of credentials needed for group security. + * + * This function will derive the Encryption Key, Group Key Hash (Session Id), and Privacy Key + * for the given Epoch Key and Compressed Fabric Id. + * @param[in] epoch_key The epoch key. Must be CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES bytes length. + * @param[in] compressed_fabric_id The compressed fabric ID for the fabric (big endian byte string) + * @param[out] operational_credentials The set of Symmetric keys used during message processing for group communication. + * @return Returns a CHIP_NO_ERROR on succcess, or CHIP_ERROR_INTERNAL if the provided key is invalid. + **/ +CHIP_ERROR DeriveGroupOperationalCredentials(const ByteSpan & epoch_key, const ByteSpan & compressed_fabric_id, + GroupOperationalCredentials & operational_credentials); +} // namespace Crypto +} // namespace chip diff --git a/src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp b/src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp new file mode 100644 index 00000000000000..fcce1f65e90a52 --- /dev/null +++ b/src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp @@ -0,0 +1,1772 @@ +/* + * + * Copyright (c) 2020-2022 Project CHIP Authors + * + * 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 + * mbedTLS based implementation of CHIP crypto primitives + */ + +#include "CHIPCryptoPAL.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(MBEDTLS_X509_CRT_PARSE_C) +#include +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace chip { +namespace Crypto { + +#define MAX_ERROR_STR_LEN 128 +#define NUM_BYTES_IN_SHA256_HASH 32 + +// In mbedTLS 3.0.0 direct access to structure fields was replaced with using MBEDTLS_PRIVATE macro. +#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) +#define CHIP_CRYPTO_PAL_PRIVATE(x) MBEDTLS_PRIVATE(x) +#else +#define CHIP_CRYPTO_PAL_PRIVATE(x) x +#endif + +#if (MBEDTLS_VERSION_NUMBER >= 0x03000000 && MBEDTLS_VERSION_NUMBER < 0x03010000) +#define CHIP_CRYPTO_PAL_PRIVATE_X509(x) MBEDTLS_PRIVATE(x) +#else +#define CHIP_CRYPTO_PAL_PRIVATE_X509(x) x +#endif + +typedef struct +{ + bool mInitialized; + bool mDRBGSeeded; + mbedtls_ctr_drbg_context mDRBGCtxt; + mbedtls_entropy_context mEntropy; +} EntropyContext; + +static EntropyContext gsEntropyContext; + +static void _log_mbedTLS_error(int error_code) +{ + if (error_code != 0 && error_code != UECC_SUCCESS) + { +#if defined(MBEDTLS_ERROR_C) + char error_str[MAX_ERROR_STR_LEN]; + mbedtls_strerror(error_code, error_str, sizeof(error_str)); + ChipLogError(Crypto, "mbedTLS error: %s", error_str); +#else + // Error codes defined in 16-bit negative hex numbers. Ease lookup by printing likewise + ChipLogError(Crypto, "mbedTLS error: -0x%04X", -static_cast(error_code)); +#endif + } +} + +static bool _isValidTagLength(size_t tag_length) +{ + if (tag_length == 8 || tag_length == 12 || tag_length == 16) + { + return true; + } + return false; +} + +static bool _isValidKeyLength(size_t length) +{ + // 16 bytes key for AES-CCM-128, 32 for AES-CCM-256 + if (length == 16 || length == 32) + { + return true; + } + return false; +} + +CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, const uint8_t * aad, size_t aad_length, + const uint8_t * key, size_t key_length, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, + uint8_t * tag, size_t tag_length) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 1; + + mbedtls_ccm_context context; + mbedtls_ccm_init(&context); + + VerifyOrExit(plaintext != nullptr || plaintext_length == 0, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(ciphertext != nullptr || plaintext_length == 0, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(key != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(_isValidKeyLength(key_length), error = CHIP_ERROR_UNSUPPORTED_ENCRYPTION_TYPE); + VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(tag != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(_isValidTagLength(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT); + if (aad_length > 0) + { + VerifyOrExit(aad != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); + } + + // Size of key = key_length * number of bits in a byte (8) + // Cast is safe because we called _isValidKeyLength above. + result = + mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, Uint8::to_const_uchar(key), static_cast(key_length * 8)); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + // Encrypt + result = mbedtls_ccm_encrypt_and_tag(&context, plaintext_length, Uint8::to_const_uchar(nonce), nonce_length, + Uint8::to_const_uchar(aad), aad_length, Uint8::to_const_uchar(plaintext), + Uint8::to_uchar(ciphertext), Uint8::to_uchar(tag), tag_length); + _log_mbedTLS_error(result); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + +exit: + mbedtls_ccm_free(&context); + return error; +} + +CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, const uint8_t * aad, size_t aad_len, + const uint8_t * tag, size_t tag_length, const uint8_t * key, size_t key_length, const uint8_t * nonce, + size_t nonce_length, uint8_t * plaintext) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 1; + + mbedtls_ccm_context context; + mbedtls_ccm_init(&context); + + VerifyOrExit(plaintext != nullptr || ciphertext_len == 0, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(ciphertext != nullptr || ciphertext_len == 0, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(tag != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(_isValidTagLength(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(key != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(_isValidKeyLength(key_length), error = CHIP_ERROR_UNSUPPORTED_ENCRYPTION_TYPE); + VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); + if (aad_len > 0) + { + VerifyOrExit(aad != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); + } + + // Size of key = key_length * number of bits in a byte (8) + // Cast is safe because we called _isValidKeyLength above. + result = + mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, Uint8::to_const_uchar(key), static_cast(key_length * 8)); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + // Decrypt + result = mbedtls_ccm_auth_decrypt(&context, ciphertext_len, Uint8::to_const_uchar(nonce), nonce_length, + Uint8::to_const_uchar(aad), aad_len, Uint8::to_const_uchar(ciphertext), + Uint8::to_uchar(plaintext), Uint8::to_const_uchar(tag), tag_length); + _log_mbedTLS_error(result); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + +exit: + mbedtls_ccm_free(&context); + return error; +} + +CHIP_ERROR Hash_SHA256(const uint8_t * data, const size_t data_length, uint8_t * out_buffer) +{ + // zero data length hash is supported. + VerifyOrReturnError(data != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(out_buffer != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + +#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) + const int result = mbedtls_sha256(Uint8::to_const_uchar(data), data_length, Uint8::to_uchar(out_buffer), 0); +#else + const int result = mbedtls_sha256_ret(Uint8::to_const_uchar(data), data_length, Uint8::to_uchar(out_buffer), 0); +#endif + + VerifyOrReturnError(result == 0, CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR Hash_SHA1(const uint8_t * data, const size_t data_length, uint8_t * out_buffer) +{ + // zero data length hash is supported. + VerifyOrReturnError(out_buffer != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + +#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) + const int result = mbedtls_sha1(Uint8::to_const_uchar(data), data_length, Uint8::to_uchar(out_buffer)); +#else + const int result = mbedtls_sha1_ret(Uint8::to_const_uchar(data), data_length, Uint8::to_uchar(out_buffer)); +#endif + + VerifyOrReturnError(result == 0, CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +static_assert(kMAX_Hash_SHA256_Context_Size >= sizeof(mbedtls_sha256_context), + "kMAX_Hash_SHA256_Context_Size is too small for the size of underlying mbedtls_sha256_context"); + +static inline mbedtls_sha256_context * to_inner_hash_sha256_context(HashSHA256OpaqueContext * context) +{ + return SafePointerCast(context); +} + +Hash_SHA256_stream::Hash_SHA256_stream(void) +{ + mbedtls_sha256_context * context = to_inner_hash_sha256_context(&mContext); + mbedtls_sha256_init(context); +} + +Hash_SHA256_stream::~Hash_SHA256_stream(void) +{ + mbedtls_sha256_context * context = to_inner_hash_sha256_context(&mContext); + mbedtls_sha256_free(context); + Clear(); +} + +CHIP_ERROR Hash_SHA256_stream::Begin(void) +{ + mbedtls_sha256_context * const context = to_inner_hash_sha256_context(&mContext); + +#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) + const int result = mbedtls_sha256_starts(context, 0); +#else + const int result = mbedtls_sha256_starts_ret(context, 0); +#endif + + VerifyOrReturnError(result == 0, CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR Hash_SHA256_stream::AddData(const ByteSpan data) +{ + mbedtls_sha256_context * const context = to_inner_hash_sha256_context(&mContext); + +#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) + const int result = mbedtls_sha256_update(context, Uint8::to_const_uchar(data.data()), data.size()); +#else + const int result = mbedtls_sha256_update_ret(context, Uint8::to_const_uchar(data.data()), data.size()); +#endif + + VerifyOrReturnError(result == 0, CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR Hash_SHA256_stream::GetDigest(MutableByteSpan & out_buffer) +{ + mbedtls_sha256_context * context = to_inner_hash_sha256_context(&mContext); + + // Back-up context as we are about to finalize the hash to extract digest. + mbedtls_sha256_context previous_ctx; + mbedtls_sha256_init(&previous_ctx); + mbedtls_sha256_clone(&previous_ctx, context); + + // Pad + compute digest, then finalize context. It is restored next line to continue. + CHIP_ERROR result = Finish(out_buffer); + + // Restore context prior to finalization. + mbedtls_sha256_clone(context, &previous_ctx); + mbedtls_sha256_free(&previous_ctx); + + return result; +} + +CHIP_ERROR Hash_SHA256_stream::Finish(MutableByteSpan & out_buffer) +{ + VerifyOrReturnError(out_buffer.size() >= kSHA256_Hash_Length, CHIP_ERROR_BUFFER_TOO_SMALL); + mbedtls_sha256_context * const context = to_inner_hash_sha256_context(&mContext); + +#if (MBEDTLS_VERSION_NUMBER >= 0x03000000) + const int result = mbedtls_sha256_finish(context, Uint8::to_uchar(out_buffer.data())); +#else + const int result = mbedtls_sha256_finish_ret(context, Uint8::to_uchar(out_buffer.data())); +#endif + + VerifyOrReturnError(result == 0, CHIP_ERROR_INTERNAL); + out_buffer = out_buffer.SubSpan(0, kSHA256_Hash_Length); + + return CHIP_NO_ERROR; +} + +void Hash_SHA256_stream::Clear(void) +{ + mbedtls_platform_zeroize(this, sizeof(*this)); +} + +CHIP_ERROR HKDF_sha::HKDF_SHA256(const uint8_t * secret, const size_t secret_length, const uint8_t * salt, const size_t salt_length, + const uint8_t * info, const size_t info_length, uint8_t * out_buffer, size_t out_length) +{ + VerifyOrReturnError(secret != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(secret_length > 0, CHIP_ERROR_INVALID_ARGUMENT); + + // Salt is optional + if (salt_length > 0) + { + VerifyOrReturnError(salt != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + } + + VerifyOrReturnError(info_length > 0, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(info != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(out_length > 0, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(out_buffer != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + + const mbedtls_md_info_t * const md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + VerifyOrReturnError(md != nullptr, CHIP_ERROR_INTERNAL); + + const int result = mbedtls_hkdf(md, Uint8::to_const_uchar(salt), salt_length, Uint8::to_const_uchar(secret), secret_length, + Uint8::to_const_uchar(info), info_length, Uint8::to_uchar(out_buffer), out_length); + _log_mbedTLS_error(result); + VerifyOrReturnError(result == 0, CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR HMAC_sha::HMAC_SHA256(const uint8_t * key, size_t key_length, const uint8_t * message, size_t message_length, + uint8_t * out_buffer, size_t out_length) +{ + VerifyOrReturnError(key != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(key_length > 0, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(message != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(message_length > 0, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(out_length >= kSHA256_Hash_Length, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(out_buffer != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + + const mbedtls_md_info_t * const md = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + VerifyOrReturnError(md != nullptr, CHIP_ERROR_INTERNAL); + + const int result = + mbedtls_md_hmac(md, Uint8::to_const_uchar(key), key_length, Uint8::to_const_uchar(message), message_length, out_buffer); + + _log_mbedTLS_error(result); + VerifyOrReturnError(result == 0, CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR PBKDF2_sha256::pbkdf2_sha256(const uint8_t * password, size_t plen, const uint8_t * salt, size_t slen, + unsigned int iteration_count, uint32_t key_length, uint8_t * output) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + const mbedtls_md_info_t * md_info; + mbedtls_md_context_t md_ctxt; + constexpr int use_hmac = 1; + + bool free_md_ctxt = false; + + VerifyOrExit(password != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(plen > 0, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(salt != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(slen >= kSpake2p_Min_PBKDF_Salt_Length, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(slen <= kSpake2p_Max_PBKDF_Salt_Length, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(key_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(output != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); + + md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); + VerifyOrExit(md_info != nullptr, error = CHIP_ERROR_INTERNAL); + + mbedtls_md_init(&md_ctxt); + free_md_ctxt = true; + + result = mbedtls_md_setup(&md_ctxt, md_info, use_hmac); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + result = mbedtls_pkcs5_pbkdf2_hmac(&md_ctxt, Uint8::to_const_uchar(password), plen, Uint8::to_const_uchar(salt), slen, + iteration_count, key_length, Uint8::to_uchar(output)); + + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + +exit: + _log_mbedTLS_error(result); + + if (free_md_ctxt) + { + mbedtls_md_free(&md_ctxt); + } + + return error; +} + +static EntropyContext * get_entropy_context() +{ + if (!gsEntropyContext.mInitialized) + { + mbedtls_entropy_init(&gsEntropyContext.mEntropy); + mbedtls_ctr_drbg_init(&gsEntropyContext.mDRBGCtxt); + + gsEntropyContext.mInitialized = true; + } + + return &gsEntropyContext; +} + +static mbedtls_ctr_drbg_context * get_drbg_context() +{ + EntropyContext * const context = get_entropy_context(); + + mbedtls_ctr_drbg_context * const drbgCtxt = &context->mDRBGCtxt; + + if (!context->mDRBGSeeded) + { + const int status = mbedtls_ctr_drbg_seed(drbgCtxt, mbedtls_entropy_func, &context->mEntropy, nullptr, 0); + if (status != 0) + { + _log_mbedTLS_error(status); + return nullptr; + } + + context->mDRBGSeeded = true; + } + + return drbgCtxt; +} + +CHIP_ERROR add_entropy_source(entropy_source fn_source, void * p_source, size_t threshold) +{ + VerifyOrReturnError(fn_source != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + + EntropyContext * const entropy_ctxt = get_entropy_context(); + VerifyOrReturnError(entropy_ctxt != nullptr, CHIP_ERROR_INTERNAL); + + const int result = + mbedtls_entropy_add_source(&entropy_ctxt->mEntropy, fn_source, p_source, threshold, MBEDTLS_ENTROPY_SOURCE_STRONG); + VerifyOrReturnError(result == 0, CHIP_ERROR_INTERNAL); + return CHIP_NO_ERROR; +} + +CHIP_ERROR DRBG_get_bytes(uint8_t * out_buffer, const size_t out_length) +{ + VerifyOrReturnError(out_buffer != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(out_length > 0, CHIP_ERROR_INVALID_ARGUMENT); + + mbedtls_ctr_drbg_context * const drbg_ctxt = get_drbg_context(); + VerifyOrReturnError(drbg_ctxt != nullptr, CHIP_ERROR_INTERNAL); + + const int result = mbedtls_ctr_drbg_random(drbg_ctxt, Uint8::to_uchar(out_buffer), out_length); + VerifyOrReturnError(result == 0, CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +static int CryptoRNG(void * ctxt, uint8_t * out_buffer, size_t out_length) +{ + return (chip::Crypto::DRBG_get_bytes(out_buffer, out_length) == CHIP_NO_ERROR) ? 0 : 1; +} + +mbedtls_ecp_group_id MapECPGroupId(SupportedECPKeyTypes keyType) +{ + switch (keyType) + { + case SupportedECPKeyTypes::ECP256R1: + return MBEDTLS_ECP_DP_SECP256R1; + default: + return MBEDTLS_ECP_DP_NONE; + } +} + +static inline mbedtls_uecc_keypair * to_keypair(P256KeypairContext * context) +{ + return SafePointerCast(context); +} + +static inline const mbedtls_uecc_keypair * to_const_keypair(const P256KeypairContext * context) +{ + return SafePointerCast(context); +} + +CHIP_ERROR P256Keypair::ECDSA_sign_msg(const uint8_t * msg, const size_t msg_length, P256ECDSASignature & out_signature) const +{ + VerifyOrReturnError(mInitialized, CHIP_ERROR_WELL_UNINITIALIZED); + VerifyOrReturnError((msg != nullptr) && (msg_length > 0), CHIP_ERROR_INVALID_ARGUMENT); + + uint8_t digest[kSHA256_Hash_Length]; + memset(&digest[0], 0, sizeof(digest)); + ReturnErrorOnFailure(Hash_SHA256(msg, msg_length, &digest[0])); + + CHIP_ERROR error = CHIP_NO_ERROR; + int result = UECC_FAILURE; + + const mbedtls_uecc_keypair * keypair = to_const_keypair(&mKeypair); + + result = uECC_sign(keypair->private_key, digest, sizeof(digest), out_signature.Bytes()); + + VerifyOrExit(result == UECC_SUCCESS, error = CHIP_ERROR_INTERNAL); + VerifyOrExit(out_signature.SetLength(kP256_ECDSA_Signature_Length_Raw) == CHIP_NO_ERROR, error = CHIP_ERROR_INTERNAL); + + keypair = nullptr; + +exit: + return error; +} + +CHIP_ERROR P256PublicKey::ECDSA_validate_msg_signature(const uint8_t * msg, const size_t msg_length, + const P256ECDSASignature & signature) const +{ +#if defined(MBEDTLS_ECDSA_C) + VerifyOrReturnError((msg != nullptr) && (msg_length > 0), CHIP_ERROR_INVALID_ARGUMENT); + + uint8_t digest[kSHA256_Hash_Length]; + memset(&digest[0], 0, sizeof(digest)); + ReturnErrorOnFailure(Hash_SHA256(msg, msg_length, &digest[0])); + + return ECDSA_validate_hash_signature(&digest[0], sizeof(digest), signature); +#else + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif +} + +CHIP_ERROR P256PublicKey::ECDSA_validate_hash_signature(const uint8_t * hash, const size_t hash_length, + const P256ECDSASignature & signature) const +{ + VerifyOrReturnError(hash != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(hash_length == kSHA256_Hash_Length, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(signature.Length() == kP256_ECDSA_Signature_Length_Raw, CHIP_ERROR_INVALID_ARGUMENT); + + CHIP_ERROR error = CHIP_NO_ERROR; + int result = UECC_FAILURE; + + const uint8_t * public_key = *this; + + // Fully padded raw uncompressed points expected, first byte is always 0x04 i.e uncompressed + result = uECC_verify(public_key + 1, hash, hash_length, Uint8::to_const_uchar(signature.ConstBytes())); + VerifyOrExit(result == UECC_SUCCESS, error = CHIP_ERROR_INVALID_SIGNATURE); + +exit: + return error; +} + +CHIP_ERROR P256Keypair::ECDH_derive_secret(const P256PublicKey & remote_public_key, P256ECDHDerivedSecret & out_secret) const +{ +#if defined(MBEDTLS_ECDH_C) + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + size_t secret_length = (out_secret.Length() == 0) ? out_secret.Capacity() : out_secret.Length(); + + const mbedtls_uecc_keypair * keypair = to_const_keypair(&mKeypair); + + VerifyOrExit(mInitialized, error = CHIP_ERROR_WELL_UNINITIALIZED); + + // Fully padded raw uncompressed points expected, first byte is always 0x04 i.e uncompressed + result = uECC_shared_secret(remote_public_key.ConstBytes() + 1, keypair->private_key, out_secret.Bytes()); + VerifyOrExit(result == UECC_SUCCESS, error = CHIP_ERROR_INTERNAL); + + SuccessOrExit(out_secret.SetLength(secret_length)); + +exit: + keypair = nullptr; + _log_mbedTLS_error(result); + return error; +#else + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif +} + +void ClearSecretData(uint8_t * buf, size_t len) +{ + mbedtls_platform_zeroize(buf, len); +} + +// THE BELOW IS FROM `third_party/openthread/repo/third_party/mbedtls/repo/library/constant_time.c` since +// mbedtls_ct_memcmp is not available on Linux somehow :( +int mbedtls_ct_memcmp_copy(const void * a, const void * b, size_t n) +{ + size_t i; + volatile const unsigned char * A = (volatile const unsigned char *) a; + volatile const unsigned char * B = (volatile const unsigned char *) b; + volatile unsigned char diff = 0; + + for (i = 0; i < n; i++) + { + /* Read volatile data in order before computing diff. + * This avoids IAR compiler warning: + * 'the order of volatile accesses is undefined ..' */ + unsigned char x = A[i], y = B[i]; + diff |= x ^ y; + } + + return ((int) diff); +} + +bool IsBufferContentEqualConstantTime(const void * a, const void * b, size_t n) +{ + return mbedtls_ct_memcmp_copy(a, b, n) == 0; +} + +CHIP_ERROR P256Keypair::Initialize(ECPKeyTarget key_target) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + int result = UECC_FAILURE; + + Clear(); + + mbedtls_uecc_keypair * keypair = to_keypair(&mKeypair); + + result = uECC_make_key(keypair->public_key, keypair->private_key); + VerifyOrExit(result == UECC_SUCCESS, error = CHIP_ERROR_INTERNAL); + + // Fully padded raw uncompressed points expected, first byte is always 0x04 i.e uncompressed + Uint8::to_uchar(mPublicKey)[0] = 0x04; + memcpy(Uint8::to_uchar(mPublicKey) + 1, keypair->public_key, 2 * NUM_ECC_BYTES); + + keypair = nullptr; + mInitialized = true; + +exit: + _log_mbedTLS_error(result); + return error; +} + +CHIP_ERROR P256Keypair::Serialize(P256SerializedKeypair & output) const +{ + const mbedtls_uecc_keypair * keypair = to_const_keypair(&mKeypair); + size_t len = output.Length() == 0 ? output.Capacity() : output.Length(); + Encoding::BufferWriter bbuf(output.Bytes(), len); + uint8_t privkey[kP256_PrivateKey_Length]; + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + + bbuf.Put(mPublicKey, mPublicKey.Length()); + + VerifyOrExit(bbuf.Available() == sizeof(privkey), error = CHIP_ERROR_INTERNAL); + VerifyOrExit(sizeof(keypair->private_key) <= bbuf.Available(), error = CHIP_ERROR_INTERNAL); + + memcpy(privkey, keypair->private_key, sizeof(privkey)); + + bbuf.Put(privkey, sizeof(privkey)); + VerifyOrExit(bbuf.Fit(), error = CHIP_ERROR_BUFFER_TOO_SMALL); + + output.SetLength(bbuf.Needed()); + +exit: + memset(privkey, 0, sizeof(privkey)); + _log_mbedTLS_error(result); + return error; +} + +CHIP_ERROR P256Keypair::Deserialize(P256SerializedKeypair & input) +{ + int result = 0; + CHIP_ERROR error = CHIP_NO_ERROR; + Encoding::BufferWriter bbuf(mPublicKey, mPublicKey.Length()); + + Clear(); + + mbedtls_uecc_keypair * keypair = to_keypair(&mKeypair); + + // Fully padded raw uncompressed points expected, first byte is always 0x04 i.e uncompressed + memcpy(keypair->public_key, input.ConstBytes() + 1, 2 * NUM_ECC_BYTES); + memcpy(keypair->private_key, input.ConstBytes() + mPublicKey.Length(), NUM_ECC_BYTES); + + keypair = nullptr; + + VerifyOrExit(input.Length() == mPublicKey.Length() + kP256_PrivateKey_Length, error = CHIP_ERROR_INVALID_ARGUMENT); + bbuf.Put(input.ConstBytes(), mPublicKey.Length()); + VerifyOrExit(bbuf.Fit(), error = CHIP_ERROR_NO_MEMORY); + + mInitialized = true; + + _log_mbedTLS_error(result); + +exit: + return error; +} + +void P256Keypair::Clear() +{ + if (mInitialized) + { + mbedtls_uecc_keypair * keypair = to_keypair(&mKeypair); + memset(keypair, 0, sizeof(mbedtls_uecc_keypair)); + mInitialized = false; + } +} + +P256Keypair::~P256Keypair() +{ + Clear(); +} + +CHIP_ERROR P256Keypair::NewCertificateSigningRequest(uint8_t * out_csr, size_t & csr_length) const +{ + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + size_t out_length; + + mbedtls_x509write_csr csr; + mbedtls_x509write_csr_init(&csr); + + mbedtls_pk_context pk; + pk.CHIP_CRYPTO_PAL_PRIVATE(pk_info) = mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY); + pk.CHIP_CRYPTO_PAL_PRIVATE(pk_ctx) = to_keypair(&mKeypair); + VerifyOrExit(pk.CHIP_CRYPTO_PAL_PRIVATE(pk_info) != nullptr, error = CHIP_ERROR_INTERNAL); + + VerifyOrExit(mInitialized, error = CHIP_ERROR_WELL_UNINITIALIZED); + + mbedtls_x509write_csr_set_key(&csr, &pk); + + mbedtls_x509write_csr_set_md_alg(&csr, MBEDTLS_MD_SHA256); + + // TODO: mbedTLS CSR parser fails if the subject name is not set (or if empty). + // CHIP Spec doesn't specify the subject name that can be used. + // Figure out the correct value and update this code. + result = mbedtls_x509write_csr_set_subject_name(&csr, "O=CSR"); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + result = mbedtls_x509write_csr_der(&csr, out_csr, csr_length, CryptoRNG, nullptr); + VerifyOrExit(result > 0, error = CHIP_ERROR_INTERNAL); + VerifyOrExit(CanCastTo(result), error = CHIP_ERROR_INTERNAL); + + out_length = static_cast(result); + result = 0; + VerifyOrExit(out_length <= csr_length, error = CHIP_ERROR_INTERNAL); + + if (csr_length != out_length) + { + // mbedTLS API writes the CSR at the end of the provided buffer. + // Let's move it to the start of the buffer. + size_t offset = csr_length - out_length; + memmove(out_csr, &out_csr[offset], out_length); + } + + csr_length = out_length; + +exit: + mbedtls_x509write_csr_free(&csr); + + _log_mbedTLS_error(result); + return error; +} + +CHIP_ERROR VerifyCertificateSigningRequest(const uint8_t * csr_buf, size_t csr_length, P256PublicKey & pubkey) +{ +#if defined(MBEDTLS_X509_CSR_PARSE_C) + ReturnErrorOnFailure(VerifyCertificateSigningRequestFormat(csr_buf, csr_length)); + + // TODO: For some embedded targets, mbedTLS library doesn't have mbedtls_x509_csr_parse_der, and mbedtls_x509_csr_parse_free. + // Taking a step back, embedded targets likely will not process CSR requests. Adding this action item to reevaluate + // this if there's a need for this processing for embedded targets. + CHIP_ERROR error = CHIP_NO_ERROR; + size_t pubkey_size = 0; + + mbedtls_ecp_keypair * keypair = nullptr; + + P256ECDSASignature signature; + MutableByteSpan out_raw_sig_span(signature.Bytes(), signature.Capacity()); + + mbedtls_x509_csr csr; + mbedtls_x509_csr_init(&csr); + + int result = mbedtls_x509_csr_parse_der(&csr, csr_buf, csr_length); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + // Verify the signature algorithm and public key type + VerifyOrExit(csr.CHIP_CRYPTO_PAL_PRIVATE(sig_md) == MBEDTLS_MD_SHA256, error = CHIP_ERROR_UNSUPPORTED_SIGNATURE_TYPE); + VerifyOrExit(csr.CHIP_CRYPTO_PAL_PRIVATE(sig_pk) == MBEDTLS_PK_ECDSA, error = CHIP_ERROR_WRONG_KEY_TYPE); + + keypair = mbedtls_pk_ec(csr.CHIP_CRYPTO_PAL_PRIVATE_X509(pk)); + + // Copy the public key from the CSR + result = mbedtls_ecp_point_write_binary(&keypair->CHIP_CRYPTO_PAL_PRIVATE(grp), &keypair->CHIP_CRYPTO_PAL_PRIVATE(Q), + MBEDTLS_ECP_PF_UNCOMPRESSED, &pubkey_size, Uint8::to_uchar(pubkey), pubkey.Length()); + + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + VerifyOrExit(pubkey_size == pubkey.Length(), error = CHIP_ERROR_INTERNAL); + + // Convert DER signature to raw signature + error = EcdsaAsn1SignatureToRaw(kP256_FE_Length, + ByteSpan{ csr.CHIP_CRYPTO_PAL_PRIVATE(sig).CHIP_CRYPTO_PAL_PRIVATE_X509(p), + csr.CHIP_CRYPTO_PAL_PRIVATE(sig).CHIP_CRYPTO_PAL_PRIVATE_X509(len) }, + out_raw_sig_span); + + VerifyOrExit(error == CHIP_NO_ERROR, error = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(out_raw_sig_span.size() == (kP256_FE_Length * 2), error = CHIP_ERROR_INTERNAL); + signature.SetLength(out_raw_sig_span.size()); + + // Verify the signature using the public key + error = pubkey.ECDSA_validate_msg_signature(csr.CHIP_CRYPTO_PAL_PRIVATE_X509(cri).CHIP_CRYPTO_PAL_PRIVATE_X509(p), + csr.CHIP_CRYPTO_PAL_PRIVATE_X509(cri).CHIP_CRYPTO_PAL_PRIVATE_X509(len), signature); + + SuccessOrExit(error); + +exit: + mbedtls_x509_csr_free(&csr); + _log_mbedTLS_error(result); + return error; +#else + ChipLogError(Crypto, "MBEDTLS_X509_CSR_PARSE_C is not enabled. CSR cannot be parsed"); + return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; +#endif +} + +typedef struct Spake2p_Context +{ + const mbedtls_md_info_t * md_info; + uECC_word_t M[2 * NUM_ECC_WORDS]; + uECC_word_t N[2 * NUM_ECC_WORDS]; + uECC_word_t X[2 * NUM_ECC_WORDS]; + uECC_word_t Y[2 * NUM_ECC_WORDS]; + uECC_word_t L[2 * NUM_ECC_WORDS]; + uECC_word_t Z[2 * NUM_ECC_WORDS]; + uECC_word_t V[2 * NUM_ECC_WORDS]; + + uECC_word_t w0[NUM_ECC_WORDS]; + uECC_word_t w1[NUM_ECC_WORDS]; + uECC_word_t xy[NUM_ECC_WORDS]; + uECC_word_t tempbn[NUM_ECC_WORDS]; +} Spake2p_Context; + +static inline Spake2p_Context * to_inner_spake2p_context(Spake2pOpaqueContext * context) +{ + return SafePointerCast(context); +} + +CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::InitInternal(void) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + + Spake2p_Context * context = to_inner_spake2p_context(&mSpake2pContext); + + memset(context, 0, sizeof(Spake2p_Context)); + + M = context->M; + N = context->N; + X = context->X; + Y = context->Y; + L = context->L; + V = context->V; + Z = context->Z; + + w0 = context->w0; + w1 = context->w1; + xy = context->xy; + tempbn = context->tempbn; + + G = curve_G; + + return error; + +exit: + _log_mbedTLS_error(result); + Clear(); + return error; +} + +void Spake2p_P256_SHA256_HKDF_HMAC::Clear() +{ + VerifyOrReturn(state != CHIP_SPAKE2P_STATE::PREINIT); + + Spake2p_Context * context = to_inner_spake2p_context(&mSpake2pContext); + memset(&context->M, 0, 2 * NUM_ECC_WORDS * sizeof(uECC_word_t)); + memset(&context->N, 0, 2 * NUM_ECC_WORDS * sizeof(uECC_word_t)); + memset(&context->X, 0, 2 * NUM_ECC_WORDS * sizeof(uECC_word_t)); + memset(&context->Y, 0, 2 * NUM_ECC_WORDS * sizeof(uECC_word_t)); + memset(&context->L, 0, 2 * NUM_ECC_WORDS * sizeof(uECC_word_t)); + memset(&context->Z, 0, 2 * NUM_ECC_WORDS * sizeof(uECC_word_t)); + memset(&context->V, 0, 2 * NUM_ECC_WORDS * sizeof(uECC_word_t)); + + memset(&context->w0, 0, NUM_ECC_WORDS * sizeof(uECC_word_t)); + memset(&context->w1, 0, NUM_ECC_WORDS * sizeof(uECC_word_t)); + memset(&context->xy, 0, NUM_ECC_WORDS * sizeof(uECC_word_t)); + memset(&context->tempbn, 0, NUM_ECC_WORDS * sizeof(uECC_word_t)); + + G = NULL; + state = CHIP_SPAKE2P_STATE::PREINIT; +} + +CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::Mac(const uint8_t * key, size_t key_len, const uint8_t * in, size_t in_len, + MutableByteSpan & out_span) +{ + HMAC_sha hmac; + VerifyOrReturnError(out_span.size() >= kSHA256_Hash_Length, CHIP_ERROR_BUFFER_TOO_SMALL); + ReturnErrorOnFailure(hmac.HMAC_SHA256(key, key_len, in, in_len, out_span.data(), kSHA256_Hash_Length)); + out_span = out_span.SubSpan(0, kSHA256_Hash_Length); + return CHIP_NO_ERROR; +} + +CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::MacVerify(const uint8_t * key, size_t key_len, const uint8_t * mac, size_t mac_len, + const uint8_t * in, size_t in_len) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + + uint8_t computed_mac[kSHA256_Hash_Length]; + MutableByteSpan computed_mac_span{ computed_mac }; + VerifyOrExit(mac_len == kSHA256_Hash_Length, error = CHIP_ERROR_INVALID_ARGUMENT); + + SuccessOrExit(error = Mac(key, key_len, in, in_len, computed_mac_span)); + VerifyOrExit(computed_mac_span.size() == mac_len, error = CHIP_ERROR_INTERNAL); + + VerifyOrExit(IsBufferContentEqualConstantTime(mac, computed_mac, kSHA256_Hash_Length), error = CHIP_ERROR_INTERNAL); + +exit: + _log_mbedTLS_error(result); + return error; +} + +CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::FELoad(const uint8_t * in, size_t in_len, void * fe) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + + uECC_word_t tmp[2 * NUM_ECC_WORDS] = { 0 }; + uECC_vli_bytesToNative(tmp, in, NUM_ECC_BYTES); + + uECC_vli_mmod((uECC_word_t *) fe, tmp, curve_n); + +exit: + _log_mbedTLS_error(result); + return error; +} + +CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::FEWrite(const void * fe, uint8_t * out, size_t out_len) +{ + uECC_vli_nativeToBytes(out, NUM_ECC_BYTES, (const unsigned int *) fe); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::FEGenerate(void * fe) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + + mbedtls_uecc_keypair keypair; + + result = UECC_FAILURE; + + result = uECC_make_key(keypair.public_key, keypair.private_key); + VerifyOrExit(result == UECC_SUCCESS, error = CHIP_ERROR_INTERNAL); + + uECC_vli_bytesToNative((uECC_word_t *) fe, keypair.private_key, NUM_ECC_BYTES); + +exit: + _log_mbedTLS_error(result); + return error; +} + +CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::FEMul(void * fer, const void * fe1, const void * fe2) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + + uECC_vli_modMult((uECC_word_t *) fer, (const uECC_word_t *) fe1, (const uECC_word_t *) fe2, (const uECC_word_t *) curve_n); + +exit: + _log_mbedTLS_error(result); + return error; +} + +CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointLoad(const uint8_t * in, size_t in_len, void * R) +{ + uint8_t tmp[2 * NUM_ECC_BYTES]; + + // Fully padded raw uncompressed points expected, first byte is always 0x04 i.e uncompressed + memcpy(tmp, in + 1, 2 * NUM_ECC_BYTES); + + uECC_vli_bytesToNative((uECC_word_t *) R, tmp, NUM_ECC_BYTES); + uECC_vli_bytesToNative((uECC_word_t *) R + NUM_ECC_WORDS, tmp + NUM_ECC_BYTES, NUM_ECC_BYTES); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointWrite(const void * R, uint8_t * out, size_t out_len) +{ + memset(out, 0, out_len); + + // Fully padded raw uncompressed points expected, first byte is always 0x04 i.e uncompressed + out[0] = 0x04; + uECC_vli_nativeToBytes(out + 1, NUM_ECC_BYTES, (uECC_word_t *) R); + uECC_vli_nativeToBytes(out + NUM_ECC_BYTES + 1, NUM_ECC_BYTES, (uECC_word_t *) R + NUM_ECC_WORDS); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointMul(void * R, const void * P1, const void * fe1) +{ + + if (EccPoint_mult_safer((uECC_word_t *) R, (const uECC_word_t *) P1, (const uECC_word_t *) fe1) != UECC_SUCCESS) + { + return CHIP_ERROR_INTERNAL; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointAddMul(void * R, const void * P1, const void * fe1, const void * P2, + const void * fe2) +{ + uECC_word_t R1[2 * NUM_ECC_WORDS]; + uECC_word_t R2[2 * NUM_ECC_WORDS]; + uECC_word_t z[NUM_ECC_WORDS]; + uint8_t ret = UECC_SUCCESS; + + if (EccPoint_mult_safer(R1, (const uECC_word_t *) P1, (const uECC_word_t *) fe1) != UECC_SUCCESS) + { + return CHIP_ERROR_INTERNAL; + } + + if (EccPoint_mult_safer(R2, (const uECC_word_t *) P2, (const uECC_word_t *) fe2) != UECC_SUCCESS) + { + return CHIP_ERROR_INTERNAL; + } + + uECC_vli_modSub(z, R2, R1, curve_p); + XYcZ_add(R1, R1 + NUM_ECC_WORDS, R2, R2 + NUM_ECC_WORDS); + uECC_vli_modInv(z, z, curve_p); + apply_z(R2, R2 + NUM_ECC_WORDS, z); + + memcpy((uECC_word_t *) R, R2, 2 * NUM_ECC_BYTES); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointInvert(void * R) +{ + uECC_word_t tmp[NUM_ECC_WORDS] = { 0 }; + + uECC_vli_sub(tmp, curve_p, (uECC_word_t *) R + NUM_ECC_WORDS); + memcpy((uECC_word_t *) R + NUM_ECC_WORDS, tmp, NUM_ECC_BYTES); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointCofactorMul(void * R) +{ + return CHIP_NO_ERROR; +} + +CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::ComputeL(uint8_t * Lout, size_t * L_len, const uint8_t * w1in, size_t w1in_len) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + + result = UECC_SUCCESS; + uECC_word_t tmp[2 * NUM_ECC_WORDS]; + uECC_word_t w1_bn[NUM_ECC_WORDS]; + uECC_word_t L_tmp[2 * NUM_ECC_WORDS]; + + uECC_vli_bytesToNative(tmp, w1in, NUM_ECC_BYTES); + + uECC_vli_mmod(w1_bn, tmp, curve_n); + + result = EccPoint_mult_safer(L_tmp, curve_G, w1_bn); + VerifyOrExit(result == UECC_SUCCESS, error = CHIP_ERROR_INTERNAL); + + // Fully padded raw uncompressed points expected, first byte is always 0x04 i.e uncompressed + Lout[0] = 0x04; + uECC_vli_nativeToBytes(Lout + 1, NUM_ECC_BYTES, L_tmp); + uECC_vli_nativeToBytes(Lout + NUM_ECC_BYTES + 1, NUM_ECC_BYTES, L_tmp + NUM_ECC_WORDS); + +exit: + _log_mbedTLS_error(result); + + return error; +} + +CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::PointIsValid(void * R) +{ + if (uECC_valid_point((const uECC_word_t *) R) != 0) + { + return CHIP_ERROR_INTERNAL; + } + + return CHIP_NO_ERROR; +} + +namespace { + +#if defined(MBEDTLS_X509_CRT_PARSE_C) +bool IsTimeGreaterThanEqual(const mbedtls_x509_time * const timeA, const mbedtls_x509_time * const timeB) +{ + + // checks if two values are different and if yes, then returns first > second. +#define RETURN_STRICTLY_GREATER_IF_DIFFERENT(component) \ + { \ + auto valueA = timeA->CHIP_CRYPTO_PAL_PRIVATE_X509(component); \ + auto valueB = timeB->CHIP_CRYPTO_PAL_PRIVATE_X509(component); \ + \ + if (valueA != valueB) \ + { \ + return valueA > valueB; \ + } \ + } + + RETURN_STRICTLY_GREATER_IF_DIFFERENT(year); + RETURN_STRICTLY_GREATER_IF_DIFFERENT(mon); + RETURN_STRICTLY_GREATER_IF_DIFFERENT(day); + RETURN_STRICTLY_GREATER_IF_DIFFERENT(hour); + RETURN_STRICTLY_GREATER_IF_DIFFERENT(min); + RETURN_STRICTLY_GREATER_IF_DIFFERENT(sec); + + // all above are equal + return true; +} + +CHIP_ERROR IsCertificateValidAtIssuance(const mbedtls_x509_crt * candidateCertificate, const mbedtls_x509_crt * issuerCertificate) +{ + mbedtls_x509_time candidateNotBeforeTime = candidateCertificate->CHIP_CRYPTO_PAL_PRIVATE_X509(valid_from); + mbedtls_x509_time issuerNotBeforeTime = issuerCertificate->CHIP_CRYPTO_PAL_PRIVATE_X509(valid_from); + mbedtls_x509_time issuerNotAfterTime = issuerCertificate->CHIP_CRYPTO_PAL_PRIVATE_X509(valid_to); + + // check if candidateCertificate is issued at or after issuerCertificate's notBefore timestamp + VerifyOrReturnError(IsTimeGreaterThanEqual(&candidateNotBeforeTime, &issuerNotBeforeTime), CHIP_ERROR_CERT_EXPIRED); + + // check if candidateCertificate is issued at or before issuerCertificate's notAfter timestamp + VerifyOrReturnError(IsTimeGreaterThanEqual(&issuerNotAfterTime, &candidateNotBeforeTime), CHIP_ERROR_CERT_EXPIRED); + + return CHIP_NO_ERROR; +} + +int CallbackForCustomValidityCheck(void * data, mbedtls_x509_crt * crt, int depth, uint32_t * flags) +{ + mbedtls_x509_crt * leafCert = reinterpret_cast(data); + mbedtls_x509_crt * issuerCert = crt; + + // Ignore any time validy error performed by the standard mbedTLS code. + *flags &= ~(static_cast(MBEDTLS_X509_BADCERT_EXPIRED | MBEDTLS_X509_BADCERT_FUTURE)); + + // Verify that the leaf certificate has a notBefore time valid within the validity period of the issuerCertificate. + // Note that this callback is invoked for each certificate in the chain. + if (IsCertificateValidAtIssuance(leafCert, issuerCert) != CHIP_NO_ERROR) + { + return MBEDTLS_ERR_X509_INVALID_DATE; + } + + return 0; +} + +constexpr uint8_t sOID_AttributeType_CommonName[] = { 0x55, 0x04, 0x03 }; +constexpr uint8_t sOID_AttributeType_MatterVendorId[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x02, 0x01 }; +constexpr uint8_t sOID_AttributeType_MatterProductId[] = { 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xA2, 0x7C, 0x02, 0x02 }; +constexpr uint8_t sOID_SigAlgo_ECDSAWithSHA256[] = { 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02 }; +constexpr uint8_t sOID_Extension_BasicConstraints[] = { 0x55, 0x1D, 0x13 }; +constexpr uint8_t sOID_Extension_KeyUsage[] = { 0x55, 0x1D, 0x0F }; +constexpr uint8_t sOID_Extension_SubjectKeyIdentifier[] = { 0x55, 0x1D, 0x0E }; +constexpr uint8_t sOID_Extension_AuthorityKeyIdentifier[] = { 0x55, 0x1D, 0x23 }; + +/** + * Compares an mbedtls_asn1_buf structure (oidBuf) to a reference OID represented as uint8_t array (oid). + */ +#define OID_CMP(oid, oidBuf) \ + ((MBEDTLS_ASN1_OID == (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(tag)) && \ + (sizeof(oid) == (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(len)) && \ + (memcmp((oid), (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(p), (oidBuf).CHIP_CRYPTO_PAL_PRIVATE_X509(len)) == 0)) + +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + +} // anonymous namespace + +CHIP_ERROR VerifyAttestationCertificateFormat(const ByteSpan & cert, AttestationCertType certType) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + mbedtls_x509_crt mbed_cert; + unsigned char * p = nullptr; + const unsigned char * end = nullptr; + size_t len = 0; + bool extBasicPresent = false; + bool extKeyUsagePresent = false; + + VerifyOrReturnError(!cert.empty(), CHIP_ERROR_INVALID_ARGUMENT); + + mbedtls_x509_crt_init(&mbed_cert); + + result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(cert.data()), cert.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + // "version" value is 1 higher than the actual encoded value. + VerifyOrExit(mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(version) - 1 == 2, error = CHIP_ERROR_INTERNAL); + + // Verify signature algorithms is ECDSA with SHA256. + VerifyOrExit(OID_CMP(sOID_SigAlgo_ECDSAWithSHA256, mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(sig_oid)), + error = CHIP_ERROR_INTERNAL); + + // Verify public key presence and format. + { + Crypto::P256PublicKey pubkey; + SuccessOrExit(error = ExtractPubkeyFromX509Cert(cert, pubkey)); + } + + p = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); + end = p + mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + while (p < end) + { + mbedtls_x509_buf extOID = { 0, 0, nullptr }; + int extCritical = 0; + + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + /* Get extension ID */ + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + extOID.CHIP_CRYPTO_PAL_PRIVATE_X509(tag) = MBEDTLS_ASN1_OID; + extOID.CHIP_CRYPTO_PAL_PRIVATE_X509(len) = len; + extOID.CHIP_CRYPTO_PAL_PRIVATE_X509(p) = p; + p += len; + + /* Get optional critical */ + result = mbedtls_asn1_get_bool(&p, end, &extCritical); + VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_INTERNAL); + + /* Data should be octet string type */ + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + if (OID_CMP(sOID_Extension_BasicConstraints, extOID)) + { + int isCA = 0; + int pathLen = -1; + unsigned char * seqStart = p; + + VerifyOrExit(extCritical, error = CHIP_ERROR_INTERNAL); + extBasicPresent = true; + + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + if (len > 0) + { + result = mbedtls_asn1_get_bool(&p, end, &isCA); + VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_INTERNAL); + + if (p != seqStart + len) + { + result = mbedtls_asn1_get_int(&p, end, &pathLen); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + } + } + + if (certType == AttestationCertType::kDAC) + { + VerifyOrExit(!isCA && pathLen == -1, error = CHIP_ERROR_INTERNAL); + } + else if (certType == AttestationCertType::kPAI) + { + VerifyOrExit(isCA && pathLen == 0, error = CHIP_ERROR_INTERNAL); + } + else + { + VerifyOrExit(isCA && (pathLen == -1 || pathLen == 0 || pathLen == 1), error = CHIP_ERROR_INTERNAL); + } + } + else if (OID_CMP(sOID_Extension_KeyUsage, extOID)) + { + mbedtls_x509_bitstring bs = { 0, 0, nullptr }; + unsigned int keyUsage = 0; + + VerifyOrExit(extCritical, error = CHIP_ERROR_INTERNAL); + extKeyUsagePresent = true; + + result = mbedtls_asn1_get_bitstring(&p, p + len, &bs); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + for (size_t i = 0; i < bs.CHIP_CRYPTO_PAL_PRIVATE_X509(len) && i < sizeof(unsigned int); i++) + { + keyUsage |= static_cast(bs.CHIP_CRYPTO_PAL_PRIVATE_X509(p)[i]) << (8 * i); + } + + if (certType == AttestationCertType::kDAC) + { + // SHALL only have the digitalSignature bit set. + VerifyOrExit(keyUsage == MBEDTLS_X509_KU_DIGITAL_SIGNATURE, error = CHIP_ERROR_INTERNAL); + } + else + { + bool keyCertSignFlag = keyUsage & MBEDTLS_X509_KU_KEY_CERT_SIGN; + bool crlSignFlag = keyUsage & MBEDTLS_X509_KU_CRL_SIGN; + bool otherFlags = + keyUsage & ~(MBEDTLS_X509_KU_CRL_SIGN | MBEDTLS_X509_KU_KEY_CERT_SIGN | MBEDTLS_X509_KU_DIGITAL_SIGNATURE); + VerifyOrExit(keyCertSignFlag && crlSignFlag && !otherFlags, error = CHIP_ERROR_INTERNAL); + } + } + else + { + p += len; + } + } + + // Verify basic and key usage extensions are present. + VerifyOrExit(extBasicPresent && extKeyUsagePresent, error = CHIP_ERROR_INTERNAL); + + // Verify that SKID and AKID extensions are present. + { + uint8_t kidBuf[kSubjectKeyIdentifierLength]; + MutableByteSpan kid(kidBuf); + SuccessOrExit(error = ExtractSKIDFromX509Cert(cert, kid)); + if (certType == AttestationCertType::kDAC || certType == AttestationCertType::kPAI) + { + // Mandatory extension for DAC and PAI certs. + SuccessOrExit(error = ExtractAKIDFromX509Cert(cert, kid)); + } + } + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbed_cert); + +#else + (void) cert; + (void) certType; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} + +CHIP_ERROR ValidateCertificateChain(const uint8_t * rootCertificate, size_t rootCertificateLen, const uint8_t * caCertificate, + size_t caCertificateLen, const uint8_t * leafCertificate, size_t leafCertificateLen, + CertificateChainValidationResult & result) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + CHIP_ERROR error = CHIP_NO_ERROR; + mbedtls_x509_crt certChain; + mbedtls_x509_crt rootCert; + int mbedResult; + uint32_t flags = 0; + + result = CertificateChainValidationResult::kInternalFrameworkError; + + VerifyOrReturnError(rootCertificate != nullptr && rootCertificateLen != 0, + (result = CertificateChainValidationResult::kRootArgumentInvalid, CHIP_ERROR_INVALID_ARGUMENT)); + VerifyOrReturnError(leafCertificate != nullptr && leafCertificateLen != 0, + (result = CertificateChainValidationResult::kLeafArgumentInvalid, CHIP_ERROR_INVALID_ARGUMENT)); + + mbedtls_x509_crt_init(&certChain); + mbedtls_x509_crt_init(&rootCert); + + /* Start of chain */ + mbedResult = mbedtls_x509_crt_parse(&certChain, Uint8::to_const_uchar(leafCertificate), leafCertificateLen); + VerifyOrExit(mbedResult == 0, (result = CertificateChainValidationResult::kLeafFormatInvalid, error = CHIP_ERROR_INTERNAL)); + + /* Add the intermediate to the chain, if present */ + if (caCertificate != nullptr && caCertificateLen > 0) + { + mbedResult = mbedtls_x509_crt_parse(&certChain, Uint8::to_const_uchar(caCertificate), caCertificateLen); + VerifyOrExit(mbedResult == 0, (result = CertificateChainValidationResult::kICAFormatInvalid, error = CHIP_ERROR_INTERNAL)); + } + + /* Parse the root cert */ + mbedResult = mbedtls_x509_crt_parse(&rootCert, Uint8::to_const_uchar(rootCertificate), rootCertificateLen); + VerifyOrExit(mbedResult == 0, (result = CertificateChainValidationResult::kRootFormatInvalid, error = CHIP_ERROR_INTERNAL)); + + /* Verify the chain against the root */ + mbedResult = + mbedtls_x509_crt_verify(&certChain, &rootCert, nullptr, nullptr, &flags, CallbackForCustomValidityCheck, &certChain); + + switch (mbedResult) + { + case 0: + VerifyOrExit(flags == 0, (result = CertificateChainValidationResult::kInternalFrameworkError, error = CHIP_ERROR_INTERNAL)); + result = CertificateChainValidationResult::kSuccess; + break; + case MBEDTLS_ERR_X509_INVALID_DATE: + case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED: + result = CertificateChainValidationResult::kChainInvalid; + error = CHIP_ERROR_CERT_NOT_TRUSTED; + break; + default: + SuccessOrExit((result = CertificateChainValidationResult::kInternalFrameworkError, error = CHIP_ERROR_INTERNAL)); + } + +exit: + _log_mbedTLS_error(mbedResult); + mbedtls_x509_crt_free(&certChain); + mbedtls_x509_crt_free(&rootCert); + +#else + (void) rootCertificate; + (void) rootCertificateLen; + (void) caCertificate; + (void) caCertificateLen; + (void) leafCertificate; + (void) leafCertificateLen; + (void) result; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} + +CHIP_ERROR IsCertificateValidAtIssuance(const ByteSpan & candidateCertificate, const ByteSpan & issuerCertificate) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + CHIP_ERROR error = CHIP_NO_ERROR; + mbedtls_x509_crt mbedCandidateCertificate; + mbedtls_x509_crt mbedIssuerCertificate; + int result; + + VerifyOrReturnError(!candidateCertificate.empty() && !issuerCertificate.empty(), CHIP_ERROR_INVALID_ARGUMENT); + + mbedtls_x509_crt_init(&mbedCandidateCertificate); + mbedtls_x509_crt_init(&mbedIssuerCertificate); + + result = mbedtls_x509_crt_parse(&mbedCandidateCertificate, Uint8::to_const_uchar(candidateCertificate.data()), + candidateCertificate.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + result = + mbedtls_x509_crt_parse(&mbedIssuerCertificate, Uint8::to_const_uchar(issuerCertificate.data()), issuerCertificate.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + // Verify that the candidateCertificate has a notBefore time valid within the validity period of the issuerCertificate. + SuccessOrExit(error = IsCertificateValidAtIssuance(&mbedCandidateCertificate, &mbedIssuerCertificate)); + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbedCandidateCertificate); + mbedtls_x509_crt_free(&mbedIssuerCertificate); + +#else + (void) candidateCertificate; + (void) issuerCertificate; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} + +CHIP_ERROR IsCertificateValidAtCurrentTime(const ByteSpan & certificate) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + CHIP_ERROR error = CHIP_NO_ERROR; + mbedtls_x509_crt mbedCertificate; + int result; + + VerifyOrReturnError(!certificate.empty(), CHIP_ERROR_INVALID_ARGUMENT); + + mbedtls_x509_crt_init(&mbedCertificate); + + result = mbedtls_x509_crt_parse(&mbedCertificate, Uint8::to_const_uchar(certificate.data()), certificate.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + // check if certificate's notBefore timestamp is earlier than or equal to current time. + result = mbedtls_x509_time_is_past(&mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(valid_from)); + VerifyOrExit(result == 1, error = CHIP_ERROR_CERT_EXPIRED); + + // check if certificate's notAfter timestamp is later than current time. + result = mbedtls_x509_time_is_future(&mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(valid_to)); + VerifyOrExit(result == 1, error = CHIP_ERROR_CERT_EXPIRED); + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbedCertificate); + +#else + (void) certificate; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} + +CHIP_ERROR ExtractPubkeyFromX509Cert(const ByteSpan & certificate, Crypto::P256PublicKey & pubkey) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + CHIP_ERROR error = CHIP_NO_ERROR; + mbedtls_x509_crt mbed_cert; + mbedtls_uecc_keypair * keypair = nullptr; + size_t pubkey_size = 0; + + mbedtls_x509_crt_init(&mbed_cert); + + int result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + VerifyOrExit(mbedtls_pk_get_type(&(mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(pk))) == MBEDTLS_PK_ECKEY, + error = CHIP_ERROR_INVALID_ARGUMENT); + + keypair = mbedtls_pk_uecc(mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(pk)); + Uint8::to_uchar(pubkey)[0] = 0x04; // uncompressed type + memcpy(Uint8::to_uchar(pubkey) + 1, keypair->public_key, 2 * NUM_ECC_BYTES); + + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbed_cert); + +#else + (void) certificate; + (void) pubkey; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} + +namespace { + +CHIP_ERROR ExtractKIDFromX509Cert(bool extractSKID, const ByteSpan & certificate, MutableByteSpan & kid) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + CHIP_ERROR error = CHIP_ERROR_NOT_FOUND; + mbedtls_x509_crt mbed_cert; + unsigned char * p = nullptr; + const unsigned char * end = nullptr; + size_t len = 0; + + mbedtls_x509_crt_init(&mbed_cert); + + int result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + // TODO: The mbedTLS team is working on supporting SKID and AKID extensions processing. + // Once it is supported, this code should be updated. + + p = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p); + end = mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(p) + + mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(v3_ext).CHIP_CRYPTO_PAL_PRIVATE_X509(len); + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + + while (p < end) + { + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OID); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + + mbedtls_x509_buf extOID = { MBEDTLS_ASN1_OID, len, p }; + bool extractCurrentExtSKID = extractSKID && OID_CMP(sOID_Extension_SubjectKeyIdentifier, extOID); + bool extractCurrentExtAKID = !extractSKID && OID_CMP(sOID_Extension_AuthorityKeyIdentifier, extOID); + p += len; + + int is_critical = 0; + result = mbedtls_asn1_get_bool(&p, end, &is_critical); + VerifyOrExit(result == 0 || result == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG, error = CHIP_ERROR_WRONG_CERT_TYPE); + + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + + if (extractCurrentExtSKID || extractCurrentExtAKID) + { + if (extractCurrentExtSKID) + { + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + } + else + { + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + result = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC); + VerifyOrExit(result == 0, error = CHIP_ERROR_WRONG_CERT_TYPE); + // Other optional fields, authorityCertIssuer and authorityCertSerialNumber, + // will be skipped if present. + } + VerifyOrExit(len == kSubjectKeyIdentifierLength, error = CHIP_ERROR_WRONG_CERT_TYPE); + VerifyOrExit(len <= kid.size(), error = CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(kid.data(), p, len); + if (kid.size() > len) + { + kid.reduce_size(len); + } + ExitNow(error = CHIP_NO_ERROR); + break; + } + p += len; + } + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbed_cert); + +#else + (void) certificate; + (void) kid; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} + +} // namespace + +CHIP_ERROR ExtractSKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan & skid) +{ + return ExtractKIDFromX509Cert(true, certificate, skid); +} + +CHIP_ERROR ExtractAKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan & akid) +{ + return ExtractKIDFromX509Cert(false, certificate, akid); +} + +CHIP_ERROR ExtractVIDPIDFromX509Cert(const ByteSpan & certificate, AttestationCertVidPid & vidpid) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + CHIP_ERROR error = CHIP_NO_ERROR; + mbedtls_x509_crt mbed_cert; + mbedtls_asn1_named_data * dnIterator = nullptr; + AttestationCertVidPid vidpidFromCN; + + mbedtls_x509_crt_init(&mbed_cert); + + int result = mbedtls_x509_crt_parse(&mbed_cert, Uint8::to_const_uchar(certificate.data()), certificate.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + for (dnIterator = &mbed_cert.CHIP_CRYPTO_PAL_PRIVATE_X509(subject); dnIterator != nullptr; + dnIterator = dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(next)) + { + DNAttrType attrType = DNAttrType::kUnspecified; + if (OID_CMP(sOID_AttributeType_CommonName, dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(oid))) + { + attrType = DNAttrType::kCommonName; + } + else if (OID_CMP(sOID_AttributeType_MatterVendorId, dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(oid))) + { + attrType = DNAttrType::kMatterVID; + } + else if (OID_CMP(sOID_AttributeType_MatterProductId, dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(oid))) + { + attrType = DNAttrType::kMatterPID; + } + + size_t val_len = dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(val).CHIP_CRYPTO_PAL_PRIVATE_X509(len); + uint8_t * val_p = dnIterator->CHIP_CRYPTO_PAL_PRIVATE_X509(val).CHIP_CRYPTO_PAL_PRIVATE_X509(p); + error = ExtractVIDPIDFromAttributeString(attrType, ByteSpan(val_p, val_len), vidpid, vidpidFromCN); + SuccessOrExit(error); + } + + // If Matter Attributes were not found use values extracted from the CN Attribute, + // which might be uninitialized as well. + if (!vidpid.Initialized()) + { + vidpid = vidpidFromCN; + } + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbed_cert); + +#else + (void) certificate; + (void) vidpid; + CHIP_ERROR error = CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) + + return error; +} + +namespace { +#if defined(MBEDTLS_X509_CRT_PARSE_C) +CHIP_ERROR ExtractRawSubjectFromX509Cert(const ByteSpan & certificate, MutableByteSpan & subject) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + uint8_t * p = nullptr; + size_t len = 0; + mbedtls_x509_crt mbedCertificate; + + ReturnErrorCodeIf(certificate.empty(), CHIP_ERROR_INVALID_ARGUMENT); + + mbedtls_x509_crt_init(&mbedCertificate); + result = mbedtls_x509_crt_parse(&mbedCertificate, Uint8::to_const_uchar(certificate.data()), certificate.size()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + len = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(subject_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(len); + p = mbedCertificate.CHIP_CRYPTO_PAL_PRIVATE_X509(subject_raw).CHIP_CRYPTO_PAL_PRIVATE_X509(p); + + VerifyOrExit(len <= subject.size(), error = CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(subject.data(), p, len); + subject.reduce_size(len); + +exit: + _log_mbedTLS_error(result); + mbedtls_x509_crt_free(&mbedCertificate); + + return error; +} +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) +} // namespace + +CHIP_ERROR ReplaceCertIfResignedCertFound(const ByteSpan & referenceCertificate, const ByteSpan * candidateCertificates, + size_t candidateCertificatesCount, ByteSpan & outCertificate) +{ +#if defined(MBEDTLS_X509_CRT_PARSE_C) + constexpr size_t kMaxCertificateSubjectLength = 150; + uint8_t referenceSubjectBuf[kMaxCertificateSubjectLength]; + uint8_t referenceSKIDBuf[kSubjectKeyIdentifierLength]; + MutableByteSpan referenceSubject(referenceSubjectBuf); + MutableByteSpan referenceSKID(referenceSKIDBuf); + + outCertificate = referenceCertificate; + + ReturnErrorCodeIf(candidateCertificates == nullptr || candidateCertificatesCount == 0, CHIP_NO_ERROR); + + ReturnErrorOnFailure(ExtractRawSubjectFromX509Cert(referenceCertificate, referenceSubject)); + ReturnErrorOnFailure(ExtractSKIDFromX509Cert(referenceCertificate, referenceSKID)); + + for (size_t i = 0; i < candidateCertificatesCount; i++) + { + const ByteSpan candidateCertificate = candidateCertificates[i]; + uint8_t candidateSubjectBuf[kMaxCertificateSubjectLength]; + uint8_t candidateSKIDBuf[kSubjectKeyIdentifierLength]; + MutableByteSpan candidateSubject(candidateSubjectBuf); + MutableByteSpan candidateSKID(candidateSKIDBuf); + + ReturnErrorOnFailure(ExtractRawSubjectFromX509Cert(candidateCertificate, candidateSubject)); + ReturnErrorOnFailure(ExtractSKIDFromX509Cert(candidateCertificate, candidateSKID)); + + if (referenceSKID.data_equal(candidateSKID) && referenceSubject.data_equal(candidateSubject)) + { + outCertificate = candidateCertificate; + return CHIP_NO_ERROR; + } + } + + return CHIP_NO_ERROR; +#else + (void) referenceCertificate; + (void) candidateCertificates; + (void) candidateCertificatesCount; + (void) outCertificate; + return CHIP_ERROR_NOT_IMPLEMENTED; +#endif // defined(MBEDTLS_X509_CRT_PARSE_C) +} + +} // namespace Crypto +} // namespace chip diff --git a/src/platform/silabs/SiWx917/wifi_args.gni b/src/platform/silabs/SiWx917/wifi_args.gni index 44f07f37985d88..d63fb5e1d89a2c 100644 --- a/src/platform/silabs/SiWx917/wifi_args.gni +++ b/src/platform/silabs/SiWx917/wifi_args.gni @@ -25,7 +25,7 @@ arm_platform_config = "${efr32_sdk_build_root}/efr32_arm.gni" mbedtls_target = "${efr32_sdk_build_root}:efr32_sdk" -chip_crypto = "tinycrypt" +chip_crypto = "platform" # Transitional CommissionableDataProvider not used anymore # examples/platform/efr32/EFR32DeviceDataProvider is now used. From 38c58757a9c80bfd7837ce8d5a8d3b4eb006c3f8 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Mon, 13 Feb 2023 16:04:52 +0000 Subject: [PATCH 2/6] Restyled by gn --- src/platform/silabs/SiWx917/BUILD.gn | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/platform/silabs/SiWx917/BUILD.gn b/src/platform/silabs/SiWx917/BUILD.gn index 8f5bc8ff90b1fe..48680f4630e1fe 100644 --- a/src/platform/silabs/SiWx917/BUILD.gn +++ b/src/platform/silabs/SiWx917/BUILD.gn @@ -70,19 +70,18 @@ static_library("SiWx917") { public_deps = [ "${chip_root}/src/platform:platform_base" ] - # Add platform crypto implementation + # Add platform crypto implementation if (chip_crypto == "platform") { sources += [ - "CHIPCryptoPALTinyCrypt.cpp", "CHIPCryptoPAL.h", - + "CHIPCryptoPALTinyCrypt.cpp", ] public_deps += [ "${chip_root}/src/crypto", "${mbedtls_root}:mbedtls", ] } - + if (chip_enable_wifi) { sources += [ "${silabs_platform_dir}/ConnectivityManagerImpl_WIFI.cpp", From d40a2c3b42575fe0664833212d44100d22f42b81 Mon Sep 17 00:00:00 2001 From: Chirag Bansal Date: Tue, 21 Feb 2023 01:35:54 +0530 Subject: [PATCH 3/6] removed chipcryptopal.h and modified Aes128KeyHandle which was missed --- src/platform/silabs/SiWx917/BUILD.gn | 1 - src/platform/silabs/SiWx917/CHIPCryptoPAL.h | 1703 ----------------- .../silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp | 30 +- 3 files changed, 7 insertions(+), 1727 deletions(-) delete mode 100644 src/platform/silabs/SiWx917/CHIPCryptoPAL.h diff --git a/src/platform/silabs/SiWx917/BUILD.gn b/src/platform/silabs/SiWx917/BUILD.gn index 48680f4630e1fe..7d8459adb384df 100644 --- a/src/platform/silabs/SiWx917/BUILD.gn +++ b/src/platform/silabs/SiWx917/BUILD.gn @@ -73,7 +73,6 @@ static_library("SiWx917") { # Add platform crypto implementation if (chip_crypto == "platform") { sources += [ - "CHIPCryptoPAL.h", "CHIPCryptoPALTinyCrypt.cpp", ] public_deps += [ diff --git a/src/platform/silabs/SiWx917/CHIPCryptoPAL.h b/src/platform/silabs/SiWx917/CHIPCryptoPAL.h deleted file mode 100644 index 7a5cb1f191d706..00000000000000 --- a/src/platform/silabs/SiWx917/CHIPCryptoPAL.h +++ /dev/null @@ -1,1703 +0,0 @@ -/* - * - * Copyright (c) 2020-2022 Project CHIP Authors - * - * 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 - * Header that exposes the platform agnostic CHIP crypto primitives - */ - -#pragma once - -#if CHIP_HAVE_CONFIG_H -#include -#endif // CHIP_HAVE_CONFIG_H - -#include - -#include -#include -#include -#include -#include - -#include -#include - -namespace chip { -namespace Crypto { - -constexpr size_t kMax_x509_Certificate_Length = 600; - -constexpr size_t kP256_FE_Length = 32; -constexpr size_t kP256_ECDSA_Signature_Length_Raw = (2 * kP256_FE_Length); -constexpr size_t kP256_Point_Length = (2 * kP256_FE_Length + 1); -constexpr size_t kSHA256_Hash_Length = 32; -constexpr size_t kSHA1_Hash_Length = 20; -constexpr size_t kSubjectKeyIdentifierLength = kSHA1_Hash_Length; -constexpr size_t kAuthorityKeyIdentifierLength = kSHA1_Hash_Length; - -constexpr size_t CHIP_CRYPTO_GROUP_SIZE_BYTES = kP256_FE_Length; -constexpr size_t CHIP_CRYPTO_PUBLIC_KEY_SIZE_BYTES = kP256_Point_Length; - -constexpr size_t CHIP_CRYPTO_AEAD_MIC_LENGTH_BYTES = 16; -constexpr size_t CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES = 16; - -constexpr size_t kMax_ECDH_Secret_Length = kP256_FE_Length; -constexpr size_t kMax_ECDSA_Signature_Length = kP256_ECDSA_Signature_Length_Raw; -constexpr size_t kMAX_FE_Length = kP256_FE_Length; -constexpr size_t kMAX_Point_Length = kP256_Point_Length; -constexpr size_t kMAX_Hash_Length = kSHA256_Hash_Length; - -// Max CSR length should be relatively small since it's a single P256 key and -// no metadata is expected to be honored by the CA. -constexpr size_t kMAX_CSR_Length = 255; - -constexpr size_t CHIP_CRYPTO_HASH_LEN_BYTES = kSHA256_Hash_Length; - -constexpr size_t kSpake2p_Min_PBKDF_Salt_Length = 16; -constexpr size_t kSpake2p_Max_PBKDF_Salt_Length = 32; -constexpr uint32_t kSpake2p_Min_PBKDF_Iterations = 1000; -constexpr uint32_t kSpake2p_Max_PBKDF_Iterations = 100000; - -constexpr size_t kP256_PrivateKey_Length = CHIP_CRYPTO_GROUP_SIZE_BYTES; -constexpr size_t kP256_PublicKey_Length = CHIP_CRYPTO_PUBLIC_KEY_SIZE_BYTES; - -constexpr size_t kAES_CCM128_Key_Length = 128u / 8u; -constexpr size_t kAES_CCM128_Block_Length = kAES_CCM128_Key_Length; -constexpr size_t kAES_CCM128_Nonce_Length = 13; -constexpr size_t kAES_CCM128_Tag_Length = 16; - -/* These sizes are hardcoded here to remove header dependency on underlying crypto library - * in a public interface file. The validity of these sizes is verified by static_assert in - * the implementation files. - */ -constexpr size_t kMAX_Spake2p_Context_Size = 1024; -constexpr size_t kMAX_P256Keypair_Context_Size = 512; - -constexpr size_t kEmitDerIntegerWithoutTagOverhead = 1; // 1 sign stuffer -constexpr size_t kEmitDerIntegerOverhead = 3; // Tag + Length byte + 1 sign stuffer - -constexpr size_t kMAX_Hash_SHA256_Context_Size = CHIP_CONFIG_SHA256_CONTEXT_SIZE; - -constexpr size_t kSpake2p_WS_Length = kP256_FE_Length + 8; -constexpr size_t kSpake2p_VerifierSerialized_Length = kP256_FE_Length + kP256_Point_Length; - -constexpr char kVIDPrefixForCNEncoding[] = "Mvid:"; -constexpr char kPIDPrefixForCNEncoding[] = "Mpid:"; -constexpr size_t kVIDandPIDHexLength = sizeof(uint16_t) * 2; -constexpr size_t kMax_CommonNameAttr_Length = 64; - -/* - * Overhead to encode a raw ECDSA signature in X9.62 format in ASN.1 DER - * - * Ecdsa-Sig-Value ::= SEQUENCE { - * r INTEGER, - * s INTEGER - * } - * - * --> SEQUENCE, universal constructed tag (0x30), length over 2 bytes, up to 255 (to support future larger sizes up to 512 bits) - * -> SEQ_OVERHEAD = 3 bytes - * --> INTEGER, universal primitive tag (0x02), length over 1 byte, one extra byte worst case - * over max for 0x00 when MSB is set. - * -> INT_OVERHEAD = 3 bytes - * - * There is 1 sequence of 2 integers. Overhead is SEQ_OVERHEAD + (2 * INT_OVERHEAD) = 3 + (2 * 3) = 9. - */ -constexpr size_t kMax_ECDSA_X9Dot62_Asn1_Overhead = 9; -constexpr size_t kMax_ECDSA_Signature_Length_Der = kMax_ECDSA_Signature_Length + kMax_ECDSA_X9Dot62_Asn1_Overhead; - -static_assert(kMax_ECDH_Secret_Length >= kP256_FE_Length, "ECDH shared secret is too short for crypto suite"); -static_assert(kMax_ECDSA_Signature_Length >= kP256_ECDSA_Signature_Length_Raw, - "ECDSA signature buffer length is too short for crypto suite"); - -constexpr size_t kCompressedFabricIdentifierSize = 8; - -/** - * Spake2+ parameters for P256 - * Defined in https://www.ietf.org/id/draft-bar-cfrg-spake2plus-01.html#name-ciphersuites - */ -const uint8_t spake2p_M_p256[65] = { - 0x04, 0x88, 0x6e, 0x2f, 0x97, 0xac, 0xe4, 0x6e, 0x55, 0xba, 0x9d, 0xd7, 0x24, 0x25, 0x79, 0xf2, 0x99, - 0x3b, 0x64, 0xe1, 0x6e, 0xf3, 0xdc, 0xab, 0x95, 0xaf, 0xd4, 0x97, 0x33, 0x3d, 0x8f, 0xa1, 0x2f, 0x5f, - 0xf3, 0x55, 0x16, 0x3e, 0x43, 0xce, 0x22, 0x4e, 0x0b, 0x0e, 0x65, 0xff, 0x02, 0xac, 0x8e, 0x5c, 0x7b, - 0xe0, 0x94, 0x19, 0xc7, 0x85, 0xe0, 0xca, 0x54, 0x7d, 0x55, 0xa1, 0x2e, 0x2d, 0x20, -}; -const uint8_t spake2p_N_p256[65] = { - 0x04, 0xd8, 0xbb, 0xd6, 0xc6, 0x39, 0xc6, 0x29, 0x37, 0xb0, 0x4d, 0x99, 0x7f, 0x38, 0xc3, 0x77, 0x07, - 0x19, 0xc6, 0x29, 0xd7, 0x01, 0x4d, 0x49, 0xa2, 0x4b, 0x4f, 0x98, 0xba, 0xa1, 0x29, 0x2b, 0x49, 0x07, - 0xd6, 0x0a, 0xa6, 0xbf, 0xad, 0xe4, 0x50, 0x08, 0xa6, 0x36, 0x33, 0x7f, 0x51, 0x68, 0xc6, 0x4d, 0x9b, - 0xd3, 0x60, 0x34, 0x80, 0x8c, 0xd5, 0x64, 0x49, 0x0b, 0x1e, 0x65, 0x6e, 0xdb, 0xe7, -}; - -/** - * Spake2+ state machine to ensure proper execution of the protocol. - */ -enum class CHIP_SPAKE2P_STATE : uint8_t -{ - PREINIT = 0, // Before any initialization - INIT, // First initialization - STARTED, // Prover & Verifier starts - R1, // Round one complete - R2, // Round two complete - KC, // Key confirmation complete -}; - -/** - * Spake2+ role. - */ -enum class CHIP_SPAKE2P_ROLE : uint8_t -{ - VERIFIER = 0, // Accessory - PROVER = 1, // Commissioner -}; - -enum class SupportedECPKeyTypes : uint8_t -{ - ECP256R1 = 0, -}; - -enum class ECPKeyTarget : uint8_t -{ - ECDH = 0, - ECDSA = 1, -}; - -/** @brief Safely clears the first `len` bytes of memory area `buf`. - * @param buf Pointer to a memory buffer holding secret data that must be cleared. - * @param len Specifies secret data size in bytes. - **/ -void ClearSecretData(uint8_t * buf, size_t len); - -/** - * Helper for clearing a C array which auto-deduces the size. - */ -template -void ClearSecretData(uint8_t (&buf)[N]) -{ - ClearSecretData(buf, N); -} - -/** - * @brief Constant-time buffer comparison - * - * This function implements constant time memcmp. It's good practice - * to use constant time functions for cryptographic functions. - * - * @param a Pointer to first buffer - * @param b Pointer to Second buffer - * @param n Number of bytes to compare - * @return true if `n` first bytes of both buffers are equal, false otherwise - */ -bool IsBufferContentEqualConstantTime(const void * a, const void * b, size_t n); - -template -class ECPKey -{ -public: - virtual ~ECPKey() {} - virtual SupportedECPKeyTypes Type() const = 0; - virtual size_t Length() const = 0; - virtual bool IsUncompressed() const = 0; - virtual operator const uint8_t *() const = 0; - virtual operator uint8_t *() = 0; - virtual const uint8_t * ConstBytes() const = 0; - virtual uint8_t * Bytes() = 0; - - virtual bool Matches(const ECPKey & other) const - { - return (this->Length() == other.Length()) && - IsBufferContentEqualConstantTime(this->ConstBytes(), other.ConstBytes(), this->Length()); - } - - virtual CHIP_ERROR ECDSA_validate_msg_signature(const uint8_t * msg, const size_t msg_length, const Sig & signature) const = 0; - virtual CHIP_ERROR ECDSA_validate_hash_signature(const uint8_t * hash, const size_t hash_length, - const Sig & signature) const = 0; -}; - -/** - * @brief Helper class for holding sensitive data that should be erased from memory after use. - * - * The sensitive data buffer is a variable-length, fixed-capacity buffer class that securely erases - * the contents of a buffer when the buffer is destroyed. - */ -template -class SensitiveDataBuffer -{ -public: - ~SensitiveDataBuffer() - { - // Sanitize after use - ClearSecretData(mBytes); - } - - SensitiveDataBuffer & operator=(const SensitiveDataBuffer & other) - { - // Guard self assignment - if (this == &other) - return *this; - - ClearSecretData(mBytes); - SetLength(other.Length()); - ::memcpy(Bytes(), other.ConstBytes(), other.Length()); - return *this; - } - - /** - * @brief Set current length of the buffer - * @return Error if new length is exceeds capacity of the buffer - */ - CHIP_ERROR SetLength(size_t length) - { - VerifyOrReturnError(length <= kCapacity, CHIP_ERROR_INVALID_ARGUMENT); - mLength = length; - return CHIP_NO_ERROR; - } - - /** - * @brief Returns current length of the buffer - */ - size_t Length() const { return mLength; } - - /** - * @brief Returns non-const pointer to start of the underlying buffer - */ - uint8_t * Bytes() { return &mBytes[0]; } - - /** - * @brief Returns const pointer to start of the underlying buffer - */ - const uint8_t * ConstBytes() const { return &mBytes[0]; } - - /** - * @brief Constructs span from the underlying buffer - */ - ByteSpan Span() const { return ByteSpan(ConstBytes(), Length()); } - - /** - * @brief Returns capacity of the buffer - */ - static constexpr size_t Capacity() { return kCapacity; } - -private: - uint8_t mBytes[kCapacity]; - size_t mLength = 0; -}; - -/** - * @brief Helper class for holding fixed-sized sensitive data that should be erased from memory after use. - * - * The sensitive data buffer is a fixed-length, fixed-capacity buffer class that securely erases - * the contents of a buffer when the buffer is destroyed. - */ -template -class SensitiveDataFixedBuffer -{ -public: - SensitiveDataFixedBuffer() = default; - - constexpr explicit SensitiveDataFixedBuffer(const uint8_t (&rawValue)[kCapacity]) - { - memcpy(&mBytes[0], &rawValue[0], kCapacity); - } - - constexpr explicit SensitiveDataFixedBuffer(const FixedByteSpan & value) - { - memcpy(&mBytes[0], value.data(), kCapacity); - } - - ~SensitiveDataFixedBuffer() - { - // Sanitize after use - ClearSecretData(mBytes); - } - - /** - * @brief Returns fixed length of the buffer - */ - constexpr size_t Length() const { return kCapacity; } - - /** - * @brief Returns non-const pointer to start of the underlying buffer - */ - uint8_t * Bytes() { return &mBytes[0]; } - - /** - * @brief Returns const pointer to start of the underlying buffer - */ - const uint8_t * ConstBytes() const { return &mBytes[0]; } - - /** - * @brief Constructs fixed span from the underlying buffer - */ - FixedByteSpan Span() const { return FixedByteSpan(mBytes); } - -private: - uint8_t mBytes[kCapacity]; -}; - -using P256ECDSASignature = SensitiveDataBuffer; -using P256ECDHDerivedSecret = SensitiveDataBuffer; - -using IdentityProtectionKey = SensitiveDataFixedBuffer; -using IdentityProtectionKeySpan = FixedByteSpan; - -class P256PublicKey : public ECPKey -{ -public: - P256PublicKey() {} - - template - constexpr P256PublicKey(const uint8_t (&raw_value)[N]) - { - static_assert(N == kP256_PublicKey_Length, "Can only array-initialize from proper bounds"); - memcpy(&bytes[0], &raw_value[0], N); - } - - template - constexpr P256PublicKey(const FixedByteSpan & value) - { - static_assert(N == kP256_PublicKey_Length, "Can only initialize from proper sized byte span"); - memcpy(&bytes[0], value.data(), N); - } - - template - P256PublicKey & operator=(const FixedByteSpan & value) - { - static_assert(N == kP256_PublicKey_Length, "Can only initialize from proper sized byte span"); - memcpy(&bytes[0], value.data(), N); - return *this; - } - - SupportedECPKeyTypes Type() const override { return SupportedECPKeyTypes::ECP256R1; } - size_t Length() const override { return kP256_PublicKey_Length; } - operator uint8_t *() override { return bytes; } - operator const uint8_t *() const override { return bytes; } - const uint8_t * ConstBytes() const override { return &bytes[0]; } - uint8_t * Bytes() override { return &bytes[0]; } - bool IsUncompressed() const override - { - constexpr uint8_t kUncompressedPointMarker = 0x04; - // SEC1 definition of an uncompressed point is (0x04 || X || Y) where X and Y are - // raw zero-padded big-endian large integers of the group size. - return (Length() == ((kP256_FE_Length * 2) + 1)) && (ConstBytes()[0] == kUncompressedPointMarker); - } - - CHIP_ERROR ECDSA_validate_msg_signature(const uint8_t * msg, size_t msg_length, - const P256ECDSASignature & signature) const override; - CHIP_ERROR ECDSA_validate_hash_signature(const uint8_t * hash, size_t hash_length, - const P256ECDSASignature & signature) const override; - -private: - uint8_t bytes[kP256_PublicKey_Length]; -}; - -template -class ECPKeypair -{ -public: - virtual ~ECPKeypair() {} - - /** @brief Generate a new Certificate Signing Request (CSR). - * @param csr Newly generated CSR in DER format - * @param csr_length The caller provides the length of input buffer (csr). The function returns the actual length of generated - *CSR. - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR NewCertificateSigningRequest(uint8_t * csr, size_t & csr_length) const = 0; - - /** - * @brief A function to sign a msg using ECDSA - * @param msg Message that needs to be signed - * @param msg_length Length of message - * @param out_signature Buffer that will hold the output signature. The signature consists of: 2 EC elements (r and s), - * in raw point form (see SEC1). - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR ECDSA_sign_msg(const uint8_t * msg, size_t msg_length, Sig & out_signature) const = 0; - - /** @brief A function to derive a shared secret using ECDH - * @param remote_public_key Public key of remote peer with which we are trying to establish secure channel. remote_public_key is - * ASN.1 DER encoded as padded big-endian field elements as described in SEC 1: Elliptic Curve Cryptography - * [https://www.secg.org/sec1-v2.pdf] - * @param out_secret Buffer to write out secret into. This is a byte array representing the x coordinate of the shared secret. - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR ECDH_derive_secret(const PK & remote_public_key, Secret & out_secret) const = 0; - - virtual const PK & Pubkey() const = 0; -}; - -struct alignas(size_t) P256KeypairContext -{ - uint8_t mBytes[kMAX_P256Keypair_Context_Size]; -}; - -using P256SerializedKeypair = SensitiveDataBuffer; - -class P256KeypairBase : public ECPKeypair -{ -public: - /** - * @brief Initialize the keypair. - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR Initialize(ECPKeyTarget key_target) = 0; - - /** - * @brief Serialize the keypair. - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR Serialize(P256SerializedKeypair & output) const = 0; - - /** - * @brief Deserialize the keypair. - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR Deserialize(P256SerializedKeypair & input) = 0; -}; - -class P256Keypair : public P256KeypairBase -{ -public: - P256Keypair() {} - ~P256Keypair() override; - - /** - * @brief Initialize the keypair. - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - CHIP_ERROR Initialize(ECPKeyTarget key_target) override; - - /** - * @brief Serialize the keypair. - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - CHIP_ERROR Serialize(P256SerializedKeypair & output) const override; - - /** - * @brief Deserialize the keypair. - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - CHIP_ERROR Deserialize(P256SerializedKeypair & input) override; - - /** - * @brief Generate a new Certificate Signing Request (CSR). - * @param csr Newly generated CSR in DER format - * @param csr_length The caller provides the length of input buffer (csr). The function returns the actual length of generated - *CSR. - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - CHIP_ERROR NewCertificateSigningRequest(uint8_t * csr, size_t & csr_length) const override; - - /** - * @brief A function to sign a msg using ECDSA - * @param msg Message that needs to be signed - * @param msg_length Length of message - * @param out_signature Buffer that will hold the output signature. The signature consists of: 2 EC elements (r and s), - * in raw point form (see SEC1). - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - CHIP_ERROR ECDSA_sign_msg(const uint8_t * msg, size_t msg_length, P256ECDSASignature & out_signature) const override; - - /** - * @brief A function to derive a shared secret using ECDH - * - * This implements the CHIP_Crypto_ECDH(PrivateKey myPrivateKey, PublicKey theirPublicKey) cryptographic primitive - * from the specification, using this class's private key from `mKeypair` as `myPrivateKey` and the remote - * public key from `remote_public_key` as `theirPublicKey`. - * - * @param remote_public_key Public key of remote peer with which we are trying to establish secure channel. remote_public_key is - * ASN.1 DER encoded as padded big-endian field elements as described in SEC 1: Elliptic Curve Cryptography - * [https://www.secg.org/sec1-v2.pdf] - * @param out_secret Buffer to write out secret into. This is a byte array representing the x coordinate of the shared secret. - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - CHIP_ERROR ECDH_derive_secret(const P256PublicKey & remote_public_key, P256ECDHDerivedSecret & out_secret) const override; - - /** @brief Return public key for the keypair. - **/ - const P256PublicKey & Pubkey() const override { return mPublicKey; } - - /** Release resources associated with this key pair */ - void Clear(); - -protected: - P256PublicKey mPublicKey; - mutable P256KeypairContext mKeypair; - bool mInitialized = false; -}; - -/** - * @brief Convert a raw ECDSA signature to ASN.1 signature (per X9.62) as used by TLS libraries. - * - * Errors are: - * - CHIP_ERROR_INVALID_ARGUMENT on any argument being invalid (e.g. nullptr), wrong sizes, - * wrong or unsupported format, - * - CHIP_ERROR_BUFFER_TOO_SMALL on running out of space at runtime. - * - CHIP_ERROR_INTERNAL on any unexpected processing error. - * - * @param[in] fe_length_bytes Field Element length in bytes (e.g. 32 for P256 curve) - * @param[in] raw_sig Raw signature of concatenated - * @param[out] out_asn1_sig ASN.1 DER signature format output buffer. Size must have space for at least - * kMax_ECDSA_X9Dot62_Asn1_Overhead. On CHIP_NO_ERROR, the out_asn1_sig buffer will be re-assigned - * to have the correct size based on variable-length output. - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - */ -CHIP_ERROR EcdsaRawSignatureToAsn1(size_t fe_length_bytes, const ByteSpan & raw_sig, MutableByteSpan & out_asn1_sig); - -/** - * @brief Convert an ASN.1 DER signature (per X9.62) as used by TLS libraries to SEC1 raw format - * - * Errors are: - * - CHIP_ERROR_INVALID_ARGUMENT on any argument being invalid (e.g. nullptr), wrong sizes, - * wrong or unsupported format, - * - CHIP_ERROR_BUFFER_TOO_SMALL on running out of space at runtime. - * - CHIP_ERROR_INTERNAL on any unexpected processing error. - * - * @param[in] fe_length_bytes Field Element length in bytes (e.g. 32 for P256 curve) - * @param[in] asn1_sig ASN.1 DER signature input - * @param[out] out_raw_sig Raw signature of concatenated format output buffer. Size must be at - * least >= `2 * fe_length_bytes`. On CHIP_NO_ERROR, the out_raw_sig buffer will be re-assigned - * to have the correct size (2 * fe_length_bytes). - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - */ -CHIP_ERROR EcdsaAsn1SignatureToRaw(size_t fe_length_bytes, const ByteSpan & asn1_sig, MutableByteSpan & out_raw_sig); - -/** - * @brief Utility to emit a DER-encoded INTEGER given a raw unsigned large integer - * in big-endian order. The `out_der_integer` span is updated to reflect the final - * variable length, including tag and length, and must have at least `kEmitDerIntegerOverhead` - * extra space in addition to the `raw_integer.size()`. - * @param[in] raw_integer Bytes of a large unsigned integer in big-endian, possibly including leading zeroes - * @param[out] out_der_integer Buffer to receive the DER-encoded integer - * @return Returns CHIP_ERROR_INVALID_ARGUMENT or CHIP_ERROR_BUFFER_TOO_SMALL on error, CHIP_NO_ERROR otherwise. - */ -CHIP_ERROR ConvertIntegerRawToDer(const ByteSpan & raw_integer, MutableByteSpan & out_der_integer); - -/** - * @brief Utility to emit a DER-encoded INTEGER given a raw unsigned large integer - * in big-endian order. The `out_der_integer` span is updated to reflect the final - * variable length, excluding tag and length, and must have at least `kEmitDerIntegerWithoutTagOverhead` - * extra space in addition to the `raw_integer.size()`. - * @param[in] raw_integer Bytes of a large unsigned integer in big-endian, possibly including leading zeroes - * @param[out] out_der_integer Buffer to receive the DER-encoded integer - * @return Returns CHIP_ERROR_INVALID_ARGUMENT or CHIP_ERROR_BUFFER_TOO_SMALL on error, CHIP_NO_ERROR otherwise. - */ -CHIP_ERROR ConvertIntegerRawToDerWithoutTag(const ByteSpan & raw_integer, MutableByteSpan & out_der_integer); - -/** - * @brief A function that implements AES-CCM encryption - * - * This implements the CHIP_Crypto_AEAD_GenerateEncrypt() cryptographic primitive - * from the specification. For an empty plaintext, the user of the API can provide - * an empty string, or a nullptr, and provide plaintext_length as 0. The output buffer, - * ciphertext can also be an empty string, or a nullptr for this case. - * - * @param plaintext Plaintext to encrypt - * @param plaintext_length Length of plain_text - * @param aad Additional authentication data - * @param aad_length Length of additional authentication data - * @param key Encryption key - * @param key_length Length of encryption key (in bytes) - * @param nonce Encryption nonce - * @param nonce_length Length of encryption nonce - * @param ciphertext Buffer to write ciphertext into. Caller must ensure this is large enough to hold the ciphertext - * @param tag Buffer to write tag into. Caller must ensure this is large enough to hold the tag - * @param tag_length Expected length of tag - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - * */ -CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, const uint8_t * aad, size_t aad_length, - const uint8_t * key, size_t key_length, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, - uint8_t * tag, size_t tag_length); - -/** - * @brief A function that implements AES-CCM decryption - * - * This implements the CHIP_Crypto_AEAD_DecryptVerify() cryptographic primitive - * from the specification. For an empty ciphertext, the user of the API can provide - * an empty string, or a nullptr, and provide ciphertext_length as 0. The output buffer, - * plaintext can also be an empty string, or a nullptr for this case. - * - * @param ciphertext Ciphertext to decrypt - * @param ciphertext_length Length of ciphertext - * @param aad Additional authentical data. - * @param aad_length Length of additional authentication data - * @param tag Tag to use to decrypt - * @param tag_length Length of tag - * @param key Decryption key - * @param key_length Length of Decryption key (in bytes) - * @param nonce Encryption nonce - * @param nonce_length Length of encryption nonce - * @param plaintext Buffer to write plaintext into - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ -CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_length, const uint8_t * aad, size_t aad_length, - const uint8_t * tag, size_t tag_length, const uint8_t * key, size_t key_length, const uint8_t * nonce, - size_t nonce_length, uint8_t * plaintext); - -/** - * @brief A function that implements AES-CTR encryption/decryption - * - * This implements the AES-CTR-Encrypt/Decrypt() cryptographic primitives per sections - * 3.7.1 and 3.7.2 of the specification. For an empty input, the user of the API - * can provide an empty string, or a nullptr, and provide input as 0. - * The output buffer can also be an empty string, or a nullptr for this case. - * - * @param input Input text to encrypt/decrypt - * @param input_length Length of ciphertext - * @param key Decryption key - * @param key_length Length of Decryption key (in bytes) - * @param nonce Encryption nonce - * @param nonce_length Length of encryption nonce - * @param output Buffer to write output into - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ -CHIP_ERROR AES_CTR_crypt(const uint8_t * input, size_t input_length, const uint8_t * key, size_t key_length, const uint8_t * nonce, - size_t nonce_length, uint8_t * output); - -/** - * @brief Generate a PKCS#10 CSR, usable for Matter, from a P256Keypair. - * - * This uses first principles ASN.1 encoding to avoid relying on the CHIPCryptoPAL backend - * itself, other than to provide an implementation of a P256Keypair * that supports - * at least `::Pubkey()` and `::ECDSA_sign_msg`. This allows using it with - * OS/Platform-bridged private key handling, without requiring a specific - * implementation of other bits like ASN.1. - * - * The CSR will have subject OU set to `CSA`. This is needed since omiting - * subject altogether often trips CSR parsing code. The profile at the CA can - * be configured to ignore CSR requested subject. - * - * @param keypair The key pair for which a CSR should be generated. Must not be null. - * @param csr_span Span to hold the resulting CSR. Must be at least kMAX_CSR_Length. Otherwise returns CHIP_ERROR_BUFFER_TOO_SMALL. - * It will get resized to actual size needed on success. - - * @return Returns a CHIP_ERROR from P256Keypair or ASN.1 backend on error, CHIP_NO_ERROR otherwise - **/ -CHIP_ERROR GenerateCertificateSigningRequest(const P256Keypair * keypair, MutableByteSpan & csr_span); - -/** - * @brief Common code to validate ASN.1 format/size of a CSR, used by VerifyCertificateSigningRequest. - * - * Ensures it's not obviously malformed and doesn't have trailing garbage. - * - * @param csr CSR in DER format - * @param csr_length The length of the CSR buffer - * @return CHIP_ERROR_UNSUPPORTED_CERT_FORMAT on invalid format, CHIP_NO_ERROR otherwise. - */ -CHIP_ERROR VerifyCertificateSigningRequestFormat(const uint8_t * csr, size_t csr_length); - -/** - * @brief Verify the Certificate Signing Request (CSR). If successfully verified, it outputs the public key from the CSR. - * - * The CSR is valid if the format is correct, the signature validates with the embedded public - * key, and there is no trailing garbage data. - * - * @param csr CSR in DER format - * @param csr_length The length of the CSR - * @param pubkey The public key from the verified CSR - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ -CHIP_ERROR VerifyCertificateSigningRequest(const uint8_t * csr, size_t csr_length, P256PublicKey & pubkey); - -/** - * @brief A function that implements SHA-256 hash - * - * This implements the CHIP_Crypto_Hash() cryptographic primitive - * in the the specification. - * - * @param data The data to hash - * @param data_length Length of the data - * @param out_buffer Pointer to buffer to write output into - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - -CHIP_ERROR Hash_SHA256(const uint8_t * data, size_t data_length, uint8_t * out_buffer); - -/** - * @brief A function that implements SHA-1 hash - * @param data The data to hash - * @param data_length Length of the data - * @param out_buffer Pointer to buffer to write output into - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - -CHIP_ERROR Hash_SHA1(const uint8_t * data, size_t data_length, uint8_t * out_buffer); - -/** - * @brief A class that defines stream based implementation of SHA-256 hash - * It's expected that the object of this class can be safely copied. - * All implementations must check for std::is_trivially_copyable. - **/ - -struct alignas(size_t) HashSHA256OpaqueContext -{ - uint8_t mOpaque[kMAX_Hash_SHA256_Context_Size]; -}; - -class Hash_SHA256_stream -{ -public: - Hash_SHA256_stream(); - ~Hash_SHA256_stream(); - - /** - * @brief Re-initialize digest computation to an empty context. - * - * @return CHIP_ERROR_INTERNAL on failure to initialize the context, - * CHIP_NO_ERROR otherwise. - */ - CHIP_ERROR Begin(); - - /** - * @brief Add some data to the digest computation, updating internal state. - * - * @param[in] data The span of bytes to include in the digest update process. - * - * @return CHIP_ERROR_INTERNAL on failure to ingest the data, CHIP_NO_ERROR otherwise. - */ - CHIP_ERROR AddData(const ByteSpan data); - - /** - * @brief Get the intermediate padded digest for the current state of the stream. - * - * More data can be added before finish is called. - * - * @param[in,out] out_buffer Output buffer to receive the digest. `out_buffer` must - * be at least `kSHA256_Hash_Length` bytes long. The `out_buffer` size - * will be set to `kSHA256_Hash_Length` on success. - * - * @return CHIP_ERROR_INTERNAL on failure to compute the digest, CHIP_ERROR_BUFFER_TOO_SMALL - * if out_buffer is too small, CHIP_NO_ERROR otherwise. - */ - CHIP_ERROR GetDigest(MutableByteSpan & out_buffer); - - /** - * @brief Finalize the stream digest computation, getting the final digest. - * - * @param[in,out] out_buffer Output buffer to receive the digest. `out_buffer` must - * be at least `kSHA256_Hash_Length` bytes long. The `out_buffer` size - * will be set to `kSHA256_Hash_Length` on success. - * - * @return CHIP_ERROR_INTERNAL on failure to compute the digest, CHIP_ERROR_BUFFER_TOO_SMALL - * if out_buffer is too small, CHIP_NO_ERROR otherwise. - */ - CHIP_ERROR Finish(MutableByteSpan & out_buffer); - - /** - * @brief Clear-out internal digest data to avoid lingering the state. - */ - void Clear(); - -private: - HashSHA256OpaqueContext mContext; -}; - -class HKDF_sha -{ -public: - HKDF_sha() {} - virtual ~HKDF_sha() {} - - /** - * @brief A function that implements SHA-256 based HKDF - * - * This implements the CHIP_Crypto_KDF() cryptographic primitive - * in the the specification. - * - * Error values are: - * - CHIP_ERROR_INVALID_ARGUMENT: for any bad arguments or nullptr input on - * any pointer. - * - CHIP_ERROR_INTERNAL: for any unexpected error arising in the underlying - * cryptographic layers. - * - * @param secret The secret to use as the key to the HKDF - * @param secret_length Length of the secret - * @param salt Optional salt to use as input to the HKDF - * @param salt_length Length of the salt - * @param info Optional info to use as input to the HKDF - * @param info_length Length of the info - * @param out_buffer Pointer to buffer to write output into. - * @param out_length Size of the underlying out_buffer. That length of output key material will be generated in out_buffer. - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - - virtual CHIP_ERROR HKDF_SHA256(const uint8_t * secret, size_t secret_length, const uint8_t * salt, size_t salt_length, - const uint8_t * info, size_t info_length, uint8_t * out_buffer, size_t out_length); -}; - -class HMAC_sha -{ -public: - HMAC_sha() {} - virtual ~HMAC_sha() {} - - /** - * @brief A function that implements SHA-256 based HMAC per FIPS1981. - * - * This implements the CHIP_Crypto_HMAC() cryptographic primitive - * in the the specification. - * - * The `out_length` must be at least kSHA256_Hash_Length, and only - * kSHA256_Hash_Length bytes are written to out_buffer. - * - * Error values are: - * - CHIP_ERROR_INVALID_ARGUMENT: for any bad arguments or nullptr input on - * any pointer. - * - CHIP_ERROR_INTERNAL: for any unexpected error arising in the underlying - * cryptographic layers. - * - * @param key The key to use for the HMAC operation - * @param key_length Length of the key - * @param message Message over which to compute the HMAC - * @param message_length Length of the message over which to compute the HMAC - * @param out_buffer Pointer to buffer into which to write the output. - * @param out_length Underlying size of the `out_buffer`. - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - - virtual CHIP_ERROR HMAC_SHA256(const uint8_t * key, size_t key_length, const uint8_t * message, size_t message_length, - uint8_t * out_buffer, size_t out_length); -}; - -/** - * @brief A cryptographically secure random number generator based on NIST SP800-90A - * @param out_buffer Buffer into which to write random bytes - * @param out_length Number of random bytes to generate - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ -CHIP_ERROR DRBG_get_bytes(uint8_t * out_buffer, size_t out_length); - -/** @brief Entropy callback function - * @param data Callback-specific data pointer - * @param output Output data to fill - * @param len Length of output buffer - * @param olen The actual amount of data that was written to output buffer - * @return 0 if success - */ -typedef int (*entropy_source)(void * data, uint8_t * output, size_t len, size_t * olen); - -/** @brief A function to add entropy sources to crypto library - * @param fn_source Function pointer to the entropy source - * @param p_source Data that should be provided when fn_source is called - * @param threshold Minimum required from source before entropy is released - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ -CHIP_ERROR add_entropy_source(entropy_source fn_source, void * p_source, size_t threshold); - -class PBKDF2_sha256 -{ -public: - PBKDF2_sha256() {} - virtual ~PBKDF2_sha256() {} - - /** @brief Function to derive key using password. SHA256 hashing algorithm is used for calculating hmac. - * @param password password used for key derivation - * @param plen length of buffer containing password - * @param salt salt to use as input to the KDF - * @param slen length of salt - * @param iteration_count number of iterations to run - * @param key_length length of output key - * @param output output buffer where the key will be written - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR pbkdf2_sha256(const uint8_t * password, size_t plen, const uint8_t * salt, size_t slen, - unsigned int iteration_count, uint32_t key_length, uint8_t * output); -}; - -/** - * The below class implements the draft 01 version of the Spake2+ protocol as - * defined in https://www.ietf.org/id/draft-bar-cfrg-spake2plus-01.html. - * - * The following describes the protocol flows: - * - * Commissioner Accessory - * ------------ --------- - * - * Init - * BeginProver - * ComputeRoundOne -------------> - * Init - * BeginVerifier - * /- ComputeRoundOne - * <------------- ComputeRoundTwo - * ComputeRoundTwo -------------> - * KeyConfirm KeyConfirm - * GetKeys GetKeys - * - **/ -class Spake2p -{ -public: - Spake2p(size_t fe_size, size_t point_size, size_t hash_size); - virtual ~Spake2p() {} - - /** - * @brief Initialize Spake2+ with some context specific information. - * - * @param context The context is arbitrary but should include information about the - * protocol being run, contain the transcript for negotiation, include - * the PKBDF parameters, etc. - * @param context_len The length of the context. - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR Init(const uint8_t * context, size_t context_len); - - /** - * @brief Free Spake2+ underlying objects. - **/ - virtual void Clear() = 0; - - /** - * @brief Start the Spake2+ process as a verifier (i.e. an accessory being provisioned). - * - * @param my_identity The verifier identity. May be NULL if identities are not established. - * @param my_identity_len The verifier identity length. - * @param peer_identity The peer identity. May be NULL if identities are not established. - * @param peer_identity_len The peer identity length. - * @param w0in The input w0 (a parameter baked into the device or computed with ComputeW0). - * @param w0in_len The input w0 length. - * @param Lin The input L (a parameter baked into the device or computed with ComputeL). - * @param Lin_len The input L length. - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR BeginVerifier(const uint8_t * my_identity, size_t my_identity_len, const uint8_t * peer_identity, - size_t peer_identity_len, const uint8_t * w0in, size_t w0in_len, const uint8_t * Lin, - size_t Lin_len); - - /** - * @brief Start the Spake2+ process as a prover (i.e. a commissioner). - * - * @param my_identity The prover identity. May be NULL if identities are not established. - * @param my_identity_len The prover identity length. - * @param peer_identity The peer identity. May be NULL if identities are not established. - * @param peer_identity_len The peer identity length. - * @param w0in The input w0 (an output from the PBKDF). - * @param w0in_len The input w0 length. - * @param w1in The input w1 (an output from the PBKDF). - * @param w1in_len The input w1 length. - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR BeginProver(const uint8_t * my_identity, size_t my_identity_len, const uint8_t * peer_identity, - size_t peer_identity_len, const uint8_t * w0in, size_t w0in_len, const uint8_t * w1in, - size_t w1in_len); - - /** - * @brief Compute the first round of the protocol. - * - * @param pab X value from commissioner. - * @param pab_len X length. - * @param out The output first round Spake2+ contribution. - * @param out_len The output first round Spake2+ contribution length. - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR ComputeRoundOne(const uint8_t * pab, size_t pab_len, uint8_t * out, size_t * out_len); - - /** - * @brief Compute the second round of the protocol. - * - * @param in The peer first round Spake2+ contribution. - * @param in_len The peer first round Spake2+ contribution length. - * @param out The output second round Spake2+ contribution. - * @param out_len The output second round Spake2+ contribution length. - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR ComputeRoundTwo(const uint8_t * in, size_t in_len, uint8_t * out, size_t * out_len); - - /** - * @brief Confirm that each party computed the same keys. - * - * @param in The peer second round Spake2+ contribution. - * @param in_len The peer second round Spake2+ contribution length. - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR KeyConfirm(const uint8_t * in, size_t in_len); - - /** - * @brief Return the shared secret. - * - * @param out The output secret. - * @param out_len The output secret length. - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - CHIP_ERROR GetKeys(uint8_t * out, size_t * out_len); - - CHIP_ERROR InternalHash(const uint8_t * in, size_t in_len); - CHIP_ERROR WriteMN(); - CHIP_ERROR GenerateKeys(); - - /** - * @brief Load a field element. - * - * @param in The input big endian field element. - * @param in_len The size of the input buffer in bytes. - * @param fe A pointer to an initialized implementation dependant field element. - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR FELoad(const uint8_t * in, size_t in_len, void * fe) = 0; - - /** - * @brief Write a field element in big-endian format. - * - * @param fe The field element to write. - * @param out The output buffer. - * @param out_len The length of the output buffer. - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR FEWrite(const void * fe, uint8_t * out, size_t out_len) = 0; - - /** - * @brief Generate a field element. - * - * @param fe A pointer to an initialized implementation dependant field element. - * - * @note The implementation must generate a random element from [0, q) where q is the curve order. - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR FEGenerate(void * fe) = 0; - - /** - * @brief Multiply two field elements, fer = fe1 * fe2. - * - * @param fer A pointer to an initialized implementation dependant field element. - * @param fe1 A pointer to an initialized implementation dependant field element. - * @param fe2 A pointer to an initialized implementation dependant field element. - * - * @note The result must be a field element (i.e. reduced by the curve order). - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR FEMul(void * fer, const void * fe1, const void * fe2) = 0; - - /** - * @brief Load a point from 0x04 || X || Y format - * - * @param in Input buffer - * @param in_len Input buffer length - * @param R A pointer to an initialized implementation dependant point. - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR PointLoad(const uint8_t * in, size_t in_len, void * R) = 0; - - /** - * @brief Write a point in 0x04 || X || Y format - * - * @param R A pointer to an initialized implementation dependant point. - * @param out Output buffer - * @param out_len Length of the output buffer - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR PointWrite(const void * R, uint8_t * out, size_t out_len) = 0; - - /** - * @brief Scalar multiplication, R = fe1 * P1. - * - * @param R Resultant point - * @param P1 Input point - * @param fe1 Input field element. - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR PointMul(void * R, const void * P1, const void * fe1) = 0; - - /** - * @brief Scalar multiplication with addition, R = fe1 * P1 + fe2 * P2. - * - * @param R Resultant point - * @param P1 Input point - * @param fe1 Input field element. - * @param P2 Input point - * @param fe2 Input field element. - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR PointAddMul(void * R, const void * P1, const void * fe1, const void * P2, const void * fe2) = 0; - - /** - * @brief Point inversion. - * - * @param R Input/Output point to point_invert - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR PointInvert(void * R) = 0; - - /** - * @brief Multiply a point by the curve cofactor. - * - * @param R Input/Output point to point_invert - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR PointCofactorMul(void * R) = 0; - - /* - * @synopsis Check if a point is on the curve. - * - * @param R Input point to check. - * - * @return CHIP_NO_ERROR if the point is valid, CHIP_ERROR otherwise. - */ - virtual CHIP_ERROR PointIsValid(void * R) = 0; - - /* - * @synopsis Compute w0sin mod p - * - * @param w0out Output field element (modulo p) - * @param w0_len Output field element length - * @param w1sin Input field element - * @param w1sin_len Input field element length - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR ComputeW0(uint8_t * w0out, size_t * w0_len, const uint8_t * w0sin, size_t w0sin_len) = 0; - - /* - * @synopsis Compute w1in*G - * - * @param Lout Output point in 0x04 || X || Y format. - * @param L_len Output point length - * @param w1in Input field element - * @param w1in_len Input field element size - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR ComputeL(uint8_t * Lout, size_t * L_len, const uint8_t * w1in, size_t w1in_len) = 0; - - void * M; - void * N; - const void * G; - void * X; - void * Y; - void * L; - void * Z; - void * V; - void * w0; - void * w1; - void * xy; - void * order; - void * tempbn; - -protected: - /** - * @brief Initialize underlying implementation curve, points, field elements, etc. - * - * @details The implementation needs to: - * 1. Initialize each of the points below and set the relevant pointers on the class: - * a. M - * b. N - * c. G - * d. X - * e. Y - * f. L - * g. Z - * h. V - * - * As an example: - * this.M = implementation_alloc_point(); - * 2. Initialize each of the field elements below and set the relevant pointers on the class: - * a. w0 - * b. w1 - * c. xy - * d. tempbn - * 3. The hashing context should be initialized - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR InitImpl() = 0; - - /** - * @brief Hash in_len bytes of in into the internal hash context. - * - * @param in The input buffer. - * @param in_len Size of the input buffer in bytes. - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR Hash(const uint8_t * in, size_t in_len) = 0; - - /** - * @brief Return the hash. - * - * @param out_span Output buffer. The size available must be >= the hash size. It gets resized - * to hash size on success. - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR HashFinalize(MutableByteSpan & out_span) = 0; - - /** - * @brief Generate a message authentication code. - * - * @param key The MAC key buffer. - * @param key_len The size of the MAC key in bytes. - * @param in The input buffer. - * @param in_len The size of the input data to MAC in bytes. - * @param out_span The output MAC buffer span. Size must be >= the hash_size. Output size is updated to fit on success. - * - * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise - **/ - virtual CHIP_ERROR Mac(const uint8_t * key, size_t key_len, const uint8_t * in, size_t in_len, MutableByteSpan & out_span) = 0; - - /** - * @brief Verify a message authentication code. - * - * @param key The MAC key buffer. - * @param key_len The size of the MAC key in bytes. - * @param mac The input MAC buffer. - * @param mac_len The size of the MAC in bytes. - * @param in The input buffer to verify. - * @param in_len The size of the input data to verify in bytes. - * - * @return Returns a CHIP_ERROR when the MAC doesn't validate, CHIP_NO_ERROR otherwise. - **/ - virtual CHIP_ERROR MacVerify(const uint8_t * key, size_t key_len, const uint8_t * mac, size_t mac_len, const uint8_t * in, - size_t in_len) = 0; - - /** - * @brief Derive an key of length out_len. - * - * @param ikm The input key material buffer. - * @param ikm_len The input key material length. - * @param salt The optional salt. This may be NULL. - * @param salt_len The size of the salt in bytes. - * @param info The info. - * @param info_len The size of the info in bytes. - * @param out The output key - * @param out_len The output key length - * - * @return Returns a CHIP_ERROR when the MAC doesn't validate, CHIP_NO_ERROR otherwise. - **/ - virtual CHIP_ERROR KDF(const uint8_t * ikm, size_t ikm_len, const uint8_t * salt, size_t salt_len, const uint8_t * info, - size_t info_len, uint8_t * out, size_t out_len) = 0; - - CHIP_SPAKE2P_ROLE role; - CHIP_SPAKE2P_STATE state = CHIP_SPAKE2P_STATE::PREINIT; - size_t fe_size; - size_t hash_size; - size_t point_size; - uint8_t Kcab[kMAX_Hash_Length]; - uint8_t Kae[kMAX_Hash_Length]; - uint8_t * Kca; - uint8_t * Kcb; - uint8_t * Ka; - uint8_t * Ke; -}; - -struct alignas(size_t) Spake2pOpaqueContext -{ - uint8_t mOpaque[kMAX_Spake2p_Context_Size]; -}; - -class Spake2p_P256_SHA256_HKDF_HMAC : public Spake2p -{ -public: - Spake2p_P256_SHA256_HKDF_HMAC() : Spake2p(kP256_FE_Length, kP256_Point_Length, kSHA256_Hash_Length) - { - memset(&mSpake2pContext, 0, sizeof(mSpake2pContext)); - } - - ~Spake2p_P256_SHA256_HKDF_HMAC() override { Spake2p_P256_SHA256_HKDF_HMAC::Clear(); } - - void Clear() override; - CHIP_ERROR Mac(const uint8_t * key, size_t key_len, const uint8_t * in, size_t in_len, MutableByteSpan & out_span) override; - CHIP_ERROR MacVerify(const uint8_t * key, size_t key_len, const uint8_t * mac, size_t mac_len, const uint8_t * in, - size_t in_len) override; - CHIP_ERROR FELoad(const uint8_t * in, size_t in_len, void * fe) override; - CHIP_ERROR FEWrite(const void * fe, uint8_t * out, size_t out_len) override; - CHIP_ERROR FEGenerate(void * fe) override; - CHIP_ERROR FEMul(void * fer, const void * fe1, const void * fe2) override; - - CHIP_ERROR PointLoad(const uint8_t * in, size_t in_len, void * R) override; - CHIP_ERROR PointWrite(const void * R, uint8_t * out, size_t out_len) override; - CHIP_ERROR PointMul(void * R, const void * P1, const void * fe1) override; - CHIP_ERROR PointAddMul(void * R, const void * P1, const void * fe1, const void * P2, const void * fe2) override; - CHIP_ERROR PointInvert(void * R) override; - CHIP_ERROR PointCofactorMul(void * R) override; - CHIP_ERROR PointIsValid(void * R) override; - - CHIP_ERROR ComputeW0(uint8_t * w0out, size_t * w0_len, const uint8_t * w0sin, size_t w0sin_len) override; - CHIP_ERROR ComputeL(uint8_t * Lout, size_t * L_len, const uint8_t * w1in, size_t w1in_len) override; - -protected: - CHIP_ERROR InitImpl() override; - CHIP_ERROR Hash(const uint8_t * in, size_t in_len) override; - CHIP_ERROR HashFinalize(MutableByteSpan & out_span) override; - CHIP_ERROR KDF(const uint8_t * secret, size_t secret_length, const uint8_t * salt, size_t salt_length, const uint8_t * info, - size_t info_length, uint8_t * out, size_t out_length) override; - -private: - CHIP_ERROR InitInternal(); - Hash_SHA256_stream sha256_hash_ctx; - - Spake2pOpaqueContext mSpake2pContext; -}; - -/** - * @brief Class used for verifying PASE secure sessions. - **/ -class Spake2pVerifier -{ -public: - uint8_t mW0[kP256_FE_Length]; - uint8_t mL[kP256_Point_Length]; - - CHIP_ERROR Serialize(MutableByteSpan & outSerialized) const; - CHIP_ERROR Deserialize(const ByteSpan & inSerialized); - - /** - * @brief Generate the Spake2+ verifier. - * - * @param pbkdf2IterCount Iteration count for PBKDF2 function - * @param salt Salt to be used for Spake2+ operation - * @param setupPin Provided setup PIN (passcode) - * - * @return CHIP_ERROR The result of Spake2+ verifier generation - */ - CHIP_ERROR Generate(uint32_t pbkdf2IterCount, const ByteSpan & salt, uint32_t setupPin); - - /** - * @brief Compute the initiator values (w0, w1) used for PAKE input. - * - * @param pbkdf2IterCount Iteration count for PBKDF2 function - * @param salt Salt to be used for Spake2+ operation - * @param setupPin Provided setup PIN (passcode) - * @param ws The output pair (w0, w1) stored sequentially - * @param ws_len The output length - * - * @return CHIP_ERROR The result from running PBKDF2 - */ - static CHIP_ERROR ComputeWS(uint32_t pbkdf2IterCount, const ByteSpan & salt, uint32_t setupPin, uint8_t * ws, uint32_t ws_len); -}; - -/** - * @brief Serialized format of the Spake2+ Verifier components. - * - * This is used when the Verifier should be presented in a serialized form. - * For example, when it is generated using PBKDF function, when stored in the - * memory or when sent over the wire. - * The serialized format is concatentation of 'W0' and 'L' verifier components: - * { Spake2pVerifier.mW0[kP256_FE_Length], Spake2pVerifier.mL[kP256_Point_Length] } - **/ -typedef uint8_t Spake2pVerifierSerialized[kSpake2p_VerifierSerialized_Length]; - -/** - * @brief Compute the compressed fabric identifier used for operational discovery service - * records from a Node's root public key and Fabric ID. On success, out_compressed_fabric_id - * will have a size of exactly kCompressedFabricIdentifierSize. - * - * Errors are: - * - CHIP_ERROR_INVALID_ARGUMENT if root_public_key is invalid - * - CHIP_ERROR_BUFFER_TOO_SMALL if out_compressed_fabric_id is too small for serialization - * - CHIP_ERROR_INTERNAL on any unexpected crypto or data conversion errors. - * - * @param[in] root_public_key The root public key associated with the node's fabric - * @param[in] fabric_id The fabric ID associated with the node's fabric - * @param[out] out_compressed_fabric_id Span where output will be written. Its size must be >= kCompressedFabricIdentifierSize. - * @returns a CHIP_ERROR (see above) on failure or CHIP_NO_ERROR otherwise. - */ -CHIP_ERROR GenerateCompressedFabricId(const Crypto::P256PublicKey & root_public_key, uint64_t fabric_id, - MutableByteSpan & out_compressed_fabric_id); - -/** - * @brief Compute the compressed fabric identifier used for operational discovery service - * records from a Node's root public key and Fabric ID. This is a conveniance - * overload that writes to a uint64_t (CompressedFabricId) type. - * - * @param[in] rootPublicKey The root public key associated with the node's fabric - * @param[in] fabricId The fabric ID associated with the node's fabric - * @param[out] compressedFabricId output location for compressed fabric ID - * @returns a CHIP_ERROR on failure or CHIP_NO_ERROR otherwise. - */ -CHIP_ERROR GenerateCompressedFabricId(const Crypto::P256PublicKey & rootPublicKey, uint64_t fabricId, - uint64_t & compressedFabricId); - -enum class CertificateChainValidationResult -{ - kSuccess = 0, - - kRootFormatInvalid = 100, - kRootArgumentInvalid = 101, - - kICAFormatInvalid = 200, - kICAArgumentInvalid = 201, - - kLeafFormatInvalid = 300, - kLeafArgumentInvalid = 301, - - kChainInvalid = 400, - - kNoMemory = 500, - - kInternalFrameworkError = 600, -}; - -CHIP_ERROR ValidateCertificateChain(const uint8_t * rootCertificate, size_t rootCertificateLen, const uint8_t * caCertificate, - size_t caCertificateLen, const uint8_t * leafCertificate, size_t leafCertificateLen, - CertificateChainValidationResult & result); - -enum class AttestationCertType -{ - kPAA = 0, - kPAI = 1, - kDAC = 2, -}; - -CHIP_ERROR VerifyAttestationCertificateFormat(const ByteSpan & cert, AttestationCertType certType); - -/** - * @brief Validate notBefore timestamp of a certificate (candidateCertificate) against validity period of the - * issuer certificate (issuerCertificate). - * - * Errors are: - * - CHIP_ERROR_CERT_EXPIRED if the candidateCertificate timestamp does not satisfy the issuerCertificate's timestamp. - * - CHIP_ERROR_INVALID_ARGUMENT when passing an invalid argument. - * - CHIP_ERROR_INTERNAL on any unexpected crypto or data conversion errors. - * - * @param candidateCertificate A DER Certificate ByteSpan those notBefore timestamp to be evaluated. - * @param issuerCertificate A DER Certificate ByteSpan used to evaluate validity timestamp of the candidateCertificate. - * - * @returns a CHIP_ERROR (see above) on failure or CHIP_NO_ERROR otherwise. - **/ -CHIP_ERROR IsCertificateValidAtIssuance(const ByteSpan & candidateCertificate, const ByteSpan & issuerCertificate); - -/** - * @brief Validate a certificate's validity date against current time. - * - * Errors are: - * - CHIP_ERROR_CERT_EXPIRED if the certificate has expired. - * - CHIP_ERROR_INVALID_ARGUMENT when passing an invalid argument. - * - CHIP_ERROR_INTERNAL on any unexpected crypto or data conversion errors. - * - * @param certificate A DER Certificate ByteSpan used as the validity reference to be checked against current time. - * - * @returns a CHIP_ERROR (see above) on failure or CHIP_NO_ERROR otherwise. - **/ -CHIP_ERROR IsCertificateValidAtCurrentTime(const ByteSpan & certificate); - -CHIP_ERROR ExtractPubkeyFromX509Cert(const ByteSpan & certificate, Crypto::P256PublicKey & pubkey); - -/** - * @brief Extracts the Subject Key Identifier from an X509 Certificate. - **/ -CHIP_ERROR ExtractSKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan & skid); - -/** - * @brief Extracts the Authority Key Identifier from an X509 Certificate. - **/ -CHIP_ERROR ExtractAKIDFromX509Cert(const ByteSpan & certificate, MutableByteSpan & akid); - -/** - * @brief Checks for resigned version of the certificate in the list and returns it. - * - * The following conditions SHOULD be satisfied for the certificate to qualify as - * a resigned version of a reference certificate: - * - SKID of the candidate and the reference certificate should match. - * - SubjectDN of the candidate and the reference certificate should match. - * - * If no resigned version is found then reference certificate itself is returned. - * - * @param referenceCertificate A DER certificate. - * @param candidateCertificates A pointer to the list of DER Certificates, which should be searched - * for the resigned version of `referenceCertificate`. - * @param candidateCertificatesCount Number of certificates in the `candidateCertificates` list. - * @param outCertificate A reference to the certificate or it's resigned version if found. - * Note that it points to either `referenceCertificate` or one of - * `candidateCertificates`, but it doesn't copy data. - * - * @returns error if there is certificate parsing/format issue or CHIP_NO_ERROR otherwise. - **/ -CHIP_ERROR ReplaceCertIfResignedCertFound(const ByteSpan & referenceCertificate, const ByteSpan * candidateCertificates, - size_t candidateCertificatesCount, ByteSpan & outCertificate); - -/** - * Defines DN attribute types that can include endocing of VID/PID parameters. - */ -enum class DNAttrType -{ - kUnspecified = 0, - kCommonName = 1, - kMatterVID = 2, - kMatterPID = 3, -}; - -/** - * @struct AttestationCertVidPid - * - * @brief - * A data structure representing Attestation Certificate VID and PID attributes. - */ -struct AttestationCertVidPid -{ - Optional mVendorId; - Optional mProductId; - - bool Initialized() const { return (mVendorId.HasValue() || mProductId.HasValue()); } -}; - -/** - * @brief Extracts VID and PID attributes from the DN Attribute string. - * If attribute is not present the corresponding output value stays uninitialized. - * - * @return CHIP_ERROR_INVALID_ARGUMENT if wrong input is provided. - * CHIP_ERROR_WRONG_CERT_DN if encoding of kMatterVID and kMatterPID attributes is wrong. - * CHIP_NO_ERROR otherwise. - **/ -CHIP_ERROR ExtractVIDPIDFromAttributeString(DNAttrType attrType, const ByteSpan & attr, - AttestationCertVidPid & vidpidFromMatterAttr, AttestationCertVidPid & vidpidFromCNAttr); - -/** - * @brief Extracts VID and PID attributes from the Subject DN of an X509 Certificate. - * If attribute is not present the corresponding output value stays uninitialized. - **/ -CHIP_ERROR ExtractVIDPIDFromX509Cert(const ByteSpan & x509Cert, AttestationCertVidPid & vidpid); - -/** - * @brief The set of credentials needed to operate group message security with symmetric keys. - */ -typedef struct GroupOperationalCredentials -{ - /// Validity start time in microseconds since 2000-01-01T00:00:00 UTC ("the Epoch") - uint64_t start_time; - /// Session Id - uint16_t hash; - /// Operational group key - uint8_t encryption_key[Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES]; - /// Privacy key - uint8_t privacy_key[Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES]; -} GroupOperationalCredentials; - -/** - * @brief Opaque context used to protect a symmetric key. The key operations must - * be performed without exposing the protected key value. - */ -class SymmetricKeyContext -{ -public: - /** - * @brief Returns the symmetric key hash - * - * TODO: Replace GetKeyHash() with DeriveGroupSessionId(SymmetricKeyContext &, uint16_t & session_id) - * - * @return Group Key Hash - */ - virtual uint16_t GetKeyHash() = 0; - - virtual ~SymmetricKeyContext() = default; - /** - * @brief Perform the message encryption as described in 4.7.2. (Security Processing of Outgoing Messages) - * @param[in] plaintext Outgoing message payload. - * @param[in] aad Additional data (message header contents) - * @param[in] nonce Nonce (Security Flags | Message Counter | Source Node ID) - * @param[out] mic Outgoing Message Integrity Check - * @param[out] ciphertext Outgoing encrypted payload. Must be at least as big as plaintext. The same buffer may be used both - * for ciphertext, and plaintext. - * @return CHIP_ERROR - */ - virtual CHIP_ERROR MessageEncrypt(const ByteSpan & plaintext, const ByteSpan & aad, const ByteSpan & nonce, - MutableByteSpan & mic, MutableByteSpan & ciphertext) const = 0; - /** - * @brief Perform the message decryption as described in 4.7.3.(Security Processing of Incoming Messages) - * @param[in] ciphertext Incoming encrypted payload - * @param[in] aad Additional data (message header contents) - * @param[in] nonce Nonce (Security Flags | Message Counter | Source Node ID) - * @param[in] mic Incoming Message Integrity Check - * @param[out] plaintext Incoming message payload. Must be at least as big as ciphertext. The same buffer may be used both - * for plaintext, and ciphertext. - * @return CHIP_ERROR - */ - virtual CHIP_ERROR MessageDecrypt(const ByteSpan & ciphertext, const ByteSpan & aad, const ByteSpan & nonce, - const ByteSpan & mic, MutableByteSpan & plaintext) const = 0; - - /** - * @brief Perform privacy encoding as described in 4.8.2. (Privacy Processing of Outgoing Messages) - * @param[in] input Message header to privacy encrypt - * @param[in] nonce Privacy Nonce = session_id | mic - * @param[out] output Message header obfuscated - * @return CHIP_ERROR - */ - virtual CHIP_ERROR PrivacyEncrypt(const ByteSpan & input, const ByteSpan & nonce, MutableByteSpan & output) const = 0; - - /** - * @brief Perform privacy decoding as described in 4.8.3. (Privacy Processing of Incoming Messages) - * @param[in] input Message header to privacy decrypt - * @param[in] nonce Privacy Nonce = session_id | mic - * @param[out] output Message header deobfuscated - * @return CHIP_ERROR - */ - virtual CHIP_ERROR PrivacyDecrypt(const ByteSpan & input, const ByteSpan & nonce, MutableByteSpan & output) const = 0; - - /** - * @brief Release resources such as dynamic memory used to allocate this instance of the SymmetricKeyContext - */ - virtual void Release() = 0; -}; - -/** - * @brief Derives the Operational Group Key using the Key Derivation Function (KDF) from the given epoch key. - * @param[in] epoch_key The epoch key. Must be CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES bytes length. - * @param[in] compressed_fabric_id The compressed fabric ID for the fabric (big endian byte string) - * @param[out] out_key Symmetric key used as the encryption key during message processing for group communication. - The buffer size must be at least CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES bytes length. - * @return Returns a CHIP_NO_ERROR on succcess, or CHIP_ERROR_INTERNAL if the provided key is invalid. - **/ -CHIP_ERROR DeriveGroupOperationalKey(const ByteSpan & epoch_key, const ByteSpan & compressed_fabric_id, MutableByteSpan & out_key); - -/** - * @brief Derives the Group Session ID from a given operational group key using - * the Key Derivation Function (Group Key Hash) - * @param[in] operational_key The operational group key. Must be CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES bytes length. - * @param[out] session_id Output of the Group Key Hash - * @return Returns a CHIP_NO_ERROR on succcess, or CHIP_ERROR_INVALID_ARGUMENT if the provided key is invalid. - **/ -CHIP_ERROR DeriveGroupSessionId(const ByteSpan & operational_key, uint16_t & session_id); - -/** - * @brief Derives the Privacy Group Key using the Key Derivation Function (KDF) from the given epoch key. - * @param[in] epoch_key The epoch key. Must be CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES bytes length. - * @param[out] out_key Symmetric key used as the privacy key during message processing for group communication. - * The buffer size must be at least CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES bytes length. - * @return Returns a CHIP_NO_ERROR on succcess, or CHIP_ERROR_INTERNAL if the provided key is invalid. - **/ -CHIP_ERROR DeriveGroupPrivacyKey(const ByteSpan & epoch_key, MutableByteSpan & out_key); - -/** - * @brief Derives the complete set of credentials needed for group security. - * - * This function will derive the Encryption Key, Group Key Hash (Session Id), and Privacy Key - * for the given Epoch Key and Compressed Fabric Id. - * @param[in] epoch_key The epoch key. Must be CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES bytes length. - * @param[in] compressed_fabric_id The compressed fabric ID for the fabric (big endian byte string) - * @param[out] operational_credentials The set of Symmetric keys used during message processing for group communication. - * @return Returns a CHIP_NO_ERROR on succcess, or CHIP_ERROR_INTERNAL if the provided key is invalid. - **/ -CHIP_ERROR DeriveGroupOperationalCredentials(const ByteSpan & epoch_key, const ByteSpan & compressed_fabric_id, - GroupOperationalCredentials & operational_credentials); -} // namespace Crypto -} // namespace chip diff --git a/src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp b/src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp index fcce1f65e90a52..7bd3ffeea5779c 100644 --- a/src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp +++ b/src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp @@ -20,7 +20,7 @@ * mbedTLS based implementation of CHIP crypto primitives */ -#include "CHIPCryptoPAL.h" +#include #include @@ -113,18 +113,8 @@ static bool _isValidTagLength(size_t tag_length) return false; } -static bool _isValidKeyLength(size_t length) -{ - // 16 bytes key for AES-CCM-128, 32 for AES-CCM-256 - if (length == 16 || length == 32) - { - return true; - } - return false; -} - CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, const uint8_t * aad, size_t aad_length, - const uint8_t * key, size_t key_length, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, + const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * ciphertext, uint8_t * tag, size_t tag_length) { CHIP_ERROR error = CHIP_NO_ERROR; @@ -135,8 +125,6 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c VerifyOrExit(plaintext != nullptr || plaintext_length == 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(ciphertext != nullptr || plaintext_length == 0, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(key != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(_isValidKeyLength(key_length), error = CHIP_ERROR_UNSUPPORTED_ENCRYPTION_TYPE); VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(tag != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); @@ -146,10 +134,9 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c VerifyOrExit(aad != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); } - // Size of key = key_length * number of bits in a byte (8) - // Cast is safe because we called _isValidKeyLength above. + // multiplying by 8 to convert key from bits to byte result = - mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, Uint8::to_const_uchar(key), static_cast(key_length * 8)); + mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, key.As(), sizeof(Aes128KeyByteArray) * 8); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); // Encrypt @@ -165,7 +152,7 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c } CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, const uint8_t * aad, size_t aad_len, - const uint8_t * tag, size_t tag_length, const uint8_t * key, size_t key_length, const uint8_t * nonce, + const uint8_t * tag, size_t tag_length, const Aes128KeyHandle & key, const uint8_t * nonce, size_t nonce_length, uint8_t * plaintext) { CHIP_ERROR error = CHIP_NO_ERROR; @@ -178,8 +165,6 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, co VerifyOrExit(ciphertext != nullptr || ciphertext_len == 0, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(tag != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(_isValidTagLength(tag_length), error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(key != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(_isValidKeyLength(key_length), error = CHIP_ERROR_UNSUPPORTED_ENCRYPTION_TYPE); VerifyOrExit(nonce != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(nonce_length > 0, error = CHIP_ERROR_INVALID_ARGUMENT); if (aad_len > 0) @@ -187,10 +172,9 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, co VerifyOrExit(aad != nullptr, error = CHIP_ERROR_INVALID_ARGUMENT); } - // Size of key = key_length * number of bits in a byte (8) - // Cast is safe because we called _isValidKeyLength above. + // multiplying by 8 to convert key from bits to byte result = - mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, Uint8::to_const_uchar(key), static_cast(key_length * 8)); + result = mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, key.As(), sizeof(Aes128KeyByteArray) * 8); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); // Decrypt From 71305cb7f5c60d3c1409ddb554cbf6c59c3fb635 Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Mon, 20 Feb 2023 20:06:36 +0000 Subject: [PATCH 4/6] Restyled by clang-format --- src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp b/src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp index 7bd3ffeea5779c..92904a1d1d60bd 100644 --- a/src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp +++ b/src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp @@ -135,8 +135,7 @@ CHIP_ERROR AES_CCM_encrypt(const uint8_t * plaintext, size_t plaintext_length, c } // multiplying by 8 to convert key from bits to byte - result = - mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, key.As(), sizeof(Aes128KeyByteArray) * 8); + result = mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, key.As(), sizeof(Aes128KeyByteArray) * 8); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); // Encrypt @@ -173,8 +172,8 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, co } // multiplying by 8 to convert key from bits to byte - result = - result = mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, key.As(), sizeof(Aes128KeyByteArray) * 8); + result = result = + mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, key.As(), sizeof(Aes128KeyByteArray) * 8); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); // Decrypt From c761c436690c8d701c449ff6480ff70f342ad24e Mon Sep 17 00:00:00 2001 From: "Restyled.io" Date: Mon, 20 Feb 2023 20:06:37 +0000 Subject: [PATCH 5/6] Restyled by gn --- src/platform/silabs/SiWx917/BUILD.gn | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/platform/silabs/SiWx917/BUILD.gn b/src/platform/silabs/SiWx917/BUILD.gn index 7d8459adb384df..d53d04894869ce 100644 --- a/src/platform/silabs/SiWx917/BUILD.gn +++ b/src/platform/silabs/SiWx917/BUILD.gn @@ -72,9 +72,7 @@ static_library("SiWx917") { # Add platform crypto implementation if (chip_crypto == "platform") { - sources += [ - "CHIPCryptoPALTinyCrypt.cpp", - ] + sources += [ "CHIPCryptoPALTinyCrypt.cpp" ] public_deps += [ "${chip_root}/src/crypto", "${mbedtls_root}:mbedtls", From 893616e14bc230c1e6d00cec807e7965b7f956f4 Mon Sep 17 00:00:00 2001 From: Chirag Bansal Date: Tue, 21 Feb 2023 01:39:38 +0530 Subject: [PATCH 6/6] result was added twice, removed one --- src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp b/src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp index 92904a1d1d60bd..9f88a1937f8eb7 100644 --- a/src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp +++ b/src/platform/silabs/SiWx917/CHIPCryptoPALTinyCrypt.cpp @@ -172,8 +172,7 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_len, co } // multiplying by 8 to convert key from bits to byte - result = result = - mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, key.As(), sizeof(Aes128KeyByteArray) * 8); + result = mbedtls_ccm_setkey(&context, MBEDTLS_CIPHER_ID_AES, key.As(), sizeof(Aes128KeyByteArray) * 8); VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); // Decrypt