Skip to content

Commit

Permalink
Throw an exception when SetupPayloadParser scans a non-CHIP QR or ent…
Browse files Browse the repository at this point in the history
…ry code. (#5676)
  • Loading branch information
austinh0 authored Apr 7, 2021
1 parent 3a1ba18 commit b382277
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ import android.content.Intent
import android.nfc.NdefMessage
import android.nfc.NfcAdapter
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import chip.setuppayload.SetupPayloadParser
import chip.setuppayload.SetupPayloadParser.UnrecognizedQrCodeException
import com.google.chip.chiptool.attestation.AttestationTestFragment
import com.google.chip.chiptool.clusterclient.OnOffClientFragment
import com.google.chip.chiptool.commissioner.CommissionerActivity
Expand All @@ -36,6 +38,8 @@ import com.google.chip.chiptool.setuppayloadscanner.CHIPDeviceDetailsFragment
import com.google.chip.chiptool.setuppayloadscanner.CHIPDeviceInfo
import com.google.chip.chiptool.setuppayloadscanner.QrCodeInfo
import chip.devicecontroller.PersistentStorage
import chip.setuppayload.SetupPayload
import chip.setuppayload.SetupPayloadParser

class CHIPToolActivity :
AppCompatActivity(),
Expand Down Expand Up @@ -148,8 +152,17 @@ class CHIPToolActivity :
val uri = records[0].toUri()
if (!uri?.scheme.equals("ch", true)) return

// TODO: Issue #4504 - Remove replacing _ with spaces after problem described in #415 will be fixed.
val setupPayload = SetupPayloadParser().parseQrCode(uri.toString().toUpperCase().replace('_', ' '))
lateinit var setupPayload: SetupPayload
try {
// TODO: Issue #4504 - Remove replacing _ with spaces after problem described in #415 will be fixed.
setupPayload =
SetupPayloadParser().parseQrCode(uri.toString().toUpperCase().replace('_', ' '))
} catch (ex: UnrecognizedQrCodeException) {
Log.e(TAG, "Unrecognized QR Code", ex)
Toast.makeText(this, "Unrecognized QR Code", Toast.LENGTH_SHORT).show()
return
}

val deviceInfo = CHIPDeviceInfo(
setupPayload.version,
setupPayload.vendorId,
Expand Down Expand Up @@ -179,6 +192,7 @@ class CHIPToolActivity :
}

companion object {
private const val TAG = "CHIPToolActivity"
private const val ARG_PROVISION_NETWORK_TYPE = "provision_network_type"

var REQUEST_CODE_COMMISSIONING = 0xB003
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.annotation.RequiresPermission
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat.checkSelfPermission
import androidx.fragment.app.Fragment
import chip.setuppayload.SetupPayload
import chip.setuppayload.SetupPayloadParser
import chip.setuppayload.SetupPayloadParser.UnrecognizedQrCodeException
import com.google.android.gms.vision.CameraSource
import com.google.android.gms.vision.barcode.Barcode
import com.google.android.gms.vision.barcode.BarcodeDetector
Expand Down Expand Up @@ -112,20 +115,36 @@ class BarcodeFragment : Fragment(), CHIPBarcodeProcessor.BarcodeDetectionListene
}
}

@SuppressLint("MissingPermission")
override fun handleScannedQrCode(barcode: Barcode) {
Handler(Looper.getMainLooper()).post {
stopCamera()

val payload = SetupPayloadParser().parseQrCode(barcode.displayValue)
lateinit var payload: SetupPayload
try {
payload = SetupPayloadParser().parseQrCode(barcode.displayValue)
} catch (ex: UnrecognizedQrCodeException) {
Log.e(TAG, "Unrecognized QR Code", ex)
Toast.makeText(requireContext(), "Unrecognized QR Code", Toast.LENGTH_SHORT).show()

// Restart camera view.
if (hasCameraPermission() && !cameraStarted) {
startCamera()
}
return@post
}
val deviceInfo = CHIPDeviceInfo(
payload.version,
payload.vendorId,
payload.productId,
payload.discriminator,
payload.setupPinCode,
payload.optionalQRCodeInfo.mapValues { (_, info) -> QrCodeInfo(info.tag, info.type, info.data, info.int32) }
payload.optionalQRCodeInfo.mapValues { (_, info) ->
QrCodeInfo(info.tag, info.type, info.data, info.int32)
}
)
FragmentUtil.getHost(this, Callback::class.java)?.onCHIPDeviceInfoReceived(deviceInfo)
FragmentUtil.getHost(this, Callback::class.java)
?.onCHIPDeviceInfoReceived(deviceInfo)
}
}

Expand Down
97 changes: 85 additions & 12 deletions src/setup_payload/java/SetupPayloadParser-JNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "QRCodeSetupPayloadParser.h"

#include <support/CHIPMem.h>
#include <support/CodeUtils.h>
#include <support/logging/CHIPLogging.h>

#include <vector>
Expand All @@ -10,9 +11,21 @@

using namespace chip;

#define SETUP_PAYLOAD_PARSER_JNI_ERROR_MIN 10000
#define SETUP_PAYLOAD_PARSER_JNI_ERROR_MAX 10999

#define _SETUP_PAYLOAD_PARSER_JNI_ERROR(e) (SETUP_PAYLOAD_PARSER_JNI_ERROR_MIN + (e))

#define SETUP_PAYLOAD_PARSER_JNI_ERROR_EXCEPTION_THROWN _SETUP_PAYLOAD_PARSER_JNI_ERROR(0)
#define SETUP_PAYLOAD_PARSER_JNI_ERROR_TYPE_NOT_FOUND _SETUP_PAYLOAD_PARSER_JNI_ERROR(1)
#define SETUP_PAYLOAD_PARSER_JNI_ERROR_METHOD_NOT_FOUND _SETUP_PAYLOAD_PARSER_JNI_ERROR(2)
#define SETUP_PAYLOAD_PARSER_JNI_ERROR_FIELD_NOT_FOUND _SETUP_PAYLOAD_PARSER_JNI_ERROR(3)

#define JNI_METHOD(RETURN, METHOD_NAME) extern "C" JNIEXPORT RETURN JNICALL Java_chip_setuppayload_SetupPayloadParser_##METHOD_NAME

static jobject TransformSetupPayload(JNIEnv * env, SetupPayload & payload);
static CHIP_ERROR ThrowUnrecognizedQRCodeException(JNIEnv * env, jstring qrCodeObj);
static CHIP_ERROR ThrowInvalidEntryCodeFormatException(JNIEnv * env, jstring entryCodeObj);

jint JNI_OnLoad(JavaVM * jvm, void * reserved)
{
Expand All @@ -23,30 +36,48 @@ jint JNI_OnLoad(JavaVM * jvm, void * reserved)

JNI_METHOD(jobject, fetchPayloadFromQrCode)(JNIEnv * env, jobject self, jstring qrCodeObj)
{
CHIP_ERROR err;
const char * qrString = NULL;
QRCodeSetupPayloadParser * qrPayloadParser = NULL;
CHIP_ERROR err = CHIP_NO_ERROR;
const char * qrString = NULL;
SetupPayload payload;

qrString = env->GetStringUTFChars(qrCodeObj, 0);
qrPayloadParser = new QRCodeSetupPayloadParser(qrString);
qrString = env->GetStringUTFChars(qrCodeObj, 0);

err = qrPayloadParser->populatePayload(payload);
err = QRCodeSetupPayloadParser(qrString).populatePayload(payload);
env->ReleaseStringUTFChars(qrCodeObj, qrString);

if (err != CHIP_NO_ERROR)
{
err = ThrowUnrecognizedQRCodeException(env, qrCodeObj);
if (err != CHIP_NO_ERROR)
{
ChipLogError(SetupPayload, "Error throwing UnrecognizedQRCodeException: %d", err);
}
return nullptr;
}

return TransformSetupPayload(env, payload);
}

JNI_METHOD(jobject, fetchPayloadFromManualEntryCode)(JNIEnv * env, jobject self, jstring entryCode)
{
CHIP_ERROR err;
const char * entryCodeString = NULL;
ManualSetupPayloadParser * manualSetupPayloadParser = NULL;
CHIP_ERROR err = CHIP_NO_ERROR;
const char * entryCodeString = NULL;
SetupPayload payload;

entryCodeString = env->GetStringUTFChars(entryCode, 0);
manualSetupPayloadParser = new ManualSetupPayloadParser(entryCodeString);
entryCodeString = env->GetStringUTFChars(entryCode, 0);
env->ReleaseStringUTFChars(entryCode, entryCodeString);

err = manualSetupPayloadParser->populatePayload(payload);
err = ManualSetupPayloadParser(entryCodeString).populatePayload(payload);

if (err != CHIP_NO_ERROR)
{
err = ThrowInvalidEntryCodeFormatException(env, entryCode);
if (err != CHIP_NO_ERROR)
{
ChipLogError(SetupPayload, "Error throwing InvalidEntryCodeFormatException: %d", err);
}
return nullptr;
}

return TransformSetupPayload(env, payload);
}
Expand Down Expand Up @@ -131,3 +162,45 @@ jobject TransformSetupPayload(JNIEnv * env, SetupPayload & payload)

return setupPayload;
}

CHIP_ERROR ThrowUnrecognizedQRCodeException(JNIEnv * env, jstring qrCodeObj)
{
CHIP_ERROR err = CHIP_NO_ERROR;
jclass exceptionCls = nullptr;
jmethodID exceptionConstructor = nullptr;
jthrowable exception = nullptr;

env->ExceptionClear();

exceptionCls = env->FindClass("chip/setuppayload/SetupPayloadParser$UnrecognizedQrCodeException");
VerifyOrExit(exceptionCls != NULL, err = SETUP_PAYLOAD_PARSER_JNI_ERROR_TYPE_NOT_FOUND);
exceptionConstructor = env->GetMethodID(exceptionCls, "<init>", "(Ljava/lang/String;)V");
VerifyOrExit(exceptionConstructor != NULL, err = SETUP_PAYLOAD_PARSER_JNI_ERROR_METHOD_NOT_FOUND);
exception = (jthrowable) env->NewObject(exceptionCls, exceptionConstructor, qrCodeObj);
VerifyOrExit(exception != NULL, err = SETUP_PAYLOAD_PARSER_JNI_ERROR_EXCEPTION_THROWN);

env->Throw(exception);
exit:
return err;
}

CHIP_ERROR ThrowInvalidEntryCodeFormatException(JNIEnv * env, jstring entryCodeObj)
{
CHIP_ERROR err = CHIP_NO_ERROR;
jclass exceptionCls = nullptr;
jmethodID exceptionConstructor = nullptr;
jthrowable exception = nullptr;

env->ExceptionClear();

exceptionCls = env->FindClass("chip/setuppayload/SetupPayloadParser$InvalidEntryCodeFormatException");
VerifyOrExit(exceptionCls != NULL, err = SETUP_PAYLOAD_PARSER_JNI_ERROR_TYPE_NOT_FOUND);
exceptionConstructor = env->GetMethodID(exceptionCls, "<init>", "(Ljava/lang/String;)V");
VerifyOrExit(exceptionConstructor != NULL, err = SETUP_PAYLOAD_PARSER_JNI_ERROR_METHOD_NOT_FOUND);
exception = (jthrowable) env->NewObject(exceptionCls, exceptionConstructor, entryCodeObj);
VerifyOrExit(exception != NULL, err = SETUP_PAYLOAD_PARSER_JNI_ERROR_EXCEPTION_THROWN);

env->Throw(exception);
exit:
return err;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,39 @@
public class SetupPayloadParser {

/** Returns {@link SetupPayload} parsed from the QR code string. */
public SetupPayload parseQrCode(String qrCodeString) {
public SetupPayload parseQrCode(String qrCodeString) throws UnrecognizedQrCodeException {
return fetchPayloadFromQrCode(qrCodeString);
}

/** Returns {@link SetupPayload} parsed from the manual entry code string. */
public SetupPayload parseManualEntryCode(String entryCodeString) {
public SetupPayload parseManualEntryCode(String entryCodeString)
throws InvalidEntryCodeFormatException {
return fetchPayloadFromManualEntryCode(entryCodeString);
}

private native SetupPayload fetchPayloadFromQrCode(String qrCodeString);
private native SetupPayload fetchPayloadFromQrCode(String qrCodeString)
throws UnrecognizedQrCodeException;

private native SetupPayload fetchPayloadFromManualEntryCode(String entryCodeString);
private native SetupPayload fetchPayloadFromManualEntryCode(String entryCodeString)
throws InvalidEntryCodeFormatException;

static {
System.loadLibrary("SetupPayloadParser");
}

public static class UnrecognizedQrCodeException extends Exception {
private static final long serialVersionUID = 1L;

public UnrecognizedQrCodeException(String qrCode) {
super(String.format("Invalid QR code string: %s", qrCode), null);
}
}

public static class InvalidEntryCodeFormatException extends Exception {
private static final long serialVersionUID = 1L;

public InvalidEntryCodeFormatException(String entryCode) {
super(String.format("Invalid format for entry code string: %s", entryCode), null);
}
}
}

0 comments on commit b382277

Please sign in to comment.