Skip to content

Commit

Permalink
Added CertChainRequest and AttestationRequest commands to lighting-ap…
Browse files Browse the repository at this point in the history
…p ZAP script

Introduced CertChainRequest, CertChainResponse, AttestationRequest and AttestationResponse commands into operational-credentials-cluster.xml

Added the above commands' callbacks to operational-credentials-server.cpp

Introduced the Attestation Nonce buffer to CHIPDevice Class

Added Certificate Chain Request and Attestation Request Commands to CHIPDeviceController

Added global method to Server.cpp in order to retrieve Server's SecureSessionMgr (needed to retrieve Attestation Challenge)

Added support to DAC/PAI certificates in CHIPDevice

Include files from the DA constructor PR
        src/credentials/DeviceAttestationConstructor.cpp
        src/credentials/DeviceAttestationConstructor.h
  • Loading branch information
Marty Leisner authored and Marty Leisner committed Sep 9, 2021
1 parent fa1abba commit d5e476f
Show file tree
Hide file tree
Showing 24 changed files with 1,223 additions and 32 deletions.
32 changes: 32 additions & 0 deletions examples/lighting-app/lighting-common/lighting-app.zap
Original file line number Diff line number Diff line change
Expand Up @@ -2981,6 +2981,22 @@
"side": "client",
"enabled": 0,
"commands": [
{
"name": "AttestationRequest",
"code": 0,
"mfgCode": null,
"source": "client",
"incoming": 1,
"outgoing": 1
},
{
"name": "CertChainRequest",
"code": 2,
"mfgCode": null,
"source": "client",
"incoming": 1,
"outgoing": 1
},
{
"name": "OpCSRRequest",
"code": 4,
Expand Down Expand Up @@ -3064,6 +3080,22 @@
"side": "server",
"enabled": 1,
"commands": [
{
"name": "AttestationResponse",
"code": 1,
"mfgCode": null,
"source": "server",
"incoming": 1,
"outgoing": 1
},
{
"name": "CertChainResponse",
"code": 3,
"mfgCode": null,
"source": "server",
"incoming": 1,
"outgoing": 1
},
{
"name": "OpCSRResponse",
"code": 5,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
#include <app/server/Server.h>
#include <app/util/af.h>
#include <app/util/attribute-storage.h>
#include <credentials/CHIPCert.h>
#include <credentials/DeviceAttestationConstructor.h>
#include <credentials/DeviceAttestationCredsProvider.h>
#include <credentials/examples/DeviceAttestationCredsExample.h>
#include <lib/core/CHIPSafeCasts.h>
#include <lib/core/PeerId.h>
#include <lib/support/CodeUtils.h>
Expand All @@ -45,6 +49,9 @@ using namespace chip;
using namespace ::chip::DeviceLayer;
using namespace ::chip::Transport;

constexpr uint16_t kDACCertificate = 1;
constexpr uint16_t kPAICertificate = 2;

// As per specifications section 11.22.5.1. Constant RESP_MAX
constexpr uint16_t kMaxRspLen = 900;

Expand Down Expand Up @@ -417,6 +424,138 @@ bool emberAfOperationalCredentialsClusterUpdateNOCCallback(EndpointId endpoint,
return true;
}

bool emberAfOperationalCredentialsClusterCertChainRequestCallback(EndpointId endpoint, app::CommandHandler * commandObj,
uint16_t certChainType)
{
EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS;
CHIP_ERROR err = CHIP_NO_ERROR;

emberAfPrintln(EMBER_AF_PRINT_DEBUG, "OpCreds: commissioner has requested Device Attestation Credentials");

app::CommandPathParams cmdParams = { emberAfCurrentEndpoint(), /* group id */ 0, ZCL_OPERATIONAL_CREDENTIALS_CLUSTER_ID,
ZCL_CERT_CHAIN_RESPONSE_COMMAND_ID, (app::CommandPathFlags::kEndpointIdValid) };

TLV::TLVWriter * writer = nullptr;
uint8_t derBuf[Credentials::kMaxDERCertLength];
MutableByteSpan derBufSpan(derBuf);

// TODO: remove line below
Credentials::SetDeviceAttestationCredentialsProvider(Credentials::Examples::GetExampleDACProvider());
Credentials::DeviceAttestationCredentialsProvider * dacProvider = Credentials::GetDeviceAttestationCredentialsProvider();

VerifyOrExit(commandObj != nullptr, err = CHIP_ERROR_INCORRECT_STATE);

SuccessOrExit(err = commandObj->PrepareCommand(cmdParams));
writer = commandObj->GetCommandDataElementTLVWriter();
if (certChainType == kDACCertificate)
{
SuccessOrExit(err = dacProvider->GetDeviceAttestationCert(derBufSpan));
}
else if (certChainType == kPAICertificate)
{
SuccessOrExit(err = dacProvider->GetProductAttestationIntermediateCert(derBufSpan));
}
else
{
SuccessOrExit(status = EMBER_ZCL_STATUS_FAILURE);
}
SuccessOrExit(err = writer->Put(TLV::ContextTag(0), derBufSpan));
SuccessOrExit(err = commandObj->FinishCommand());

exit:
if (status == EMBER_ZCL_STATUS_FAILURE)
{
emberAfPrintln(EMBER_AF_PRINT_DEBUG, "OpCreds: Failed CertChainRequest.");
emberAfSendImmediateDefaultResponse(status);
}
if (err != CHIP_NO_ERROR)
{
ChipLogError(Zcl, "Failed to encode response command: %s", ErrorStr(err));
}

return true;
}

bool emberAfOperationalCredentialsClusterAttestationRequestCallback(EndpointId endpoint, app::CommandHandler * commandObj,
ByteSpan attestationNonce)
{
EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS;
CHIP_ERROR err = CHIP_NO_ERROR;
TLV::TLVWriter * writer = nullptr;
Platform::ScopedMemoryBuffer<uint8_t> attestationElements;
size_t attestationElementsLen;
Crypto::P256ECDSASignature signature;
PeerConnectionState * state;
uint8_t md[Crypto::kSHA256_Hash_Length];
MutableByteSpan messageDigestSpan(md);

emberAfPrintln(EMBER_AF_PRINT_DEBUG, "OpCreds: commissioner has requested Attestation");

app::CommandPathParams cmdParams = { emberAfCurrentEndpoint(), /* group id */ 0, ZCL_OPERATIONAL_CREDENTIALS_CLUSTER_ID,
ZCL_ATTESTATION_RESPONSE_COMMAND_ID, (app::CommandPathFlags::kEndpointIdValid) };

// TODO: remove line below
Credentials::SetDeviceAttestationCredentialsProvider(Credentials::Examples::GetExampleDACProvider());
Credentials::DeviceAttestationCredentialsProvider * dacProvider = Credentials::GetDeviceAttestationCredentialsProvider();

VerifyOrExit(commandObj != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
VerifyOrExit(attestationNonce.size() == 32, status = EMBER_ZCL_STATUS_FAILURE);

{
uint16_t vendorId = 0xFFF1;
uint16_t profileNum = 0x003E;
std::vector<ByteSpan> vendorReserved;
uint8_t certDeclBuf[512];
MutableByteSpan certDeclSpan(certDeclBuf);

SuccessOrExit(err = dacProvider->GetCertificationDeclaration(certDeclSpan));

attestationElementsLen = certDeclSpan.size() + attestationNonce.size() + sizeof(uint64_t) * 8;
VerifyOrExit(attestationElements.Alloc(attestationElementsLen), err = CHIP_ERROR_NO_MEMORY);

MutableByteSpan attestationElementsSpan(attestationElements.Get(), attestationElementsLen);
SuccessOrExit(err = Credentials::ConstructAttestationElements(certDeclSpan, attestationNonce, 0, ByteSpan(), vendorReserved,
vendorId, profileNum, attestationElementsSpan));
attestationElementsLen = attestationElementsSpan.size();
}

// Retrieve attestation challenge
state = commandObj->GetExchangeContext()->GetExchangeMgr()->GetSessionMgr()->GetPeerConnectionState(
commandObj->GetExchangeContext()->GetSecureSession());
VerifyOrExit(state != nullptr, status = EMBER_ZCL_STATUS_FAILURE);

{
Hash_SHA256_stream hashStream;
SuccessOrExit(err = hashStream.Begin());
SuccessOrExit(err = hashStream.AddData(ByteSpan(attestationElements.Get(), attestationElementsLen)));
SuccessOrExit(err = hashStream.AddData(state->GetSecureSession().GetAttestationChallenge()));
SuccessOrExit(err = hashStream.Finish(messageDigestSpan));

MutableByteSpan signatureSpan(signature, signature.Capacity());
SuccessOrExit(err = dacProvider->SignWithDeviceAttestationKey(messageDigestSpan, signatureSpan));
SuccessOrExit(err = signature.SetLength(signatureSpan.size()));
}

SuccessOrExit(err = commandObj->PrepareCommand(cmdParams));
writer = commandObj->GetCommandDataElementTLVWriter();
SuccessOrExit(err = writer->Put(TLV::ContextTag(0), ByteSpan(attestationElements.Get(), attestationElementsLen)));
SuccessOrExit(err = writer->Put(TLV::ContextTag(1), ByteSpan(signature, signature.Length())));
SuccessOrExit(err = commandObj->FinishCommand());

exit:
if (status == EMBER_ZCL_STATUS_FAILURE)
{
emberAfPrintln(EMBER_AF_PRINT_DEBUG, "OpCreds: Failed AttestationRequest.");
emberAfSendImmediateDefaultResponse(status);
}
if (err != CHIP_NO_ERROR)
{
ChipLogError(Zcl, "Failed to encode response command: %s", ErrorStr(err));
}

return true;
}

bool emberAfOperationalCredentialsClusterOpCSRRequestCallback(EndpointId endpoint, app::CommandHandler * commandObj,
ByteSpan CSRNonce)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,27 @@ limitations under the License.
<!-- 400 = 400 bytes for root cert -->
<attribute side="server" code="0x0004" define="TRUSTED_ROOTS" type="ARRAY" entryType="OCTET_STRING" length="400" writable="false" optional="false">TrustedRootCertificates</attribute>

<command source="client" code="0x00" name="AttestationRequest" optional="false">
<description>Sender is requesting attestation information from the receiver.</description>
<arg name="AttestationNonce" type="OCTET_STRING"/>
</command>

<command source="server" code="0x01" name="AttestationResponse" optional="false">
<description>An attestation information confirmation from the server.</description>
<arg name="AttestationElements" type="OCTET_STRING"/>
<arg name="Signature" type="OCTET_STRING"/>
</command>

<command source="client" code="0x02" name="CertChainRequest" optional="false">
<description>Sender is requesting a device attestation certificate from the receiver.</description>
<arg name="CertChainType" type="INT16U"/>
</command>

<command source="server" code="0x03" name="CertChainResponse" optional="false">
<description>A device attestation certificate (DAC) from the server.</description>
<arg name="Certificate" type="OCTET_STRING"/>
</command>

<command source="client" code="0x04" name="OpCSRRequest" optional="false">
<description>Sender is requesting a certificate signing request (CSR) from the receiver.</description>
<arg name="CSRNonce" type="OCTET_STRING"/>
Expand Down
78 changes: 78 additions & 0 deletions src/controller/CHIPDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,9 @@ void Device::Reset()
mExchangeMgr->CloseAllContextsForDelegate(this);
}
mExchangeMgr = nullptr;

ReleaseDAC();
ReleasePAI();
}

CHIP_ERROR Device::LoadSecureSessionParameters(ResetTransport resetNeeded)
Expand Down Expand Up @@ -624,6 +627,78 @@ void Device::OnSessionEstablished()
}
}

void Device::ReleaseDAC()
{
if (mDAC != nullptr)
{
Platform::MemoryFree(mDAC);
}
mDACLen = 0;
mDAC = nullptr;
}

CHIP_ERROR Device::SetDAC(const ByteSpan & dac)
{
if (dac.size() == 0)
{
ReleaseDAC();
return CHIP_NO_ERROR;
}

VerifyOrReturnError(dac.size() <= Crypto::kMax_x509_Certificate_Length, CHIP_ERROR_INVALID_ARGUMENT);
if (mDACLen != 0)
{
ReleaseDAC();
}

VerifyOrReturnError(CanCastTo<uint16_t>(dac.size()), CHIP_ERROR_INVALID_ARGUMENT);
if (mDAC == nullptr)
{
mDAC = static_cast<uint8_t *>(chip::Platform::MemoryAlloc(dac.size()));
}
VerifyOrReturnError(mDAC != nullptr, CHIP_ERROR_NO_MEMORY);
mDACLen = static_cast<uint16_t>(dac.size());
memcpy(mDAC, dac.data(), mDACLen);

return CHIP_NO_ERROR;
}

void Device::ReleasePAI()
{
if (mPAI != nullptr)
{
chip::Platform::MemoryFree(mPAI);
}
mPAILen = 0;
mPAI = nullptr;
}

CHIP_ERROR Device::SetPAI(const chip::ByteSpan & pai)
{
if (pai.size() == 0)
{
ReleasePAI();
return CHIP_NO_ERROR;
}

VerifyOrReturnError(pai.size() <= Crypto::kMax_x509_Certificate_Length, CHIP_ERROR_INVALID_ARGUMENT);
if (mPAILen != 0)
{
ReleasePAI();
}

VerifyOrReturnError(CanCastTo<uint16_t>(pai.size()), CHIP_ERROR_INVALID_ARGUMENT);
if (mPAI == nullptr)
{
mPAI = static_cast<uint8_t *>(chip::Platform::MemoryAlloc(pai.size()));
}
VerifyOrReturnError(mPAI != nullptr, CHIP_ERROR_NO_MEMORY);
mPAILen = static_cast<uint16_t>(pai.size());
memcpy(mPAI, pai.data(), mPAILen);

return CHIP_NO_ERROR;
}

CHIP_ERROR Device::EstablishConnectivity(Callback::Callback<OnDeviceConnected> * onConnection,
Callback::Callback<OnDeviceConnectionFailure> * onFailure)
{
Expand Down Expand Up @@ -752,6 +827,9 @@ Device::~Device()
// point.
mExchangeMgr->CloseAllContextsForDelegate(this);
}

ReleaseDAC();
ReleasePAI();
}

CHIP_ERROR Device::ReduceNOCChainBufferSize(size_t new_size)
Expand Down
31 changes: 29 additions & 2 deletions src/controller/CHIPDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,9 @@ class DeviceController;
class DeviceStatusDelegate;
struct SerializedDevice;

constexpr size_t kMaxBlePendingPackets = 1;
constexpr uint32_t kOpCSRNonceLength = 32;
constexpr size_t kMaxBlePendingPackets = 1;
constexpr size_t kOpCSRNonceLength = 32;
constexpr size_t kAttestationNonceLength = 32;

using DeviceTransportMgr = TransportMgr<Transport::UDP /* IPv6 */
#if INET_CONFIG_ENABLE_IPV4
Expand Down Expand Up @@ -412,6 +413,23 @@ class DLL_EXPORT Device : public Messaging::ExchangeDelegate, public SessionEsta

ByteSpan GetCSRNonce() const { return ByteSpan(mCSRNonce, sizeof(mCSRNonce)); }

CHIP_ERROR SetAttestationNonce(ByteSpan attestationNonce)
{
VerifyOrReturnError(attestationNonce.size() == sizeof(mAttestationNonce), CHIP_ERROR_INVALID_ARGUMENT);
memcpy(mAttestationNonce, attestationNonce.data(), attestationNonce.size());
return CHIP_NO_ERROR;
}

ByteSpan GetAttestationNonce() const { return ByteSpan(mAttestationNonce, sizeof(mAttestationNonce)); }

bool AreCredentialsAvailable() const { return (mDAC != nullptr && mDACLen != 0); }

ByteSpan GetDAC() const { return ByteSpan(mDAC, mDACLen); }
ByteSpan GetPAI() const { return ByteSpan(mPAI, mPAILen); }

CHIP_ERROR SetDAC(const ByteSpan & dac);
CHIP_ERROR SetPAI(const ByteSpan & pai);

MutableByteSpan GetMutableNOCChain() { return MutableByteSpan(mNOCChainBuffer, sizeof(mNOCChainBuffer)); }

CHIP_ERROR ReduceNOCChainBufferSize(size_t new_size);
Expand Down Expand Up @@ -510,6 +528,9 @@ class DLL_EXPORT Device : public Messaging::ExchangeDelegate, public SessionEsta

CHIP_ERROR WarmupCASESession();

void ReleaseDAC();
void ReleasePAI();

static void OnOpenPairingWindowSuccessResponse(void * context);
static void OnOpenPairingWindowFailureResponse(void * context, uint8_t status);

Expand All @@ -525,6 +546,12 @@ class DLL_EXPORT Device : public Messaging::ExchangeDelegate, public SessionEsta
PersistentStorageDelegate * mStorageDelegate = nullptr;

uint8_t mCSRNonce[kOpCSRNonceLength];
uint8_t mAttestationNonce[kAttestationNonceLength];

uint8_t * mDAC = nullptr;
uint16_t mDACLen = 0;
uint8_t * mPAI = nullptr;
uint16_t mPAILen = 0;

// The chain can contain ICAC and OpCert
uint8_t mNOCChainBuffer[Credentials::kMaxCHIPCertLength * 2];
Expand Down
Loading

0 comments on commit d5e476f

Please sign in to comment.