blob: df8f61fd754cd8eb20b01db96ac65be43b49c59a [file] [log] [blame]
/*
*
* Copyright (c) 2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file implements unit tests for the Weave key export protocol.
*
*/
#include <stdio.h>
#include <nltest.h>
#include "ToolCommon.h"
#include "TestGroupKeyStore.h"
#include <Weave/Support/ErrorStr.h>
#include <Weave/Core/WeaveTLV.h>
#include <Weave/Profiles/security/WeaveSig.h>
#include <Weave/Profiles/security/WeaveSecurity.h>
#include <Weave/Profiles/security/WeaveKeyExport.h>
#include <Weave/Profiles/security/WeavePrivateKey.h>
#include <Weave/Profiles/security/WeaveApplicationKeys.h>
#include <Weave/Support/crypto/EllipticCurve.h>
#include <Weave/Support/NestCerts.h>
#include <Weave/Support/ASN1.h>
#if INET_LWIP
#include "lwip/tcpip.h"
#endif // INET_LWIP
using namespace nl::Inet;
using namespace nl::Weave::TLV;
using namespace nl::Weave::Profiles::Security;
using namespace nl::Weave::Profiles::Security::KeyExport;
using namespace nl::Weave::Profiles::Security::AppKeys;
using namespace nl::Weave::ASN1;
using nl::Weave::Crypto::EncodedECPublicKey;
using nl::Weave::Crypto::EncodedECPrivateKey;
#define DEBUG_PRINT_ENABLE 0
static WEAVE_ERROR InitValidationContext(ValidationContext& validContext)
{
WEAVE_ERROR err;
ASN1UniversalTime validTime;
// Arrange to validate the signature for code signing purposes.
memset(&validContext, 0, sizeof(validContext));
validContext.RequiredKeyUsages = kKeyUsageFlag_DigitalSignature;
// Set the effective validation time.
validTime.Year = 2017;
validTime.Month = 01;
validTime.Day = 31;
validTime.Hour = validTime.Minute = validTime.Second = 0;
err = PackCertTime(validTime, validContext.EffectiveTime);
SuccessOrExit(err);
exit:
return err;
}
class InitiatorKeyExportDelegate : public WeaveKeyExportDelegate
{
private:
enum
{
// Max Device Private Key Size -- Size of the temporary buffer used to hold
// a device's TLV encoded private key.
kMaxDevicePrivateKeySize = 300,
// Max Validation Certs -- This controls the maximum number of certificates
// that can be involved in the validation of an image signature. It must
// include room for the signing cert, the trust anchors and any intermediate
// certs included in the signature object.
kMaxCerts = 4,
// Certificate Decode Buffer Size -- Size of the temporary buffer used to decode
// certs. The buffer must be big enough to hold the ASN1 DER encoding of the
// TBSCertificate portion of the largest cert involved in signature verification.
// Note that all certificates included in the signature are decoded using this
// buffer, even if they are ultimately not involved in verifying the image
// signature.
kCertDecodeBufferSize = 644
};
public:
// Get the key export certificate set for the local node.
virtual WEAVE_ERROR GetNodeCertSet(bool isInitiator, WeaveCertificateSet& certSet)
{
WEAVE_ERROR err;
WeaveCertificateData *cert;
bool certSetInitialized = false;
VerifyOrExit(isInitiator, err = WEAVE_ERROR_INVALID_ARGUMENT);
// Initialize certificate set.
err = certSet.Init(kMaxCerts, kCertDecodeBufferSize, nl::Weave::Platform::Security::MemoryAlloc, nl::Weave::Platform::Security::MemoryFree);
SuccessOrExit(err);
certSetInitialized = true;
// Load Nest development root cert and mark it trusted.
err = certSet.LoadCert(nl::NestCerts::Development::Root::Cert, nl::NestCerts::Development::Root::CertLength, 0, cert);
SuccessOrExit(err);
cert->CertFlags |= kCertFlag_IsTrusted;
// Load the intermediate (DeviceCA) cert.
err = certSet.LoadCert(nl::NestCerts::Development::DeviceCA::Cert, nl::NestCerts::Development::DeviceCA::CertLength, 0, cert);
SuccessOrExit(err);
// Load the signing cert.
err = certSet.LoadCert(TestDevice1_Cert, TestDevice1_CertLength, 0, cert);
SuccessOrExit(err);
exit:
if (err != WEAVE_NO_ERROR && certSetInitialized)
certSet.Release();
return err;
}
// Called when the key export engine is done with the certificate set returned by GetNodeCertSet().
virtual WEAVE_ERROR ReleaseNodeCertSet(bool isInitiator, WeaveCertificateSet& certSet)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(isInitiator, err = WEAVE_ERROR_INVALID_ARGUMENT);
certSet.Release();
exit:
return err;
}
// Get the local node's private key.
virtual WEAVE_ERROR GetNodePrivateKey(bool isInitiator, const uint8_t *& weavePrivKey, uint16_t& weavePrivKeyLen)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(isInitiator, err = WEAVE_ERROR_INVALID_ARGUMENT);
weavePrivKey = (uint8_t *)nl::Weave::Platform::Security::MemoryAlloc(kMaxDevicePrivateKeySize);
VerifyOrExit(weavePrivKey != NULL, err = WEAVE_ERROR_NO_MEMORY);
memcpy((uint8_t *)weavePrivKey, TestDevice1_PrivateKey, TestDevice1_PrivateKeyLength);
weavePrivKeyLen = TestDevice1_PrivateKeyLength;
exit:
return err;
}
// Called when the key export engine is done with the buffer returned by GetNodePrivateKey().
virtual WEAVE_ERROR ReleaseNodePrivateKey(bool isInitiator, const uint8_t *& weavePrivKey)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(isInitiator, err = WEAVE_ERROR_INVALID_ARGUMENT);
if (weavePrivKey != NULL)
{
nl::Weave::Platform::Security::MemoryFree((void *)weavePrivKey);
weavePrivKey = NULL;
}
exit:
return err;
}
// Prepare the supplied certificate set and validation context for use in validating the certificate of a peer.
// This method is responsible for loading the trust anchors into the certificate set.
virtual WEAVE_ERROR BeginCertValidation(bool isInitiator, WeaveCertificateSet& certSet, ValidationContext& validContext)
{
WEAVE_ERROR err;
WeaveCertificateData *cert;
bool certSetInitialized = false;
VerifyOrExit(isInitiator, err = WEAVE_ERROR_INVALID_ARGUMENT);
// Initialize certificate set.
err = certSet.Init(kMaxCerts, kCertDecodeBufferSize, nl::Weave::Platform::Security::MemoryAlloc, nl::Weave::Platform::Security::MemoryFree);
SuccessOrExit(err);
certSetInitialized = true;
// Load Nest development root cert and mark it trusted.
err = certSet.LoadCert(nl::NestCerts::Development::Root::Cert, nl::NestCerts::Development::Root::CertLength, 0, cert);
SuccessOrExit(err);
cert->CertFlags |= kCertFlag_IsTrusted;
// Initialize the validation context.
InitValidationContext(validContext);
exit:
if (err != WEAVE_NO_ERROR && certSetInitialized)
certSet.Release();
return err;
}
// Called with the results of validating the peer's certificate.
// Requestor verifies that response came from expected node.
virtual WEAVE_ERROR HandleCertValidationResult(bool isInitiator, WeaveCertificateSet& certSet, ValidationContext& validContext,
const IPPacketInfo *pktInfo, const WeaveMessageInfo *msgInfo, uint32_t requestedKeyId)
{
WEAVE_ERROR err;
VerifyOrExit(isInitiator, err = WEAVE_ERROR_INVALID_ARGUMENT);
// Client root key export response message should be signed by the Weave device certificate.
if (requestedKeyId == WeaveKeyId::kClientRootKey &&
validContext.SigningCert->SubjectDN.AttrOID == ASN1::kOID_AttributeType_WeaveDeviceId)
err = WEAVE_NO_ERROR;
else
err = WEAVE_ERROR_UNAUTHORIZED_KEY_EXPORT_RESPONSE;
exit:
return err;
}
// Called when peer certificate validation is complete.
virtual WEAVE_ERROR EndCertValidation(bool isInitiator, WeaveCertificateSet& certSet, ValidationContext& validContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(isInitiator, err = WEAVE_ERROR_INVALID_ARGUMENT);
certSet.Release();
exit:
return err;
}
// Called by requestor and responder to verify that received message was appropriately secured when the message isn't signed.
virtual WEAVE_ERROR ValidateUnsignedKeyExportMessage(bool isInitiator, const IPPacketInfo *pktInfo, const WeaveMessageInfo *msgInfo, uint32_t requestedKeyId)
{
WEAVE_ERROR err;
VerifyOrExit(isInitiator, err = WEAVE_ERROR_INVALID_ARGUMENT);
// IN THIS TEST ONLY:
// - Fabric secret can be exported by any Weave node if the request/response messages are encrypted
// with session key, which was created during PASE handshake.
// - Intermediate application key can be exported by the service end point.
// IN THE REAL IMPLEMENTATION:
// - Currently there is no use case where fabric secret or any other key can be exported if the
// request/response messages are unsigned. This function should always return
// WEAVE_ERROR_UNAUTHORIZED_KEY_EXPORT_RESPONSE error.
if (requestedKeyId == WeaveKeyId::kFabricSecret &&
WeaveKeyId::IsSessionKey(msgInfo->KeyId) &&
msgInfo->PeerAuthMode == kWeaveAuthMode_PASE_PairingCode)
err = WEAVE_NO_ERROR;
else if (WeaveKeyId::GetType(requestedKeyId) == WeaveKeyId::kType_AppIntermediateKey &&
WeaveKeyId::IsSessionKey(msgInfo->KeyId) &&
msgInfo->PeerAuthMode == kWeaveAuthMode_CASE_ServiceEndPoint)
err = WEAVE_NO_ERROR;
else
err = WEAVE_ERROR_UNAUTHORIZED_KEY_EXPORT_RESPONSE;
exit:
return err;
}
};
class ResponderKeyExportDelegate : public WeaveKeyExportDelegate
{
private:
enum
{
// Max Device Private Key Size -- Size of the temporary buffer used to hold
// a device's TLV encoded private key.
kMaxDevicePrivateKeySize = 300,
// Max Validation Certs -- This controls the maximum number of certificates
// that can be involved in the validation of an image signature. It must
// include room for the signing cert, the trust anchors and any intermediate
// certs included in the signature object.
kMaxCerts = 4,
// Certificate Decode Buffer Size -- Size of the temporary buffer used to decode
// certs. The buffer must be big enough to hold the ASN1 DER encoding of the
// TBSCertificate portion of the largest cert involved in signature verification.
// Note that all certificates included in the signature are decoded using this
// buffer, even if they are ultimately not involved in verifying the image
// signature.
kCertDecodeBufferSize = 644
};
public:
// Get the key export certificate set for the local node.
virtual WEAVE_ERROR GetNodeCertSet(bool isInitiator, WeaveCertificateSet& certSet)
{
WEAVE_ERROR err;
WeaveCertificateData *cert;
bool certSetInitialized = false;
VerifyOrExit(!isInitiator, err = WEAVE_ERROR_INVALID_ARGUMENT);
// Initialize certificate set.
err = certSet.Init(kMaxCerts, kCertDecodeBufferSize, nl::Weave::Platform::Security::MemoryAlloc, nl::Weave::Platform::Security::MemoryFree);
SuccessOrExit(err);
certSetInitialized = true;
// Load Nest development root cert and mark it trusted.
err = certSet.LoadCert(nl::NestCerts::Development::Root::Cert, nl::NestCerts::Development::Root::CertLength, 0, cert);
SuccessOrExit(err);
cert->CertFlags |= kCertFlag_IsTrusted;
// Load the intermediate (DeviceCA) cert.
err = certSet.LoadCert(nl::NestCerts::Development::DeviceCA::Cert, nl::NestCerts::Development::DeviceCA::CertLength, 0, cert);
SuccessOrExit(err);
// Load the signing cert.
err = certSet.LoadCert(TestDevice2_Cert, TestDevice2_CertLength, 0, cert);
SuccessOrExit(err);
exit:
if (err != WEAVE_NO_ERROR && certSetInitialized)
certSet.Release();
return err;
}
// Called when the key export engine is done with the certificate set returned by GetNodeCertSet().
virtual WEAVE_ERROR ReleaseNodeCertSet(bool isInitiator, WeaveCertificateSet& certSet)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(!isInitiator, err = WEAVE_ERROR_INVALID_ARGUMENT);
certSet.Release();
exit:
return err;
}
// Get the local node's private key.
virtual WEAVE_ERROR GetNodePrivateKey(bool isInitiator, const uint8_t *& weavePrivKey, uint16_t& weavePrivKeyLen)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(!isInitiator, err = WEAVE_ERROR_INVALID_ARGUMENT);
weavePrivKey = (uint8_t *)nl::Weave::Platform::Security::MemoryAlloc(kMaxDevicePrivateKeySize);
VerifyOrExit(weavePrivKey != NULL, err = WEAVE_ERROR_NO_MEMORY);
memcpy((uint8_t *)weavePrivKey, TestDevice2_PrivateKey, TestDevice2_PrivateKeyLength);
weavePrivKeyLen = TestDevice2_PrivateKeyLength;
exit:
return err;
}
// Called when the key export engine is done with the buffer returned by GetNodePrivateKey().
virtual WEAVE_ERROR ReleaseNodePrivateKey(bool isInitiator, const uint8_t *& weavePrivKey)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(!isInitiator, err = WEAVE_ERROR_INVALID_ARGUMENT);
if (weavePrivKey != NULL)
{
nl::Weave::Platform::Security::MemoryFree((void *)weavePrivKey);
weavePrivKey = NULL;
}
exit:
return err;
}
// Prepare the supplied certificate set and validation context for use in validating the certificate of a peer.
// This method is responsible for loading the trust anchors into the certificate set.
virtual WEAVE_ERROR BeginCertValidation(bool isInitiator, WeaveCertificateSet& certSet, ValidationContext& validContext)
{
WEAVE_ERROR err;
WeaveCertificateData *cert;
bool certSetInitialized = false;
VerifyOrExit(!isInitiator, err = WEAVE_ERROR_INVALID_ARGUMENT);
// Initialize certificate set.
err = certSet.Init(kMaxCerts, kCertDecodeBufferSize, nl::Weave::Platform::Security::MemoryAlloc, nl::Weave::Platform::Security::MemoryFree);
SuccessOrExit(err);
certSetInitialized = true;
// Load Nest development root cert and mark it trusted.
err = certSet.LoadCert(nl::NestCerts::Development::Root::Cert, nl::NestCerts::Development::Root::CertLength, 0, cert);
SuccessOrExit(err);
cert->CertFlags |= kCertFlag_IsTrusted;
// Initialize the validation context.
InitValidationContext(validContext);
exit:
if (err != WEAVE_NO_ERROR && certSetInitialized)
certSet.Release();
return err;
}
// Called with the results of validating the peer's certificate.
// Responder verifies that requestor is authorized to export the specified key.
virtual WEAVE_ERROR HandleCertValidationResult(bool isInitiator, WeaveCertificateSet& certSet, ValidationContext& validContext,
const IPPacketInfo *pktInfo, const WeaveMessageInfo *msgInfo, uint32_t requestedKeyId)
{
WEAVE_ERROR err;
VerifyOrExit(!isInitiator, err = WEAVE_ERROR_INVALID_ARGUMENT);
// IN THIS TEST ONLY:
// - Client root key can be exported by any Weave node if the request message was signed
// and the trust anchor is Nest root certificate.
// IN THE REAL IMPLEMENTATION:
// - Client root key can be exported only by mobiles, i.e. the trust anchor should be an access
// token certificate.
if (requestedKeyId == WeaveKeyId::kClientRootKey && validContext.TrustAnchor == &certSet.Certs[0])
err = WEAVE_NO_ERROR;
else
err = WEAVE_ERROR_UNAUTHORIZED_KEY_EXPORT_REQUEST;
exit:
return err;
}
// Called when peer certificate validation is complete.
virtual WEAVE_ERROR EndCertValidation(bool isInitiator, WeaveCertificateSet& certSet, ValidationContext& validContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(!isInitiator, err = WEAVE_ERROR_INVALID_ARGUMENT);
certSet.Release();
exit:
return err;
}
// Called by requestor and responder to verify that received message was appropriately secured when the message isn't signed.
virtual WEAVE_ERROR ValidateUnsignedKeyExportMessage(bool isInitiator, const IPPacketInfo *pktInfo, const WeaveMessageInfo *msgInfo, uint32_t requestedKeyId)
{
WEAVE_ERROR err;
VerifyOrExit(!isInitiator, err = WEAVE_ERROR_INVALID_ARGUMENT);
// IN THIS TEST ONLY:
// - Fabric secret can be exported by any Weave node if the request/response messages are encrypted
// with session key, which was created during PASE handshake.
// - Intermediate application key can be exported by the service end point.
// IN THE REAL IMPLEMENTATION:
// - Currently there is no use case where fabric secret or any other key can be exported if the
// request/response messages are unsigned. This function should always return
// WEAVE_ERROR_UNAUTHORIZED_KEY_EXPORT_REQUEST error.
if (requestedKeyId == WeaveKeyId::kFabricSecret &&
WeaveKeyId::IsSessionKey(msgInfo->KeyId) &&
msgInfo->PeerAuthMode == kWeaveAuthMode_PASE_PairingCode)
err = WEAVE_NO_ERROR;
else if (WeaveKeyId::GetType(requestedKeyId) == WeaveKeyId::kType_AppIntermediateKey &&
WeaveKeyId::IsSessionKey(msgInfo->KeyId) &&
msgInfo->PeerAuthMode == kWeaveAuthMode_CASE_ServiceEndPoint)
err = WEAVE_NO_ERROR;
else
err = WEAVE_ERROR_UNAUTHORIZED_KEY_EXPORT_REQUEST;
exit:
return err;
}
};
enum
{
kKeyExportRequestError_None = 0,
kKeyExportRequestError_InvalidConfig,
};
enum
{
kKeyExportResponseError_None = 0,
kKeyExportResponseError_Reconfigure,
kKeyExportResponseError_NoCommonConfig,
};
// Test input vector format.
struct TestContext {
uint8_t config;
bool signMessages;
const uint32_t* requestedKeyId;
const uint32_t* expectedKeyId;
const uint8_t* expectedKey;
const uint8_t* expectedKeyLen;
const uint16_t* msgKeyId;
WeaveAuthMode msgKeyAuthMode;
uint8_t requestErrorType;
uint8_t responseErrorType;
};
static const uint32_t sFabricSecretId = WeaveKeyId::kFabricSecret;
static const uint32_t sClientRootKeyId = WeaveKeyId::kClientRootKey;
static const uint16_t sNoneKeyId = WeaveKeyId::kNone;
// Test input data.
static struct TestContext sContext[] = {
// Proposed Config1 tests.
{ kKeyExportConfig_Config1, false, &sFabricSecretId, &sFabricSecretId, sFabricSecret,
&sFabricSecretLen, &sTestDefaultSessionKeyId, kWeaveAuthMode_PASE_PairingCode,
kKeyExportRequestError_InvalidConfig, kKeyExportResponseError_None },
{ kKeyExportConfig_Config1, false, &sIntermediateKeyId_FRK_EC, &sIntermediateKeyId_FRK_E2, sIntermediateKey_FRK_E2,
&sIntermediateKeyLen_FRK_E2, &sTestDefaultSessionKeyId, kWeaveAuthMode_CASE_ServiceEndPoint,
kKeyExportRequestError_None, kKeyExportResponseError_None },
{ kKeyExportConfig_Config1, true, &sClientRootKeyId, &sClientRootKeyId, sClientRootKey,
&sClientRootKeyLen, &sNoneKeyId, kWeaveAuthMode_Unauthenticated,
kKeyExportRequestError_None, kKeyExportResponseError_None },
// Proposed Config2 tests.
{ kKeyExportConfig_Config2, false, &sFabricSecretId, &sFabricSecretId, sFabricSecret,
&sFabricSecretLen, &sTestDefaultSessionKeyId, kWeaveAuthMode_PASE_PairingCode,
kKeyExportRequestError_None, kKeyExportResponseError_None },
{ kKeyExportConfig_Config2, false, &sIntermediateKeyId_FRK_EC, &sIntermediateKeyId_FRK_E2, sIntermediateKey_FRK_E2,
&sIntermediateKeyLen_FRK_E2, &sTestDefaultSessionKeyId, kWeaveAuthMode_CASE_ServiceEndPoint,
kKeyExportRequestError_InvalidConfig, kKeyExportResponseError_None },
{ kKeyExportConfig_Config2, true, &sClientRootKeyId, &sClientRootKeyId, sClientRootKey,
&sClientRootKeyLen, &sNoneKeyId, kWeaveAuthMode_Unauthenticated,
kKeyExportRequestError_None, kKeyExportResponseError_None },
// Proposed Config1 reconfigured to Config2 tests.
{ kKeyExportConfig_Config1, false, &sFabricSecretId, &sFabricSecretId, sFabricSecret,
&sFabricSecretLen, &sTestDefaultSessionKeyId, kWeaveAuthMode_PASE_PairingCode,
kKeyExportRequestError_None, kKeyExportResponseError_Reconfigure },
{ kKeyExportConfig_Config1, false, &sIntermediateKeyId_FRK_EC, &sIntermediateKeyId_FRK_E2, sIntermediateKey_FRK_E2,
&sIntermediateKeyLen_FRK_E2, &sTestDefaultSessionKeyId, kWeaveAuthMode_CASE_ServiceEndPoint,
kKeyExportRequestError_None, kKeyExportResponseError_Reconfigure },
{ kKeyExportConfig_Config1, true, &sClientRootKeyId, &sClientRootKeyId, sClientRootKey,
&sClientRootKeyLen, &sNoneKeyId, kWeaveAuthMode_Unauthenticated,
kKeyExportRequestError_InvalidConfig, kKeyExportResponseError_Reconfigure },
// Proposed Config2 reconfigured to Config1 tests.
{ kKeyExportConfig_Config2, false, &sFabricSecretId, &sFabricSecretId, sFabricSecret,
&sFabricSecretLen, &sTestDefaultSessionKeyId, kWeaveAuthMode_PASE_PairingCode,
kKeyExportRequestError_None, kKeyExportResponseError_Reconfigure },
{ kKeyExportConfig_Config2, false, &sIntermediateKeyId_FRK_EC, &sIntermediateKeyId_FRK_E2, sIntermediateKey_FRK_E2,
&sIntermediateKeyLen_FRK_E2, &sTestDefaultSessionKeyId, kWeaveAuthMode_CASE_ServiceEndPoint,
kKeyExportRequestError_None, kKeyExportResponseError_Reconfigure },
{ kKeyExportConfig_Config2, true, &sClientRootKeyId, &sClientRootKeyId, sClientRootKey,
&sClientRootKeyLen, &sNoneKeyId, kWeaveAuthMode_Unauthenticated,
kKeyExportRequestError_None, kKeyExportResponseError_Reconfigure },
// No common Configs for requester and responder tests.
{ kKeyExportConfig_Config1, false, &sFabricSecretId, &sFabricSecretId, sFabricSecret,
&sFabricSecretLen, &sTestDefaultSessionKeyId, kWeaveAuthMode_PASE_PairingCode,
kKeyExportRequestError_InvalidConfig, kKeyExportResponseError_NoCommonConfig },
{ kKeyExportConfig_Config2, false, &sIntermediateKeyId_FRK_EC, &sIntermediateKeyId_FRK_E2, sIntermediateKey_FRK_E2,
&sIntermediateKeyLen_FRK_E2, &sTestDefaultSessionKeyId, kWeaveAuthMode_CASE_ServiceEndPoint,
kKeyExportRequestError_None, kKeyExportResponseError_NoCommonConfig },
};
// Number of test context examples.
static const size_t kTestElements = sizeof(sContext) / sizeof(struct TestContext);
static void KeyExportProtocol_Test(nlTestSuite *inSuite, void *inContext)
{
WEAVE_ERROR err;
WeaveKeyExport initiatorEng;
WeaveKeyExport responderEng;
InetBuffer *msgBuf = NULL;
InitiatorKeyExportDelegate initiatorDelegate;
ResponderKeyExportDelegate responderDelegate;
TestGroupKeyStore keyStore;
uint32_t exportedKeyId;
uint16_t exportedKeyLen;
uint8_t exportedKey[AppKeys::kWeaveFabricSecretSize];
uint16_t dataLen;
IPPacketInfo pktInfo;
WeaveMessageInfo msgInfo;
struct TestContext *theContext = (struct TestContext *)(inContext);
for (size_t ith = 0; ith < kTestElements; ith++, theContext++)
{
uint8_t proposedConfig = theContext->config;
bool signMessages = theContext->signMessages;
uint32_t keyId = *theContext->requestedKeyId;
uint32_t expectedKeyId = *theContext->expectedKeyId;
const uint8_t* expectedKey = theContext->expectedKey;
uint8_t expectedKeyLen = *theContext->expectedKeyLen;
msgInfo.Clear();
msgInfo.KeyId = *theContext->msgKeyId;
msgInfo.PeerAuthMode = theContext->msgKeyAuthMode;
sCurrentUTCTime = sEpochKey2_StartTime + 1;
if (theContext->responseErrorType == kKeyExportResponseError_Reconfigure)
printf("Running Key Export Protocol Test with proposed Config%d (Reconfigured to Config%d) with %s messages to export KeyId = %08X.\n",
proposedConfig, (KeyExport::kKeyExportSupportedConfig_All & ~proposedConfig), (signMessages ? "Signed" : "Unsigned"), keyId);
else if (theContext->responseErrorType == kKeyExportResponseError_NoCommonConfig)
printf("Running Key Export Protocol Test with proposed Config%d while responder only supports Config%d, which results in NoCommonConfig error.\n",
proposedConfig, (KeyExport::kKeyExportSupportedConfig_All & ~proposedConfig));
else
printf("Running Key Export Protocol Test with proposed Config%d with %s messages to export KeyId = %08X.\n",
proposedConfig, (signMessages ? "Signed" : "Unsigned"), keyId);
{
initiatorEng.Reset();
initiatorEng.Init(&initiatorDelegate);
if (theContext->requestErrorType == kKeyExportRequestError_InvalidConfig)
initiatorEng.SetAllowedConfigs(0);
else if (theContext->responseErrorType == kKeyExportResponseError_NoCommonConfig)
initiatorEng.SetAllowedConfigs(proposedConfig);
else
initiatorEng.SetAllowedConfigs(KeyExport::kKeyExportSupportedConfig_All);
msgBuf = InetBuffer::New();
NL_TEST_ASSERT(inSuite, msgBuf != NULL);
err = initiatorEng.GenerateKeyExportRequest(msgBuf->Start(), msgBuf->AvailableDataLength(), dataLen, proposedConfig, keyId, signMessages);
if (theContext->requestErrorType == kKeyExportRequestError_InvalidConfig)
{
NL_TEST_ASSERT(inSuite, err == WEAVE_ERROR_INVALID_KEY_EXPORT_CONFIGURATION);
initiatorEng.Reset();
initiatorEng.Init(&initiatorDelegate);
if (theContext->responseErrorType == kKeyExportResponseError_NoCommonConfig)
initiatorEng.SetAllowedConfigs(proposedConfig);
else
initiatorEng.SetAllowedConfigs(KeyExport::kKeyExportSupportedConfig_All);
InetBuffer::Free(msgBuf);
msgBuf = InetBuffer::New();
NL_TEST_ASSERT(inSuite, msgBuf != NULL);
err = initiatorEng.GenerateKeyExportRequest(msgBuf->Start(), msgBuf->AvailableDataLength(), dataLen, proposedConfig, keyId, signMessages);
}
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
msgBuf->SetDataLength(dataLen);
}
#if DEBUG_PRINT_ENABLE
printf("KeyExportRequest Message (%d bytes):\n", msgBuf->DataLength());
DumpMemoryCStyle(msgBuf->Start(), msgBuf->DataLength(), " ", 16);
#endif
{
responderEng.Reset();
responderEng.Init(&responderDelegate, &keyStore);
if (theContext->responseErrorType == kKeyExportResponseError_Reconfigure ||
theContext->responseErrorType == kKeyExportResponseError_NoCommonConfig)
responderEng.SetAllowedConfigs(KeyExport::kKeyExportSupportedConfig_All & ~proposedConfig);
else
responderEng.SetAllowedConfigs(KeyExport::kKeyExportSupportedConfig_All);
err = responderEng.ProcessKeyExportRequest(msgBuf->Start(), msgBuf->DataLength(), &pktInfo, &msgInfo);
if (theContext->responseErrorType == kKeyExportResponseError_Reconfigure)
NL_TEST_ASSERT(inSuite, err == WEAVE_ERROR_KEY_EXPORT_RECONFIGURE_REQUIRED);
else if (theContext->responseErrorType == kKeyExportResponseError_NoCommonConfig)
NL_TEST_ASSERT(inSuite, err == WEAVE_ERROR_NO_COMMON_KEY_EXPORT_CONFIGURATIONS);
else
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
InetBuffer::Free(msgBuf);
msgBuf = NULL;
if (theContext->responseErrorType == kKeyExportResponseError_Reconfigure)
{
msgBuf = InetBuffer::New();
NL_TEST_ASSERT(inSuite, msgBuf != NULL);
err = responderEng.GenerateKeyExportReconfigure(msgBuf->Start(), msgBuf->AvailableDataLength(), dataLen);
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
msgBuf->SetDataLength(dataLen);
#if DEBUG_PRINT_ENABLE
printf("KeyExportReconfigure Message (%d bytes):\n", msgBuf->DataLength());
DumpMemoryCStyle(msgBuf->Start(), msgBuf->DataLength(), " ", 16);
#endif
uint8_t newConfig;
err = initiatorEng.ProcessKeyExportReconfigure(msgBuf->Start(), msgBuf->DataLength(), newConfig);
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
InetBuffer::Free(msgBuf);
msgBuf = NULL;
msgBuf = InetBuffer::New();
NL_TEST_ASSERT(inSuite, msgBuf != NULL);
err = initiatorEng.GenerateKeyExportRequest(msgBuf->Start(), msgBuf->AvailableDataLength(), dataLen, newConfig, keyId, signMessages);
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
msgBuf->SetDataLength(dataLen);
#if DEBUG_PRINT_ENABLE
printf("Reconfigured KeyExportRequest Message (%d bytes):\n", msgBuf->DataLength());
DumpMemoryCStyle(msgBuf->Start(), msgBuf->DataLength(), " ", 16);
#endif
// For responder this request is unrelated to the previous key export request.
responderEng.Reset();
responderEng.Init(&responderDelegate, &keyStore);
responderEng.SetAllowedConfigs(KeyExport::kKeyExportSupportedConfig_All & ~proposedConfig);
err = responderEng.ProcessKeyExportRequest(msgBuf->Start(), msgBuf->DataLength(), &pktInfo, &msgInfo);
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
InetBuffer::Free(msgBuf);
msgBuf = NULL;
}
else if (theContext->responseErrorType == kKeyExportResponseError_NoCommonConfig)
{
continue;
}
msgBuf = InetBuffer::New();
NL_TEST_ASSERT(inSuite, msgBuf != NULL);
err = responderEng.GenerateKeyExportResponse(msgBuf->Start(), msgBuf->AvailableDataLength(), dataLen);
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
msgBuf->SetDataLength(dataLen);
}
#if DEBUG_PRINT_ENABLE
printf("KeyExportResponse Message (%d bytes):\n", msgBuf->DataLength());
DumpMemoryCStyle(msgBuf->Start(), msgBuf->DataLength(), " ", 16);
#endif
{
err = initiatorEng.ProcessKeyExportResponse(msgBuf->Start(), msgBuf->DataLength(), &pktInfo, &msgInfo,
exportedKey, sizeof(exportedKey), exportedKeyLen, exportedKeyId);
NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR);
InetBuffer::Free(msgBuf);
msgBuf = NULL;
}
#if DEBUG_PRINT_ENABLE
printf("Exported Client Root Key:\n");
DumpMemoryCStyle(exportedKey, exportedKeyLen, " ", 16);
#endif
// Compare the exported key.
NL_TEST_ASSERT(inSuite, exportedKeyId == expectedKeyId);
NL_TEST_ASSERT(inSuite, exportedKeyLen == expectedKeyLen);
NL_TEST_ASSERT(inSuite, memcmp(exportedKey, expectedKey, exportedKeyLen) == 0);
}
}
/**
* Test Suite. It lists all the test functions.
*/
static const nlTest sTests[] = {
NL_TEST_DEF("KeyExportProtocol", KeyExportProtocol_Test),
NL_TEST_SENTINEL()
};
int main(int argc, char *argv[])
{
WEAVE_ERROR err;
nlTestSuite testSuite = {
"weave-key-export-protocol",
&sTests[0]
};
#if INET_LWIP
tcpip_init(NULL, NULL);
#endif // INET_LWIP
err = nl::Weave::Platform::Security::InitSecureRandomDataSource(NULL, 64, NULL, 0);
FAIL_ERROR(err, "InitSecureRandomDataSource() failed");
nl_test_set_output_style(OUTPUT_CSV);
nlTestRunner(&testSuite, &sContext);
return nlTestRunnerStats(&testSuite);
}