Skip to content

Minimal Java library for BIP32 hierarchical deterministic key derivation and BIP39 mnemonic handling.

License

Notifications You must be signed in to change notification settings

398ja/bip-utils

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

88 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

BIP32/BIP39 Utilities for NUT-13

Minimal Java library for BIP32 hierarchical deterministic key derivation and BIP39 mnemonic handling, specifically designed for NUT-13 (Cashu ecash) implementation.

Features

  • Full BIP39 Support: Generate, validate, and convert mnemonic phrases to seeds
  • Multi-Language Wordlists: English, French, Spanish, and Japanese support
  • BIP32 Derivation: Hierarchical deterministic key derivation
  • NUT-13 Specific: Purpose-built utilities for Cashu ecash secret derivation
  • Well-tested: 100+ tests with known BIP39/BIP32 test vectors
  • Minimal Dependencies: Uses only bitcoinj-core for cryptographic operations

Requirements

  • Java 21 or higher
  • Maven 3.6+

Installation

Build the project:

mvn clean install

Add to your pom.xml:

<dependency>
    <groupId>xyz.tcheeric</groupId>
    <artifactId>bip-utils</artifactId>
    <version>2.0.0</version>
</dependency>

Usage

BIP39 - Mnemonic Generation and Validation

import xyz.tcheeric.bips.bip39.Bip39;
import xyz.tcheeric.bips.bip39.WordList;

// Generate a random 12-word mnemonic (English)
String mnemonic = Bip39.generateMnemonic(12);
// Example: "witch collapse practice feed shame open despair creek road again ice least"

// Generate with different word counts (12, 15, 18, 21, or 24)
String mnemonic24 = Bip39.generateMnemonic(24);

// Generate in different languages
String frenchMnemonic = Bip39.generateMnemonic(12, WordList.french());
String spanishMnemonic = Bip39.generateMnemonic(12, WordList.spanish());
String japaneseMnemonic = Bip39.generateMnemonic(12, WordList.japanese());

// Validate a mnemonic
boolean isValid = Bip39.isValidMnemonic(mnemonic);

// Get detailed validation results
MnemonicValidator.ValidationResult result = Bip39.validateMnemonic(mnemonic);
if (!result.isValid()) {
    System.out.println("Error: " + result.getErrorMessage());
}

// Convert mnemonic to seed
byte[] seed = Bip39.mnemonicToSeed(mnemonic, ""); // No passphrase
byte[] seedWithPassphrase = Bip39.mnemonicToSeed(mnemonic, "my passphrase");

// Generate mnemonic from custom entropy
byte[] entropy = new byte[16]; // 16 bytes = 128 bits = 12 words
// ... fill entropy with secure random bytes
String customMnemonic = Bip39.generateMnemonic(entropy);

BIP32 - Hierarchical Deterministic Key Derivation

import xyz.tcheeric.bips.bip32.Bip32;
import xyz.tcheeric.bips.bip39.Bip39;
import org.bitcoinj.crypto.DeterministicKey;

// Generate mnemonic and derive master key
String mnemonic = Bip39.generateMnemonic(12);
DeterministicKey masterKey = Bip39.mnemonicToMasterKey(mnemonic, "");

// Or do it step by step
byte[] seed = Bip39.mnemonicToSeed(mnemonic, "");
DeterministicKey masterKey = Bip32.deriveMasterKey(seed);

// Derive child key using BIP32 path
DeterministicKey childKey = Bip32.deriveKey(masterKey, "m/44'/0'/0'/0/0");

// Get private key bytes
byte[] privateKey = Bip32.getPrivateKeyBytes(childKey);

// Get public key bytes (compressed)
byte[] publicKey = Bip32.getPublicKeyBytes(childKey, true);

// Get chain code
byte[] chainCode = Bip32.getChainCode(childKey);

NUT-13 Specific Derivation

NUT-13 defines a specific derivation path for Cashu ecash tokens:

Path Format: m/129372'/0'/{keyset_id_int}'/{counter}'/{0|1}

Where:

  • 129372' = Purpose (UTF-8 encoding of πŸ₯œ peanut emoji)
  • 0' = Coin type (always 0, independent of ecash unit)
  • keyset_id_int' = Keyset ID converted to integer via modulo 2^31-1
  • counter' = Increments per successful mint (starts at 0)
  • 0 = Derive secret
  • 1 = Derive blinding factor (r)
import org.bitcoinj.crypto.DeterministicKey;
import xyz.tcheeric.bips.bip32.Bip32;
import xyz.tcheeric.bips.bip32.nut.Nut13Derivation;

String mnemonic = "your twelve word mnemonic phrase goes here for wallet recovery";
String keysetId = "009a1f293253e41e"; // Hex keyset ID from mint
int counter = 0; // Increment for each mint operation

Nut13Derivation.Nut13DerivationParams params = Nut13Derivation.Nut13DerivationParams.builder()
    .mnemonicPhrase(mnemonic)
    .passphrase("") // passphrase (empty if none)
    .keysetIdHex(keysetId)
    .counter(counter)
    .build();

// Derive secret
byte[] secret = Nut13Derivation.deriveSecretFromMnemonic(params);

// Derive blinding factor
byte[] blindingFactor = Nut13Derivation.deriveBlindingFactorFromMnemonic(params);

// Or derive both at once
byte[] seed = Bip32.mnemonicToSeed(mnemonic, "");
DeterministicKey masterKey = Bip32.deriveMasterKey(seed);
Nut13Derivation.SecretAndBlindingFactor pair =
    Nut13Derivation.deriveSecretAndBlindingFactor(masterKey, keysetId, counter);

System.out.println("Secret: " + pair.getSecretHex());
System.out.println("Blinding Factor: " + pair.getBlindingFactorHex());

Complete Workflow Example

import xyz.tcheeric.bips.*;
import org.bitcoinj.crypto.DeterministicKey;

public class CashuWallet {
    private final DeterministicKey masterKey;

    public CashuWallet(String mnemonic, String passphrase) {
        byte[] seed = Bip32.mnemonicToSeed(mnemonic, passphrase);
        this.masterKey = Bip32.deriveMasterKey(seed);
    }

    // Derive secrets for a mint operation
    public TokenSecrets mintToken(String keysetId, int counter) {
        byte[] secret = Nut13Derivation.deriveSecret(masterKey, keysetId, counter);
        byte[] blindingFactor = Nut13Derivation.deriveBlindingFactor(masterKey, keysetId, counter);

        return new TokenSecrets(secret, blindingFactor);
    }

    // Recover previously minted tokens
    public TokenSecrets recoverToken(String keysetId, int counter) {
        // Same derivation process - deterministic recovery
        return mintToken(keysetId, counter);
    }

    static class TokenSecrets {
        final byte[] secret;
        final byte[] blindingFactor;

        TokenSecrets(byte[] secret, byte[] blindingFactor) {
            this.secret = secret;
            this.blindingFactor = blindingFactor;
        }
    }
}

// Usage
CashuWallet wallet = new CashuWallet(
    "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
    ""
);

// First mint
TokenSecrets mint0 = wallet.mintToken("009a1f293253e41e", 0);

// Second mint (increment counter)
TokenSecrets mint1 = wallet.mintToken("009a1f293253e41e", 1);

// Recover from mnemonic after wallet loss
CashuWallet recoveredWallet = new CashuWallet(
    "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
    ""
);
TokenSecrets recovered = recoveredWallet.recoverToken("009a1f293253e41e", 0);
// recovered.secret will match mint0.secret

Keyset ID Conversion

// Convert keyset hex ID to integer for derivation
String keysetIdHex = "009a1f293253e41e";
int keysetIdInt = Nut13Derivation.keysetIdToInt(keysetIdHex);
// Result: 242349537

// Build the full derivation paths
String secretPath = Nut13Derivation.buildSecretDerivationPath(keysetIdInt, 0);
String blindingPath = Nut13Derivation.buildBlindingFactorDerivationPath(keysetIdInt, 0);
// secretPath: "m/129372'/0'/242349537'/0'/0"
// blindingPath: "m/129372'/0'/242349537'/0'/1"

Utility Methods

import xyz.tcheeric.bips.util.HexUtils;
import xyz.tcheeric.bips.util.CryptoUtils;

// Hex conversion (recommended: use utility classes)
byte[] bytes = HexUtils.fromHex("deadbeef");
String hex = HexUtils.toHex(bytes);

// Deprecated alternatives (still work but use HexUtils internally)
byte[] bytesOld = Bip32.hexToBytes("deadbeef");
String hexOld = Bip32.bytesToHex(bytes);

// SHA-256 hashing (recommended: use utility classes)
byte[] hash = CryptoUtils.sha256("hello world".getBytes());

// Deprecated alternative (still works but uses CryptoUtils internally)
byte[] hashOld = Bip32.sha256("hello world".getBytes());

// Parse BIP32 path
List<ChildNumber> path = Bip32.parsePath("m/44'/0'/0'/0/0");

Testing

Run all tests:

mvn -q verify

The suite covers:

  • BIP39 mnemonic generation and validation across supported wordlists and reference vectors
  • BIP32 derivation, path parsing, and key material extraction
  • NUT-13 deterministic derivation, counter management, and recovery scenarios

Project Structure

src/
β”œβ”€β”€ main/
β”‚   β”œβ”€β”€ java/xyz/tcheeric/bips/
β”‚   β”‚   β”œβ”€β”€ bip32/
β”‚   β”‚   β”‚   β”œβ”€β”€ Bip32.java           # BIP32 utilities
β”‚   β”‚   β”‚   └── nut/
β”‚   β”‚   β”‚       └── Nut13Derivation.java  # NUT-13 Cashu derivation
β”‚   β”‚   └── bip39/
β”‚   β”‚       β”œβ”€β”€ Bip39.java           # High-level BIP39 API
β”‚   β”‚       β”œβ”€β”€ MnemonicGenerator.java
β”‚   β”‚       β”œβ”€β”€ MnemonicValidator.java
β”‚   β”‚       β”œβ”€β”€ SeedCalculator.java
β”‚   β”‚       └── WordList.java        # Multi-language wordlists
β”‚   └── resources/bip39-wordlists/
β”‚       β”œβ”€β”€ english.txt
β”‚       β”œβ”€β”€ french.txt
β”‚       β”œβ”€β”€ spanish.txt
β”‚       └── japanese.txt
└── test/java/xyz/tcheeric/bips/
    β”œβ”€β”€ bip32/
    β”‚   β”œβ”€β”€ Bip32Test.java
    β”‚   └── nut/
    β”‚       └── Nut13DerivationTest.java
    └── bip39/
        β”œβ”€β”€ Bip39Test.java
        β”œβ”€β”€ MnemonicGeneratorTest.java
        β”œβ”€β”€ MnemonicValidatorTest.java
        └── WordListTest.java

Dependencies

  • bitcoinj-core (0.17): BIP32/BIP39 cryptographic operations
  • slf4j-api (2.0.9): Logging API
  • jsr305 (3.0.2, provided): Null safety annotations
  • JUnit 5 (5.10.0, test): Testing framework
  • AssertJ (3.24.2, test): Fluent assertions

Documentation

Structure

docs/
β”œβ”€β”€ README.md
β”œβ”€β”€ tutorials/
β”‚   └── getting-started.md
β”œβ”€β”€ how-to/
β”‚   └── implement-nut13.md
β”œβ”€β”€ reference/
β”‚   └── api-overview.md
└── explanation/
    β”œβ”€β”€ nut13-protocol.md
    └── security.md

The documentation suite follows the DiΓ‘taxis framework. Start at docs/README.md for an index of all resources.

Available Guides

Recent Updates

Documentation now reflects the refactored package structure:

  • All references to Bip32Utils were renamed to Bip32
  • NUT-13 imports now use xyz.tcheeric.bips.bip32.nut.Nut13Derivation
  • Project diagrams and examples match the current package layout
  • Code snippets were re-verified against the latest release

Expansion Roadmap

Upcoming documentation work items:

  • Tutorials: Understanding BIP39 Mnemonics, Understanding BIP32 Derivation
  • How-To Guides: Generate Multi-Language Mnemonics, Validate Mnemonic Phrases, Derive BIP32 Keys
  • Reference: Detailed BIP39, BIP32, and NUT-13 API references, Supported Languages, Error Codes
  • Explanations: Why BIP39 Matters, How BIP32 Works, Design Decisions

Maintenance

Keep the documentation current by updating API references alongside code changes, expanding how-to guides for new features, reviewing security guidance for new threats, and verifying code snippets whenever the library evolves.

Security Considerations

  1. Mnemonic Storage: Never store mnemonics in plain text. Use secure storage mechanisms.
  2. Private Keys: Private keys should be cleared from memory after use when possible.
  3. Passphrase: BIP39 passphrase adds additional security layer. Use strong passphrases.
  4. Counter Management: Maintain accurate counter state to avoid key reuse.
  5. Production Use: Always use cryptographically secure random number generators for mnemonic generation.

NUT-13 Specification

For complete NUT-13 specification, see: https://github.com/cashubtc/nuts/blob/main/13.md

License

This project is derived from bitcoin-utils and maintains compatibility with the original codebase.

References

About

Minimal Java library for BIP32 hierarchical deterministic key derivation and BIP39 mnemonic handling.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages