-
Notifications
You must be signed in to change notification settings - Fork 91
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
62 changed files
with
4,687 additions
and
703 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,4 @@ | |
.idea | ||
neutrino_test | ||
|
||
/bin |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,50 @@ | ||
 | ||
|
||
## About | ||
Welcome! | ||
|
||
You can use this tool to swipe all the funds in your muun account to an address of your choosing. | ||
You can use this tool to transfer all funds from your Muun wallet to an address of your choosing. | ||
|
||
To do this you will need: | ||
* The recovery code, that you set up when you created your muun account | ||
* The two encrypted private keys that you exported from your muun wallet | ||
* A destination Bitcoin address where all your funds will be sent | ||
**This process requires no collaboration from Muun to work**. We wholeheartedly believe that self-custodianship | ||
is an essential right, and we want to create a world in which people have complete and exclusive | ||
control over their own money. Bitcoin has finally made this possible. | ||
|
||
The process of scanning the blockchain to recover your funds can take several hours, please be ready to keep it running. The scan starts at the block your wallet was created to make it faster, but depending on when that was it can take long. | ||
## Usage | ||
|
||
## Setup | ||
To execute a recovery, you will need: | ||
|
||
1. **Your Recovery Code**, which you wrote down during your security setup | ||
2. **Your Emergency Kit PDF**, which you exported from the app | ||
3. **Your destination bitcoin address**, where all your funds will be sent | ||
|
||
Once you have that, you must: | ||
|
||
1. Install [golang](https://golang.org/) | ||
2. Open a terminal window | ||
3. Run this code: | ||
3. Run: | ||
|
||
git clone https://github.com/muun/recovery | ||
cd recovery | ||
./recovery-tool <path to your Emergency Kit PDF> | ||
|
||
``` | ||
git clone https://github.com/muun/recovery | ||
cd recovery | ||
go run -mod=vendor . | ||
``` | ||
The recovery process takes only a few minutes (depending on your connection). | ||
|
||
## Questions | ||
|
||
If you have any questions, contact us at [email protected] | ||
If you have any questions, we'll be happy to answer them. Contact us at [[email protected]](mailto:[email protected]) | ||
|
||
## Auditing | ||
|
||
* Most of the key handling and transaction crafting operations happens in the **libwallet** module. | ||
* All the blockchain scan code is in the **neutrino** module. | ||
Begin by reading `main.go`, and follow calls to other files and modules as you see fit. We always work | ||
to improve code quality and readability with each release, so that auditing is easier and more effective. | ||
|
||
The low-level encryption, key handling and transaction crafting code can be found in the `libwallet` | ||
module, and it's the same our iOS and Android applications use. | ||
|
||
|
||
## Responsible Disclosure | ||
|
||
Send us an email to report any security related bugs or vulnerabilities at [[email protected]](mailto:[email protected]). | ||
|
||
You can encrypt your email message using our public PGP key. | ||
|
||
Public key fingerprint: `1299 28C1 E79F E011 6DA4 C80F 8DB7 FD0F 61E6 ED76` | ||
Public key fingerprint: `1299 28C1 E79F E011 6DA4 C80F 8DB7 FD0F 61E6 ED76` |
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
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
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,64 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/hex" | ||
log "log" | ||
"fmt" | ||
|
||
"github.com/btcsuite/btcutil/base58" | ||
"github.com/muun/libwallet" | ||
"github.com/muun/libwallet/emergencykit" | ||
) | ||
|
||
var defaultNetwork = libwallet.Mainnet() | ||
|
||
func buildExtendedKeys(rawKey1, rawKey2, recoveryCode string) ( | ||
*libwallet.DecryptedPrivateKey, | ||
*libwallet.DecryptedPrivateKey) { | ||
|
||
// Always take the salt from the second key (the same salt was used, but our older key format | ||
// is missing the salt on the first key): | ||
salt := extractSalt(rawKey2) | ||
|
||
decryptionKey, err := libwallet.RecoveryCodeToKey(recoveryCode, salt) | ||
func decodeKeysFromInput(rawKey1 string, rawKey2 string) ([]*libwallet.EncryptedPrivateKeyInfo, error) { | ||
key1, err := libwallet.DecodeEncryptedPrivateKey(rawKey1) | ||
if err != nil { | ||
log.Fatalf("failed to process recovery code: %v", err) | ||
return nil, fmt.Errorf("failed to decode first key: %w", err) | ||
} | ||
|
||
key1, err := decryptionKey.DecryptKey(rawKey1, defaultNetwork) | ||
key2, err := libwallet.DecodeEncryptedPrivateKey(rawKey2) | ||
if err != nil { | ||
log.Fatalf("failed to decrypt first key: %v", err) | ||
return nil, fmt.Errorf("failed to decode second key: %w", err) | ||
} | ||
|
||
key2, err := decryptionKey.DecryptKey(rawKey2, defaultNetwork) | ||
if err != nil { | ||
log.Fatalf("failed to decrypt second key: %v", err) | ||
return []*libwallet.EncryptedPrivateKeyInfo{key1, key2}, nil | ||
} | ||
|
||
func decodeKeysFromMetadata(meta *emergencykit.Metadata) ([]*libwallet.EncryptedPrivateKeyInfo, error) { | ||
decodedKeys := make([]*libwallet.EncryptedPrivateKeyInfo, len(meta.EncryptedKeys)) | ||
|
||
for i, metaKey := range meta.EncryptedKeys { | ||
decodedKeys[i] = &libwallet.EncryptedPrivateKeyInfo{ | ||
Version: meta.Version, | ||
Birthday: meta.BirthdayBlock, | ||
EphPublicKey: metaKey.DhPubKey, | ||
CipherText: metaKey.EncryptedPrivKey, | ||
Salt: metaKey.Salt, | ||
} | ||
} | ||
|
||
return key1, key2 | ||
return decodedKeys, nil | ||
} | ||
|
||
func extractSalt(rawKey string) string { | ||
bytes := base58.Decode(rawKey) | ||
saltBytes := bytes[len(bytes)-8:] | ||
func decryptKeys(encryptedKeys []*libwallet.EncryptedPrivateKeyInfo, recoveryCode string) ([]*libwallet.DecryptedPrivateKey, error) { | ||
// Always take the salt from the second key (the same salt was used for all keys, but our legacy | ||
// key format did not include it in the first key): | ||
salt := encryptedKeys[1].Salt | ||
|
||
decryptionKey, err := libwallet.RecoveryCodeToKey(recoveryCode, salt) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to process recovery code: %w", err) | ||
} | ||
|
||
decryptedKeys := make([]*libwallet.DecryptedPrivateKey, len(encryptedKeys)) | ||
|
||
for i, encryptedKey := range encryptedKeys { | ||
decryptedKey, err := decryptionKey.DecryptKey(encryptedKey, defaultNetwork) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to decrypt key %d: %w", i, err) | ||
} | ||
|
||
decryptedKeys[i] = decryptedKey | ||
} | ||
|
||
return hex.EncodeToString(saltBytes) | ||
return decryptedKeys, nil | ||
} |
Oops, something went wrong.