From ec66b41609d217bb65a457cece1457bd531d34eb Mon Sep 17 00:00:00 2001 From: callebtc <93376500+callebtc@users.noreply.github.com> Date: Sun, 20 Jul 2025 13:53:58 +0200 Subject: [PATCH] heavy logging --- .../com/bitchat/android/noise/NoiseSession.kt | 16 +++++-- .../android/noise/NoiseSessionManager.kt | 33 ++++++++----- .../protocol/HandshakeState.java | 29 ++++++++++- .../protocol/SymmetricState.java | 48 +++++++++++++++++++ 4 files changed, 110 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/noise/NoiseSession.kt b/app/src/main/java/com/bitchat/android/noise/NoiseSession.kt index 4e891245..b1188281 100644 --- a/app/src/main/java/com/bitchat/android/noise/NoiseSession.kt +++ b/app/src/main/java/com/bitchat/android/noise/NoiseSession.kt @@ -123,9 +123,17 @@ class NoiseSession( try { Log.d(TAG, "Creating HandshakeState with role: ${if (role == HandshakeState.INITIATOR) "INITIATOR" else "RESPONDER"}") + // LOGGING: Track Android handshake initialization (matching iOS) + Log.d(TAG, "=== ANDROID NOISE SESSION - BEFORE HANDSHAKE INIT ===") + Log.d(TAG, "Creating NoiseHandshakeState for peer: $peerID") + Log.d(TAG, "Role: ${if (role == HandshakeState.INITIATOR) "INITIATOR" else "RESPONDER"}") + handshakeState = HandshakeState(PROTOCOL_NAME, role) Log.d(TAG, "HandshakeState created successfully") + Log.d(TAG, "=== ANDROID NOISE SESSION - AFTER HANDSHAKE INIT ===") + Log.d(TAG, "NoiseHandshakeState created and mixPreMessageKeys() completed") + if (handshakeState?.needsLocalKeyPair() == true) { Log.d(TAG, "Local static key pair is required for XX pattern") @@ -181,6 +189,8 @@ class NoiseSession( */ @Synchronized fun startHandshake(): ByteArray { + Log.d(TAG, "Starting noise XX handshake with $peerID as INITIATOR") + if (!isInitiator) { throw IllegalStateException("Only initiator can start handshake") } @@ -188,9 +198,7 @@ class NoiseSession( if (state != NoiseSessionState.Uninitialized) { throw IllegalStateException("Handshake already started") } - - Log.d(TAG, "Starting real XX handshake with $peerID as initiator") - + try { // Initialize handshake as initiator initializeNoiseHandshake(HandshakeState.INITIATOR) @@ -262,7 +270,7 @@ class NoiseSession( HandshakeState.SPLIT -> { // Handshake complete, split into transport keys completeHandshake() - Log.d(TAG, "XX handshake completed with $peerID") + Log.d(TAG, "🔒 XX handshake completed with $peerID") null } diff --git a/app/src/main/java/com/bitchat/android/noise/NoiseSessionManager.kt b/app/src/main/java/com/bitchat/android/noise/NoiseSessionManager.kt index 3b7fba22..7167a986 100644 --- a/app/src/main/java/com/bitchat/android/noise/NoiseSessionManager.kt +++ b/app/src/main/java/com/bitchat/android/noise/NoiseSessionManager.kt @@ -22,7 +22,15 @@ class NoiseSessionManager( var onSessionFailed: ((String, Throwable) -> Unit)? = null // MARK: - Simple Session Management - + + /** + * Add new session for a peer + */ + fun addSession(peerID: String, session: NoiseSession) { + sessions[peerID] = session + Log.d(TAG, "Added new session for $peerID") + } + /** * Get existing session for a peer */ @@ -45,6 +53,8 @@ class NoiseSessionManager( * SIMPLIFIED: Initiate handshake - no tie breaker, just start */ fun initiateHandshake(peerID: String): ByteArray { + Log.d(TAG, "initiateHandshake($peerID)") + // Remove any existing session first removeSession(peerID) @@ -55,11 +65,12 @@ class NoiseSessionManager( localStaticPrivateKey = localStaticPrivateKey, localStaticPublicKey = localStaticPublicKey ) - sessions[peerID] = session + Log.d(TAG, "Storing new INITIATOR session for $peerID") + addSession(peerID, session) try { val handshakeData = session.startHandshake() - Log.d(TAG, "Started handshake with $peerID as initiator") + Log.d(TAG, "Started handshake with $peerID as INITIATOR") return handshakeData } catch (e: Exception) { sessions.remove(peerID) @@ -74,18 +85,18 @@ class NoiseSessionManager( Log.d(TAG, "handleIncomingHandshake($peerID, ${message.size} bytes)") try { - var session = sessions[peerID] + var session = getSession(peerID) // If no session exists, create one as responder if (session == null) { - Log.d(TAG, "Creating new responder session for $peerID") + Log.d(TAG, "Creating new RESPONDER session for $peerID") session = NoiseSession( peerID = peerID, isInitiator = false, localStaticPrivateKey = localStaticPrivateKey, localStaticPublicKey = localStaticPublicKey ) - sessions[peerID] = session + addSession(peerID, session) } // Process handshake message @@ -114,7 +125,7 @@ class NoiseSessionManager( * SIMPLIFIED: Encrypt data */ fun encrypt(data: ByteArray, peerID: String): ByteArray { - val session = sessions[peerID] ?: throw IllegalStateException("No session found for $peerID") + val session = getSession(peerID) ?: throw IllegalStateException("No session found for $peerID") if (!session.isEstablished()) { throw IllegalStateException("Session not established with $peerID") } @@ -125,7 +136,7 @@ class NoiseSessionManager( * SIMPLIFIED: Decrypt data */ fun decrypt(encryptedData: ByteArray, peerID: String): ByteArray { - val session = sessions[peerID] + val session = getSession(peerID) if (session == null) { Log.e(TAG, "No session found for $peerID when trying to decrypt") throw IllegalStateException("No session found for $peerID") @@ -141,7 +152,7 @@ class NoiseSessionManager( * Check if session is established with peer */ fun hasEstablishedSession(peerID: String): Boolean { - val hasSession = sessions[peerID]?.isEstablished() ?: false + val hasSession = getSession(peerID)?.isEstablished() ?: false Log.d(TAG, "hasEstablishedSession($peerID): $hasSession") return hasSession } @@ -150,14 +161,14 @@ class NoiseSessionManager( * Get remote static public key for a peer (if session established) */ fun getRemoteStaticKey(peerID: String): ByteArray? { - return sessions[peerID]?.getRemoteStaticPublicKey() + return getSession(peerID)?.getRemoteStaticPublicKey() } /** * Get handshake hash for channel binding (if session established) */ fun getHandshakeHash(peerID: String): ByteArray? { - return sessions[peerID]?.getHandshakeHash() + return getSession(peerID)?.getHandshakeHash() } /** diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/HandshakeState.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/HandshakeState.java index 0bc2ed1b..91fec78a 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/HandshakeState.java +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/HandshakeState.java @@ -35,6 +35,8 @@ import javax.crypto.ShortBufferException; */ public class HandshakeState implements Destroyable { + private static final String TAG = "AndroidHandshake"; + private SymmetricState symmetric; private boolean isInitiator; private DHState localKeyPair; @@ -465,6 +467,17 @@ public class HandshakeState implements Destroyable { // Empty value for when the prologue is not supplied. private static final byte[] emptyPrologue = new byte [0]; + /** + * Converts a byte array to hex string for logging (matching iOS hex format) + */ + private static String bytesToHex(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } + /** * Starts the handshake running. * @@ -510,12 +523,20 @@ public class HandshakeState implements Destroyable { if (preSharedKey == null) throw new IllegalStateException("Pre-shared key required"); } + + // Log the symmetric state BEFORE any mixing operations (matching iOS) + Log.d(TAG, "=== ANDROID HANDSHAKE START - INITIAL STATE ==="); + Log.d(TAG, "Protocol: " + symmetric.getProtocolName()); + Log.d(TAG, "Role: " + (isInitiator ? "INITIATOR" : "RESPONDER")); + Log.d(TAG, "Initial symmetric hash: " + bytesToHex(symmetric.getHandshakeHash())); // Hash the prologue value. + Log.d(TAG, "Mixing empty prologue"); if (prologue != null) symmetric.mixHash(prologue, 0, prologue.length); else symmetric.mixHash(emptyPrologue, 0, 0); + Log.d(TAG, "Hash after empty prologue: " + bytesToHex(symmetric.getHandshakeHash())); // Hash the pre-shared key into the chaining key and handshake hash. if (preSharedKey != null) @@ -523,6 +544,7 @@ public class HandshakeState implements Destroyable { // Mix the pre-supplied public keys into the handshake hash. if (isInitiator) { + Log.d(TAG, "XX pattern - no pre-message keys to mix"); if ((requirements & LOCAL_PREMSG) != 0) symmetric.mixPublicKey(localKeyPair); if ((requirements & FALLBACK_PREMSG) != 0) { @@ -535,6 +557,7 @@ public class HandshakeState implements Destroyable { if ((requirements & REMOTE_PREMSG) != 0) symmetric.mixPublicKey(remotePublicKey); } else { + Log.d(TAG, "XX pattern - no pre-message keys to mix"); if ((requirements & REMOTE_PREMSG) != 0) symmetric.mixPublicKey(remotePublicKey); if ((requirements & FALLBACK_PREMSG) != 0) { @@ -547,7 +570,11 @@ public class HandshakeState implements Destroyable { if ((requirements & LOCAL_PREMSG) != 0) symmetric.mixPublicKey(localKeyPair); } - // Log the symmetric.hash in hex: + + // Log final state after all initialization (matching iOS) + Log.d(TAG, "=== ANDROID HANDSHAKE START - FINAL STATE ==="); + Log.d(TAG, "Final symmetric hash after mixPreMessageKeys(): " + bytesToHex(symmetric.getHandshakeHash())); + Log.d(TAG, "==========================================="); // The handshake has officially started - set the first action. if (isInitiator) diff --git a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/SymmetricState.java b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/SymmetricState.java index ffdb8428..0e70f9f3 100644 --- a/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/SymmetricState.java +++ b/app/src/main/java/com/bitchat/android/noise/southernstorm/protocol/SymmetricState.java @@ -22,6 +22,8 @@ package com.bitchat.android.noise.southernstorm.protocol; +import android.util.Log; + import java.io.UnsupportedEncodingException; import java.security.DigestException; import java.security.MessageDigest; @@ -36,6 +38,8 @@ import javax.crypto.ShortBufferException; */ class SymmetricState implements Destroyable { + private static final String TAG = "AndroidSymmetric"; + private String name; private CipherState cipher; private MessageDigest hash; @@ -43,6 +47,17 @@ class SymmetricState implements Destroyable { private byte[] h; private byte[] prev_h; + /** + * Converts a byte array to hex string for logging (matching iOS hex format) + */ + private static String bytesToHex(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + sb.append(String.format("%02x", b)); + } + return sb.toString(); + } + /** * Constructs a new symmetric state object. * @@ -79,6 +94,14 @@ class SymmetricState implements Destroyable { } System.arraycopy(h, 0, ck, 0, hashLength); + + // LOGGING: Initial symmetric state after protocol name initialization (matching iOS) + Log.d(TAG, "=== ANDROID SYMMETRIC STATE INITIALIZED ==="); + Log.d(TAG, "Protocol: " + protocolName); + Log.d(TAG, "Initial hash (h): " + bytesToHex(h)); + Log.d(TAG, "Initial chaining key (ck): " + bytesToHex(ck)); + Log.d(TAG, "Hash length: " + h.length); + Log.d(TAG, "========================================="); } /** @@ -111,6 +134,14 @@ class SymmetricState implements Destroyable { */ public void mixKey(byte[] data, int offset, int length) { + // LOGGING: Before mixKey operation (matching iOS) + byte[] inputData = new byte[length]; + System.arraycopy(data, offset, inputData, 0, length); + Log.d(TAG, "*** Android mixKey() BEFORE ***"); + Log.d(TAG, "Input data (" + length + " bytes): " + bytesToHex(inputData)); + Log.d(TAG, "Current CK: " + bytesToHex(ck)); + Log.d(TAG, "Current Hash: " + bytesToHex(h)); + int keyLength = cipher.getKeyLength(); byte[] tempKey = new byte [keyLength]; try { @@ -119,6 +150,12 @@ class SymmetricState implements Destroyable { } finally { Noise.destroy(tempKey); } + + // LOGGING: After mixKey operation (matching iOS) + Log.d(TAG, "*** Android mixKey() AFTER ***"); + Log.d(TAG, "New CK: " + bytesToHex(ck)); + Log.d(TAG, "Hash unchanged: " + bytesToHex(h)); + Log.d(TAG, "Cipher now has key: " + (cipher.getMACLength() > 0)); } /** @@ -130,7 +167,18 @@ class SymmetricState implements Destroyable { */ public void mixHash(byte[] data, int offset, int length) { + // LOGGING: Before mixHash operation (matching iOS) + byte[] inputData = new byte[length]; + System.arraycopy(data, offset, inputData, 0, length); + Log.d(TAG, "*** Android mixHash() BEFORE ***"); + Log.d(TAG, "Input data (" + length + " bytes): " + bytesToHex(inputData)); + Log.d(TAG, "Current Hash: " + bytesToHex(h)); + hashTwo(h, 0, h.length, data, offset, length, h, 0, h.length); + + // LOGGING: After mixHash operation (matching iOS) + Log.d(TAG, "*** Android mixHash() AFTER ***"); + Log.d(TAG, "New Hash: " + bytesToHex(h)); } /**