Replace novacrypt BIP39 library with the zcash one
which is MIT licensed instead of GPLv3 Change-Id: I30041de5ab1c1f8c7d5f57d6c60e28300a285259
This commit is contained in:
parent
fd4c92bd84
commit
05640ebb63
31 changed files with 231 additions and 129 deletions
|
@ -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",
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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]))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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é d’entrer ce mot.</string>
|
||||
<string name="recovery_code_input_hint_12">Mot 12</string>
|
||||
<string name="recovery_code_input_hint_11">Mot 11</string>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
5
libs/Android.bp
Normal 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
BIN
libs/kotlin-bip39-1.0.2.jar
Normal file
Binary file not shown.
|
@ -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",
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in a new issue