| /* |
| * |
| * Copyright (c) 2016-2017 Nest Labs, Inc. |
| * 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. |
| */ |
| |
| /** |
| * @file |
| * This file implements unit tests for the Weave application keys library. |
| * |
| */ |
| |
| #include <stdio.h> |
| #include <nltest.h> |
| #include <string.h> |
| |
| #include "ToolCommon.h" |
| #include "TestGroupKeyStore.h" |
| #include <Weave/Support/ErrorStr.h> |
| #include <Weave/Core/WeaveKeyIds.h> |
| #include <Weave/Core/WeaveEncoding.h> |
| #include <Weave/Profiles/security/WeaveApplicationKeys.h> |
| #include <Weave/Profiles/security/WeavePasscodes.h> |
| #include <Weave/Support/crypto/WeaveCrypto.h> |
| |
| using namespace nl::Weave::Encoding; |
| using namespace nl::Weave::Crypto; |
| using namespace nl::Weave::Platform::Security; |
| using namespace nl::Weave::Profiles::Security::AppKeys; |
| using namespace nl::Weave::Profiles::Security::Passcodes; |
| |
| #define DEBUG_PRINT_ENABLE 0 |
| |
| static const uint8_t sWeavePasscode[] = |
| { |
| 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42 |
| }; |
| |
| static const uint8_t sWeavePaddedPasscode[] = |
| { |
| 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x00, 0x00, 0x00, 0x00 |
| }; |
| |
| static const uint8_t sWeaveEncryptedPasscodeConfig1[] = |
| { |
| 0x01, 0x00, 0x00, 0x00, 0x00, 0xC9, 0x25, 0xA8, 0xF4, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, |
| 0x37, 0x38, 0x39, 0x41, 0x42, 0x00, 0x00, 0x00, 0x00, 0x7A, 0x3E, 0xD3, 0xA4, 0x18, 0x86, 0x25, |
| 0x09, 0xCA, 0x96, 0xF7, 0xC9, 0x05, 0x42, 0x13, 0x43, |
| }; |
| |
| static const uint8_t sWeaveEncryptedPasscodeConfig2_Rotating[] = |
| { |
| 0x02, 0x04, 0x54, 0x00, 0x00, 0xC9, 0x25, 0xA8, 0xF4, 0xC7, 0x0A, 0x3E, 0xBA, 0xDF, 0x33, 0xA1, |
| 0xCE, 0xB4, 0x94, 0xF0, 0xE0, 0xE6, 0x23, 0x98, 0x2F, 0x52, 0xD0, 0xC7, 0xAE, 0xB5, 0x1B, 0xCB, |
| 0x4D, 0xFD, 0x72, 0x77, 0xE7, 0xA6, 0x95, 0xFB, 0xAC, |
| }; |
| |
| static const uint8_t sWeaveEncryptedPasscodeConfig2_Static[] = |
| { |
| 0x02, 0x04, 0x44, 0x00, 0x00, 0xC9, 0x25, 0xA8, 0xF4, 0x3E, 0x8D, 0xA7, 0x68, 0xC7, 0x67, 0x91, |
| 0xF9, 0x16, 0xC3, 0x42, 0x2C, 0x82, 0x26, 0x4B, 0xDE, 0x14, 0x39, 0x2B, 0x38, 0x7B, 0xDA, 0x88, |
| 0xF8, 0xFD, 0x72, 0x77, 0xE7, 0xA6, 0x95, 0xFB, 0xAC, |
| }; |
| |
| static const uint8_t sLongWeavePasscode[] = |
| { |
| 0x5A, 0x59, 0x58, 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B |
| }; |
| |
| |
| void PasscodeEncryptConfig1_Test1(nlTestSuite *inSuite, void *inContext) |
| { |
| WEAVE_ERROR err; |
| const uint8_t config = kPasscode_Config1_TEST_ONLY; |
| uint8_t passcode[kPasscodePaddedLen]; |
| size_t passcodeLen; |
| uint8_t encPasscode[kPasscodeMaxEncryptedLen]; |
| uint8_t encPasscodeManual[kPasscodeMaxEncryptedLen]; |
| size_t encPasscodeLen; |
| |
| // Encrypt passcode. |
| err = EncryptPasscode(config, kPasscodeConfig1_KeyId, sPasscodeEncryptionKeyNonce, |
| sWeavePasscode, sizeof(sWeavePasscode), |
| encPasscode, sizeof(encPasscode), encPasscodeLen, NULL); |
| NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, encPasscodeLen == sizeof(sWeaveEncryptedPasscodeConfig1)); |
| NL_TEST_ASSERT(inSuite, memcmp(encPasscode, sWeaveEncryptedPasscodeConfig1, encPasscodeLen) == 0); |
| |
| #if DEBUG_PRINT_ENABLE |
| printf("Encrypted passcode:\n"); |
| DumpMemoryCStyle(encPasscode, encPasscodeLen, " ", 16); |
| #endif |
| |
| // Decrypt passcode. |
| err = DecryptPasscode(encPasscode, encPasscodeLen, passcode, sizeof(passcode), passcodeLen, NULL); |
| NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, passcodeLen == sizeof(sWeavePasscode)); |
| NL_TEST_ASSERT(inSuite, memcmp(passcode, sWeavePasscode, passcodeLen) == 0); |
| |
| // Use HKDF function manually to encrypt passcode. |
| encPasscodeManual[0] = config; |
| LittleEndian::Put32(encPasscodeManual + 1, kPasscodeConfig1_KeyId); |
| LittleEndian::Put32(encPasscodeManual + 5, sPasscodeEncryptionKeyNonce); |
| |
| // -- copy unencrypted passcode. |
| memcpy(encPasscodeManual + 9, sWeavePaddedPasscode, kPasscodePaddedLen); |
| |
| // -- generate passcode authenticator. |
| nl::Weave::Platform::Security::SHA1 hash; |
| uint8_t digest[SHA1::kHashLength]; |
| hash.Begin(); |
| hash.AddData(encPasscodeManual, sizeof(uint8_t)); |
| hash.AddData(encPasscodeManual + 5, sizeof(uint32_t) + kPasscodePaddedLen); |
| hash.Finish(digest); |
| memcpy(encPasscodeManual + 9 + kPasscodePaddedLen, digest, kPasscodeAuthenticatorLen); |
| |
| // -- generate passcode fingerprint. |
| hash.Begin(); |
| hash.AddData(sWeavePaddedPasscode, kPasscodePaddedLen); |
| hash.Finish(digest); |
| memcpy(encPasscodeManual + 9 + kPasscodePaddedLen + kPasscodeAuthenticatorLen, digest, kPasscodeFingerprintLen); |
| |
| // -- compare the result. |
| NL_TEST_ASSERT(inSuite, memcmp(encPasscode, encPasscodeManual, encPasscodeLen) == 0); |
| |
| #if DEBUG_PRINT_ENABLE |
| printf("Manually generated passcode:\n"); |
| DumpMemoryCStyle(encPasscodeManual, encPasscodeLen, " ", 16); |
| #endif |
| } |
| |
| void PasscodeEncryptConfig2_Test1(nlTestSuite *inSuite, void *inContext) |
| { |
| WEAVE_ERROR err; |
| const uint8_t config = kPasscode_Config2; |
| TestGroupKeyStore keyStore; |
| uint8_t passcode[kPasscodePaddedLen]; |
| size_t passcodeLen; |
| uint8_t encPasscode[kPasscodeMaxEncryptedLen]; |
| uint8_t encPasscodeManual[kPasscodeMaxEncryptedLen]; |
| size_t encPasscodeLen; |
| |
| // Encrypt passcode. |
| err = EncryptPasscode(config, sPasscodeEncRotatingKeyId_CRK_E0_G4, sPasscodeEncryptionKeyNonce, |
| sWeavePasscode, sizeof(sWeavePasscode), |
| encPasscode, sizeof(encPasscode), encPasscodeLen, &keyStore); |
| NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, encPasscodeLen == sizeof(sWeaveEncryptedPasscodeConfig2_Rotating)); |
| NL_TEST_ASSERT(inSuite, memcmp(encPasscode, sWeaveEncryptedPasscodeConfig2_Rotating, encPasscodeLen) == 0); |
| |
| #if DEBUG_PRINT_ENABLE |
| printf("Encrypted passcode:\n"); |
| DumpMemoryCStyle(encPasscode, encPasscodeLen, " ", 16); |
| #endif |
| |
| // Decrypt passcode. |
| err = DecryptPasscode(encPasscode, encPasscodeLen, passcode, sizeof(passcode), passcodeLen, &keyStore); |
| NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, passcodeLen == sizeof(sWeavePasscode)); |
| NL_TEST_ASSERT(inSuite, memcmp(passcode, sWeavePasscode, passcodeLen) == 0); |
| |
| // Use HKDF function manually to encrypt passcode. |
| encPasscodeManual[0] = config; |
| LittleEndian::Put32(encPasscodeManual + 1, sPasscodeEncRotatingKeyId_CRK_E0_G4); |
| LittleEndian::Put32(encPasscodeManual + 5, sPasscodeEncryptionKeyNonce); |
| |
| // -- encrypt passcode. |
| AES128BlockCipherEnc aes128Enc; |
| aes128Enc.SetKey(sPasscodeEncRotatingKey_CRK_E0_G4); |
| aes128Enc.EncryptBlock(sWeavePaddedPasscode, encPasscodeManual + 9); |
| |
| // -- generate passcode authenticator. |
| HMACSHA1 hmac; |
| uint8_t digest[SHA1::kHashLength]; |
| hmac.Begin(sPasscodeEncRotatingKey_CRK_E0_G4 + kPasscodeEncryptionKeyLen, kPasscodeAuthenticationKeyLen); |
| hmac.AddData(encPasscodeManual, sizeof(uint8_t)); |
| hmac.AddData(encPasscodeManual + 5, sizeof(uint32_t) + kPasscodePaddedLen); |
| hmac.Finish(digest); |
| memcpy(encPasscodeManual + 9 + kPasscodePaddedLen, digest, kPasscodeAuthenticatorLen); |
| |
| // -- generate passcode fingerprint. |
| hmac.Begin(sPasscodeFingerprintKey_CRK_G4, kPasscodeFingerprintKeyLen); |
| hmac.AddData(sWeavePaddedPasscode, kPasscodePaddedLen); |
| hmac.Finish(digest); |
| memcpy(encPasscodeManual + 9 + kPasscodePaddedLen + kPasscodeAuthenticatorLen, digest, kPasscodeFingerprintLen); |
| |
| // -- compare the result. |
| NL_TEST_ASSERT(inSuite, memcmp(encPasscode, encPasscodeManual, encPasscodeLen) == 0); |
| |
| #if DEBUG_PRINT_ENABLE |
| printf("Manually generated passcode:\n"); |
| DumpMemoryCStyle(encPasscodeManual, encPasscodeLen, " ", 16); |
| #endif |
| } |
| |
| void PasscodeEncryptConfig2_Test2(nlTestSuite *inSuite, void *inContext) |
| { |
| WEAVE_ERROR err; |
| const uint8_t config = kPasscode_Config2; |
| TestGroupKeyStore keyStore; |
| uint8_t passcode[kPasscodePaddedLen]; |
| size_t passcodeLen; |
| uint8_t encPasscode[kPasscodeMaxEncryptedLen]; |
| uint8_t encPasscodeManual[kPasscodeMaxEncryptedLen]; |
| size_t encPasscodeLen; |
| |
| // Encrypt passcode. |
| err = EncryptPasscode(config, sPasscodeEncStaticKeyId_CRK_G4, sPasscodeEncryptionKeyNonce, |
| sWeavePasscode, sizeof(sWeavePasscode), |
| encPasscode, sizeof(encPasscode), encPasscodeLen, &keyStore); |
| NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, encPasscodeLen == sizeof(sWeaveEncryptedPasscodeConfig2_Static)); |
| NL_TEST_ASSERT(inSuite, memcmp(encPasscode, sWeaveEncryptedPasscodeConfig2_Static, encPasscodeLen) == 0); |
| |
| #if DEBUG_PRINT_ENABLE |
| printf("Encrypted passcode:\n"); |
| DumpMemoryCStyle(encPasscode, encPasscodeLen, " ", 16); |
| #endif |
| |
| // Decrypt passcode. |
| err = DecryptPasscode(encPasscode, encPasscodeLen, passcode, sizeof(passcode), passcodeLen, &keyStore); |
| NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, passcodeLen == sizeof(sWeavePasscode)); |
| NL_TEST_ASSERT(inSuite, memcmp(passcode, sWeavePasscode, passcodeLen) == 0); |
| |
| // Use HKDF function manually to encrypt passcode. |
| encPasscodeManual[0] = config; |
| LittleEndian::Put32(encPasscodeManual + 1, sPasscodeEncStaticKeyId_CRK_G4); |
| LittleEndian::Put32(encPasscodeManual + 5, sPasscodeEncryptionKeyNonce); |
| |
| // -- encrypt passcode. |
| AES128BlockCipherEnc aes128Enc; |
| aes128Enc.SetKey(sPasscodeEncStaticKey_CRK_G4); |
| aes128Enc.EncryptBlock(sWeavePaddedPasscode, encPasscodeManual + 9); |
| |
| // -- generate passcode authenticator. |
| HMACSHA1 hmac; |
| uint8_t digest[SHA1::kHashLength]; |
| hmac.Begin(sPasscodeEncStaticKey_CRK_G4 + kPasscodeEncryptionKeyLen, kPasscodeAuthenticationKeyLen); |
| hmac.AddData(encPasscodeManual, sizeof(uint8_t)); |
| hmac.AddData(encPasscodeManual + 5, sizeof(uint32_t) + kPasscodePaddedLen); |
| hmac.Finish(digest); |
| memcpy(encPasscodeManual + 9 + kPasscodePaddedLen, digest, kPasscodeAuthenticatorLen); |
| |
| // -- generate passcode fingerprint. |
| hmac.Begin(sPasscodeFingerprintKey_CRK_G4, kPasscodeFingerprintKeyLen); |
| hmac.AddData(sWeavePaddedPasscode, kPasscodePaddedLen); |
| hmac.Finish(digest); |
| memcpy(encPasscodeManual + 9 + kPasscodePaddedLen + kPasscodeAuthenticatorLen, digest, kPasscodeFingerprintLen); |
| |
| // -- compare the result. |
| NL_TEST_ASSERT(inSuite, memcmp(encPasscode, encPasscodeManual, encPasscodeLen) == 0); |
| |
| #if DEBUG_PRINT_ENABLE |
| printf("Manually generated passcode:\n"); |
| DumpMemoryCStyle(encPasscodeManual, encPasscodeLen, " ", 16); |
| #endif |
| } |
| |
| void PasscodeEncryptConfig2_LongPasscodeTest(nlTestSuite *inSuite, void *inContext) |
| { |
| WEAVE_ERROR err; |
| const uint8_t config = kPasscode_Config2; |
| TestGroupKeyStore keyStore; |
| uint8_t passcode[kPasscodePaddedLen]; |
| size_t passcodeLen; |
| uint8_t encPasscode[kPasscodeMaxEncryptedLen]; |
| size_t encPasscodeLen; |
| |
| // Encrypt passcode. |
| err = EncryptPasscode(config, sPasscodeEncStaticKeyId_CRK_G4, sPasscodeEncryptionKeyNonce, |
| sLongWeavePasscode, sizeof(sLongWeavePasscode), |
| encPasscode, sizeof(encPasscode), encPasscodeLen, &keyStore); |
| NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR); |
| |
| // Decrypt passcode. |
| err = DecryptPasscode(encPasscode, encPasscodeLen, passcode, sizeof(passcode), passcodeLen, &keyStore); |
| NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, passcodeLen == sizeof(sLongWeavePasscode)); |
| NL_TEST_ASSERT(inSuite, memcmp(passcode, sLongWeavePasscode, passcodeLen) == 0); |
| } |
| |
| void EncryptedPasscodeUtils_Test1(nlTestSuite *inSuite, void *inContext) |
| { |
| WEAVE_ERROR err; |
| uint8_t config; |
| uint32_t keyId, nonce; |
| uint8_t fingerprint[kPasscodeFingerprintLen]; |
| size_t fingerprintLen; |
| |
| err = GetEncryptedPasscodeConfig(sWeaveEncryptedPasscodeConfig1, sizeof(sWeaveEncryptedPasscodeConfig1), config); |
| NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, config == kPasscode_Config1_TEST_ONLY); |
| |
| err = GetEncryptedPasscodeConfig(sWeaveEncryptedPasscodeConfig2_Static, sizeof(sWeaveEncryptedPasscodeConfig2_Static), config); |
| NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, config == kPasscode_Config2); |
| |
| err = GetEncryptedPasscodeKeyId(sWeaveEncryptedPasscodeConfig2_Rotating, sizeof(sWeaveEncryptedPasscodeConfig2_Rotating), keyId); |
| NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, keyId == sPasscodeEncRotatingKeyId_CRK_E0_G4); |
| |
| err = GetEncryptedPasscodeNonce(sWeaveEncryptedPasscodeConfig2_Rotating, sizeof(sWeaveEncryptedPasscodeConfig2_Rotating), nonce); |
| NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, nonce == sPasscodeEncryptionKeyNonce); |
| |
| err = GetEncryptedPasscodeFingerprint(sWeaveEncryptedPasscodeConfig2_Rotating, sizeof(sWeaveEncryptedPasscodeConfig2_Rotating), fingerprint, sizeof(fingerprint), fingerprintLen); |
| NL_TEST_ASSERT(inSuite, err == WEAVE_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, fingerprintLen == kPasscodeFingerprintLen); |
| const uint8_t *expectedFingerprint = sWeaveEncryptedPasscodeConfig2_Rotating + sizeof(sWeaveEncryptedPasscodeConfig2_Rotating) - kPasscodeFingerprintLen; |
| NL_TEST_ASSERT(inSuite, memcmp(fingerprint, expectedFingerprint, fingerprintLen) == 0); |
| |
| #if WEAVE_CONFIG_SUPPORT_PASSCODE_CONFIG1_TEST_ONLY |
| NL_TEST_ASSERT(inSuite, true == IsSupportedPasscodeEncryptionConfig(kPasscode_Config1_TEST_ONLY)); |
| #endif |
| NL_TEST_ASSERT(inSuite, true == IsSupportedPasscodeEncryptionConfig(kPasscode_Config2)); |
| NL_TEST_ASSERT(inSuite, false == IsSupportedPasscodeEncryptionConfig(0xFF)); |
| } |
| |
| |
| int main(int argc, char *argv[]) |
| { |
| static const nlTest tests[] = { |
| #if WEAVE_CONFIG_SUPPORT_PASSCODE_CONFIG1_TEST_ONLY |
| NL_TEST_DEF("PasscodeEncryptConfig1_Test1", PasscodeEncryptConfig1_Test1), |
| #endif |
| #if WEAVE_CONFIG_SUPPORT_PASSCODE_CONFIG2 |
| NL_TEST_DEF("PasscodeEncryptConfig2_Test1", PasscodeEncryptConfig2_Test1), |
| NL_TEST_DEF("PasscodeEncryptConfig2_Test2", PasscodeEncryptConfig2_Test2), |
| NL_TEST_DEF("PasscodeEncryptConfig2_LongPasscodeTest", PasscodeEncryptConfig2_LongPasscodeTest), |
| #endif |
| NL_TEST_DEF("EncryptedPasscodeUtils_Test1", EncryptedPasscodeUtils_Test1), |
| NL_TEST_SENTINEL() |
| }; |
| |
| static nlTestSuite testSuite = { |
| "passcode-encryption", |
| &tests[0] |
| }; |
| |
| nl_test_set_output_style(OUTPUT_CSV); |
| |
| nlTestRunner(&testSuite, NULL); |
| |
| return nlTestRunnerStats(&testSuite); |
| } |