From 62fce859b9f29d79f8b8f4a10f6bdbf54f8158e7 Mon Sep 17 00:00:00 2001
From: zzz <zzz@i2pmail.org>
Date: Tue, 29 Dec 2020 14:15:00 -0500
Subject: [PATCH] Ratchet: mixHash() not required after message for N pattern

---
 .../noise/protocol/HandshakeState.java        | 24 +++++++++++++++----
 .../noise/protocol/SymmetricState.java        | 19 +++++++++++++++
 2 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/router/java/src/com/southernstorm/noise/protocol/HandshakeState.java b/router/java/src/com/southernstorm/noise/protocol/HandshakeState.java
index cd6a414e8d..2297ae2529 100644
--- a/router/java/src/com/southernstorm/noise/protocol/HandshakeState.java
+++ b/router/java/src/com/southernstorm/noise/protocol/HandshakeState.java
@@ -648,10 +648,19 @@ public class HandshakeState implements Destroyable, Cloneable {
 			}
 			
 			// Add the payload to the message buffer and encrypt it.
-			if (payload != null)
-				messagePosn += symmetric.encryptAndHash(payload, payloadOffset, message, messagePosn, payloadLength);
-			else
-				messagePosn += symmetric.encryptAndHash(message, messagePosn, message, messagePosn, 0);
+			if (payload != null) {
+				// no need to hash for N, we don't split() and no more messages follow
+				if (patternId.equals(PATTERN_ID_N))
+					messagePosn += symmetric.encryptOnly(payload, payloadOffset, message, messagePosn, payloadLength);
+				else
+					messagePosn += symmetric.encryptAndHash(payload, payloadOffset, message, messagePosn, payloadLength);
+			} else {
+				// no need to hash for N, we don't split() and no more messages follow
+				if (patternId.equals(PATTERN_ID_N))
+					messagePosn += symmetric.encryptOnly(message, messagePosn, message, messagePosn, 0);
+				else
+					messagePosn += symmetric.encryptAndHash(message, messagePosn, message, messagePosn, 0);
+			}
 			success = true;
 		} finally {
 			// If we failed, then clear any sensitive data that may have
@@ -819,7 +828,12 @@ public class HandshakeState implements Destroyable, Cloneable {
 			}
 			
 			// Decrypt the message payload.
-			int payloadLength = symmetric.decryptAndHash(message, messageOffset, payload, payloadOffset, messageEnd - messageOffset);
+			int payloadLength;
+			// no need to hash for N, we don't split() and no more messages follow
+			if (patternId.equals(PATTERN_ID_N))
+				payloadLength = symmetric.decryptOnly(message, messageOffset, payload, payloadOffset, messageEnd - messageOffset);
+			else
+				payloadLength = symmetric.decryptAndHash(message, messageOffset, payload, payloadOffset, messageEnd - messageOffset);
 			success = true;
 			return payloadLength;
 		} finally {
diff --git a/router/java/src/com/southernstorm/noise/protocol/SymmetricState.java b/router/java/src/com/southernstorm/noise/protocol/SymmetricState.java
index 4b6341b1eb..4c7f09ee5f 100644
--- a/router/java/src/com/southernstorm/noise/protocol/SymmetricState.java
+++ b/router/java/src/com/southernstorm/noise/protocol/SymmetricState.java
@@ -273,6 +273,16 @@ class SymmetricState implements Destroyable, Cloneable {
 		return ciphertextLength;
 	}
 
+	/**
+	 * I2P - Same as encryptAndHash() but without the post-mixHash(), for N only.
+	 * @since 0.9.49
+	 */
+	public int encryptOnly(byte[] plaintext, int plaintextOffset, byte[] ciphertext, int ciphertextOffset, int length) throws ShortBufferException
+	{
+		int ciphertextLength = cipher.encryptWithAd(h, plaintext, plaintextOffset, ciphertext, ciphertextOffset, length);
+		return ciphertextLength;
+	}
+
 	/**
 	 * Decrypts a block of ciphertext and mixes it into the handshake hash.
 	 * 
@@ -302,6 +312,15 @@ class SymmetricState implements Destroyable, Cloneable {
 		return cipher.decryptWithAd(prev_h, ciphertext, ciphertextOffset, plaintext, plaintextOffset, length);
 	}
 
+	/**
+	 * I2P - Same as decryptAndHash() but without the post-mixHash(), for N only.
+	 * @since 0.9.49
+	 */
+	public int decryptOnly(byte[] ciphertext, int ciphertextOffset, byte[] plaintext, int plaintextOffset, int length) throws ShortBufferException, BadPaddingException
+	{
+		return cipher.decryptWithAd(h, ciphertext, ciphertextOffset, plaintext, plaintextOffset, length);
+	}
+
 	/**
 	 * Splits the symmetric state into two ciphers for session encryption.
 	 * 
-- 
GitLab