This repository has been archived on 2024-12-19. You can view files and clone it, but cannot push or open issues or pull requests.
PyBitmessage-2024-12-19/src/highlevelcrypto.py

143 lines
4.8 KiB
Python
Raw Normal View History

"""
High level cryptographic functions based on `.pyelliptic` OpenSSL bindings.
.. note::
Upstream pyelliptic was upgraded from SHA1 to SHA256 for signing.
We must upgrade PyBitmessage gracefully.
`More discussion. <https://github.com/yann2192/pyelliptic/issues/32>`_
"""
2016-03-23 23:26:57 +01:00
from binascii import hexlify
2013-01-16 17:52:52 +01:00
import pyelliptic
from bmconfigparser import BMConfigParser
from pyelliptic import OpenSSL
from pyelliptic import arithmetic as a
2013-01-16 17:52:52 +01:00
def makeCryptor(privkey):
"""Return a private `.pyelliptic.ECC` instance"""
2014-07-15 00:01:56 +02:00
private_key = a.changebase(privkey, 16, 256, minlen=32)
public_key = pointMult(private_key)
privkey_bin = '\x02\xca\x00\x20'.encode('raw_unicode_escape') + private_key
2019-10-22 15:34:32 +02:00
pubkey_bin = '\x02\xca\x00\x20'.encode(
'raw_unicode_escape') + public_key[1:-32] + '\x00\x20'.encode(
'utf-8') + public_key[-32:]
cryptor = pyelliptic.ECC(curve='secp256k1', privkey=privkey_bin, pubkey=pubkey_bin)
2014-07-15 00:01:56 +02:00
return cryptor
2013-01-16 17:52:52 +01:00
def hexToPubkey(pubkey):
"""Convert a pubkey from hex to binary"""
pubkey_raw = a.changebase(pubkey[2:], 16, 256, minlen=64)
2020-01-08 12:45:45 +01:00
pubkey_bin = '\x02\xca\x00 '.encode('raw_unicode_escape') + pubkey_raw[:32] + '\x00 '.encode() + pubkey_raw[32:]
2014-07-15 00:01:56 +02:00
return pubkey_bin
2013-01-16 17:52:52 +01:00
def makePubCryptor(pubkey):
"""Return a public `.pyelliptic.ECC` instance"""
2014-07-15 00:01:56 +02:00
pubkey_bin = hexToPubkey(pubkey)
return pyelliptic.ECC(curve='secp256k1', pubkey=pubkey_bin)
2013-01-16 17:52:52 +01:00
def privToPub(privkey):
"""Converts hex private key into hex public key"""
2014-07-15 00:01:56 +02:00
private_key = a.changebase(privkey, 16, 256, minlen=32)
public_key = pointMult(private_key)
2016-03-23 23:26:57 +01:00
return hexlify(public_key)
def encrypt(msg, hexPubkey):
"""Encrypts message with hex public key"""
return pyelliptic.ECC(curve='secp256k1').encrypt(
msg, hexToPubkey(hexPubkey))
def decrypt(msg, hexPrivkey):
"""Decrypts message with hex private key"""
2014-07-15 00:01:56 +02:00
return makeCryptor(hexPrivkey).decrypt(msg)
def decryptFast(msg, cryptor):
"""Decrypts message with an existing `.pyelliptic.ECC` object"""
2014-07-15 00:01:56 +02:00
return cryptor.decrypt(msg)
def sign(msg, hexPrivkey):
"""
Signs with hex private key using SHA1 or SHA256 depending on
"digestalg" setting
"""
digestAlg = BMConfigParser().safeGet(
'bitmessagesettings', 'digestalg', 'sha1')
if digestAlg == "sha1":
# SHA1, this will eventually be deprecated
return makeCryptor(hexPrivkey).sign(
msg, digest_alg=OpenSSL.digest_ecdsa_sha1)
elif digestAlg == "sha256":
# SHA256. Eventually this will become the default
return makeCryptor(hexPrivkey).sign(msg, digest_alg=OpenSSL.EVP_sha256)
else:
raise ValueError("Unknown digest algorithm %s" % digestAlg)
def verify(msg, sig, hexPubkey):
"""Verifies with hex public key using SHA1 or SHA256"""
2015-03-27 20:25:32 +01:00
# As mentioned above, we must upgrade gracefully to use SHA256. So
# let us check the signature using both SHA1 and SHA256 and if one
# of them passes then we will be satisfied. Eventually this can
# be simplified and we'll only check with SHA256.
2014-08-27 09:14:32 +02:00
try:
# old SHA1 algorithm.
sigVerifyPassed = makePubCryptor(hexPubkey).verify(
sig, msg, digest_alg=OpenSSL.digest_ecdsa_sha1)
2015-03-27 20:25:32 +01:00
except:
sigVerifyPassed = False
if sigVerifyPassed:
# The signature check passed using SHA1
return True
# The signature check using SHA1 failed. Let us try it with SHA256.
2015-03-27 20:25:32 +01:00
try:
return makePubCryptor(hexPubkey).verify(
sig, msg, digest_alg=OpenSSL.EVP_sha256)
2014-08-27 09:14:32 +02:00
except:
return False
def pointMult(secret):
"""
Does an EC point multiplication; turns a private key into a public key.
Evidently, this type of error can occur very rarely:
>>> File "highlevelcrypto.py", line 54, in pointMult
>>> group = OpenSSL.EC_KEY_get0_group(k)
>>> WindowsError: exception: access violation reading 0x0000000000000008
"""
while True:
try:
k = OpenSSL.EC_KEY_new_by_curve_name(
OpenSSL.get_curve('secp256k1'))
priv_key = OpenSSL.BN_bin2bn(secret, 32, None)
group = OpenSSL.EC_KEY_get0_group(k)
pub_key = OpenSSL.EC_POINT_new(group)
OpenSSL.EC_POINT_mul(group, pub_key, priv_key, None, None, None)
OpenSSL.EC_KEY_set_private_key(k, priv_key)
OpenSSL.EC_KEY_set_public_key(k, pub_key)
size = OpenSSL.i2o_ECPublicKey(k, None)
mb = OpenSSL.create_string_buffer(size)
OpenSSL.i2o_ECPublicKey(k, OpenSSL.byref(OpenSSL.pointer(mb)))
OpenSSL.EC_POINT_free(pub_key)
OpenSSL.BN_free(priv_key)
OpenSSL.EC_KEY_free(k)
return mb.raw
except Exception:
import traceback
import time
traceback.print_exc()
time.sleep(0.2)