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-android",
"seedvault-lib-koin-androidx-viewmodel", "seedvault-lib-koin-androidx-viewmodel",
// bip39 // bip39
"seedvault-lib-novacrypto-bip39", "seedvault-lib-kotlin-bip39",
], ],
manifest: "app/src/main/AndroidManifest.xml", 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: ['*.jar'], dir: "${rootProject.rootDir}/libs/koin-android")
implementation fileTree(include: ['*.aar'], 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) * 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 androidx.recyclerview.widget.RecyclerView.Adapter
import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.R
class RecoveryCodeAdapter(private val items: List<CharSequence>) : class RecoveryCodeAdapter(private val items: List<CharArray>) :
Adapter<RecoveryCodeViewHolder>() { Adapter<RecoveryCodeViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): 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 num = v.findViewById<TextView>(R.id.num)
private val word = v.findViewById<TextView>(R.id.word) 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() 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.appcompat.app.AlertDialog
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.fragment.app.Fragment 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.snackbar.Snackbar
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import com.stevesoltys.seedvault.R import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.isDebugBuild import com.stevesoltys.seedvault.isDebugBuild
import com.stevesoltys.seedvault.ui.LiveEventHandler 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 org.koin.androidx.viewmodel.ext.android.sharedViewModel
import java.util.Locale
internal const val ARG_FOR_NEW_CODE = "forVerifyingNewCode" internal const val ARG_FOR_NEW_CODE = "forVerifyingNewCode"
@ -101,7 +102,10 @@ class RecoveryCodeInputFragment : Fragment() {
backView.setOnClickListener { requireActivity().finishAfterTransition() } 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) { for (i in 0 until WORD_NUM) {
val wordLayout = getWordLayout(i) val wordLayout = getWordLayout(i)
@ -122,14 +126,6 @@ class RecoveryCodeInputFragment : Fragment() {
if (forVerifyingNewCode && isDebugBuild() && !viewModel.isRestore) debugPreFill() 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 { private fun getInput(): List<CharSequence> = ArrayList<String>(WORD_NUM).apply {
for (i in 0 until WORD_NUM) add(getWordLayout(i).editText!!.text.toString()) for (i in 0 until WORD_NUM) add(getWordLayout(i).editText!!.text.toString())
} }
@ -139,10 +135,10 @@ class RecoveryCodeInputFragment : Fragment() {
if (!allFilledOut(input)) return if (!allFilledOut(input)) return
try { try {
viewModel.validateAndContinue(input, forVerifyingNewCode) viewModel.validateAndContinue(input, forVerifyingNewCode)
} catch (e: InvalidChecksumException) { } catch (e: ChecksumException) {
Toast.makeText(context, R.string.recovery_code_error_checksum_word, LENGTH_LONG).show() Toast.makeText(context, R.string.recovery_code_error_checksum_word, LENGTH_LONG).show()
} catch (e: WordNotFoundException) { } catch (e: InvalidWordException) {
showWrongWordError(input, e) showWrongWordError(input)
} }
} }
@ -155,10 +151,11 @@ class RecoveryCodeInputFragment : Fragment() {
return true return true
} }
private fun showWrongWordError(input: List<CharSequence>, e: WordNotFoundException) { private fun showWrongWordError(input: List<CharSequence>) {
val i = input.indexOf(e.word) val words = Mnemonics.getCachedWords(Locale.ENGLISH.language)
val i = input.indexOfFirst { it !in words }
if (i == -1) throw AssertionError() 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) showError(i, str)
} }
@ -235,7 +232,7 @@ class RecoveryCodeInputFragment : Fragment() {
private fun debugPreFill() { private fun debugPreFill() {
val words = viewModel.wordList val words = viewModel.wordList
for (i in words.indices) { 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, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View {
val v: View = inflater.inflate(R.layout.fragment_recovery_code_output, container, false) val v: View = inflater.inflate(R.layout.fragment_recovery_code_output, container, false)
wordList = v.findViewById(R.id.wordList) wordList = v.findViewById(R.id.wordList)

View file

@ -4,6 +4,11 @@ import android.app.backup.IBackupManager
import android.os.UserHandle import android.os.UserHandle
import android.util.Log import android.util.Log
import androidx.lifecycle.AndroidViewModel 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.App
import com.stevesoltys.seedvault.crypto.Crypto import com.stevesoltys.seedvault.crypto.Crypto
import com.stevesoltys.seedvault.crypto.KeyManager 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.transport.backup.BackupCoordinator
import com.stevesoltys.seedvault.ui.LiveEvent import com.stevesoltys.seedvault.ui.LiveEvent
import com.stevesoltys.seedvault.ui.MutableLiveEvent 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.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.calyxos.backup.storage.api.StorageBackup import org.calyxos.backup.storage.api.StorageBackup
import java.io.IOException import java.io.IOException
import java.security.SecureRandom import java.security.SecureRandom
import java.util.ArrayList
internal const val WORD_NUM = 12 internal const val WORD_NUM = 12
internal const val WORD_LIST_SIZE = 2048
private val TAG = RecoveryCodeViewModel::class.java.simpleName private val TAG = RecoveryCodeViewModel::class.java.simpleName
@ -43,14 +36,12 @@ internal class RecoveryCodeViewModel(
private val storageBackup: StorageBackup private val storageBackup: StorageBackup
) : AndroidViewModel(app) { ) : AndroidViewModel(app) {
internal val wordList: List<CharSequence> by lazy { internal val wordList: List<CharArray> by lazy {
val items: ArrayList<CharSequence> = ArrayList(WORD_NUM) // we use our own entropy to not having to trust the library to use SecureRandom
val entropy = ByteArray(Words.TWELVE.byteLength()) val entropy = ByteArray(Mnemonics.WordCount.COUNT_12.bitLength / 8)
SecureRandom().nextBytes(entropy) SecureRandom().nextBytes(entropy)
MnemonicGenerator(English.INSTANCE).createMnemonic(entropy) { // create the words from the entropy
if (it != " ") items.add(it) Mnemonics.MnemonicCode(entropy).words
}
items
} }
private val mConfirmButtonClicked = MutableLiveEvent<Boolean>() private val mConfirmButtonClicked = MutableLiveEvent<Boolean>()
@ -65,17 +56,15 @@ internal class RecoveryCodeViewModel(
internal var isRestore: Boolean = false internal var isRestore: Boolean = false
@Throws(WordNotFoundException::class, InvalidChecksumException::class) @Throws(InvalidWordException::class, ChecksumException::class)
fun validateAndContinue(input: List<CharSequence>, forVerifyingNewCode: Boolean) { fun validateAndContinue(input: List<CharSequence>, forVerifyingNewCode: Boolean) {
val code = Mnemonics.MnemonicCode(input.toMnemonicChars())
try { try {
MnemonicValidator.ofWordList(English.INSTANCE).validate(input) code.validate()
} catch (e: UnexpectedWhiteSpaceException) { } catch (e: WordCountException) {
throw AssertionError(e)
} catch (e: InvalidWordCountException) {
throw AssertionError(e) throw AssertionError(e)
} }
val mnemonic = input.joinToString(" ") val seed = code.toSeed()
val seed = SeedCalculator(JavaxPBKDF2WithHmacSHA512.INSTANCE).calculateSeed(mnemonic, "")
if (forVerifyingNewCode) { if (forVerifyingNewCode) {
keyManager.storeBackupKey(seed) keyManager.storeBackupKey(seed)
keyManager.storeMainKey(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_title">Sicherung läuft</string>
<string name="notification_channel_title">Sicherungsbenachrichtigung</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_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_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_12">Wort 12</string>
<string name="recovery_code_input_hint_11">Wort 11</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_title">Εκτελείται δημιουργία αντιγράφων ασφαλείας</string>
<string name="notification_channel_title">Ειδοποίηση δημιουργίας αντιγράφων ασφαλείας</string> <string name="notification_channel_title">Ειδοποίηση δημιουργίας αντιγράφων ασφαλείας</string>
<string name="recovery_code_error_checksum_word">Ο κωδικός σας δεν είναι έγκυρος. Ελέγξτε όλες τις λέξεις και δοκιμάστε ξανά!</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_error_empty_word">Ξεχάσατε να εισαγάγετε αυτήν τη λέξη.</string>
<string name="recovery_code_input_hint_12">Λέξη 12</string> <string name="recovery_code_input_hint_12">Λέξη 12</string>
<string name="recovery_code_input_hint_11">Λέξη 11</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_success_title">Respaldo terminó</string>
<string name="notification_title">Ejecución de respaldo</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_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_error_empty_word">Olvidaste ingresar esta palabra.</string>
<string name="recovery_code_input_hint_12">Palabra 12</string> <string name="recovery_code_input_hint_12">Palabra 12</string>
<string name="recovery_code_input_hint_11">Palabra 11</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_title">Copia de seguridad en ejecución</string>
<string name="notification_channel_title">Notificación de copia de seguridad</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_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_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_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> <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_error_action">Réparer</string>
<string name="notification_channel_title">Notification de la sauvegarde</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_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_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_12">Mot 12</string>
<string name="recovery_code_input_hint_11">Mot 11</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_title">הגיבוי פועל</string>
<string name="notification_channel_title">התראת גיבוי</string> <string name="notification_channel_title">התראת גיבוי</string>
<string name="recovery_code_error_checksum_word">הקוד שלך שגוי. נא לאמת את כל המילים ולנסות שוב!</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_error_empty_word">שכחת למלא את המילה הזאת.</string>
<string name="recovery_code_input_hint_12">מילה 12</string> <string name="recovery_code_input_hint_12">מילה 12</string>
<string name="recovery_code_input_hint_11">מילה 11</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_title">Sigurnosna kopija pokrenuta</string>
<string name="notification_channel_title">Obavijest sigurnosne kopije</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_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_error_empty_word">Ova riječ nije upisana.</string>
<string name="recovery_code_input_hint_12">Riječ 12</string> <string name="recovery_code_input_hint_12">Riječ 12</string>
<string name="recovery_code_input_hint_11">Riječ 11</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_title">Öryggisafritun í gangi</string>
<string name="notification_channel_title">Tilkynning um öryggisafritatöku</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_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_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_12">Orð 12</string>
<string name="recovery_code_input_hint_11">Orð 11</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_title">Backup in esecuzione</string>
<string name="notification_channel_title">Notificazione backup</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_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_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_12">Parola 12</string>
<string name="recovery_code_input_hint_11">Parola 11</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_restore_error_action">앱 설치 제거</string>
<string name="notification_backup_already_running">백업이 이미 진행 중</string> <string name="notification_backup_already_running">백업이 이미 진행 중</string>
<string name="recovery_code_error_checksum_word">코드가 잘못되었습니다. 모든 단어를 확인하고 다시 시도하세요!</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_error_empty_word">이 단어를 입력하지 않았습니다.</string>
<string name="recovery_code_confirm_intro">복구 코드가 맞는지 확인하려면 12개 단어로 이루어진 복구 코드를 입력하세요.</string> <string name="recovery_code_confirm_intro">복구 코드가 맞는지 확인하려면 12개 단어로 이루어진 복구 코드를 입력하세요.</string>
<string name="recovery_code_input_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_title">Sikkerhetskopiering utføres</string>
<string name="notification_channel_title">Sikkerhetskopieringsmerknad</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_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_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_12">Ord 12</string>
<string name="recovery_code_input_hint_11">Ord 11</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_title">Backup em execução</string>
<string name="notification_channel_title">Notificação de backup</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_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_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_12">Palavra 12</string>
<string name="recovery_code_input_hint_11">Palavra 11</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_title">Backup em execução</string>
<string name="notification_channel_title">Notificação de backup</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_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_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_12">Palavra 12</string>
<string name="recovery_code_input_hint_11">Palavra 11</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_title">Резервное копирование запущено</string>
<string name="notification_channel_title">Уведомление о резервном копировании</string> <string name="notification_channel_title">Уведомление о резервном копировании</string>
<string name="recovery_code_error_checksum_word">Ваш код недействителен. Пожалуйста, проверьте все слова и попробуйте еще раз!</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_error_empty_word">Вы забыли ввести это слово.</string>
<string name="recovery_code_input_hint_12">Слово 12</string> <string name="recovery_code_input_hint_12">Слово 12</string>
<string name="recovery_code_input_hint_11">Слово 11</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_title">Yedekleme çalışıyor</string>
<string name="notification_channel_title">Yedekleme bildirimi</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_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_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_12">12. sözcük</string>
<string name="recovery_code_input_hint_11">11. 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_title">备份运行中</string>
<string name="notification_channel_title">备份提示</string> <string name="notification_channel_title">备份提示</string>
<string name="recovery_code_error_checksum_word">您的代码无效。请检查所有单词,然后重试!</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_error_empty_word">你忘了输入这个词。</string>
<string name="recovery_code_done_button">完成</string> <string name="recovery_code_done_button">完成</string>
<string name="recovery_code_confirm_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_11">Word 11</string>
<string name="recovery_code_input_hint_12">Word 12</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_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_invalid_word">Wrong word.</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_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_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_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> <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) { for (b in this) {
str += String.format("%02X$spacer", b) str += String.format("%02X$spacer", b)
} }
return str return str.trimEnd()
} }
fun ByteArray.toIntString(): String { fun ByteArray.toIntString(): String {

View file

@ -1,11 +1,10 @@
package com.stevesoltys.seedvault.crypto 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 com.stevesoltys.seedvault.toHexString
import io.github.novacrypto.bip39.JavaxPBKDF2WithHmacSHA512 import com.stevesoltys.seedvault.ui.recoverycode.toMnemonicChars
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 org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
@ -2067,62 +2066,197 @@ class WordListTest {
@Test @Test
fun `word list of library did not change`() { fun `word list of library did not change`() {
val libWords = WordList().words
for (i in words.indices) { for (i in words.indices) {
assertEquals(words[i], English.INSTANCE.getWord(i)) assertEquals(words[i], libWords[i])
} }
} }
@Test @Test
fun `test createMnemonic`() { fun `test creating MnemonicCode from entropy`() {
val entropy = ByteArray(Words.TWELVE.byteLength()) val entropy = ByteArray(Mnemonics.WordCount.COUNT_12.bitLength / 8)
Random.nextBytes(entropy) Random.nextBytes(entropy)
val list = ArrayList<String>(12) val code = Mnemonics.MnemonicCode(entropy)
MnemonicGenerator(English.INSTANCE).createMnemonic(entropy) { assertEquals(12, code.words.size)
if (it != " ") list.add(it.toString()) for (word in code) {
} assertTrue(word in words, "$word unknown")
assertEquals(12, list.size)
for (word in list) {
assertTrue(word in words)
} }
} }
@Test @Test
@Suppress("MaxLineLength") fun `12 not validating words generate seed that novacrypt generated`() {
fun `12 words generate expected seed`() {
assertEquals( assertEquals(
"64AA8C388EC0F3A13C7E51653BC766E30668D30952AB34381C4B174BF3278774" + "64AA8C388EC0F3A13C7E51653BC766E30668D30952AB34381C4B174BF3278774" +
"B4EE43D0BA08BCBCE0D0B806DEB7AA364A83525C34847078B2A8002A3E116066", "B4EE43D0BA08BCBCE0D0B806DEB7AA364A83525C34847078B2A8002A3E116066",
SeedCalculator(JavaxPBKDF2WithHmacSHA512.INSTANCE).calculateSeed( Mnemonics.MnemonicCode(
"write wrong yard year yellow you young youth zebra zero zone zoo", "" "write wrong yard year yellow you young youth zebra zero zone zoo"
).toHexString("") ).toSeed(validate = false).toHexString("")
) )
assertEquals( assertEquals(
"E911FAA42F389AA9F6D5A40B2ECB876B06D6D1FFBD5885C54720398EB11918CA" + "E911FAA42F389AA9F6D5A40B2ECB876B06D6D1FFBD5885C54720398EB11918CA" +
"B8F7BAD70FD5BE39BEB4EB065610700D1CFF1D4BFAA26F998357E15E79002779", "B8F7BAD70FD5BE39BEB4EB065610700D1CFF1D4BFAA26F998357E15E79002779",
SeedCalculator(JavaxPBKDF2WithHmacSHA512.INSTANCE).calculateSeed( Mnemonics.MnemonicCode(
"matrix lava they brand negative spray floor gym purity picture ritual disorder", "" "matrix lava they brand negative spray floor gym purity picture ritual disorder"
).toHexString("") ).toSeed(validate = false).toHexString("")
) )
assertEquals( assertEquals(
"DDB26091680CF30D0DC615546E4612327DB287B6B2B8B8947A3E12580315D38C" + "DDB26091680CF30D0DC615546E4612327DB287B6B2B8B8947A3E12580315D38C" +
"3BF7DD0EB4E9E50B10A41925332E0C8ED43C80DBA29281EF331A1DFA858BF1C9", "3BF7DD0EB4E9E50B10A41925332E0C8ED43C80DBA29281EF331A1DFA858BF1C9",
SeedCalculator(JavaxPBKDF2WithHmacSHA512.INSTANCE).calculateSeed( Mnemonics.MnemonicCode(
"middle rack south alert ribbon tube hope involve defy oxygen gloom rabbit", "" "middle rack south alert ribbon tube hope involve defy oxygen gloom rabbit"
).toHexString("") ).toSeed(validate = false).toHexString("")
) )
assertEquals( assertEquals(
"4815B580D0DCDA08334C92B3CB9A8436CD581C55841FB2794FB1E3D6E389F447" + "4815B580D0DCDA08334C92B3CB9A8436CD581C55841FB2794FB1E3D6E389F447" +
"C8C6520B2FE567720950F5B39BE7EC42C0BC98D3C63F8FEF642B5BD3EE4CDD7B", "C8C6520B2FE567720950F5B39BE7EC42C0BC98D3C63F8FEF642B5BD3EE4CDD7B",
SeedCalculator(JavaxPBKDF2WithHmacSHA512.INSTANCE).calculateSeed( Mnemonics.MnemonicCode(
"interest mask trial hold foot segment fade page monitor apple garden shuffle", "" "interest mask trial hold foot segment fade page monitor apple garden shuffle"
).toHexString("") ).toSeed(validate = false).toHexString("")
) )
assertEquals( assertEquals(
"FF462543D8FB9DAE6C17FA7BA047238664207FCC797D6688E10DD1B3CFD183D4" + "FF462543D8FB9DAE6C17FA7BA047238664207FCC797D6688E10DD1B3CFD183D4" +
"928AD088E8287B69BABCAEB0F87A2DFF2ADD49A7FDB7EB2554D7344F09C41A76", "928AD088E8287B69BABCAEB0F87A2DFF2ADD49A7FDB7EB2554D7344F09C41A76",
SeedCalculator(JavaxPBKDF2WithHmacSHA512.INSTANCE).calculateSeed( Mnemonics.MnemonicCode(
"palace glory gospel garment obscure person edge total hunt fix setup uphold\n", "" "palace glory gospel garment obscure person edge total hunt fix setup uphold\n"
).toHexString("") ).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",
}