From d301669726b7c446de13e159a723d57a1c9f238c Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Thu, 15 Oct 2020 11:01:22 +0000
Subject: [PATCH] Router: Don't re-derive public key from private for every
 HandshakeState

---
 .../noise/protocol/Curve25519DHState.java     | 23 +++++++++++++++++++
 .../southernstorm/noise/protocol/DHState.java | 17 ++++++++++++++
 .../net/i2p/data/i2np/BuildRequestRecord.java |  4 ++--
 .../crypto/ratchet/ECIESAEADEngine.java       |  8 +++----
 .../transport/ntcp/InboundEstablishState.java |  4 ++--
 .../transport/ntcp/OutboundNTCP2State.java    |  4 ++--
 6 files changed, 50 insertions(+), 10 deletions(-)

diff --git a/router/java/src/com/southernstorm/noise/protocol/Curve25519DHState.java b/router/java/src/com/southernstorm/noise/protocol/Curve25519DHState.java
index 62524153ac..1a3d7fb014 100644
--- a/router/java/src/com/southernstorm/noise/protocol/Curve25519DHState.java
+++ b/router/java/src/com/southernstorm/noise/protocol/Curve25519DHState.java
@@ -107,12 +107,35 @@ class Curve25519DHState implements DHState, Cloneable {
 		System.arraycopy(privateKey, 0, key, offset, 32);
 	}
 
+	/**
+	 * @deprecated use setKeys()
+	 */
+	@Deprecated
 	@Override
 	public void setPrivateKey(byte[] key, int offset) {
 		System.arraycopy(key, offset, privateKey, 0, 32);
 		Curve25519.eval(publicKey, 0, privateKey, null);
 		mode = 0x03;
 	}
+	
+	/**
+	 * Sets the private and public keys for this object.
+	 * I2P for efficiency, since setPrivateKey() calculates the public key
+	 * and overwrites it.
+	 * Does NOT check that the two keys match.
+	 * 
+	 * @param privkey The buffer containing the private key.
+	 * @param privoffset The first offset in the buffer that contains the key.
+	 * @param pubkey The buffer containing the public key.
+	 * @param puboffset The first offset in the buffer that contains the key.
+	 * @since 0.9.48
+	 */
+	@Override
+	public void setKeys(byte[] privkey, int privoffset, byte[] pubkey, int puboffset) {
+		System.arraycopy(privkey, privoffset, privateKey, 0, 32);
+		System.arraycopy(pubkey, puboffset, publicKey, 0, 32);
+		mode = 0x03;
+	}
 
 	@Override
 	public void setToNullPublicKey() {
diff --git a/router/java/src/com/southernstorm/noise/protocol/DHState.java b/router/java/src/com/southernstorm/noise/protocol/DHState.java
index a3a9cdef5b..62b135a3f7 100644
--- a/router/java/src/com/southernstorm/noise/protocol/DHState.java
+++ b/router/java/src/com/southernstorm/noise/protocol/DHState.java
@@ -95,8 +95,25 @@ public interface DHState extends Destroyable, Cloneable {
 	 * 
 	 * If this object previously held only a public key, then
 	 * this function will change it into a key pair.
+	 * 
+	 * @deprecated use setKeys()
 	 */
+	@Deprecated
 	void setPrivateKey(byte[] key, int offset);
+	
+	/**
+	 * Sets the private and public keys for this object.
+	 * I2P for efficiency, since setPrivateKey() calculates the public key
+	 * and overwrites it.
+	 * Does NOT check that the two keys match.
+	 * 
+	 * @param privkey The buffer containing the private key.
+	 * @param privoffset The first offset in the buffer that contains the key.
+	 * @param pubkey The buffer containing the private key.
+	 * @param puboffset The first offset in the buffer that contains the key.
+	 * @since 0.9.48
+	 */
+	void setKeys(byte[] privkey, int privoffset, byte[] pubkey, int puboffset);
 
 	/**
 	 * Sets this object to the null public key and clears the private key.
diff --git a/router/java/src/net/i2p/data/i2np/BuildRequestRecord.java b/router/java/src/net/i2p/data/i2np/BuildRequestRecord.java
index bd5085667f..48ed6b717a 100644
--- a/router/java/src/net/i2p/data/i2np/BuildRequestRecord.java
+++ b/router/java/src/net/i2p/data/i2np/BuildRequestRecord.java
@@ -380,8 +380,8 @@ public class BuildRequestRecord {
             try {
                 KeyFactory kf = TEST ? TESTKF : ctx.commSystem().getXDHFactory();
                 state = new HandshakeState(HandshakeState.PATTERN_ID_N, HandshakeState.RESPONDER, kf);
-                state.getLocalKeyPair().setPublicKey(ourKey.toPublic().getData(), 0);
-                state.getLocalKeyPair().setPrivateKey(ourKey.getData(), 0);
+                state.getLocalKeyPair().setKeys(ourKey.getData(), 0,
+                                                ourKey.toPublic().getData(), 0);
                 state.start();
                 decrypted = new byte[LENGTH_EC];
                 state.readMessage(encryptedRecord.getData(), PEER_SIZE, EncryptedBuildRecord.LENGTH - PEER_SIZE,
diff --git a/router/java/src/net/i2p/router/crypto/ratchet/ECIESAEADEngine.java b/router/java/src/net/i2p/router/crypto/ratchet/ECIESAEADEngine.java
index 56183161cf..36bd14da41 100644
--- a/router/java/src/net/i2p/router/crypto/ratchet/ECIESAEADEngine.java
+++ b/router/java/src/net/i2p/router/crypto/ratchet/ECIESAEADEngine.java
@@ -356,8 +356,8 @@ public final class ECIESAEADEngine {
         } catch (GeneralSecurityException gse) {
             throw new IllegalStateException("bad proto", gse);
         }
-        state.getLocalKeyPair().setPublicKey(targetPrivateKey.toPublic().getData(), 0);
-        state.getLocalKeyPair().setPrivateKey(targetPrivateKey.getData(), 0);
+        state.getLocalKeyPair().setKeys(targetPrivateKey.getData(), 0,
+                                        targetPrivateKey.toPublic().getData(), 0);
         state.start();
         if (_log.shouldDebug())
             _log.debug("State before decrypt new session: " + state);
@@ -786,8 +786,8 @@ public final class ECIESAEADEngine {
             throw new IllegalStateException("bad proto", gse);
         }
         state.getRemotePublicKey().setPublicKey(target.getData(), 0);
-        state.getLocalKeyPair().setPublicKey(priv.toPublic().getData(), 0);
-        state.getLocalKeyPair().setPrivateKey(priv.getData(), 0);
+        state.getLocalKeyPair().setKeys(priv.getData(), 0,
+                                        priv.toPublic().getData(), 0);
         state.start();
         if (_log.shouldDebug())
             _log.debug("State before encrypt new session: " + state);
diff --git a/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java b/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java
index b7d0d6f990..1bd397cc04 100644
--- a/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java
+++ b/router/java/src/net/i2p/router/transport/ntcp/InboundEstablishState.java
@@ -685,8 +685,8 @@ class InboundEstablishState extends EstablishBase implements NTCP2Payload.Payloa
             } catch (GeneralSecurityException gse) {
                 throw new IllegalStateException("bad proto", gse);
             }
-            _handshakeState.getLocalKeyPair().setPublicKey(_transport.getNTCP2StaticPubkey(), 0);
-            _handshakeState.getLocalKeyPair().setPrivateKey(_transport.getNTCP2StaticPrivkey(), 0);
+            _handshakeState.getLocalKeyPair().setKeys(_transport.getNTCP2StaticPrivkey(), 0,
+                                                      _transport.getNTCP2StaticPubkey(), 0);
             Hash h = _context.routerHash();
             SessionKey bobHash = new SessionKey(h.getData());
             // save encrypted data for CBC for msg 2
diff --git a/router/java/src/net/i2p/router/transport/ntcp/OutboundNTCP2State.java b/router/java/src/net/i2p/router/transport/ntcp/OutboundNTCP2State.java
index be1f9c8908..7ecc42a0d7 100644
--- a/router/java/src/net/i2p/router/transport/ntcp/OutboundNTCP2State.java
+++ b/router/java/src/net/i2p/router/transport/ntcp/OutboundNTCP2State.java
@@ -217,8 +217,8 @@ class OutboundNTCP2State implements EstablishState {
             return;
         }
         _handshakeState.getRemotePublicKey().setPublicKey(bk, 0);
-        _handshakeState.getLocalKeyPair().setPublicKey(_transport.getNTCP2StaticPubkey(), 0);
-        _handshakeState.getLocalKeyPair().setPrivateKey(_transport.getNTCP2StaticPrivkey(), 0);
+        _handshakeState.getLocalKeyPair().setKeys(_transport.getNTCP2StaticPrivkey(), 0,
+                                                  _transport.getNTCP2StaticPubkey(), 0);
         // output to _tmp
         try {
             _handshakeState.start();
-- 
GitLab