Gen/Check certs against Bangkok requirements 77/24077/13
authorSteven Saunders <s.saunders-contractor@cablelabs.com>
Fri, 2 Feb 2018 21:50:23 +0000 (14:50 -0700)
committerNathan Heldt-Sheller <nathan.heldt-sheller@intel.com>
Fri, 2 Mar 2018 19:28:03 +0000 (19:28 +0000)
Change-Id: I3ee0fb005c6c9ce91a0e4c16454b6856c508616f
Signed-off-by: Steven Saunders <s.saunders-contractor@cablelabs.com>
18 files changed:
.gitignore
resource/csdk/connectivity/common/inc/cacommonutil.h
resource/csdk/connectivity/inc/cacertprofile.h [new file with mode: 0644]
resource/csdk/connectivity/src/SConscript
resource/csdk/connectivity/src/adapter_util/ca_adapter_net_ssl.c
resource/csdk/connectivity/src/adapter_util/cacertprofile.c [new file with mode: 0644]
resource/csdk/connectivity/test/SConscript
resource/csdk/connectivity/test/cacertprofiletest.cpp [new file with mode: 0644]
resource/csdk/security/include/occertutility.h
resource/csdk/security/include/srmutility.h
resource/csdk/security/provisioning/sample/certgenerator.cpp
resource/csdk/security/provisioning/sample/oic_svr_db_client.dat
resource/csdk/security/provisioning/sample/oic_svr_db_client.json
resource/csdk/security/provisioning/sample/oic_svr_db_server_mfg.dat
resource/csdk/security/provisioning/sample/oic_svr_db_server_mfg.json
resource/csdk/security/provisioning/sample/provisioningclient.c
resource/csdk/security/src/occertutility.c
resource/csdk/stack/octbstack_product_secured.def

index 264f247..cfda32d 100644 (file)
@@ -67,6 +67,9 @@ proguard-project.txt
 *.iml
 .idea
 
+# Ignore Project files for VS Code
+.vscode/
+
 # Ignore CTags default data
 tags
 
index 337c62d..e1db745 100644 (file)
@@ -89,6 +89,31 @@ extern "C"
         return; \
     } \
 
+/**
+ * Macro to verify an expression, goto exit: if not satisfied
+ *
+ * @param  log_tag    log tag
+ * @param  expr       Expression to verify.
+ * @param  msg        Message to log prior to exiting
+ * @param  log_level  logging level
+ *
+ * @note Invoking function must define "exit:" label for goto functionality to work correctly.
+ */
+#define VERIFY_SUCCESS_OR_EXIT(log_tag, expr, msg, log_level) do{ if (!(expr)) \
+    {OIC_LOG((log_level), (log_tag), (msg)); goto exit; } }while(0)
+
+
+/**
+ * Macro to verify an expression, or return
+ *
+ * @param  log_tag    log tag
+ * @param  expr       Expression to verify.
+ * @param  msg        Message to log prior to exiting
+ * @param  log_level  logging level
+ */
+#define VERIFY_SUCCESS_OR_RETURN(log_tag, expr, msg, log_level) do{ if (!(expr)) \
+    {OIC_LOG((log_level), (log_tag), (msg)); return; } }while(0)
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif /* __cplusplus */
diff --git a/resource/csdk/connectivity/inc/cacertprofile.h b/resource/csdk/connectivity/inc/cacertprofile.h
new file mode 100644 (file)
index 0000000..0dd8a07
--- /dev/null
@@ -0,0 +1,194 @@
+//******************************************************************
+// Copyright 2018 Cable Television Laboratories, Inc.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+/**
+ * @file
+ *
+ * This file contains common utility functions for validating OCF certificate profiles
+ */
+
+#ifndef CA_CERT_PROFILE_H_
+#define CA_CERT_PROFILE_H_
+
+#if defined(__WITH_DTLS__) || defined (__WITH_TLS__)
+
+#include <mbedtls/x509_crt.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/**
+ * Certificate Types
+ */
+
+typedef enum
+{
+    CERT_CA_ROOT = 0,
+    CERT_CA_INT,
+    CERT_EE
+} CertType;
+
+/**
+ * Cert profile return values
+ */
+typedef enum
+{
+    CP_STATUS_OK  = 0,       /* Success */
+    CP_NO_EE_CERT,           /* No end entity cert in cert chain */
+    CP_MUL_EE_CERTS,         /* Multiple end entity certs in cert chain */
+    CP_PROFILE_VIOLATION,    /* Certificate(s) don't satisfy OCF profile requirements */
+    CP_INVALID_CERT_INPUT,   /* Certificate input is invalid (or null) */
+    CP_DATE_ERROR,           /* Problem setting or reading certificate validity dates */
+    CP_BUF_TOO_SMALL,        /* Supplied buffer is not long enough for desired operation */
+    CP_STATUS_FAILED = 255   /* Failure */
+} CertProfileResult;
+
+/**
+ * Return values for cert chain functions that return a counts or -1 on failure
+ */
+#define CP_INVALID_CERT_CHAIN -1
+#define CP_INVALID_CERT_LIST -1
+
+/**
+ * Macro to log an mbedtls error
+ * For mbedtls functions that return 0 as non-error
+ *
+ * @param  log_tag    log tag
+ * @param  ret        value returned by mbedtls call
+ * @param  log_level  logging level
+ * @param  buf        char* buffer for error string processing
+ * @param  buf_size   size of bug
+ *
+ * NOTE: you must include "mbedtls/error.h" in order to use this
+ */
+#define CP_LOG_MBED_ERROR(log_tag, ret, buf, buf_size, log_level) do{ if (0!=(ret)) { \
+    mbedtls_strerror((ret), (buf), (buf_size));                                    \
+    OIC_LOG_V((log_level), (log_tag), "mbedtls error:  %s", (buf)); } }while(0)
+
+/**
+ * Cert profile violation flags
+ */
+typedef unsigned long CertProfileViolations;
+
+#define CP_NO_VIOLATIONS                      (0)
+#define CP_PROCESSING_ERROR                   (1 << 1)
+#define CP_INVALID_SIG_ALGORITHM              (1 << 2)
+#define CP_INVALID_VERSION                    (1 << 3)
+#define CP_INVALID_PUB_KEY_ALGORITHM          (1 << 4)
+#define CP_INVALID_KEY_USAGE_MISSING          (1 << 5)
+#define CP_INVALID_KEY_USAGE_EXTRA            (1 << 6)
+#define CP_INVALID_BASIC_CONSTRAINTS_CA       (1 << 7)
+#define CP_INVALID_BASIC_CONSTRAINTS_PATH_LEN (1 << 8)
+#define CP_INVALID_EKU_NO_SERVER_AUTH         (1 << 9)
+#define CP_INVALID_EKU_NO_CLIENT_AUTH         (1 << 10)
+#define CP_INVALID_EKU_NO_OCF_ID_OID          (1 << 11)
+#define CP_INVALID_EKU_INCLUDES_ANY           (1 << 12)
+#define CP_INVALID_ISSUER_SUBJ_MISMATCH       (1 << 13)
+#define CP_NOT_YET_VALID                      (1 << 14)
+#define CP_EXPIRED                            (1 << 15)
+
+
+/**
+ * Validate an end-entity certificate against OCF cert profile requirements
+ *
+ * @param[in]   cert   end entity cert to validate
+ *
+ * @return      Success: CP_NO_VIOLATIONS
+ *              Errors: 1 or more violation bits set
+ */
+CertProfileViolations ValidateEndEntityCertProfile(const mbedtls_x509_crt *cert);
+
+/**
+ * Validate an intermediate-ca certificate against OCF cert profile requirements
+ *
+ * @param[in]   cert   end entity ca cert to validate
+ *
+ * @return      Success: CP_NO_VIOLATIONS
+ *              Errors: 1 or more violation bits set
+ */
+CertProfileViolations ValidateIntermediateCACertProfile(const mbedtls_x509_crt *cert);
+
+/**
+ * Validate a root-ca certificate against OCF cert profile requirements
+ *
+ * @param[in]   cert   intermediate ca cert to validate
+ *
+ * @return      Success: CP_NO_VIOLATIONS
+ *              Errors: 1 or more violation bits set
+ */
+CertProfileViolations ValidateRootCACertProfile(const mbedtls_x509_crt *cert);
+
+/**
+ * Validate a certificate's time window
+ *
+ * @param[in]   cert   cert to validate
+ *
+ * @return      Success: CP_NO_VIOLATIONS
+ *              Errors: CP_NOT_YET_VALID and/or CP_EXPIRED
+ */
+CertProfileViolations ValidateCertTimeWindow(const mbedtls_x509_crt *cert);
+
+/**
+ * Given a cert chain intended for authentication, validated that the certs
+ * within the chain conform to OCF certificate profile requirements.
+ *
+ * @param[in]   certChain  cert chain intended for authentication
+ *
+ * @return      Number of certs in the list that did not validate.
+ *              0 = success
+ *              CP_INVALID_CERT_CHAIN for invalid input chain
+ *              > 0 = some number of certs did not validate
+ */
+int ValidateAuthCertChainProfiles(const mbedtls_x509_crt *certChain);
+
+/**
+ * Given a list of root CA certs, validated that the certs
+ * within the list conform to OCF certificate profile requirements.
+ *
+ * @param[in]   certList      Chain chain intended for authentication
+ *
+ * @return      Number of certs in the list that did not validate.
+ *              0 = success
+ *              CP_INVALID_CERT_LIST for invalid input cert list
+ *              > 0 = some number of certs did not validate
+ */
+int ValidateRootCACertListProfiles(const mbedtls_x509_crt *certList);
+
+/**
+ * Given a list of certificates, validate that they are currently
+ * within each certificate's valid time window
+ *
+ * @param[in]   certList   List of certs for which to check time window
+ *
+ * @return      number of certs in the list that are NOT in valid time window relative to now
+ *              0 = success
+ *              CP_INVALID_CERT_LIST for invalid input cert list
+ *              > 0 = some number of certs did not validate
+ */
+int CheckCertListTimeWindows(const mbedtls_x509_crt *certList);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif // (__WITH_DTLS__) || defined (__WITH_TLS__)
+
+#endif  /* CA_CERT_PROFILE_H_ */
index acd4e8b..e3af45a 100644 (file)
@@ -49,7 +49,10 @@ connectivity_env.SConscript(root_dir + '/common/SConscript',
 connectivity_env.SConscript(root_dir + '/util/SConscript',
                             exports='connectivity_env')
 
-src_files = [File('adapter_util/caadapterutils.c'),]
+src_files = [File(src) for src in (
+    'adapter_util/caadapterutils.c',
+    'adapter_util/cacertprofile.c'
+)]
 
 if (('BLE' in ca_transport) or ('ALL' in ca_transport)):
     src_files.append(File('adapter_util/cafragmentation.c'))
index 1786a13..e5cee10 100644 (file)
@@ -32,6 +32,7 @@
 #include "ca_adapter_net_ssl.h"
 #include "cacommon.h"
 #include "caipinterface.h"
+#include "cacertprofile.h"
 #include "oic_malloc.h"
 #include "experimental/ocrandom.h"
 #include "experimental/byte_array.h"
@@ -758,6 +759,19 @@ static int InitPKIX(CATransportAdapter_t adapter)
         OIC_LOG_V(WARNING, NET_SSL_TAG, "Own certificate chain parsing error: %d certs failed to parse", errNum);
         goto required;
     }
+
+    ret = ValidateAuthCertChainProfiles(&g_caSslContext->crt);
+    if (CP_INVALID_CERT_CHAIN == ret)
+    {
+        OIC_LOG(ERROR, NET_SSL_TAG, "Invalid own cert chain");
+        goto required;
+    }
+    else if (0 != ret)
+    {
+        OIC_LOG_V(ERROR, NET_SSL_TAG, "%d certificate(s) in own cert chain do not satisfy OCF profile requirements", ret);
+        goto required;
+    }
+
     ret =  mbedtls_pk_parse_key(&g_caSslContext->pkey, pkiInfo.key.data, pkiInfo.key.len,
                                                                                NULL, 0);
     if (0 != ret)
@@ -808,6 +822,24 @@ static int InitPKIX(CATransportAdapter_t adapter)
     {
         OIC_LOG_V(WARNING, NET_SSL_TAG, "CA chain parsing warning: %d certs failed to parse", errNum);
     }
+    else
+    {
+        ret = ValidateRootCACertListProfiles(&g_caSslContext->ca);
+        if (CP_INVALID_CERT_LIST == ret)
+        {
+            OIC_LOG(ERROR, NET_SSL_TAG, "Invalid own CA cert chain");
+            OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
+            DeInitPkixInfo(&pkiInfo);
+            return -1;
+        }
+        else if (0 < ret )
+        {
+            OIC_LOG_V(ERROR, NET_SSL_TAG, "%d certificate(s) in own CA cert chain violate OCF Root CA cert profile requirements", ret);
+            OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
+            DeInitPkixInfo(&pkiInfo);
+            return -1;
+        }
+    }
 
     ret = mbedtls_x509_crl_parse_der(&g_caSslContext->crl, pkiInfo.crl.data, pkiInfo.crl.len);
     if(0 != ret)
@@ -2204,6 +2236,22 @@ CAResult_t CAdecryptSsl(const CASecureEndpoint_t *sep, uint8_t *data, size_t dat
             mbedtls_x509_crt *peerCert = peer->ssl.session_negotiate->peer_cert;
             if (NULL != peerCert)
             {
+                ret = ValidateAuthCertChainProfiles(peerCert);
+                if (CP_INVALID_CERT_CHAIN == ret)
+                {
+                    oc_mutex_unlock(g_sslContextMutex);
+                    OIC_LOG(ERROR, NET_SSL_TAG, "Invalid peer cert chain");
+                    OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
+                    return CA_STATUS_FAILED;
+                }
+                else if (0 != ret)
+                {
+                    oc_mutex_unlock(g_sslContextMutex);
+                    OIC_LOG_V(ERROR, NET_SSL_TAG, "%d certificate(s) in peer cert chain do not satisfy OCF profile requirements", ret);
+                    OIC_LOG_V(DEBUG, NET_SSL_TAG, "Out %s", __func__);
+                    return CA_STATUS_FAILED;
+                }
+
                 ret = PeerCertExtractCN(peerCert);
                 if (CA_STATUS_OK != ret)
                 {
diff --git a/resource/csdk/connectivity/src/adapter_util/cacertprofile.c b/resource/csdk/connectivity/src/adapter_util/cacertprofile.c
new file mode 100644 (file)
index 0000000..e7063ba
--- /dev/null
@@ -0,0 +1,490 @@
+//******************************************************************
+// Copyright 2018 Cable Television Laboratories, Inc.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// 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 lan guage governing permissions and
+// limitations under the License.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+#include <string.h>
+#include <time.h>
+
+#include <mbedtls/error.h>
+#include <mbedtls/x509_crt.h>
+#include <mbedtls/oid.h>
+
+#include "cacommonutil.h"
+#include "cacertprofile.h"
+#include "experimental/logger.h"
+
+#define TAG "OIC_CC_CERT_PROFILE"
+
+// OCF Compliant ID Cert profiles
+static const mbedtls_x509_crt_profile s_certProfile = {
+    MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256),        // MD algorithms
+    MBEDTLS_X509_ID_FLAG(MBEDTLS_PK_ECKEY) |        // Allowed key types
+    MBEDTLS_X509_ID_FLAG(MBEDTLS_PK_ECDSA),
+    MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_SECP256R1), // EC curves
+    0                                               // RSA minimum key length - not used because we only use EC key pairs
+};
+
+// OID for ID certificates (1.3.6.1.4.1.44924.1.6) suitable for mbedTLS check
+static const char s_ekuIdOid[] = MBEDTLS_OID_ISO_IDENTIFIED_ORG "\x06\x01\x04\x01\x82\xDE\x7C\x01\x06";
+
+
+static const unsigned int s_eeKeyUsage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
+                                         MBEDTLS_X509_KU_KEY_AGREEMENT;
+
+static const unsigned int s_eeNonKeyUsage = MBEDTLS_X509_KU_NON_REPUDIATION |
+                                            MBEDTLS_X509_KU_DATA_ENCIPHERMENT |
+                                            MBEDTLS_X509_KU_KEY_ENCIPHERMENT |
+                                            MBEDTLS_X509_KU_KEY_CERT_SIGN |
+                                            MBEDTLS_X509_KU_CRL_SIGN |
+                                            MBEDTLS_X509_KU_ENCIPHER_ONLY |
+                                            MBEDTLS_X509_KU_DECIPHER_ONLY;
+
+static const unsigned int s_caKeyUsage = MBEDTLS_X509_KU_KEY_CERT_SIGN |
+                                         MBEDTLS_X509_KU_CRL_SIGN;
+
+static const unsigned int s_caNonKeyUsage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE |
+                                            MBEDTLS_X509_KU_NON_REPUDIATION  |
+                                            MBEDTLS_X509_KU_KEY_ENCIPHERMENT |
+                                            MBEDTLS_X509_KU_DATA_ENCIPHERMENT |
+                                            MBEDTLS_X509_KU_KEY_AGREEMENT |
+                                            MBEDTLS_X509_KU_ENCIPHER_ONLY |
+                                            MBEDTLS_X509_KU_DECIPHER_ONLY;
+
+#if defined(__WITH_DTLS__) || defined (__WITH_TLS__)
+
+static CertProfileResult FindEndEntityCert(const mbedtls_x509_crt *certChain, mbedtls_x509_crt const **eeCert)
+{
+    *eeCert = NULL;
+
+    const mbedtls_x509_crt* curCert = certChain;
+    while (NULL != curCert)
+    {
+        if (0 == curCert->ca_istrue)
+        {
+            // first EE
+            if (NULL == *eeCert)
+            {
+                *eeCert = curCert;
+            }
+            // more than 1 EE is an error condition
+            else
+            {
+                *eeCert = NULL;
+                OIC_LOG(ERROR, TAG, "More than 1 end entity cert in chain");
+                return CP_MUL_EE_CERTS;
+            }
+        }
+        curCert = curCert->next;
+    }
+
+    if (NULL == *eeCert)
+    {
+        OIC_LOG(WARNING, TAG, "No end entity cert in chain");
+        return CP_NO_EE_CERT;
+    }
+
+    return CP_STATUS_OK;
+}
+
+static CertProfileResult CheckMdAlgorithm(const mbedtls_x509_crt_profile *profile, mbedtls_md_type_t mdAlgorithm)
+{
+    if ((MBEDTLS_X509_ID_FLAG(mdAlgorithm) & profile->allowed_mds) != 0)
+    {
+        return CP_STATUS_OK;
+    }
+    return CP_STATUS_FAILED;
+}
+
+static CertProfileResult CheckPubKeyAlgorithm(const mbedtls_x509_crt_profile *profile, mbedtls_pk_type_t pkAlgorithm)
+{
+    if ((MBEDTLS_X509_ID_FLAG(pkAlgorithm) & profile->allowed_pks) != 0)
+    {
+        return CP_STATUS_OK;
+    }
+    return CP_STATUS_FAILED;
+}
+
+
+// returns CP_STATUS_OK if pathlen is valid for cert type, othewise returns CP_STATUS_FAILED
+static CertProfileResult CheckPathLen( CertType certType, int mbedMaxPathLen) {
+
+    // mbedtls max_pathlen behaviour:
+    // CA Cert: Expects RFC5280_val as encoding input
+    //          Provides RFC5280_val+1 as decoding output, where 0 = not present
+    // EE Cert: Does not encode
+    //          Provides 0 as decoding output
+
+    switch(certType)
+    {
+        case CERT_CA_ROOT:
+            return (0 == mbedMaxPathLen) ? CP_STATUS_OK : CP_STATUS_FAILED;
+        case CERT_CA_INT:
+            return (1 == mbedMaxPathLen) ? CP_STATUS_OK : CP_STATUS_FAILED;
+        case CERT_EE:
+            return (0 == mbedMaxPathLen) ? CP_STATUS_OK : CP_STATUS_FAILED;
+        default:
+        {
+            OIC_LOG(WARNING, TAG, "Unkown cert type for pathlen check");
+            return CP_STATUS_FAILED;
+        }
+    }
+}
+
+// Check all cert entries that are common across root-ca, intermediate-ca, and ee certs
+static CertProfileViolations ValidateCommonCertProfileEntries(const mbedtls_x509_crt *cert) {
+
+    CertProfileResult cpResult = CP_STATUS_OK;
+    CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
+
+    // Start with time windows
+    // notBefore: Required
+    // notAfter: Required
+    profileViolations = ValidateCertTimeWindow(cert);
+    if (CP_NO_VIOLATIONS != profileViolations)
+    {
+        OIC_LOG(ERROR, TAG, "Cert has expired or is not yet valid");
+    }
+
+    // signatureAlgorithm
+    // ecdsa-with-SHA256 (OID: 1.2.840.10045.4.3.2)
+    cpResult = CheckMdAlgorithm(&s_certProfile, cert->sig_md);
+    if (CP_STATUS_OK != cpResult)
+    {
+        OIC_LOG(ERROR, TAG, "Cert signature algorithm must be SHA256");
+        profileViolations |= CP_INVALID_SIG_ALGORITHM;
+    }
+
+    // Version: v3
+    // mbedTLS version 3 = x509 v3
+    if (3 != cert->version)
+    {
+        OIC_LOG(ERROR, TAG, "Cert is not x509 v3");
+        profileViolations |= CP_INVALID_VERSION;
+    }
+
+    // Subject Public Key Info
+    // id-ecPublicKey (OID: 1.2.840.10045.2.1) secp256r1 (OID:1.2.840.10045.3.1.7)
+    cpResult = CheckPubKeyAlgorithm(&s_certProfile, cert->sig_pk);
+    if (CP_STATUS_OK != cpResult)
+    {
+        OIC_LOG(ERROR, TAG, "Cert public key algorithm must be ECDSA");
+        profileViolations |= CP_INVALID_PUB_KEY_ALGORITHM;
+    }
+
+    // SerialNumber: SHALL be a positive integer, unique among all certificates issued by Root CA
+    // Not possible to validate SN uniqueness
+
+    // Issuer: SHALL match the Subject field of the issuing Root CA
+    // mbedTLS will check proper chaining during DTLS handshake
+
+    return profileViolations;
+}
+
+
+CertProfileViolations ValidateEndEntityCertProfile(const mbedtls_x509_crt *cert)
+{
+
+    // OCF requirements exist for the following extensions, but w/o mbedTLS support
+    // * check for certificate policies, if present must be 1.3.6.1.4.1.51414.0.1.1
+    // * cRL Distributiojn Points
+
+    if (NULL == cert)
+    {
+        return CP_INVALID_CERT_INPUT;
+    }
+
+    int mbedRet = 0;
+    CertProfileResult cpResult = CP_STATUS_OK;
+    CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
+
+    // Check all common entries first
+    profileViolations = ValidateCommonCertProfileEntries(cert);
+
+    // keyUsage (REQUIRED/Critical)
+    // digitalSignature (0) and keyAgreement(4) bits SHALL be the only bits enabled
+    mbedRet = mbedtls_x509_crt_check_key_usage(cert, s_eeKeyUsage);
+    if (0 != mbedRet)
+    {
+        OIC_LOG(ERROR, TAG, "End entity cert key usage must include digitalSignature & keyAgreement");
+        profileViolations |= CP_INVALID_KEY_USAGE_MISSING;
+    }
+
+    if (0 != (cert->key_usage & s_eeNonKeyUsage))
+    {
+        OIC_LOG(ERROR, TAG, "End entity cert key usage must not include usages other than digitalSignature & keyAgreement");
+        profileViolations |= CP_INVALID_KEY_USAGE_EXTRA;
+    }
+
+    // basicConstraints (OPTIONAL/Critical)
+    // cA = FALSE
+    if (1 == cert->ca_istrue)
+    {
+        OIC_LOG(ERROR, TAG, "End entity cert marked as CA cert");
+        profileViolations |= CP_INVALID_BASIC_CONSTRAINTS_CA;
+    }
+
+    // pathLenConstraint: not present
+    cpResult = CheckPathLen(CERT_EE, cert->max_pathlen);
+    if (CP_STATUS_OK != cpResult)
+    {
+        OIC_LOG(ERROR, TAG, "Invalid End entity max pathlen");
+        profileViolations |= CP_INVALID_BASIC_CONSTRAINTS_PATH_LEN;
+    }
+
+    // extendedKeyUsage (REQUIRED/Non-critical)
+    // Mandatory: serverAuthentication - 1.3.6.1.5.5.7.3.1
+    mbedRet = mbedtls_x509_crt_check_extended_key_usage(cert,
+                                                        MBEDTLS_OID_SERVER_AUTH,
+                                                        MBEDTLS_OID_SIZE(MBEDTLS_OID_SERVER_AUTH));
+    if (0 != mbedRet)
+    {
+        OIC_LOG(ERROR, TAG, "End entity cert extended key usage must include serverAuthentication");
+        profileViolations |= CP_INVALID_EKU_NO_SERVER_AUTH;
+    }
+
+    // extendedKeyUsage (REQUIRED/Non-critical)
+    // Mandatory: clientAuthentication - 1.3.6.1.5.5.7.3.2
+    mbedRet = mbedtls_x509_crt_check_extended_key_usage(cert,
+                                                        MBEDTLS_OID_CLIENT_AUTH,
+                                                        MBEDTLS_OID_SIZE(MBEDTLS_OID_CLIENT_AUTH));
+    if (0 != mbedRet)
+    {
+        OIC_LOG(ERROR, TAG, "End entity cert extended key usage must include clientAuthentication");
+        profileViolations  |= CP_INVALID_EKU_NO_CLIENT_AUTH;
+    }
+
+    // extendedKeyUsage (REQUIRED/Non-critical)
+    // Mandatory: OCF Identity certificate - 1.3.6.1.4.1.44924.1.6
+    mbedRet = mbedtls_x509_crt_check_extended_key_usage(cert, s_ekuIdOid, MBEDTLS_OID_SIZE(s_ekuIdOid));
+    if (0 != mbedRet)
+    {
+        OIC_LOG(ERROR, TAG, "End entity cert extended key usage must include OCF ID OID (1.3.6.1.4.1.44924.1.6");
+        profileViolations |= CP_INVALID_EKU_NO_OCF_ID_OID;
+    }
+
+    // extendedKeyUsage (REQUIRED/Non-critical)
+    // CAs SHALL NOT issue certificates with the anyExtendedKeyUsage OID (2.5.29.37.0)
+    mbedRet = mbedtls_x509_crt_check_extended_key_usage(cert,
+                                                        MBEDTLS_OID_ANY_EXTENDED_KEY_USAGE,
+                                                        MBEDTLS_OID_SIZE(MBEDTLS_OID_ANY_EXTENDED_KEY_USAGE));
+    if (0 == mbedRet)
+    {
+        OIC_LOG(ERROR, TAG, "End entity cert extended key usage must not include anyExtendedKeyUsage");
+        profileViolations |= CP_INVALID_EKU_INCLUDES_ANY;
+    }
+
+    // subjectAlternativeName: No requirements for ID certs
+
+    return profileViolations;
+}
+
+CertProfileViolations ValidateIntermediateCACertProfile(const mbedtls_x509_crt *cert)
+{
+
+    // OCF requirements exist for the following extensions, but w/o mbedTLS support
+    // * cRL Distribution Points
+
+    if (NULL == cert)
+    {
+        return CP_INVALID_CERT_INPUT;
+    }
+
+    int mbedRet = 0;
+    CertProfileResult cpResult = CP_STATUS_OK;
+    CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
+
+    // Check all common entries first
+    profileViolations = ValidateCommonCertProfileEntries(cert);
+
+    // keyUsage: REQUIRED/Critical
+    // keyCertSign (5) & cRLSign (6) bits SHALL be the only bits enabled
+    mbedRet = mbedtls_x509_crt_check_key_usage(cert, s_caKeyUsage);
+    if (0 != mbedRet)
+    {
+        OIC_LOG(ERROR, TAG, "Intermediate CA cert key usage must include keyCertSign & cRLSign");
+        profileViolations |= CP_INVALID_KEY_USAGE_MISSING;
+    }
+
+    if (0 != (cert->key_usage & s_caNonKeyUsage))
+    {
+        OIC_LOG(ERROR, TAG, "Intermediate CA cert key usage must not include usages other than keyCertSign & cRLSign");
+        profileViolations |= CP_INVALID_KEY_USAGE_EXTRA;
+    }
+
+    // basicConstraints REQUIRED/Critical
+    // cA = TRUE
+    if (1 != cert->ca_istrue)
+    {
+        OIC_LOG(ERROR, TAG, "Intermediate CA cert not marked as CA cert");
+        profileViolations |= CP_INVALID_BASIC_CONSTRAINTS_CA;
+    }
+
+    // pathLenConstraint
+    // 0 - can only sign end-entity certs
+    cpResult = CheckPathLen(CERT_CA_INT, cert->max_pathlen);
+    if (CP_STATUS_OK != cpResult)
+    {
+        OIC_LOG(ERROR, TAG, "Invalid Intermediate CA max pathlen");
+        profileViolations |= CP_INVALID_BASIC_CONSTRAINTS_PATH_LEN;
+    }
+    return profileViolations;
+}
+
+CertProfileViolations ValidateRootCACertProfile(const mbedtls_x509_crt *cert)
+{
+    if (NULL == cert)
+    {
+        return CP_INVALID_CERT_INPUT;
+    }
+
+    int mbedRet = 0;
+    CertProfileResult cpResult = CP_STATUS_OK;
+    CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
+
+    // Check all common entries first
+    profileViolations = ValidateCommonCertProfileEntries(cert);
+
+    // Issuer: SHALL match the Subject field
+    // Subject: SHALL match the Issuer field
+    if ( (cert->issuer_raw.len != cert->subject_raw.len) ||
+         (0 != memcmp(cert->issuer_raw.p, cert->subject_raw.p, cert->issuer_raw.len)))
+    {
+        OIC_LOG(ERROR, TAG, "Root CA cert subject must be the same as issuer");
+        profileViolations |= CP_INVALID_ISSUER_SUBJ_MISMATCH;
+
+    }
+
+    // keyUsage: REQUIRED/Critical
+    // keyCertSign (5) & cRLSign (6) bits SHALL be the only bits enabled
+    mbedRet = mbedtls_x509_crt_check_key_usage(cert, s_caKeyUsage);
+    if (0 != mbedRet)
+    {
+        OIC_LOG(ERROR, TAG, "Root CA cert key usage must include keyCertSign & cRLSign");
+        profileViolations |= CP_INVALID_KEY_USAGE_MISSING;
+    }
+    if (0 != (cert->key_usage & s_caNonKeyUsage))
+    {
+        OIC_LOG(ERROR, TAG, "Root CA cert key usage must not include usages other than keyCertSign & cRLSign");
+        profileViolations |= CP_INVALID_KEY_USAGE_EXTRA;
+    }
+
+    // basicConstraints REQUIRED Critical cA = TRUE
+    if (1 != cert->ca_istrue)
+    {
+        OIC_LOG(ERROR, TAG, "Root CA cert not marked as CA cert");
+        profileViolations |= CP_INVALID_BASIC_CONSTRAINTS_CA;
+    }
+
+    // pathLenConstraint = not present (unlimited)
+    cpResult = CheckPathLen(CERT_CA_ROOT, cert->max_pathlen);
+    if (CP_STATUS_OK != cpResult)
+    {
+        OIC_LOG(ERROR, TAG, "Invalid Root CA max pathlen");
+        profileViolations |= CP_INVALID_BASIC_CONSTRAINTS_PATH_LEN;
+    }
+
+    return profileViolations;
+}
+
+int ValidateAuthCertChainProfiles(const mbedtls_x509_crt *certChain)
+{
+    int numViolations = 0;
+    CertProfileResult cpResult = CP_STATUS_OK;
+    CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
+
+    const mbedtls_x509_crt* eeCert = NULL;
+    cpResult = FindEndEntityCert(certChain, &eeCert);
+    if ((CP_STATUS_OK != cpResult) || (NULL == eeCert))
+    {
+        return CP_INVALID_CERT_CHAIN;
+    }
+
+    const mbedtls_x509_crt* curCert = certChain;
+    while (NULL != curCert)
+    {
+        if (curCert == eeCert)
+        {
+            profileViolations = ValidateEndEntityCertProfile(curCert);
+        }
+        else
+        {
+            profileViolations = ValidateIntermediateCACertProfile(curCert);
+        }
+        if (CP_NO_VIOLATIONS != profileViolations)
+        {
+           numViolations++;
+        }
+        curCert = curCert->next;
+    }
+    return numViolations;
+}
+
+int ValidateRootCACertListProfiles(const mbedtls_x509_crt *certList)
+{
+    int numViolations = 0;
+    CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
+
+    const mbedtls_x509_crt* curCert = certList;
+    while (NULL != curCert)
+    {
+        profileViolations = ValidateRootCACertProfile(curCert);
+        if (CP_NO_VIOLATIONS != profileViolations)
+        {
+           numViolations++;
+        }
+        curCert = curCert->next;
+    }
+    return numViolations;
+}
+
+CertProfileViolations ValidateCertTimeWindow(const mbedtls_x509_crt *cert)
+{
+     CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
+
+    if (mbedtls_x509_time_is_future(&cert->valid_from))
+    {
+        OIC_LOG(WARNING, TAG, "Certificate is not yet valid");
+        profileViolations |= CP_NOT_YET_VALID;
+    }
+    if (mbedtls_x509_time_is_past(&cert->valid_to))
+    {
+        OIC_LOG(WARNING, TAG, "Certificate is no longer valid");
+        profileViolations |= CP_EXPIRED;
+    }
+    return profileViolations;
+}
+
+int CheckCertListTimeWindows(const mbedtls_x509_crt *certList)
+{
+    int numInvalid = 0;
+    CertProfileViolations profileViolations = CP_NO_VIOLATIONS;
+
+    const mbedtls_x509_crt* curCert = certList;
+    while (NULL != curCert)
+    {
+        profileViolations = ValidateCertTimeWindow(curCert);
+        if (CP_NO_VIOLATIONS != profileViolations)
+        {
+            numInvalid++;
+        }
+        curCert = curCert->next;
+    }
+    return numInvalid;
+}
+
+#endif // (__WITH_DTLS__) || defined (__WITH_TLS__)
index d8bda02..9a7ed3c 100644 (file)
@@ -53,6 +53,8 @@ catest_env.PrependUnique(CPPPATH=[
     '#/resource/csdk/include',
     '#/resource/csdk/stack/include',
     '#/resource/oc_logger/include',
+    '#/resource/csdk/security/include',
+    '#/resource/c_common/ocrandom/include/experimental',
 ])
 
 catest_env.PrependUnique(LIBS=['coap'])
@@ -110,6 +112,9 @@ if 'IP' in target_transport or 'ALL' in target_transport:
 if catest_env.get('SECURED') == '1' and catest_env.get('WITH_TCP') == True:
     tests_src.append('ssladapter_test.cpp')
 
+if catest_env.get('SECURED') == '1':
+    tests_src.append('cacertprofiletest.cpp')
+
 catests = catest_env.Program('catests', tests_src)
 
 Alias("test", [catests])
diff --git a/resource/csdk/connectivity/test/cacertprofiletest.cpp b/resource/csdk/connectivity/test/cacertprofiletest.cpp
new file mode 100644 (file)
index 0000000..f88dd40
--- /dev/null
@@ -0,0 +1,998 @@
+//******************************************************************
+// Copyright 2018 Cable Television Laboratories, Inc.
+//
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+//
+// Licensed under the Apache License, Version 2.0 s(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.
+///
+//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+// Warning disabled globally but VS2013 ignores the /wd4200 option in C++ files.
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#pragma warning(disable : 4200)
+#endif
+
+#include <gtest/gtest.h>
+
+#include <mbedtls/error.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/x509_crt.h>
+#include <mbedtls/ctr_drbg.h>
+#include <mbedtls/oid.h>
+#include <mbedtls/pem.h>
+
+#include "oic_platform.h"
+#include "oic_malloc.h"
+#include "octypes.h"
+#include "ocrandom.h"
+#include "cacommonutil.h"
+#include "cacertprofile.h"
+#include "occertutility.h"
+
+#define TAG  "CA_CERT_PROFILE_UNIT_TEST"
+
+#define PERSONALIZATION_STRING "IOTIVITY_CERT_TEST"
+
+typedef char ValidityTime[50];
+
+#define ARRAY_LEN(a) (sizeof(a) / sizeof(*a))
+
+// helper functions
+static CertProfileResult GenerateTestCert(CertType certType, CertProfileViolations violations, mbedtls_x509_crt *outCert);
+static CertProfileResult GenerateInternalCert(CertType certType, mbedtls_x509_crt *issuerCert, mbedtls_x509_crt *outCert);
+static void InitTestCert(mbedtls_x509_crt *cert);
+static void FreeTestCert(mbedtls_x509_crt *cert);
+static CertProfileResult SetNotBefore(ValidityTime notBefore, bool invalid);
+static CertProfileResult SetNotAfter(ValidityTime notAfter, bool invalid);
+
+class CACertProfileTests : public testing::Test {
+    protected:
+    virtual void SetUp()
+    {
+    }
+
+    virtual void TearDown()
+    {
+    }
+};
+
+//*****************************************************************************
+// Test Data
+//*****************************************************************************
+
+static const mbedtls_x509_crt_profile s_certProfile = {
+    MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256),        // MD algorithms
+    MBEDTLS_X509_ID_FLAG(MBEDTLS_PK_ECKEY) |        // Allowed key types
+    MBEDTLS_X509_ID_FLAG(MBEDTLS_PK_ECDSA),
+    MBEDTLS_X509_ID_FLAG(MBEDTLS_ECP_DP_SECP256R1), // EC curves
+    0                                               // RSA minimum key length - not used because we only use EC key pairs
+};
+                                      //    total len of eku byte string, will need to be written into final buff
+                                      //    |
+static uint8_t s_ekuHeaderBytes[] = { 0x30, 0x00 };
+
+// serverAuth (1.3.6.1.5.5.7.3.1)             tag   len   1.3.  6.    1.    5.    5.    7.    3.    1
+static uint8_t s_ekuServerAuthOidBytes[]  = { 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01 };
+
+// clientAuth (1.3.6.1.5.5.7.3.2)             tag   len   1.3.  6.    1.    5.    5.    7.    3.    2
+static uint8_t s_ekuClientAuthOidBytes[]  = { 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02 };
+
+
+// OCF ID OID (1.3.6.1.4.1.44924.1.6)         tag   len   1.3.  6.    1.    4.    1.    44924.            1.    6
+static uint8_t s_ekuOcfIdentityOidBytes[] = { 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xDE, 0x7C, 0x01, 0x06 };
+
+// any (2.5.29.37.0)                  tag   len   2.5.  29.   37.   0
+static uint8_t s_ekuAnyOidBytes[] = { 0x06, 0x04, 0x55, 0x1D, 0x25, 0x00 };
+
+static const OCByteString s_ekuHeader = { s_ekuHeaderBytes, sizeof(s_ekuHeaderBytes) };
+static const OCByteString s_ekuServerAuthOid = { s_ekuServerAuthOidBytes, sizeof(s_ekuServerAuthOidBytes) };
+static const OCByteString s_ekuClientAuthOid = { s_ekuClientAuthOidBytes, sizeof(s_ekuClientAuthOidBytes) };
+static const OCByteString s_ekuOcfIdentityOid = { s_ekuOcfIdentityOidBytes, sizeof(s_ekuOcfIdentityOidBytes) };
+static const OCByteString s_ekuAnyOid = { s_ekuAnyOidBytes, sizeof(s_ekuAnyOidBytes) };
+
+
+// The key pairs below are OCF compliant ellliptic curve key pairs
+// (RFC 5480 secp256r1 keys) generated by the IoTivity`certgenerator` utility.
+// They are used as the public and signing keys for certificates
+// that are generated for unit tests in this module.
+
+static const char* s_rootCaPubKey = "-----BEGIN PUBLIC KEY-----\n"
+"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAET0k0tJJu5fnSXa5Wik7VPxpbiWeT\n"
+"rWnvGY8Jv2/xsODb6A7SZPvpu4svwBkO2Wy0gCDwRtwi08ZLemgrtA7w7g==\n"
+"-----END PUBLIC KEY-----\n";
+
+static const char* s_rootCaPrivKey = "-----BEGIN EC PRIVATE KEY-----\n"
+"MHcCAQEEIGrNaXfihmU41VIYHdXACpy6/X9X0MB9g0vtb/ujV403oAoGCCqGSM49\n"
+"AwEHoUQDQgAET0k0tJJu5fnSXa5Wik7VPxpbiWeTrWnvGY8Jv2/xsODb6A7SZPvp\n"
+"u4svwBkO2Wy0gCDwRtwi08ZLemgrtA7w7g==\n"
+"-----END EC PRIVATE KEY-----\n";
+
+static const char* s_intCaPubKey = "-----BEGIN PUBLIC KEY-----\n"
+"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEQQ9DVoBQlnYFjBBvV1sligvG60T\n"
+"vF0O1kXWjUOOFaVHVooHOnxUv1jQFNhH0wERkIYdHGA0DrkbLiWNe0qjDg==\n"
+"-----END PUBLIC KEY-----\n";
+
+static const char* s_intCaPrivKey = "-----BEGIN EC PRIVATE KEY-----\n"
+"MHgCAQEEIQCtAr1LI2tSUGDkaANJOaII9I9RuNl/smpx61ZL1mvm1aAKBggqhkjO\n"
+"PQMBB6FEA0IABBEEPQ1aAUJZ2BYwQb1dbJYoLxutE7xdDtZF1o1DjhWlR1aKBzp8\n"
+"VL9Y0BTYR9MBEZCGHRxgNA65Gy4ljXtKow4=\n"
+"-----END EC PRIVATE KEY-----\n";
+
+static const char* s_eePubKey = "-----BEGIN PUBLIC KEY-----\n"
+"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEK+y7B9e0o7+6SeH2o7YnJKSpY5eO\n"
+"cw29uqb5kTvGj7hOoBK/Ulr492YKlxouF+j1pXUvZYRy2aMoUnm2sGNd4Q==\n"
+"-----END PUBLIC KEY-----\n";
+
+static const char* s_eePrivKey = "-----BEGIN EC PRIVATE KEY-----\n"
+"MHcCAQEEIFiqzuEt5rOCRB4D3fHW+L22vsM7mvnK+Np8vLDl3BwCoAoGCCqGSM49\n"
+"AwEHoUQDQgAEK+y7B9e0o7+6SeH2o7YnJKSpY5eOcw29uqb5kTvGj7hOoBK/Ulr4\n"
+"92YKlxouF+j1pXUvZYRy2aMoUnm2sGNd4Q==\n"
+"-----END EC PRIVATE KEY-----\n";
+
+
+// The key pair below are NON OCF compliant RSA keys generated using
+// openssl.  The pair is used in negative testing to create
+// certificates that violate the OCF crypto type requirements
+
+static const char* s_nonEccPubKey = "-----BEGIN PUBLIC KEY-----\n"
+"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwwInaid7Q3LSM+rvmI0a\n"
+"46/IWxWkSbTP91W2UQ2jt5/iwZQUEaKRxnGUtW+TdigKb1XTEHLoOa2vBnO6OCzW\n"
+"TfeA4aJSCO0cc/oW6inZqnp+3aAMoE34Y1PjOM69enY1WpbQGxoYxXpHjjuQ8M7i\n"
+"xw2xnb03HRgvy/Yg8dJ2+QZTopuxqh4Z9qHBALFZW04iS2e4GPj1NQCoc/MBsX4e\n"
+"WQV1UkeuYhbj1w3KDMDV6hcfNFp/A0XEmPh+ZcXTVRT0EHHY9sLGiVkjYX4b0i1u\n"
+"3lOv2imbIu3mPnhSUaFFBENTL+MQcvnsaHS/WNZL7tlaTWCHStlJnAlKke1rmdBB\n"
+"2wIDAQAB\n"
+"-----END PUBLIC KEY-----\n";
+
+static const char* s_nonEccPrivKey = "-----BEGIN RSA PRIVATE KEY-----\n"
+"MIIEpQIBAAKCAQEAwwInaid7Q3LSM+rvmI0a46/IWxWkSbTP91W2UQ2jt5/iwZQU\n"
+"EaKRxnGUtW+TdigKb1XTEHLoOa2vBnO6OCzWTfeA4aJSCO0cc/oW6inZqnp+3aAM\n"
+"oE34Y1PjOM69enY1WpbQGxoYxXpHjjuQ8M7ixw2xnb03HRgvy/Yg8dJ2+QZTopux\n"
+"qh4Z9qHBALFZW04iS2e4GPj1NQCoc/MBsX4eWQV1UkeuYhbj1w3KDMDV6hcfNFp/\n"
+"A0XEmPh+ZcXTVRT0EHHY9sLGiVkjYX4b0i1u3lOv2imbIu3mPnhSUaFFBENTL+MQ\n"
+"cvnsaHS/WNZL7tlaTWCHStlJnAlKke1rmdBB2wIDAQABAoIBADfxeT8x3mHGIdl1\n"
+"D5EzAcDJ3++JcN0YOwUYmLn98Lav7oxcH7nDapCEawX14VbnO78e3vL8E/TgB7MR\n"
+"Jh4RdtVdKKMSPviClUQMPeClauVGSPjpflXKqHROT4QvgxoLqVi4pTDxNqQIZVi/\n"
+"a+A5acNzAT5gVtxI4QVhMahOGYVsszaTrCf/9/rKleqKOnFyNq4QNtPtdUv+RT9B\n"
+"Sk5XruOJ3hR6Piivw5uLBYahiFbafXieQv4WAgTwZY0Hx1X5nhW3xbA2LXX4PuPv\n"
+"+DPBPtS2qGVxEc487UhhMbqBGygvOPycIYyXXUPdlN8hQkvA/vk9bEDg6Koro/MQ\n"
+"GbflZxkCgYEA+fLnwwyo6Yy4t5Gss8me0F5SA3praoGUfuN2xpzYy99puLlXazSi\n"
+"FZ2PrbiufeUJ1Fuf2AVu8/SdKKuwoinYT5MMUhbCm+ywMlGhf5aACV3XQxATU+si\n"
+"/YF8KDxRV6amBxqhmZahadKWmpV/nO7Xs1E/sIUuIIRWYa+qcnG/vJUCgYEAx7q/\n"
+"Sq3nkPhbNqbUUp9/FuHP2vz241Y5j5kHbCwiUQ5Qs9ZibShWbHEXz3uV2bWvUmb0\n"
+"eO+/Ch38SVSKpClwrubX2ABJZjxXnQiGRHdZ+Z7bepTTzNEdonlVL68D5f7Mix16\n"
+"db96+ZM6cD36k8Duiekuv4LrVyDRUDHZIwNW+K8CgYEA7nQxbSCXyPAqjV6Zz4/L\n"
+"Rmilf5H8BkkHHSLufr7GCYHjv4p6ftlgxJRjrU1iktK+VOzwistsNaspf9KEGcEo\n"
+"D4pvF6WFFfJxih/cnfc7wTPYmK8ucUIDSKyOgWhCBggDnwVmJDvTVDzG10VG0LX/\n"
+"7J/XzhTQk88NBfbr+nDQhh0CgYEAmfUz+4buF2u6M/iJdl8IAkVyieWxEjCnEi/t\n"
+"7s5ebYjRYK8DFA2iANl0dzVWn4lTaft+u7Ji2pOeo5ANhtmCyO+cth4SdkJRbvTh\n"
+"Oumda7Ouj2Elmp4fb9yDc8w+/w1X8mx4t0cwri11QIwmGbsApUIgDPQIIRNlBaJH\n"
+"NJwgcHUCgYEAzjkPOMoS4idZk7e1f/ymprGT6nsbwo2yyD18yYzaNHpSnIkZpJm0\n"
+"LXOoB+APM8Y9Hl3xHUtWV8WN1CmuWFyK7SvCPeqf6xm+fAFyRvFhX7Qm9ZMJLCif\n"
+"KCYeMZY1ax+OqnwZ79+8UB2gbjQ5efbdb8jVrZDEABCRp3Ko+/tGVYg=\n"
+"-----END RSA PRIVATE KEY-----\n";
+
+static const char* s_rootCaSerial = "244240106504148688103282289603386694025638582413";
+static const char* s_intCaSerial = "398222190774066044346258430305847925228418308044";
+static const char* s_eeSerial = "611138144226495869526348350957100882548422091813";
+
+static const char* s_rootCaIssuer = "O=OCF, OU=P256 Test Infrastructure, CN=Root Test CA";
+static const char* s_rootCaSubject = "O=OCF, OU=P256 Test Infrastructure, CN=Root Test CA";
+
+static const char* s_intCaIssuer = "O=OCF, OU=P256 Test Infrastructure, CN=Root Test CA";
+static const char* s_intCaSubject = "O=OCF, OU=P256 Test Infrastructure, CN=Intermediate Test CA";
+
+static const char* s_eeIssuer = "O=OCF, OU=P256 Test Infrastructure, CN=Intermediate Test CA";
+static const char* s_eeSubject = "O=OCF, OU=P256 Test Infrastructure, CN=11111111-1111-1111-1111-111111111111";
+static const char* s_eeUuid = "11111111-1111-1111-1111-111111111111";
+
+//*****************************************************************************
+// Tests
+//*****************************************************************************
+
+
+TEST_F(CACertProfileTests, RootCAProfiles)
+{
+    CertProfileResult cpResult = CP_STATUS_OK;
+    CertProfileViolations violations = CP_NO_VIOLATIONS;
+    CertProfileViolations expectedViolations = CP_NO_VIOLATIONS;
+    mbedtls_x509_crt cert;
+
+    CertProfileViolations violationsToTest[] =
+    {
+        CP_NO_VIOLATIONS,
+        CP_NOT_YET_VALID,
+        CP_EXPIRED,
+        CP_INVALID_SIG_ALGORITHM,
+        CP_INVALID_VERSION,
+        CP_INVALID_PUB_KEY_ALGORITHM,
+        CP_INVALID_ISSUER_SUBJ_MISMATCH,
+        CP_INVALID_KEY_USAGE_MISSING,
+        CP_INVALID_KEY_USAGE_EXTRA,
+        CP_INVALID_BASIC_CONSTRAINTS_CA,
+        CP_INVALID_BASIC_CONSTRAINTS_PATH_LEN
+    };
+
+    for (unsigned int i = 0; i < ARRAY_LEN(violationsToTest); i++)
+    {
+        InitTestCert(&cert);
+
+        cpResult = GenerateTestCert(CERT_CA_ROOT, violationsToTest[i], &cert);
+        EXPECT_TRUE(CP_STATUS_OK == cpResult);
+        if (CP_STATUS_OK == cpResult)
+        {
+            expectedViolations = violationsToTest[i];
+            violations = ValidateRootCACertProfile(&cert);
+            EXPECT_TRUE(violations == expectedViolations);
+        }
+        else
+        {
+            OIC_LOG(ERROR, TAG, "Could not generate root CA test cert");
+        }
+
+        FreeTestCert(&cert);
+    }
+}
+
+TEST_F(CACertProfileTests, IntermediateCAProfiles)
+{
+    CertProfileResult cpResult = CP_STATUS_OK;
+    CertProfileViolations violations = CP_NO_VIOLATIONS;
+    CertProfileViolations expectedViolations = CP_NO_VIOLATIONS;
+    mbedtls_x509_crt cert;
+
+    CertProfileViolations violationsToTest[] =
+    {
+        CP_NO_VIOLATIONS,
+        CP_NOT_YET_VALID,
+        CP_EXPIRED,
+        CP_INVALID_SIG_ALGORITHM,
+        CP_INVALID_VERSION,
+        CP_INVALID_PUB_KEY_ALGORITHM,
+        CP_INVALID_KEY_USAGE_MISSING,
+        CP_INVALID_KEY_USAGE_EXTRA,
+        CP_INVALID_BASIC_CONSTRAINTS_CA,
+        CP_INVALID_BASIC_CONSTRAINTS_PATH_LEN
+    };
+
+    for (unsigned int i = 0; i < ARRAY_LEN(violationsToTest); i++)
+    {
+        InitTestCert(&cert);
+
+        cpResult = GenerateTestCert(CERT_CA_INT, violationsToTest[i], &cert);
+        EXPECT_TRUE(CP_STATUS_OK == cpResult);
+        if (CP_STATUS_OK == cpResult)
+        {
+            expectedViolations = violationsToTest[i];
+            if (CP_INVALID_BASIC_CONSTRAINTS_CA == violationsToTest[i])
+            {
+                // Because we are faking non-CA cert, mbed will not encode the max_pathlen
+                // and we will get a pathlen violation as well
+                expectedViolations |= CP_INVALID_BASIC_CONSTRAINTS_PATH_LEN;
+            }
+            violations = ValidateIntermediateCACertProfile(&cert);
+            EXPECT_TRUE(violations == expectedViolations);
+        }
+        else
+        {
+            OIC_LOG(ERROR, TAG, "Could not generate root CA test cert");
+        }
+
+        FreeTestCert(&cert);
+    }
+}
+
+TEST_F(CACertProfileTests, EndEntityProfiles)
+{
+    CertProfileResult cpResult = CP_STATUS_OK;
+    CertProfileViolations violations = CP_NO_VIOLATIONS;
+    CertProfileViolations expectedViolations = CP_NO_VIOLATIONS;
+    mbedtls_x509_crt cert;
+
+    CertProfileViolations violationsToTest[] =
+    {
+        CP_NO_VIOLATIONS,
+        CP_NOT_YET_VALID,
+        CP_EXPIRED,
+        CP_INVALID_SIG_ALGORITHM,
+        CP_INVALID_VERSION,
+        CP_INVALID_PUB_KEY_ALGORITHM,
+        CP_INVALID_KEY_USAGE_MISSING,
+        CP_INVALID_KEY_USAGE_EXTRA,
+        CP_INVALID_BASIC_CONSTRAINTS_CA,
+        CP_INVALID_BASIC_CONSTRAINTS_PATH_LEN,
+        CP_INVALID_EKU_NO_SERVER_AUTH,
+        CP_INVALID_EKU_NO_CLIENT_AUTH,
+        CP_INVALID_EKU_NO_OCF_ID_OID,
+        CP_INVALID_EKU_INCLUDES_ANY
+    };
+
+    for (unsigned int i = 0; i < ARRAY_LEN(violationsToTest); i++)
+    {
+        InitTestCert(&cert);
+
+        cpResult = GenerateTestCert(CERT_EE, violationsToTest[i], &cert);
+        EXPECT_TRUE(CP_STATUS_OK == cpResult);
+        if (CP_STATUS_OK == cpResult)
+        {
+            expectedViolations = violationsToTest[i];
+            if (CP_INVALID_BASIC_CONSTRAINTS_PATH_LEN == violationsToTest[i])
+            {
+                // Because mbed does not support max_pathlen for EE certs,
+                // it is returned as zero, no matter what was specified at encoding,
+                // so this test will errantly pass
+                expectedViolations = CP_NO_VIOLATIONS;
+            }
+            violations = ValidateEndEntityCertProfile(&cert);
+            EXPECT_TRUE(violations == expectedViolations);
+        }
+        else
+        {
+            OIC_LOG(ERROR, TAG, "Could not generate End Entity test cert");
+        }
+
+        FreeTestCert(&cert);
+    }
+}
+
+TEST_F(CACertProfileTests, InternalRootCACert)
+{
+    CertProfileResult cpResult = CP_STATUS_OK;
+    CertProfileViolations violations = CP_NO_VIOLATIONS;
+
+    mbedtls_x509_crt rootCert;
+
+    InitTestCert(&rootCert);
+
+    cpResult = GenerateInternalCert(CERT_CA_ROOT, NULL, &rootCert);
+    EXPECT_TRUE(CP_STATUS_OK == cpResult);
+    if (CP_STATUS_OK == cpResult)
+    {
+        violations = ValidateRootCACertProfile(&rootCert);
+        EXPECT_TRUE(violations == CP_NO_VIOLATIONS);
+    }
+    else
+    {
+        OIC_LOG(ERROR, TAG, "Could not generate internal CA test cert");
+    }
+
+    FreeTestCert(&rootCert);
+}
+
+TEST_F(CACertProfileTests, InternalIntermediateCert)
+{
+    CertProfileResult cpResult = CP_STATUS_OK;
+    CertProfileViolations violations = CP_NO_VIOLATIONS;
+
+    mbedtls_x509_crt rootCert;
+    mbedtls_x509_crt intCert;
+
+    InitTestCert(&rootCert);
+    InitTestCert(&intCert);
+
+    cpResult = GenerateInternalCert(CERT_CA_ROOT, NULL, &rootCert);
+    EXPECT_TRUE(CP_STATUS_OK == cpResult);
+    VERIFY_SUCCESS_OR_RETURN(TAG, (CP_STATUS_OK == cpResult), "Could not generate issuing Root CA cert", ERROR);
+    violations = ValidateRootCACertProfile(&rootCert);
+    EXPECT_TRUE(violations == CP_NO_VIOLATIONS);
+    VERIFY_SUCCESS_OR_RETURN(TAG, (violations == CP_NO_VIOLATIONS), "Invalid Internal Root CA cert generated ", ERROR);
+
+    cpResult = GenerateInternalCert(CERT_CA_INT , &rootCert, &intCert);
+    EXPECT_TRUE(CP_STATUS_OK == cpResult);
+    if (CP_STATUS_OK == cpResult)
+    {
+        violations = ValidateIntermediateCACertProfile(&intCert);
+        EXPECT_TRUE(violations == CP_NO_VIOLATIONS);
+    }
+    else
+    {
+        OIC_LOG(ERROR, TAG, "Could not generate internal CA test cert");
+    }
+
+    FreeTestCert(&rootCert);
+    FreeTestCert(&intCert);
+}
+
+TEST_F(CACertProfileTests, InternalEndEntityCert)
+{
+    CertProfileResult cpResult = CP_STATUS_OK;
+    CertProfileViolations violations = CP_NO_VIOLATIONS;
+
+    mbedtls_x509_crt rootCert;
+    mbedtls_x509_crt intCert;
+    mbedtls_x509_crt eeCert;
+
+    InitTestCert(&rootCert);
+    InitTestCert(&intCert);
+    InitTestCert(&eeCert);
+
+    cpResult = GenerateInternalCert(CERT_CA_ROOT, NULL, &rootCert);
+    EXPECT_TRUE(CP_STATUS_OK == cpResult);
+    VERIFY_SUCCESS_OR_RETURN(TAG, (CP_STATUS_OK == cpResult), "Could not generate issuing Root CA cert", ERROR);
+    violations = ValidateRootCACertProfile(&rootCert);
+    EXPECT_TRUE(violations == CP_NO_VIOLATIONS);
+    VERIFY_SUCCESS_OR_RETURN(TAG, (violations == CP_NO_VIOLATIONS), "Invalid Internal Root CA cert generated ", ERROR);
+
+    cpResult = GenerateInternalCert(CERT_CA_INT, &rootCert, &intCert);
+    EXPECT_TRUE(CP_STATUS_OK == cpResult);
+    VERIFY_SUCCESS_OR_RETURN(TAG, (CP_STATUS_OK == cpResult), "Could not generate issuing Intermediate CA cert", ERROR);
+    violations = ValidateIntermediateCACertProfile(&intCert);
+    EXPECT_TRUE(violations == CP_NO_VIOLATIONS);
+    VERIFY_SUCCESS_OR_RETURN(TAG, (violations == CP_NO_VIOLATIONS), "Invalid Internal Intermediate CA cert generated ", ERROR);
+
+    cpResult = GenerateInternalCert(CERT_EE, &intCert, &eeCert);
+    EXPECT_TRUE(CP_STATUS_OK == cpResult);
+    if (CP_STATUS_OK == cpResult)
+    {
+        violations = ValidateEndEntityCertProfile(&eeCert);
+        EXPECT_TRUE(violations == CP_NO_VIOLATIONS);
+    }
+    else
+    {
+        OIC_LOG(ERROR, TAG, "Could not generate internal CA test cert");
+    }
+
+    FreeTestCert(&rootCert);
+    FreeTestCert(&intCert);
+    InitTestCert(&eeCert);
+}
+
+TEST_F(CACertProfileTests, VerifyInternalCertChain)
+{
+    CertProfileResult cpResult = CP_STATUS_OK;
+    CertProfileViolations violations = CP_NO_VIOLATIONS;
+    int mbedRet = 0;
+
+    mbedtls_x509_crt rootCert;
+    mbedtls_x509_crt intCert;
+    mbedtls_x509_crt eeCert;
+    mbedtls_x509_crt *certChain;
+
+    InitTestCert(&rootCert);
+    InitTestCert(&intCert);
+    InitTestCert(&eeCert);
+
+    cpResult = GenerateInternalCert(CERT_CA_ROOT, NULL, &rootCert);
+    EXPECT_TRUE(CP_STATUS_OK == cpResult);
+    VERIFY_SUCCESS_OR_RETURN(TAG, (CP_STATUS_OK == cpResult), "Could not generate issuing Root CA cert", ERROR);
+    violations = ValidateRootCACertProfile(&rootCert);
+    EXPECT_TRUE(violations == CP_NO_VIOLATIONS);
+    VERIFY_SUCCESS_OR_RETURN(TAG, (violations == CP_NO_VIOLATIONS), "Invalid Internal Root CA cert generated ", ERROR);
+
+    cpResult = GenerateInternalCert(CERT_CA_INT, &rootCert, &intCert);
+    EXPECT_TRUE(CP_STATUS_OK == cpResult);
+    VERIFY_SUCCESS_OR_RETURN(TAG, (CP_STATUS_OK == cpResult), "Could not generate issuing Intermediate CA cert", ERROR);
+    violations = ValidateIntermediateCACertProfile(&intCert);
+    EXPECT_TRUE(violations == CP_NO_VIOLATIONS);
+    VERIFY_SUCCESS_OR_RETURN(TAG, (violations == CP_NO_VIOLATIONS), "Invalid Internal Intermediate CA cert generated ", ERROR);
+
+    cpResult = GenerateInternalCert(CERT_EE, &intCert, &eeCert);
+    EXPECT_TRUE(CP_STATUS_OK == cpResult);
+    VERIFY_SUCCESS_OR_RETURN(TAG, (CP_STATUS_OK == cpResult), "Could not generate End Entity CA cert", ERROR);
+    EXPECT_TRUE(violations == CP_NO_VIOLATIONS);
+    VERIFY_SUCCESS_OR_RETURN(TAG, (violations == CP_NO_VIOLATIONS), "Invalid Internal End Entity cert generated ", ERROR);
+
+    uint32_t flags;
+    certChain = &eeCert;
+    certChain->next = &intCert;
+    mbedRet = mbedtls_x509_crt_verify_with_profile(
+        certChain, &rootCert, NULL,
+        &s_certProfile, NULL,
+        &flags, NULL, NULL);
+    EXPECT_TRUE(0 == mbedRet);
+
+    FreeTestCert(&rootCert);
+    FreeTestCert(&intCert);
+    InitTestCert(&eeCert);
+}
+
+
+//*****************************************************************************
+// Helper fxns
+//*****************************************************************************
+
+static CertProfileResult SetNotBefore(ValidityTime notBefore, bool invalid)
+{
+    time_t nowTimeT = time(NULL);
+    struct tm *now = gmtime(&nowTimeT);
+
+    if (invalid)
+    {
+        now->tm_year += 10;
+    }
+
+    if (0 == strftime(notBefore, sizeof(ValidityTime), "%Y%m%d%H%M%S", now))
+    {
+        return CP_DATE_ERROR;
+    }
+    return CP_STATUS_OK;
+}
+
+static CertProfileResult SetNotAfter(ValidityTime notAfter, bool invalid)
+{
+    time_t nowTimeT = time(NULL);
+    struct tm *now = gmtime(&nowTimeT);
+
+    if (invalid)
+    {
+        now->tm_year -= 10;
+    }
+    else
+    {
+        now->tm_year += 10;
+    }
+
+    if (0 == strftime(notAfter, sizeof(ValidityTime), "%Y%m%d%H%M%S", now))
+    {
+        return CP_DATE_ERROR;
+    }
+    return CP_STATUS_OK;
+}
+
+// returns length of constructed byte stream, or -1 on error
+static int constructEku( unsigned int numOids, const OCByteString **oidList, unsigned char *buf, unsigned int buflen)
+{
+    size_t encodingLen = 0;
+    unsigned char *curPtr = buf;
+    unsigned char *bufEnd = buf+buflen;
+
+    // add header
+    if ( (curPtr + s_ekuHeader.len > bufEnd) ||
+         (encodingLen + s_ekuHeader.len) > 127)
+    {
+        return -1;
+    }
+    memcpy(curPtr, s_ekuHeader.bytes, s_ekuHeader.len);
+    encodingLen += s_ekuHeader.len;
+    curPtr += s_ekuHeader.len;
+
+     // add supplied OIDs
+    for ( unsigned int i = 0; i < numOids; i++ )
+    {
+        if ( (curPtr + oidList[i]->len > bufEnd) ||
+             (encodingLen + s_ekuHeader.len) > 127)
+        {
+            return -1;
+        }
+        memcpy(curPtr, oidList[i]->bytes, oidList[i]->len);
+        encodingLen += oidList[i]->len;
+        curPtr += oidList[i]->len;
+    }
+
+    // write the length into the encoding (2nd byte), only supporting short form
+    if ( encodingLen > 127 )
+    {
+        return -1;
+    }
+    *(buf+1) = (unsigned char)(encodingLen - s_ekuHeader.len);
+
+    return (int)encodingLen;
+}
+
+// must call InitTestCert before calling, and FreeTestCert when done
+static CertProfileResult GenerateTestCert(CertType certType, CertProfileViolations violations, mbedtls_x509_crt *outCert)
+{
+    OC_UNUSED(s_eePrivKey);
+
+    int mbedRet = 0;
+    CertProfileResult cpResult = CP_STATUS_OK;
+
+    char buf[2048];
+    char mbedErrBuf[256];
+
+    int isCa = 0;
+    int maxPathLen = 0;
+    unsigned int usage = 0;
+    bool makeDateInvalid = false;
+
+    mbedtls_x509write_cert outCertCtx;
+    mbedtls_pk_context subjectPubKeyCtx;
+    mbedtls_pk_context issuerPrivKeyCtx;
+    mbedtls_mpi serialMpi;
+    mbedtls_entropy_context entropy;
+    mbedtls_ctr_drbg_context ctr_drbg;
+
+    mbedtls_x509write_crt_init(&outCertCtx);
+    mbedtls_pk_init(&subjectPubKeyCtx);
+    mbedtls_pk_init(&issuerPrivKeyCtx);
+    mbedtls_mpi_init(&serialMpi);
+    mbedtls_entropy_init(&entropy);
+    mbedtls_ctr_drbg_init(&ctr_drbg);
+
+    const char *subjectPubKey = NULL;
+    const char *issuerPrivKey = NULL;
+    const char *serial = NULL;
+    const char *subject = NULL;
+    const char *issuer = NULL;
+
+    switch ( certType )
+    {
+    case CERT_CA_ROOT:
+        subjectPubKey = (violations & CP_INVALID_PUB_KEY_ALGORITHM) ? s_nonEccPubKey : s_rootCaPubKey;
+        issuerPrivKey = (violations & CP_INVALID_PUB_KEY_ALGORITHM) ? s_nonEccPrivKey : s_rootCaPrivKey;
+        serial = s_rootCaSerial;
+        subject = s_rootCaSubject;
+        issuer = (violations & CP_INVALID_ISSUER_SUBJ_MISMATCH) ? s_eeIssuer : s_rootCaIssuer;
+        break;
+    case CERT_CA_INT:
+        subjectPubKey = (violations & CP_INVALID_PUB_KEY_ALGORITHM) ? s_nonEccPubKey : s_intCaPubKey;
+        issuerPrivKey = (violations & CP_INVALID_PUB_KEY_ALGORITHM) ? s_nonEccPrivKey : s_rootCaPrivKey;
+        serial = s_intCaSerial;
+        subject = s_intCaSubject;
+        issuer = s_intCaIssuer;
+        break;
+    case CERT_EE:
+        subjectPubKey =  (violations & CP_INVALID_PUB_KEY_ALGORITHM) ? s_nonEccPubKey : s_eePubKey;
+        issuerPrivKey = (violations & CP_INVALID_PUB_KEY_ALGORITHM) ? s_nonEccPrivKey : s_intCaPrivKey;
+        serial = s_eeSerial;
+        subject = s_eeSubject;
+        issuer = s_eeIssuer;
+        break;
+    default:
+        OIC_LOG(ERROR, TAG, "Unknown cert type for internal cert generation");
+        return CP_STATUS_FAILED;
+    };
+
+    // Set up
+
+    mbedRet = mbedtls_mpi_read_string(&serialMpi, 10, serial);
+    CP_LOG_MBED_ERROR(TAG, mbedRet, mbedErrBuf, sizeof(mbedErrBuf), ERROR);
+    cpResult = (0 == mbedRet) ? CP_STATUS_OK : CP_STATUS_FAILED;
+    VERIFY_SUCCESS_OR_EXIT(TAG, (0 == mbedRet), "Problem reading serial number", ERROR);
+
+    mbedRet = mbedtls_pk_parse_public_key(&subjectPubKeyCtx, (const uint8_t *)subjectPubKey, strlen(subjectPubKey) + 1);
+    CP_LOG_MBED_ERROR(TAG, mbedRet, mbedErrBuf, sizeof(mbedErrBuf), ERROR);
+    cpResult = (0 == mbedRet) ? CP_STATUS_OK : CP_STATUS_FAILED;
+    VERIFY_SUCCESS_OR_EXIT(TAG, (0 == mbedRet), "Problem parsing public key", ERROR);
+
+    mbedRet = mbedtls_pk_parse_key(&issuerPrivKeyCtx, (const uint8_t *)issuerPrivKey, strlen(issuerPrivKey) + 1, NULL, 0);
+    CP_LOG_MBED_ERROR(TAG, mbedRet, mbedErrBuf, sizeof(mbedErrBuf), ERROR);
+    cpResult = (0 == mbedRet) ? CP_STATUS_OK : CP_STATUS_FAILED;
+    VERIFY_SUCCESS_OR_EXIT(TAG, (0 == mbedRet), "Problem parsing private key", ERROR);
+
+    mbedRet = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
+                                (const uint8_t *)PERSONALIZATION_STRING,
+                                sizeof(PERSONALIZATION_STRING));
+    CP_LOG_MBED_ERROR(TAG, mbedRet, mbedErrBuf, sizeof(mbedErrBuf), ERROR);
+    cpResult = (0 == mbedRet) ? CP_STATUS_OK : CP_STATUS_FAILED;
+    VERIFY_SUCCESS_OR_EXIT(TAG, (0 == mbedRet), "Problem setting  seed", ERROR);
+
+    mbedtls_ctr_drbg_set_prediction_resistance(&ctr_drbg, MBEDTLS_CTR_DRBG_PR_ON);
+
+    // Validity window
+
+    ValidityTime notValidBefore;
+    makeDateInvalid = (violations & CP_NOT_YET_VALID) ? true : false;
+    cpResult = SetNotBefore(notValidBefore, makeDateInvalid);
+    VERIFY_SUCCESS_OR_EXIT(TAG, (CP_STATUS_OK == cpResult), "Problem setting not before date", ERROR);
+
+    ValidityTime notValidAfter;
+    makeDateInvalid = (violations & CP_EXPIRED) ? true : false;
+    cpResult = SetNotAfter(notValidAfter, makeDateInvalid);
+    VERIFY_SUCCESS_OR_EXIT(TAG, (CP_STATUS_OK == cpResult), "Problem setting not after date", ERROR);
+
+    mbedRet = mbedtls_x509write_crt_set_validity(&outCertCtx, notValidBefore, notValidAfter);
+    CP_LOG_MBED_ERROR(TAG, mbedRet, mbedErrBuf, sizeof(mbedErrBuf), ERROR);
+    cpResult = (0 == mbedRet) ? CP_STATUS_OK : CP_STATUS_FAILED;
+    VERIFY_SUCCESS_OR_EXIT(TAG, (0 == mbedRet), "Problem setting certificate time window", ERROR);
+
+    // Version
+
+    // mbedtls only encodes V3, will fake violations below
+    mbedtls_x509write_crt_set_version(&outCertCtx, MBEDTLS_X509_CRT_VERSION_3);
+
+    // Sig Type
+
+    if (violations & CP_INVALID_SIG_ALGORITHM)
+    {
+        mbedtls_x509write_crt_set_md_alg(&outCertCtx, MBEDTLS_MD_SHA224);
+    }
+    else
+    {
+        mbedtls_x509write_crt_set_md_alg(&outCertCtx, MBEDTLS_MD_SHA256);
+    }
+
+    // Serial Number
+
+    mbedRet = mbedtls_x509write_crt_set_serial(&outCertCtx, &serialMpi);
+    CP_LOG_MBED_ERROR(TAG, mbedRet, mbedErrBuf, sizeof(mbedErrBuf), ERROR);
+    cpResult = (0 == mbedRet) ? CP_STATUS_OK : CP_STATUS_FAILED;
+    VERIFY_SUCCESS_OR_EXIT(TAG, 0 == mbedRet, "Problem writing certificate serial number", ERROR);
+
+    // Subject
+
+    mbedRet = mbedtls_x509write_crt_set_subject_name(&outCertCtx, subject);
+    CP_LOG_MBED_ERROR(TAG, mbedRet, mbedErrBuf, sizeof(mbedErrBuf), ERROR);
+    cpResult = (0 == mbedRet) ? CP_STATUS_OK : CP_STATUS_FAILED;
+    VERIFY_SUCCESS_OR_EXIT(TAG, 0 == mbedRet, "Problem writing certificate subject", ERROR);
+
+    // Issuer
+
+    mbedRet = mbedtls_x509write_crt_set_issuer_name(&outCertCtx, issuer);
+    CP_LOG_MBED_ERROR(TAG, mbedRet, mbedErrBuf, sizeof(mbedErrBuf), ERROR);
+    cpResult = (0 == mbedRet) ? CP_STATUS_OK : CP_STATUS_FAILED;
+    VERIFY_SUCCESS_OR_EXIT(TAG, 0 == mbedRet, "Problem writing certificate issuer", ERROR);
+
+    // Keys
+
+    mbedtls_x509write_crt_set_subject_key(&outCertCtx, &subjectPubKeyCtx);
+    mbedtls_x509write_crt_set_issuer_key(&outCertCtx, &issuerPrivKeyCtx);
+
+    // Basic Constraints
+
+    // mbedtls max_pathlen behaviour:
+    // CA Cert: Expects RFC5280_val as encoding input
+    //          Provides RFC5280_val+1 as decoding output, where 0 = not present
+    // EE Cert: Does not encode
+    //          Provides 0 as decoding output
+
+    switch (certType)
+    {
+    case CERT_CA_ROOT:
+        isCa = (violations & CP_INVALID_BASIC_CONSTRAINTS_CA) ? 0 : 1;
+        maxPathLen = (violations & CP_INVALID_BASIC_CONSTRAINTS_PATH_LEN) ? 2 :-1;
+        break;
+    case CERT_CA_INT:
+        isCa = (violations & CP_INVALID_BASIC_CONSTRAINTS_CA) ? 0 : 1;
+        maxPathLen = (violations & CP_INVALID_BASIC_CONSTRAINTS_PATH_LEN) ? -1 : 0;
+        break;
+    case CERT_EE:
+        isCa = (violations & CP_INVALID_BASIC_CONSTRAINTS_CA) ? 1 : 0;
+        maxPathLen = (violations & CP_INVALID_BASIC_CONSTRAINTS_PATH_LEN) ? 10 : -1;
+        break;
+    };
+
+    mbedRet = mbedtls_x509write_crt_set_basic_constraints(&outCertCtx, isCa, maxPathLen);
+    CP_LOG_MBED_ERROR(TAG, mbedRet, mbedErrBuf, sizeof(mbedErrBuf), ERROR);
+    cpResult = (0 == mbedRet) ? CP_STATUS_OK : CP_STATUS_FAILED;
+    VERIFY_SUCCESS_OR_EXIT(TAG, 0 == mbedRet, "Problem writing basic constraints", ERROR);
+
+    // Key Usage
+
+    switch (certType)
+    {
+    case CERT_CA_ROOT:
+        usage = MBEDTLS_X509_KU_KEY_CERT_SIGN | MBEDTLS_X509_KU_CRL_SIGN;
+        if (violations & CP_INVALID_KEY_USAGE_MISSING) {
+            usage &= ~MBEDTLS_X509_KU_CRL_SIGN;
+        }
+        if (violations & CP_INVALID_KEY_USAGE_EXTRA) {
+            usage |= MBEDTLS_X509_KU_DIGITAL_SIGNATURE;
+        }
+        break;
+    case CERT_CA_INT:
+        usage = MBEDTLS_X509_KU_KEY_CERT_SIGN | MBEDTLS_X509_KU_CRL_SIGN;
+        if (violations & CP_INVALID_KEY_USAGE_MISSING) {
+            usage &= ~MBEDTLS_X509_KU_KEY_CERT_SIGN;
+        }
+        if (violations & CP_INVALID_KEY_USAGE_EXTRA) {
+            usage |= MBEDTLS_X509_KU_KEY_ENCIPHERMENT;
+        }
+        break;
+    case CERT_EE:
+        usage = MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_AGREEMENT;
+        if ( violations & CP_INVALID_KEY_USAGE_MISSING ) {
+            usage &= ~MBEDTLS_X509_KU_KEY_AGREEMENT;
+        }
+        if (violations & CP_INVALID_KEY_USAGE_EXTRA) {
+            usage |= MBEDTLS_X509_KU_NON_REPUDIATION;
+        }
+        break;
+    };
+
+    mbedRet = mbedtls_x509write_crt_set_key_usage(&outCertCtx, usage);
+    cpResult = (0 == mbedRet) ? CP_STATUS_OK : CP_STATUS_FAILED;
+    CP_LOG_MBED_ERROR(TAG, mbedRet, mbedErrBuf, sizeof(mbedErrBuf), ERROR);
+    VERIFY_SUCCESS_OR_EXIT(TAG, (0 == mbedRet), "Problem writing key usages", ERROR);
+
+    // Extended Key Usage
+
+    if (CERT_EE == certType)
+    {
+        const OCByteString *oidList[4];
+
+        unsigned int numOids = 0;
+        if (!(violations & CP_INVALID_EKU_NO_SERVER_AUTH))
+        {
+            oidList[numOids] = &s_ekuServerAuthOid;
+            numOids++;
+        }
+
+        if (!(violations & CP_INVALID_EKU_NO_CLIENT_AUTH))
+        {
+            oidList[numOids] = &s_ekuClientAuthOid;
+            numOids++;
+        }
+
+        if (!(violations & CP_INVALID_EKU_NO_OCF_ID_OID))
+        {
+            oidList[numOids] = &s_ekuOcfIdentityOid;
+            numOids++;
+        }
+
+        if (violations & CP_INVALID_EKU_INCLUDES_ANY)
+        {
+            oidList[numOids] = &s_ekuAnyOid;
+            numOids++;
+        }
+
+        int encodingLen = constructEku(numOids, oidList, (unsigned char*)buf, sizeof(buf));
+        cpResult = (0 < encodingLen) ? CP_STATUS_OK : CP_STATUS_FAILED;
+        VERIFY_SUCCESS_OR_EXIT(TAG, (0 < encodingLen), "Insufficient buffer size for EKU encoding", ERROR);
+
+
+        mbedRet = mbedtls_x509write_crt_set_extension(
+                    &outCertCtx,
+                    MBEDTLS_OID_EXTENDED_KEY_USAGE,
+                    MBEDTLS_OID_SIZE(MBEDTLS_OID_EXTENDED_KEY_USAGE), 0,
+                    (const unsigned char*)buf, encodingLen);
+        CP_LOG_MBED_ERROR(TAG, mbedRet, mbedErrBuf, sizeof(mbedErrBuf), ERROR);
+        cpResult = (0 == mbedRet) ? CP_STATUS_OK : CP_STATUS_FAILED;
+        VERIFY_SUCCESS_OR_EXIT(TAG, (0 == mbedRet), "Problem writing eku OCF OID", ERROR);
+    }
+
+    // Create the cert
+
+    mbedRet = mbedtls_x509write_crt_pem(&outCertCtx, (uint8_t *)buf, sizeof(buf), mbedtls_ctr_drbg_random, &ctr_drbg);
+    CP_LOG_MBED_ERROR(TAG, mbedRet, mbedErrBuf, sizeof(mbedErrBuf), ERROR);
+    cpResult = (0 == mbedRet) ? CP_STATUS_OK : CP_STATUS_FAILED;
+    VERIFY_SUCCESS_OR_EXIT(TAG, (0 == mbedRet), "Problem writing certificate", ERROR);
+
+    mbedRet = mbedtls_x509_crt_parse(outCert, (const unsigned char*)buf, sizeof(buf));
+    CP_LOG_MBED_ERROR(TAG, mbedRet, mbedErrBuf, sizeof(mbedErrBuf), ERROR);
+    cpResult = (0 == mbedRet) ? CP_STATUS_OK : CP_STATUS_FAILED;
+    VERIFY_SUCCESS_OR_EXIT(TAG, (0 == mbedRet), "Problem parsing cert buffer", ERROR);
+
+    if (violations & CP_INVALID_VERSION )
+    {
+        outCert->version = 1;
+    }
+
+exit:
+    mbedtls_pk_free(&subjectPubKeyCtx);
+    mbedtls_pk_free(&issuerPrivKeyCtx);
+    mbedtls_x509write_crt_free(&outCertCtx);
+    mbedtls_entropy_free(&entropy);
+    mbedtls_ctr_drbg_free(&ctr_drbg);
+
+    return cpResult;
+}
+
+// must call InitTestCert before calling, and FreeTestCert when done
+// pass issuerCert as NULL for root CA
+static CertProfileResult GenerateInternalCert(CertType certType, mbedtls_x509_crt *issuerCert, mbedtls_x509_crt *outCert)
+{
+    char pemBuf[1024];
+    char mbedErrBuf[256];
+
+    CertProfileResult cpResult = CP_STATUS_OK;
+    OCStackResult stackResult = OC_STACK_OK;
+    int mbedRet = 0;
+
+    char *certificate = NULL;
+    size_t certificateLen = 0;
+
+    const char *subjectPubKey = NULL;
+    const char *issuerPrivKey = NULL;
+    const char *serial = NULL;
+    const char *subject = NULL;
+    const char *issuerCertPem = NULL;
+    OicUuid_t subjectUuid;
+
+    switch ( certType )
+    {
+    case CERT_CA_ROOT:
+        subjectPubKey = s_rootCaPubKey;
+        issuerPrivKey = s_rootCaPrivKey;
+        serial = s_rootCaSerial;
+        subject = s_rootCaSubject;
+        break;
+    case CERT_CA_INT:
+        subjectPubKey = s_intCaPubKey;
+        issuerPrivKey = s_rootCaPrivKey;
+        serial = s_intCaSerial;
+        subject = s_intCaSubject;
+        break;
+    case CERT_EE:
+        subjectPubKey = s_eePubKey;
+        issuerPrivKey = s_intCaPrivKey;
+        serial = s_eeSerial;
+        if (!OCConvertStringToUuid(s_eeUuid, subjectUuid.id))
+        {
+            OIC_LOG(ERROR, TAG, "Could not generate end entity cert UUID");
+            cpResult = CP_STATUS_FAILED;
+            goto exit;
+        }
+        break;
+    default:
+        OIC_LOG(ERROR, TAG, "Unknown cert type for internal cert generation");
+        cpResult = CP_STATUS_FAILED;
+        goto exit;
+    };
+
+    ValidityTime notValidBefore;
+    cpResult = SetNotBefore(notValidBefore, false);
+    VERIFY_SUCCESS_OR_EXIT(TAG, (CP_STATUS_OK == cpResult), "Problem setting not before date", ERROR);
+
+    ValidityTime notValidAfter;
+    cpResult = SetNotAfter(notValidAfter, false);
+    VERIFY_SUCCESS_OR_EXIT(TAG, (CP_STATUS_OK == cpResult), "Problem setting not after date", ERROR);
+
+    // need to convert issuer cert to PEM
+    if ( (CERT_CA_ROOT != certType) && (NULL != issuerCert) )
+    {
+        size_t pemLen = 0;
+        mbedRet = mbedtls_pem_write_buffer( "-----BEGIN CERTIFICATE-----\n",
+                                            "-----END CERTIFICATE-----\n",
+                                            issuerCert->raw.p, issuerCert->raw.len,
+                                            (unsigned char*)pemBuf, sizeof(pemBuf), &pemLen );
+        CP_LOG_MBED_ERROR(TAG, mbedRet, mbedErrBuf, sizeof(mbedErrBuf), ERROR);
+        cpResult = (0 == mbedRet) ? CP_STATUS_OK : CP_STATUS_FAILED;
+        VERIFY_SUCCESS_OR_EXIT(TAG, 0 == mbedRet, "Problem generating PEM from issuer cert", ERROR);
+        issuerCertPem = pemBuf;
+    }
+
+    switch ( certType )
+    {
+    case CERT_CA_ROOT:
+        stackResult = OCGenerateRootCACertificate(
+            subject, subjectPubKey,
+            issuerCertPem, issuerPrivKey,
+            serial,
+            notValidBefore, notValidAfter,
+            &certificate, &certificateLen);
+        break;
+    case CERT_CA_INT:
+        stackResult = OCGenerateIntermediateCACertificate(
+            subject, subjectPubKey,
+            issuerCertPem, issuerPrivKey,
+            serial,
+            notValidBefore, notValidAfter,
+            &certificate, &certificateLen);
+        break;
+    case CERT_EE:
+        stackResult = OCGenerateIdentityCertificate(
+            &subjectUuid, subjectPubKey,
+            issuerCertPem, issuerPrivKey,
+            serial,
+            notValidBefore, notValidAfter,
+            &certificate, &certificateLen);
+        break;
+    default:
+        OIC_LOG(ERROR, TAG, "Unknown cert type for internal cert generation");
+        cpResult = CP_STATUS_FAILED;
+        goto exit;
+    };
+    VERIFY_SUCCESS_OR_EXIT(TAG, (OC_STACK_OK == stackResult), "Problem generating internal certificate", ERROR);
+
+    mbedRet = mbedtls_x509_crt_parse(outCert, (const unsigned char*)certificate, certificateLen);
+    CP_LOG_MBED_ERROR(TAG, mbedRet, mbedErrBuf, sizeof(mbedErrBuf), ERROR);
+    cpResult = (0 == mbedRet) ? CP_STATUS_OK : CP_STATUS_FAILED;
+    VERIFY_SUCCESS_OR_EXIT(TAG, (0 == mbedRet), "Problem converting internal cert to mbed format", ERROR);
+
+exit:
+    OICFree(certificate);
+    return cpResult;
+    };
+
+static void InitTestCert(mbedtls_x509_crt *cert)
+{
+    mbedtls_x509_crt_init(cert);
+}
+
+
+static void FreeTestCert(mbedtls_x509_crt *cert)
+{
+    mbedtls_x509_crt_free(cert);
+}
+
index d88d2f6..76473a6 100644 (file)
@@ -60,7 +60,7 @@ OCStackResult OC_CALL OCGenerateKeyPair(char **publicKey, size_t *publicKeyLen,
                                         char **privateKey, size_t *privateKeyLen);
 
 /**
- * Generate a certificate to act as a Certificate Authority (CA).
+ * Generate a certificate to act as a Root Certificate Authority (CA).
  *
  * @param subject               Comma-separated list of RDN types and values:
  *                              e.g. "C=US, O=Open Connectivity Foundation, CN=Main CA"
@@ -86,7 +86,45 @@ OCStackResult OC_CALL OCGenerateKeyPair(char **publicKey, size_t *publicKeyLen,
  *
  * @return OC_STACK_OK if successful, error code otherwise
  */
-OCStackResult OC_CALL OCGenerateCACertificate(
+OCStackResult OC_CALL OCGenerateRootCACertificate(
+    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 to act as an Intermediate 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 OC_CALL OCGenerateIntermediateCACertificate(
     const char *subject,
     const char *subjectPublicKey,
     const char *issuerCert,
index 0aa9d82..aa833a2 100644 (file)
@@ -62,6 +62,14 @@ struct OicParseQueryIter
 #define VERIFY_SUCCESS(tag, op, logLevel) do{ if (!(op)) \
             {OIC_LOG((logLevel), tag, #op " failed!!"); goto exit; } }while(0)
 
+/**
+ * Macro to verify a conditional, if fails, log supplied message and goto exit
+ * eg: VERIFY_SUCCESS_OR_LOG_AND_EXIT(TAG, OC_STACK_OK == foo(), ERROR);
+ * @note Invoking function must define "exit:" label for goto functionality to work correctly.
+ */
+#define VERIFY_OR_LOG_AND_EXIT(tag, op, msg, logLevel) do{ if (!(op)) \
+            {OIC_LOG((logLevel), tag, msg); goto exit; } }while(0)
+
 /**
  * Macro to verify expression evaluates to bool true.
  * eg: VERIFY_TRUE_OR_EXIT(TAG, OC_STACK_OK == foo(), ERROR);
@@ -93,6 +101,15 @@ struct OicParseQueryIter
 #define VERIFY_NOT_NULL_RETURN(tag, arg, logLevel, retValue) do { if (NULL == (arg)) \
             { OIC_LOG((logLevel), tag, #arg " is NULL"); return retValue; } } while(0)
 
+/**
+ * Macro to log an mbedtls error
+ * For mbedtls functions that return 0 as non-error
+ * @note Invoker must provide message buffer, and must include "mbedtls/error.h"
+ */
+#define LOG_MBED_ERROR(tag, ret, buf, bufSize, logLevel) do{ if (0!=(ret)) { \
+    mbedtls_strerror((ret), (buf), (bufSize));                               \
+    OIC_LOG_V((logLevel), (tag), "mbedtls error:  %s", (buf)); } }while(0)
+
 /**
  * This method initializes the @ref OicParseQueryIter_t struct.
  *
index c2a9c08..020c4c0 100644 (file)
@@ -91,7 +91,8 @@ static char *ReadLine(char *buf, size_t len)
 }
 
 typedef enum {
-    CA_CERT,
+    ROOT_CA_CERT,
+    INT_CA_CERT,
     IDENTITY_CERT,
     ROLE_CERT
 } CertType;
@@ -127,11 +128,7 @@ static void DoGenCertificate(CertType certType)
         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)
+    if (ROOT_CA_CERT != certType)
     {
         printf("Issuer cert/key pair name (do not include .crt, .pub, or .prv): ");
         if (NULL == ReadLine(issKeyPairName, sizeof(issKeyPairName)))
@@ -207,7 +204,7 @@ static void DoGenCertificate(CertType certType)
     f = NULL;
 
     // -- Load issuer cert if applicable --
-    if (CA_CERT != certType)
+    if (ROOT_CA_CERT != certType)
     {
         snprintf(filename, sizeof(filename), "%s.crt", issKeyPairName);
         f = fopen(filename, "rb");
@@ -242,7 +239,7 @@ static void DoGenCertificate(CertType certType)
 
     // -- Prompt user for subject name --
 
-    if (CA_CERT == certType)
+    if (IDENTITY_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 : ");
@@ -298,8 +295,8 @@ static void DoGenCertificate(CertType certType)
 
     switch (certType)
     {
-    case CA_CERT:
-        res = OCGenerateCACertificate(
+    case ROOT_CA_CERT:
+        res = OCGenerateRootCACertificate(
             subject,
             publicKey.data(),
             NULL,
@@ -311,6 +308,19 @@ static void DoGenCertificate(CertType certType)
             &certificateLen);
         break;
 
+    case INT_CA_CERT:
+        res = OCGenerateIntermediateCACertificate(
+            subject,
+            publicKey.data(),
+            issuerCert.data(),
+            privateKey.data(),
+            serial,
+            notValidBefore,
+            notValidAfter,
+            &certificate,
+            &certificateLen);
+        break;
+
     case IDENTITY_CERT:
         res = OCGenerateIdentityCertificate(
             &subjectUuid,
@@ -484,10 +494,12 @@ int main()
         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(" 2. Generate a self-signed Root CA certificate (requires a key pair for the CA)\n");
+        printf(" 3. Generate an Intermediate CA certificate, signed by a Root CA\n");
+        printf("       (requires the Root CA's private key and certificate\n");
+        printf(" 4. Generate an identity certificate for a particular device UUID\n");
+        printf("       (requires the Root/Intermediate CA's private key and certificate, and the device's public key)\n");
+        printf(" 5. 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");
@@ -509,12 +521,15 @@ int main()
             DoGenKeyPair();
             break;
         case 2:
-            DoGenCertificate(CA_CERT);
+            DoGenCertificate(ROOT_CA_CERT);
             break;
         case 3:
-            DoGenCertificate(IDENTITY_CERT);
+            DoGenCertificate(INT_CA_CERT);
             break;
         case 4:
+            DoGenCertificate(IDENTITY_CERT);
+            break;
+        case 5:
             DoGenCertificate(ROLE_CERT);
             break;
         case 0:
index d518516..41b7c9f 100644 (file)
Binary files a/resource/csdk/security/provisioning/sample/oic_svr_db_client.dat and b/resource/csdk/security/provisioning/sample/oic_svr_db_client.dat differ
index 3368347..dbe1f6b 100644 (file)
@@ -3,36 +3,46 @@
         "creds": [\r
             {\r
                 "credid": 1,\r
-                "subjectuuid": "61646d69-6e44-6576-6963-655575696430",\r
+                "subjectuuid": "11111111-1111-1111-1111-111111111111",\r
                 "credtype": 8,\r
                 "publicdata": {\r
-                    "encoding": "oic.sec.encoding.der",\r
-                    "data": "308201FA3082019FA003020102020105300A06082A8648CE3D04030230683132303006035504030C29757569643A33313331333133312D333133312D333133312D333133312D333133313331333133313331310B3009060355040613024B523110300E060355040A0C0753616D73756E6731133011060355040B0C0A4F434620537562204341301E170D3136313130343134353535335A170D3336313130343134353535335A3068310B3009060355040613024B523110300E060355040A130753616D73756E6731133011060355040B130A4F4346204465766963653132303006035504031329757569643A36313634366436392D366534342D363537362D363936332D3635353537353639363433303059301306072A8648CE3D020106082A8648CE3D030107034200044310BC484A3B33F03B9BC66021B93A2BEA388D49398791C8E10E70437A40548DDA5F389FC16DA44E1A4DDC739D30C1CFD6AC82D141897129D8C162601D804323A33A303830360603551D11042F302D822B7573657269643A38646561366332642D653064392D346536342D383035632D333230316638376336633934300A06082A8648CE3D0403020349003046022100C34554A93FCF4EA1A9CC9783A7F29B6CC2B86FEBCB15495DEECD8548EAB3414B02210090CDD6731FE3BE7E2BE5F13F178B9A59E8DAC8EFBEFBFE4D9F456F629E73AA55"\r
+                   "encoding": "oic.sec.encoding.der",\r
+                   "data": "308201c230820166a0030201020214189cfc9c5e03a00e787cd8f72c05f02bad9ca6c9300c06082a8648ce3d04030205003040310b3009060355040613025553310c300a060355040a13034f4346312330210603550403131a44455620496e7420434120466f722050726f7620436c69656e74301e170d3138303232323231303935395a170d3238303232323231303935395a30343132303006035504031329757569643a31313131313131312d313131312d313131312d313131312d3131313131313131313131313059301306072a8648ce3d020106082a8648ce3d03010703420004330c7dd98deab941369ca64bfb15399829eefc7676028fd3e434dc940b9f469dd418e1d731f8f588b713a9b5670ae2cbc42257bfa8c126c44021fb3ff87ddf26a348304630090603551d1304023000300e0603551d0f0101ff04040302018830290603551d250422302006082b0601050507030106082b06010505070302060a2b0601040182de7c0106300c06082a8648ce3d04030205000348003045022100bacad2ab82a4b9ffe8e0f83c2182f1977f7f1445d966e4cb76a898abee9de447022065f9c8bcf6d85aa57170cfd0a7e460e0f1e238ed6e17cfaa9c859ab764fa416f"\r
                 },\r
                 "credusage": "oic.sec.cred.mfgcert",\r
                 "privatedata": {\r
-                    "encoding": "oic.sec.encoding.raw",\r
-                    "data": "3077020101042074A0348F8CB40E58FABAFAC494C4472CA04BECFEA6340276DFB4BA2F609F1A6FA00A06082A8648CE3D030107A144034200044310BC484A3B33F03B9BC66021B93A2BEA388D49398791C8E10E70437A40548DDA5F389FC16DA44E1A4DDC739D30C1CFD6AC82D141897129D8C162601D804323"\r
+                   "encoding": "oic.sec.encoding.raw",\r
+                   "data": "30770201010420a7327fd4cab3939e1dac3af863e79d36cc745260240330aca768d573f7aeb39ca00a06082a8648ce3d030107a14403420004330c7dd98deab941369ca64bfb15399829eefc7676028fd3e434dc940b9f469dd418e1d731f8f588b713a9b5670ae2cbc42257bfa8c126c44021fb3ff87ddf26"\r
                 }\r
-            },\r
-            {\r
+             },\r
+             {\r
                 "credid": 2,\r
+                "subjectuuid": "11111111-1111-1111-1111-111111111111",\r
+                "credtype": 8,\r
+                "publicdata": {\r
+                   "encoding": "oic.sec.encoding.der",\r
+                   "data": "308201cc30820171a00302010202143eb82f1a2ea07f120da3747b359d01dac141cc8b300c06082a8648ce3d04030205003041310b3009060355040613025553310c300a060355040a13034f4346312430220603550403131b44455620526f6f7420434120466f722050726f7620436c69656e74301e170d3138303232323231303932325a170d3238303232323231303932325a3040310b3009060355040613025553310c300a060355040a13034f4346312330210603550403131a44455620496e7420434120466f722050726f7620436c69656e743059301306072a8648ce3d020106082a8648ce3d03010703420004a80432e8346caffcc4c7de41f723bb04a566ac20d610e8df2e40605dd0fd2fbb173c4e6615e03219e8487f4249b046e74175621db8e2ee79242af2e59a8d7c7ca3463044300f0603551d13040830060101ff020100300e0603551d0f0101ff04040302010630210603551d25041a3018060a2b0601040182de7c0106060a2b0601040182de7c0107300c06082a8648ce3d04030205000347003044022001c91215ffb31949eef14f1b5f4529c4414d05b8d4f1bd059d4e96bdaf0f7e5702200b229045fa11bcd546cc0bbcf28ebb5f0a97aa41b0ff3fd2bb7543a88dfde0fe"\r
+                },\r
+                "credusage": "oic.sec.cred.mfgcert"\r
+             },\r
+            {\r
+                "credid": 3,\r
                 "subjectuuid": "*",\r
                 "credtype": 8,\r
                 "publicdata": {\r
-                    "encoding": "oic.sec.encoding.der",\r
-                    "data": "308201CF30820175A003020102020101300A06082A8648CE3D04030230683132303006035504030C29757569643A33313331333133312D333133312D333133312D333133312D333133313331333133313331310B3009060355040613024B523110300E060355040A0C0753616D73756E6731133011060355040B0C0A4F434620537562204341301E170D3136313130343132343933325A170D3336313130343132343933325A30683132303006035504030C29757569643A33313331333133312D333133312D333133312D333133312D333133313331333133313331310B3009060355040613024B523110300E060355040A0C0753616D73756E6731133011060355040B0C0A4F4346205375622043413059301306072A8648CE3D020106082A8648CE3D03010703420004A334EF1F497964DF840DF2F5BA2BFD6A0241FAD9C0D8E88A71821A46FD5CF800F5099627BD5473AE495678EB2D6F62474CFAC6C1C8B9DB47FA86373AB8330EBCA310300E300C0603551D13040530030101FF300A06082A8648CE3D0403020348003045022100AF5E06A44002579F13F47B19F299078A7B35FB6B2C707F7CC926319F744F2BB40220533AC74FA77F42AAFEAA2EED7E1BA2A440DEA6A99C7C3377D86AC4231B1D6D3B",\r
-                    "revstat": false\r
+                   "encoding": "oic.sec.encoding.der",\r
+                   "data": "308201ca3082016da00302010202147b9f928b0cf7012243be983c5c892eb7a5f3d8b5300c06082a8648ce3d04030205003040310b3009060355040613025553310c300a060355040a13034f4346312330210603550403131a44455620526f6f7420434120466f72204d666720536572766572301e170d3138303232323231313133375a170d3238303232323231313133375a3040310b3009060355040613025553310c300a060355040a13034f4346312330210603550403131a44455620526f6f7420434120466f72204d6667205365727665723059301306072a8648ce3d020106082a8648ce3d03010703420004e6fb0e47db7e1f2b2eed26261e1bd21e5db666519c9376e5d0e026469d56fa6eaac5199ca201f82fd49ac694e072699b9be749e39bd18ca9be70dfb14c82288ea3433041300c0603551d13040530030101ff300e0603551d0f0101ff04040302010630210603551d25041a3018060a2b0601040182de7c0106060a2b0601040182de7c0107300c06082a8648ce3d04030205000349003046022100a3d697f9cfe04483e29aae7bfd5f61b2bfe322ee838ecb75eb2975485275d491022100abc33276d6c6df073b868470959d6b2a0fc53a35bb7726625a7ce28b3e68f390",\r
+                   "revstat": false\r
                 },\r
                 "credusage": "oic.sec.cred.mfgtrustca"\r
-            }\r
+             }\r
         ],\r
-        "rowneruuid": "00000000-0000-0000-0000-000000000000",\r
+        "rowneruuid": "11111111-1111-1111-1111-111111111111",\r
         "rt": ["oic.r.cred"],\r
         "if": ["oic.if.baseline"]\r
-    },\r
+     },\r
     "acl": {\r
-        "aclist2": [\r
+      "aclist2": [\r
             {\r
                 "aceid": 1,\r
                 "subject": { "conntype": "anon-clear" },\r
                 "permission": 14\r
             }\r
         ],\r
-        "rowneruuid": "61646d69-6e44-6576-6963-655575696430",\r
-        "rt": ["oic.r.acl"],\r
-        "if": ["oic.if.baseline"]\r
-    },\r
-    "pstat": {\r
-        "dos": {\r
-            "s": 3,\r
-            "p": false\r
-        },\r
-        "isop": true,\r
-        "cm": 0,\r
-        "tm": 0,\r
-        "om": 4,\r
-        "sm": 4,\r
-        "rowneruuid": "61646d69-6e44-6576-6963-655575696430",\r
-        "rt": ["oic.r.pstat"],\r
-        "if": ["oic.if.baseline"]\r
-    },\r
-    "doxm": {\r
-        "oxms": [0],\r
-        "oxmsel": 0,\r
-        "sct": 9,\r
-        "owned": true,\r
-        "deviceuuid": "61646D69-6E44-6576-6963-655575696430",\r
-        "devowneruuid": "61646D69-6E44-6576-6963-655575696430",\r
-        "rowneruuid": "61646D69-6E44-6576-6963-655575696430",\r
-        "rt": ["oic.r.doxm"],\r
-        "if": ["oic.if.baseline"]\r
-    }\r
+      "rowneruuid": "11111111-1111-1111-1111-111111111111",\r
+      "rt": ["oic.r.acl"],\r
+      "if": ["oic.if.baseline"]\r
+   },\r
+   "pstat": {\r
+      "dos": {\r
+         "s": 1,\r
+         "p": false\r
+      },\r
+      "isop": false,\r
+      "cm": 2,\r
+      "tm": 0,\r
+      "om": 4,\r
+      "sm": 4,\r
+      "rowneruuid": "11111111-1111-1111-1111-111111111111",\r
+      "rt": ["oic.r.pstat"],\r
+      "if": ["oic.if.baseline"]\r
+   },\r
+   "doxm": {\r
+      "oxms": [2],\r
+      "oxmsel": 2,\r
+      "sct": 9,\r
+      "owned": false,\r
+      "deviceuuid": "11111111-1111-1111-1111-111111111111",\r
+      "devowneruuid": "11111111-1111-1111-1111-111111111111",\r
+      "rowneruuid": "11111111-1111-1111-1111-111111111111",\r
+      "x.org.iotivity.dpc": true,\r
+      "rt": ["oic.r.doxm"],\r
+      "if": ["oic.if.baseline"]\r
+   }\r
 }\r
index 2616f41..291313c 100644 (file)
Binary files a/resource/csdk/security/provisioning/sample/oic_svr_db_server_mfg.dat and b/resource/csdk/security/provisioning/sample/oic_svr_db_server_mfg.dat differ
index c92fd71..0f9df48 100644 (file)
@@ -1,37 +1,47 @@
 {
-   "cred": {
-      "creds": [
-         {
-            "credid": 1,
-            "subjectuuid": "4d617566-6163-7475-7265-724365727430",
-            "credtype": 8,
-            "publicdata": {
-               "encoding": "oic.sec.encoding.der",
-               "data": "308201F93082019FA003020102020107300A06082A8648CE3D04030230683132303006035504030C29757569643A33313331333133312D333133312D333133312D333133312D333133313331333133313331310B3009060355040613024B523110300E060355040A0C0753616D73756E6731133011060355040B0C0A4F434620537562204341301E170D3136313130343135303830395A170D3336313130343135303830395A3068310B3009060355040613024B523110300E060355040A130753616D73756E6731133011060355040B130A4F4346204465766963653132303006035504031329757569643A36613735373337342D373736662D373236622D343436352D3736353537353639363433303059301306072A8648CE3D020106082A8648CE3D03010703420004A86446F9A4B5A424922F4FB16730C80B21BEF558F792517D7737FDC49FD8CF982910F617805698DD4EE4DDA6C3B30918246B4D3540C74B836B1ECAC1A122B1BAA33A303830360603551D11042F302D822B7573657269643A36373434363731642D323936332D346339322D613862622D333831313762343836353865300A06082A8648CE3D0403020348003045022100D75F26C5D486782C9C8CDAAF3CB60562298E873EDD1C297C64A4112989CF8C65022060625A591EEA2E1B58E1A52B1AD9F086C5F1F265BE8FB8C024CB6C9FC69C9FCC"
-            },
-            "credusage": "oic.sec.cred.mfgcert",
-            "privatedata": {
-               "encoding": "oic.sec.encoding.raw",
-               "data": "3078020101042100E00D6E162B33F56D50B40E57288DF284F76D5CE7F1F800F7559882AB126B5813A00A06082A8648CE3D030107A14403420004A86446F9A4B5A424922F4FB16730C80B21BEF558F792517D7737FDC49FD8CF982910F617805698DD4EE4DDA6C3B30918246B4D3540C74B836B1ECAC1A122B1BA"
-            }
-         },
-         {
-            "credid": 2,
-            "subjectuuid": "*",
-            "credtype": 8,
-            "publicdata": {
-               "encoding": "oic.sec.encoding.der",
-               "data": "308201CF30820175A003020102020101300A06082A8648CE3D04030230683132303006035504030C29757569643A33313331333133312D333133312D333133312D333133312D333133313331333133313331310B3009060355040613024B523110300E060355040A0C0753616D73756E6731133011060355040B0C0A4F434620537562204341301E170D3136313130343132343933325A170D3336313130343132343933325A30683132303006035504030C29757569643A33313331333133312D333133312D333133312D333133312D333133313331333133313331310B3009060355040613024B523110300E060355040A0C0753616D73756E6731133011060355040B0C0A4F4346205375622043413059301306072A8648CE3D020106082A8648CE3D03010703420004A334EF1F497964DF840DF2F5BA2BFD6A0241FAD9C0D8E88A71821A46FD5CF800F5099627BD5473AE495678EB2D6F62474CFAC6C1C8B9DB47FA86373AB8330EBCA310300E300C0603551D13040530030101FF300A06082A8648CE3D0403020348003045022100AF5E06A44002579F13F47B19F299078A7B35FB6B2C707F7CC926319F744F2BB40220533AC74FA77F42AAFEAA2EED7E1BA2A440DEA6A99C7C3377D86AC4231B1D6D3B",
-               "revstat": false
-            },
-            "credusage": "oic.sec.cred.mfgtrustca"
-         }
-      ],
-      "rowneruuid": "4d617566-6163-7475-7265-724365727430",
-      "rt": ["oic.r.cred"],
-      "if": ["oic.if.baseline"]
-   },
-   "acl": {
+    "cred": {
+        "creds": [
+            {
+                "credid": 1,
+                "subjectuuid": "22222222-2222-2222-2222-222222222222",
+                "credtype": 8,
+                "publicdata": {
+                   "encoding": "oic.sec.encoding.der",
+                   "data": "308201c030820165a003020102021447efad722789c14dd7432c8ad8d107254322981f300c06082a8648ce3d0403020500303f310b3009060355040613025553310c300a060355040a13034f4346312230200603550403131944657620496e7420434120466f72204d666720536572766572301e170d3138303232323231313235345a170d3238303232323231313235345a30343132303006035504031329757569643a32323232323232322d323232322d323232322d323232322d3232323232323232323232323059301306072a8648ce3d020106082a8648ce3d03010703420004e8bc72ef357e44b2cb4c5ecee53d4b5887a7e49189a11de194891a248d78bff123591b969f4e5458fd81adbcbf3d2a456d6c343f850cc588f8d9c29c8777519da348304630090603551d1304023000300e0603551d0f0101ff04040302018830290603551d250422302006082b0601050507030106082b06010505070302060a2b0601040182de7c0106300c06082a8648ce3d0403020500034700304402205ef55c6c7afc4f5ec2ed8a1cb15690c3e366349ad54efd6c0636bd944f20dafa02207c51bc360fc4d343a0de66094dc67d00377b93b6e760be170ba7dc7d33bda3c7"
+                },
+                "credusage": "oic.sec.cred.mfgcert",
+                "privatedata": {
+                   "encoding": "oic.sec.encoding.raw",
+                   "data": "307702010104205aa7ccc369023bf3594a7048a7a0902c5628852d39efa3a5b21a49be8e7b490aa00a06082a8648ce3d030107a14403420004e8bc72ef357e44b2cb4c5ecee53d4b5887a7e49189a11de194891a248d78bff123591b969f4e5458fd81adbcbf3d2a456d6c343f850cc588f8d9c29c8777519d"
+                }
+             },
+             {
+                "credid": 2,
+                "subjectuuid": "22222222-2222-2222-2222-222222222222",
+                "credtype": 8,
+                "publicdata": {
+                   "encoding": "oic.sec.encoding.der",
+                   "data": "308201cc3082016fa00302010202143c63b103218a700a32848897865164d9799a6c13300c06082a8648ce3d04030205003040310b3009060355040613025553310c300a060355040a13034f4346312330210603550403131a44455620526f6f7420434120466f72204d666720536572766572301e170d3138303232323231313230305a170d3238303232323231313230305a303f310b3009060355040613025553310c300a060355040a13034f4346312230200603550403131944657620496e7420434120466f72204d6667205365727665723059301306072a8648ce3d020106082a8648ce3d030107034200043b8c398c16c68fce8c0a7dbc9cbb54413ac8ef3198de013a9b33c8b3f420b2206aad8d44033aee18266e23f3211995a7a4b29e0e8bf44aeca47a58ca00972089a3463044300f0603551d13040830060101ff020100300e0603551d0f0101ff04040302010630210603551d25041a3018060a2b0601040182de7c0106060a2b0601040182de7c0107300c06082a8648ce3d0403020500034900304602210095813d2af81116ab69576fd3d9103d29849246700aeb80611cb787059223c369022100fdb4e83be362b40efd202f34107de9920b4ef8ec1f1f57db8ece3acd0e923bed"
+                },
+                "credusage": "oic.sec.cred.mfgcert"
+             },
+            {
+                "credid": 3,
+                "subjectuuid": "*",
+                "credtype": 8,
+                "publicdata": {
+                   "encoding": "oic.sec.encoding.der",
+                   "data": "308201cc3082016fa003020102021467944d2fdb52be342eacbe332990f229771b0695300c06082a8648ce3d04030205003041310b3009060355040613025553310c300a060355040a13034f4346312430220603550403131b44455620526f6f7420434120466f722050726f7620436c69656e74301e170d3138303232323231303835325a170d3238303232323231303835325a3041310b3009060355040613025553310c300a060355040a13034f4346312430220603550403131b44455620526f6f7420434120466f722050726f7620436c69656e743059301306072a8648ce3d020106082a8648ce3d030107034200041e75544d42938a72c375b73d436ed4600824eee28295a253ff4fdd15a7c41ae96c07be2a3599fae987e039882ebf9ddb7bad000c66dd6145db05a00569643bb4a3433041300c0603551d13040530030101ff300e0603551d0f0101ff04040302010630210603551d25041a3018060a2b0601040182de7c0106060a2b0601040182de7c0107300c06082a8648ce3d04030205000349003046022100a9959d87f3ba6b9846c62804e83e209829de67349cbc1e4434132d677eeb94f2022100a01144a0e0b3f02f329025253c49c7f504763fb3351a6438fca1d2a3339ed321",
+                   "revstat": false
+                },
+                "credusage": "oic.sec.cred.mfgtrustca"
+             }
+        ],
+        "rowneruuid": "00000000-0000-0000-0000-000000000000",
+        "rt": ["oic.r.cred"],
+        "if": ["oic.if.baseline"]
+     },
+    "acl": {
       "aclist2": [
             {
                 "aceid": 1,
@@ -71,7 +81,7 @@
                 "permission": 14
             }
         ],
-      "rowneruuid": "4d617566-6163-7475-7265-724365727430",
+      "rowneruuid": "00000000-0000-0000-0000-000000000000",
       "rt": ["oic.r.acl"],
       "if": ["oic.if.baseline"]
    },
@@ -85,7 +95,7 @@
       "tm": 0,
       "om": 4,
       "sm": 4,
-      "rowneruuid": "4d617566-6163-7475-7265-724365727430",
+      "rowneruuid": "00000000-0000-0000-0000-000000000000",
       "rt": ["oic.r.pstat"],
       "if": ["oic.if.baseline"]
    },
       "oxmsel": 2,
       "sct": 9,
       "owned": false,
-      "deviceuuid": "4d617566-6163-7475-7265-724365727430",
-      "devowneruuid": "4d617566-6163-7475-7265-724365727430",
-      "rowneruuid": "4d617566-6163-7475-7265-724365727430",
+      "deviceuuid": "22222222-2222-2222-2222-222222222222",
+      "devowneruuid": "00000000-0000-0000-0000-000000000000",
+      "rowneruuid": "00000000-0000-0000-0000-000000000000",
       "x.org.iotivity.dpc": true,
       "rt": ["oic.r.doxm"],
       "if": ["oic.if.baseline"]
index 00fb1ab..6e402d2 100644 (file)
@@ -963,7 +963,7 @@ static int setupCA()
     }
 
     /* Create a CA certificate */
-    res = OCGenerateCACertificate(
+    res = OCGenerateRootCACertificate(
         "C=US, O=Open Connectivity Foundation, CN=IoTivity test code CA",  // subject
         publicKey,
         NULL,               // Issuer private key is null
@@ -975,7 +975,7 @@ static int setupCA()
         &caCertLen);
     if (res != OC_STACK_OK)
     {
-        OIC_LOG_V(ERROR, TAG, "OCGenerateCACertificate failed, error: %d", res);
+        OIC_LOG_V(ERROR, TAG, "OCGenerateRootCACertificate failed, error: %d", res);
         goto exit;
     }
 
index 18c8422..d76d2cd 100644 (file)
@@ -42,6 +42,7 @@
 // headers required for mbed TLS
 #include "mbedtls/config.h"
 #include "mbedtls/platform.h"
+#include "mbedtls/error.h"
 #include "mbedtls/entropy.h"
 #include "mbedtls/ctr_drbg.h"
 #include "mbedtls/x509_csr.h"
 
 #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 identity certificates */
+static const unsigned char s_ekuIdentity[] = {
+    0x30, 0x20,
+    0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, // serverAuth (1.3.6.1.5.5.7.3.1)
+    0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, // clientAuth (1.3.6.1.5.5.7.3.2)
+    0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0xDE, 0x7C, 0x01, 0x06 // OCF ID OID (1.3.6.1.4.1.44924.1.6)
+};
 
 /* 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 };
@@ -221,7 +227,8 @@ exit:
 }
 
 typedef enum {
-    CERT_TYPE_CA,
+    CERT_TYPE_ROOT_CA,
+    CERT_TYPE_INTERMEDIATE_CA,
     CERT_TYPE_IDENTITY,
     CERT_TYPE_ROLE
 } CertificateType_t;
@@ -251,6 +258,8 @@ static OCStackResult GenerateCertificate(
     mbedtls_ctr_drbg_context ctr_drbg;
 
     char buf[2048];
+    char mbedErrorBuf[256];
+
 
     if (NULL == subjectPublicKey || NULL == issuerPrivateKey || NULL == subject || NULL == serial ||
         NULL == notValidBefore || NULL == notValidAfter)
@@ -269,23 +278,28 @@ static OCStackResult GenerateCertificate(
     memset(certificate, 0, sizeof(*certificate));
 
     ret = mbedtls_mpi_read_string(&serialMpi, 10, serial);
-    VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+    LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+    VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret, "Could not parse serial number for internal cert generation", ERROR);
 
     ret = mbedtls_pk_parse_public_key(&subjKeyCtx, (const uint8_t *)subjectPublicKey, strlen(subjectPublicKey) + 1);
-    VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+    LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+    VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret, "Could not parse public key for internal cert generation",  ERROR);
 
     ret = mbedtls_pk_parse_key(&issKeyCtx, (const uint8_t *)issuerPrivateKey, strlen(issuerPrivateKey) + 1, NULL, 0);
-    VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+    LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+    VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret, "Could not parse private key for internal cert generation",  ERROR);
 
     /* If issuerCert is NULL, then the cert will be self-signed. */
     if (NULL != issuerCert)
     {
         ret = mbedtls_x509_crt_parse(&issCertCtx, (const uint8_t *)issuerCert, strlen(issuerCert) + 1);
-        VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+        LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+        VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret, "Could not parse issuer cert for internal cert generation",  ERROR);
     }
 
     ret = mbedtls_x509write_crt_set_validity(&outCertCtx, notValidBefore, notValidAfter);
-    VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+    LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+    VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret, "Could not write validity time for internal cert generation",  ERROR);
 
     mbedtls_x509write_crt_set_version(&outCertCtx, MBEDTLS_X509_CRT_VERSION_3);
     mbedtls_x509write_crt_set_md_alg(&outCertCtx, MBEDTLS_MD_SHA256);
@@ -294,20 +308,23 @@ static OCStackResult GenerateCertificate(
 
     ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func,
         &entropy, (const uint8_t *)PERSONALIZATION_STRING, sizeof(PERSONALIZATION_STRING));
-    VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+    LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+    VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret, "Could not generate seed for internal cert generation",  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);
+    LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+    VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret, "Could not write serial number for internal cert generation",  ERROR);
 
     ret = mbedtls_x509write_crt_set_subject_name(&outCertCtx, subject);
-    VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+    LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+    VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret, "Could not write subject name for internal cert generation",  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);
+        VERIFY_OR_LOG_AND_EXIT(TAG, 0 < ret,  "Could not parse subject name from issuer for internal cert generation", ERROR);
         ret = mbedtls_x509write_crt_set_issuer_name(&outCertCtx, buf);
     }
     else
@@ -315,30 +332,50 @@ static OCStackResult GenerateCertificate(
         /* 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);
+    LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+    VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret, "Could not write issuer name for internal cert generation",  ERROR);
 
     mbedtls_x509write_crt_set_subject_key(&outCertCtx, &subjKeyCtx);
 
     mbedtls_x509write_crt_set_issuer_key(&outCertCtx, &issKeyCtx);
 
-    if (certType == CERT_TYPE_CA)
+    /*
+     * mbedtls max_pathlen behaviour
+     * CA Cert: Expects RFC5280_val as encoding input
+     *          Provides RFC5280_val+1 as decoding output, where 0 = not present
+     * EE Cert: Does not encode
+     *          Provides 0 as decoding output
+    */
+
+    if (CERT_TYPE_ROOT_CA == certType)
     {
         ret = mbedtls_x509write_crt_set_basic_constraints(&outCertCtx, 1, -1);
-        VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+        LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+        VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret, "Could not write basic constraints for internal root ca cert generation",  ERROR);
+        ret = mbedtls_x509write_crt_set_key_usage(&outCertCtx,
+            MBEDTLS_X509_KU_KEY_CERT_SIGN | MBEDTLS_X509_KU_CRL_SIGN);
+        LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+        VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret, "Could not write key usage for internal root ca cert generation",  ERROR);
+    }
+    else if (CERT_TYPE_INTERMEDIATE_CA == certType)
+    {
+        ret = mbedtls_x509write_crt_set_basic_constraints(&outCertCtx, 1, 0);
+        LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+        VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret, "Could not write basic constraints for internal intermediate ca cert generation",  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);
+            MBEDTLS_X509_KU_KEY_CERT_SIGN | MBEDTLS_X509_KU_CRL_SIGN);
+        LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+        VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret, "Could not write key usage for internal intermediate ca cert generation",  ERROR);
     }
     else
     {
-        ret = mbedtls_x509write_crt_set_basic_constraints(&outCertCtx, 0, 0);
-        VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+        ret = mbedtls_x509write_crt_set_basic_constraints(&outCertCtx, 0, -1);
+        LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+        VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret, "Could not write basic constraints for internal root ee cert generation",  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);
+            MBEDTLS_X509_KU_DIGITAL_SIGNATURE | MBEDTLS_X509_KU_KEY_AGREEMENT);
+        LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+        VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret, "Could not write key usage for internal ee cert generation",  ERROR);
     }
 
     switch (certType)
@@ -348,18 +385,21 @@ static OCStackResult GenerateCertificate(
             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);
+        LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+        VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret,  "Could not write eku for internal role cert generation", ERROR);
         ret = snprintf(buf, sizeof(buf), "CN=%s%s%s", role, (NULL != authority) ? ",OU=" : "", (NULL != authority) ? authority : "");
         // To prevent sign-compare warning sizeof(buf) is cast to int. This is safe because the max size of buf fits into int.
         // Note ret value from snprintf may be negative if there was an error so it should not be cast to size_t.
-        VERIFY_SUCCESS(TAG, ret < (int)sizeof(buf), ERROR);
+        VERIFY_OR_LOG_AND_EXIT(TAG, ret < (int)sizeof(buf),  "snprintf error during internal cert generation", 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);
+        LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+        VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret,  "Could not parse direcotry name for internal role cert generation", ERROR);
 
         ret = mbedtls_x509write_crt_set_subject_alt_names(&outCertCtx, &names);
-        VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+        LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+        VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret,  "Could not write subject alt names for internal role cert generation", ERROR);
         break;
 
     case CERT_TYPE_IDENTITY:
@@ -367,24 +407,28 @@ static OCStackResult GenerateCertificate(
             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);
+        LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+        VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret,  "Could not write eku for internal ee cert generation", ERROR);
         break;
 
-    case CERT_TYPE_CA:
+    case CERT_TYPE_ROOT_CA:
+    case CERT_TYPE_INTERMEDIATE_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);
+        LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+        VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret, "Could not write eku for internal root/int ca cert generation",  ERROR);
         break;
 
     default:
         assert(false);
-        VERIFY_SUCCESS(TAG, false, ERROR);
+        VERIFY_OR_LOG_AND_EXIT(TAG, false, "Unkown cert type during internal cert generation", ERROR);
     }
 
     ret = mbedtls_x509write_crt_pem(&outCertCtx, (uint8_t *)buf, sizeof(buf), mbedtls_ctr_drbg_random, &ctr_drbg);
-    VERIFY_SUCCESS(TAG, 0 == ret, ERROR);
+    LOG_MBED_ERROR(TAG, ret, mbedErrorBuf, sizeof(mbedErrorBuf), ERROR);
+    VERIFY_OR_LOG_AND_EXIT(TAG, 0 == ret, "Could not generate pem buffer for internal cert generation",  ERROR);
 
     certificate->len = strlen(buf) + 1;
     certificate->bytes = (uint8_t *)OICCalloc(1, certificate->len);
@@ -414,7 +458,43 @@ exit:
     return res;
 }
 
-OCStackResult OC_CALL OCGenerateCACertificate(
+OCStackResult OC_CALL OCGenerateRootCACertificate(
+    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_ROOT_CA,
+        subject,
+        subjectPublicKey,
+        issuerCert,
+        issuerPrivateKey,
+        serial,
+        notValidBefore,
+        notValidAfter,
+        NULL,
+        NULL,
+        &byteStr);
+
+    if (OC_STACK_OK == res)
+    {
+        *certificate = (char *)byteStr.bytes;
+        *certificateLen = byteStr.len;
+    }
+
+    return res;
+}
+
+OCStackResult OC_CALL OCGenerateIntermediateCACertificate(
     const char *subject,
     const char *subjectPublicKey,
     const char *issuerCert,
@@ -429,7 +509,7 @@ OCStackResult OC_CALL OCGenerateCACertificate(
     OCByteString byteStr = { 0 };
 
     res = GenerateCertificate(
-        CERT_TYPE_CA,
+        CERT_TYPE_INTERMEDIATE_CA,
         subject,
         subjectPublicKey,
         issuerCert,
index f9d96b8..09d97ee 100644 (file)
@@ -13,11 +13,12 @@ OCDiscoverSingleDevice
 OCDiscoverSingleDeviceInUnicast
 OCDiscoverUnownedDevices
 OCDoOwnershipTransfer
-OCGenerateCACertificate
 OCGenerateIdentityCertificate
+OCGenerateIntermediateCACertificate
 OCGenerateKeyPair
 OCGenerateRandomSerialNumber
 OCGenerateRoleCertificate
+OCGenerateRootCACertificate
 OCGetACLResource
 OCGetACL2Resource
 OCGetCredResource