Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unsupported hash type ripemd160 #118

Closed
yashasvi-ranawat opened this issue Nov 26, 2022 · 9 comments · Fixed by #120
Closed

Unsupported hash type ripemd160 #118

yashasvi-ranawat opened this issue Nov 26, 2022 · 9 comments · Fixed by #120

Comments

@yashasvi-ranawat
Copy link
Contributor

Tested on OpenSSL 3.0.2, Python 3.10, Linux Ubuntu 22.04 LTS.

Hashlib raises ValueError: unsupported hash type ripemd160, in ripemd160_sha256 function in crypto.py. Found the reason for the issue here.

Hashlib uses OpenSSL for ripemd160 and apparently OpenSSL disabled some older crypto algos around version 3.0 in November 2021. All the functions are still there but require manual enabling. See issue 16994 of OpenSSL github project for details.

The stackoverflow answer details how to enable legacy crypto algorithms, but I think a default solution is needed to use ripemd160.

@merc1er
Copy link
Member

merc1er commented Nov 26, 2022

How does this impact BitCash?

@yashasvi-ranawat
Copy link
Contributor Author

BitCash uses the hash160 (ripemd160_sha256) function to convert public keys to cashaddress and public key hashes for output locking script of P2PKH outputs. This affects the primary functionality of BitCash as a P2PKH wallet.

hash160 uses the hashlib of python, which uses the OpenSSL implementation of ripemd160 which no longer works by default (as tested on OpenSSL 3.0.2, Python 3.10, Linux Ubuntu 22.04 LTS).

@merc1er merc1er changed the title unsupported hash type ripemd160 Unsupported hash type ripemd160 Nov 26, 2022
@merc1er
Copy link
Member

merc1er commented Nov 26, 2022

I am unable to reproduce this.

Can you send a minimal reproducible example?

@yashasvi-ranawat
Copy link
Contributor Author

from hashlib import new, sha256
new("ripemd160", sha256(b"a").digest()).digest()

I tested this on OpenSSL 3.0.2, Python 3.10.6.
Apparently OpenSSL had already marked ripemd160 as legacy algorithm. Do check if you have already enabled legacy_sect as noted here, if you are on the same version of OpenSSL.

This is what my default openssl.cnf file looked like. (only showing relevant options)

openssl_conf = openssl_init

[openssl_init]
providers = provider_sect

[provider_sect]
default = default_sect

[default_sect]
# activate = 1

This led the above python code to give "ValueError: unsupported hash type ripemd160"

After when I enabled legacy algorithms, as given here, the ripemd160 code gave no errors.

@merc1er
Copy link
Member

merc1er commented Nov 26, 2022

from hashlib import new, sha256
new("ripemd160", sha256(b"a").digest()).digest()

This works on my environment (macOS, Python 3.10.6, LibreSSL 3.3.6).

In [1]: from hashlib import new, sha256
   ...: new("ripemd160", sha256(b"a").digest()).digest()
Out[1]: b'\x99CU\x19\x9eQo\xf7lO\xa4\xaa\xb3\x937\xb9\xd8L\xf1+'

@merc1er
Copy link
Member

merc1er commented Nov 26, 2022

I can try to reproduce in a different environment.

What would be a way to solve this within BitCash? And how much work would that involve?

@yashasvi-ranawat
Copy link
Contributor Author

It seems its a OpenSSl only problem. Other bitcoin libraries have dealt with the same: bitcoin/bitcoin#23710.

Most straightforward way would be to code python native ripemd160 code, as done here

@merc1er
Copy link
Member

merc1er commented Nov 26, 2022

Seems pretty straightforward:

def ripemd160(data):
    """Compute the RIPEMD-160 hash of data."""
    # Initialize state.
    state = (0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0)
    # Process full 64-byte blocks in the input.
    for b in range(len(data) >> 6):
        state = compress(*state, data[64*b:64*(b+1)])
    # Construct final blocks (with padding and size).
    pad = b"\x80" + b"\x00" * ((119 - len(data)) & 63)
    fin = data[len(data) & ~63:] + pad + (8 * len(data)).to_bytes(8, 'little')
    # Process final blocks.
    for b in range(len(fin) >> 6):
        state = compress(*state, fin[64*b:64*(b+1)])
    # Produce output.
    return b"".join((h & 0xffffffff).to_bytes(4, 'little') for h in state)

Would you be willing to submit a PR for that?

@merc1er
Copy link
Member

merc1er commented Nov 28, 2022

I am able to reproduce. Looking forward to merging this change 👍🏻

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants