So I've been mulling pieces of this idea for a while and finally found a way to make it click. I will use simplified system with example numbers for clarity of explanation, but if of course it's all configurable.
Situation:- User has encrypted wallet, but can't remember the passphrase. And no paper backup, of course. (Assumption: they know something about their passphrase, just not all of it)
- Will pay some amount for individuals with significant computing power to help brute-force the passphrase.
- Doesn't want to give the whole wallet to brute-forcers because they can just take all the funds when they find the passphrase
- Brute-forcer should be able to prove they found the passphrase without giving it away
- If they can prove that they found the passphrase, they don't want to simply give it to the user, because the user might not pay out
My solution has the following CONOPs (concept of operations):- User's wallet is setup to save extra data about the passphrase that does not compromise its security or the benefits of keystretching.
- User distributes an encrypted test string that is half known data, half secret data (perhaps in a forum post)
- User also provides a description of what they know about the passphrase
- Brute-forcers will test passphrases until they find one with the correct prefix (the known data). At this point they have the secret data.
- Brute-forcer will send the user their address using the secret as an HMAC key, to prove that he found the passphrase
- User verifies HMAC and constructs a finder's fee tranasaction to the brute-forcer that requires both their signature and the encryption key to redeem it.
Yes, the encryption key goes into the blockchain -- it's the only way to claim the coins.
Therefore, zero trust is required to exchange the private key for a finder's fee!Details:Assume simple key-stretching that produces an encryption key from a passphrase by simply doing SHA256
1,000(passphrase). The specifics of the key-stretching don't really matter -- this can be adapted to any key-stretching scheme. When the user sets/changes their passphrase on their wallet, it will encrypt the private keys using AES256, using key = SHA256
1,000(passphrase). The wallet will additionally save the following three pieces of information with the wallet:
- plainTestStr = "WalletTestString" + SecureRandom(16))
- encrTestStr = aes256_encrypt(SHA2561,000(passphrase), plainTestStr)
- revealKeyStr = SHA2561,002(passphrase) = SHA2562(key)
When the user forgets their passphrase, there will be a way to pop up a recovery window, where they can describe everything they know about their passphrase. It will then generate a message that says something like the following, which can be posted on a mailing list, forum, etc:
Passphrase recovery help needed: 25 BTC finder's fee
Encrypted Test String: abc13f98228aa3
Initialization Vector: 1832c1bf
Decrypted result starts with: "WalletTestString"
Email finder's fee payment address to: helpme@ididntmakeapaperbackup.com
Passphrase details:
I'm pretty sure the passphrase is 10-14 characters long, it contains two of '!', '@' and '#'. It has the word "kitten" and a 5-digit number in it. My best guess of the password is "41876!kittens#"
The brute-forcer will test passphrases until they find one that decrypts the test string to "WalletTestString
87cca180a167". The brute-forcer then constructs an email or forum message reply:
I found it. Send finder's fee to 1xK39z375mPHn7aa
----
27da5542189ee
Where the hex at the end is
HMAC(87cca180a167, "I found it. Send finder's fee to 1xK39z375mPHn7aa")
(the first hex arg is the secret part of the test string)
The user will plug the message into their wallet app which has the encrypted wallet they can't recover. It has the 87cc verification string stored in the wallet (but was never revealed to the brute-forcers), and then they use that to verify the HMAC. This proves that the person found the passphrase and proves they want the funds send to 1xK39z375mPHn7aa.
Last step: the user constructs the following transaction TxOut script, for 25 BTC:
OP_DUP
<PubKeyUser>
OP_CHECKSIG
OP_NOTIF
<PubKeyBruteForcer>
OP_CHECKSIGVERIFY
OP_HASH256
<SHA2562(encryptionkey)>
OP_EQUAL
OP_ENDIF
NOTE: It's not the passphrase that the claimant puts in the script, it's the actual encryption key (SHA256
1,000(passphrase)) that they put in the script. Then the script will double hash it to get SHA256
1,002(passphrase) and compare to the target string. SHA256
1,000(passphrase) must be stored in the wallet at encryption time, in addition to the test string. Note that you don't lose the benefits of key-stretching because the SHA256
1,000(passphrase) has the full 256-bits of entropy. It's still quicker to brute-force the lower-entropy passphrase hashed 1,000 times, than brute-force the 256-bit key without any hashing.
The user can redeem their coins (in the event they're not claimed) by simply putting "OP_0 <SigUser>" into the spending script.
The brute-forcer can redeem the coins by putting "<HA256
1,000(passphrase)> <SigBruteForcer>" into the spending script