Skip to content

Commit

Permalink
Added New BytesToHex() Helper Functions.
Browse files Browse the repository at this point in the history
Using these new functions in the CHIP Cert encoding/decoding mothods.
  • Loading branch information
emargolis committed Mar 8, 2022
1 parent 2822511 commit cfdd6e8
Show file tree
Hide file tree
Showing 5 changed files with 313 additions and 99 deletions.
9 changes: 6 additions & 3 deletions src/credentials/CHIPCert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include <lib/core/CHIPCore.h>
#include <lib/core/CHIPSafeCasts.h>
#include <lib/core/CHIPTLV.h>
#include <lib/support/BytesToHex.h>
#include <lib/support/CHIPMem.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/SafeInt.h>
Expand Down Expand Up @@ -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::BytesToHex(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<uint32_t>(rdn[i].mChipVal));
ReturnErrorOnFailure(Encoding::BytesToHex(static_cast<uint32_t>(rdn[i].mChipVal), chipAttrStr,
sizeof(chipAttrStr), Encoding::HexFlags::kUppercase));
asn1Attr = CharSpan(chipAttrStr, kChip32bitAttrUTF8Length);
asn1Tag = kASN1UniversalTag_UTF8String;
}
Expand Down
73 changes: 22 additions & 51 deletions src/credentials/CHIPCertFromX509.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include <lib/core/CHIPSafeCasts.h>
#include <lib/core/CHIPTLV.h>
#include <lib/core/Optional.h>
#include <lib/support/BytesToHex.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/SafeInt.h>
#include <protocols/Protocols.h>
Expand All @@ -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<uint64_t> & fabric)
{
Expand Down Expand Up @@ -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::UppercaseHexToBytes(reinterpret_cast<const char *>(reader.GetValue()),
static_cast<size_t>(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
Expand Down Expand Up @@ -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<CASEAuthTag>(chipAttr), CHIP_ERROR_WRONG_CERT_DN);
VerifyOrReturnError(IsValidCASEAuthTag(static_cast<CASEAuthTag>(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::UppercaseHexToBytes(reinterpret_cast<const char *>(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;
Expand Down
105 changes: 78 additions & 27 deletions src/lib/support/BytesToHex.cpp
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -16,6 +16,7 @@
*/

#include "BytesToHex.h"
#include <lib/core/CHIPEncoding.h>

#include <cstring>
#include <stdio.h>
Expand All @@ -38,7 +39,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<HexFlags> flags)
{
if (srcLen != 2)
{
Expand All @@ -54,12 +55,12 @@ CHIP_ERROR MakeU8FromAsciiHex(const char * src, const size_t srcLen, uint8_t * v
{
ret = static_cast<uint8_t>(ret + cval - static_cast<uint8_t>('0'));
}
// Only uppercase is supported according to spec.
else if (c >= 'A' && c <= 'F')
{
ret = static_cast<uint8_t>(ret + cval - static_cast<uint8_t>('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<uint8_t>(ret + cval - static_cast<uint8_t>('a') + 0xA);
}
Expand All @@ -72,6 +73,34 @@ 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<HexFlags> 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;
}

memset(dest_bytes, 0, dest_size_max);
size_t bytesFilled = 0;

for (size_t i = 0; i < src_size; i += 2)
{
if (MakeU8FromAsciiHex(src_hex + i, 2, &dest_bytes[i / 2], flags) != CHIP_NO_ERROR)
{
memset(dest_bytes, 0, dest_size_max);
return 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<HexFlags> flags)
Expand Down Expand Up @@ -113,42 +142,64 @@ CHIP_ERROR BytesToHex(const uint8_t * src_bytes, size_t src_size, char * dest_he

CHIP_ERROR BytesToHex(uint64_t src, char * dest_hex, size_t dest_size_max, BitFlags<HexFlags> 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);
}

CHIP_ERROR BytesToHex(uint32_t src, char * dest_hex, size_t dest_size_max, BitFlags<HexFlags> flags)
{
uint8_t buf[sizeof(src)] = { 0 };
Encoding::BigEndian::Put32(buf, src);
return BytesToHex(buf, sizeof(buf), dest_hex, dest_size_max, flags);
}

CHIP_ERROR BytesToHex(uint16_t src, char * dest_hex, size_t dest_size_max, BitFlags<HexFlags> flags)
{
uint8_t buf[sizeof(src)] = { 0 };
Encoding::BigEndian::Put16(buf, src);
return BytesToHex(buf, sizeof(buf), dest_hex, dest_size_max, flags);
}

return BytesToHex(buf, 8, 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 HexToBytes(const char * srcHex, const size_t srcLen, uint8_t * destBytes, size_t destMaxLen)
size_t UppercaseHexToBytes(const char * src_hex, const size_t src_size, uint64_t & dest)
{
if ((srcHex == nullptr) || (destBytes == nullptr))
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 UppercaseHexToBytes(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 UppercaseHexToBytes(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
Expand Down
Loading

0 comments on commit cfdd6e8

Please sign in to comment.