blob: 0db56ac00e2b69714e543600a0892ced67d74a44 [file] [log] [blame]
/**
*
* Copyright (c) 2020 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file describes a Manul Setup Payload parser based on the
* CHIP specification.
*/
#include "ManualSetupPayloadParser.h"
#include <support/logging/CHIPLogging.h>
#include <support/verhoeff/Verhoeff.h>
#include <iostream>
#include <math.h>
#include <string>
#include <vector>
using namespace chip;
using namespace std;
static CHIP_ERROR checkDecimalStringValidity(string decimalString, string & decimalStringWithoutCheckDigit)
{
if (decimalString.length() < 2)
{
ChipLogError(SetupPayload, "Failed decoding base10. Input was empty. %zu", decimalString.length());
return CHIP_ERROR_INVALID_STRING_LENGTH;
}
string repWithoutCheckChar = decimalString.substr(0, decimalString.length() - 1);
char checkChar = decimalString.back();
if (!Verhoeff10::ValidateCheckChar(checkChar, repWithoutCheckChar.c_str()))
{
return CHIP_ERROR_INTEGRITY_CHECK_FAILED;
}
decimalStringWithoutCheckDigit = repWithoutCheckChar;
return CHIP_NO_ERROR;
}
static CHIP_ERROR checkCodeLengthValidity(string decimalString, bool isLongCode)
{
size_t expectedCharLength = isLongCode ? kManualSetupLongCodeCharLength : kManualSetupShortCodeCharLength;
if (decimalString.length() != expectedCharLength)
{
ChipLogError(SetupPayload, "Failed decoding base10. Input length %zu was not expected length %zu", decimalString.length(),
expectedCharLength);
return CHIP_ERROR_INVALID_STRING_LENGTH;
}
return CHIP_NO_ERROR;
}
// Extract n bits starting at index i and store it in dest
static CHIP_ERROR extractBits(uint32_t number, uint64_t & dest, int index, int numberBits, int maxBits)
{
if ((index + numberBits) > maxBits)
{
ChipLogError(SetupPayload, "Number %u maxBits %d index %d n %d", number, maxBits, index, numberBits);
return CHIP_ERROR_INVALID_STRING_LENGTH;
}
dest = (((1 << numberBits) - 1) & (number >> index));
return CHIP_NO_ERROR;
}
static CHIP_ERROR toNumber(string decimalString, uint64_t & dest)
{
uint64_t number = 0;
for (uint i = 0; i < decimalString.length(); i++)
{
if (!isdigit(decimalString[i]))
{
ChipLogError(SetupPayload, "Failed decoding base10. Character was invalid %c", decimalString[i]);
return CHIP_ERROR_INVALID_INTEGER_VALUE;
}
number *= 10;
number += decimalString[i] - '0';
}
dest = number;
return CHIP_NO_ERROR;
}
// Populate numberOfChars into dest from decimalString starting at startIndex (least significant digit = left-most digit)
static CHIP_ERROR readDigitsFromDecimalString(string decimalString, int & index, uint64_t & dest, size_t numberOfCharsToRead)
{
if (decimalString.length() < numberOfCharsToRead || (numberOfCharsToRead + index > decimalString.length()))
{
ChipLogError(SetupPayload, "Failed decoding base10. Input was too short. %zu", decimalString.length());
return CHIP_ERROR_INVALID_STRING_LENGTH;
}
else if (index < 0)
{
ChipLogError(SetupPayload, "Failed decoding base10. Index was negative. %d", index);
return CHIP_ERROR_INVALID_ARGUMENT;
}
string decimalSubstring = decimalString.substr(index, numberOfCharsToRead);
index += numberOfCharsToRead;
return toNumber(decimalSubstring, dest);
}
// Populate numberOfBits into dest from number starting at startIndex (LSB = right-most bit)
static CHIP_ERROR readBitsFromNumber(int32_t number, int & index, uint64_t & dest, size_t numberOfBitsToRead, size_t maxBits)
{
uint64_t bits = 0;
CHIP_ERROR result = extractBits(number, bits, index, numberOfBitsToRead, maxBits);
if (result != CHIP_NO_ERROR)
{
return result;
}
index += numberOfBitsToRead;
dest = bits;
return CHIP_NO_ERROR;
}
CHIP_ERROR ManualSetupPayloadParser::populatePayload(SetupPayload & outPayload)
{
CHIP_ERROR result = CHIP_NO_ERROR;
SetupPayload payload;
string representationWithoutCheckDigit;
result = checkDecimalStringValidity(mDecimalStringRepresentation, representationWithoutCheckDigit);
if (result != CHIP_NO_ERROR)
{
return result;
}
int stringOffset = 0;
uint64_t shortCode;
result = readDigitsFromDecimalString(representationWithoutCheckDigit, stringOffset, shortCode, kManualSetupShortCodeCharLength);
if (result != CHIP_NO_ERROR)
{
return result;
}
bool isLongCode = (shortCode & 1) == 1;
result = checkCodeLengthValidity(representationWithoutCheckDigit, isLongCode);
if (result != CHIP_NO_ERROR)
{
return result;
}
int numberOffset = 1;
uint64_t setUpPINCode;
size_t maxShortCodeBitsLength = 1 + kSetupPINCodeFieldLengthInBits + kManualSetupDiscriminatorFieldLengthInBits;
uint64_t discriminator;
result = readBitsFromNumber(shortCode, numberOffset, discriminator, kManualSetupDiscriminatorFieldLengthInBits,
maxShortCodeBitsLength);
if (result != CHIP_NO_ERROR)
{
return result;
}
result = readBitsFromNumber(shortCode, numberOffset, setUpPINCode, kSetupPINCodeFieldLengthInBits, maxShortCodeBitsLength);
if (result != CHIP_NO_ERROR)
{
return result;
}
else if (setUpPINCode == 0)
{
ChipLogError(SetupPayload, "Failed decoding base10. SetUpPINCode was 0.");
return CHIP_ERROR_INVALID_ARGUMENT;
}
if (isLongCode)
{
uint64_t vendorID;
result =
readDigitsFromDecimalString(representationWithoutCheckDigit, stringOffset, vendorID, kManualSetupVendorIdCharLength);
if (result != CHIP_NO_ERROR)
{
return result;
}
uint64_t productID;
result =
readDigitsFromDecimalString(representationWithoutCheckDigit, stringOffset, productID, kManualSetupProductIdCharLength);
if (result != CHIP_NO_ERROR)
{
return result;
}
outPayload.vendorID = vendorID;
outPayload.productID = productID;
}
outPayload.setUpPINCode = setUpPINCode;
outPayload.discriminator = discriminator;
return result;
}