--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Microsoft
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//******************************************************************
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <vector>
+
+#include "ocstack.h"
+#include "oic_malloc.h"
+#include "ocrandom.h"
+#include "occertutility.h"
+
+#ifdef HAVE_WINDOWS_H
+#include <windows.h>
+/** @todo stop-gap for naming issue. Windows.h does not like us to use ERROR */
+#ifdef ERROR
+#undef ERROR
+#endif //ERROR
+#endif //HAVE_WINDOWS_H
+
+static long GetFileSize(FILE *f)
+{
+ long offset, size;
+
+ offset = ftell(f);
+
+ if (offset < 0)
+ {
+ return offset;
+ }
+
+ if (fseek(f, 0, SEEK_END) < 0)
+ {
+ return -1;
+ }
+
+ size = ftell(f);
+
+ if (fseek(f, 0, SEEK_SET) < 0)
+ {
+ return -1;
+ }
+
+ return size;
+}
+
+static char *ReadLine(char *buf, size_t len)
+{
+ char *p;
+ int lenAsInt;
+
+ if (len > 0xFFFFFFFF)
+ {
+ return NULL;
+ }
+ else
+ {
+ lenAsInt = (int)len;
+ }
+
+ if (NULL == fgets(buf, lenAsInt, stdin))
+ {
+ return NULL;
+ }
+
+ /* Trim newline that fgets stores in the buffer. */
+ for (p = buf; ((p - buf) < lenAsInt) && (*p != '\r') && (*p != '\n'); p++);
+
+ *p = '\0';
+
+ return buf;
+}
+
+typedef enum {
+ CA_CERT,
+ IDENTITY_CERT,
+ ROLE_CERT
+} CertType;
+
+static void DoGenCertificate(CertType certType)
+{
+ OCStackResult res;
+ char subjKeyPairName[50];
+ char issKeyPairName[50];
+ char filename[70];
+ char subject[100];
+ char authority[100];
+ OicUuid_t subjectUuid;
+ char notValidBefore[50];
+ char notValidAfter[50];
+ char *serial = NULL;
+ size_t serialLen = 0;
+ long fileLen = 0;
+ size_t bytesProcessed = 0;
+ std::vector<char> publicKey;
+ std::vector<char> privateKey;
+ std::vector<char> issuerCert;
+ FILE *f = NULL;
+ time_t nowTimeT;
+ struct tm *now;
+ char *certificate = NULL;
+ size_t certificateLen = 0;
+
+ printf("Subject key pair name (do not include .pub or .prv): ");
+ if (NULL == ReadLine(subjKeyPairName, sizeof(subjKeyPairName)))
+ {
+ printf("Failed to get key pair name!");
+ goto exit;
+ }
+
+ /* This sample only supports self-signed CAs. Intermediates can be created with
+ * the helper functions, though, by providing a different issuer private key and
+ * certificate.
+ */
+ if (CA_CERT != certType)
+ {
+ printf("Issuer cert/key pair name (do not include .crt, .pub, or .prv): ");
+ if (NULL == ReadLine(issKeyPairName, sizeof(issKeyPairName)))
+ {
+ printf("Failed to get key pair name!");
+ goto exit;
+ }
+ }
+ else
+ {
+ strcpy(issKeyPairName, subjKeyPairName);
+ }
+
+ // -- Load public key --
+
+ sprintf(filename, "%s.pub", subjKeyPairName);
+ f = fopen(filename, "rb");
+ if (NULL == f)
+ {
+ printf("Could not open public key!\n");
+ goto exit;
+ }
+
+ fileLen = GetFileSize(f);
+ if (fileLen < 0)
+ {
+ printf("Could not get size of public key!\n");
+ goto exit;
+ }
+
+ publicKey.resize(fileLen);
+ bytesProcessed = fread(publicKey.data(), 1, publicKey.size(), f);
+ if (bytesProcessed < publicKey.size())
+ {
+ printf("Could not read public key! Got %zu, expected %zu\n", bytesProcessed, publicKey.size());
+ goto exit;
+ }
+
+ if (0 != fclose(f))
+ {
+ printf("Warning: could not fclose public key\n");
+ }
+
+ // -- Load private key --
+ sprintf(filename, "%s.prv", issKeyPairName);
+ f = fopen(filename, "rb");
+ if (NULL == f)
+ {
+ printf("Could not open private key!\n");
+ goto exit;
+ }
+
+ fileLen = GetFileSize(f);
+ if (fileLen < 0)
+ {
+ printf("Could not get size of private key!\n");
+ goto exit;
+ }
+
+ privateKey.resize(fileLen);
+ bytesProcessed = fread(privateKey.data(), 1, privateKey.size(), f);
+ if (bytesProcessed < privateKey.size())
+ {
+ printf("Could not read private key! Got %zu, expected %zu\n", bytesProcessed, privateKey.size());
+ goto exit;
+ }
+
+ if (0 != fclose(f))
+ {
+ printf("Warning: could not fclose private key\n");
+ }
+
+ f = NULL;
+
+ // -- Load issuer cert if applicable --
+ if (CA_CERT != certType)
+ {
+ sprintf(filename, "%s.crt", issKeyPairName);
+ f = fopen(filename, "rb");
+ if (NULL == f)
+ {
+ printf("Could not open issuer certificate file!\n");
+ goto exit;
+ }
+
+ fileLen = GetFileSize(f);
+ if (fileLen < 0)
+ {
+ printf("Could not get size of issuer certificate!\n");
+ goto exit;
+ }
+
+ issuerCert.resize(fileLen);
+ bytesProcessed = fread(issuerCert.data(), 1, issuerCert.size(), f);
+ if (bytesProcessed < issuerCert.size())
+ {
+ printf("Could not read issuer certificate! Got %zu, expected %zu\n", bytesProcessed, issuerCert.size());
+ goto exit;
+ }
+
+ if (0 != fclose(f))
+ {
+ printf("Warning: could not fclose issuer certificate\n");
+ }
+
+ f = NULL;
+ }
+
+ // -- Prompt user for subject name --
+
+ if (CA_CERT == certType)
+ {
+ printf("Subject name as comma-separated list of RDN types and values\n");
+ printf("e.g.: C=US, O=Open Connectivity Foundation, CN=Main CA : ");
+ if (NULL == ReadLine(subject, sizeof(subject)))
+ {
+ printf("Failed to get subject!\n");
+ goto exit;
+ }
+ }
+ else
+ {
+ printf("Subject UUID in the RFC 4122 form (XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX): ");
+ if (NULL == ReadLine(subject, sizeof(subject)))
+ {
+ printf("Failed to get subject UUID!");
+ goto exit;
+ }
+
+ if (!OCConvertStringToUuid(subject, subjectUuid.id))
+ {
+ printf("Failed to parse input UUID!\n");
+ goto exit;
+ }
+ }
+
+ // -- Compute validity to be ten years from current time --
+ nowTimeT = time(NULL);
+ now = gmtime(&nowTimeT);
+
+ if (0 == strftime(notValidBefore, sizeof(notValidBefore), "%Y%m%d%H%M%S", now))
+ {
+ printf("Failed to format notValidBefore time!");
+ goto exit;
+ }
+
+ now->tm_year += 10;
+
+ if (0 == strftime(notValidAfter, sizeof(notValidAfter), "%Y%m%d%H%M%S", now))
+ {
+ printf("Failed to format notValidAfter time!");
+ goto exit;
+ }
+
+ // -- Generate random serial number. A real CA should record serial numbers as they are used, and --
+ // -- make sure none are ever repeated! --
+
+ res = OCGenerateRandomSerialNumber(&serial, &serialLen);
+ if (OC_STACK_OK != res)
+ {
+ printf("Failed to generate a serial number!\n");
+ goto exit;
+ }
+
+ switch (certType)
+ {
+ case CA_CERT:
+ res = OCGenerateCACertificate(
+ subject,
+ publicKey.data(),
+ NULL,
+ privateKey.data(),
+ serial,
+ notValidBefore,
+ notValidAfter,
+ &certificate,
+ &certificateLen);
+ break;
+
+ case IDENTITY_CERT:
+ res = OCGenerateIdentityCertificate(
+ &subjectUuid,
+ publicKey.data(),
+ issuerCert.data(),
+ privateKey.data(),
+ serial,
+ notValidBefore,
+ notValidAfter,
+ &certificate,
+ &certificateLen);
+ break;
+
+ case ROLE_CERT:
+ printf("Role name: ");
+ /* We don't need subject's contents now, so reuse the buffer.*/
+ if (NULL == ReadLine(subject, sizeof(subject)))
+ {
+ printf("Failed to read role name!\n");
+ goto exit;
+ }
+ printf("Authority name (press ENTER for none): ");
+ if (NULL == ReadLine(authority, sizeof(authority)))
+ {
+ printf("Failed to read authority name!\n");
+ goto exit;
+ }
+ res = OCGenerateRoleCertificate(
+ &subjectUuid,
+ publicKey.data(),
+ issuerCert.data(),
+ privateKey.data(),
+ serial,
+ notValidBefore,
+ notValidAfter,
+ subject,
+ ('\0' != *authority)?authority:NULL,
+ &certificate,
+ &certificateLen);
+ break;
+ }
+
+ if (OC_STACK_OK != res)
+ {
+ printf("Failed to generate certificate!\n");
+ goto exit;
+ }
+
+ sprintf(filename, "%s.crt", subjKeyPairName);
+ f = fopen(filename, "wb");
+ if (NULL == f)
+ {
+ printf("Failed to open certificate file for writing!\n");
+ goto exit;
+ }
+
+ bytesProcessed = fwrite(certificate, 1, certificateLen, f);
+ if (bytesProcessed < certificateLen)
+ {
+ printf("Failed to write certificate! Had %zu bytes, wrote %zu\n", certificateLen, bytesProcessed);
+ goto exit;
+ }
+
+ if (0 != fclose(f))
+ {
+ printf("Warning: failed to close certificate file\n");
+ goto exit;
+ }
+
+ f = NULL;
+
+ printf("Wrote certificate file.\n");
+
+exit:
+
+ OICFree(serial);
+ OICFree(certificate);
+ if (NULL != f)
+ {
+ if (0 != fclose(f))
+ {
+ printf("Warning: failed to fclose\n");
+ }
+ }
+}
+
+static void DoGenKeyPair()
+{
+ OCStackResult res = OC_STACK_OK;
+ char nameBuf[100];
+ char *publicKey = NULL;
+ char *privateKey = NULL;
+ size_t publicKeyLen = 0;
+ size_t privateKeyLen = 0;
+ FILE *f = NULL;
+
+ printf("Name the key pair: ");
+ if (NULL == ReadLine(nameBuf, sizeof(nameBuf)))
+ {
+ printf("Failed to read name!");
+ return;
+ }
+
+ res = OCGenerateKeyPair(&publicKey, &publicKeyLen, &privateKey, &privateKeyLen);
+ if (OC_STACK_OK == res)
+ {
+ char filename[120];
+ size_t written;
+
+ sprintf(filename, "%s.pub", nameBuf);
+ f = fopen(filename, "wb");
+ if (NULL == f)
+ {
+ printf("Couldn't open %s to write public key!\n", filename);
+ goto exit;
+ }
+
+ written = fwrite(publicKey, 1, publicKeyLen, f);
+ if (written < publicKeyLen)
+ {
+ printf("Failed to write public key! Had %zu, wrote %zu\n", publicKeyLen, written);
+ goto exit;
+ }
+
+ if (0 != fclose(f))
+ {
+ printf("Warning: failed to fclose. Errno: %d\n", errno);
+ }
+
+ printf("Wrote public key.\n");
+
+ sprintf(filename, "%s.prv", nameBuf);
+ f = fopen(filename, "wb");
+ if (NULL == f)
+ {
+ printf("Couldn't open %s to write private key!\n", filename);
+ goto exit;
+ }
+ written = fwrite(privateKey, 1, privateKeyLen, f);
+ if (written < privateKeyLen)
+ {
+ printf("Failed to write private key! Had %zu, wrote %zu\n", privateKeyLen, written);
+ goto exit;
+ }
+
+ if (0 != fclose(f))
+ {
+ printf("Warning: failed to fclose. Errno: %d\n", errno);
+ }
+ f = NULL;
+
+ printf("Wrote private key.\n");
+ }
+ else
+ {
+ printf("Failed!\n");
+ }
+
+exit:
+
+ OICFree(publicKey);
+ OICClearMemory(privateKey, privateKeyLen);
+ OICFree(privateKey);
+ if (NULL != f)
+ {
+ if (0 != fclose(f))
+ {
+ printf("Warning: failed to fclose. errno: %d\n", errno);
+ }
+ }
+}
+
+int main()
+{
+ for (;;)
+ {
+ int option;
+
+ printf("-- Certificate Generator Sample Utility --\n\n");
+
+ printf(" 1. Generate a new key pair\n");
+ printf(" 2. Generate a self-signed CA certificate (requires a key pair for the CA)\n");
+ printf(" 3. Generate an identity certificate for a particular device UUID\n");
+ printf(" (requires the CA's private key and certificate, and the device's public key)\n");
+ printf(" 4. Generate a role certificate for a particular device UUID and role\n");
+ printf(" (requires the CA's private key and certificate, and the device's public key)\n");
+
+ printf("\n");
+ printf(" 0. Exit\n");
+ printf("\n");
+ printf("Select: ");
+
+ if (scanf("%d", &option) < 1)
+ {
+ printf("Failed to read the option! Exiting.\n");
+ return 0;
+ }
+
+ while ('\n' != getchar()); /* Consume the rest of the line so it doesn't get fed to the next input. */
+
+ switch (option)
+ {
+ case 1:
+ DoGenKeyPair();
+ break;
+ case 2:
+ DoGenCertificate(CA_CERT);
+ break;
+ case 3:
+ DoGenCertificate(IDENTITY_CERT);
+ break;
+ case 4:
+ DoGenCertificate(ROLE_CERT);
+ break;
+ case 0:
+ return 0;
+ default:
+ printf("Invalid option %d\n\n", option);
+ break;
+ }
+ }
+
+ return 0;
+}
\ No newline at end of file
--- /dev/null
+//******************************************************************
+//
+// Copyright 2017 Microsoft
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//******************************************************************
+
+#if defined(__WITH_TLS__) || defined(__WITH_DTLS__)
+
+#ifndef OCCERTUTILITY_H_
+#define OCCERTUTILITY_H_
+
+#include "securevirtualresourcetypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Generate a random serial number suitable for use with a certificate.
+ * Callers must record used serial numbers and ensure serial numbers do not
+ * get repeated!
+ *
+ * @param[OUT] serial Pointer to a string that will be set to the location of the ASCII serial number.
+ * Memory for this buffer will be allocated internally; caller must call OICFree on serial
+ * when finished to free its memory.
+ * @param[OUT] serialLen Variable to receive size of serial, which will include the terminating NULL.
+ *
+ * @return OC_STACK_OK if generated successfully; error otherwise.
+ */
+OCStackResult OCGenerateRandomSerialNumber(char **serial, size_t *serialLen);
+
+/**
+ * Generate a new NIST P-256 elliptic curve key pair and return the public and private
+ * keys as PEM encoded strings.
+ *
+ * @param[OUT] publicKey Pointer to receive PEM encoded public key. Memory is allocated internally
+ * and must be freed by caller with OICFree.
+ * @param[OUT] publicKeyLen Variable to receive length of publicKey, which will include terminating NULL.
+ * @param[OUT] privateKey Pointer to receive PEM encoded private key. Memory is allocated internally
+ * and, when finished, caller should first call OICClearMemory to securely erase
+ * its contents and then freed with OICFree. Caller is responsible for ensuring
+ * this key remains protected and secret!
+ * @param[OUT] privateKeyLen Variable to receive length of privateKey, which will include terminating NULL.
+ */
+OCStackResult OCGenerateKeyPair(char **publicKey, size_t *publicKeyLen,
+ char **privateKey, size_t *privateKeyLen);
+
+/**
+ * Generate a certificate to act as a Certificate Authority (CA).
+ *
+ * @param subject Comma-separated list of RDN types and values:
+ * e.g. "C=US, O=Open Connectivity Foundation, CN=Main CA"
+ * @param subjectPublicKey Subject's public key in PEM format
+ * @param issuerCert Issuer's certificate in PEM format
+ * If this certificate will be self-signed, pass in NULL.
+ * @param issuerPrivateKey Issuer's private key in PEM format
+ * If self-signing (issuerCert is NULL), this must be the private
+ * key corresponding to subjectPublicKey.
+ * @param serial String containing the serial number in ASCII numerals:
+ * e.g., "12345". Caller must ensure each certificate issued by a
+ * CA has a unique serial number, and it is recommended to generate
+ * them randomly by producing 19 random bytes and converting them to
+ * a numerical value. See GenerateRandomSerialNumber().
+ * @param notValidBefore The notValidBefore date in UTC for the certificate in the form YYYYMMDDhhmmss
+ * e.g., "20131231235959" for December 31st 2013 at 23:59:59
+ * @param notValidAfter The notValidAfter date in UTC for the certificate in the form YYYYMMDDhhmmss
+ * e.g., "20140101010203" for January 1st 2014 at 01:02:03
+ * @param[OUT] certificate Pointer to a buffer that will receive the PEM-encoded certificate. Memory will be
+ * allocated internally; caller must call OICFree on certificate when finished to free
+ * its memory.
+ * @param[OUT] certificateLen Variable to receive the size of certificate, which will include the terminating NULL.
+ *
+ * @return OC_STACK_OK if successful, error code otherwise
+ */
+OCStackResult OCGenerateCACertificate(
+ const char *subject,
+ const char *subjectPublicKey,
+ const char *issuerCert,
+ const char *issuerPrivateKey,
+ const char *serial,
+ const char *notValidBefore,
+ const char *notValidAfter,
+ char **certificate,
+ size_t *certificateLen);
+
+/**
+ * Generate a certificate for a device's identity.
+ *
+ * @param subjectUuid UUID for the device to use the certificate.
+ * @param subjectPublicKey Subject's public key in PEM format
+ * @param issuerCert Issuer's certificate in PEM format
+ * @param issuerPrivateKey Issuer's private key in PEM format
+ * @param serial String containing the serial number in ASCII numerals:
+ * e.g., "12345". Caller must ensure each certificate issued by a
+ * CA has a unique serial number, and it is recommended to generate
+ * them randomly by producing 19 random bytes and converting them to
+ * a numerical value. See GenerateRandomSerialNumber().
+ * @param notValidBefore The notValidBefore date in UTC for the certificate in the form YYYYMMDDhhmmss
+ * e.g., "20131231235959" for December 31st 2013 at 23:59:59
+ * @param notValidAfter The notValidAfter date in UTC for the certificate in the form YYYYMMDDhhmmss
+ * e.g., "20140101010203" for January 1st 2014 at 01:02:03
+ * @param[OUT] certificate Pointer to a buffer that will receive the PEM-encoded certificate. Memory will be
+ * allocated internally; caller must call OICFree on certificate when finished to free
+ * its memory.
+ * @param[OUT] certificateLen Variable to receive the size of certificate, which will include the terminating NULL.
+ *
+ * @return OC_STACK_OK if successful, error code otherwise
+ */
+OCStackResult OCGenerateIdentityCertificate(
+ const OicUuid_t *subjectUuid,
+ const char *subjectPublicKey,
+ const char *issuerCert,
+ const char *issuerPrivateKey,
+ const char *serial,
+ const char *notValidBefore,
+ const char *notValidAfter,
+ char **certificate,
+ size_t *certificateLen);
+
+/**
+ * Generate a certificate for a device's role.
+ *
+ * @param subjectUuid UUID for the device to use the certificate.
+ * @param subjectPublicKey Subject's public key in PEM format
+ * @param issuerCert Issuer's certificate in PEM format
+ * @param issuerPrivateKey Issuer's private key in PEM format
+ * @param serial String containing the serial number in ASCII numerals:
+ * e.g., "12345". Caller must ensure each certificate issued by a
+ * CA has a unique serial number, and it is recommended to generate
+ * them randomly by producing 19 random bytes and converting them to
+ * a numerical value. See GenerateRandomSerialNumber().
+ * @param notValidBefore The notValidBefore date in UTC for the certificate in the form YYYYMMDDhhmmss
+ * e.g., "20131231235959" for December 31st 2013 at 23:59:59
+ * @param notValidAfter The notValidAfter date in UTC for the certificate in the form YYYYMMDDhhmmss
+ * e.g., "20140101010203" for January 1st 2014 at 01:02:03
+ * @param role The role to encode in the certificate.
+ * @param authority The role authority to encode in the certificate. This field is optional;
+ * to omit, use NULL, and authority is construed to be the certificate issuer.
+ * @param[OUT] certificate Pointer to a buffer that will receive the PEM-encoded certificate. Memory will be
+ * allocated internally; caller must call OICFree on certificate when finished to free
+ * its memory.
+ * @param[OUT] certificateLen Variable to receive the size of certificate, which will include the terminating NULL.
+ *
+ * @return OC_STACK_OK if successful, error code otherwise
+ */
+OCStackResult OCGenerateRoleCertificate(
+ const OicUuid_t *subjectUuid,
+ const char *subjectPublicKey,
+ const char *issuerCert,
+ const char *issuerPrivateKey,
+ const char *serial,
+ const char *notValidBefore,
+ const char *notValidAfter,
+ const char *role,
+ const char *authority,
+ char **certificate,
+ size_t *certificateLen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* OCCERTUTILITY_H_ */
+
+#endif /* defined(__WITH_TLS__) || defined(__WITH_DTLS__) */
\ No newline at end of file
--- /dev/null
+/* *****************************************************************
+ *
+ * Copyright 2017 Microsoft. All Rights Reserved.
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * *****************************************************************/
+
+#if defined(__WITH_TLS__) || defined(__WITH_DTLS__)
+
+#include "iotivity_config.h"
+
+#include "logger.h"
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+#include "oic_malloc.h"
+#include "oic_string.h"
+#include "cacommon.h"
+#include "ocrandom.h"
+#include "cacommonutil.h"
+
+#include "ocpayload.h"
+#include "payload_logging.h"
+#include "pmutility.h"
+#include "srmutility.h"
+
+// headers required for mbed TLS
+#include "mbedtls/config.h"
+#include "mbedtls/platform.h"
+#include "mbedtls/entropy.h"
+#include "mbedtls/ctr_drbg.h"
+#include "mbedtls/x509_csr.h"
+#include "mbedtls/x509_crt.h"
+#include "mbedtls/oid.h"
+
+#ifndef NDEBUG
+#include "mbedtls/debug.h"
+#include "mbedtls/version.h"
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+
+#include "occertutility.h"
+
+#define TAG "OIC_OCCERTUTILITY"
+
+/**
+ * @def PERSONALIZATION_STRING
+ * @brief Personalization string for the mbedtls RNG
+ */
+#define PERSONALIZATION_STRING "IOTIVITY_RND"
+
+#define MAX_URI_QUERY MAX_URI_LENGTH + MAX_QUERY_LENGTH
+
+#define MAX_STRING_LEN 254
+
+/* ASN.1 DER encoding of the EKU for identity certificates (1.3.6.1.4.1.44924.1.6) */
+static const unsigned char s_ekuIdentity[] = { 0x30, 0x0C, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xDE, 0x7C, 0x01, 0x06 };
+
+/* ASN.1 DER encoding of the EKU for role certificates (1.3.6.1.4.1.44924.1.7) */
+static const unsigned char s_ekuRole[] = { 0x30, 0x0C, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xDE, 0x7C, 0x01, 0x07 };
+
+/* ASN.1 DER encoding of the EKU for both identity and roles (for use by CAs) */
+static const unsigned char s_ekuCA[] = { 0x30, 0x18, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xDE, 0x7C, 0x01, 0x06, 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xDE, 0x7C, 0x01, 0x07 };
+
+/**
+ * Generates elliptic curve keypair.
+ *
+ * @param[out] pk mbedtls public key container
+ *
+ * @return 0 on success or <0 on error
+ */
+static int GenerateEccKeyPair(mbedtls_pk_context *pk)
+{
+ int ret = 0;
+ mbedtls_entropy_context entropy;
+ mbedtls_ctr_drbg_context ctr_drbg;
+
+ OIC_LOG_V(DEBUG, TAG, "In %s", __func__);
+ VERIFY_NON_NULL_RET(pk, TAG, "Param pk is NULL", -1);
+
+ // Initialize the DRBG context
+ mbedtls_ctr_drbg_init(&ctr_drbg);
+ mbedtls_entropy_init(&entropy);
+ ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func,
+ &entropy, (const unsigned char *)PERSONALIZATION_STRING, sizeof(PERSONALIZATION_STRING));
+
+ if (0 > ret)
+ {
+ OIC_LOG_V(ERROR, TAG, "Seed initialization failed! %d", ret);
+ OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+ goto exit;
+ }
+ mbedtls_ctr_drbg_set_prediction_resistance(&ctr_drbg, MBEDTLS_CTR_DRBG_PR_ON);
+ ret = mbedtls_pk_setup(pk, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
+ if (0 > ret)
+ {
+ OIC_LOG_V(ERROR, TAG, "mbedtls_pk_setup error %d", ret);
+ OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+ goto exit;
+ }
+ ret = mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256R1, mbedtls_pk_ec(*pk), mbedtls_ctr_drbg_random, &ctr_drbg);
+ if (0 > ret)
+ {
+ OIC_LOG(ERROR, TAG, "mbedtls_ecp_gen_keypair error");
+ OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+ goto exit;
+ }
+
+exit:
+
+ mbedtls_ctr_drbg_free(&ctr_drbg);
+ mbedtls_entropy_free(&entropy);
+
+ OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+ return 0;
+}
+
+OCStackResult OCGenerateRandomSerialNumber(char **serial, size_t *serialLen)
+{
+ int ret = 0;
+ OCStackResult res = OC_STACK_ERROR;
+ unsigned char random[20]; /* Per RFC 5280, 20 octets is the maximum length of a serial number. */
+ mbedtls_mpi serialMpi;
+
+ VERIFY_NOT_NULL_RETURN(TAG, serial, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, serialLen, ERROR, OC_STACK_INVALID_PARAM);
+
+ mbedtls_mpi_init(&serialMpi);
+ memset(serial, 0, sizeof(*serial));
+
+ VERIFY_SUCCESS(TAG, OCGetRandomBytes(random, sizeof(random)), ERROR);
+
+ /* Per RFC 5280, 20 octets is the maximum length of a serial number. In ASN.1, if the highest-order
+ * bit is set it causes a padding octet to be written, which would be 21 and non-compliant.
+ * Therefore, always clear the highest-order bit. Integers in ASN.1 are always big-Endian.
+ */
+ random[0] &= 0x7F;
+
+ /* Import into a large integer object and then output as a string. */
+ ret = mbedtls_mpi_read_binary(&serialMpi, random, sizeof(random));
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ /* Get the needed string length and allocate. */
+ ret = mbedtls_mpi_write_string(&serialMpi, 10, NULL, 0, serialLen);
+ VERIFY_SUCCESS(TAG, ret == MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL, ERROR);
+ *serial = OICCalloc(1, *serialLen);
+ VERIFY_NOT_NULL(TAG, *serial, ERROR);
+
+ /* Do the write for real. */
+ ret = mbedtls_mpi_write_string(&serialMpi, 10, *serial, *serialLen, serialLen);
+ VERIFY_SUCCESS(TAG, ret == 0, ERROR);
+
+ res = OC_STACK_OK;
+
+exit:
+
+ if (OC_STACK_OK != res)
+ {
+ OICFree(*serial);
+ *serial = NULL;
+ *serialLen = 0;
+ }
+ mbedtls_mpi_free(&serialMpi);
+
+ return res;
+}
+
+OCStackResult OCGenerateKeyPair(char **publicKey, size_t *publicKeyLen,
+ char **privateKey, size_t *privateKeyLen)
+{
+ int ret = 0;
+ mbedtls_pk_context keyPair;
+ unsigned char buf[2048];
+
+ mbedtls_pk_init(&keyPair);
+
+ VERIFY_NOT_NULL_RETURN(TAG, publicKey, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, publicKeyLen, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, privateKey, ERROR, OC_STACK_INVALID_PARAM);
+ VERIFY_NOT_NULL_RETURN(TAG, privateKeyLen, ERROR, OC_STACK_INVALID_PARAM);
+
+ *publicKey = NULL;
+ *publicKeyLen = 0;
+ *privateKey = NULL;
+ *privateKeyLen = 0;
+
+ ret = OCInternalGenerateKeyPair(&keyPair);
+ if (ret != 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to generate key pair: %d", ret);
+ goto exit;
+ }
+
+ ret = mbedtls_pk_write_pubkey_pem(&keyPair, buf, sizeof(buf));
+ if (ret != 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to export public key as PEM: %d", ret);
+ goto exit;
+ }
+
+ *publicKeyLen = strlen(buf) + 1;
+ *publicKey = OICCalloc(1, *publicKeyLen);
+ if (NULL == *publicKey)
+ {
+ OIC_LOG(ERROR, TAG, "Could not allocate memory for public key");
+ ret = -1;
+ goto exit;
+ }
+ memcpy(*publicKey, buf, *publicKeyLen);
+
+ ret = mbedtls_pk_write_key_pem(&keyPair, buf, sizeof(buf));
+ if (ret != 0)
+ {
+ OIC_LOG_V(ERROR, TAG, "Failed to export private key as PEM: %d", ret);
+ goto exit;
+ }
+
+ *privateKeyLen = strlen(buf) + 1;
+ *privateKey = OICCalloc(1, *privateKeyLen);
+ if (NULL == *privateKey)
+ {
+ OIC_LOG(ERROR, TAG, "Could not allocate memory for private key");
+ ret = -1;
+ goto exit;
+ }
+ memcpy(*privateKey, buf, *privateKeyLen);
+
+exit:
+
+ mbedtls_pk_free(&keyPair);
+
+ OICClearMemory(buf, sizeof(buf));
+
+ if (ret != 0)
+ {
+ OICFree(*publicKey);
+ OICClearMemory(*privateKey, *privateKeyLen);
+ OICFree(*privateKey);
+
+ *publicKey = NULL;
+ *publicKeyLen = 0;
+ *privateKey = NULL;
+ *privateKeyLen = 0;
+
+ return OC_STACK_ERROR;
+ }
+ else
+ {
+ return OC_STACK_OK;
+ }
+}
+
+typedef enum {
+ CERT_TYPE_CA,
+ CERT_TYPE_IDENTITY,
+ CERT_TYPE_ROLE
+} CertificateType_t;
+
+static OCStackResult GenerateCertificate(
+ CertificateType_t certType,
+ const char *subject,
+ const char *subjectPublicKey,
+ const char *issuerCert,
+ const char *issuerPrivateKey,
+ const char *serial,
+ const char *notValidBefore,
+ const char *notValidAfter,
+ const char *role,
+ const char *authority,
+ OCByteString *certificate)
+{
+ OCStackResult res = OC_STACK_INVALID_PARAM;
+ int ret = 0;
+ mbedtls_x509write_cert outCertCtx;
+ mbedtls_pk_context subjKeyCtx;
+ mbedtls_pk_context issKeyCtx;
+ mbedtls_x509_crt issCertCtx;
+ mbedtls_mpi serialMpi;
+ mbedtls_x509_general_names names;
+ mbedtls_entropy_context entropy;
+ mbedtls_ctr_drbg_context ctr_drbg;
+
+ char buf[2048];
+
+ if (NULL == subjectPublicKey || NULL == issuerPrivateKey || NULL == subject || NULL == serial ||
+ NULL == notValidBefore || NULL == notValidAfter)
+ {
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ mbedtls_x509write_crt_init(&outCertCtx);
+ mbedtls_pk_init(&subjKeyCtx);
+ mbedtls_pk_init(&issKeyCtx);
+ mbedtls_x509_crt_init(&issCertCtx);
+ mbedtls_mpi_init(&serialMpi);
+ memset(&names, 0, sizeof(names));
+ mbedtls_ctr_drbg_init(&ctr_drbg);
+ mbedtls_entropy_init(&entropy);
+ memset(certificate, 0, sizeof(*certificate));
+
+ ret = mbedtls_mpi_read_string(&serialMpi, 10, serial);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ ret = mbedtls_pk_parse_public_key(&subjKeyCtx, subjectPublicKey, strlen(subjectPublicKey) + 1);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ ret = mbedtls_pk_parse_key(&issKeyCtx, issuerPrivateKey, strlen(issuerPrivateKey) + 1, NULL, 0);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ /* If issuerCert is NULL, then the cert will be self-signed. */
+ if (NULL != issuerCert)
+ {
+ ret = mbedtls_x509_crt_parse(&issCertCtx, issuerCert, strlen(issuerCert) + 1);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ }
+
+ ret = mbedtls_x509write_crt_set_validity(&outCertCtx, notValidBefore, notValidAfter);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ mbedtls_x509write_crt_set_version(&outCertCtx, MBEDTLS_X509_CRT_VERSION_3);
+ mbedtls_x509write_crt_set_md_alg(&outCertCtx, MBEDTLS_MD_SHA256);
+
+ res = OC_STACK_ERROR;
+
+ ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func,
+ &entropy, PERSONALIZATION_STRING, sizeof(PERSONALIZATION_STRING));
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ mbedtls_ctr_drbg_set_prediction_resistance(&ctr_drbg, MBEDTLS_CTR_DRBG_PR_ON);
+
+ ret = mbedtls_x509write_crt_set_serial(&outCertCtx, &serialMpi);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ ret = mbedtls_x509write_crt_set_subject_name(&outCertCtx, subject);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ if (NULL != issuerCert)
+ {
+ // mbedtls_x509_dn_gets returns the number of bytes written to buf.
+ ret = mbedtls_x509_dn_gets(buf, sizeof(buf), &issCertCtx.subject);
+ VERIFY_SUCCESS(TAG, 0 < ret, ERROR);
+ ret = mbedtls_x509write_crt_set_issuer_name(&outCertCtx, buf);
+ }
+ else
+ {
+ /* If self-signed, use the same contents of subject for the issuer name. */
+ ret = mbedtls_x509write_crt_set_issuer_name(&outCertCtx, subject);
+ }
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ mbedtls_x509write_crt_set_subject_key(&outCertCtx, &subjKeyCtx);
+
+ mbedtls_x509write_crt_set_issuer_key(&outCertCtx, &issKeyCtx);
+
+ if (certType == CERT_TYPE_CA)
+ {
+ ret = mbedtls_x509write_crt_set_basic_constraints(&outCertCtx, 1, -1);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ ret = mbedtls_x509write_crt_set_key_usage(&outCertCtx,
+ MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_CERT_SIGN);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ }
+ else
+ {
+ ret = mbedtls_x509write_crt_set_basic_constraints(&outCertCtx, 0, 0);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ ret = mbedtls_x509write_crt_set_key_usage(&outCertCtx,
+ MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
+ MBEDTLS_X509_KU_KEY_ENCIPHERMENT |
+ MBEDTLS_X509_KU_DATA_ENCIPHERMENT |
+ MBEDTLS_X509_KU_KEY_AGREEMENT);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ }
+
+ switch (certType)
+ {
+ case CERT_TYPE_ROLE:
+ ret = mbedtls_x509write_crt_set_extension(&outCertCtx,
+ MBEDTLS_OID_EXTENDED_KEY_USAGE, MBEDTLS_OID_SIZE(MBEDTLS_OID_EXTENDED_KEY_USAGE),
+ 0,
+ s_ekuRole, sizeof(s_ekuRole));
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ ret = snprintf(buf, sizeof(buf), "CN=%s%s%s", role, (NULL != authority) ? ",OU=" : "", (NULL != authority) ? authority : "");
+ VERIFY_SUCCESS(TAG, ret < sizeof(buf), ERROR);
+ names.next = NULL;
+ names.general_name.name_type = MBEDTLS_X509_GENERALNAME_DIRECTORYNAME;
+ ret = mbedtls_x509_string_to_names(&names.general_name.directory_name, buf);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ ret = mbedtls_x509write_crt_set_subject_alt_names(&outCertCtx, &names);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ break;
+
+ case CERT_TYPE_IDENTITY:
+ ret = mbedtls_x509write_crt_set_extension(&outCertCtx,
+ MBEDTLS_OID_EXTENDED_KEY_USAGE, MBEDTLS_OID_SIZE(MBEDTLS_OID_EXTENDED_KEY_USAGE),
+ 0,
+ s_ekuIdentity, sizeof(s_ekuIdentity));
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ break;
+
+ case CERT_TYPE_CA:
+ ret = mbedtls_x509write_crt_set_extension(&outCertCtx,
+ MBEDTLS_OID_EXTENDED_KEY_USAGE, MBEDTLS_OID_SIZE(MBEDTLS_OID_EXTENDED_KEY_USAGE),
+ 0,
+ s_ekuCA, sizeof(s_ekuCA));
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+ break;
+
+ default:
+ assert(false);
+ VERIFY_SUCCESS(TAG, false, ERROR);
+ }
+
+ ret = mbedtls_x509write_crt_pem(&outCertCtx, buf, sizeof(buf), mbedtls_ctr_drbg_random, &ctr_drbg);
+ VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+
+ certificate->len = strlen(buf) + 1;
+ certificate->bytes = (uint8_t *)OICCalloc(1, certificate->len);
+ VERIFY_NOT_NULL(TAG, certificate->bytes, ERROR);
+ memcpy(certificate->bytes, buf, certificate->len);
+
+ res = OC_STACK_OK;
+
+exit:
+
+ if (OC_STACK_OK != res)
+ {
+ OICFree(certificate->bytes);
+ certificate->bytes = NULL;
+ certificate->len = 0;
+ }
+
+ mbedtls_ctr_drbg_free(&ctr_drbg);
+ mbedtls_entropy_free(&entropy);
+ mbedtls_asn1_free_named_data_list(&names.general_name.directory_name);
+ mbedtls_mpi_free(&serialMpi);
+ mbedtls_x509_crt_free(&issCertCtx);
+ mbedtls_pk_free(&issKeyCtx);
+ mbedtls_pk_free(&subjKeyCtx);
+ mbedtls_x509write_crt_free(&outCertCtx);
+
+ return res;
+}
+
+OCStackResult OCGenerateCACertificate(
+ const char *subject,
+ const char *subjectPublicKey,
+ const char *issuerCert,
+ const char *issuerPrivateKey,
+ const char *serial,
+ const char *notValidBefore,
+ const char *notValidAfter,
+ char **certificate,
+ size_t *certificateLen)
+{
+ OCStackResult res = OC_STACK_OK;
+ OCByteString byteStr = { 0 };
+
+ res = GenerateCertificate(
+ CERT_TYPE_CA,
+ subject,
+ subjectPublicKey,
+ issuerCert,
+ issuerPrivateKey,
+ serial,
+ notValidBefore,
+ notValidAfter,
+ NULL,
+ NULL,
+ &byteStr);
+
+ if (OC_STACK_OK == res)
+ {
+ *certificate = byteStr.bytes;
+ *certificateLen = byteStr.len;
+ }
+
+ return res;
+}
+
+OCStackResult OCGenerateIdentityCertificate(
+ const OicUuid_t *subjectUuid,
+ const char *subjectPublicKey,
+ const char *issuerCert,
+ const char *issuerPrivateKey,
+ const char *serial,
+ const char *notValidBefore,
+ const char *notValidAfter,
+ char **certificate,
+ size_t *certificateLen)
+{
+ OCStackResult res = OC_STACK_OK;
+ OCByteString byteStr = { 0 };
+ char uuidStr[UUID_STRING_SIZE] = { 0 } ;
+ char subject[sizeof(uuidStr) + sizeof(SUBJECT_PREFIX)] = { 0 } ;
+
+ if (NULL == issuerCert)
+ {
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ if (!OCConvertUuidToString(subjectUuid->id, uuidStr))
+ {
+ OIC_LOG(ERROR, TAG, "Could not convert UUID");
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ if (snprintf(subject, sizeof(subject), "%s%s", SUBJECT_PREFIX, uuidStr) == sizeof(subject))
+ {
+ OIC_LOG(ERROR, TAG, "Could not write subject string");
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ res = GenerateCertificate(
+ CERT_TYPE_IDENTITY,
+ subject,
+ subjectPublicKey,
+ issuerCert,
+ issuerPrivateKey,
+ serial,
+ notValidBefore,
+ notValidAfter,
+ NULL,
+ NULL,
+ &byteStr);
+
+ if (OC_STACK_OK == res)
+ {
+ *certificate = byteStr.bytes;
+ *certificateLen = byteStr.len;
+ }
+
+ return res;
+}
+
+OCStackResult OCGenerateRoleCertificate(
+ const OicUuid_t *subjectUuid,
+ const char *subjectPublicKey,
+ const char *issuerCert,
+ const char *issuerPrivateKey,
+ const char *serial,
+ const char *notValidBefore,
+ const char *notValidAfter,
+ const char *role,
+ const char *authority,
+ char **certificate,
+ size_t *certificateLen)
+{
+ OCStackResult res = OC_STACK_ERROR;
+ OCByteString byteStr;
+ char uuidStr[UUID_STRING_SIZE] = { 0 };
+ char subject[sizeof(uuidStr) + sizeof(SUBJECT_PREFIX)] = { 0 };
+
+ memset(&byteStr, 0, sizeof(byteStr));
+
+ if (NULL == role || NULL == issuerCert)
+ {
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ if (!OCConvertUuidToString(subjectUuid->id, uuidStr))
+ {
+ OIC_LOG(ERROR, TAG, "Could not convert UUID");
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ if (snprintf(subject, sizeof(subject), "%s%s", SUBJECT_PREFIX, uuidStr) == sizeof(subject))
+ {
+ OIC_LOG(ERROR, TAG, "Could not write subject string");
+ return OC_STACK_INVALID_PARAM;
+ }
+
+ res = GenerateCertificate(
+ CERT_TYPE_ROLE,
+ subject,
+ subjectPublicKey,
+ issuerCert,
+ issuerPrivateKey,
+ serial,
+ notValidBefore,
+ notValidAfter,
+ role,
+ authority,
+ &byteStr);
+
+ if (OC_STACK_OK == res)
+ {
+ *certificate = byteStr.bytes;
+ *certificateLen = byteStr.len;
+ }
+
+ return res;
+}
+
+#endif /* defined(__WITH_TLS__) || defined(__WITH_DTLS__) */
\ No newline at end of file