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 3e9f6ff..e583238 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 @@ -35,7 +35,7 @@ import java.util.Base64; * - Сервер сохраняет sessionPubKeyB64 в active_sessions.session_key. * * Подпись deviceKey (Ed25519) проверяется над строкой (UTF-8): - * AUTH_CREATE_SESSION:{login}:{timeMs}:{authNonce}:{sessionPubKeyB64}:{storagePwd} + * AUTH_CREATE_SESSION:{login}:{timeMs}:{authNonce} * * На выходе: * - создаётся запись active_sessions @@ -106,7 +106,7 @@ public class Net_CreateAuthSession__Handler implements JsonMessageHandler { return err; } - // Проверим, что ключ декодируется в 32 байта + // Проверим, что sessionPubKeyB64 декодируется в 32 байта byte[] sessionPubKey32; try { sessionPubKey32 = decodeBase64Any(sessionPubKeyB64); @@ -183,8 +183,6 @@ public class Net_CreateAuthSession__Handler implements JsonMessageHandler { login, authNonce, timeMs, - sessionPubKeyB64, - storagePwd, signatureB64 ); } catch (IllegalArgumentException ex) { @@ -288,15 +286,14 @@ public class Net_CreateAuthSession__Handler implements JsonMessageHandler { String login, String authNonce, long timeMs, - String sessionPubKeyB64, - String storagePwd, String signatureB64 ) throws IllegalArgumentException { + // deviceKey (pub, 32) byte[] publicKey32 = Ed25519Util.keyFromBase64(user.getDeviceKey()); byte[] signature64 = decodeBase64Any(signatureB64); - String preimageStr = "AUTH_CREATE_SESSION:" + login + ":" + timeMs + ":" + authNonce + ":" + sessionPubKeyB64 + ":" + storagePwd; + String preimageStr = "AUTH_CREATE_SESSION:" + login + ":" + timeMs + ":" + authNonce; byte[] preimage = preimageStr.getBytes(StandardCharsets.UTF_8); return Ed25519Util.verify(preimage, signature64, publicKey32); @@ -309,11 +306,15 @@ public class Net_CreateAuthSession__Handler implements JsonMessageHandler { } 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(s); + return Base64.getUrlDecoder().decode(x); } catch (IllegalArgumentException ignore) { - return Base64.getDecoder().decode(s); + return Base64.getDecoder().decode(x); } } } \ No newline at end of file diff --git a/src/main/java/server/ws/TODO SERVER CODE UPDATE.txt b/src/main/java/server/ws/TODO SERVER CODE UPDATE.txt new file mode 100644 index 0000000..c1e8572 --- /dev/null +++ b/src/main/java/server/ws/TODO SERVER CODE UPDATE.txt @@ -0,0 +1,6 @@ +ура всё прошло. + +Другой надеюсь крайний на сегодня вопрос . + + +Сформулируй Подробное туду с тем что переделать сервак что бы он не обрывал соединение сразу из за любой ошибки (а видимо только в каких то случах гд енадо ... но в каких :) \ No newline at end of file diff --git a/src/test/java/test/it/runner/IT_RunAllMain.java b/src/test/java/test/it/runner/IT_RunAllMain.java index 5e81b9c..fb5fca2 100644 --- a/src/test/java/test/it/runner/IT_RunAllMain.java +++ b/src/test/java/test/it/runner/IT_RunAllMain.java @@ -15,8 +15,17 @@ import java.util.List; */ public class IT_RunAllMain { + /** + * Настройка поведения прогона: + * - true : остановить запуск сразу после первого упавшего теста + * - false : прогнать все тесты до конца, даже если некоторые упали + */ + private static final boolean STOP_ON_FIRST_FAIL = true; + public static void main(String[] args) { int failed = runAll(); + // при желании можно вернуть код выхода ОС: + // System.exit(failed == 0 ? 0 : 1); } public static int runAll() { @@ -24,13 +33,30 @@ public class IT_RunAllMain { List summaries = new ArrayList<>(); int failed = 0; - TestLog.title("IT RUN: запуск всех тестов подряд"); + TestLog.title("IT RUN: запуск всех тестов подряд" + + (STOP_ON_FIRST_FAIL ? " (STOP_ON_FIRST_FAIL=ON)" : " (STOP_ON_FIRST_FAIL=OFF)")); - String s1 = IT_01_AddUser.run(); summaries.add(s1); if (s1.contains("FAIL:")) failed++; - String s2 = IT_02_Sessions.run(); summaries.add(s2); if (s2.contains("FAIL:")) failed++; - String s3 = IT_03_AddBlock_NoAuth.run(); summaries.add(s3); if (s3.contains("FAIL:")) failed++; - String s4 = IT_04_UserParams_NoAuth.run(); summaries.add(s4); if (s4.contains("FAIL:")) failed++; + String s1 = IT_01_AddUser.run(); summaries.add(s1); + if (s1.contains("FAIL:")) { failed++; if (STOP_ON_FIRST_FAIL) return finishEarly(summaries, failed); } + String s2 = IT_02_Sessions.run(); summaries.add(s2); + if (s2.contains("FAIL:")) { failed++; if (STOP_ON_FIRST_FAIL) return finishEarly(summaries, failed); } + + String s3 = IT_03_AddBlock_NoAuth.run(); summaries.add(s3); + if (s3.contains("FAIL:")) { failed++; if (STOP_ON_FIRST_FAIL) return finishEarly(summaries, failed); } + + String s4 = IT_04_UserParams_NoAuth.run(); summaries.add(s4); + if (s4.contains("FAIL:")) { failed++; if (STOP_ON_FIRST_FAIL) return finishEarly(summaries, failed); } + + return finish(summaries, failed); + } + + private static int finishEarly(List summaries, int failed) { + TestLog.boom("⛔ Остановка прогона: найден FAIL, STOP_ON_FIRST_FAIL=ON"); + return finish(summaries, failed); + } + + private static int finish(List summaries, int failed) { TestLog.title("IT RUN RESULT (per test)"); for (String s : summaries) System.out.println(s); @@ -39,4 +65,4 @@ public class IT_RunAllMain { return failed; } -} \ No newline at end of file +} diff --git a/src/test/java/test/it/utils/TestConfig.java b/src/test/java/test/it/utils/TestConfig.java index 1561da4..8f0014a 100644 --- a/src/test/java/test/it/utils/TestConfig.java +++ b/src/test/java/test/it/utils/TestConfig.java @@ -9,13 +9,14 @@ import java.util.concurrent.ConcurrentHashMap; /** * TestConfig — конфиг IT тестов: * - 3 пользователя (TestUser1/2/3) - * - ключи по login через map (device/solana/blockchain) + * - ключи по login через map (device/solana/blockchain/session) * - blockchainName = login + "-" + "001" * * Важно: - * - privateKey = Ed25519Util.generatePrivateKeyFromString(login) (sha256, 32 bytes) + * - privateKey = Ed25519Util.generatePrivateKeyFromString(seed) (sha256(seed) => 32 bytes) * - publicKey = Ed25519Util.derivePublicKey(privateKey) - * - пока device/solana/blockchain ключи одинаковые (один seed на login) + * - device/solana/blockchain ключи пока одинаковые (seed = login) + * - session ключ отдельный (seed = "session:" + login) — чтобы SessionLogin был честнее. */ public final class TestConfig { @@ -58,6 +59,10 @@ public final class TestConfig { private static final Map bchPriv = new ConcurrentHashMap<>(); private static final Map bchPub = new ConcurrentHashMap<>(); + // NEW: session keys (для SessionLogin v2) + private static final Map sessionPriv = new ConcurrentHashMap<>(); + private static final Map sessionPub = new ConcurrentHashMap<>(); + static { initUserKeys(LOGIN()); initUserKeys(LOGIN2()); @@ -65,6 +70,7 @@ public final class TestConfig { } private static void initUserKeys(String login) { + // seed = login byte[] priv = Ed25519Util.generatePrivateKeyFromString(login); // sha256(login) => 32 bytes byte[] pub = Ed25519Util.derivePublicKey(priv); @@ -77,6 +83,13 @@ public final class TestConfig { bchPriv.put(login, priv); bchPub.put(login, pub); + + // session seed = "session:" + login (отдельно!) + byte[] sPriv = Ed25519Util.generatePrivateKeyFromString("session:" + login); + byte[] sPub = Ed25519Util.derivePublicKey(sPriv); + + sessionPriv.put(login, sPriv); + sessionPub.put(login, sPub); } // ============ requested getters (with your names) ============ @@ -90,11 +103,18 @@ public final class TestConfig { public static byte[] getBlockchainPrivatKey(String login) { return cloneOrThrow(bchPriv.get(login), "bchPriv", login); } public static byte[] getBlockchainPublicKey(String login) { return cloneOrThrow(bchPub.get(login), "bchPub", login); } + // NEW: session getters + public static byte[] getSessionPrivatKey(String login) { return cloneOrThrow(sessionPriv.get(login), "sessionPriv", login); } + public static byte[] getSessionPublicKey(String login) { return cloneOrThrow(sessionPub.get(login), "sessionPub", login); } + // ============ base64 helpers ============ public static String devicePublicKeyB64(String login) { return Base64.getEncoder().encodeToString(getDevicePublicKey(login)); } public static String solanaPublicKeyB64(String login) { return Base64.getEncoder().encodeToString(getSolanaPublicKey(login)); } public static String blockchainPublicKeyB64(String login) { return Base64.getEncoder().encodeToString(getBlockchainPublicKey(login)); } + // NEW: session pub b64 helper + public static String sessionPublicKeyB64(String login) { return Base64.getEncoder().encodeToString(getSessionPublicKey(login)); } + // ============ backward-compatible helpers for "user1" ============ public static String BCH_NAME() { return getBlockchainName(LOGIN()); } public static String BCH_NAME2() { return getBlockchainName(LOGIN2()); } @@ -114,6 +134,11 @@ public final class TestConfig { public static String DEVICE2_PUBKEY_B64() { return devicePublicKeyB64(LOGIN2()); } public static String DEVICE3_PUBKEY_B64() { return devicePublicKeyB64(LOGIN3()); } + // NEW: session pub b64 compat + public static String SESSION_PUBKEY_B64() { return sessionPublicKeyB64(LOGIN()); } + public static String SESSION2_PUBKEY_B64() { return sessionPublicKeyB64(LOGIN2()); } + public static String SESSION3_PUBKEY_B64() { return sessionPublicKeyB64(LOGIN3()); } + // ============ misc ============ public static String fakeStoragePwd() { return "pwd-" + System.nanoTime(); diff --git a/src/test/java/test/it/utils/json/JsonBuilders.java b/src/test/java/test/it/utils/json/JsonBuilders.java index 0ec5e31..1dd15b7 100644 --- a/src/test/java/test/it/utils/json/JsonBuilders.java +++ b/src/test/java/test/it/utils/json/JsonBuilders.java @@ -59,13 +59,20 @@ public final class JsonBuilders { } // ---------------- CreateAuthSession (v2) ---------------- - // v2: sessionKey генерируется на клиенте, на сервер отправляем только sessionPubKey (base64). - // подпись шага CreateAuthSession всё ещё делается deviceKey: "AUTHORIFICATED:" + timeMs + authNonce + // v2: sessionKey генерируется/хранится на клиенте, на сервер отправляем sessionPubKeyB64 (base64). + // + // ВАЖНО (новое правило): + // Подпись CreateAuthSession делается ТОЛЬКО deviceKey над строкой: + // preimage = "AUTH_CREATE_SESSION:" + login + ":" + timeMs + ":" + authNonce + // + // storagePwd и sessionPubKeyB64 НЕ входят в preimage. public static String createAuthSessionV2(String login, String authNonce, String storagePwd, String sessionPubKeyB64) { long timeMs = System.currentTimeMillis(); + + // подпись делаем devicePrivKey byte[] devicePriv = TestConfig.getDevicePrivatKey(login); - String sigB64 = signAuthorificated(authNonce, timeMs, devicePriv); + String sigB64 = signAuthCreateSession(login, timeMs, authNonce, devicePriv); String requestId = TestIds.next("create"); return """ @@ -106,6 +113,8 @@ public final class JsonBuilders { } // ---------------- SessionLogin (v2) ---------------- + // Подпись SessionLogin по-прежнему делается sessionPrivKey: + // preimage = "SESSION_LOGIN:" + sessionId + ":" + timeMs + ":" + nonce public static String sessionLogin(String sessionId, String nonce, byte[] sessionPrivKey) { long timeMs = System.currentTimeMillis(); @@ -136,8 +145,6 @@ public final class JsonBuilders { "op": "ListSessions", "requestId": "%s", "payload": { - "timeMs": %d, - "signatureB64": "%s" } } """.formatted(requestId, timeMs, signatureB64); @@ -153,9 +160,7 @@ public final class JsonBuilders { "op": "CloseActiveSession", "requestId": "%s", "payload": { - "sessionId": "%s", - "timeMs": %d, - "signatureB64": "%s" + "sessionId": "%s" } } """.formatted(requestId, sessionId, timeMs, signatureB64); @@ -175,12 +180,12 @@ public final class JsonBuilders { } /** - * Подпись для режима AUTH_IN_PROGRESS: - * preimage = "AUTHORIFICATED:" + timeMs + authNonce + * Подпись CreateAuthSession(v2): + * preimage = "AUTH_CREATE_SESSION:" + login + ":" + timeMs + ":" + authNonce * подписываем devicePrivKey. */ - public static String signAuthorificated(String authNonce, long timeMs, byte[] devicePrivKey) { - String preimageStr = "AUTHORIFICATED:" + timeMs + authNonce; + public static String signAuthCreateSession(String login, long timeMs, String authNonce, byte[] devicePrivKey) { + String preimageStr = "AUTH_CREATE_SESSION:" + login + ":" + timeMs + ":" + authNonce; byte[] preimage = preimageStr.getBytes(StandardCharsets.UTF_8); byte[] sig = Ed25519Util.sign(preimage, devicePrivKey); return Base64.getEncoder().encodeToString(sig);