SHA256
Миграция PDA на client.key
This commit is contained in:
+3
-3
@@ -11,7 +11,7 @@
|
||||
* legacy(empty password):
|
||||
* secret = SHA256(base64(SHA256(password)) + "master.secret")
|
||||
* keyPair_i = Ed25519(SHA256(base64(secret) + "|" + suffix_i))
|
||||
* suffixes = ["root.key", "bch.key", "dev.key"]
|
||||
* suffixes = ["root.key", "blockchain.key", "client.key"]
|
||||
*
|
||||
* Плата: Waveshare ESP32-S3-Touch-AMOLED-2.16
|
||||
* SD : SDMMC 1-bit CLK=GPIO2, CMD=GPIO1, D0=GPIO3
|
||||
@@ -116,8 +116,8 @@ static bool gKbNums = false;
|
||||
// ═══════════════════════════════════════════════════════════
|
||||
struct KeyPair { uint8_t pub[32]; uint8_t priv[32]; };
|
||||
static KeyPair gKeys[3];
|
||||
static const char * KEY_SUFFIXES[3] = {"root.key", "bch.key", "dev.key"};
|
||||
static const char * KEY_LABELS[3] = {"root.key", "bch.key", "dev.key"};
|
||||
static const char * KEY_SUFFIXES[3] = {"root.key", "blockchain.key", "client.key"};
|
||||
static const char * KEY_LABELS[3] = {"root.key", "blockchain.key", "client.key"};
|
||||
static uint32_t gElapsedSec = 0;
|
||||
|
||||
// Base58 представления (43-44 символа для 32 байт + \0)
|
||||
|
||||
+65
-65
@@ -81,7 +81,7 @@ static const char *kPaymentsInflowSeed = "shine_payments_inflow_vault";
|
||||
static const char *kLastBlockPrefix = "SHiNE_LAST_BLOCK";
|
||||
static const char *kProgramDerivedAddressMarker = "ProgramDerivedAddress";
|
||||
static const uint8_t kBlockTypeRootKey = 1;
|
||||
static const uint8_t kBlockTypeDeviceKey = 2;
|
||||
static const uint8_t kBlockTypeClientKey = 2;
|
||||
static const uint8_t kBlockTypeBlockchainRegistry = 3;
|
||||
static const uint8_t kBlockTypeServerProfile = 30;
|
||||
static const uint8_t kBlockTypeAccessServers = 40;
|
||||
@@ -230,7 +230,7 @@ struct ShinePdaUserState {
|
||||
uint8_t sessionsMode = 1;
|
||||
uint8_t trustedCount = 0;
|
||||
uint8_t rootKey32[32] = {};
|
||||
uint8_t deviceKey32[32] = {};
|
||||
uint8_t clientKey32[32] = {};
|
||||
uint8_t blockchainKey32[32] = {};
|
||||
String blockchainName;
|
||||
uint64_t paidLimitBytes = 0;
|
||||
@@ -542,7 +542,7 @@ static std::vector<uint8_t> buildUnsignedCreateRecord(
|
||||
const String &blockchainName,
|
||||
const String &serverAddress,
|
||||
const uint8_t rootPub[32],
|
||||
const uint8_t devicePub[32],
|
||||
const uint8_t clientPub[32],
|
||||
const uint8_t blockchainPub[32],
|
||||
const uint8_t lastBlockSignature[64],
|
||||
uint64_t paidLimitBytes,
|
||||
@@ -552,7 +552,7 @@ static std::vector<uint8_t> buildCreateInstructionData(
|
||||
const String &blockchainName,
|
||||
const String &serverAddress,
|
||||
const uint8_t rootPub[32],
|
||||
const uint8_t devicePub[32],
|
||||
const uint8_t clientPub[32],
|
||||
const uint8_t blockchainPub[32],
|
||||
const uint8_t lastBlockSignature[64],
|
||||
const uint8_t rootSignature[64],
|
||||
@@ -566,7 +566,7 @@ static std::vector<uint8_t> buildUpdateInstructionData(const ShinePdaUserState &
|
||||
const uint8_t rootSignature64[64]);
|
||||
static std::vector<uint8_t> buildLegacyMessage(
|
||||
const uint8_t recentBlockhash[32],
|
||||
const uint8_t devicePub[32],
|
||||
const uint8_t clientPub[32],
|
||||
const uint8_t userPda[32],
|
||||
const uint8_t inflowVault[32],
|
||||
const uint8_t economyConfig[32],
|
||||
@@ -575,7 +575,7 @@ static std::vector<uint8_t> buildLegacyMessage(
|
||||
const std::vector<uint8_t> &createData);
|
||||
static std::vector<uint8_t> buildUpdateLegacyMessage(
|
||||
const uint8_t recentBlockhash[32],
|
||||
const uint8_t devicePub[32],
|
||||
const uint8_t clientPub[32],
|
||||
const uint8_t userPda[32],
|
||||
const uint8_t inflowVault[32],
|
||||
const uint8_t economyConfig[32],
|
||||
@@ -1234,8 +1234,8 @@ static void refreshDerivedKeys() {
|
||||
return;
|
||||
}
|
||||
deriveKeyPairFromSecretSuffix(gSecretBytes, "root.key", gRootPubB58, gRootPrivB58);
|
||||
deriveKeyPairFromSecretSuffix(gSecretBytes, "bch.key", gBlockchainPubB58, gBlockchainPrivB58);
|
||||
deriveKeyPairFromSecretSuffix(gSecretBytes, "dev.key", gDevicePubB58, gDevicePrivB58);
|
||||
deriveKeyPairFromSecretSuffix(gSecretBytes, "blockchain.key", gBlockchainPubB58, gBlockchainPrivB58);
|
||||
deriveKeyPairFromSecretSuffix(gSecretBytes, "client.key", gDevicePubB58, gDevicePrivB58);
|
||||
deriveKeyPairFromSecretSuffix(gSecretBytes, homeserverKeySuffix(), gHomeserverPubB58, gHomeserverPrivB58);
|
||||
String customName = gCustomWalletName;
|
||||
customName.trim();
|
||||
@@ -1255,7 +1255,7 @@ static String selectedWalletDisplayName() {
|
||||
}
|
||||
case WALLET_SELECTION_DEVICE:
|
||||
default:
|
||||
return "DeviceKey";
|
||||
return "ClientKey";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1267,7 +1267,7 @@ static String selectedWalletTypeCode() {
|
||||
return "custom";
|
||||
case WALLET_SELECTION_DEVICE:
|
||||
default:
|
||||
return "dev.key";
|
||||
return "client.key";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1282,7 +1282,7 @@ static String selectedWalletDerivationSuffix() {
|
||||
}
|
||||
case WALLET_SELECTION_DEVICE:
|
||||
default:
|
||||
return "dev.key";
|
||||
return "client.key";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1759,9 +1759,9 @@ static std::vector<uint8_t> serializeUnsignedRecordState(const ShinePdaUserState
|
||||
out.push_back(0);
|
||||
pushFixed(out, state.rootKey32, 32);
|
||||
|
||||
out.push_back(kBlockTypeDeviceKey);
|
||||
out.push_back(kBlockTypeClientKey);
|
||||
out.push_back(0);
|
||||
pushFixed(out, state.deviceKey32, 32);
|
||||
pushFixed(out, state.clientKey32, 32);
|
||||
|
||||
out.push_back(kBlockTypeBlockchainRegistry);
|
||||
out.push_back(0);
|
||||
@@ -1837,7 +1837,7 @@ static std::vector<uint8_t> buildUpdateInstructionData(const ShinePdaUserState &
|
||||
pushU32LE(out, nextVersion);
|
||||
pushFixed(out, prevHash32, 32);
|
||||
pushU64LE(out, 0);
|
||||
pushFixed(out, state.deviceKey32, 32);
|
||||
pushFixed(out, state.clientKey32, 32);
|
||||
pushFixed(out, state.blockchainKey32, 32);
|
||||
pushStrU8(out, state.blockchainName);
|
||||
pushU64LE(out, state.usedBytes);
|
||||
@@ -1882,7 +1882,7 @@ static std::vector<uint8_t> buildUnsignedCreateRecord(
|
||||
const String &blockchainName,
|
||||
const String &serverAddress,
|
||||
const uint8_t rootPub[32],
|
||||
const uint8_t devicePub[32],
|
||||
const uint8_t clientPub[32],
|
||||
const uint8_t blockchainPub[32],
|
||||
const uint8_t lastBlockSignature[64],
|
||||
uint64_t paidLimitBytes,
|
||||
@@ -1905,9 +1905,9 @@ static std::vector<uint8_t> buildUnsignedCreateRecord(
|
||||
out.push_back(0);
|
||||
pushFixed(out, rootPub, 32);
|
||||
|
||||
out.push_back(kBlockTypeDeviceKey);
|
||||
out.push_back(kBlockTypeClientKey);
|
||||
out.push_back(0);
|
||||
pushFixed(out, devicePub, 32);
|
||||
pushFixed(out, clientPub, 32);
|
||||
|
||||
out.push_back(kBlockTypeBlockchainRegistry);
|
||||
out.push_back(0);
|
||||
@@ -1954,7 +1954,7 @@ static std::vector<uint8_t> buildCreateInstructionData(
|
||||
const String &blockchainName,
|
||||
const String &serverAddress,
|
||||
const uint8_t rootPub[32],
|
||||
const uint8_t devicePub[32],
|
||||
const uint8_t clientPub[32],
|
||||
const uint8_t blockchainPub[32],
|
||||
const uint8_t lastBlockSignature[64],
|
||||
const uint8_t rootSignature[64],
|
||||
@@ -1966,7 +1966,7 @@ static std::vector<uint8_t> buildCreateInstructionData(
|
||||
pushFixed(out, rootPub, 32);
|
||||
pushU64LE(out, createdAtMs);
|
||||
pushU64LE(out, 0);
|
||||
pushFixed(out, devicePub, 32);
|
||||
pushFixed(out, clientPub, 32);
|
||||
pushFixed(out, blockchainPub, 32);
|
||||
pushStrU8(out, blockchainName);
|
||||
pushU64LE(out, 0);
|
||||
@@ -2215,7 +2215,7 @@ static bool simulateTransactionForError(const String &txBase64, String &messageO
|
||||
|
||||
static std::vector<uint8_t> buildLegacyMessage(
|
||||
const uint8_t recentBlockhash[32],
|
||||
const uint8_t devicePub[32],
|
||||
const uint8_t clientPub[32],
|
||||
const uint8_t userPda[32],
|
||||
const uint8_t inflowVault[32],
|
||||
const uint8_t economyConfig[32],
|
||||
@@ -2234,7 +2234,7 @@ static std::vector<uint8_t> buildLegacyMessage(
|
||||
base58ToFixed32(kShineLoginGuardProgramId, loginGuardProgram);
|
||||
|
||||
std::vector<std::vector<uint8_t>> accountKeys;
|
||||
accountKeys.emplace_back(devicePub, devicePub + 32);
|
||||
accountKeys.emplace_back(clientPub, clientPub + 32);
|
||||
accountKeys.emplace_back(userPda, userPda + 32);
|
||||
accountKeys.emplace_back(inflowVault, inflowVault + 32);
|
||||
accountKeys.emplace_back(systemProgram, systemProgram + 32);
|
||||
@@ -2282,7 +2282,7 @@ static std::vector<uint8_t> buildLegacyMessage(
|
||||
|
||||
static std::vector<uint8_t> buildUpdateLegacyMessage(
|
||||
const uint8_t recentBlockhash[32],
|
||||
const uint8_t devicePub[32],
|
||||
const uint8_t clientPub[32],
|
||||
const uint8_t userPda[32],
|
||||
const uint8_t inflowVault[32],
|
||||
const uint8_t economyConfig[32],
|
||||
@@ -2299,7 +2299,7 @@ static std::vector<uint8_t> buildUpdateLegacyMessage(
|
||||
base58ToFixed32(kSysvarInstructionsId, sysvarInstructions);
|
||||
|
||||
std::vector<std::vector<uint8_t>> accountKeys;
|
||||
accountKeys.emplace_back(devicePub, devicePub + 32);
|
||||
accountKeys.emplace_back(clientPub, clientPub + 32);
|
||||
accountKeys.emplace_back(userPda, userPda + 32);
|
||||
accountKeys.emplace_back(inflowVault, inflowVault + 32);
|
||||
accountKeys.emplace_back(systemProgram, systemProgram + 32);
|
||||
@@ -2708,17 +2708,17 @@ static bool registerHomeserverOnSolana(String &messageOut) {
|
||||
uint8_t blockchainSeed[32] = {};
|
||||
uint8_t blockchainPub[32] = {};
|
||||
uint8_t blockchainSec[64] = {};
|
||||
uint8_t deviceSeed[32] = {};
|
||||
uint8_t devicePub[32] = {};
|
||||
uint8_t clientSeed[32] = {};
|
||||
uint8_t clientPub[32] = {};
|
||||
uint8_t deviceSec[64] = {};
|
||||
if (!deriveSeedKeypairFromBase58(gRootPrivB58, rootSeed, rootPub, rootSec) ||
|
||||
!deriveSeedKeypairFromBase58(gBlockchainPrivB58, blockchainSeed, blockchainPub, blockchainSec) ||
|
||||
!deriveSeedKeypairFromBase58(gDevicePrivB58, deviceSeed, devicePub, deviceSec)) {
|
||||
!deriveSeedKeypairFromBase58(gDevicePrivB58, clientSeed, clientPub, deviceSec)) {
|
||||
return failWithDiag("Failed to restore keys");
|
||||
}
|
||||
diagDetails += String("root_pub=") + bytesToBase58(rootPub, 32) + "\n";
|
||||
diagDetails += String("blockchain_pub=") + bytesToBase58(blockchainPub, 32) + "\n";
|
||||
diagDetails += String("device_pub=") + bytesToBase58(devicePub, 32) + "\n";
|
||||
diagDetails += String("device_pub=") + bytesToBase58(clientPub, 32) + "\n";
|
||||
|
||||
String blockchainName = cleanLogin + "-001";
|
||||
diagDetails += String("blockchain_name=") + blockchainName + "\n";
|
||||
@@ -2736,7 +2736,7 @@ static bool registerHomeserverOnSolana(String &messageOut) {
|
||||
diagDetails += String("created_at_ms=") + String((unsigned long long)createdAtMs) + "\n";
|
||||
std::vector<uint8_t> unsignedRecord = buildUnsignedCreateRecord(
|
||||
cleanLogin, blockchainName, gShineServerUrl,
|
||||
rootPub, devicePub, blockchainPub,
|
||||
rootPub, clientPub, blockchainPub,
|
||||
lastBlockSignature, startBonusLimit, createdAtMs);
|
||||
uint8_t unsignedHash[32];
|
||||
uint8_t rootSignature[64];
|
||||
@@ -2750,7 +2750,7 @@ static bool registerHomeserverOnSolana(String &messageOut) {
|
||||
|
||||
std::vector<uint8_t> createData = buildCreateInstructionData(
|
||||
cleanLogin, blockchainName, gShineServerUrl,
|
||||
rootPub, devicePub, blockchainPub,
|
||||
rootPub, clientPub, blockchainPub,
|
||||
lastBlockSignature, rootSignature, createdAtMs);
|
||||
std::vector<uint8_t> edRootData = buildEd25519InstructionData(rootSignature, rootPub, unsignedHash);
|
||||
std::vector<uint8_t> edBchData = buildEd25519InstructionData(lastBlockSignature, blockchainPub, lastBlockHash);
|
||||
@@ -2765,7 +2765,7 @@ static bool registerHomeserverOnSolana(String &messageOut) {
|
||||
|
||||
std::vector<uint8_t> message = buildLegacyMessage(
|
||||
recentBlockhash,
|
||||
devicePub,
|
||||
clientPub,
|
||||
userPda,
|
||||
inflowVault,
|
||||
economyConfig,
|
||||
@@ -3012,17 +3012,17 @@ static bool updateHomeserverSessionOnSolana(bool requireExisting, String &messag
|
||||
uint8_t rootSeed[32] = {};
|
||||
uint8_t rootPub[32] = {};
|
||||
uint8_t rootSec[64] = {};
|
||||
uint8_t deviceSeed[32] = {};
|
||||
uint8_t devicePub[32] = {};
|
||||
uint8_t clientSeed[32] = {};
|
||||
uint8_t clientPub[32] = {};
|
||||
uint8_t deviceSec[64] = {};
|
||||
uint8_t homeserverPub[32] = {};
|
||||
if (!deriveSeedKeypairFromBase58(gRootPrivB58, rootSeed, rootPub, rootSec)
|
||||
|| !deriveSeedKeypairFromBase58(gDevicePrivB58, deviceSeed, devicePub, deviceSec)
|
||||
|| !deriveSeedKeypairFromBase58(gDevicePrivB58, clientSeed, clientPub, deviceSec)
|
||||
|| !base58ToFixed32(gHomeserverPubB58, homeserverPub)) {
|
||||
return failWithDiag("Failed to restore local keys");
|
||||
}
|
||||
if (memcmp(devicePub, currentState.deviceKey32, 32) != 0) {
|
||||
return failWithDiag("Device key does not match PDA");
|
||||
if (memcmp(clientPub, currentState.clientKey32, 32) != 0) {
|
||||
return failWithDiag("Client key does not match PDA");
|
||||
}
|
||||
|
||||
uint8_t userPda[32] = {};
|
||||
@@ -3048,7 +3048,7 @@ static bool updateHomeserverSessionOnSolana(bool requireExisting, String &messag
|
||||
|
||||
ShinePdaUserState nextState = currentState;
|
||||
memcpy(nextState.rootKey32, rootPub, 32);
|
||||
memcpy(nextState.deviceKey32, devicePub, 32);
|
||||
memcpy(nextState.clientKey32, clientPub, 32);
|
||||
nextState.updatedAtMs = shineNowMs();
|
||||
nextState.recordNumber = currentState.recordNumber + 1;
|
||||
if (nextState.sessionsMode == 0) {
|
||||
@@ -3121,7 +3121,7 @@ static bool updateHomeserverSessionOnSolana(bool requireExisting, String &messag
|
||||
|
||||
std::vector<uint8_t> message = buildUpdateLegacyMessage(
|
||||
recentBlockhash,
|
||||
devicePub,
|
||||
clientPub,
|
||||
userPda,
|
||||
inflowVault,
|
||||
economyConfig,
|
||||
@@ -3289,9 +3289,9 @@ static bool parseShineUserPdaBytes(const std::vector<uint8_t> &bytes, ShinePdaUs
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (blockType == kBlockTypeDeviceKey) {
|
||||
if (!readBytes(outState.deviceKey32, 32)) {
|
||||
errorOut = "Bad device key block";
|
||||
if (blockType == kBlockTypeClientKey) {
|
||||
if (!readBytes(outState.clientKey32, 32)) {
|
||||
errorOut = "Bad client key block";
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
@@ -3520,10 +3520,10 @@ static void refreshAccountPdaStatus() {
|
||||
}
|
||||
|
||||
uint8_t rootPub[32] = {};
|
||||
uint8_t devicePub[32] = {};
|
||||
uint8_t clientPub[32] = {};
|
||||
uint8_t blockchainPub[32] = {};
|
||||
if (!base58ToFixed32(gRootPubB58, rootPub)
|
||||
|| !base58ToFixed32(gDevicePubB58, devicePub)
|
||||
|| !base58ToFixed32(gDevicePubB58, clientPub)
|
||||
|| !base58ToFixed32(gBlockchainPubB58, blockchainPub)) {
|
||||
gAccountPdaStatus = ACCOUNT_PDA_MISMATCH;
|
||||
gAccountPdaStatusMessage = "local keys invalid";
|
||||
@@ -3537,8 +3537,8 @@ static void refreshAccountPdaStatus() {
|
||||
mismatch = "root key mismatch";
|
||||
} else if (memcmp(blockchainPub, pdaState.blockchainKey32, 32) != 0) {
|
||||
mismatch = "blockchain key mismatch";
|
||||
} else if (memcmp(devicePub, pdaState.deviceKey32, 32) != 0) {
|
||||
mismatch = "device key mismatch";
|
||||
} else if (memcmp(clientPub, pdaState.clientKey32, 32) != 0) {
|
||||
mismatch = "client key mismatch";
|
||||
} else if (gHomeserverValue.isEmpty()) {
|
||||
mismatch = "homeserver not set";
|
||||
} else {
|
||||
@@ -3963,20 +3963,20 @@ static bool buildPkcs8FromSeed32(const uint8_t seed32[32], String &pkcs8B64Out)
|
||||
static bool buildPairingSecretsPayload(const PairingRequestUiItem &item, String &payloadJsonOut, String &errorOut) {
|
||||
payloadJsonOut = "";
|
||||
errorOut = "";
|
||||
uint8_t deviceSeed[32] = {};
|
||||
uint8_t devicePub[32] = {};
|
||||
uint8_t clientSeed[32] = {};
|
||||
uint8_t clientPub[32] = {};
|
||||
uint8_t deviceSec[64] = {};
|
||||
if (!deriveSeedKeypairFromBase58(gDevicePrivB58, deviceSeed, devicePub, deviceSec)) {
|
||||
errorOut = "Failed to derive device key";
|
||||
if (!deriveSeedKeypairFromBase58(gDevicePrivB58, clientSeed, clientPub, deviceSec)) {
|
||||
errorOut = "Failed to derive client key";
|
||||
return false;
|
||||
}
|
||||
String devicePkcs8;
|
||||
if (!buildPkcs8FromSeed32(deviceSeed, devicePkcs8)) {
|
||||
errorOut = "Failed to encode device key";
|
||||
if (!buildPkcs8FromSeed32(clientSeed, devicePkcs8)) {
|
||||
errorOut = "Failed to encode client key";
|
||||
return false;
|
||||
}
|
||||
payloadJsonOut = String("{\"v\":1,\"type\":\"shine-esp-pairing-transfer\",\"login\":\"") + jsonEscape(gLoginValue)
|
||||
+ "\",\"mode\":\"device-only\",\"keys\":{\"deviceKey\":\"" + jsonEscape(devicePkcs8)
|
||||
+ "\",\"mode\":\"device-only\",\"keys\":{\"clientKey\":\"" + jsonEscape(devicePkcs8)
|
||||
+ "\",\"blockchainKey\":\"\",\"rootKey\":\"\"},\"payloadType\":1,\"createdAtMs\":"
|
||||
+ String((unsigned long long)shineNowMs()) + "}";
|
||||
return true;
|
||||
@@ -3985,11 +3985,11 @@ static bool buildPairingSecretsPayload(const PairingRequestUiItem &item, String
|
||||
static bool createDelegatedWalletSessionPayload(const PairingRequestUiItem &item, String &payloadJsonOut, String &errorOut) {
|
||||
payloadJsonOut = "";
|
||||
errorOut = "";
|
||||
uint8_t deviceSeed[32] = {};
|
||||
uint8_t devicePub[32] = {};
|
||||
uint8_t clientSeed[32] = {};
|
||||
uint8_t clientPub[32] = {};
|
||||
uint8_t deviceSec[64] = {};
|
||||
if (!deriveSeedKeypairFromBase58(gDevicePrivB58, deviceSeed, devicePub, deviceSec)) {
|
||||
errorOut = "Failed to derive device key";
|
||||
if (!deriveSeedKeypairFromBase58(gDevicePrivB58, clientSeed, clientPub, deviceSec)) {
|
||||
errorOut = "Failed to derive client key";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4040,7 +4040,7 @@ static bool createDelegatedWalletSessionPayload(const PairingRequestUiItem &item
|
||||
+ "\",\"storagePwd\":\"" + jsonEscape(storagePwd)
|
||||
+ "\",\"timeMs\":" + String((unsigned long long)timeMs)
|
||||
+ ",\"authNonce\":\"" + jsonEscape(authNonce)
|
||||
+ "\",\"deviceKey\":\"" + jsonEscape(bytesToBase64String(devicePub, 32))
|
||||
+ "\",\"clientKey\":\"" + jsonEscape(bytesToBase64String(clientPub, 32))
|
||||
+ "\",\"signatureB64\":\"" + jsonEscape(bytesToBase64String(signature, 64))
|
||||
+ "\",\"sessionType\":50"
|
||||
+ ",\"clientPlatform\":\"" + jsonEscape(clientPlatform)
|
||||
@@ -4262,13 +4262,13 @@ static bool ensureShineSessionAuthenticated(String &errorOut) {
|
||||
diagDetails += String("server_time_offset_ms=") + String((long long)gShineServerTimeOffsetMs) + "\n";
|
||||
}
|
||||
|
||||
uint8_t deviceSeed[32] = {};
|
||||
uint8_t devicePub[32] = {};
|
||||
uint8_t clientSeed[32] = {};
|
||||
uint8_t clientPub[32] = {};
|
||||
uint8_t deviceSec[64] = {};
|
||||
uint8_t subSeed[32] = {};
|
||||
uint8_t subPub[32] = {};
|
||||
uint8_t subSec[64] = {};
|
||||
if (!deriveSeedKeypairFromBase58(gDevicePrivB58, deviceSeed, devicePub, deviceSec)
|
||||
if (!deriveSeedKeypairFromBase58(gDevicePrivB58, clientSeed, clientPub, deviceSec)
|
||||
|| !deriveSeedKeypairFromBase58(gHomeserverPrivB58, subSeed, subPub, subSec)) {
|
||||
return failWithDiag("local key derive failed");
|
||||
}
|
||||
@@ -4368,7 +4368,7 @@ static bool ensureShineSessionAuthenticated(String &errorOut) {
|
||||
+ "\",\"storagePwd\":\"" + jsonEscape(gShineStoragePwd)
|
||||
+ "\",\"timeMs\":" + String((unsigned long long)timeMs)
|
||||
+ ",\"authNonce\":\"" + jsonEscape(authNonce)
|
||||
+ "\",\"deviceKey\":\"" + jsonEscape(bytesToBase64String(devicePub, 32))
|
||||
+ "\",\"clientKey\":\"" + jsonEscape(bytesToBase64String(clientPub, 32))
|
||||
+ "\",\"signatureB64\":\"" + jsonEscape(bytesToBase64String(signature, 64))
|
||||
+ "\",\"sessionType\":" + String((unsigned int)kSessionTypeHomeserver)
|
||||
+ ",\"clientPlatform\":\"" + jsonEscape(kSessionClientPlatformEsp32)
|
||||
@@ -4510,7 +4510,7 @@ static void loadPrefs() {
|
||||
gShineServerUrl = gPrefs.getString("shine_server", "https://shineup.me");
|
||||
gLoginValue = gPrefs.getString("login", "");
|
||||
gHomeserverValue = gPrefs.getString("homeserver", "homeserver1");
|
||||
String walletTypeStored = gPrefs.getString("wallet_type", "dev.key");
|
||||
String walletTypeStored = gPrefs.getString("wallet_type", "client.key");
|
||||
if (walletTypeStored == "root.key") {
|
||||
gSelectedWalletType = WALLET_SELECTION_ROOT;
|
||||
} else if (walletTypeStored == "custom") {
|
||||
@@ -5786,7 +5786,7 @@ static void drawWalletSelectScreen() {
|
||||
makeTitle("SELECT WALLET", 22, &lv_font_montserrat_24);
|
||||
String currentLine = String("Current: ") + selectedWalletDisplayName();
|
||||
makeBody(currentLine.c_str(), 88, 420);
|
||||
String deviceLabel = String(gSelectedWalletType == WALLET_SELECTION_DEVICE ? "✓ DeviceKey" : "DeviceKey");
|
||||
String deviceLabel = String(gSelectedWalletType == WALLET_SELECTION_DEVICE ? "✓ ClientKey" : "ClientKey");
|
||||
String rootLabel = String(gSelectedWalletType == WALLET_SELECTION_ROOT ? "✓ RootKey" : "RootKey");
|
||||
String customBase = gCustomWalletName;
|
||||
customBase.trim();
|
||||
@@ -6081,7 +6081,7 @@ static void drawPairingRequestDetailScreen() {
|
||||
String question = String("Connect session ") + pairingSessionNameLabel(item) + "?";
|
||||
String explain = item.requesterSessionType == 50
|
||||
? "Wallet session. No keys will be transferred."
|
||||
: "Client session. Only device key will be transferred. No additional keys will be sent.";
|
||||
: "Client session. Only client key will be transferred. No additional keys will be sent.";
|
||||
String sessionNameText = String("Session: ") + pairingSessionNameLabel(item);
|
||||
String sessionKindText = String("Kind: ") + pairingSessionKindLabel(item.requesterSessionType);
|
||||
|
||||
@@ -6301,8 +6301,8 @@ static void drawSecretShowScreen() {
|
||||
addKeyBlock("Root key priv (base58)", "sha256(base64(secret)|root.key)", gRootPrivB58);
|
||||
addKeyBlock("Blockchain key (base58)", "pub from sha256(base64(secret)|bch.key)", gBlockchainPubB58);
|
||||
addKeyBlock("Blockchain key priv (base58)", "sha256(base64(secret)|bch.key)", gBlockchainPrivB58);
|
||||
addKeyBlock("Device key (base58)", "pub from sha256(base64(secret)|dev.key)", gDevicePubB58);
|
||||
addKeyBlock("Device key priv (base58)", "sha256(base64(secret)|dev.key)", gDevicePrivB58);
|
||||
addKeyBlock("Client key (base58)", "pub from sha256(base64(secret)|client.key)", gDevicePubB58);
|
||||
addKeyBlock("Client key priv (base58)", "sha256(base64(secret)|client.key)", gDevicePrivB58);
|
||||
addKeyBlock("Homeserver key (base58)", String("pub from sha256(base64(secret)|") + homeserverKeySuffix() + ")", gHomeserverPubB58);
|
||||
addKeyBlock("Homeserver key priv (base58)", String("sha256(base64(secret)|") + homeserverKeySuffix() + ")", gHomeserverPrivB58);
|
||||
} else {
|
||||
|
||||
+17
-17
@@ -228,7 +228,7 @@ struct DerivedKeyState {
|
||||
uint8_t rootSk[64];
|
||||
uint8_t blockchainPub[32];
|
||||
uint8_t blockchainSk[64];
|
||||
uint8_t devicePub[32];
|
||||
uint8_t clientPub[32];
|
||||
uint8_t deviceSk[64];
|
||||
};
|
||||
|
||||
@@ -246,7 +246,7 @@ static const char *kPaymentsInflowSeed = "shine_payments_inflow_vault";
|
||||
static const char *kProgramDerivedAddressMarker = "ProgramDerivedAddress";
|
||||
static const char *kLastBlockPrefix = "SHiNE_LAST_BLOCK";
|
||||
static const uint8_t kBlockTypeRootKey = 1;
|
||||
static const uint8_t kBlockTypeDeviceKey = 2;
|
||||
static const uint8_t kBlockTypeClientKey = 2;
|
||||
static const uint8_t kBlockTypeBlockchainRegistry = 3;
|
||||
static const uint8_t kBlockTypeServerProfile = 30;
|
||||
static const uint8_t kBlockTypeAccessServers = 40;
|
||||
@@ -786,8 +786,8 @@ static bool deriveKeysFromMasterSecret(const uint8_t masterSecret[32]) {
|
||||
if (secretB64.length() == 0) {
|
||||
return false;
|
||||
}
|
||||
const char *suffixes[3] = {"root.key", "bch.key", "dev.key"};
|
||||
uint8_t *pubs[3] = {gDerivedKeys.rootPub, gDerivedKeys.blockchainPub, gDerivedKeys.devicePub};
|
||||
const char *suffixes[3] = {"root.key", "blockchain.key", "client.key"};
|
||||
uint8_t *pubs[3] = {gDerivedKeys.rootPub, gDerivedKeys.blockchainPub, gDerivedKeys.clientPub};
|
||||
uint8_t *sks[3] = {gDerivedKeys.rootSk, gDerivedKeys.blockchainSk, gDerivedKeys.deviceSk};
|
||||
for (int i = 0; i < 3; i++) {
|
||||
String material = secretB64 + "|" + suffixes[i];
|
||||
@@ -822,7 +822,7 @@ static bool restoreDerivedKeysFromSecret() {
|
||||
return false;
|
||||
}
|
||||
gData.secretReady = true;
|
||||
gData.walletAddress = bytesToBase58(gDerivedKeys.devicePub, 32);
|
||||
gData.walletAddress = bytesToBase58(gDerivedKeys.clientPub, 32);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -835,7 +835,7 @@ static bool deriveFreshSecretAndWallet() {
|
||||
return false;
|
||||
}
|
||||
gData.secret = bytesToBase58(secret, sizeof(secret));
|
||||
gData.walletAddress = bytesToBase58(gDerivedKeys.devicePub, 32);
|
||||
gData.walletAddress = bytesToBase58(gDerivedKeys.clientPub, 32);
|
||||
gData.secretReady = true;
|
||||
return true;
|
||||
}
|
||||
@@ -889,7 +889,7 @@ static std::vector<uint8_t> buildUnsignedCreateRecord(
|
||||
const String &blockchainName,
|
||||
const String &serverAddress,
|
||||
const uint8_t rootPub[32],
|
||||
const uint8_t devicePub[32],
|
||||
const uint8_t clientPub[32],
|
||||
const uint8_t blockchainPub[32],
|
||||
const uint8_t lastBlockSignature[64],
|
||||
uint64_t createdAtMs) {
|
||||
@@ -911,9 +911,9 @@ static std::vector<uint8_t> buildUnsignedCreateRecord(
|
||||
out.push_back(0);
|
||||
pushFixed(out, rootPub, 32);
|
||||
|
||||
out.push_back(kBlockTypeDeviceKey);
|
||||
out.push_back(kBlockTypeClientKey);
|
||||
out.push_back(0);
|
||||
pushFixed(out, devicePub, 32);
|
||||
pushFixed(out, clientPub, 32);
|
||||
|
||||
out.push_back(kBlockTypeBlockchainRegistry);
|
||||
out.push_back(0);
|
||||
@@ -960,7 +960,7 @@ static std::vector<uint8_t> buildCreateInstructionData(
|
||||
const String &blockchainName,
|
||||
const String &serverAddress,
|
||||
const uint8_t rootPub[32],
|
||||
const uint8_t devicePub[32],
|
||||
const uint8_t clientPub[32],
|
||||
const uint8_t blockchainPub[32],
|
||||
const uint8_t lastBlockSignature[64],
|
||||
const uint8_t rootSignature[64],
|
||||
@@ -972,7 +972,7 @@ static std::vector<uint8_t> buildCreateInstructionData(
|
||||
pushFixed(out, rootPub, 32);
|
||||
pushU64LE(out, createdAtMs);
|
||||
pushU64LE(out, 0);
|
||||
pushFixed(out, devicePub, 32);
|
||||
pushFixed(out, clientPub, 32);
|
||||
pushFixed(out, blockchainPub, 32);
|
||||
pushStrU8(out, blockchainName);
|
||||
pushU64LE(out, 0);
|
||||
@@ -1079,7 +1079,7 @@ static bool pdaAlreadyExists(const String &login, String &pdaAddress, String &me
|
||||
|
||||
static std::vector<uint8_t> buildLegacyMessage(
|
||||
const uint8_t recentBlockhash[32],
|
||||
const uint8_t devicePub[32],
|
||||
const uint8_t clientPub[32],
|
||||
const uint8_t userPda[32],
|
||||
const uint8_t inflowVault[32],
|
||||
const uint8_t economyConfig[32],
|
||||
@@ -1098,7 +1098,7 @@ static std::vector<uint8_t> buildLegacyMessage(
|
||||
base58ToFixed32(kShineLoginGuardProgramId, loginGuardProgram);
|
||||
|
||||
std::vector<std::vector<uint8_t>> accountKeys;
|
||||
accountKeys.emplace_back(devicePub, devicePub + 32);
|
||||
accountKeys.emplace_back(clientPub, clientPub + 32);
|
||||
accountKeys.emplace_back(userPda, userPda + 32);
|
||||
accountKeys.emplace_back(inflowVault, inflowVault + 32);
|
||||
accountKeys.emplace_back(systemProgram, systemProgram + 32);
|
||||
@@ -1244,7 +1244,7 @@ static bool registerHomeserverOnSolana(String &messageOut) {
|
||||
uint64_t createdAtMs = (uint64_t)millis() + 1704067200000ULL;
|
||||
std::vector<uint8_t> unsignedRecord = buildUnsignedCreateRecord(
|
||||
gData.login, blockchainName, gData.wsUrl,
|
||||
gDerivedKeys.rootPub, gDerivedKeys.devicePub, gDerivedKeys.blockchainPub,
|
||||
gDerivedKeys.rootPub, gDerivedKeys.clientPub, gDerivedKeys.blockchainPub,
|
||||
lastBlockSignature, createdAtMs);
|
||||
uint8_t unsignedHash[32];
|
||||
uint8_t rootSignature[64];
|
||||
@@ -1256,7 +1256,7 @@ static bool registerHomeserverOnSolana(String &messageOut) {
|
||||
|
||||
std::vector<uint8_t> createData = buildCreateInstructionData(
|
||||
gData.login, blockchainName, gData.wsUrl,
|
||||
gDerivedKeys.rootPub, gDerivedKeys.devicePub, gDerivedKeys.blockchainPub,
|
||||
gDerivedKeys.rootPub, gDerivedKeys.clientPub, gDerivedKeys.blockchainPub,
|
||||
lastBlockSignature, rootSignature, createdAtMs);
|
||||
std::vector<uint8_t> edRootData = buildEd25519InstructionData(rootSignature, gDerivedKeys.rootPub, unsignedHash);
|
||||
std::vector<uint8_t> edBchData = buildEd25519InstructionData(lastBlockSignature, gDerivedKeys.blockchainPub, lastBlockHash);
|
||||
@@ -1269,7 +1269,7 @@ static bool registerHomeserverOnSolana(String &messageOut) {
|
||||
|
||||
std::vector<uint8_t> message = buildLegacyMessage(
|
||||
recentBlockhash,
|
||||
gDerivedKeys.devicePub,
|
||||
gDerivedKeys.clientPub,
|
||||
userPda,
|
||||
inflowVault,
|
||||
economyConfig,
|
||||
@@ -2107,7 +2107,7 @@ static void drawConfirmScreen() {
|
||||
String text = "Выполнить действие?";
|
||||
if (gConfirmTarget == CONFIRM_REGISTER) {
|
||||
title = "Регистрация";
|
||||
text = "Отправить create_user_pda в Solana через device key этого устройства?";
|
||||
text = "Отправить create_user_pda в Solana через client key этого устройства?";
|
||||
} else if (gConfirmTarget == CONFIRM_CLEAR_ACCOUNT) {
|
||||
title = "Очистка";
|
||||
text = "Удалить секрет, кошелёк и статус регистрации?";
|
||||
|
||||
+4
-4
@@ -70,11 +70,11 @@
|
||||
Фоновая логика:
|
||||
- пока открыт `HOME`, экран сам обновляется примерно раз в секунду;
|
||||
- при наличии `login + secret + homeserver` и Wi-Fi устройство читает Solana `user_pda` напрямую через RPC;
|
||||
- сравниваются `root key`, `blockchain key`, `device key` и `homeserver` session-запись типа `100`;
|
||||
- сравниваются `root key`, `blockchain key`, `client key` и `homeserver` session-запись типа `100`;
|
||||
- для строки `SHiNE:` устройство держит отдельную WebSocket-сессию с сервером SHiNE:
|
||||
- авторизация через `AuthChallenge/CreateAuthSession` или `SessionChallenge/SessionLogin`;
|
||||
- session key = публичный `homeserver key`;
|
||||
- подтверждение создания сессии подписывается `device key`;
|
||||
- подтверждение создания сессии подписывается `client key`;
|
||||
- heartbeat выполняется `Ping` раз в минуту.
|
||||
|
||||
## SETTINGS_MENU
|
||||
@@ -214,8 +214,8 @@
|
||||
- `Root key priv (base58)`;
|
||||
- `Blockchain key (base58)`;
|
||||
- `Blockchain key priv (base58)`;
|
||||
- `Device key (base58)`;
|
||||
- `Device key priv (base58)`;
|
||||
- `Client key (base58)`;
|
||||
- `Client key priv (base58)`;
|
||||
- `Homeserver key (base58)`;
|
||||
- `Homeserver key priv (base58)`;
|
||||
- для каждого поля показывается формула derivation;
|
||||
|
||||
@@ -178,7 +178,7 @@
|
||||
- вместо старых двух кнопок `баланс` и `QR` показывается одна широкая кнопка;
|
||||
- текст кнопки: `Wallet: <selected wallet name>`;
|
||||
- доступные имена:
|
||||
- `DeviceKey`
|
||||
- `ClientKey`
|
||||
- `RootKey`
|
||||
- либо сохранённое имя `custom`-кошелька;
|
||||
- после старта устройства баланс активного выбранного кошелька пытается загрузиться автоматически, если уже есть секрет и `Wi-Fi`;
|
||||
@@ -429,7 +429,7 @@
|
||||
- верхняя кнопка открывает экран выбора кошелька `WALLET_SELECT`;
|
||||
- `Показать баланс кошелька` читает реальный баланс именно активного выбранного кошелька из `Solana RPC`;
|
||||
- `Показать QR-код кошелька` открывает экран `WALLET_QR`;
|
||||
- этот экран не меняет логику Solana-регистрации пользователя: on-chain регистрация и проверки `PDA` по-прежнему завязаны на `device key`.
|
||||
- этот экран не меняет логику Solana-регистрации пользователя: on-chain регистрация и проверки `PDA` по-прежнему завязаны на `client key`.
|
||||
|
||||
## Экран WALLET_SELECT
|
||||
|
||||
@@ -437,14 +437,14 @@
|
||||
|
||||
- строку `Current: <selected wallet name>`;
|
||||
- три кнопки выбора:
|
||||
- `DeviceKey`
|
||||
- `ClientKey`
|
||||
- `RootKey`
|
||||
- `Custom` или `Custom: <имя>`;
|
||||
- у текущего выбора видна галочка.
|
||||
|
||||
Поведение:
|
||||
|
||||
- `DeviceKey` активирует кошелёк, выведенный из suffix `dev.key`;
|
||||
- `ClientKey` активирует кошелёк, выведенный из suffix `client.key`;
|
||||
- `RootKey` активирует кошелёк, выведенный из suffix `root.key`;
|
||||
- `Custom` использует derivation:
|
||||
`sha256(base64(secret32) + "|wallet." + customName)`;
|
||||
@@ -533,7 +533,7 @@
|
||||
- строку `Session: <platform/name>`;
|
||||
- строку `Kind: Client session` или `Kind: Wallet session`;
|
||||
- пояснение:
|
||||
- для client session: `Only device key will be transferred. No additional keys will be sent.`
|
||||
- для client session: `Only client key will be transferred. No additional keys will be sent.`
|
||||
- для wallet session: `No keys will be transferred.`
|
||||
|
||||
Кнопки:
|
||||
@@ -544,7 +544,7 @@
|
||||
Поведение:
|
||||
|
||||
- `YES` подтверждает заявку:
|
||||
- для client session устройство передаёт только `device key`;
|
||||
- для client session устройство передаёт только `client key`;
|
||||
- для wallet session устройство выпускает отдельную `wallet-session` без передачи ключей;
|
||||
- `NO` отклоняет заявку;
|
||||
- после любого решения устройство возвращается в список `REQUESTS` и обновляет его;
|
||||
|
||||
Reference in New Issue
Block a user