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

Changes to sync QR code for Desktop and Android #11199

Merged
merged 8 commits into from
Nov 30, 2021
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
15 changes: 15 additions & 0 deletions android/java/org/chromium/chrome/browser/BraveSyncWorker.java
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,18 @@ public String GetWordsFromSeedHex(String seedHex) {
return BraveSyncWorkerJni.get().getWordsFromSeedHex(seedHex);
}

public String GetQrDataJson(String seedHex) {
return BraveSyncWorkerJni.get().getQrDataJson(seedHex);
}

public int GetQrCodeValidationResult(String jsonQr) {
return BraveSyncWorkerJni.get().getQrCodeValidationResult(jsonQr);
}

public String GetSeedHexFromQrJson(String jsonQr) {
return BraveSyncWorkerJni.get().getSeedHexFromQrJson(jsonQr);
}

public void RequestSync() {
BraveSyncWorkerJni.get().requestSync(mNativeBraveSyncWorker);
}
Expand Down Expand Up @@ -189,6 +201,9 @@ interface Natives {

String getSeedHexFromWords(String passphrase);
String getWordsFromSeedHex(String seedHex);
String getQrDataJson(String seedHex);
int getQrCodeValidationResult(String jsonQr);
String getSeedHexFromQrJson(String jsonQr);
void saveCodeWords(long nativeBraveSyncWorker, String passphrase);

void finalizeSyncSetup(long nativeBraveSyncWorker);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@ public void onSyncError(String message) {
@Override
public void run() {
showEndDialog(
getResources().getString(R.string.sync_device_failure) + messageFinal);
getResources().getString(R.string.sync_device_failure) + messageFinal,
() -> {});
}
});
} catch (Exception exc) {
Expand Down Expand Up @@ -708,7 +709,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) {
// This function used when we have scanned the QR code to connect to the chain
private void seedHexReceived(String seedHex) {
assert seedHex != null && !seedHex.isEmpty();
assert isBarCodeValid(seedHex);
assert isSeedHexValid(seedHex);

if (null == getActivity()) {
return;
Expand Down Expand Up @@ -944,8 +945,8 @@ public void onDestroy() {

// Barcode is valid when its raw representation has length of 64
// and when it's possible to convert the barcode to bip39 code words
private boolean isBarCodeValid(String barcode) {
Log.v(TAG, "isBarCodeValid barcode length=" + barcode.length());
private boolean isSeedHexValid(String barcode) {
Log.v(TAG, "isSeedHexValid barcode length=" + barcode.length());
if (barcode == null) {
Log.e(TAG, "Barcode is empty");
return false;
Expand All @@ -962,36 +963,63 @@ private boolean isBarCodeValid(String barcode) {
return true;
}

private String getQrCodeValidationString(String jsonQr) {
Log.v(TAG, "getQrCodeValidationString jsonQr length=" + jsonQr.length());
int validationResult = getBraveSyncWorker().GetQrCodeValidationResult(jsonQr);
Log.v(TAG, "validationResult is " + validationResult);
switch (validationResult) {
case 0:
// kValid, empty string indicates there is no error
return "";
case 3:
// kVersionDeprecated
return getResources().getString(R.string.brave_sync_code_from_deprecated_version);
case 4:
// kExpired
return getResources().getString(R.string.brave_sync_code_expired);
default:
// These three different types of errors have the same message
// kNotWellFormed
// kVersionNotRecognized
// kValidForTooLong
return getResources().getString(R.string.brave_sync_wrong_qrcode_error);
}
}

private final Object mQrInProcessingLock = new Object();
private boolean mQrInProcessing;

@Override
public void onDetectedQrCode(Barcode barcode) {
if (barcode != null) {
final String barcodeValue = barcode.displayValue;
if (!isBarCodeValid(barcodeValue)) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
showEndDialog(
getResources().getString(R.string.brave_sync_wrong_qrcode_error));
showMainSyncScrypt();
}
});
return;
}

synchronized (mQrInProcessingLock) {
// If camera looks on the QR image and final security warning is shown,
// we could arrive here again and show 2nd alert while the 1st is not yet closed
if (mQrInProcessing) {
return;
}

mQrInProcessing = true;
}

String seedHex = barcodeValue;
final String jsonQr = barcode.displayValue;
String validationError = getQrCodeValidationString(jsonQr);

if (!validationError.isEmpty()) {
getActivity().runOnUiThread(() -> {
showEndDialog(validationError, new Runnable() {
@Override
public void run() {
synchronized (mQrInProcessingLock) {
assert mQrInProcessing;
mQrInProcessing = false;
}
}
});
});
return;
}

String seedHex = getBraveSyncWorker().GetSeedHexFromQrJson(jsonQr);

// It is supposed to be
// getActivity().runOnUiThread(() -> {
Expand Down Expand Up @@ -1030,7 +1058,7 @@ public void run() {
}
}

private void showEndDialog(String message) {
private void showEndDialog(String message, Runnable runWhenDismissed) {
AlertDialog.Builder alert =
new AlertDialog.Builder(getActivity(), R.style.Theme_Chromium_AlertDialog);
if (null == alert) {
Expand All @@ -1046,6 +1074,7 @@ public void onClick(DialogInterface dialog, int button) {}
.setPositiveButton(R.string.ok, onClickListener)
.create();
alertDialog.getDelegate().setHandleNativeActionModesEnabled(false);
alertDialog.setOnDismissListener((dialog) -> { runWhenDismissed.run(); });
alertDialog.show();
}

Expand Down Expand Up @@ -1236,21 +1265,25 @@ public void run() {
Log.e(TAG, "setAddMobileDeviceLayout seedHex is empty");
assert false;
} else {
fillQrCode(seedHex);
if (!isSeedHexValid(seedHex)) {
Log.e(TAG, "fillQrCode - invalid QR code");
// Normally must not reach here ever, because the code is validated right
// after scan
assert false;
showEndDialog(
getResources().getString(R.string.sync_device_failure), () -> {});
return;
} else {
String qrCodeString = getBraveSyncWorker().GetQrDataJson(seedHex);
assert qrCodeString != null && !qrCodeString.isEmpty();
fillQrCode(qrCodeString);
}
}
}
});
}

private void fillQrCode(String qrDataFinal) {
if (!isBarCodeValid(qrDataFinal)) {
Log.e(TAG, "fillQrCode - invalid QR code");
// Normally must not reach here ever, because the code is validated right after scan
assert false;
showEndDialog(getResources().getString(R.string.sync_device_failure));
return;
}

new Thread(new Runnable() {
@Override
public void run() {
Expand Down
63 changes: 57 additions & 6 deletions browser/android/brave_sync_worker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include "brave/build/android/jni_headers/BraveSyncWorker_jni.h"
#include "brave/components/brave_sync/brave_sync_prefs.h"
#include "brave/components/brave_sync/crypto/crypto.h"
#include "brave/components/brave_sync/qr_code_data.h"
#include "brave/components/brave_sync/qr_code_validator.h"
#include "brave/components/brave_sync/sync_service_impl_helper.h"
#include "brave/components/sync/driver/brave_sync_service_impl.h"

Expand Down Expand Up @@ -294,11 +296,7 @@ JNI_BraveSyncWorker_GetSeedHexFromWords(
return base::android::ConvertUTF8ToJavaString(env, sync_code_hex);
}

static base::android::ScopedJavaLocalRef<jstring>
JNI_BraveSyncWorker_GetWordsFromSeedHex(
JNIEnv* env,
const base::android::JavaParamRef<jstring>& seed_hex) {
std::string str_seed_hex = base::android::ConvertJavaStringToUTF8(seed_hex);
std::string GetWordsFromSeedHex(const std::string& str_seed_hex) {
DCHECK(!str_seed_hex.empty());

std::vector<uint8_t> bytes;
Expand All @@ -308,7 +306,8 @@ JNI_BraveSyncWorker_GetWordsFromSeedHex(
if (bytes.size(), SEED_BYTES_COUNT) {
sync_code_words = brave_sync::crypto::PassphraseFromBytes32(bytes);
if (sync_code_words.empty()) {
VLOG(1) << __func__ << " PassphraseFromBytes32 failed for " << seed_hex;
VLOG(1) << __func__ << " PassphraseFromBytes32 failed for "
<< str_seed_hex;
}
} else {
LOG(ERROR) << "wrong seed bytes " << bytes.size();
Expand All @@ -318,8 +317,60 @@ JNI_BraveSyncWorker_GetWordsFromSeedHex(
VLOG(1) << __func__ << " HexStringToBytes failed for " << str_seed_hex;
}

return sync_code_words;
}

static base::android::ScopedJavaLocalRef<jstring>
JNI_BraveSyncWorker_GetWordsFromSeedHex(
JNIEnv* env,
const base::android::JavaParamRef<jstring>& seed_hex) {
std::string str_seed_hex = base::android::ConvertJavaStringToUTF8(seed_hex);
std::string sync_code_words = GetWordsFromSeedHex(str_seed_hex);
return base::android::ConvertUTF8ToJavaString(env, sync_code_words);
}

static base::android::ScopedJavaLocalRef<jstring>
JNI_BraveSyncWorker_GetQrDataJson(
JNIEnv* env,
const base::android::JavaParamRef<jstring>& seed_hex) {
std::string str_seed_hex = base::android::ConvertJavaStringToUTF8(seed_hex);
DCHECK(!str_seed_hex.empty());

const std::string qr_code_string =
brave_sync::QrCodeData::CreateWithActualDate(str_seed_hex)->ToJson();

return base::android::ConvertUTF8ToJavaString(env, qr_code_string);
}

int JNI_BraveSyncWorker_GetQrCodeValidationResult(
JNIEnv* env,
const base::android::JavaParamRef<jstring>& json_qr) {
std::string str_json_qr = base::android::ConvertJavaStringToUTF8(json_qr);
DCHECK(!str_json_qr.empty());
return static_cast<int>(
brave_sync::QrCodeDataValidator::ValidateQrDataJson(str_json_qr));
}

static base::android::ScopedJavaLocalRef<jstring>
JNI_BraveSyncWorker_GetSeedHexFromQrJson(
JNIEnv* env,
const base::android::JavaParamRef<jstring>& json_qr) {
std::string str_json_qr = base::android::ConvertJavaStringToUTF8(json_qr);
DCHECK(!str_json_qr.empty());

auto qr_data = brave_sync::QrCodeData::FromJson(str_json_qr);

std::string result;
if (qr_data) {
result = qr_data->sync_code_hex;
} else {
result = str_json_qr;
}

DCHECK(!GetWordsFromSeedHex(result).empty());

return base::android::ConvertUTF8ToJavaString(env, result);
}

} // namespace android
} // namespace chrome
6 changes: 6 additions & 0 deletions browser/ui/android/strings/android_brave_strings.grd
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,12 @@ until they verify, or until 90 days have passed.
<message name="IDS_BRAVE_SYNC_WRONG_QRCODE_ERROR" desc="Text for wrong Brave Sync QR code error.">
Wrong sync QR code
</message>
<message name="IDS_BRAVE_SYNC_CODE_FROM_DEPRECATED_VERSION" desc="Text for error when trying to use old version sync code after v1 sunset date.">
This code was generated by a deprecated version of Brave, please upgrade your other device first
</message>
<message name="IDS_BRAVE_SYNC_CODE_EXPIRED" desc="Text for error when trying to the sync code which has expired.">
This code has expired. Please generate a new one on your other device and try again.
</message>
<message name="IDS_BRAVE_SYNC_OFFICIAL" desc="Text for Brave Sync preference.">
Brave Sync
</message>
Expand Down
5 changes: 4 additions & 1 deletion browser/ui/webui/settings/brave_sync_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "base/strings/string_number_conversions.h"
#include "brave/components/brave_sync/brave_sync_prefs.h"
#include "brave/components/brave_sync/crypto/crypto.h"
#include "brave/components/brave_sync/qr_code_data.h"
#include "brave/components/brave_sync/sync_service_impl_helper.h"
#include "brave/components/sync/driver/brave_sync_service_impl.h"
#include "brave/components/sync_device_info/brave_device_info.h"
Expand Down Expand Up @@ -109,6 +110,8 @@ void BraveSyncHandler::HandleGetQRCode(base::Value::ConstListView args) {
// QR code version 3 can only carry 84 bytes so we hex encode 32 bytes
// seed then we will have 64 bytes input data
const std::string sync_code_hex = base::HexEncode(seed.data(), seed.size());
const std::string qr_code_string =
brave_sync::QrCodeData::CreateWithActualDate(sync_code_hex)->ToJson();

base::Value callback_id_disconnect(args[0].Clone());
base::Value callback_id_arg(args[0].Clone());
Expand All @@ -123,7 +126,7 @@ void BraveSyncHandler::HandleGetQRCode(base::Value::ConstListView args) {

qrcode_generator::mojom::GenerateQRCodeRequestPtr request =
qrcode_generator::mojom::GenerateQRCodeRequest::New();
request->data = sync_code_hex;
request->data = qr_code_string;
request->should_render = true;
request->render_dino = false;
request->render_module_style =
Expand Down
28 changes: 28 additions & 0 deletions components/brave_sync/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,20 @@ source_set("prefs") {
]
}

source_set("qr_code_data") {
sources = [
"qr_code_data.cc",
"qr_code_data.h",
"qr_code_validator.cc",
"qr_code_validator.h",
]

deps = [
":crypto",
"//base",
]
}

static_library("sync_service_impl_helper") {
sources = [
"sync_service_impl_helper.cc",
Expand All @@ -89,10 +103,24 @@ group("brave_sync") {
":features",
":network_time_helper",
":prefs",
":qr_code_data",
"//base",
]
}

group("constants") {
public_configs = [ ":brave_sync_config" ]
}

source_set("unit_tests") {
testonly = true
sources = [
"//brave/components/brave_sync/qr_code_data_unittest.cc",
"//brave/components/brave_sync/qr_code_validator_unittest.cc",
]

deps = [
":qr_code_data",
"//base/test:test_support",
]
}
Loading