Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#245] Release v1.0.7 #246

Merged
merged 8 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ and this library adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.0.7] - 2023-01-02

### Changed
- Gradle 8.5
- Kotlin 1.9.21
- Other dependency update

## [1.0.6] - 2023-09-27
- Gradle 8.3
Expand Down
340 changes: 171 additions & 169 deletions bip39-lib/gradle.lockfile

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ object Mnemonics {

class MnemonicCode(val chars: CharArray, val languageCode: String = DEFAULT_LANGUAGE_CODE) :
Closeable, Iterable<String> {

constructor(
phrase: String,
languageCode: String = DEFAULT_LANGUAGE_CODE
Expand All @@ -67,39 +66,42 @@ object Mnemonics {
* chars can be enough.
*/
val words: List<CharArray>
get() = ArrayList<CharArray>(wordCount).apply {
var cursor = 0
chars.forEachIndexed { i, c ->
if (c == ' ' || i == chars.lastIndex) {
add(chars.copyOfRange(cursor, if (chars[i].isWhitespace()) i else i + 1))
cursor = i + 1
get() =
ArrayList<CharArray>(wordCount).apply {
var cursor = 0
chars.forEachIndexed { i, c ->
if (c == ' ' || i == chars.lastIndex) {
add(chars.copyOfRange(cursor, if (chars[i].isWhitespace()) i else i + 1))
cursor = i + 1
}
}
}
}

fun clear() = chars.fill(0.toChar())

fun isEmpty() = chars.isEmpty()

override fun iterator(): Iterator<String> = object : Iterator<String> {
var cursor: Int = 0
override fun hasNext() = cursor < chars.size - 1
override fun iterator(): Iterator<String> =
object : Iterator<String> {
var cursor: Int = 0

override fun next(): String {
val nextSpaceIndex = nextSpaceIndex()
val word = chars.concatToString(cursor, cursor + (nextSpaceIndex - cursor))
cursor = nextSpaceIndex + 1
return word
}
override fun hasNext() = cursor < chars.size - 1

override fun next(): String {
val nextSpaceIndex = nextSpaceIndex()
val word = chars.concatToString(cursor, cursor + (nextSpaceIndex - cursor))
cursor = nextSpaceIndex + 1
return word
}

private fun nextSpaceIndex(): Int {
var i = cursor
while (i < chars.size - 1) {
if (chars[i].isWhitespace()) return i else i++
private fun nextSpaceIndex(): Int {
var i = cursor
while (i < chars.size - 1) {
if (chars[i].isWhitespace()) return i else i++
}
return chars.size
}
return chars.size
}
}

fun validate() {
// verify: word count is supported
Expand Down Expand Up @@ -195,7 +197,6 @@ object Mnemonics {
}

companion object {

/**
* Utility function to create a mnemonic code as a character array from the given
* entropy. Typically, new mnemonic codes are created starting with a WordCount
Expand Down Expand Up @@ -223,7 +224,10 @@ object Mnemonics {

// inner function that updates the index and copies a word after every 11 bits
// Note: the excess bits of the checksum are intentionally ignored, per BIP-39
fun processBit(bit: Boolean, chars: ArrayList<Char>) {
fun processBit(
bit: Boolean,
chars: ArrayList<Char>
) {
// update the index
index = index shl 1
if (bit) index = index or 1
Expand Down Expand Up @@ -272,7 +276,6 @@ object Mnemonics {
val bitLength = count / 3 * 32

companion object {

/**
* Convert a count into an instance of [WordCount].
*/
Expand Down Expand Up @@ -349,9 +352,10 @@ fun MnemonicCode.toSeed(
}
}

fun WordCount.toEntropy(): ByteArray = ByteArray(bitLength / 8).apply {
Mnemonics.secureRandom.nextBytes(this)
}
fun WordCount.toEntropy(): ByteArray =
ByteArray(bitLength / 8).apply {
Mnemonics.secureRandom.nextBytes(this)
}

//
// Private Extensions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import cash.z.ecc.android.bip39.Mnemonics.DEFAULT_LANGUAGE_CODE
* any code.
*/
class WordList internal constructor(val languageCode: String = DEFAULT_LANGUAGE_CODE) {

init {
validate(languageCode)
}
Expand All @@ -24,7 +23,6 @@ class WordList internal constructor(val languageCode: String = DEFAULT_LANGUAGE_
val words get() = _words

companion object {

private const val WORD_LIST_ELEMENT_COUNT = 2048

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package cash.z.ecc.android.crypto

internal expect class PBEKeySpecCommon(password: CharArray?, salt: ByteArray, iterationCount: Int, keyLength: Int) {

var password: CharArray?
private set
var salt: ByteArray?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package cash.z.ecc.android.crypto

internal expect object Pbkdf2Sha512 {

/**
* Generate a derived key from the given parameters.
*
Expand All @@ -10,5 +9,10 @@ internal expect object Pbkdf2Sha512 {
* @param c the iteration count
* @param dkLen the key length in bits
*/
fun derive(p: CharArray, s: ByteArray, c: Int, dkLen: Int): ByteArray
fun derive(
p: CharArray,
s: ByteArray,
c: Int,
dkLen: Int
): ByteArray
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ internal expect class SecretKeyFactoryCommon {

companion object {
fun getInstance(algorithm: String): SecretKeyFactoryCommon
fun getInstance(algorithm: String, provider: FallbackProvider): SecretKeyFactoryCommon

fun getInstance(
algorithm: String,
provider: FallbackProvider
): SecretKeyFactoryCommon
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ import io.kotest.matchers.shouldNotBe
private const val DEFAULT_LANGUAGE_CODE = "en"

class MnemonicsTest : BehaviorSpec({
val validPhrase = "void come effort suffer camp survey warrior heavy shoot primary clutch crush open amazing" +
" screen patrol group space point ten exist slush involve unfold"
val validPhrase =
"void come effort suffer camp survey warrior heavy shoot primary clutch crush open amazing" +
" screen patrol group space point ten exist slush involve unfold"

Given("a valid, known mnemonic phrase") {
When("it is converted into a seed") {
Expand Down Expand Up @@ -186,9 +187,10 @@ class MnemonicsTest : BehaviorSpec({
}
}
When("it contains an invalid word") {
val mnemonicPhrase = validPhrase.split(' ').let { words ->
validPhrase.replace(words[23], "convincee")
}
val mnemonicPhrase =
validPhrase.split(' ').let { words ->
validPhrase.replace(words[23], "convincee")
}
mnemonicPhrase.asClue {
Then("validate() fails with a word validation error") {
shouldThrow<Mnemonics.InvalidWordException> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ package cash.z.ecc.android.bip39.utils
fun ByteArray.toHex(): String {
val sb = StringBuilder(size * 2)
for (b in this) {
val hexValue = b.let { if (it >= 0) it.toInt() else 256 + it }
.toString(16)
.let { if (it.length < 2) "0$it" else it }
val hexValue =
b.let { if (it >= 0) it.toInt() else 256 + it }
.toString(16)
.let { if (it.length < 2) "0$it" else it }
sb.append(hexValue)
}
return sb.toString()
Expand All @@ -23,17 +24,21 @@ fun String.fromHex(): ByteArray {
return data
}

fun String.swap(srcWord: Int, destWord: Int = srcWord + 1): String {
fun String.swap(
srcWord: Int,
destWord: Int = srcWord + 1
): String {
require(srcWord < destWord) { "srcWord must be less than destWord" }
require(destWord <= count { it == ' ' }) { "there aren't that many words" }

return split(' ').let { words ->
words.reduceIndexed { i, result, word ->
val next = when (i) {
srcWord -> words[destWord]
destWord -> words[srcWord]
else -> word
}
val next =
when (i) {
srcWord -> words[destWord]
destWord -> words[srcWord]
else -> word
}
if (srcWord == 0 && i == 1) "${words[destWord]} $next" else "$result $next"
}
}
Expand Down
Loading