Replace novacrypt BIP39 library with the zcash one

which is MIT licensed instead of GPLv3

Change-Id: I30041de5ab1c1f8c7d5f57d6c60e28300a285259
This commit is contained in:
Torsten Grote 2021-07-02 16:46:55 -03:00 committed by Chirayu Desai
parent fd4c92bd84
commit 05640ebb63
31 changed files with 231 additions and 129 deletions

View file

@ -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",

View file

@ -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)

View file

@ -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<CharSequence>) :
class RecoveryCodeAdapter(private val items: List<CharArray>) :
Adapter<RecoveryCodeViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecoveryCodeViewHolder {
@ -30,9 +30,9 @@ class RecoveryCodeViewHolder(v: View) : RecyclerView.ViewHolder(v) {
private val num = v.findViewById<TextView>(R.id.num)
private val word = v.findViewById<TextView>(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)
}
}

View file

@ -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<String>(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<String> {
val adapter = ArrayAdapter<String>(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<CharSequence> = ArrayList<String>(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<CharSequence>, e: WordNotFoundException) {
val i = input.indexOf(e.word)
private fun showWrongWordError(input: List<CharSequence>) {
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]))
}
}

View file

@ -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)

View file

@ -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<CharSequence> by lazy {
val items: ArrayList<CharSequence> = ArrayList(WORD_NUM)
val entropy = ByteArray(Words.TWELVE.byteLength())
internal val wordList: List<CharArray> 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<Boolean>()
@ -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<CharSequence>, 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<CharSequence>.toMnemonicChars(): CharArray {
return joinToString(" ").toCharArray()
}

View file

@ -18,7 +18,7 @@
<string name="notification_title">Sicherung läuft</string>
<string name="notification_channel_title">Sicherungsbenachrichtigung</string>
<string name="recovery_code_error_checksum_word">Ihr Wiederherstellungsschlüssel ist ungültig. Bitte prüfen Sie alle eingegebenen Wörter und versuchen Sie es erneut!</string>
<string name="recovery_code_error_invalid_word">Falsches Wort. Meinten Sie %1$s oder %2$s\?</string>
<string name="recovery_code_error_invalid_word">Falsches Wort.</string>
<string name="recovery_code_error_empty_word">Sie vergaßen, dieses Wort einzugeben.</string>
<string name="recovery_code_input_hint_12">Wort 12</string>
<string name="recovery_code_input_hint_11">Wort 11</string>

View file

@ -80,7 +80,7 @@
<string name="notification_title">Εκτελείται δημιουργία αντιγράφων ασφαλείας</string>
<string name="notification_channel_title">Ειδοποίηση δημιουργίας αντιγράφων ασφαλείας</string>
<string name="recovery_code_error_checksum_word">Ο κωδικός σας δεν είναι έγκυρος. Ελέγξτε όλες τις λέξεις και δοκιμάστε ξανά!</string>
<string name="recovery_code_error_invalid_word">Λάθος λέξη. Εννοείτε %1$s ή %2$s;</string>
<string name="recovery_code_error_invalid_word">Λάθος λέξη.</string>
<string name="recovery_code_error_empty_word">Ξεχάσατε να εισαγάγετε αυτήν τη λέξη.</string>
<string name="recovery_code_input_hint_12">Λέξη 12</string>
<string name="recovery_code_input_hint_11">Λέξη 11</string>

View file

@ -46,7 +46,7 @@
<string name="notification_success_title">Respaldo terminó</string>
<string name="notification_title">Ejecución de respaldo</string>
<string name="recovery_code_error_checksum_word">Tu código no es válido. Por favor revise todas las palabras e intente nuevamente!</string>
<string name="recovery_code_error_invalid_word">Palabra equivocada. ¿Quisiste decir %1$s o %2$s\?</string>
<string name="recovery_code_error_invalid_word">Palabra equivocada.</string>
<string name="recovery_code_error_empty_word">Olvidaste ingresar esta palabra.</string>
<string name="recovery_code_input_hint_12">Palabra 12</string>
<string name="recovery_code_input_hint_11">Palabra 11</string>

View file

@ -72,7 +72,7 @@
<string name="notification_title">Copia de seguridad en ejecución</string>
<string name="notification_channel_title">Notificación de copia de seguridad</string>
<string name="recovery_code_error_checksum_word">Su código es inválido. Por favor, compruebe todas las palabras e inténtelo de nuevo!</string>
<string name="recovery_code_error_invalid_word">Palabra equivocada. ¿Quisiste decir %1$s o %2$s \?</string>
<string name="recovery_code_error_invalid_word">Palabra equivocada.</string>
<string name="recovery_code_input_intro">Ingrese su código de recuperación de 12 palabras que anotó al configurar las copias de seguridad.</string>
<string name="recovery_code_confirm_intro">Ingrese su código de recuperación de 12 palabras para asegurarse de que funcionará cuando lo necesite.</string>
<string name="recovery_code_confirm_button">Confirma código</string>

View file

@ -16,7 +16,7 @@
<string name="notification_error_action">Réparer</string>
<string name="notification_channel_title">Notification de la sauvegarde</string>
<string name="recovery_code_error_checksum_word">Votre code est invalide. Veuillez vérifier tous les mots et réessayer !</string>
<string name="recovery_code_error_invalid_word">Mot erroné. Vouliez-vous dire %1$s ou %2$s \?</string>
<string name="recovery_code_error_invalid_word">Mot erroné.</string>
<string name="recovery_code_error_empty_word">Vous avez oublié dentrer ce mot.</string>
<string name="recovery_code_input_hint_12">Mot 12</string>
<string name="recovery_code_input_hint_11">Mot 11</string>

View file

@ -6,7 +6,7 @@
<string name="notification_title">הגיבוי פועל</string>
<string name="notification_channel_title">התראת גיבוי</string>
<string name="recovery_code_error_checksum_word">הקוד שלך שגוי. נא לאמת את כל המילים ולנסות שוב!</string>
<string name="recovery_code_error_invalid_word">מילה שגויה. התכוונת למילה %1$s או %2$s\?</string>
<string name="recovery_code_error_invalid_word">מילה שגויה.</string>
<string name="recovery_code_error_empty_word">שכחת למלא את המילה הזאת.</string>
<string name="recovery_code_input_hint_12">מילה 12</string>
<string name="recovery_code_input_hint_11">מילה 11</string>

View file

@ -49,7 +49,7 @@
<string name="notification_title">Sigurnosna kopija pokrenuta</string>
<string name="notification_channel_title">Obavijest sigurnosne kopije</string>
<string name="recovery_code_error_checksum_word">Tvoja lozinka je neispravna. Provjeri sve riječi i pokušaj ponovo!</string>
<string name="recovery_code_error_invalid_word">Kriva riječ. Je li misliš %1$s ili %2$s\?</string>
<string name="recovery_code_error_invalid_word">Kriva riječ.</string>
<string name="recovery_code_error_empty_word">Ova riječ nije upisana.</string>
<string name="recovery_code_input_hint_12">Riječ 12</string>
<string name="recovery_code_input_hint_11">Riječ 11</string>

View file

@ -47,7 +47,7 @@
<string name="notification_title">Öryggisafritun í gangi</string>
<string name="notification_channel_title">Tilkynning um öryggisafritatöku</string>
<string name="recovery_code_error_checksum_word">Kóðinn er ógildur. Athugað öll orðin og prófaðu aftur!</string>
<string name="recovery_code_error_invalid_word">Rangt orð. Meintirðu %1$s or %2$s\?</string>
<string name="recovery_code_error_invalid_word">Rangt orð.</string>
<string name="recovery_code_error_empty_word">Þú gleymdir að slá inn þetta orð.</string>
<string name="recovery_code_input_hint_12">Orð 12</string>
<string name="recovery_code_input_hint_11">Orð 11</string>

View file

@ -9,7 +9,7 @@
<string name="notification_title">Backup in esecuzione</string>
<string name="notification_channel_title">Notificazione backup</string>
<string name="recovery_code_error_checksum_word">Il tuo codice non è valido, Controlla tutte le parole e riprova!</string>
<string name="recovery_code_error_invalid_word">Parola sbagliata. Intendevi %1$s o %2$s\?</string>
<string name="recovery_code_error_invalid_word">Parola sbagliata.</string>
<string name="recovery_code_error_empty_word">Hai dimenticato di inserire questa parola.</string>
<string name="recovery_code_input_hint_12">Parola 12</string>
<string name="recovery_code_input_hint_11">Parola 11</string>

View file

@ -41,7 +41,7 @@
<string name="notification_restore_error_action">앱 설치 제거</string>
<string name="notification_backup_already_running">백업이 이미 진행 중</string>
<string name="recovery_code_error_checksum_word">코드가 잘못되었습니다. 모든 단어를 확인하고 다시 시도하세요!</string>
<string name="recovery_code_error_invalid_word">단어가 틀렸습니다. %1$s 또는 %2$s를 입력하려고 하셨나요\?</string>
<string name="recovery_code_error_invalid_word">단어가 틀렸습니다.</string>
<string name="recovery_code_error_empty_word">이 단어를 입력하지 않았습니다.</string>
<string name="recovery_code_confirm_intro">복구 코드가 맞는지 확인하려면 12개 단어로 이루어진 복구 코드를 입력하세요.</string>
<string name="recovery_code_input_intro">백업을 설정할 때 지정했던 12개 단어로 이루어진 복구 코드를 입력하세요.</string>

View file

@ -61,7 +61,7 @@
<string name="notification_title">Sikkerhetskopiering utføres</string>
<string name="notification_channel_title">Sikkerhetskopieringsmerknad</string>
<string name="recovery_code_error_checksum_word">Koden din er ugyldig. Sjekk alle ordene og prøv igjen.</string>
<string name="recovery_code_error_invalid_word">Feil ord. Mente du %1$s eller %2$s\?</string>
<string name="recovery_code_error_invalid_word">Feil ord.</string>
<string name="recovery_code_error_empty_word">Du glemte å skrive inn dette ordet.</string>
<string name="recovery_code_input_hint_12">Ord 12</string>
<string name="recovery_code_input_hint_11">Ord 11</string>

View file

@ -12,7 +12,7 @@
<string name="notification_title">Backup em execução</string>
<string name="notification_channel_title">Notificação de backup</string>
<string name="recovery_code_error_checksum_word">Seu código é inválido. Por favor, verifique todas as palavras e tente novamente!</string>
<string name="recovery_code_error_invalid_word">Palavra errada. Você quis dizer %1$s ou %2$s\?</string>
<string name="recovery_code_error_invalid_word">Palavra errada.</string>
<string name="recovery_code_error_empty_word">Você se esqueceu de inserir esta palavra.</string>
<string name="recovery_code_input_hint_12">Palavra 12</string>
<string name="recovery_code_input_hint_11">Palavra 11</string>

View file

@ -49,7 +49,7 @@
<string name="notification_title">Backup em execução</string>
<string name="notification_channel_title">Notificação de backup</string>
<string name="recovery_code_error_checksum_word">Seu código é inválido. Por favor, verifique todas as palavras e tente novamente!</string>
<string name="recovery_code_error_invalid_word">Palavra errada. Quis dizer %1$s ou %2$s\?</string>
<string name="recovery_code_error_invalid_word">Palavra errada.</string>
<string name="recovery_code_error_empty_word">Se esqueceu de inserir esta palavra.</string>
<string name="recovery_code_input_hint_12">Palavra 12</string>
<string name="recovery_code_input_hint_11">Palavra 11</string>

View file

@ -53,7 +53,7 @@
<string name="notification_title">Резервное копирование запущено</string>
<string name="notification_channel_title">Уведомление о резервном копировании</string>
<string name="recovery_code_error_checksum_word">Ваш код недействителен. Пожалуйста, проверьте все слова и попробуйте еще раз!</string>
<string name="recovery_code_error_invalid_word">Неверное слово. Вы имели в виду %1$s или %2$s\?</string>
<string name="recovery_code_error_invalid_word">Неверное слово.</string>
<string name="recovery_code_error_empty_word">Вы забыли ввести это слово.</string>
<string name="recovery_code_input_hint_12">Слово 12</string>
<string name="recovery_code_input_hint_11">Слово 11</string>

View file

@ -48,7 +48,7 @@
<string name="notification_title">Yedekleme çalışıyor</string>
<string name="notification_channel_title">Yedekleme bildirimi</string>
<string name="recovery_code_error_checksum_word">Kodunuz geçersiz. Lütfen tüm sözcükleri gözden geçirin ve tekrar deneyin!</string>
<string name="recovery_code_error_invalid_word">Yanlış sözcük. %1$s veya %2$s demek mi istediniz\?</string>
<string name="recovery_code_error_invalid_word">Yanlış sözcük.</string>
<string name="recovery_code_error_empty_word">Bu sözcüğü girmeyi unuttunuz.</string>
<string name="recovery_code_input_hint_12">12. sözcük</string>
<string name="recovery_code_input_hint_11">11. sözcük</string>

View file

@ -75,7 +75,7 @@
<string name="notification_title">备份运行中</string>
<string name="notification_channel_title">备份提示</string>
<string name="recovery_code_error_checksum_word">您的代码无效。请检查所有单词,然后重试!</string>
<string name="recovery_code_error_invalid_word">错误的词。你的意思是%1$s或%2$s</string>
<string name="recovery_code_error_invalid_word">错误的词。</string>
<string name="recovery_code_error_empty_word">你忘了输入这个词。</string>
<string name="recovery_code_done_button">完成</string>
<string name="recovery_code_confirm_button">确认代码</string>

View file

@ -82,8 +82,8 @@
<string name="recovery_code_input_hint_11">Word 11</string>
<string name="recovery_code_input_hint_12">Word 12</string>
<string name="recovery_code_error_empty_word">You forgot to enter this word.</string>
<string name="recovery_code_error_invalid_word">Wrong word. Did you mean %1$s or %2$s?</string>
<string name="recovery_code_error_checksum_word">Your code is invalid. Please check all words and try again!</string>
<string name="recovery_code_error_invalid_word">Wrong word.</string>
<string name="recovery_code_error_checksum_word">Your code is invalid. Please check all words as well as their position and try again!</string>
<string name="recovery_code_verification_ok_title">Recovery code verified</string>
<string name="recovery_code_verification_ok_message">Your code is correct and will work for restoring your backup.</string>
<string name="recovery_code_verification_error_title">Incorrect recovery code</string>

View file

@ -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 {

View file

@ -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<String>(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<CharSequence>(
"return",
"wear",
"false",
"wrestle",
"quantum",
"cruel",
"evidence",
"cigar",
"stem",
"pilot",
"easy",
"blood"
).toMnemonicChars()
).toEntropy().toHexString()
)
}

5
libs/Android.bp Normal file
View file

@ -0,0 +1,5 @@
java_import {
name: "seedvault-lib-kotlin-bip39",
jars: ["kotlin-bip39-1.0.2.jar"],
sdk_version: "current",
}

BIN
libs/kotlin-bip39-1.0.2.jar Normal file

Binary file not shown.

View file

@ -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",
}