Зделал всё только на база64 без всяких урл сафе

Вроде всё работает
тест весь проходит
This commit is contained in:
AidarKC 2026-01-29 19:29:08 +03:00
parent 0b2bee0a3d
commit b5706d3ed5
13 changed files with 102 additions and 86 deletions

View File

@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -euo pipefail
OUTFILE="all_files.txt"
# очищаем или создаём файл
: > "$OUTFILE"
# собрать только *.java файлы и вывести их содержимое в файл
find . -type f -name "*.java" | sort | while read -r f; do
cat "$f" >> "$OUTFILE"
echo >> "$OUTFILE" # пустая строка-разделитель
done
# скопировать весь файл в буфер обмена (Wayland)
wl-copy < "$OUTFILE"
echo "Готово!"
echo "Все .java файлы собраны в $OUTFILE"
echo "Содержимое скопировано в буфер обмена (Wayland)"

View File

@ -0,0 +1,40 @@
package server.logic.ws_protocol;
import java.util.Base64;
/**
* Единая утилита Base64 для всего WS-протокола.
*
* ВАЖНО:
* - Используем ТОЛЬКО стандартный Base64 (RFC 4648) алфавит: '+' и '/'.
* - Без padding '=' (чтобы строки были короче и стабильнее для JSON).
* - Декодер при этом спокойно принимает и с '=' и без '='.
*/
public final class Base64Ws {
private static final Base64.Encoder ENC = Base64.getEncoder().withoutPadding();
private static final Base64.Decoder DEC = Base64.getDecoder();
private Base64Ws() {}
public static String encode(byte[] bytes) {
if (bytes == null) throw new IllegalArgumentException("bytes == null");
return ENC.encodeToString(bytes);
}
public static byte[] decode(String b64) throws IllegalArgumentException {
if (b64 == null) throw new IllegalArgumentException("base64 is null");
String s = b64.trim();
if (s.isEmpty()) throw new IllegalArgumentException("base64 is empty");
return DEC.decode(s);
}
public static byte[] decodeLen(String b64, int expectedLen, String fieldName) throws IllegalArgumentException {
byte[] v = decode(b64);
if (v.length != expectedLen) {
String f = (fieldName == null || fieldName.isBlank()) ? "value" : fieldName;
throw new IllegalArgumentException(f + " must be " + expectedLen + " bytes, got " + v.length);
}
return v;
}
}

View File

@ -1,5 +1,6 @@
package server.logic.ws_protocol.JSON.handlers.auth; package server.logic.ws_protocol.JSON.handlers.auth;
import server.logic.ws_protocol.Base64Ws;
import server.logic.ws_protocol.JSON.ConnectionContext; import server.logic.ws_protocol.JSON.ConnectionContext;
import server.logic.ws_protocol.JSON.entyties.Net_Request; import server.logic.ws_protocol.JSON.entyties.Net_Request;
import server.logic.ws_protocol.JSON.entyties.Net_Response; import server.logic.ws_protocol.JSON.entyties.Net_Response;
@ -12,7 +13,6 @@ import shine.db.dao.SolanaUsersDAO;
import shine.db.entities.SolanaUserEntry; import shine.db.entities.SolanaUserEntry;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Base64;
/** /**
* AuthChallenge (v2) шаг 1 создания новой сессии. * AuthChallenge (v2) шаг 1 создания новой сессии.
@ -72,7 +72,7 @@ public class Net_AuthChallenge_Handler implements JsonMessageHandler {
byte[] buf = new byte[32]; byte[] buf = new byte[32];
RANDOM.nextBytes(buf); RANDOM.nextBytes(buf);
String authNonce = Base64.getUrlEncoder().withoutPadding().encodeToString(buf); String authNonce = Base64Ws.encode(buf);
ctx.setAuthNonce(authNonce); ctx.setAuthNonce(authNonce);

View File

@ -1,8 +1,8 @@
package server.logic.ws_protocol.JSON.handlers.auth; package server.logic.ws_protocol.JSON.handlers.auth;
import org.eclipse.jetty.websocket.api.Session;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import server.logic.ws_protocol.Base64Ws;
import server.logic.ws_protocol.JSON.ActiveConnectionsRegistry; import server.logic.ws_protocol.JSON.ActiveConnectionsRegistry;
import server.logic.ws_protocol.JSON.ConnectionContext; import server.logic.ws_protocol.JSON.ConnectionContext;
import server.logic.ws_protocol.JSON.entyties.Net_Request; import server.logic.ws_protocol.JSON.entyties.Net_Request;
@ -20,10 +20,11 @@ import shine.geo.ClientInfoService;
import shine.geo.GeoLookupService; import shine.geo.GeoLookupService;
import utils.crypto.Ed25519Util; import utils.crypto.Ed25519Util;
import org.eclipse.jetty.websocket.api.Session;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Base64;
/** /**
* CreateAuthSession (v2) шаг 2 создания новой сессии (ТОЛЬКО deviceKey). * CreateAuthSession (v2) шаг 2 создания новой сессии (ТОЛЬКО deviceKey).
@ -109,7 +110,7 @@ public class Net_CreateAuthSession__Handler implements JsonMessageHandler {
// Проверим, что sessionPubKeyB64 декодируется в 32 байта // Проверим, что sessionPubKeyB64 декодируется в 32 байта
byte[] sessionPubKey32; byte[] sessionPubKey32;
try { try {
sessionPubKey32 = decodeBase64Any(sessionPubKeyB64); sessionPubKey32 = Base64Ws.decode(sessionPubKeyB64);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
Net_Response err = NetExceptionResponseFactory.error( Net_Response err = NetExceptionResponseFactory.error(
req, req,
@ -291,7 +292,7 @@ public class Net_CreateAuthSession__Handler implements JsonMessageHandler {
// deviceKey (pub, 32) // deviceKey (pub, 32)
byte[] publicKey32 = Ed25519Util.keyFromBase64(user.getDeviceKey()); byte[] publicKey32 = Ed25519Util.keyFromBase64(user.getDeviceKey());
byte[] signature64 = decodeBase64Any(signatureB64); byte[] signature64 = Base64Ws.decode(signatureB64);
String preimageStr = "AUTH_CREATE_SESSION:" + login + ":" + timeMs + ":" + authNonce; String preimageStr = "AUTH_CREATE_SESSION:" + login + ":" + timeMs + ":" + authNonce;
byte[] preimage = preimageStr.getBytes(StandardCharsets.UTF_8); byte[] preimage = preimageStr.getBytes(StandardCharsets.UTF_8);
@ -302,19 +303,6 @@ public class Net_CreateAuthSession__Handler implements JsonMessageHandler {
private static String generateRandom32B64Url() { private static String generateRandom32B64Url() {
byte[] buf = new byte[32]; byte[] buf = new byte[32];
RANDOM.nextBytes(buf); RANDOM.nextBytes(buf);
return Base64.getUrlEncoder().withoutPadding().encodeToString(buf); return Base64Ws.encode(buf);
}
private static byte[] decodeBase64Any(String s) throws IllegalArgumentException {
if (s == null) throw new IllegalArgumentException("base64 is null");
String x = s.trim();
if (x.isEmpty()) throw new IllegalArgumentException("base64 is empty");
// сначала url-safe, потом обычный
try {
return Base64.getUrlDecoder().decode(x);
} catch (IllegalArgumentException ignore) {
return Base64.getDecoder().decode(x);
}
} }
} }

View File

@ -1,5 +1,6 @@
package server.logic.ws_protocol.JSON.handlers.auth; package server.logic.ws_protocol.JSON.handlers.auth;
import server.logic.ws_protocol.Base64Ws;
import server.logic.ws_protocol.JSON.ConnectionContext; import server.logic.ws_protocol.JSON.ConnectionContext;
import server.logic.ws_protocol.JSON.entyties.Net_Request; import server.logic.ws_protocol.JSON.entyties.Net_Request;
import server.logic.ws_protocol.JSON.entyties.Net_Response; import server.logic.ws_protocol.JSON.entyties.Net_Response;
@ -13,7 +14,6 @@ import shine.db.entities.ActiveSessionEntry;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Base64;
/** /**
* SessionChallenge (v2) шаг 1 входа в существующую сессию. * SessionChallenge (v2) шаг 1 входа в существующую сессию.
@ -70,7 +70,7 @@ public class Net_SessionChallenge_Handler implements JsonMessageHandler {
byte[] buf = new byte[32]; byte[] buf = new byte[32];
RANDOM.nextBytes(buf); RANDOM.nextBytes(buf);
String nonce = Base64.getUrlEncoder().withoutPadding().encodeToString(buf); String nonce = Base64Ws.encode(buf);
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
ctx.setSessionLoginNonce(nonce); ctx.setSessionLoginNonce(nonce);

View File

@ -2,6 +2,7 @@ package server.logic.ws_protocol.JSON.handlers.auth;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import server.logic.ws_protocol.Base64Ws;
import server.logic.ws_protocol.JSON.ActiveConnectionsRegistry; import server.logic.ws_protocol.JSON.ActiveConnectionsRegistry;
import server.logic.ws_protocol.JSON.ConnectionContext; import server.logic.ws_protocol.JSON.ConnectionContext;
import server.logic.ws_protocol.JSON.entyties.Net_Request; import server.logic.ws_protocol.JSON.entyties.Net_Request;
@ -21,7 +22,6 @@ import utils.crypto.Ed25519Util;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Base64;
/** /**
* SessionLogin (v2) шаг 2 входа в существующую сессию (по sessionKey). * SessionLogin (v2) шаг 2 входа в существующую сессию (по sessionKey).
@ -121,7 +121,7 @@ public class Net_SessionLogin_Handler implements JsonMessageHandler {
); );
} }
String sessionPubKeyB64 = session.getSessionKey(); // это pubKey String sessionPubKeyB64 = session.getSessionKey(); // это pubKey (Base64(32))
if (sessionPubKeyB64 == null || sessionPubKeyB64.isBlank()) { if (sessionPubKeyB64 == null || sessionPubKeyB64.isBlank()) {
return NetExceptionResponseFactory.error( return NetExceptionResponseFactory.error(
req, req,
@ -250,20 +250,15 @@ public class Net_SessionLogin_Handler implements JsonMessageHandler {
String signatureB64 String signatureB64
) throws IllegalArgumentException { ) throws IllegalArgumentException {
// pubKey: Base64(32). (Ed25519Util.keyFromBase64 должен использовать стандартный Base64)
byte[] publicKey32 = Ed25519Util.keyFromBase64(sessionPubKeyB64); byte[] publicKey32 = Ed25519Util.keyFromBase64(sessionPubKeyB64);
byte[] signature64 = decodeBase64Any(signatureB64);
// signature: Base64(64) через единую утилиту WS-протокола
byte[] signature64 = Base64Ws.decodeLen(signatureB64, 64, "signatureB64");
String preimageStr = "SESSION_LOGIN:" + sessionId + ":" + timeMs + ":" + nonce; String preimageStr = "SESSION_LOGIN:" + sessionId + ":" + timeMs + ":" + nonce;
byte[] preimage = preimageStr.getBytes(StandardCharsets.UTF_8); byte[] preimage = preimageStr.getBytes(StandardCharsets.UTF_8);
return Ed25519Util.verify(preimage, signature64, publicKey32); return Ed25519Util.verify(preimage, signature64, publicKey32);
} }
private static byte[] decodeBase64Any(String s) throws IllegalArgumentException {
try {
return Base64.getUrlDecoder().decode(s);
} catch (IllegalArgumentException ignore) {
return Base64.getDecoder().decode(s);
}
}
} }

View File

@ -18,7 +18,7 @@ import server.logic.ws_protocol.JSON.entyties.Net_Request;
*/ */
public class Net_CreateAuthSession_Request extends Net_Request { public class Net_CreateAuthSession_Request extends Net_Request {
/** Клиентский пароль для хранения данных (base64url от 32 байт). */ /** Клиентский пароль для хранения данных (base64 от 32 байт). */
private String storagePwd; private String storagePwd;
/** Публичный ключ сессии (sessionPubKey), base64 от 32 байт. */ /** Публичный ключ сессии (sessionPubKey), base64 от 32 байт. */

View File

@ -14,13 +14,13 @@ import server.logic.ws_protocol.JSON.entyties.Net_Response;
* "requestId": "...", * "requestId": "...",
* "status": 200, * "status": 200,
* "payload": { * "payload": {
* "sessionId": "base64url(32)" * "sessionId": "base64(32)"
* } * }
* } * }
*/ */
public class Net_CreateAuthSession_Response extends Net_Response { public class Net_CreateAuthSession_Response extends Net_Response {
/** Идентификатор сессии, base64url от 32 байт. */ /** Идентификатор сессии, base64 от 32 байт. */
private String sessionId; private String sessionId;
public String getSessionId() { public String getSessionId() {

View File

@ -4,7 +4,7 @@ import server.logic.ws_protocol.JSON.entyties.Net_Response;
/** /**
* Ответ на SessionChallenge (v2). * Ответ на SessionChallenge (v2).
* payload: { "nonce": "base64url(32)" } * payload: { "nonce": "base64(32)" }
*/ */
public class Net_SessionChallenge_Response extends Net_Response { public class Net_SessionChallenge_Response extends Net_Response {

View File

@ -4,7 +4,7 @@ import server.logic.ws_protocol.JSON.entyties.Net_Response;
/** /**
* Ответ на SessionLogin (v2). * Ответ на SessionLogin (v2).
* payload: { "storagePwd": "base64url(32)" } * payload: { "storagePwd": "base64(32)" }
*/ */
public class Net_SessionLogin_Response extends Net_Response { public class Net_SessionLogin_Response extends Net_Response {

View File

@ -7,6 +7,7 @@ import blockchain.body.BodyHasLine;
import blockchain.body.BodyHasTarget; import blockchain.body.BodyHasTarget;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import server.logic.ws_protocol.Base64Ws;
import server.logic.ws_protocol.JSON.ConnectionContext; import server.logic.ws_protocol.JSON.ConnectionContext;
import server.logic.ws_protocol.JSON.entyties.Net_Request; import server.logic.ws_protocol.JSON.entyties.Net_Request;
import server.logic.ws_protocol.JSON.entyties.Net_Response; import server.logic.ws_protocol.JSON.entyties.Net_Response;
@ -23,7 +24,6 @@ import shine.db.entities.BlockEntry;
import utils.blockchain.BlockchainNameUtil; import utils.blockchain.BlockchainNameUtil;
import java.util.Arrays; import java.util.Arrays;
import java.util.Base64;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
/** /**
@ -325,7 +325,7 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
private static byte[] decodeBase64(String b64) { private static byte[] decodeBase64(String b64) {
if (b64 == null) throw new IllegalArgumentException("blockBytesB64 == null"); if (b64 == null) throw new IllegalArgumentException("blockBytesB64 == null");
return Base64.getDecoder().decode(b64); return Base64Ws.decode(b64);
} }
private static long safeAdd(long a, long b) { private static long safeAdd(long a, long b) {

View File

@ -2,6 +2,7 @@ package server.logic.ws_protocol.JSON.handlers.tempToTest;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import server.logic.ws_protocol.Base64Ws;
import server.logic.ws_protocol.JSON.ConnectionContext; import server.logic.ws_protocol.JSON.ConnectionContext;
import server.logic.ws_protocol.JSON.entyties.Net_Request; import server.logic.ws_protocol.JSON.entyties.Net_Request;
import server.logic.ws_protocol.JSON.entyties.Net_Response; import server.logic.ws_protocol.JSON.entyties.Net_Response;
@ -19,7 +20,6 @@ import utils.blockchain.BlockchainNameUtil;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Base64;
public class Net_AddUser_Handler implements JsonMessageHandler { public class Net_AddUser_Handler implements JsonMessageHandler {
@ -62,33 +62,30 @@ public class Net_AddUser_Handler implements JsonMessageHandler {
try { try {
// базовая валидация форматов ключей: Base64(32 bytes) // базовая валидация форматов ключей: Base64(32 bytes)
byte[] solanaKey32 = Base64.getDecoder().decode(req.getSolanaKey()); byte[] solanaKey32;
if (solanaKey32.length != 32) { byte[] blockchainKey32;
byte[] deviceKey32;
try {
solanaKey32 = Base64Ws.decodeLen(req.getSolanaKey(), 32, "solanaKey");
blockchainKey32 = Base64Ws.decodeLen(req.getBlockchainKey(), 32, "blockchainKey");
deviceKey32 = Base64Ws.decodeLen(req.getDeviceKey(), 32, "deviceKey");
} catch (IllegalArgumentException e) {
return NetExceptionResponseFactory.error( return NetExceptionResponseFactory.error(
req, req,
WireCodes.Status.BAD_REQUEST, WireCodes.Status.BAD_REQUEST,
"BAD_SOLANA_KEY", "BAD_KEY_FORMAT",
"solanaKey должен быть Base64(32 bytes)" e.getMessage()
); );
} }
byte[] blockchainKey32 = Base64.getDecoder().decode(req.getBlockchainKey()); // (переменные не используются дальше, но оставляем для ясности проверки длины)
if (blockchainKey32.length != 32) { if (solanaKey32.length != 32 || blockchainKey32.length != 32 || deviceKey32.length != 32) {
return NetExceptionResponseFactory.error( return NetExceptionResponseFactory.error(
req, req,
WireCodes.Status.BAD_REQUEST, WireCodes.Status.BAD_REQUEST,
"BAD_BLOCKCHAIN_KEY", "BAD_KEY_FORMAT",
"blockchainKey должен быть Base64(32 bytes)" "solanaKey/blockchainKey/deviceKey должны быть Base64(32 bytes)"
);
}
byte[] deviceKey32 = Base64.getDecoder().decode(req.getDeviceKey());
if (deviceKey32.length != 32) {
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.BAD_REQUEST,
"BAD_DEVICE_KEY",
"deviceKey должен быть Base64(32 bytes)"
); );
} }
@ -167,13 +164,6 @@ public class Net_AddUser_Handler implements JsonMessageHandler {
return resp; return resp;
} catch (IllegalArgumentException e) {
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.BAD_REQUEST,
"BAD_KEY_FORMAT",
e.getMessage()
);
} catch (SQLException e) { } catch (SQLException e) {
log.error("❌ DB error AddUser", e); log.error("❌ DB error AddUser", e);
return NetExceptionResponseFactory.error( return NetExceptionResponseFactory.error(

View File

@ -2,6 +2,7 @@ package server.logic.ws_protocol.JSON.handlers.userParams;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import server.logic.ws_protocol.Base64Ws;
import server.logic.ws_protocol.JSON.ConnectionContext; import server.logic.ws_protocol.JSON.ConnectionContext;
import server.logic.ws_protocol.JSON.entyties.Net_Request; import server.logic.ws_protocol.JSON.entyties.Net_Request;
import server.logic.ws_protocol.JSON.entyties.Net_Response; import server.logic.ws_protocol.JSON.entyties.Net_Response;
@ -21,7 +22,6 @@ import utils.crypto.Ed25519Util;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Base64;
/** /**
* Net_UpsertUserParam_Handler * Net_UpsertUserParam_Handler
@ -73,8 +73,8 @@ public class Net_UpsertUserParam_Handler implements JsonMessageHandler {
byte[] pubKey32; byte[] pubKey32;
byte[] sig64; byte[] sig64;
try { try {
pubKey32 = Base64.getDecoder().decode(deviceKeyB64); pubKey32 = Base64Ws.decodeLen(deviceKeyB64, 32, "device_key");
sig64 = Base64.getDecoder().decode(signatureB64); sig64 = Base64Ws.decodeLen(signatureB64, 64, "signature");
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
return NetExceptionResponseFactory.error( return NetExceptionResponseFactory.error(
req, req,
@ -84,23 +84,6 @@ public class Net_UpsertUserParam_Handler implements JsonMessageHandler {
); );
} }
if (pubKey32.length != 32) {
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.BAD_REQUEST,
"BAD_DEVICE_KEY",
"device_key должен быть Base64(32 bytes)"
);
}
if (sig64.length != 64) {
return NetExceptionResponseFactory.error(
req,
WireCodes.Status.BAD_REQUEST,
"BAD_SIGNATURE",
"signature должна быть Base64(64 bytes)"
);
}
// ---------------- Signature verify ---------------- // ---------------- Signature verify ----------------
String signText = ShineSignatureConstants.USER_PARAMETER_PREFIX String signText = ShineSignatureConstants.USER_PARAMETER_PREFIX
+ login + login