diff --git a/convex-core/src/main/java/convex/core/crypto/BIP39.java b/convex-core/src/main/java/convex/core/crypto/BIP39.java index fb2c3c658..a85598557 100644 --- a/convex-core/src/main/java/convex/core/crypto/BIP39.java +++ b/convex-core/src/main/java/convex/core/crypto/BIP39.java @@ -225,13 +225,24 @@ public static Blob getSeed(List words, String passphrase) throws NoSuchA joined=Normalizer.normalize(joined, Normalizer.Form.NFKD); char[] pass= joined.toCharArray(); + return getSeedInternal(pass,passphrase); + } + + public static Blob getSeed(String mnemonic, String passphrase) throws NoSuchAlgorithmException, InvalidKeySpecException { + mnemonic=mnemonic.trim().replaceAll("\\s+"," "); + mnemonic=Normalizer.normalize(mnemonic, Normalizer.Form.NFKD); + char[] normalisedMnemonic= mnemonic.toCharArray(); + return getSeedInternal(normalisedMnemonic,passphrase); + } + + private static Blob getSeedInternal(char[] normalisedMnemonic, String passphrase) throws NoSuchAlgorithmException, InvalidKeySpecException { // Normalise passphrase and convert to byte array passphrase=Normalizer.normalize(passphrase, Normalizer.Form.NFKD); byte[] salt = ("mnemonic"+passphrase).getBytes(StandardCharsets.UTF_8); // Generate seed SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512"); - KeySpec keyspec = new PBEKeySpec(pass, salt, 2048, SEED_LENGTH * 8); + KeySpec keyspec = new PBEKeySpec(normalisedMnemonic, salt, 2048, SEED_LENGTH * 8); Key key = factory.generateSecret(keyspec); // Wrap result as Blob @@ -239,6 +250,8 @@ public static Blob getSeed(List words, String passphrase) throws NoSuchA return Blob.wrap(bs); } + + public static String createSecureRandom() { return createSecureRandom(12); } diff --git a/convex-core/src/test/java/convex/core/crypto/BIP39Test.java b/convex-core/src/test/java/convex/core/crypto/BIP39Test.java index 6d3447dc1..766127da7 100644 --- a/convex-core/src/test/java/convex/core/crypto/BIP39Test.java +++ b/convex-core/src/test/java/convex/core/crypto/BIP39Test.java @@ -1,6 +1,7 @@ package convex.core.crypto; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; @@ -20,4 +21,21 @@ public void testSeed() throws NoSuchAlgorithmException, InvalidKeySpecException String exSeed="8212cc694344bbc4ae70505948c58194c16cd10599b2e93f0f7f638aaa108009a5707f9274fc6bdeb23bf30783d0c2c7bb556a7aa7b9064dab6df9b8c469e39c"; assertEquals(exSeed,BIP39.getSeed(tw1,"").toHexString()); } + + /** + * Test vector from https://iancoleman.io/bip39/ + * @throws NoSuchAlgorithmException + * @throws InvalidKeySpecException + */ + @Test + public void testExample15() throws NoSuchAlgorithmException, InvalidKeySpecException { + String s1="slush blind shaft return gentle isolate notice busy silent toast joy again almost perfect century"; + List tw1=List.of(s1.split(" ")); + String exSeed="8ac1d802490b34488eb265d72b3de8aa4cbe4ad0c674ccc083463a3cb9466ab11933f6251aec5b6b2260442435bd2f5257aa3fc219745f642295d8b6e401fe3f"; + assertEquals(exSeed,BIP39.getSeed(tw1,"").toHexString()); + + String s2=s1.replaceAll(" " , " "); + assertNotEquals(s1,s2); + assertEquals(exSeed,BIP39.getSeed(s2,"").toHexString()); + } }