Matter SDK Coverage Report
Current view: top level - crypto - CHIPCryptoPAL.cpp (source / functions) Coverage Total Hit
Test: SHA:ff120be2ae00467d4c4cc25a54f1a72dc0ed46d8 Lines: 99.3 % 574 570
Test Date: 2025-06-26 07:09:10 Functions: 100.0 % 42 42

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2020-2022 Project CHIP Authors
       4              :  *
       5              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6              :  *    you may not use this file except in compliance with the License.
       7              :  *    You may obtain a copy of the License at
       8              :  *
       9              :  *        http://www.apache.org/licenses/LICENSE-2.0
      10              :  *
      11              :  *    Unless required by applicable law or agreed to in writing, software
      12              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14              :  *    See the License for the specific language governing permissions and
      15              :  *    limitations under the License.
      16              :  */
      17              : 
      18              : /**
      19              :  *    @file
      20              :  *      Platform agnostic implementation of CHIP crypto algorithms
      21              :  */
      22              : 
      23              : #include "CHIPCryptoPAL.h"
      24              : 
      25              : #include "SessionKeystore.h"
      26              : 
      27              : #include <lib/asn1/ASN1.h>
      28              : #include <lib/asn1/ASN1Macros.h>
      29              : #include <lib/core/CHIPEncoding.h>
      30              : #include <lib/support/Base64.h>
      31              : #include <lib/support/BufferReader.h>
      32              : #include <lib/support/BufferWriter.h>
      33              : #include <lib/support/BytesToHex.h>
      34              : #include <lib/support/CHIPMemString.h>
      35              : #include <lib/support/CodeUtils.h>
      36              : #include <lib/support/Span.h>
      37              : #include <lib/support/StringBuilder.h>
      38              : #include <lib/support/TypeTraits.h>
      39              : #include <stdint.h>
      40              : #include <string.h>
      41              : 
      42              : using chip::ByteSpan;
      43              : using chip::MutableByteSpan;
      44              : using chip::Encoding::BufferWriter;
      45              : using chip::Encoding::LittleEndian::Reader;
      46              : 
      47              : using namespace chip::ASN1;
      48              : 
      49              : namespace chip {
      50              : namespace Crypto {
      51              : namespace {
      52              : 
      53              : constexpr uint8_t kIntegerTag         = 0x02u;
      54              : constexpr uint8_t kSeqTag             = 0x30u;
      55              : constexpr size_t kMinSequenceOverhead = 1 /* tag */ + 1 /* length */ + 1 /* actual data or second length byte*/;
      56              : 
      57              : /**
      58              :  * @brief Utility to convert DER-encoded INTEGER into a raw integer buffer in big-endian order
      59              :  *        with leading zeroes if the output buffer is larger than needed.
      60              :  * @param[in] reader Reader instance from which the input will be read
      61              :  * @param[out] raw_integer_out Buffer to receive the DER-encoded integer
      62              :  * @return CHIP_ERROR_INVALID_ARGUMENT or CHIP_ERROR_BUFFER_TOO_SMALL on error, CHIP_NO_ERROR otherwise
      63              :  */
      64          368 : CHIP_ERROR ReadDerUnsignedIntegerIntoRaw(Reader & reader, MutableByteSpan raw_integer_out)
      65              : {
      66          368 :     uint8_t cur_byte = 0;
      67              : 
      68          368 :     ReturnErrorOnFailure(reader.Read8(&cur_byte).StatusCode());
      69              : 
      70              :     // We expect first tag to be INTEGER
      71          368 :     VerifyOrReturnError(cur_byte == kIntegerTag, CHIP_ERROR_INVALID_ARGUMENT);
      72              : 
      73              :     // Read the length
      74          368 :     size_t integer_len = 0;
      75          368 :     ReturnErrorOnFailure(chip::Crypto::ReadDerLength(reader, integer_len));
      76              : 
      77              :     // Clear the destination buffer, so we can blit the unsigned value into place
      78          368 :     memset(raw_integer_out.data(), 0, raw_integer_out.size());
      79              : 
      80              :     // Check for pseudo-zero to mark unsigned value
      81              :     // This means we have too large an integer (should be at most 1 byte too large), it's invalid
      82          368 :     VerifyOrReturnError(integer_len <= (raw_integer_out.size() + 1), CHIP_ERROR_INVALID_ARGUMENT);
      83              : 
      84          368 :     if (integer_len == (raw_integer_out.size() + 1u))
      85              :     {
      86              :         // Means we had a 0x00 byte stuffed due to MSB being high in original integer
      87          207 :         ReturnErrorOnFailure(reader.Read8(&cur_byte).StatusCode());
      88              : 
      89              :         // The extra byte must be a leading zero
      90          207 :         VerifyOrReturnError(cur_byte == 0, CHIP_ERROR_INVALID_ARGUMENT);
      91          207 :         --integer_len;
      92              :     }
      93              : 
      94              :     // We now have the rest of the tag that is a "minimal length" unsigned integer.
      95              :     // Blit it at the correct offset, since the order we use is MSB first for
      96              :     // both ASN.1 and EC curve raw points.
      97          368 :     size_t offset = raw_integer_out.size() - integer_len;
      98          368 :     return reader.ReadBytes(raw_integer_out.data() + offset, integer_len).StatusCode();
      99              : }
     100              : 
     101          285 : CHIP_ERROR ConvertIntegerRawToDerInternal(const ByteSpan & raw_integer, MutableByteSpan & out_der_integer,
     102              :                                           bool include_tag_and_length)
     103              : {
     104          285 :     if (raw_integer.empty() || out_der_integer.empty())
     105              :     {
     106            2 :         return CHIP_ERROR_INVALID_ARGUMENT;
     107              :     }
     108              : 
     109          283 :     Reader reader(raw_integer);
     110          283 :     BufferWriter writer(out_der_integer);
     111              : 
     112          283 :     bool needs_leading_zero_byte = false;
     113              : 
     114          283 :     uint8_t cur_byte = 0;
     115          469 :     while ((reader.Remaining() > 0) && (reader.Read8(&cur_byte).StatusCode() == CHIP_NO_ERROR) && (cur_byte == 0))
     116              :     {
     117              :         // Omit all leading zeros
     118              :     }
     119              : 
     120          283 :     if ((cur_byte & 0x80u) != 0)
     121              :     {
     122              :         // If overall MSB (from leftmost byte) is set, we will need to push out a zero to avoid it being
     123              :         // considered a negative number.
     124          137 :         needs_leading_zero_byte = true;
     125              :     }
     126              : 
     127              :     // The + 1 is to account for the last consumed byte of the loop to skip leading zeros
     128          283 :     size_t length = reader.Remaining() + 1 + (needs_leading_zero_byte ? 1 : 0);
     129              : 
     130          283 :     if (length > 127)
     131              :     {
     132              :         // We do not support length over more than 1 bytes.
     133            0 :         return CHIP_ERROR_INVALID_ARGUMENT;
     134              :     }
     135              : 
     136          283 :     if (include_tag_and_length)
     137              :     {
     138              :         // Put INTEGER tag
     139           24 :         writer.Put(kIntegerTag);
     140              : 
     141              :         // Put length over 1 byte (i.e. MSB clear)
     142           24 :         writer.Put(static_cast<uint8_t>(length));
     143              :     }
     144              : 
     145              :     // If leading zero or no more bytes remaining, must ensure we start with at least a zero byte
     146          283 :     if (needs_leading_zero_byte)
     147              :     {
     148          137 :         writer.Put(static_cast<uint8_t>(0u));
     149              :     }
     150              : 
     151              :     // Put first consumed byte from last read iteration of leading zero suppression
     152          283 :     writer.Put(cur_byte);
     153              : 
     154              :     // Fill the rest from the input in order
     155         8406 :     while (reader.Read8(&cur_byte).StatusCode() == CHIP_NO_ERROR)
     156              :     {
     157              :         // Emit all other bytes as-is
     158         8123 :         writer.Put(cur_byte);
     159              :     }
     160              : 
     161          283 :     size_t actually_written = 0;
     162          283 :     if (!writer.Fit(actually_written))
     163              :     {
     164            7 :         return CHIP_ERROR_BUFFER_TOO_SMALL;
     165              :     }
     166              : 
     167          276 :     out_der_integer = out_der_integer.SubSpan(0, actually_written);
     168              : 
     169          276 :     return CHIP_NO_ERROR;
     170              : }
     171              : 
     172              : /**
     173              :  * @brief Find a 4 uppercase hex digit hex value after a prefix string. Used to implement
     174              :  *        fallback CN VID/PID encoding for PAA/PAI/DAC.
     175              :  *
     176              :  * @param[in] buffer - buffer in which to find the substring.
     177              :  * @param[in] prefix - prefix to match, which must be followed by 4 uppercase hex characters
     178              :  * @param[out] out_hex_value - on CHIP_NO_ERROR return, this will be the 16-bit hex value decoded.
     179              :  * @return CHIP_NO_ERROR on success, CHIP_ERROR_NOT_FOUND if not detected and
     180              :  *         CHIP_ERROR_WRONG_CERT_DN if we saw the prefix but no valid hex string.
     181              :  */
     182          679 : CHIP_ERROR Find16BitUpperCaseHexAfterPrefix(const ByteSpan & buffer, const char * prefix, uint16_t & out_hex_value)
     183              : {
     184          679 :     chip::CharSpan prefix_span = chip::CharSpan::fromCharString(prefix);
     185              : 
     186          679 :     bool found_prefix_at_least_once = false;
     187              : 
     188              :     // Scan string from left to right, to find the desired full matching substring.
     189              :     //
     190              :     // IMPORTANT NOTE: We are trying to find the equivalent of prefix + [0-9A-F]{4}.
     191              :     // The appearance of the full prefix, but not followed by the hex value, must
     192              :     // be detected, as it is illegal if there isn't a valid prefix within the string.
     193              :     // This is why we first check for the prefix and then maybe check for the hex
     194              :     // value, rather than doing a single check of making sure there is enough space
     195              :     // for both.
     196         9852 :     for (size_t start_idx = 0; start_idx < buffer.size(); start_idx++)
     197              :     {
     198         9852 :         const uint8_t * cursor = buffer.data() + start_idx;
     199         9852 :         size_t remaining       = buffer.size() - start_idx;
     200              : 
     201         9852 :         if (remaining < prefix_span.size())
     202              :         {
     203              :             // We can't possibly match prefix if not enough bytes left.
     204          607 :             break;
     205              :         }
     206              : 
     207              :         // Try to match the prefix at current position.
     208         9248 :         if (memcmp(cursor, prefix_span.data(), prefix_span.size()) != 0)
     209              :         {
     210              :             // Did not find prefix, move to next position.
     211         9154 :             continue;
     212              :         }
     213              : 
     214              :         // Otherwise, found prefix, skip to possible hex value.
     215           94 :         found_prefix_at_least_once = true;
     216           94 :         cursor += prefix_span.size();
     217           94 :         remaining -= prefix_span.size();
     218              : 
     219           94 :         constexpr size_t expected_hex_len = HEX_ENCODED_LENGTH(sizeof(uint16_t));
     220           94 :         if (remaining < expected_hex_len)
     221              :         {
     222              :             // We can't possibly match the hex values if not enough bytes left.
     223            3 :             break;
     224              :         }
     225              : 
     226              :         char hex_buf[expected_hex_len];
     227           91 :         memcpy(&hex_buf[0], cursor, sizeof(hex_buf));
     228              : 
     229           91 :         if (Encoding::UppercaseHexToUint16(&hex_buf[0], sizeof(hex_buf), out_hex_value) != 0)
     230              :         {
     231              :             // Found first full valid match, return success, out_hex_value already updated.
     232           72 :             return CHIP_NO_ERROR;
     233              :         }
     234              : 
     235              :         // Otherwise, did not find what we were looking for, try next position until exhausted.
     236              :     }
     237              : 
     238          607 :     return found_prefix_at_least_once ? CHIP_ERROR_WRONG_CERT_DN : CHIP_ERROR_NOT_FOUND;
     239              : }
     240              : 
     241              : } // namespace
     242              : 
     243              : using HKDF_sha_crypto = HKDF_sha;
     244              : 
     245          328 : CHIP_ERROR Spake2p::InternalHash(const uint8_t * in, size_t in_len)
     246              : {
     247          328 :     const uint64_t u64_len = in_len;
     248              : 
     249              :     uint8_t lb[8];
     250          328 :     lb[0] = static_cast<uint8_t>((u64_len >> 0) & 0xff);
     251          328 :     lb[1] = static_cast<uint8_t>((u64_len >> 8) & 0xff);
     252          328 :     lb[2] = static_cast<uint8_t>((u64_len >> 16) & 0xff);
     253          328 :     lb[3] = static_cast<uint8_t>((u64_len >> 24) & 0xff);
     254          328 :     lb[4] = static_cast<uint8_t>((u64_len >> 32) & 0xff);
     255          328 :     lb[5] = static_cast<uint8_t>((u64_len >> 40) & 0xff);
     256          328 :     lb[6] = static_cast<uint8_t>((u64_len >> 48) & 0xff);
     257          328 :     lb[7] = static_cast<uint8_t>((u64_len >> 56) & 0xff);
     258              : 
     259          328 :     ReturnErrorOnFailure(Hash(lb, sizeof(lb)));
     260          328 :     if (in != nullptr)
     261              :     {
     262          179 :         ReturnErrorOnFailure(Hash(in, in_len));
     263              :     }
     264              : 
     265          328 :     return CHIP_NO_ERROR;
     266              : }
     267              : 
     268          156 : Spake2p::Spake2p(size_t _fe_size, size_t _point_size, size_t _hash_size)
     269              : {
     270          156 :     fe_size    = _fe_size;
     271          156 :     point_size = _point_size;
     272          156 :     hash_size  = _hash_size;
     273              : 
     274          156 :     Kca = &Kcab[0];
     275          156 :     Kcb = &Kcab[hash_size / 2];
     276          156 :     Ka  = &Kae[0];
     277          156 :     Ke  = &Kae[hash_size / 2];
     278              : 
     279          156 :     M  = nullptr;
     280          156 :     N  = nullptr;
     281          156 :     G  = nullptr;
     282          156 :     X  = nullptr;
     283          156 :     Y  = nullptr;
     284          156 :     L  = nullptr;
     285          156 :     Z  = nullptr;
     286          156 :     V  = nullptr;
     287          156 :     w0 = nullptr;
     288          156 :     w1 = nullptr;
     289          156 :     xy = nullptr;
     290              : 
     291          156 :     order  = nullptr;
     292          156 :     tempbn = nullptr;
     293          156 : }
     294              : 
     295          148 : CHIP_ERROR Spake2p::Init(const uint8_t * context, size_t context_len)
     296              : {
     297          148 :     if (state != CHIP_SPAKE2P_STATE::PREINIT)
     298              :     {
     299            1 :         Clear();
     300              :     }
     301              : 
     302          148 :     ReturnErrorOnFailure(InitImpl());
     303          148 :     ReturnErrorOnFailure(PointLoad(spake2p_M_p256, sizeof(spake2p_M_p256), M));
     304          148 :     ReturnErrorOnFailure(PointLoad(spake2p_N_p256, sizeof(spake2p_N_p256), N));
     305          148 :     ReturnErrorOnFailure(InternalHash(context, context_len));
     306              : 
     307          148 :     state = CHIP_SPAKE2P_STATE::INIT;
     308          148 :     return CHIP_NO_ERROR;
     309              : }
     310              : 
     311           20 : CHIP_ERROR Spake2p::WriteMN()
     312              : {
     313           20 :     ReturnErrorOnFailure(InternalHash(spake2p_M_p256, sizeof(spake2p_M_p256)));
     314           20 :     ReturnErrorOnFailure(InternalHash(spake2p_N_p256, sizeof(spake2p_N_p256)));
     315              : 
     316           20 :     return CHIP_NO_ERROR;
     317              : }
     318              : 
     319           10 : CHIP_ERROR Spake2p::BeginVerifier(const uint8_t * my_identity, size_t my_identity_len, const uint8_t * peer_identity,
     320              :                                   size_t peer_identity_len, const uint8_t * w0in, size_t w0in_len, const uint8_t * Lin,
     321              :                                   size_t Lin_len)
     322              : {
     323           10 :     VerifyOrReturnError(state == CHIP_SPAKE2P_STATE::INIT, CHIP_ERROR_INTERNAL);
     324              : 
     325           10 :     ReturnErrorOnFailure(InternalHash(peer_identity, peer_identity_len));
     326           10 :     ReturnErrorOnFailure(InternalHash(my_identity, my_identity_len));
     327           10 :     ReturnErrorOnFailure(WriteMN());
     328           10 :     ReturnErrorOnFailure(FELoad(w0in, w0in_len, w0));
     329           10 :     ReturnErrorOnFailure(PointLoad(Lin, Lin_len, L));
     330              : 
     331           10 :     state = CHIP_SPAKE2P_STATE::STARTED;
     332           10 :     role  = CHIP_SPAKE2P_ROLE::VERIFIER;
     333           10 :     return CHIP_NO_ERROR;
     334              : }
     335              : 
     336           10 : CHIP_ERROR Spake2p::BeginProver(const uint8_t * my_identity, size_t my_identity_len, const uint8_t * peer_identity,
     337              :                                 size_t peer_identity_len, const uint8_t * w0sin, size_t w0sin_len, const uint8_t * w1sin,
     338              :                                 size_t w1sin_len)
     339              : {
     340           10 :     VerifyOrReturnError(state == CHIP_SPAKE2P_STATE::INIT, CHIP_ERROR_INTERNAL);
     341              : 
     342           10 :     ReturnErrorOnFailure(InternalHash(my_identity, my_identity_len));
     343           10 :     ReturnErrorOnFailure(InternalHash(peer_identity, peer_identity_len));
     344           10 :     ReturnErrorOnFailure(WriteMN());
     345           10 :     ReturnErrorOnFailure(FELoad(w0sin, w0sin_len, w0));
     346           10 :     ReturnErrorOnFailure(FELoad(w1sin, w1sin_len, w1));
     347              : 
     348           10 :     state = CHIP_SPAKE2P_STATE::STARTED;
     349           10 :     role  = CHIP_SPAKE2P_ROLE::PROVER;
     350           10 :     return CHIP_NO_ERROR;
     351              : }
     352              : 
     353           20 : CHIP_ERROR Spake2p::ComputeRoundOne(const uint8_t * pab, size_t pab_len, uint8_t * out, size_t * out_len)
     354              : {
     355           20 :     CHIP_ERROR error = CHIP_ERROR_INTERNAL;
     356           20 :     void * MN        = nullptr; // Choose M if a prover, N if a verifier
     357           20 :     void * XY        = nullptr; // Choose X if a prover, Y if a verifier
     358              : 
     359           20 :     VerifyOrExit(state == CHIP_SPAKE2P_STATE::STARTED, error = CHIP_ERROR_INTERNAL);
     360           20 :     VerifyOrExit(*out_len >= point_size, error = CHIP_ERROR_INTERNAL);
     361              : 
     362           20 :     ReturnErrorOnFailure(FEGenerate(xy));
     363              : 
     364           20 :     if (role == CHIP_SPAKE2P_ROLE::PROVER)
     365              :     {
     366           10 :         MN = M;
     367           10 :         XY = X;
     368              :     }
     369           10 :     else if (role == CHIP_SPAKE2P_ROLE::VERIFIER)
     370              :     {
     371           10 :         MN = N;
     372           10 :         XY = Y;
     373              :     }
     374           20 :     VerifyOrExit(MN != nullptr, error = CHIP_ERROR_INTERNAL);
     375           20 :     VerifyOrExit(XY != nullptr, error = CHIP_ERROR_INTERNAL);
     376              : 
     377           20 :     SuccessOrExit(error = PointAddMul(XY, G, xy, MN, w0));
     378           20 :     SuccessOrExit(error = PointWrite(XY, out, *out_len));
     379              : 
     380           20 :     state = CHIP_SPAKE2P_STATE::R1;
     381           20 :     error = CHIP_NO_ERROR;
     382           20 : exit:
     383           20 :     *out_len = point_size;
     384           20 :     return error;
     385              : }
     386              : 
     387           20 : CHIP_ERROR Spake2p::ComputeRoundTwo(const uint8_t * in, size_t in_len, uint8_t * out, size_t * out_len)
     388              : {
     389           20 :     CHIP_ERROR error = CHIP_ERROR_INTERNAL;
     390           20 :     MutableByteSpan out_span{ out, *out_len };
     391              :     uint8_t point_buffer[kMAX_Point_Length];
     392           20 :     void * MN        = nullptr; // Choose N if a prover, M if a verifier
     393           20 :     void * XY        = nullptr; // Choose Y if a prover, X if a verifier
     394           20 :     uint8_t * Kcaorb = nullptr; // Choose Kca if a prover, Kcb if a verifier
     395              : 
     396           20 :     VerifyOrExit(*out_len >= hash_size, error = CHIP_ERROR_INTERNAL);
     397           20 :     VerifyOrExit(state == CHIP_SPAKE2P_STATE::R1, error = CHIP_ERROR_INTERNAL);
     398           20 :     VerifyOrExit(in_len == point_size, error = CHIP_ERROR_INTERNAL);
     399              : 
     400           20 :     if (role == CHIP_SPAKE2P_ROLE::PROVER)
     401              :     {
     402           10 :         SuccessOrExit(error = PointWrite(X, point_buffer, point_size));
     403           10 :         SuccessOrExit(error = InternalHash(point_buffer, point_size));
     404           10 :         SuccessOrExit(error = InternalHash(in, in_len));
     405              : 
     406           10 :         MN     = N;
     407           10 :         XY     = Y;
     408           10 :         Kcaorb = Kca;
     409              :     }
     410           10 :     else if (role == CHIP_SPAKE2P_ROLE::VERIFIER)
     411              :     {
     412           10 :         SuccessOrExit(error = InternalHash(in, in_len));
     413           10 :         SuccessOrExit(error = PointWrite(Y, point_buffer, point_size));
     414           10 :         SuccessOrExit(error = InternalHash(point_buffer, point_size));
     415              : 
     416           10 :         MN     = M;
     417           10 :         XY     = X;
     418           10 :         Kcaorb = Kcb;
     419              :     }
     420           20 :     VerifyOrExit(MN != nullptr, error = CHIP_ERROR_INTERNAL);
     421           20 :     VerifyOrExit(XY != nullptr, error = CHIP_ERROR_INTERNAL);
     422              : 
     423           20 :     SuccessOrExit(error = PointLoad(in, in_len, XY));
     424           20 :     SuccessOrExit(error = PointIsValid(XY));
     425           20 :     SuccessOrExit(error = FEMul(tempbn, xy, w0));
     426           20 :     SuccessOrExit(error = PointInvert(MN));
     427           20 :     SuccessOrExit(error = PointAddMul(Z, XY, xy, MN, tempbn));
     428           20 :     SuccessOrExit(error = PointCofactorMul(Z));
     429              : 
     430           20 :     if (role == CHIP_SPAKE2P_ROLE::PROVER)
     431              :     {
     432           10 :         SuccessOrExit(error = FEMul(tempbn, w1, w0));
     433           10 :         SuccessOrExit(error = PointAddMul(V, XY, w1, MN, tempbn));
     434              :     }
     435           10 :     else if (role == CHIP_SPAKE2P_ROLE::VERIFIER)
     436              :     {
     437           10 :         SuccessOrExit(error = PointMul(V, L, xy));
     438              :     }
     439              : 
     440           20 :     SuccessOrExit(error = PointCofactorMul(V));
     441           20 :     SuccessOrExit(error = PointWrite(Z, point_buffer, point_size));
     442           20 :     SuccessOrExit(error = InternalHash(point_buffer, point_size));
     443              : 
     444           20 :     SuccessOrExit(error = PointWrite(V, point_buffer, point_size));
     445           20 :     SuccessOrExit(error = InternalHash(point_buffer, point_size));
     446              : 
     447           20 :     SuccessOrExit(error = FEWrite(w0, point_buffer, fe_size));
     448           20 :     SuccessOrExit(error = InternalHash(point_buffer, fe_size));
     449              : 
     450           20 :     SuccessOrExit(error = GenerateKeys());
     451              : 
     452           20 :     SuccessOrExit(error = Mac(Kcaorb, hash_size / 2, in, in_len, out_span));
     453           20 :     VerifyOrExit(out_span.size() == hash_size, error = CHIP_ERROR_INTERNAL);
     454              : 
     455           20 :     state = CHIP_SPAKE2P_STATE::R2;
     456           20 :     error = CHIP_NO_ERROR;
     457           20 : exit:
     458           20 :     *out_len = hash_size;
     459           20 :     return error;
     460              : }
     461              : 
     462           20 : CHIP_ERROR Spake2p::GenerateKeys()
     463              : {
     464              :     static const uint8_t info_keyconfirm[16] = { 'C', 'o', 'n', 'f', 'i', 'r', 'm', 'a', 't', 'i', 'o', 'n', 'K', 'e', 'y', 's' };
     465              : 
     466           20 :     MutableByteSpan Kae_span{ &Kae[0], sizeof(Kae) };
     467              : 
     468           20 :     ReturnErrorOnFailure(HashFinalize(Kae_span));
     469           20 :     ReturnErrorOnFailure(KDF(Ka, hash_size / 2, nullptr, 0, info_keyconfirm, sizeof(info_keyconfirm), Kcab, hash_size));
     470              : 
     471           20 :     return CHIP_NO_ERROR;
     472              : }
     473              : 
     474           19 : CHIP_ERROR Spake2p::KeyConfirm(const uint8_t * in, size_t in_len)
     475              : {
     476              :     uint8_t point_buffer[kP256_Point_Length];
     477           19 :     void * XY        = nullptr; // Choose X if a prover, Y if a verifier
     478           19 :     uint8_t * Kcaorb = nullptr; // Choose Kcb if a prover, Kca if a verifier
     479              : 
     480           19 :     VerifyOrReturnError(state == CHIP_SPAKE2P_STATE::R2, CHIP_ERROR_INTERNAL);
     481              : 
     482           19 :     if (role == CHIP_SPAKE2P_ROLE::PROVER)
     483              :     {
     484           10 :         XY     = X;
     485           10 :         Kcaorb = Kcb;
     486              :     }
     487            9 :     else if (role == CHIP_SPAKE2P_ROLE::VERIFIER)
     488              :     {
     489            9 :         XY     = Y;
     490            9 :         Kcaorb = Kca;
     491              :     }
     492           19 :     VerifyOrReturnError(XY != nullptr, CHIP_ERROR_INTERNAL);
     493           19 :     VerifyOrReturnError(Kcaorb != nullptr, CHIP_ERROR_INTERNAL);
     494              : 
     495           19 :     ReturnErrorOnFailure(PointWrite(XY, point_buffer, point_size));
     496              : 
     497           19 :     CHIP_ERROR err = MacVerify(Kcaorb, hash_size / 2, in, in_len, point_buffer, point_size);
     498           19 :     if (err == CHIP_ERROR_INTERNAL)
     499              :     {
     500            1 :         ChipLogError(SecureChannel, "Failed to verify peer's MAC. This can happen when setup code is incorrect.");
     501              :     }
     502           19 :     ReturnErrorOnFailure(err);
     503              : 
     504           18 :     state = CHIP_SPAKE2P_STATE::KC;
     505           18 :     return CHIP_NO_ERROR;
     506              : }
     507              : 
     508           18 : CHIP_ERROR Spake2p::GetKeys(SessionKeystore & keystore, HkdfKeyHandle & key)
     509              : {
     510           18 :     VerifyOrReturnError(state == CHIP_SPAKE2P_STATE::KC, CHIP_ERROR_INTERNAL);
     511              : 
     512           18 :     return keystore.CreateKey(ByteSpan(Ke, hash_size / 2), key);
     513              : }
     514              : 
     515          148 : CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::InitImpl()
     516              : {
     517          148 :     ReturnErrorOnFailure(sha256_hash_ctx.Begin());
     518          148 :     ReturnErrorOnFailure(InitInternal());
     519          148 :     return CHIP_NO_ERROR;
     520              : }
     521              : 
     522          507 : CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::Hash(const uint8_t * in, size_t in_len)
     523              : {
     524          507 :     ReturnErrorOnFailure(sha256_hash_ctx.AddData(ByteSpan{ in, in_len }));
     525          507 :     return CHIP_NO_ERROR;
     526              : }
     527              : 
     528           20 : CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::HashFinalize(MutableByteSpan & out_span)
     529              : {
     530           20 :     ReturnErrorOnFailure(sha256_hash_ctx.Finish(out_span));
     531           20 :     return CHIP_NO_ERROR;
     532              : }
     533              : 
     534           20 : CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::KDF(const uint8_t * ikm, const size_t ikm_len, const uint8_t * salt,
     535              :                                               const size_t salt_len, const uint8_t * info, const size_t info_len, uint8_t * out,
     536              :                                               size_t out_len)
     537              : {
     538           20 :     HKDF_sha_crypto mHKDF;
     539              : 
     540           20 :     ReturnErrorOnFailure(mHKDF.HKDF_SHA256(ikm, ikm_len, salt, salt_len, info, info_len, out, out_len));
     541              : 
     542           20 :     return CHIP_NO_ERROR;
     543           20 : }
     544              : 
     545            3 : CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::ComputeW0(uint8_t * w0out, size_t * w0_len, const uint8_t * w0sin, size_t w0sin_len)
     546              : {
     547            3 :     ReturnErrorOnFailure(FELoad(w0sin, w0sin_len, w0));
     548            3 :     ReturnErrorOnFailure(FEWrite(w0, w0out, *w0_len));
     549              : 
     550            3 :     return CHIP_NO_ERROR;
     551              : }
     552              : 
     553            2 : CHIP_ERROR Spake2pVerifier::Serialize(MutableByteSpan & outSerialized) const
     554              : {
     555            2 :     VerifyOrReturnError(outSerialized.size() >= kSpake2p_VerifierSerialized_Length, CHIP_ERROR_INVALID_ARGUMENT);
     556              : 
     557            2 :     memcpy(&outSerialized.data()[0], mW0, sizeof(mW0));
     558            2 :     memcpy(&outSerialized.data()[sizeof(mW0)], mL, sizeof(mL));
     559              : 
     560            2 :     outSerialized.reduce_size(kSpake2p_VerifierSerialized_Length);
     561              : 
     562            2 :     return CHIP_NO_ERROR;
     563              : }
     564              : 
     565            9 : CHIP_ERROR Spake2pVerifier::Deserialize(const ByteSpan & inSerialized)
     566              : {
     567            9 :     VerifyOrReturnError(inSerialized.size() >= kSpake2p_VerifierSerialized_Length, CHIP_ERROR_INVALID_ARGUMENT);
     568              : 
     569            8 :     memcpy(mW0, &inSerialized.data()[0], sizeof(mW0));
     570            8 :     memcpy(mL, &inSerialized.data()[sizeof(mW0)], sizeof(mL));
     571              : 
     572            8 :     return CHIP_NO_ERROR;
     573              : }
     574              : 
     575            3 : CHIP_ERROR Spake2pVerifier::Generate(uint32_t pbkdf2IterCount, const ByteSpan & salt, uint32_t setupPin)
     576              : {
     577            3 :     uint8_t serializedWS[kSpake2p_WS_Length * 2] = { 0 };
     578            3 :     ReturnErrorOnFailure(ComputeWS(pbkdf2IterCount, salt, setupPin, serializedWS, sizeof(serializedWS)));
     579              : 
     580            3 :     CHIP_ERROR err = CHIP_NO_ERROR;
     581              :     size_t len;
     582              : 
     583              :     // Create local Spake2+ object for w0 and L computations.
     584            3 :     Spake2p_P256_SHA256_HKDF_HMAC spake2p;
     585            3 :     uint8_t context[kSHA256_Hash_Length] = { 0 };
     586            3 :     SuccessOrExit(err = spake2p.Init(context, sizeof(context)));
     587              : 
     588              :     // Compute w0
     589            3 :     len = sizeof(mW0);
     590            3 :     SuccessOrExit(err = spake2p.ComputeW0(mW0, &len, &serializedWS[0], kSpake2p_WS_Length));
     591            3 :     VerifyOrExit(len == sizeof(mW0), err = CHIP_ERROR_INTERNAL);
     592              : 
     593              :     // Compute L
     594            3 :     len = sizeof(mL);
     595            3 :     SuccessOrExit(err = spake2p.ComputeL(mL, &len, &serializedWS[kSpake2p_WS_Length], kSpake2p_WS_Length));
     596            3 :     VerifyOrExit(len == sizeof(mL), err = CHIP_ERROR_INTERNAL);
     597              : 
     598            3 : exit:
     599            3 :     spake2p.Clear();
     600            3 :     return err;
     601            3 : }
     602              : 
     603            9 : CHIP_ERROR Spake2pVerifier::ComputeWS(uint32_t pbkdf2IterCount, const ByteSpan & salt, uint32_t setupPin, uint8_t * ws,
     604              :                                       uint32_t ws_len)
     605              : {
     606            9 :     PBKDF2_sha256 pbkdf2;
     607              :     uint8_t littleEndianSetupPINCode[sizeof(uint32_t)];
     608            9 :     Encoding::LittleEndian::Put32(littleEndianSetupPINCode, setupPin);
     609              : 
     610            9 :     VerifyOrReturnError(salt.size() >= kSpake2p_Min_PBKDF_Salt_Length && salt.size() <= kSpake2p_Max_PBKDF_Salt_Length,
     611              :                         CHIP_ERROR_INVALID_ARGUMENT);
     612            9 :     VerifyOrReturnError(pbkdf2IterCount >= kSpake2p_Min_PBKDF_Iterations && pbkdf2IterCount <= kSpake2p_Max_PBKDF_Iterations,
     613              :                         CHIP_ERROR_INVALID_ARGUMENT);
     614              : 
     615            9 :     return pbkdf2.pbkdf2_sha256(littleEndianSetupPINCode, sizeof(littleEndianSetupPINCode), salt.data(), salt.size(),
     616            9 :                                 pbkdf2IterCount, ws_len, ws);
     617            9 : }
     618              : 
     619          625 : CHIP_ERROR ReadDerLength(Reader & reader, size_t & length)
     620              : {
     621          625 :     length = 0;
     622              : 
     623          625 :     uint8_t cur_byte = 0;
     624          625 :     ReturnErrorOnFailure(reader.Read8(&cur_byte).StatusCode());
     625              : 
     626          624 :     if ((cur_byte & (1u << 7)) == 0)
     627              :     {
     628              :         // 7 bit length, the rest of the byte is the length.
     629          553 :         length = cur_byte & 0x7Fu;
     630          553 :         return CHIP_NO_ERROR;
     631              :     }
     632              : 
     633           71 :     CHIP_ERROR err = CHIP_ERROR_INVALID_ARGUMENT;
     634              : 
     635              :     // Did not early return: > 7 bit length, the number of bytes of the length is provided next.
     636           71 :     uint8_t length_bytes = cur_byte & 0x7Fu;
     637           71 :     VerifyOrReturnError((length_bytes >= 1) && (length_bytes <= sizeof(size_t)), CHIP_ERROR_INVALID_ARGUMENT);
     638           70 :     VerifyOrReturnError(reader.HasAtLeast(length_bytes), CHIP_ERROR_BUFFER_TOO_SMALL);
     639              : 
     640          148 :     for (uint8_t i = 0; i < length_bytes; i++)
     641              :     {
     642           83 :         uint8_t cur_length_byte = 0;
     643           83 :         err                     = reader.Read8(&cur_length_byte).StatusCode();
     644           83 :         if (err != CHIP_NO_ERROR)
     645            0 :             break;
     646              : 
     647              :         // Cannot have zero padding on multi-byte lengths in DER, so first
     648              :         // byte must always be > 0.
     649           83 :         if ((i == 0) && (cur_length_byte == 0))
     650              :         {
     651            2 :             return CHIP_ERROR_INVALID_ARGUMENT;
     652              :         }
     653              : 
     654           81 :         length <<= 8;
     655           81 :         length |= cur_length_byte;
     656              :     }
     657              : 
     658              :     // Single-byte long length cannot be < 128: DER always encodes on smallest size
     659              :     // possible, so length zero should have been a single byte short length.
     660           65 :     if ((length_bytes == 1) && (length < 128))
     661              :     {
     662            1 :         return CHIP_ERROR_INVALID_ARGUMENT;
     663              :     }
     664              : 
     665           64 :     return CHIP_NO_ERROR;
     666              : }
     667              : 
     668          261 : CHIP_ERROR ConvertIntegerRawToDerWithoutTag(const ByteSpan & raw_integer, MutableByteSpan & out_der_integer)
     669              : {
     670          261 :     return ConvertIntegerRawToDerInternal(raw_integer, out_der_integer, /* include_tag_and_length = */ false);
     671              : }
     672              : 
     673           24 : CHIP_ERROR ConvertIntegerRawToDer(const ByteSpan & raw_integer, MutableByteSpan & out_der_integer)
     674              : {
     675           24 :     return ConvertIntegerRawToDerInternal(raw_integer, out_der_integer, /* include_tag_and_length = */ true);
     676              : }
     677              : 
     678            5 : CHIP_ERROR EcdsaRawSignatureToAsn1(size_t fe_length_bytes, const ByteSpan & raw_sig, MutableByteSpan & out_asn1_sig)
     679              : {
     680            5 :     VerifyOrReturnError(fe_length_bytes > 0, CHIP_ERROR_INVALID_ARGUMENT);
     681            5 :     VerifyOrReturnError(raw_sig.size() == (2u * fe_length_bytes), CHIP_ERROR_INVALID_ARGUMENT);
     682            5 :     VerifyOrReturnError(out_asn1_sig.size() >= (raw_sig.size() + kMax_ECDSA_X9Dot62_Asn1_Overhead), CHIP_ERROR_BUFFER_TOO_SMALL);
     683              : 
     684              :     // Write both R an S integers past the overhead, we will shift them back later if we only needed 2 size bytes.
     685            5 :     uint8_t * cursor = out_asn1_sig.data() + kMinSequenceOverhead;
     686            5 :     size_t remaining = out_asn1_sig.size() - kMinSequenceOverhead;
     687              : 
     688            5 :     size_t integers_length = 0;
     689              : 
     690              :     // Write R (first `fe_length_bytes` block of raw signature)
     691              :     {
     692            5 :         MutableByteSpan out_der_integer(cursor, remaining);
     693            5 :         ReturnErrorOnFailure(ConvertIntegerRawToDer(raw_sig.SubSpan(0, fe_length_bytes), out_der_integer));
     694            5 :         VerifyOrReturnError(out_der_integer.size() <= remaining, CHIP_ERROR_INTERNAL);
     695              : 
     696            5 :         integers_length += out_der_integer.size();
     697            5 :         remaining -= out_der_integer.size();
     698            5 :         cursor += out_der_integer.size();
     699              :     }
     700              : 
     701              :     // Write S (second `fe_length_bytes` block of raw signature)
     702              :     {
     703            5 :         MutableByteSpan out_der_integer(cursor, remaining);
     704            5 :         ReturnErrorOnFailure(ConvertIntegerRawToDer(raw_sig.SubSpan(fe_length_bytes, fe_length_bytes), out_der_integer));
     705            5 :         VerifyOrReturnError(out_der_integer.size() <= remaining, CHIP_ERROR_INTERNAL);
     706            5 :         integers_length += out_der_integer.size();
     707              :     }
     708              : 
     709              :     // We only support outputs that would use 1 or 2 bytes of DER length after the SEQUENCE tag
     710            5 :     VerifyOrReturnError(integers_length <= UINT8_MAX, CHIP_ERROR_INVALID_ARGUMENT);
     711              : 
     712              :     // We now know the length of both variable sized integers in the sequence, so we
     713              :     // can write the tag and length.
     714            5 :     BufferWriter writer(out_asn1_sig);
     715              : 
     716              :     // Put SEQUENCE tag
     717            5 :     writer.Put(kSeqTag);
     718              : 
     719              :     // Put the length over 1 or two bytes depending on case
     720            5 :     constexpr uint8_t kExtendedLengthMarker = 0x80u;
     721            5 :     if (integers_length > 127u)
     722              :     {
     723            1 :         writer.Put(static_cast<uint8_t>(kExtendedLengthMarker | 1)); // Length is extended length, over 1 subsequent byte
     724            1 :         writer.Put(static_cast<uint8_t>(integers_length));
     725              :     }
     726              :     else
     727              :     {
     728              :         // Length is directly in the first byte with MSB clear if <= 127.
     729            4 :         writer.Put(static_cast<uint8_t>(integers_length));
     730              :     }
     731              : 
     732              :     // Put the contents of the integers previously serialized in the buffer.
     733              :     // The writer.Put is memmove-safe, so the shifting will happen from the read
     734              :     // of the same buffer where the write is taking place.
     735            5 :     writer.Put(out_asn1_sig.data() + kMinSequenceOverhead, integers_length);
     736              : 
     737            5 :     size_t actually_written = 0;
     738            5 :     VerifyOrReturnError(writer.Fit(actually_written), CHIP_ERROR_BUFFER_TOO_SMALL);
     739              : 
     740            5 :     out_asn1_sig = out_asn1_sig.SubSpan(0, actually_written);
     741            5 :     return CHIP_NO_ERROR;
     742              : }
     743              : 
     744          184 : CHIP_ERROR EcdsaAsn1SignatureToRaw(size_t fe_length_bytes, const ByteSpan & asn1_sig, MutableByteSpan & out_raw_sig)
     745              : {
     746          184 :     VerifyOrReturnError(fe_length_bytes > 0, CHIP_ERROR_INVALID_ARGUMENT);
     747          184 :     VerifyOrReturnError(asn1_sig.size() > kMinSequenceOverhead, CHIP_ERROR_BUFFER_TOO_SMALL);
     748              : 
     749              :     // Output raw signature is <r,s> both of which are of fe_length_bytes (see SEC1).
     750          184 :     VerifyOrReturnError(out_raw_sig.size() >= (2u * fe_length_bytes), CHIP_ERROR_BUFFER_TOO_SMALL);
     751              : 
     752          184 :     Reader reader(asn1_sig);
     753              : 
     754              :     // Make sure we have a starting Sequence
     755          184 :     uint8_t tag = 0;
     756          184 :     ReturnErrorOnFailure(reader.Read8(&tag).StatusCode());
     757          184 :     VerifyOrReturnError(tag == kSeqTag, CHIP_ERROR_INVALID_ARGUMENT);
     758              : 
     759              :     // Read length of sequence
     760          184 :     size_t tag_len = 0;
     761          184 :     ReturnErrorOnFailure(ReadDerLength(reader, tag_len));
     762              : 
     763              :     // Length of sequence must match what is left of signature
     764          184 :     VerifyOrReturnError(tag_len == reader.Remaining(), CHIP_ERROR_INVALID_ARGUMENT);
     765              : 
     766              :     // Can now clear raw signature integers r,s one by one
     767          184 :     uint8_t * raw_cursor = out_raw_sig.data();
     768              : 
     769              :     // Read R
     770          184 :     ReturnErrorOnFailure(ReadDerUnsignedIntegerIntoRaw(reader, MutableByteSpan{ raw_cursor, fe_length_bytes }));
     771              : 
     772          184 :     raw_cursor += fe_length_bytes;
     773              : 
     774              :     // Read S
     775          184 :     ReturnErrorOnFailure(ReadDerUnsignedIntegerIntoRaw(reader, MutableByteSpan{ raw_cursor, fe_length_bytes }));
     776              : 
     777          184 :     out_raw_sig = out_raw_sig.SubSpan(0, (2u * fe_length_bytes));
     778              : 
     779          184 :     return CHIP_NO_ERROR;
     780              : }
     781              : 
     782           10 : CHIP_ERROR AES_CTR_crypt(const uint8_t * input, size_t input_length, const Aes128KeyHandle & key, const uint8_t * nonce,
     783              :                          size_t nonce_length, uint8_t * output)
     784              : {
     785              :     // Discard tag portion of CCM to apply only CTR mode encryption/decryption.
     786           10 :     constexpr size_t kTagLen = Crypto::kAES_CCM128_Tag_Length;
     787              :     uint8_t tag[kTagLen];
     788              : 
     789           10 :     return AES_CCM_encrypt(input, input_length, nullptr, 0, key, nonce, nonce_length, output, tag, kTagLen);
     790              : }
     791              : 
     792          814 : CHIP_ERROR GenerateCompressedFabricId(const Crypto::P256PublicKey & root_public_key, uint64_t fabric_id,
     793              :                                       MutableByteSpan & out_compressed_fabric_id)
     794              : {
     795          814 :     VerifyOrReturnError(root_public_key.IsUncompressed(), CHIP_ERROR_INVALID_ARGUMENT);
     796          813 :     VerifyOrReturnError(out_compressed_fabric_id.size() >= kCompressedFabricIdentifierSize, CHIP_ERROR_BUFFER_TOO_SMALL);
     797              : 
     798              :     // Ensure proper endianness for Fabric ID (i.e. big-endian as it appears in certificates)
     799              :     uint8_t fabric_id_as_big_endian_salt[kCompressedFabricIdentifierSize];
     800          812 :     chip::Encoding::BigEndian::Put64(&fabric_id_as_big_endian_salt[0], fabric_id);
     801              : 
     802              :     // Compute Compressed fabric reference per spec pseudocode
     803              :     //   CompressedFabricIdentifier =
     804              :     //     CHIP_Crypto_KDF(
     805              :     //       inputKey := TargetOperationalRootPublicKey,
     806              :     //       salt:= TargetOperationalFabricID,
     807              :     //       info := CompressedFabricInfo,
     808              :     //       len := 64)
     809              :     //
     810              :     // NOTE: len=64 bits is implied by output buffer size when calling HKDF_sha::HKDF_SHA256.
     811              : 
     812          812 :     constexpr uint8_t kCompressedFabricInfo[16] = /* "CompressedFabric" */
     813              :         { 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x46, 0x61, 0x62, 0x72, 0x69, 0x63 };
     814          812 :     HKDF_sha hkdf;
     815              : 
     816              :     // Must drop uncompressed point form format specifier (first byte), per spec method
     817          812 :     ByteSpan input_key_span(root_public_key.ConstBytes() + 1, root_public_key.Length() - 1);
     818              : 
     819          812 :     CHIP_ERROR status = hkdf.HKDF_SHA256(
     820              :         input_key_span.data(), input_key_span.size(), &fabric_id_as_big_endian_salt[0], sizeof(fabric_id_as_big_endian_salt),
     821              :         &kCompressedFabricInfo[0], sizeof(kCompressedFabricInfo), out_compressed_fabric_id.data(), kCompressedFabricIdentifierSize);
     822              : 
     823              :     // Resize output to final bounds on success
     824          812 :     if (status == CHIP_NO_ERROR)
     825              :     {
     826          812 :         out_compressed_fabric_id = out_compressed_fabric_id.SubSpan(0, kCompressedFabricIdentifierSize);
     827              :     }
     828              : 
     829          812 :     return status;
     830          812 : }
     831              : 
     832           11 : CHIP_ERROR GenerateCompressedFabricId(const Crypto::P256PublicKey & rootPublicKey, uint64_t fabricId, uint64_t & compressedFabricId)
     833              : {
     834              :     uint8_t allocated[sizeof(fabricId)];
     835           11 :     MutableByteSpan span(allocated);
     836           11 :     ReturnErrorOnFailure(GenerateCompressedFabricId(rootPublicKey, fabricId, span));
     837              :     // Decode compressed fabric ID accounting for endianness, as GenerateCompressedFabricId()
     838              :     // returns a binary buffer and is agnostic of usage of the output as an integer type.
     839           11 :     compressedFabricId = Encoding::BigEndian::Get64(allocated);
     840           11 :     return CHIP_NO_ERROR;
     841              : }
     842              : 
     843              : /* Operational Group Key Group, Security Info: "GroupKey v1.0" */
     844              : static const uint8_t kGroupSecurityInfo[] = { 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x20, 0x76, 0x31, 0x2e, 0x30 };
     845              : 
     846              : /* Group Key Derivation Function, Info: "GroupKeyHash" ” */
     847              : static const uint8_t kGroupKeyHashInfo[]  = { 0x47, 0x72, 0x6f, 0x75, 0x70, 0x4b, 0x65, 0x79, 0x48, 0x61, 0x73, 0x68 };
     848              : static const uint8_t kGroupKeyHashSalt[0] = {};
     849              : 
     850              : /*
     851              :     OperationalGroupKey =
     852              :         Crypto_KDF
     853              :         (
     854              :             InputKey = Epoch Key,
     855              :             Salt = CompressedFabricIdentifier,
     856              :             Info = "GroupKey v1.0",
     857              :             Length = CRYPTO_SYMMETRIC_KEY_LENGTH_BITS
     858              :         )
     859              : */
     860          200 : CHIP_ERROR DeriveGroupOperationalKey(const ByteSpan & epoch_key, const ByteSpan & compressed_fabric_id, MutableByteSpan & out_key)
     861              : {
     862          200 :     VerifyOrReturnError(Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES == epoch_key.size(), CHIP_ERROR_INVALID_ARGUMENT);
     863          199 :     VerifyOrReturnError(Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES <= out_key.size(), CHIP_ERROR_INVALID_ARGUMENT);
     864              : 
     865          199 :     Crypto::HKDF_sha crypto;
     866          199 :     return crypto.HKDF_SHA256(epoch_key.data(), epoch_key.size(), compressed_fabric_id.data(), compressed_fabric_id.size(),
     867              :                               kGroupSecurityInfo, sizeof(kGroupSecurityInfo), out_key.data(),
     868          199 :                               Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES);
     869          199 : }
     870              : 
     871              : /*
     872              :     GKH = Crypto_KDF (
     873              :         InputKey = OperationalGroupKey,
     874              :         Salt = [],
     875              :         Info = "GroupKeyHash",
     876              :         Length = 16)
     877              : */
     878          199 : CHIP_ERROR DeriveGroupSessionId(const ByteSpan & operational_key, uint16_t & session_id)
     879              : {
     880          199 :     VerifyOrReturnError(Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES == operational_key.size(), CHIP_ERROR_INVALID_ARGUMENT);
     881          198 :     Crypto::HKDF_sha crypto;
     882              :     uint8_t out_key[sizeof(uint16_t)];
     883              : 
     884          198 :     ReturnErrorOnFailure(crypto.HKDF_SHA256(operational_key.data(), operational_key.size(), kGroupKeyHashSalt,
     885              :                                             sizeof(kGroupKeyHashSalt), kGroupKeyHashInfo, sizeof(kGroupKeyHashInfo), out_key,
     886              :                                             sizeof(out_key)));
     887          198 :     session_id = Encoding::BigEndian::Get16(out_key);
     888          198 :     return CHIP_NO_ERROR;
     889          198 : }
     890              : 
     891              : /* Operational Group Key Group, PrivacyKey Info: "PrivacyKey" */
     892              : static const uint8_t kGroupPrivacyInfo[] = { 'P', 'r', 'i', 'v', 'a', 'c', 'y', 'K', 'e', 'y' };
     893              : 
     894              : /*
     895              :     PrivacyKey =
     896              :          Crypto_KDF
     897              :          (
     898              :             InputKey = EncryptionKey,
     899              :             Salt = [],
     900              :             Info = "PrivacyKey",
     901              :             Length = CRYPTO_SYMMETRIC_KEY_LENGTH_BITS
     902              :          )
     903              : */
     904         1085 : CHIP_ERROR DeriveGroupPrivacyKey(const ByteSpan & encryption_key, MutableByteSpan & out_key)
     905              : {
     906         1085 :     VerifyOrReturnError(Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES == encryption_key.size(), CHIP_ERROR_INVALID_ARGUMENT);
     907         1084 :     VerifyOrReturnError(Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES <= out_key.size(), CHIP_ERROR_INVALID_ARGUMENT);
     908              : 
     909         1084 :     constexpr ByteSpan null_span = ByteSpan();
     910              : 
     911         1084 :     Crypto::HKDF_sha crypto;
     912         1084 :     return crypto.HKDF_SHA256(encryption_key.data(), encryption_key.size(), null_span.data(), null_span.size(), kGroupPrivacyInfo,
     913         1084 :                               sizeof(kGroupPrivacyInfo), out_key.data(), Crypto::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES);
     914         1084 : }
     915              : 
     916          196 : CHIP_ERROR DeriveGroupOperationalCredentials(const ByteSpan & epoch_key, const ByteSpan & compressed_fabric_id,
     917              :                                              GroupOperationalCredentials & operational_credentials)
     918              : {
     919          196 :     MutableByteSpan encryption_key(operational_credentials.encryption_key);
     920          196 :     MutableByteSpan privacy_key(operational_credentials.privacy_key);
     921              : 
     922          196 :     ReturnErrorOnFailure(Crypto::DeriveGroupOperationalKey(epoch_key, compressed_fabric_id, encryption_key));
     923          196 :     ReturnErrorOnFailure(Crypto::DeriveGroupSessionId(encryption_key, operational_credentials.hash));
     924          196 :     ReturnErrorOnFailure(Crypto::DeriveGroupPrivacyKey(encryption_key, privacy_key));
     925              : 
     926          196 :     return CHIP_NO_ERROR;
     927              : }
     928              : 
     929            8 : CHIP_ERROR GenerateVendorFabricBindingMessage(FabricBindingVersion fabricBindingVersion, const P256PublicKey & rootPublicKey,
     930              :                                               FabricId fabricId, uint16_t vendorId, MutableByteSpan & outputSpan)
     931              : {
     932              :     // Only V1 supported yet.
     933            8 :     switch (fabricBindingVersion)
     934              :     {
     935            7 :     case FabricBindingVersion::kVersion1:
     936            7 :         break;
     937            1 :     default:
     938            1 :         return CHIP_ERROR_INVALID_ARGUMENT;
     939              :     }
     940              : 
     941            7 :     Encoding::BigEndian::BufferWriter writer(outputSpan);
     942              : 
     943              :     // vendor_fabric_binding_message := fabric_binding_version (1 byte) || root_public_key || fabric_id || vendor_id
     944            7 :     writer.Put8(to_underlying(fabricBindingVersion))
     945            7 :         .Put(rootPublicKey.ConstBytes(), rootPublicKey.Length())
     946            7 :         .Put64(fabricId)
     947            7 :         .Put16(vendorId);
     948              : 
     949            7 :     size_t actuallyWritten = 0;
     950            7 :     VerifyOrReturnError(writer.Fit(actuallyWritten), CHIP_ERROR_BUFFER_TOO_SMALL);
     951              : 
     952            6 :     outputSpan.reduce_size(actuallyWritten);
     953            6 :     return CHIP_NO_ERROR;
     954              : }
     955              : 
     956           13 : CHIP_ERROR GenerateVendorIdVerificationToBeSigned(FabricIndex fabricIndex, const ByteSpan & clientChallenge,
     957              :                                                   const ByteSpan & attestationChallenge,
     958              :                                                   const ByteSpan & vendorFabricBindingMessage,
     959              :                                                   const ByteSpan & vidVerificationStatement, MutableByteSpan & outputSpan)
     960              : {
     961           13 :     VerifyOrReturnError((clientChallenge.size() == kVendorIdVerificationClientChallengeSize) &&
     962              :                             (attestationChallenge.size() == CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES) &&
     963              :                             !vendorFabricBindingMessage.empty(),
     964              :                         CHIP_ERROR_INVALID_ARGUMENT);
     965              : 
     966              :     // Extract binding version from vendorFabricBindingMessage. Only V1 supported yet.
     967            6 :     uint8_t fabricBindingVersion = vendorFabricBindingMessage[0];
     968            6 :     VerifyOrReturnError(fabricBindingVersion == to_underlying(FabricBindingVersion::kVersion1), CHIP_ERROR_INVALID_ARGUMENT);
     969              : 
     970            5 :     Encoding::BigEndian::BufferWriter writer(outputSpan);
     971              : 
     972              :     // vendor_id_verification_tbs := fabric_binding_version || client_challenge || attestation_challenge || fabric_index ||
     973              :     // vendor_fabric_binding_message || <vid_verification_statement>
     974            5 :     writer.Put8(fabricBindingVersion)
     975            5 :         .Put(clientChallenge.data(), clientChallenge.size())
     976            5 :         .Put(attestationChallenge.data(), attestationChallenge.size())
     977            5 :         .Put8(fabricIndex)
     978            5 :         .Put(vendorFabricBindingMessage.data(), vendorFabricBindingMessage.size())
     979            5 :         .Put(vidVerificationStatement.data(), vidVerificationStatement.size());
     980              : 
     981            5 :     size_t actuallyWritten = 0;
     982            5 :     VerifyOrReturnError(writer.Fit(actuallyWritten), CHIP_ERROR_BUFFER_TOO_SMALL);
     983              : 
     984            4 :     outputSpan.reduce_size(actuallyWritten);
     985            4 :     return CHIP_NO_ERROR;
     986              : }
     987              : 
     988          825 : CHIP_ERROR ExtractVIDPIDFromAttributeString(DNAttrType attrType, const ByteSpan & attr,
     989              :                                             AttestationCertVidPid & vidpidFromMatterAttr, AttestationCertVidPid & vidpidFromCNAttr)
     990              : {
     991          825 :     VerifyOrReturnError(attrType != DNAttrType::kUnspecified, CHIP_NO_ERROR);
     992          824 :     VerifyOrReturnError(!attr.empty(), CHIP_ERROR_INVALID_ARGUMENT);
     993              : 
     994          823 :     if (attrType == DNAttrType::kMatterVID || attrType == DNAttrType::kMatterPID)
     995              :     {
     996              :         uint16_t matterAttr;
     997          482 :         VerifyOrReturnError(attr.size() == kVIDandPIDHexLength, CHIP_ERROR_WRONG_CERT_DN);
     998          472 :         VerifyOrReturnError(Encoding::UppercaseHexToUint16(reinterpret_cast<const char *>(attr.data()), attr.size(), matterAttr) ==
     999              :                                 sizeof(matterAttr),
    1000              :                             CHIP_ERROR_WRONG_CERT_DN);
    1001              : 
    1002          468 :         if (attrType == DNAttrType::kMatterVID)
    1003              :         {
    1004              :             // Not more than one VID attribute can be present.
    1005          291 :             VerifyOrReturnError(!vidpidFromMatterAttr.mVendorId.HasValue(), CHIP_ERROR_WRONG_CERT_DN);
    1006          291 :             vidpidFromMatterAttr.mVendorId.SetValue(static_cast<VendorId>(matterAttr));
    1007              :         }
    1008              :         else
    1009              :         {
    1010              :             // Not more than one PID attribute can be present.
    1011          177 :             VerifyOrReturnError(!vidpidFromMatterAttr.mProductId.HasValue(), CHIP_ERROR_WRONG_CERT_DN);
    1012          177 :             vidpidFromMatterAttr.mProductId.SetValue(matterAttr);
    1013              :         }
    1014          468 :     }
    1015              :     // Otherwise, it is a CommonName attribute.
    1016          345 :     else if (!vidpidFromCNAttr.Initialized())
    1017              :     {
    1018          345 :         ByteSpan attr_source_span{ attr };
    1019          345 :         if (attr_source_span.size() > chip::Crypto::kMax_CommonNameAttr_Length)
    1020              :         {
    1021            0 :             attr_source_span.reduce_size(chip::Crypto::kMax_CommonNameAttr_Length);
    1022              :         }
    1023              : 
    1024              :         // Try to find a valid Vendor ID encoded in fallback method.
    1025          345 :         uint16_t vid   = 0;
    1026          345 :         CHIP_ERROR err = Find16BitUpperCaseHexAfterPrefix(attr_source_span, kVIDPrefixForCNEncoding, vid);
    1027          345 :         if (err == CHIP_NO_ERROR)
    1028              :         {
    1029           41 :             vidpidFromCNAttr.mVendorId.SetValue(static_cast<VendorId>(vid));
    1030              :         }
    1031          304 :         else if (err != CHIP_ERROR_NOT_FOUND)
    1032              :         {
    1033              :             // This indicates a bad/ambiguous format.
    1034           20 :             return err;
    1035              :         }
    1036              : 
    1037              :         // Try to find a valid Product ID encoded in fallback method.
    1038          334 :         uint16_t pid = 0;
    1039          334 :         err          = Find16BitUpperCaseHexAfterPrefix(attr_source_span, kPIDPrefixForCNEncoding, pid);
    1040          334 :         if (err == CHIP_NO_ERROR)
    1041              :         {
    1042           31 :             vidpidFromCNAttr.mProductId.SetValue(pid);
    1043              :         }
    1044          303 :         else if (err != CHIP_ERROR_NOT_FOUND)
    1045              :         {
    1046              :             // This indicates a bad/ambiguous format.
    1047            9 :             return err;
    1048              :         }
    1049              :     }
    1050              : 
    1051          793 :     return CHIP_NO_ERROR;
    1052              : }
    1053              : 
    1054              : // Generates the to-be-signed portion of a PKCS#10 CSR (`CertificationRequestInformation`)
    1055              : // that contains the
    1056            2 : static CHIP_ERROR GenerateCertificationRequestInformation(ASN1Writer & writer, const Crypto::P256PublicKey & pubkey)
    1057              : {
    1058            2 :     CHIP_ERROR err = CHIP_NO_ERROR;
    1059              :     /**
    1060              :      *
    1061              :      *  CertificationRequestInfo ::=
    1062              :      *     SEQUENCE {
    1063              :      *        version       INTEGER { v1(0) } (v1,...),
    1064              :      *        subject       Name,
    1065              :      *        subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
    1066              :      *        attributes    [0] Attributes{{ CRIAttributes }}
    1067              :      * }
    1068              :      */
    1069            2 :     ASN1_START_SEQUENCE
    1070              :     {
    1071            2 :         ASN1_ENCODE_INTEGER(0); // version INTEGER { v1(0) }
    1072              : 
    1073              :         // subject Name
    1074            2 :         ASN1_START_SEQUENCE
    1075              :         {
    1076            2 :             ASN1_START_SET
    1077              :             {
    1078            2 :                 ASN1_START_SEQUENCE
    1079              :                 {
    1080              :                     // Any subject, placeholder is good, since this
    1081              :                     // is going to usually be ignored
    1082            2 :                     ASN1_ENCODE_OBJECT_ID(kOID_AttributeType_OrganizationalUnitName);
    1083            2 :                     ASN1_ENCODE_STRING(kASN1UniversalTag_UTF8String, "CSA", static_cast<uint16_t>(strlen("CSA")));
    1084              :                 }
    1085            2 :                 ASN1_END_SEQUENCE;
    1086              :             }
    1087            2 :             ASN1_END_SET;
    1088              :         }
    1089            2 :         ASN1_END_SEQUENCE;
    1090              : 
    1091              :         // subjectPKInfo
    1092            2 :         ASN1_START_SEQUENCE
    1093              :         {
    1094            2 :             ASN1_START_SEQUENCE
    1095              :             {
    1096            2 :                 ASN1_ENCODE_OBJECT_ID(kOID_PubKeyAlgo_ECPublicKey);
    1097            2 :                 ASN1_ENCODE_OBJECT_ID(kOID_EllipticCurve_prime256v1);
    1098              :             }
    1099            2 :             ASN1_END_SEQUENCE;
    1100            2 :             ReturnErrorOnFailure(writer.PutBitString(0, pubkey, static_cast<uint8_t>(pubkey.Length())));
    1101              :         }
    1102            2 :         ASN1_END_SEQUENCE;
    1103              : 
    1104              :         // attributes [0]
    1105            2 :         ASN1_START_CONSTRUCTED(kASN1TagClass_ContextSpecific, 0)
    1106              :         {
    1107              :             // Using a plain empty attributes request
    1108            2 :             ASN1_START_SEQUENCE
    1109              :             {
    1110            2 :                 ASN1_ENCODE_OBJECT_ID(kOID_Extension_CSRRequest);
    1111            2 :                 ASN1_START_SET
    1112              :                 {
    1113            2 :                     ASN1_START_SEQUENCE {}
    1114            2 :                     ASN1_END_SEQUENCE;
    1115              :                 }
    1116            2 :                 ASN1_END_SET;
    1117              :             }
    1118            2 :             ASN1_END_SEQUENCE;
    1119              :         }
    1120            2 :         ASN1_END_CONSTRUCTED;
    1121              :     }
    1122            2 :     ASN1_END_SEQUENCE;
    1123            2 : exit:
    1124            2 :     return err;
    1125              : }
    1126              : 
    1127            3 : CHIP_ERROR GenerateCertificateSigningRequest(const P256Keypair * keypair, MutableByteSpan & csr_span)
    1128              : {
    1129            3 :     VerifyOrReturnError(keypair != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
    1130            2 :     VerifyOrReturnError(csr_span.size() >= kMIN_CSR_Buffer_Size, CHIP_ERROR_BUFFER_TOO_SMALL);
    1131              : 
    1132              :     // First pass: Generate the CertificatioRequestInformation inner
    1133              :     // encoding one time, to sign it, before re-generating it within the
    1134              :     // full ASN1 writer later, since it's easier than trying to
    1135              :     // figure-out the span we need to sign of the overall object.
    1136            1 :     P256ECDSASignature signature;
    1137              : 
    1138              :     {
    1139              :         // The first pass will just generate a signature, so we can use the
    1140              :         // output buffer as scratch to avoid needing more stack space. There
    1141              :         // are no secrets here and the contents is not reused since all we
    1142              :         // need is the signature which is already separately stored.
    1143              :         ASN1Writer toBeSignedWriter;
    1144            1 :         toBeSignedWriter.Init(csr_span);
    1145            1 :         CHIP_ERROR err = GenerateCertificationRequestInformation(toBeSignedWriter, keypair->Pubkey());
    1146            1 :         ReturnErrorOnFailure(err);
    1147              : 
    1148            1 :         size_t encodedLen = (uint16_t) toBeSignedWriter.GetLengthWritten();
    1149              :         // This should not/will not happen
    1150            1 :         if (encodedLen > csr_span.size())
    1151              :         {
    1152            0 :             return CHIP_ERROR_INTERNAL;
    1153              :         }
    1154              : 
    1155            1 :         err = keypair->ECDSA_sign_msg(csr_span.data(), encodedLen, signature);
    1156            1 :         ReturnErrorOnFailure(err);
    1157              :     }
    1158              : 
    1159              :     // Second pass: Generate the entire CSR body, restarting a new write
    1160              :     // of the CertificationRequestInformation (cheap) and adding the
    1161              :     // signature.
    1162              :     //
    1163              :     // See RFC2986 for ASN.1 module, repeated here in snippets
    1164            1 :     CHIP_ERROR err = CHIP_NO_ERROR;
    1165              : 
    1166              :     ASN1Writer writer;
    1167            1 :     writer.Init(csr_span);
    1168              : 
    1169            1 :     ASN1_START_SEQUENCE
    1170              :     {
    1171              : 
    1172              :         /*  CertificationRequestInfo ::=
    1173              :          *     SEQUENCE {
    1174              :          *        version       INTEGER { v1(0) } (v1,...),
    1175              :          *        subject       Name,
    1176              :          *        subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }},
    1177              :          *        attributes    [0] Attributes{{ CRIAttributes }}
    1178              :          *     }
    1179              :          */
    1180            1 :         GenerateCertificationRequestInformation(writer, keypair->Pubkey());
    1181              : 
    1182              :         // algorithm  AlgorithmIdentifier
    1183            1 :         ASN1_START_SEQUENCE
    1184              :         {
    1185              :             // See RFC5480 sec 2.1
    1186            1 :             ASN1_ENCODE_OBJECT_ID(kOID_SigAlgo_ECDSAWithSHA256);
    1187              :         }
    1188            1 :         ASN1_END_SEQUENCE;
    1189              : 
    1190              :         // signature  BIT STRING --> ECDSA-with-SHA256 signature with P256 key with R,S integers format
    1191              :         // (see RFC3279 sec 2.2.3 ECDSA Signature Algorithm)
    1192            1 :         ASN1_START_BIT_STRING_ENCAPSULATED
    1193              :         {
    1194              :             // Convert raw signature to embedded signature
    1195            1 :             FixedByteSpan<Crypto::kP256_ECDSA_Signature_Length_Raw> rawSig(signature.Bytes());
    1196              : 
    1197              :             uint8_t derInt[kP256_FE_Length + kEmitDerIntegerWithoutTagOverhead];
    1198              : 
    1199              :             // Ecdsa-Sig-Value ::= SEQUENCE
    1200            1 :             ASN1_START_SEQUENCE
    1201              :             {
    1202              :                 using P256IntegerSpan = FixedByteSpan<Crypto::kP256_FE_Length>;
    1203              :                 // r INTEGER
    1204              :                 {
    1205            1 :                     MutableByteSpan derIntSpan(derInt, sizeof(derInt));
    1206            1 :                     ReturnErrorOnFailure(ConvertIntegerRawToDerWithoutTag(P256IntegerSpan(rawSig.data()), derIntSpan));
    1207            1 :                     ReturnErrorOnFailure(writer.PutValue(kASN1TagClass_Universal, kASN1UniversalTag_Integer, false,
    1208              :                                                          derIntSpan.data(), static_cast<uint16_t>(derIntSpan.size())));
    1209              :                 }
    1210              : 
    1211              :                 // s INTEGER
    1212              :                 {
    1213            1 :                     MutableByteSpan derIntSpan(derInt, sizeof(derInt));
    1214            1 :                     ReturnErrorOnFailure(
    1215              :                         ConvertIntegerRawToDerWithoutTag(P256IntegerSpan(rawSig.data() + kP256_FE_Length), derIntSpan));
    1216            1 :                     ReturnErrorOnFailure(writer.PutValue(kASN1TagClass_Universal, kASN1UniversalTag_Integer, false,
    1217              :                                                          derIntSpan.data(), static_cast<uint16_t>(derIntSpan.size())));
    1218              :                 }
    1219              :             }
    1220            1 :             ASN1_END_SEQUENCE;
    1221              :         }
    1222            1 :         ASN1_END_ENCAPSULATED;
    1223              :     }
    1224            1 :     ASN1_END_SEQUENCE;
    1225              : 
    1226            1 : exit:
    1227              :     // Update size of output buffer on success
    1228            1 :     if (err == CHIP_NO_ERROR)
    1229              :     {
    1230            1 :         csr_span.reduce_size(writer.GetLengthWritten());
    1231              :     }
    1232            1 :     return err;
    1233            1 : }
    1234              : 
    1235           60 : CHIP_ERROR VerifyCertificateSigningRequestFormat(const uint8_t * csr, size_t csr_length)
    1236              : {
    1237              :     // Ensure we have enough size to validate header, and that our assumptions are met
    1238              :     // for some tag computations below. A csr_length > 65535 would never be seen in
    1239              :     // practice.
    1240           60 :     VerifyOrReturnError((csr_length >= 16) && (csr_length <= 65535), CHIP_ERROR_UNSUPPORTED_CERT_FORMAT);
    1241              : 
    1242           58 :     Reader reader(csr, csr_length);
    1243              : 
    1244              :     // Ensure we have an outermost SEQUENCE
    1245           58 :     uint8_t seq_header = 0;
    1246           58 :     ReturnErrorOnFailure(reader.Read8(&seq_header).StatusCode());
    1247           58 :     VerifyOrReturnError(seq_header == kSeqTag, CHIP_ERROR_UNSUPPORTED_CERT_FORMAT);
    1248              : 
    1249           56 :     size_t seq_length = 0;
    1250           56 :     VerifyOrReturnError(ReadDerLength(reader, seq_length) == CHIP_NO_ERROR, CHIP_ERROR_UNSUPPORTED_CERT_FORMAT);
    1251              :     // Ensure that outer length matches sequence length + tag overhead, otherwise
    1252              :     // we have trailing garbage
    1253           56 :     size_t header_overhead = (seq_length <= 127) ? 2 : ((seq_length <= 255) ? 3 : 4);
    1254           56 :     VerifyOrReturnError(csr_length == (seq_length + header_overhead), CHIP_ERROR_UNSUPPORTED_CERT_FORMAT);
    1255              : 
    1256           52 :     return CHIP_NO_ERROR;
    1257              : }
    1258              : 
    1259           78 : const char * PemEncoder::NextLine()
    1260              : {
    1261           78 :     bool hasLine = false;
    1262              : 
    1263           78 :     switch (mState)
    1264              :     {
    1265            8 :     case State::kPrintHeader: {
    1266              :         // The `.48s` is to make sure the header is not wider than out internal string buffer. We clamp the header.
    1267            8 :         mStringBuilder.Reset().AddFormat("-----BEGIN %.48s-----", mEncodedElement);
    1268            8 :         mState  = mDerBytes.empty() ? State::kPrintFooter : State::kPrintBody;
    1269            8 :         hasLine = true;
    1270            8 :         break;
    1271              :     }
    1272           52 :     case State::kPrintBody: {
    1273           52 :         size_t remaining      = mDerBytes.size() - mProcessedBytes;
    1274           52 :         size_t chunkSizeBytes = std::min(remaining, kNumBytesPerLine);
    1275              : 
    1276              :         {
    1277              :             char base64EncodedBuf[kLineBufferSize];
    1278              :             size_t encodedLen = static_cast<size_t>(
    1279           52 :                 Base64Encode(mDerBytes.data() + mProcessedBytes, static_cast<uint16_t>(chunkSizeBytes), base64EncodedBuf));
    1280           52 :             VerifyOrDie(encodedLen < sizeof(base64EncodedBuf));
    1281           52 :             base64EncodedBuf[encodedLen] = '\0';
    1282           52 :             mStringBuilder.Reset().Add(base64EncodedBuf);
    1283              :         }
    1284              : 
    1285           52 :         mProcessedBytes += chunkSizeBytes;
    1286           52 :         mState  = (mProcessedBytes < mDerBytes.size()) ? State::kPrintBody : State::kPrintFooter;
    1287           52 :         hasLine = true;
    1288           52 :         break;
    1289              :     }
    1290            8 :     case State::kPrintFooter: {
    1291              :         // The `.50s` is to make sure the header is not wider than out internal string buffer. We clamp the footer.
    1292            8 :         mStringBuilder.Reset().AddFormat("-----END %.50s-----", mEncodedElement);
    1293            8 :         mState  = State::kDone;
    1294            8 :         hasLine = true;
    1295            8 :         break;
    1296              :     }
    1297           10 :     case State::kDone:
    1298              :         [[fallthrough]];
    1299              :     default: {
    1300              :         // Default initialized StringBuilder: empty output.
    1301           10 :         mState  = State::kDone;
    1302           10 :         hasLine = false;
    1303           10 :         break;
    1304              :     }
    1305              :     }
    1306              : 
    1307              :     // All the string should have fit based on the logic. It would be a public
    1308              :     // API invariant failure if this ever fails.
    1309           78 :     VerifyOrDie(mStringBuilder.Fit());
    1310              : 
    1311           78 :     return hasLine ? mStringBuilder.c_str() : nullptr;
    1312              : }
    1313              : 
    1314              : } // namespace Crypto
    1315              : } // namespace chip
        

Generated by: LCOV version 2.0-1