[IOT-1785] Support for Certificate Provisioning
authorGreg Zaverucha <gregz@microsoft.com>
Wed, 8 Mar 2017 20:41:34 +0000 (12:41 -0800)
committerKevin Kane <kkane@microsoft.com>
Thu, 9 Mar 2017 17:34:42 +0000 (17:34 +0000)
- Shell script to create IoTivity identity certificates using
OpenSSL command line tools.
- Partial unit test for providing credentails to the CA adapter
layer.  Tests loading of PEM cert and key.
- Misc changes and fixes to the cred resource
- updated provisioning client to provision certs
- helper code to create certs from CSRs

Change-Id: I7d3ab4810c7f7d6247ed90420cb97cbdb5f829a4
Signed-off-by: Greg Zaverucha <gregz@microsoft.com>
Signed-off-by: Kevin Kane <kkane@microsoft.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/17509
Reviewed-by: Alex Kelley <alexke@microsoft.com>
Tested-by: jenkins-iotivity <jenkins@iotivity.org>
Reviewed-by: Dan Mihai <Daniel.Mihai@microsoft.com>
12 files changed:
resource/csdk/connectivity/src/adapter_util/ca_adapter_net_ssl.c
resource/csdk/connectivity/test/ca_api_unittest.cpp
resource/csdk/security/include/occertutility.h
resource/csdk/security/provisioning/include/internal/secureresourceprovider.h
resource/csdk/security/provisioning/include/ocprovisioningmanager.h
resource/csdk/security/provisioning/sample/provisioningclient.c
resource/csdk/security/provisioning/src/ocprovisioningmanager.c
resource/csdk/security/provisioning/src/secureresourceprovider.c
resource/csdk/security/provisioning/unittest/secureresourceprovider.cpp
resource/csdk/security/src/credresource.c
resource/csdk/security/src/occertutility.c
resource/csdk/stack/octbstack_product_secured.def

index a65e8cb..07e101e 100755 (executable)
@@ -1334,7 +1334,7 @@ static void SetupCipher(mbedtls_ssl_config * config, CATransportAdapter_t adapte
 
     g_getCredentialTypesCallback(g_caSslContext->cipherFlag);
     // Retrieve the PSK credential from SRM
-    if (0 != InitPskIdentity(config))
+    if (true == g_caSslContext->cipherFlag[0] && 0 != InitPskIdentity(config))
     {
         OIC_LOG(ERROR, NET_SSL_TAG, "PSK identity initialization failed!");
     }
index e234da0..2b3850a 100755 (executable)
@@ -100,7 +100,6 @@ static const uint16_t PORT = 4545;
 static const char NORMAL_INFO_DATA[] =
                                     "{\"oc\":[{\"href\":\"%s\",\"prop\":{\"rt\":[\"core.led\"],"
                                      "\"if\":[\"oc.mi.def\"],\"obs\":1}}]}";
-
 #ifdef __WITH_DTLS__
 
 // Iotivity Device Identity.
@@ -164,6 +163,74 @@ int32_t CAGetDtlsPskCredentials( CADtlsPskCredType_t type,
     printf("CAGetDtlsPskCredentials OUT\n");
     return ret;
 }
+
+const char* our_cert = "-----BEGIN CERTIFICATE-----\n"
+"MIIBhTCCASugAwIBAgIJAPZ5mB94RwYHMAoGCCqGSM49BAMCMCUxIzAhBgNVBAoM\n"
+"GklvVGl2aXR5VGVzdFNlbGZTaWduZWROYW1lMB4XDTE2MTIxNjIxMjcyMVoXDTMw\n"
+"MDgyNTIxMjcyMVowITEfMB0GA1UECgwWSW9UaXZpdHlUZXN0Q2xpZW50TmFtZTBZ\n"
+"MBMGByqGSM49AgEGCCqGSM49AwEHA0IABF8OxpJNe01ZPEFpXUUhjUV5uwJM1TF3\n"
+"ZSt0tJ71lQiRZ9cbl5z31acRpsZM+fXiR+wkR4xoP7iIyDdTHHVHtkSjSDBGMBUG\n"
+"A1UdJQQOMAwGCisGAQQBgt58AQYwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBQH\n"
+"EWLwaDfA+o6U4wmQKVoK9I3B/DAKBggqhkjOPQQDAgNIADBFAiAbDHQHzSjNiDeQ\n"
+"OaJYRMLIW2dIlabiQ5pxkW/jEaRszAIhAPzuoNdrQTRbnqCy0hmS9hFt8MxDrrBh\n"
+"7jHARQm/5pko\n"
+"-----END CERTIFICATE-----\n";
+
+const char* our_key = "-----BEGIN EC PRIVATE KEY-----\n"
+"MHcCAQEEIOV0iG5CndNK6JhB8nDcqQjNjgRWe/LQWPNPua3w7nHToAoGCCqGSM49\n"
+"AwEHoUQDQgAEXw7Gkk17TVk8QWldRSGNRXm7AkzVMXdlK3S0nvWVCJFn1xuXnPfV\n"
+"pxGmxkz59eJH7CRHjGg/uIjIN1McdUe2RA==\n"
+"-----END EC PRIVATE KEY-----\n";
+
+const char* our_ca = "-----BEGIN CERTIFICATE-----\n"
+"MIIBlzCCATygAwIBAgIJALxGf3YRERn1MAoGCCqGSM49BAMCMCUxIzAhBgNVBAoM\n"
+"GklvVGl2aXR5VGVzdFNlbGZTaWduZWROYW1lMB4XDTE2MTIxNjIxMjcyMVoXDTMw\n"
+"MDgyNTIxMjcyMVowJTEjMCEGA1UECgwaSW9UaXZpdHlUZXN0U2VsZlNpZ25lZE5h\n"
+"bWUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATo/zp8PXaA/drJQKSG3TlerO0F\n"
+"eHkpRkmXMeLFLrImqo1w9OyLfVmrpBrCDjf83BkwLYp19bkYizL2Yk9zIQ4Do1Uw\n"
+"UzAhBgNVHSUEGjAYBgorBgEEAYLefAEGBgorBgEEAYLefAEHMA8GA1UdEwEB/wQF\n"
+"MAMBAf8wHQYDVR0OBBYEFAcRYvBoN8D6jpTjCZApWgr0jcH8MAoGCCqGSM49BAMC\n"
+"A0kAMEYCIQCuZb1LMTthWy9rPgy2FQgoFHB2LXUJlgRLJeO/gTFqgQIhANRvr1Py\n"
+"5Bp6asye5FK4VUj6tARxmRNeNLvwonLrqp2w\n"
+"-----END CERTIFICATE-----\n";
+
+// Invoked by the CA stack to retrieve credentials from this module
+void provide_x509_cert_and_key(PkiInfo_t* inf)
+{
+    /* PEM data must end in newline and be null terminated for IoTivity */
+
+    inf->crt.data = (uint8_t*) our_cert;
+    inf->crt.len = strlen(our_cert) + 1;
+    inf->key.data = (uint8_t*) our_key;
+    inf->key.len = strlen(our_key) + 1;
+    inf->ca.data = (uint8_t*) our_ca;
+    inf->ca.len = strlen(our_ca) + 1;
+
+    // CRL not provided
+    inf->crl.data = NULL;
+    inf->crl.len = 0;
+
+    return;
+}
+// Empty version, for testing.
+void badPkixInfoHandler(PkiInfo_t* inf)
+{
+    return;
+}
+
+void provide_supported_credential_types(bool* list)
+{
+    list[1] = true;
+    /*
+     * Note: there is a default implementation of this in credresource.c, exposed by 
+     * pkix_interface.h, called InitManufacturerCipherSuiteList.  If the cred resource 
+     * has a credential of the required type, it updates list accordingly. 
+     *
+     * In a separate test, we could use the cred resource and APIs (credresource.h). 
+     */
+    return;
+}
+
 #endif  //__WITH_DTLS__
 
 // CAInitialize TC
@@ -293,6 +360,43 @@ TEST_F(CATests, SendRequestTestWithInvalidAddress)
     tempRep = NULL;
 }
 
+#if defined(__WITH_DTLS__)
+TEST_F(CATests, DISABLED_PkiTest)
+{
+    // @todo: this test is disabled for now, it crashes with an invalid write. Cert data
+    // provided by the provide_x509_cert_and_key is stored as const char, but ParseChain()
+    // (in ca_adapter_net_ssl.c) writes to it while reading.  We could change the test to 
+    // provide data on the heap, but the CA stack should not be changing data provided to it
+    // by callbacks. 
+
+    char* local_addr = "127.0.0.1";
+    uint16_t local_port = 5503;
+    CAEndpoint_t* serverAddr = NULL;
+
+    CARegisterHandler(request_handler, response_handler, error_handler);
+
+    EXPECT_EQ(CA_STATUS_OK, CASelectNetwork(CA_ADAPTER_IP));
+    CACreateEndpoint(CA_DEFAULT_FLAGS, CA_ADAPTER_IP, local_addr, local_port, &serverAddr);
+    ASSERT_TRUE(serverAddr != NULL);
+
+    // Register a credential types handler (tells the CA layer which creds are supported)
+    EXPECT_EQ(CA_STATUS_OK, CAregisterGetCredentialTypesHandler(provide_supported_credential_types));
+
+    // Limit ourselves to 0xC0AE : TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 since it's non-PSK
+    EXPECT_EQ(CA_STATUS_OK, CASelectCipherSuite(0xC0AE, serverAddr->adapter));
+
+    // Register an empty callback to provide the keys, expect failure when initializing the handshake.
+    EXPECT_EQ(CA_STATUS_OK, CAregisterPkixInfoHandler(badPkixInfoHandler));
+    EXPECT_EQ(CA_STATUS_FAILED, CAInitiateHandshake(serverAddr));
+
+    // Register a working callback to provide the keys, expect success.
+    EXPECT_EQ(CA_STATUS_OK, CAregisterPkixInfoHandler(provide_x509_cert_and_key));
+    EXPECT_EQ(CA_STATUS_OK, CAInitiateHandshake(serverAddr)); 
+
+    CADestroyEndpoint(serverAddr);
+}
+#endif /* defined(__WITH_DTLS__) */
+
 // CASendRequest TC
 // check return value when a NULL is passed instead of a valid CARequestInfo_t address
 TEST_F(CATests, SendRequestTestWithNullAddr)
@@ -481,6 +585,8 @@ TEST_F(CATests, RegisterDTLSCredentialsHandlerTest)
 {
 #ifdef __WITH_DTLS__
     EXPECT_EQ(CA_STATUS_OK, CAregisterPskCredentialsHandler(CAGetDtlsPskCredentials));
+    EXPECT_EQ(CA_STATUS_OK, CAregisterPkixInfoHandler(provide_x509_cert_and_key));
+    EXPECT_EQ(CA_STATUS_OK, CAregisterGetCredentialTypesHandler(provide_supported_credential_types));
 #endif
 }
 
index 43c5bf8..7962cd0 100644 (file)
@@ -170,6 +170,50 @@ OCStackResult OCGenerateRoleCertificate(
     char **certificate,
     size_t *certificateLen);
 
+/**
+ * Extract a UUID from a CSR.
+ *
+ * @param[in]  csr  The CSR containing the UUID as null-terminated PEM.
+ * @param[out] uuid The UUID in the CSR
+ *
+ * @return 0 on success, nonzero otherwise
+ */
+OCStackResult OCGetUuidFromCSR(const char* csr, OicUuid_t* uuid);
+
+/**
+ * Extract a public key from a CSR.
+ *
+ * @param[in]  csr       The CSR containing the public key, as null-terminated PEM.
+ * @param[out] publicKey The public key is output here as null-terminated PEM.
+ *                       Callers must call OICFree when finished.
+ *
+ * @return 0 on success, nonzero otherwise
+ */
+OCStackResult OCGetPublicKeyFromCSR(const char* csr, char** publicKey);
+
+/**
+ * Verify the signature in a CSR is valid.
+ *
+ * @param[in] csr The CSR to check, as null-terminated PEM.
+ *
+ * @returns 0 on success, nonzero otherwise
+ *
+ * @remark Requires that ECDSA with SHA-256 be used for the signature.
+ */
+OCStackResult OCVerifyCSRSignature(const char* csr);
+
+/**
+ * Convert a CSR from DER encoding to PEM encoding.
+ *
+ * @param[in] derCSR The CSR to convert, encoded as DER
+ * @param[in] derCSRLen Then number of bytes in derCSR
+ * @param[out] pemCSR The output, PEM encoded, null-terminated CSR. Callers 
+ *                    call OICFree when finished. 
+ *
+ * @returns 0 on success, nonzero otherwise
+*/
+OCStackResult OCConvertDerCSRToPem(const char* derCSR, size_t derCSRLen, char** pemCSR);
+
 #ifdef __cplusplus
 }
 #endif
index ac1d640..a7364c9 100644 (file)
@@ -35,7 +35,7 @@ extern "C"
 /**
  * API to send ACL information to resource.
  *
- * @param[in] ctx Application context would be returned in result callback.
+ * @param[in] ctx Application context to be returned in result callback.
  * @param[in] selectedDeviceInfo Selected target device.
  * @param[in] acl ACL to provision.
  * @param[in] resultCallback callback provided by API user, callback will be called when
@@ -56,7 +56,7 @@ OCStackResult SRPSaveACL(const OicSecAcl_t *acl);
 /**
  * API to request CRED information to resource.
  *
- * @param[in] ctx Application context would be returned in result callback.
+ * @param[in] ctx Application context to be returned in result callback.
  * @param[in] selectedDeviceInfo Selected target device.
  * @param[in] resultCallback callback provided by API user, callback will be called when
  *            provisioning request recieves a response from resource server.
@@ -68,7 +68,7 @@ OCStackResult SRPGetCredResource(void *ctx, const OCProvisionDev_t *selectedDevi
 /**
  * API to request ACL information to resource.
  *
- * @param[in] ctx Application context would be returned in result callback.
+ * @param[in] ctx Application context to be returned in result callback.
  * @param[in] selectedDeviceInfo Selected target device.
  * @param[in] resultCallback callback provided by API user, callback will be called when
  *            provisioning request recieves a response from resource server.
@@ -93,7 +93,7 @@ OCStackResult SRPGetCSRResource(void *ctx, const OCProvisionDev_t *selectedDevic
 /**
  * function to provision Trust certificate chain to devices.
  *
- * @param[in] ctx Application context would be returned in result callback.
+ * @param[in] ctx Application context to be returned in result callback.
  * @param[in] type Type of credentials to be provisioned to the device.
  * @param[in] credId CredId of trust certificate chain to be provisioned to the device.
  * @param[in] selectedDeviceInfo Pointer to OCProvisionDev_t instance,respresenting resource to be provsioned.
@@ -145,7 +145,7 @@ void SRPRemoveTrustCertChainNotifier(void);
 /**
  * API to send Direct-Pairing Configuration to a device.
  *
- * @param[in] ctx Application context would be returned in result callback.
+ * @param[in] ctx Application context to be returned in result callback.
  * @param[in] selectedDeviceInfo Selected target device.
  * @param[in] pconf PCONF pointer.
  * @param[in] resultCallback callback provided by API user, callback will be called when
@@ -170,11 +170,13 @@ OCStackResult SRPProvisionDirectPairing(void *ctx, const OCProvisionDev_t *selec
 /**
  * API to provision credential to devices.
  *
- * @param[in] ctx Application context would be returned in result callback.
+ * @param[in] ctx Application context to be returned in result callback.
  * @param[in] type Type of credentials to be provisioned to the device.
  * @param[in] keySize size of key
- * @param[in] pDev1 Pointer to PMOwnedDeviceInfo_t instance,respresenting resource to be provsioned.
-   @param[in] pDev2 Pointer to PMOwnedDeviceInfo_t instance,respresenting resource to be provsioned.
+ * @param[in] pDev1 Pointer to PMOwnedDeviceInfo_t instance, respresenting resource to be provsioned.
+ * @param[in] pDev2 Pointer to PMOwnedDeviceInfo_t instance, respresenting resource to be provsioned.
+ * @param[in] pemCert When provisioning a certificate (type is SIGNED_ASYMMETRIC_KEY), this is the 
+ *                    certificate, encoded as PEM. 
  * @param[in] resultCallback callback provided by API user, callback will be called when
  *            provisioning request recieves a response from first resource server.
  * @return OC_STACK_OK in case of success and other value otherwise.
@@ -182,13 +184,14 @@ OCStackResult SRPProvisionDirectPairing(void *ctx, const OCProvisionDev_t *selec
 OCStackResult SRPProvisionCredentials(void *ctx,OicSecCredType_t type, size_t keySize,
                                       const OCProvisionDev_t *pDev1,
                                       const OCProvisionDev_t *pDev2,
+                                      const char* pemCert,
                                       OCProvisionResultCB resultCallback);
 
 /**
  * Function to unlink devices.
  * This function will remove the credential & relationship between the two devices.
  *
- * @param[in] ctx Application context would be returned in result callback
+ * @param[in] ctx Application context to be returned in result callback
  * @param[in] pTargetDev1 first device information to be unlinked.
  * @param[in] pTargetDev2 second device information to be unlinked.
  * @param[in] resultCallback callback provided by API user, callback will be called when
@@ -205,7 +208,7 @@ OCStackResult SRPUnlinkDevices(void* ctx,
  * Function to device revocation.
  * This function will remove credential of target device from all devices in subnet.
  *
- * @param[in] ctx Application context would be returned in result callback
+ * @param[in] ctx Application context to be returned in result callback
  * @param[in] waitTimeForOwnedDeviceDiscovery Maximum wait time for owned device discovery.(seconds)
  * @param[in] pTargetDev Device information to be revoked.
  * @param[in] resultCallback callback provided by API user, callback will be called when
@@ -221,18 +224,18 @@ OCStackResult SRPRemoveDevice(void* ctx,
                               OCProvisionResultCB resultCallback);
 
 /**
-* Function to device revocation
-* This function will remove credential of target device from all devices in subnet.
-*
-* @param[in] ctx Application context would be returned in result callback
-* @param[in] pOwnedDevList List of owned devices
-* @param[in] pTargetDev Device information to be revoked.
-* @param[in] resultCallback callback provided by API user, callback will be called when
-*            credential revocation is finished.
-* @return  OC_STACK_OK in case of success and other value otherwise.
-*          If OC_STACK_OK is returned, the caller of this API should wait for callback.
-*          OC_STACK_CONTINUE means operation is success but no request is need to be initiated.
-*/
+ * Function to device revocation
+ * This function will remove credential of target device from all devices in subnet.
+ *
+ * @param[in] ctx Application context to be returned in result callback
+ * @param[in] pOwnedDevList List of owned devices
+ * @param[in] pTargetDev Device information to be revoked.
+ * @param[in] resultCallback callback provided by API user, callback will be called when
+ *            credential revocation is finished.
+ * @return  OC_STACK_OK in case of success and other value otherwise.
+ *          If OC_STACK_OK is returned, the caller of this API should wait for callback.
+ *          OC_STACK_CONTINUE means operation is success but no request is need to be initiated.
+ */
 OCStackResult SRPRemoveDeviceWithoutDiscovery(void* ctx, const OCProvisionDev_t* pOwnedDevList,
                              const OCProvisionDev_t* pTargetDev, OCProvisionResultCB resultCallback);
 
@@ -240,7 +243,7 @@ OCStackResult SRPRemoveDeviceWithoutDiscovery(void* ctx, const OCProvisionDev_t*
  * Function to sync-up credential and ACL of the target device.
  * This function will remove credential and ACL of target device from all devices in subnet.
  *
- * @param[in] ctx Application context would be returned in result callback
+ * @param[in] ctx Application context to be returned in result callback
  * @param[in] waitTimeForOwnedDeviceDiscovery Maximum wait time for owned device discovery.(seconds)
  * @param[in] pTargetDev Device information to be revoked.
  * @param[in] resultCallback callback provided by API user, callback will be called when
index ef716c7..f02a375 100644 (file)
@@ -295,6 +295,21 @@ OCStackResult OCProvisionCredentials(void *ctx, OicSecCredType_t type, size_t ke
                                       const OCProvisionDev_t *pDev2,\r
                                       OCProvisionResultCB resultCallback);\r
 \r
+/**\r
+ * API to provision a certificate to a device.\r
+ *\r
+ * @param[in] ctx Application context returned in result callback.\r
+ * @param[in] pDev Pointer to OCProvisionDev_t instance, respresenting the device to be provsioned.\r
+ * @param[in] pemCert Certificate to provision, encoded as PEM\r
+ * @param[in] resultCallback callback provided by API user, callback will be called when\r
+ *            provisioning request receives a response from first resource server.\r
+ * @return OC_STACK_OK in case of success and other value otherwise.\r
+ */\r
+OCStackResult OCProvisionCertificate(void *ctx,\r
+                                     const OCProvisionDev_t *pDev,\r
+                                     const char* pemCert,\r
+                                     OCProvisionResultCB resultCallback);\r
+\r
 #ifdef MULTIPLE_OWNER\r
 /**\r
  * API to provision preconfigured PIN to device(NOT LIST).\r
@@ -388,16 +403,16 @@ OCStackResult OCRemoveDevice(void* ctx,
                              OCProvisionResultCB resultCallback);\r
 \r
 /**\r
-* Function to device revocation\r
-* This function will remove credential of target device from all devices in subnet.\r
-*\r
-* @param[in] ctx Application context would be returned in result callback\r
-* @param[in] waitTimeForOwnedDeviceDiscovery Maximum wait time for owned device discovery.(seconds)\r
-* @param[in] pTargetUuid Device information to be revoked.\r
-* @param[in] resultCallback callback provided by API user, callback will be called when\r
-*            credential revocation is finished.\r
+ * Function to device revocation\r
+ * This function will remove credential of target device from all devices in subnet.\r
+ *\r
+ * @param[in] ctx Application context would be returned in result callback\r
+ * @param[in] waitTimeForOwnedDeviceDiscovery Maximum wait time for owned device discovery.(seconds)\r
+ * @param[in] pTargetUuid Device information to be revoked.\r
+ * @param[in] resultCallback callback provided by API user, callback will be called when\r
+ *            credential revocation is finished.\r
  * @return  OC_STACK_OK in case of success and other value otherwise.\r
-*/\r
+ */\r
 OCStackResult OCRemoveDeviceWithUuid(void* ctx,\r
                                      unsigned short waitTimeForOwnedDeviceDiscovery,\r
                                      const OicUuid_t* pTargetUuid,\r
@@ -531,6 +546,17 @@ OCStackResult OCProvisionTrustCertChain(void *ctx, OicSecCredType_t type, uint16
  */\r
 OCStackResult OCSaveTrustCertChain(uint8_t *trustCertChain, size_t chainSize,\r
                                         OicEncodingType_t encodingType, uint16_t *credId);\r
+\r
+/**\r
+ * Function to save an identity certificate chain into Cred of SVR.\r
+ *\r
+ * @param[in] cert Certificate chain to be saved in Cred of SVR, PEM encoded, null terminated\r
+ * @param[in] key private key corresponding to the certificate, PEM encoded, null terminated\r
+ * @param[out] credId CredId of saved certificate chain in Cred of SVR.\r
+ * @return  OC_STACK_OK in case of success and other value otherwise.\r
+ */\r
+OCStackResult OCSaveOwnCertChain(char* cert, char* key, uint16_t *credId);\r
+\r
 /**\r
  * function to register callback, for getting notification for TrustCertChain change.\r
  *\r
index 0b2c4b3..4721c8f 100644 (file)
@@ -40,6 +40,7 @@
 #include "mbedtls/config.h"
 #include "mbedtls/pem.h"
 #include "mbedtls/x509_csr.h"
+#include "occertutility.h"
 
 #ifdef _MSC_VER
 #include <io.h>
@@ -69,6 +70,7 @@ extern "C"
 #define _33_PROVIS_DP_              33
 #define _34_CHECK_LINK_STATUS_      34
 #define _35_SAVE_ACL_               35
+#define _36_PROVIS_CERT_            36
 #define _40_UNLINK_PAIR_DEVS_       40
 #define _50_REMOVE_SELEC_DEV_       50
 #define _51_REMOVE_DEV_WITH_UUID_   51
@@ -105,6 +107,9 @@ static const OicSecPrm_t  SUPPORTED_PRMS[1] =
     PRM_PRE_CONFIGURED,
 };
 
+static char* TEST_CERT_NOT_BEFORE = "20170101000000"; // Not before field for certificates, in format YYYYMMDDhhmmss
+static char* TEST_CERT_NOT_AFTER = "20270101000000";  // + ten years
+
 // |g_ctx| means provision manager application context and
 // the following, includes |un/own_list|, could be variables, which |g_ctx| has,
 // for accessing all function(s) for these, they are declared on global domain
@@ -119,8 +124,13 @@ static int g_unown_cnt;
 static OCProvisionDev_t* g_mot_enable_list;
 static int g_mot_enable_cnt;
 #endif //MULTIPLE_OWNER
+char* g_caKeyPem;   /* Test CA private key */
+char* g_caCertPem;  /* Test CA certificate */
+uint16_t g_caCredId = 0;   /* ID of CA's OCF identity cert */
+char* g_csr;    /* Certificate signing request from device */
 
-static bool g_doneCB;
+static bool g_doneCB;    /* Set to true by the callback to indicate it completed. */
+static bool g_successCB; /* Set to true by the callback to indicate success. */
 #ifdef __WITH_TLS__
 static int secure_protocol = 1;
 static void setDevProtocol(OCProvisionDev_t* dev_lst);
@@ -138,6 +148,11 @@ static FILE* fopen_prvnMng(const char*, const char*);
 static int waitCallbackRet(void);
 static int selectTwoDiffNum(int*, int*, const int, const char*);
 
+/* At a few places in this file, warning 4028 is incorrectly produced, disable it for the whole file. */
+#ifdef _MSC_VER
+#pragma warning( disable : 4028)
+#endif
+
 // callback function(s) for provisioning client using C-level provisioning API
 static void ownershipTransferCB(void* ctx, size_t nOfRes, OCProvisionResult_t* arr, bool hasError)
 {
@@ -172,11 +187,30 @@ static void provisionCredCB(void* ctx, size_t nOfRes, OCProvisionResult_t* arr,
     if(!hasError)
     {
         OIC_LOG_V(INFO, TAG, "Provision Credential SUCCEEDED - ctx: %s", (char*) ctx);
+        g_successCB = true;
     }
     else
     {
         OIC_LOG_V(ERROR, TAG, "Provision Credential FAILED - ctx: %s", (char*) ctx);
         printResultList((const OCProvisionResult_t*) arr, nOfRes);
+        g_successCB = false;
+    }
+    g_doneCB = true;
+}
+
+/* Function of type OCProvisionResultCB from \resource\csdk\security\provisioning\include\pmtypes.h */
+void provisionTrustChainCB(void* ctx, int nOfRes, OCProvisionResult_t *arr, bool hasError)
+{
+    if (!hasError)
+    {
+        OIC_LOG_V(INFO, TAG, "Provision Credential SUCCEEDED - ctx: %s", (char*)ctx);
+        g_successCB = true;
+    }
+    else
+    {
+        OIC_LOG_V(ERROR, TAG, "Provision Credential FAILED - ctx: %s", (char*)ctx);
+        printResultList((const OCProvisionResult_t*)arr, nOfRes);
+        g_successCB = false;
     }
     g_doneCB = true;
 }
@@ -291,7 +325,7 @@ static void getCsrCB(void* ctx, size_t nOfRes, OCPMGetCsrResult_t* arr, bool has
         }
         else
         {
-            size_t lst_cnt;
+            size_t lst_cnt;\r
             for (lst_cnt = 0; nOfRes > lst_cnt; ++lst_cnt)
             {
                 printf("     [%" PRIuPTR "] ", lst_cnt + 1);
@@ -304,6 +338,56 @@ static void getCsrCB(void* ctx, size_t nOfRes, OCPMGetCsrResult_t* arr, bool has
     g_doneCB = true;
 }
 
+static void getCsrForCertProvCB(void* ctx, size_t nOfRes, OCPMGetCsrResult_t* arr, bool hasError)
+{
+    g_successCB = false;
+
+    if (!hasError)
+    {
+        if (nOfRes != 1)
+        {
+            OIC_LOG_V(ERROR, TAG, "getCsrForCertProvCB FAILED - ctx: %s", (char*)ctx);
+            goto exit;
+        }
+
+        if (arr[0].encoding == OIC_ENCODING_DER)
+        {
+            OCStackResult res = OCConvertDerCSRToPem((char*)arr[0].csr, arr[0].csrLen, &g_csr);
+            if (res != OC_STACK_OK)
+            {
+                OIC_LOG_V(ERROR, TAG, "getCsrForCertProvCB FAILED (CSR re-encoding failed) - error: %d, ctx: %s", res, (char*)ctx);
+                goto exit;
+            }
+            g_successCB = true;
+        }
+        else if(arr[0].encoding == OIC_ENCODING_PEM)
+        {
+            g_csr = (char*)OICCalloc(1, arr[0].csrLen);
+            if (g_csr == NULL)
+            {
+                OIC_LOG_V(ERROR, TAG, "getCsrForCertProvCB FAILED (memory allocation) - ctx: %s", (char*)ctx);
+                goto exit;
+            }
+
+            memcpy(g_csr, arr[0].csr, arr[0].csrLen);
+
+            OIC_LOG(INFO, TAG, "getCsrForCertProvCB success");
+            g_successCB = true;
+        }
+        else
+        {
+            OIC_LOG_V(ERROR, TAG, "getCsrForCertProvCB FAILED (unknown encoding) - ctx: %s", (char*)ctx);
+            goto exit;
+        }
+    }
+    else
+    {
+        OIC_LOG_V(ERROR, TAG, "getCsrForCertProvCB FAILED - ctx: %s", (char*)ctx);
+    }
+
+exit:
+    g_doneCB = true;
+}
 
 static void provisionDPCB(void* ctx, int nOfRes, OCProvisionResult_t* arr, bool hasError)
 {
@@ -783,6 +867,345 @@ static int provisionCred(void)
     return 0;
 }
 
+/*
+ *   Initialize the provisioning client for certificate provisioning.
+ *   This function:
+ *   1. Generates a root key pair for a CA.
+ *   2. Generates a self-signed root certificate for the CA public key.
+ *   3. Saves this root as a trust anchor locally.
+ *   4. Generate and store an IoTivity key and cert (issued from the CA root cert).
+ *      This is an EE cert the CA/OBT will use in DTLS.
+ *
+ *   @param[out] credid parameter for the ID of the CA credential
+ */
+static int setupCA()
+{
+    char* publicKey = NULL;
+    size_t publicKeyLen = 0;
+    size_t caKeyLen = 0;
+    char* serial = NULL;
+    size_t serialLen = 0;
+    size_t caCertLen = 0;
+    char* idPublicKey = NULL;
+    char* idKey = NULL;
+    char* idCert = NULL;
+    size_t idCertLen = 0;
+    size_t idKeyLen = 0;
+
+    if (g_caCredId == 0)
+    {
+        printf("Setting up CA for certificate provisioning\n");
+    }
+    else
+    {
+        printf("Skipping CA setup, already done\n");
+        return 0;
+    }
+
+    /* Create CA keypair, serial number */
+    OCStackResult res = OCGenerateKeyPair(&publicKey, &publicKeyLen, &g_caKeyPem, &caKeyLen);
+    if (res != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "OCGenerateKeyPair failed, error: %d", res);
+        goto exit;
+    }
+
+    res = OCGenerateRandomSerialNumber(&serial, &serialLen);
+    if (res != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "OCGenerateRandomSerialNumber failed, error: %d", res);
+        goto exit;
+    }
+
+    /* Create a CA certificate */
+    res = OCGenerateCACertificate(
+        "C=US, O=Open Connectivity Foundation, CN=IoTivity test code CA",  // subject
+        publicKey,
+        NULL,               // Issuer private key is null
+        g_caKeyPem,         // use CA's own key to create self-signed cert
+        serial,
+        TEST_CERT_NOT_BEFORE,
+        TEST_CERT_NOT_AFTER,
+        &g_caCertPem,
+        &caCertLen);
+    if (res != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "OCGenerateCACertificate failed, error: %d", res);
+        goto exit;
+    }
+
+    /* Set our own trust anchor so that we trust certs we've issued. */\r
+    res = OCSaveTrustCertChain((uint8_t*) g_caCertPem, caCertLen, OIC_ENCODING_PEM, &g_caCredId);
+    if (OC_STACK_OK != res)
+    {
+        OIC_LOG_V(ERROR, TAG, "OCSaveTrustCertChain error: %d", res);
+        goto exit;
+    }
+
+    /* Create identity certificate for use by the CA. */
+    res = OCGenerateKeyPair(&idPublicKey, &publicKeyLen, &idKey, &idKeyLen);
+    if (res != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "OCGenerateKeyPair failed, error: %d", res);
+        goto exit;
+    }
+
+    res = OCGenerateRandomSerialNumber(&serial, &serialLen);
+    if (res != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "OCGenerateRandomSerialNumber failed, error: %d", res);
+        goto exit;
+    }
+
+    OCUUIdentity deviceId = { 0 };
+    res = OCGetDeviceId(&deviceId);
+    if (res != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to get own UUID, error: %d", res);
+        goto exit;
+    }
+
+    OicUuid_t uuid = { 0 };
+    memcpy(uuid.id, deviceId.id, sizeof(uuid.id));
+
+    res = OCGenerateIdentityCertificate(
+        &uuid,
+        idPublicKey,
+        g_caCertPem,
+        g_caKeyPem,
+        serial,
+        TEST_CERT_NOT_BEFORE,
+        TEST_CERT_NOT_AFTER,
+        &idCert,
+        &idCertLen);
+    if (res != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to create identity cert for CA, error: %d", res);
+        goto exit;
+    }
+
+    uint16_t idCertCredId = 0;
+    res = OCSaveOwnCertChain(idCert, idKey, &idCertCredId);
+    if (res != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to save CA's identity cert & key, error: %d", res);
+        goto exit;
+    }
+
+exit:
+    OICFree(publicKey);
+    OICFree(serial);
+    OICFree(idPublicKey);
+    if (idKey != NULL)
+    {
+        OICClearMemory(idKey, idKeyLen);
+        OICFree(idKey);
+    }
+    OICFree(idCert);
+
+    if (res != OC_STACK_OK)
+    {
+        return -1;
+    }
+
+    return 0;
+}
+
+/*
+ * Create an identity certificate for a device, based on the information in its CSR. 
+ * Assumes the csr has already been validated wtih OCVerifyCSRSignature.
+ */
+static int createIdentityCertFromCSR(const char* caKeyPem, const char* caCertPem, char* csr, 
+    char** deviceCert)
+{
+    char* publicKey = NULL;
+    char* serial = NULL;
+    size_t serialLen;
+    OicUuid_t uuid = { 0 };
+    OCStackResult res = OC_STACK_ERROR;
+
+    res = OCGetUuidFromCSR(csr, &uuid);
+    if (res != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to get UUID from CSR, error: %d", res);
+        goto exit;
+    }
+    /* Note: a real OBT must make sure the UUID isn't already in use on the network. */
+
+    res = OCGetPublicKeyFromCSR(csr, &publicKey);
+    if (res != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to get public key from CSR, error: %d", res);
+        goto exit;
+    }
+
+    res = OCGenerateRandomSerialNumber(&serial, &serialLen);
+    if (res != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "OCGenerateRandomSerialNumber failed, error: %d", res);
+        goto exit;
+    }
+
+    size_t deviceCertLen;
+    res = OCGenerateIdentityCertificate(
+            &uuid,
+            publicKey,
+            caCertPem,
+            caKeyPem,
+            serial,
+            TEST_CERT_NOT_BEFORE,
+            TEST_CERT_NOT_AFTER,
+            deviceCert,
+            &deviceCertLen);
+
+    if(res != OC_STACK_OK)
+    {
+        OIC_LOG_V(ERROR, TAG, "OCGenerateIdentityCertificate failed, error: %d", res);
+        goto exit;
+    }
+
+exit:
+    OICFree(publicKey);
+    OICFree(serial);
+
+    if (res != OC_STACK_OK)
+    {
+        return -1;
+    }
+
+    return 0;
+}
+
+static int provisionCert(void)
+{
+    // make sure we own at least one device to provision
+    if (!g_own_list || g_own_cnt == 0)
+    {
+        printf("   > Owned Device List, to Provision Credentials, is Empty\n");
+        printf("   > Please Register Unowned Devices first, with [20] Menu\n");
+        return 0;  // normal case
+    }
+
+    // select device for provisioning certificate
+    int dev_num = 0;
+    for (; ; )
+    {
+        printf("   > Enter Device Number, for certificate provisioning: ");
+        for (int ret = 0; 1 != ret; )
+        {
+            ret = scanf("%d", &dev_num);
+            for (; 0x20 <= getchar(); );  // for removing overflow garbages
+                                          // '0x20<=code' is character region
+        }
+        if (0<dev_num && g_own_cnt >= dev_num)
+        {
+            break;
+        }
+        printf("     Entered Wrong Number. Please Enter Again\n");
+    }
+
+    OCProvisionDev_t* targetDevice = getDevInst((const OCProvisionDev_t*)g_own_list, dev_num);
+    if (targetDevice == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "Error, invalid device %d");
+        return -1;
+    }
+
+    // Install the CA trust anchor
+    if (setupCA() != 0)
+    {
+        printf("     Failed to setup CA\n");
+        return -1;
+    }
+
+    // Provision the CA root cert to the target device
+    printf("   > Saving root certificate (trust anchor) to selected device..\n");
+    g_doneCB = false;
+    OicSecCredType_t type = SIGNED_ASYMMETRIC_KEY;
+
+    OCStackResult rst = OCProvisionTrustCertChain((void*)g_ctx, type, g_caCredId, targetDevice, &provisionTrustChainCB);
+    if (OC_STACK_OK != rst)
+    {
+        OIC_LOG_V(ERROR, TAG, "OCProvisionTrustCertChain returned error: %d", rst);
+        return -1;
+    }
+
+    if (waitCallbackRet())  // input |g_doneCB| flag implicitly
+    {
+        OIC_LOG(ERROR, TAG, "OCProvisionTrustCertChain callback error");
+        return -1;
+    }
+    if (!g_successCB)
+    {
+        return -1;
+    }
+
+    // Request a CSR from the device, check the CSR signature
+    printf("   > Getting CSR from device..\n");
+    g_doneCB = false;
+    rst = OCGetCSRResource((void*)g_ctx, targetDevice, getCsrForCertProvCB);
+    if (OC_STACK_OK != rst)
+    {
+        OIC_LOG_V(ERROR, TAG, "OCGetCSRResource API error: %d", rst);
+        return -1;
+    }
+    if (waitCallbackRet())  // input |g_doneCB| flag implicitly
+    {
+        OIC_LOG(ERROR, TAG, "OCGetCSRResource callback error");
+        return -1;
+    }
+    if (!g_successCB)
+    {
+        return -1;
+    }
+
+    rst = OCVerifyCSRSignature(g_csr);
+    if (OC_STACK_OK != rst)
+    {
+        OIC_LOG(ERROR, TAG, "Failed to validate CSR signature");
+        OICFreeAndSetToNull(&g_csr);
+        return -1;
+    }
+
+    printf("   > Creating a certificate for the device..\n");
+    char* deviceCert;
+    int ret = createIdentityCertFromCSR(g_caKeyPem, g_caCertPem, g_csr, &deviceCert);
+    OICFreeAndSetToNull(&g_csr);
+    if (ret != 0)
+    {
+        OIC_LOG(ERROR, TAG, "Failed to generate certificate");
+        OICFree(deviceCert);
+        return -1;
+    }
+
+    //Provision the new cert
+    printf("   > Provisioning certificate credential to selected device..\n");
+    g_doneCB = false;
+    rst = OCProvisionCertificate((void *)g_ctx, targetDevice, deviceCert, provisionCredCB);
+    if (OC_STACK_OK != rst)
+    {
+        OIC_LOG_V(ERROR, TAG, "OCProvisionCertificate returned error: %d", rst);
+        OICFree(deviceCert);
+        return -1;
+    }
+    if (waitCallbackRet())  // input |g_doneCB| flag implicitly
+    {
+        OIC_LOG(ERROR, TAG, "OCProvisionCertificate callback error");
+        OICFree(deviceCert);
+        return -1;
+    }
+    if (!g_successCB)
+    {
+        OICFree(deviceCert);
+        return -1;
+    }
+
+    printf("   > Provisioned certificate crendentials\n");
+    OICFree(deviceCert);
+
+    return 0;
+}
+
 static int provisionAcl(void)
 {
     // check |own_list| for provisioning access control list
@@ -923,19 +1346,9 @@ static int provisionDirectPairing(void)
     // for error checking, the return value saved and printed
     g_doneCB = false;
     printf("   Atempt Direct-Pairing Provisioning (PIN : [%s])..\n", (char*)pconf.pin.val);
-#if defined(_MSC_VER)
-#pragma warning(push)
-    // Suppress C4028: formal parameter 2 different from declaration. getDevInst returns
-    // OCProvisionDev_t *, which OCProvisionDirectPairing takes as its second parameter.
-    // The compiler doesn't understand this.
-#pragma warning(disable:4028)
-#endif
     OCStackResult rst = OCProvisionDirectPairing((void*) g_ctx,
-                                       getDevInst((const OCProvisionDev_t *) g_own_list, dev_num),
+                                       getDevInst((const OCProvisionDev_t*) g_own_list, dev_num),
                                        &pconf, provisionDPCB);
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
     if(OC_STACK_OK != rst)
     {
         OIC_LOG_V(ERROR, TAG, "OCProvisionDirectPairing API error: %d", rst);
@@ -2553,7 +2966,9 @@ static void printMenu(void)
     printf("** 32. Provision the Selected Access Control List(ACL)\n");
     printf("** 33. Provision Direct-Pairing Configuration\n");
     printf("** 34. Check Linked Status of the Selected Device on PRVN DB\n");
-    printf("** 35. Save the Selected Access Control List(ACL) into local SVR DB\n\n");
+    printf("** 35. Save the Selected Access Control List(ACL) into local SVR DB\n");
+    printf("** 36. Provision certificate credential\n\n");
+
 
     printf("** [D] UNLINK PAIRWISE THINGS\n");
     printf("** 40. Unlink Pairwise Things\n\n");
@@ -2729,6 +3144,12 @@ int main()
                 OIC_LOG(ERROR, TAG, "_35_SAVE_ACL_: error");
             }
             break;
+        case _36_PROVIS_CERT_:
+            if (provisionCert())
+            {
+                OIC_LOG(ERROR, TAG, "_36_PROVIS_CERT_: error");
+            }
+            break;
         case _40_UNLINK_PAIR_DEVS_:
             if(unlinkPairwise())
             {
index 91cf836..b14f0c4 100644 (file)
@@ -420,10 +420,31 @@ OCStackResult OCProvisionCredentials(void *ctx, OicSecCredType_t type, size_t ke
                                       OCProvisionResultCB resultCallback)
 {
     return SRPProvisionCredentials(ctx, type, keySize,
-                                      pDev1, pDev2, resultCallback);
+                                      pDev1, pDev2, NULL, resultCallback);
 
 }
 
+#if defined(__WITH_DTLS__) || defined(__WITH_TLS__)
+/**\r
+* API to provision a certificate to a device.\r
+*\r
+* @param[in] ctx Application context returned in result callback.\r
+* @param[in] pDev Pointer to OCProvisionDev_t instance, respresenting the device to be provsioned.\r
+* @param[in] pemCert Certificate to provision, encoded as PEM\r
+* @param[in] resultCallback callback provided by API user, callback will be called when\r
+*            provisioning request receives a response from first resource server.\r
+* @return OC_STACK_OK in case of success and other value otherwise.\r
+*/
+OCStackResult OCProvisionCertificate(void *ctx,\r
+    const OCProvisionDev_t *pDev,\r
+    const char* pemCert,\r
+    OCProvisionResultCB resultCallback)
+{
+    return SRPProvisionCredentials(ctx, SIGNED_ASYMMETRIC_KEY, 0,
+        pDev, NULL, pemCert, resultCallback);
+}
+#endif
+
 /**
  * this function sends Direct-Pairing Configuration to a device.
  *
@@ -1166,7 +1187,7 @@ OCStackResult OCProvisionPairwiseDevices(void* ctx, OicSecCredType_t type, size_
     link->currentCountResults = 0;
     link->resArr = (OCProvisionResult_t*) OICMalloc(sizeof(OCProvisionResult_t)*noOfResults);
     res = SRPProvisionCredentials(link, type, keySize,
-                                     pDev1, pDev2, &ProvisionCredsCB);
+                                     pDev1, pDev2, NULL, &ProvisionCredsCB);
     if (res != OC_STACK_OK)
     {
         OICFree(link->resArr);
@@ -1416,6 +1437,29 @@ OCStackResult OCSaveTrustCertChain(uint8_t *trustCertChain, size_t chainSize,
     return SRPSaveTrustCertChain(trustCertChain, chainSize, encodingType, credId);
 }
 
+/**\r
+ * Function to save an identity certificate chain into Cred of SVR.\r
+ *\r
+ * @param[in] cert Certificate chain to be saved in Cred of SVR, PEM encoded, null terminated\r
+ * @param[in] key key corresponding to the certificate, PEM encoded, null terminated\r
+ * @param[out] credId CredId of saved certificate chain in Cred of SVR.\r
+ * @return  OC_STACK_OK in case of success and other value otherwise.\r
+ */\r
+OCStackResult OCSaveOwnCertChain(char* cert, char* key, uint16_t *credId)
+{
+    OicSecKey_t ownCert = { 0 };
+    ownCert.data = (uint8_t*) cert;
+    ownCert.len = strlen(cert) + 1;
+    ownCert.encoding = OIC_ENCODING_PEM;
+
+    OicSecKey_t ownKey = { 0 };
+    ownKey.data = (uint8_t*) key;
+    ownKey.len = strlen(key) + 1;
+    ownKey.encoding = OIC_ENCODING_PEM;
+
+    return SRPSaveOwnCertChain(&ownCert, &ownKey, credId);
+}
+
 /**
  * function to register notifier for Trustcertchain change.
  *
index 3d47ee8..1b309a8 100644 (file)
@@ -297,7 +297,6 @@ static OCStackApplicationResult provisionCredentialCB1(void *ctx, OCDoHandle UNU
     return OC_STACK_DELETE_TRANSACTION;
 }
 
-
 /**
  * Internal function for handling credential generation and sending credential to resource server.
  *
@@ -451,10 +450,41 @@ static OCStackApplicationResult provisionCertCB(void *ctx, OCDoHandle UNUSED,
     return OC_STACK_DELETE_TRANSACTION;
 }
 
+static OCStackApplicationResult provisionIdentityCertCB(void *ctx, OCDoHandle UNUSED,
+    OCClientResponse *clientResponse)
+{
+    // Just call the callback provided to SRProvisionCredentials
+    VERIFY_NOT_NULL_RETURN(TAG, ctx, ERROR, OC_STACK_DELETE_TRANSACTION);
+    CredentialData_t* credData = (CredentialData_t *)ctx;
+    (void)UNUSED;
+    bool hasError;
+
+    // We expect OC_STACK_RESOURCE_CHANGED, anything else is an error
+    if (clientResponse && (OC_STACK_RESOURCE_CHANGED == clientResponse->result))
+    {
+        hasError = false;
+    }
+    else
+    {
+        hasError = true;
+    }
+
+    OCProvisionResultCB resultCallback = credData->resultCallback;
+    VERIFY_NOT_NULL_RETURN(TAG, resultCallback, ERROR, OC_STACK_DELETE_TRANSACTION);
+
+    ((OCProvisionResultCB)(resultCallback))(credData->ctx, credData->numOfResults,
+        credData->resArr, hasError);
+
+    OICFree(credData);
+
+    return OC_STACK_DELETE_TRANSACTION;
+
+}
+
 OCStackResult SRPProvisionTrustCertChain(void *ctx, OicSecCredType_t type, uint16_t credId,
         const OCProvisionDev_t *selectedDeviceInfo, OCProvisionResultCB resultCallback)
 {
-    OIC_LOG(INFO, TAG, "In SRPProvisionTrustCertChain");
+    OIC_LOG(INFO, TAG, "IN SRPProvisionTrustCertChain");
     VERIFY_NOT_NULL_RETURN(TAG, selectedDeviceInfo, ERROR,  OC_STACK_INVALID_PARAM);
     VERIFY_NOT_NULL_RETURN(TAG, resultCallback, ERROR,  OC_STACK_INVALID_CALLBACK);
     if (SIGNED_ASYMMETRIC_KEY != type)
@@ -478,7 +508,7 @@ OCStackResult SRPProvisionTrustCertChain(void *ctx, OicSecCredType_t type, uint1
         return OC_STACK_NO_MEMORY;
     }
     secPayload->base.type = PAYLOAD_TYPE_SECURITY;
-    int secureFlag = 0;
+    int secureFlag = 1; /* Don't send the private key to the device, if it happens to be present */
     if(OC_STACK_OK != CredToCBORPayload(trustCertChainCred, &secPayload->securityData, &secPayload->payloadSize, secureFlag))
     {
         DeleteCredList(trustCertChainCred);
@@ -542,6 +572,9 @@ OCStackResult SRPProvisionTrustCertChain(void *ctx, OicSecCredType_t type, uint1
     }
 
     VERIFY_SUCCESS_RETURN(TAG, (OC_STACK_OK == ret), ERROR, OC_STACK_ERROR);
+
+    OIC_LOG(INFO, TAG, "OUT SRPProvisionTrustCertChain");
+
     return OC_STACK_OK;
 }
 
@@ -560,12 +593,12 @@ OCStackResult SRPSaveTrustCertChain(uint8_t *trustCertChain, size_t chainSize,
     res = GetDoxmDeviceID(&cred->subject);
     if (OC_STACK_OK != res)
     {
-        OIC_LOG(ERROR, TAG, "Cann't get the device id(GetDoxmDeviceID)");
+        OIC_LOG(ERROR, TAG, "Can't get the device id(GetDoxmDeviceID)");
         DeleteCredList(cred);
         return res;
     }
 
-    cred->credUsage= (char *)OICCalloc(1, strlen(TRUST_CA)+1 );
+    cred->credUsage= (char *)OICCalloc(1, strlen(TRUST_CA) + 1);
     VERIFY_NOT_NULL_RETURN(TAG, cred->credUsage, ERROR, OC_STACK_NO_MEMORY);
     OICStrcpy(cred->credUsage, strlen(TRUST_CA) + 1, TRUST_CA);
 
@@ -577,12 +610,18 @@ OCStackResult SRPSaveTrustCertChain(uint8_t *trustCertChain, size_t chainSize,
         VERIFY_NOT_NULL_RETURN(TAG, cred->optionalData.data, ERROR, OC_STACK_NO_MEMORY);
         cred->optionalData.len = chainSize + 1;
     }
-    else
+    else if (encodingType == OIC_ENCODING_DER)
     {
         cred->optionalData.data = (uint8_t *)OICCalloc(1, chainSize);
         VERIFY_NOT_NULL_RETURN(TAG, cred->optionalData.data, ERROR, OC_STACK_NO_MEMORY);
         cred->optionalData.len = chainSize;
     }
+    else
+    {
+        OIC_LOG_V(ERROR, TAG, "Unknown encoding in %s", __func__);
+        DeleteCredList(cred);
+        return OC_STACK_INVALID_PARAM;
+    }
     memcpy(cred->optionalData.data, trustCertChain, chainSize);
     cred->optionalData.encoding = encodingType;
     cred->optionalData.revstat = false;
@@ -610,14 +649,15 @@ OCStackResult SRPSaveTrustCertChain(uint8_t *trustCertChain, size_t chainSize,
     return res;
 }
 
-OCStackResult SRPSaveOwnCertChain(OicSecKey_t * cert, OicSecKey_t * key, uint16_t *credId)
+static OCStackResult saveCertChain(OicSecKey_t * cert, OicSecKey_t * key, uint16_t *credId, const char* usage)
 {
-    OIC_LOG_V(DEBUG, TAG, "In %s", __func__);
+    OIC_LOG_V(DEBUG, TAG, "IN %s", __func__);
     VERIFY_NOT_NULL_RETURN(TAG, cert, ERROR,  OC_STACK_INVALID_PARAM);
     VERIFY_NOT_NULL_RETURN(TAG, cert->data, ERROR,  OC_STACK_INVALID_PARAM);
     VERIFY_NOT_NULL_RETURN(TAG, key, ERROR,  OC_STACK_INVALID_PARAM);
     VERIFY_NOT_NULL_RETURN(TAG, key->data, ERROR,  OC_STACK_INVALID_PARAM);
     VERIFY_NOT_NULL_RETURN(TAG, credId, ERROR,  OC_STACK_INVALID_PARAM);
+    VERIFY_NOT_NULL_RETURN(TAG, usage, ERROR, OC_STACK_INVALID_PARAM);
 
     OCStackResult res = OC_STACK_ERROR;
 
@@ -634,9 +674,9 @@ OCStackResult SRPSaveOwnCertChain(OicSecKey_t * cert, OicSecKey_t * key, uint16_
         return res;
     }
 
-    cred->credUsage= (char *)OICCalloc(1, strlen(PRIMARY_CERT)+1 );
+    cred->credUsage= (char *)OICCalloc(1, strlen(usage) + 1);
     VERIFY_NOT_NULL_RETURN(TAG, cred->credUsage, ERROR, OC_STACK_NO_MEMORY);
-    OICStrcpy(cred->credUsage, strlen(PRIMARY_CERT) + 1, PRIMARY_CERT) ;
+    OICStrcpy(cred->credUsage, strlen(usage) + 1, usage);
 
     cred->credType = SIGNED_ASYMMETRIC_KEY;
 
@@ -662,15 +702,22 @@ OCStackResult SRPSaveOwnCertChain(OicSecKey_t * cert, OicSecKey_t * key, uint16_
     }
     *credId = cred->credId;
 
-    OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+    OIC_LOG_V(DEBUG, TAG, "OUT %s", __func__);
 
     return res;
 }
+
+OCStackResult SRPSaveOwnCertChain(OicSecKey_t * cert, OicSecKey_t * key, uint16_t *credId)
+{
+    return saveCertChain(cert, key, credId, PRIMARY_CERT);
+}
+
 #endif // __WITH_DTLS__ || __WITH_TLS__
 
 OCStackResult SRPProvisionCredentials(void *ctx, OicSecCredType_t type, size_t keySize,
                                       const OCProvisionDev_t *pDev1,
                                       const OCProvisionDev_t *pDev2,
+                                      const char* pemCert,
                                       OCProvisionResultCB resultCallback)
 {
     VERIFY_NOT_NULL_RETURN(TAG, pDev1, ERROR,  OC_STACK_INVALID_PARAM);
@@ -680,13 +727,13 @@ OCStackResult SRPProvisionCredentials(void *ctx, OicSecCredType_t type, size_t k
     }
     if (!resultCallback)
     {
-        OIC_LOG(INFO, TAG, "SRPUnlinkDevices : NULL Callback");
+        OIC_LOG(INFO, TAG, "SRPProvisionCredentials: NULL Callback");
         return OC_STACK_INVALID_CALLBACK;
     }
     if (SYMMETRIC_PAIR_WISE_KEY == type &&
         0 == memcmp(&pDev1->doxm->deviceID, &pDev2->doxm->deviceID, sizeof(OicUuid_t)))
     {
-        OIC_LOG(INFO, TAG, "SRPUnlinkDevices : Same device ID");
+        OIC_LOG(INFO, TAG, "SRPProvisionCredentials : Same device ID");
         return OC_STACK_INVALID_PARAM;
     }
 
@@ -778,6 +825,55 @@ OCStackResult SRPProvisionCredentials(void *ctx, OicSecCredType_t type, size_t k
             VERIFY_SUCCESS_RETURN(TAG, (res==OC_STACK_OK), ERROR, OC_STACK_ERROR);
             return res;
         }
+        case SIGNED_ASYMMETRIC_KEY:
+        {
+            /* pDev1 is the device to be provisioned, checked non-null above */
+            /* pDev2 is not used, should be NULL */
+            /* size param is not used. */
+            /* pemCert is the cerficiate to be provisioned */
+            VERIFY_NOT_NULL_RETURN(TAG, pemCert, ERROR, OC_STACK_INVALID_PARAM);
+
+            OicSecKey_t deviceCert = { 0 };
+            deviceCert.data = (uint8_t*) pemCert; /* Casting away const is OK here */
+            deviceCert.len = strlen(pemCert) + 1;
+            deviceCert.encoding = OIC_ENCODING_PEM;
+
+            /* Create a credential object */
+            OicSecCred_t* cred =  GenerateCredential(&pDev1->doxm->deviceID, SIGNED_ASYMMETRIC_KEY,
+                    &deviceCert, NULL, // oic.sec.cred.publicdata = deviceCert, .privatedata = NULL
+                    &provTooldeviceID, NULL); // rowner is the provisioning tool and no eowner
+            VERIFY_NOT_NULL_RETURN(TAG, cred, ERROR, OC_STACK_ERROR);
+
+            cred->publicData.encoding = OIC_ENCODING_PEM;
+            cred->credUsage = OICStrdup(PRIMARY_CERT);
+
+            /* Create credential data (used by the response handler provisionIdentityCertCB and freed there) */
+            CredentialData_t *credData = (CredentialData_t *)OICCalloc(1, sizeof(CredentialData_t)); 
+            if (NULL == credData)
+            {
+                DeleteCredList(cred);
+                OIC_LOG(ERROR, TAG, "Memory allocation problem");
+                return OC_STACK_NO_MEMORY;
+            }
+            credData->deviceInfo1 = pDev1;
+            credData->deviceInfo2 = NULL;
+            credData->credInfo = cred;
+            credData->ctx = ctx;
+            credData->credInfoFirst = cred;
+            credData->numOfResults = 0;
+            credData->resultCallback = resultCallback;
+            credData->resArr = NULL;
+
+            /* Note: the callback of type OCClientResponseHandler, thin wrapper that calls resultCallback */
+            OCStackResult res = provisionCredentials(cred, pDev1, credData, &provisionIdentityCertCB); 
+            if (res != OC_STACK_OK)
+            {
+                OICFree(credData);
+            }
+
+            DeleteCredList(cred);
+            return OC_STACK_OK;
+        }
         default:
         {
             OIC_LOG(ERROR, TAG, "Invalid option.");
@@ -2722,44 +2818,46 @@ static void registerResultForGetCSRResourceCB(GetCsrData_t *getCsrData,
  * @return  OC_STACK_DELETE_TRANSACTION to delete the transaction
  *          and  OC_STACK_KEEP_TRANSACTION to keep it.
  */
-static OCStackApplicationResult SRPGetCSRResourceCB(void *ctx, OCDoHandle UNUSED,
-                                                    OCClientResponse *clientResponse)
-{
-    OIC_LOG_V(INFO, TAG, "Inside SRPGetCSRResourceCB.");
-    OC_UNUSED(UNUSED);
-    VERIFY_NOT_NULL_RETURN(TAG, ctx, ERROR, OC_STACK_DELETE_TRANSACTION);
-    GetCsrData_t *getCsrData = (GetCsrData_t*)ctx;
-    OCGetCSRResultCB resultCallback = getCsrData->resultCallback;
-
-    if (clientResponse)
-    {
-        if (OC_STACK_OK == clientResponse->result)
-        {
-            uint8_t *payload = ((OCSecurityPayload*)clientResponse->payload)->securityData;
-            size_t size = ((OCSecurityPayload*)clientResponse->payload)->payloadSize;
-
-            OIC_LOG_BUFFER(DEBUG, TAG, payload, size);
-
-            registerResultForGetCSRResourceCB(getCsrData, OC_STACK_OK, payload, size);
-        }
-    }
-    else
-    {
-        registerResultForGetCSRResourceCB(getCsrData, OC_STACK_ERROR, NULL, 0);
-    }
-
-    resultCallback(getCsrData->ctx, getCsrData->numOfResults,
-                   getCsrData->resArr,
-                   false);
-    OIC_LOG_V(ERROR, TAG, "SRPGetCSRResourceCB received Null clientResponse");
-    for (size_t i = 0; i < getCsrData->numOfResults; i++)
-    {
-        OICFree(getCsrData->resArr[i].csr);
-    }
-    OICFree(getCsrData->resArr);
-    OICFree(getCsrData);
-
-    return OC_STACK_DELETE_TRANSACTION;
+static OCStackApplicationResult SRPGetCSRResourceCB(void *ctx, OCDoHandle UNUSED,\r
+    OCClientResponse *clientResponse)\r
+{\r
+    size_t i = 0;\r
+    OIC_LOG_V(INFO, TAG, "IN %s", __func__);\r
+    (void)UNUSED;\r
+    VERIFY_NOT_NULL_RETURN(TAG, ctx, ERROR, OC_STACK_DELETE_TRANSACTION);\r
+    GetCsrData_t *getCsrData = (GetCsrData_t*)ctx;\r
+    OCGetCSRResultCB resultCallback = getCsrData->resultCallback;\r
+\r
+    if (clientResponse)\r
+    {\r
+        if (OC_STACK_OK == clientResponse->result)\r
+        {\r
+            uint8_t *payload = ((OCSecurityPayload*)clientResponse->payload)->securityData;\r
+            size_t size = ((OCSecurityPayload*)clientResponse->payload)->payloadSize;\r
+\r
+            OIC_LOG_BUFFER(DEBUG, TAG, payload, size);\r
+\r
+            registerResultForGetCSRResourceCB(getCsrData, OC_STACK_OK, payload, size);\r
+        }\r
+    }\r
+    else\r
+    {\r
+        registerResultForGetCSRResourceCB(getCsrData, OC_STACK_ERROR, NULL, 0);\r
+    }\r
+\r
+    ((OCGetCSRResultCB)(resultCallback))(getCsrData->ctx, getCsrData->numOfResults,\r
+        getCsrData->resArr,\r
+        false);\r
+    OIC_LOG_V(ERROR, TAG, "%s: received Null clientResponse", __func__);\r
+    for (i = 0; i < getCsrData->numOfResults; i++)\r
+    {\r
+        OICFree(getCsrData->resArr[i].csr);\r
+    }\r
+    OICFree(getCsrData->resArr);\r
+    OICFree(getCsrData);\r
+    OIC_LOG_V(INFO, TAG, "OUT %s", __func__);\r
+\r
+    return OC_STACK_DELETE_TRANSACTION;\r
 }
 
 
index 81b38e8..7b7862c 100644 (file)
@@ -93,27 +93,27 @@ TEST(SRPProvisionCredentialsTest, NullDevice1)
 {
     EXPECT_EQ(OC_STACK_INVALID_PARAM, SRPProvisionCredentials(NULL, credType,
                                                               OWNER_PSK_LENGTH_128, NULL,
-                                                              &pDev2, &provisioningCB));
+                                                              &pDev2, NULL, &provisioningCB));
 }
 
 TEST(SRPProvisionCredentialsTest, SamelDeviceId)
 {
     EXPECT_EQ(OC_STACK_INVALID_PARAM, SRPProvisionCredentials(NULL, credType,
                                                               OWNER_PSK_LENGTH_128, &pDev1,
-                                                              &pDev1, &provisioningCB));
+                                                              &pDev1, NULL, &provisioningCB));
 }
 
 TEST(SRPProvisionCredentialsTest, NullCallback)
 {
     EXPECT_EQ(OC_STACK_INVALID_CALLBACK, SRPProvisionCredentials(NULL, credType,
                                                                  OWNER_PSK_LENGTH_128,
-                                                                 &pDev1, &pDev2, NULL));
+                                                                 &pDev1, &pDev2, NULL, NULL));
 }
 
 TEST(SRPProvisionCredentialsTest, InvalidKeySize)
 {
     EXPECT_EQ(OC_STACK_INVALID_PARAM, SRPProvisionCredentials(NULL, credType,
-                                                                0, &pDev1, &pDev2,
+                                                                0, &pDev1, &pDev2, NULL,
                                                                 &provisioningCB));
 }
 
index 1578551..f89d83b 100755 (executable)
@@ -65,6 +65,7 @@
 
 #if defined(__WITH_DTLS__) || defined (__WITH_TLS__)
 #include <mbedtls/ssl_ciphersuites.h>
+#include "mbedtls/pk.h"
 #endif
 
 #define TAG  "OIC_SRM_CREDL"
@@ -579,9 +580,57 @@ static CborError DeserializeSecOptFromCbor(CborValue *rootMap, OicSecOpt_t *valu
     return cborFindResult;
 }
 
+/* Produce debugging output for all credentials, output metadata. */
+static void logCredMetadata()
+{
+#if defined(TB_LOG)
+    OicSecCred_t * temp = NULL;
+    size_t count = 0;
+    char uuidString[UUID_STRING_SIZE];
+    OicUuid_t ownUuid;
+
+    OIC_LOG_V(DEBUG, TAG, "IN %s:", __func__);
+
+    if (GetDoxmDeviceID(&ownUuid) == OC_STACK_OK && OCConvertUuidToString(ownUuid.id, uuidString))
+    {
+        OIC_LOG_V(DEBUG, TAG, "Own UUID: %s", uuidString);
+    }
+
+    LL_FOREACH(gCred, temp)
+    {
+        count++;
+        OIC_LOG(DEBUG, TAG, " ");
+        OIC_LOG_V(DEBUG, TAG, "Cred ID: %d", temp->credId);
+        if (OCConvertUuidToString(temp->subject.id, uuidString))
+        {
+            OIC_LOG_V(DEBUG, TAG, "Subject UUID: %s", uuidString);
+        }
+        OIC_LOG_V(DEBUG, TAG, "Cred Type: %d", temp->credType);
+        OIC_LOG_V(DEBUG, TAG, "privateData length: %d, encoding: %d", temp->privateData.len, temp->privateData.encoding);
+
+#if defined(__WITH_DTLS__) || defined(__WITH_TLS__)
+        OIC_LOG_V(DEBUG, TAG, "publicData length: %d, encoding: %d", temp->publicData.len, temp->publicData.encoding);           
+        if (temp->credUsage)
+        {
+            OIC_LOG_V(DEBUG, TAG, "credUsage: %s", temp->credUsage);
+        }
+
+        OIC_LOG_V(DEBUG, TAG, "optionalData length: %d, encoding: %d", temp->optionalData.len, temp->optionalData.encoding);
+#endif
+
+    }
+
+    OIC_LOG_V(DEBUG, TAG, "Found %d credentials.", count);
+
+    OIC_LOG_V(DEBUG, TAG, "OUT %s:", __func__);
+#endif
+}
+
+
 OCStackResult CredToCBORPayload(const OicSecCred_t *credS, uint8_t **cborPayload,
                                 size_t *cborSize, int secureFlag)
 {
+    OIC_LOG_V(DEBUG, TAG, "IN %s:", __func__);
     if (NULL == credS || NULL == cborPayload || NULL != *cborPayload || NULL == cborSize)
     {
         return OC_STACK_INVALID_PARAM;
@@ -850,6 +899,8 @@ exit:
         ret = OC_STACK_ERROR;
     }
 
+    OIC_LOG_V(DEBUG, TAG, "OUT %s:", __func__);
+
     return ret;
 }
 
@@ -1337,6 +1388,9 @@ static bool UpdatePersistentStorage(const OicSecCred_t *cred)
     }
 
     OIC_LOG(DEBUG, TAG, "OUT Cred UpdatePersistentStorage");
+
+    logCredMetadata();
+
     return ret;
 }
 
@@ -2101,7 +2155,7 @@ static OCEntityHandlerResult HandlePostRequest(OCEntityHandlerRequest * ehReques
 #endif//__WITH_DTLS__
     }
 
-    if (OC_EH_CHANGED != ret)
+    if (OC_EH_CHANGED != ret && cred != NULL)
     {
         if(OC_STACK_OK != RemoveCredential(&cred->subject))
         {
@@ -2443,15 +2497,16 @@ OicSecCred_t* GetCredEntryByCredId(const uint16_t credId)
                 cred->privateData.encoding = tmpCred->privateData.encoding;
             }
 #if defined(__WITH_X509__) || defined(__WITH_TLS__)
-            else if (tmpCred->publicData.data)
+            if (tmpCred->publicData.data)
             {
                 cred->publicData.data = (uint8_t *)OICCalloc(1, tmpCred->publicData.len);
                 VERIFY_NOT_NULL(TAG, cred->publicData.data, ERROR);
 
                 memcpy(cred->publicData.data, tmpCred->publicData.data, tmpCred->publicData.len);
                 cred->publicData.len = tmpCred->publicData.len;
+                cred->publicData.encoding = tmpCred->publicData.encoding;
             }
-            else if (tmpCred->optionalData.data)
+            if (tmpCred->optionalData.data)
             {
                 cred->optionalData.data = (uint8_t *)OICCalloc(1, tmpCred->optionalData.len);
                 VERIFY_NOT_NULL(TAG, cred->optionalData.data, ERROR);
@@ -2461,7 +2516,6 @@ OicSecCred_t* GetCredEntryByCredId(const uint16_t credId)
                 cred->optionalData.encoding = tmpCred->optionalData.encoding;
                 cred->optionalData.revstat= tmpCred->optionalData.revstat;
             }
-
             if (tmpCred->credUsage)
             {
                 cred->credUsage = OICStrdup(tmpCred->credUsage);
@@ -2803,6 +2857,7 @@ void GetDerCaCert(ByteArray_t * crt, const char * usage)
     LL_FOREACH(gCred, temp)
     {
         if ((SIGNED_ASYMMETRIC_KEY == temp->credType) &&
+            (temp->credUsage != NULL) &&
             (0 == strcmp(temp->credUsage, usage)) && (false == temp->optionalData.revstat))
         {
             if(OIC_ENCODING_BASE64 == temp->optionalData.encoding)
@@ -2857,6 +2912,7 @@ void GetDerOwnCert(ByteArray_t * crt, const char * usage)
     LL_FOREACH(gCred, temp)
     {
         if (SIGNED_ASYMMETRIC_KEY == temp->credType &&
+            temp->credUsage != NULL &&
             0 == strcmp(temp->credUsage, usage))
         {
             crt->data = OICRealloc(crt->data, crt->len + temp->publicData.len);
@@ -2887,13 +2943,58 @@ void GetDerKey(ByteArray_t * key, const char * usage)
     LL_FOREACH(gCred, temp)
     {
         if ((SIGNED_ASYMMETRIC_KEY == temp->credType || ASYMMETRIC_KEY == temp->credType) && 
-            NULL != temp->credUsage && 
+            NULL != temp->credUsage &&
             0 == strcmp(temp->credUsage, usage))
         {
-            key->data = OICRealloc(key->data, key->len + temp->privateData.len);
-            memcpy(key->data + key->len, temp->privateData.data, temp->privateData.len);
-            key->len += temp->privateData.len;
-            OIC_LOG_V(DEBUG, TAG, "Key for %s found", usage);
+            
+            if (temp->privateData.encoding == OIC_ENCODING_PEM)
+            {
+                /* Convert PEM to DER */
+                mbedtls_pk_context ctx;
+                mbedtls_pk_init(&ctx);
+
+                int ret = mbedtls_pk_parse_key(&ctx, temp->privateData.data, temp->privateData.len, NULL, 0);
+                if (ret != 0)
+                {
+                    mbedtls_pk_free(&ctx);
+                    OIC_LOG_V(ERROR, TAG, "Key for %s found, but failed to convert from PEM to DER (while reading PEM)", usage);
+                    return;
+                }
+
+                key->data = OICRealloc(key->data, key->len + temp->privateData.len);
+                if (key->data == NULL)
+                {
+                    mbedtls_pk_free(&ctx);
+                    OIC_LOG(ERROR, TAG, "Realloc failed to increase key->data length");
+                    return;
+                }
+
+                key->len += temp->privateData.len;
+                ret = mbedtls_pk_write_key_der(&ctx, key->data, key->len);
+                if (ret < 1) /* return value is the number of bytes written, or error */
+                {
+                    mbedtls_pk_free(&ctx);
+                    key->len = 0;
+                    OIC_LOG_V(ERROR, TAG, "Key for %s found, but failed to convert from PEM to DER (while writing DER)", usage);
+                    return;
+                }
+                key->data = OICRealloc(key->data, ret);
+                key->len = ret;
+                break;
+                
+            }
+            else if(temp->privateData.encoding == OIC_ENCODING_DER)
+            {
+                key->data = OICRealloc(key->data, key->len + temp->privateData.len);
+                memcpy(key->data + key->len, temp->privateData.data, temp->privateData.len);
+                key->len += temp->privateData.len;
+                OIC_LOG_V(DEBUG, TAG, "Key for %s found", usage);
+                break;
+            }
+            else
+            {
+                OIC_LOG_V(WARNING, TAG, "Key for %s found, but it has an unknown encoding", usage);
+            }
         }
     }
     if(0 == key->len)
@@ -2931,7 +3032,7 @@ void InitCipherSuiteListInternal(bool * list, const char * usage)
             }
             case SIGNED_ASYMMETRIC_KEY:
             {
-                if (0 == strcmp(temp->credUsage, usage))
+                if (NULL != temp->credUsage && 0 == strcmp(temp->credUsage, usage))
                 {
                     list[1] = true;
                     OIC_LOG_V(DEBUG, TAG, "SIGNED_ASYMMETRIC_KEY found for %s", usage);
index 4ced39a..7161c9e 100644 (file)
 #include "mbedtls/entropy.h"
 #include "mbedtls/ctr_drbg.h"
 #include "mbedtls/x509_csr.h"
+#include "mbedtls/oid.h"
 #include "mbedtls/x509_crt.h"
 #include "mbedtls/oid.h"
+#include "mbedtls/pem.h"
+#include "mbedtls/base64.h"
 
 #ifndef NDEBUG
 #include "mbedtls/debug.h"
@@ -610,4 +613,216 @@ OCStackResult OCGenerateRoleCertificate(
     return res;
 }
 
+
+/* Verify the signature in a CSR is valid. */
+static int VerifyCSRSignature(mbedtls_x509_csr* csr)
+{
+    unsigned char hash[MBEDTLS_MD_MAX_SIZE];
+
+    if (csr->sig_md != MBEDTLS_MD_SHA256)
+    {
+        OIC_LOG(ERROR, TAG, "Unexpected digest used in CSR\n");
+        return -1;
+    }
+
+    if ((csr->cri.len == 0) || (csr->cri.p == NULL))
+    {
+        OIC_LOG(ERROR, TAG, "Missing CertificateRequestInfo field in CSR\n");
+        return -1;
+    }
+
+    if ((csr->sig.len == 0) || (csr->sig.p == NULL))
+    {
+        OIC_LOG(ERROR, TAG, "Missing signature field in CSR\n");
+        return -1;
+    }
+
+    if (MBEDTLS_OID_CMP(MBEDTLS_OID_ECDSA_SHA256, &csr->sig_oid) != 0)
+    {
+        char buf[256];
+        if (mbedtls_oid_get_numeric_string(buf, sizeof(buf), &csr->sig_oid) > 0)
+        {
+            OIC_LOG_V(ERROR, TAG, "Unexpected signature OID in CSR (got %s)\n", buf);
+        }
+        else
+        {
+            OIC_LOG(ERROR, TAG, "Unexpected signature OID in CSR\n");
+        }
+        return -1;
+    }
+
+    if (mbedtls_pk_get_type(&csr->pk) != MBEDTLS_PK_ECKEY)
+    {
+        OIC_LOG(ERROR, TAG, "Unexpected public key type in CSR\n");
+        return -1;
+    }
+
+    /* mbedtls_pk_get_bitlen returns the bit length of the curve */
+    if (mbedtls_pk_get_bitlen(&csr->pk) != 256)
+    {
+        OIC_LOG(ERROR, TAG, "Unexpected public length in CSR\n");
+        return -1;
+    }
+
+    mbedtls_ecp_keypair* ecKey = mbedtls_pk_ec(csr->pk);
+    if ((ecKey != NULL) && (ecKey->grp.id != MBEDTLS_ECP_DP_SECP256R1))
+    {
+        OIC_LOG(ERROR, TAG, "Unexpected curve parameters in CSR\n");
+        return -1;
+    }
+
+    /* Hash the CertificateRequestInfoField (https://tools.ietf.org/html/rfc2986#section-3) */
+    int ret = mbedtls_md(mbedtls_md_info_from_type(csr->sig_md), csr->cri.p, csr->cri.len, hash);
+    if (ret != 0)
+    {
+        OIC_LOG(ERROR, TAG, "Failed to hash CertificateRequestInfoField\n");
+        return ret;
+    }
+
+    /* the length of hash is determined from csr->sig_md*/
+    ret = mbedtls_pk_verify(&csr->pk, csr->sig_md, hash, 0, csr->sig.p, csr->sig.len);
+
+    return ret;
+}
+
+OCStackResult OCVerifyCSRSignature(const char* csr)
+{
+    mbedtls_x509_csr csrObj;
+
+    mbedtls_x509_csr_init(&csrObj);
+    int ret = mbedtls_x509_csr_parse(&csrObj, (const unsigned char*)csr, strlen(csr) + 1);
+    if (ret < 0)
+    {
+        OIC_LOG_V(ERROR, TAG, "Couldn't parse CSR: %d", ret);
+        mbedtls_x509_csr_free(&csrObj);
+        return OC_STACK_ERROR;
+    }
+
+    ret = VerifyCSRSignature(&csrObj);
+
+    mbedtls_x509_csr_free(&csrObj);
+
+    if (ret != 0)
+    {
+        return OC_STACK_ERROR;
+    }
+
+    return OC_STACK_OK;
+}
+
+OCStackResult OCGetUuidFromCSR(const char* csr, OicUuid_t* uuid)
+{
+    mbedtls_x509_csr csrObj;
+
+    mbedtls_x509_csr_init(&csrObj);
+    int ret = mbedtls_x509_csr_parse(&csrObj, (const unsigned char*)csr, strlen(csr) + 1);
+    if (ret < 0)
+    {
+        OIC_LOG_V(ERROR, TAG, "Couldn't parse CSR: %d", ret);
+        mbedtls_x509_csr_free(&csrObj);
+        return OC_STACK_ERROR;
+    }
+
+    char uuidStr[UUID_STRING_SIZE + sizeof(SUBJECT_PREFIX) - 1] = { 0 };   // Both constants count NULL, subtract one
+    ret = mbedtls_x509_dn_gets(uuidStr, sizeof(uuidStr), &csrObj.subject);
+    if (ret != (sizeof(uuidStr) - 1))
+    {
+        OIC_LOG_V(ERROR, TAG, "mbedtls_x509_dn_gets returned length or error: %d, expected %d", ret, sizeof(uuidStr) - 1);
+        mbedtls_x509_csr_free(&csrObj);
+        return OC_STACK_ERROR;
+    }
+
+    if (!OCConvertStringToUuid(uuidStr + sizeof(SUBJECT_PREFIX) - 1, uuid->id))
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to convert UUID: '%s'", uuidStr);
+        mbedtls_x509_csr_free(&csrObj);
+        return OC_STACK_ERROR;
+    }
+
+    mbedtls_x509_csr_free(&csrObj);
+    return OC_STACK_OK;
+}
+
+OCStackResult OCGetPublicKeyFromCSR(const char* csr, char** publicKey)
+{
+    mbedtls_x509_csr csrObj;
+
+    mbedtls_x509_csr_init(&csrObj);
+    int ret = mbedtls_x509_csr_parse(&csrObj, (const unsigned char*)csr, strlen(csr) + 1);
+    if (ret < 0)
+    {
+        OIC_LOG_V(ERROR, TAG, "Couldn't parse CSR: %d", ret);
+        mbedtls_x509_csr_free(&csrObj);
+        return OC_STACK_ERROR;
+    }
+
+    char subjectPublicKey[500] = { 0 };
+    ret = mbedtls_pk_write_pubkey_pem(&csrObj.pk, (unsigned char*)subjectPublicKey, sizeof(subjectPublicKey));
+    if (ret != 0)
+    {
+        OIC_LOG_V(ERROR, TAG, "Failed to write subject public key as PEM: %d", ret);
+        mbedtls_x509_csr_free(&csrObj);
+        return OC_STACK_ERROR;
+    }
+
+    size_t pkLen = strlen(subjectPublicKey) + 1;
+    *publicKey = (char*) OICCalloc(1, pkLen);
+    if (*publicKey == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "Failed to allocate memory for public key");
+        mbedtls_x509_csr_free(&csrObj);
+        return OC_STACK_ERROR;
+    }
+
+    memcpy(*publicKey, subjectPublicKey, pkLen);
+    mbedtls_x509_csr_free(&csrObj);
+
+    return OC_STACK_OK;
+}
+
+OCStackResult OCConvertDerCSRToPem(const char* derCSR, size_t derCSRLen, char** pemCSR)
+{
+    const char* pemHeader = "-----BEGIN CERTIFICATE REQUEST-----\n";
+    const char* pemFooter = "-----END CERTIFICATE REQUEST-----\n";
+
+    /* Get the length required for output*/
+    size_t pemCSRLen;
+    int ret = mbedtls_pem_write_buffer(pemHeader,
+        pemFooter,
+        (const unsigned char*)derCSR,
+        derCSRLen,
+        NULL,
+        0,
+        &pemCSRLen);
+    if (ret != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL)
+    {
+        OIC_LOG_V(ERROR, TAG, "Couldn't convert CSR into PEM, failed getting required length: %d", ret);
+        return OC_STACK_ERROR;
+    }
+
+    *pemCSR = OICCalloc(1, pemCSRLen + 1);
+    if (*pemCSR == NULL)
+    {
+        OIC_LOG(ERROR, TAG, "Failed to allocate memory for PEM CSR");
+        return OC_STACK_ERROR;
+    }
+
+    /* Try the conversion */
+    ret = mbedtls_pem_write_buffer(pemHeader, pemFooter,
+        (const unsigned char *)derCSR,
+        derCSRLen,
+        (unsigned char*) *pemCSR,
+        pemCSRLen,
+        &pemCSRLen);
+    if (ret < 0)
+    {
+        OIC_LOG_V(ERROR, TAG, "Couldn't convert CSR into PEM, failed getting required length: %d", ret);
+        OICFree(*pemCSR);
+        *pemCSR = NULL;
+        return OC_STACK_ERROR;
+    }
+
+    return OC_STACK_OK;
+}
+
 #endif /* defined(__WITH_TLS__) || defined(__WITH_DTLS__) */
\ No newline at end of file
index cd9cac8..3d2ceda 100644 (file)
@@ -15,6 +15,7 @@ InputPinCodeCallback
 LoadSecretJustWorksCallback
 
 OCConfigSelfOwnership
+OCConvertDerCSRToPem
 OCDeleteACLList
 OCDeleteDiscoveredDevices
 OCDeletePdAclList
@@ -23,19 +24,22 @@ OCDiscoverOwnedDevices
 OCDiscoverSingleDevice
 OCDiscoverUnownedDevices
 OCDoOwnershipTransfer
-OCGenerateCACertificate
-OCGenerateIdentityCertificate
-OCGenerateKeyPair
-OCGenerateRandomSerialNumber
+OCGenerateCACertificate\r
+OCGenerateIdentityCertificate\r
+OCGenerateKeyPair\r
+OCGenerateRandomSerialNumber\r
 OCGenerateRoleCertificate
 OCGetACLResource
 OCGetCredResource
 OCGetCSRResource
 OCGetDevInfoFromNetwork
 OCGetLinkedStatus
+OCGetPublicKeyFromCSR
+OCGetUuidFromCSR
 OCInitPM
 OCProvisionACL
 OCSaveACL
+OCProvisionCertificate
 OCProvisionCredentials
 OCProvisionDirectPairing
 OCProvisionPairwiseDevices
@@ -48,9 +52,11 @@ OCRemoveTrustCertChainNotifier
 OCResetDevice
 OCResetSVRDB
 OCSaveTrustCertChain
+OCSaveOwnCertChain
 OCSetOwnerTransferCallbackData
 OCUnlinkDevices
 OCSetOxmAllowStatus
+OCVerifyCSRSignature 
 
 SetClosePinDisplayCB
 SetDisplayPinWithContextCB