Skip to content
Snippets Groups Projects
Commit 5c6a3511 authored by Bhaskar Sarma's avatar Bhaskar Sarma Committed by Sagar Dhawan
Browse files

Implement a QRCode payload generator

parent 1da29946
No related branches found
No related tags found
No related merge requests found
TOPTARGETS := all clean check
SUBDIRS = lwip system lib/core lib/support ble inet
SUBDIRS = lwip system lib/core lib/support ble inet qrcode
$(TOPTARGETS): $(SUBDIRS)
$(SUBDIRS):
......
TOP_DIR = ../..
Test_Dir = tests
.PHONY: all clean run_tests
all: run_tests
include $(TOP_DIR)/.yams/cpp_rules.min
Module_Includes = \
-I. \
-I$(TOP_DIR)/src/include \
-I$(TOP_DIR)/src/lib/ \
-I$(TOP_DIR)/build/config/standalone/
Module_Test_Includes = $(Module_Includes)
CPP_Files = \
SetupCodeUtils.cpp \
SetupPayloadGenerator.cpp \
libqr.a: $(CPP_Objects)
ar rvs $@ $^
@echo "LINK => $@"
clean: my_clean
@rm -f $(LIB_NAME) *.gcda *.gcno *.gcov
/*
*
* <COPYRIGHT>
*
* 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 implements converting an input into a Base45 String
*
*/
#include "SetupCodeUtils.h"
#include <map>
#include <string>
using namespace std;
namespace chip {
map <int, char> base45CharacterMap()
{
map <int, char> characterMap;
// Special characters
characterMap[36] = ' ';
characterMap[37] = '$';
characterMap[38] = '%';
characterMap[39] = '*';
characterMap[40] = '+';
characterMap[41] = '-';
characterMap[42] = '.';
characterMap[43] = '/';
characterMap[44] = ':';
return characterMap;
}
string base45EncodedString(uint64_t input, size_t minLength)
{
static std::map<int, char> BS45_CHARS = base45CharacterMap();
string result;
int radix = 45;
do {
int remainder = input % radix;
char base45char;
if (remainder >= 0 && remainder <= 9) {
// Numbers
base45char = '0' + remainder;
}
else if (remainder >= 10 && remainder <= 35) {
// Uppercase Characters
base45char = 'A' + (remainder - 10);
}
else {
// Special Characters
base45char = BS45_CHARS[remainder];
}
result += base45char;
input = input / radix;
} while (input != 0);
while (result.length() < minLength) {
result.append("0");
}
reverse(result.begin(), result.end());
return result;
}
} // namespace chip
/*
*
* <COPYRIGHT>
*
* 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
* Utility header to encode an input into a Base45 String
*/
#ifndef _SETUP_CODE_UTILS_H_
#define _SETUP_CODE_UTILS_H_
#include <string>
using namespace std;
namespace chip {
std::string base45EncodedString(uint64_t input, size_t minLength);
} // namespace chip
#endif /* _SETUP_CODE_UTILS_H_ */
\ No newline at end of file
/*
*
* <COPYRIGHT>
*
* 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 QRCode Setup Payload class to hold
* data enumerated from a byte stream
*/
#ifndef _SETUP_PAYLOAD_H_
#define _SETUP_PAYLOAD_H_
#include <stdint.h>
namespace chip {
class SetupPayload
{
public:
uint8_t version;
uint16_t vendorID;
uint16_t productID;
uint8_t requiresCustomFlow;
uint16_t rendezvousInformation;
uint16_t discriminator;
uint32_t setUpPINCode;
};
}; // namespace chip
#endif /* _SETUP_PAYLOAD_H_ */
\ No newline at end of file
/*
*
* <COPYRIGHT>
*
* 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 implements a QRCode Setup Payload generator in accordance
* with the CHIP specification.
*
*/
#include "SetupPayloadGenerator.h"
#include "SetupCodeUtils.h"
#include <iostream>
#include <vector>
using namespace chip;
using namespace std;
void SetupPayloadGenerator::resetBitSet()
{
mPayloadBitsIndex = kTotalPayloadDataSizeInBits;
mPayloadBits.reset();
}
// Populates numberOfBits starting from LSB of input into mPayloadBits
void SetupPayloadGenerator::populateInteger(uint64_t input, size_t numberOfBits)
{
int numberOfBitsModified = 0;
mPayloadBitsIndex -= numberOfBits;
int currentIndex = mPayloadBitsIndex;
int endIndex = currentIndex + (numberOfBits - 1);
int itercount = 1;
while (currentIndex <= endIndex) {
itercount++;
input & 1 ? mPayloadBits.set(currentIndex) : mPayloadBits.reset(currentIndex);
currentIndex++;
input /= 2;
}
}
void SetupPayloadGenerator::populateVersion()
{
populateInteger(mPayload.version, kVersionFieldLengthInBits);
}
void SetupPayloadGenerator::populateVendorID()
{
populateInteger(mPayload.vendorID, kVendorIDFieldLengthInBits);
}
void SetupPayloadGenerator::populateProductID()
{
populateInteger(mPayload.productID, kProductIDFieldLengthInBits);
}
void SetupPayloadGenerator::populateCustomFlowRequiredField()
{
populateInteger(mPayload.requiresCustomFlow, kCustomFlowRequiredFieldLengthInBits);
}
void SetupPayloadGenerator::populateRendezVousInfo()
{
populateInteger(mPayload.rendezvousInformation, kRendezvousInfoFieldLengthInBits);
}
void SetupPayloadGenerator::populateDiscriminator()
{
populateInteger(mPayload.discriminator, kPayloadDiscriminatorFieldLengthInBits);
}
void SetupPayloadGenerator::populateSetupPIN()
{
populateInteger(mPayload.setUpPINCode, kSetupPINCodeFieldLengthInBits);
}
void SetupPayloadGenerator::populateReservedField()
{
populateInteger(0, kReservedFieldLengthInBits);
}
void SetupPayloadGenerator::generateBitSet()
{
resetBitSet();
populateVersion();
populateVendorID();
populateProductID();
populateCustomFlowRequiredField();
populateRendezVousInfo();
populateDiscriminator();
populateSetupPIN();
populateReservedField();
}
string SetupPayloadGenerator::payloadBinaryRepresentation()
{
generateBitSet();
return mPayloadBits.to_string();
}
// This function assumes bits.size() % 8 == 0
// TODO: Can this method be written in a more elegant way?
vector<uint16_t> arrayFromBits(bitset<kTotalPayloadDataSizeInBits> bits)
{
vector<uint16_t> resultVector;
size_t numberOfBits = bits.size();
size_t numberOfBytes = numberOfBits / 8;
bool oddNumOfBytes = (numberOfBytes % 2) ? true : false;
// Traversing in reverse, hence startIndex > endIndex
int endIndex = 0;
int startIndex = bits.size() - 1;
/*
Every 2 bytes (16 bits) of binary source data are encoded to 3 characters of the Base-45 alphabet.
If an odd number of bytes are to be encoded, the remaining single byte will be encoded
to 2 characters of the Base-45 alphabet.
*/
if (oddNumOfBytes) {
endIndex = 8;
}
while (startIndex > endIndex) {
int currentIntegerIndex = startIndex;
uint16_t result = 0;
for (int i = currentIntegerIndex; i > currentIntegerIndex - 16; i--) {
result = result << 1;
result = result | bits.test(i);
}
resultVector.push_back(result);
startIndex -= 16;
}
// If we have odd number of bytes append the last byte.
if (oddNumOfBytes) {
uint16_t result = 0;
for (int i = 7; i >= 0 ; i--) {
result = result << 1;
result = result & bits.test(i);
}
resultVector.push_back(result);
}
return resultVector;
}
string SetupPayloadGenerator::payloadBase45Representation()
{
generateBitSet();
vector<uint16_t> integerArray = arrayFromBits(mPayloadBits);
string result;
for (int idx = 0; idx < integerArray.size(); idx++) {
result += base45EncodedString(integerArray[idx], 3);
}
return result;
}
/*
*
* <COPYRIGHT>
*
* 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 QRCode Setup Payload generator based on the
* CHIP specification.
*
* The encoding of the binary data to a base45 string is as follows:
* - Every 2 bytes (16 bits) of binary source data are encoded to 3
* characters of the Base-45 alphabet.
* - If an odd number of bytes are to be encoded, the remaining
* single byte is encoded to 2 characters of the Base-45 alphabet.
*/
#include "SetupPayload.h"
#include <bitset>
#include <string>
using namespace std;
#ifndef _SETUP_PAYLOAD_GENERATOR_
#define _SETUP_PAYLOAD_GENERATOR_
const int kVersionFieldLengthInBits = 3;
const int kVendorIDFieldLengthInBits = 16;
const int kProductIDFieldLengthInBits = 16;
const int kCustomFlowRequiredFieldLengthInBits = 1;
const int kRendezvousInfoFieldLengthInBits = 8;
const int kPayloadDiscriminatorFieldLengthInBits = 8;
const int kSetupPINCodeFieldLengthInBits = 27;
const int kReservedFieldLengthInBits = 1;
const int kTotalPayloadDataSizeInBits = ( kVersionFieldLengthInBits + \
kVendorIDFieldLengthInBits + \
kProductIDFieldLengthInBits + \
kCustomFlowRequiredFieldLengthInBits +
kRendezvousInfoFieldLengthInBits + \
kPayloadDiscriminatorFieldLengthInBits + \
kSetupPINCodeFieldLengthInBits + \
kReservedFieldLengthInBits
);
namespace chip {
class SetupPayloadGenerator
{
private:
bitset <kTotalPayloadDataSizeInBits> mPayloadBits;
SetupPayload mPayload;
// points to the current index within the bitset
int mPayloadBitsIndex;
void populateInteger(uint64_t input, size_t numberOfBits);
void populateVersion();
void populateVendorID();
void populateProductID();
void populateCustomFlowRequiredField();
void populateRendezVousInfo();
void populateDiscriminator();
void populateSetupPIN();
void populateReservedField();
void resetBitSet();
void generateBitSet();
public:
string payloadBinaryRepresentation();
string payloadBase45Representation();
SetupPayloadGenerator(SetupPayload setupPayload) : mPayload(setupPayload) {};
};
}; // namespace chip
#endif /* _SETUP_PAYLOAD_GENERATOR_ */
\ No newline at end of file
/*
*
* <COPYRIGHT>
*
* 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.
*/
#include <assert.h>
#include <iostream>
#include "SetupCodeUtils.cpp"
#include "SetupPayload.h"
#include "SetupPayloadGenerator.cpp"
using namespace chip;
using namespace std;
void testPayloadByteArrayRep() {
printf("---Running Test--- %s\n", __FUNCTION__);
SetupPayload payload;
payload.version = 5;
payload.vendorID = 12;
payload.productID = 1;
payload.requiresCustomFlow = 0;
payload.rendezvousInformation = 1;
payload.discriminator = 128;
payload.setUpPINCode = 2048;
SetupPayloadGenerator generator(payload);
string result = generator.payloadBinaryRepresentation();
string expectedResult =
"101000000000000110000000000000000010000000011000000000000000000000010000"
"00000000";
assert(result.compare(expectedResult) == 0);
}
void testPayloadBase45Rep() {
printf("---Running Test--- %s\n", __FUNCTION__);
SetupPayload payload;
payload.version = 5;
payload.vendorID = 12;
payload.productID = 1;
payload.requiresCustomFlow = 0;
payload.rendezvousInformation = 1;
payload.discriminator = 128;
payload.setUpPINCode = 2048;
SetupPayloadGenerator generator(payload);
string result = generator.payloadBase45Representation();
string expectedResult = "KABG8842Q000211";
assert(result.compare(expectedResult) == 0);
}
void testBase45Encoding() {
printf("---Running Test--- %s\n", __FUNCTION__);
uint16_t input = 10;
string result = base45EncodedString(input, 3);
string expectedResult = "00A";
assert(result.compare(expectedResult) == 0);
}
void testBitsetLen() {
printf("---Running Test--- %s\n", __FUNCTION__);
assert(kTotalPayloadDataSizeInBits % 8 == 0);
}
int main(int argc, char** argv) {
printf("---Running Test--- tests from %s\n", __FILE__);
testBitsetLen();
testPayloadByteArrayRep();
testPayloadBase45Rep();
testBase45Encoding();
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment