Skip to content

Instantly share code, notes, and snippets.

@gpip
Last active January 28, 2018 18:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gpip/ed7595abf368e6858a3723555ba734e2 to your computer and use it in GitHub Desktop.
Save gpip/ed7595abf368e6858a3723555ba734e2 to your computer and use it in GitHub Desktop.
import struct
import hashlib
import binascii
# The following is required in case you want to get the account address and
# decrypt private keys.
# pip install ed25519 pyblake2 PyCrypto
import ed25519
from pyblake2 import blake2b
from Crypto.Cipher import AES
from Crypto.IO import PKCS8
from Crypto.Util import asn1
X509 = binascii.unhexlify('302a300506032b6570032100')
def _read_size(f):
size = 0
for i in range(4):
b = ord(f.read(1))
size = (size << 7) | (b & 0x7f)
if b & 0x80 == 0:
break
return size
def _read_int(f):
return struct.unpack('>I', f.read(4))[0]
def _hash160(data):
first = blake2b(data, digest_size=32).digest()
return hashlib.new('ripemd160', first).digest()
def _decode_account(f, version):
if version == 1:
iv = f.read(_read_int(f))
public_key = f.read(_read_int(f))
encrypted_privkey = f.read(_read_int(f))
elif version == 2:
iv = f.read(_read_size(f))
public_key = f.read(_read_size(f))
encrypted_privkey = f.read(_read_size(f))
address = _hash160(public_key)
public_key = public_key[len(X509):]
return {
'iv': iv,
'public_key': public_key,
'encrypted_privkey': encrypted_privkey,
'address': address
}
def _decrypt(enc, key, iv):
cipher = AES.new(key, AES.MODE_CBC, iv)
raw_pad = cipher.decrypt(enc)
# Remove padding
raw = raw_pad[0:-ord(raw_pad[-1])]
return raw
def decode(f, raw_password=None):
key = None
if raw_password is not None:
key = blake2b(raw_password, digest_size=32).digest()
version = _read_int(f)
print 'Wallet version %d\n' % version
if version not in (1, 2):
raise Exception('Unknown wallet version %d' % version)
num_accounts = _read_int(f)
for i in xrange(num_accounts):
acc = _decode_account(f, version)
print 'Account #%d' % i
print 'Address: 0x%s' % binascii.hexlify(acc['address'])
print 'Public key: %s' % binascii.hexlify(acc['public_key'])
if key:
privkey_pkcs8 = _decrypt(acc['encrypted_privkey'], key, acc['iv'])
# Decode from PKCS#8 DER.
privkey_der = PKCS8.unwrap(privkey_pkcs8)[1]
privkey = asn1.DerOctetString(privkey_der)
privkey = privkey.decode(privkey.payload).payload
signing_key = ed25519.SigningKey(privkey)
if signing_key.get_verifying_key().to_bytes() != acc['public_key']:
raise Exception('key mismatch, check your password')
print 'Private key: %s' % binascii.hexlify(privkey)
print
if version == 2:
# Decode address aliases (didn't try this part).
iv = f.read(_read_size(f))
aliases_encrypted = f.read(_read_size(f))
try:
address_count = _read_int(f)
except struct.error:
address_count = 0
if key:
aliases_raw = _decrypt(aliases_encrypted, key, iv)
aliases = dict([(f.read(_read_size(f)), f.read(_read_size(f)))
for i in xrange(address_count)])
print 'Aliases:', aliases
if __name__ == "__main__":
import os
import sys
with open(sys.argv[1], 'rb') as wallet_file:
decode(wallet_file, os.getenv('SEMUX_WALLET_PWD'))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment