diff --git a/shine-server-net-protocol/src/main/java/server/concat_to_file.sh b/shine-server-net-protocol/src/main/java/server/concat_to_file.sh new file mode 100755 index 0000000..f6db1f1 --- /dev/null +++ b/shine-server-net-protocol/src/main/java/server/concat_to_file.sh @@ -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)" diff --git a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/Base64Ws.java b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/Base64Ws.java new file mode 100644 index 0000000..ded95af --- /dev/null +++ b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/Base64Ws.java @@ -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; + } +} \ No newline at end of file diff --git a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/Net_AuthChallenge_Handler.java b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/Net_AuthChallenge_Handler.java index fb6cbe7..7cb0eb7 100644 --- a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/Net_AuthChallenge_Handler.java +++ b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/Net_AuthChallenge_Handler.java @@ -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); diff --git a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/Net_CreateAuthSession__Handler.java b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/Net_CreateAuthSession__Handler.java index e583238..11dd424 100644 --- a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/Net_CreateAuthSession__Handler.java +++ b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/Net_CreateAuthSession__Handler.java @@ -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); } } \ No newline at end of file diff --git a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/Net_SessionChallenge_Handler.java b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/Net_SessionChallenge_Handler.java index 6a4e7a6..43e51cd 100644 --- a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/Net_SessionChallenge_Handler.java +++ b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/Net_SessionChallenge_Handler.java @@ -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); diff --git a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/Net_SessionLogin_Handler.java b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/Net_SessionLogin_Handler.java index b2b0a8c..a0a5af6 100644 --- a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/Net_SessionLogin_Handler.java +++ b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/Net_SessionLogin_Handler.java @@ -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); - } - } } \ No newline at end of file diff --git a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/entyties/Net_CreateAuthSession_Request.java b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/entyties/Net_CreateAuthSession_Request.java index d851e2e..b0d7794 100644 --- a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/entyties/Net_CreateAuthSession_Request.java +++ b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/entyties/Net_CreateAuthSession_Request.java @@ -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 байт. */ diff --git a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/entyties/Net_CreateAuthSession_Response.java b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/entyties/Net_CreateAuthSession_Response.java index 6df3ec2..78b1782 100644 --- a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/entyties/Net_CreateAuthSession_Response.java +++ b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/entyties/Net_CreateAuthSession_Response.java @@ -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() { diff --git a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/entyties/Net_SessionChallenge_Response.java b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/entyties/Net_SessionChallenge_Response.java index fc4c80d..6f0402c 100644 --- a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/entyties/Net_SessionChallenge_Response.java +++ b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/entyties/Net_SessionChallenge_Response.java @@ -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 { diff --git a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/entyties/Net_SessionLogin_Response.java b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/entyties/Net_SessionLogin_Response.java index 2e046a3..4b4f252 100644 --- a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/entyties/Net_SessionLogin_Response.java +++ b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/auth/entyties/Net_SessionLogin_Response.java @@ -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 { diff --git a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/blockchain/Net_AddBlock_Handler.java b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/blockchain/Net_AddBlock_Handler.java index 519a08f..6145d1d 100644 --- a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/blockchain/Net_AddBlock_Handler.java +++ b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/blockchain/Net_AddBlock_Handler.java @@ -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) { diff --git a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/Net_AddUser_Handler.java b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/Net_AddUser_Handler.java index da58433..8b56632 100644 --- a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/Net_AddUser_Handler.java +++ b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/Net_AddUser_Handler.java @@ -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( diff --git a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/userParams/Net_UpsertUserParam_Handler.java b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/userParams/Net_UpsertUserParam_Handler.java index 2522311..5f1b179 100644 --- a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/userParams/Net_UpsertUserParam_Handler.java +++ b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/userParams/Net_UpsertUserParam_Handler.java @@ -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