[IOT-1622] Use PBKDF2 code from mbedTLS
authorGreg Zaverucha <gregz@microsoft.com>
Tue, 6 Dec 2016 22:25:12 +0000 (14:25 -0800)
committerKevin Kane <kkane@microsoft.com>
Mon, 19 Dec 2016 17:57:01 +0000 (17:57 +0000)
Replace the PBKDF2 implementation in IoTivity
(resource\csdk\security\src\pbkdf2.c) that uses TinyDTLS\92s
implementation of HMAC, with the implementation from mbedTLS.

Add test vectors created with the old implementation to make sure
the new implementation will interoperate.

Change-Id: I8d7e7c5e9ff0cfc8a2a149dee5aa765fc6e00a9c
Signed-off-by: Greg Zaverucha <gregz@microsoft.com>
Reviewed-on: https://gerrit.iotivity.org/gerrit/15727
Tested-by: jenkins-iotivity <jenkins-iotivity@opendaylight.org>
Reviewed-by: Kevin Kane <kkane@microsoft.com>
(cherry picked from commit cbb3e4e08dce06798ef8122c0ab945ee67973aea)
Reviewed-on: https://gerrit.iotivity.org/gerrit/15803

resource/csdk/security/include/pbkdf2.h
resource/csdk/security/src/pbkdf2.c
resource/csdk/security/unittest/SConscript
resource/csdk/security/unittest/pbkdf2tests.cpp [new file with mode: 0644]

index 2957db8..d2408d5 100644 (file)
@@ -31,7 +31,8 @@ extern "C"
 
 /**
  * The number of iterations desired to derived key.
- * (Recommened by RFC 2898)
+ * As specified in the OCF Security Specification. This choice is required for 
+ * interoperability in the Random PIN OTM. 
  */
 #define PBKDF_ITERATIONS 1000
 
index 9bbd760..20d637f 100644 (file)
-//******************************************************************
-//
-// Copyright 2015 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.
-//
-//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+/******************************************************************
+*
+* Copyright 2016 Microsoft Corporation
+*
+*
+*
+* 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 <string.h>
-#include <math.h>
-#include "platform_features.h"
-#include "pbkdf2.h"
-#include "hmac.h"
-#include "debug.h"
 #include "logger.h"
+#include "mbedtls/pkcs5.h"
+#include "mbedtls/md.h"
 
-#define TAG "OIC_PBDKF2"
-#define XOR_BUF(in, out, bufSize)\
-do \
-{\
-    size_t i=0;\
-    for (i=0; i< (bufSize); i++)\
-    {\
-        (out)[i] = (in)[i] ^ (out)[i];\
-    }\
-} while(0)\
+#define TAG "OCF_PBDKF2"
 
-static int isLittle()
-{
-    static int a = 1;
-    static int flag = -1;
-    if (flag == -1)
-    {
-        if (  ((uint8_t *)&a)[0]  == 0x1) // little
-            flag = 1;
-        else
-            flag = 0;
-    }
-    return flag;
-}
-
-static void GetBigEndianBuf(uint8_t *buf, int num)
-{
-    uint8_t *nBuf = (uint8_t *)&num;
-    if ( isLittle() == 1 )
-    {
-        size_t i = 0;
-        for (i = 0; i < sizeof(int); i++)
-        {
-            buf[i] = nBuf[ sizeof(int) - i - 1];
-        }
-    }
-    else
-    {
-        memcpy(buf, nBuf, sizeof(int));
-    }
-}
-
-// TODO: Add comments to explain implementation.
 int DeriveCryptoKeyFromPassword(const unsigned char *passwd, size_t pLen,
-                                const uint8_t *salt, const size_t saltLen,
-                                const size_t iterations,
-                                const size_t keyLen, uint8_t *derivedKey)
+    const uint8_t *salt, const size_t saltLen,
+    const size_t iterations,
+    const size_t keyLen, uint8_t *derivedKey)
 {
-    int res = 0;
-    uint8_t buf[DTLS_HMAC_DIGEST_SIZE] = {0,};
-    uint8_t uBuf[DTLS_HMAC_DIGEST_SIZE] = {0,};
+    mbedtls_md_context_t sha_ctx;
+    const mbedtls_md_info_t *info_sha;
+    int ret = -1; 
 
-    size_t nBlocks = 0;
-    size_t nOctetInLastBlock = 0;
+    /* Setup the hash/HMAC function, for the PBKDF2 function. */
+    mbedtls_md_init(&sha_ctx);
 
-    nBlocks = (size_t)ceil ((double)keyLen / (double)DTLS_HMAC_DIGEST_SIZE);
-    nOctetInLastBlock = keyLen - (nBlocks - 1) * DTLS_HMAC_DIGEST_SIZE;
-
-    dtls_hmac_context_t *ctx = NULL;
-    ctx = dtls_hmac_new( (const unsigned char *)passwd, pLen);
-    if (NULL == ctx)
+    info_sha = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
+    if (info_sha == NULL)
     {
-        OIC_LOG(ERROR, TAG, "DTLS HMAC Context is NULL");
-        goto bail;
+        OIC_LOG(ERROR, TAG, "Failed to get hash information");
+        return ret;
     }
 
-    size_t i = 1;
-    size_t idx = 0; //index for buffer
-    size_t counter = 0;
-    while (i != nBlocks + 1)
+    ret = mbedtls_md_setup(&sha_ctx, info_sha, 1);
+    if (ret != 0)
     {
-        counter = 0 ;
-        dtls_hmac_init(ctx, (const unsigned char *)passwd, pLen);
-        while (counter != iterations)
-        {
-            if (counter == 0)
-            {
-                uint8_t intBuf[4] = {0x00, 0x00, 0x00, 0x00};
-                dtls_hmac_update(ctx, salt, saltLen);
-                GetBigEndianBuf(intBuf, i);
-                dtls_hmac_update(ctx, intBuf, 4);
-
-                int len = dtls_hmac_finalize(ctx, buf);
-                if (DTLS_HMAC_DIGEST_SIZE != len)
-                {
-                    OIC_LOG(ERROR, TAG, "DTLS HMAC is failed");
-                    res = -1;
-                }
-                memcpy(uBuf, buf, DTLS_HMAC_DIGEST_SIZE);
-            }
-            else
-            {
-                dtls_hmac_init(ctx, (const unsigned char *)passwd, pLen);
-                dtls_hmac_update(ctx, buf, DTLS_HMAC_DIGEST_SIZE);
-                int len = dtls_hmac_finalize(ctx, buf);
-                if (DTLS_HMAC_DIGEST_SIZE != len)
-                {
-                    OIC_LOG(ERROR, TAG, "DTLS HMAC is failed");
-                    res = -1;
-                }
-                XOR_BUF(buf, uBuf, DTLS_HMAC_DIGEST_SIZE);
-            }
-            counter++;
-        }
-
-
-        if (i == nBlocks)
-        {
-            memcpy(derivedKey + idx, uBuf, nOctetInLastBlock);
-        }
-        else
-        {
-            memcpy(derivedKey + idx, uBuf, DTLS_HMAC_DIGEST_SIZE);
-            idx += DTLS_HMAC_DIGEST_SIZE;
-        }
-        i++;
+        OIC_LOG(ERROR, TAG, "Failed to setup hash function");
+        return ret;
     }
 
-bail:
-    dtls_hmac_free(ctx);
-    return res;
-}
+    ret = mbedtls_pkcs5_pbkdf2_hmac(&sha_ctx, passwd, pLen, salt, saltLen, iterations, keyLen, derivedKey);
+    if (ret != 0)
+    {
+        OIC_LOG(ERROR, TAG, "Call to mbedtls PBKDF2 function failed");    
+    }
 
+    mbedtls_md_free(&sha_ctx);
+    return ret;
+}
\ No newline at end of file
index cf846af..76d6835 100644 (file)
@@ -95,6 +95,7 @@ unittest = srmtest_env.Program('unittest', ['aclresourcetest.cpp',
                                             'srmutility.cpp',
                                             'iotvticalendartest.cpp',
                                             'base64tests.cpp',
+                                            'pbkdf2tests.cpp',
                                             'svcresourcetest.cpp',
                                             'srmtestcommon.cpp',
                                             'directpairingtest.cpp',
diff --git a/resource/csdk/security/unittest/pbkdf2tests.cpp b/resource/csdk/security/unittest/pbkdf2tests.cpp
new file mode 100644 (file)
index 0000000..b54de2d
--- /dev/null
@@ -0,0 +1,218 @@
+ /******************************************************************
+  *
+  * Copyright 2016 Microsoft Corporation
+  *
+  *
+  *
+  * 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 "gtest/gtest.h"
+#include "pbkdf2.h"
+#include <stdlib.h>
+#include <stdint.h>
+
+/*
+ * Tests for the PBKDF2 implementation. 
+ * There is a disabled test here that generates test vectors. Test vectors 
+ * for PBKDF2-HMAC-SHA256 are not generally available, so we created our own to
+ * ensure consistency when changing the PBKDF2 implementation (initially based 
+ * on tiny DTLS, now moving to mbedTLS).
+ */
+
+static void print_buffer(const char* label, const uint8_t* buffer, size_t len)
+{
+    printf("%s:", label);
+
+    if (len == 0)
+    {
+        printf(" NULL\n");
+        return;
+    }
+    
+    printf("{");
+    for (size_t i = 0; i < len - 1; i++)
+    {
+        printf("0x%02X, ", buffer[i]);
+    }
+    printf("0x%02X}\n", buffer[len-1]);
+
+}
+
+static void print_vector(const char* label, 
+    const unsigned char* passwd, size_t pLen,
+    const uint8_t* salt, const size_t saltLen,
+    const size_t iterations,
+    const size_t keyLen, uint8_t* derivedKey)
+{
+    printf("%s\n", label);
+    print_buffer("Password", passwd, pLen);
+    print_buffer("Salt", salt, saltLen);
+    printf("Iterations: %zu\n", iterations);
+    print_buffer("Derived Key", derivedKey, keyLen);
+}
+
+TEST(PBKDF2Tests, DISABLED_CreateTestVectors)
+{
+    unsigned char passwd[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
+    uint8_t salt[16] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
+    uint8_t derivedKey[16];
+
+    /* Create and output test vector 1 */
+    int ret = DeriveCryptoKeyFromPassword(passwd, sizeof(passwd),
+        salt, sizeof(salt),
+        PBKDF_ITERATIONS,
+        sizeof(derivedKey), derivedKey);
+
+    EXPECT_EQ(ret, 0);
+    print_vector("PBKDF2 Test Vector 1:", passwd, sizeof(passwd), salt, sizeof(salt), PBKDF_ITERATIONS, sizeof(derivedKey), derivedKey);
+    printf("\n");
+
+    /* Create and output test vector 2 */
+    ret = DeriveCryptoKeyFromPassword(passwd, 1, salt, 1, 1, 4, derivedKey);
+    EXPECT_EQ(ret, 0);
+    print_vector("PBKDF2 Test Vector 2:", passwd, 1, salt, 1, 1, 4, derivedKey);
+    printf("\n");
+
+    /* Create and output test vector 3 */
+    uint8_t lgDerivedKey[65];   /* two SHA-256 blocks + one byte */
+    ret = DeriveCryptoKeyFromPassword(passwd, sizeof(passwd), salt, sizeof(salt), PBKDF_ITERATIONS, sizeof(lgDerivedKey), lgDerivedKey);
+    EXPECT_EQ(ret, 0);
+    print_vector("PBKDF2 Test Vector 3:", passwd, sizeof(passwd), salt, sizeof(salt), PBKDF_ITERATIONS, sizeof(lgDerivedKey), lgDerivedKey);
+    printf("\n");
+
+    /* Create and output test vector 4 */
+    uint8_t lgSalt[48];
+    memset(lgSalt, 0x42, sizeof(lgSalt));
+    ret = DeriveCryptoKeyFromPassword(passwd, sizeof(passwd), lgSalt, sizeof(lgSalt), PBKDF_ITERATIONS, sizeof(derivedKey), derivedKey);
+    EXPECT_EQ(ret, 0);
+    print_vector("PBKDF2 Test Vector 4:", passwd, sizeof(passwd), lgSalt, sizeof(lgSalt), PBKDF_ITERATIONS, sizeof(derivedKey), derivedKey);
+    printf("\n");
+
+    /* Create and output test vector 5 */
+    ret = DeriveCryptoKeyFromPassword(passwd, sizeof(passwd), NULL, 0, PBKDF_ITERATIONS, sizeof(derivedKey), derivedKey);
+    EXPECT_EQ(ret, 0);
+    print_vector("PBKDF2 Test Vector 5:", passwd, sizeof(passwd), NULL, 0, PBKDF_ITERATIONS, sizeof(derivedKey), derivedKey);
+    printf("\n");
+
+    /* Create and output test vector 6 */
+    uint8_t lgPasswd[48];
+    memset(lgPasswd, 0x42, sizeof(lgPasswd));
+    ret = DeriveCryptoKeyFromPassword(lgPasswd, sizeof(lgPasswd), salt, sizeof(salt), PBKDF_ITERATIONS, sizeof(derivedKey), derivedKey);
+    EXPECT_EQ(ret, 0);
+    print_vector("PBKDF2 Test Vector 6:", lgPasswd, sizeof(lgPasswd), salt, sizeof(salt), PBKDF_ITERATIONS, sizeof(derivedKey), derivedKey);
+    printf("\n");
+}
+
+TEST(PBKDF2Tests, TestVector1)
+{
+    unsigned char passwd[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 };\r
+    uint8_t salt[16] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00 };\r
+    size_t iterations = 1000;\r
+    uint8_t derivedKey[16] = { 0xB2, 0x1A, 0xA9, 0x99, 0x87, 0x40, 0xC1, 0x9E, 0x55, 0xD7, 0xFD, 0xED, 0xB2, 0x5C, 0xCE, 0x75 };
+    uint8_t reDerivedKey[16] = {};
+
+    int ret = DeriveCryptoKeyFromPassword(passwd, sizeof(passwd),
+        salt, sizeof(salt),
+        iterations,
+        sizeof(reDerivedKey), reDerivedKey);
+
+    EXPECT_EQ(ret, 0);
+    EXPECT_EQ(memcmp(derivedKey, reDerivedKey, sizeof(derivedKey)), 0);
+}
+
+TEST(PBKDF2Tests, TestVector2)
+{\r
+    unsigned char passwd[1] = { 0x31 };\r
+    uint8_t salt[1] = { 0x01 };\r
+    size_t iterations = 1;\r
+    uint8_t derivedKey[4] = { 0x20, 0xA3, 0x2A, 0x13 };
+    uint8_t reDerivedKey[4] = {};
+
+    int ret = DeriveCryptoKeyFromPassword(passwd, sizeof(passwd),
+        salt, sizeof(salt),
+        iterations,
+        sizeof(reDerivedKey), reDerivedKey);
+
+    EXPECT_EQ(ret, 0);
+    EXPECT_EQ(memcmp(derivedKey, reDerivedKey, sizeof(derivedKey)), 0);
+}
+
+
+TEST(PBKDF2Tests, TestVector3)
+{
+    unsigned char passwd[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 };\r
+    uint8_t salt[16] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00 };\r
+    size_t iterations = 1000;\r
+    uint8_t derivedKey[65] = { 0xB2, 0x1A, 0xA9, 0x99, 0x87, 0x40, 0xC1, 0x9E, 0x55, 0xD7, 0xFD, 0xED, 0xB2, 0x5C, 0xCE, 0x75, 0x2E, 0xA1, 0xA0, 0x43, 0x71, 0x0F, 0xE7, 0xD0, 0xF4, 0xF6, 0x79, 0x01, 0xB5, 0x47, 0x72, 0x20, 0xC0, 0x14, 0x25, 0x5E, 0x4B, 0xA7, 0x40, 0xEC, 0x6A, 0x29, 0x55, 0x0B, 0xDC, 0x2C, 0x10, 0x87, 0xD3, 0xE7, 0x4F, 0xF7, 0xD8, 0x55, 0x64, 0x95, 0xA8, 0x63, 0x6E, 0x64, 0x0E, 0x63, 0xE1, 0x8E, 0xC9 };
+    uint8_t reDerivedKey[65] = {};
+
+    int ret = DeriveCryptoKeyFromPassword(passwd, sizeof(passwd),
+        salt, sizeof(salt),
+        iterations,
+        sizeof(reDerivedKey), reDerivedKey);
+
+    EXPECT_EQ(ret, 0);
+    EXPECT_EQ(memcmp(derivedKey, reDerivedKey, sizeof(derivedKey)), 0);
+}
+
+TEST(PBKDF2Tests, TestVector4)
+{
+    unsigned char passwd[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 };\r
+    uint8_t salt[48] = { 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42 };\r
+    size_t iterations = 1000;\r
+    uint8_t derivedKey[16] = { 0x14, 0x01, 0x5B, 0x8F, 0x23, 0x00, 0x57, 0x4F, 0x48, 0x7B, 0x0A, 0x85, 0x79, 0xFD, 0x3C, 0x31 };
+    uint8_t reDerivedKey[16] = {};
+
+    int ret = DeriveCryptoKeyFromPassword(passwd, sizeof(passwd),
+        salt, sizeof(salt),
+        iterations,
+        sizeof(reDerivedKey), reDerivedKey);
+
+    EXPECT_EQ(ret, 0);
+    EXPECT_EQ(memcmp(derivedKey, reDerivedKey, sizeof(derivedKey)), 0);
+}
+
+TEST(PBKDF2Tests, TestVector5)
+{
+    unsigned char passwd[8] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 };\r
+    size_t iterations = 1000;\r
+    uint8_t derivedKey[16] = { 0x8D, 0x9B, 0x3E, 0x30, 0xED, 0x2C, 0x67, 0x8A, 0x58, 0x1A, 0x2D, 0x40, 0x7A, 0x47, 0xB4, 0x05 };
+    uint8_t reDerivedKey[16] = {};
+
+    int ret = DeriveCryptoKeyFromPassword(passwd, sizeof(passwd),
+        NULL, 0,
+        iterations,
+        sizeof(reDerivedKey), reDerivedKey);
+
+    EXPECT_EQ(ret, 0);
+    EXPECT_EQ(memcmp(derivedKey, reDerivedKey, sizeof(derivedKey)), 0);
+}
+
+TEST(PBKDF2Tests, TestVector6)
+{
+    unsigned char passwd[48] = { 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42 };\r
+    uint8_t salt[16] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00 };\r
+    size_t iterations = 1000;\r
+    uint8_t derivedKey[16] = { 0x03, 0xB4, 0x80, 0xEE, 0x1B, 0x2D, 0x55, 0xA8, 0x90, 0x88, 0xB5, 0xCE, 0x9F, 0x1C, 0x4E, 0x40 };
+    uint8_t reDerivedKey[sizeof(derivedKey)] = {};
+
+    int ret = DeriveCryptoKeyFromPassword(passwd, sizeof(passwd),
+        salt, sizeof(salt),
+        iterations,
+        sizeof(reDerivedKey), reDerivedKey);
+
+    EXPECT_EQ(ret, 0);
+    EXPECT_EQ(memcmp(derivedKey, reDerivedKey, sizeof(derivedKey)), 0);
+}
\ No newline at end of file