diff --git a/Android.bp b/Android.bp index a8f4ab85..5664d6d1 100644 --- a/Android.bp +++ b/Android.bp @@ -41,7 +41,7 @@ android_app { "seedvault-lib-koin-android", "seedvault-lib-koin-androidx-viewmodel", // bip39 - "seedvault-lib-novacrypto-bip39", + "seedvault-lib-kotlin-bip39", ], manifest: "app/src/main/AndroidManifest.xml", diff --git a/app/build.gradle b/app/build.gradle index 706a6de2..fe1a84da 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -123,7 +123,7 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: "${rootProject.rootDir}/libs/koin-android") implementation fileTree(include: ['*.aar'], dir: "${rootProject.rootDir}/libs/koin-android") - implementation fileTree(include: ['*.jar'], dir: "${rootProject.rootDir}/libs/novacrypto-bip39") + implementation fileTree(include: ['kotlin-bip39-1.0.2.jar'], dir: "${rootProject.rootDir}/libs") /** * Test Dependencies (do not concern the AOSP build) diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeAdapter.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeAdapter.kt index 606e024b..1003300e 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeAdapter.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeAdapter.kt @@ -8,7 +8,7 @@ import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.Adapter import com.stevesoltys.seedvault.R -class RecoveryCodeAdapter(private val items: List) : +class RecoveryCodeAdapter(private val items: List) : Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecoveryCodeViewHolder { @@ -30,9 +30,9 @@ class RecoveryCodeViewHolder(v: View) : RecyclerView.ViewHolder(v) { private val num = v.findViewById(R.id.num) private val word = v.findViewById(R.id.word) - internal fun bind(number: Int, item: CharSequence) { + internal fun bind(number: Int, item: CharArray) { num.text = number.toString() - word.text = item + word.text = String(item) } } diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt index 4b707b84..fdd8c1e2 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt @@ -19,15 +19,16 @@ import androidx.activity.result.contract.ActivityResultContracts.StartActivityFo import androidx.appcompat.app.AlertDialog import androidx.constraintlayout.widget.ConstraintLayout import androidx.fragment.app.Fragment +import cash.z.ecc.android.bip39.Mnemonics +import cash.z.ecc.android.bip39.Mnemonics.ChecksumException +import cash.z.ecc.android.bip39.Mnemonics.InvalidWordException import com.google.android.material.snackbar.Snackbar import com.google.android.material.textfield.TextInputLayout import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.isDebugBuild import com.stevesoltys.seedvault.ui.LiveEventHandler -import io.github.novacrypto.bip39.Validation.InvalidChecksumException -import io.github.novacrypto.bip39.Validation.WordNotFoundException -import io.github.novacrypto.bip39.wordlists.English import org.koin.androidx.viewmodel.ext.android.sharedViewModel +import java.util.Locale internal const val ARG_FOR_NEW_CODE = "forVerifyingNewCode" @@ -101,7 +102,10 @@ class RecoveryCodeInputFragment : Fragment() { backView.setOnClickListener { requireActivity().finishAfterTransition() } } - val adapter = getAdapter() + val adapterLayout = android.R.layout.simple_list_item_1 + val adapter = ArrayAdapter(requireContext(), adapterLayout).apply { + addAll(Mnemonics.getCachedWords(Locale.ENGLISH.language)) + } for (i in 0 until WORD_NUM) { val wordLayout = getWordLayout(i) @@ -122,14 +126,6 @@ class RecoveryCodeInputFragment : Fragment() { if (forVerifyingNewCode && isDebugBuild() && !viewModel.isRestore) debugPreFill() } - private fun getAdapter(): ArrayAdapter { - val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1) - for (i in 0 until WORD_LIST_SIZE) { - adapter.add(English.INSTANCE.getWord(i)) - } - return adapter - } - private fun getInput(): List = ArrayList(WORD_NUM).apply { for (i in 0 until WORD_NUM) add(getWordLayout(i).editText!!.text.toString()) } @@ -139,10 +135,10 @@ class RecoveryCodeInputFragment : Fragment() { if (!allFilledOut(input)) return try { viewModel.validateAndContinue(input, forVerifyingNewCode) - } catch (e: InvalidChecksumException) { + } catch (e: ChecksumException) { Toast.makeText(context, R.string.recovery_code_error_checksum_word, LENGTH_LONG).show() - } catch (e: WordNotFoundException) { - showWrongWordError(input, e) + } catch (e: InvalidWordException) { + showWrongWordError(input) } } @@ -155,10 +151,11 @@ class RecoveryCodeInputFragment : Fragment() { return true } - private fun showWrongWordError(input: List, e: WordNotFoundException) { - val i = input.indexOf(e.word) + private fun showWrongWordError(input: List) { + val words = Mnemonics.getCachedWords(Locale.ENGLISH.language) + val i = input.indexOfFirst { it !in words } if (i == -1) throw AssertionError() - val str = getString(R.string.recovery_code_error_invalid_word, e.suggestion1, e.suggestion2) + val str = getString(R.string.recovery_code_error_invalid_word) showError(i, str) } @@ -235,7 +232,7 @@ class RecoveryCodeInputFragment : Fragment() { private fun debugPreFill() { val words = viewModel.wordList for (i in words.indices) { - getWordLayout(i).editText!!.setText(words[i]) + getWordLayout(i).editText!!.setText(String(words[i])) } } diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeOutputFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeOutputFragment.kt index 35f6af76..d8b3a5ef 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeOutputFragment.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeOutputFragment.kt @@ -23,7 +23,7 @@ class RecoveryCodeOutputFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { + ): View { val v: View = inflater.inflate(R.layout.fragment_recovery_code_output, container, false) wordList = v.findViewById(R.id.wordList) diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeViewModel.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeViewModel.kt index 3b8c6173..3eb3329e 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeViewModel.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeViewModel.kt @@ -4,6 +4,11 @@ import android.app.backup.IBackupManager import android.os.UserHandle import android.util.Log import androidx.lifecycle.AndroidViewModel +import cash.z.ecc.android.bip39.Mnemonics +import cash.z.ecc.android.bip39.Mnemonics.ChecksumException +import cash.z.ecc.android.bip39.Mnemonics.InvalidWordException +import cash.z.ecc.android.bip39.Mnemonics.WordCountException +import cash.z.ecc.android.bip39.toSeed import com.stevesoltys.seedvault.App import com.stevesoltys.seedvault.crypto.Crypto import com.stevesoltys.seedvault.crypto.KeyManager @@ -11,26 +16,14 @@ import com.stevesoltys.seedvault.transport.TRANSPORT_ID import com.stevesoltys.seedvault.transport.backup.BackupCoordinator import com.stevesoltys.seedvault.ui.LiveEvent import com.stevesoltys.seedvault.ui.MutableLiveEvent -import io.github.novacrypto.bip39.JavaxPBKDF2WithHmacSHA512 -import io.github.novacrypto.bip39.MnemonicGenerator -import io.github.novacrypto.bip39.MnemonicValidator -import io.github.novacrypto.bip39.SeedCalculator -import io.github.novacrypto.bip39.Validation.InvalidChecksumException -import io.github.novacrypto.bip39.Validation.InvalidWordCountException -import io.github.novacrypto.bip39.Validation.UnexpectedWhiteSpaceException -import io.github.novacrypto.bip39.Validation.WordNotFoundException -import io.github.novacrypto.bip39.Words -import io.github.novacrypto.bip39.wordlists.English import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.calyxos.backup.storage.api.StorageBackup import java.io.IOException import java.security.SecureRandom -import java.util.ArrayList internal const val WORD_NUM = 12 -internal const val WORD_LIST_SIZE = 2048 private val TAG = RecoveryCodeViewModel::class.java.simpleName @@ -43,14 +36,12 @@ internal class RecoveryCodeViewModel( private val storageBackup: StorageBackup ) : AndroidViewModel(app) { - internal val wordList: List by lazy { - val items: ArrayList = ArrayList(WORD_NUM) - val entropy = ByteArray(Words.TWELVE.byteLength()) + internal val wordList: List by lazy { + // we use our own entropy to not having to trust the library to use SecureRandom + val entropy = ByteArray(Mnemonics.WordCount.COUNT_12.bitLength / 8) SecureRandom().nextBytes(entropy) - MnemonicGenerator(English.INSTANCE).createMnemonic(entropy) { - if (it != " ") items.add(it) - } - items + // create the words from the entropy + Mnemonics.MnemonicCode(entropy).words } private val mConfirmButtonClicked = MutableLiveEvent() @@ -65,17 +56,15 @@ internal class RecoveryCodeViewModel( internal var isRestore: Boolean = false - @Throws(WordNotFoundException::class, InvalidChecksumException::class) + @Throws(InvalidWordException::class, ChecksumException::class) fun validateAndContinue(input: List, forVerifyingNewCode: Boolean) { + val code = Mnemonics.MnemonicCode(input.toMnemonicChars()) try { - MnemonicValidator.ofWordList(English.INSTANCE).validate(input) - } catch (e: UnexpectedWhiteSpaceException) { - throw AssertionError(e) - } catch (e: InvalidWordCountException) { + code.validate() + } catch (e: WordCountException) { throw AssertionError(e) } - val mnemonic = input.joinToString(" ") - val seed = SeedCalculator(JavaxPBKDF2WithHmacSHA512.INSTANCE).calculateSeed(mnemonic, "") + val seed = code.toSeed() if (forVerifyingNewCode) { keyManager.storeBackupKey(seed) keyManager.storeMainKey(seed) @@ -118,3 +107,7 @@ internal class RecoveryCodeViewModel( } } + +internal fun List.toMnemonicChars(): CharArray { + return joinToString(" ").toCharArray() +} diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index bd7b8d08..8cbee9f7 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -18,7 +18,7 @@ Sicherung läuft Sicherungsbenachrichtigung Ihr Wiederherstellungsschlüssel ist ungültig. Bitte prüfen Sie alle eingegebenen Wörter und versuchen Sie es erneut! - Falsches Wort. Meinten Sie %1$s oder %2$s\? + Falsches Wort. Sie vergaßen, dieses Wort einzugeben. Wort 12 Wort 11 diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 67988b49..c7924e52 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -80,7 +80,7 @@ Εκτελείται δημιουργία αντιγράφων ασφαλείας Ειδοποίηση δημιουργίας αντιγράφων ασφαλείας Ο κωδικός σας δεν είναι έγκυρος. Ελέγξτε όλες τις λέξεις και δοκιμάστε ξανά! - Λάθος λέξη. Εννοείτε %1$s ή %2$s; + Λάθος λέξη. Ξεχάσατε να εισαγάγετε αυτήν τη λέξη. Λέξη 12 Λέξη 11 diff --git a/app/src/main/res/values-es-rUS/strings.xml b/app/src/main/res/values-es-rUS/strings.xml index 82a77787..d5735baa 100644 --- a/app/src/main/res/values-es-rUS/strings.xml +++ b/app/src/main/res/values-es-rUS/strings.xml @@ -46,7 +46,7 @@ Respaldo terminó Ejecución de respaldo Tu código no es válido. Por favor revise todas las palabras e intente nuevamente! - Palabra equivocada. ¿Quisiste decir %1$s o %2$s\? + Palabra equivocada. Olvidaste ingresar esta palabra. Palabra 12 Palabra 11 diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index d69cef52..a3ec950c 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -72,7 +72,7 @@ Copia de seguridad en ejecución Notificación de copia de seguridad Su código es inválido. Por favor, compruebe todas las palabras e inténtelo de nuevo! - Palabra equivocada. ¿Quisiste decir %1$s o %2$s \? + Palabra equivocada. Ingrese su código de recuperación de 12 palabras que anotó al configurar las copias de seguridad. Ingrese su código de recuperación de 12 palabras para asegurarse de que funcionará cuando lo necesite. Confirma código diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 80b59e8c..ee6ad5e2 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -16,7 +16,7 @@ Réparer Notification de la sauvegarde Votre code est invalide. Veuillez vérifier tous les mots et réessayer ! - Mot erroné. Vouliez-vous dire %1$s ou %2$s \? + Mot erroné. Vous avez oublié d’entrer ce mot. Mot 12 Mot 11 diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index a1efca53..29b4a1ce 100644 --- a/app/src/main/res/values-he/strings.xml +++ b/app/src/main/res/values-he/strings.xml @@ -6,7 +6,7 @@ הגיבוי פועל התראת גיבוי הקוד שלך שגוי. נא לאמת את כל המילים ולנסות שוב! - מילה שגויה. התכוונת למילה %1$s או %2$s\? + מילה שגויה. שכחת למלא את המילה הזאת. מילה 12 מילה 11 diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index c68e40fa..d5d26369 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -49,7 +49,7 @@ Sigurnosna kopija pokrenuta Obavijest sigurnosne kopije Tvoja lozinka je neispravna. Provjeri sve riječi i pokušaj ponovo! - Kriva riječ. Je li misliš %1$s ili %2$s\? + Kriva riječ. Ova riječ nije upisana. Riječ 12 Riječ 11 diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index 2e5cbbb6..1ceb3e91 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -47,7 +47,7 @@ Öryggisafritun í gangi Tilkynning um öryggisafritatöku Kóðinn er ógildur. Athugað öll orðin og prófaðu aftur! - Rangt orð. Meintirðu %1$s or %2$s\? + Rangt orð. Þú gleymdir að slá inn þetta orð. Orð 12 Orð 11 diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index c48c143c..7252c597 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -9,7 +9,7 @@ Backup in esecuzione Notificazione backup Il tuo codice non è valido, Controlla tutte le parole e riprova! - Parola sbagliata. Intendevi %1$s o %2$s\? + Parola sbagliata. Hai dimenticato di inserire questa parola. Parola 12 Parola 11 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index d6e84788..0f1b1a69 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -41,7 +41,7 @@ 앱 설치 제거 백업이 이미 진행 중 코드가 잘못되었습니다. 모든 단어를 확인하고 다시 시도하세요! - 단어가 틀렸습니다. %1$s 또는 %2$s를 입력하려고 하셨나요\? + 단어가 틀렸습니다. 이 단어를 입력하지 않았습니다. 복구 코드가 맞는지 확인하려면 12개 단어로 이루어진 복구 코드를 입력하세요. 백업을 설정할 때 지정했던 12개 단어로 이루어진 복구 코드를 입력하세요. diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 4428e1e4..4b79a885 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -61,7 +61,7 @@ Sikkerhetskopiering utføres Sikkerhetskopieringsmerknad Koden din er ugyldig. Sjekk alle ordene og prøv igjen. - Feil ord. Mente du %1$s eller %2$s\? + Feil ord. Du glemte å skrive inn dette ordet. Ord 12 Ord 11 diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 1e692a51..a84fb883 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -12,7 +12,7 @@ Backup em execução Notificação de backup Seu código é inválido. Por favor, verifique todas as palavras e tente novamente! - Palavra errada. Você quis dizer %1$s ou %2$s\? + Palavra errada. Você se esqueceu de inserir esta palavra. Palavra 12 Palavra 11 diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml index 521c917e..a00429da 100644 --- a/app/src/main/res/values-pt/strings.xml +++ b/app/src/main/res/values-pt/strings.xml @@ -49,7 +49,7 @@ Backup em execução Notificação de backup Seu código é inválido. Por favor, verifique todas as palavras e tente novamente! - Palavra errada. Quis dizer %1$s ou %2$s\? + Palavra errada. Se esqueceu de inserir esta palavra. Palavra 12 Palavra 11 diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index ae511a53..4b2d9679 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -53,7 +53,7 @@ Резервное копирование запущено Уведомление о резервном копировании Ваш код недействителен. Пожалуйста, проверьте все слова и попробуйте еще раз! - Неверное слово. Вы имели в виду %1$s или %2$s\? + Неверное слово. Вы забыли ввести это слово. Слово 12 Слово 11 diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index be1d199f..cb9975e6 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -48,7 +48,7 @@ Yedekleme çalışıyor Yedekleme bildirimi Kodunuz geçersiz. Lütfen tüm sözcükleri gözden geçirin ve tekrar deneyin! - Yanlış sözcük. %1$s veya %2$s demek mi istediniz\? + Yanlış sözcük. Bu sözcüğü girmeyi unuttunuz. 12. sözcük 11. sözcük diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index da558c78..3abfcff3 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -75,7 +75,7 @@ 备份运行中 备份提示 您的代码无效。请检查所有单词,然后重试! - 错误的词。你的意思是%1$s或%2$s? + 错误的词。 你忘了输入这个词。 完成 确认代码 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d7eaa927..e502e9c7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -82,8 +82,8 @@ Word 11 Word 12 You forgot to enter this word. - Wrong word. Did you mean %1$s or %2$s? - Your code is invalid. Please check all words and try again! + Wrong word. + Your code is invalid. Please check all words as well as their position and try again! Recovery code verified Your code is correct and will work for restoring your backup. Incorrect recovery code diff --git a/app/src/sharedTest/java/com/stevesoltys/seedvault/TestUtils.kt b/app/src/sharedTest/java/com/stevesoltys/seedvault/TestUtils.kt index be475cd5..9a9c0f95 100644 --- a/app/src/sharedTest/java/com/stevesoltys/seedvault/TestUtils.kt +++ b/app/src/sharedTest/java/com/stevesoltys/seedvault/TestUtils.kt @@ -44,7 +44,7 @@ fun ByteArray.toHexString(spacer: String = " "): String { for (b in this) { str += String.format("%02X$spacer", b) } - return str + return str.trimEnd() } fun ByteArray.toIntString(): String { diff --git a/app/src/test/java/com/stevesoltys/seedvault/crypto/WordListTest.kt b/app/src/test/java/com/stevesoltys/seedvault/crypto/WordListTest.kt index 58c771d3..3e54e42e 100644 --- a/app/src/test/java/com/stevesoltys/seedvault/crypto/WordListTest.kt +++ b/app/src/test/java/com/stevesoltys/seedvault/crypto/WordListTest.kt @@ -1,11 +1,10 @@ package com.stevesoltys.seedvault.crypto +import cash.z.ecc.android.bip39.Mnemonics +import cash.z.ecc.android.bip39.WordList +import cash.z.ecc.android.bip39.toSeed import com.stevesoltys.seedvault.toHexString -import io.github.novacrypto.bip39.JavaxPBKDF2WithHmacSHA512 -import io.github.novacrypto.bip39.MnemonicGenerator -import io.github.novacrypto.bip39.SeedCalculator -import io.github.novacrypto.bip39.Words -import io.github.novacrypto.bip39.wordlists.English +import com.stevesoltys.seedvault.ui.recoverycode.toMnemonicChars import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test @@ -2067,62 +2066,197 @@ class WordListTest { @Test fun `word list of library did not change`() { + val libWords = WordList().words for (i in words.indices) { - assertEquals(words[i], English.INSTANCE.getWord(i)) + assertEquals(words[i], libWords[i]) } } @Test - fun `test createMnemonic`() { - val entropy = ByteArray(Words.TWELVE.byteLength()) + fun `test creating MnemonicCode from entropy`() { + val entropy = ByteArray(Mnemonics.WordCount.COUNT_12.bitLength / 8) Random.nextBytes(entropy) - val list = ArrayList(12) - MnemonicGenerator(English.INSTANCE).createMnemonic(entropy) { - if (it != " ") list.add(it.toString()) - } - assertEquals(12, list.size) - for (word in list) { - assertTrue(word in words) + val code = Mnemonics.MnemonicCode(entropy) + assertEquals(12, code.words.size) + for (word in code) { + assertTrue(word in words, "$word unknown") } } @Test - @Suppress("MaxLineLength") - fun `12 words generate expected seed`() { + fun `12 not validating words generate seed that novacrypt generated`() { assertEquals( "64AA8C388EC0F3A13C7E51653BC766E30668D30952AB34381C4B174BF3278774" + "B4EE43D0BA08BCBCE0D0B806DEB7AA364A83525C34847078B2A8002A3E116066", - SeedCalculator(JavaxPBKDF2WithHmacSHA512.INSTANCE).calculateSeed( - "write wrong yard year yellow you young youth zebra zero zone zoo", "" - ).toHexString("") + Mnemonics.MnemonicCode( + "write wrong yard year yellow you young youth zebra zero zone zoo" + ).toSeed(validate = false).toHexString("") ) assertEquals( "E911FAA42F389AA9F6D5A40B2ECB876B06D6D1FFBD5885C54720398EB11918CA" + "B8F7BAD70FD5BE39BEB4EB065610700D1CFF1D4BFAA26F998357E15E79002779", - SeedCalculator(JavaxPBKDF2WithHmacSHA512.INSTANCE).calculateSeed( - "matrix lava they brand negative spray floor gym purity picture ritual disorder", "" - ).toHexString("") + Mnemonics.MnemonicCode( + "matrix lava they brand negative spray floor gym purity picture ritual disorder" + ).toSeed(validate = false).toHexString("") ) assertEquals( "DDB26091680CF30D0DC615546E4612327DB287B6B2B8B8947A3E12580315D38C" + "3BF7DD0EB4E9E50B10A41925332E0C8ED43C80DBA29281EF331A1DFA858BF1C9", - SeedCalculator(JavaxPBKDF2WithHmacSHA512.INSTANCE).calculateSeed( - "middle rack south alert ribbon tube hope involve defy oxygen gloom rabbit", "" - ).toHexString("") + Mnemonics.MnemonicCode( + "middle rack south alert ribbon tube hope involve defy oxygen gloom rabbit" + ).toSeed(validate = false).toHexString("") ) assertEquals( "4815B580D0DCDA08334C92B3CB9A8436CD581C55841FB2794FB1E3D6E389F447" + "C8C6520B2FE567720950F5B39BE7EC42C0BC98D3C63F8FEF642B5BD3EE4CDD7B", - SeedCalculator(JavaxPBKDF2WithHmacSHA512.INSTANCE).calculateSeed( - "interest mask trial hold foot segment fade page monitor apple garden shuffle", "" - ).toHexString("") + Mnemonics.MnemonicCode( + "interest mask trial hold foot segment fade page monitor apple garden shuffle" + ).toSeed(validate = false).toHexString("") ) assertEquals( "FF462543D8FB9DAE6C17FA7BA047238664207FCC797D6688E10DD1B3CFD183D4" + "928AD088E8287B69BABCAEB0F87A2DFF2ADD49A7FDB7EB2554D7344F09C41A76", - SeedCalculator(JavaxPBKDF2WithHmacSHA512.INSTANCE).calculateSeed( - "palace glory gospel garment obscure person edge total hunt fix setup uphold\n", "" - ).toHexString("") + Mnemonics.MnemonicCode( + "palace glory gospel garment obscure person edge total hunt fix setup uphold\n" + ).toSeed(validate = false).toHexString("") + ) + } + + @Test + fun `12 valid words generate seed that novacrypt generated`() { + assertEquals( + "C6F9762718449C9D0794FEC140D2C8D4E23FF8E3701D64C03DDD13C69BC73E48" + + "6AB89AB2C7C9BEA43F4AF839F2078595851D5D48FEC6A9FC6C25F399DBB909F9", + Mnemonics.MnemonicCode( + "script vault basic album cotton car entire jaguar correct anger select flower" + ).toSeed().toHexString("") + ) + assertEquals( + "13C5188428B1DF8A5333E60BA7EC47F7E75585315C73BD19812D3591C5F4C52B" + + "B2FC1FF40B1942E2A1EF9F34F586114ED37D46A5A3907A43B317E937C1D9D2CD", + Mnemonics.MnemonicCode( + "drastic toy fatal goose treat saddle chalk fame dismiss employ super behind" + ).toSeed().toHexString("") + ) + assertEquals( + "40B41BB22AC3A507F26A78E027A3B3C5C8F45FF0F5593D82762C74AE69FA548B" + + "A72C0CED31DED6211884B412E7B80F932F9830FA7A67CDB5B28604213DE6599C", + Mnemonics.MnemonicCode( + "nation infant heart virus argue two vivid slam lend decorate turn wish" + ).toSeed().toHexString("") + ) + assertEquals( + "B6D755172B6E9353A25EB3559336C17A8619F3EBE55E8A9A74A44E1AB88EF5E2" + + "C6E12FE132E42A55CC3F8F9224E6A0ABC9C3FF4EB9523A4E9750CDAAEFBA6282", + Mnemonics.MnemonicCode( + "elbow boy powder robot eagle rival neutral pigeon oil shrimp demand health" + ).toSeed().toHexString("") + ) + assertEquals( + "3EDB1292B4D124426201AC523FCC2572184E0B63667DA7DF105AD8FCCD16C074" + + "C6DAF9C7D644B4B48AF75185D21B9E7D778FFE55F836C539581DEBB98C331526", + Mnemonics.MnemonicCode( + "build setup screen solution prepare spice organ ten loud seek ask attract" + ).toSeed().toHexString("") + ) + assertEquals( + "65986351CD054822B40E417855AC2B5651C5F87892F17ED2A984F6B59DD5FB4E" + + "6A4568ABF7E06D93CBCC69BB68F2625E3E8AF2751106380922D49C0D0D0B456B", + Mnemonics.MnemonicCode( + "unhappy welcome pizza inflict inherit village minimum orient cheap swear grunt giraffe" // ktlint-disable max-line-length + ).toSeed().toHexString("") + ) + assertEquals( + "639034B381740A9FA5B8A84715CF18B21EBD343DD91F7B6124A0EFC32A636619" + + "49B02A7810B1A99D8E8CC4CD7D046CE59EAAADBB52DDC0B5036EFED007E1CFF6", + Mnemonics.MnemonicCode( + "rather suit pluck afford avocado diary swap library earn song rival fiber" + ).toSeed().toHexString("") + ) + assertEquals( + "43E1417221BFB40851DE286B543B51DEE9C01D239B2C2E8A355D45B3DF95DFAA" + + "C8DBCAEF1D864D91759A07057DBDB891900D583CAB09BD0655493912108AE65A", + Mnemonics.MnemonicCode( + "toss note family morning silk edge high error appear tilt almost myth" + ).toSeed().toHexString("") + ) + assertEquals( + "14084AAF9CFCAC386D4CE5B9140BEADBF727B1B09786A67A574B668A1A4AE0A3" + + "21B8D4E7BC005980B088A160B6EC08A1CB892C2090C58D95A7C6AAD16C14EE1E", + Mnemonics.MnemonicCode( + "parrot burden release bronze section fantasy ridge blood direct physical spoil asthma" // ktlint-disable max-line-length + ).toSeed().toHexString("") + ) + assertEquals( + "E11E8737327EE6A640761B3888C349D829A60FEAEFB7914D2AE1616F0AC45B9A" + + "322F41D0030C89E209300FA25615FE6B5BDEF73F3E5CE21167685E8A27EE0790", + Mnemonics.MnemonicCode( + "version deliver worry sick flee submit pledge adapt night swear glare adult" + ).toSeed().toHexString("") + ) + assertEquals( + "02652896F67695C03F379A354685A8A0B92D0F303F77461476E80BB594EAD84B" + + "D00B2943C2229ED843C65F6C53A376005871FF74F834E6B6E3B57FFD83D3FB12", + Mnemonics.MnemonicCode( + "exercise curtain initial model travel client twist neutral peace unfold start shell" // ktlint-disable max-line-length + ).toSeed().toHexString("") + ) + } + + @Test + fun `entropy generates same words that novacrypt generated`() { + assertEquals( + "B8 3F 0D 49 7F 2A F4 69 13 71 47 D5 54 9D 17 0B", + Mnemonics.MnemonicCode( + "return wear false wrestle quantum cruel evidence cigar stem pilot easy blood" + ).toEntropy().toHexString() + ) + assertEquals( + "AA 8E 3F 1C EF 60 20 D7 D2 BB FD A0 AA AD 69 09", + Mnemonics.MnemonicCode( + "pride impose shrimp tell acoustic hip enough leisure pass fever fog basket" + ).toEntropy().toHexString() + ) + assertEquals( + "CE E8 17 7E AB 9E 49 8A 0B 32 3C 97 94 07 0C 32", + Mnemonics.MnemonicCode( + "solve doll text fire tonight shallow coast elegant nurse parent seek grass" + ).toEntropy().toHexString() + ) + assertEquals( + "AA 27 4B D8 7B 0D 18 EB 5D 08 BD 11 73 36 C1 06", + Mnemonics.MnemonicCode( + "pretty demise voyage voyage spice interest injury bless badge often raccoon artefact" // ktlint-disable max-line-length + ).toEntropy().toHexString() + ) + assertEquals( + "16 5F A7 40 26 E9 51 70 1B 7A 5C D2 AB CD 73 7E", + Mnemonics.MnemonicCode( + "bind wood source evidence never retreat hospital entire sport fury fresh woman" + ).toEntropy().toHexString() + ) + } + + @Test + fun `test create MnemonicCode from List of CharSequence`() { + assertEquals( + "B8 3F 0D 49 7F 2A F4 69 13 71 47 D5 54 9D 17 0B", + Mnemonics.MnemonicCode( + listOf( + "return", + "wear", + "false", + "wrestle", + "quantum", + "cruel", + "evidence", + "cigar", + "stem", + "pilot", + "easy", + "blood" + ).toMnemonicChars() + ).toEntropy().toHexString() ) } diff --git a/libs/Android.bp b/libs/Android.bp new file mode 100644 index 00000000..2fa04ce0 --- /dev/null +++ b/libs/Android.bp @@ -0,0 +1,5 @@ +java_import { + name: "seedvault-lib-kotlin-bip39", + jars: ["kotlin-bip39-1.0.2.jar"], + sdk_version: "current", +} diff --git a/libs/kotlin-bip39-1.0.2.jar b/libs/kotlin-bip39-1.0.2.jar new file mode 100644 index 00000000..c05e1e05 Binary files /dev/null and b/libs/kotlin-bip39-1.0.2.jar differ diff --git a/libs/novacrypto-bip39/Android.bp b/libs/novacrypto-bip39/Android.bp deleted file mode 100644 index e1947407..00000000 --- a/libs/novacrypto-bip39/Android.bp +++ /dev/null @@ -1,27 +0,0 @@ -java_import { - name: "seedvault-lib-novacrypto-bip39-nodeps", - jars: ["BIP39-2019.01.27.jar"], - sdk_version: "current", -} - -java_library_static { - name: "seedvault-lib-novacrypto-bip39", - static_libs: [ - "seedvault-lib-novacrypto-bip39-nodeps", - "seedvault-lib-novacrypto-sha256", - "seedvault-lib-novacrypto-toruntime", - ], - sdk_version: "current", -} - -java_import { - name: "seedvault-lib-novacrypto-sha256", - jars: ["SHA256-2019.01.27.jar"], - sdk_version: "current", -} - -java_import { - name: "seedvault-lib-novacrypto-toruntime", - jars: ["ToRuntime-0.9.0.jar"], - sdk_version: "current", -} diff --git a/libs/novacrypto-bip39/BIP39-2019.01.27.jar b/libs/novacrypto-bip39/BIP39-2019.01.27.jar deleted file mode 100644 index 806af43d..00000000 Binary files a/libs/novacrypto-bip39/BIP39-2019.01.27.jar and /dev/null differ diff --git a/libs/novacrypto-bip39/SHA256-2019.01.27.jar b/libs/novacrypto-bip39/SHA256-2019.01.27.jar deleted file mode 100644 index ae871853..00000000 Binary files a/libs/novacrypto-bip39/SHA256-2019.01.27.jar and /dev/null differ diff --git a/libs/novacrypto-bip39/ToRuntime-0.9.0.jar b/libs/novacrypto-bip39/ToRuntime-0.9.0.jar deleted file mode 100644 index d5aa5d63..00000000 Binary files a/libs/novacrypto-bip39/ToRuntime-0.9.0.jar and /dev/null differ