WIP: Implement Certificate Provisioning Client at the Weave Device Layer.
diff --git a/src/adaptations/device-layer/CertificateProvisioningClient.cpp b/src/adaptations/device-layer/CertificateProvisioningClient.cpp
new file mode 100644
index 0000000..a4010d8
--- /dev/null
+++ b/src/adaptations/device-layer/CertificateProvisioningClient.cpp
@@ -0,0 +1,544 @@
+/*
+ *
+ * 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
diff --git a/src/adaptations/device-layer/ESP32/BLEManagerImpl.cpp b/src/adaptations/device-layer/ESP32/BLEManagerImpl.cpp
index 9107270..8c06ac2 100644
--- a/src/adaptations/device-layer/ESP32/BLEManagerImpl.cpp
+++ b/src/adaptations/device-layer/ESP32/BLEManagerImpl.cpp
@@ -245,6 +245,7 @@
case DeviceEventType::kFabricMembershipChange:
case DeviceEventType::kServiceProvisioningChange:
case DeviceEventType::kAccountPairingChange:
+ case DeviceEventType::kDeviceCredentialsChange:
// If WOBLE_DISABLE_ADVERTISING_WHEN_PROVISIONED is enabled, and there is a change to the
// device's provisioning state, then automatically disable WoBLE advertising if the device
@@ -1302,4 +1303,3 @@
} // namespace nl
#endif // WEAVE_DEVICE_CONFIG_ENABLE_WOBLE
-
diff --git a/src/adaptations/device-layer/ESP32/ConnectivityManagerImpl.cpp b/src/adaptations/device-layer/ESP32/ConnectivityManagerImpl.cpp
index 93e26bd..42a5397 100644
--- a/src/adaptations/device-layer/ESP32/ConnectivityManagerImpl.cpp
+++ b/src/adaptations/device-layer/ESP32/ConnectivityManagerImpl.cpp
@@ -1,5 +1,6 @@
/*
*
+ * Copyright (c) 2019-2020 Google LLC.
* Copyright (c) 2018 Nest Labs, Inc.
* All rights reserved.
*
@@ -525,18 +526,19 @@
DriveServiceTunnelState();
}
-#if !WEAVE_DEVICE_CONFIG_DISABLE_ACCOUNT_PAIRING
+#if !WEAVE_DEVICE_CONFIG_DISABLE_ACCOUNT_PAIRING || WEAVE_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING
- // Handle account pairing changes.
- else if (event->Type == DeviceEventType::kAccountPairingChange)
- {
- // When account pairing successfully completes, if the tunnel to the
- // service is subject to routing restrictions (imposed because at the time
- // the tunnel was established the device was not paired to an account)
+ // Handle account pairing and device credentials changes.
+ else if (((event->Type == DeviceEventType::kAccountPairingChange) && event->AccountPairingChange.IsPairedToAccount) ||
+ ((event->Type == DeviceEventType::kDeviceCredentialsChange) && event->DeviceCredentialsChange.AreCredentialsProvisioned))
+ {
+ // When account pairing successfully completes or new device credentials
+ // provisioned, if the tunnel to the service is subject to routing restrictions
+ // (imposed because at the time the tunnel was established the device was
+ // not paired to an account or the device only had initial self-signed certificate)
// then force the tunnel to close. This will result in the tunnel being
// re-established, which should lift the service-side restrictions.
- if (event->AccountPairingChange.IsPairedToAccount &&
- GetFlag(mFlags, kFlag_ServiceTunnelStarted) &&
+ if (GetFlag(mFlags, kFlag_ServiceTunnelStarted) &&
ServiceTunnelAgent.IsTunnelRoutingRestricted())
{
WeaveLogProgress(DeviceLayer, "Restarting service tunnel to lift routing restrictions");
@@ -546,7 +548,7 @@
}
}
-#endif // !WEAVE_DEVICE_CONFIG_DISABLE_ACCOUNT_PAIRING
+#endif // !WEAVE_DEVICE_CONFIG_DISABLE_ACCOUNT_PAIRING || WEAVE_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING
}
void ConnectivityManagerImpl::_OnWiFiScanDone()
diff --git a/src/adaptations/device-layer/Makefile.am b/src/adaptations/device-layer/Makefile.am
index 0812312..76cb5a9 100644
--- a/src/adaptations/device-layer/Makefile.am
+++ b/src/adaptations/device-layer/Makefile.am
@@ -1,6 +1,6 @@
#
# Copyright (c) 2014-2018 Nest Labs, Inc.
-# Copyright (c) 2018 Google LLC
+# Copyright (c) 2018-2020 Google LLC
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -66,6 +66,7 @@
include/Weave/DeviceLayer/WeaveDeviceEvent.h \
include/Weave/DeviceLayer/WeaveDeviceLayer.h \
include/Weave/DeviceLayer/internal/BLEManager.h \
+ include/Weave/DeviceLayer/internal/CertificateProvisioningClient.h \
include/Weave/DeviceLayer/internal/DeviceControlServer.h \
include/Weave/DeviceLayer/internal/DeviceDescriptionServer.h \
include/Weave/DeviceLayer/internal/DeviceIdentityTraitDataSource.h \
@@ -111,6 +112,7 @@
libDeviceLayer_a_SOURCES = \
CASEAuth.cpp \
+ CertificateProvisioningClient.cpp \
DeviceControlServer.cpp \
DeviceDescriptionServer.cpp \
DeviceIdentityTraitDataSource.cpp \
diff --git a/src/adaptations/device-layer/ServiceProvisioningServer.cpp b/src/adaptations/device-layer/ServiceProvisioningServer.cpp
index c0de161..f54fed8 100644
--- a/src/adaptations/device-layer/ServiceProvisioningServer.cpp
+++ b/src/adaptations/device-layer/ServiceProvisioningServer.cpp
@@ -1,5 +1,6 @@
/*
*
+ * Copyright (c) 2019-2020 Google LLC.
* Copyright (c) 2018 Nest Labs, Inc.
* All rights reserved.
*
@@ -22,14 +23,19 @@
using namespace ::nl;
using namespace ::nl::Weave;
+using namespace ::nl::Weave::TLV;
using namespace ::nl::Weave::Profiles;
using namespace ::nl::Weave::Profiles::ServiceProvisioning;
+using namespace ::nl::Weave::Profiles::Security;
namespace nl {
namespace Weave {
namespace DeviceLayer {
namespace Internal {
+using ::nl::Weave::Platform::Security::MemoryAlloc;
+using ::nl::Weave::Platform::Security::MemoryFree;
+
ServiceProvisioningServer ServiceProvisioningServer::sInstance;
WEAVE_ERROR ServiceProvisioningServer::Init(void)
@@ -46,6 +52,10 @@
mProvServiceBinding = NULL;
mWaitingForServiceConnectivity = false;
+#if WEAVE_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING
+ mCertProvClient = NULL;
+#endif
+
exit:
return err;
}
@@ -91,7 +101,12 @@
PlatformMgr().PostEvent(&event);
}
-#if !WEAVE_DEVICE_CONFIG_DISABLE_ACCOUNT_PAIRING
+#if WEAVE_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING
+
+ // Initiate the process of sending a GetCertificateRequest to the Certificate Provisioning service.
+ PlatformMgr().ScheduleWork(AsyncStartCertificateProvisioning);
+
+#elif !WEAVE_DEVICE_CONFIG_DISABLE_ACCOUNT_PAIRING
// Initiate the process of sending a PairDeviceToAccount request to the Service Provisioning service.
PlatformMgr().ScheduleWork(AsyncStartPairDeviceToAccount);
@@ -304,6 +319,15 @@
mProvServiceBinding = NULL;
}
+#if WEAVE_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING
+ // If necessary, free memory allocated for the certificate provisioning engine.
+ if (mCertProvClient != NULL)
+ {
+ MemoryFree(mCertProvClient);
+ mCertProvClient = NULL;
+ }
+#endif // WEAVE_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING
+
// Return immediately if for some reason the client's RegisterServicePairAccount request
// is no longer pending. Note that, even if the PairDeviceToAccount request succeeded,
// the device must clear the persisted service configuration in this case because it has
@@ -374,6 +398,33 @@
}
}
+#if WEAVE_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING
+void ServiceProvisioningServer::AsyncStartCertificateProvisioning(intptr_t arg)
+{
+ WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+ // Allocate temporary memory to hold the certificate provisioning client.
+ sInstance.mCertProvClient = static_cast<CertificateProvisioningClient *>(MemoryAlloc(sizeof(CertificateProvisioningClient)));
+ VerifyOrExit(sInstance.mCertProvClient != NULL, err = WEAVE_ERROR_NO_MEMORY);
+
+ // Initialize certificate provisioning client.
+ err = sInstance.mCertProvClient->Init(WeaveCertProvEngine::kReqType_GetInitialOpDeviceCert, sInstance.EncodeGetCertificateRequestAuthInfo);
+ SuccessOrExit(err);
+
+ // Start certificate provisioning.
+ sInstance.mCertProvClient->StartCertificateProvisioning();
+
+exit:
+ if (err != WEAVE_NO_ERROR)
+ {
+ MemoryFree(sInstance.mCertProvClient);
+ sInstance.mCertProvClient = NULL;
+
+ sInstance.HandlePairDeviceToAccountResult(err, kWeaveProfile_Common, Profiles::Common::kStatus_InternalServerProblem);
+ }
+}
+#endif // WEAVE_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING
+
void ServiceProvisioningServer::AsyncStartPairDeviceToAccount(intptr_t arg)
{
sInstance.StartPairDeviceToAccount();
@@ -415,6 +466,23 @@
}
}
+WEAVE_ERROR ServiceProvisioningServer::EncodeGetCertificateRequestAuthInfo(TLVWriter & writer)
+{
+ WEAVE_ERROR err = WEAVE_NO_ERROR;
+ const RegisterServicePairAccountMessage & regServiceMsg = sInstance.mCurClientOpMsg.RegisterServicePairAccount;
+
+ // Encode pairing token.
+ err = writer.PutBytes(ContextTag(kTag_GetCertReqMsg_Authorize_PairingToken), regServiceMsg.PairingToken, regServiceMsg.PairingTokenLen);
+ SuccessOrExit(err);
+
+ // Encode pairing initialization data.
+ err = writer.PutBytes(ContextTag(kTag_GetCertReqMsg_Authorize_PairingInitData), regServiceMsg.PairingInitData, regServiceMsg.PairingInitDataLen);
+ SuccessOrExit(err);
+
+exit:
+ return err;
+}
+
#else // !WEAVE_DEVICE_CONFIG_DISABLE_ACCOUNT_PAIRING
void ServiceProvisioningServer::HandlePairDeviceToAccountResult(WEAVE_ERROR err, uint32_t statusReportProfileId, uint16_t statusReportStatusCode)
diff --git a/src/adaptations/device-layer/include/Weave/DeviceLayer/PlatformManager.h b/src/adaptations/device-layer/include/Weave/DeviceLayer/PlatformManager.h
index 37d5fd4..ca3eb53 100644
--- a/src/adaptations/device-layer/include/Weave/DeviceLayer/PlatformManager.h
+++ b/src/adaptations/device-layer/include/Weave/DeviceLayer/PlatformManager.h
@@ -1,5 +1,6 @@
/*
*
+ * Copyright (c) 2019 Google LLC.
* Copyright (c) 2018 Nest Labs, Inc.
* All rights reserved.
*
@@ -36,6 +37,7 @@
class TraitManager;
class TimeSyncManager;
namespace Internal {
+class CertificateProvisioningClient;
class FabricProvisioningServer;
class ServiceProvisioningServer;
class BLEManagerImpl;
@@ -81,6 +83,7 @@
friend class ConfigurationManagerImpl;
friend class TraitManager;
friend class TimeSyncManager;
+ friend class Internal::CertificateProvisioningClient;
friend class Internal::FabricProvisioningServer;
friend class Internal::ServiceProvisioningServer;
friend class Internal::BLEManagerImpl;
diff --git a/src/adaptations/device-layer/include/Weave/DeviceLayer/WeaveDeviceConfig.h b/src/adaptations/device-layer/include/Weave/DeviceLayer/WeaveDeviceConfig.h
index f1dcdcd..f124419 100644
--- a/src/adaptations/device-layer/include/Weave/DeviceLayer/WeaveDeviceConfig.h
+++ b/src/adaptations/device-layer/include/Weave/DeviceLayer/WeaveDeviceConfig.h
@@ -501,6 +501,39 @@
#define WEAVE_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING 0
#endif
+/**
+ * WEAVE_DEVICE_CONFIG_CERTIFICATE_PROVISIONING_ENDPOINT_ID
+ *
+ * Specifies the service endpoint id of the Weave Certificate Provisioning service. When a device
+ * undergoes certificate provisioning, this is the endpoint to which it will send its Get Certificate
+ * request.
+ */
+#ifndef WEAVE_DEVICE_CONFIG_CERTIFICATE_PROVISIONING_ENDPOINT_ID
+#define WEAVE_DEVICE_CONFIG_CERTIFICATE_PROVISIONING_ENDPOINT_ID 0x18B4300200000016ULL
+#endif
+
+/**
+ * WEAVE_DEVICE_CONFIG_CERTIFICATE_PROVISIONING_CONNECTIVITY_TIMEOUT
+ *
+ * The maximum amount of time (in milliseconds) to wait for service connectivity during the device
+ * certificate provisioning step. More specifically, this is the maximum amount of time the device will
+ * wait for connectivity to be established with the service at the point where the device waiting
+ * to send a Get Certificate request to the Certificate Provisioning service.
+ */
+#ifndef WEAVE_DEVICE_CONFIG_CERTIFICATE_PROVISIONING_CONNECTIVITY_TIMEOUT
+#define WEAVE_DEVICE_CONFIG_CERTIFICATE_PROVISIONING_CONNECTIVITY_TIMEOUT 10000
+#endif
+
+/**
+ * WEAVE_DEVICE_CONFIG_GET_CERTIFICATE_REQUEST_TIMEOUT
+ *
+ * Specifies the maximum amount of time (in milliseconds) to wait for a response from the Certificate
+ * Provisioning service.
+ */
+#ifndef WEAVE_DEVICE_CONFIG_GET_CERTIFICATE_REQUEST_TIMEOUT
+#define WEAVE_DEVICE_CONFIG_GET_CERTIFICATE_REQUEST_TIMEOUT 10000
+#endif
+
// -------------------- Thread Configuration --------------------
/**
diff --git a/src/adaptations/device-layer/include/Weave/DeviceLayer/WeaveDeviceEvent.h b/src/adaptations/device-layer/include/Weave/DeviceLayer/WeaveDeviceEvent.h
index 194204c..91ce198 100644
--- a/src/adaptations/device-layer/include/Weave/DeviceLayer/WeaveDeviceEvent.h
+++ b/src/adaptations/device-layer/include/Weave/DeviceLayer/WeaveDeviceEvent.h
@@ -1,5 +1,6 @@
/*
*
+ * Copyright (c) 2019 Google LLC.
* Copyright (c) 2018 Nest Labs, Inc.
* All rights reserved.
*
@@ -196,6 +197,13 @@
* Signals that the state of WoBLE advertising has changed.
*/
kWoBLEAdvertisingChange,
+
+ /**
+ * Device Credentials Change
+ *
+ * Signals that the device's credentials has changed
+ */
+ kDeviceCredentialsChange,
};
/**
@@ -397,6 +405,10 @@
{
ActivityChange Result;
} WoBLEAdvertisingChange;
+ struct
+ {
+ bool AreCredentialsProvisioned;
+ } DeviceCredentialsChange;
};
void Clear() { memset(this, 0, sizeof(*this)); }
diff --git a/src/adaptations/device-layer/include/Weave/DeviceLayer/internal/CertificateProvisioningClient.h b/src/adaptations/device-layer/include/Weave/DeviceLayer/internal/CertificateProvisioningClient.h
new file mode 100644
index 0000000..25f4072
--- /dev/null
+++ b/src/adaptations/device-layer/include/Weave/DeviceLayer/internal/CertificateProvisioningClient.h
@@ -0,0 +1,112 @@
+/*
+ *
+ * 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.
+ */
+
+/**
+ * @file
+ * Defines the Device Layer CertificateProvisioningClient object.
+ */
+
+#ifndef CERTIFICATE_PROVISIONING_CLIENT_H
+#define CERTIFICATE_PROVISIONING_CLIENT_H
+
+#include <Weave/DeviceLayer/internal/WeaveDeviceLayerInternal.h>
+#include <Weave/Profiles/security/WeaveCertProvisioning.h>
+
+namespace nl {
+namespace Weave {
+namespace DeviceLayer {
+namespace Internal {
+
+using nl::Weave::Profiles::Security::CertProvisioning::WeaveCertProvEngine;
+
+/**
+ * Implements the Weave Certificate Provisioning profile for a Weave device.
+ */
+class CertificateProvisioningClient final
+ : public ::nl::Weave::Profiles::Security::CertProvisioning::WeaveNodeOpAuthDelegate,
+ public ::nl::Weave::Profiles::Security::CertProvisioning::WeaveNodeMfrAttestDelegate
+{
+
+public:
+
+ /**
+ * This function is the callback that is invoked by Certificate Provisioning Client to add
+ * authorization info to the GetCertificateRequest message in a TLV form to the supplied TLV writer.
+ *
+ * @param[in] writer A reference to a TLV writer to write request authorization data.
+ */
+ typedef WEAVE_ERROR (* EncodeReqAuthInfoFunct)(TLVWriter & writer);
+
+ // ===== Members for internal use by other Device Layer components.
+
+ WEAVE_ERROR Init(uint8_t reqType);
+ WEAVE_ERROR Init(uint8_t reqType, EncodeReqAuthInfoFunct encodeReqAuthInfo);
+ void OnPlatformEvent(const WeaveDeviceEvent * event);
+
+ // ===== Methods that implement the WeaveNodeOpAuthDelegate interface
+
+ WEAVE_ERROR EncodeOpCert(TLVWriter & writer, uint64_t tag) __OVERRIDE;
+ WEAVE_ERROR EncodeOpRelatedCerts(TLVWriter & writer, uint64_t tag) __OVERRIDE;
+ WEAVE_ERROR GenerateAndEncodeOpSig(const uint8_t * hash, uint8_t hashLen, TLVWriter & writer, uint64_t tag) __OVERRIDE;
+
+ // ===== Methods that implement the WeaveNodeMfrAttestDelegate interface
+
+ WEAVE_ERROR EncodeMAInfo(TLVWriter & writer) __OVERRIDE;
+ WEAVE_ERROR GenerateAndEncodeMASig(const uint8_t * data, uint16_t dataLen, TLVWriter & writer) __OVERRIDE;
+
+ // ===== Members that override virtual methods on ServiceProvisioningDelegate
+
+ void StartCertificateProvisioning(void);
+ void HandleCertificateProvisioningResult(WEAVE_ERROR localErr, uint32_t serverStatusProfileId, uint16_t serverStatusCode);
+
+private:
+
+ // ===== Members for internal use by this class only.
+
+ WeaveCertProvEngine mCertProvEngine;
+ ::nl::Weave::Binding * mBinding;
+ uint8_t mReqType;
+ bool mDoMfrAttest;
+ EncodeReqAuthInfoFunct mEncodeReqAuthInfo;
+ bool mWaitingForServiceConnectivity;
+
+ void SendGetCertificateRequest(void);
+
+ static void HandleServiceConnectivityTimeout(::nl::Weave::System::Layer * layer, void * appState, ::nl::Weave::System::Error err);
+ static void HandleCertProvBindingEvent(void * appState, nl::Weave::Binding::EventType eventType,
+ const nl::Weave::Binding::InEventParam & inParam, nl::Weave::Binding::OutEventParam & outParam);
+ static void CertProvClientEventHandler(void * appState, WeaveCertProvEngine::EventType eventType, const WeaveCertProvEngine::InEventParam & inParam, WeaveCertProvEngine::OutEventParam & outParam);
+
+protected:
+
+ // Construction/destruction limited to subclasses.
+ CertificateProvisioningClient() = default;
+ ~CertificateProvisioningClient() = default;
+
+ // No copy, move or assignment.
+ CertificateProvisioningClient(const CertificateProvisioningClient &) = delete;
+ CertificateProvisioningClient(const CertificateProvisioningClient &&) = delete;
+ CertificateProvisioningClient & operator=(const CertificateProvisioningClient &) = delete;
+};
+
+} // namespace Internal
+} // namespace DeviceLayer
+} // namespace Weave
+} // namespace nl
+
+#endif // CERTIFICATE_PROVISIONING_CLIENT_H
diff --git a/src/adaptations/device-layer/include/Weave/DeviceLayer/internal/ServiceProvisioningServer.h b/src/adaptations/device-layer/include/Weave/DeviceLayer/internal/ServiceProvisioningServer.h
index 364ed65..57871f5 100644
--- a/src/adaptations/device-layer/include/Weave/DeviceLayer/internal/ServiceProvisioningServer.h
+++ b/src/adaptations/device-layer/include/Weave/DeviceLayer/internal/ServiceProvisioningServer.h
@@ -1,5 +1,6 @@
/*
*
+ * Copyright (c) 2019-2020 Google LLC.
* Copyright (c) 2018 Nest Labs, Inc.
* All rights reserved.
*
@@ -26,6 +27,7 @@
#include <Weave/DeviceLayer/internal/WeaveDeviceLayerInternal.h>
#include <Weave/Profiles/service-provisioning/ServiceProvisioning.h>
+#include <Weave/DeviceLayer/internal/CertificateProvisioningClient.h>
namespace nl {
namespace Weave {
@@ -84,14 +86,19 @@
::nl::Weave::Binding * mProvServiceBinding;
bool mWaitingForServiceConnectivity;
+#if WEAVE_DEVICE_CONFIG_ENABLE_JUST_IN_TIME_PROVISIONING
+ CertificateProvisioningClient * mCertProvClient;
+#endif
void StartPairDeviceToAccount(void);
void SendPairDeviceToAccountRequest(void);
+ static void AsyncStartCertificateProvisioning(intptr_t arg);
static void AsyncStartPairDeviceToAccount(intptr_t arg);
static void HandleServiceConnectivityTimeout(::nl::Weave::System::Layer * layer, void * appState, ::nl::Weave::System::Error err);
static void HandleProvServiceBindingEvent(void * appState, nl::Weave::Binding::EventType eventType,
const nl::Weave::Binding::InEventParam & inParam, nl::Weave::Binding::OutEventParam & outParam);
+ static WEAVE_ERROR EncodeGetCertificateRequestAuthInfo(TLVWriter &writer);
protected:
diff --git a/src/adaptations/device-layer/nRF5/BLEManagerImpl.cpp b/src/adaptations/device-layer/nRF5/BLEManagerImpl.cpp
index 2ed18c6..5866cc5 100644
--- a/src/adaptations/device-layer/nRF5/BLEManagerImpl.cpp
+++ b/src/adaptations/device-layer/nRF5/BLEManagerImpl.cpp
@@ -282,6 +282,7 @@
case DeviceEventType::kFabricMembershipChange:
case DeviceEventType::kServiceProvisioningChange:
case DeviceEventType::kAccountPairingChange:
+ case DeviceEventType::kDeviceCredentialsChange:
// If WOBLE_DISABLE_ADVERTISING_WHEN_PROVISIONED is enabled, and there is a change to the
// device's provisioning state, then automatically disable WoBLE advertising if the device
diff --git a/src/include/Makefile.am b/src/include/Makefile.am
index 1c55496..14d7830 100644
--- a/src/include/Makefile.am
+++ b/src/include/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2019 Google LLC.
+# Copyright (c) 2019-2020 Google LLC.
# Copyright (c) 2014-2017 Nest Labs, Inc.
# All rights reserved.
#
@@ -1061,6 +1061,7 @@
nl_public_WeaveDeviceLayer_internal_header_sources = \
$(nl_public_WeaveDeviceLayer_source_dirstem)/internal/BLEManager.h \
+$(nl_public_WeaveDeviceLayer_source_dirstem)/internal/CertificateProvisioningClient.h \
$(nl_public_WeaveDeviceLayer_source_dirstem)/internal/DeviceControlServer.h \
$(nl_public_WeaveDeviceLayer_source_dirstem)/internal/DeviceDescriptionServer.h \
$(nl_public_WeaveDeviceLayer_source_dirstem)/internal/DeviceIdentityTraitDataSource.h \