Minimal Java library for BIP32 hierarchical deterministic key derivation and BIP39 mnemonic handling, specifically designed for NUT-13 (Cashu ecash) implementation.
- 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
- Java 21 or higher
- Maven 3.6+
Build the project:
mvn clean installAdd to your pom.xml:
<dependency>
<groupId>xyz.tcheeric</groupId>
<artifactId>bip-utils</artifactId>
<version>2.0.0</version>
</dependency>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);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 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-1counter'= Increments per successful mint (starts at 0)0= Derive secret1= 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());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// 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"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");Run all tests:
mvn -q verifyThe 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
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
- 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
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.
- Tutorials: Getting Started
- How-To: Implement NUT-13 Deterministic Secrets
- Reference: API Overview
- Explanations: NUT-13 Protocol Deep Dive, Security Considerations
Documentation now reflects the refactored package structure:
- All references to
Bip32Utilswere renamed toBip32 - 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
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
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.
- Mnemonic Storage: Never store mnemonics in plain text. Use secure storage mechanisms.
- Private Keys: Private keys should be cleared from memory after use when possible.
- Passphrase: BIP39 passphrase adds additional security layer. Use strong passphrases.
- Counter Management: Maintain accurate counter state to avoid key reuse.
- Production Use: Always use cryptographically secure random number generators for mnemonic generation.
For complete NUT-13 specification, see: https://github.com/cashubtc/nuts/blob/main/13.md
This project is derived from bitcoin-utils and maintains compatibility with the original codebase.