Skip to content

Commit

Permalink
Fix corner cases of handling of Common Name fallback encoding (#28911)
Browse files Browse the repository at this point in the history
* Fix corner cases of handling of Common Name fallback encoding

Problem:
- Appearance of a Mpid:/Mvid: in a DAC/PAI/PAA DN was deemed OK
  by previous code, but this caused a critical ambiguity in PAIs which
  would possibly cause fall-back to non-PID-scoped PAI interpretation.
- Related to https://github.com/CHIP-Specifications/connectedhomeip-spec/issues/7470
- Fixes #28898

This PR:

- Replaces the logic for fallback encodign conversion to take
  the first legitimate fully matching case for Mvid: and Mpid:
  and detect errors where either of these is present but without
  a following Mpid/Mvid.
- Updates unit tests to improve coverage and to properly mark as
  invalid some cases marked invalid in spec which where deemed
  valid by prior code by mistake

Testing done:

- Integration tests still pass (relater to Commissioner DUT).
- Test vectors updated.
- New unit tests added.

* Restyled by clang-format

* Restyled by prettier-json

* Address review comments by revamping algorithm

* Fix leftover comment follow-ups from @bzbarsky-apple from #28899

* Restyled by clang-format

* Add more comments and fix clang-tidy

* Address more review comments

---------

Co-authored-by: tennessee.carmelveilleux@gmail.com <tennessee@google.com>
Co-authored-by: Restyled.io <commits@restyled.io>
  • Loading branch information
3 people authored and web-flow committed Aug 29, 2023
1 parent 96c9357 commit 2ac6bb8
Show file tree
Hide file tree
Showing 9 changed files with 349 additions and 77 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"description": "PAI Test Vector: Fallback VID and PID encoding example from spec: invalid, since substring following Mpid: is not exactly 4 uppercase hexadecimal digits",
"is_success_case": "true",
"dac_cert": "308201d93082017fa00302010202082b33ac6e16c75827300a06082a8648ce3d040302303b3139303706035504030c3041434d45204d617474657220446576656c20504149203543444139383939204d7669643a46464631204d7069643a42313020170d3232303532373030303030305a180f39393939313233313233353935395a30463118301606035504030c0f4d617474657220546573742044414331143012060a2b0601040182a27c02010c044646463131143012060a2b0601040182a27c02020c04303042313059301306072a8648ce3d020106082a8648ce3d03010703420004410ca0cf2143eb925ff8f89a4471a73904b0330f3e52154eeeaaaf18c0a54cfb02129483b22d6a8da0db09defee44990edc69d3fbb52cf33ab455db1b5a5ac7fa360305e300c0603551d130101ff04023000300e0603551d0f0101ff040403020780301d0603551d0e04160414a59346294c65373c763039bdd0d7f5cb7ded0fae301f0603551d230418301680146e31cd74718cd9105e98710237d19eb2fe480d86300a06082a8648ce3d040302034800304502203c63e51f456137ef959bb51487aa25616d8ab6f12bbc534f77842189bd495a9e022100fd3f7de5dc7237b97f3bccf4255a7dd5501179c8cf480e34809cfdd8b1dc53d7",
"pai_cert": "308201ca3082016fa0030201020208652ab3c085eed037300a06082a8648ce3d04030230303118301606035504030c0f4d617474657220546573742050414131143012060a2b0601040182a27c02010c04464646313020170d3232303532373030303030305a180f39393939313233313233353935395a303b3139303706035504030c3041434d45204d617474657220446576656c20504149203543444139383939204d7669643a46464631204d7069643a42313059301306072a8648ce3d020106082a8648ce3d0301070342000431319f3024f53e8bcd44acb77a4a8a1d4df61966f0b4a06ffb8fa5af5ba2988fa3e2c67747fb59e94e9d356e9509dbc95d9345ca0c5e78078c89360fe749ca2da366306430120603551d130101ff040830060101ff020100300e0603551d0f0101ff040403020106301d0603551d0e041604146e31cd74718cd9105e98710237d19eb2fe480d86301f0603551d230418301680146afd22771f511fecbf1641976710dcdc31a1717e300a06082a8648ce3d040302034900304602210093091c5610f921d9d7e3afe68e77fe9fdf2857ba09e2bcd2e503196187e79032022100ce8f88e1e9469044ef583fc06ed7de227779757206f3779e7517663d826df612",
"certification_declaration": "3081e806092a864886f70d010702a081da3081d7020103310d300b0609608648016503040201304406092a864886f70d010701a0370435152400012501f1ff360204b118250334122c04135a494732303134315a423333303030312d32342405002406002507769824080018317d307b020103801462fa823359acfaa9963e1cfa140addf504f37160300b0609608648016503040201300a06082a8648ce3d04030204473045022100ffe39b9f479f0f6c98a02a3a97c0a8c7c1754d3c87e7b70f6c676bc12ed83dc302203c6f75ed2d90c9c75a28836c6cdf4191f4c9ad9d940d5108217ac597c2f16980",
"dac_private_key": "da503e4cbc05d3f3e5054da8f42f8c523a37f93d2ccc0c8a62e4830f66293182",
"dac_public_key": "04410ca0cf2143eb925ff8f89a4471a73904b0330f3e52154eeeaaaf18c0a54cfb02129483b22d6a8da0db09defee44990edc69d3fbb52cf33ab455db1b5a5ac7f"
"is_success_case": "false",
"dac_cert": "308201d93082017fa003020102020846cb41af3a7fc9c3300a06082a8648ce3d040302303b3139303706035504030c3041434d45204d617474657220446576656c20504149203543444139383939204d7669643a46464631204d7069643a42313020170d3232303932333030303030305a180f39393939313233313233353935395a30463118301606035504030c0f4d617474657220546573742044414331143012060a2b0601040182a27c02010c044646463131143012060a2b0601040182a27c02020c04303042313059301306072a8648ce3d020106082a8648ce3d03010703420004d3e1c5422ab213f118f959a33d2204342cf598dffcf5cec0474a1bba6e4c1a7263a59c605cf892038a704fd266149b5c89fe35e17e8dda279bb03cb1c7bf8ca6a360305e300c0603551d130101ff04023000300e0603551d0f0101ff040403020780301d0603551d0e0416041430616dcabe874ac65e3dce88de7c5cc5defd86f9301f0603551d230418301680148fa036268e8eedbc73e12a0eeadcb2fbec8faaac300a06082a8648ce3d040302034800304502204ef134d60bb150def30f7c2df8f88c46755e36f48a12831aaedf17c4944cd70d022100f61555085107e4e783a5ad6abc64e1ee1f0d719b7d83a5f8bace41949f85c284",
"pai_cert": "308201c93082016fa003020102020821b17d362fe9afae300a06082a8648ce3d04030230303118301606035504030c0f4d617474657220546573742050414131143012060a2b0601040182a27c02010c04464646313020170d3232303932333030303030305a180f39393939313233313233353935395a303b3139303706035504030c3041434d45204d617474657220446576656c20504149203543444139383939204d7669643a46464631204d7069643a42313059301306072a8648ce3d020106082a8648ce3d03010703420004e72dbe2a0f600358978022ea70fb7ef3ef14de92a0dfb9cfb83b542fbccb7b8a7b13d3f37a26dfd2400224ae4ab82e1c13ca57a45e4ee54e862a192a6d4f1f3aa366306430120603551d130101ff040830060101ff020100300e0603551d0f0101ff040403020106301d0603551d0e041604148fa036268e8eedbc73e12a0eeadcb2fbec8faaac301f0603551d230418301680146afd22771f511fecbf1641976710dcdc31a1717e300a06082a8648ce3d040302034800304502210093a63c6f397d39dfa5b60c06c2839448ab5db0bf0728c7449c66bdc55e3ff6c302207b0c997ff95f1795b86828d1137b34f3f2aef1ffb2183a6e4c02836c1c735171",
"certification_declaration": "3081e906092a864886f70d010702a081db3081d8020103310d300b0609608648016503040201304406092a864886f70d010701a0370435152400012501f1ff360204b118250334122c04135a494732303134315a423333303030312d32342405002406002507769824080018317e307c020103801462fa823359acfaa9963e1cfa140addf504f37160300b0609608648016503040201300a06082a8648ce3d04030204483046022100e1d4967878513c474101db44c68061689be47443e21f6675c34b872edc5a319c022100a963e07bcd8e4abeb8b4fcb19f6a59c92f1a3bad4d7153f333684f93a836e3f9",
"dac_private_key": "05da60e1debbee09f31881ac82eb6ce5116db802606077661ff968312465fa75",
"dac_public_key": "04d3e1c5422ab213f118f959a33d2204342cf598dffcf5cec0474a1bba6e4c1a7263a59c605cf892038a704fd266149b5c89fe35e17e8dda279bb03cb1c7bf8ca6"
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"description": "PAI Test Vector: Fallback VID and PID encoding example from spec: invalid, since substring following Mpid: is not exactly 4 uppercase hexadecimal digits",
"is_success_case": "true",
"dac_cert": "308201d83082017da00302010202084e7b4a5111ae21af300a06082a8648ce3d04030230393137303506035504030c2e41434d45204d617474657220446576656c20504149203543444139383939204d7069643a204d7669643a464646313020170d3232303532373030303030305a180f39393939313233313233353935395a30463118301606035504030c0f4d617474657220546573742044414331143012060a2b0601040182a27c02010c044646463131143012060a2b0601040182a27c02020c04303042313059301306072a8648ce3d020106082a8648ce3d03010703420004760c320e7c2d565082eefdeba1d66f357ef50b87a98c90c3bb4b314a79c42a3e4014296a1ab0bf166dcefef3a86c30e3334f85debb09250b64752f945d44089fa360305e300c0603551d130101ff04023000300e0603551d0f0101ff040403020780301d0603551d0e04160414d7e07a4b844e5ba0fe9bef40476e9e8ac9e8a5bf301f0603551d2304183016801455140af506976d92611c51cec3c2037790dd47c0300a06082a8648ce3d0403020349003046022100dc41cf2813b922f0f9c84fb55059b5d2e3f684923a1d83f6d9aabaee60cbfbd4022100a2495b08a4a6aa7bb4ec9f7000a410b04bb69f48c4557f5bdc4864fcbaaa787d",
"pai_cert": "308201c63082016da00302010202083d95522ea6682a4c300a06082a8648ce3d04030230303118301606035504030c0f4d617474657220546573742050414131143012060a2b0601040182a27c02010c04464646313020170d3232303532373030303030305a180f39393939313233313233353935395a30393137303506035504030c2e41434d45204d617474657220446576656c20504149203543444139383939204d7069643a204d7669643a464646313059301306072a8648ce3d020106082a8648ce3d03010703420004fdea8bcdc408e5a885cbc62176957583a0421e4e44711a080ff1572262e7afefc4d6b56a788d4d3b571ac25d678af964e8a235b05fe112f99eee3bf57d717cd8a366306430120603551d130101ff040830060101ff020100300e0603551d0f0101ff040403020106301d0603551d0e0416041455140af506976d92611c51cec3c2037790dd47c0301f0603551d230418301680146afd22771f511fecbf1641976710dcdc31a1717e300a06082a8648ce3d04030203470030440220143dfe46f8ffaa1321a281c1c5cfd0c3be4976ed0582d26a529a93ea18783637022045a5fd90301d1156fb3ec2dfe273f9ca663b01b41885790b81182937ed1b9f6c",
"certification_declaration": "3081e706092a864886f70d010702a081d93081d6020103310d300b0609608648016503040201304406092a864886f70d010701a0370435152400012501f1ff360204b118250334122c04135a494732303134315a423333303030312d32342405002406002507769824080018317c307a020103801462fa823359acfaa9963e1cfa140addf504f37160300b0609608648016503040201300a06082a8648ce3d0403020446304402206d1701c37ff7fffb8dada85e67146289458cc617ac0efc3c115f076ff75d0b280220725e63658d8715bc8dbe2459c499a8456254eec0a518e0205ec52ec80649af4e",
"dac_private_key": "f46cd9b9fbed2827bb506067c19897f23c8ddf68acff11f191e5833b377cee2d",
"dac_public_key": "04760c320e7c2d565082eefdeba1d66f357ef50b87a98c90c3bb4b314a79c42a3e4014296a1ab0bf166dcefef3a86c30e3334f85debb09250b64752f945d44089f"
"is_success_case": "false",
"dac_cert": "308201d83082017da0030201020208697ea481e17f0f9c300a06082a8648ce3d04030230393137303506035504030c2e41434d45204d617474657220446576656c20504149203543444139383939204d7069643a204d7669643a464646313020170d3232303932333030303030305a180f39393939313233313233353935395a30463118301606035504030c0f4d617474657220546573742044414331143012060a2b0601040182a27c02010c044646463131143012060a2b0601040182a27c02020c04303042313059301306072a8648ce3d020106082a8648ce3d030107034200046eb8078776faf91061011baca81d4577b5b6b64e6f3009ddbc05aea20eaa044dbde2f70716de1d0ea2221a57ba5ca2ab23995fff18e63c6c675e403f7bf7ed53a360305e300c0603551d130101ff04023000300e0603551d0f0101ff040403020780301d0603551d0e041604141c1bdaa7b3c18076e81187da63295ee068a7fe29301f0603551d23041830168014bd781517ec524b8b752e7e529063de96d6c2deb1300a06082a8648ce3d0403020349003046022100ca59fb5cc9fd777641ba761c9fbf16dc24f044fcfca5dead17ed7cae02a3f64d022100bb501e52bef2e0ff3d9b6c9bc8dac1c6adf94a19d66f38cf840b89e0bbd7dc12",
"pai_cert": "308201c63082016da00302010202082dbfc034ee2ebe32300a06082a8648ce3d04030230303118301606035504030c0f4d617474657220546573742050414131143012060a2b0601040182a27c02010c04464646313020170d3232303932333030303030305a180f39393939313233313233353935395a30393137303506035504030c2e41434d45204d617474657220446576656c20504149203543444139383939204d7069643a204d7669643a464646313059301306072a8648ce3d020106082a8648ce3d030107034200041a872e39c4f8337945c51b1a5dfa009e0f8402a59d996da057aa370acb18450bc3316babcf7c6eed0555300d7e9e4ac42c0e9556857835e8e8de2d91a1bbd27da366306430120603551d130101ff040830060101ff020100300e0603551d0f0101ff040403020106301d0603551d0e04160414bd781517ec524b8b752e7e529063de96d6c2deb1301f0603551d230418301680146afd22771f511fecbf1641976710dcdc31a1717e300a06082a8648ce3d040302034700304402205c38ff93c407961a98e61bc23ea65381a760f97ac0481ece8ee21bcd451b8c2002201d6c7ee510aec10ea02402f5f6d80bc74489fb8793d64a77e06b8fece69cd4b8",
"certification_declaration": "3081e706092a864886f70d010702a081d93081d6020103310d300b0609608648016503040201304406092a864886f70d010701a0370435152400012501f1ff360204b118250334122c04135a494732303134315a423333303030312d32342405002406002507769824080018317c307a020103801462fa823359acfaa9963e1cfa140addf504f37160300b0609608648016503040201300a06082a8648ce3d0403020446304402201f38b426df25ab4f552ded014845afb1a353e808e9250fc19dcfcab3d717679f0220575a610c996f0221628ff9a76123d3462922f91b6011ba727e6d42884ae88c08",
"dac_private_key": "8c0efeb0d08af4384bf3cf75256c3a47a88a514426d911375ac5bc7d03564212",
"dac_public_key": "046eb8078776faf91061011baca81d4577b5b6b64e6f3009ddbc05aea20eaa044dbde2f70716de1d0ea2221a57ba5ca2ab23995fff18e63c6c675e403f7bf7ed53"
}
133 changes: 100 additions & 33 deletions src/crypto/CHIPCryptoPAL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <lib/support/BytesToHex.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/Span.h>
#include <stdint.h>
#include <string.h>

using chip::ByteSpan;
Expand All @@ -38,6 +39,8 @@ using chip::Encoding::LittleEndian::Reader;

using namespace chip::ASN1;

namespace chip {
namespace Crypto {
namespace {

constexpr uint8_t kIntegerTag = 0x02u;
Expand Down Expand Up @@ -192,10 +195,76 @@ CHIP_ERROR ConvertIntegerRawToDerInternal(const ByteSpan & raw_integer, MutableB
return CHIP_NO_ERROR;
}

} // namespace
/**
* @brief Find a 4 uppercase hex digit hex value after a prefix string. Used to implement
* fallback CN VID/PID encoding for PAA/PAI/DAC.
*
* @param[in] buffer - buffer in which to find the substring.
* @param[in] prefix - prefix to match, which must be followed by 4 uppercase hex characters
* @param[out] out_hex_value - on CHIP_NO_ERROR return, this will be the 16-bit hex value decoded.
* @return CHIP_NO_ERROR on success, CHIP_ERROR_NOT_FOUND if not detected and
* CHIP_ERROR_WRONG_CERT_DN if we saw the prefix but no valid hex string.
*/
CHIP_ERROR Find16BitUpperCaseHexAfterPrefix(const ByteSpan & buffer, const char * prefix, uint16_t & out_hex_value)
{
chip::CharSpan prefix_span = chip::CharSpan::fromCharString(prefix);

namespace chip {
namespace Crypto {
bool found_prefix_at_least_once = false;

// Scan string from left to right, to find the desired full matching substring.
//
// IMPORTANT NOTE: We are trying to find the equivalent of prefix + [0-9A-F]{4}.
// The appearance of the full prefix, but not followed by the hex value, must
// be detected, as it is illegal if there isn't a valid prefix within the string.
// This is why we first check for the prefix and then maybe check for the hex
// value, rather than doing a single check of making sure there is enough space
// for both.
for (size_t start_idx = 0; start_idx < buffer.size(); start_idx++)
{
const uint8_t * cursor = buffer.data() + start_idx;
size_t remaining = buffer.size() - start_idx;

if (remaining < prefix_span.size())
{
// We can't possibly match prefix if not enough bytes left.
break;
}

// Try to match the prefix at current position.
if (memcmp(cursor, prefix_span.data(), prefix_span.size()) != 0)
{
// Did not find prefix, move to next position.
continue;
}

// Otherwise, found prefix, skip to possible hex value.
found_prefix_at_least_once = true;
cursor += prefix_span.size();
remaining -= prefix_span.size();

constexpr size_t expected_hex_len = HEX_ENCODED_LENGTH(sizeof(uint16_t));
if (remaining < expected_hex_len)
{
// We can't possibly match the hex values if not enough bytes left.
break;
}

char hex_buf[expected_hex_len];
memcpy(&hex_buf[0], cursor, sizeof(hex_buf));

if (Encoding::UppercaseHexToUint16(&hex_buf[0], sizeof(hex_buf), out_hex_value) != 0)
{
// Found first full valid match, return success, out_hex_value already updated.
return CHIP_NO_ERROR;
}

// Otherwise, did not find what we were looking for, try next position until exhausted.
}

return found_prefix_at_least_once ? CHIP_ERROR_WRONG_CERT_DN : CHIP_ERROR_NOT_FOUND;
}

} // namespace

#ifdef ENABLE_HSM_HKDF
using HKDF_sha_crypto = HKDF_shaHSM;
Expand Down Expand Up @@ -873,41 +942,39 @@ CHIP_ERROR ExtractVIDPIDFromAttributeString(DNAttrType attrType, const ByteSpan
// Otherwise, it is a CommonName attribute.
else if (!vidpidFromCNAttr.Initialized())
{
char cnAttr[kMax_CommonNameAttr_Length + 1];
if (attr.size() <= chip::Crypto::kMax_CommonNameAttr_Length)
ByteSpan attr_source_span{ attr };
if (attr_source_span.size() > chip::Crypto::kMax_CommonNameAttr_Length)
{
memcpy(cnAttr, attr.data(), attr.size());
cnAttr[attr.size()] = 0;
attr_source_span.reduce_size(chip::Crypto::kMax_CommonNameAttr_Length);
}

char * vid = strstr(cnAttr, kVIDPrefixForCNEncoding);
if (vid != nullptr)
{
vid += strlen(kVIDPrefixForCNEncoding);
if (cnAttr + attr.size() >= vid + kVIDandPIDHexLength)
{
uint16_t matterAttr;
if (Encoding::UppercaseHexToUint16(vid, kVIDandPIDHexLength, matterAttr) == sizeof(matterAttr))
{
vidpidFromCNAttr.mVendorId.SetValue(static_cast<VendorId>(matterAttr));
}
}
}
// Try to find a valid Vendor ID encoded in fallback method.
uint16_t vid = 0;
CHIP_ERROR err = Find16BitUpperCaseHexAfterPrefix(attr_source_span, kVIDPrefixForCNEncoding, vid);
if (err == CHIP_NO_ERROR)
{
vidpidFromCNAttr.mVendorId.SetValue(static_cast<VendorId>(vid));
}
else if (err != CHIP_ERROR_NOT_FOUND)
{
// This indicates a bad/ambiguous format.
return err;
}

char * pid = strstr(cnAttr, kPIDPrefixForCNEncoding);
if (pid != nullptr)
{
pid += strlen(kPIDPrefixForCNEncoding);
if (cnAttr + attr.size() >= pid + kVIDandPIDHexLength)
{
uint16_t matterAttr;
if (Encoding::UppercaseHexToUint16(pid, kVIDandPIDHexLength, matterAttr) == sizeof(matterAttr))
{
vidpidFromCNAttr.mProductId.SetValue(matterAttr);
}
}
}
// Try to find a valid Product ID encoded in fallback method.
uint16_t pid = 0;
err = Find16BitUpperCaseHexAfterPrefix(attr_source_span, kPIDPrefixForCNEncoding, pid);
if (err == CHIP_NO_ERROR)
{
vidpidFromCNAttr.mProductId.SetValue(pid);
}
else if (err != CHIP_ERROR_NOT_FOUND)
{
// This indicates a bad/ambiguous format.
return err;
}
}

return CHIP_NO_ERROR;
}

Expand Down
5 changes: 3 additions & 2 deletions src/crypto/CHIPCryptoPAL.h
Original file line number Diff line number Diff line change
Expand Up @@ -633,8 +633,9 @@ CHIP_ERROR AES_CCM_decrypt(const uint8_t * ciphertext, size_t ciphertext_length,
* be configured to ignore CSR requested subject.
*
* @param keypair The key pair for which a CSR should be generated. Must not be null.
* @param csr_span Span to hold the resulting CSR. Must be at least kMAX_CSR_Length. Otherwise returns CHIP_ERROR_BUFFER_TOO_SMALL.
* It will get resized to actual size needed on success.
* @param csr_span Span to hold the resulting CSR. Must have size at least kMIN_CSR_Buffer_Size.
* Otherwise returns CHIP_ERROR_BUFFER_TOO_SMALL. It will get resized to
* actual size needed on success.
* @return Returns a CHIP_ERROR from P256Keypair or ASN.1 backend on error, CHIP_NO_ERROR otherwise
**/
Expand Down
2 changes: 1 addition & 1 deletion src/crypto/OperationalKeystore.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class OperationalKeystore
* Only one pending operational keypair is supported at a time.
*
* @param fabricIndex - FabricIndex for which a new keypair must be made available
* @param outCertificateSigningRequest - Buffer to contain the CSR. Must be at least `kMAX_CSR_Length` large.
* @param outCertificateSigningRequest - Buffer to contain the CSR. Must have size at least `kMIN_CSR_Buffer_Size`.
*
* @retval CHIP_NO_ERROR on success
* @retval CHIP_ERROR_BUFFER_TOO_SMALL if `outCertificateSigningRequest` buffer is too small
Expand Down

0 comments on commit 2ac6bb8

Please sign in to comment.