Browse Source
All tests now pass. The next step is to publish the library artifacts and then setup CI.master
Kevin Gorham
4 years ago
9 changed files with 3105 additions and 37 deletions
@ -1,2 +1,151 @@ |
|||
# android-bip39 |
|||
A concise implementation of BIP-0039 in Kotlin for Android. |
|||
[![license](https://img.shields.io/github/license/zcash/android-bip39.svg?maxAge=2592000&style=plastic)](https://github.com/zcash/android-bip39/blob/master/LICENSE) |
|||
[![CircleCI](https://img.shields.io/circleci/build/github/zcash/android-bip39/master?style=plastic)](https://circleci.com/gh/zcash/android-bip39/tree/master) |
|||
[![@gmale](https://img.shields.io/badge/contact-android@z.cash-5AA9E7.svg?style=plastic)](https://github.com/gmale) |
|||
![Bintray](https://img.shields.io/bintray/v/ecc-mobile/android-bip39/android-bip39?color=success&style=plastic) |
|||
[![Keybase ZEC](https://img.shields.io/keybase/zec/kevinecc?logoColor=red&style=social)](https://keybase.io/kevinecc) |
|||
|
|||
|
|||
## Introduction |
|||
A concise implementation of [BIP-0039](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) in Kotlin for Android. |
|||
|
|||
### Motivation |
|||
|
|||
* There are not many bip-39 implementations for android |
|||
* Most that do exist are not Kotlin |
|||
* or they are not idiomatic (because they are direct Java ports to Kotlin) |
|||
* or they have restrictive licenses |
|||
* No other implementation uses [CharArrays](https://stackoverflow.com/a/8881376/178433), from the ground up, for [added security](https://docs.oracle.com/javase/6/docs/technotes/guides/security/crypto/CryptoSpec.html#PBEEx) and lower chances of [accidentally logging](https://stackoverflow.com/a/8885343/178433) sensitive info. |
|||
|
|||
Consequently, this library strives to use both [idiomatic Kotlin](https://kotlinlang.org/docs/reference/idioms.html) and `CharArrays` whenever possible. It also aims to be concise and thoroughly tested. As a pure kotlin library, it probably also works outside of Android but that is not an explicit goal. |
|||
|
|||
Plus, it uses a permissive MIT license and no dependencies beyond Kotlin's stdlib! |
|||
|
|||
## Getting Started |
|||
### Gradle |
|||
|
|||
Add dependencies: |
|||
|
|||
```groovy |
|||
dependencies { |
|||
implementation 'cash.z.ecc.android:android-bip39:1.0.0-beta02' |
|||
} |
|||
|
|||
repository { |
|||
jcenter() |
|||
} |
|||
``` |
|||
*** |
|||
|
|||
## Usage |
|||
This library prefers `CharArrays` over `Strings` for [added security](https://stackoverflow.com/a/8881376/178433). |
|||
Note: If strings or lists are desired, it is very easy (but not recommended) to convert to/from a CharArray via `String(charArray)` or `String(charArray).split(' ')`. |
|||
* Create new 24-word mnemonic phrase |
|||
```kotlin |
|||
val mnemonicCode: MnemonicCode = MnemonicCode(WordCount.COUNT_24) |
|||
|
|||
// assert: mnemonicCode.wordCount == 24, mnemonicCode.languageCode == "en" |
|||
``` |
|||
* Generate seed |
|||
```kotlin |
|||
val seed: ByteArray = mnemonicCode.toSeed() |
|||
``` |
|||
* Generate seed from existing mnemonic |
|||
```kotlin |
|||
val preExistingPhraseString = "scheme spot photo card baby mountain device kick cradle pact join borrow" |
|||
val preExistingPhraseChars = validPhraseString.toCharArray() |
|||
|
|||
// from CharArray |
|||
seed = MnemonicCode(preExistingPhraseChars).toSeed() |
|||
|
|||
// from String |
|||
seed = MnemonicCode(preExistingPhraseString).toSeed() |
|||
``` |
|||
* Generate seed with passphrase |
|||
```kotlin |
|||
// normal way |
|||
val passphrase = "bitcoin".toCharArray() |
|||
mnemonicCode.toSeed(passphrase) |
|||
|
|||
// more private way (erase at the end) |
|||
charArrayOf('z', 'c', 'a', 's', 'h').let { passphrase -> |
|||
mnemonicCode.toSeed(passphrase) |
|||
passphrase.fill('0') // erased! |
|||
} |
|||
``` |
|||
* Generate raw entropy for a corresponding word count |
|||
```kotlin |
|||
val entropy: ByteArray = WordCount.COUNT_18.toEntropy() |
|||
|
|||
// this can be used to directly generate a mnemonic: |
|||
val mnemonicCode = MnemonicCode(entropy) |
|||
|
|||
// note: that gives the same result as calling: |
|||
MnemonicCode(WordCount.COUNT_18) |
|||
``` |
|||
* Validate pre-existing or user-provided mnemonic |
|||
(NOTE: mnemonics generated by the library "from scratch" are valid, by definition) |
|||
```kotlin |
|||
// throws a typed exception when invalid: |
|||
// ChecksumException - when checksum fails, usually meaning words are swapped |
|||
// WordCountException(count) - invalid number of words |
|||
// InvalidWordException(word) - contains a word not found on the list |
|||
mnemonicCode.validate() |
|||
``` |
|||
* Iterate over words |
|||
```kotlin |
|||
// mnemonicCodes are iterable |
|||
for (word in mnemonicCode) { |
|||
println(word) |
|||
} |
|||
|
|||
mnemonicCode.forEach { word -> |
|||
println(word) |
|||
} |
|||
``` |
|||
* Clean up! |
|||
```kotlin |
|||
mnemonicCode.clear() // code words are deleted and no longer available for attacker |
|||
``` |
|||
#### Advanced Usage |
|||
These generated codes are compatible with kotlin's [scoped resource usage](https://kotlinlang.org/docs/tutorials/kotlin-for-py/scoped-resource-usage.html) |
|||
* Leverage `use` to automatically clean-up after use |
|||
```kotlin |
|||
MnemonicCode(WordCount.COUNT_24).use { |
|||
// Do something with the words (wordCount == 24) |
|||
} |
|||
// memory has been cleared at this point (wordCount == 0) |
|||
``` |
|||
* Generate original entropy that was used to create the mnemonic |
|||
(or throw exception if the mnemonic is invalid). |
|||
* Note: Calling this function only succeeds when the entropy is valid so it also can be used, indirectly, for validation. In fact, currently, it is called as part of the `MnemonicCode::validate()` function. |
|||
```kotlin |
|||
val entropy: ByteArray = MnemonicCode(preExistingPhraseString).toEntropy() |
|||
``` |
|||
* Mnemonics generated by the library do not need to be validated while creating the corresponding seed. That step can be skipped for a little added speed and security (because validation generates strings on the heap--which might get improved in a future release). |
|||
```kotlin |
|||
seed = MnemonicCode(WordCount.COUNT_24).toSeed(validate = false) |
|||
``` |
|||
* Other languages are not yet supported but the API for them is in place. It accepts any `ISO 639-1` language code. For now, using it with anything other than "en" will result in an `UnsupportedOperationException`. |
|||
```kotlin |
|||
// results in exception, for now |
|||
val mnemonicCode = MnemonicCode(WordCount.COUNT_24, languageCode = Locale.GERMAN.language) |
|||
|
|||
// english is the only language that doesn't crash |
|||
val mnemonicCode = MnemonicCode(WordCount.COUNT_24, languageCode = Locale.ENGLISH.language) |
|||
``` |
|||
|
|||
## Test Results |
|||
![Screenshot from 2020-06-04 04-05-37](https://user-images.githubusercontent.com/1699841/83732898-ba1c5180-a61a-11ea-92a5-16397a1660e7.png) |
|||
|
|||
|
|||
## Credits |
|||
* [zcash/ebfull](https://github.com/ebfull) - zcash core dev and BIP-0039 co-author who inspired me to create this library |
|||
* [bitcoinj](https://github.com/bitcoinj/bitcoinj/blob/master/core/src/main/java/org/bitcoinj/crypto/MnemonicCode.java) - Java implementation from which much of this code was adapted |
|||
* [Trezor](https://github.com/trezor/python-mnemonic/blob/master/vectors.json) - for their OG [test data set](https://github.com/trezor/python-mnemonic/blob/master/vectors.json) that has excellent edge cases |
|||
* [Cole Barnes](http://cryptofreek.org/2012/11/29/pbkdf2-pure-java-implementation/) - whose PBKDF2SHA512 Java implementation is floating around _everywhere_ online |
|||
* [Ken Sedgwick](https://github.com/ksedgwic) - who adapted Cole Barnes' work to use SHA-512 |
|||
|
|||
## License |
|||
MIT |
|||
|
|||
|
@ -0,0 +1,127 @@ |
|||
package cash.z.ecc.android.bip39 |
|||
|
|||
/* |
|||
* Copyright (c) 2012 Cole Barnes [cryptofreek{at}gmail{dot}com] |
|||
* |
|||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
* of this software and associated documentation files (the "Software"), to deal |
|||
* in the Software without restriction, including without limitation the rights |
|||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
* copies of the Software, and to permit persons to whom the Software is |
|||
* furnished to do so, subject to the following conditions: |
|||
* |
|||
* The above copyright notice and this permission notice shall be included in |
|||
* all copies or substantial portions of the Software. |
|||
* |
|||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
* SOFTWARE. |
|||
* |
|||
*/ |
|||
|
|||
import java.io.ByteArrayOutputStream |
|||
import java.nio.ByteBuffer |
|||
import java.nio.ByteOrder |
|||
import javax.crypto.Mac |
|||
import javax.crypto.spec.SecretKeySpec |
|||
import kotlin.experimental.xor |
|||
|
|||
|
|||
/** |
|||
* |
|||
* This is a clean-room implementation of PBKDF2 using RFC 2898 as a reference. |
|||
* |
|||
* |
|||
* RFC 2898: http://tools.ietf.org/html/rfc2898#section-5.2 |
|||
* |
|||
* |
|||
* This code passes all RFC 6070 test vectors: http://tools.ietf.org/html/rfc6070 |
|||
* |
|||
* |
|||
* http://cryptofreek.org/2012/11/29/pbkdf2-pure-java-implementation/<br></br> |
|||
* Modified to use SHA-512 - Ken Sedgwick ken@bonsai.com |
|||
*/ |
|||
object Pbkdf2Sha256 { |
|||
fun derive(P: CharArray, S: CharArray, c: Int, dkLen: Int): ByteArray { |
|||
val baos = ByteArrayOutputStream() |
|||
|
|||
try { |
|||
val hLen = 20 |
|||
|
|||
if (dkLen > (Math.pow(2.0, 32.0) - 1) * hLen) { |
|||
throw IllegalArgumentException("derived key too long") |
|||
} else { |
|||
val l = Math.ceil(dkLen.toDouble() / hLen.toDouble()).toInt() |
|||
// int r = dkLen - (l-1)*hLen; |
|||
|
|||
for (i in 1..l) { |
|||
val T = F(P, S, c, i) |
|||
baos.write(T!!) |
|||
} |
|||
} |
|||
} catch (e: Exception) { |
|||
throw RuntimeException(e) |
|||
} |
|||
|
|||
val baDerived = ByteArray(dkLen) |
|||
System.arraycopy(baos.toByteArray(), 0, baDerived, 0, baDerived.size) |
|||
|
|||
return baDerived |
|||
} |
|||
|
|||
@Throws(Exception::class) |
|||
private fun F(P: CharArray, S: CharArray, c: Int, i: Int): ByteArray? { |
|||
var U_LAST: ByteArray? = null |
|||
var U_XOR: ByteArray? = null |
|||
|
|||
val pBytes = ByteArray(P.size).apply { |
|||
P.forEachIndexed { i, c -> this[i] = c.toByte() } |
|||
} |
|||
|
|||
val sBytes = ByteArray(S.size).apply { |
|||
S.forEachIndexed { i, c -> this[i] = c.toByte() } |
|||
} |
|||
|
|||
val key = SecretKeySpec(pBytes, "HmacSHA512") |
|||
val mac = Mac.getInstance(key.algorithm) |
|||
mac.init(key) |
|||
|
|||
for (j in 0 until c) { |
|||
if (j == 0) { |
|||
val baS = sBytes |
|||
val baI = INT(i) |
|||
val baU = ByteArray(baS.size + baI.size) |
|||
|
|||
System.arraycopy(baS, 0, baU, 0, baS.size) |
|||
System.arraycopy(baI, 0, baU, baS.size, baI.size) |
|||
|
|||
U_XOR = mac.doFinal(baU) |
|||
U_LAST = U_XOR |
|||
mac.reset() |
|||
} else { |
|||
val baU = mac.doFinal(U_LAST) |
|||
mac.reset() |
|||
|
|||
for (k in U_XOR!!.indices) { |
|||
U_XOR[k] = (U_XOR[k].xor(baU[k])) |
|||
} |
|||
|
|||
U_LAST = baU |
|||
} |
|||
} |
|||
|
|||
return U_XOR |
|||
} |
|||
|
|||
private fun INT(i: Int): ByteArray { |
|||
val bb = ByteBuffer.allocate(4) |
|||
bb.order(ByteOrder.BIG_ENDIAN) |
|||
bb.putInt(i) |
|||
|
|||
return bb.array() |
|||
} |
|||
} |
File diff suppressed because it is too large
@ -0,0 +1,108 @@ |
|||
package cash.z.ecc.android.bip39 |
|||
|
|||
import cash.z.ecc.android.bip39.Mnemonics.MnemonicCode |
|||
import cash.z.ecc.android.bip39.Mnemonics.WordCount |
|||
import io.kotest.assertions.asClue |
|||
import io.kotest.assertions.throwables.shouldNotThrowAny |
|||
import io.kotest.core.spec.style.ShouldSpec |
|||
import io.kotest.matchers.shouldBe |
|||
import io.kotest.matchers.string.shouldContain |
|||
|
|||
class ReadmeExamplesTest : ShouldSpec({ |
|||
val validPhrase = |
|||
"still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread" |
|||
val validPhraseChars = validPhrase.toCharArray() |
|||
context("Example: Create 24-word mnemonic phrase") { |
|||
val mnemonicCode = MnemonicCode(WordCount.COUNT_24) |
|||
should("result in a valid 24-word phrase") { |
|||
mnemonicCode.wordCount shouldBe 24 |
|||
} |
|||
should("result in a valid phrase"){ |
|||
shouldNotThrowAny { |
|||
mnemonicCode.validate() |
|||
} |
|||
} |
|||
} |
|||
context("Example: Generate seed") { |
|||
val mnemonicCode = MnemonicCode(WordCount.COUNT_24) |
|||
should("result in a valid 24-word phrase") { |
|||
mnemonicCode.toSeed() |
|||
mnemonicCode.wordCount shouldBe 24 |
|||
} |
|||
should("result in a valid phrase"){ |
|||
shouldNotThrowAny { |
|||
mnemonicCode.validate() |
|||
} |
|||
} |
|||
} |
|||
context("Example: Generate seed from existing mnemonic chars") { |
|||
val mnemonicCode = MnemonicCode(validPhrase.toCharArray()) |
|||
should("result in a valid 24-word phrase") { |
|||
mnemonicCode.toSeed() |
|||
mnemonicCode.wordCount shouldBe 24 |
|||
} |
|||
should("result in a valid phrase"){ |
|||
shouldNotThrowAny { |
|||
mnemonicCode.validate() |
|||
} |
|||
} |
|||
} |
|||
context("Example: Generate seed from existing mnemonic string") { |
|||
val mnemonicCode = MnemonicCode(validPhrase) |
|||
should("result in a valid 24-word phrase") { |
|||
mnemonicCode.toSeed() |
|||
mnemonicCode.wordCount shouldBe 24 |
|||
} |
|||
should("result in a valid phrase"){ |
|||
shouldNotThrowAny { |
|||
mnemonicCode.validate() |
|||
} |
|||
} |
|||
} |
|||
context("Example: Generate seed with passphrase") { |
|||
val passphrase = "bitcoin".toCharArray() |
|||
should("'normal way' results in a 64 byte seed") { |
|||
val seed = MnemonicCode(validPhrase).toSeed(passphrase) |
|||
seed.size shouldBe 64 |
|||
} |
|||
should("'private way' results in a 64 byte seed") { |
|||
var seed: ByteArray |
|||
charArrayOf('z', 'c', 'a', 's', 'h').let { passphrase -> |
|||
seed = MnemonicCode(validPhrase).toSeed(passphrase) |
|||
String(passphrase) shouldBe "zcash" |
|||
passphrase.fill('0') |
|||
String(passphrase) shouldBe "00000" |
|||
} |
|||
seed.size shouldBe 64 |
|||
} |
|||
} |
|||
context("Example: Iterate over mnemonic codes") { |
|||
val mnemonicCode = MnemonicCode(validPhrase) |
|||
should("work in a for loop") { |
|||
var count = 0 |
|||
for (word in mnemonicCode) { |
|||
count++ |
|||
validPhrase shouldContain word |
|||
} |
|||
count shouldBe 24 |
|||
} |
|||
should("work with forEach"){ |
|||
var count = 0 |
|||
mnemonicCode.forEach { word -> |
|||
count++ |
|||
validPhrase shouldContain word |
|||
} |
|||
count shouldBe 24 |
|||
} |
|||
} |
|||
context("Example: auto-clear") { |
|||
should("clear the mnemonic when done") { |
|||
val mnemonicCode = MnemonicCode(WordCount.COUNT_24) |
|||
mnemonicCode.use { |
|||
mnemonicCode.wordCount shouldBe 24 |
|||
} |
|||
// content gets automatically cleared after use! |
|||
mnemonicCode.wordCount shouldBe 0 |
|||
} |
|||
} |
|||
}) |
@ -0,0 +1,148 @@ |
|||
{ |
|||
"english": [ |
|||
[ |
|||
"00000000000000000000000000000000", |
|||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about", |
|||
"c55257c360c07c72029aebc1b53c05ed0362ada38ead3e3e9efa3708e53495531f09a6987599d18264c1e1c92f2cf141630c7a3c4ab7c81b2f001698e7463b04", |
|||
"xprv9s21ZrQH143K3h3fDYiay8mocZ3afhfULfb5GX8kCBdno77K4HiA15Tg23wpbeF1pLfs1c5SPmYHrEpTuuRhxMwvKDwqdKiGJS9XFKzUsAF" |
|||
], |
|||
[ |
|||
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", |
|||
"legal winner thank year wave sausage worth useful legal winner thank yellow", |
|||
"2e8905819b8723fe2c1d161860e5ee1830318dbf49a83bd451cfb8440c28bd6fa457fe1296106559a3c80937a1c1069be3a3a5bd381ee6260e8d9739fce1f607", |
|||
"xprv9s21ZrQH143K2gA81bYFHqU68xz1cX2APaSq5tt6MFSLeXnCKV1RVUJt9FWNTbrrryem4ZckN8k4Ls1H6nwdvDTvnV7zEXs2HgPezuVccsq" |
|||
], |
|||
[ |
|||
"80808080808080808080808080808080", |
|||
"letter advice cage absurd amount doctor acoustic avoid letter advice cage above", |
|||
"d71de856f81a8acc65e6fc851a38d4d7ec216fd0796d0a6827a3ad6ed5511a30fa280f12eb2e47ed2ac03b5c462a0358d18d69fe4f985ec81778c1b370b652a8", |
|||
"xprv9s21ZrQH143K2shfP28KM3nr5Ap1SXjz8gc2rAqqMEynmjt6o1qboCDpxckqXavCwdnYds6yBHZGKHv7ef2eTXy461PXUjBFQg6PrwY4Gzq" |
|||
], |
|||
[ |
|||
"ffffffffffffffffffffffffffffffff", |
|||
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong", |
|||
"ac27495480225222079d7be181583751e86f571027b0497b5b5d11218e0a8a13332572917f0f8e5a589620c6f15b11c61dee327651a14c34e18231052e48c069", |
|||
"xprv9s21ZrQH143K2V4oox4M8Zmhi2Fjx5XK4Lf7GKRvPSgydU3mjZuKGCTg7UPiBUD7ydVPvSLtg9hjp7MQTYsW67rZHAXeccqYqrsx8LcXnyd" |
|||
], |
|||
[ |
|||
"000000000000000000000000000000000000000000000000", |
|||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent", |
|||
"035895f2f481b1b0f01fcf8c289c794660b289981a78f8106447707fdd9666ca06da5a9a565181599b79f53b844d8a71dd9f439c52a3d7b3e8a79c906ac845fa", |
|||
"xprv9s21ZrQH143K3mEDrypcZ2usWqFgzKB6jBBx9B6GfC7fu26X6hPRzVjzkqkPvDqp6g5eypdk6cyhGnBngbjeHTe4LsuLG1cCmKJka5SMkmU" |
|||
], |
|||
[ |
|||
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", |
|||
"legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will", |
|||
"f2b94508732bcbacbcc020faefecfc89feafa6649a5491b8c952cede496c214a0c7b3c392d168748f2d4a612bada0753b52a1c7ac53c1e93abd5c6320b9e95dd", |
|||
"xprv9s21ZrQH143K3Lv9MZLj16np5GzLe7tDKQfVusBni7toqJGcnKRtHSxUwbKUyUWiwpK55g1DUSsw76TF1T93VT4gz4wt5RM23pkaQLnvBh7" |
|||
], |
|||
[ |
|||
"808080808080808080808080808080808080808080808080", |
|||
"letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always", |
|||
"107d7c02a5aa6f38c58083ff74f04c607c2d2c0ecc55501dadd72d025b751bc27fe913ffb796f841c49b1d33b610cf0e91d3aa239027f5e99fe4ce9e5088cd65", |
|||
"xprv9s21ZrQH143K3VPCbxbUtpkh9pRG371UCLDz3BjceqP1jz7XZsQ5EnNkYAEkfeZp62cDNj13ZTEVG1TEro9sZ9grfRmcYWLBhCocViKEJae" |
|||
], |
|||
[ |
|||
"ffffffffffffffffffffffffffffffffffffffffffffffff", |
|||
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when", |
|||
"0cd6e5d827bb62eb8fc1e262254223817fd068a74b5b449cc2f667c3f1f985a76379b43348d952e2265b4cd129090758b3e3c2c49103b5051aac2eaeb890a528", |
|||
"xprv9s21ZrQH143K36Ao5jHRVhFGDbLP6FCx8BEEmpru77ef3bmA928BxsqvVM27WnvvyfWywiFN8K6yToqMaGYfzS6Db1EHAXT5TuyCLBXUfdm" |
|||
], |
|||
[ |
|||
"0000000000000000000000000000000000000000000000000000000000000000", |
|||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art", |
|||
"bda85446c68413707090a52022edd26a1c9462295029f2e60cd7c4f2bbd3097170af7a4d73245cafa9c3cca8d561a7c3de6f5d4a10be8ed2a5e608d68f92fcc8", |
|||
"xprv9s21ZrQH143K32qBagUJAMU2LsHg3ka7jqMcV98Y7gVeVyNStwYS3U7yVVoDZ4btbRNf4h6ibWpY22iRmXq35qgLs79f312g2kj5539ebPM" |
|||
], |
|||
[ |
|||
"7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f", |
|||
"legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title", |
|||
"bc09fca1804f7e69da93c2f2028eb238c227f2e9dda30cd63699232578480a4021b146ad717fbb7e451ce9eb835f43620bf5c514db0f8add49f5d121449d3e87", |
|||
"xprv9s21ZrQH143K3Y1sd2XVu9wtqxJRvybCfAetjUrMMco6r3v9qZTBeXiBZkS8JxWbcGJZyio8TrZtm6pkbzG8SYt1sxwNLh3Wx7to5pgiVFU" |
|||
], |
|||
[ |
|||
"8080808080808080808080808080808080808080808080808080808080808080", |
|||
"letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless", |
|||
"c0c519bd0e91a2ed54357d9d1ebef6f5af218a153624cf4f2da911a0ed8f7a09e2ef61af0aca007096df430022f7a2b6fb91661a9589097069720d015e4e982f", |
|||
"xprv9s21ZrQH143K3CSnQNYC3MqAAqHwxeTLhDbhF43A4ss4ciWNmCY9zQGvAKUSqVUf2vPHBTSE1rB2pg4avopqSiLVzXEU8KziNnVPauTqLRo" |
|||
], |
|||
[ |
|||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", |
|||
"zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote", |
|||
"dd48c104698c30cfe2b6142103248622fb7bb0ff692eebb00089b32d22484e1613912f0a5b694407be899ffd31ed3992c456cdf60f5d4564b8ba3f05a69890ad", |
|||
"xprv9s21ZrQH143K2WFF16X85T2QCpndrGwx6GueB72Zf3AHwHJaknRXNF37ZmDrtHrrLSHvbuRejXcnYxoZKvRquTPyp2JiNG3XcjQyzSEgqCB" |
|||
], |
|||
[ |
|||
"9e885d952ad362caeb4efe34a8e91bd2", |
|||
"ozone drill grab fiber curtain grace pudding thank cruise elder eight picnic", |
|||
"274ddc525802f7c828d8ef7ddbcdc5304e87ac3535913611fbbfa986d0c9e5476c91689f9c8a54fd55bd38606aa6a8595ad213d4c9c9f9aca3fb217069a41028", |
|||
"xprv9s21ZrQH143K2oZ9stBYpoaZ2ktHj7jLz7iMqpgg1En8kKFTXJHsjxry1JbKH19YrDTicVwKPehFKTbmaxgVEc5TpHdS1aYhB2s9aFJBeJH" |
|||
], |
|||
[ |
|||
"6610b25967cdcca9d59875f5cb50b0ea75433311869e930b", |
|||
"gravity machine north sort system female filter attitude volume fold club stay feature office ecology stable narrow fog", |
|||
"628c3827a8823298ee685db84f55caa34b5cc195a778e52d45f59bcf75aba68e4d7590e101dc414bc1bbd5737666fbbef35d1f1903953b66624f910feef245ac", |
|||
"xprv9s21ZrQH143K3uT8eQowUjsxrmsA9YUuQQK1RLqFufzybxD6DH6gPY7NjJ5G3EPHjsWDrs9iivSbmvjc9DQJbJGatfa9pv4MZ3wjr8qWPAK" |
|||
], |
|||
[ |
|||
"68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c", |
|||
"hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length", |
|||
"64c87cde7e12ecf6704ab95bb1408bef047c22db4cc7491c4271d170a1b213d20b385bc1588d9c7b38f1b39d415665b8a9030c9ec653d75e65f847d8fc1fc440", |
|||
"xprv9s21ZrQH143K2XTAhys3pMNcGn261Fi5Ta2Pw8PwaVPhg3D8DWkzWQwjTJfskj8ofb81i9NP2cUNKxwjueJHHMQAnxtivTA75uUFqPFeWzk" |
|||
], |
|||
[ |
|||
"c0ba5a8e914111210f2bd131f3d5e08d", |
|||
"scheme spot photo card baby mountain device kick cradle pact join borrow", |
|||
"ea725895aaae8d4c1cf682c1bfd2d358d52ed9f0f0591131b559e2724bb234fca05aa9c02c57407e04ee9dc3b454aa63fbff483a8b11de949624b9f1831a9612", |
|||
"xprv9s21ZrQH143K3FperxDp8vFsFycKCRcJGAFmcV7umQmcnMZaLtZRt13QJDsoS5F6oYT6BB4sS6zmTmyQAEkJKxJ7yByDNtRe5asP2jFGhT6" |
|||
], |
|||
[ |
|||
"6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3", |
|||
"horn tenant knee talent sponsor spell gate clip pulse soap slush warm silver nephew swap uncle crack brave", |
|||
"fd579828af3da1d32544ce4db5c73d53fc8acc4ddb1e3b251a31179cdb71e853c56d2fcb11aed39898ce6c34b10b5382772db8796e52837b54468aeb312cfc3d", |
|||
"xprv9s21ZrQH143K3R1SfVZZLtVbXEB9ryVxmVtVMsMwmEyEvgXN6Q84LKkLRmf4ST6QrLeBm3jQsb9gx1uo23TS7vo3vAkZGZz71uuLCcywUkt" |
|||
], |
|||
[ |
|||
"9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863", |
|||
"panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed nation flash devote level hobby quick inner drive ghost inside", |
|||
"72be8e052fc4919d2adf28d5306b5474b0069df35b02303de8c1729c9538dbb6fc2d731d5f832193cd9fb6aeecbc469594a70e3dd50811b5067f3b88b28c3e8d", |
|||
"xprv9s21ZrQH143K2WNnKmssvZYM96VAr47iHUQUTUyUXH3sAGNjhJANddnhw3i3y3pBbRAVk5M5qUGFr4rHbEWwXgX4qrvrceifCYQJbbFDems" |
|||
], |
|||
[ |
|||
"23db8160a31d3e0dca3688ed941adbf3", |
|||
"cat swing flag economy stadium alone churn speed unique patch report train", |
|||
"deb5f45449e615feff5640f2e49f933ff51895de3b4381832b3139941c57b59205a42480c52175b6efcffaa58a2503887c1e8b363a707256bdd2b587b46541f5", |
|||
"xprv9s21ZrQH143K4G28omGMogEoYgDQuigBo8AFHAGDaJdqQ99QKMQ5J6fYTMfANTJy6xBmhvsNZ1CJzRZ64PWbnTFUn6CDV2FxoMDLXdk95DQ" |
|||
], |
|||
[ |
|||
"8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0", |
|||
"light rule cinnamon wrap drastic word pride squirrel upgrade then income fatal apart sustain crack supply proud access", |
|||
"4cbdff1ca2db800fd61cae72a57475fdc6bab03e441fd63f96dabd1f183ef5b782925f00105f318309a7e9c3ea6967c7801e46c8a58082674c860a37b93eda02", |
|||
"xprv9s21ZrQH143K3wtsvY8L2aZyxkiWULZH4vyQE5XkHTXkmx8gHo6RUEfH3Jyr6NwkJhvano7Xb2o6UqFKWHVo5scE31SGDCAUsgVhiUuUDyh" |
|||
], |
|||
[ |
|||
"066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad", |
|||
"all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform", |
|||
"26e975ec644423f4a4c4f4215ef09b4bd7ef924e85d1d17c4cf3f136c2863cf6df0a475045652c57eb5fb41513ca2a2d67722b77e954b4b3fc11f7590449191d", |
|||
"xprv9s21ZrQH143K3rEfqSM4QZRVmiMuSWY9wugscmaCjYja3SbUD3KPEB1a7QXJoajyR2T1SiXU7rFVRXMV9XdYVSZe7JoUXdP4SRHTxsT1nzm" |
|||
], |
|||
[ |
|||
"f30f8c1da665478f49b001d94c5fc452", |
|||
"vessel ladder alter error federal sibling chat ability sun glass valve picture", |
|||
"2aaa9242daafcee6aa9d7269f17d4efe271e1b9a529178d7dc139cd18747090bf9d60295d0ce74309a78852a9caadf0af48aae1c6253839624076224374bc63f", |
|||
"xprv9s21ZrQH143K2QWV9Wn8Vvs6jbqfF1YbTCdURQW9dLFKDovpKaKrqS3SEWsXCu6ZNky9PSAENg6c9AQYHcg4PjopRGGKmdD313ZHszymnps" |
|||
], |
|||
[ |
|||
"c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05", |
|||
"scissors invite lock maple supreme raw rapid void congress muscle digital elegant little brisk hair mango congress clump", |
|||
"7b4a10be9d98e6cba265566db7f136718e1398c71cb581e1b2f464cac1ceedf4f3e274dc270003c670ad8d02c4558b2f8e39edea2775c9e232c7cb798b069e88", |
|||
"xprv9s21ZrQH143K4aERa2bq7559eMCCEs2QmmqVjUuzfy5eAeDX4mqZffkYwpzGQRE2YEEeLVRoH4CSHxianrFaVnMN2RYaPUZJhJx8S5j6puX" |
|||
], |
|||
[ |
|||
"f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f", |
|||
"void come effort suffer camp survey warrior heavy shoot primary clutch crush open amazing screen patrol group space point ten exist slush involve unfold", |
|||
"01f5bced59dec48e362f2c45b5de68b9fd6c92c6634f44d6d40aab69056506f0e35524a518034ddc1192e1dacd32c1ed3eaa3c3b131c88ed8e7e54c49a5d0998", |
|||
"xprv9s21ZrQH143K39rnQJknpH1WEPFJrzmAqqasiDcVrNuk926oizzJDDQkdiTvNPr2FYDYzWgiMiC63YmfPAa2oPyNB23r2g7d1yiK6WpqaQS" |
|||
] |
|||
] |
|||
} |
Loading…
Reference in new issue