Passkeys: Set BE and BS flags to true (#13042)

Passkeys: Set BE flag to true

---------

Co-authored-by: varjolintu <sami.vanttinen@ahmala.org>
This commit is contained in:
Sami Vänttinen 2026-03-08 14:08:15 +02:00 committed by GitHub
parent 6f6076ab81
commit 1ce2ce9bb8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 68 additions and 27 deletions

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2025 KeePassXC Team <team@keepassxc.org> * Copyright (C) 2026 KeePassXC Team <team@keepassxc.org>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -125,14 +125,16 @@ PublicKeyCredential BrowserPasskeys::buildRegisterPublicKeyCredential(const QJso
QJsonObject BrowserPasskeys::buildGetPublicKeyCredential(const QJsonObject& assertionOptions, QJsonObject BrowserPasskeys::buildGetPublicKeyCredential(const QJsonObject& assertionOptions,
const QString& credentialId, const QString& credentialId,
const QString& userHandle, const QString& userHandle,
const QString& privateKeyPem) const QString& privateKeyPem,
const bool beFlag,
const bool bsFlag)
{ {
if (!passkeyUtils()->checkCredentialAssertionOptions(assertionOptions)) { if (!passkeyUtils()->checkCredentialAssertionOptions(assertionOptions)) {
return {}; return {};
} }
const auto authenticatorData = const auto authenticatorData = buildAuthenticatorData(
buildAuthenticatorData(assertionOptions["rpId"].toString(), assertionOptions["extensions"].toString()); assertionOptions["rpId"].toString(), assertionOptions["extensions"].toString(), beFlag, bsFlag);
const auto clientDataJson = assertionOptions["clientDataJson"].toString(); const auto clientDataJson = assertionOptions["clientDataJson"].toString();
const auto clientDataArray = clientDataJson.toUtf8(); const auto clientDataArray = clientDataJson.toUtf8();
@ -171,8 +173,12 @@ QByteArray BrowserPasskeys::buildAttestationObject(const QJsonObject& credential
result.append(rpIdHash); result.append(rpIdHash);
// Use default flags // Use default flags
const auto flags = setFlagsFromJson(QJsonObject( const auto flags = setFlagsFromJson(QJsonObject({{"ED", !extensions.isEmpty()},
{{"ED", !extensions.isEmpty()}, {"AT", true}, {"BS", false}, {"BE", false}, {"UV", true}, {"UP", true}})); {"AT", true},
{"BS", DEFAULT_BS_FLAG},
{"BE", DEFAULT_BE_FLAG},
{"UV", true},
{"UP", true}}));
result.append(flags); result.append(flags);
// Signature counter (not supported, always 0 // Signature counter (not supported, always 0
@ -204,7 +210,10 @@ QByteArray BrowserPasskeys::buildAttestationObject(const QJsonObject& credential
} }
// Build a short version of the attestation object for webauthn.get // Build a short version of the attestation object for webauthn.get
QByteArray BrowserPasskeys::buildAuthenticatorData(const QString& rpId, const QString& extensions) QByteArray BrowserPasskeys::buildAuthenticatorData(const QString& rpId,
const QString& extensions,
const bool beFlag,
const bool bsFlag)
{ {
QByteArray result; QByteArray result;
@ -212,7 +221,7 @@ QByteArray BrowserPasskeys::buildAuthenticatorData(const QString& rpId, const QS
result.append(rpIdHash); result.append(rpIdHash);
const auto flags = setFlagsFromJson(QJsonObject( const auto flags = setFlagsFromJson(QJsonObject(
{{"ED", !extensions.isEmpty()}, {"AT", false}, {"BS", false}, {"BE", false}, {"UV", true}, {"UP", true}})); {{"ED", !extensions.isEmpty()}, {"AT", false}, {"BS", bsFlag}, {"BE", beFlag}, {"UV", true}, {"UP", true}}));
result.append(flags); result.append(flags);
// Signature counter (not supported, always 0 // Signature counter (not supported, always 0

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org> * Copyright (C) 2026 KeePassXC Team <team@keepassxc.org>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -25,6 +25,8 @@
#include <botan/asn1_obj.h> #include <botan/asn1_obj.h>
#include <botan/bigint.h> #include <botan/bigint.h>
#define DEFAULT_BE_FLAG true
#define DEFAULT_BS_FLAG true
#define ID_BYTES 32 #define ID_BYTES 32
#define HASH_BYTES 32 #define HASH_BYTES 32
#define RSA_BITS 2048 #define RSA_BITS 2048
@ -87,7 +89,9 @@ public:
QJsonObject buildGetPublicKeyCredential(const QJsonObject& assertionOptions, QJsonObject buildGetPublicKeyCredential(const QJsonObject& assertionOptions,
const QString& credentialId, const QString& credentialId,
const QString& userHandle, const QString& userHandle,
const QString& privateKeyPem); const QString& privateKeyPem,
const bool beFlag = DEFAULT_BE_FLAG,
const bool bsFlag = DEFAULT_BE_FLAG);
static const QString AAGUID; static const QString AAGUID;
@ -113,7 +117,10 @@ private:
const QString& credentialId, const QString& credentialId,
const QByteArray& cborEncodedPublicKey, const QByteArray& cborEncodedPublicKey,
const TestingVariables& testingVariables = {}); const TestingVariables& testingVariables = {});
QByteArray buildAuthenticatorData(const QString& rpId, const QString& extensions); QByteArray buildAuthenticatorData(const QString& rpId,
const QString& extensions,
const bool beFlag = DEFAULT_BE_FLAG,
const bool bsFlag = DEFAULT_BE_FLAG);
AttestationKeyPair buildCredentialPrivateKey(int alg, const TestingVariables& testingVariables = {}); AttestationKeyPair buildCredentialPrivateKey(int alg, const TestingVariables& testingVariables = {});
QByteArray QByteArray
buildSignature(const QByteArray& authenticatorData, const QByteArray& clientData, const QString& privateKeyPem); buildSignature(const QByteArray& authenticatorData, const QByteArray& clientData, const QString& privateKeyPem);

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2025 KeePassXC Team <team@keepassxc.org> * Copyright (C) 2026 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com> * Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
* Copyright (C) 2013 Francois Ferrand * Copyright (C) 2013 Francois Ferrand
* *
@ -775,8 +775,16 @@ QJsonObject BrowserService::showPasskeysAuthenticationPrompt(const QJsonObject&
const auto credentialId = passkeyUtils()->getCredentialIdFromEntry(selectedEntry); const auto credentialId = passkeyUtils()->getCredentialIdFromEntry(selectedEntry);
const auto userHandle = selectedEntry->attributes()->value(EntryAttributes::KPEX_PASSKEY_USER_HANDLE); const auto userHandle = selectedEntry->attributes()->value(EntryAttributes::KPEX_PASSKEY_USER_HANDLE);
auto publicKeyCredential = // Get BE and BS flags if present
browserPasskeys()->buildGetPublicKeyCredential(assertionOptions, credentialId, userHandle, privateKeyPem); const auto beFlag = selectedEntry->attributes()->hasKey(EntryAttributes::KPEX_PASSKEY_FLAG_BE)
? selectedEntry->attributes()->value(EntryAttributes::KPEX_PASSKEY_FLAG_BE) == TRUE_STR
: DEFAULT_BE_FLAG;
const auto bsFlag = selectedEntry->attributes()->hasKey(EntryAttributes::KPEX_PASSKEY_FLAG_BS)
? selectedEntry->attributes()->value(EntryAttributes::KPEX_PASSKEY_FLAG_BS) == TRUE_STR
: DEFAULT_BS_FLAG;
auto publicKeyCredential = browserPasskeys()->buildGetPublicKeyCredential(
assertionOptions, credentialId, userHandle, privateKeyPem, beFlag, bsFlag);
if (publicKeyCredential.isEmpty()) { if (publicKeyCredential.isEmpty()) {
return getPasskeyError(ERROR_PASSKEYS_UNKNOWN_ERROR); return getPasskeyError(ERROR_PASSKEYS_UNKNOWN_ERROR);
} }
@ -856,6 +864,8 @@ void BrowserService::addPasskeyToEntry(Entry* entry,
entry->attributes()->set(EntryAttributes::KPEX_PASSKEY_PRIVATE_KEY_PEM, privateKey, true); entry->attributes()->set(EntryAttributes::KPEX_PASSKEY_PRIVATE_KEY_PEM, privateKey, true);
entry->attributes()->set(EntryAttributes::KPEX_PASSKEY_RELYING_PARTY, rpId); entry->attributes()->set(EntryAttributes::KPEX_PASSKEY_RELYING_PARTY, rpId);
entry->attributes()->set(EntryAttributes::KPEX_PASSKEY_USER_HANDLE, userHandle, true); entry->attributes()->set(EntryAttributes::KPEX_PASSKEY_USER_HANDLE, userHandle, true);
entry->attributes()->set(EntryAttributes::KPEX_PASSKEY_FLAG_BE, TRUE_STR);
entry->attributes()->set(EntryAttributes::KPEX_PASSKEY_FLAG_BS, TRUE_STR);
entry->addTag(tr("Passkey")); entry->addTag(tr("Passkey"));
entry->endUpdate(); entry->endUpdate();

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org> * Copyright (C) 2026 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de> * Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@ -46,6 +46,8 @@ const QString EntryAttributes::KPEX_PASSKEY_RELYING_PARTY = QStringLiteral("KPEX
const QString EntryAttributes::KPEX_PASSKEY_USER_HANDLE = QStringLiteral("KPEX_PASSKEY_USER_HANDLE"); const QString EntryAttributes::KPEX_PASSKEY_USER_HANDLE = QStringLiteral("KPEX_PASSKEY_USER_HANDLE");
const QString EntryAttributes::KPEX_PASSKEY_PRIVATE_KEY_START = QStringLiteral("-----BEGIN PRIVATE KEY-----"); const QString EntryAttributes::KPEX_PASSKEY_PRIVATE_KEY_START = QStringLiteral("-----BEGIN PRIVATE KEY-----");
const QString EntryAttributes::KPEX_PASSKEY_PRIVATE_KEY_END = QStringLiteral("-----END PRIVATE KEY-----"); const QString EntryAttributes::KPEX_PASSKEY_PRIVATE_KEY_END = QStringLiteral("-----END PRIVATE KEY-----");
const QString EntryAttributes::KPEX_PASSKEY_FLAG_BE = QStringLiteral("KPEX_PASSKEY_FLAG_BE");
const QString EntryAttributes::KPEX_PASSKEY_FLAG_BS = QStringLiteral("KPEX_PASSKEY_FLAG_BS");
// For compatibility with StrongBox // For compatibility with StrongBox
const QString EntryAttributes::KPEX_PASSKEY_GENERATED_USER_ID = QStringLiteral("KPEX_PASSKEY_GENERATED_USER_ID"); const QString EntryAttributes::KPEX_PASSKEY_GENERATED_USER_ID = QStringLiteral("KPEX_PASSKEY_GENERATED_USER_ID");

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org> * Copyright (C) 2026 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de> * Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
@ -75,6 +75,8 @@ public:
static const QString KPEX_PASSKEY_USER_HANDLE; static const QString KPEX_PASSKEY_USER_HANDLE;
static const QString KPEX_PASSKEY_PRIVATE_KEY_START; static const QString KPEX_PASSKEY_PRIVATE_KEY_START;
static const QString KPEX_PASSKEY_PRIVATE_KEY_END; static const QString KPEX_PASSKEY_PRIVATE_KEY_END;
static const QString KPEX_PASSKEY_FLAG_BE;
static const QString KPEX_PASSKEY_FLAG_BS;
static bool isDefaultAttribute(const QString& key); static bool isDefaultAttribute(const QString& key);
static bool isPasskeyAttribute(const QString& key); static bool isPasskeyAttribute(const QString& key);

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2025 KeePassXC Team <team@keepassxc.org> * Copyright (C) 2026 KeePassXC Team <team@keepassxc.org>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -77,7 +77,7 @@ const QString PublicKeyCredential = R"(
"id": "yrzFJ5lwcpTwYMOdXSmxF5b5cYQlqBMzbbU_d-oFLO8", "id": "yrzFJ5lwcpTwYMOdXSmxF5b5cYQlqBMzbbU_d-oFLO8",
"rawId": "cabcc52799707294f060c39d5d29b11796f9718425a813336db53f77ea052cef", "rawId": "cabcc52799707294f060c39d5d29b11796f9718425a813336db53f77ea052cef",
"response": { "response": {
"attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVikdKbqkhPJnC90siSSsyDPQCYqlMGpUKA5fyklC2CEHvBFAAAAAP2xQbJdhEQ-ijVGmMIFpQIAIMq8xSeZcHKU8GDDnV0psReW-XGEJagTM221P3fqBSzvpQECAyYgASFYIHK1iVimeR02UYipyiEKrKhhfhJRMew8EbDWGKtMZ2wUIlggbtZ70X11SLx17QFDWVAR3_qqk5OqrRS--Whc7hyw9YU", "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YVikdKbqkhPJnC90siSSsyDPQCYqlMGpUKA5fyklC2CEHvBdAAAAAP2xQbJdhEQ-ijVGmMIFpQIAIMq8xSeZcHKU8GDDnV0psReW-XGEJagTM221P3fqBSzvpQECAyYgASFYIHK1iVimeR02UYipyiEKrKhhfhJRMew8EbDWGKtMZ2wUIlggbtZ70X11SLx17QFDWVAR3_qqk5OqrRS--Whc7hyw9YU",
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoibFZlSHpWeFdzcjhNUXhNa1pGMHRpNkZYaGRnTWxqcUt6Z0EtcV96azJNbmlpM2VKNDdWRjk3c3FVb1lrdFZDODVXQVoxdUlBU20tYV9sREZad3NMZnciLCJvcmlnaW4iOiJodHRwczovL3dlYmF1dGhuLmlvIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ" "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoibFZlSHpWeFdzcjhNUXhNa1pGMHRpNkZYaGRnTWxqcUt6Z0EtcV96azJNbmlpM2VKNDdWRjk3c3FVb1lrdFZDODVXQVoxdUlBU20tYV9sREZad3NMZnciLCJvcmlnaW4iOiJodHRwczovL3dlYmF1dGhuLmlvIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ"
}, },
"type": "public-key" "type": "public-key"
@ -185,6 +185,8 @@ void TestPasskeys::testDecodeResponseData()
QCOMPARE(authData["rpIdHash"].toString(), QString("dKbqkhPJnC90siSSsyDPQCYqlMGpUKA5fyklC2CEHvA")); QCOMPARE(authData["rpIdHash"].toString(), QString("dKbqkhPJnC90siSSsyDPQCYqlMGpUKA5fyklC2CEHvA"));
QCOMPARE(flags["AT"], true); QCOMPARE(flags["AT"], true);
QCOMPARE(flags["UP"], true); QCOMPARE(flags["UP"], true);
QCOMPARE(flags["BE"], true);
QCOMPARE(flags["BS"], true);
QCOMPARE(publicKey["1"], 2); QCOMPARE(publicKey["1"], 2);
QCOMPARE(publicKey["3"], -7); QCOMPARE(publicKey["3"], -7);
QCOMPARE(publicKey["-1"], 1); QCOMPARE(publicKey["-1"], 1);
@ -285,13 +287,18 @@ void TestPasskeys::testCreatingAttestationObjectWithEC()
result, result,
QString("\xA3" QString("\xA3"
"cfmtdnonegattStmt\xA0hauthDataX\xA4t\xA6\xEA\x92\x13\xC9\x9C/t\xB2$\x92\xB3 \xCF@&*\x94\xC1\xA9P\xA0" "cfmtdnonegattStmt\xA0hauthDataX\xA4t\xA6\xEA\x92\x13\xC9\x9C/t\xB2$\x92\xB3 \xCF@&*\x94\xC1\xA9P\xA0"
"9\x7F)%\x0B`\x84\x1E\xF0" "9\x7F)%\x0B`\x84\x1E\xF0]\x00\x00\x00\x00\xFD\xB1"
"E\x00\x00\x00\x01\x01\x02\x03\x04\x05\x06\x07\b\x01\x02\x03\x04\x05\x06\x07\b\x00 \x8B\xB0\xCA" "A\xB2]\x84"
"6\x17\xD6\xDE\x01\x11|\xEA\x94\r\xA0R\xC0\x80_\xF3r\xFBr\xB5\x02\x03:" "D>\x8A"
"\xBAr\x0Fi\x81\xFE\xA5\x01\x02\x03& \x01!X " "5F\x98\xC2\x05\xA5\x02\x00 \xCA\xBC\xC5'\x99pr\x94\xF0`\xC3\x9D])\xB1\x17\x96\xF9q\x84%\xA8\x13"
"e\xE2\xF2\x1F:cq\xD3G\xEA\xE0\xF7\x1F\xCF\xFA\\\xABO\xF6\x86\x88\x80\t\xAE\x81\x8BT\xB2\x9B\x15\x85~" "3m\xB5?w\xEA\x05,\xEF\xA5\x01\x02\x03& \x01!X \x06\xEC\xAF"
"\"X \\\x8E\x1E@\xDB\x97T-\xF8\x9B\xB0\xAD" "4[b\x91"
"5\xDC\x12^\xC3\x95\x05\xC6\xDF^\x03\xCB\xB4Q\x91\xFF|\xDB\x94\xB7")); "am\x19Y\x03\xA6P*\xCA"
"1\xC4\x95\xA8i\xE5\xF0\x87\xE5\xD4\xB8"
"2\xCD\b\x85\xDD\"X \xE2\xEE\x7F\xE9\x0F\x0E\xE9\x1D\x07\x83J\x03\t\xDB"
"B$\xB1\x0B\xD3%\xFF\x18"
"2\xE1S\x99\xB7\x1D"
"B\x04\xE7\x83"));
// Double check that the result can be decoded // Double check that the result can be decoded
BrowserCbor browserCbor; BrowserCbor browserCbor;
@ -312,6 +319,8 @@ void TestPasskeys::testCreatingAttestationObjectWithEC()
QCOMPARE(authData["rpIdHash"].toString(), QString("dKbqkhPJnC90siSSsyDPQCYqlMGpUKA5fyklC2CEHvA")); QCOMPARE(authData["rpIdHash"].toString(), QString("dKbqkhPJnC90siSSsyDPQCYqlMGpUKA5fyklC2CEHvA"));
QCOMPARE(flags["AT"], true); QCOMPARE(flags["AT"], true);
QCOMPARE(flags["UP"], true); QCOMPARE(flags["UP"], true);
QCOMPARE(flags["BE"], true);
QCOMPARE(flags["BS"], true);
QCOMPARE(publicKey["1"], WebAuthnCoseKeyType::EC2); QCOMPARE(publicKey["1"], WebAuthnCoseKeyType::EC2);
QCOMPARE(publicKey["3"], WebAuthnAlgorithms::ES256); QCOMPARE(publicKey["3"], WebAuthnAlgorithms::ES256);
QCOMPARE(publicKey["-1"], 1); QCOMPARE(publicKey["-1"], 1);
@ -368,6 +377,8 @@ void TestPasskeys::testCreatingAttestationObjectWithRSA()
QCOMPARE(authData["rpIdHash"].toString(), QString("dKbqkhPJnC90siSSsyDPQCYqlMGpUKA5fyklC2CEHvA")); QCOMPARE(authData["rpIdHash"].toString(), QString("dKbqkhPJnC90siSSsyDPQCYqlMGpUKA5fyklC2CEHvA"));
QCOMPARE(flags["AT"], true); QCOMPARE(flags["AT"], true);
QCOMPARE(flags["UP"], true); QCOMPARE(flags["UP"], true);
QCOMPARE(flags["BE"], true);
QCOMPARE(flags["BS"], true);
QCOMPARE(publicKey["1"], WebAuthnCoseKeyType::RSA); QCOMPARE(publicKey["1"], WebAuthnCoseKeyType::RSA);
QCOMPARE(publicKey["3"], WebAuthnAlgorithms::RS256); QCOMPARE(publicKey["3"], WebAuthnAlgorithms::RS256);
QCOMPARE(publicKey["-1"], predefinedModulus); QCOMPARE(publicKey["-1"], predefinedModulus);
@ -438,14 +449,14 @@ void TestPasskeys::testGet()
QCOMPARE(publicKeyCredential["id"].toString(), id); QCOMPARE(publicKeyCredential["id"].toString(), id);
auto response = publicKeyCredential["response"].toObject(); auto response = publicKeyCredential["response"].toObject();
QCOMPARE(response["authenticatorData"].toString(), QString("dKbqkhPJnC90siSSsyDPQCYqlMGpUKA5fyklC2CEHvAFAAAAAA")); QCOMPARE(response["authenticatorData"].toString(), QString("dKbqkhPJnC90siSSsyDPQCYqlMGpUKA5fyklC2CEHvAdAAAAAA"));
QCOMPARE(response["clientDataJSON"].toString(), QCOMPARE(response["clientDataJSON"].toString(),
QString("eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiOXozNnZUZlFUTDk1TGY3V25aZ3l0ZTdvaEdlRi1YUmlMeGtML" QString("eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiOXozNnZUZlFUTDk1TGY3V25aZ3l0ZTdvaEdlRi1YUmlMeGtML"
"Ux1R1Uxem9wUm1NSVVBMUxWd3pHcHlJbTFmT0JuMVFuUmEwUUgyN0FEQWFKR0h5c1EiLCJvcmlnaW4iOiJodHRwczovL3dlYm" "Ux1R1Uxem9wUm1NSVVBMUxWd3pHcHlJbTFmT0JuMVFuUmEwUUgyN0FEQWFKR0h5c1EiLCJvcmlnaW4iOiJodHRwczovL3dlYm"
"F1dGhuLmlvIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ")); "F1dGhuLmlvIiwiY3Jvc3NPcmlnaW4iOmZhbHNlfQ"));
QCOMPARE( QCOMPARE(
response["signature"].toString(), response["signature"].toString(),
QString("MEYCIQCpbDaYJ4b2ofqWBxfRNbH3XCpsyao7Iui5lVuJRU9HIQIhAPl5moNZgJu5zmurkKK_P900Ct6wd3ahVIqCEqTeeRdE")); QString("MEUCIQCvg3nXO2fiNK9ockxscgPtoM9_u6ERaW2-F1L99YasOAIgNhYOjPJyKJ-W8roV531kC59ss1USas7jy8TfRnbJLtg"));
auto clientDataJson = response["clientDataJSON"].toString(); auto clientDataJson = response["clientDataJSON"].toString();
auto clientDataByteArray = browserMessageBuilder()->getArrayFromBase64(clientDataJson); auto clientDataByteArray = browserMessageBuilder()->getArrayFromBase64(clientDataJson);