diff --git a/libi2pd/Crypto.cpp b/libi2pd/Crypto.cpp
index 0a7c56ed..cc424515 100644
--- a/libi2pd/Crypto.cpp
+++ b/libi2pd/Crypto.cpp
@@ -286,6 +286,19 @@ namespace crypto
 		m_Ctx = BN_CTX_new ();
 #endif		
 	}	
+
+	X25519Keys::X25519Keys (const uint8_t * priv, const uint8_t * pub)
+	{
+#if OPENSSL_X25519		
+		m_Pkey = EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, priv, 32);
+		m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL);
+		memcpy (m_PublicKey, pub, 32); // TODO: verify against m_Pkey
+#else
+		memcpy (m_PrivateKey, priv, 32);
+		memcpy (m_PublicKey, pub, 32);
+		m_Ctx = BN_CTX_new ();
+#endif		
+	}	
 	
 	X25519Keys::~X25519Keys ()
 	{
@@ -304,11 +317,13 @@ namespace crypto
 		m_Pkey = nullptr;
 		EVP_PKEY_keygen_init (m_Ctx);
 		EVP_PKEY_keygen (m_Ctx, &m_Pkey);
+		EVP_PKEY_CTX_free (m_Ctx);
+		m_Ctx = EVP_PKEY_CTX_new (m_Pkey, NULL); // TODO: do we really need to re-create m_Ctx?
 		size_t len = 32;
 		EVP_PKEY_get_raw_public_key (m_Pkey, m_PublicKey, &len);
 		// TODO: remove 
 		len = 32;
-		EVP_PKEY_get_raw_private_key (m_EphemeralPkey, m_PrivateKey, &len);
+		EVP_PKEY_get_raw_private_key (m_Pkey, m_PrivateKey, &len);
 #else		
 		RAND_bytes (m_PrivateKey, 32);
 		GetEd25519 ()->ScalarMulB (m_PrivateKey, m_PublicKey, m_Ctx);
diff --git a/libi2pd/Crypto.h b/libi2pd/Crypto.h
index 353a8165..b02db36c 100644
--- a/libi2pd/Crypto.h
+++ b/libi2pd/Crypto.h
@@ -66,6 +66,7 @@ namespace crypto
 		public:
 
 			X25519Keys ();
+			X25519Keys (const uint8_t * priv, const uint8_t * pub); // for RouterContext
 			~X25519Keys ();
 
 			void GenerateKeys ();
diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp
index d0e16d90..5c4cc9ce 100644
--- a/libi2pd/NTCP2.cpp
+++ b/libi2pd/NTCP2.cpp
@@ -31,19 +31,14 @@ namespace transport
 	NTCP2Establisher::NTCP2Establisher ():
 		m_SessionRequestBuffer (nullptr), m_SessionCreatedBuffer (nullptr), m_SessionConfirmedBuffer (nullptr) 
 	{ 
-		m_Ctx = BN_CTX_new (); 
 		CreateEphemeralKey ();
 	}
 
 	NTCP2Establisher::~NTCP2Establisher () 
 	{ 
-		BN_CTX_free (m_Ctx); 
 		delete[] m_SessionRequestBuffer; 
 		delete[] m_SessionCreatedBuffer;
 		delete[] m_SessionConfirmedBuffer;
-#if OPENSSL_X25519
-		EVP_PKEY_free (m_EphemeralPkey);
-#endif
 	}
 
 	void NTCP2Establisher::MixKey (const uint8_t * inputKeyMaterial, uint8_t * derived)
@@ -59,7 +54,7 @@ namespace transport
 		HMAC(EVP_sha256(), tempKey, 32, m_CK, 33, derived, &len); 	
 	}
 
-	void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, const uint8_t * priv, const uint8_t * rs, const uint8_t * epub)
+	void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub)
 	{
 		static const uint8_t protocolNameHash[] = 
 		{ 
@@ -83,20 +78,20 @@ namespace transport
 		SHA256_Update (&ctx, m_H, 32);
 		SHA256_Update (&ctx, epub, 32);
 		SHA256_Final (m_H, &ctx);
-		// x25519 between rs and priv
+		// x25519 between pub and priv
 		uint8_t inputKeyMaterial[32];
-		i2p::crypto::GetEd25519 ()->ScalarMul (pub, priv, inputKeyMaterial, m_Ctx); // rs*priv
+		priv.Agree (pub, inputKeyMaterial);
 		MixKey (inputKeyMaterial, m_K);
 	}
 
 	void NTCP2Establisher::KDF1Alice ()
 	{
-		KeyDerivationFunction1 (m_RemoteStaticKey, GetPriv (), m_RemoteStaticKey, GetPub ());
+		KeyDerivationFunction1 (m_RemoteStaticKey, m_EphemeralKeys, m_RemoteStaticKey, GetPub ());
 	}
 	
 	void NTCP2Establisher::KDF1Bob ()
 	{
-		KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetNTCP2StaticPrivateKey (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ()); 
+		KeyDerivationFunction1 (GetRemotePub (), i2p::context.GetStaticKeys (), i2p::context.GetNTCP2StaticPublicKey (), GetRemotePub ()); 
 	}
 
 	void NTCP2Establisher::KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub)
@@ -140,14 +135,14 @@ namespace transport
 	void NTCP2Establisher::KDF3Alice ()
 	{
 		uint8_t inputKeyMaterial[32];
-		i2p::crypto::GetEd25519 ()->ScalarMul (GetRemotePub (), i2p::context.GetNTCP2StaticPrivateKey (), inputKeyMaterial, m_Ctx); 
+		i2p::context.GetStaticKeys ().Agree (GetRemotePub (), inputKeyMaterial);		 
 		MixKey (inputKeyMaterial, m_K);
 	}
 
 	void NTCP2Establisher::KDF3Bob ()
 	{
 		uint8_t inputKeyMaterial[32];
-		i2p::crypto::GetEd25519 ()->ScalarMul (m_RemoteStaticKey, GetPriv (), inputKeyMaterial, m_Ctx); 
+		m_EphemeralKeys.Agree (m_RemoteStaticKey, inputKeyMaterial); 
 		MixKey (inputKeyMaterial, m_K);
 	}
 
diff --git a/libi2pd/NTCP2.h b/libi2pd/NTCP2.h
index 73001fc2..07f44371 100644
--- a/libi2pd/NTCP2.h
+++ b/libi2pd/NTCP2.h
@@ -80,7 +80,6 @@ namespace transport
 		~NTCP2Establisher ();
 		
 		const uint8_t * GetPub () const { return m_EphemeralKeys.GetPublicKey (); };
-		const uint8_t * GetPriv () const { return m_EphemeralKeys.GetPrivateKey (); };
 		const uint8_t * GetRemotePub () const { return m_RemoteEphemeralPublicKey; }; // Y for Alice and X for Bob
 		uint8_t * GetRemotePub () { return m_RemoteEphemeralPublicKey; }; // to set
 
@@ -96,7 +95,7 @@ namespace transport
 		void KDF3Bob ();
 
 		void MixKey (const uint8_t * inputKeyMaterial, uint8_t * derived);
-		void KeyDerivationFunction1 (const uint8_t * pub, const uint8_t * priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH
+		void KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub); // for SessionRequest, (pub, priv) for DH
 		void KeyDerivationFunction2 (const uint8_t * sessionRequest, size_t sessionRequestLen, const uint8_t * epub); // for SessionCreate
 		void CreateEphemeralKey ();
 
@@ -110,7 +109,6 @@ namespace transport
 		bool ProcessSessionConfirmedMessagePart1 (const uint8_t * nonce);
 		bool ProcessSessionConfirmedMessagePart2 (const uint8_t * nonce, uint8_t * m3p2Buf);
 
-		BN_CTX * m_Ctx;
 		i2p::crypto::X25519Keys m_EphemeralKeys;
 		uint8_t m_RemoteEphemeralPublicKey[32]; // x25519
 		uint8_t m_RemoteStaticKey[32], m_IV[16], m_H[32] /*h*/, m_CK[33] /*ck*/, m_K[32] /*k*/;
diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp
index 6ad3d159..d8ef4c4a 100644
--- a/libi2pd/RouterContext.cpp
+++ b/libi2pd/RouterContext.cpp
@@ -620,4 +620,14 @@ namespace i2p
 	{
 		return m_Decryptor ? m_Decryptor->Decrypt (encrypted, data, ctx, false) : false;
 	}
+
+	i2p::crypto::X25519Keys& RouterContext::GetStaticKeys ()
+	{
+		if (!m_StaticKeys)
+		{	
+			if (!m_NTCP2Keys) NewNTCP2Keys ();
+			m_StaticKeys.reset (new i2p::crypto::X25519Keys (m_NTCP2Keys->staticPrivateKey, m_NTCP2Keys->staticPublicKey));
+		}
+		return *m_StaticKeys;		
+	}	
 }
diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h
index 3f3a18c5..0b88ea82 100644
--- a/libi2pd/RouterContext.h
+++ b/libi2pd/RouterContext.h
@@ -62,6 +62,7 @@ namespace i2p
 			const uint8_t * GetNTCP2StaticPublicKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPublicKey : nullptr; };
 			const uint8_t * GetNTCP2StaticPrivateKey () const { return m_NTCP2Keys ? m_NTCP2Keys->staticPrivateKey : nullptr; };
 			const uint8_t * GetNTCP2IV () const { return m_NTCP2Keys ? m_NTCP2Keys->iv : nullptr; };
+			i2p::crypto::X25519Keys& GetStaticKeys (); 
 
 			uint32_t GetUptime () const;
 			uint32_t GetStartupTime () const { return m_StartupTime; };
@@ -143,6 +144,7 @@ namespace i2p
 			int m_NetID;
 			std::mutex m_GarlicMutex;
 			std::unique_ptr<NTCP2PrivateKeys> m_NTCP2Keys;
+			std::unique_ptr<i2p::crypto::X25519Keys> m_StaticKeys;
 	};
 
 	extern RouterContext context;