Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement a QRCode payload generator #46

Merged
merged 7 commits into from
Mar 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 check

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

$(TOPTARGETS): $(SUBDIRS)
$(SUBDIRS):
Expand Down
29 changes: 29 additions & 0 deletions src/setup_payload/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
TOP_DIR = ../..
Test_Dir = tests

.PHONY: all clean check

all: libqr.a check

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 \
SetupPayload.cpp \
QRCodeSetupPayloadGenerator.cpp \

libqr.a: $(CPP_Objects)
ar rvs $@ $^
@echo "LINK => $@"

clean: my_clean
@rm -f libqr.a *.gcda *.gcno *.gcov

189 changes: 189 additions & 0 deletions src/setup_payload/QRCodeSetupPayloadGenerator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/*
*
* <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 "QRCodeSetupPayloadGenerator.h"
#include "SetupCodeUtils.h"

#include <iostream>
#include <vector>

#include <stdlib.h>

using namespace chip;
using namespace std;

void QRCodeSetupPayloadGenerator::resetBitSet()
{
mPayloadBitsIndex = kTotalPayloadDataSizeInBits;
mPayloadBits.reset();
}

// Populates numberOfBits starting from LSB of input into mPayloadBits
void QRCodeSetupPayloadGenerator::populateInteger(uint64_t input, size_t numberOfBits)
{
if (mPayloadBitsIndex < numberOfBits || input >= 1 << numberOfBits)
{
abort();
}
mPayloadBitsIndex -= numberOfBits;
int currentIndex = mPayloadBitsIndex;

int endIndex = currentIndex + (numberOfBits - 1);

while (currentIndex <= endIndex)
{
input & 1 ? mPayloadBits.set(currentIndex) : mPayloadBits.reset(currentIndex);
currentIndex++;
input /= 2;
}
}

void QRCodeSetupPayloadGenerator::populateVersion()
{
populateInteger(mPayload.version, kVersionFieldLengthInBits);
}

void QRCodeSetupPayloadGenerator::populateVendorID()
{
populateInteger(mPayload.vendorID, kVendorIDFieldLengthInBits);
}

void QRCodeSetupPayloadGenerator::populateProductID()
{
populateInteger(mPayload.productID, kProductIDFieldLengthInBits);
}

void QRCodeSetupPayloadGenerator::populateCustomFlowRequiredField()
{
populateInteger(mPayload.requiresCustomFlow, kCustomFlowRequiredFieldLengthInBits);
}

void QRCodeSetupPayloadGenerator::populateRendezvousInfo()
{
populateInteger(mPayload.rendezvousInformation, kRendezvousInfoFieldLengthInBits);
}

void QRCodeSetupPayloadGenerator::populateDiscriminator()
{
populateInteger(mPayload.discriminator, kPayloadDiscriminatorFieldLengthInBits);
}

void QRCodeSetupPayloadGenerator::populateSetupPIN()
{
populateInteger(mPayload.setUpPINCode, kSetupPINCodeFieldLengthInBits);
}

void QRCodeSetupPayloadGenerator::populateReservedField()
{
populateInteger(0, kReservedFieldLengthInBits);
}

void QRCodeSetupPayloadGenerator::generateBitSet()
{
resetBitSet();
populateVersion();
populateVendorID();
populateProductID();
populateCustomFlowRequiredField();
populateRendezvousInfo();
populateDiscriminator();
populateSetupPIN();
populateReservedField();
}

string QRCodeSetupPayloadGenerator::payloadBinaryRepresentation()
{
if (mPayload.isValid())
{
generateBitSet();
return mPayloadBits.to_string();
}
return 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 QRCodeSetupPayloadGenerator::payloadBase45Representation()
{
if (mPayload.isValid())
{
generateBitSet();
vector<uint16_t> integerArray = arrayFromBits(mPayloadBits);
string result;
for (int idx = 0; idx < integerArray.size(); idx++)
{
result += base45EncodedString(integerArray[idx], 3);
}
return result;
}
return string();
}
69 changes: 69 additions & 0 deletions src/setup_payload/QRCodeSetupPayloadGenerator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
*
* <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 _QR_CODE_SETUP_PAYLOAD_GENERATOR_
#define _QR_CODE_SETUP_PAYLOAD_GENERATOR_

namespace chip {

class QRCodeSetupPayloadGenerator
{
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();
QRCodeSetupPayloadGenerator(SetupPayload setupPayload) : mPayload(setupPayload){};
};

}; // namespace chip

#endif /* _QR_CODE_SETUP_PAYLOAD_GENERATOR_ */
57 changes: 57 additions & 0 deletions src/setup_payload/SetupCodeUtils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
*
* <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 <string>
#include <algorithm>

using namespace std;

namespace chip {

char base45CharacterSet[45] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':' };

string base45EncodedString(uint64_t input, size_t minLength)
{
string result;
int radix = 45;
do
{
char base45char = base45CharacterSet[input % radix];
result += base45char;
input = input / radix;
} while (input != 0);

while (result.length() < minLength)
{
result.append("0");
}

reverse(result.begin(), result.end());
return result;
}

} // namespace chip
37 changes: 37 additions & 0 deletions src/setup_payload/SetupCodeUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
*
* <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>
#include <stdint.h>

using namespace std;

namespace chip {

std::string base45EncodedString(uint64_t input, size_t minLength);

} // namespace chip

#endif /* _SETUP_CODE_UTILS_H_ */
Loading