Skip to content

Commit

Permalink
Implement a QRCode payload generator
Browse files Browse the repository at this point in the history
  • Loading branch information
Bhaskar-Sarma authored and sagar-apple committed Mar 12, 2020
1 parent 34e2b26 commit 29026a7
Show file tree
Hide file tree
Showing 8 changed files with 543 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
TOPTARGETS := all clean run_tests

SUBDIRS = lwip system lib/core lib/support ble inet
SUBDIRS = lwip system lib/core lib/support ble inet qrcode

$(TOPTARGETS): $(SUBDIRS)
$(SUBDIRS):
Expand Down
28 changes: 28 additions & 0 deletions src/qrcode/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
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

82 changes: 82 additions & 0 deletions src/qrcode/SetupCodeUtils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
*
* <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
36 changes: 36 additions & 0 deletions src/qrcode/SetupCodeUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
*
* <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_ */
46 changes: 46 additions & 0 deletions src/qrcode/SetupPayload.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
*
* <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_ */
172 changes: 172 additions & 0 deletions src/qrcode/SetupPayloadGenerator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
*
* <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;
}
Loading

0 comments on commit 29026a7

Please sign in to comment.