Giter VIP home page Giter VIP logo

kotlin-bip39's Introduction

kotlin-bip39

license maven

Introduction

A concise implementation of BIP-0039 in Kotlin for Android.

Only about 30kB in total size. For comparison, the entire library is about 3X the size of this README file (because there are no dependencies)!

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
  • Most implementations fail to validate the checksum, which can easily lead to loss of funds!
    • validating the checksum prevents: leading/trailing white space, valid words in the wrong order, mistyping a valid word (like chief instead of chef) and other similar issues that could invalidate a backup or lose funds.
  • No other implementation uses CharArrays, from the ground up, for added security and lower chances of accidentally logging sensitive info.

Consequently, this library strives to use both idiomatic Kotlin 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 (Update: confirmed to also work on a Ktor server).

Plus, it uses a permissive MIT license and no dependencies beyond Kotlin's stdlib!

Getting Started

Gradle

Add dependencies (see Maven badge above for latest version number):

dependencies {
    implementation "cash.z.ecc.android:kotlin-bip39:${latestVersion}"
}

repository {
    mavenCentral()
}

Usage

This library prefers CharArrays over Strings for added security.
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
import cash.z.ecc.android.bip39.Mnemonics.MnemonicCode

val mnemonicCode: MnemonicCode = MnemonicCode(WordCount.COUNT_24)

// assert: mnemonicCode.wordCount == 24, mnemonicCode.languageCode == "en"
  • Generate seed
val seed: ByteArray = mnemonicCode.toSeed()
  • Generate seed from existing mnemonic
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
// 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
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)
// 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
// mnemonicCodes are iterable
for (word in mnemonicCode) {
    println(word)
}

mnemonicCode.forEach { word ->
    println(word)
}
  • Clean up!
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

  • Leverage use to automatically clean-up after use
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.
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).
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.
// 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)

Known issues

  • When publishing the library, a Gradle warning will be printed. This is a known issue in Kotlin Multiplatform and can be safely ignored.

Credits

  • zcash/ebfull - Zcash core dev and BIP-0039 co-author who inspired creation of this library
  • bitcoinj - Java implementation from which much of this code was adapted
  • Trezor - for their OG test data set that has excellent edge cases
  • Cole Barnes - whose PBKDF2SHA512 Java implementation is floating around everywhere online
  • Ken Sedgwick - who adapted Cole Barnes' work to use SHA-512

kotlin-bip39's People

Contributors

burnoo avatar ccjernigan avatar dependabot[bot] avatar gasstan avatar gmale avatar honzar avatar luca992 avatar pacu avatar rex4539 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

kotlin-bip39's Issues

Update readme to remove reference to Jcenter

The artifact is published in Maven Central, so the readme can be updated to reflect this. There are two changes:

  • Replace jcenter with maven central
  • Bump the version in the readme, since maven central only has a slightly newer version.

Block plugins from adding maven repositories

Is your feature request related to a problem? Please describe.

When Gradle plugins are applied, they can define their own Maven repositories. This could allow dependencies to come from sources that we don't trust.

Describe the solution you'd like

In settings.gradle.kts, configure

repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)

This is likely blocked until this is resolved: https://youtrack.jetbrains.com/issue/KT-52383/Kotlin-Multiplatform-Gradle-Plugin-Unable-to-disable-project-rep

Convert build scripts to kts

For consistency with our other Gradle and Kotlin projects, convert our build scripts to kts.

This should likely be implemented after #15 and #16 since those have some syntax changes for build scripts.

Configure Dependabot for GitHub Actions Versions

Is your feature request related to a problem? Please describe.

It is annoying to keep GitHub Actions dependencies updated.

Describe the solution you'd like

Although dependabot does not support Gradle yet, it does support GitHub Actions. So we can configure dependabot to help with this.

Switch to Adoptium JDK

Is your feature request related to a problem? Please describe.

Gradle toolchains only support Adoptium JDKs, however we have been recommending Zulu. This means different developers may have inconsistent JVMs. Although they should be compatible, it would be great to eliminate this variability in builds.

Describe the solution you'd like

Now that Adoptium publishes a Mac ARM JDK 11, we can remove using Zulu.

  • Update readme to remove references to Azul
  • Update GitHub Actions to use Adoptium

Configure Deployment through GitHub Actions

Is your feature request related to a problem? Please describe.

In order to be able to release updates to the library, we should have automated deployments.

Describe the solution you'd like

Create an automated deployment with GitHub Actions, similar to the deployment configured in the Zcash Android SDK.

Steps that need to be completed:

  1. Add markdown documentation on how CI works in this repo
  2. Add documentation on how to consume snapshot deployments
  3. Add secrets to GitHub for the main branch deployments. The necessary secrets are defined here: https://github.com/zcash/kotlin-bip39/blob/main/.github/workflows/deploy-release.yml
  4. Test the deployments

Configure for included builds

Is your feature request related to a problem? Please describe.

When doing multiple included builds with build-conventions, things can break when modules or conventions have the same name.

This will be a problem if trying to use an included build for both bip-39 and the Android SDK.

Describe the solution you'd like

Rename the build-conventions folder to have a namespace.

Coroutines 1.6.3

For a Gradle dependency:

  1. Update the dependency version in the root gradle.properties
  2. Update the dependency locks
    1. For Gradle dependencies: ./gradlew resolveAll --write-locks
  3. Verify no unexpected entries appear in the lockfiles. A supply chain attack could occur during this stage. The lockfile narrows the supply chain attack window to this very moment (as opposed to every time a build occurs).
  4. Are there any new APIs or possible migrations for this dependency?
    1. Minor update
    2. https://github.com/Kotlin/kotlinx.coroutines/releases/tag/1.6.3

Configure Code Coverage Reports

Is your feature request related to a problem? Please describe.

Developers have a hard time understanding how well the tests cover the library.

Describe the solution you'd like

Introduce code coverage, most likely with the JetBrains Kover Gradle plugin.

Alternatives you've considered

We can manually apply Jacoco to generate coverage, although Kover will be more robust.

Duplicate Gradle Wrapper

There's a duplicate copy of the Gradle wrapper under lib/. We just need the copies of these in the repo root. To resolve this, delete the following paths:

  • lib/gradle
  • lib/gradlew
  • lib/gradlew.bat

MnemonicCode Doesn't Implement Iterator Interface Correctly

Running detekt points out:

kotlin-bip39/lib/src/jvmMain/kotlin/cash/z/ecc/android/bip39/Mnemonics.kt:85:53: This implementation of Iterator does not correctly implement the next() method as it doesn't throw a NoSuchElementException when no elements remain in the Iterator. [IteratorNotThrowingNoSuchElementException]

Note that the detekt baseline will suppress this warning. When implementing this change, be sure to update the detekt baseline file to remove this suppression.

Improve performance initializing Mnemonics

When Mnemonics is first touched, it performs an initialization of SecureRandom which is known to be slow.

While documenting this could be helpful, it doesn't give clients using this library a lot of options to work around it. That's because Mnemonics is an object, so initialization is effectively a JVM static initializer when means it runs as soon as something touches the object.

Possible alternative implementations:

  • Defer SecureRandom initialization until it is needed, giving clients better control over timing
  • Convert Mnemonics to a class, so that initialization happens during construction which allows clients better control over timing of the initialization
  • Create a companion object initializer. This could be a suspend function, allowing secure random injection into a private constructor for Mnemonics
  • ... There may be other options to consider as well

Configure GitHub Actions

Create a basic GitHub action script to validate the gradle wrapper, compile the library, run ktlint, and detekt.

#24 is a followup issue to run the tests. Note that tests aren't being configured in this issue, because running the Android tests on CI can be complex. If we migrate to Multiplatform, then running those tests would be easier.

Configure dependency locking

Is your feature request related to a problem? Please describe.

To reduce the risk of software supply chain issues, we should enable dependency locking.

Although the published library itself doesn't depend on third party libraries, the Gradle build does rely on some plug-ins and the tests rely on some third party libraries.

Describe the solution you'd like

Enable dependency locking for the build scripts, build-conventions, as well as add a new dependency locking convention similar to https://github.com/zcash/secant-android-wallet/blob/main/build-convention/src/main/kotlin/zcash.dependency-conventions.gradle.kts

Kotlin 1.7.0

For a Gradle dependency:

  1. Update the dependency version in the root gradle.properties
  2. Update the dependency locks
    1. For Gradle plugins: ./gradlew dependencies --write-locks
    2. For Gradle dependencies: ./gradlew resolveAll --write-locks
  3. Verify no unexpected entries appear in the lockfiles. A supply chain attack could occur during this stage. The lockfile narrows the supply chain attack window to this very moment (as opposed to every time a build occurs).
  4. Are there any new APIs or possible migrations for this dependency?
    1. This should be relatively minor for our library
    2. There are some new regex APIs
    3. min/max are back on collections
    4. https://kotlinlang.org/docs/whatsnew17.html

ktlint 0.46.0

  1. Update the dependency version in the root gradle.properties
  2. Update the dependency locks
    1. For Gradle plugins: ./gradlew dependencies --write-locks
  3. Verify no unexpected entries appear in the lockfiles. A supply chain attack could occur during this stage. The lockfile narrows the supply chain attack window to this very moment (as opposed to every time a build occurs)
  4. Are there any new APIs or possible migrations for this dependency?
    1. Note there are some new rules introduced with this update that will require formatting changes or new @Suppress statements.

Prepare for 1.0.3 release

We need to prepare for a future release by:

  • Bumping the version to 1.0.3
  • Updating the Maven properties
  • Updating the changelog
  • Adding documentation on deployment
  • Testing out automated deployment

Dokka 1.6.21

For a Gradle dependency:

  1. Update the dependency version in the root gradle.properties
  2. Update the dependency locks
  1. Are there any new APIs or possible migrations for this dependency?

Rename default branch

Is your feature request related to a problem? Please describe.

Our default branch names are not consistent for our Android projects.

Describe the solution you'd like

Align branch names for consistency, changing the default branch to main.

required changes include:

  • Setting the GitHub setting for the default branch
  • Verifying that branch protection/security rules are still in place and that deployment secrets are only available to the branch called main
  • Updating the deploy-snapshot.yml and deploy-release.yml to run on the main branch instead of master

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.