replace : iotivity -> iotivity-sec
[platform/upstream/iotivity.git] / resource / csdk / security / src / pkix_interface.c
index 7c4daaa..dc807d3 100644 (file)
-//******************************************************************
-//
-// Copyright 2016 Intel Mobile Communications GmbH All Rights Reserved.
-//
-//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+/*****************************************************************
+ *
+ * Copyright 2016 Samsung Electronics All Rights Reserved.
+ *
+ *
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * *****************************************************************/
 
 #include "pkix_interface.h"
 #include "credresource.h"
 #include "crlresource.h"
 #include "srmresourcestrings.h"
+#include "casecurityinterface.h"
 #include "logger.h"
+#ifdef __TIZENRT__
+#include "oic_malloc.h"
+#endif
 
 #define TAG "OIC_SRM_PKIX_INTERFACE"
 
-void GetPkixInfo(PkiInfo_t * inf)
+static HWPkixContext_t gHwPkixCtx = {
+    .getHwKeyContext = NULL,
+    .freeHwKeyContext = NULL,
+    .getOwnCertCb = NULL,
+    .setupPkContextCb = NULL,
+    .hwKeyCtx = NULL
+};
+
+/*
+ * returns the number of removed zeros
+ */
+static size_t remove_useless_leading_zeros(uint8_t *asn_integer_buf, uint32_t size)
+{
+    /*
+    */
+    uint8_t buf[32 + 4];
+    uint8_t tag = asn_integer_buf[0];
+    uint8_t len = asn_integer_buf[1];
+    uint8_t *p = asn_integer_buf + 2;
+    uint32_t idx = 0;
+
+    if (size < 2)
+    {
+        return 0;
+    }
+
+    if (tag != MBEDTLS_ASN1_INTEGER)
+    {
+        return 0;
+    }
+
+    if ((uint32_t)len + 2 != size || sizeof(buf) < size)
+    {
+        return 0;
+    }
+
+    for (idx = 0; idx < (uint32_t)len - 1; idx++)
+    {
+        if (p[idx] != 0)
+        {
+            break;
+        }
+        if (p[idx] == 0 && ((p[idx + 1] & 0x80) != 0)) // 00000000 1XXXXXXXX (bit)
+        {
+            break;
+        }
+    }
+
+    if (idx == 0)
+    {
+        return 0;
+    }
+
+    len -= idx;
+    asn_integer_buf[1] = len;
+
+    memcpy(buf, p + idx, len);
+    memcpy(p, buf, len);
+
+    return idx;
+}
+
+void CheckInvalidDERSignature(uint8_t *crtBuf, size_t *crtBufLen)
 {
     OIC_LOG_V(DEBUG, TAG, "In %s", __func__);
+    if (NULL == crtBuf)
+    {
+        OIC_LOG(ERROR, TAG, "Param buf is NULL");
+        return;
+    }
+    if (NULL == crtBufLen)
+    {
+        OIC_LOG(ERROR, TAG, "Param bufLen is NULL");
+        return;
+    }
+
+    mbedtls_x509_crt deviceCert;
+    int ret = 0;
+
+    // check only first(i.e., device) certificate
+    if (crtBuf[0] == 0x30 && crtBuf[1] == 0x82)
+    {
+        uint8_t *sign_ptr = NULL;
+        /**
+        * structure of r_buf & s_buf
+        * +---------------+-------------+----------------------------+
+        * | tag (INTEGER) | length (1B) | value (r or s in integer)  |
+        * +---------------+-------------+----------------------------+
+        */
+        uint8_t r_buf[32 + 4]; // for ECC 256 sign
+        uint8_t s_buf[32 + 4];
+        uint32_t r_len = 0;
+        uint32_t s_len = 0;
+        size_t sign_len = 0;
+        uint32_t removed = 0;
+        uint32_t removed_total = 0;
+        size_t org_len = 0;
+
+        mbedtls_x509_crt_init(&deviceCert);
+
+        unsigned char * tmp = (unsigned char *)crtBuf + 1;
+        if ( 0 != mbedtls_asn1_get_len(&tmp, crtBuf + *crtBufLen, &org_len))
+        {
+            OIC_LOG(ERROR, TAG, "Invalid parsed length");
+            goto exit;
+        }
+
+        if (org_len < *crtBufLen)
+        {
+            ret = mbedtls_x509_crt_parse_der(&deviceCert, crtBuf, org_len + 4);
+            if (0 != ret)
+            {
+                OIC_LOG_V(ERROR, TAG, "mbedtls_x509_crt_parse_der returned -0x%04x", -(ret));
+                goto exit;
+            }
+
+            if (NULL == deviceCert.sig.p)
+            {
+                OIC_LOG(ERROR, TAG, "Cert's signature is NULL");
+                goto exit;
+            }
+            sign_ptr = (uint8_t*)deviceCert.sig.p;
+            ret = mbedtls_asn1_get_tag(&sign_ptr, deviceCert.sig.p + deviceCert.sig.len, &sign_len,
+                MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
+            if (0 != ret)
+            {
+                OIC_LOG_V(ERROR, TAG, "mbedtls_asn1_get_tag error : %d", ret);
+                goto exit;
+            }
+            if (sign_ptr)
+            {
+                r_len = sign_ptr[1] + 2; // including header itself
+            }
+            if (r_len > deviceCert.sig.len)
+            {
+                OIC_LOG_V(ERROR, TAG, "signature length check error #1 : %d", ret);
+                goto exit;
+            }
+            OIC_LOG_V(DEBUG, TAG, "r_len = %d", r_len);
+            memcpy(r_buf, sign_ptr, r_len);
+            removed = remove_useless_leading_zeros(r_buf, r_len);
+
+            /* Do not change order */
+            sign_ptr += r_len;
+            r_len -= removed;
+            removed_total += removed;
+            if (sign_ptr)
+            {
+                s_len = sign_ptr[1] + 2; // including header itself
+            }
+            if (s_len + r_len > deviceCert.sig.len)
+            {
+                OIC_LOG_V(ERROR, TAG, "signature length check error #2 : %d", ret);
+                goto exit;
+            }
+            OIC_LOG_V(DEBUG, TAG, "s_len = %d", s_len);
+            memcpy(s_buf, sign_ptr, s_len);
+            removed = remove_useless_leading_zeros(s_buf, s_len);
+            s_len -= removed;
+            removed_total += removed;
+
+            if (removed_total > 0)
+            {
+                // if length of signature is incorrect.
+                OIC_LOG_V(INFO, TAG, "Cert Length (Before) : %lu", *crtBufLen);
+                OIC_LOG(INFO, TAG, "Invalid length of signature is dectected.");
+                OIC_LOG(INFO, TAG, "Update signature...");
+
+                uint16_t crt_len = 0;
+                /* change bitstring length */
+                {
+                    uint8_t *sign_len_ptr = deviceCert.sig.p + 1;
+                    *sign_len_ptr -= removed_total;
+                }
+                /* change signature length */
+                {
+                    uint8_t *sign_len_ptr = deviceCert.sig.p - 1;
+                    if (*sign_len_ptr == 0)
+                    {
+                        sign_len_ptr--;
+                    }
+                    *sign_len_ptr -= removed_total;
+                }
+                /* change signature */
+                {
+                    memcpy(deviceCert.sig.p + 2, r_buf, r_len);
+                    memcpy(deviceCert.sig.p + 2 + r_len, s_buf, s_len);
+                }
+                /* change certificate length */
+                {
+                    uint8_t *crt_len_ptr = deviceCert.raw.p + 2;
+                    crt_len = (crt_len_ptr[0] << 8) & 0x0000FF00;
+                    crt_len |= (crt_len_ptr[1] & 0x000000FF);
+                    crt_len -= removed_total;
+                    crt_len_ptr[0] = (uint8_t)(crt_len >> 8);
+                    crt_len_ptr[1] = (uint8_t)(crt_len);
+                }
+                crt_len += 4; // include header and length field
+                /* change raw data and crt parse cert again */
+                {
+                    mbedtls_x509_crt crt_cpy;
+                    mbedtls_x509_crt_init( &crt_cpy );
+                    if ((ret = mbedtls_x509_crt_parse_der(&crt_cpy, deviceCert.raw.p, (size_t)crt_len)) != 0)
+                    {
+                        OIC_LOG_V(ERROR, TAG, "mbedtls_x509_crt_parse error : %d", ret);
+                        mbedtls_x509_crt_free(&crt_cpy);
+                        goto exit;
+                    }
+
+                    org_len += 4; // include header and length field
+                    size_t remained_len = (*crtBufLen - org_len);
+                    memcpy(crtBuf, deviceCert.raw.p, crt_len);
+                    memcpy(crtBuf + crt_len, crtBuf + org_len, remained_len);
+                    *crtBufLen = (size_t)crt_len + remained_len;
+
+                    mbedtls_x509_crt_free(&crt_cpy);
+                    OIC_LOG_V(INFO, TAG, "Dev cert : %lu -> %lu", org_len, crt_len);
+                    OIC_LOG_V(INFO, TAG, "Remained chain : %lu", remained_len);
+                    OIC_LOG_V(INFO, TAG, "Cert Length (After) : %lu", *crtBufLen);
+                }
+            }
+            else
+            {
+                // if length of signature is correct.
+                OIC_LOG(INFO, TAG, "Don't need to update signature...");
+            }
+
+        }
+    }
+    else
+    {
+        OIC_LOG(ERROR, TAG, "PEM or invalid cert format detected. finish to check");
+    }
+
+exit:
+    mbedtls_x509_crt_free(&deviceCert);
+    OIC_LOG_V(DEBUG, TAG, "Cert chain length = %d", *crtBufLen);
+    OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+}
+
+/**
+ * Internal API to load the key/cert from HW PKI
+ *
+ * @return true on HW PKI is available
+ */
+static bool GetPkixInfoFromHw(PkiInfo_t * inf)
+{
+    OIC_LOG_V(INFO, TAG, "In %s", __func__);
+
+    if (!gHwPkixCtx.hwKeyCtx && gHwPkixCtx.getHwKeyContext)
+    {
+        gHwPkixCtx.hwKeyCtx = gHwPkixCtx.getHwKeyContext(
+                                                        HWKEY_SVC_IOTIVITY,
+                                                        HWKEY_USAGE_PRIMARY, NULL);
+        if (!gHwPkixCtx.hwKeyCtx)
+        {
+            OIC_LOG(WARNING, TAG, "gHwPkixCtx.getHwKeyContext return null");
+        }
+    }
+
+    if (gHwPkixCtx.hwKeyCtx && gHwPkixCtx.getOwnCertCb)
+    {
+        int ret = gHwPkixCtx.getOwnCertCb(gHwPkixCtx.hwKeyCtx, &inf->crt.data, &inf->crt.len);
+        if (0 != ret)
+        {
+            OIC_LOG_V(ERROR, TAG, "gHwPkixCtx.getOwnCertCb error : %d", ret);
+            OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+            return false;
+        }
+
+        // check and fix invalid cert signature
+        CheckInvalidDERSignature(inf->crt.data, &inf->crt.len);
+
+        OIC_LOG_V(INFO, TAG, "Cert chain length = %d", inf->crt.len);
+        OIC_LOG_V(INFO, TAG, "Out %s", __func__);
+        return true;
+    }
+    OIC_LOG_V(INFO, TAG, "Out %s", __func__);
+    return false;
+}
+
+void GetPkixInfo(PkiInfo_t * inf)
+{
+    OIC_LOG_V(INFO, TAG, "In %s", __func__);
     if (NULL == inf)
     {
         OIC_LOG(ERROR, TAG, "NULL passed");
         OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
         return;
     }
-    GetDerOwnCert(&inf->crt, PRIMARY_CERT);
-    GetDerKey(&inf->key, PRIMARY_CERT);
+
+    if (GetPkixInfoFromHw(inf))
+    {
+        OIC_LOG(INFO, TAG, "H/W based PKI will be used.");
+    }
+    else
+    {
+        GetDerOwnCert(&inf->crt, PRIMARY_CERT);
+        GetDerKey(&inf->key, PRIMARY_CERT);
+    }
     GetDerCaCert(&inf->ca, TRUST_CA);
     GetDerCrl(&inf->crl);
-    OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+
+    OIC_LOG_V(INFO, TAG, "Out %s", __func__);
 }
 
 void GetManufacturerPkixInfo(PkiInfo_t * inf)
 {
-    OIC_LOG_V(DEBUG, TAG, "In %s", __func__);
+    OIC_LOG_V(INFO, TAG, "In %s", __func__);
     if (NULL == inf)
     {
         OIC_LOG(ERROR, TAG, "NULL passed");
         OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
         return;
     }
-    GetDerOwnCert(&inf->crt, MF_PRIMARY_CERT);
-    GetDerKey(&inf->key, MF_PRIMARY_CERT);
+
+    if (GetPkixInfoFromHw(inf))
+    {
+        OIC_LOG(INFO, TAG, "H/W based PKI will be used.");
+    }
+    else
+    {
+        GetDerOwnCert(&inf->crt, MF_PRIMARY_CERT);
+        GetDerKey(&inf->key, MF_PRIMARY_CERT);
+    }
     GetDerCaCert(&inf->ca, MF_TRUST_CA);
     // CRL not provided
+#ifdef __TIZENRT__
+    OICFree(inf->crl.data);
+#endif
     inf->crl.data = NULL;
     inf->crl.len = 0;
-    OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+    OIC_LOG_V(INFO, TAG, "Out %s", __func__);
 }
 
 void InitCipherSuiteList(bool * list)
@@ -70,6 +375,10 @@ void InitCipherSuiteList(bool * list)
         return;
     }
     InitCipherSuiteListInternal(list, TRUST_CA);
+    if (gHwPkixCtx.getOwnCertCb)
+    {
+        list[1] = true;
+    }
     OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
 }
 
@@ -83,5 +392,68 @@ void InitManufacturerCipherSuiteList(bool * list)
         return;
     }
     InitCipherSuiteListInternal(list, MF_TRUST_CA);
+    if (gHwPkixCtx.getOwnCertCb)
+    {
+        list[1] = true;
+    }
     OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
 }
+
+int SetupHwPkContext(mbedtls_pk_context* pkCtx)
+{
+    OIC_LOG_V(DEBUG, TAG, "In %s", __func__);
+    if (NULL == pkCtx)
+    {
+        OIC_LOG(ERROR, TAG, "NULL passed");
+        OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+        return -1;
+    }
+
+    if (gHwPkixCtx.setupPkContextCb)
+    {
+        OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+        return gHwPkixCtx.setupPkContextCb(pkCtx, gHwPkixCtx.hwKeyCtx);
+    }
+    else
+    {
+        OIC_LOG(ERROR, TAG, "gHwPkixCallbacks.setupPkContextCb is NULL");
+    }
+
+    OIC_LOG_V(DEBUG, TAG, "Out %s", __func__);
+
+    return -1;
+}
+
+int SetHwPkixCallbacks(GetHwKeyContext getHwKeyContext,
+                        FreeHwKeyContext freeHwKeyContext,
+                        GetOwnCertFromHwCallback getOwnCertCb,
+                        SetupPkContextFromHwCallback setupPkContextCb)
+{
+    OIC_LOG_V(INFO, TAG, "In %s", __func__);
+
+    if (NULL == getHwKeyContext || NULL == freeHwKeyContext
+        || NULL == getOwnCertCb || NULL == setupPkContextCb)
+    {
+        OIC_LOG(ERROR, TAG, "NULL Passed");
+        OIC_LOG(ERROR, TAG, "Callback function parameters can not be null");
+        return -1;
+    }
+
+    gHwPkixCtx.getHwKeyContext = getHwKeyContext;
+    gHwPkixCtx.freeHwKeyContext = freeHwKeyContext;
+    gHwPkixCtx.getOwnCertCb = getOwnCertCb;
+    gHwPkixCtx.setupPkContextCb = setupPkContextCb;
+
+    if (gHwPkixCtx.hwKeyCtx)
+    {
+        gHwPkixCtx.freeHwKeyContext(gHwPkixCtx.hwKeyCtx);
+    }
+    gHwPkixCtx.hwKeyCtx = NULL;
+
+    // setup pkcontext handler
+    CAregisterSetupPkContextHandler(SetupHwPkContext);
+
+    OIC_LOG_V(INFO, TAG, "Out %s", __func__);
+    return 0;
+}
+