|
|
@ -383,21 +383,6 @@ def ECC_YfromX(x,curved=curve_secp256k1, odd=True): |
|
|
|
return [_p-My,offset] |
|
|
|
raise Exception('ECC_YfromX: No Y found') |
|
|
|
|
|
|
|
def private_header(msg,v): |
|
|
|
assert v<1, "Can't write version %d private header"%v |
|
|
|
r = '' |
|
|
|
if v==0: |
|
|
|
r += ('%08x'%len(msg)).decode('hex') |
|
|
|
r += sha256(msg)[:2] |
|
|
|
return ('%02x'%v).decode('hex') + ('%04x'%len(r)).decode('hex') + r |
|
|
|
|
|
|
|
def public_header(pubkey,v): |
|
|
|
assert v<1, "Can't write version %d public header"%v |
|
|
|
r = '' |
|
|
|
if v==0: |
|
|
|
r = sha256(pubkey)[:2] |
|
|
|
return '\x6a\x6a' + ('%02x'%v).decode('hex') + ('%04x'%len(r)).decode('hex') + r |
|
|
|
|
|
|
|
|
|
|
|
def negative_point(P): |
|
|
|
return Point( P.curve(), P.x(), -P.y(), P.order() ) |
|
|
@ -493,83 +478,67 @@ class EC_KEY(object): |
|
|
|
raise Exception("Bad signature") |
|
|
|
|
|
|
|
|
|
|
|
# ecdsa encryption/decryption methods |
|
|
|
# credits: jackjack, https://github.com/jackjack-jj/jeeq |
|
|
|
# ecies encryption/decryption methods; aes-256-cbc is used as the cipher; hmac-sha256 is used as the mac |
|
|
|
|
|
|
|
@classmethod |
|
|
|
def encrypt_message(self, message, pubkey): |
|
|
|
generator = generator_secp256k1 |
|
|
|
curved = curve_secp256k1 |
|
|
|
r = '' |
|
|
|
msg = private_header(message,0) + message |
|
|
|
msg = msg + ('\x00'*( 32-(len(msg)%32) )) |
|
|
|
msgs = chunks(msg,32) |
|
|
|
|
|
|
|
_r = generator.order() |
|
|
|
str_to_long = string_to_number |
|
|
|
|
|
|
|
P = generator |
|
|
|
|
|
|
|
pk = ser_to_point(pubkey) |
|
|
|
if not ecdsa.ecdsa.point_is_valid(generator_secp256k1, pk.x(), pk.y()): |
|
|
|
raise Exception('invalid pubkey') |
|
|
|
|
|
|
|
ephemeral_exponent = number_to_string(ecdsa.util.randrange(pow(2,256)), generator_secp256k1.order()) |
|
|
|
ephemeral = EC_KEY(ephemeral_exponent) |
|
|
|
|
|
|
|
ecdh_key = (pk * ephemeral.privkey.secret_multiplier).x() |
|
|
|
ecdh_key = ('%064x' % ecdh_key).decode('hex') |
|
|
|
key = hashlib.sha512(ecdh_key).digest() |
|
|
|
key_e, key_m = key[:32], key[32:] |
|
|
|
|
|
|
|
iv_ciphertext = aes.encryptData(key_e, message) |
|
|
|
|
|
|
|
for i in range(len(msgs)): |
|
|
|
n = ecdsa.util.randrange( pow(2,256) ) |
|
|
|
Mx = str_to_long(msgs[i]) |
|
|
|
My, xoffset = ECC_YfromX(Mx, curved) |
|
|
|
M = Point( curved, Mx+xoffset, My, _r ) |
|
|
|
T = P*n |
|
|
|
U = pk*n + M |
|
|
|
toadd = point_to_ser(T) + point_to_ser(U) |
|
|
|
toadd = chr(ord(toadd[0])-2 + 2*xoffset) + toadd[1:] |
|
|
|
r += toadd |
|
|
|
ephemeral_pubkey = ephemeral.get_public_key(compressed=True).decode('hex') |
|
|
|
encrypted = 'BIE1' + hash_160(pubkey) + ephemeral_pubkey + iv_ciphertext |
|
|
|
mac = hmac.new(key_m, encrypted, hashlib.sha256).digest() |
|
|
|
|
|
|
|
return base64.b64encode(public_header(pubkey,0) + r) |
|
|
|
return base64.b64encode(encrypted + mac) |
|
|
|
|
|
|
|
|
|
|
|
def decrypt_message(self, enc): |
|
|
|
G = generator_secp256k1 |
|
|
|
curved = curve_secp256k1 |
|
|
|
pvk = self.secret |
|
|
|
pubkeys = [point_to_ser(G*pvk,True), point_to_ser(G*pvk,False)] |
|
|
|
enc = base64.b64decode(enc) |
|
|
|
str_to_long = string_to_number |
|
|
|
|
|
|
|
assert enc[:2]=='\x6a\x6a' |
|
|
|
|
|
|
|
phv = str_to_long(enc[2]) |
|
|
|
assert phv==0, "Can't read version %d public header"%phv |
|
|
|
hs = str_to_long(enc[3:5]) |
|
|
|
public_header=enc[5:5+hs] |
|
|
|
checksum_pubkey=public_header[:2] |
|
|
|
address=filter(lambda x:sha256(x)[:2]==checksum_pubkey, pubkeys) |
|
|
|
assert len(address)>0, 'Bad private key' |
|
|
|
address=address[0] |
|
|
|
enc=enc[5+hs:] |
|
|
|
r = '' |
|
|
|
for Tser,User in map(lambda x:[x[:33],x[33:]], chunks(enc,66)): |
|
|
|
ots = ord(Tser[0]) |
|
|
|
xoffset = ots>>1 |
|
|
|
Tser = chr(2+(ots&1))+Tser[1:] |
|
|
|
T = ser_to_point(Tser) |
|
|
|
U = ser_to_point(User) |
|
|
|
V = T*pvk |
|
|
|
Mcalc = U + negative_point(V) |
|
|
|
r += ('%064x'%(Mcalc.x()-xoffset)).decode('hex') |
|
|
|
|
|
|
|
pvhv = str_to_long(r[0]) |
|
|
|
assert pvhv==0, "Can't read version %d private header"%pvhv |
|
|
|
phs = str_to_long(r[1:3]) |
|
|
|
private_header = r[3:3+phs] |
|
|
|
size = str_to_long(private_header[:4]) |
|
|
|
checksum = private_header[4:6] |
|
|
|
r = r[3+phs:] |
|
|
|
|
|
|
|
msg = r[:size] |
|
|
|
hashmsg = sha256(msg)[:2] |
|
|
|
checksumok = hashmsg==checksum |
|
|
|
|
|
|
|
return [msg, checksumok, address] |
|
|
|
def decrypt_message(self, encrypted): |
|
|
|
|
|
|
|
encrypted = base64.b64decode(encrypted) |
|
|
|
|
|
|
|
if len(encrypted) < 105: |
|
|
|
raise Exception('invalid ciphertext: length') |
|
|
|
|
|
|
|
magic = encrypted[:4] |
|
|
|
recipient_pubkeyhash = encrypted[4:24] |
|
|
|
ephemeral_pubkey = encrypted[24:57] |
|
|
|
iv_ciphertext = encrypted[57:-32] |
|
|
|
mac = encrypted[-32:] |
|
|
|
|
|
|
|
if magic != 'BIE1': |
|
|
|
raise Exception('invalid ciphertext: invalid magic bytes') |
|
|
|
|
|
|
|
if hash_160(self.get_public_key().decode('hex')) != recipient_pubkeyhash: |
|
|
|
raise Exception('invalid ciphertext: invalid key') |
|
|
|
|
|
|
|
try: |
|
|
|
ephemeral_pubkey = ser_to_point(ephemeral_pubkey) |
|
|
|
except AssertionError, e: |
|
|
|
raise Exception('invalid ciphertext: invalid ephemeral pubkey') |
|
|
|
|
|
|
|
if not ecdsa.ecdsa.point_is_valid(generator_secp256k1, ephemeral_pubkey.x(), ephemeral_pubkey.y()): |
|
|
|
raise Exception('invalid ciphertext: invalid ephemeral pubkey') |
|
|
|
|
|
|
|
ecdh_key = (ephemeral_pubkey * self.privkey.secret_multiplier).x() |
|
|
|
ecdh_key = ('%064x' % ecdh_key).decode('hex') |
|
|
|
key = hashlib.sha512(ecdh_key).digest() |
|
|
|
key_e, key_m = key[:32], key[32:] |
|
|
|
if mac != hmac.new(key_m, encrypted[:-32], hashlib.sha256).digest(): |
|
|
|
raise Exception('invalid ciphertext: invalid mac') |
|
|
|
|
|
|
|
return aes.decryptData(key_e, iv_ciphertext) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -779,7 +748,7 @@ def test_crypto(): |
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
|
#test_crypto() |
|
|
|
test_crypto() |
|
|
|
test_bip32("000102030405060708090a0b0c0d0e0f", "m/0'/1/2'/2/1000000000") |
|
|
|
test_bip32("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542","m/0/2147483647'/1/2147483646'/2") |
|
|
|
|
|
|
|