From 2299784957935bcd59fe74602e0912d2c83b2ef5 Mon Sep 17 00:00:00 2001 From: Evgeny Margolis Date: Tue, 8 Mar 2022 15:09:54 -0800 Subject: [PATCH] Added New BytesToHex() Helper Functions. (#15947) Using these new functions in the CHIP Cert encoding/decoding mothods. --- src/credentials/CHIPCert.cpp | 9 +- src/credentials/CHIPCertFromX509.cpp | 73 +++++----------- src/lib/dnssd/ServiceNaming.cpp | 4 +- src/lib/support/BytesToHex.cpp | 102 ++++++++++++++++------- src/lib/support/BytesToHex.h | 65 +++++++++++---- src/lib/support/tests/TestBytesToHex.cpp | 102 ++++++++++++++++++++--- 6 files changed, 242 insertions(+), 113 deletions(-) diff --git a/src/credentials/CHIPCert.cpp b/src/credentials/CHIPCert.cpp index a741edcd1a50e8..ba802d41273fd2 100644 --- a/src/credentials/CHIPCert.cpp +++ b/src/credentials/CHIPCert.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -787,20 +788,22 @@ CHIP_ERROR ChipDN::EncodeToASN1(ASN1Writer & writer) const { ASN1_START_SET { - char chipAttrStr[kChip64bitAttrUTF8Length + 1]; + char chipAttrStr[kChip64bitAttrUTF8Length]; CharSpan asn1Attr; uint8_t asn1Tag; chip::ASN1::OID attrOID = rdn[i].mAttrOID; if (IsChip64bitDNAttr(attrOID)) { - snprintf(chipAttrStr, sizeof(chipAttrStr), ChipLogFormatX64, ChipLogValueX64(rdn[i].mChipVal)); + ReturnErrorOnFailure( + Encoding::Uint64ToHex(rdn[i].mChipVal, chipAttrStr, sizeof(chipAttrStr), Encoding::HexFlags::kUppercase)); asn1Attr = CharSpan(chipAttrStr, kChip64bitAttrUTF8Length); asn1Tag = kASN1UniversalTag_UTF8String; } else if (IsChip32bitDNAttr(attrOID)) { - snprintf(chipAttrStr, sizeof(chipAttrStr), "%08" PRIX32, static_cast(rdn[i].mChipVal)); + ReturnErrorOnFailure(Encoding::Uint32ToHex(static_cast(rdn[i].mChipVal), chipAttrStr, + sizeof(chipAttrStr), Encoding::HexFlags::kUppercase)); asn1Attr = CharSpan(chipAttrStr, kChip32bitAttrUTF8Length); asn1Tag = kASN1UniversalTag_UTF8String; } diff --git a/src/credentials/CHIPCertFromX509.cpp b/src/credentials/CHIPCertFromX509.cpp index 0ddacb40e2cfc5..31c64e3ef7c36c 100644 --- a/src/credentials/CHIPCertFromX509.cpp +++ b/src/credentials/CHIPCertFromX509.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -49,40 +50,6 @@ using namespace chip::TLV; using namespace chip::Protocols; using namespace chip::Crypto; -static CHIP_ERROR ParseChipAttribute(ASN1Reader & reader, uint64_t & chipAttrOut) -{ - CHIP_ERROR err = CHIP_NO_ERROR; - const uint8_t * value = reader.GetValue(); - uint32_t valueLen = reader.GetValueLen(); - - chipAttrOut = 0; - - VerifyOrExit(value != nullptr, err = ASN1_ERROR_INVALID_ENCODING); - VerifyOrExit(valueLen == kChip32bitAttrUTF8Length || valueLen == kChip64bitAttrUTF8Length, err = ASN1_ERROR_INVALID_ENCODING); - - for (uint32_t i = 0; i < valueLen; i++) - { - chipAttrOut <<= 4; - uint8_t ch = value[i]; - if (ch >= '0' && ch <= '9') - { - chipAttrOut |= (ch - '0'); - } - // CHIP Id attribute encodings only support uppercase chars. - else if (ch >= 'A' && ch <= 'F') - { - chipAttrOut |= (ch - 'A' + 10); - } - else - { - ExitNow(err = ASN1_ERROR_INVALID_ENCODING); - } - } - -exit: - return err; -} - static CHIP_ERROR ConvertDistinguishedName(ASN1Reader & reader, TLVWriter & writer, Tag tag, uint64_t & subjectOrIssuer, Optional & fabric) { @@ -133,13 +100,14 @@ static CHIP_ERROR ConvertDistinguishedName(ASN1Reader & reader, TLVWriter & writ tlvTagNum |= 0x80; } - // If the attribute is a CHIP-defined attribute that contains a 64-bit or 32-bit value. - if (IsChipDNAttr(attrOID)) + // If 64-bit CHIP attribute. + if (IsChip64bitDNAttr(attrOID)) { - // Parse the attribute string into a CHIP attribute. uint64_t chipAttr; - err = ParseChipAttribute(reader, chipAttr); - SuccessOrExit(err); + VerifyOrReturnError(Encoding::UppercaseHexToUint64(reinterpret_cast(reader.GetValue()), + static_cast(reader.GetValueLen()), + chipAttr) == sizeof(uint64_t), + err = ASN1_ERROR_INVALID_ENCODING); // Certificates use a combination of OIDs for Issuer and Subject. // NOC: Issuer = kOID_AttributeType_ChipRootId or kOID_AttributeType_ChipICAId @@ -167,23 +135,26 @@ static CHIP_ERROR ConvertDistinguishedName(ASN1Reader & reader, TLVWriter & writ VerifyOrReturnError(IsValidFabricId(chipAttr), CHIP_ERROR_WRONG_CERT_DN); fabric.SetValue(chipAttr); } - else if (attrOID == chip::ASN1::kOID_AttributeType_ChipCASEAuthenticatedTag) - { - VerifyOrReturnError(CanCastTo(chipAttr), CHIP_ERROR_WRONG_CERT_DN); - VerifyOrReturnError(IsValidCASEAuthTag(static_cast(chipAttr)), CHIP_ERROR_WRONG_CERT_DN); - } - // Write the CHIP attribute value into the TLV. - err = writer.Put(ContextTag(tlvTagNum), chipAttr); - SuccessOrExit(err); + ReturnErrorOnFailure(writer.Put(ContextTag(tlvTagNum), chipAttr)); } + // If 32-bit CHIP attribute. + else if (IsChip32bitDNAttr(attrOID)) + { + CASEAuthTag chipAttr; + VerifyOrReturnError(Encoding::UppercaseHexToUint32(reinterpret_cast(reader.GetValue()), + reader.GetValueLen(), chipAttr) == sizeof(CASEAuthTag), + err = ASN1_ERROR_INVALID_ENCODING); - // + VerifyOrReturnError(IsValidCASEAuthTag(chipAttr), CHIP_ERROR_WRONG_CERT_DN); + + ReturnErrorOnFailure(writer.Put(ContextTag(tlvTagNum), chipAttr)); + } + // Otherwise, it is a string. else { - err = - writer.PutString(ContextTag(tlvTagNum), Uint8::to_const_char(reader.GetValue()), reader.GetValueLen()); - SuccessOrExit(err); + ReturnErrorOnFailure( + writer.PutString(ContextTag(tlvTagNum), Uint8::to_const_char(reader.GetValue()), reader.GetValueLen())); } } ASN1_EXIT_SEQUENCE; diff --git a/src/lib/dnssd/ServiceNaming.cpp b/src/lib/dnssd/ServiceNaming.cpp index f0c43e77e6c487..e1a2c8c6d9df95 100644 --- a/src/lib/dnssd/ServiceNaming.cpp +++ b/src/lib/dnssd/ServiceNaming.cpp @@ -136,8 +136,8 @@ CHIP_ERROR MakeServiceSubtype(char * buffer, size_t bufferLen, DiscoveryFilter s break; case DiscoveryFilterType::kCompressedFabricId: requiredSize = snprintf(buffer, bufferLen, "_I"); - return Encoding::BytesToHex(subtype.code, &buffer[requiredSize], bufferLen - requiredSize, - Encoding::HexFlags::kUppercaseAndNullTerminate); + return Encoding::Uint64ToHex(subtype.code, &buffer[requiredSize], bufferLen - requiredSize, + Encoding::HexFlags::kUppercaseAndNullTerminate); break; case DiscoveryFilterType::kInstanceName: requiredSize = snprintf(buffer, bufferLen, "%s", subtype.instanceName); diff --git a/src/lib/support/BytesToHex.cpp b/src/lib/support/BytesToHex.cpp index b795c877606ff2..21b8b9a123bc8a 100644 --- a/src/lib/support/BytesToHex.cpp +++ b/src/lib/support/BytesToHex.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2021-2022 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ */ #include "BytesToHex.h" +#include +#include #include #include @@ -38,7 +40,7 @@ char NibbleToHex(uint8_t nibble, bool uppercase) } } -CHIP_ERROR MakeU8FromAsciiHex(const char * src, const size_t srcLen, uint8_t * val) +CHIP_ERROR MakeU8FromAsciiHex(const char * src, const size_t srcLen, uint8_t * val, BitFlags flags) { if (srcLen != 2) { @@ -54,12 +56,12 @@ CHIP_ERROR MakeU8FromAsciiHex(const char * src, const size_t srcLen, uint8_t * v { ret = static_cast(ret + cval - static_cast('0')); } - // Only uppercase is supported according to spec. else if (c >= 'A' && c <= 'F') { ret = static_cast(ret + cval - static_cast('A') + 0xA); } - else if (c >= 'a' && c <= 'f') + // If kUppercase flag is not set then lowercase are also allowed. + else if (!flags.Has(HexFlags::kUppercase) && c >= 'a' && c <= 'f') { ret = static_cast(ret + cval - static_cast('a') + 0xA); } @@ -72,6 +74,28 @@ CHIP_ERROR MakeU8FromAsciiHex(const char * src, const size_t srcLen, uint8_t * v return CHIP_NO_ERROR; } +size_t HexToBytes(const char * src_hex, const size_t src_size, uint8_t * dest_bytes, size_t dest_size_max, BitFlags flags) +{ + if ((src_hex == nullptr) || (dest_bytes == nullptr)) + { + return 0; + } + // Octet string where each octet is 2 ascii digits representing the hex value + // Each is represented by two ascii chars, so must be even number + if ((src_size & 0x1) != 0 || src_size > dest_size_max * 2) + { + return 0; + } + + size_t bytesFilled = 0; + for (size_t i = 0; i < src_size; i += 2) + { + VerifyOrReturnError(MakeU8FromAsciiHex(src_hex + i, 2, &dest_bytes[i / 2], flags) == CHIP_NO_ERROR, 0); + bytesFilled++; + } + return bytesFilled; +} + } // namespace CHIP_ERROR BytesToHex(const uint8_t * src_bytes, size_t src_size, char * dest_hex, size_t dest_size_max, BitFlags flags) @@ -111,44 +135,66 @@ CHIP_ERROR BytesToHex(const uint8_t * src_bytes, size_t src_size, char * dest_he return CHIP_NO_ERROR; } -CHIP_ERROR BytesToHex(uint64_t src, char * dest_hex, size_t dest_size_max, BitFlags flags) +CHIP_ERROR Uint64ToHex(uint64_t src, char * dest_hex, size_t dest_size_max, BitFlags flags) { - uint8_t buf[8]; - for (int i = 7; i >= 0; --i) - { - buf[i] = src & 0xFF; - src = src >> 8; - } + uint8_t buf[sizeof(src)] = { 0 }; + Encoding::BigEndian::Put64(buf, src); + return BytesToHex(buf, sizeof(buf), dest_hex, dest_size_max, flags); +} - return BytesToHex(buf, 8, dest_hex, dest_size_max, flags); +CHIP_ERROR Uint32ToHex(uint32_t src, char * dest_hex, size_t dest_size_max, BitFlags flags) +{ + uint8_t buf[sizeof(src)] = { 0 }; + Encoding::BigEndian::Put32(buf, src); + return BytesToHex(buf, sizeof(buf), dest_hex, dest_size_max, flags); } -size_t HexToBytes(const char * srcHex, const size_t srcLen, uint8_t * destBytes, size_t destMaxLen) +CHIP_ERROR Uint16ToHex(uint16_t src, char * dest_hex, size_t dest_size_max, BitFlags flags) { - if ((srcHex == nullptr) || (destBytes == nullptr)) + uint8_t buf[sizeof(src)] = { 0 }; + Encoding::BigEndian::Put16(buf, src); + return BytesToHex(buf, sizeof(buf), dest_hex, dest_size_max, flags); +} + +size_t HexToBytes(const char * src_hex, const size_t src_size, uint8_t * dest_bytes, size_t dest_size_max) +{ + return HexToBytes(src_hex, src_size, dest_bytes, dest_size_max, HexFlags::kNone); +} + +size_t UppercaseHexToUint64(const char * src_hex, const size_t src_size, uint64_t & dest) +{ + uint8_t buf[sizeof(uint64_t)] = { 0 }; + size_t decoded_size = HexToBytes(src_hex, src_size, buf, sizeof(buf), HexFlags::kUppercase); + if (decoded_size != sizeof(buf)) { return 0; } - // Octet string where each octet is 2 ascii digits representing the hex value - // Each is represented by two ascii chars, so must be even number - if ((srcLen & 0x1) != 0 || srcLen > destMaxLen * 2) + dest = Encoding::BigEndian::Get64(buf); + return decoded_size; +} + +size_t UppercaseHexToUint32(const char * src_hex, const size_t src_size, uint32_t & dest) +{ + uint8_t buf[sizeof(uint32_t)] = { 0 }; + size_t decoded_size = HexToBytes(src_hex, src_size, buf, sizeof(buf), HexFlags::kUppercase); + if (decoded_size != sizeof(buf)) { return 0; } + dest = Encoding::BigEndian::Get32(buf); + return decoded_size; +} - memset(destBytes, 0, destMaxLen); - size_t bytesFilled = 0; - - for (size_t i = 0; i < srcLen; i += 2) +size_t UppercaseHexToUint16(const char * src_hex, const size_t src_size, uint16_t & dest) +{ + uint8_t buf[sizeof(uint16_t)] = { 0 }; + size_t decoded_size = HexToBytes(src_hex, src_size, buf, sizeof(buf), HexFlags::kUppercase); + if (decoded_size != sizeof(buf)) { - if (MakeU8FromAsciiHex(srcHex + i, 2, &destBytes[i / 2]) != CHIP_NO_ERROR) - { - memset(destBytes, 0, destMaxLen); - return 0; - } - bytesFilled++; + return 0; } - return bytesFilled; + dest = Encoding::BigEndian::Get16(buf); + return decoded_size; } } // namespace Encoding diff --git a/src/lib/support/BytesToHex.h b/src/lib/support/BytesToHex.h index b4a92e9e36293f..cbc53a42b7bad0 100644 --- a/src/lib/support/BytesToHex.h +++ b/src/lib/support/BytesToHex.h @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2021-2022 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,7 +40,8 @@ enum class HexFlags : int /** * Encode a buffer of bytes into hexadecimal, with or without null-termination - * and using either lowercase or uppercase hex. + * and using either lowercase or uppercase hex. The input bytes are assumed to be + * in a big-engian order. The output is also in a big-endian order. * * Default is lowercase output, not null-terminated. * @@ -73,8 +74,9 @@ enum class HexFlags : int CHIP_ERROR BytesToHex(const uint8_t * src_bytes, size_t src_size, char * dest_hex, size_t dest_size_max, BitFlags flags); /** - * Encode a uin64_t into hexadecimal, with or without null-termination - * and using either lowercase or uppercase hex. + * Encode a uint64_t into hexadecimal, with or without null-termination + * and using either lowercase or uppercase hex. The output will be in a big-engian + * order. * * Default is lowercase output, not null-terminated. * @@ -98,8 +100,13 @@ CHIP_ERROR BytesToHex(const uint8_t * src_bytes, size_t src_size, char * dest_he * @return CHIP_ERROR_INVALID_ARGUMENT if either src_bytes or dest_hex is nullptr * @return CHIP_NO_ERROR on success */ +CHIP_ERROR Uint64ToHex(uint64_t src, char * dest_hex, size_t dest_size_max, BitFlags flags); -CHIP_ERROR BytesToHex(uint64_t src, char * dest_hex, size_t dest_size_max, BitFlags flags); +/** Same as Uint64ToHex() but for uint32_t. */ +CHIP_ERROR Uint32ToHex(uint32_t src, char * dest_hex, size_t dest_size_max, BitFlags flags); + +/** Same as Uint64ToHex() but for uint16_t. */ +CHIP_ERROR Uint16ToHex(uint16_t src, char * dest_hex, size_t dest_size_max, BitFlags flags); // Alias for Uppercase option, no null-termination inline CHIP_ERROR BytesToUppercaseHexBuffer(const uint8_t * src_bytes, size_t src_size, char * dest_hex_buf, size_t dest_size_max) @@ -126,23 +133,47 @@ inline CHIP_ERROR BytesToLowercaseHexString(const uint8_t * src_bytes, size_t sr } /** - * Convert a buffer of hexadecimal characters to bytes. Supports both lowercase + * Convert a buffer of hexadecimal characters to bytes. Supports both lowercase * and uppercase (or a mix of cases) hexadecimal characters. Supported input is - * [0-9a-fA-F]. + * [0-9a-fA-F]. The input is assumed to be in a big-endian order. The output is + * also in a big-endian order. * - * @param srcHex a pointer to the character buffer to convert. It is not - * assumed to be null-terminated. - * @param srcLen the length of the character buffer to convert. - * @param destBytes the buffer to fill with the decoded bytes. - * @param destMaxLen the total size of the buffer to be filled. + * @param src_hex a pointer to the character buffer to convert. It is not + * assumed to be null-terminated. + * @param src_size the number of characters to convert from src_hex. + * @param dest_bytes the buffer to fill with the decoded bytes. + * @param dest_size_max the total size of the buffer to be filled. * * @return 0 on errors: - * - destMaxLen not big enough. - * - srcLen not even. - * - Some character not in [0-9a-fA-F] is present in srcHex. - * number of bytes actually decoded from the string on success. + * - dest_size_max not big enough. + * - src_size not even. + * - Some character not in [0-9a-fA-F] is present in src_hex. + * Otherwise, returns number of bytes actually decoded from the string on success. */ -size_t HexToBytes(const char * srcHex, const size_t srcLen, uint8_t * destBytes, size_t destMaxLen); +size_t HexToBytes(const char * src_hex, const size_t src_size, uint8_t * dest_bytes, size_t dest_size_max); + +/** + * Convert a buffer of hexadecimal characters into uint64_t. Supports only + * uppercase hexadecimal input characters. Supported input is [0-9A-F]. + * The input is assumed to be in a big-endian order. + * + * @param src_hex a pointer to the character buffer to convert. It is not + * assumed to be null-terminated. + * @param src_size the number of characters to convert from src_hex. + * @param dest 64-bit number to output. + * + * @return 0 on errors: + * - src_size not even. + * - Some character not in [0-9A-F] is present in src_hex. + * Otherwise, returns 8 (number of bytes actually decoded from the string) on success. + */ +size_t UppercaseHexToUint64(const char * src_hex, const size_t src_size, uint64_t & dest); + +/** Same as UppercaseHexToUint64() but for uint32_t. */ +size_t UppercaseHexToUint32(const char * src_hex, const size_t src_size, uint32_t & dest); + +/** Same as UppercaseHexToUint64() but for uint16_t. */ +size_t UppercaseHexToUint16(const char * src_hex, const size_t src_size, uint16_t & dest); } // namespace Encoding } // namespace chip diff --git a/src/lib/support/tests/TestBytesToHex.cpp b/src/lib/support/tests/TestBytesToHex.cpp index d84fc6e70f2f83..1cdbdfc9673b4e 100644 --- a/src/lib/support/tests/TestBytesToHex.cpp +++ b/src/lib/support/tests/TestBytesToHex.cpp @@ -1,6 +1,6 @@ /* * - * Copyright (c) 2021 Project CHIP Authors + * Copyright (c) 2021-2022 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -193,41 +193,118 @@ void TestBytesToHexErrors(nlTestSuite * inSuite, void * inContext) void TestBytesToHexUint64(nlTestSuite * inSuite, void * inContext) { // Different values in each byte and each nibble should let us know if the conversion is correct. - uint64_t test = 0x0123456789ABCDEF; + uint64_t test = 0x0123456789ABCDEF; + uint32_t test32_0 = 0x01234567; + uint32_t test32_1 = 0x89ABCDEF; + uint16_t test16_0 = 0x0123; + uint16_t test16_1 = 0x4567; + uint16_t test16_2 = 0x89AB; + uint16_t test16_3 = 0xCDEF; char buf[17]; char upperExpected[] = "0123456789ABCDEF"; char lowerExpected[] = "0123456789abcdef"; - // Lower case. + // Lower case - uint64_t. memset(buf, 1, sizeof(buf)); - NL_TEST_ASSERT(inSuite, BytesToHex(test, buf, sizeof(buf), HexFlags::kNone) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, Uint64ToHex(test, buf, sizeof(buf), HexFlags::kNone) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, memcmp(buf, lowerExpected, strlen(lowerExpected)) == 0); // No null termination. NL_TEST_ASSERT(inSuite, buf[16] == 1); - // Upper case. + // Lower case - uint32_t. memset(buf, 1, sizeof(buf)); - NL_TEST_ASSERT(inSuite, BytesToHex(test, buf, sizeof(buf), HexFlags::kUppercase) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, Uint32ToHex(test32_0, buf, sizeof(uint32_t) * 2, HexFlags::kNone) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, + Uint32ToHex(test32_1, &buf[sizeof(uint32_t) * 2], sizeof(uint32_t) * 2, HexFlags::kNone) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, memcmp(buf, lowerExpected, strlen(lowerExpected)) == 0); + // No null termination. + NL_TEST_ASSERT(inSuite, buf[16] == 1); + + // Upper case - uint64_t. + memset(buf, 1, sizeof(buf)); + NL_TEST_ASSERT(inSuite, Uint64ToHex(test, buf, sizeof(buf), HexFlags::kUppercase) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, memcmp(buf, upperExpected, strlen(upperExpected)) == 0); + // No null termination. + NL_TEST_ASSERT(inSuite, buf[16] == 1); + + // Upper case - uint16_t. + memset(buf, 1, sizeof(buf)); + NL_TEST_ASSERT(inSuite, Uint16ToHex(test16_0, buf, sizeof(uint16_t) * 2, HexFlags::kUppercase) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, + Uint16ToHex(test16_1, &buf[sizeof(uint16_t) * 2 * 1], sizeof(uint16_t) * 2, HexFlags::kUppercase) == + CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, + Uint16ToHex(test16_2, &buf[sizeof(uint16_t) * 2 * 2], sizeof(uint16_t) * 2, HexFlags::kUppercase) == + CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, + Uint16ToHex(test16_3, &buf[sizeof(uint16_t) * 2 * 3], sizeof(uint16_t) * 2, HexFlags::kUppercase) == + CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, memcmp(buf, upperExpected, strlen(upperExpected)) == 0); // No null termination. NL_TEST_ASSERT(inSuite, buf[16] == 1); // Lower case with null termination. memset(buf, 1, sizeof(buf)); - NL_TEST_ASSERT(inSuite, BytesToHex(test, buf, sizeof(buf), HexFlags::kNullTerminate) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, Uint64ToHex(test, buf, sizeof(buf), HexFlags::kNullTerminate) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, memcmp(buf, lowerExpected, sizeof(lowerExpected)) == 0); // Upper case with null termination. memset(buf, 1, sizeof(buf)); - NL_TEST_ASSERT(inSuite, BytesToHex(test, buf, sizeof(buf), HexFlags::kUppercaseAndNullTerminate) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, Uint64ToHex(test, buf, sizeof(buf), HexFlags::kUppercaseAndNullTerminate) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, memcmp(buf, upperExpected, sizeof(upperExpected)) == 0); // Too small buffer - NL_TEST_ASSERT(inSuite, BytesToHex(test, buf, sizeof(buf) - 2, HexFlags::kNone) == CHIP_ERROR_BUFFER_TOO_SMALL); - NL_TEST_ASSERT(inSuite, BytesToHex(test, buf, sizeof(buf) - 2, HexFlags::kUppercase) == CHIP_ERROR_BUFFER_TOO_SMALL); - NL_TEST_ASSERT(inSuite, BytesToHex(test, buf, sizeof(buf) - 1, HexFlags::kNullTerminate) == CHIP_ERROR_BUFFER_TOO_SMALL); + NL_TEST_ASSERT(inSuite, Uint64ToHex(test, buf, sizeof(buf) - 2, HexFlags::kNone) == CHIP_ERROR_BUFFER_TOO_SMALL); + NL_TEST_ASSERT(inSuite, Uint64ToHex(test, buf, sizeof(buf) - 2, HexFlags::kUppercase) == CHIP_ERROR_BUFFER_TOO_SMALL); + NL_TEST_ASSERT(inSuite, Uint64ToHex(test, buf, sizeof(buf) - 1, HexFlags::kNullTerminate) == CHIP_ERROR_BUFFER_TOO_SMALL); NL_TEST_ASSERT(inSuite, - BytesToHex(test, buf, sizeof(buf) - 1, HexFlags::kUppercaseAndNullTerminate) == CHIP_ERROR_BUFFER_TOO_SMALL); + Uint64ToHex(test, buf, sizeof(buf) - 1, HexFlags::kUppercaseAndNullTerminate) == CHIP_ERROR_BUFFER_TOO_SMALL); +} + +void TestHexToBytesAndUint(nlTestSuite * inSuite, void * inContext) +{ + // Different values in each byte and each nibble should let us know if the conversion is correct. + char hexInLowercase[] = "0123456789abcdef"; + char hexInUppercase[] = "0123456789ABCDEF"; + char hexInUppercase32[] = "6789ABCD"; + char hexInUppercase16[] = "D7AB"; + uint8_t bytesOutExpected[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF }; + uint64_t test64OutExpected = 0x0123456789ABCDEF; + uint32_t test32OutExpected = 0x6789ABCD; + uint16_t test16OutExpected = 0xD7AB; + uint64_t test64Out; + uint32_t test32Out; + uint16_t test16Out; + uint8_t buf[8]; + + // Lower case - bytes. + memset(buf, 0, sizeof(buf)); + NL_TEST_ASSERT(inSuite, HexToBytes(hexInLowercase, strlen(hexInLowercase), buf, sizeof(buf)) == sizeof(buf)); + NL_TEST_ASSERT(inSuite, memcmp(buf, bytesOutExpected, sizeof(buf)) == 0); + + // Upper case - bytes. + memset(buf, 0, sizeof(buf)); + NL_TEST_ASSERT(inSuite, HexToBytes(hexInUppercase, strlen(hexInUppercase), buf, sizeof(buf)) == sizeof(buf)); + NL_TEST_ASSERT(inSuite, memcmp(buf, bytesOutExpected, sizeof(buf)) == 0); + + // Lower case - uint64_t. + test64Out = 0; + NL_TEST_ASSERT(inSuite, UppercaseHexToUint64(hexInLowercase, strlen(hexInLowercase), test64Out) == 0); + + // Upper case - uint64_t. + test64Out = 0; + NL_TEST_ASSERT(inSuite, UppercaseHexToUint64(hexInUppercase, strlen(hexInUppercase), test64Out) == sizeof(uint64_t)); + NL_TEST_ASSERT(inSuite, test64Out == test64OutExpected); + + // Upper case - uint32_t. + test32Out = 0; + NL_TEST_ASSERT(inSuite, UppercaseHexToUint32(hexInUppercase32, strlen(hexInUppercase32), test32Out) == sizeof(uint32_t)); + NL_TEST_ASSERT(inSuite, test32Out == test32OutExpected); + + // Upper case - uint16_t. + test16Out = 0; + NL_TEST_ASSERT(inSuite, UppercaseHexToUint16(hexInUppercase16, strlen(hexInUppercase16), test16Out) == sizeof(uint16_t)); + NL_TEST_ASSERT(inSuite, test16Out == test16OutExpected); } const nlTest sTests[] = { @@ -235,6 +312,7 @@ const nlTest sTests[] = { NL_TEST_DEF("TestBytesToHexNullTerminated", TestBytesToHexNullTerminated), // NL_TEST_DEF("TestBytesToHexErrors", TestBytesToHexErrors), // NL_TEST_DEF("TestBytesToHexUint64", TestBytesToHexUint64), // + NL_TEST_DEF("TestHexToBytesAndUint", TestHexToBytesAndUint), // NL_TEST_SENTINEL() // };