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