SHA256
Добавить SendSignal и remote AddBlock через homeserver
This commit is contained in:
+310
@@ -285,6 +285,17 @@ struct ActiveWalletSignRequest {
|
||||
Screen returnScreen = SCREEN_HOME;
|
||||
};
|
||||
|
||||
struct PendingRemoteAddBlockRequest {
|
||||
String fromLogin;
|
||||
String fromSessionId;
|
||||
String signalRequestId;
|
||||
String signalType;
|
||||
String data;
|
||||
String sessionSignatureB64;
|
||||
String clientSignatureB64;
|
||||
uint64_t timeMs = 0;
|
||||
};
|
||||
|
||||
static lv_disp_draw_buf_t gDrawBuf;
|
||||
static lv_color_t *gBuf1 = nullptr;
|
||||
static lv_color_t *gBuf2 = nullptr;
|
||||
@@ -415,6 +426,7 @@ static String gCustomWalletName;
|
||||
static String gCustomWalletPubB58;
|
||||
static String gCustomWalletPrivB58;
|
||||
static std::vector<PendingWalletRpcRequest> gPendingWalletRpcRequests;
|
||||
static std::vector<PendingRemoteAddBlockRequest> gPendingRemoteAddBlockRequests;
|
||||
static const int kWalletRpcSignalTypeRequest = 9100;
|
||||
static const int kWalletRpcSignalTypeResponse = 9101;
|
||||
static ActiveWalletSignRequest gActiveWalletSignRequest;
|
||||
@@ -477,7 +489,9 @@ static void refreshSelectedWalletBalanceState();
|
||||
static bool loadSelectedWalletBalanceLamports(uint64_t &lamportsOut, String &messageOut);
|
||||
static bool loadBalanceLamportsForAddress(const String &address, uint64_t &lamportsOut, String &messageOut);
|
||||
static bool queueIncomingWalletRpcRequest(const String &frame);
|
||||
static bool queueIncomingRemoteAddBlockRequest(const String &frame);
|
||||
static void processPendingWalletRpcRequests();
|
||||
static void processPendingRemoteAddBlockRequests();
|
||||
static void pumpShineIncomingFrames(uint32_t maxFrames = 3);
|
||||
static String loginDisplayValue();
|
||||
static String homeserverDisplayValue();
|
||||
@@ -617,6 +631,22 @@ static bool sendWalletRpcResponse(const String &toLogin,
|
||||
const String &targetSessionId,
|
||||
const String &callId,
|
||||
const String &responseData);
|
||||
static bool sendSignalResponse(const String &toLogin,
|
||||
const String &targetSessionId,
|
||||
const String &signalType,
|
||||
const String &signalRequestId,
|
||||
const String &responseData);
|
||||
static bool buildSendSignalSignatures(const String &toLogin,
|
||||
const String &targetMode,
|
||||
const String &targetSessionId,
|
||||
const String &signalType,
|
||||
const String &signalRequestId,
|
||||
const String &data,
|
||||
uint64_t timeMs,
|
||||
bool includeClientSignature,
|
||||
String &sessionSignatureB64Out,
|
||||
String &clientSignatureB64Out,
|
||||
String &errorOut);
|
||||
static void queueWalletSignRequest(const PendingWalletRpcRequest &item,
|
||||
const String &requestId,
|
||||
const String &publicKeyBase58,
|
||||
@@ -2695,6 +2725,126 @@ static bool sendWalletRpcResponse(const String &toLogin,
|
||||
return shineWsRequest(gShineWs, "CallSignalToSession", req, response, SHINE_RPC_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
static bool buildSendSignalSignatures(const String &toLogin,
|
||||
const String &targetMode,
|
||||
const String &targetSessionId,
|
||||
const String &signalType,
|
||||
const String &signalRequestId,
|
||||
const String &data,
|
||||
uint64_t timeMs,
|
||||
bool includeClientSignature,
|
||||
String &sessionSignatureB64Out,
|
||||
String &clientSignatureB64Out,
|
||||
String &errorOut) {
|
||||
sessionSignatureB64Out = "";
|
||||
clientSignatureB64Out = "";
|
||||
errorOut = "";
|
||||
|
||||
if (gLoginValue.isEmpty() || gShineSessionId.isEmpty()) {
|
||||
errorOut = "session_context_missing";
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t dataHash32[32] = {};
|
||||
sha256calc(reinterpret_cast<const uint8_t *>(data.c_str()), data.length(), dataHash32);
|
||||
String dataSha256B64 = bytesToBase64String(dataHash32, sizeof(dataHash32));
|
||||
|
||||
uint8_t subSeed[32] = {};
|
||||
uint8_t subPub[32] = {};
|
||||
uint8_t subSec[64] = {};
|
||||
if (!deriveSeedKeypairFromBase58(gHomeserverPrivB58, subSeed, subPub, subSec)) {
|
||||
errorOut = "homeserver_session_key_unavailable";
|
||||
return false;
|
||||
}
|
||||
|
||||
String sessionPreimage = String("SEND_SIGNAL_SESSION:")
|
||||
+ gLoginValue + ":"
|
||||
+ gShineSessionId + ":"
|
||||
+ toLogin + ":"
|
||||
+ targetMode + ":"
|
||||
+ targetSessionId + ":"
|
||||
+ signalType + ":"
|
||||
+ signalRequestId + ":"
|
||||
+ String((unsigned long long)timeMs) + ":"
|
||||
+ dataSha256B64;
|
||||
uint8_t sessionSignature[64] = {};
|
||||
std::vector<uint8_t> sessionMessage(reinterpret_cast<const uint8_t *>(sessionPreimage.c_str()),
|
||||
reinterpret_cast<const uint8_t *>(sessionPreimage.c_str()) + sessionPreimage.length());
|
||||
if (!signMessageEd25519(sessionMessage, subSec, sessionSignature)) {
|
||||
errorOut = "session_signal_sign_failed";
|
||||
return false;
|
||||
}
|
||||
sessionSignatureB64Out = bytesToBase64String(sessionSignature, sizeof(sessionSignature));
|
||||
|
||||
if (!includeClientSignature) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t clientSeed[32] = {};
|
||||
uint8_t clientPub[32] = {};
|
||||
uint8_t clientSec[64] = {};
|
||||
if (!deriveSeedKeypairFromBase58(gDevicePrivB58, clientSeed, clientPub, clientSec)) {
|
||||
errorOut = "client_key_unavailable";
|
||||
return false;
|
||||
}
|
||||
|
||||
String clientPreimage = String("SEND_SIGNAL_CLIENT:")
|
||||
+ gLoginValue + ":"
|
||||
+ gShineSessionId + ":"
|
||||
+ toLogin + ":"
|
||||
+ targetMode + ":"
|
||||
+ targetSessionId + ":"
|
||||
+ signalType + ":"
|
||||
+ signalRequestId + ":"
|
||||
+ String((unsigned long long)timeMs) + ":"
|
||||
+ dataSha256B64;
|
||||
uint8_t clientSignature[64] = {};
|
||||
std::vector<uint8_t> clientMessage(reinterpret_cast<const uint8_t *>(clientPreimage.c_str()),
|
||||
reinterpret_cast<const uint8_t *>(clientPreimage.c_str()) + clientPreimage.length());
|
||||
if (!signMessageEd25519(clientMessage, clientSec, clientSignature)) {
|
||||
errorOut = "client_signal_sign_failed";
|
||||
return false;
|
||||
}
|
||||
clientSignatureB64Out = bytesToBase64String(clientSignature, sizeof(clientSignature));
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool sendSignalResponse(const String &toLogin,
|
||||
const String &targetSessionId,
|
||||
const String &signalType,
|
||||
const String &signalRequestId,
|
||||
const String &responseData) {
|
||||
String response;
|
||||
uint64_t timeMs = shineNowMs();
|
||||
String sessionSignatureB64;
|
||||
String clientSignatureB64;
|
||||
String error;
|
||||
if (!buildSendSignalSignatures(toLogin,
|
||||
"single_session",
|
||||
targetSessionId,
|
||||
signalType,
|
||||
signalRequestId,
|
||||
responseData,
|
||||
timeMs,
|
||||
false,
|
||||
sessionSignatureB64,
|
||||
clientSignatureB64,
|
||||
error)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String req = String("{\"toLogin\":\"") + jsonEscape(toLogin)
|
||||
+ "\",\"targetMode\":\"single_session\""
|
||||
+ ",\"targetSessionId\":\"" + jsonEscape(targetSessionId)
|
||||
+ "\",\"signalType\":\"" + jsonEscape(signalType)
|
||||
+ "\",\"signalRequestId\":\"" + jsonEscape(signalRequestId)
|
||||
+ "\",\"data\":\"" + jsonEscape(responseData)
|
||||
+ "\",\"timeMs\":" + String((unsigned long long)timeMs)
|
||||
+ ",\"sessionSignatureB64\":\"" + jsonEscape(sessionSignatureB64)
|
||||
+ "\",\"clientSignatureB64\":\"" + jsonEscape(clientSignatureB64) + "\"}";
|
||||
return shineWsRequest(gShineWs, "SendSignal", req, response, SHINE_RPC_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
static void queueWalletSignRequest(const PendingWalletRpcRequest &item,
|
||||
const String &requestId,
|
||||
const String &publicKeyBase58,
|
||||
@@ -4075,6 +4225,7 @@ static bool shineWsRequest(SimpleWebSocketClient &ws, const String &op, const St
|
||||
return true;
|
||||
}
|
||||
queueIncomingWalletRpcRequest(frame);
|
||||
queueIncomingRemoteAddBlockRequest(frame);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -4121,6 +4272,50 @@ static bool queueIncomingWalletRpcRequest(const String &frame) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool queueIncomingRemoteAddBlockRequest(const String &frame) {
|
||||
if (frame.indexOf("\"op\":\"IncomingSignal\"") < 0 || frame.indexOf("\"event\":true") < 0) {
|
||||
return false;
|
||||
}
|
||||
int payloadPos = frame.indexOf("\"payload\":");
|
||||
if (payloadPos < 0) {
|
||||
return false;
|
||||
}
|
||||
int objectStart = frame.indexOf('{', payloadPos);
|
||||
if (objectStart < 0) {
|
||||
return false;
|
||||
}
|
||||
int objectEnd = -1;
|
||||
String payloadJson;
|
||||
if (!extractJsonObjectAt(frame, objectStart, objectEnd, payloadJson)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String signalType;
|
||||
if (!jsonStringField(payloadJson, "signalType", signalType)) {
|
||||
return false;
|
||||
}
|
||||
if (signalType != "remote_addblock_request") {
|
||||
return false;
|
||||
}
|
||||
|
||||
PendingRemoteAddBlockRequest item;
|
||||
jsonStringField(payloadJson, "fromLogin", item.fromLogin);
|
||||
jsonStringField(payloadJson, "fromSessionId", item.fromSessionId);
|
||||
jsonStringField(payloadJson, "signalRequestId", item.signalRequestId);
|
||||
jsonStringField(payloadJson, "data", item.data);
|
||||
jsonStringField(payloadJson, "sessionSignatureB64", item.sessionSignatureB64);
|
||||
jsonStringField(payloadJson, "clientSignatureB64", item.clientSignatureB64);
|
||||
uint64_t timeMs = 0;
|
||||
jsonInt64Field(payloadJson, "timeMs", timeMs);
|
||||
item.timeMs = timeMs;
|
||||
item.signalType = signalType;
|
||||
if (item.fromLogin.isEmpty() || item.fromSessionId.isEmpty() || item.signalRequestId.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
gPendingRemoteAddBlockRequests.push_back(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void processPendingWalletRpcRequests() {
|
||||
if (gPendingWalletRpcRequests.empty() || !gShineAuthenticated || !gShineWs.connected || !gShineWs.client.connected()) {
|
||||
return;
|
||||
@@ -4180,6 +4375,119 @@ static void processPendingWalletRpcRequests() {
|
||||
}
|
||||
}
|
||||
|
||||
static void processPendingRemoteAddBlockRequests() {
|
||||
if (gPendingRemoteAddBlockRequests.empty() || !gShineAuthenticated || !gShineWs.connected || !gShineWs.client.connected()) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (!gPendingRemoteAddBlockRequests.empty()) {
|
||||
PendingRemoteAddBlockRequest item = gPendingRemoteAddBlockRequests.front();
|
||||
gPendingRemoteAddBlockRequests.erase(gPendingRemoteAddBlockRequests.begin());
|
||||
|
||||
String responseData;
|
||||
String requestLogin;
|
||||
String blockchainName;
|
||||
String prevBlockHash;
|
||||
String blockPreimageB64;
|
||||
uint64_t blockNumberU64 = 0;
|
||||
|
||||
if (item.fromLogin != gLoginValue) {
|
||||
responseData = String("{\"ok\":false,\"error\":\"forbidden_login\",\"errorMessage\":\"Signal login mismatch\",\"requestId\":\"")
|
||||
+ jsonEscape(item.signalRequestId) + "\"}";
|
||||
sendSignalResponse(item.fromLogin, item.fromSessionId, "remote_addblock_result", item.signalRequestId, responseData);
|
||||
continue;
|
||||
}
|
||||
if (item.sessionSignatureB64.isEmpty() || item.clientSignatureB64.isEmpty()) {
|
||||
responseData = String("{\"ok\":false,\"error\":\"missing_required_signatures\",\"errorMessage\":\"Client and session signatures are required\",\"requestId\":\"")
|
||||
+ jsonEscape(item.signalRequestId) + "\"}";
|
||||
sendSignalResponse(item.fromLogin, item.fromSessionId, "remote_addblock_result", item.signalRequestId, responseData);
|
||||
continue;
|
||||
}
|
||||
|
||||
jsonStringField(item.data, "login", requestLogin);
|
||||
jsonStringField(item.data, "blockchainName", blockchainName);
|
||||
jsonStringField(item.data, "prevBlockHash", prevBlockHash);
|
||||
jsonStringField(item.data, "blockPreimageB64", blockPreimageB64);
|
||||
jsonInt64Field(item.data, "blockNumber", blockNumberU64);
|
||||
if (requestLogin != gLoginValue || blockchainName.isEmpty() || blockPreimageB64.isEmpty() || prevBlockHash.isEmpty()) {
|
||||
responseData = String("{\"ok\":false,\"error\":\"bad_request\",\"errorMessage\":\"Missing required AddBlock fields\",\"requestId\":\"")
|
||||
+ jsonEscape(item.signalRequestId) + "\"}";
|
||||
sendSignalResponse(item.fromLogin, item.fromSessionId, "remote_addblock_result", item.signalRequestId, responseData);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> preimage;
|
||||
if (!base64DecodeStd(blockPreimageB64, preimage) || preimage.empty()) {
|
||||
responseData = String("{\"ok\":false,\"error\":\"bad_preimage_base64\",\"errorMessage\":\"Invalid AddBlock preimage base64\",\"requestId\":\"")
|
||||
+ jsonEscape(item.signalRequestId) + "\"}";
|
||||
sendSignalResponse(item.fromLogin, item.fromSessionId, "remote_addblock_result", item.signalRequestId, responseData);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t blockchainSeed[32] = {};
|
||||
uint8_t blockchainPub[32] = {};
|
||||
uint8_t blockchainSec[64] = {};
|
||||
if (!deriveSeedKeypairFromBase58(gBlockchainPrivB58, blockchainSeed, blockchainPub, blockchainSec)) {
|
||||
responseData = String("{\"ok\":false,\"error\":\"blockchain_key_unavailable\",\"errorMessage\":\"Blockchain key is unavailable on homeserver\",\"requestId\":\"")
|
||||
+ jsonEscape(item.signalRequestId) + "\"}";
|
||||
sendSignalResponse(item.fromLogin, item.fromSessionId, "remote_addblock_result", item.signalRequestId, responseData);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t hash32[32] = {};
|
||||
sha256calc(preimage.data(), preimage.size(), hash32);
|
||||
uint8_t signature[64] = {};
|
||||
if (!signMessageEd25519(std::vector<uint8_t>(hash32, hash32 + 32), blockchainSec, signature)) {
|
||||
responseData = String("{\"ok\":false,\"error\":\"sign_failed\",\"errorMessage\":\"Failed to sign AddBlock hash\",\"requestId\":\"")
|
||||
+ jsonEscape(item.signalRequestId) + "\"}";
|
||||
sendSignalResponse(item.fromLogin, item.fromSessionId, "remote_addblock_result", item.signalRequestId, responseData);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> fullBlock = preimage;
|
||||
fullBlock.push_back(0x01);
|
||||
fullBlock.push_back(0x00);
|
||||
fullBlock.insert(fullBlock.end(), signature, signature + 64);
|
||||
String addBlockReq = String("{\"blockchainName\":\"") + jsonEscape(blockchainName)
|
||||
+ "\",\"blockNumber\":" + String((unsigned long long)blockNumberU64)
|
||||
+ ",\"prevBlockHash\":\"" + jsonEscape(prevBlockHash)
|
||||
+ "\",\"blockBytesB64\":\"" + jsonEscape(bytesToBase64String(fullBlock.data(), fullBlock.size())) + "\"}";
|
||||
String addBlockResp;
|
||||
bool addBlockOk = shineWsRequest(gShineWs, "AddBlock", addBlockReq, addBlockResp, SHINE_RPC_TIMEOUT_MS);
|
||||
uint64_t statusCode = 0;
|
||||
jsonInt64Field(addBlockResp, "status", statusCode);
|
||||
String serverLastHash;
|
||||
String errorCode;
|
||||
String errorMessage;
|
||||
uint64_t serverLastNumber = 0;
|
||||
jsonStringField(addBlockResp, "serverLastGlobalHash", serverLastHash);
|
||||
jsonStringField(addBlockResp, "code", errorCode);
|
||||
jsonStringField(addBlockResp, "message", errorMessage);
|
||||
jsonInt64Field(addBlockResp, "serverLastGlobalNumber", serverLastNumber);
|
||||
|
||||
if (!addBlockOk || statusCode != 200) {
|
||||
if (errorCode.isEmpty()) {
|
||||
errorCode = addBlockOk ? "addblock_rejected" : "addblock_request_failed";
|
||||
}
|
||||
if (errorMessage.isEmpty()) {
|
||||
errorMessage = addBlockOk ? "AddBlock rejected by server" : "AddBlock request failed";
|
||||
}
|
||||
responseData = String("{\"ok\":false,\"error\":\"") + jsonEscape(errorCode)
|
||||
+ "\",\"errorMessage\":\"" + jsonEscape(errorMessage)
|
||||
+ "\",\"requestId\":\"" + jsonEscape(item.signalRequestId)
|
||||
+ "\",\"serverLastGlobalNumber\":" + String((unsigned long long)serverLastNumber)
|
||||
+ ",\"serverLastGlobalHash\":\"" + jsonEscape(serverLastHash) + "\"}";
|
||||
sendSignalResponse(item.fromLogin, item.fromSessionId, "remote_addblock_result", item.signalRequestId, responseData);
|
||||
continue;
|
||||
}
|
||||
|
||||
responseData = String("{\"ok\":true,\"requestId\":\"") + jsonEscape(item.signalRequestId)
|
||||
+ "\",\"serverLastGlobalNumber\":" + String((unsigned long long)serverLastNumber)
|
||||
+ ",\"serverLastGlobalHash\":\"" + jsonEscape(serverLastHash) + "\"}";
|
||||
sendSignalResponse(item.fromLogin, item.fromSessionId, "remote_addblock_result", item.signalRequestId, responseData);
|
||||
}
|
||||
}
|
||||
|
||||
static void pumpShineIncomingFrames(uint32_t maxFrames) {
|
||||
if (!gShineAuthenticated || !gShineWs.connected || !gShineWs.client.connected()) {
|
||||
return;
|
||||
@@ -4193,6 +4501,7 @@ static void pumpShineIncomingFrames(uint32_t maxFrames) {
|
||||
break;
|
||||
}
|
||||
queueIncomingWalletRpcRequest(frame);
|
||||
queueIncomingRemoteAddBlockRequest(frame);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4748,6 +5057,7 @@ static void manageShineConnection() {
|
||||
|
||||
pumpShineIncomingFrames();
|
||||
processPendingWalletRpcRequests();
|
||||
processPendingRemoteAddBlockRequests();
|
||||
}
|
||||
|
||||
static void upsertKnownWifi(const String &ssid, const String &password) {
|
||||
|
||||
Reference in New Issue
Block a user