Skip to content

Commit 99e2ff1

Browse files
committed
first commit
0 parents  commit 99e2ff1

17 files changed

+3464
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*.pyc
2+
*.swp
3+
config.cfg
4+

LICENCE

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
The MIT License (MIT)
2+
3+
Permission is hereby granted, free of charge, to any person obtaining
4+
a copy of this software and associated documentation files (the
5+
"Software"), to deal in the Software without restriction, including
6+
without limitation the rights to use, copy, modify, merge, publish,
7+
distribute, sublicense, and/or sell copies of the Software, and to
8+
permit persons to whom the Software is furnished to do so, subject to
9+
the following conditions:
10+
11+
The above copyright notice and this permission notice shall be
12+
included in all copies or substantial portions of the Software.
13+
14+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Electrum Personal Server
2+
3+
Electrum Personal Server is an implementation of the Electrum server protocol
4+
which fulfills the specific need of using the Electrum UI with full node
5+
verification and privacy, but without the heavyweight server backend, for a
6+
single user. It allows the user to benefit from all of Bitcoin Core's
7+
resource-saving features like pruning, blocksonly and disabled txindex. All
8+
of Electrum's feature-richness like hardware wallet integration,
9+
multisignature wallets, offline signing, mnemonic recovery phrases and so on
10+
can still be used, but backed by the user's own full node.
11+
12+
Using Electrum with Electrum Personal Server is probably the most
13+
resource-efficent way right now to use a hardware wallet connected to your
14+
own full node.
15+
16+
For a longer explaination of this project and why it's important, see the
17+
[bitcointalk thread](https://bitcointalk.org/index.php?topic=2664747.msg27179198).
18+
19+
See also the Electrum bitcoin wallet [website](https://electrum.org/).
20+
21+
## How To Use
22+
23+
This application requires python3 and a Bitcoin full node built with wallet
24+
capability.
25+
26+
Download the latest release or clone the git repository. Enter the directory
27+
and rename the file `config.cfg_sample` to `config.cfg`, edit this file to
28+
configure your bitcoin node json-rpc authentication details. Next add your
29+
wallet addresses to the `[wallets]` section.
30+
31+
Finally run `./server.py` on Linux or double-click `run-server.bat` on Windows.
32+
The first time the server is run it will import all configured addresses as
33+
watch-only into the Bitcoin node, and then exit giving you a chance to
34+
`-rescan` if your wallet contains historical transactions.
35+
36+
Electrum wallet must be configured to connect to the server. SSL must be
37+
disabled which can be done either by `Tools` -> `Connection` -> Uncheck box
38+
`Use SSL`, or starting with the command line flag `--nossl`, depending on the
39+
version of Electrum. Tell Electrum to connect to the server in
40+
`Tools` -> `Server`, usually `localhost` if running on the same machine.
41+
42+
Note that you can also try this with on [testnet bitcoin](https://en.bitcoin.it/wiki/Testnet).
43+
Electrum can be started in testnet mode with the command line flag `--testnet`.
44+
45+
#### Exposure to the Internet
46+
47+
You really don't want other people connecting to your server. They won't be
48+
able to synchronize their wallet, and they could potentially learn all your
49+
wallet addresses.
50+
51+
By default the server will bind to and accept connections only from `localhost`
52+
so you should either run Electrum wallet from the same computer or use a SSH
53+
tunnel.
54+
55+
## Project Readiness
56+
57+
This project is in alpha stages as there are several essential missing
58+
features such as:
59+
60+
* Merkle proofs are not handled, so every confirmed transaction is labelled
61+
`Not Verified`.
62+
63+
* The server does not support SSL so Electrum must be configured to disable it.
64+
65+
* Deterministic wallets and master public keys are not supported. Addresses
66+
must be imported individually.
67+
68+
* Bech32 bitcoin addresses are not supported.
69+
70+
* The Electrum server protocol has a caveat about multiple transactions included
71+
in the same block. So there may be weird behaviour if that happens.
72+
73+
* There's no way to turn off debug messages, so the console will be spammed by
74+
them when used.
75+
76+
When trying this, make sure you report any crashes, odd behaviour or times when
77+
Electrum disconnects (which indicates the server behaved unexpectedly).
78+
79+
Someone should try running this on a Raspberry PI.
80+
81+
## Contributing
82+
83+
I welcome contributions. Please keep lines under 80 characters in length and
84+
ideally don't add any external dependencies to keep this as easy to install as
85+
possible.
86+
87+
I can be contacted on freenode IRC on the `#bitcoin` and `#electrum` channels.
88+

bitcoin/__init__.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from bitcoin.py2specials import *
2+
from bitcoin.py3specials import *
3+
secp_present = False
4+
try:
5+
import secp256k1
6+
secp_present = True
7+
from bitcoin.secp256k1_main import *
8+
from bitcoin.secp256k1_transaction import *
9+
from bitcoin.secp256k1_deterministic import *
10+
except ImportError as e:
11+
from bitcoin.main import *
12+
from bitcoin.deterministic import *
13+
from bitcoin.transaction import *

bitcoin/deterministic.py

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
from bitcoin.main import *
2+
import hmac
3+
import hashlib
4+
from binascii import hexlify
5+
6+
# Below code ASSUMES binary inputs and compressed pubkeys
7+
MAINNET_PRIVATE = b'\x04\x88\xAD\xE4'
8+
MAINNET_PUBLIC = b'\x04\x88\xB2\x1E'
9+
TESTNET_PRIVATE = b'\x04\x35\x83\x94'
10+
TESTNET_PUBLIC = b'\x04\x35\x87\xCF'
11+
PRIVATE = [MAINNET_PRIVATE, TESTNET_PRIVATE]
12+
PUBLIC = [MAINNET_PUBLIC, TESTNET_PUBLIC]
13+
14+
# BIP32 child key derivation
15+
16+
def raw_bip32_ckd(rawtuple, i):
17+
vbytes, depth, fingerprint, oldi, chaincode, key = rawtuple
18+
i = int(i)
19+
20+
if vbytes in PRIVATE:
21+
priv = key
22+
pub = privtopub(key)
23+
else:
24+
pub = key
25+
26+
if i >= 2**31:
27+
if vbytes in PUBLIC:
28+
raise Exception("Can't do private derivation on public key!")
29+
I = hmac.new(chaincode, b'\x00' + priv[:32] + encode(i, 256, 4),
30+
hashlib.sha512).digest()
31+
else:
32+
I = hmac.new(chaincode, pub + encode(i, 256, 4),
33+
hashlib.sha512).digest()
34+
35+
if vbytes in PRIVATE:
36+
newkey = add_privkeys(I[:32] + B'\x01', priv)
37+
fingerprint = bin_hash160(privtopub(key))[:4]
38+
if vbytes in PUBLIC:
39+
newkey = add_pubkeys(compress(privtopub(I[:32])), key)
40+
fingerprint = bin_hash160(key)[:4]
41+
42+
return (vbytes, depth + 1, fingerprint, i, I[32:], newkey)
43+
44+
45+
def bip32_serialize(rawtuple):
46+
vbytes, depth, fingerprint, i, chaincode, key = rawtuple
47+
i = encode(i, 256, 4)
48+
chaincode = encode(hash_to_int(chaincode), 256, 32)
49+
keydata = b'\x00' + key[:-1] if vbytes in PRIVATE else key
50+
bindata = vbytes + from_int_to_byte(
51+
depth % 256) + fingerprint + i + chaincode + keydata
52+
return changebase(bindata + bin_dbl_sha256(bindata)[:4], 256, 58)
53+
54+
55+
def bip32_deserialize(data):
56+
dbin = changebase(data, 58, 256)
57+
if bin_dbl_sha256(dbin[:-4])[:4] != dbin[-4:]:
58+
raise Exception("Invalid checksum")
59+
vbytes = dbin[0:4]
60+
depth = from_byte_to_int(dbin[4])
61+
fingerprint = dbin[5:9]
62+
i = decode(dbin[9:13], 256)
63+
chaincode = dbin[13:45]
64+
key = dbin[46:78] + b'\x01' if vbytes in PRIVATE else dbin[45:78]
65+
return (vbytes, depth, fingerprint, i, chaincode, key)
66+
67+
68+
def raw_bip32_privtopub(rawtuple):
69+
vbytes, depth, fingerprint, i, chaincode, key = rawtuple
70+
newvbytes = MAINNET_PUBLIC if vbytes == MAINNET_PRIVATE else TESTNET_PUBLIC
71+
return (newvbytes, depth, fingerprint, i, chaincode, privtopub(key))
72+
73+
74+
def bip32_privtopub(data):
75+
return bip32_serialize(raw_bip32_privtopub(bip32_deserialize(data)))
76+
77+
78+
def bip32_ckd(data, i):
79+
return bip32_serialize(raw_bip32_ckd(bip32_deserialize(data), i))
80+
81+
82+
def bip32_master_key(seed, vbytes=MAINNET_PRIVATE):
83+
I = hmac.new(
84+
from_string_to_bytes("Bitcoin seed"), seed, hashlib.sha512).digest()
85+
return bip32_serialize((vbytes, 0, b'\x00' * 4, 0, I[32:], I[:32] + b'\x01'
86+
))
87+
88+
89+
def bip32_bin_extract_key(data):
90+
return bip32_deserialize(data)[-1]
91+
92+
93+
def bip32_extract_key(data):
94+
return safe_hexlify(bip32_deserialize(data)[-1])
95+
96+
# Exploits the same vulnerability as above in Electrum wallets
97+
# Takes a BIP32 pubkey and one of the child privkeys of its corresponding
98+
# privkey and returns the BIP32 privkey associated with that pubkey
99+
100+
def raw_crack_bip32_privkey(parent_pub, priv):
101+
vbytes, depth, fingerprint, i, chaincode, key = priv
102+
pvbytes, pdepth, pfingerprint, pi, pchaincode, pkey = parent_pub
103+
i = int(i)
104+
105+
if i >= 2**31:
106+
raise Exception("Can't crack private derivation!")
107+
108+
I = hmac.new(pchaincode, pkey + encode(i, 256, 4), hashlib.sha512).digest()
109+
110+
pprivkey = subtract_privkeys(key, I[:32] + b'\x01')
111+
112+
newvbytes = MAINNET_PRIVATE if vbytes == MAINNET_PUBLIC else TESTNET_PRIVATE
113+
return (newvbytes, pdepth, pfingerprint, pi, pchaincode, pprivkey)
114+
115+
116+
def crack_bip32_privkey(parent_pub, priv):
117+
dsppub = bip32_deserialize(parent_pub)
118+
dspriv = bip32_deserialize(priv)
119+
return bip32_serialize(raw_crack_bip32_privkey(dsppub, dspriv))
120+
121+
def bip32_descend(*args):
122+
if len(args) == 2:
123+
key, path = args
124+
else:
125+
key, path = args[0], map(int, args[1:])
126+
for p in path:
127+
key = bip32_ckd(key, p)
128+
return bip32_extract_key(key)

0 commit comments

Comments
 (0)