From f3c588d90f817ccfc55e54e5fe1dc02802cfaf0d Mon Sep 17 00:00:00 2001 From: catbref Date: Sat, 25 May 2019 19:18:01 +0100 Subject: [PATCH] Fix sometimes erroneous Ed25519 public key to X25519 public key conversion. Added mass (x1000) testing of key conversion and shared secret calculations. Fix incorrect proxy private key test that has expected result from previous algorithm. Added another test HTML/JS file. --- .../org/qora/crypto/BouncyCastle25519.java | 2 + src/test/java/org/qora/test/CryptoTests.java | 47 ++++++++++++++++++- src/test/resources/ed25519-to-x25519.html | 22 +++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 src/test/resources/ed25519-to-x25519.html diff --git a/src/main/java/org/qora/crypto/BouncyCastle25519.java b/src/main/java/org/qora/crypto/BouncyCastle25519.java index c5e8b824..b20c7a64 100644 --- a/src/main/java/org/qora/crypto/BouncyCastle25519.java +++ b/src/main/java/org/qora/crypto/BouncyCastle25519.java @@ -71,6 +71,8 @@ public class BouncyCastle25519 { int[] u = new int[X25519Field.SIZE]; X25519Field.mul(onePlusY, oneMinusYInverted, u); + X25519Field.normalize(u); + byte[] x25519PublicKey = new byte[X25519.SCALAR_SIZE]; X25519Field.encode(u, x25519PublicKey, 0); diff --git a/src/test/java/org/qora/test/CryptoTests.java b/src/test/java/org/qora/test/CryptoTests.java index 083eb434..94690814 100644 --- a/src/test/java/org/qora/test/CryptoTests.java +++ b/src/test/java/org/qora/test/CryptoTests.java @@ -64,6 +64,29 @@ public class CryptoTests extends Common { assertTrue(account.verify(signature, message)); } + @Test + public void testMassEd25519ToX25519() { + // Lots of random tests just in case of leading sign bit issues + SecureRandom random = new SecureRandom(); + + for (int i = 0; i < 1000; ++i) { + byte[] ed25519PrivateKey = new byte[32]; + random.nextBytes(ed25519PrivateKey); + PrivateKeyAccount account = new PrivateKeyAccount(null, ed25519PrivateKey); + + byte[] x25519PrivateKey = BouncyCastle25519.toX25519PrivateKey(account.getPrivateKey()); + X25519PrivateKeyParameters x25519PrivateKeyParams = new X25519PrivateKeyParameters(x25519PrivateKey, 0); + + // Derive X25519 public key from X25519 private key + byte[] x25519PublicKeyFromPrivate = x25519PrivateKeyParams.generatePublicKey().getEncoded(); + + // Derive X25519 public key from Ed25519 public key + byte[] x25519PublicKeyFromEd25519 = BouncyCastle25519.toX25519PublicKey(account.getPublicKey()); + + assertEquals(String.format("Public keys do not match, from private key %s", Base58.encode(ed25519PrivateKey)), Base58.encode(x25519PublicKeyFromPrivate), Base58.encode(x25519PublicKeyFromEd25519)); + } + } + @Test public void testBCseed() { final String privateKey58 = "A9MNsATgQgruBUjxy2rjWY36Yf19uRioKZbiLFT2P7c6"; @@ -197,12 +220,34 @@ public class CryptoTests extends Common { assertEquals("shared secrets do not match", Base58.encode(ourSharedSecret), Base58.encode(theirSharedSecret)); } + @Test + public void testMassRandomBCSharedSecrets() { + // Lots of random shared secret tests just in case of leading sign bit issues + SecureRandom random = new SecureRandom(); + + for (int i = 0; i < 1000; ++i) { + byte[] ourPrivateKey = new byte[32]; + random.nextBytes(ourPrivateKey); + PrivateKeyAccount ourAccount = new PrivateKeyAccount(null, ourPrivateKey); + + byte[] theirPrivateKey = new byte[32]; + random.nextBytes(theirPrivateKey); + PrivateKeyAccount theirAccount = new PrivateKeyAccount(null, theirPrivateKey); + + byte[] ourSharedSecret = calcBCSharedSecret(ourPrivateKey, theirAccount.getPublicKey()); + + byte[] theirSharedSecret = calcBCSharedSecret(theirPrivateKey, ourAccount.getPublicKey()); + + assertEquals("#" + i + " shared secrets do not match", Base58.encode(ourSharedSecret), Base58.encode(theirSharedSecret)); + } + } + @Test public void testProxyKeys() { final byte[] ourPrivateKey = Base58.decode("A9MNsATgQgruBUjxy2rjWY36Yf19uRioKZbiLFT2P7c6"); final byte[] theirPublicKey = Base58.decode("C6wuddsBV3HzRrXUtezE7P5MoRXp5m3mEDokRDGZB6ry"); - final String expectedProxyPrivateKey = "CwBXkJRRaGzWRvdE9vewVUbcYNSVrcTpunNWm8zidArZ"; + final String expectedProxyPrivateKey = "6KszntmNuXmpUkzLfuttgMPeownctxrnyZUG9rErKJJx"; PrivateKeyAccount mintingAccount = new PrivateKeyAccount(null, ourPrivateKey); byte[] proxyPrivateKey = mintingAccount.getProxyPrivateKey(theirPublicKey); diff --git a/src/test/resources/ed25519-to-x25519.html b/src/test/resources/ed25519-to-x25519.html new file mode 100644 index 00000000..852257da --- /dev/null +++ b/src/test/resources/ed25519-to-x25519.html @@ -0,0 +1,22 @@ + + + + + + + + + + \ No newline at end of file