From 07b430ed71631016cd44206b50bd6c16f1ffc3c6 Mon Sep 17 00:00:00 2001 From: Greg Zaverucha Date: Wed, 8 Mar 2017 12:41:34 -0800 Subject: [PATCH] [IOT-1785] Support for Certificate Provisioning - 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 Signed-off-by: Kevin Kane Reviewed-on: https://gerrit.iotivity.org/gerrit/17509 Reviewed-by: Alex Kelley Tested-by: jenkins-iotivity Reviewed-by: Dan Mihai --- .../src/adapter_util/ca_adapter_net_ssl.c | 2 +- .../csdk/connectivity/test/ca_api_unittest.cpp | 108 ++++- resource/csdk/security/include/occertutility.h | 44 ++ .../include/internal/secureresourceprovider.h | 49 +-- .../provisioning/include/ocprovisioningmanager.h | 44 +- .../provisioning/sample/provisioningclient.c | 449 ++++++++++++++++++++- .../provisioning/src/ocprovisioningmanager.c | 48 ++- .../provisioning/src/secureresourceprovider.c | 200 ++++++--- .../unittest/secureresourceprovider.cpp | 8 +- resource/csdk/security/src/credresource.c | 121 +++++- resource/csdk/security/src/occertutility.c | 215 ++++++++++ resource/csdk/stack/octbstack_product_secured.def | 14 +- 12 files changed, 1183 insertions(+), 119 deletions(-) diff --git a/resource/csdk/connectivity/src/adapter_util/ca_adapter_net_ssl.c b/resource/csdk/connectivity/src/adapter_util/ca_adapter_net_ssl.c index a65e8cb..07e101e 100755 --- a/resource/csdk/connectivity/src/adapter_util/ca_adapter_net_ssl.c +++ b/resource/csdk/connectivity/src/adapter_util/ca_adapter_net_ssl.c @@ -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!"); } diff --git a/resource/csdk/connectivity/test/ca_api_unittest.cpp b/resource/csdk/connectivity/test/ca_api_unittest.cpp index e234da0..2b3850a 100755 --- a/resource/csdk/connectivity/test/ca_api_unittest.cpp +++ b/resource/csdk/connectivity/test/ca_api_unittest.cpp @@ -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 } diff --git a/resource/csdk/security/include/occertutility.h b/resource/csdk/security/include/occertutility.h index 43c5bf8..7962cd0 100644 --- a/resource/csdk/security/include/occertutility.h +++ b/resource/csdk/security/include/occertutility.h @@ -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 diff --git a/resource/csdk/security/provisioning/include/internal/secureresourceprovider.h b/resource/csdk/security/provisioning/include/internal/secureresourceprovider.h index ac1d640..a7364c9 100644 --- a/resource/csdk/security/provisioning/include/internal/secureresourceprovider.h +++ b/resource/csdk/security/provisioning/include/internal/secureresourceprovider.h @@ -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 diff --git a/resource/csdk/security/provisioning/include/ocprovisioningmanager.h b/resource/csdk/security/provisioning/include/ocprovisioningmanager.h index ef716c7..f02a375 100644 --- a/resource/csdk/security/provisioning/include/ocprovisioningmanager.h +++ b/resource/csdk/security/provisioning/include/ocprovisioningmanager.h @@ -295,6 +295,21 @@ OCStackResult OCProvisionCredentials(void *ctx, OicSecCredType_t type, size_t ke const OCProvisionDev_t *pDev2, OCProvisionResultCB resultCallback); +/** + * API to provision a certificate to a device. + * + * @param[in] ctx Application context returned in result callback. + * @param[in] pDev Pointer to OCProvisionDev_t instance, respresenting the device to be provsioned. + * @param[in] pemCert Certificate to provision, encoded as PEM + * @param[in] resultCallback callback provided by API user, callback will be called when + * provisioning request receives a response from first resource server. + * @return OC_STACK_OK in case of success and other value otherwise. + */ +OCStackResult OCProvisionCertificate(void *ctx, + const OCProvisionDev_t *pDev, + const char* pemCert, + OCProvisionResultCB resultCallback); + #ifdef MULTIPLE_OWNER /** * API to provision preconfigured PIN to device(NOT LIST). @@ -388,16 +403,16 @@ OCStackResult OCRemoveDevice(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] waitTimeForOwnedDeviceDiscovery Maximum wait time for owned device discovery.(seconds) -* @param[in] pTargetUuid Device information to be revoked. -* @param[in] resultCallback callback provided by API user, callback will be called when -* credential revocation is finished. + * 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] waitTimeForOwnedDeviceDiscovery Maximum wait time for owned device discovery.(seconds) + * @param[in] pTargetUuid 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. -*/ + */ OCStackResult OCRemoveDeviceWithUuid(void* ctx, unsigned short waitTimeForOwnedDeviceDiscovery, const OicUuid_t* pTargetUuid, @@ -531,6 +546,17 @@ OCStackResult OCProvisionTrustCertChain(void *ctx, OicSecCredType_t type, uint16 */ OCStackResult OCSaveTrustCertChain(uint8_t *trustCertChain, size_t chainSize, OicEncodingType_t encodingType, uint16_t *credId); + +/** + * Function to save an identity certificate chain into Cred of SVR. + * + * @param[in] cert Certificate chain to be saved in Cred of SVR, PEM encoded, null terminated + * @param[in] key private key corresponding to the certificate, PEM encoded, null terminated + * @param[out] credId CredId of saved certificate chain in Cred of SVR. + * @return OC_STACK_OK in case of success and other value otherwise. + */ +OCStackResult OCSaveOwnCertChain(char* cert, char* key, uint16_t *credId); + /** * function to register callback, for getting notification for TrustCertChain change. * diff --git a/resource/csdk/security/provisioning/sample/provisioningclient.c b/resource/csdk/security/provisioning/sample/provisioningclient.c index 0b2c4b3..4721c8f 100644 --- a/resource/csdk/security/provisioning/sample/provisioningclient.c +++ b/resource/csdk/security/provisioning/sample/provisioningclient.c @@ -40,6 +40,7 @@ #include "mbedtls/config.h" #include "mbedtls/pem.h" #include "mbedtls/x509_csr.h" +#include "occertutility.h" #ifdef _MSC_VER #include @@ -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; 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. */ + 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) + { + 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()) { diff --git a/resource/csdk/security/provisioning/src/ocprovisioningmanager.c b/resource/csdk/security/provisioning/src/ocprovisioningmanager.c index 91cf836..b14f0c4 100644 --- a/resource/csdk/security/provisioning/src/ocprovisioningmanager.c +++ b/resource/csdk/security/provisioning/src/ocprovisioningmanager.c @@ -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__) +/** +* API to provision a certificate to a device. +* +* @param[in] ctx Application context returned in result callback. +* @param[in] pDev Pointer to OCProvisionDev_t instance, respresenting the device to be provsioned. +* @param[in] pemCert Certificate to provision, encoded as PEM +* @param[in] resultCallback callback provided by API user, callback will be called when +* provisioning request receives a response from first resource server. +* @return OC_STACK_OK in case of success and other value otherwise. +*/ +OCStackResult OCProvisionCertificate(void *ctx, + const OCProvisionDev_t *pDev, + const char* pemCert, + 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); } +/** + * Function to save an identity certificate chain into Cred of SVR. + * + * @param[in] cert Certificate chain to be saved in Cred of SVR, PEM encoded, null terminated + * @param[in] key key corresponding to the certificate, PEM encoded, null terminated + * @param[out] credId CredId of saved certificate chain in Cred of SVR. + * @return OC_STACK_OK in case of success and other value otherwise. + */ +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. * diff --git a/resource/csdk/security/provisioning/src/secureresourceprovider.c b/resource/csdk/security/provisioning/src/secureresourceprovider.c index 3d47ee8..1b309a8 100644 --- a/resource/csdk/security/provisioning/src/secureresourceprovider.c +++ b/resource/csdk/security/provisioning/src/secureresourceprovider.c @@ -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, + OCClientResponse *clientResponse) +{ + size_t i = 0; + OIC_LOG_V(INFO, TAG, "IN %s", __func__); + (void)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); + } + + ((OCGetCSRResultCB)(resultCallback))(getCsrData->ctx, getCsrData->numOfResults, + getCsrData->resArr, + false); + OIC_LOG_V(ERROR, TAG, "%s: received Null clientResponse", __func__); + for (i = 0; i < getCsrData->numOfResults; i++) + { + OICFree(getCsrData->resArr[i].csr); + } + OICFree(getCsrData->resArr); + OICFree(getCsrData); + OIC_LOG_V(INFO, TAG, "OUT %s", __func__); + + return OC_STACK_DELETE_TRANSACTION; } diff --git a/resource/csdk/security/provisioning/unittest/secureresourceprovider.cpp b/resource/csdk/security/provisioning/unittest/secureresourceprovider.cpp index 81b38e8..7b7862c 100644 --- a/resource/csdk/security/provisioning/unittest/secureresourceprovider.cpp +++ b/resource/csdk/security/provisioning/unittest/secureresourceprovider.cpp @@ -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)); } diff --git a/resource/csdk/security/src/credresource.c b/resource/csdk/security/src/credresource.c index 1578551..f89d83b 100755 --- a/resource/csdk/security/src/credresource.c +++ b/resource/csdk/security/src/credresource.c @@ -65,6 +65,7 @@ #if defined(__WITH_DTLS__) || defined (__WITH_TLS__) #include +#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); diff --git a/resource/csdk/security/src/occertutility.c b/resource/csdk/security/src/occertutility.c index 4ced39a..7161c9e 100644 --- a/resource/csdk/security/src/occertutility.c +++ b/resource/csdk/security/src/occertutility.c @@ -43,8 +43,11 @@ #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 diff --git a/resource/csdk/stack/octbstack_product_secured.def b/resource/csdk/stack/octbstack_product_secured.def index cd9cac8..3d2ceda 100644 --- a/resource/csdk/stack/octbstack_product_secured.def +++ b/resource/csdk/stack/octbstack_product_secured.def @@ -15,6 +15,7 @@ InputPinCodeCallback LoadSecretJustWorksCallback OCConfigSelfOwnership +OCConvertDerCSRToPem OCDeleteACLList OCDeleteDiscoveredDevices OCDeletePdAclList @@ -23,19 +24,22 @@ OCDiscoverOwnedDevices OCDiscoverSingleDevice OCDiscoverUnownedDevices OCDoOwnershipTransfer -OCGenerateCACertificate -OCGenerateIdentityCertificate -OCGenerateKeyPair -OCGenerateRandomSerialNumber +OCGenerateCACertificate +OCGenerateIdentityCertificate +OCGenerateKeyPair +OCGenerateRandomSerialNumber 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 -- 2.7.4