blob: a4010d8875b17dcf52df30ebac1d24b414da667c [file] [log] [blame]
/*
*
* Copyright (c) 2019-2020 Google LLC.
* 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.
*/
#include <Weave/DeviceLayer/internal/WeaveDeviceLayerInternal.h>
#include <Weave/DeviceLayer/internal/CertificateProvisioningClient.h>
#include <Weave/Core/WeaveTLV.h>
#include <Weave/Profiles/common/CommonProfile.h>
#include <Weave/Profiles/security/WeaveCert.h>
#include <Weave/Profiles/security/WeavePrivateKey.h>
#include <Weave/Profiles/security/WeaveSig.h>
using namespace ::nl;
using namespace ::nl::Weave;
using namespace ::nl::Weave::TLV;
using namespace ::nl::Weave::Profiles;
using namespace ::nl::Weave::Profiles::Security;
using namespace ::nl::Weave::Profiles::Security::CertProvisioning;
using namespace ::nl::Weave::Platform::Security;
namespace nl {
namespace Weave {
namespace DeviceLayer {
namespace Internal {
using ::nl::Weave::Platform::Security::MemoryAlloc;
using ::nl::Weave::Platform::Security::MemoryFree;
#if WEAVE_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING
WEAVE_ERROR CertificateProvisioningClient::Init(uint8_t reqType)
{
return Init(reqType, NULL);
}
/**
* Initialize certificate provisioning client.
*
* @param[in] reqType Get certificate request type.
* @param[in] encodeReqAuthInfo A pointer to a function that generates ECDSA signature on the given
* certificate hash using operational device private key.
*
* @retval #WEAVE_NO_ERROR If certificate provisioning client was successfully initialized.
*/
WEAVE_ERROR CertificateProvisioningClient::Init(uint8_t reqType, EncodeReqAuthInfoFunct encodeReqAuthInfo)
{
mReqType = reqType;
mDoMfrAttest = (reqType == WeaveCertProvEngine::kReqType_GetInitialOpDeviceCert) ? true : false;
mEncodeReqAuthInfo = encodeReqAuthInfo;
mBinding = NULL;
mWaitingForServiceConnectivity = false;
return WEAVE_NO_ERROR;
}
/**
* Handler for Certificate Provisioning Client API events.
*
* @param[in] appState A pointer to application-defined state information associated with the client object.
* @param[in] eventType Event ID passed by the event callback.
* @param[in] inParam Reference of input event parameters passed by the event callback.
* @param[in] outParam Reference of output event parameters passed by the event callback.
*
*/
void CertificateProvisioningClient::CertProvClientEventHandler(void * appState, WeaveCertProvEngine::EventType eventType, const WeaveCertProvEngine::InEventParam & inParam, WeaveCertProvEngine::OutEventParam & outParam)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
CertificateProvisioningClient * client = static_cast<CertificateProvisioningClient *>(appState);
WeaveCertProvEngine * certProvEngine = inParam.Source;
switch (eventType)
{
case WeaveCertProvEngine::kEvent_PrepareAuthorizeInfo:
{
if (client->mEncodeReqAuthInfo != NULL)
{
WeaveLogProgress(DeviceLayer, "Preparing authorization information for the GetCertificateRequest message");
err = client->mEncodeReqAuthInfo(*inParam.PrepareAuthorizeInfo.Writer);
SuccessOrExit(err);
}
break;
}
case WeaveCertProvEngine::kEvent_ResponseReceived:
{
if (inParam.ResponseReceived.ReplaceCert)
{
// Store service issued operational device certificate.
err = ConfigurationMgr().StoreDeviceCertificate(inParam.ResponseReceived.Cert, inParam.ResponseReceived.CertLen);
SuccessOrExit(err);
if (inParam.ResponseReceived.RelatedCerts != NULL)
{
// Store device intermediate CA certificates related to the service issued operational device certificate.
err = ConfigurationMgr().StoreDeviceIntermediateCACerts(inParam.ResponseReceived.RelatedCerts, inParam.ResponseReceived.RelatedCertsLen);
SuccessOrExit(err);
}
// Post an event alerting other subsystems that the device now has new certificate.
{
WeaveDeviceEvent event;
event.Type = DeviceEventType::kDeviceCredentialsChange;
event.DeviceCredentialsChange.AreCredentialsProvisioned = true;
PlatformMgr().PostEvent(&event);
}
WeaveLogProgress(DeviceLayer, "Stored new operational device certificate received from the CA service");
}
else
{
WeaveLogProgress(DeviceLayer, "CA service reported: no need to replace operational device certificate");
}
certProvEngine->AbortCertificateProvisioning();
break;
}
case WeaveCertProvEngine::kEvent_CommunicationError:
{
if (inParam.CommunicationError.Reason == WEAVE_ERROR_STATUS_REPORT_RECEIVED)
{
WeaveLogError(DeviceLayer, "Received status report from the CA service: %s",
nl::StatusReportStr(inParam.CommunicationError.RcvdStatusReport->mProfileId, inParam.CommunicationError.RcvdStatusReport->mStatusCode));
}
else
{
WeaveLogError(DeviceLayer, "Failed to prepare/send GetCertificateRequest message: %s", ErrorStr(inParam.CommunicationError.Reason));
}
certProvEngine->AbortCertificateProvisioning();
break;
}
default:
WeaveLogError(DeviceLayer, "Unrecognized certificate provisioning API event");
break;
}
exit:
if (eventType == WeaveCertProvEngine::kEvent_PrepareAuthorizeInfo)
outParam.PrepareAuthorizeInfo.Error = err;
else if (eventType == WeaveCertProvEngine::kEvent_ResponseReceived)
outParam.ResponseReceived.Error = err;
}
// ===== Methods that implement the WeaveNodeOpAuthDelegate interface
WEAVE_ERROR CertificateProvisioningClient::EncodeOpCert(TLVWriter & writer, uint64_t tag)
{
WEAVE_ERROR err;
uint8_t * cert = NULL;
size_t certLen = 0;
// Determine the length of the operational device certificate.
err = ConfigurationMgr().GetDeviceCertificate((uint8_t *)NULL, 0, certLen);
SuccessOrExit(err);
// Fail if no operational device certificate has been configured.
VerifyOrExit(certLen != 0, err = WEAVE_ERROR_CERT_NOT_FOUND);
// Create a temporary buffer to hold the operational device certificate.
cert = static_cast<uint8_t *>(MemoryAlloc(certLen));
VerifyOrExit(cert != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Read the operational device certificate.
err = ConfigurationMgr().GetDeviceCertificate(cert, certLen, certLen);
SuccessOrExit(err);
// Copy encoded operational device certificate.
err = writer.CopyContainer(tag, cert, certLen);
SuccessOrExit(err);
exit:
if (cert != NULL)
{
MemoryFree(cert);
}
return err;
}
WEAVE_ERROR CertificateProvisioningClient::EncodeOpRelatedCerts(TLVWriter & writer, uint64_t tag)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uint8_t * icaCerts = NULL;
size_t icaCertsLen = 0;
// Determine if present and the length of the operational device intermediate CA certificates.
err = ConfigurationMgr().GetDeviceIntermediateCACerts((uint8_t *)NULL, 0, icaCertsLen);
if (err == WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND)
{
// Exit without error if operational device intermediate CA certificates is not configured.
ExitNow(err = WEAVE_NO_ERROR);
}
SuccessOrExit(err);
VerifyOrExit(icaCertsLen != 0, err = WEAVE_ERROR_CERT_NOT_FOUND);
// Create a temporary buffer to hold the operational device intermediate CA certificates.
icaCerts = static_cast<uint8_t *>(MemoryAlloc(icaCertsLen));
VerifyOrExit(icaCerts != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Read the operational device intermediate CA certificates.
err = ConfigurationMgr().GetDeviceCertificate(icaCerts, icaCertsLen, icaCertsLen);
SuccessOrExit(err);
// Copy encoded operational device intermediate CA certificates.
err = writer.CopyContainer(tag, icaCerts, icaCertsLen);
SuccessOrExit(err);
exit:
if (icaCerts != NULL)
{
MemoryFree(icaCerts);
}
return err;
}
WEAVE_ERROR CertificateProvisioningClient::GenerateAndEncodeOpSig(const uint8_t * hash, uint8_t hashLen, TLVWriter & writer, uint64_t tag)
{
WEAVE_ERROR err;
uint8_t * privKey = NULL;
size_t privKeyLen = 0;
// Determine the length of the operational device private key.
err = ConfigurationMgr().GetDevicePrivateKey((uint8_t *)NULL, 0, privKeyLen);
SuccessOrExit(err);
// Fail if no operational device private key has been configured.
VerifyOrExit(privKeyLen != 0, err = WEAVE_ERROR_KEY_NOT_FOUND);
// Create a temporary buffer to hold the operational device private key.
privKey = static_cast<uint8_t *>(MemoryAlloc(privKeyLen));
VerifyOrExit(privKey != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Read the operational device private key.
err = ConfigurationMgr().GetDevicePrivateKey(privKey, privKeyLen, privKeyLen);
SuccessOrExit(err);
// Generate and encode operational device signature.
err = GenerateAndEncodeWeaveECDSASignature(writer, tag, hash, hashLen, privKey, privKeyLen);
SuccessOrExit(err);
exit:
if (privKey != NULL)
{
MemoryFree(privKey);
}
return err;
}
// ===== Methods that implement the WeaveNodeMfrAttestDelegate interface
WEAVE_ERROR CertificateProvisioningClient::EncodeMAInfo(TLVWriter & writer)
{
WEAVE_ERROR err;
uint8_t * cert = NULL;
size_t certLen = 0;
size_t icaCertsLen = 0;
// Determine the length of the manufacturer assigned device certificate.
err = ConfigurationMgr().GetManufacturerDeviceCertificate((uint8_t *)NULL, 0, certLen);
SuccessOrExit(err);
// Fail if no manufacturer attestation device certificate has been configured.
VerifyOrExit(certLen != 0, err = WEAVE_ERROR_CERT_NOT_FOUND);
// Create a temporary buffer to hold the manufacturer attestation device certificate.
cert = static_cast<uint8_t *>(MemoryAlloc(certLen));
VerifyOrExit(cert != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Read the manufacturer assigned device certificate.
err = ConfigurationMgr().GetManufacturerDeviceCertificate(cert, certLen, certLen);
SuccessOrExit(err);
// Copy encoded manufacturer attestation device certificate.
err = writer.CopyContainer(ContextTag(kTag_GetCertReqMsg_MfrAttest_WeaveCert), cert, certLen);
SuccessOrExit(err);
// Determine if present and the length of the manufacturer assigned device intermediate CA certificates.
err = ConfigurationMgr().GetManufacturerDeviceIntermediateCACerts((uint8_t *)NULL, 0, icaCertsLen);
if (err == WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND)
{
// Exit without error if manufacturer assigned intermediate CA certificates is not configured.
ExitNow(err = WEAVE_NO_ERROR);
}
SuccessOrExit(err);
{
VerifyOrExit(icaCertsLen != 0, err = WEAVE_ERROR_CERT_NOT_FOUND);
// If needed, allocate larger buffer to hold the intermediate CA certificates.
if (icaCertsLen > certLen)
{
MemoryFree(cert);
cert = static_cast<uint8_t *>(MemoryAlloc(icaCertsLen));
VerifyOrExit(cert != NULL, err = WEAVE_ERROR_NO_MEMORY);
}
// Read the manufacturer assigned device intermediate CA certificates.
err = ConfigurationMgr().GetManufacturerDeviceIntermediateCACerts(cert, icaCertsLen, icaCertsLen);
SuccessOrExit(err);
// Copy encoded manufacturer attestation device intermediate CA certificates.
err = writer.CopyContainer(ContextTag(kTag_GetCertReqMsg_MfrAttest_WeaveRelCerts), cert, icaCertsLen);
SuccessOrExit(err);
}
exit:
if (cert != NULL)
{
MemoryFree(cert);
}
return err;
}
WEAVE_ERROR CertificateProvisioningClient::GenerateAndEncodeMASig(const uint8_t * data, uint16_t dataLen, TLVWriter & writer)
{
WEAVE_ERROR err;
uint8_t * privKey = NULL;
size_t privKeyLen = 0;
nl::Weave::Platform::Security::SHA256 sha256;
uint8_t hash[SHA256::kHashLength];
// Determine the length of the manufacturer attestation device private key.
err = ConfigurationMgr().GetManufacturerDevicePrivateKey((uint8_t *)NULL, 0, privKeyLen);
SuccessOrExit(err);
// Fail if no manufacturer attestation device private key has been configured.
VerifyOrExit(privKeyLen != 0, err = WEAVE_ERROR_KEY_NOT_FOUND);
// Create a temporary buffer to hold the manufacturer attestation device private key.
privKey = static_cast<uint8_t *>(MemoryAlloc(privKeyLen));
VerifyOrExit(privKey != NULL, err = WEAVE_ERROR_NO_MEMORY);
// Read the manufacturer attestation device private key.
err = ConfigurationMgr().GetManufacturerDevicePrivateKey(privKey, privKeyLen, privKeyLen);
SuccessOrExit(err);
// Calculate data hash.
sha256.Begin();
sha256.AddData(data, dataLen);
sha256.Finish(hash);
// Encode manufacturer attestation device signature algorithm: ECDSAWithSHA256.
err = writer.Put(ContextTag(kTag_GetCertReqMsg_MfrAttestSigAlgo), static_cast<uint16_t>(ASN1::kOID_SigAlgo_ECDSAWithSHA256));
SuccessOrExit(err);
// Generate and encode manufacturer attestation device signature.
err = GenerateAndEncodeWeaveECDSASignature(writer, ContextTag(kTag_GetCertReqMsg_MfrAttestSig_ECDSA),
hash, SHA256::kHashLength, privKey, privKeyLen);
SuccessOrExit(err);
exit:
if (privKey != NULL)
{
MemoryFree(privKey);
}
return err;
}
// ===== Members for internal use by this class only.
void CertificateProvisioningClient::OnPlatformEvent(const WeaveDeviceEvent * event)
{
// If a tunnel to the service has been established...
// OR if service connectivity has been established (e.g. via Thread)...
if ((event->Type == DeviceEventType::kServiceTunnelStateChange &&
event->ServiceTunnelStateChange.Result == kConnectivity_Established) ||
(event->Type == DeviceEventType::kServiceConnectivityChange &&
event->ServiceConnectivityChange.Overall.Result == kConnectivity_Established))
{
// If the system is waiting for the service connectivity to be established,
// initiate the Certificate Provisioning now.
if (mWaitingForServiceConnectivity)
{
StartCertificateProvisioning();
}
}
}
void CertificateProvisioningClient::StartCertificateProvisioning(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
// If the system does not currently have a tunnel established with the service,
// AND the system does not have service connectivity by some other means (e.g. Thread)
// wait a period of time for connectivity to be established.
if (!ConnectivityMgr().HaveServiceConnectivity() && !ConnectivityMgr().IsServiceTunnelConnected())
{
mWaitingForServiceConnectivity = true;
err = SystemLayer.StartTimer(WEAVE_DEVICE_CONFIG_CERTIFICATE_PROVISIONING_CONNECTIVITY_TIMEOUT,
HandleServiceConnectivityTimeout, this);
SuccessOrExit(err);
ExitNow();
WeaveLogProgress(DeviceLayer, "Waiting for service connectivity to complete RegisterServicePairDevice action");
}
mWaitingForServiceConnectivity = false;
SystemLayer.CancelTimer(HandleServiceConnectivityTimeout, this);
WeaveLogProgress(DeviceLayer, "Initiating communication with Service Provisioning service");
// Create a binding and begin the process of preparing it for talking to the Certificate Provisioning
// service. When this completes HandleCertProvBindingEvent will be called with a BindingReady event.
mBinding = nl::Weave::DeviceLayer::ExchangeMgr.NewBinding(HandleCertProvBindingEvent, NULL);
VerifyOrExit(mBinding != NULL, err = WEAVE_ERROR_NO_MEMORY);
err = mBinding->BeginConfiguration()
.Target_ServiceEndpoint(WEAVE_DEVICE_CONFIG_CERTIFICATE_PROVISIONING_ENDPOINT_ID)
.Transport_UDP_WRM()
.Exchange_ResponseTimeoutMsec(WEAVE_DEVICE_CONFIG_GET_CERTIFICATE_REQUEST_TIMEOUT)
.Security_SharedCASESession()
.PrepareBinding();
SuccessOrExit(err);
err = mCertProvEngine.Init(mBinding, this, this, CertProvClientEventHandler, this);
SuccessOrExit(err);
exit:
if (err != WEAVE_NO_ERROR)
{
HandleCertificateProvisioningResult(err, kWeaveProfile_Common, Profiles::Common::kStatus_InternalError);
}
}
void CertificateProvisioningClient::SendGetCertificateRequest(void)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
WeaveLogProgress(DeviceLayer, "Sending GetCertificateRequest to Certificate Provisioning service");
err = mCertProvEngine.StartCertificateProvisioning(mReqType, mDoMfrAttest);
SuccessOrExit(err);
exit:
if (err != WEAVE_NO_ERROR)
{
HandleCertificateProvisioningResult(err, kWeaveProfile_Common, Profiles::Common::kStatus_InternalError);
}
}
void CertificateProvisioningClient::HandleCertificateProvisioningResult(WEAVE_ERROR err, uint32_t statusReportProfileId, uint16_t statusReportStatusCode)
{
// Close the binding if necessary.
if (mBinding != NULL)
{
mBinding->Close();
mBinding = NULL;
}
if (err != WEAVE_NO_ERROR)
{
WeaveLogError(DeviceLayer, "Certificate Provisioning failed with %s: %s",
(err == WEAVE_ERROR_STATUS_REPORT_RECEIVED) ? "status report from service" : "local error",
(err == WEAVE_ERROR_STATUS_REPORT_RECEIVED)
? ::nl::StatusReportStr(statusReportProfileId, statusReportStatusCode)
: ::nl::ErrorStr(err));
// Choose an appropriate StatusReport to return if not already given.
if (statusReportProfileId == 0 && statusReportStatusCode == 0)
{
if (err == WEAVE_ERROR_TIMEOUT)
{
statusReportProfileId = kWeaveProfile_Security;
statusReportStatusCode = Profiles::Security::kStatusCode_ServiceCommunicationError;
}
else
{
statusReportProfileId = kWeaveProfile_Common;
statusReportStatusCode = Profiles::Common::kStatus_InternalError;
}
}
// TODO: Error CallBack to the the Calling Application.
}
}
void CertificateProvisioningClient::HandleServiceConnectivityTimeout(System::Layer * aSystemLayer, void * aAppState, System::Error aErr)
{
CertificateProvisioningClient *client = static_cast<CertificateProvisioningClient *>(aAppState);
client->HandleCertificateProvisioningResult(WEAVE_ERROR_TIMEOUT, 0, 0);
}
void CertificateProvisioningClient::HandleCertProvBindingEvent(void * appState, Binding::EventType eventType,
const Binding::InEventParam & inParam, Binding::OutEventParam & outParam)
{
uint32_t statusReportProfileId;
uint16_t statusReportStatusCode;
CertificateProvisioningClient *client = static_cast<CertificateProvisioningClient *>(appState);
switch (eventType)
{
case Binding::kEvent_BindingReady:
client->SendGetCertificateRequest();
break;
case Binding::kEvent_PrepareFailed:
if (inParam.PrepareFailed.StatusReport != NULL)
{
statusReportProfileId = inParam.PrepareFailed.StatusReport->mProfileId;
statusReportStatusCode = inParam.PrepareFailed.StatusReport->mStatusCode;
}
else
{
statusReportProfileId = kWeaveProfile_Security;
statusReportStatusCode = Profiles::Security::kStatusCode_ServiceCommunicationError;
}
client->HandleCertificateProvisioningResult(inParam.PrepareFailed.Reason,
statusReportProfileId, statusReportStatusCode);
break;
default:
Binding::DefaultEventHandler(appState, eventType, inParam, outParam);
break;
}
}
#endif // WEAVE_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING
} // namespace Internal
} // namespace DeviceLayer
} // namespace Weave
} // namespace nl