You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

82 lines
2.7 KiB

import base64
import hmac
import hashlib
import pyaes
import os
import jmbitcoin as btc
from bitcointx.core.key import CPubKey
ECIES_MAGIC_BYTES = b'BIE1'
class ECIESDecryptionError(Exception):
pass
# AES primitives. See BIP-SNICKER for specification.
def aes_encrypt(key, data, iv):
encrypter = pyaes.Encrypter(
pyaes.AESModeOfOperationCBC(key, iv=iv))
enc_data = encrypter.feed(data)
enc_data += encrypter.feed()
return enc_data
def aes_decrypt(key, data, iv):
decrypter = pyaes.Decrypter(
pyaes.AESModeOfOperationCBC(key, iv=iv))
try:
dec_data = decrypter.feed(data)
dec_data += decrypter.feed()
except ValueError:
# note decryption errors can come from PKCS7 padding errors
raise ECIESDecryptionError()
return dec_data
def ecies_encrypt(message, pubkey):
""" Take a message in bytes and a secp256k1 public key
in compressed byte serialization, and output the
ECIES encryption, using magic bytes as defined in this module,
sha512 for the key expansion, and AES-CBC for the encryption;
these choices are aligned with that used by Electrum.
"""
# create an ephemeral pubkey for this encryption:
while True:
r = os.urandom(32)
# use compressed serialization of the pubkey R:
try:
R = btc.privkey_to_pubkey(r + b"\x01")
break
except:
# accounts for improbable overflow:
continue
# note that this is *not* ECDH as in the secp256k1_ecdh module,
# since it uses sha512:
ecdh_key = btc.multiply(r, pubkey)
key = hashlib.sha512(ecdh_key).digest()
iv, key_e, key_m = key[0:16], key[16:32], key[32:]
ciphertext = aes_encrypt(key_e, message, iv=iv)
encrypted = ECIES_MAGIC_BYTES + R + ciphertext
mac = hmac.new(key_m, encrypted, hashlib.sha256).digest()
return base64.b64encode(encrypted + mac)
def ecies_decrypt(privkey, encrypted):
if len(privkey) == 33 and privkey[-1] == 1:
privkey = privkey[:32]
encrypted = base64.b64decode(encrypted)
if len(encrypted) < 85:
raise Exception('invalid ciphertext: length')
magic = encrypted[:4]
if magic != ECIES_MAGIC_BYTES:
raise ECIESDecryptionError()
ephemeral_pubkey = encrypted[4:37]
testR = CPubKey(ephemeral_pubkey)
if not testR.is_fullyvalid():
raise ECIESDecryptionError()
ciphertext = encrypted[37:-32]
mac = encrypted[-32:]
ecdh_key = btc.multiply(privkey, ephemeral_pubkey)
key = hashlib.sha512(ecdh_key).digest()
iv, key_e, key_m = key[0:16], key[16:32], key[32:]
if mac != hmac.new(key_m, encrypted[:-32], hashlib.sha256).digest():
raise ECIESDecryptionError()
return aes_decrypt(key_e, ciphertext, iv=iv)