blob: 9465d934207d1d3c508c44435b9b0fb7e4b80ae9 [file] [log] [blame]
/*
*
* Copyright (c) 2021 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.
*/
#pragma once
#include <crypto/CHIPCryptoPAL.h>
#include <lib/core/CHIPCallback.h>
#include <lib/core/CHIPError.h>
#include <lib/core/CHIPVendorIdentifiers.hpp>
#include <lib/support/ScopedBuffer.h>
#include <lib/support/Span.h>
#include <stdlib.h>
namespace chip {
namespace Credentials {
enum class AttestationVerificationResult : uint16_t
{
kSuccess = 0,
kPaaUntrusted = 100,
kPaaNotFound = 101,
kPaaExpired = 102,
kPaaSignatureInvalid = 103,
kPaaRevoked = 104,
kPaaFormatInvalid = 105,
kPaaArgumentInvalid = 106,
kPaiExpired = 200,
kPaiSignatureInvalid = 201,
kPaiRevoked = 202,
kPaiFormatInvalid = 203,
kPaiArgumentInvalid = 204,
kPaiVendorIdMismatch = 205,
kPaiAuthorityNotFound = 206,
kPaiMissing = 207,
kDacExpired = 300,
kDacSignatureInvalid = 301,
kDacRevoked = 302,
kDacFormatInvalid = 303,
kDacArgumentInvalid = 304,
kDacVendorIdMismatch = 305,
kDacProductIdMismatch = 306,
kDacAuthorityNotFound = 307,
kFirmwareInformationMismatch = 400,
kFirmwareInformationMissing = 401,
kAttestationSignatureInvalid = 500,
kAttestationElementsMalformed = 501,
kAttestationNonceMismatch = 502,
kAttestationSignatureInvalidFormat = 503,
kCertificationDeclarationNoKeyId = 600,
kCertificationDeclarationNoCertificateFound = 601,
kCertificationDeclarationInvalidSignature = 602,
kCertificationDeclarationInvalidFormat = 603,
kCertificationDeclarationInvalidVendorId = 604,
kCertificationDeclarationInvalidProductId = 605,
kCertificationDeclarationInvalidPAA = 606,
kNoMemory = 700,
kInvalidArgument = 800,
kInternalError = 900,
kNotImplemented = 0xFFFFU,
// TODO: Add more attestation verification errors
};
enum CertificateType : uint8_t
{
kUnknown = 0,
kDAC = 1,
kPAI = 2,
};
struct DeviceInfoForAttestation
{
// Vendor ID reported by device in Basic Information cluster
uint16_t vendorId = VendorId::NotSpecified;
// Product ID reported by device in Basic Information cluster
uint16_t productId = 0;
// Vendor ID from DAC
uint16_t dacVendorId = VendorId::NotSpecified;
// Product ID from DAC
uint16_t dacProductId = 0;
// Vendor ID from PAI cert
uint16_t paiVendorId = VendorId::NotSpecified;
// Product ID from PAI cert (0 if absent)
uint16_t paiProductId = 0;
// Vendor ID from PAA cert
uint16_t paaVendorId = VendorId::NotSpecified;
// Subject Key Identifier (SKID) from PAA cert
uint8_t paaSKID[Crypto::kSubjectKeyIdentifierLength] = { 0 };
};
/**
* @brief Helper utility to model a basic trust store usable for device attestation verifiers.
*
* API is synchronous. Real commissioner implementations may entirely
* hide Product Attestation Authority cert lookup behind the DeviceAttestationVerifier and
* never use this interface at all. It is provided as a utility to help build DeviceAttestationVerifier
* implementations suitable for testing or examples.
*/
class AttestationTrustStore
{
public:
AttestationTrustStore() = default;
virtual ~AttestationTrustStore() = default;
// Not copyable
AttestationTrustStore(const AttestationTrustStore &) = delete;
AttestationTrustStore & operator=(const AttestationTrustStore &) = delete;
/**
* @brief Look-up a PAA cert by SKID
*
* The implementations of this interface must have access to a set of PAAs to trust.
*
* Interface is synchronous, and therefore this should not be used unless to expose a PAA
* store that is both fully local and quick to access.
*
* @param[in] skid Buffer containing the subject key identifier (SKID) of the PAA to look-up
* @param[in,out] outPaaDerBuffer Buffer to receive the contents of the PAA root cert, if found.
* Size will be updated to match actual size.
*
* @returns CHIP_NO_ERROR on success, CHIP_INVALID_ARGUMENT if `skid` or `outPaaDerBuffer` arguments
* are not usable, CHIP_BUFFER_TOO_SMALL if certificate doesn't fit in `outPaaDerBuffer`
* span, CHIP_ERROR_CA_CERT_NOT_FOUND if no PAA found that matches `skid.
*
*/
virtual CHIP_ERROR GetProductAttestationAuthorityCert(const ByteSpan & skid, MutableByteSpan & outPaaDerBuffer) const = 0;
};
/**
* @brief Helper utility to model obtaining verifying keys by Key ID
*
* API is synchronous. Real commissioner implementations may entirely
* hide key lookup behind the DeviceAttestationVerifier and never use this interface at all.
* It is provided as a utility to help build DeviceAttestationVerifier
* implementations suitable for testing or examples.
*/
class WellKnownKeysTrustStore
{
public:
WellKnownKeysTrustStore() = default;
virtual ~WellKnownKeysTrustStore() = default;
// Not copyable
WellKnownKeysTrustStore(const WellKnownKeysTrustStore &) = delete;
WellKnownKeysTrustStore & operator=(const WellKnownKeysTrustStore &) = delete;
/**
* @brief Add a trusted key directly
*
* @param[in] kid - Key ID to use. Usually 20 bytes long, max 32 bytes.
* @param[in] pubKey - Verifying public key to attach to the key ID.
*
* @return CHIP_NO_ERROR on success, CHIP_INVALID_ARGUMENT if `kid` or `pubKey` arguments
* are not usable. CHIP_ERROR_NO_MEMORY if the trust store is full.
*/
virtual CHIP_ERROR AddTrustedKey(const ByteSpan & kid, const Crypto::P256PublicKey & pubKey) = 0;
/**
* @brief Add a trusted key via a public certificate.
*
* The subject public key of the certificate will be used.
* The subject key ID extensions of the certificate will be the `kid`.
*
* Verification of trust chaining is at the discretion of the implementation.
*
* @param[in] derCertBytes - Certificate containing the X.509 DER certificate with the key.
*
* @return CHIP_NO_ERROR on success, CHIP_INVALID_ARGUMENT if derCertBytes is improperly
* formatted or not trusted. CHIP_ERROR_NO_MEMORY if the trust store is full.
*/
virtual CHIP_ERROR AddTrustedKey(const ByteSpan & derCertBytes) = 0;
/**
* @brief Look-up a verifying key by Key ID
*
* Interface is synchronous.
*
* @param[in] kid Buffer containing the key identifier (KID) of the verifying key to look-up. Usually
* a SHA-1-sized buffer (20 bytes).
* @param[out] outPubKey Reference to where the verifying key found will be stored on CHIP_NO_ERROR
*
* @returns CHIP_NO_ERROR on success, CHIP_INVALID_ARGUMENT if `kid` or `pubKey` arguments
* are not usable, CHIP_ERROR_KEY_NOT_FOUND if no key is found that matches `kid`.
*/
virtual CHIP_ERROR LookupVerifyingKey(const ByteSpan & kid, Crypto::P256PublicKey & outPubKey) const = 0;
/**
* @brief Returns true if `kid` identifies a known test key.
*
* @param kid - Key ID to use. Usually 20 bytes long, max 32 bytes.
* @return true if it's a test/development-only signing key identifier, false otherwise
*/
virtual bool IsCdTestKey(const ByteSpan & kid) const = 0;
};
/**
* @brief Basic AttestationTrustStore that holds all data within caller-owned memory.
*
* This is useful to wrap a fixed constant array of certificates into a trust store
* implementation.
*/
class ArrayAttestationTrustStore : public AttestationTrustStore
{
public:
ArrayAttestationTrustStore(const ByteSpan * derCerts, size_t numCerts) : mDerCerts(derCerts), mNumCerts(numCerts) {}
CHIP_ERROR GetProductAttestationAuthorityCert(const ByteSpan & skid, MutableByteSpan & outPaaDerBuffer) const override
{
VerifyOrReturnError(!skid.empty() && (skid.data() != nullptr), CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(skid.size() == Crypto::kSubjectKeyIdentifierLength, CHIP_ERROR_INVALID_ARGUMENT);
size_t paaIdx;
ByteSpan candidate;
for (paaIdx = 0; paaIdx < mNumCerts; ++paaIdx)
{
uint8_t skidBuf[Crypto::kSubjectKeyIdentifierLength] = { 0 };
candidate = mDerCerts[paaIdx];
MutableByteSpan candidateSkidSpan{ skidBuf };
VerifyOrReturnError(CHIP_NO_ERROR == Crypto::ExtractSKIDFromX509Cert(candidate, candidateSkidSpan),
CHIP_ERROR_INTERNAL);
if (skid.data_equal(candidateSkidSpan))
{
// Found a match
return CopySpanToMutableSpan(candidate, outPaaDerBuffer);
}
}
return CHIP_ERROR_CA_CERT_NOT_FOUND;
}
protected:
const ByteSpan * mDerCerts;
const size_t mNumCerts;
};
class DeviceAttestationVerifier
{
public:
DeviceAttestationVerifier() = default;
virtual ~DeviceAttestationVerifier() = default;
// Not copyable
DeviceAttestationVerifier(const DeviceAttestationVerifier &) = delete;
DeviceAttestationVerifier & operator=(const DeviceAttestationVerifier &) = delete;
struct AttestationInfo
{
AttestationInfo(const ByteSpan & attestationElements, const ByteSpan & attestationChallenge,
const ByteSpan & attestationSignature, const ByteSpan & paiDer, const ByteSpan & dacDer,
const ByteSpan & attestationNonce, VendorId remoteVendorId, uint16_t remoteProductId) :
attestationElementsBuffer(attestationElements),
attestationChallengeBuffer(attestationChallenge), attestationSignatureBuffer(attestationSignature),
paiDerBuffer(paiDer), dacDerBuffer(dacDer), attestationNonceBuffer(attestationNonce), vendorId(remoteVendorId),
productId(remoteProductId)
{}
const ByteSpan
attestationElementsBuffer; // Buffer containing attestation elements portion of Attestation Response (raw TLV)
const ByteSpan attestationChallengeBuffer; // Buffer containing the attestation challenge from the secure session
const ByteSpan attestationSignatureBuffer; // Buffer the signature portion of Attestation Response
const ByteSpan paiDerBuffer; // Buffer containing the PAI certificate from device in DER format.
const ByteSpan dacDerBuffer; // Buffer containing the DAC certificate from device in DER format.
const ByteSpan attestationNonceBuffer; // Buffer containing attestation nonce.
VendorId vendorId;
uint16_t productId;
};
// Copies the bytes passed to it, and holds the PAI, DAC, and CD for additional verification step
class AttestationDeviceInfo
{
public:
AttestationDeviceInfo(const AttestationInfo & attestationInfo);
~AttestationDeviceInfo() = default;
// Returns buffer containing the PAI certificate from device in DER format.
const ByteSpan paiDerBuffer() const { return ByteSpan(mPaiDerBuffer.Get(), mPaiDerBuffer.AllocatedSize()); }
// Returns buffer containing the DAC certificate from device in DER format.
const ByteSpan dacDerBuffer() const { return ByteSpan(mDacDerBuffer.Get(), mDacDerBuffer.AllocatedSize()); }
// Returns optional buffer containing the certificate declaration from device.
const Optional<ByteSpan> cdBuffer() const
{
if (mCdBuffer.Get())
{
return MakeOptional(ByteSpan(mDacDerBuffer.Get(), mDacDerBuffer.AllocatedSize()));
}
else
{
return Optional<ByteSpan>();
}
}
uint16_t BasicInformationVendorId() const { return mBasicInformationVendorId; }
uint16_t BasicInformationProductId() const { return mBasicInformationProductId; }
private:
Platform::ScopedMemoryBufferWithSize<uint8_t> mPaiDerBuffer;
Platform::ScopedMemoryBufferWithSize<uint8_t> mDacDerBuffer;
Platform::ScopedMemoryBufferWithSize<uint8_t> mCdBuffer;
uint16_t mBasicInformationVendorId;
uint16_t mBasicInformationProductId;
};
typedef void (*OnAttestationInformationVerification)(void * context, const AttestationInfo & info,
AttestationVerificationResult result);
/**
* @brief Verify an attestation information payload against a DAC/PAI chain.
*
* @param[in] info All of the information required to verify the attestation.
* @param[in] onCompletion Callback handler to provide Attestation Information Verification result to the caller of
* VerifyAttestationInformation()
*/
virtual void VerifyAttestationInformation(const AttestationInfo & info,
Callback::Callback<OnAttestationInformationVerification> * onCompletion) = 0;
/**
* @brief Verify a CMS Signed Data signature against the CSA certificate of Subject Key Identifier that matches
* the subjectKeyIdentifier field of cmsEnvelopeBuffer.
*
* @param[in] cmsEnvelopeBuffer A ByteSpan with a CMS signed message.
* @param[out] certDeclBuffer A ByteSpan to hold the CD content extracted from the CMS signed message.
*
* @returns AttestationVerificationResult::kSuccess on success or another specific
* value from AttestationVerificationResult enum on failure.
*/
virtual AttestationVerificationResult ValidateCertificationDeclarationSignature(const ByteSpan & cmsEnvelopeBuffer,
ByteSpan & certDeclBuffer) = 0;
/**
* @brief Verify a CMS Signed Data Payload against the Basic Information Cluster and DAC/PAI's Vendor and Product IDs
*
* @param[in] certDeclBuffer A ByteSpan with the Certification Declaration content.
* @param[in] firmwareInfo A ByteSpan with the Firmware Information content.
* @param[in] deviceInfo The device information
*
* @returns AttestationVerificationResult::kSuccess on success or another specific
* value from AttestationVerificationResult enum on failure.
*/
virtual AttestationVerificationResult ValidateCertificateDeclarationPayload(const ByteSpan & certDeclBuffer,
const ByteSpan & firmwareInfo,
const DeviceInfoForAttestation & deviceInfo) = 0;
// TODO: Validate Firmware Information
/**
* @brief Verify an operational certificate signing request payload against the DAC's public key.
*
* @param[in] nocsrElementsBuffer Buffer containing CSR elements as per specifications section 11.22.5.6. NOCSR Elements.
* @param[in] attestationChallengeBuffer Buffer containing the attestation challenge from the secure session
* @param[in] attestationSignatureBuffer Buffer containing the signature portion of CSR Response
* @param[in] dacPublicKey Public Key from the DAC's certificate received from device.
* @param[in] csrNonce Buffer containing CSR nonce.
*/
virtual CHIP_ERROR VerifyNodeOperationalCSRInformation(const ByteSpan & nocsrElementsBuffer,
const ByteSpan & attestationChallengeBuffer,
const ByteSpan & attestationSignatureBuffer,
const Crypto::P256PublicKey & dacPublicKey,
const ByteSpan & csrNonce) = 0;
/**
* @brief Get the trust store used for the attestation verifier.
*
* Returns nullptr if not supported. Be careful not to hold-on to the trust store
* for too long. It is only expected to have same lifetime as the DeviceAttestationVerifier.
*
* @return a pointer to the trust store or nullptr if none is directly accessible.
*/
virtual WellKnownKeysTrustStore * GetCertificationDeclarationTrustStore() { return nullptr; }
void EnableCdTestKeySupport(bool enabled) { mEnableCdTestKeySupport = enabled; }
bool IsCdTestKeySupported() const { return mEnableCdTestKeySupport; }
protected:
CHIP_ERROR ValidateAttestationSignature(const Crypto::P256PublicKey & pubkey, const ByteSpan & attestationElements,
const ByteSpan & attestationChallenge, const Crypto::P256ECDSASignature & signature);
// Default to support the "development" test key for legacy purposes (since the DefaultDACVerifier)
// always supported development keys.
bool mEnableCdTestKeySupport = true;
};
/**
* Instance getter for the global DeviceAttestationVerifier.
*
* Callers have to externally synchronize usage of this function.
*
* @return The global device attestation verifier. Assume never null.
*/
DeviceAttestationVerifier * GetDeviceAttestationVerifier();
/**
* Instance setter for the global DeviceAttestationVerifier.
*
* Callers have to externally synchronize usage of this function.
*
* If the `verifier` is nullptr, no change is done.
*
* @param[in] verifier the DeviceAttestationVerifier to start returning with the getter
*/
void SetDeviceAttestationVerifier(DeviceAttestationVerifier * verifier);
} // namespace Credentials
} // namespace chip