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

View File

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

View File

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

View File

@ -2,6 +2,7 @@ package server.logic.ws_protocol.JSON.handlers.auth;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import server.logic.ws_protocol.Base64Ws;
import server.logic.ws_protocol.JSON.ActiveConnectionsRegistry;
import server.logic.ws_protocol.JSON.ConnectionContext;
import server.logic.ws_protocol.JSON.entyties.Net_Request;
@ -21,7 +22,6 @@ import utils.crypto.Ed25519Util;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.util.Base64;
/**
* 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()) {
return NetExceptionResponseFactory.error(
req,
@ -250,20 +250,15 @@ public class Net_SessionLogin_Handler implements JsonMessageHandler {
String signatureB64
) throws IllegalArgumentException {
// pubKey: Base64(32). (Ed25519Util.keyFromBase64 должен использовать стандартный Base64)
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;
byte[] preimage = preimageStr.getBytes(StandardCharsets.UTF_8);
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 {
/** Клиентский пароль для хранения данных (base64url от 32 байт). */
/** Клиентский пароль для хранения данных (base64 от 32 байт). */
private String storagePwd;
/** Публичный ключ сессии (sessionPubKey), base64 от 32 байт. */

View File

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

View File

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

View File

@ -7,6 +7,7 @@ import blockchain.body.BodyHasLine;
import blockchain.body.BodyHasTarget;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import server.logic.ws_protocol.Base64Ws;
import server.logic.ws_protocol.JSON.ConnectionContext;
import server.logic.ws_protocol.JSON.entyties.Net_Request;
import server.logic.ws_protocol.JSON.entyties.Net_Response;
@ -23,7 +24,6 @@ import shine.db.entities.BlockEntry;
import utils.blockchain.BlockchainNameUtil;
import java.util.Arrays;
import java.util.Base64;
import java.util.concurrent.locks.ReentrantLock;
/**
@ -325,7 +325,7 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
private static byte[] decodeBase64(String b64) {
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) {

View File

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

View File

@ -2,6 +2,7 @@ package server.logic.ws_protocol.JSON.handlers.userParams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import server.logic.ws_protocol.Base64Ws;
import server.logic.ws_protocol.JSON.ConnectionContext;
import server.logic.ws_protocol.JSON.entyties.Net_Request;
import server.logic.ws_protocol.JSON.entyties.Net_Response;
@ -21,7 +22,6 @@ import utils.crypto.Ed25519Util;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Base64;
/**
* Net_UpsertUserParam_Handler
@ -73,8 +73,8 @@ public class Net_UpsertUserParam_Handler implements JsonMessageHandler {
byte[] pubKey32;
byte[] sig64;
try {
pubKey32 = Base64.getDecoder().decode(deviceKeyB64);
sig64 = Base64.getDecoder().decode(signatureB64);
pubKey32 = Base64Ws.decodeLen(deviceKeyB64, 32, "device_key");
sig64 = Base64Ws.decodeLen(signatureB64, 64, "signature");
} catch (IllegalArgumentException e) {
return NetExceptionResponseFactory.error(
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 ----------------
String signText = ShineSignatureConstants.USER_PARAMETER_PREFIX
+ login