Implemented tests for Certificate Provisioning Protocol.

  -- Implemented unit level tests (TestCertProv).
  -- Implemented Mock CA Service for testing purposes.
  -- Implemented Certificate Provisioning Client to mimic
     the client (certificate request initiator) behavior.
  -- Implemented Certificate Provisioning Service to mimic
     the CA Service (responder to the certificate request) behavior.
diff --git a/src/lib/profiles/security/WeaveCert.cpp b/src/lib/profiles/security/WeaveCert.cpp
index 26f84be..39edc62 100644
--- a/src/lib/profiles/security/WeaveCert.cpp
+++ b/src/lib/profiles/security/WeaveCert.cpp
@@ -49,8 +49,6 @@
 using namespace nl::Weave::Profiles;
 using namespace nl::Weave::Crypto;
 
-extern WEAVE_ERROR DecodeConvertTBSCert(TLVReader& reader, ASN1Writer& writer, WeaveCertificateData& certData);
-
 #if HAVE_MALLOC && HAVE_FREE
 static void *DefaultAlloc(size_t size)
 {
diff --git a/src/lib/profiles/security/WeaveCert.h b/src/lib/profiles/security/WeaveCert.h
index 862b2fe..d12001e 100644
--- a/src/lib/profiles/security/WeaveCert.h
+++ b/src/lib/profiles/security/WeaveCert.h
@@ -42,6 +42,7 @@
 
 using nl::Weave::TLV::TLVReader;
 using nl::Weave::TLV::TLVWriter;
+using nl::Weave::ASN1::ASN1Writer;
 using nl::Weave::ASN1::OID;
 using nl::Weave::Crypto::EncodedECPublicKey;
 using nl::Weave::Crypto::EncodedECPrivateKey;
@@ -295,6 +296,8 @@
 
 extern WEAVE_ERROR DecodeWeaveDN(TLVReader& reader, WeaveDN& dn);
 
+extern WEAVE_ERROR DecodeConvertTBSCert(TLVReader& reader, ASN1Writer& writer, WeaveCertificateData& certData);
+
 extern WEAVE_ERROR ConvertX509CertToWeaveCert(const uint8_t *x509Cert, uint32_t x509CertLen, uint8_t *weaveCertBuf, uint32_t weaveCertBufSize, uint32_t& weaveCertLen);
 extern WEAVE_ERROR ConvertWeaveCertToX509Cert(const uint8_t *weaveCert, uint32_t weaveCertLen, uint8_t *x509CertBuf, uint32_t x509CertBufSize, uint32_t& x509CertLen);
 
diff --git a/src/lib/support/WeaveSupport.am b/src/lib/support/WeaveSupport.am
index a35366a..220839b 100644
--- a/src/lib/support/WeaveSupport.am
+++ b/src/lib/support/WeaveSupport.am
@@ -58,6 +58,7 @@
     @top_builddir@/src/lib/support/crypto/HashAlgos-OpenSSL.cpp                             \
     @top_builddir@/src/lib/support/crypto/HashAlgos-MinCrypt.cpp                            \
     @top_builddir@/src/lib/support/crypto/HashAlgos-mbedTLS.cpp                             \
+    @top_builddir@/src/lib/support/crypto/RSA.cpp                                           \
     @top_builddir@/src/lib/support/crypto/WeaveCrypto.cpp                                   \
     @top_builddir@/src/lib/support/crypto/WeaveCrypto-OpenSSL.cpp                           \
     @top_builddir@/src/lib/support/crypto/WeaveRNG-OpenSSL.cpp                              \
diff --git a/src/test-apps/.gitignore b/src/test-apps/.gitignore
index a77e898..b63bfba 100644
--- a/src/test-apps/.gitignore
+++ b/src/test-apps/.gitignore
@@ -15,6 +15,7 @@
 TestASN1
 TestBinding
 TestCASE
+TestCertProv
 TestCodeUtils
 TestCrypto
 TestDataManagement
@@ -87,6 +88,8 @@
 weave-bdx-server
 weave-bdx-server-development
 weave-bdx-server-v0
+weave-cert-prov-client
+weave-cert-prov-server
 weave-connection-tunnel
 weave-dd-client
 weave-device-descriptor
diff --git a/src/test-apps/CASEOptions.cpp b/src/test-apps/CASEOptions.cpp
index 90531b8..296c46c 100644
--- a/src/test-apps/CASEOptions.cpp
+++ b/src/test-apps/CASEOptions.cpp
@@ -270,66 +270,6 @@
     return true;
 }
 
-bool CASEOptions::ReadCertFile(const char *fileName, uint8_t *& certBuf, uint16_t& certLen)
-{
-    uint32_t len;
-
-    static const char *certB64Prefix = "1QAABAAB";
-    static const size_t certB64PrefixLen = sizeof(certB64Prefix) - 1;
-
-    // Read the specified file into a malloced buffer.
-    certBuf = ReadFileArg(fileName, len, UINT16_MAX);
-    if (certBuf == NULL)
-        return false;
-
-    // If the certificate is in base-64 format, convert it to raw TLV.
-    if (len > certB64PrefixLen && memcmp(certBuf, certB64Prefix, certB64PrefixLen) == 0)
-    {
-        len = nl::Base64Decode((const char *)certBuf, len, (uint8_t *)certBuf);
-        if (len == UINT16_MAX)
-        {
-            printf("Invalid certificate format: %s\n", fileName);
-            free(certBuf);
-            certBuf = NULL;
-            return false;
-        }
-    }
-
-    certLen = (uint16_t)len;
-
-    return true;
-}
-
-bool CASEOptions::ReadPrivateKeyFile(const char *fileName, uint8_t *& keyBuf, uint16_t& keyLen)
-{
-    uint32_t len;
-
-    static const char *keyB64Prefix = "1QAABAAC";
-    static const size_t keyB64PrefixLen = sizeof(keyB64Prefix) - 1;
-
-    // Read the specified file into a malloced buffer.
-    keyBuf = ReadFileArg(fileName, len, UINT16_MAX);
-    if (keyBuf == NULL)
-        return false;
-
-    // If the private key is in base-64 format, convert it to raw TLV.
-    if (len > keyB64PrefixLen && memcmp(keyBuf, keyB64Prefix, keyB64PrefixLen) == 0)
-    {
-        len = nl::Base64Decode((const char *)keyBuf, len, (uint8_t *)keyBuf);
-        if (len == UINT16_MAX)
-        {
-            printf("Invalid private key format: %s\n", fileName);
-            free(keyBuf);
-            keyBuf = NULL;
-            return false;
-        }
-    }
-
-    keyLen = (uint16_t)len;
-
-    return true;
-}
-
 WEAVE_ERROR CASEOptions::GetNodeCert(const uint8_t *& nodeCert, uint16_t & nodeCertLen)
 {
     nodeCert = NodeCert;
@@ -355,8 +295,8 @@
     {
         if (!GetTestNodePrivateKey(FabricState.LocalNodeId, weavePrivKey, weavePrivKeyLen))
         {
-        printf("ERROR: Node private key not configured\n");
-        return WEAVE_ERROR_KEY_NOT_FOUND;
+            printf("ERROR: Node private key not configured\n");
+            return WEAVE_ERROR_KEY_NOT_FOUND;
         }
     }
     return WEAVE_NO_ERROR;
diff --git a/src/test-apps/CASEOptions.h b/src/test-apps/CASEOptions.h
index 4ea4169..0b21aa6 100644
--- a/src/test-apps/CASEOptions.h
+++ b/src/test-apps/CASEOptions.h
@@ -73,9 +73,6 @@
 
     virtual bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg) __OVERRIDE;
 
-    static bool ReadCertFile(const char *fileName, uint8_t *& certBuf, uint16_t& certLen);
-    static bool ReadPrivateKeyFile(const char *fileName, uint8_t *& keyBuf, uint16_t& keyLen);
-
 #if !WEAVE_CONFIG_LEGACY_CASE_AUTH_DELEGATE
 
     // ===== Methods that implement the WeaveCASEAuthDelegate interface
diff --git a/src/test-apps/CertProvOptions.cpp b/src/test-apps/CertProvOptions.cpp
new file mode 100644
index 0000000..90424fd
--- /dev/null
+++ b/src/test-apps/CertProvOptions.cpp
@@ -0,0 +1,1543 @@
+/*
+ *
+ *    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
+ *      Implementation of CertProvOptions object, which provides an implementation of the
+ *      WeaveNodeOpAuthDelegate and WeaveNodeMfrAttestDelegate interfaces for use
+ *      in test applications.
+ *
+ */
+
+#include "stdio.h"
+
+#include "ToolCommon.h"
+#include "TestWeaveCertData.h"
+#include <Weave/Support/CodeUtils.h>
+#include <Weave/Core/WeaveTLV.h>
+#include <Weave/Profiles/WeaveProfiles.h>
+#include <Weave/Profiles/security/WeaveSig.h>
+#include <Weave/Profiles/security/WeaveSecurityDebug.h>
+#include <Weave/Profiles/security/WeavePrivateKey.h>
+#include <Weave/Profiles/security/WeaveCertProvisioning.h>
+#include <Weave/Profiles/service-provisioning/ServiceProvisioning.h>
+#include <Weave/Support/TimeUtils.h>
+#include <Weave/Support/NestCerts.h>
+#include <Weave/Support/ErrorStr.h>
+#include <Weave/Support/ASN1.h>
+#include <Weave/Support/crypto/HashAlgos.h>
+#include "CertProvOptions.h"
+
+using namespace nl::Weave;
+using namespace nl::Weave::TLV;
+using namespace nl::Weave::ASN1;
+using namespace nl::Weave::Profiles;
+using namespace nl::Weave::Profiles::Security;
+using namespace nl::Weave::Profiles::Security::CertProvisioning;
+
+const uint8_t TestPairingToken[] =
+{
+    /*
+    -----BEGIN PAIRING TOKEN-----
+    1QAABAAJADUBMAEITi8yS0HXOtskAgQ3AyyBEERVTU1ZLUFDQ09VTlQtSUQYJgTLqPobJgVLNU9C
+    NwYsgRBEVU1NWS1BQ0NPVU5ULUlEGCQHAiYIJQBaIzAKOQQr2dtaYu+6sVMqD5ljt4owxYpBKaUZ
+    TksL837axemzNfB1GG1JXYbERCUHQbTTqe/utCrWCl2d4DWDKQEYNYIpASQCBRg1hCkBNgIEAgQB
+    GBg1gTACCEI8lV9GHlLbGDWAMAIIQjyVX0YeUtsYNQwwAR0AimGGYj0XstLP0m05PeQlaeCR6gVq
+    dc7dReuDzzACHHS0K6RtFGW3t3GaWq9k0ohgbrOxoDHKkm/K8kMYGDUCJgElAFojMAIcuvzjT4a/
+    fDgScCv5oxC/T5vz7zAPpURNQjpnajADOQQr2dtaYu+6sVMqD5ljt4owxYpBKaUZTksL837axemz
+    NfB1GG1JXYbERCUHQbTTqe/utCrWCl2d4BgY
+    -----END PAIRING TOKEN-----
+    */
+    0xd5, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, 0x35, 0x01, 0x30, 0x01, 0x08, 0x4e, 0x2f, 0x32, 0x4b,
+    0x41, 0xd7, 0x3a, 0xdb, 0x24, 0x02, 0x04, 0x37, 0x03, 0x2c, 0x81, 0x10, 0x44, 0x55, 0x4d, 0x4d,
+    0x59, 0x2d, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x2d, 0x49, 0x44, 0x18, 0x26, 0x04, 0xcb,
+    0xa8, 0xfa, 0x1b, 0x26, 0x05, 0x4b, 0x35, 0x4f, 0x42, 0x37, 0x06, 0x2c, 0x81, 0x10, 0x44, 0x55,
+    0x4d, 0x4d, 0x59, 0x2d, 0x41, 0x43, 0x43, 0x4f, 0x55, 0x4e, 0x54, 0x2d, 0x49, 0x44, 0x18, 0x24,
+    0x07, 0x02, 0x26, 0x08, 0x25, 0x00, 0x5a, 0x23, 0x30, 0x0a, 0x39, 0x04, 0x2b, 0xd9, 0xdb, 0x5a,
+    0x62, 0xef, 0xba, 0xb1, 0x53, 0x2a, 0x0f, 0x99, 0x63, 0xb7, 0x8a, 0x30, 0xc5, 0x8a, 0x41, 0x29,
+    0xa5, 0x19, 0x4e, 0x4b, 0x0b, 0xf3, 0x7e, 0xda, 0xc5, 0xe9, 0xb3, 0x35, 0xf0, 0x75, 0x18, 0x6d,
+    0x49, 0x5d, 0x86, 0xc4, 0x44, 0x25, 0x07, 0x41, 0xb4, 0xd3, 0xa9, 0xef, 0xee, 0xb4, 0x2a, 0xd6,
+    0x0a, 0x5d, 0x9d, 0xe0, 0x35, 0x83, 0x29, 0x01, 0x18, 0x35, 0x82, 0x29, 0x01, 0x24, 0x02, 0x05,
+    0x18, 0x35, 0x84, 0x29, 0x01, 0x36, 0x02, 0x04, 0x02, 0x04, 0x01, 0x18, 0x18, 0x35, 0x81, 0x30,
+    0x02, 0x08, 0x42, 0x3c, 0x95, 0x5f, 0x46, 0x1e, 0x52, 0xdb, 0x18, 0x35, 0x80, 0x30, 0x02, 0x08,
+    0x42, 0x3c, 0x95, 0x5f, 0x46, 0x1e, 0x52, 0xdb, 0x18, 0x35, 0x0c, 0x30, 0x01, 0x1d, 0x00, 0x8a,
+    0x61, 0x86, 0x62, 0x3d, 0x17, 0xb2, 0xd2, 0xcf, 0xd2, 0x6d, 0x39, 0x3d, 0xe4, 0x25, 0x69, 0xe0,
+    0x91, 0xea, 0x05, 0x6a, 0x75, 0xce, 0xdd, 0x45, 0xeb, 0x83, 0xcf, 0x30, 0x02, 0x1c, 0x74, 0xb4,
+    0x2b, 0xa4, 0x6d, 0x14, 0x65, 0xb7, 0xb7, 0x71, 0x9a, 0x5a, 0xaf, 0x64, 0xd2, 0x88, 0x60, 0x6e,
+    0xb3, 0xb1, 0xa0, 0x31, 0xca, 0x92, 0x6f, 0xca, 0xf2, 0x43, 0x18, 0x18, 0x35, 0x02, 0x26, 0x01,
+    0x25, 0x00, 0x5a, 0x23, 0x30, 0x02, 0x1c, 0xba, 0xfc, 0xe3, 0x4f, 0x86, 0xbf, 0x7c, 0x38, 0x12,
+    0x70, 0x2b, 0xf9, 0xa3, 0x10, 0xbf, 0x4f, 0x9b, 0xf3, 0xef, 0x30, 0x0f, 0xa5, 0x44, 0x4d, 0x42,
+    0x3a, 0x67, 0x6a, 0x30, 0x03, 0x39, 0x04, 0x2b, 0xd9, 0xdb, 0x5a, 0x62, 0xef, 0xba, 0xb1, 0x53,
+    0x2a, 0x0f, 0x99, 0x63, 0xb7, 0x8a, 0x30, 0xc5, 0x8a, 0x41, 0x29, 0xa5, 0x19, 0x4e, 0x4b, 0x0b,
+    0xf3, 0x7e, 0xda, 0xc5, 0xe9, 0xb3, 0x35, 0xf0, 0x75, 0x18, 0x6d, 0x49, 0x5d, 0x86, 0xc4, 0x44,
+    0x25, 0x07, 0x41, 0xb4, 0xd3, 0xa9, 0xef, 0xee, 0xb4, 0x2a, 0xd6, 0x0a, 0x5d, 0x9d, 0xe0, 0x18,
+    0x18,
+};
+
+const uint16_t TestPairingTokenLength = sizeof(TestPairingToken);
+
+const uint8_t TestPairingInitData[] =
+{
+    0x6E, 0x3C, 0x71, 0x5B, 0xE0, 0x19, 0xD4, 0x35, 0x83, 0x29, 0x01, 0x18, 0x35, 0x82, 0x29, 0x01,
+    0x24, 0x02, 0x05, 0x18, 0x35, 0x84, 0x29, 0x01, 0x36, 0x02, 0x04, 0x02, 0x04, 0x01,
+};
+
+const uint16_t TestPairingInitDataLength = sizeof(TestPairingInitData);
+
+const uint32_t TestDevice1_MfrAttest_HMACKeyId = 0xCAFECAFE;
+
+const uint8_t TestDevice1_MfrAttest_HMACMetaData[] =
+{
+    0x2a, 0xd6, 0x0a, 0x29, 0x01, 0x6E, 0x71, 0x29, 0x01, 0x18, 0x35
+};
+
+const uint16_t TestDevice1_MfrAttest_HMACMetaDataLength = sizeof(TestDevice1_MfrAttest_HMACMetaData);
+
+const uint8_t TestDevice1_MfrAttest_HMACKey[] =
+{
+    0xd9, 0xdb, 0x5a, 0x62, 0xE0, 0x19, 0xD4, 0x35, 0x83, 0x29, 0x01, 0x18, 0x35, 0x82, 0x29, 0x01,
+    0x24, 0x02, 0x05, 0x18, 0x36, 0x02, 0x04, 0x02, 0x04, 0x01, 0x29, 0x01, 0x0b, 0xf3, 0xa0, 0x31
+};
+
+const uint16_t TestDevice1_MfrAttest_HMACKeyLength = sizeof(TestDevice1_MfrAttest_HMACKey);
+
+DeviceCredentialsStore gDeviceCredsStore;
+
+CertProvOptions gCertProvOptions;
+
+DeviceCredentialsStore::DeviceCredentialsStore()
+{
+    mDeviceId = kNodeIdNotSpecified;
+    mDevicePrivateKeyLen = 0;
+    mDeviceCertLen = 0;
+    mDeviceICACertsLen = 0;
+}
+
+WEAVE_ERROR DeviceCredentialsStore::StoreDeviceCert(const uint8_t * cert, uint16_t certLen)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    VerifyOrExit(cert != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
+    VerifyOrExit(certLen <= sizeof(mDeviceCert), err = WEAVE_ERROR_BUFFER_TOO_SMALL);
+
+    memcpy(mDeviceCert, cert, certLen);
+    mDeviceCertLen = certLen;
+
+exit:
+    return err;
+}
+
+WEAVE_ERROR DeviceCredentialsStore::StoreDeviceICACerts(const uint8_t * certs, uint16_t certsLen)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    VerifyOrExit(certs != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
+    VerifyOrExit(certsLen <= sizeof(mDeviceICACerts), err = WEAVE_ERROR_BUFFER_TOO_SMALL);
+
+    memcpy(mDeviceICACerts, certs, certsLen);
+    mDeviceICACertsLen = certsLen;
+
+exit:
+    return err;
+}
+
+WEAVE_ERROR DeviceCredentialsStore::StoreDevicePrivateKey(const uint8_t * key, uint16_t keyLen)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    VerifyOrExit(key != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
+    VerifyOrExit(keyLen <= sizeof(mDevicePrivateKey), err = WEAVE_ERROR_BUFFER_TOO_SMALL);
+
+    memcpy(mDevicePrivateKey, key, keyLen);
+    mDevicePrivateKeyLen = keyLen;
+
+exit:
+    return err;
+}
+
+WEAVE_ERROR DeviceCredentialsStore::GetDeviceId(uint64_t & deviceId)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    VerifyOrExit(mDeviceId != kNodeIdNotSpecified, err = WEAVE_ERROR_WRONG_NODE_ID);
+
+    deviceId = mDeviceId;
+
+exit:
+    return err;
+}
+
+WEAVE_ERROR DeviceCredentialsStore::GetDeviceCert(const uint8_t *& cert, uint16_t & certLen)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    VerifyOrExit(mDeviceCertLen > 0, err = WEAVE_ERROR_CERT_NOT_FOUND);
+
+    cert = mDeviceCert;
+    certLen = mDeviceCertLen;
+
+exit:
+    return err;
+}
+
+WEAVE_ERROR DeviceCredentialsStore::GetDeviceICACerts(const uint8_t *& cert, uint16_t & certLen)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    VerifyOrExit(mDeviceICACertsLen > 0, err = WEAVE_ERROR_CERT_NOT_FOUND);
+
+    cert = mDeviceICACerts;
+    certLen = mDeviceICACertsLen;
+
+exit:
+    return err;
+}
+
+WEAVE_ERROR DeviceCredentialsStore::GetDevicePrivateKey(const uint8_t *& key, uint16_t & keyLen)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    VerifyOrExit(mDevicePrivateKeyLen > 0, err = WEAVE_ERROR_KEY_NOT_FOUND);
+
+    key = mDevicePrivateKey;
+    keyLen = mDevicePrivateKeyLen;
+
+exit:
+    return err;
+}
+
+void DeviceCredentialsStore::ClearDeviceId(void)
+{
+    mDeviceId = kNodeIdNotSpecified;
+}
+
+void DeviceCredentialsStore::ClearDeviceCert(void)
+{
+    ClearSecretData(mDeviceCert, sizeof(mDeviceCert));
+    mDeviceCertLen = 0;
+}
+
+void DeviceCredentialsStore::ClearDeviceICACerts(void)
+{
+    ClearSecretData(mDeviceICACerts, sizeof(mDeviceICACerts));
+    mDeviceICACertsLen = 0;
+}
+
+void DeviceCredentialsStore::ClearDevicePrivateKey(void)
+{
+    ClearSecretData(mDevicePrivateKey, sizeof(mDevicePrivateKey));
+    mDevicePrivateKeyLen = 0;
+}
+
+void DeviceCredentialsStore::ClearDeviceCredentials(void)
+{
+    ClearDeviceId();
+    ClearDeviceCert();
+    ClearDeviceICACerts();
+    ClearDevicePrivateKey();
+}
+
+bool DeviceCredentialsStore::DeviceIdExists(void)
+{
+    return (mDeviceId != kNodeIdNotSpecified);
+}
+
+bool DeviceCredentialsStore::DeviceCertExists(void)
+{
+    return (mDeviceCertLen > 0);
+}
+
+bool DeviceCredentialsStore::DeviceICACertsExists(void)
+{
+    return (mDeviceICACertsLen > 0);
+}
+
+bool DeviceCredentialsStore::DevicePrivateKeyExists(void)
+{
+    return (mDevicePrivateKeyLen > 0);
+}
+
+bool DeviceCredentialsStore::DeviceCredentialsExist(void)
+{
+    return (DeviceIdExists() && DeviceCertExists() && DevicePrivateKeyExists());
+}
+
+static WEAVE_ERROR GenerateDeviceECDSASignature(const uint8_t *hash, uint8_t hashLen, EncodedECDSASignature& ecdsaSig)
+{
+    WEAVE_ERROR err;
+    const uint8_t * key = NULL;
+    uint16_t keyLen;
+    uint32_t weaveCurveId;
+    EncodedECPrivateKey devicePrivKey;
+    EncodedECPublicKey devicePubKey;
+
+    err = gDeviceCredsStore.GetDevicePrivateKey(key, keyLen);
+    SuccessOrExit(err);
+
+    err = DecodeWeaveECPrivateKey(key, keyLen, weaveCurveId, devicePubKey, devicePrivKey);
+    SuccessOrExit(err);
+
+    err = nl::Weave::Crypto::GenerateECDSASignature(WeaveCurveIdToOID(weaveCurveId),
+                                                    hash, hashLen, devicePrivKey, ecdsaSig);
+    SuccessOrExit(err);
+
+exit:
+    return err;
+}
+
+WEAVE_ERROR DeviceCredentialsStore::GenerateAndStoreDeviceCredentials(uint64_t deviceId)
+{
+    WEAVE_ERROR err;
+    uint8_t weaveKey[kWeaveDevicePrivateKeyBufSize];
+    uint32_t weaveKeyLen;
+    uint8_t weaveCert[kWeaveDeviceCertBufSize];
+    uint16_t weaveCertLen;
+    uint8_t privKey[EncodedECPrivateKey::kMaxValueLength];
+    uint8_t pubKey[EncodedECPublicKey::kMaxValueLength];
+    EncodedECPublicKey devicePubKey;
+    EncodedECPrivateKey devicePrivKey;
+
+    // If not specified, generate random device Id.
+    if (deviceId == kNodeIdNotSpecified)
+    {
+        err = GenerateWeaveNodeId(deviceId);
+        SuccessOrExit(err);
+    }
+
+    // Store generated device Id.
+    err = StoreDeviceId(deviceId);
+    SuccessOrExit(err);
+
+    devicePrivKey.PrivKey = privKey;
+    devicePrivKey.PrivKeyLen = sizeof(privKey);
+
+    devicePubKey.ECPoint = pubKey;
+    devicePubKey.ECPointLen = sizeof(pubKey);
+
+    // Generate random EC private/public key pair.
+    err = GenerateECDHKey(WeaveCurveIdToOID(WEAVE_CONFIG_OPERATIONAL_DEVICE_CERT_CURVE_ID), devicePubKey, devicePrivKey);
+    SuccessOrExit(err);
+
+    // Encode Weave device EC private/public key pair into EllipticCurvePrivateKey TLV structure.
+    err = EncodeWeaveECPrivateKey(WEAVE_CONFIG_OPERATIONAL_DEVICE_CERT_CURVE_ID, &devicePubKey, devicePrivKey,
+                                  weaveKey, sizeof(weaveKey), weaveKeyLen);
+    SuccessOrExit(err);
+
+    // Store generated device private key.
+    err = StoreDevicePrivateKey(weaveKey, weaveKeyLen);
+    SuccessOrExit(err);
+
+    // Generate self-signed operational device certificate.
+    err = GenerateOperationalDeviceCert(deviceId, devicePubKey, weaveCert, sizeof(weaveCert), weaveCertLen, GenerateDeviceECDSASignature);
+    SuccessOrExit(err);
+
+    // Store generated device certificate.
+    err = StoreDeviceCert(weaveCert, weaveCertLen);
+    SuccessOrExit(err);
+
+exit:
+    if (err != WEAVE_NO_ERROR)
+    {
+        ClearDeviceCredentials();
+    }
+    return err;
+}
+
+WEAVE_ERROR DeviceCredentialsStore::GenerateAndReplaceCurrentDeviceCert(void)
+{
+    WEAVE_ERROR err;
+    const uint8_t * currentCert;
+    uint16_t currentCertLen;
+    uint8_t cert[kWeaveDeviceCertBufSize];
+    uint16_t certLen;
+    uint8_t icaCert[kWeaveDeviceCertBufSize];
+    uint16_t icaCertLen;
+    WeaveCertificateSet certSet;
+    bool certSetInitialized = false;
+    WeaveCertificateData *certData = NULL;
+
+    // Get current certificate data.
+    {
+        err = GetDeviceCert(currentCert, currentCertLen);
+        SuccessOrExit(err);
+
+        err = certSet.Init(1, nl::TestCerts::kTestCertBufSize);
+        SuccessOrExit(err);
+
+        certSetInitialized = true;
+
+        // Load Weave operational device certificate.
+        err = certSet.LoadCert(currentCert, currentCertLen, kDecodeFlag_GenerateTBSHash, certData);
+        SuccessOrExit(err);
+    }
+
+    // Generate service assigned test device certificate.
+    err = GenerateTestDeviceCert(certData->SubjectDN.AttrValue.WeaveId, certData->PublicKey.EC,
+                                 cert, sizeof(cert), certLen);
+    SuccessOrExit(err);
+
+    err = StoreDeviceCert(cert, certLen);
+    SuccessOrExit(err);
+
+    // Write test intermediate CA certificate in a Weave TLV array form.
+    {
+        TLVWriter writer;
+        TLVType containerType;
+
+        writer.Init(icaCert, sizeof(icaCert));
+
+        err = writer.StartContainer(ProfileTag(kWeaveProfile_Security, kTag_WeaveCertificateList), kTLVType_Array, containerType);
+        SuccessOrExit(err);
+
+        err = writer.CopyContainer(AnonymousTag, nl::NestCerts::Development::DeviceCA::Cert, nl::NestCerts::Development::DeviceCA::CertLength);
+        SuccessOrExit(err);
+
+        err = writer.EndContainer(containerType);
+        SuccessOrExit(err);
+
+        err = writer.Finalize();
+        SuccessOrExit(err);
+
+        icaCertLen = writer.GetLengthWritten();
+    }
+
+    err = StoreDeviceICACerts(icaCert, icaCertLen);
+    SuccessOrExit(err);
+
+exit:
+    if (certSetInitialized)
+        certSet.Release();
+
+    return err;
+}
+
+NL_DLL_EXPORT WEAVE_ERROR GenerateTestDeviceCert(uint64_t deviceId, EncodedECPublicKey& devicePubKey,
+                                                 uint8_t *cert, uint16_t certBufSize, uint16_t& certLen)
+{
+    return GenerateTestDeviceCert(deviceId, devicePubKey,
+                                  nl::TestCerts::sTestCert_CA_Weave, nl::TestCerts::sTestCertLength_CA_Weave,
+                                  nl::TestCerts::sTestCert_CA_PrivateKey_Weave, nl::TestCerts::sTestCertLength_CA_PrivateKey_Weave,
+                                  cert, certBufSize, certLen);
+}
+
+NL_DLL_EXPORT WEAVE_ERROR GenerateTestDeviceCert(uint64_t deviceId, EncodedECPublicKey& devicePubKey,
+                                                 const uint8_t *caCert, uint16_t caCertLen,
+                                                 const uint8_t *caKey, uint16_t caKeyLen,
+                                                 uint8_t *cert, uint16_t certBufSize, uint16_t& certLen)
+{
+    WEAVE_ERROR err;
+    TLVWriter writer;
+    TLVType containerType;
+    TLVType containerType2;
+    TLVType containerType3;
+    uint8_t *certDecodeBuf = NULL;
+    WeaveCertificateSet certSet;
+    bool certSetInitialized = false;
+    WeaveCertificateData *caCertData = NULL;
+    WeaveCertificateData *certData = NULL;
+
+    VerifyOrExit(devicePubKey.ECPoint != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
+    VerifyOrExit(caCert != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
+    VerifyOrExit(caKey != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
+    VerifyOrExit(cert != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+    // Get CA certificate data.
+    {
+        err = certSet.Init(1, nl::TestCerts::kTestCertBufSize);
+        SuccessOrExit(err);
+
+        certSetInitialized = true;
+
+        // Load Weave operational device certificate.
+        err = certSet.LoadCert(caCert, caCertLen, kDecodeFlag_GenerateTBSHash, caCertData);
+        SuccessOrExit(err);
+    }
+
+    writer.Init(cert, certBufSize);
+
+    err = writer.StartContainer(ProfileTag(kWeaveProfile_Security, kTag_WeaveCertificate), kTLVType_Structure, containerType);
+    SuccessOrExit(err);
+
+    // Certificate serial number.
+    {
+        enum
+        {
+            kCertSerialNumber_Length          = 8,      // Length of the certificate serial number.
+            kCertSerialNumber_FirstByteMask   = 0x7F,   // Mask applied on the first byte of the key Id value.
+            kCertSerialNumber_FirstBytePrefix = 0x40,   // 4-bit Type value (0100) added at the beginning of the key Id value.
+        };
+        uint8_t certSerialNumber[kCertSerialNumber_Length];
+
+        // Generate a random value to be used as the serial number.
+        err = nl::Weave::Platform::Security::GetSecureRandomData(certSerialNumber, kCertSerialNumber_Length);
+        SuccessOrExit(err);
+
+        // Apply mask to avoid negative numbers.
+        certSerialNumber[0] &= kCertSerialNumber_FirstByteMask;
+
+        // Apply mask to guarantee the first byte is not zero.
+        certSerialNumber[0] |= kCertSerialNumber_FirstBytePrefix;
+
+        err = writer.PutBytes(ContextTag(kTag_SerialNumber), certSerialNumber, sizeof(certSerialNumber));
+        SuccessOrExit(err);
+    }
+
+    // Weave signature algorithm.
+    err = writer.Put(ContextTag(kTag_SignatureAlgorithm), static_cast<uint8_t>(kOID_SigAlgo_ECDSAWithSHA256 & ~kOIDCategory_Mask));
+    SuccessOrExit(err);
+
+    // Certificate issuer Id.
+    {
+        err = writer.StartContainer(ContextTag(kTag_Issuer), kTLVType_Path, containerType2);
+        SuccessOrExit(err);
+
+        err = writer.Put(ContextTag(kOID_AttributeType_WeaveDeviceId & kOID_Mask), caCertData->SubjectDN.AttrValue.WeaveId);
+        SuccessOrExit(err);
+
+        err = writer.EndContainer(containerType2);
+        SuccessOrExit(err);
+    }
+
+    // Certificate validity times.
+    err = writer.Put(ContextTag(kTag_NotBefore), PackedCertDateToTime(WEAVE_CONFIG_OP_DEVICE_CERT_VALID_DATE_NOT_BEFORE));
+    SuccessOrExit(err);
+
+    // Certificate validity period is 10 year.
+    err = writer.Put(ContextTag(kTag_NotAfter), PackedCertDateToTime(WEAVE_CONFIG_OP_DEVICE_CERT_VALID_DATE_NOT_BEFORE + (10 * 12 * 31)));
+    SuccessOrExit(err);
+
+    // Certificate subject Id.
+    {
+        err = writer.StartContainer(ContextTag(kTag_Subject), kTLVType_Path, containerType2);
+        SuccessOrExit(err);
+
+        err = writer.Put(ContextTag(kOID_AttributeType_WeaveDeviceId & kOID_Mask), deviceId);
+        SuccessOrExit(err);
+
+        err = writer.EndContainer(containerType2);
+        SuccessOrExit(err);
+    }
+
+    // EC public key algorithm.
+    err = writer.Put(ContextTag(kTag_PublicKeyAlgorithm), static_cast<uint8_t>(kOID_PubKeyAlgo_ECPublicKey & kOID_Mask));
+    SuccessOrExit(err);
+
+    // EC public key curve Id.
+    err = writer.Put(ContextTag(kTag_EllipticCurveIdentifier), static_cast<uint32_t>(WEAVE_CONFIG_OPERATIONAL_DEVICE_CERT_CURVE_ID));
+    SuccessOrExit(err);
+
+    // EC public key.
+    err = writer.PutBytes(ContextTag(kTag_EllipticCurvePublicKey), devicePubKey.ECPoint, devicePubKey.ECPointLen);
+    SuccessOrExit(err);
+
+    // Certificate extension: basic constraints.
+    {
+        err = writer.StartContainer(ContextTag(kTag_BasicConstraints), kTLVType_Structure, containerType2);
+        SuccessOrExit(err);
+
+        // This extension is critical.
+        err = writer.PutBoolean(ContextTag(kTag_BasicConstraints_Critical), true);
+        SuccessOrExit(err);
+
+        err = writer.EndContainer(containerType2);
+        SuccessOrExit(err);
+    }
+
+    // Certificate extension: key usage.
+    {
+        err = writer.StartContainer(ContextTag(kTag_KeyUsage), kTLVType_Structure, containerType2);
+        SuccessOrExit(err);
+
+        // This extension is critical.
+        err = writer.PutBoolean(ContextTag(kTag_KeyUsage_Critical), true);
+        SuccessOrExit(err);
+
+        err = writer.Put(ContextTag(kTag_KeyUsage_KeyUsage), static_cast<uint16_t>(kKeyUsageFlag_DigitalSignature | kKeyUsageFlag_KeyEncipherment));
+        SuccessOrExit(err);
+
+        err = writer.EndContainer(containerType2);
+        SuccessOrExit(err);
+    }
+
+    // Certificate extension: extended key usage.
+    {
+        err = writer.StartContainer(ContextTag(kTag_ExtendedKeyUsage), kTLVType_Structure, containerType2);
+        SuccessOrExit(err);
+
+        // This extension is critical.
+        err = writer.PutBoolean(ContextTag(kTag_ExtendedKeyUsage_Critical), true);
+        SuccessOrExit(err);
+
+        err = writer.StartContainer(ContextTag(kTag_ExtendedKeyUsage_KeyPurposes), kTLVType_Array, containerType3);
+        SuccessOrExit(err);
+
+        // Key purpose is client authentication.
+        err = writer.Put(AnonymousTag, static_cast<uint8_t>(kOID_KeyPurpose_ClientAuth & kOID_Mask));
+        SuccessOrExit(err);
+
+        // Key purpose is server authentication.
+        err = writer.Put(AnonymousTag, static_cast<uint8_t>(kOID_KeyPurpose_ServerAuth & kOID_Mask));
+        SuccessOrExit(err);
+
+        err = writer.EndContainer(containerType3);
+        SuccessOrExit(err);
+
+        err = writer.EndContainer(containerType2);
+        SuccessOrExit(err);
+    }
+
+    // Certificate extension: subject key identifier.
+    {
+        /* Use "truncated" SHA-1 hash. Per RFC5280:
+         *
+         *     "(2) The keyIdentifier is composed of a four-bit type field with the value 0100 followed
+         *     by the least significant 60 bits of the SHA-1 hash of the value of the BIT STRING
+         *     subjectPublicKey (excluding the tag, length, and number of unused bits)."
+         */
+        enum
+        {
+            kCertKeyId_Length          = 8,      // Length of the certificate key identifier.
+            kCertKeyId_FirstByte       = nl::Weave::Platform::Security::SHA1::kHashLength - kCertKeyId_Length,
+                                                 // First byte of the SHA1 hash that used to generate certificate keyId.
+            kCertKeyId_FirstByteMask   = 0x0F,   // Mask applied on the first byte of the key Id value.
+            kCertKeyId_FirstBytePrefix = 0x40,   // 4-bit Type value (0100) added at the beginning of the key Id value.
+        };
+        nl::Weave::Platform::Security::SHA1 sha1;
+        uint8_t hash[nl::Weave::Platform::Security::SHA1::kHashLength];
+        uint8_t *certKeyId = &hash[kCertKeyId_FirstByte];
+
+        sha1.Begin();
+        sha1.AddData(devicePubKey.ECPoint, devicePubKey.ECPointLen);
+        sha1.Finish(hash);
+
+        certKeyId[0] &= kCertKeyId_FirstByteMask;
+        certKeyId[0] |= kCertKeyId_FirstBytePrefix;
+
+        err = writer.StartContainer(ContextTag(kTag_SubjectKeyIdentifier), kTLVType_Structure, containerType2);
+        SuccessOrExit(err);
+
+        err = writer.PutBytes(ContextTag(kTag_SubjectKeyIdentifier_KeyIdentifier), certKeyId, kCertKeyId_Length);
+        SuccessOrExit(err);
+
+        err = writer.EndContainer(containerType2);
+        SuccessOrExit(err);
+    }
+
+    // Certificate extension: authority key identifier.
+    {
+        err = writer.StartContainer(ContextTag(kTag_AuthorityKeyIdentifier), kTLVType_Structure, containerType2);
+        SuccessOrExit(err);
+
+        err = writer.PutBytes(ContextTag(kTag_AuthorityKeyIdentifier_KeyIdentifier), caCertData->SubjectKeyId.Id, caCertData->SubjectKeyId.Len);
+        SuccessOrExit(err);
+
+        err = writer.EndContainer(containerType2);
+        SuccessOrExit(err);
+    }
+
+    // Start the ECDSASignature structure.
+    // Note that the ECDSASignature tag is added here but the actual certificate data (S and R values)
+    // will be written later. This is needed to prevent DecodeConvertTBSCert() function from failing.
+    // This function expects to read new non-hashable element after all TBS data is converted.
+    err = writer.StartContainer(ContextTag(kTag_ECDSASignature), kTLVType_Structure, containerType2);
+    SuccessOrExit(err);
+
+    {
+        enum
+        {
+            kCertDecodeBufferSize      = 1024,   // Maximum ASN1 encoded size of the operational device certificate.
+        };
+        TLVReader reader;
+        ASN1Writer tbsWriter;
+        TLVType readContainerType;
+
+        reader.Init(cert, certBufSize);
+
+        // Parse the beginning of the WeaveSignature structure.
+        err = reader.Next(kTLVType_Structure, ProfileTag(kWeaveProfile_Security, kTag_WeaveCertificate));
+        SuccessOrExit(err);
+
+        // Enter the certificate structure.
+        err = reader.EnterContainer(readContainerType);
+        SuccessOrExit(err);
+
+        // Allocate decode memory buffer.
+        certDecodeBuf = static_cast<uint8_t *>(nl::Weave::Platform::Security::MemoryAlloc(kCertDecodeBufferSize));
+        VerifyOrExit(certDecodeBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
+
+        // Allocate certificate data  structure.
+        certData = static_cast<WeaveCertificateData *>(nl::Weave::Platform::Security::MemoryAlloc(sizeof(WeaveCertificateData)));
+        VerifyOrExit(certData != NULL, err = WEAVE_ERROR_NO_MEMORY);
+
+        // Initialize an ASN1Writer and convert the TBS (to-be-signed) portion of
+        // the certificate to ASN.1 DER encoding.
+        tbsWriter.Init(certDecodeBuf, kCertDecodeBufferSize);
+        err = DecodeConvertTBSCert(reader, tbsWriter, *certData);
+        SuccessOrExit(err);
+
+        // Finish writing the ASN.1 DER encoding of the TBS certificate.
+        err = tbsWriter.Finalize();
+        SuccessOrExit(err);
+
+        // Generate a SHA hash of the encoded TBS certificate.
+        nl::Weave::Platform::Security::SHA256 sha256;
+        sha256.Begin();
+        sha256.AddData(certDecodeBuf, tbsWriter.GetLengthWritten());
+        sha256.Finish(certData->TBSHash);
+
+        uint32_t caCurveId;
+        EncodedECPublicKey caPubKey;
+        EncodedECPrivateKey caPrivKey;
+
+        // Decode the CA private key.
+        err = DecodeWeaveECPrivateKey(caKey, caKeyLen, caCurveId, caPubKey, caPrivKey);
+        SuccessOrExit(err);
+
+        // Reuse already allocated decode buffer to hold the generated signature value.
+        EncodedECDSASignature ecdsaSig;
+        ecdsaSig.R = certDecodeBuf;
+        ecdsaSig.RLen = EncodedECDSASignature::kMaxValueLength;
+        ecdsaSig.S = certDecodeBuf + EncodedECDSASignature::kMaxValueLength;
+        ecdsaSig.SLen = EncodedECDSASignature::kMaxValueLength;
+
+        // Generate an ECDSA signature for the given message hash.
+        err = nl::Weave::Crypto::GenerateECDSASignature(WeaveCurveIdToOID(caCurveId), certData->TBSHash, nl::Weave::Platform::Security::SHA256::kHashLength, caPrivKey, ecdsaSig);
+        SuccessOrExit(err);
+
+        // Write the R value.
+        err = writer.PutBytes(ContextTag(kTag_ECDSASignature_r), ecdsaSig.R, ecdsaSig.RLen);
+        SuccessOrExit(err);
+
+        // Write the S value.
+        err = writer.PutBytes(ContextTag(kTag_ECDSASignature_s), ecdsaSig.S, ecdsaSig.SLen);
+        SuccessOrExit(err);
+    }
+
+    err = writer.EndContainer(containerType2);
+    SuccessOrExit(err);
+
+    err = writer.EndContainer(containerType);
+    SuccessOrExit(err);
+
+    err = writer.Finalize();
+    SuccessOrExit(err);
+
+    certLen = static_cast<uint16_t>(writer.GetLengthWritten());
+
+exit:
+    if (certDecodeBuf != NULL)
+        nl::Weave::Platform::Security::MemoryFree(certDecodeBuf);
+
+    if (certSetInitialized)
+        certSet.Release();
+
+    return err;
+}
+
+WEAVE_ERROR ValidateWeaveDeviceCert(WeaveCertificateSet & certSet)
+{
+    WEAVE_ERROR err;
+    WeaveCertificateData * cert = certSet.Certs;
+    bool isSelfSigned = cert->IssuerDN.IsEqual(cert->SubjectDN);
+    enum { kLastSecondOfDay = nl::kSecondsPerDay - 1 };
+
+    // Verify that the certificate of device type.
+    VerifyOrExit(cert->CertType == kCertType_Device, err = WEAVE_ERROR_WRONG_CERT_TYPE);
+
+    // Verify correct subject attribute.
+    VerifyOrExit(cert->SubjectDN.AttrOID == ASN1::kOID_AttributeType_WeaveDeviceId, err = WEAVE_ERROR_WRONG_CERT_SUBJECT);
+
+    // Verify that the key usage extension exists in the certificate and that the corresponding usages are supported.
+    VerifyOrExit((cert->CertFlags & kCertFlag_ExtPresent_KeyUsage) != 0 &&
+                 (cert->KeyUsageFlags == (kKeyUsageFlag_DigitalSignature | kKeyUsageFlag_KeyEncipherment)),
+                 err = WEAVE_ERROR_CERT_USAGE_NOT_ALLOWED);
+
+    // Verify the validity time of the certificate.
+    {
+        uint32_t effectiveTime;
+        ASN1UniversalTime effectiveTimeASN1 = {
+            .Year   = 2020,
+            .Month  = 01,
+            .Day    = 01,
+            .Hour   = 00,
+            .Minute = 00,
+            .Second = 00
+        };
+        err = PackCertTime(effectiveTimeASN1, effectiveTime);
+        SuccessOrExit(err);
+
+        VerifyOrExit(effectiveTime >= PackedCertDateToTime(cert->NotBeforeDate), err = WEAVE_ERROR_CERT_NOT_VALID_YET);
+
+        VerifyOrExit(effectiveTime <= PackedCertDateToTime(cert->NotAfterDate) + kLastSecondOfDay, err = WEAVE_ERROR_CERT_EXPIRED);
+    }
+
+    // Verify that a hash of the 'to-be-signed' portion of the certificate has been computed. We will need this to
+    // verify the cert's signature below.
+    VerifyOrExit((cert->CertFlags & kCertFlag_TBSHashPresent) != 0, err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+    // Verify correct public key algorithm.
+    VerifyOrExit(cert->PubKeyAlgoOID == kOID_PubKeyAlgo_ECPublicKey, err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+    // Verify correct key purpose.
+    VerifyOrExit(cert->KeyPurposeFlags == (kKeyPurposeFlag_ServerAuth | kKeyPurposeFlag_ClientAuth), err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+    // Verify correct EC curve.
+    VerifyOrExit((cert->PubKeyCurveId == kWeaveCurveId_prime256v1) ||
+                 (cert->PubKeyCurveId == kWeaveCurveId_secp224r1), err = WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE);
+
+    if (isSelfSigned)
+    {
+        // Verify that the certificate is self-signed.
+        VerifyOrExit(cert->AuthKeyId.IsEqual(cert->SubjectKeyId), err = WEAVE_ERROR_WRONG_CERT_SUBJECT);
+
+        // Verify the signature algorithm.
+        VerifyOrExit(cert->SigAlgoOID == kOID_SigAlgo_ECDSAWithSHA256, err = WEAVE_ERROR_WRONG_CERT_SIGNATURE_ALGORITHM);
+
+        // Verify certificate signature.
+        err = VerifyECDSASignature(WeaveCurveIdToOID(cert->PubKeyCurveId),
+                                   cert->TBSHash, SHA256::kHashLength,
+                                   cert->Signature.EC, cert->PublicKey.EC);
+        SuccessOrExit(err);
+    }
+    else
+    {
+        CertificateKeyId caKeyId;
+        EncodedECPublicKey caPublicKey;
+        OID caCurveOID;
+        uint8_t tbsHashLen;
+
+        if (cert->IssuerDN.AttrValue.WeaveId == nl::NestCerts::Development::DeviceCA::CAId)
+        {
+            caKeyId.Id = nl::NestCerts::Development::DeviceCA::SubjectKeyId;
+            caKeyId.Len = static_cast<uint8_t>(nl::NestCerts::Development::DeviceCA::SubjectKeyIdLength);
+
+            caPublicKey.ECPoint = const_cast<uint8_t *>(nl::NestCerts::Development::DeviceCA::PublicKey);
+            caPublicKey.ECPointLen = static_cast<uint16_t>(nl::NestCerts::Development::DeviceCA::PublicKeyLength);
+
+            caCurveOID = WeaveCurveIdToOID(nl::NestCerts::Development::DeviceCA::CurveOID);
+        }
+        else if (cert->IssuerDN.AttrValue.WeaveId == nl::TestCerts::sTestCert_CA_Id)
+        {
+            caKeyId.Id = nl::TestCerts::sTestCert_CA_SubjectKeyId;
+            caKeyId.Len = nl::TestCerts::sTestCertLength_CA_SubjectKeyId;
+
+            caPublicKey.ECPoint = const_cast<uint8_t *>(nl::TestCerts::sTestCert_CA_PublicKey);
+            caPublicKey.ECPointLen = static_cast<uint16_t>(nl::TestCerts::sTestCertLength_CA_PublicKey);
+
+            caCurveOID = WeaveCurveIdToOID(nl::TestCerts::sTestCert_CA_CurveId);
+        }
+        else
+        {
+            ExitNow(err = WEAVE_ERROR_WRONG_CERT_SUBJECT);
+        }
+
+        // Verify that the certificate is signed by the device CA.
+        VerifyOrExit(cert->AuthKeyId.IsEqual(caKeyId), err = WEAVE_ERROR_WRONG_CERT_SUBJECT);
+
+        // Verify the signature algorithm.
+        VerifyOrExit((cert->SigAlgoOID == kOID_SigAlgo_ECDSAWithSHA256) ||
+                     (cert->SigAlgoOID == kOID_SigAlgo_ECDSAWithSHA1), err = WEAVE_ERROR_WRONG_CERT_SIGNATURE_ALGORITHM);
+
+        tbsHashLen = ((cert->SigAlgoOID == kOID_SigAlgo_ECDSAWithSHA256) ? static_cast<uint8_t>(SHA256::kHashLength) : static_cast<uint8_t>(SHA1::kHashLength));
+
+        // Verify certificate signature.
+        err = VerifyECDSASignature(caCurveOID, cert->TBSHash, tbsHashLen, cert->Signature.EC, caPublicKey);
+        SuccessOrExit(err);
+    }
+
+exit:
+    return err;
+}
+
+/**
+ *  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 CertProvOptions::CertProvClientEventHandler(void * appState, WeaveCertProvEngine::EventType eventType,
+                                                 const WeaveCertProvEngine::InEventParam & inParam, WeaveCertProvEngine::OutEventParam & outParam)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+    WeaveCertificateSet certSet;
+    bool certSetInitialized = false;
+    CertProvOptions * certProvOptions = static_cast<CertProvOptions *>(appState);
+    WeaveCertProvEngine * certProvEngine = inParam.Source;
+    Binding *binding = certProvEngine->GetBinding();
+    uint64_t peerNodeId;
+    IPAddress peerAddr;
+    uint16_t peerPort;
+    InterfaceId peerInterfaceId;
+
+    if (binding != NULL)
+    {
+        peerNodeId = binding->GetPeerNodeId();
+        binding->GetPeerIPAddress(peerAddr, peerPort, peerInterfaceId);
+    }
+
+    switch (eventType)
+    {
+    case WeaveCertProvEngine::kEvent_PrepareAuthorizeInfo:
+    {
+        if (binding != NULL)
+            WeaveLogDetail(SecurityManager, "WeaveCertProvEngine::kEvent_PrepareAuthorizeInfo; to node %" PRIX64 " (%s)", peerNodeId, peerAddr);
+        else
+            WeaveLogDetail(SecurityManager, "WeaveCertProvEngine::kEvent_PrepareAuthorizeInfo");
+
+        if (certProvOptions->IncludeAuthorizeInfo)
+        {
+            TLVWriter * writer = inParam.PrepareAuthorizeInfo.Writer;
+
+            // Pairing Token.
+            err = writer->PutBytes(ContextTag(kTag_GetCertReqMsg_Authorize_PairingToken), certProvOptions->PairingToken, certProvOptions->PairingTokenLen);
+            SuccessOrExit(err);
+
+            // Pairing Initialization Data.
+            err = writer->PutBytes(ContextTag(kTag_GetCertReqMsg_Authorize_PairingInitData), certProvOptions->PairingInitData, certProvOptions->PairingInitDataLen);
+            SuccessOrExit(err);
+        }
+        break;
+    }
+
+    case WeaveCertProvEngine::kEvent_ResponseReceived:
+    {
+        if (inParam.ResponseReceived.ReplaceCert)
+        {
+            enum
+            {
+                kMaxCerts                         = 4,      // Maximum number of certificates in the certificate verification chain.
+                kCertDecodeBufSize                = 1024,   // Size of buffer needed to hold any of the test certificates
+                                                            // (in either Weave or DER form), or to decode the certificates.
+            };
+            WeaveCertificateData *certData;
+            const uint8_t * cert = inParam.ResponseReceived.Cert;
+            uint16_t certLen = inParam.ResponseReceived.CertLen;
+            const uint8_t * relatedCerts = inParam.ResponseReceived.RelatedCerts;
+            uint16_t relatedCertsLen = inParam.ResponseReceived.RelatedCertsLen;
+
+            if (binding != NULL)
+                WeaveLogDetail(SecurityManager, "WeaveCertProvEngine::kEvent_ResponseReceived; from node %" PRIX64 " (%s)", peerNodeId, peerAddr);
+            else
+                WeaveLogDetail(SecurityManager, "WeaveCertProvEngine::kEvent_ResponseReceived");
+
+            // This certificate validation step is added for testing purposes only.
+            // In reality, device doesn't have to validate certificate issued by the CA service.
+            {
+                err = certSet.Init(kMaxCerts, kCertDecodeBufSize);
+                SuccessOrExit(err);
+
+                certSetInitialized = true;
+
+                err = certSet.LoadCert(cert, certLen, kDecodeFlag_GenerateTBSHash, certData);
+                SuccessOrExit(err);
+
+                if (relatedCerts != NULL)
+                {
+                    err = certSet.LoadCerts(relatedCerts, relatedCertsLen, kDecodeFlag_GenerateTBSHash);
+                    SuccessOrExit(err);
+                }
+
+                err = ValidateWeaveDeviceCert(certSet);
+                SuccessOrExit(err);
+            }
+
+            // Store service assigned operational device certificate.
+            err = gDeviceCredsStore.StoreDeviceCert(cert, certLen);
+            SuccessOrExit(err);
+
+            // Store service assigned device intermediate CA certificates.
+            if (relatedCerts != NULL)
+            {
+                err = gDeviceCredsStore.StoreDeviceICACerts(relatedCerts, relatedCertsLen);
+                SuccessOrExit(err);
+            }
+        }
+        else
+        {
+            if (binding != NULL)
+                WeaveLogDetail(SecurityManager, "WeaveCertProvEngine::kEvent_ResponseReceived; received status report from node %" PRIX64 " (%s): "
+                               "No Need to Replace Operational Device Certificate", peerNodeId, peerAddr);
+            else
+                WeaveLogDetail(SecurityManager, "WeaveCertProvEngine::kEvent_ResponseReceived; received status report: No Need to Replace Operational Device Certificate");
+        }
+
+        certProvEngine->AbortCertificateProvisioning();
+
+        break;
+    }
+
+    case WeaveCertProvEngine::kEvent_CommunicationError:
+    {
+        if (inParam.CommunicationError.Reason == WEAVE_ERROR_STATUS_REPORT_RECEIVED)
+        {
+            if (binding != NULL)
+                WeaveLogError(SecurityManager, "WeaveCertProvEngine::kEvent_CommunicationError; received status report from node %" PRIX64 " (%s): %s", peerNodeId, peerAddr,
+                              nl::StatusReportStr(inParam.CommunicationError.RcvdStatusReport->mProfileId, inParam.CommunicationError.RcvdStatusReport->mStatusCode));
+            else
+                WeaveLogError(SecurityManager, "WeaveCertProvEngine::kEvent_CommunicationError; received status report: %s",
+                              nl::StatusReportStr(inParam.CommunicationError.RcvdStatusReport->mProfileId, inParam.CommunicationError.RcvdStatusReport->mStatusCode));
+        }
+        else
+        {
+            if (binding != NULL)
+                WeaveLogError(SecurityManager, "WeaveCertProvEngine::kEvent_CommunicationError with node %" PRIX64 " (%s): %s",
+                              peerNodeId, peerAddr, ErrorStr(inParam.CommunicationError.Reason));
+            else
+                WeaveLogError(SecurityManager, "WeaveCertProvEngine::kEvent_CommunicationError: %s",
+                              ErrorStr(inParam.CommunicationError.Reason));
+        }
+
+        certProvEngine->AbortCertificateProvisioning();
+
+        break;
+    }
+
+    default:
+        if (binding != NULL)
+            WeaveLogError(SecurityManager, "WeaveCertProvEngine: unrecognized API event with node %" PRIX64 " (%s)", peerNodeId, peerAddr);
+        else
+            WeaveLogError(SecurityManager, "WeaveCertProvEngine: unrecognized API event");
+        break;
+    }
+
+exit:
+    if (eventType == WeaveCertProvEngine::kEvent_PrepareAuthorizeInfo)
+        outParam.PrepareAuthorizeInfo.Error = err;
+    else if (eventType == WeaveCertProvEngine::kEvent_ResponseReceived)
+        outParam.ResponseReceived.Error = err;
+
+    if (certSetInitialized)
+        certSet.Release();
+}
+
+bool ParseGetCertReqType(const char *str, uint8_t& output)
+{
+    int reqType;
+
+    if (!ParseInt(str, reqType))
+        return false;
+
+    switch (reqType)
+    {
+    case 1:
+        output = WeaveCertProvEngine::kReqType_GetInitialOpDeviceCert;
+        return true;
+    case 2:
+        output = WeaveCertProvEngine::kReqType_RotateOpDeviceCert;
+        return true;
+    default:
+        return false;
+    }
+}
+
+bool ParseMfrAttestType(const char *str, uint8_t& output)
+{
+    int maType;
+
+    if (!ParseInt(str, maType))
+        return false;
+
+    switch (maType)
+    {
+    case 1:
+        output = kMfrAttestType_WeaveCert;
+        return true;
+    case 2:
+        output = kMfrAttestType_X509Cert;
+        return true;
+    case 3:
+        output = kMfrAttestType_HMAC;
+        return true;
+    default:
+        return false;
+    }
+}
+
+CertProvOptions::CertProvOptions()
+{
+    static OptionDef optionDefs[] =
+    {
+        { "get-cert-req-type",      kArgumentRequired,      kToolCommonOpt_GetCertReqType        },
+        { "pairing-token",          kArgumentRequired,      kToolCommonOpt_PairingToken          },
+        { "send-auth-info",         kNoArgument,            kToolCommonOpt_SendAuthorizeInfo     },
+        { "op-cert",                kArgumentRequired,      kToolCommonOpt_OpCert                },
+        { "op-key",                 kArgumentRequired,      kToolCommonOpt_OpKey                 },
+        { "op-ca-cert",             kArgumentRequired,      kToolCommonOpt_OpICACerts            },
+        { "send-op-ca-cert",        kNoArgument,            kToolCommonOpt_SendOpICACerts        },
+        { "ma-type",                kArgumentRequired,      kToolCommonOpt_MfrAttestType         },
+        { "ma-node-id",             kArgumentRequired,      kToolCommonOpt_MfrAttestNodeId       },
+        { "ma-cert",                kArgumentRequired,      kToolCommonOpt_MfrAttestCert         },
+        { "ma-key",                 kArgumentRequired,      kToolCommonOpt_MfrAttestKey          },
+        { "ma-ca-cert",             kArgumentRequired,      kToolCommonOpt_MfrAttestICACert1     },
+        { "ma-ca-cert2",            kArgumentRequired,      kToolCommonOpt_MfrAttestICACert2     },
+        { "send-ma-ca-cert",        kNoArgument,            kToolCommonOpt_SendMfrAttestICACerts },
+        { NULL }
+    };
+    OptionDefs = optionDefs;
+
+    HelpGroupName = "CERTIFICATE PROVISIONING OPTIONS";
+
+    OptionHelp =
+        "  --get-cert-req-type <int>\n"
+        "       Get Certificate Request type. If not specified the default value is used.\n"
+        "       Valid values are:\n"
+        "           1 - get initial operational certificate (default).\n"
+        "           2 - rotate operational certificate.\n"
+        "\n"
+        "  --pairing-token <pairing-token-file>\n"
+        "       File containing a Weave Pairing Token to be used to authorize the certificate\n"
+        "       provisioning request. If not specified the default test pairing token is used.\n"
+        "\n"
+        "  --send-auth-info\n"
+        "       Include an authorization information in the Get Certificate Request message.\n"
+        "\n"
+        "  --op-cert <cert-file>\n"
+        "       File containing a Weave Operational certificate to be used to authenticate the node\n"
+        "       when establishing a CASE session. The file can contain either raw TLV or\n"
+        "       base-64. If not specified the default test certificate is used.\n"
+        "\n"
+        "  --op-key <key-file>\n"
+        "       File containing an Operational private key to be used to authenticate the node's\n"
+        "       when establishing a CASE session. The file can contain either raw TLV or\n"
+        "       base-64. If not specified the default test key is used.\n"
+        "\n"
+        "  --op-ca-cert <cert-file>\n"
+        "       File containing a Weave Operational CA certificate to be included along with the\n"
+        "       node's Operational certificate in the Get Certificat Request message. The file can contain\n"
+        "       either raw TLV or base-64. If not specified the default test CA certificate is used.\n"
+        "\n"
+        "  --send-op-ca-cert\n"
+        "       Include a Weave Operational CA certificate in the Get Certificat Request message.\n"
+        "       This option is set automatically when op-ca-cert is specified.\n"
+        "\n"
+        "  --ma-type <int>\n"
+        "       Device Manufacturer Attestation type. If not specified the default value is used.\n"
+        "       Supported options are:\n"
+        "           1 - Weave certificate (default).\n"
+        "           2 - X509 RSA certificate.\n"
+        "           3 - HMAC Attestation.\n"
+        "\n"
+        "  --ma-node-id <int>\n"
+        "       Device Manufacturer Attestation node id. If not specified the default test device #1\n"
+        "       node id is used.\n"
+        "\n"
+        "  --ma-cert <cert-file>\n"
+        "       File containing a Weave Manufacturer Attestation certificate to be used to authenticate\n"
+        "       the node's manufacturer. The file can contain either raw TLV or base-64. If not\n"
+        "       specified the default test certificate is used.\n"
+        "\n"
+        "  --ma-key <key-file>\n"
+        "       File containing a Manufacturer Attestation private key to be used to authenticate\n"
+        "       the node's manufacturer. The file can contain either raw TLV orbase-64. If not\n"
+        "       specified the default test key is used.\n"
+        "\n"
+        "  --ma-ca-cert <cert-file>\n"
+        "       File containing a Weave Manufacturer Attestation CA certificate to be included along\n"
+        "       with the node's Manufacturer Attestation certificate in the Get Certificat Request\n"
+        "       message. The file can contain either raw TLV or base-64. If not specified the default\n"
+        "       test CA certificate is used.\n"
+        "\n"
+        "  --ma-ca-cert2 <cert-file>\n"
+        "       File containing a Weave Manufacturer Attestation second CA certificate to be included along\n"
+        "       with the node's Manufacturer Attestation certificate in the Get Certificat Request\n"
+        "       message. The file can contain either raw TLV or base-64. If not specified the default\n"
+        "       test CA certificate is used.\n"
+        "\n"
+        "  --send-ma-ca-cert\n"
+        "       Include a Weave Manufacturer Attestation CA certificate in the Get Certificat Request message.\n"
+        "       This option is set automatically when ma-ca-cert is specified.\n"
+      "";
+
+    // Defaults
+    DeviceId = kNodeIdNotSpecified;
+    RequestType = WeaveCertProvEngine::kReqType_GetInitialOpDeviceCert;
+    IncludeAuthorizeInfo = false;
+    PairingToken = TestPairingToken;
+    PairingTokenLen = TestPairingTokenLength;
+    PairingInitData = TestPairingInitData;
+    PairingInitDataLen = TestPairingInitDataLength;
+    OperationalCert = NULL;
+    OperationalCertLen = 0;
+    OperationalPrivateKey = NULL;
+    OperationalPrivateKeyLen = 0;
+    IncludeOperationalICACerts = false;
+    OperationalICACerts = NULL;
+    OperationalICACertsLen = 0;
+    MfrAttestType = kMfrAttestType_WeaveCert;
+    MfrAttestDeviceId = TestDevice1_NodeId;
+    MfrAttestCert = NULL;
+    MfrAttestCertLen = 0;
+    MfrAttestPrivateKey = NULL;
+    MfrAttestPrivateKeyLen = 0;
+    IncludeMfrAttestICACerts = false;
+    MfrAttestICACert1 = NULL;
+    MfrAttestICACert1Len = 0;
+    MfrAttestICACert2 = NULL;
+    MfrAttestICACert2Len = 0;
+}
+
+bool CertProvOptions::HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg)
+{
+    uint32_t len;
+
+    switch (id)
+    {
+    case kToolCommonOpt_GetCertReqType:
+        if (!ParseGetCertReqType(arg, RequestType))
+        {
+            PrintArgError("%s: Invalid value specified for GetCertificate request type: %s\n", progName, arg);
+            return false;
+        }
+        break;
+
+    case kToolCommonOpt_PairingToken:
+        PairingToken = ReadFileArg(arg, len);
+        if (PairingToken == NULL)
+            return false;
+        PairingTokenLen = len;
+        break;
+
+    case kToolCommonOpt_PairingInitData:
+        PairingInitData = ReadFileArg(arg, len);
+        if (PairingInitData == NULL)
+            return false;
+        PairingInitDataLen = len;
+        break;
+
+    case kToolCommonOpt_SendAuthorizeInfo:
+        IncludeAuthorizeInfo = true;
+        break;
+
+    case kToolCommonOpt_OpCert:
+        if (!ReadCertFile(arg, (uint8_t *&)OperationalCert, OperationalCertLen))
+            return false;
+        break;
+
+    case kToolCommonOpt_OpKey:
+        if (!ReadPrivateKeyFile(arg, (uint8_t *&)OperationalPrivateKey, OperationalPrivateKeyLen))
+            return false;
+        break;
+
+    case kToolCommonOpt_OpICACerts:
+        if (!ReadCertFile(arg, (uint8_t *&)OperationalICACerts, OperationalICACertsLen))
+            return false;
+        break;
+
+   case kToolCommonOpt_SendOpICACerts:
+        IncludeOperationalICACerts = true;
+        break;
+
+    case kToolCommonOpt_MfrAttestType:
+        if (!ParseMfrAttestType(arg, MfrAttestType))
+        {
+            PrintArgError("%s: Invalid value specified for manufacturer attestation type: %s\n", progName, arg);
+            return false;
+        }
+        break;
+
+    case kToolCommonOpt_MfrAttestNodeId:
+        if (!ParseNodeId(arg, MfrAttestDeviceId))
+        {
+            PrintArgError("%s: Invalid value specified for manufacturer attestation node id: %s\n", progName, arg);
+            return false;
+        }
+        break;
+
+    case kToolCommonOpt_MfrAttestCert:
+        if (!ReadCertFile(arg, (uint8_t *&)MfrAttestCert, MfrAttestCertLen))
+            return false;
+        break;
+
+    case kToolCommonOpt_MfrAttestKey:
+        if (!ReadPrivateKeyFile(arg, (uint8_t *&)MfrAttestPrivateKey, MfrAttestPrivateKeyLen))
+            return false;
+        break;
+
+    case kToolCommonOpt_MfrAttestICACert1:
+        if (!ReadCertFile(arg, (uint8_t *&)MfrAttestICACert1, MfrAttestICACert1Len))
+            return false;
+        break;
+
+    case kToolCommonOpt_MfrAttestICACert2:
+        if (!ReadCertFile(arg, (uint8_t *&)MfrAttestICACert2, MfrAttestICACert2Len))
+            return false;
+        break;
+
+    case kToolCommonOpt_SendMfrAttestICACerts:
+        IncludeMfrAttestICACerts = true;
+        break;
+
+    default:
+        PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", progName, name);
+        return false;
+    }
+
+    return true;
+}
+
+// ===== Methods that implement the WeaveNodeOpAuthDelegate interface
+
+WEAVE_ERROR CertProvOptions::EncodeOpCert(TLVWriter & writer, uint64_t tag)
+{
+    WEAVE_ERROR err;
+    const uint8_t * cert = OperationalCert;
+    uint16_t certLen = OperationalCertLen;
+
+    if (cert == NULL || certLen == 0)
+    {
+        err = gDeviceCredsStore.GetDeviceCert(cert, certLen);
+        SuccessOrExit(err);
+    }
+
+    err = writer.CopyContainer(tag, cert, certLen);
+    SuccessOrExit(err);
+
+exit:
+    return err;
+}
+
+WEAVE_ERROR CertProvOptions::EncodeOpRelatedCerts(TLVWriter & writer, uint64_t tag)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+    TLVType containerType;
+    const uint8_t * cert = OperationalICACerts;
+    uint16_t certLen = OperationalICACertsLen;
+
+    if (IncludeOperationalICACerts)
+    {
+        if (cert == NULL || certLen == 0)
+        {
+            err = gDeviceCredsStore.GetDeviceICACerts(cert, certLen);
+            SuccessOrExit(err);
+        }
+
+        err = writer.CopyContainer(tag, cert, certLen);
+        SuccessOrExit(err);
+    }
+
+exit:
+    return err;
+}
+
+WEAVE_ERROR CertProvOptions::GenerateAndEncodeOpSig(const uint8_t * hash, uint8_t hashLen, TLVWriter & writer, uint64_t tag)
+{
+    WEAVE_ERROR err;
+    const uint8_t * key = OperationalPrivateKey;
+    uint16_t keyLen = OperationalPrivateKeyLen;
+
+    if (key == NULL || keyLen == 0)
+    {
+        err = gDeviceCredsStore.GetDevicePrivateKey(key, keyLen);
+        SuccessOrExit(err);
+    }
+
+    err = GenerateAndEncodeWeaveECDSASignature(writer, tag, hash, hashLen, key, keyLen);
+    SuccessOrExit(err);
+
+exit:
+    return err;
+}
+
+// ===== Methods that implement the WeaveNodeMfrAttestDelegate interface
+
+WEAVE_ERROR CertProvOptions::EncodeMAInfo(TLVWriter & writer)
+{
+    WEAVE_ERROR err;
+    TLVType containerType;
+    const uint8_t * cert = MfrAttestCert;
+    uint16_t certLen = MfrAttestCertLen;
+    const uint8_t * caCert = MfrAttestICACert1;
+    uint16_t caCertLen = MfrAttestICACert1Len;
+    const uint8_t * caCert2 = MfrAttestICACert2;
+    uint16_t caCert2Len = MfrAttestICACert2Len;
+
+    if (MfrAttestType == kMfrAttestType_WeaveCert)
+    {
+        if (cert == NULL || certLen == 0)
+        {
+            if (!GetTestNodeCert(MfrAttestDeviceId, cert, certLen))
+            {
+                printf("ERROR: Node manufacturer attestation certificate not configured\n");
+                ExitNow(err = WEAVE_ERROR_CERT_NOT_FOUND);
+            }
+        }
+
+        err = writer.CopyContainer(ContextTag(kTag_GetCertReqMsg_MfrAttest_WeaveCert), cert, certLen);
+        SuccessOrExit(err);
+
+        if (IncludeMfrAttestICACerts)
+        {
+            if (caCert == NULL || caCertLen == 0)
+            {
+                caCert = nl::NestCerts::Development::DeviceCA::Cert;
+                caCertLen = nl::NestCerts::Development::DeviceCA::CertLength;
+            }
+
+            err = writer.StartContainer(ContextTag(kTag_GetCertReqMsg_MfrAttest_WeaveRelCerts), kTLVType_Array, containerType);
+            SuccessOrExit(err);
+
+            err = writer.CopyContainer(AnonymousTag, caCert, caCertLen);
+            SuccessOrExit(err);
+
+            err = writer.EndContainer(containerType);
+            SuccessOrExit(err);
+        }
+    }
+    else if (MfrAttestType == kMfrAttestType_X509Cert)
+    {
+        if (cert == NULL || certLen == 0)
+        {
+            cert  = TestDevice1_X509_RSA_Cert;
+            certLen  = TestDevice1_X509_RSA_CertLength;
+        }
+
+        // Copy the test device manufacturer attestation X509 RSA certificate into supplied TLV writer.
+        err = writer.PutBytes(ContextTag(kTag_GetCertReqMsg_MfrAttest_X509Cert), cert, certLen);
+        SuccessOrExit(err);
+
+        if (IncludeMfrAttestICACerts)
+        {
+            if (caCert == NULL || caCertLen == 0)
+            {
+                caCert = TestDevice1_X509_RSA_ICACert1;
+                caCertLen = TestDevice1_X509_RSA_ICACert1Length;
+
+                caCert2 = TestDevice1_X509_RSA_ICACert2;
+                caCert2Len = TestDevice1_X509_RSA_ICACert2Length;
+            }
+
+            // Start the RelatedCertificates array. This contains the list of certificates the signature verifier
+            // will need to verify the signature.
+            err = writer.StartContainer(ContextTag(kTag_GetCertReqMsg_MfrAttest_X509RelCerts), kTLVType_Array, containerType);
+            SuccessOrExit(err);
+
+            // Copy first Intermidiate CA (ICA) certificate.
+            err = writer.PutBytes(AnonymousTag, caCert, caCertLen);
+            SuccessOrExit(err);
+
+            // Copy second Intermidiate CA (ICA) certificate.
+            if (caCert2 != NULL && caCert2Len > 0)
+            {
+                err = writer.PutBytes(AnonymousTag, caCert2, caCert2Len);
+                SuccessOrExit(err);
+            }
+
+            err = writer.EndContainer(containerType);
+            SuccessOrExit(err);
+        }
+    }
+    else if (MfrAttestType == kMfrAttestType_HMAC)
+    {
+        err = writer.Put(ContextTag(kTag_GetCertReqMsg_MfrAttest_HMACKeyId), TestDevice1_MfrAttest_HMACKeyId);
+        SuccessOrExit(err);
+
+        if (IncludeMfrAttestICACerts)
+        {
+            err = writer.PutBytes(ContextTag(kTag_GetCertReqMsg_MfrAttest_HMACMetaData), TestDevice1_MfrAttest_HMACMetaData, TestDevice1_MfrAttest_HMACMetaDataLength);
+            SuccessOrExit(err);
+        }
+    }
+    else
+    {
+        ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT);
+    }
+
+exit:
+    return err;
+}
+
+WEAVE_ERROR CertProvOptions::GenerateAndEncodeMASig(const uint8_t * data, uint16_t dataLen, TLVWriter & writer)
+{
+    WEAVE_ERROR err;
+    const uint8_t * key = MfrAttestPrivateKey;
+    uint16_t keyLen = MfrAttestPrivateKeyLen;
+    nl::Weave::Platform::Security::SHA256 sha256;
+    uint8_t hash[SHA256::kHashLength];
+
+    // Calculate data hash.
+    if (MfrAttestType == kMfrAttestType_WeaveCert || MfrAttestType == kMfrAttestType_X509Cert)
+    {
+        sha256.Begin();
+        sha256.AddData(data, dataLen);
+        sha256.Finish(hash);
+    }
+
+    if (MfrAttestType == kMfrAttestType_WeaveCert)
+    {
+        if (key == NULL || keyLen == 0)
+        {
+            if (!GetTestNodePrivateKey(MfrAttestDeviceId, key, keyLen))
+            {
+                printf("ERROR: Node manufacturer attestation private key not configured\n");
+                ExitNow(err = WEAVE_ERROR_KEY_NOT_FOUND);
+            }
+        }
+
+        err = writer.Put(ContextTag(kTag_GetCertReqMsg_MfrAttestSigAlgo), static_cast<uint16_t>(ASN1::kOID_SigAlgo_ECDSAWithSHA256));
+        SuccessOrExit(err);
+
+        err = GenerateAndEncodeWeaveECDSASignature(writer, ContextTag(kTag_GetCertReqMsg_MfrAttestSig_ECDSA), hash, SHA256::kHashLength, key, keyLen);
+        SuccessOrExit(err);
+    }
+    else if (MfrAttestType == kMfrAttestType_X509Cert)
+    {
+#if WEAVE_WITH_OPENSSL
+        if (key == NULL || keyLen == 0)
+        {
+            key = TestDevice1_X509_RSA_PrivateKey;
+            keyLen = TestDevice1_X509_RSA_PrivateKeyLength;
+        }
+
+        err = writer.Put(ContextTag(kTag_GetCertReqMsg_MfrAttestSigAlgo), static_cast<uint16_t>(ASN1::kOID_SigAlgo_SHA256WithRSAEncryption));
+        SuccessOrExit(err);
+
+        err = nl::Weave::Crypto::GenerateAndEncodeWeaveRSASignature(ASN1::kOID_SigAlgo_SHA256WithRSAEncryption,
+                                                                    writer, ContextTag(kTag_GetCertReqMsg_MfrAttestSig_RSA),
+                                                                    hash, SHA256::kHashLength, key, keyLen);
+        SuccessOrExit(err);
+#else
+        printf("ERROR: Manufacturer Attestation X509 encoded certificates not supported.\n");
+        ExitNow(err = WEAVE_ERROR_NOT_IMPLEMENTED);
+#endif
+    }
+    else if (MfrAttestType == kMfrAttestType_HMAC)
+    {
+        if (key == NULL || keyLen == 0)
+        {
+            key = TestDevice1_MfrAttest_HMACKey;
+            keyLen = TestDevice1_MfrAttest_HMACKeyLength;
+        }
+
+        err = writer.Put(ContextTag(kTag_GetCertReqMsg_MfrAttestSigAlgo), static_cast<uint16_t>(ASN1::kOID_SigAlgo_HMACWithSHA256));
+        SuccessOrExit(err);
+
+        err = GenerateAndEncodeWeaveHMACSignature(ASN1::kOID_SigAlgo_HMACWithSHA256, writer, ContextTag(kTag_GetCertReqMsg_MfrAttestSig_HMAC),
+                                                  data, dataLen, key, keyLen);
+        SuccessOrExit(err);
+    }
+    else
+    {
+        ExitNow(err = WEAVE_ERROR_INVALID_ARGUMENT);
+    }
+
+exit:
+    return err;
+}
diff --git a/src/test-apps/CertProvOptions.h b/src/test-apps/CertProvOptions.h
new file mode 100644
index 0000000..f2423e7
--- /dev/null
+++ b/src/test-apps/CertProvOptions.h
@@ -0,0 +1,168 @@
+/*
+ *
+ *    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
+ *      Definition of CertProvOptions class, which handles Certificate Provisioning specific
+ *      command line options and provides an implementation of the WeaveNodeOpAuthDelegate and
+ *      WeaveNodeMfrAttestDelegate interfaces for use in test applications.
+ *
+ */
+
+#ifndef CERTPROVOPTIONS_H_
+#define CERTPROVOPTIONS_H_
+
+#include <Weave/Core/WeaveCore.h>
+#include <Weave/Profiles/security/WeaveSecurity.h>
+#include <Weave/Profiles/security/WeaveCertProvisioning.h>
+#include "ToolCommonOptions.h"
+
+using namespace nl::Weave::Profiles::Security::CertProvisioning;
+
+
+extern WEAVE_ERROR GenerateTestDeviceCert(uint64_t deviceId, EncodedECPublicKey& devicePubKey,
+                                          uint8_t *cert, uint16_t certBufSize, uint16_t& certLen);
+
+extern WEAVE_ERROR GenerateTestDeviceCert(uint64_t deviceId, EncodedECPublicKey& devicePubKey,
+                                          const uint8_t *caCert, uint16_t caCertLen,
+                                          const uint8_t *caKey, uint16_t caKeyLen,
+                                          uint8_t *cert, uint16_t certBufSize, uint16_t& certLen);
+
+class DeviceCredentialsStore
+{
+public:
+
+    enum
+    {
+        kWeaveDeviceCertBufSize           = 300,    // Size of buffer needed to hold Weave device certificate.
+        kWeaveDevicePrivateKeyBufSize     = 128,    // Size of buffer needed to hold Weave device private key.
+    };
+
+    DeviceCredentialsStore();
+
+    WEAVE_ERROR StoreDeviceId(uint64_t deviceId) { mDeviceId = deviceId; return WEAVE_NO_ERROR; };
+    WEAVE_ERROR StoreDeviceCert(const uint8_t * cert, uint16_t certLen);
+    WEAVE_ERROR StoreDeviceICACerts(const uint8_t * certs, uint16_t certsLen);
+    WEAVE_ERROR StoreDevicePrivateKey(const uint8_t * key, uint16_t keyLen);
+
+    WEAVE_ERROR GetDeviceId(uint64_t & deviceId);
+    WEAVE_ERROR GetDeviceCert(const uint8_t *& cert, uint16_t & certLen);
+    WEAVE_ERROR GetDeviceICACerts(const uint8_t *& cert, uint16_t & certLen);
+    WEAVE_ERROR GetDevicePrivateKey(const uint8_t *& key, uint16_t & keyLen);
+
+    void ClearDeviceId(void);
+    void ClearDeviceCert(void);
+    void ClearDeviceICACerts(void);
+    void ClearDevicePrivateKey(void);
+    void ClearDeviceCredentials(void);
+
+    bool DeviceIdExists(void);
+    bool DeviceCertExists(void);
+    bool DeviceICACertsExists(void);
+    bool DevicePrivateKeyExists(void);
+    bool DeviceCredentialsExist(void);
+
+    WEAVE_ERROR GenerateAndStoreDeviceCredentials(uint64_t deviceId = kNodeIdNotSpecified);
+    WEAVE_ERROR GenerateAndReplaceCurrentDeviceCert(void);
+
+private:
+
+    uint64_t mDeviceId;
+    uint8_t mDevicePrivateKey[kWeaveDevicePrivateKeyBufSize];
+    uint16_t mDevicePrivateKeyLen;
+    uint8_t mDeviceCert[kWeaveDeviceCertBufSize];
+    uint16_t mDeviceCertLen;
+    uint8_t mDeviceICACerts[kWeaveDeviceCertBufSize];
+    uint16_t mDeviceICACertsLen;
+};
+
+enum
+{
+    kMfrAttestType_Undefined                    = 0,
+    kMfrAttestType_WeaveCert                    = 1,
+    kMfrAttestType_X509Cert                     = 2,
+    kMfrAttestType_HMAC                         = 3,
+};
+
+extern void CertProvClientEventHandler(void * appState, WeaveCertProvEngine::EventType eventType, const WeaveCertProvEngine::InEventParam & inParam, WeaveCertProvEngine::OutEventParam & outParam);
+
+class CertProvOptions : public WeaveNodeOpAuthDelegate, public WeaveNodeMfrAttestDelegate, public OptionSetBase
+{
+public:
+    uint64_t DeviceId;
+
+    uint8_t RequestType;
+
+    bool IncludeAuthorizeInfo;
+    const uint8_t *PairingToken;
+    uint16_t PairingTokenLen;
+    const uint8_t *PairingInitData;
+    uint16_t PairingInitDataLen;
+
+    const uint8_t *OperationalCert;
+    uint16_t OperationalCertLen;
+
+    const uint8_t *OperationalPrivateKey;
+    uint16_t OperationalPrivateKeyLen;
+
+    bool IncludeOperationalICACerts;
+    const uint8_t *OperationalICACerts;
+    uint16_t OperationalICACertsLen;
+
+    uint64_t MfrAttestDeviceId;
+
+    uint8_t MfrAttestType;
+
+    const uint8_t *MfrAttestCert;
+    uint16_t MfrAttestCertLen;
+
+    const uint8_t *MfrAttestPrivateKey;
+    uint16_t MfrAttestPrivateKeyLen;
+
+    bool IncludeMfrAttestICACerts;
+    const uint8_t *MfrAttestICACert1;
+    uint16_t MfrAttestICACert1Len;
+    const uint8_t *MfrAttestICACert2;
+    uint16_t MfrAttestICACert2Len;
+
+    CertProvOptions();
+
+    static void CertProvClientEventHandler(void * appState, WeaveCertProvEngine::EventType eventType,
+                                           const WeaveCertProvEngine::InEventParam & inParam, WeaveCertProvEngine::OutEventParam & outParam);
+
+private:
+
+    virtual bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg) __OVERRIDE;
+
+    // ===== 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;
+};
+
+extern DeviceCredentialsStore gDeviceCredsStore;
+
+extern CertProvOptions gCertProvOptions;
+
+#endif /* CERTPROVOPTIONS_H_ */
diff --git a/src/test-apps/Certs.cpp b/src/test-apps/Certs.cpp
index 68918cd..0a70e9e 100644
--- a/src/test-apps/Certs.cpp
+++ b/src/test-apps/Certs.cpp
@@ -1,5 +1,6 @@
 /*
  *
+ *    Copyright (c) 2019 Google LLC.
  *    Copyright (c) 2013-2017 Nest Labs, Inc.
  *    All rights reserved.
  *
@@ -1171,7 +1172,6 @@
     { 0, NULL, 0, NULL, 0 }
 };
 
-
 TestCACert TestCACerts[] =
 {
     { TestMockRoot_CAId, TestMockRoot_Cert, TestMockRoot_CertLength },
@@ -1215,3 +1215,498 @@
         }
     return false;
 }
+
+extern const uint8_t TestDevice1_X509_RSA_PrivateKey[] =
+{
+
+    /*
+    -----BEGIN RSA PRIVATE KEY-----
+    MIIEogIBAAKCAQEAsvcl5D4YDk1TaYefzdQcoQTfDLQJ1DE/LLfBd3djeiPhVtvq
+    gdmc1s3/w6sj2cCRnf85VURnt5dlsbaCYOSYRBJKqX5aoU6KCzSqD5l8OO/OG+c9
+    s7hrDZVpJ8PLC8iQRSXYmkmHpmDWHZyLZcesUddgNKeqe1qVjGY97q9oRbMGzsT7
+    LqLhaR8ve0+esrRnZwOWYTFoHmncCrABYkCs8dapRnhRWGTTOyhwZJbgI+vMuveg
+    AM4C4vjG/Bt5IhwjXF2R88oZ1ybpvPIytB+TT1PypxuuxYjO84c/9isZBP29G5Ui
+    7LoFOI6D0QBc91dLyF/ZBtfCrZrR73fRY9yX3QIDAQABAoIBAFFz8ycCq+g2gXRS
+    agVAOReAJBSgDKkrENnFeKRrDjeVBQaHaSBYbu3FLFdeGR8OajhC5VFNpPcGTR6p
+    NoXrBPJWcOzbuVwZZvLasVwQO12ep2xDvu2BThgMnKOglVVzn1YZd5AhT0AGau1n
+    Rnq4elF1eS/977Xc8JvKHP35j7fIlHych8uiDTKJxu8mzxzpCIdLb5S1WzpjQQcK
+    zc7HhZvPtcqfYY5QTeuiyM4mQuLRNVvovq6X9VIswly6cgtZRpX3KKe5AY1fEjfa
+    SMwM+069SVhW6ubXvPB2wl0uSSg3/DjyC7DCPpiu4g38kFlS0Hz2EkMYHFHYMwCW
+    6K236nECgYEA4e8kgbFC/QztgMAm561LkjN6hEkaPkC+Yon6zs+TIjtcCl5leKrj
+    cMG75OxopB1exz3YC4Uz9kMp9AtF9I1dILbrl2EWEjF7yzFsoGKwnZSuzIu3VbtZ
+    iTHlz6cMiB0tTR8mser6f+dqD8uXCo7FeTv+CPskpsTdXpuubcQHG7cCgYEAysfu
+    KQS4+L9Hm1xkkTndXxGDuY4tnbKzlODnANlrww3oFfpTw2DmG3rk5w3nBlu5IlnQ
+    W6wNo8L6fMJg3+JE+/4jrvzWfIVbwGL5OWrSjqNUcBvez1xZMQTrmhbhipE5RyjV
+    zdiJC1niRoa17UFwvzjwFWHzcuKOYGeUKqGC0QsCgYA3nu3317HMJlCZ77QkOO9v
+    0KiKxIxnYvz7uUg7fbKVLNPd4ZtNd5SCf89H7kNck7ZvinQTcfl22NYNNHFGYT7Z
+    /O5G2Cnc1L3LKiG54lHkmWPnC0ZZHsROGDChTFizcatjXxXhmx2MO2ZK+S339Wn8
+    DJ7fiyRcwf5VejIY57dwRQKBgEjkUWIBRpRz/cOFFMl3aXHxE86xowga4p7TsXYG
+    scvtxc2QrGeA/3ZFWN8Nikwo0IXejx1E3apOPkh2fug7p9yFYEJYtKkSLwcbDMds
+    9L89Su75tcAITC9ou2AqdWygA1zm+uQBwFGKP+JmLiNY8LRsPTESgrZ7Zf6VfRdN
+    8349AoGAZlHOiirU6UDSN7ShW56ETQLoQ4ZtCpEWDhT8sRrYFG+formzskTcO7zq
+    wpbTRzmgnBtZT+lpiPuQEUBWaDs8bql48DNfyu51IGVJfrzUxa0q44955pWFgP7A
+    bX1ZbTUztvwM18GL+tomtBDwFVCyJZDekpX34yD8IC0gjaDtXPo=
+    -----END RSA PRIVATE KEY-----
+    */
+
+    0x30, 0x82, 0x04, 0xA2, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xB2, 0xF7, 0x25, 0xE4,
+    0x3E, 0x18, 0x0E, 0x4D, 0x53, 0x69, 0x87, 0x9F, 0xCD, 0xD4, 0x1C, 0xA1, 0x04, 0xDF, 0x0C, 0xB4,
+    0x09, 0xD4, 0x31, 0x3F, 0x2C, 0xB7, 0xC1, 0x77, 0x77, 0x63, 0x7A, 0x23, 0xE1, 0x56, 0xDB, 0xEA,
+    0x81, 0xD9, 0x9C, 0xD6, 0xCD, 0xFF, 0xC3, 0xAB, 0x23, 0xD9, 0xC0, 0x91, 0x9D, 0xFF, 0x39, 0x55,
+    0x44, 0x67, 0xB7, 0x97, 0x65, 0xB1, 0xB6, 0x82, 0x60, 0xE4, 0x98, 0x44, 0x12, 0x4A, 0xA9, 0x7E,
+    0x5A, 0xA1, 0x4E, 0x8A, 0x0B, 0x34, 0xAA, 0x0F, 0x99, 0x7C, 0x38, 0xEF, 0xCE, 0x1B, 0xE7, 0x3D,
+    0xB3, 0xB8, 0x6B, 0x0D, 0x95, 0x69, 0x27, 0xC3, 0xCB, 0x0B, 0xC8, 0x90, 0x45, 0x25, 0xD8, 0x9A,
+    0x49, 0x87, 0xA6, 0x60, 0xD6, 0x1D, 0x9C, 0x8B, 0x65, 0xC7, 0xAC, 0x51, 0xD7, 0x60, 0x34, 0xA7,
+    0xAA, 0x7B, 0x5A, 0x95, 0x8C, 0x66, 0x3D, 0xEE, 0xAF, 0x68, 0x45, 0xB3, 0x06, 0xCE, 0xC4, 0xFB,
+    0x2E, 0xA2, 0xE1, 0x69, 0x1F, 0x2F, 0x7B, 0x4F, 0x9E, 0xB2, 0xB4, 0x67, 0x67, 0x03, 0x96, 0x61,
+    0x31, 0x68, 0x1E, 0x69, 0xDC, 0x0A, 0xB0, 0x01, 0x62, 0x40, 0xAC, 0xF1, 0xD6, 0xA9, 0x46, 0x78,
+    0x51, 0x58, 0x64, 0xD3, 0x3B, 0x28, 0x70, 0x64, 0x96, 0xE0, 0x23, 0xEB, 0xCC, 0xBA, 0xF7, 0xA0,
+    0x00, 0xCE, 0x02, 0xE2, 0xF8, 0xC6, 0xFC, 0x1B, 0x79, 0x22, 0x1C, 0x23, 0x5C, 0x5D, 0x91, 0xF3,
+    0xCA, 0x19, 0xD7, 0x26, 0xE9, 0xBC, 0xF2, 0x32, 0xB4, 0x1F, 0x93, 0x4F, 0x53, 0xF2, 0xA7, 0x1B,
+    0xAE, 0xC5, 0x88, 0xCE, 0xF3, 0x87, 0x3F, 0xF6, 0x2B, 0x19, 0x04, 0xFD, 0xBD, 0x1B, 0x95, 0x22,
+    0xEC, 0xBA, 0x05, 0x38, 0x8E, 0x83, 0xD1, 0x00, 0x5C, 0xF7, 0x57, 0x4B, 0xC8, 0x5F, 0xD9, 0x06,
+    0xD7, 0xC2, 0xAD, 0x9A, 0xD1, 0xEF, 0x77, 0xD1, 0x63, 0xDC, 0x97, 0xDD, 0x02, 0x03, 0x01, 0x00,
+    0x01, 0x02, 0x82, 0x01, 0x00, 0x51, 0x73, 0xF3, 0x27, 0x02, 0xAB, 0xE8, 0x36, 0x81, 0x74, 0x52,
+    0x6A, 0x05, 0x40, 0x39, 0x17, 0x80, 0x24, 0x14, 0xA0, 0x0C, 0xA9, 0x2B, 0x10, 0xD9, 0xC5, 0x78,
+    0xA4, 0x6B, 0x0E, 0x37, 0x95, 0x05, 0x06, 0x87, 0x69, 0x20, 0x58, 0x6E, 0xED, 0xC5, 0x2C, 0x57,
+    0x5E, 0x19, 0x1F, 0x0E, 0x6A, 0x38, 0x42, 0xE5, 0x51, 0x4D, 0xA4, 0xF7, 0x06, 0x4D, 0x1E, 0xA9,
+    0x36, 0x85, 0xEB, 0x04, 0xF2, 0x56, 0x70, 0xEC, 0xDB, 0xB9, 0x5C, 0x19, 0x66, 0xF2, 0xDA, 0xB1,
+    0x5C, 0x10, 0x3B, 0x5D, 0x9E, 0xA7, 0x6C, 0x43, 0xBE, 0xED, 0x81, 0x4E, 0x18, 0x0C, 0x9C, 0xA3,
+    0xA0, 0x95, 0x55, 0x73, 0x9F, 0x56, 0x19, 0x77, 0x90, 0x21, 0x4F, 0x40, 0x06, 0x6A, 0xED, 0x67,
+    0x46, 0x7A, 0xB8, 0x7A, 0x51, 0x75, 0x79, 0x2F, 0xFD, 0xEF, 0xB5, 0xDC, 0xF0, 0x9B, 0xCA, 0x1C,
+    0xFD, 0xF9, 0x8F, 0xB7, 0xC8, 0x94, 0x7C, 0x9C, 0x87, 0xCB, 0xA2, 0x0D, 0x32, 0x89, 0xC6, 0xEF,
+    0x26, 0xCF, 0x1C, 0xE9, 0x08, 0x87, 0x4B, 0x6F, 0x94, 0xB5, 0x5B, 0x3A, 0x63, 0x41, 0x07, 0x0A,
+    0xCD, 0xCE, 0xC7, 0x85, 0x9B, 0xCF, 0xB5, 0xCA, 0x9F, 0x61, 0x8E, 0x50, 0x4D, 0xEB, 0xA2, 0xC8,
+    0xCE, 0x26, 0x42, 0xE2, 0xD1, 0x35, 0x5B, 0xE8, 0xBE, 0xAE, 0x97, 0xF5, 0x52, 0x2C, 0xC2, 0x5C,
+    0xBA, 0x72, 0x0B, 0x59, 0x46, 0x95, 0xF7, 0x28, 0xA7, 0xB9, 0x01, 0x8D, 0x5F, 0x12, 0x37, 0xDA,
+    0x48, 0xCC, 0x0C, 0xFB, 0x4E, 0xBD, 0x49, 0x58, 0x56, 0xEA, 0xE6, 0xD7, 0xBC, 0xF0, 0x76, 0xC2,
+    0x5D, 0x2E, 0x49, 0x28, 0x37, 0xFC, 0x38, 0xF2, 0x0B, 0xB0, 0xC2, 0x3E, 0x98, 0xAE, 0xE2, 0x0D,
+    0xFC, 0x90, 0x59, 0x52, 0xD0, 0x7C, 0xF6, 0x12, 0x43, 0x18, 0x1C, 0x51, 0xD8, 0x33, 0x00, 0x96,
+    0xE8, 0xAD, 0xB7, 0xEA, 0x71, 0x02, 0x81, 0x81, 0x00, 0xE1, 0xEF, 0x24, 0x81, 0xB1, 0x42, 0xFD,
+    0x0C, 0xED, 0x80, 0xC0, 0x26, 0xE7, 0xAD, 0x4B, 0x92, 0x33, 0x7A, 0x84, 0x49, 0x1A, 0x3E, 0x40,
+    0xBE, 0x62, 0x89, 0xFA, 0xCE, 0xCF, 0x93, 0x22, 0x3B, 0x5C, 0x0A, 0x5E, 0x65, 0x78, 0xAA, 0xE3,
+    0x70, 0xC1, 0xBB, 0xE4, 0xEC, 0x68, 0xA4, 0x1D, 0x5E, 0xC7, 0x3D, 0xD8, 0x0B, 0x85, 0x33, 0xF6,
+    0x43, 0x29, 0xF4, 0x0B, 0x45, 0xF4, 0x8D, 0x5D, 0x20, 0xB6, 0xEB, 0x97, 0x61, 0x16, 0x12, 0x31,
+    0x7B, 0xCB, 0x31, 0x6C, 0xA0, 0x62, 0xB0, 0x9D, 0x94, 0xAE, 0xCC, 0x8B, 0xB7, 0x55, 0xBB, 0x59,
+    0x89, 0x31, 0xE5, 0xCF, 0xA7, 0x0C, 0x88, 0x1D, 0x2D, 0x4D, 0x1F, 0x26, 0xB1, 0xEA, 0xFA, 0x7F,
+    0xE7, 0x6A, 0x0F, 0xCB, 0x97, 0x0A, 0x8E, 0xC5, 0x79, 0x3B, 0xFE, 0x08, 0xFB, 0x24, 0xA6, 0xC4,
+    0xDD, 0x5E, 0x9B, 0xAE, 0x6D, 0xC4, 0x07, 0x1B, 0xB7, 0x02, 0x81, 0x81, 0x00, 0xCA, 0xC7, 0xEE,
+    0x29, 0x04, 0xB8, 0xF8, 0xBF, 0x47, 0x9B, 0x5C, 0x64, 0x91, 0x39, 0xDD, 0x5F, 0x11, 0x83, 0xB9,
+    0x8E, 0x2D, 0x9D, 0xB2, 0xB3, 0x94, 0xE0, 0xE7, 0x00, 0xD9, 0x6B, 0xC3, 0x0D, 0xE8, 0x15, 0xFA,
+    0x53, 0xC3, 0x60, 0xE6, 0x1B, 0x7A, 0xE4, 0xE7, 0x0D, 0xE7, 0x06, 0x5B, 0xB9, 0x22, 0x59, 0xD0,
+    0x5B, 0xAC, 0x0D, 0xA3, 0xC2, 0xFA, 0x7C, 0xC2, 0x60, 0xDF, 0xE2, 0x44, 0xFB, 0xFE, 0x23, 0xAE,
+    0xFC, 0xD6, 0x7C, 0x85, 0x5B, 0xC0, 0x62, 0xF9, 0x39, 0x6A, 0xD2, 0x8E, 0xA3, 0x54, 0x70, 0x1B,
+    0xDE, 0xCF, 0x5C, 0x59, 0x31, 0x04, 0xEB, 0x9A, 0x16, 0xE1, 0x8A, 0x91, 0x39, 0x47, 0x28, 0xD5,
+    0xCD, 0xD8, 0x89, 0x0B, 0x59, 0xE2, 0x46, 0x86, 0xB5, 0xED, 0x41, 0x70, 0xBF, 0x38, 0xF0, 0x15,
+    0x61, 0xF3, 0x72, 0xE2, 0x8E, 0x60, 0x67, 0x94, 0x2A, 0xA1, 0x82, 0xD1, 0x0B, 0x02, 0x81, 0x80,
+    0x37, 0x9E, 0xED, 0xF7, 0xD7, 0xB1, 0xCC, 0x26, 0x50, 0x99, 0xEF, 0xB4, 0x24, 0x38, 0xEF, 0x6F,
+    0xD0, 0xA8, 0x8A, 0xC4, 0x8C, 0x67, 0x62, 0xFC, 0xFB, 0xB9, 0x48, 0x3B, 0x7D, 0xB2, 0x95, 0x2C,
+    0xD3, 0xDD, 0xE1, 0x9B, 0x4D, 0x77, 0x94, 0x82, 0x7F, 0xCF, 0x47, 0xEE, 0x43, 0x5C, 0x93, 0xB6,
+    0x6F, 0x8A, 0x74, 0x13, 0x71, 0xF9, 0x76, 0xD8, 0xD6, 0x0D, 0x34, 0x71, 0x46, 0x61, 0x3E, 0xD9,
+    0xFC, 0xEE, 0x46, 0xD8, 0x29, 0xDC, 0xD4, 0xBD, 0xCB, 0x2A, 0x21, 0xB9, 0xE2, 0x51, 0xE4, 0x99,
+    0x63, 0xE7, 0x0B, 0x46, 0x59, 0x1E, 0xC4, 0x4E, 0x18, 0x30, 0xA1, 0x4C, 0x58, 0xB3, 0x71, 0xAB,
+    0x63, 0x5F, 0x15, 0xE1, 0x9B, 0x1D, 0x8C, 0x3B, 0x66, 0x4A, 0xF9, 0x2D, 0xF7, 0xF5, 0x69, 0xFC,
+    0x0C, 0x9E, 0xDF, 0x8B, 0x24, 0x5C, 0xC1, 0xFE, 0x55, 0x7A, 0x32, 0x18, 0xE7, 0xB7, 0x70, 0x45,
+    0x02, 0x81, 0x80, 0x48, 0xE4, 0x51, 0x62, 0x01, 0x46, 0x94, 0x73, 0xFD, 0xC3, 0x85, 0x14, 0xC9,
+    0x77, 0x69, 0x71, 0xF1, 0x13, 0xCE, 0xB1, 0xA3, 0x08, 0x1A, 0xE2, 0x9E, 0xD3, 0xB1, 0x76, 0x06,
+    0xB1, 0xCB, 0xED, 0xC5, 0xCD, 0x90, 0xAC, 0x67, 0x80, 0xFF, 0x76, 0x45, 0x58, 0xDF, 0x0D, 0x8A,
+    0x4C, 0x28, 0xD0, 0x85, 0xDE, 0x8F, 0x1D, 0x44, 0xDD, 0xAA, 0x4E, 0x3E, 0x48, 0x76, 0x7E, 0xE8,
+    0x3B, 0xA7, 0xDC, 0x85, 0x60, 0x42, 0x58, 0xB4, 0xA9, 0x12, 0x2F, 0x07, 0x1B, 0x0C, 0xC7, 0x6C,
+    0xF4, 0xBF, 0x3D, 0x4A, 0xEE, 0xF9, 0xB5, 0xC0, 0x08, 0x4C, 0x2F, 0x68, 0xBB, 0x60, 0x2A, 0x75,
+    0x6C, 0xA0, 0x03, 0x5C, 0xE6, 0xFA, 0xE4, 0x01, 0xC0, 0x51, 0x8A, 0x3F, 0xE2, 0x66, 0x2E, 0x23,
+    0x58, 0xF0, 0xB4, 0x6C, 0x3D, 0x31, 0x12, 0x82, 0xB6, 0x7B, 0x65, 0xFE, 0x95, 0x7D, 0x17, 0x4D,
+    0xF3, 0x7E, 0x3D, 0x02, 0x81, 0x80, 0x66, 0x51, 0xCE, 0x8A, 0x2A, 0xD4, 0xE9, 0x40, 0xD2, 0x37,
+    0xB4, 0xA1, 0x5B, 0x9E, 0x84, 0x4D, 0x02, 0xE8, 0x43, 0x86, 0x6D, 0x0A, 0x91, 0x16, 0x0E, 0x14,
+    0xFC, 0xB1, 0x1A, 0xD8, 0x14, 0x6F, 0x9F, 0xA2, 0xB9, 0xB3, 0xB2, 0x44, 0xDC, 0x3B, 0xBC, 0xEA,
+    0xC2, 0x96, 0xD3, 0x47, 0x39, 0xA0, 0x9C, 0x1B, 0x59, 0x4F, 0xE9, 0x69, 0x88, 0xFB, 0x90, 0x11,
+    0x40, 0x56, 0x68, 0x3B, 0x3C, 0x6E, 0xA9, 0x78, 0xF0, 0x33, 0x5F, 0xCA, 0xEE, 0x75, 0x20, 0x65,
+    0x49, 0x7E, 0xBC, 0xD4, 0xC5, 0xAD, 0x2A, 0xE3, 0x8F, 0x79, 0xE6, 0x95, 0x85, 0x80, 0xFE, 0xC0,
+    0x6D, 0x7D, 0x59, 0x6D, 0x35, 0x33, 0xB6, 0xFC, 0x0C, 0xD7, 0xC1, 0x8B, 0xFA, 0xDA, 0x26, 0xB4,
+    0x10, 0xF0, 0x15, 0x50, 0xB2, 0x25, 0x90, 0xDE, 0x92, 0x95, 0xF7, 0xE3, 0x20, 0xFC, 0x20, 0x2D,
+    0x20, 0x8D, 0xA0, 0xED, 0x5C, 0xFA,
+};
+
+extern const uint16_t TestDevice1_X509_RSA_PrivateKeyLength = sizeof(TestDevice1_X509_RSA_PrivateKey);
+
+extern const uint8_t TestDevice1_X509_RSA_Cert[] =
+{
+
+    /*
+    -----BEGIN CERTIFICATE-----
+    MIIEATCCAumgAwIBAgICAVwwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMx
+    EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEzAR
+    BgNVBAoMCkdvb2dsZSBJbmMxDTALBgNVBAsMBENhc3QxHDAaBgNVBAMME0Nhc3Qg
+    VGVzdCBBdWRpbyBJQ0EwHhcNMTgwODI0MjIxODM0WhcNMTkwMTMxMjIxODM0WjB/
+    MQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91
+    bnRhaW4gVmlldzETMBEGA1UECgwKR29vZ2xlIEluYzENMAsGA1UECwwEQ2FzdDEf
+    MB0GA1UEAwwWQ2FzdCBUZXN0IEF1ZGlvIERldmljZTCCASIwDQYJKoZIhvcNAQEB
+    BQADggEPADCCAQoCggEBALL3JeQ+GA5NU2mHn83UHKEE3wy0CdQxPyy3wXd3Y3oj
+    4Vbb6oHZnNbN/8OrI9nAkZ3/OVVEZ7eXZbG2gmDkmEQSSql+WqFOigs0qg+ZfDjv
+    zhvnPbO4aw2VaSfDywvIkEUl2JpJh6Zg1h2ci2XHrFHXYDSnqntalYxmPe6vaEWz
+    Bs7E+y6i4WkfL3tPnrK0Z2cDlmExaB5p3AqwAWJArPHWqUZ4UVhk0zsocGSW4CPr
+    zLr3oADOAuL4xvwbeSIcI1xdkfPKGdcm6bzyMrQfk09T8qcbrsWIzvOHP/YrGQT9
+    vRuVIuy6BTiOg9EAXPdXS8hf2QbXwq2a0e930WPcl90CAwEAAaOBiTCBhjAJBgNV
+    HRMEAjAAMB0GA1UdDgQWBBSWrc2xhFX96LoUFpfjbOto/N8EaTAfBgNVHSMEGDAW
+    gBSO2zh0skDMPeznfas3b3eUKvaqbzALBgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYI
+    KwYBBQUHAwIwFwYDVR0gBBAwDjAMBgorBgEEAdZ5AgUCMA0GCSqGSIb3DQEBCwUA
+    A4IBAQArQuSfmrxO9uZTNPVmHy5+AT2Tv+Tie1gAs8i/5dy5gBDJ2RNfhOPyj7A+
+    OtpA+AhuZQJB9d1qacpGNWhQF2f/DW5gYqSnz8ZD55rzG5nkGV937rNogEY2e2O7
+    dUtipcvfEhpMIvCsEJpIs/6ulGvb74/62Buf3wUFn24Nkf16J7JSX4h79P1mc87L
+    Q0Z57LLrc7l6QFhO5Aoy5/khtz9iTPqJfob4LL//x/DIdf9ZPcSY/kIh5afq9A2K
+    9ERWH06TtkEiA2wSICP78GVJJOf9/87H6GdYmWDzRI3dCoV05nECF9dnXYnDSs+R
+    1nSRbjURlnWJRj7jOcx5QnUUEmOV
+    -----END CERTIFICATE-----
+    */
+
+    0x30, 0x82, 0x04, 0x01, 0x30, 0x82, 0x02, 0xE9, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x01,
+    0x5C, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00,
+    0x30, 0x7C, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+    0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x0A, 0x43, 0x61, 0x6C, 0x69, 0x66, 0x6F,
+    0x72, 0x6E, 0x69, 0x61, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0C, 0x0D, 0x4D,
+    0x6F, 0x75, 0x6E, 0x74, 0x61, 0x69, 0x6E, 0x20, 0x56, 0x69, 0x65, 0x77, 0x31, 0x13, 0x30, 0x11,
+    0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x0A, 0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x20, 0x49, 0x6E,
+    0x63, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C, 0x04, 0x43, 0x61, 0x73, 0x74,
+    0x31, 0x1C, 0x30, 0x1A, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x13, 0x43, 0x61, 0x73, 0x74, 0x20,
+    0x54, 0x65, 0x73, 0x74, 0x20, 0x41, 0x75, 0x64, 0x69, 0x6F, 0x20, 0x49, 0x43, 0x41, 0x30, 0x1E,
+    0x17, 0x0D, 0x31, 0x38, 0x30, 0x38, 0x32, 0x34, 0x32, 0x32, 0x31, 0x38, 0x33, 0x34, 0x5A, 0x17,
+    0x0D, 0x31, 0x39, 0x30, 0x31, 0x33, 0x31, 0x32, 0x32, 0x31, 0x38, 0x33, 0x34, 0x5A, 0x30, 0x7F,
+    0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30,
+    0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x0A, 0x43, 0x61, 0x6C, 0x69, 0x66, 0x6F, 0x72, 0x6E,
+    0x69, 0x61, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0C, 0x0D, 0x4D, 0x6F, 0x75,
+    0x6E, 0x74, 0x61, 0x69, 0x6E, 0x20, 0x56, 0x69, 0x65, 0x77, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03,
+    0x55, 0x04, 0x0A, 0x0C, 0x0A, 0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x20, 0x49, 0x6E, 0x63, 0x31,
+    0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C, 0x04, 0x43, 0x61, 0x73, 0x74, 0x31, 0x1F,
+    0x30, 0x1D, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x16, 0x43, 0x61, 0x73, 0x74, 0x20, 0x54, 0x65,
+    0x73, 0x74, 0x20, 0x41, 0x75, 0x64, 0x69, 0x6F, 0x20, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x30,
+    0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
+    0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00,
+    0xB2, 0xF7, 0x25, 0xE4, 0x3E, 0x18, 0x0E, 0x4D, 0x53, 0x69, 0x87, 0x9F, 0xCD, 0xD4, 0x1C, 0xA1,
+    0x04, 0xDF, 0x0C, 0xB4, 0x09, 0xD4, 0x31, 0x3F, 0x2C, 0xB7, 0xC1, 0x77, 0x77, 0x63, 0x7A, 0x23,
+    0xE1, 0x56, 0xDB, 0xEA, 0x81, 0xD9, 0x9C, 0xD6, 0xCD, 0xFF, 0xC3, 0xAB, 0x23, 0xD9, 0xC0, 0x91,
+    0x9D, 0xFF, 0x39, 0x55, 0x44, 0x67, 0xB7, 0x97, 0x65, 0xB1, 0xB6, 0x82, 0x60, 0xE4, 0x98, 0x44,
+    0x12, 0x4A, 0xA9, 0x7E, 0x5A, 0xA1, 0x4E, 0x8A, 0x0B, 0x34, 0xAA, 0x0F, 0x99, 0x7C, 0x38, 0xEF,
+    0xCE, 0x1B, 0xE7, 0x3D, 0xB3, 0xB8, 0x6B, 0x0D, 0x95, 0x69, 0x27, 0xC3, 0xCB, 0x0B, 0xC8, 0x90,
+    0x45, 0x25, 0xD8, 0x9A, 0x49, 0x87, 0xA6, 0x60, 0xD6, 0x1D, 0x9C, 0x8B, 0x65, 0xC7, 0xAC, 0x51,
+    0xD7, 0x60, 0x34, 0xA7, 0xAA, 0x7B, 0x5A, 0x95, 0x8C, 0x66, 0x3D, 0xEE, 0xAF, 0x68, 0x45, 0xB3,
+    0x06, 0xCE, 0xC4, 0xFB, 0x2E, 0xA2, 0xE1, 0x69, 0x1F, 0x2F, 0x7B, 0x4F, 0x9E, 0xB2, 0xB4, 0x67,
+    0x67, 0x03, 0x96, 0x61, 0x31, 0x68, 0x1E, 0x69, 0xDC, 0x0A, 0xB0, 0x01, 0x62, 0x40, 0xAC, 0xF1,
+    0xD6, 0xA9, 0x46, 0x78, 0x51, 0x58, 0x64, 0xD3, 0x3B, 0x28, 0x70, 0x64, 0x96, 0xE0, 0x23, 0xEB,
+    0xCC, 0xBA, 0xF7, 0xA0, 0x00, 0xCE, 0x02, 0xE2, 0xF8, 0xC6, 0xFC, 0x1B, 0x79, 0x22, 0x1C, 0x23,
+    0x5C, 0x5D, 0x91, 0xF3, 0xCA, 0x19, 0xD7, 0x26, 0xE9, 0xBC, 0xF2, 0x32, 0xB4, 0x1F, 0x93, 0x4F,
+    0x53, 0xF2, 0xA7, 0x1B, 0xAE, 0xC5, 0x88, 0xCE, 0xF3, 0x87, 0x3F, 0xF6, 0x2B, 0x19, 0x04, 0xFD,
+    0xBD, 0x1B, 0x95, 0x22, 0xEC, 0xBA, 0x05, 0x38, 0x8E, 0x83, 0xD1, 0x00, 0x5C, 0xF7, 0x57, 0x4B,
+    0xC8, 0x5F, 0xD9, 0x06, 0xD7, 0xC2, 0xAD, 0x9A, 0xD1, 0xEF, 0x77, 0xD1, 0x63, 0xDC, 0x97, 0xDD,
+    0x02, 0x03, 0x01, 0x00, 0x01, 0xA3, 0x81, 0x89, 0x30, 0x81, 0x86, 0x30, 0x09, 0x06, 0x03, 0x55,
+    0x1D, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04,
+    0x14, 0x96, 0xAD, 0xCD, 0xB1, 0x84, 0x55, 0xFD, 0xE8, 0xBA, 0x14, 0x16, 0x97, 0xE3, 0x6C, 0xEB,
+    0x68, 0xFC, 0xDF, 0x04, 0x69, 0x30, 0x1F, 0x06, 0x03, 0x55, 0x1D, 0x23, 0x04, 0x18, 0x30, 0x16,
+    0x80, 0x14, 0x8E, 0xDB, 0x38, 0x74, 0xB2, 0x40, 0xCC, 0x3D, 0xEC, 0xE7, 0x7D, 0xAB, 0x37, 0x6F,
+    0x77, 0x94, 0x2A, 0xF6, 0xAA, 0x6F, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x1D, 0x0F, 0x04, 0x04, 0x03,
+    0x02, 0x07, 0x80, 0x30, 0x13, 0x06, 0x03, 0x55, 0x1D, 0x25, 0x04, 0x0C, 0x30, 0x0A, 0x06, 0x08,
+    0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x17, 0x06, 0x03, 0x55, 0x1D, 0x20, 0x04,
+    0x10, 0x30, 0x0E, 0x30, 0x0C, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6, 0x79, 0x02, 0x05,
+    0x02, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00,
+    0x03, 0x82, 0x01, 0x01, 0x00, 0x2B, 0x42, 0xE4, 0x9F, 0x9A, 0xBC, 0x4E, 0xF6, 0xE6, 0x53, 0x34,
+    0xF5, 0x66, 0x1F, 0x2E, 0x7E, 0x01, 0x3D, 0x93, 0xBF, 0xE4, 0xE2, 0x7B, 0x58, 0x00, 0xB3, 0xC8,
+    0xBF, 0xE5, 0xDC, 0xB9, 0x80, 0x10, 0xC9, 0xD9, 0x13, 0x5F, 0x84, 0xE3, 0xF2, 0x8F, 0xB0, 0x3E,
+    0x3A, 0xDA, 0x40, 0xF8, 0x08, 0x6E, 0x65, 0x02, 0x41, 0xF5, 0xDD, 0x6A, 0x69, 0xCA, 0x46, 0x35,
+    0x68, 0x50, 0x17, 0x67, 0xFF, 0x0D, 0x6E, 0x60, 0x62, 0xA4, 0xA7, 0xCF, 0xC6, 0x43, 0xE7, 0x9A,
+    0xF3, 0x1B, 0x99, 0xE4, 0x19, 0x5F, 0x77, 0xEE, 0xB3, 0x68, 0x80, 0x46, 0x36, 0x7B, 0x63, 0xBB,
+    0x75, 0x4B, 0x62, 0xA5, 0xCB, 0xDF, 0x12, 0x1A, 0x4C, 0x22, 0xF0, 0xAC, 0x10, 0x9A, 0x48, 0xB3,
+    0xFE, 0xAE, 0x94, 0x6B, 0xDB, 0xEF, 0x8F, 0xFA, 0xD8, 0x1B, 0x9F, 0xDF, 0x05, 0x05, 0x9F, 0x6E,
+    0x0D, 0x91, 0xFD, 0x7A, 0x27, 0xB2, 0x52, 0x5F, 0x88, 0x7B, 0xF4, 0xFD, 0x66, 0x73, 0xCE, 0xCB,
+    0x43, 0x46, 0x79, 0xEC, 0xB2, 0xEB, 0x73, 0xB9, 0x7A, 0x40, 0x58, 0x4E, 0xE4, 0x0A, 0x32, 0xE7,
+    0xF9, 0x21, 0xB7, 0x3F, 0x62, 0x4C, 0xFA, 0x89, 0x7E, 0x86, 0xF8, 0x2C, 0xBF, 0xFF, 0xC7, 0xF0,
+    0xC8, 0x75, 0xFF, 0x59, 0x3D, 0xC4, 0x98, 0xFE, 0x42, 0x21, 0xE5, 0xA7, 0xEA, 0xF4, 0x0D, 0x8A,
+    0xF4, 0x44, 0x56, 0x1F, 0x4E, 0x93, 0xB6, 0x41, 0x22, 0x03, 0x6C, 0x12, 0x20, 0x23, 0xFB, 0xF0,
+    0x65, 0x49, 0x24, 0xE7, 0xFD, 0xFF, 0xCE, 0xC7, 0xE8, 0x67, 0x58, 0x99, 0x60, 0xF3, 0x44, 0x8D,
+    0xDD, 0x0A, 0x85, 0x74, 0xE6, 0x71, 0x02, 0x17, 0xD7, 0x67, 0x5D, 0x89, 0xC3, 0x4A, 0xCF, 0x91,
+    0xD6, 0x74, 0x91, 0x6E, 0x35, 0x11, 0x96, 0x75, 0x89, 0x46, 0x3E, 0xE3, 0x39, 0xCC, 0x79, 0x42,
+    0x75, 0x14, 0x12, 0x63, 0x95,
+};
+
+extern const uint16_t TestDevice1_X509_RSA_CertLength = sizeof(TestDevice1_X509_RSA_Cert);
+
+extern const uint8_t TestDevice1_X509_RSA_ICACert1[] =
+{
+
+    /*
+    -----BEGIN CERTIFICATE-----
+    MIID7DCCAtSgAwIBAgICATgwDQYJKoZIhvcNAQELBQAwezELMAkGA1UEBhMCVVMx
+    EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEzAR
+    BgNVBAoMCkdvb2dsZSBJbmMxDTALBgNVBAsMBENhc3QxGzAZBgNVBAMMEkNhc3Qg
+    VGVzdCBSb290IElDQTAeFw0xODAzMTIyMzU1MDFaFw0zODAzMDcyMzU1MDFaMHwx
+    CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3Vu
+    dGFpbiBWaWV3MRMwEQYDVQQKDApHb29nbGUgSW5jMQ0wCwYDVQQLDARDYXN0MRww
+    GgYDVQQDDBNDYXN0IFRlc3QgQXVkaW8gSUNBMIIBIjANBgkqhkiG9w0BAQEFAAOC
+    AQ8AMIIBCgKCAQEA1Y2yj6kw5fykMoTW7j9T1w5VScQeWSVnPgFWz5obQRZ7VrYP
+    Oi/ZHvb0ycN+s6j1g2xyDMFghlecODA9k0AYZR6Sw7EpkpoGvDA/5mbVlN7D6/tl
+    LnLBka5kU1qhZfzVd6xRFY4gq3LYgz3qZ8mt9ApeqiXw2hZk24IGd8WVusvOWRir
+    +c3ynUboLSfJ5SEVwO265tbrp0cTlWSA6TxF40d0ClrgrrGTkoXQ2VnR5xAJ7Q7F
+    nFes/QLQvX05JJEOEF0l0eCSsqmyyDYVDoNBCyTPsSHl7FFAzpukrKr2fB/ONt7F
+    RrkvRsifb0U8x7g2FjM6tJy9MmdAeDUQlyDzWwIDAQABo3kwdzAPBgNVHRMECDAG
+    AQH/AgEAMB0GA1UdDgQWBBSO2zh0skDMPeznfas3b3eUKvaqbzAfBgNVHSMEGDAW
+    gBQa+LZlKyhYfOWkFt2l8Tk0jkS11jALBgNVHQ8EBAMCAQYwFwYDVR0gBBAwDjAM
+    BgorBgEEAdZ5AgUCMA0GCSqGSIb3DQEBCwUAA4IBAQANr3/keM6aq1H3SC1uluw4
+    Po8NkY+ksmrV6tjYKPGMlU12vAVzDazuIsTOxl1OfUhFQwjYpNYUibK61aFPvDcj
+    JGqfzap5m7qzcxl9KwiqMCuBrbZv/IUjQnOWqQrlB+vjQo9Sju1WOEpmyytnWzkl
+    670xoNxlw5ik99+Qh4sG19Ts1E9ck26YvRAc3oXKk3wh+QIU9IKDXOqeZ/2DpVix
+    mSv8s/phC/Q9NWlcDWWJmf8/8RM1OhhaqGUkcqSEW0zWz1nRQUs/iPcpKm+eO4Cj
+    o+p5Udp1wo7j/Kv+CcluBPnaeqO9guplGUiiKlFsfgXFOUdGwY8x96ltRqdbmy1H
+    -----END CERTIFICATE-----
+    */
+
+    0x30, 0x82, 0x03, 0xEC, 0x30, 0x82, 0x02, 0xD4, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x01,
+    0x38, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00,
+    0x30, 0x7B, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+    0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x0A, 0x43, 0x61, 0x6C, 0x69, 0x66, 0x6F,
+    0x72, 0x6E, 0x69, 0x61, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0C, 0x0D, 0x4D,
+    0x6F, 0x75, 0x6E, 0x74, 0x61, 0x69, 0x6E, 0x20, 0x56, 0x69, 0x65, 0x77, 0x31, 0x13, 0x30, 0x11,
+    0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x0A, 0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x20, 0x49, 0x6E,
+    0x63, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C, 0x04, 0x43, 0x61, 0x73, 0x74,
+    0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x12, 0x43, 0x61, 0x73, 0x74, 0x20,
+    0x54, 0x65, 0x73, 0x74, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x49, 0x43, 0x41, 0x30, 0x1E, 0x17,
+    0x0D, 0x31, 0x38, 0x30, 0x33, 0x31, 0x32, 0x32, 0x33, 0x35, 0x35, 0x30, 0x31, 0x5A, 0x17, 0x0D,
+    0x33, 0x38, 0x30, 0x33, 0x30, 0x37, 0x32, 0x33, 0x35, 0x35, 0x30, 0x31, 0x5A, 0x30, 0x7C, 0x31,
+    0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11,
+    0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x0A, 0x43, 0x61, 0x6C, 0x69, 0x66, 0x6F, 0x72, 0x6E, 0x69,
+    0x61, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0C, 0x0D, 0x4D, 0x6F, 0x75, 0x6E,
+    0x74, 0x61, 0x69, 0x6E, 0x20, 0x56, 0x69, 0x65, 0x77, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55,
+    0x04, 0x0A, 0x0C, 0x0A, 0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x20, 0x49, 0x6E, 0x63, 0x31, 0x0D,
+    0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C, 0x04, 0x43, 0x61, 0x73, 0x74, 0x31, 0x1C, 0x30,
+    0x1A, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x13, 0x43, 0x61, 0x73, 0x74, 0x20, 0x54, 0x65, 0x73,
+    0x74, 0x20, 0x41, 0x75, 0x64, 0x69, 0x6F, 0x20, 0x49, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30,
+    0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82,
+    0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xD5, 0x8D, 0xB2, 0x8F,
+    0xA9, 0x30, 0xE5, 0xFC, 0xA4, 0x32, 0x84, 0xD6, 0xEE, 0x3F, 0x53, 0xD7, 0x0E, 0x55, 0x49, 0xC4,
+    0x1E, 0x59, 0x25, 0x67, 0x3E, 0x01, 0x56, 0xCF, 0x9A, 0x1B, 0x41, 0x16, 0x7B, 0x56, 0xB6, 0x0F,
+    0x3A, 0x2F, 0xD9, 0x1E, 0xF6, 0xF4, 0xC9, 0xC3, 0x7E, 0xB3, 0xA8, 0xF5, 0x83, 0x6C, 0x72, 0x0C,
+    0xC1, 0x60, 0x86, 0x57, 0x9C, 0x38, 0x30, 0x3D, 0x93, 0x40, 0x18, 0x65, 0x1E, 0x92, 0xC3, 0xB1,
+    0x29, 0x92, 0x9A, 0x06, 0xBC, 0x30, 0x3F, 0xE6, 0x66, 0xD5, 0x94, 0xDE, 0xC3, 0xEB, 0xFB, 0x65,
+    0x2E, 0x72, 0xC1, 0x91, 0xAE, 0x64, 0x53, 0x5A, 0xA1, 0x65, 0xFC, 0xD5, 0x77, 0xAC, 0x51, 0x15,
+    0x8E, 0x20, 0xAB, 0x72, 0xD8, 0x83, 0x3D, 0xEA, 0x67, 0xC9, 0xAD, 0xF4, 0x0A, 0x5E, 0xAA, 0x25,
+    0xF0, 0xDA, 0x16, 0x64, 0xDB, 0x82, 0x06, 0x77, 0xC5, 0x95, 0xBA, 0xCB, 0xCE, 0x59, 0x18, 0xAB,
+    0xF9, 0xCD, 0xF2, 0x9D, 0x46, 0xE8, 0x2D, 0x27, 0xC9, 0xE5, 0x21, 0x15, 0xC0, 0xED, 0xBA, 0xE6,
+    0xD6, 0xEB, 0xA7, 0x47, 0x13, 0x95, 0x64, 0x80, 0xE9, 0x3C, 0x45, 0xE3, 0x47, 0x74, 0x0A, 0x5A,
+    0xE0, 0xAE, 0xB1, 0x93, 0x92, 0x85, 0xD0, 0xD9, 0x59, 0xD1, 0xE7, 0x10, 0x09, 0xED, 0x0E, 0xC5,
+    0x9C, 0x57, 0xAC, 0xFD, 0x02, 0xD0, 0xBD, 0x7D, 0x39, 0x24, 0x91, 0x0E, 0x10, 0x5D, 0x25, 0xD1,
+    0xE0, 0x92, 0xB2, 0xA9, 0xB2, 0xC8, 0x36, 0x15, 0x0E, 0x83, 0x41, 0x0B, 0x24, 0xCF, 0xB1, 0x21,
+    0xE5, 0xEC, 0x51, 0x40, 0xCE, 0x9B, 0xA4, 0xAC, 0xAA, 0xF6, 0x7C, 0x1F, 0xCE, 0x36, 0xDE, 0xC5,
+    0x46, 0xB9, 0x2F, 0x46, 0xC8, 0x9F, 0x6F, 0x45, 0x3C, 0xC7, 0xB8, 0x36, 0x16, 0x33, 0x3A, 0xB4,
+    0x9C, 0xBD, 0x32, 0x67, 0x40, 0x78, 0x35, 0x10, 0x97, 0x20, 0xF3, 0x5B, 0x02, 0x03, 0x01, 0x00,
+    0x01, 0xA3, 0x79, 0x30, 0x77, 0x30, 0x0F, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x04, 0x08, 0x30, 0x06,
+    0x01, 0x01, 0xFF, 0x02, 0x01, 0x00, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04,
+    0x14, 0x8E, 0xDB, 0x38, 0x74, 0xB2, 0x40, 0xCC, 0x3D, 0xEC, 0xE7, 0x7D, 0xAB, 0x37, 0x6F, 0x77,
+    0x94, 0x2A, 0xF6, 0xAA, 0x6F, 0x30, 0x1F, 0x06, 0x03, 0x55, 0x1D, 0x23, 0x04, 0x18, 0x30, 0x16,
+    0x80, 0x14, 0x1A, 0xF8, 0xB6, 0x65, 0x2B, 0x28, 0x58, 0x7C, 0xE5, 0xA4, 0x16, 0xDD, 0xA5, 0xF1,
+    0x39, 0x34, 0x8E, 0x44, 0xB5, 0xD6, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x1D, 0x0F, 0x04, 0x04, 0x03,
+    0x02, 0x01, 0x06, 0x30, 0x17, 0x06, 0x03, 0x55, 0x1D, 0x20, 0x04, 0x10, 0x30, 0x0E, 0x30, 0x0C,
+    0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0xD6, 0x79, 0x02, 0x05, 0x02, 0x30, 0x0D, 0x06, 0x09,
+    0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+    0x0D, 0xAF, 0x7F, 0xE4, 0x78, 0xCE, 0x9A, 0xAB, 0x51, 0xF7, 0x48, 0x2D, 0x6E, 0x96, 0xEC, 0x38,
+    0x3E, 0x8F, 0x0D, 0x91, 0x8F, 0xA4, 0xB2, 0x6A, 0xD5, 0xEA, 0xD8, 0xD8, 0x28, 0xF1, 0x8C, 0x95,
+    0x4D, 0x76, 0xBC, 0x05, 0x73, 0x0D, 0xAC, 0xEE, 0x22, 0xC4, 0xCE, 0xC6, 0x5D, 0x4E, 0x7D, 0x48,
+    0x45, 0x43, 0x08, 0xD8, 0xA4, 0xD6, 0x14, 0x89, 0xB2, 0xBA, 0xD5, 0xA1, 0x4F, 0xBC, 0x37, 0x23,
+    0x24, 0x6A, 0x9F, 0xCD, 0xAA, 0x79, 0x9B, 0xBA, 0xB3, 0x73, 0x19, 0x7D, 0x2B, 0x08, 0xAA, 0x30,
+    0x2B, 0x81, 0xAD, 0xB6, 0x6F, 0xFC, 0x85, 0x23, 0x42, 0x73, 0x96, 0xA9, 0x0A, 0xE5, 0x07, 0xEB,
+    0xE3, 0x42, 0x8F, 0x52, 0x8E, 0xED, 0x56, 0x38, 0x4A, 0x66, 0xCB, 0x2B, 0x67, 0x5B, 0x39, 0x25,
+    0xEB, 0xBD, 0x31, 0xA0, 0xDC, 0x65, 0xC3, 0x98, 0xA4, 0xF7, 0xDF, 0x90, 0x87, 0x8B, 0x06, 0xD7,
+    0xD4, 0xEC, 0xD4, 0x4F, 0x5C, 0x93, 0x6E, 0x98, 0xBD, 0x10, 0x1C, 0xDE, 0x85, 0xCA, 0x93, 0x7C,
+    0x21, 0xF9, 0x02, 0x14, 0xF4, 0x82, 0x83, 0x5C, 0xEA, 0x9E, 0x67, 0xFD, 0x83, 0xA5, 0x58, 0xB1,
+    0x99, 0x2B, 0xFC, 0xB3, 0xFA, 0x61, 0x0B, 0xF4, 0x3D, 0x35, 0x69, 0x5C, 0x0D, 0x65, 0x89, 0x99,
+    0xFF, 0x3F, 0xF1, 0x13, 0x35, 0x3A, 0x18, 0x5A, 0xA8, 0x65, 0x24, 0x72, 0xA4, 0x84, 0x5B, 0x4C,
+    0xD6, 0xCF, 0x59, 0xD1, 0x41, 0x4B, 0x3F, 0x88, 0xF7, 0x29, 0x2A, 0x6F, 0x9E, 0x3B, 0x80, 0xA3,
+    0xA3, 0xEA, 0x79, 0x51, 0xDA, 0x75, 0xC2, 0x8E, 0xE3, 0xFC, 0xAB, 0xFE, 0x09, 0xC9, 0x6E, 0x04,
+    0xF9, 0xDA, 0x7A, 0xA3, 0xBD, 0x82, 0xEA, 0x65, 0x19, 0x48, 0xA2, 0x2A, 0x51, 0x6C, 0x7E, 0x05,
+    0xC5, 0x39, 0x47, 0x46, 0xC1, 0x8F, 0x31, 0xF7, 0xA9, 0x6D, 0x46, 0xA7, 0x5B, 0x9B, 0x2D, 0x47,
+};
+
+extern const uint16_t TestDevice1_X509_RSA_ICACert1Length = sizeof(TestDevice1_X509_RSA_ICACert1);
+
+extern const uint8_t TestDevice1_X509_RSA_ICACert2[] =
+{
+
+    /*
+    -----BEGIN CERTIFICATE-----
+    MIIDzDCCArSgAwIBAgICATYwDQYJKoZIhvcNAQELBQAwdTELMAkGA1UEBhMCVVMx
+    EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEzAR
+    BgNVBAoMCkdvb2dsZSBJbmMxDTALBgNVBAsMBENhc3QxFTATBgNVBAMMDENhc3Qg
+    Um9vdCBDQTAeFw0xODAzMTIyMzQ4MzVaFw0zODAzMDcyMzQ4MzVaMHsxCzAJBgNV
+    BAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW
+    aWV3MRMwEQYDVQQKDApHb29nbGUgSW5jMQ0wCwYDVQQLDARDYXN0MRswGQYDVQQD
+    DBJDYXN0IFRlc3QgUm9vdCBJQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+    AoIBAQDrqU17a/iZxhBMMiKBj7nj8QFJmbJU131Xyw9p5VzwaJm/5Y7kGArxJHiR
+    +1qURs0/1Vnijv6jhOCLLj+KUdPyUvtXKVFPkQ7IXOrGnuV2gS5M8I6GDu/pKp0z
+    X8+94MBQbX+vODiLXtahzXwjEsHkqwjwy2tuo/qGna8n83fWGLavedmtlVCVIRdw
+    y2VOr5xXJ2IL0+U1Qh275Lp56iYdOCWLFjpjgJJVrmfI90+iqo3syEFKknm6JsoR
+    R1smAgR8yEZwNN2SdipFWun59OthNGS3+oB5v7N4oCc7NttpPotbOOR1FHucF/SJ
+    HEVKqMpJyjowOEXfHYfA1Tj2oUkTAgMBAAGjYDBeMA8GA1UdEwQIMAYBAf8CAQEw
+    HQYDVR0OBBYEFBr4tmUrKFh85aQW3aXxOTSORLXWMB8GA1UdIwQYMBaAFHyaHn3f
+    eVS818xeypmGRXlldCgZMAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEA
+    d5GrKh+V1uDMeU1UdLP8OxwOg4DXqBna6qK3pZBEcnLaZv7/KydTTUzGSrdSVCEx
+    P8rn7XB5mxhyItHQSIuPNwKLTN4UjiaaSebl+HFBJwM9uTIqMIy32IswZEv2ULIU
+    tztmFqpKf1Mfm67u0jcxtZM3KpCGIhcVsCS2eiGsBEfXds6dXalO6t7aCRNvRaDm
+    eA09ePKateEemtaTDWEJKYNfNGZNW8OyRaHcTXwE8HMrO2s4wFFW0k3oRmiddxBC
+    YCel+gXvw1UiD3pINLrlH36NNFxyfBfgLhZ7Idyi4LPBvrouQ2qcC76N0l1rbloT
+    /9mT+6JUYu/LVFGfwtTtWQ==
+    -----END CERTIFICATE-----
+    */
+
+    0x30, 0x82, 0x03, 0xCC, 0x30, 0x82, 0x02, 0xB4, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x01,
+    0x36, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00,
+    0x30, 0x75, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31,
+    0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x0A, 0x43, 0x61, 0x6C, 0x69, 0x66, 0x6F,
+    0x72, 0x6E, 0x69, 0x61, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0C, 0x0D, 0x4D,
+    0x6F, 0x75, 0x6E, 0x74, 0x61, 0x69, 0x6E, 0x20, 0x56, 0x69, 0x65, 0x77, 0x31, 0x13, 0x30, 0x11,
+    0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x0A, 0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x20, 0x49, 0x6E,
+    0x63, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C, 0x04, 0x43, 0x61, 0x73, 0x74,
+    0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x0C, 0x43, 0x61, 0x73, 0x74, 0x20,
+    0x52, 0x6F, 0x6F, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1E, 0x17, 0x0D, 0x31, 0x38, 0x30, 0x33, 0x31,
+    0x32, 0x32, 0x33, 0x34, 0x38, 0x33, 0x35, 0x5A, 0x17, 0x0D, 0x33, 0x38, 0x30, 0x33, 0x30, 0x37,
+    0x32, 0x33, 0x34, 0x38, 0x33, 0x35, 0x5A, 0x30, 0x7B, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55,
+    0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C,
+    0x0A, 0x43, 0x61, 0x6C, 0x69, 0x66, 0x6F, 0x72, 0x6E, 0x69, 0x61, 0x31, 0x16, 0x30, 0x14, 0x06,
+    0x03, 0x55, 0x04, 0x07, 0x0C, 0x0D, 0x4D, 0x6F, 0x75, 0x6E, 0x74, 0x61, 0x69, 0x6E, 0x20, 0x56,
+    0x69, 0x65, 0x77, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x0A, 0x47, 0x6F,
+    0x6F, 0x67, 0x6C, 0x65, 0x20, 0x49, 0x6E, 0x63, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04,
+    0x0B, 0x0C, 0x04, 0x43, 0x61, 0x73, 0x74, 0x31, 0x1B, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03,
+    0x0C, 0x12, 0x43, 0x61, 0x73, 0x74, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x52, 0x6F, 0x6F, 0x74,
+    0x20, 0x49, 0x43, 0x41, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86,
+    0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A,
+    0x02, 0x82, 0x01, 0x01, 0x00, 0xEB, 0xA9, 0x4D, 0x7B, 0x6B, 0xF8, 0x99, 0xC6, 0x10, 0x4C, 0x32,
+    0x22, 0x81, 0x8F, 0xB9, 0xE3, 0xF1, 0x01, 0x49, 0x99, 0xB2, 0x54, 0xD7, 0x7D, 0x57, 0xCB, 0x0F,
+    0x69, 0xE5, 0x5C, 0xF0, 0x68, 0x99, 0xBF, 0xE5, 0x8E, 0xE4, 0x18, 0x0A, 0xF1, 0x24, 0x78, 0x91,
+    0xFB, 0x5A, 0x94, 0x46, 0xCD, 0x3F, 0xD5, 0x59, 0xE2, 0x8E, 0xFE, 0xA3, 0x84, 0xE0, 0x8B, 0x2E,
+    0x3F, 0x8A, 0x51, 0xD3, 0xF2, 0x52, 0xFB, 0x57, 0x29, 0x51, 0x4F, 0x91, 0x0E, 0xC8, 0x5C, 0xEA,
+    0xC6, 0x9E, 0xE5, 0x76, 0x81, 0x2E, 0x4C, 0xF0, 0x8E, 0x86, 0x0E, 0xEF, 0xE9, 0x2A, 0x9D, 0x33,
+    0x5F, 0xCF, 0xBD, 0xE0, 0xC0, 0x50, 0x6D, 0x7F, 0xAF, 0x38, 0x38, 0x8B, 0x5E, 0xD6, 0xA1, 0xCD,
+    0x7C, 0x23, 0x12, 0xC1, 0xE4, 0xAB, 0x08, 0xF0, 0xCB, 0x6B, 0x6E, 0xA3, 0xFA, 0x86, 0x9D, 0xAF,
+    0x27, 0xF3, 0x77, 0xD6, 0x18, 0xB6, 0xAF, 0x79, 0xD9, 0xAD, 0x95, 0x50, 0x95, 0x21, 0x17, 0x70,
+    0xCB, 0x65, 0x4E, 0xAF, 0x9C, 0x57, 0x27, 0x62, 0x0B, 0xD3, 0xE5, 0x35, 0x42, 0x1D, 0xBB, 0xE4,
+    0xBA, 0x79, 0xEA, 0x26, 0x1D, 0x38, 0x25, 0x8B, 0x16, 0x3A, 0x63, 0x80, 0x92, 0x55, 0xAE, 0x67,
+    0xC8, 0xF7, 0x4F, 0xA2, 0xAA, 0x8D, 0xEC, 0xC8, 0x41, 0x4A, 0x92, 0x79, 0xBA, 0x26, 0xCA, 0x11,
+    0x47, 0x5B, 0x26, 0x02, 0x04, 0x7C, 0xC8, 0x46, 0x70, 0x34, 0xDD, 0x92, 0x76, 0x2A, 0x45, 0x5A,
+    0xE9, 0xF9, 0xF4, 0xEB, 0x61, 0x34, 0x64, 0xB7, 0xFA, 0x80, 0x79, 0xBF, 0xB3, 0x78, 0xA0, 0x27,
+    0x3B, 0x36, 0xDB, 0x69, 0x3E, 0x8B, 0x5B, 0x38, 0xE4, 0x75, 0x14, 0x7B, 0x9C, 0x17, 0xF4, 0x89,
+    0x1C, 0x45, 0x4A, 0xA8, 0xCA, 0x49, 0xCA, 0x3A, 0x30, 0x38, 0x45, 0xDF, 0x1D, 0x87, 0xC0, 0xD5,
+    0x38, 0xF6, 0xA1, 0x49, 0x13, 0x02, 0x03, 0x01, 0x00, 0x01, 0xA3, 0x60, 0x30, 0x5E, 0x30, 0x0F,
+    0x06, 0x03, 0x55, 0x1D, 0x13, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xFF, 0x02, 0x01, 0x01, 0x30,
+    0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04, 0x14, 0x1A, 0xF8, 0xB6, 0x65, 0x2B, 0x28,
+    0x58, 0x7C, 0xE5, 0xA4, 0x16, 0xDD, 0xA5, 0xF1, 0x39, 0x34, 0x8E, 0x44, 0xB5, 0xD6, 0x30, 0x1F,
+    0x06, 0x03, 0x55, 0x1D, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x7C, 0x9A, 0x1E, 0x7D, 0xDF,
+    0x79, 0x54, 0xBC, 0xD7, 0xCC, 0x5E, 0xCA, 0x99, 0x86, 0x45, 0x79, 0x65, 0x74, 0x28, 0x19, 0x30,
+    0x0B, 0x06, 0x03, 0x55, 0x1D, 0x0F, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0D, 0x06, 0x09,
+    0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00,
+    0x77, 0x91, 0xAB, 0x2A, 0x1F, 0x95, 0xD6, 0xE0, 0xCC, 0x79, 0x4D, 0x54, 0x74, 0xB3, 0xFC, 0x3B,
+    0x1C, 0x0E, 0x83, 0x80, 0xD7, 0xA8, 0x19, 0xDA, 0xEA, 0xA2, 0xB7, 0xA5, 0x90, 0x44, 0x72, 0x72,
+    0xDA, 0x66, 0xFE, 0xFF, 0x2B, 0x27, 0x53, 0x4D, 0x4C, 0xC6, 0x4A, 0xB7, 0x52, 0x54, 0x21, 0x31,
+    0x3F, 0xCA, 0xE7, 0xED, 0x70, 0x79, 0x9B, 0x18, 0x72, 0x22, 0xD1, 0xD0, 0x48, 0x8B, 0x8F, 0x37,
+    0x02, 0x8B, 0x4C, 0xDE, 0x14, 0x8E, 0x26, 0x9A, 0x49, 0xE6, 0xE5, 0xF8, 0x71, 0x41, 0x27, 0x03,
+    0x3D, 0xB9, 0x32, 0x2A, 0x30, 0x8C, 0xB7, 0xD8, 0x8B, 0x30, 0x64, 0x4B, 0xF6, 0x50, 0xB2, 0x14,
+    0xB7, 0x3B, 0x66, 0x16, 0xAA, 0x4A, 0x7F, 0x53, 0x1F, 0x9B, 0xAE, 0xEE, 0xD2, 0x37, 0x31, 0xB5,
+    0x93, 0x37, 0x2A, 0x90, 0x86, 0x22, 0x17, 0x15, 0xB0, 0x24, 0xB6, 0x7A, 0x21, 0xAC, 0x04, 0x47,
+    0xD7, 0x76, 0xCE, 0x9D, 0x5D, 0xA9, 0x4E, 0xEA, 0xDE, 0xDA, 0x09, 0x13, 0x6F, 0x45, 0xA0, 0xE6,
+    0x78, 0x0D, 0x3D, 0x78, 0xF2, 0x9A, 0xB5, 0xE1, 0x1E, 0x9A, 0xD6, 0x93, 0x0D, 0x61, 0x09, 0x29,
+    0x83, 0x5F, 0x34, 0x66, 0x4D, 0x5B, 0xC3, 0xB2, 0x45, 0xA1, 0xDC, 0x4D, 0x7C, 0x04, 0xF0, 0x73,
+    0x2B, 0x3B, 0x6B, 0x38, 0xC0, 0x51, 0x56, 0xD2, 0x4D, 0xE8, 0x46, 0x68, 0x9D, 0x77, 0x10, 0x42,
+    0x60, 0x27, 0xA5, 0xFA, 0x05, 0xEF, 0xC3, 0x55, 0x22, 0x0F, 0x7A, 0x48, 0x34, 0xBA, 0xE5, 0x1F,
+    0x7E, 0x8D, 0x34, 0x5C, 0x72, 0x7C, 0x17, 0xE0, 0x2E, 0x16, 0x7B, 0x21, 0xDC, 0xA2, 0xE0, 0xB3,
+    0xC1, 0xBE, 0xBA, 0x2E, 0x43, 0x6A, 0x9C, 0x0B, 0xBE, 0x8D, 0xD2, 0x5D, 0x6B, 0x6E, 0x5A, 0x13,
+    0xFF, 0xD9, 0x93, 0xFB, 0xA2, 0x54, 0x62, 0xEF, 0xCB, 0x54, 0x51, 0x9F, 0xC2, 0xD4, 0xED, 0x59,
+};
+
+extern const uint16_t TestDevice1_X509_RSA_ICACert2Length = sizeof(TestDevice1_X509_RSA_ICACert2);
+
+extern const uint8_t TestDevice_X509_RSA_RootCert[] =
+{
+
+    /*
+    -----BEGIN CERTIFICATE-----
+    MIIDxTCCAq2gAwIBAgIBAjANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJVUzET
+    MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzETMBEG
+    A1UECgwKR29vZ2xlIEluYzENMAsGA1UECwwEQ2FzdDEVMBMGA1UEAwwMQ2FzdCBS
+    b290IENBMB4XDTE0MDQwMjE3MzQyNloXDTM0MDMyODE3MzQyNlowdTELMAkGA1UE
+    BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZp
+    ZXcxEzARBgNVBAoMCkdvb2dsZSBJbmMxDTALBgNVBAsMBENhc3QxFTATBgNVBAMM
+    DENhc3QgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALrZ
+    ZZ3aOdPBd/bU0K6PWAhoOUqV7XDP/XkIqarl6binLaBnR4qeyc9wswWHaRHscJiX
+    w+bDw+u9xrA9/E/BXjif2s9zMAZbeTfBXoyHR5SaQZIq1pXEcVwnXQixgMaSvRvj
+    QZeh7HWfVZ4+n48cx2VkB9OzlqEEn5HE3gp7bNnIwHgxoBlCqeiD48788c7CLiRG
+    lQkZysBGsuUButdP87/2aa2ZBPqgBzkO5t9RRwfA5KlcS5TFL7OgMH/nlWuyrzIN
+    8YzVbct7R6cIq8sno03PSlrxBdH4YsUQKnRpquZLlvub2GPkWGbTrYpu/3te+aVW
+    Hi2CMVvw4iTmQUofrhMCAwEAAaNgMF4wDwYDVR0TBAgwBgEB/wIBAjAdBgNVHQ4E
+    FgQUfJoefd95VLzXzF7KmYZFeWV0KBkwHwYDVR0jBBgwFoAUfJoefd95VLzXzF7K
+    mYZFeWV0KBkwCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQCA9Fr7PSgZ
+    USDX1PsSl0pl8lg1kncwavHXtlEaf5rNx3sDQq1VagCv8OEGwr1reHXb/kERU0o5
+    u5o6xlk0Lywz47LWXH/deOtxWznag5DFMeI/I+/a6ystd17ew0PSyWtZgsrV7fqh
+    ZFvL8Q0aYuGc6KcYcPBfF5b47Ybbrh3gzz5dLu4WbZUrPP2X8wVaJGhNObb45Fi6
+    9eAmeFHFW11OCeVsR4t6Wi6JU+bMNlsmPPhyQwKC0ivN8NOj7BM+UtWDPQfcHUNl
+    ejMCAaPOt9ZgUTsJwiOKMv6YGWBik4XNNEbb1SMPedp3ACoCbYNYzgN3NeGjIJPC
+    SqKkRhx1LB9N
+    -----END CERTIFICATE-----
+    */
+
+    0x30, 0x82, 0x03, 0xC5, 0x30, 0x82, 0x02, 0xAD, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x02,
+    0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30,
+    0x75, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13,
+    0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x0A, 0x43, 0x61, 0x6C, 0x69, 0x66, 0x6F, 0x72,
+    0x6E, 0x69, 0x61, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0C, 0x0D, 0x4D, 0x6F,
+    0x75, 0x6E, 0x74, 0x61, 0x69, 0x6E, 0x20, 0x56, 0x69, 0x65, 0x77, 0x31, 0x13, 0x30, 0x11, 0x06,
+    0x03, 0x55, 0x04, 0x0A, 0x0C, 0x0A, 0x47, 0x6F, 0x6F, 0x67, 0x6C, 0x65, 0x20, 0x49, 0x6E, 0x63,
+    0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x0C, 0x04, 0x43, 0x61, 0x73, 0x74, 0x31,
+    0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C, 0x0C, 0x43, 0x61, 0x73, 0x74, 0x20, 0x52,
+    0x6F, 0x6F, 0x74, 0x20, 0x43, 0x41, 0x30, 0x1E, 0x17, 0x0D, 0x31, 0x34, 0x30, 0x34, 0x30, 0x32,
+    0x31, 0x37, 0x33, 0x34, 0x32, 0x36, 0x5A, 0x17, 0x0D, 0x33, 0x34, 0x30, 0x33, 0x32, 0x38, 0x31,
+    0x37, 0x33, 0x34, 0x32, 0x36, 0x5A, 0x30, 0x75, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+    0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0C, 0x0A,
+    0x43, 0x61, 0x6C, 0x69, 0x66, 0x6F, 0x72, 0x6E, 0x69, 0x61, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03,
+    0x55, 0x04, 0x07, 0x0C, 0x0D, 0x4D, 0x6F, 0x75, 0x6E, 0x74, 0x61, 0x69, 0x6E, 0x20, 0x56, 0x69,
+    0x65, 0x77, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x0C, 0x0A, 0x47, 0x6F, 0x6F,
+    0x67, 0x6C, 0x65, 0x20, 0x49, 0x6E, 0x63, 0x31, 0x0D, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x04, 0x0B,
+    0x0C, 0x04, 0x43, 0x61, 0x73, 0x74, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0C,
+    0x0C, 0x43, 0x61, 0x73, 0x74, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x43, 0x41, 0x30, 0x82, 0x01,
+    0x22, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00,
+    0x03, 0x82, 0x01, 0x0F, 0x00, 0x30, 0x82, 0x01, 0x0A, 0x02, 0x82, 0x01, 0x01, 0x00, 0xBA, 0xD9,
+    0x65, 0x9D, 0xDA, 0x39, 0xD3, 0xC1, 0x77, 0xF6, 0xD4, 0xD0, 0xAE, 0x8F, 0x58, 0x08, 0x68, 0x39,
+    0x4A, 0x95, 0xED, 0x70, 0xCF, 0xFD, 0x79, 0x08, 0xA9, 0xAA, 0xE5, 0xE9, 0xB8, 0xA7, 0x2D, 0xA0,
+    0x67, 0x47, 0x8A, 0x9E, 0xC9, 0xCF, 0x70, 0xB3, 0x05, 0x87, 0x69, 0x11, 0xEC, 0x70, 0x98, 0x97,
+    0xC3, 0xE6, 0xC3, 0xC3, 0xEB, 0xBD, 0xC6, 0xB0, 0x3D, 0xFC, 0x4F, 0xC1, 0x5E, 0x38, 0x9F, 0xDA,
+    0xCF, 0x73, 0x30, 0x06, 0x5B, 0x79, 0x37, 0xC1, 0x5E, 0x8C, 0x87, 0x47, 0x94, 0x9A, 0x41, 0x92,
+    0x2A, 0xD6, 0x95, 0xC4, 0x71, 0x5C, 0x27, 0x5D, 0x08, 0xB1, 0x80, 0xC6, 0x92, 0xBD, 0x1B, 0xE3,
+    0x41, 0x97, 0xA1, 0xEC, 0x75, 0x9F, 0x55, 0x9E, 0x3E, 0x9F, 0x8F, 0x1C, 0xC7, 0x65, 0x64, 0x07,
+    0xD3, 0xB3, 0x96, 0xA1, 0x04, 0x9F, 0x91, 0xC4, 0xDE, 0x0A, 0x7B, 0x6C, 0xD9, 0xC8, 0xC0, 0x78,
+    0x31, 0xA0, 0x19, 0x42, 0xA9, 0xE8, 0x83, 0xE3, 0xCE, 0xFC, 0xF1, 0xCE, 0xC2, 0x2E, 0x24, 0x46,
+    0x95, 0x09, 0x19, 0xCA, 0xC0, 0x46, 0xB2, 0xE5, 0x01, 0xBA, 0xD7, 0x4F, 0xF3, 0xBF, 0xF6, 0x69,
+    0xAD, 0x99, 0x04, 0xFA, 0xA0, 0x07, 0x39, 0x0E, 0xE6, 0xDF, 0x51, 0x47, 0x07, 0xC0, 0xE4, 0xA9,
+    0x5C, 0x4B, 0x94, 0xC5, 0x2F, 0xB3, 0xA0, 0x30, 0x7F, 0xE7, 0x95, 0x6B, 0xB2, 0xAF, 0x32, 0x0D,
+    0xF1, 0x8C, 0xD5, 0x6D, 0xCB, 0x7B, 0x47, 0xA7, 0x08, 0xAB, 0xCB, 0x27, 0xA3, 0x4D, 0xCF, 0x4A,
+    0x5A, 0xF1, 0x05, 0xD1, 0xF8, 0x62, 0xC5, 0x10, 0x2A, 0x74, 0x69, 0xAA, 0xE6, 0x4B, 0x96, 0xFB,
+    0x9B, 0xD8, 0x63, 0xE4, 0x58, 0x66, 0xD3, 0xAD, 0x8A, 0x6E, 0xFF, 0x7B, 0x5E, 0xF9, 0xA5, 0x56,
+    0x1E, 0x2D, 0x82, 0x31, 0x5B, 0xF0, 0xE2, 0x24, 0xE6, 0x41, 0x4A, 0x1F, 0xAE, 0x13, 0x02, 0x03,
+    0x01, 0x00, 0x01, 0xA3, 0x60, 0x30, 0x5E, 0x30, 0x0F, 0x06, 0x03, 0x55, 0x1D, 0x13, 0x04, 0x08,
+    0x30, 0x06, 0x01, 0x01, 0xFF, 0x02, 0x01, 0x02, 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04,
+    0x16, 0x04, 0x14, 0x7C, 0x9A, 0x1E, 0x7D, 0xDF, 0x79, 0x54, 0xBC, 0xD7, 0xCC, 0x5E, 0xCA, 0x99,
+    0x86, 0x45, 0x79, 0x65, 0x74, 0x28, 0x19, 0x30, 0x1F, 0x06, 0x03, 0x55, 0x1D, 0x23, 0x04, 0x18,
+    0x30, 0x16, 0x80, 0x14, 0x7C, 0x9A, 0x1E, 0x7D, 0xDF, 0x79, 0x54, 0xBC, 0xD7, 0xCC, 0x5E, 0xCA,
+    0x99, 0x86, 0x45, 0x79, 0x65, 0x74, 0x28, 0x19, 0x30, 0x0B, 0x06, 0x03, 0x55, 0x1D, 0x0F, 0x04,
+    0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01,
+    0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x80, 0xF4, 0x5A, 0xFB, 0x3D, 0x28, 0x19,
+    0x51, 0x20, 0xD7, 0xD4, 0xFB, 0x12, 0x97, 0x4A, 0x65, 0xF2, 0x58, 0x35, 0x92, 0x77, 0x30, 0x6A,
+    0xF1, 0xD7, 0xB6, 0x51, 0x1A, 0x7F, 0x9A, 0xCD, 0xC7, 0x7B, 0x03, 0x42, 0xAD, 0x55, 0x6A, 0x00,
+    0xAF, 0xF0, 0xE1, 0x06, 0xC2, 0xBD, 0x6B, 0x78, 0x75, 0xDB, 0xFE, 0x41, 0x11, 0x53, 0x4A, 0x39,
+    0xBB, 0x9A, 0x3A, 0xC6, 0x59, 0x34, 0x2F, 0x2C, 0x33, 0xE3, 0xB2, 0xD6, 0x5C, 0x7F, 0xDD, 0x78,
+    0xEB, 0x71, 0x5B, 0x39, 0xDA, 0x83, 0x90, 0xC5, 0x31, 0xE2, 0x3F, 0x23, 0xEF, 0xDA, 0xEB, 0x2B,
+    0x2D, 0x77, 0x5E, 0xDE, 0xC3, 0x43, 0xD2, 0xC9, 0x6B, 0x59, 0x82, 0xCA, 0xD5, 0xED, 0xFA, 0xA1,
+    0x64, 0x5B, 0xCB, 0xF1, 0x0D, 0x1A, 0x62, 0xE1, 0x9C, 0xE8, 0xA7, 0x18, 0x70, 0xF0, 0x5F, 0x17,
+    0x96, 0xF8, 0xED, 0x86, 0xDB, 0xAE, 0x1D, 0xE0, 0xCF, 0x3E, 0x5D, 0x2E, 0xEE, 0x16, 0x6D, 0x95,
+    0x2B, 0x3C, 0xFD, 0x97, 0xF3, 0x05, 0x5A, 0x24, 0x68, 0x4D, 0x39, 0xB6, 0xF8, 0xE4, 0x58, 0xBA,
+    0xF5, 0xE0, 0x26, 0x78, 0x51, 0xC5, 0x5B, 0x5D, 0x4E, 0x09, 0xE5, 0x6C, 0x47, 0x8B, 0x7A, 0x5A,
+    0x2E, 0x89, 0x53, 0xE6, 0xCC, 0x36, 0x5B, 0x26, 0x3C, 0xF8, 0x72, 0x43, 0x02, 0x82, 0xD2, 0x2B,
+    0xCD, 0xF0, 0xD3, 0xA3, 0xEC, 0x13, 0x3E, 0x52, 0xD5, 0x83, 0x3D, 0x07, 0xDC, 0x1D, 0x43, 0x65,
+    0x7A, 0x33, 0x02, 0x01, 0xA3, 0xCE, 0xB7, 0xD6, 0x60, 0x51, 0x3B, 0x09, 0xC2, 0x23, 0x8A, 0x32,
+    0xFE, 0x98, 0x19, 0x60, 0x62, 0x93, 0x85, 0xCD, 0x34, 0x46, 0xDB, 0xD5, 0x23, 0x0F, 0x79, 0xDA,
+    0x77, 0x00, 0x2A, 0x02, 0x6D, 0x83, 0x58, 0xCE, 0x03, 0x77, 0x35, 0xE1, 0xA3, 0x20, 0x93, 0xC2,
+    0x4A, 0xA2, 0xA4, 0x46, 0x1C, 0x75, 0x2C, 0x1F, 0x4D,
+};
+
+extern const uint16_t TestDevice_X509_RSA_RootCertLength = sizeof(TestDevice_X509_RSA_RootCert);
diff --git a/src/test-apps/Makefile.am b/src/test-apps/Makefile.am
index 9d35c48..c46a09a 100644
--- a/src/test-apps/Makefile.am
+++ b/src/test-apps/Makefile.am
@@ -1,5 +1,5 @@
 #
-#    Copyright (c) 2018-2019 Google LLC
+#    Copyright (c) 2018-2020 Google LLC
 #    Copyright (c) 2014-2018 Nest Labs, Inc.
 #    All rights reserved.
 #
@@ -81,9 +81,11 @@
 #
 noinst_HEADERS                                             = \
     CASEOptions.h                                            \
+    CertProvOptions.h                                        \
     DMTestClient.h                                           \
     DeviceDescOptions.h                                      \
     KeyExportOptions.h                                       \
+    MockCAService.h                                          \
     MockDCLPServer.h                                         \
     MockDCServer.h                                           \
     MockDDServer.h                                           \
@@ -117,7 +119,7 @@
     MockWdmViewServer.h                                      \
     PASEEngineTest.h                                         \
     TAKEOptions.h                                            \
-    TapAddrAutoconf.h                                               \
+    TapAddrAutoconf.h                                        \
     TestDRBG.h                                               \
     TestEventLoggingSchemaExamples.h                         \
     TestGroupKeyStore.h                                      \
@@ -197,11 +199,13 @@
 
 libWeaveTestCommon_a_SOURCES                   = \
     CASEOptions.cpp                              \
+    CertProvOptions.cpp                          \
     KeyExportOptions.cpp                         \
     TAKEOptions.cpp                              \
     DeviceDescOptions.cpp                        \
     Certs.cpp                                    \
     TestGroupKeyStore.cpp                        \
+    TestWeaveCertData.cpp                        \
     ToolCommon.cpp                               \
     ToolCommonOptions.cpp                        \
     TapAddrAutoconf.cpp                          \
@@ -293,6 +297,8 @@
 endif
 
 libexec_PROGRAMS                              += \
+    weave-cert-prov-client                       \
+    weave-cert-prov-server                       \
     weave-device-descriptor                      \
     weave-key-export                             \
     weave-ping                                   \
@@ -308,6 +314,7 @@
     TestAppKeys                                  \
     TestArgParser                                \
     TestCASE                                     \
+    TestCertProv                                 \
     TestCodeUtils                                \
     TestCrypto                                   \
     TestDRBG                                     \
@@ -423,6 +430,7 @@
     TestAppKeys                                  \
     TestArgParser                                \
     TestCASE                                     \
+    TestCertProv                                 \
     TestCodeUtils                                \
     TestCrypto                                   \
     TestDRBG                                     \
@@ -1169,6 +1177,10 @@
 TestCASE_LDFLAGS                         = $(AM_CPPFLAGS)
 TestCASE_LDADD                           = libWeaveTestCommon.a $(COMMON_LDADD)
 
+TestCertProv_SOURCES                     = TestCertProv.cpp MockCAService.cpp
+TestCertProv_LDFLAGS                     = $(AM_CPPFLAGS)
+TestCertProv_LDADD                       = libWeaveTestCommon.a $(COMMON_LDADD)
+
 TestCodeUtils_SOURCES                    = TestCodeUtils.cpp
 TestCodeUtils_LDADD                      =
 
@@ -1417,7 +1429,7 @@
                                            schema/weave/trait/locale/LocaleCapabilitiesTrait.cpp	\
                                            schema/weave/trait/security/BoltLockSettingsTrait.cpp	\
                                            schema/weave/trait/telemetry/NetworkWiFiTelemetryTrait.cpp	\
-										   MockWdmNodeOptions.cpp                   \
+                                           MockWdmNodeOptions.cpp                                       \
                                            MockWdmViewServer.cpp					\
                                            MockWdmViewClient.cpp					\
                                            MockWdmSubscriptionInitiator.cpp				\
@@ -1485,7 +1497,7 @@
                                            MockWdmSubscriptionInitiator.cpp                         \
                                            MockWdmTestVerifier.cpp	                            \
                                            MockWdmSubscriptionResponder.cpp                         \
-										   MockWdmNodeOptions.cpp                                   \
+                                           MockWdmNodeOptions.cpp                                   \
                                            MockLoggingManager.cpp                                   \
                                            MockEvents.cpp
 
@@ -1567,6 +1579,15 @@
 weave_dd_client_LDFLAGS                  = ${AM_CPPFLAGS}
 weave_dd_client_LDADD                    = libWeaveTestCommon.a $(COMMON_LDADD)
 
+weave_cert_prov_client_SOURCES           = weave-cert-prov-client.cpp
+weave_cert_prov_client_LDFLAGS           = ${AM_CPPFLAGS}
+weave_cert_prov_client_LDADD             = libWeaveTestCommon.a $(COMMON_LDADD)
+
+weave_cert_prov_server_SOURCES           = weave-cert-prov-server.cpp          \
+                                           MockCAService.cpp
+weave_cert_prov_server_LDFLAGS           = ${AM_CPPFLAGS}
+weave_cert_prov_server_LDADD             = libWeaveTestCommon.a $(COMMON_LDADD)
+
 weave_device_descriptor_SOURCES          = weave-device-descriptor.cpp
 weave_device_descriptor_LDFLAGS          = ${AM_CPPFLAGS}
 weave_device_descriptor_LDADD            = libWeaveTestCommon.a $(COMMON_LDADD)
diff --git a/src/test-apps/MockCAService.cpp b/src/test-apps/MockCAService.cpp
new file mode 100644
index 0000000..4588f99
--- /dev/null
+++ b/src/test-apps/MockCAService.cpp
@@ -0,0 +1,696 @@
+/*
+ *
+ *    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
+ *      This file implements a derived unsolicited responder
+ *      (i.e., server) for the Certificate Provisioned protocol of the
+ *      Weave Security profile used for the Weave mock
+ *      device command line functional testing tool.
+ *
+ */
+
+#define __STDC_FORMAT_MACROS
+
+#include <inttypes.h>
+
+#include "ToolCommon.h"
+#include "MockCAService.h"
+#include <Weave/Support/CodeUtils.h>
+#include <Weave/Support/NestCerts.h>
+#include <Weave/Support/crypto/HashAlgos.h>
+#include <Weave/Support/crypto/RSA.h>
+#include <Weave/Profiles/WeaveProfiles.h>
+#include <Weave/Profiles/common/CommonProfile.h>
+#include <Weave/Profiles/security/WeaveSig.h>
+#include <Weave/Profiles/security/WeaveCert.h>
+#include <Weave/Profiles/security/WeavePrivateKey.h>
+#include <Weave/Profiles/status-report/StatusReportProfile.h>
+
+#if WEAVE_WITH_OPENSSL
+#include <openssl/x509.h>
+#endif
+
+using namespace nl::Weave::Profiles;
+using namespace nl::Weave::Profiles::Security;
+using namespace nl::Weave::Profiles::Security::CertProvisioning;
+using namespace nl::Weave::Encoding;
+using namespace nl::Weave::TLV;
+using namespace nl::Weave::ASN1;
+
+#if WEAVE_WITH_OPENSSL
+
+static WEAVE_ERROR ValidateX509DeviceCert(X509Cert *certSet, uint8_t certCount)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+    BIO *certBuf[kMaxCertCount] = { NULL };
+    X509 *cert[kMaxCertCount] = { NULL };
+    X509_STORE *store = NULL;
+    X509_STORE_CTX *ctx = NULL;
+    X509_VERIFY_PARAM *param = NULL;
+    int res;
+
+    VerifyOrExit(certSet != NULL && certCount > 0 && certCount <= kMaxCertCount, err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+    store = X509_STORE_new();
+    VerifyOrExit(store != NULL, err = WEAVE_ERROR_NO_MEMORY);
+
+    for (int i = 0; i < certCount; i++)
+    {
+        VerifyOrExit(certSet[i].Cert != NULL && certSet[i].Len > 0, err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+        certBuf[i] = BIO_new_mem_buf(certSet[i].Cert, certSet[i].Len);
+        VerifyOrExit(certBuf[i] != NULL, err = WEAVE_ERROR_NO_MEMORY);
+
+        cert[i] = d2i_X509_bio(certBuf[i], NULL);
+        VerifyOrExit(cert[i] != NULL, err = WEAVE_ERROR_NO_MEMORY);
+
+        if (i > 0)
+        {
+            res = X509_STORE_add_cert(store, cert[i]);
+            VerifyOrExit(res == 1, err = WEAVE_ERROR_NO_MEMORY);
+        }
+    }
+
+    ctx = X509_STORE_CTX_new();
+    VerifyOrExit(ctx != NULL, err = WEAVE_ERROR_NO_MEMORY);
+
+    param = X509_VERIFY_PARAM_new();
+    VerifyOrExit(param != NULL, err = WEAVE_ERROR_NO_MEMORY);
+
+    X509_VERIFY_PARAM_clear_flags(param, X509_V_FLAG_USE_CHECK_TIME);
+    X509_STORE_CTX_set0_param(ctx, param);
+
+    res = X509_STORE_CTX_init(ctx, store, cert[0], NULL);
+    VerifyOrExit(res == 1, err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+    res = X509_verify_cert(ctx);
+    VerifyOrExit(res == 1, err = WEAVE_ERROR_INVALID_SIGNATURE);
+
+exit:
+    if (NULL != param) X509_VERIFY_PARAM_free(param);
+    if (NULL != ctx) X509_STORE_CTX_free(ctx);
+    if (NULL != store) X509_STORE_free(store);
+    for (int i = 0; i < certCount; i++)
+    {
+        if (NULL != cert[i]) X509_free(cert[i]);
+        if (NULL != certBuf[i]) BIO_free(certBuf[i]);
+    }
+
+    return err;
+}
+
+#endif // WEAVE_WITH_OPENSSL
+
+GetCertificateRequestMessage::GetCertificateRequestMessage()
+{
+    mReqType = WeaveCertProvEngine::kReqType_NotSpecified;
+    mMfrAttestType = kMfrAttestType_Undefined;
+
+    AuthorizeInfoPairingToken = NULL;
+    AuthorizeInfoPairingTokenLen = 0;
+    AuthorizeInfoPairingInitData = NULL;
+    AuthorizeInfoPairingInitDataLen = 0;
+
+    memset(MfrAttestX509CertSet, 0, sizeof(MfrAttestX509CertSet));
+    MfrAttestX509CertCount = 0;
+
+    OperationalSigAlgo = kOID_NotSpecified;
+    memset(&OperationalSig, 0, sizeof(OperationalSig));
+    MfrAttestSigAlgo = kOID_NotSpecified;
+    memset(&MfrAttestSig, 0, sizeof(MfrAttestSig));
+
+    mOperationalCertSetInitialized = false;
+    mMfrAttestCertSetInitialized = false;
+
+    mTBSDataStart = NULL;
+    mTBSDataLen = 0;
+}
+
+WEAVE_ERROR GetCertificateRequestMessage::Decode(PacketBuffer *msgBuf)
+{
+    WEAVE_ERROR err;
+    TLVReader reader;
+    TLVType outerContainer;
+    WeaveCertificateData * certData;
+
+    reader.Init(msgBuf);
+
+    // Advance the reader to the start of the GetCertificateRequest message structure.
+    err = reader.Next(kTLVType_Structure, AnonymousTag);
+    SuccessOrExit(err);
+
+    err = reader.EnterContainer(outerContainer);
+    SuccessOrExit(err);
+
+    // Request Type.
+    {
+        mTBSDataStart = reader.GetReadPoint();
+
+        err = reader.Next(kTLVType_UnsignedInteger, ContextTag(kTag_GetCertReqMsg_ReqType));
+        SuccessOrExit(err);
+
+        err = reader.Get(mReqType);
+        SuccessOrExit(err);
+
+        VerifyOrExit(RequestType() == WeaveCertProvEngine::kReqType_GetInitialOpDeviceCert ||
+                     RequestType() == WeaveCertProvEngine::kReqType_RotateOpDeviceCert, err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+        err = reader.Next();
+        SuccessOrExit(err);
+    }
+
+    // Request authorization information - pairing token (optional).
+    if (reader.GetType() == kTLVType_ByteString && reader.GetTag() == ContextTag(kTag_GetCertReqMsg_Authorize_PairingToken))
+    {
+        err = reader.GetDataPtr(AuthorizeInfoPairingToken);
+        SuccessOrExit(err);
+
+        AuthorizeInfoPairingTokenLen = reader.GetLength();
+
+        err = reader.Next();
+        SuccessOrExit(err);
+
+        // Request authorization information - pairing init data (optional).
+        if (reader.GetType() == kTLVType_ByteString && reader.GetTag() == ContextTag(kTag_GetCertReqMsg_Authorize_PairingInitData))
+        {
+            err = reader.GetDataPtr(AuthorizeInfoPairingInitData);
+            SuccessOrExit(err);
+
+            AuthorizeInfoPairingInitDataLen = reader.GetLength();
+
+            err = reader.Next();
+            SuccessOrExit(err);
+        }
+    }
+
+    // Operational Device Certificate.
+    {
+        VerifyOrExit(reader.GetType() == kTLVType_Structure, err = WEAVE_ERROR_WRONG_TLV_TYPE);
+        VerifyOrExit(reader.GetTag() == ContextTag(kTag_GetCertReqMsg_OpDeviceCert), err = WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT);
+
+        err = OperationalCertSet.Init(kMaxCertCount, nl::TestCerts::kTestCertBufSize);
+        SuccessOrExit(err);
+
+        mOperationalCertSetInitialized = true;
+
+        // Load Weave operational device certificate.
+        err = OperationalCertSet.LoadCert(reader, kDecodeFlag_GenerateTBSHash, certData);
+        SuccessOrExit(err);
+
+        mTBSDataLen = reader.GetReadPoint() - mTBSDataStart;
+
+        err = reader.Next();
+        SuccessOrExit(err);
+    }
+
+    // Load intermediate certificates (optional).
+    if (reader.GetType() == kTLVType_Array && reader.GetTag() == ContextTag(kTag_GetCertReqMsg_OpRelatedCerts))
+    {
+        // Intermediate certificates are not expected when self-signed certificate is used
+        // in the Get Initial Operational Device Certificate Request.
+        VerifyOrExit(RequestType() != WeaveCertProvEngine::kReqType_GetInitialOpDeviceCert, err = WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT);
+
+        err = OperationalCertSet.LoadCerts(reader, kDecodeFlag_GenerateTBSHash);
+        SuccessOrExit(err);
+
+        mTBSDataLen = reader.GetReadPoint() - mTBSDataStart;
+
+        err = reader.Next();
+        SuccessOrExit(err);
+    }
+
+    // Manufacturer Attestation Information (optional).
+    if (reader.GetType() == kTLVType_Structure && reader.GetTag() == ContextTag(kTag_GetCertReqMsg_MfrAttest_WeaveCert))
+    {
+        err = MfrAttestWeaveCertSet.Init(kMaxCertCount, nl::TestCerts::kTestCertBufSize);
+        SuccessOrExit(err);
+
+        mMfrAttestCertSetInitialized = true;
+
+        // Load manufacturer attestation Weave certificate.
+        err = MfrAttestWeaveCertSet.LoadCert(reader, kDecodeFlag_GenerateTBSHash, certData);
+        SuccessOrExit(err);
+
+        mTBSDataLen = reader.GetReadPoint() - mTBSDataStart;
+
+        err = reader.Next(kTLVType_Array, ContextTag(kTag_GetCertReqMsg_MfrAttest_WeaveRelCerts));
+
+        if (err == WEAVE_NO_ERROR)
+        {
+            // Load intermediate certificate.
+            err = MfrAttestWeaveCertSet.LoadCerts(reader, kDecodeFlag_GenerateTBSHash);
+            SuccessOrExit(err);
+
+            mTBSDataLen = reader.GetReadPoint() - mTBSDataStart;
+
+            err = reader.Next();
+            SuccessOrExit(err);
+        }
+
+        MfrAttestType(kMfrAttestType_WeaveCert);
+    }
+    else if (reader.GetType() == kTLVType_ByteString && reader.GetTag() == ContextTag(kTag_GetCertReqMsg_MfrAttest_X509Cert))
+    {
+        err = reader.GetDataPtr(const_cast<const uint8_t *&>(MfrAttestX509CertSet[MfrAttestX509CertCount].Cert));
+        SuccessOrExit(err);
+
+        MfrAttestX509CertSet[MfrAttestX509CertCount++].Len = reader.GetLength();
+
+        mTBSDataLen = MfrAttestX509CertSet[0].Cert + reader.GetLength() - mTBSDataStart;
+
+        err = reader.Next(kTLVType_Array, ContextTag(kTag_GetCertReqMsg_MfrAttest_X509RelCerts));
+
+        // Intermediate certificates (optional).
+        if (err == WEAVE_NO_ERROR)
+        {
+            TLVType outerContainer2;
+
+            err = reader.EnterContainer(outerContainer2);
+            SuccessOrExit(err);
+
+            err = reader.Next();
+
+            while (err != WEAVE_END_OF_TLV)
+            {
+                SuccessOrExit(err);
+
+                VerifyOrExit(MfrAttestX509CertCount < kMaxCertCount, err = WEAVE_ERROR_BUFFER_TOO_SMALL);
+
+                err = reader.GetDataPtr(const_cast<const uint8_t *&>(MfrAttestX509CertSet[MfrAttestX509CertCount].Cert));
+                SuccessOrExit(err);
+
+                MfrAttestX509CertSet[MfrAttestX509CertCount++].Len = reader.GetLength();
+
+                err = reader.Next();
+            }
+
+            err = reader.ExitContainer(outerContainer2);
+            SuccessOrExit(err);
+
+            mTBSDataLen = reader.GetReadPoint() - mTBSDataStart;
+
+            err = reader.Next();
+            SuccessOrExit(err);
+        }
+
+        MfrAttestType(kMfrAttestType_X509Cert);
+    }
+    else if (reader.GetType() == kTLVType_UnsignedInteger && reader.GetTag() == ContextTag(kTag_GetCertReqMsg_MfrAttest_HMACKeyId))
+    {
+        err = reader.Get(MfrAttestHMACKeyId);
+        SuccessOrExit(err);
+
+        mTBSDataLen = reader.GetReadPoint() - mTBSDataStart;
+
+        err = reader.Next();
+        SuccessOrExit(err);
+
+        // Request authorization information - pairing init data (optional).
+        if (reader.GetType() == kTLVType_ByteString && reader.GetTag() == ContextTag(kTag_GetCertReqMsg_MfrAttest_HMACMetaData))
+        {
+            err = reader.GetDataPtr(MfrAttestHMACMetaData);
+            SuccessOrExit(err);
+
+            MfrAttestHMACMetaDataLen = reader.GetLength();
+
+            mTBSDataLen = MfrAttestHMACMetaData + MfrAttestHMACMetaDataLen - mTBSDataStart;
+
+            err = reader.Next();
+            SuccessOrExit(err);
+        }
+
+        MfrAttestType(kMfrAttestType_HMAC);
+    }
+    else
+    {
+        VerifyOrExit(!MfrAttestRequired(), err = WEAVE_ERROR_INVALID_ARGUMENT);
+    }
+
+    // Operational Device Signature.
+    {
+        VerifyOrExit(reader.GetType() == kTLVType_UnsignedInteger, err = WEAVE_ERROR_WRONG_TLV_TYPE);
+        VerifyOrExit(reader.GetTag() == ContextTag(kTag_GetCertReqMsg_OpDeviceSigAlgo), err = WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT);
+
+        err = reader.Get(OperationalSigAlgo);
+        SuccessOrExit(err);
+
+        err = reader.Next(kTLVType_Structure, ContextTag(kTag_GetCertReqMsg_OpDeviceSig_ECDSA));
+        SuccessOrExit(err);
+
+        err = DecodeWeaveECDSASignature(reader, OperationalSig);
+        SuccessOrExit(err);
+
+        err = reader.Next();
+    }
+
+    // Manufacturer Attestation Signature (optional).
+    if (MfrAttestPresent())
+    {
+        VerifyOrExit(reader.GetType() == kTLVType_UnsignedInteger, err = WEAVE_ERROR_WRONG_TLV_TYPE);
+        VerifyOrExit(reader.GetTag() == ContextTag(kTag_GetCertReqMsg_MfrAttestSigAlgo), err = WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT);
+
+        err = reader.Get(MfrAttestSigAlgo);
+        SuccessOrExit(err);
+
+        err = reader.Next();
+        SuccessOrExit(err);
+
+        if (reader.GetType() == kTLVType_Structure && reader.GetTag() == ContextTag(kTag_GetCertReqMsg_MfrAttestSig_ECDSA))
+        {
+            VerifyOrExit(MfrAttestType() == kMfrAttestType_WeaveCert, err = WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT);
+
+            err = DecodeWeaveECDSASignature(reader, MfrAttestSig.EC);
+            SuccessOrExit(err);
+        }
+        else if (reader.GetType() == kTLVType_ByteString && reader.GetTag() == ContextTag(kTag_GetCertReqMsg_MfrAttestSig_RSA))
+        {
+            VerifyOrExit(MfrAttestType() == kMfrAttestType_X509Cert, err = WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT);
+
+            err = MfrAttestSig.RSA.ReadSignature(reader);
+            SuccessOrExit(err);
+        }
+        else if (reader.GetType() == kTLVType_ByteString && reader.GetTag() == ContextTag(kTag_GetCertReqMsg_MfrAttestSig_HMAC))
+        {
+            VerifyOrExit(MfrAttestType() == kMfrAttestType_HMAC, err = WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT);
+
+            err = MfrAttestSig.HMAC.ReadSignature(reader);
+            SuccessOrExit(err);
+
+            err = reader.Next();
+        }
+        else
+        {
+            // Any other manufacturer attestation types are not currently supported.
+            ExitNow(err = WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT);
+        }
+    }
+
+    err = reader.VerifyEndOfContainer();
+    SuccessOrExit(err);
+
+    err = reader.ExitContainer(outerContainer);
+    SuccessOrExit(err);
+
+exit:
+    return err;
+}
+
+WEAVE_ERROR GetCertificateRequestMessage::GenerateTBSHash(uint8_t *tbsHash)
+{
+    nl::Weave::Platform::Security::SHA256 sha256;
+
+    sha256.Begin();
+    sha256.AddData(mTBSDataStart, mTBSDataLen);
+    sha256.Finish(tbsHash);
+
+    return WEAVE_NO_ERROR;
+}
+
+MockCAService::MockCAService()
+{
+    mExchangeMgr = NULL;
+    mLogMessageData = false;
+    mIncludeRelatedCerts = false;
+    mDoNotRotateCert = false;
+
+    mCACert = nl::TestCerts::sTestCert_CA_Weave;
+    mCACertLen = nl::TestCerts::sTestCertLength_CA_Weave;
+
+    mCAPrivateKey = nl::TestCerts::sTestCert_CA_PrivateKey_Weave;
+    mCAPrivateKeyLen = nl::TestCerts::sTestCertLength_CA_PrivateKey_Weave;
+}
+
+WEAVE_ERROR MockCAService::Init(nl::Weave::WeaveExchangeManager *exchangeMgr)
+{
+    WEAVE_ERROR err;
+
+    mExchangeMgr = exchangeMgr;
+
+    // Register to receive unsolicited GetCertificateRequest messages from the exchange manager.
+    err = mExchangeMgr->RegisterUnsolicitedMessageHandler(kWeaveProfile_Security, kMsgType_GetCertificateRequest, HandleClientRequest, this);
+    SuccessOrExit(err);
+
+exit:
+    return err;
+}
+
+WEAVE_ERROR MockCAService::Shutdown()
+{
+    if (mExchangeMgr != NULL)
+        mExchangeMgr->UnregisterUnsolicitedMessageHandler(kWeaveProfile_Security, kMsgType_GetCertificateRequest);
+    return WEAVE_NO_ERROR;
+}
+
+void MockCAService::HandleClientRequest(nl::Weave::ExchangeContext *ec, const nl::Inet::IPPacketInfo *pktInfo,
+                                        const nl::Weave::WeaveMessageInfo *msgInfo, uint32_t profileId,
+                                        uint8_t msgType, PacketBuffer *reqMsg)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+    MockCAService *server = static_cast<MockCAService *>(ec->AppState);
+    GetCertificateRequestMessage getCertMsg;
+    PacketBuffer *respMsg = NULL;
+    char ipAddrStr[64];
+    ec->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
+
+    VerifyOrExit(profileId == kWeaveProfile_Security &&
+                 msgType == kMsgType_GetCertificateRequest, err = WEAVE_ERROR_NO_MEMORY);
+
+    printf("GetCertificate request received from node %" PRIX64 " (%s)\n", ec->PeerNodeId, ipAddrStr);
+
+    err = server->ProcessGetCertificateRequest(reqMsg, getCertMsg);
+    SuccessOrExit(err);
+
+    if ((getCertMsg.RequestType() == WeaveCertProvEngine::kReqType_RotateOpDeviceCert) && server->mDoNotRotateCert)
+    {
+        err = server->SendStatusReport(ec, Security::kStatusCode_NoNewCertRequired);
+        SuccessOrExit(err);
+    }
+    else
+    {
+        respMsg = PacketBuffer::New();
+        VerifyOrExit(respMsg != NULL, err = WEAVE_ERROR_NO_MEMORY);
+
+        err = server->GenerateGetCertificateResponse(respMsg, *getCertMsg.OperationalCertSet.Certs);
+        SuccessOrExit(err);
+
+        err = ec->SendMessage(kWeaveProfile_Security, kMsgType_GetCertificateResponse, respMsg, 0);
+        respMsg = NULL;
+        SuccessOrExit(err);
+    }
+
+exit:
+    if (err != WEAVE_NO_ERROR)
+        server->SendStatusReport(ec, Security::kStatusCode_UnathorizedGetCertRequest);
+
+    if (reqMsg != NULL)
+        PacketBuffer::Free(reqMsg);
+
+    if (respMsg != NULL)
+        PacketBuffer::Free(respMsg);
+}
+
+WEAVE_ERROR MockCAService::SendStatusReport(nl::Weave::ExchangeContext *ec, uint16_t statusCode)
+{
+    WEAVE_ERROR err;
+    PacketBuffer *statusMsg;
+    StatusReporting::StatusReport statusReport;
+
+    statusMsg = PacketBuffer::New();
+    VerifyOrExit(statusMsg != NULL, err = WEAVE_ERROR_NO_MEMORY);
+
+    statusReport.mProfileId = kWeaveProfile_Security;
+    statusReport.mStatusCode = statusCode;
+
+    err = statusReport.pack(statusMsg);
+    SuccessOrExit(err);
+
+    err = ec->SendMessage(kWeaveProfile_Common, Common::kMsgType_StatusReport, statusMsg, 0);
+    statusMsg = NULL;
+    SuccessOrExit(err);
+
+exit:
+    if (statusMsg != NULL)
+        PacketBuffer::Free(statusMsg);
+
+    return err;
+}
+
+WEAVE_ERROR MockCAService::ProcessGetCertificateRequest(PacketBuffer *msgBuf, GetCertificateRequestMessage & msg)
+{
+    WEAVE_ERROR err;
+    uint8_t tbsHash[SHA256::kHashLength];
+
+    err = msg.Decode(msgBuf);
+    SuccessOrExit(err);
+
+    // Validate Manufacturer Attestation Information if present.
+    if (msg.AuthorizeInfoPresent())
+    {
+        int res;
+
+        VerifyOrExit(msg.AuthorizeInfoPairingTokenLen == TestPairingTokenLength, err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+        res = memcmp(msg.AuthorizeInfoPairingToken, TestPairingToken, TestPairingTokenLength);
+        VerifyOrExit(res == 0, err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+        VerifyOrExit(msg.AuthorizeInfoPairingInitDataLen == TestPairingInitDataLength, err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+        res = memcmp(msg.AuthorizeInfoPairingInitData, TestPairingInitData, TestPairingInitDataLength);
+        VerifyOrExit(res == 0, err = WEAVE_ERROR_INVALID_ARGUMENT);
+    }
+
+    err = ValidateWeaveDeviceCert(msg.OperationalCertSet);
+    SuccessOrExit(err);
+
+    if (msg.MfrAttestRequired())
+    {
+        VerifyOrExit(msg.MfrAttestPresent(), err = WEAVE_ERROR_INVALID_ARGUMENT);
+    }
+
+    // Validate Manufacturer Attestation Information if present.
+    if (msg.MfrAttestPresent())
+    {
+        if (msg.MfrAttestType() == kMfrAttestType_WeaveCert)
+        {
+            err = ValidateWeaveDeviceCert(msg.MfrAttestWeaveCertSet);
+            SuccessOrExit(err);
+        }
+        else if (msg.MfrAttestType() == kMfrAttestType_X509Cert)
+        {
+            // Add Trusted X509 Root Certificate.
+            msg.MfrAttestX509CertSet[msg.MfrAttestX509CertCount].Cert = TestDevice_X509_RSA_RootCert;
+            msg.MfrAttestX509CertSet[msg.MfrAttestX509CertCount++].Len = TestDevice_X509_RSA_RootCertLength;
+
+            err = ValidateX509DeviceCert(msg.MfrAttestX509CertSet, msg.MfrAttestX509CertCount);
+            SuccessOrExit(err);
+        }
+        else if (msg.MfrAttestType() == kMfrAttestType_HMAC)
+        {
+            VerifyOrExit(msg.MfrAttestHMACKeyId == TestDevice1_MfrAttest_HMACKeyId, err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+            if (msg.MfrAttestHMACMetaData != NULL)
+            {
+                int res;
+
+                VerifyOrExit(msg.MfrAttestHMACMetaDataLen == TestDevice1_MfrAttest_HMACMetaDataLength, err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+                res = memcmp(msg.MfrAttestHMACMetaData, TestDevice1_MfrAttest_HMACMetaData, TestDevice1_MfrAttest_HMACMetaDataLength);
+                VerifyOrExit(res == 0, err = WEAVE_ERROR_INVALID_ARGUMENT);
+            }
+        }
+        else
+        {
+            ExitNow(WEAVE_ERROR_INVALID_ARGUMENT);
+        }
+    }
+
+    err = msg.GenerateTBSHash(tbsHash);
+    SuccessOrExit(err);
+
+    // Only ECDSAWithSHA256 algorithm is allowed for operational signature.
+    VerifyOrExit(msg.OperationalSigAlgo == kOID_SigAlgo_ECDSAWithSHA256, err = WEAVE_ERROR_INVALID_ARGUMENT);
+
+    // Verify operational signature.
+    err = VerifyECDSASignature(WeaveCurveIdToOID(msg.OperationalCertSet.Certs->PubKeyCurveId),
+                               tbsHash, SHA256::kHashLength, msg.OperationalSig, msg.OperationalCertSet.Certs->PublicKey.EC);
+    SuccessOrExit(err);
+
+    // Verify Manufacturer Attestation Signature if present.
+    if (msg.MfrAttestPresent())
+    {
+        if (msg.MfrAttestSigAlgo == kOID_SigAlgo_ECDSAWithSHA256)
+        {
+            err = VerifyECDSASignature(WeaveCurveIdToOID(msg.MfrAttestWeaveCertSet.Certs->PubKeyCurveId),
+                                       tbsHash, SHA256::kHashLength, msg.MfrAttestSig.EC, msg.MfrAttestWeaveCertSet.Certs->PublicKey.EC);
+            SuccessOrExit(err);
+        }
+        else if (msg.MfrAttestSigAlgo == kOID_SigAlgo_SHA256WithRSAEncryption)
+        {
+#if WEAVE_WITH_OPENSSL
+            err = VerifyRSASignature(kOID_SigAlgo_SHA256WithRSAEncryption, tbsHash, SHA256::kHashLength, msg.MfrAttestSig.RSA,
+                                     msg.MfrAttestX509CertSet[0].Cert, msg.MfrAttestX509CertSet[0].Len);
+            SuccessOrExit(err);
+#else
+            ExitNow(err = WEAVE_ERROR_NOT_IMPLEMENTED);
+#endif
+        }
+        else if (msg.MfrAttestSigAlgo == kOID_SigAlgo_HMACWithSHA256)
+        {
+            err = VerifyHMACSignature(kOID_SigAlgo_HMACWithSHA256, msg.mTBSDataStart, msg.mTBSDataLen, msg.MfrAttestSig.HMAC,
+                                      TestDevice1_MfrAttest_HMACKey, TestDevice1_MfrAttest_HMACKeyLength);
+            SuccessOrExit(err);
+        }
+        else
+        {
+            ExitNow(err = WEAVE_ERROR_UNSUPPORTED_SIGNATURE_TYPE);
+        }
+    }
+
+exit:
+    return err;
+}
+
+WEAVE_ERROR MockCAService::GenerateGetCertificateResponse(PacketBuffer * msgBuf, WeaveCertificateData & receivedDeviceCertData)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+    TLVWriter writer;
+    TLVType containerType;
+    uint8_t cert[nl::TestCerts::kTestCertBufSize];
+    uint16_t certLen;
+
+    err = GenerateTestDeviceCert(receivedDeviceCertData.SubjectDN.AttrValue.WeaveId, receivedDeviceCertData.PublicKey.EC,
+                                 mCACert, mCACertLen,
+                                 mCAPrivateKey, mCAPrivateKeyLen,
+                                 cert, sizeof(cert), certLen);
+    SuccessOrExit(err);
+
+    writer.Init(msgBuf);
+
+    err = writer.StartContainer(AnonymousTag, kTLVType_Structure, containerType);
+    SuccessOrExit(err);
+
+    err = writer.CopyContainer(ContextTag(kTag_GetCertRespMsg_OpDeviceCert), cert, certLen);
+    SuccessOrExit(err);
+
+    if (mIncludeRelatedCerts)
+    {
+        TLVType containerType2;
+
+        // Start the RelatedCertificates array. This contains the list of certificates the signature verifier
+        // will need to verify the signature.
+        err = writer.StartContainer(ContextTag(kTag_GetCertRespMsg_OpRelatedCerts), kTLVType_Array, containerType2);
+        SuccessOrExit(err);
+
+        // Copy the intermediate test device CA certificate.
+        err = writer.CopyContainer(AnonymousTag, mCACert, mCACertLen);
+        SuccessOrExit(err);
+
+        err = writer.EndContainer(containerType2);
+        SuccessOrExit(err);
+    }
+
+    err = writer.EndContainer(containerType);
+    SuccessOrExit(err);
+
+    err = writer.Finalize();
+    SuccessOrExit(err);
+
+exit:
+    return err;
+}
diff --git a/src/test-apps/MockCAService.h b/src/test-apps/MockCAService.h
new file mode 100644
index 0000000..c1f2614
--- /dev/null
+++ b/src/test-apps/MockCAService.h
@@ -0,0 +1,143 @@
+/*
+ *
+ *    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
+ *      This file defines a derived unsolicited responder
+ *      (i.e., server) for the Certificate Provisioned protocol of the
+ *      Weave Security profile used for the Weave mock
+ *      device command line functional testing tool.
+ *
+ */
+
+#ifndef MOCKCASERVICE_H_
+#define MOCKCASERVICE_H_
+
+#include "CertProvOptions.h"
+#include <Weave/Core/WeaveCore.h>
+#include <Weave/Profiles/security/WeaveCertProvisioning.h>
+
+using ::nl::Weave::System::PacketBuffer;
+
+enum {
+    kMaxCertCount = 4,
+};
+
+struct X509Cert
+{
+    const uint8_t *Cert;
+    uint16_t Len;
+};
+
+class GetCertificateRequestMessage
+{
+public:
+    GetCertificateRequestMessage();
+
+    WeaveCertificateSet OperationalCertSet;
+
+    const uint8_t *mTBSDataStart;
+    uint16_t mTBSDataLen;
+
+    const uint8_t *AuthorizeInfoPairingToken;
+    uint16_t AuthorizeInfoPairingTokenLen;
+    const uint8_t *AuthorizeInfoPairingInitData;
+    uint16_t AuthorizeInfoPairingInitDataLen;
+
+    WeaveCertificateSet MfrAttestWeaveCertSet;
+    X509Cert MfrAttestX509CertSet[kMaxCertCount];
+    uint8_t MfrAttestX509CertCount;
+    uint32_t MfrAttestHMACKeyId;
+    const uint8_t * MfrAttestHMACMetaData;
+    uint16_t MfrAttestHMACMetaDataLen;
+
+    OID OperationalSigAlgo;
+    EncodedECDSASignature OperationalSig;
+
+    OID MfrAttestSigAlgo;
+    union
+    {
+        EncodedECDSASignature EC;
+        EncodedRSASignature RSA;
+        EncodedHMACSignature HMAC;
+    } MfrAttestSig;
+
+    uint8_t RequestType() const { return mReqType; }
+    GetCertificateRequestMessage& RequestType (uint8_t val) { mReqType = val; return *this; }
+
+    uint8_t MfrAttestType() const { return mMfrAttestType; }
+    GetCertificateRequestMessage& MfrAttestType (uint8_t val) { mMfrAttestType = val; return *this; }
+
+    bool AuthorizeInfoPresent() const { return (AuthorizeInfoPairingToken != NULL); }
+    bool MfrAttestPresent() const { return (mMfrAttestType != kMfrAttestType_Undefined); }
+    bool MfrAttestRequired() const { return (mReqType == nl::Weave::Profiles::Security::CertProvisioning::WeaveCertProvEngine::kReqType_GetInitialOpDeviceCert); }
+
+    WEAVE_ERROR Decode(PacketBuffer *msgBuf);
+    WEAVE_ERROR GenerateTBSHash(uint8_t *tbsHash);
+
+private:
+    uint8_t mReqType;
+    uint8_t mMfrAttestType;
+
+    bool mOperationalCertSetInitialized;
+    bool mMfrAttestCertSetInitialized;
+};
+
+class MockCAService
+{
+public:
+    MockCAService();
+
+    WEAVE_ERROR Init(nl::Weave::WeaveExchangeManager *exchangeMgr);
+    WEAVE_ERROR Shutdown();
+
+    WEAVE_ERROR ProcessGetCertificateRequest(PacketBuffer *msgBuf, GetCertificateRequestMessage & msg);
+    WEAVE_ERROR GenerateGetCertificateResponse(PacketBuffer * msgBuf, WeaveCertificateData & receivedDeviceCertData);
+
+    bool LogMessageData() const { return mLogMessageData; }
+    MockCAService& LogMessageData(bool val) { mLogMessageData = val; return *this; }
+
+    bool IncludeRelatedCerts() const { return mIncludeRelatedCerts; }
+    MockCAService& IncludeRelatedCerts(bool val) { mIncludeRelatedCerts = val; return *this; }
+
+    bool DoNotRotateCert() const { return mDoNotRotateCert; }
+    MockCAService& DoNotRotateCert(bool val) { mDoNotRotateCert = val; return *this; }
+
+    void SetCACert(const uint8_t * cert, uint16_t certLen) { mCACert = cert; mCACertLen = certLen; }
+    void SetCAPrivateKey(const uint8_t * privateKey, uint16_t privateKeyLen) { mCAPrivateKey = privateKey; mCAPrivateKeyLen = privateKeyLen; }
+
+private:
+    nl::Weave::WeaveExchangeManager *mExchangeMgr;
+    bool mLogMessageData;
+    bool mIncludeRelatedCerts;
+    bool mDoNotRotateCert;
+
+    const uint8_t * mCACert;
+    uint16_t mCACertLen;
+
+    const uint8_t * mCAPrivateKey;
+    uint16_t mCAPrivateKeyLen;
+
+    WEAVE_ERROR SendStatusReport(nl::Weave::ExchangeContext *ec, uint16_t statusCode);
+
+    static void HandleClientRequest(nl::Weave::ExchangeContext *ec, const nl::Inet::IPPacketInfo *addrInfo,
+                                    const nl::Weave::WeaveMessageInfo *msgInfo, uint32_t profileId,
+                                    uint8_t msgType, PacketBuffer *payload);
+};
+
+#endif /* MOCKCASERVICE_H_ */
diff --git a/src/test-apps/TestCertProv.cpp b/src/test-apps/TestCertProv.cpp
new file mode 100644
index 0000000..bf86f48
--- /dev/null
+++ b/src/test-apps/TestCertProv.cpp
@@ -0,0 +1,795 @@
+/*
+ *
+ *    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
+ *      Unit tests for the WeaveCertProvClient class.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "ToolCommon.h"
+#include "CertProvOptions.h"
+#include "MockCAService.h"
+#include <Weave/Support/ErrorStr.h>
+#include <Weave/Core/WeaveTLV.h>
+#include <Weave/Profiles/security/WeaveSecurity.h>
+#include <Weave/Profiles/security/WeaveCertProvisioning.h>
+#include <Weave/Profiles/security/WeavePrivateKey.h>
+#include <Weave/Profiles/security/WeaveSig.h>
+#include <Weave/Support/crypto/EllipticCurve.h>
+#include <Weave/Support/NestCerts.h>
+#include <Weave/Support/RandUtils.h>
+
+#if WEAVE_WITH_OPENSSL
+#include <openssl/rsa.h>
+#include <openssl/pem.h>
+#endif
+
+#if WEAVE_SYSTEM_CONFIG_USE_LWIP
+#include "lwip/tcpip.h"
+#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
+
+using namespace nl::Weave::TLV;
+using namespace nl::Weave::Profiles::Security;
+using namespace nl::Weave::Profiles::Security::CertProvisioning;
+using namespace nl::Weave::ASN1;
+
+using nl::Weave::Crypto::EncodedECPublicKey;
+using nl::Weave::Crypto::EncodedECPrivateKey;
+using nl::Weave::Profiles::Security::CertProvisioning::WeaveCertProvEngine;
+
+#define DEBUG_PRINT_ENABLE 0
+
+uint32_t debugPrintCount = 0;
+
+#define TOOL_NAME "TestCertProv"
+
+static bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg);
+
+const char *gCurTest = NULL;
+
+#define VerifyOrQuit(TST, MSG) \
+do { \
+    if (!(TST)) \
+    { \
+        fprintf(stdout, "%s FAILED: ", (gCurTest != NULL) ? gCurTest : __FUNCTION__); \
+        fputs(MSG, stdout); \
+        fputs("\n", stdout); \
+        exit(-1); \
+    } \
+} while (0)
+
+#define SuccessOrQuit(ERR, MSG) \
+do { \
+    if ((ERR) != WEAVE_NO_ERROR) \
+    { \
+        fprintf(stdout, "%s FAILED: ", (gCurTest != NULL) ? gCurTest : __FUNCTION__); \
+        fputs(MSG, stdout); \
+        fputs(": ", stdout); \
+        fputs(ErrorStr(ERR), stdout); \
+        fputs("\n", stdout); \
+        exit(-1); \
+    } \
+} while (0)
+
+extern WEAVE_ERROR MakeCertInfo(uint8_t *buf, uint16_t bufSize, uint16_t& certInfoLen,
+                                const uint8_t *entityCert, uint16_t entityCertLen,
+                                const uint8_t *intermediateCert, uint16_t intermediateCertLen);
+
+class MessageMutator
+{
+public:
+    virtual ~MessageMutator() { }
+    virtual void Reset() = 0;
+    virtual void MutateMessage(const char *msgType, PacketBuffer *msgBuf, WeaveCertProvEngine& clientEng, MockCAService& serviceEng) = 0;
+    virtual bool IsComplete() = 0;
+};
+
+class NullMutator : public MessageMutator
+{
+public:
+    virtual void Reset() { }
+    virtual void MutateMessage(const char *msgType, PacketBuffer *msgBuf, WeaveCertProvEngine& clientEng, MockCAService& serviceEng) { };
+    virtual bool IsComplete() { return true; }
+};
+
+static NullMutator gNullMutator;
+
+class MessageFuzzer : public MessageMutator
+{
+public:
+    MessageFuzzer(const char *msgType)
+    {
+        mMsgType = msgType;
+        mIndex = 0;
+        mSkipStart = 0;
+        mSkipLen = 0;
+        mComplete = false;
+        mTimeLimit = 0;
+    }
+
+    virtual void Reset() { mIndex = 0; mComplete = false; }
+
+    virtual void MutateMessage(const char *msgType, PacketBuffer *msgBuf, WeaveCertProvEngine& clientEng, MockCAService& serviceEng)
+    {
+        if (strcmp(msgType, mMsgType) == 0)
+        {
+            uint8_t *msgStart = msgBuf->Start();
+            uint16_t msgLen = msgBuf->DataLength();
+            uint8_t fuzzMask;
+
+            VerifyOrQuit(msgLen > 0, "Unexpected packet length");
+
+            if (mIndex == mSkipStart)
+                mIndex += mSkipLen;
+
+            if (mIndex >= msgLen)
+                mIndex = msgLen - 1;
+
+            do
+                fuzzMask = GetRandU8();
+            while (fuzzMask == 0 ||
+                   // Make sure the EndOfContainer element modifies its Type field - otherwize it might still be interpreted as EndOfContainer element.
+                   ((msgStart[mIndex] == kTLVElementType_EndOfContainer) && ((fuzzMask & kTLVTypeMask) == 0)));
+
+            printf("MessageFuzzer: %s message mutated (offset %u, fuzz mask 0x%02X, orig value 0x%02X)\n", msgType, mIndex, fuzzMask, msgStart[mIndex]);
+
+            msgStart[mIndex] ^= fuzzMask;
+
+            mIndex++;
+
+            mComplete = (mIndex >= msgLen);
+        }
+    }
+
+    virtual bool IsComplete()
+    {
+        if (mComplete)
+            return true;
+
+        if (mTimeLimit != 0)
+        {
+            time_t now;
+            time(&now);
+            if (now >= mTimeLimit)
+                return true;
+        }
+
+        return false;
+    }
+
+    MessageFuzzer& Skip(uint16_t start, uint16_t len) { mSkipStart = start; mSkipLen = len; return *this; }
+
+    MessageFuzzer& TimeLimit(time_t timeLimit) { mTimeLimit = timeLimit; return *this; }
+
+private:
+    const char *mMsgType;
+    uint16_t mIndex;
+    uint16_t mSkipStart;
+    uint16_t mSkipLen;
+    bool mComplete;
+    time_t mTimeLimit;
+};
+
+class CertProvEngineTest
+{
+public:
+    CertProvEngineTest(const char *testName)
+    {
+        mTestName = testName;
+        memset(mExpectedErrors, 0, sizeof(mExpectedErrors));
+        mMutator = &gNullMutator;
+        mMfrAttestType = kMfrAttestType_WeaveCert;
+        mReqType = WeaveCertProvEngine::kReqType_GetInitialOpDeviceCert;
+        mLogMessageData = false;
+        mClientIncludeAuthorizeInfo = false;
+        mClientIncludeOperationalRelatedCerts = false;
+        mClientIncludeMfrAttestInfo = true;
+        mClientIncludeMfrAttestRelatedCerts = false;
+        mServerIncludeDeviceCACert = false;
+    }
+
+    const char *TestName() const { return mTestName; }
+
+    uint8_t RequestType() const { return mReqType; }
+    CertProvEngineTest& RequestType (uint8_t val) { mReqType = val; return *this; }
+
+    uint8_t MfrAttestType() const { return mMfrAttestType; }
+    CertProvEngineTest& MfrAttestType (uint8_t val) { mMfrAttestType = val; return *this; }
+
+    bool LogMessageData() const { return mLogMessageData; }
+    CertProvEngineTest& LogMessageData(bool val) { mLogMessageData = val; return *this; }
+
+    bool ClientIncludeAuthorizeInfo() const { return mClientIncludeAuthorizeInfo; }
+    CertProvEngineTest& ClientIncludeAuthorizeInfo(bool val) { mClientIncludeAuthorizeInfo = val; return *this; }
+
+    bool ClientIncludeOperationalRelatedCerts() const { return mClientIncludeOperationalRelatedCerts; }
+    CertProvEngineTest& ClientIncludeOperationalRelatedCerts(bool val) { mClientIncludeOperationalRelatedCerts = val; return *this; }
+
+    bool ClientIncludeMfrAttestInfo() const { return mClientIncludeMfrAttestInfo; }
+    CertProvEngineTest& ClientIncludeMfrAttestInfo(bool val) { mClientIncludeMfrAttestInfo = val; return *this; }
+
+    bool ClientIncludeMfrAttestRelatedCerts() const { return mClientIncludeMfrAttestRelatedCerts; }
+    CertProvEngineTest& ClientIncludeMfrAttestRelatedCerts(bool val) { mClientIncludeMfrAttestRelatedCerts = val; return *this; }
+
+    bool ServerIncludeRelatedCerts() const { return mServerIncludeDeviceCACert; }
+    CertProvEngineTest& ServerIncludeRelatedCerts(bool val) { mServerIncludeDeviceCACert = val; return *this; }
+
+    CertProvEngineTest& ExpectError(WEAVE_ERROR err)
+    {
+        return ExpectError(NULL, err);
+    }
+
+    CertProvEngineTest& ExpectError(const char *opName, WEAVE_ERROR err)
+    {
+        for (size_t i = 0; i < kMaxExpectedErrors; i++)
+        {
+            if (mExpectedErrors[i].Error == WEAVE_NO_ERROR)
+            {
+                mExpectedErrors[i].Error = err;
+                mExpectedErrors[i].OpName = opName;
+                break;
+            }
+        }
+
+        return *this;
+    }
+
+    bool IsExpectedError(const char *opName, WEAVE_ERROR err) const
+    {
+        for (size_t i = 0; i < kMaxExpectedErrors && mExpectedErrors[i].Error != WEAVE_NO_ERROR; i++)
+        {
+            if (mExpectedErrors[i].Error == err &&
+                (mExpectedErrors[i].OpName == NULL || strcmp(mExpectedErrors[i].OpName, opName) == 0))
+                return true;
+        }
+        return false;
+    }
+
+    bool IsSuccessExpected() const { return mExpectedErrors[0].Error == WEAVE_NO_ERROR; }
+
+    CertProvEngineTest& Mutator(MessageMutator *mutator) { mMutator = mutator; return *this; }
+
+    void Run() const;
+
+private:
+    enum
+    {
+        kMaxExpectedErrors = 32
+    };
+
+    struct ExpectedError
+    {
+        const char *OpName;
+        WEAVE_ERROR Error;
+    };
+
+    const char *mTestName;
+    uint8_t mReqType;
+    uint8_t mMfrAttestType;
+    bool mLogMessageData;
+    bool mClientIncludeAuthorizeInfo;
+    bool mClientIncludeOperationalRelatedCerts;
+    bool mClientIncludeMfrAttestInfo;
+    bool mClientIncludeMfrAttestRelatedCerts;
+    bool mServerIncludeDeviceCACert;
+    ExpectedError mExpectedErrors[kMaxExpectedErrors];
+    MessageMutator *mMutator;
+
+    void PrintGetCertificateRequestMessage(PacketBuffer *msgBuf) const;
+};
+
+void CertProvEngineTest::PrintGetCertificateRequestMessage(PacketBuffer *msgBuf) const
+{
+    debugPrintCount++;
+    printf("// ------------------- GET CERTIFICATE REQUEST MESSAGE EXAMPLE %02d --------------------------\n", debugPrintCount);
+    printf("// Operational Device Id                 : 0x%" PRIX64 "\n", gCertProvOptions.DeviceId);
+    printf("// GetCertReqMsg_ReqType                 : %s\n", (RequestType() == WeaveCertProvEngine::kReqType_GetInitialOpDeviceCert) ?
+           "Get Initial Operational Device Certificate" : "Rotate Operational Device Certificate");
+    printf("// GetCertAuthorizeInfo                  : %s\n", ClientIncludeAuthorizeInfo() ? "Yes" : "-----");
+    printf("// GetCertReqMsg_OpDeviceCert            : %s\n", (RequestType() == WeaveCertProvEngine::kReqType_GetInitialOpDeviceCert) ?
+           "TestDevice1_OperationalSelfSignedCert" : "TestDevice1_OperationalServiceAssignedCert");
+    printf("// GetCertReqMsg_OpRelatedCerts          : %s\n", ClientIncludeOperationalRelatedCerts() ? "nl::NestCerts::Development::DeviceCA::Cert" : "-----");
+    if (ClientIncludeMfrAttestInfo())
+    {
+        if ((MfrAttestType() == kMfrAttestType_WeaveCert))
+        {
+            printf("// GetCertReqMsg_MfrAttest_WeaveCert     : %s\n", "TestDevice1_Cert");
+            printf("// GetCertReqMsg_MfrAttest_WeaveRelCerts : %s\n", ClientIncludeMfrAttestRelatedCerts() ? "nl::NestCerts::Development::DeviceCA::Cert" : "-----");
+        }
+        else if ((MfrAttestType() == kMfrAttestType_X509Cert))
+        {
+            printf("// GetCertReqMsg_MfrAttest_X509Cert      : %s\n", "TestDevice1_X509_RSA_Cert");
+            printf("// GetCertReqMsg_MfrAttest_X509RelCerts  : %s\n", ClientIncludeMfrAttestRelatedCerts() ? "TestDevice1_X509_RSA_ICACert1 (_ICACert2)" : "-----");
+        }
+        else if ((MfrAttestType() == kMfrAttestType_HMAC))
+        {
+            printf("// GetCertReqMsg_MfrAttest_HMACKeyId     : 0x%" PRIX32 "\n", TestDevice1_MfrAttest_HMACKeyId);
+        }
+    }
+    else
+    {
+        printf("// GetCertReqMsg_MfrAttestInfo           : -----\n");
+    }
+    printf("// GetCertReqMsg_OpDeviceSigAlgo         : ECDSAWithSHA256\n");
+    printf("// GetCertReqMsg_OpDeviceSig_ECDSA       : ECDSASignature\n");
+    if (ClientIncludeMfrAttestInfo())
+    {
+        if ((MfrAttestType() == kMfrAttestType_WeaveCert))
+        {
+            printf("// GetCertReqMsg_MfrAttestSigAlgo        : ECDSAWithSHA256\n");
+            printf("// GetCertReqMsg_MfrAttestSig_ECDSA      : ECDSASignature\n");
+        }
+        else if ((MfrAttestType() == kMfrAttestType_X509Cert))
+        {
+            printf("// GetCertReqMsg_MfrAttestSigAlgo        : SHA256WithRSAEncryption\n");
+            printf("// GetCertReqMsg_MfrAttestSig_RSA        : RSASignature\n");
+        }
+        else if ((MfrAttestType() == kMfrAttestType_HMAC))
+        {
+            printf("// GetCertReqMsg_MfrAttestSigAlgo        : HMACWithSHA256\n");
+            printf("// GetCertReqMsg_MfrAttestSig_HMAC       : HMACSignature\n");
+        }
+    }
+    else
+    {
+        printf("// GetCertReqMsg_MfrAttestSig            : -----\n");
+    }
+    printf("// EXPECTED RESULT                       : %s\n", IsSuccessExpected() ? "SUCCESS" : "ERROR");
+    printf("// -----------------------------------------------------------------------------------------\n");
+
+    uint8_t * data = msgBuf->Start();
+    uint16_t dataLen = msgBuf->DataLength();
+
+    printf("\nextern const uint8_t sGetCertRequestMsg_Example%02d[] =\n{", debugPrintCount);
+
+    for (uint32_t i = 0; i < dataLen; i++)
+    {
+        if (i % 16 == 0)
+            printf("\n    ");
+        printf("0x%02X, ", data[i]);
+    }
+
+    printf("\n};\n\n");
+}
+
+void CertProvEngineTest::Run() const
+{
+    WEAVE_ERROR err;
+    WeaveCertProvEngine clientEng;
+    MockCAService serviceEng;
+    PacketBuffer *msgBuf = NULL;
+    PacketBuffer *msgBuf2 = NULL;
+    WeaveExchangeManager exchangeMgr;
+
+    printf("========== Starting Test: %s\n", TestName());
+    printf("    Manufacturer Attestation Type             : %s\n", (MfrAttestType() == kMfrAttestType_WeaveCert) ? "Weave Certificate" : "X509 Certificate");
+    printf("    Request Type                              : %s\n", (RequestType() == WeaveCertProvEngine::kReqType_GetInitialOpDeviceCert) ? "GetInitialOpDeviceCert" : "RotateCert");
+    printf("    Client Include Authorization Info         : %s\n", ClientIncludeAuthorizeInfo() ? "Yes" : "No");
+    printf("    Client Include Op Related Certs           : %s\n", ClientIncludeOperationalRelatedCerts() ? "Yes" : "No");
+    printf("    Client Include Manufacturer Attest Info   : %s\n", ClientIncludeMfrAttestInfo() ? "Yes" : "No");
+    printf("    Client Include Manuf Attest Related Certs : %s\n", ClientIncludeMfrAttestRelatedCerts() ? "Yes" : "No");
+    printf("    Server Include Op Related Certs           : %s\n", ServerIncludeRelatedCerts() ? "Yes" : "No");
+    printf("    Expected Error                            : %s\n", IsSuccessExpected() ? "No" : "Yes");
+    printf("==========\n");
+
+    gCurTest = TestName();
+
+    mMutator->Reset();
+
+    gCertProvOptions.MfrAttestDeviceId = TestDevice1_NodeId;
+    gCertProvOptions.RequestType = RequestType();
+    gCertProvOptions.IncludeAuthorizeInfo = ClientIncludeAuthorizeInfo();
+    gCertProvOptions.IncludeOperationalICACerts = ClientIncludeOperationalRelatedCerts();
+    gCertProvOptions.MfrAttestType = MfrAttestType();
+    gCertProvOptions.IncludeMfrAttestICACerts = ClientIncludeMfrAttestRelatedCerts();
+
+    gDeviceCredsStore.GenerateAndStoreDeviceCredentials();
+    if (RequestType() == WeaveCertProvEngine::kReqType_RotateOpDeviceCert)
+        gDeviceCredsStore.GenerateAndReplaceCurrentDeviceCert();
+
+    err = gDeviceCredsStore.GetDeviceId(gCertProvOptions.DeviceId);
+    SuccessOrQuit(err, "gDeviceCredsStore.GetDeviceId() failed");
+
+    do
+    {
+        clientEng.Init(NULL, &gCertProvOptions, &gCertProvOptions, gCertProvOptions.CertProvClientEventHandler, &gCertProvOptions);
+        serviceEng.Init(&exchangeMgr);
+        serviceEng.LogMessageData(LogMessageData());
+        serviceEng.IncludeRelatedCerts(ServerIncludeRelatedCerts());
+
+        // ========== Client Forms GetCertificateRequest ==========
+
+        {
+            msgBuf = PacketBuffer::New();
+            VerifyOrQuit(msgBuf != NULL, "PacketBuffer::New() failed");
+            printf("\nCalling WeaveCertProvEngine::GenerateGetCertificateRequest\n");
+
+            err = clientEng.GenerateGetCertificateRequest(msgBuf, RequestType(), ClientIncludeMfrAttestInfo());
+
+#if DEBUG_PRINT_ENABLE
+            PrintGetCertificateRequestMessage(msgBuf);
+#endif
+
+            if (IsExpectedError("WeaveCertProvEngine::GenerateGetCertificateRequest", err))
+                goto onExpectedError;
+
+            SuccessOrQuit(err, "WeaveCertProvEngine::GenerateGetCertificateRequest() failed");
+        }
+
+        // ========== Client Sends GetCertificateRequest to the CA Service ==========
+
+        mMutator->MutateMessage("GetCertificateRequest", msgBuf, clientEng, serviceEng);
+
+        printf("Client->Service: GetCertificateRequest Message (%d bytes)\n", msgBuf->DataLength());
+        if (LogMessageData())
+            DumpMemory(msgBuf->Start(), msgBuf->DataLength(), "    ", 16);
+
+        // ========== CA Service Processes GetCertificateRequest ==========
+
+        {
+            printf("Service: Calling ProcessGetCertificateRequest\n");
+
+            GetCertificateRequestMessage msg;
+
+            err = serviceEng.ProcessGetCertificateRequest(msgBuf, msg);
+
+            if (IsExpectedError("Service:ProcessGetCertificateRequest", err))
+                goto onExpectedError;
+
+            SuccessOrQuit(err, "MockCAService::ProcessGetCertificateRequest() failed");
+
+            // ========== CA Service Forms GetCertificateResponse ==========
+
+            msgBuf2 = PacketBuffer::New();
+            VerifyOrQuit(msgBuf2 != NULL, "PacketBuffer::New() failed");
+
+            printf("Service: Calling GenerateGetCertificateResponse\n");
+
+            err = serviceEng.GenerateGetCertificateResponse(msgBuf2, *msg.OperationalCertSet.Certs);
+
+            PacketBuffer::Free(msgBuf);
+            msgBuf = NULL;
+
+            if (IsExpectedError("Service:GenerateGetCertificateResponse", err))
+                goto onExpectedError;
+
+            SuccessOrQuit(err, "MockCAService::GenerateGetCertificateResponse() failed");
+        }
+
+        // ========== CA Service Sends GetCertificateResponse to Client ==========
+
+        mMutator->MutateMessage("GetCertificateResponse", msgBuf2, clientEng, serviceEng);
+
+        printf("Service->Client: GetCertificateResponse Message (%d bytes)\n", msgBuf2->DataLength());
+        if (LogMessageData())
+            DumpMemory(msgBuf2->Start(), msgBuf2->DataLength(), "    ", 16);
+
+        // ========== Client Processes GetCertificateResponse ==========
+
+        {
+            printf("Client: Calling ProcessGetCertificateResponse\n");
+
+            err = clientEng.ProcessGetCertificateResponse(msgBuf2);
+
+            PacketBuffer::Free(msgBuf2);
+            msgBuf2 = NULL;
+
+            if (IsExpectedError("Client:ProcessGetCertificateResponse", err))
+                goto onExpectedError;
+
+            SuccessOrQuit(err, "CertProvisioningClient::ProcessGetCertificateResponse() failed");
+        }
+
+        VerifyOrQuit(clientEng.GetState() == WeaveCertProvEngine::kState_Idle, "Client not in Idle state");
+
+        VerifyOrQuit(IsSuccessExpected(), "Test succeeded unexpectedly");
+
+    onExpectedError:
+
+        if (msgBuf != NULL)
+        {
+            PacketBuffer::Free(msgBuf);
+            msgBuf = NULL;
+        }
+
+        if (msgBuf2 != NULL)
+        {
+            PacketBuffer::Free(msgBuf2);
+            msgBuf2 = NULL;
+        }
+
+        clientEng.Shutdown();
+        serviceEng.Shutdown();
+
+    } while (!mMutator->IsComplete());
+
+    printf("Test Complete: %s\n\n", TestName());
+
+    gCurTest = NULL;
+}
+
+void CertProvEngineTests_GetInitialCertTests()
+{
+    bool logData = false;
+
+    struct TestCase
+    {
+        uint8_t maType;             // Manufacturer Attestation Type.
+        uint8_t reqType;            // Request Type.
+        bool cIncludeAI;            // Client Includes Request Authorization Information.
+        bool cIncludeOpRC;          // Client Includes Operational Device Related Certificates.
+        bool cIncludeMA;            // Client Includes Manufacturer Attestation Information.
+        bool cIncludeMARC;          // Client Includes Manufacturer Attestation Related Certificates.
+        bool sIncludeSOpRC;         // Server Includes Operational Device Related Certificates.
+        struct
+        {
+            WEAVE_ERROR err;        // Expected error.
+            const char * opName;    // Function name.
+        } ExpectedResult;
+    };
+
+    enum
+    {
+        // Short-hand names to make the test cases table more concise.
+        WeaveCert             = kMfrAttestType_WeaveCert,
+        X509Cert              = kMfrAttestType_X509Cert,
+        HMAC                  = kMfrAttestType_HMAC,
+        InitReq               = WeaveCertProvEngine::kReqType_GetInitialOpDeviceCert,
+        RotateReq             = WeaveCertProvEngine::kReqType_RotateOpDeviceCert,
+    };
+
+    static const TestCase sTestCases[] =
+    {
+        // Manuf                 Client     Client     Client     Client     Server
+        // Attest     Req        Includes   Includes   Includes   Includes   Includes
+        // Type       Type       AuthInfo   OpRCerts   ManufAtt   MARCerts   OpRCerts    Expected Result
+        // ==============================================================================================================
+
+        // Basic testing of certificate provisioning protocol with different load orders.
+        {  WeaveCert, InitReq,   false,     false,     false,     false,     false,      { WEAVE_ERROR_INVALID_ARGUMENT, "Service:ProcessGetCertificateRequest" } },
+        {  WeaveCert, InitReq,   false,     false,     true,      false,     false,      { WEAVE_NO_ERROR, NULL } },
+        {  WeaveCert, InitReq,   false,     false,     true,      true,      true,       { WEAVE_NO_ERROR, NULL } },
+        {  WeaveCert, InitReq,   false,     true,      true,      true,      false,      { WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT, "Service:ProcessGetCertificateRequest" } },
+
+        {  WeaveCert, InitReq,   true,      false,     false,     false,     false,      { WEAVE_ERROR_INVALID_ARGUMENT, "Service:ProcessGetCertificateRequest" } },
+        {  WeaveCert, InitReq,   true,      false,     true,      false,     false,      { WEAVE_NO_ERROR, NULL } },
+        {  WeaveCert, InitReq,   true,      false,     true,      true,      true,       { WEAVE_NO_ERROR, NULL } },
+        {  WeaveCert, InitReq,   true,      true,      true,      true,      false,      { WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT, "Service:ProcessGetCertificateRequest" } },
+
+        {  WeaveCert, RotateReq, false,     false,     false,     false,     false,      { WEAVE_NO_ERROR, NULL } },
+        {  WeaveCert, RotateReq, false,     false,     true,      false,     false,      { WEAVE_NO_ERROR, NULL } },
+        {  WeaveCert, RotateReq, false,     false,     true,      true,      true,       { WEAVE_NO_ERROR, NULL } },
+        {  WeaveCert, RotateReq, false,     true,      true,      true,      false,      { WEAVE_NO_ERROR, NULL } },
+
+        {  WeaveCert, RotateReq, true,      false,     false,     false,     false,      { WEAVE_NO_ERROR, NULL } },
+        {  WeaveCert, RotateReq, true,      false,     true,      false,     false,      { WEAVE_NO_ERROR, NULL } },
+        {  WeaveCert, RotateReq, true,      false,     true,      true,      true,       { WEAVE_NO_ERROR, NULL } },
+        {  WeaveCert, RotateReq, true,      true,      true,      true,      false,      { WEAVE_NO_ERROR, NULL } },
+
+#if WEAVE_SYSTEM_CONFIG_PACKETBUFFER_CAPACITY_MAX > 4400
+        {  X509Cert,  InitReq,   false,     false,     true,      false,     false,      { WEAVE_ERROR_INVALID_SIGNATURE, "Service:ProcessGetCertificateRequest" } },
+        {  X509Cert,  InitReq,   false,     false,     true,      true,      true,       { WEAVE_NO_ERROR, NULL } },
+        {  X509Cert,  InitReq,   false,     true,      true,      true,      false,      { WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT, "Service:ProcessGetCertificateRequest" } },
+
+        {  X509Cert,  InitReq,   true,      false,     true,      false,     false,      { WEAVE_ERROR_INVALID_SIGNATURE, "Service:ProcessGetCertificateRequest" } },
+        {  X509Cert,  InitReq,   true,      false,     true,      true,      true,       { WEAVE_NO_ERROR, NULL } },
+        {  X509Cert,  InitReq,   true,      true,      true,      true,      false,      { WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT, "Service:ProcessGetCertificateRequest" } },
+
+        {  X509Cert,  RotateReq, false,     false,     true,      false,     false,      { WEAVE_ERROR_INVALID_SIGNATURE, "Service:ProcessGetCertificateRequest" } },
+        {  X509Cert,  RotateReq, false,     false,     true,      true,      true,       { WEAVE_NO_ERROR, NULL } },
+        {  X509Cert,  RotateReq, false,     true,      true,      true,      false,      { WEAVE_NO_ERROR, NULL } },
+
+        {  X509Cert,  RotateReq, true,      false,     true,      false,     false,      { WEAVE_ERROR_INVALID_SIGNATURE, "Service:ProcessGetCertificateRequest" } },
+        {  X509Cert,  RotateReq, true,      false,     true,      true,      true,       { WEAVE_NO_ERROR, NULL } },
+        {  X509Cert,  RotateReq, true,      true,      true,      true,      false,      { WEAVE_NO_ERROR, NULL } },
+#endif // WEAVE_SYSTEM_CONFIG_PACKETBUFFER_CAPACITY_MAX > 4000
+
+        {  HMAC,      InitReq,   false,     false,     true,      false,     false,      { WEAVE_NO_ERROR, NULL } },
+        {  HMAC,      InitReq,   false,     false,     true,      false,     false,      { WEAVE_NO_ERROR, NULL } },
+        {  HMAC,      InitReq,   true,      false,     true,      false,     true,       { WEAVE_NO_ERROR, NULL } },
+        {  HMAC,      InitReq,   true,      true,      true,      false,     true,       { WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT, "Service:ProcessGetCertificateRequest" } },
+
+        {  HMAC,      RotateReq, true,      false,     true,      false,     false,      { WEAVE_NO_ERROR, NULL } },
+        {  HMAC,      RotateReq, false,     true,      true,      false,     true,       { WEAVE_NO_ERROR, NULL } },
+    };
+
+    static const size_t sNumTestCases = sizeof(sTestCases) / sizeof(sTestCases[0]);
+
+    for (unsigned i = 0; i < sNumTestCases; i++)
+    {
+        const TestCase& testCase = sTestCases[i];
+
+        // Basic sanity test
+        CertProvEngineTest("Basic")
+            .MfrAttestType(testCase.maType)
+            .RequestType(testCase.reqType)
+            .ClientIncludeAuthorizeInfo(testCase.cIncludeAI)
+            .ClientIncludeOperationalRelatedCerts(testCase.cIncludeOpRC)
+            .ClientIncludeMfrAttestInfo(testCase.cIncludeMA)
+            .ClientIncludeMfrAttestRelatedCerts(testCase.cIncludeMARC)
+            .ServerIncludeRelatedCerts(testCase.sIncludeSOpRC)
+            .ExpectError(testCase.ExpectedResult.opName, testCase.ExpectedResult.err)
+            .LogMessageData(logData)
+            .Run();
+    }
+}
+
+uint32_t gFuzzTestDurationSecs = 5;
+
+void CertProvEngineTests_FuzzTests()
+{
+    time_t now, endTime;
+
+    time(&now);
+    endTime = now + gFuzzTestDurationSecs;
+
+    while (true)
+    {
+        time(&now);
+        if (now >= endTime)
+            break;
+
+        // Fuzz contents of GetCertificateRequest message, verify protocol error.
+        {
+            MessageFuzzer fuzzer = MessageFuzzer("GetCertificateRequest")
+                // .Skip(8, 8)
+                .TimeLimit(endTime);
+            CertProvEngineTest("Mutate GetCertificateRequest")
+                .Mutator(&fuzzer)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_WRONG_TLV_TYPE)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_INVALID_TLV_TAG)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_INVALID_TLV_ELEMENT)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_END_OF_TLV)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_TLV_UNDERRUN)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_UNKNOWN_IMPLICIT_TLV_TAG)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_UNSUPPORTED_SIGNATURE_TYPE)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_INVALID_SIGNATURE)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_INVALID_ARGUMENT)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_CA_CERT_NOT_FOUND)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_UNSUPPORTED_CERT_FORMAT)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_WRONG_CERT_SUBJECT)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_WRONG_CERT_TYPE)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_INCORRECT_STATE)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_CERT_NOT_VALID_YET)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_CERT_EXPIRED)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_CERT_USAGE_NOT_ALLOWED)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_WRONG_CERT_SIGNATURE_ALGORITHM)
+                .ExpectError("Service:ProcessGetCertificateRequest", ASN1_ERROR_UNKNOWN_OBJECT_ID)
+                .ExpectError("Service:ProcessGetCertificateRequest", ASN1_ERROR_OVERFLOW)
+                .ExpectError("Service:ProcessGetCertificateRequest", ASN1_ERROR_UNSUPPORTED_ENCODING)
+                .ExpectError("Service:ProcessGetCertificateRequest", WEAVE_ERROR_NOT_IMPLEMENTED)  // TODO: Remove once X509 RSA Certificates are Supported
+                .Run();
+        }
+
+        // Fuzz contents of GetCertificateResponse message, verify protocol error.
+        {
+            MessageFuzzer fuzzer = MessageFuzzer("GetCertificateResponse")
+                .TimeLimit(endTime);
+            CertProvEngineTest("Mutate GetCertificateResponse")
+                .Mutator(&fuzzer)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_WRONG_TLV_TYPE)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_UNEXPECTED_TLV_ELEMENT)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_INVALID_TLV_TAG)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_INVALID_TLV_ELEMENT)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_END_OF_TLV)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_TLV_UNDERRUN)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_UNKNOWN_IMPLICIT_TLV_TAG)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_UNSUPPORTED_ELLIPTIC_CURVE)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_UNSUPPORTED_SIGNATURE_TYPE)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_INVALID_SIGNATURE)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_INVALID_ARGUMENT)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_CA_CERT_NOT_FOUND)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_UNSUPPORTED_CERT_FORMAT)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_WRONG_CERT_SUBJECT)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_WRONG_CERT_TYPE)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_INCORRECT_STATE)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_CERT_NOT_VALID_YET)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_CERT_EXPIRED)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_CERT_USAGE_NOT_ALLOWED)
+                .ExpectError("Client:ProcessGetCertificateResponse", ASN1_ERROR_UNKNOWN_OBJECT_ID)
+                .ExpectError("Client:ProcessGetCertificateResponse", ASN1_ERROR_OVERFLOW)
+                .ExpectError("Client:ProcessGetCertificateResponse", ASN1_ERROR_UNSUPPORTED_ENCODING)
+                .ExpectError("Client:ProcessGetCertificateResponse", WEAVE_ERROR_NOT_IMPLEMENTED)  // TODO: Remove once X509 RSA Certificates are Supported
+                .LogMessageData(false)
+                .Run();
+        }
+    }
+
+}
+
+static OptionDef gToolOptionDefs[] =
+{
+    { "fuzz-duration", kArgumentRequired, 'f' },
+    { NULL }
+};
+
+static const char *const gToolOptionHelp =
+    "  -f, --fuzz-duration <seconds>\n"
+    "       Fuzzing duration in seconds.\n"
+    "\n";
+
+static OptionSet gToolOptions =
+{
+    HandleOption,
+    gToolOptionDefs,
+    "GENERAL OPTIONS",
+    gToolOptionHelp
+};
+
+static HelpOptions gHelpOptions(
+    TOOL_NAME,
+    "Usage: " TOOL_NAME " [<options...>]\n",
+    WEAVE_VERSION_STRING "\n" WEAVE_TOOL_COPYRIGHT,
+    "Unit tests for Weave CASE engine.\n"
+);
+
+static OptionSet *gToolOptionSets[] =
+{
+    &gToolOptions,
+    &gHelpOptions,
+    NULL
+};
+
+int main(int argc, char *argv[])
+{
+    WEAVE_ERROR err;
+
+#if WEAVE_SYSTEM_CONFIG_USE_LWIP
+    tcpip_init(NULL, NULL);
+#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
+
+    err = nl::Weave::Platform::Security::InitSecureRandomDataSource(NULL, 64, NULL, 0);
+    FAIL_ERROR(err, "InitSecureRandomDataSource() failed");
+
+    if (!ParseArgs(TOOL_NAME, argc, argv, gToolOptionSets))
+    {
+        exit(EXIT_FAILURE);
+    }
+
+    CertProvEngineTests_GetInitialCertTests();
+    CertProvEngineTests_FuzzTests();
+
+    printf("All tests succeeded\n");
+
+    exit(EXIT_SUCCESS);
+}
+
+static bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg)
+{
+    switch (id)
+    {
+    case 'f':
+        if (!ParseInt(arg, gFuzzTestDurationSecs))
+        {
+            PrintArgError("%s: Invalid value specified for fuzz duration: %s\n", progName, arg);
+            return false;
+        }
+        break;
+    default:
+        PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", progName, name);
+        return false;
+    }
+
+    return true;
+}
diff --git a/src/test-apps/TestWeaveCert.cpp b/src/test-apps/TestWeaveCert.cpp
index fc1dad1..d4a5059 100644
--- a/src/test-apps/TestWeaveCert.cpp
+++ b/src/test-apps/TestWeaveCert.cpp
@@ -804,65 +804,6 @@
     printf("%s passed\n", __FUNCTION__);
 }
 
-void WeaveCertTest_GenerateAndPrintTestOperationalDeviceCert()
-{
-    WEAVE_ERROR err;
-    uint64_t deviceId;
-    uint8_t certBuf[kTestCertBufSize];
-    uint16_t certLen;
-    uint8_t keyBuf[kTestCertBufSize];
-    uint32_t keyLen;
-    uint8_t devicePrivKeyBuf[EncodedECPrivateKey::kMaxValueLength];
-    uint8_t devicePubKeyBuf[EncodedECPublicKey::kMaxValueLength];
-    EncodedECPublicKey devicePubKey;
-
-    enum
-    {
-        kTestDevice_OpDeviceIdBase     = 0x1AB4300000000000ULL,
-        kTestDevice_NumberOfOpCerts    = 10,
-    };
-
-    sDevicePrivKey.PrivKey = devicePrivKeyBuf;
-    sDevicePrivKey.PrivKeyLen = sizeof(devicePrivKeyBuf);
-
-    devicePubKey.ECPoint = devicePubKeyBuf;
-    devicePubKey.ECPointLen = sizeof(devicePubKeyBuf);
-
-
-    for (int i = 1; i <= kTestDevice_NumberOfOpCerts; i++)
-    {
-        deviceId = kTestDevice_OpDeviceIdBase + i;
-
-        err = GenerateECDHKey(WeaveCurveIdToOID(WEAVE_CONFIG_OPERATIONAL_DEVICE_CERT_CURVE_ID), devicePubKey, sDevicePrivKey);
-        SuccessOrFail(err, "GenerateECDHKey() returned error");
-
-        err = EncodeWeaveECPrivateKey(WEAVE_CONFIG_OPERATIONAL_DEVICE_CERT_CURVE_ID, &devicePubKey, sDevicePrivKey,
-                                      keyBuf, sizeof(keyBuf), keyLen);
-        SuccessOrFail(err, "EncodeWeaveECPrivateKey() returned error");
-
-        err = GenerateOperationalDeviceCert(deviceId, devicePubKey, certBuf, sizeof(certBuf), certLen, sGenerateCertSignature);
-        SuccessOrFail(err, "GenerateOperationalDeviceCert() returned error");
-
-        printf("// Operational node Id, self-signed certificate and private key for Test Device %d\n//\n\n", i);
-
-        printf("uint64_t TestDevice%d_OperationalNodeId = 0x%" PRIX64 "ULL;\n\n", i, deviceId);
-
-        printf("uint8_t TestDevice%d_OperationalCert[] = \n{\n", i);
-        DumpMemoryCStyle(certBuf, certLen, "    ", 16);
-        printf("};\n\n");
-
-        printf("uint16_t TestDevice%d_OperationalCertLength = sizeof(TestDevice%d_OperationalCert);\n\n", i, i);
-
-        printf("uint8_t TestDevice%d_OperationalPrivateKey[] = \n{\n", i);
-        DumpMemoryCStyle(keyBuf, keyLen, "    ", 16);
-        printf("};\n\n");
-
-        printf("uint16_t TestDevice%d_OperationalPrivateKeyLength = sizeof(TestDevice%d_OperationalPrivateKey);\n\n\n", i, i);
-    }
-
-    printf("%s passed\n", __FUNCTION__);
-}
-
 int main(int argc, char *argv[])
 {
     WeaveCertTest_WeaveToX509();
@@ -872,8 +813,5 @@
     WeaveCertTest_CertUsage();
     WeaveCertTest_CertType();
     WeaveCertTest_GenerateOperationalDeviceCert();
-#if DEBUG_PRINT_ENABLE
-    WeaveCertTest_GenerateAndPrintTestOperationalDeviceCert();
-#endif
     printf("All tests passed.\n");
 }
diff --git a/src/test-apps/ToolCommon.h b/src/test-apps/ToolCommon.h
index 13cdb00..4849a45 100644
--- a/src/test-apps/ToolCommon.h
+++ b/src/test-apps/ToolCommon.h
@@ -1,5 +1,6 @@
 /*
  *
+ *    Copyright (c) 2019-2020 Google LLC.
  *    Copyright (c) 2013-2017 Nest Labs, Inc.
  *    All rights reserved.
  *
@@ -51,6 +52,7 @@
 #include "TAKEOptions.h"
 #include "KeyExportOptions.h"
 #include "DeviceDescOptions.h"
+#include "TestWeaveCertData.h"
 
 #include <Weave/WeaveVersion.h>
 #include <SystemLayer/SystemLayer.h>
@@ -58,6 +60,7 @@
 #include <Weave/Core/WeaveCore.h>
 #include <Weave/Support/CodeUtils.h>
 #include <Weave/Support/ErrorStr.h>
+#include <Weave/Support/crypto/RSA.h>
 #include <Weave/Core/WeaveStats.h>
 
 #if CONFIG_BLE_PLATFORM_BLUEZ
@@ -66,7 +69,6 @@
 #include <PlatformLayer/Ble/Bluez/WoBluezLayer.h>
 #endif // CONFIG_BLE_PLATFORM_BLUEZ
 
-
 using namespace nl::Inet;
 using namespace nl::Weave;
 using namespace nl::Weave::Profiles;
@@ -123,6 +125,29 @@
 extern uint8_t TestDevice2_PrivateKey[];
 extern uint16_t TestDevice2_PrivateKeyLength;
 
+extern const uint8_t TestDevice1_X509_RSA_PrivateKey[];
+extern const uint16_t TestDevice1_X509_RSA_PrivateKeyLength;
+extern const uint8_t TestDevice1_X509_RSA_Cert[];
+extern const uint16_t TestDevice1_X509_RSA_CertLength;
+extern const uint8_t TestDevice1_X509_RSA_ICACert1[];
+extern const uint16_t TestDevice1_X509_RSA_ICACert1Length;
+extern const uint8_t TestDevice1_X509_RSA_ICACert2[];
+extern const uint16_t TestDevice1_X509_RSA_ICACert2Length;
+
+extern const uint8_t TestDevice_X509_RSA_RootCert[];
+extern const uint16_t TestDevice_X509_RSA_RootCertLength;
+
+extern const uint8_t TestPairingToken[];
+extern const uint16_t TestPairingTokenLength;
+extern const uint8_t TestPairingInitData[];
+extern const uint16_t TestPairingInitDataLength;
+
+extern const uint32_t TestDevice1_MfrAttest_HMACKeyId;
+extern const uint8_t TestDevice1_MfrAttest_HMACMetaData[];
+extern const uint16_t TestDevice1_MfrAttest_HMACMetaDataLength;
+extern const uint8_t TestDevice1_MfrAttest_HMACKey[];
+extern const uint16_t TestDevice1_MfrAttest_HMACKeyLength;
+
 extern bool sSuppressAccessControls;
 
 extern void InitToolCommon();
@@ -154,6 +179,8 @@
 extern bool GetTestCACert(uint64_t caId, const uint8_t *& cert, uint16_t& certLen);
 extern bool GetTestCAPrivateKey(uint64_t caId, const uint8_t *& key, uint16_t& keyLen);
 
+extern WEAVE_ERROR ValidateWeaveDeviceCert(WeaveCertificateSet & certSet);
+
 #if CONFIG_BLE_PLATFORM_BLUEZ
 void *WeaveBleIOLoop(void *arg);
 nl::Ble::Platform::BlueZ::BluezBlePlatformDelegate *getBluezPlatformDelegate();
diff --git a/src/test-apps/ToolCommonOptions.cpp b/src/test-apps/ToolCommonOptions.cpp
index d8b25a3..272c2dd 100644
--- a/src/test-apps/ToolCommonOptions.cpp
+++ b/src/test-apps/ToolCommonOptions.cpp
@@ -1,5 +1,6 @@
 /*
  *
+ *    Copyright (c) 2019 Google LLC.
  *    Copyright (c) 2013-2017 Nest Labs, Inc.
  *    All rights reserved.
  *
@@ -32,6 +33,7 @@
 #include <stdint.h>
 #include <stdio.h>
 
+#include "ToolCommon.h"
 #include "ToolCommonOptions.h"
 #include "TestPersistedStorageImplementation.h"
 #include <Weave/WeaveVersion.h>
@@ -39,6 +41,7 @@
 #include <SystemLayer/SystemFaultInjection.h>
 #include <Weave/Support/WeaveFaultInjection.h>
 #include <InetLayer/InetFaultInjection.h>
+#include <Weave/Support/Base64.h>
 
 using namespace nl::Inet;
 using namespace nl::Weave;
@@ -1120,4 +1123,64 @@
     return true;
 }
 
+bool ReadCertFile(const char *fileName, uint8_t *& certBuf, uint16_t& certLen)
+{
+    uint32_t len;
+
+    static const char *certB64Prefix = "1QAABAAB";
+    static const size_t certB64PrefixLen = sizeof(certB64Prefix) - 1;
+
+    // Read the specified file into a malloced buffer.
+    certBuf = ReadFileArg(fileName, len, UINT16_MAX);
+    if (certBuf == NULL)
+        return false;
+
+    // If the certificate is in base-64 format, convert it to raw TLV.
+    if (len > certB64PrefixLen && memcmp(certBuf, certB64Prefix, certB64PrefixLen) == 0)
+    {
+        len = nl::Base64Decode((const char *)certBuf, len, (uint8_t *)certBuf);
+        if (len == UINT16_MAX)
+        {
+            printf("Invalid certificate format: %s\n", fileName);
+            free(certBuf);
+            certBuf = NULL;
+            return false;
+        }
+    }
+
+    certLen = (uint16_t)len;
+
+    return true;
+}
+
+bool ReadPrivateKeyFile(const char *fileName, uint8_t *& keyBuf, uint16_t& keyLen)
+{
+    uint32_t len;
+
+    static const char *keyB64Prefix = "1QAABAAC";
+    static const size_t keyB64PrefixLen = sizeof(keyB64Prefix) - 1;
+
+    // Read the specified file into a malloced buffer.
+    keyBuf = ReadFileArg(fileName, len, UINT16_MAX);
+    if (keyBuf == NULL)
+        return false;
+
+    // If the private key is in base-64 format, convert it to raw TLV.
+    if (len > keyB64PrefixLen && memcmp(keyBuf, keyB64Prefix, keyB64PrefixLen) == 0)
+    {
+        len = nl::Base64Decode((const char *)keyBuf, len, (uint8_t *)keyBuf);
+        if (len == UINT16_MAX)
+        {
+            printf("Invalid private key format: %s\n", fileName);
+            free(keyBuf);
+            keyBuf = NULL;
+            return false;
+        }
+    }
+
+    keyLen = (uint16_t)len;
+
+    return true;
+}
+
 #endif // WEAVE_CONFIG_ENABLE_DNS_RESOLVER
diff --git a/src/test-apps/ToolCommonOptions.h b/src/test-apps/ToolCommonOptions.h
index d4a4b56..734895a 100644
--- a/src/test-apps/ToolCommonOptions.h
+++ b/src/test-apps/ToolCommonOptions.h
@@ -1,5 +1,6 @@
 /*
  *
+ *    Copyright (c) 2019-2020 Google LLC.
  *    Copyright (c) 2013-2017 Nest Labs, Inc.
  *    All rights reserved.
  *
@@ -54,6 +55,18 @@
     kToolCommonOpt_NodeKey,
     kToolCommonOpt_CACert,
     kToolCommonOpt_NoCACert,
+    kToolCommonOpt_GetCertReqType,
+    kToolCommonOpt_OpCert,
+    kToolCommonOpt_OpKey,
+    kToolCommonOpt_OpICACerts,
+    kToolCommonOpt_SendOpICACerts,
+    kToolCommonOpt_MfrAttestType,
+    kToolCommonOpt_MfrAttestNodeId,
+    kToolCommonOpt_MfrAttestCert,
+    kToolCommonOpt_MfrAttestKey,
+    kToolCommonOpt_MfrAttestICACert1,
+    kToolCommonOpt_MfrAttestICACert2,
+    kToolCommonOpt_SendMfrAttestICACerts,
     kToolCommonOpt_EventDelay,
     kToolCommonOpt_FaultInjection,
     kToolCommonOpt_FaultTestIterations,
@@ -67,6 +80,9 @@
     kToolCommonOpt_KeyExportConfig,
     kToolCommonOpt_AllowedKeyExportConfigs,
     kToolCommonOpt_AccessToken,
+    kToolCommonOpt_SendAuthorizeInfo,
+    kToolCommonOpt_PairingToken,
+    kToolCommonOpt_PairingInitData,
     kToolCommonOpt_DebugLwIP,
     kToolCommonOpt_DeviceSerialNum,
     kToolCommonOpt_DeviceVendorId,
@@ -296,6 +312,9 @@
 
 extern bool ResolveWeaveNetworkOptions(const char * progName, WeaveNodeOptions &weaveOptions, NetworkOptions &networkOptions);
 
+extern bool ReadCertFile(const char *fileName, uint8_t *& certBuf, uint16_t& certLen);
+
+extern bool ReadPrivateKeyFile(const char *fileName, uint8_t *& keyBuf, uint16_t& keyLen);
 
 
 #endif // TOOLCOMMONOPTIONS_H_
diff --git a/src/test-apps/weave-cert-prov-client.cpp b/src/test-apps/weave-cert-prov-client.cpp
new file mode 100644
index 0000000..e7357fd
--- /dev/null
+++ b/src/test-apps/weave-cert-prov-client.cpp
@@ -0,0 +1,498 @@
+/*
+ *
+ *    Copyright (c) 2019 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
+ *      This file implements a command line tool, weave-cert-prov-client, for the
+ *      Weave Certificate Provisioning Protocol (Security Profile).
+ *
+ *      The weave-cert-prov-client tool implements a facility for acting as a client
+ *      (originator) for the certificate provisioning request, with a variety of options.
+ *
+ */
+
+#define __STDC_FORMAT_MACROS
+#define __STDC_LIMIT_MACROS
+
+#include <inttypes.h>
+#include <limits.h>
+#include <signal.h>
+
+#include "ToolCommon.h"
+#include "CertProvOptions.h"
+#include <Weave/WeaveVersion.h>
+#include <Weave/Core/WeaveSecurityMgr.h>
+#include <Weave/Core/WeaveTLV.h>
+#include <Weave/Profiles/security/WeaveSecurity.h>
+#include <Weave/Profiles/security/WeaveCertProvisioning.h>
+#include <Weave/Profiles/service-directory/ServiceDirectory.h>
+#include <Weave/Support/crypto/WeaveCrypto.h>
+#include <Weave/Support/TimeUtils.h>
+#include <Weave/Support/WeaveFaultInjection.h>
+
+using nl::StatusReportStr;
+using namespace nl::Weave::TLV;
+using namespace nl::Weave::Profiles::Security;
+using namespace nl::Weave::Profiles::Security::CertProvisioning;
+
+#define TOOL_NAME "weave-cert-prov-client"
+
+static bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg);
+static bool HandleNonOptionArgs(const char *progName, int argc, char *argv[]);
+static void DriveSending();
+static void HandleConnectionReceived(WeaveMessageLayer *msgLayer, WeaveConnection *con);
+static void ParseDestAddress();
+static void BindingEventHandler(void *appState, Binding::EventType eventType, const Binding::InEventParam& inParam, Binding::OutEventParam& outParam);
+
+const nl::Weave::ExchangeContext::Timeout kResponseTimeoutMsec = 5000;
+
+uint32_t MaxGetCertCount = UINT32_MAX;
+uint32_t GetCertInterval = 5000; // 5 sec
+bool UseTCP = true;
+bool Debug = false;
+uint64_t DestNodeId;
+const char *DestAddr = NULL;
+IPAddress DestIPAddr;
+uint16_t DestPort = WEAVE_PORT;
+InterfaceId DestIntf = INET_NULL_INTERFACEID;
+uint64_t LastGetCertTime = 0;
+bool WaitingForGetCertResponse = false;
+uint32_t GetCertRequestCount = 0;
+uint32_t GetCertResponseCount = 0;
+
+WeaveCertProvEngine  CertProvClient;
+
+#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
+bool UseWRMP = false;
+#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
+
+static OptionDef gToolOptionDefs[] =
+{
+    { "dest-addr",         kArgumentRequired, 'D' },
+    { "count",             kArgumentRequired, 'c' },
+    { "interval",          kArgumentRequired, 'i' },
+    { "tcp",               kNoArgument,       't' },
+    { "udp",               kNoArgument,       'u' },
+#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
+    { "wrmp",              kNoArgument,       'w' },
+#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
+    { NULL }
+};
+
+static const char *const gToolOptionHelp =
+    "  -D, --dest-addr <host>[:<port>][%<interface>]\n"
+    "       Send Get Certificate Requests to a specific address rather than one\n"
+    "       derived from the destination node id. <host> can be a hostname,\n"
+    "       an IPv4 address or an IPv6 address. If <port> is specified, Get Certificate\n"
+    "       Requests will be sent to the specified port. If <interface> is\n"
+    "       specified, Get Certificate Requests will be sent over the specified local\n"
+    "       interface.\n"
+    "\n"
+    "       NOTE: When specifying a port with an IPv6 address, the IPv6 address\n"
+    "       must be enclosed in brackets, e.g. [fd00:0:1:1::1]:11095.\n"
+    "\n"
+    "  -c, --count <num>\n"
+    "       Send the specified number of Get Certificate Requests and exit.\n"
+    "\n"
+    "  -i, --interval <ms>\n"
+    "       Send Get Certificate Requests at the specified interval in milliseconds.\n"
+    "\n"
+    "  -t, --tcp\n"
+    "       Use TCP to send Get Certificate Requests. This is the default.\n"
+    "\n"
+    "  -u, --udp\n"
+    "       Use UDP to send Get Certificate Requests.\n"
+    "\n"
+#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
+    "  -w, --wrmp\n"
+    "       Use UDP with Weave reliable messaging to send Get Certificate Requests.\n"
+    "\n"
+#endif
+    ;
+
+static OptionSet gToolOptions =
+{
+    HandleOption,
+    gToolOptionDefs,
+    "GENERAL OPTIONS",
+    gToolOptionHelp
+};
+
+static HelpOptions gHelpOptions(
+    TOOL_NAME,
+    "Usage: " TOOL_NAME " [<options...>] <dest-node-id>[@<dest-host>[:<dest-port>][%<interface>]]\n"
+    WEAVE_VERSION_STRING "\n" WEAVE_TOOL_COPYRIGHT,
+    "Send key export request and receive key export response messages.\n"
+);
+
+static OptionSet *gToolOptionSets[] =
+{
+    &gToolOptions,
+    &gNetworkOptions,
+    &gWeaveNodeOptions,
+    &gWRMPOptions,
+    &gCASEOptions,
+    &gCertProvOptions,
+    &gDeviceDescOptions,
+    &gServiceDirClientOptions,
+    &gFaultInjectionOptions,
+    &gHelpOptions,
+    NULL
+};
+
+static void ResetTestContext(void)
+{
+    Done = false;
+    WaitingForGetCertResponse = false;
+    GetCertRequestCount = 0;
+    GetCertResponseCount = 0;
+}
+
+int main(int argc, char *argv[])
+{
+    WEAVE_ERROR err;
+    nl::Weave::System::Stats::Snapshot before;
+    nl::Weave::System::Stats::Snapshot after;
+    const bool printStats = true;
+    uint32_t iter;
+
+#if WEAVE_CONFIG_TEST
+    SetupFaultInjectionContext(argc, argv);
+    SetSignalHandler(DoneOnHandleSIGUSR1);
+#endif
+
+    {
+        unsigned int seed;
+        err = nl::Weave::Platform::Security::GetSecureRandomData((uint8_t *)&seed, sizeof(seed));
+        FAIL_ERROR(err, "Random number generator seeding failed");
+        srand(seed);
+    }
+
+    if (argc == 1)
+    {
+        gHelpOptions.PrintBriefUsage(stderr);
+        exit(EXIT_FAILURE);
+    }
+
+    if (!ParseArgsFromEnvVar(TOOL_NAME, TOOL_OPTIONS_ENV_VAR_NAME, gToolOptionSets, NULL, true) ||
+        !ParseArgs(TOOL_NAME, argc, argv, gToolOptionSets, HandleNonOptionArgs) ||
+        !ResolveWeaveNetworkOptions(TOOL_NAME, gWeaveNodeOptions, gNetworkOptions))
+    {
+        exit(EXIT_FAILURE);
+    }
+
+    InitSystemLayer();
+    InitNetwork();
+    InitWeaveStack(!UseTCP, true);
+
+    // Create a binding for the CertProvClient.
+    Binding *binding = ExchangeMgr.NewBinding(BindingEventHandler, NULL);
+
+    // Initialize the CertProvClient object.
+    err = CertProvClient.Init(binding, &gCertProvOptions, &gCertProvOptions, gCertProvOptions.CertProvClientEventHandler, &gCertProvOptions);
+    FAIL_ERROR(err, "WeaveCertProvEngine.Init failed");
+
+    // Release the binding (the CertProvClient retains its own reference).
+    binding->Release();
+
+#if WEAVE_CONFIG_TEST
+    nl::Weave::Stats::UpdateSnapshot(before);
+#endif
+
+    // Arrange to get called for various activities in the message layer.
+    MessageLayer.OnConnectionReceived = HandleConnectionReceived;
+    MessageLayer.OnReceiveError = HandleMessageReceiveError;
+    MessageLayer.OnAcceptError = HandleAcceptConnectionError;
+
+    PrintNodeConfig();
+
+    if (!UseTCP && DestAddr != NULL)
+        ParseDestAddress();
+
+    printf("Sending");
+    if (MaxGetCertCount != UINT32_MAX)
+        printf(" %u", MaxGetCertCount);
+    printf(" Get Certificate Requests via %s to node %" PRIX64, UseTCP ? "TCP" : (UseWRMP ? "UDP with WRMP" : "UDP"), DestNodeId);
+    if (DestAddr != NULL)
+        printf(" (%s)", DestAddr);
+    printf(" every %" PRId32 " ms\n", GetCertInterval);
+
+#if WEAVE_CONFIG_TEST
+    for (iter = 0; iter < gFaultInjectionOptions.TestIterations; iter++)
+    {
+        printf("Iteration %u\n", iter);
+#endif // WEAVE_CONFIG_TEST
+
+        while (!Done)
+        {
+            struct timeval sleepTime;
+            sleepTime.tv_sec = 0;
+            sleepTime.tv_usec = 100000;
+
+            ServiceNetwork(sleepTime);
+
+            if (!Done)
+                DriveSending();
+
+            fflush(stdout);
+        }
+
+        ResetTestContext();
+
+#if WEAVE_CONFIG_TEST
+        if (gSigusr1Received)
+        {
+            printf("Sigusr1Received\n");
+            break;
+        }
+    }
+#endif // WEAVE_CONFIG_TEST
+
+    CertProvClient.Shutdown();
+
+#if WEAVE_CONFIG_TEST
+    ProcessStats(before, after, printStats, NULL);
+    PrintFaultInjectionCounters();
+#endif // WEAVE_CONFIG_TEST
+
+    ShutdownWeaveStack();
+    ShutdownNetwork();
+    ShutdownSystemLayer();
+
+    return EXIT_SUCCESS;
+}
+
+bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg)
+{
+    switch (id)
+    {
+    case 't':
+        UseTCP = true;
+        break;
+    case 'u':
+        UseTCP = false;
+        break;
+#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
+    case 'w':
+        UseTCP = false;
+        UseWRMP = true;
+        break;
+#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
+    case 'c':
+        if (!ParseInt(arg, MaxGetCertCount))
+        {
+            PrintArgError("%s: Invalid value specified for send count: %s\n", progName, arg);
+            return false;
+        }
+        break;
+    case 'i':
+        if (!ParseInt(arg, GetCertInterval))
+        {
+            PrintArgError("%s: Invalid value specified for send interval: %s\n", progName, arg);
+            return false;
+        }
+        break;
+    case 'D':
+        DestAddr = arg;
+        break;
+    default:
+        PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", progName, name);
+        return false;
+    }
+
+    return true;
+}
+
+bool HandleNonOptionArgs(const char *progName, int argc, char *argv[])
+{
+    if (argc > 0)
+    {
+        if (argc > 1)
+        {
+            PrintArgError("%s: Unexpected argument: %s\n", progName, argv[1]);
+            return false;
+        }
+
+        const char *nodeId = argv[0];
+        char *p = (char *)strchr(nodeId, '@');
+        if (p != NULL)
+        {
+            *p = 0;
+            DestAddr = p+1;
+        }
+
+        if (!ParseNodeId(nodeId, DestNodeId))
+        {
+            PrintArgError("%s: Invalid value specified for destination node Id: %s\n", progName, nodeId);
+            return false;
+        }
+    }
+    else
+    {
+        PrintArgError("%s: Please specify destination node Id\n", progName);
+        return false;
+    }
+
+    return true;
+}
+
+void DriveSending()
+{
+    WEAVE_ERROR err;
+
+    if (Now() < LastGetCertTime + GetCertInterval)
+        return;
+
+    if (WaitingForGetCertResponse)
+    {
+        printf("No get certificate response received\n");
+
+        WaitingForGetCertResponse = false;
+
+        // Rescan interfaces to see if we got any new IP addresses
+        if (!UseTCP)
+        {
+            printf("Refreshing endpoints\n");
+            err = MessageLayer.RefreshEndpoints();
+            if (err != WEAVE_NO_ERROR)
+                printf("WeaveMessageLayer.RefreshEndpoints() failed: %s\n", ErrorStr(err));
+        }
+    }
+
+    if (MaxGetCertCount != UINT32_MAX && GetCertRequestCount >= MaxGetCertCount)
+    {
+        Done = true;
+        return;
+    }
+
+    err = CertProvClient.StartCertificateProvisioning(gCertProvOptions.RequestType, (gCertProvOptions.RequestType == WeaveCertProvEngine::kReqType_GetInitialOpDeviceCert));
+    LastGetCertTime = Now();
+    if (err == WEAVE_NO_ERROR)
+    {
+        GetCertRequestCount++;
+        WaitingForGetCertResponse = true;
+    }
+    else
+    {
+        printf("CertProvClient.StartCertificateProvisioning() failed: %s\n", ErrorStr(err));
+    }
+}
+
+void HandleConnectionReceived(WeaveMessageLayer *msgLayer, WeaveConnection *con)
+{
+    char ipAddrStr[64];
+    con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
+
+    printf("Connection received from node %" PRIX64 " (%s)\n", con->PeerNodeId, ipAddrStr);
+}
+
+void ParseDestAddress()
+{
+    // NOTE: This function is only used when communicating over UDP.  Code in the WeaveConnection object handles
+    // parsing the destination node address for TCP connections.
+
+    WEAVE_ERROR err;
+    const char *addr;
+    uint16_t addrLen;
+    const char *intfName;
+    uint16_t intfNameLen;
+
+    err = ParseHostPortAndInterface(DestAddr, strlen(DestAddr), addr, addrLen, DestPort, intfName, intfNameLen);
+    if (err != INET_NO_ERROR)
+    {
+        printf("Invalid destination address: %s\n", DestAddr);
+        exit(EXIT_FAILURE);
+    }
+
+    if (!IPAddress::FromString(addr, DestIPAddr))
+    {
+        printf("Invalid destination address: %s\n", DestAddr);
+        exit(EXIT_FAILURE);
+    }
+
+    if (intfName != NULL)
+    {
+        err = InterfaceNameToId(intfName, DestIntf);
+        if (err != INET_NO_ERROR)
+        {
+            printf("Invalid interface name: %s\n", intfName);
+            exit(EXIT_FAILURE);
+        }
+    }
+}
+
+void BindingEventHandler(void *appState, Binding::EventType eventType, const Binding::InEventParam& inParam, Binding::OutEventParam& outParam)
+{
+    switch (eventType)
+    {
+    case Binding::kEvent_PrepareRequested:
+    {
+        Binding::Configuration bindingConfig = inParam.Source->BeginConfiguration();
+
+        // Configure the target node Id.
+        bindingConfig.Target_NodeId(DestNodeId);
+
+        // Configure the target address.
+        if (DestAddr != NULL)
+        {
+            bindingConfig.TargetAddress_IP(DestIPAddr, DestPort, DestIntf);
+        }
+
+        // Configure the transport.
+        if (UseTCP)
+        {
+            bindingConfig.Transport_TCP();
+        }
+        else if (!UseWRMP)
+        {
+            bindingConfig.Transport_UDP();
+        }
+        else
+        {
+            bindingConfig.Transport_UDP_WRM();
+            bindingConfig.Transport_DefaultWRMPConfig(gWRMPOptions.GetWRMPConfig());
+        }
+
+        // Configure the security mode.
+        switch (gWeaveSecurityMode.SecurityMode)
+        {
+        case WeaveSecurityMode::kNone:
+        default:
+            bindingConfig.Security_None();
+            break;
+        case WeaveSecurityMode::kCASE:
+            bindingConfig.Security_CASESession();
+            break;
+        case WeaveSecurityMode::kCASEShared:
+            bindingConfig.Security_SharedCASESession();
+            break;
+        case WeaveSecurityMode::kGroupEnc:
+            bindingConfig.Security_Key(gGroupKeyEncOptions.GetEncKeyId());
+            break;
+        }
+
+        bindingConfig.Exchange_ResponseTimeoutMsec(kResponseTimeoutMsec);
+
+        outParam.PrepareRequested.PrepareError = bindingConfig.PrepareBinding();
+        break;
+    }
+    default:
+        Binding::DefaultEventHandler(appState, eventType, inParam, outParam);
+        break;
+    }
+}
diff --git a/src/test-apps/weave-cert-prov-server.cpp b/src/test-apps/weave-cert-prov-server.cpp
new file mode 100644
index 0000000..4c7b9b6
--- /dev/null
+++ b/src/test-apps/weave-cert-prov-server.cpp
@@ -0,0 +1,243 @@
+/*
+ *
+ *    Copyright (c) 2019 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
+ *      This file implements a command line tool, weave-cert-prov-server, for the
+ *      Weave Certificate Provisioning Protocol (Security Profile).
+ *
+ *      The weave-cert-prov-server tool implements a facility for acting as a CA server
+ *      (responder) for the certificate provisioning request, with a variety of options.
+ *
+ */
+
+#define __STDC_FORMAT_MACROS
+#define __STDC_LIMIT_MACROS
+
+#include <inttypes.h>
+#include <limits.h>
+#include <signal.h>
+
+#include "ToolCommon.h"
+#include "CertProvOptions.h"
+#include "MockCAService.h"
+#include <Weave/WeaveVersion.h>
+#include <Weave/Core/WeaveSecurityMgr.h>
+#include <Weave/Core/WeaveTLV.h>
+#include <Weave/Profiles/security/WeaveSecurity.h>
+#include <Weave/Profiles/security/WeaveCertProvisioning.h>
+#include <Weave/Profiles/service-directory/ServiceDirectory.h>
+#include <Weave/Support/crypto/WeaveCrypto.h>
+#include <Weave/Support/TimeUtils.h>
+#include <Weave/Support/WeaveFaultInjection.h>
+
+using nl::StatusReportStr;
+using namespace nl::Weave::TLV;
+using namespace nl::Weave::Profiles::Security;
+using namespace nl::Weave::Profiles::Security::CertProvisioning;
+
+#define TOOL_NAME "weave-cert-prov-server"
+
+static bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg);
+static void HandleConnectionReceived(WeaveMessageLayer *msgLayer, WeaveConnection *con);
+
+MockCAService  CertProvServer;
+
+enum NameResolutionStateEnum
+{
+    kNameResolutionState_NotStarted,
+    kNameResolutionState_InProgress,
+    kNameResolutionState_Complete
+} NameResolutionState = kNameResolutionState_NotStarted;
+
+static OptionDef gToolOptionDefs[] =
+{
+    { "ca-cert",           kArgumentRequired, 'c' },
+    { "ca-key",            kArgumentRequired, 'k' },
+    { "send-ca-cert",      kNoArgument,       's' },
+    { "do-not-rotate",     kNoArgument,       'r' },
+    { NULL }
+};
+
+static const char *const gToolOptionHelp =
+    "  -c, --ca-cert <cert-file>\n"
+    "       File containing device operational CA certificate to be included along with the node's\n"
+    "       operational certificate in the Get Certificat Response message. The file can contain\n"
+    "       either raw TLV or base-64. If not specified the default test CA certificate is used.\n"
+    "\n"
+    "  -k, --ca-key <key-file>\n"
+    "       File containing device operaional CA private key to be used to sign all leaf (node's)\n"
+    "       operational certificates. The file can contain either raw TLV orbase-64. If not\n"
+    "       specified the default test CA key is used.\n"
+    "\n"
+    "  -s, --send-ca-cert\n"
+    "       Include device operational CA certificate in the Get Certificat Response message.\n"
+    "       This option is set automatically when ca-cert is specified.\n"
+    "\n"
+    "  -r, --do-not-rotate\n"
+    "       Do not issue new certificate to the Rotate Device Operational Certificate Request.\n"
+    "       By default the GetCertificateResponse will be sent to this request.\n"
+    "\n"
+    ;
+
+static OptionSet gToolOptions =
+{
+    HandleOption,
+    gToolOptionDefs,
+    "GENERAL OPTIONS",
+    gToolOptionHelp
+};
+
+static HelpOptions gHelpOptions(
+    TOOL_NAME,
+    "Usage: " TOOL_NAME " [<options...>]\n"
+    WEAVE_VERSION_STRING "\n" WEAVE_TOOL_COPYRIGHT,
+    "Receive and process get certificate request and send get certificate response messages.\n"
+);
+
+static OptionSet *gToolOptionSets[] =
+{
+    &gToolOptions,
+    &gNetworkOptions,
+    &gWeaveNodeOptions,
+    &gWRMPOptions,
+    &gDeviceDescOptions,
+    &gHelpOptions,
+    NULL
+};
+
+int main(int argc, char *argv[])
+{
+    WEAVE_ERROR err;
+
+    {
+        unsigned int seed;
+        err = nl::Weave::Platform::Security::GetSecureRandomData((uint8_t *)&seed, sizeof(seed));
+        FAIL_ERROR(err, "Random number generator seeding failed");
+        srand(seed);
+    }
+
+    if (argc == 1)
+    {
+        gHelpOptions.PrintBriefUsage(stderr);
+        exit(EXIT_FAILURE);
+    }
+
+    if (!ParseArgsFromEnvVar(TOOL_NAME, TOOL_OPTIONS_ENV_VAR_NAME, gToolOptionSets, NULL, true) ||
+        !ParseArgs(TOOL_NAME, argc, argv, gToolOptionSets) ||
+        !ResolveWeaveNetworkOptions(TOOL_NAME, gWeaveNodeOptions, gNetworkOptions))
+    {
+        exit(EXIT_FAILURE);
+    }
+
+    InitSystemLayer();
+    InitNetwork();
+    InitWeaveStack(true, true);
+    MessageLayer.RefreshEndpoints();
+
+    // Initialize the CertProvServer object.
+    err = CertProvServer.Init(&ExchangeMgr);
+    FAIL_ERROR(err, "MockCAService.Init failed");
+
+    // Arrange to get called for various activities in the message layer.
+    MessageLayer.OnConnectionReceived = HandleConnectionReceived;
+    MessageLayer.OnReceiveError = HandleMessageReceiveError;
+    MessageLayer.OnAcceptError = HandleAcceptConnectionError;
+
+    PrintNodeConfig();
+
+    while (!Done)
+    {
+        struct timeval sleepTime;
+        sleepTime.tv_sec = 0;
+        sleepTime.tv_usec = 100000;
+
+        ServiceNetwork(sleepTime);
+        fflush(stdout);
+    }
+
+    if (gSigusr1Received)
+    {
+        printf("Sigusr1Received\n");
+        fflush(stdout);
+    }
+
+    CertProvServer.Shutdown();
+
+    ShutdownWeaveStack();
+    ShutdownNetwork();
+    ShutdownSystemLayer();
+
+    return EXIT_SUCCESS;
+}
+
+bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg)
+{
+    switch (id)
+    {
+    case 'c':
+    {
+        uint8_t * cert;
+        uint16_t certLen;
+
+        if (!ReadCertFile(arg, cert, certLen))
+            return false;
+
+        CertProvServer.SetCACert(cert, certLen);
+
+        CertProvServer.IncludeRelatedCerts(true);
+
+        break;
+    }
+
+    case 'k':
+    {
+        uint8_t * key;
+        uint16_t keyLen;
+
+        if (!ReadPrivateKeyFile(arg, key, keyLen))
+            return false;
+
+        CertProvServer.SetCAPrivateKey(key, keyLen);
+
+        break;
+    }
+
+    case 's':
+        CertProvServer.IncludeRelatedCerts(true);
+        break;
+
+    case 'r':
+        CertProvServer.DoNotRotateCert(true);
+        break;
+
+    default:
+        PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", progName, name);
+        return false;
+    }
+
+    return true;
+}
+
+void HandleConnectionReceived(WeaveMessageLayer *msgLayer, WeaveConnection *con)
+{
+    char ipAddrStr[64];
+    con->PeerAddr.ToString(ipAddrStr, sizeof(ipAddrStr));
+
+    printf("Connection received from node %" PRIX64 " (%s)\n", con->PeerNodeId, ipAddrStr);
+}