Last active
March 20, 2025 02:36
-
-
Save LucasAlfare/1d128bfcac34f190fedb0b196890c34f to your computer and use it in GitHub Desktop.
Convert a 256-bit private key into a 24-word BIP-39 mnemonic and restore it back—downloads the official word list from GitHub.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Simple Kotlin Program for BIP-39 Mnemonic Generation and Key Verification | |
* | |
* This program performs two important functions: | |
* 1. It generates a 24-word mnemonic phrase from a 256-bit binary private key. | |
* 2. It converts that mnemonic phrase back into the original private key (displayed in hexadecimal). | |
* | |
* Why is this useful? | |
* ------------------------------------------ | |
* In Bitcoin and many other cryptocurrencies, private keys are essential for controlling your funds. | |
* However, remembering or writing down a long binary or hexadecimal string is hard and error-prone. | |
* BIP-39 (Bitcoin Improvement Proposal 39) defines a way to convert a binary private key into a list of | |
* simple words (called a mnemonic) that is easier to remember, write down, or store. | |
* | |
* But why include a function that converts the mnemonic back to the original private key? | |
* ---------------------------------------------------------------------------------------- | |
* That function proves that the 24 words accurately represent the original 256-bit number. | |
* By deriving the private key from the mnemonic, you can verify that no data was lost or altered during | |
* the conversion process. In other words, it ensures the mnemonic is a faithful backup of your private key. | |
* | |
* Some Bitcoin Theory (Explained Simply): | |
* ------------------------------------------ | |
* - Bitcoin uses cryptography to secure transactions. Your private key is like the master key to your money. | |
* - The mnemonic phrase is a human-friendly backup that can be used to regenerate your private key. | |
* - It is very important to keep your private key (or the mnemonic) safe and never share it with anyone. | |
* | |
* Important Safety Advice: | |
* -------------------------- | |
* - Never run or use codes online that you do not fully understand or trust, especially when dealing with private keys. | |
* - Always keep your private key offline if possible, to prevent hacking or theft. | |
* - This code is provided as an example. For real applications, consider using well-reviewed software and libraries. | |
* | |
* How to Use This Program: | |
* ------------------------- | |
* 1. Open this file in your favorite Kotlin IDE or any text editor. | |
* 2. In the main() function, locate the variable "binaryPrivateKey". | |
* 3. Replace the sample 256-bit binary string with your own private key. | |
* - Make sure the string is exactly 256 characters long and only contains '0' and '1'. | |
* 4. Run the program. | |
* 5. The program will display: | |
* - The 24 mnemonic words that represent your private key. | |
* - The original private key (in hexadecimal) derived from those words. | |
* | |
* This explanation is written in simple language so that even someone with little programming or technical | |
* background (or even a very clever animal!) can understand the purpose and functionality of this code. | |
* | |
* Enjoy and use responsibly! | |
*/ | |
import java.net.URL | |
import java.security.MessageDigest | |
// Function to load the BIP-39 English word list from the provided URL. | |
fun loadWordList(): List<String> { | |
val url = "https://raw.githubusercontent.com/bitcoin/bips/refs/heads/master/bip-0039/english.txt" | |
return URL(url).readText().lines().filter { it.isNotBlank() } | |
} | |
// Function to generate the 24-word mnemonic from a 256-bit binary string. | |
fun generateMnemonic(entropyBin: String, wordList: List<String>): List<String> { | |
// Validate that the binary string is exactly 256 characters long. | |
if (entropyBin.length != 256) | |
throw IllegalArgumentException("Entropy must be exactly 256 bits.") | |
// Convert the binary string into a byte array (32 bytes). | |
val entropyBytes = ByteArray(32) | |
for (i in 0 until 32) { | |
val byteStr = entropyBin.substring(i * 8, i * 8 + 8) | |
entropyBytes[i] = byteStr.toInt(2).toByte() | |
} | |
// Calculate the checksum using SHA-256. | |
// For 256-bit entropy, the checksum is 8 bits. | |
val digest = MessageDigest.getInstance("SHA-256") | |
val hash = digest.digest(entropyBytes) | |
val checksum = (hash[0].toInt() and 0xFF).toString(2).padStart(8, '0') | |
// Combine the entropy and the checksum to form a 264-bit string. | |
val fullBin = entropyBin + checksum | |
// Split the 264-bit string into 24 groups of 11 bits each. | |
val mnemonic = mutableListOf<String>() | |
for (i in 0 until 24) { | |
val chunk = fullBin.substring(i * 11, i * 11 + 11) | |
val index = chunk.toInt(2) | |
mnemonic.add(wordList[index]) | |
} | |
return mnemonic | |
} | |
// Function to derive the original private key (in hexadecimal) from the 24-word mnemonic. | |
// This function is important because it verifies that the mnemonic words correctly encode | |
// the original private key, ensuring that the backup is accurate. | |
fun deriveKeyFromMnemonic(mnemonic: List<String>, wordList: List<String>): String { | |
// Convert each word to its corresponding index (11-bit binary) in the word list. | |
val fullBin = mnemonic.joinToString(separator = "") { word -> | |
val index = wordList.indexOf(word) | |
if (index < 0) throw IllegalArgumentException("Word '$word' not found in the word list.") | |
index.toString(2).padStart(11, '0') | |
} | |
// The first 256 bits are the entropy; the last 8 bits are the checksum. | |
val entropyBin = fullBin.substring(0, 256) | |
val checksumBin = fullBin.substring(256) | |
// Recalculate the checksum to verify the mnemonic. | |
val entropyBytes = ByteArray(32) | |
for (i in 0 until 32) { | |
val byteStr = entropyBin.substring(i * 8, i * 8 + 8) | |
entropyBytes[i] = byteStr.toInt(2).toByte() | |
} | |
val digest = MessageDigest.getInstance("SHA-256") | |
val hash = digest.digest(entropyBytes) | |
val expectedChecksum = (hash[0].toInt() and 0xFF).toString(2).padStart(8, '0') | |
if (expectedChecksum != checksumBin) | |
throw IllegalArgumentException("Checksum does not match. The mnemonic may be invalid.") | |
// Convert the 256-bit entropy into a hexadecimal string. | |
return entropyBytes.joinToString("") { "%02x".format(it) } | |
} | |
// Main function to execute the program. | |
fun main() { | |
// Load the BIP-39 word list from GitHub. | |
val wordList = loadWordList() | |
// Insert your 256-bit binary private key below. | |
// Ensure the binary string is exactly 256 characters long. | |
val binaryPrivateKey = "0000000100100011010001010110011110001001101010111100110111101111" + | |
"0000000100100011010001010110011110001001101010111100110111101111" | |
// Note: The above sample string is created by repeating a 128-bit sequence twice. | |
// Replace it with your actual 256-bit private key. | |
try { | |
// Generate the mnemonic words from the binary private key. | |
val mnemonic = generateMnemonic(binaryPrivateKey, wordList) | |
println("Mnemonic (24 words):") | |
println(mnemonic.joinToString(" ")) | |
// Derive and display the original private key in hexadecimal format. | |
// This confirms that the mnemonic accurately represents your private key. | |
val hexKey = deriveKeyFromMnemonic(mnemonic, wordList) | |
println("\nDerived Private Key (hexadecimal):") | |
println(hexKey) | |
} catch (e: Exception) { | |
println("Error: ${e.message}") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment