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

Port ecdasig_parse_der_lax, remove openssl dependency #77

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
if [ "$RUNNER_OS" == "macOS" ]; then
brew install automake openssl@1.1
fi
sh -c 'git clone https://github.com/bitcoin/secp256k1.git /tmp/libsecp256k1 && cd /tmp/libsecp256k1 && git checkout $(grep -A1 '!LIBSECP256K1_COMMIT_MARKER_DO_NOT_MOVE_OR_EDIT!' README.md |tail -n 1|sed -E 's/`([a-f0-9][a-f0-9]*)`/\1/g') && ./autogen.sh && ./configure --disable-coverage --disable-benchmark --disable-tests --disable-exhaustive-tests --enable-module-recovery --enable-module-ecdh --enable-experimental --enable-module-schnorrsig && make && sudo make install'
sh -c 'git clone https://github.com/bitcoin/bitcoin.git /tmp/bitcoin && cd /tmp/bitcoin && git checkout v0.20.1 && ./autogen.sh && ./configure --without-qtdbus --without-qrencode --without-miniupnpc --disable-tests --disable-wallet --disable-zmq --with-libs --disable-util-cli --disable-util-tx --disable-util-wallet --disable-bench --without-daemon --without-gui --disable-fuzz --disable-ccache --disable-static --with-system-libsecp256k1 && make && sudo make install'
python -m pip install flake8 pytest coverage mypy typing-extensions types-contextvars
Expand All @@ -42,4 +39,4 @@ jobs:
env:
PYTHON_BITCOINTX_ALLOW_LIBSECP256K1_EXPERIMENTAL_MODULES_USE: 1
LD_LIBRARY_PATH: /usr/local/lib
run: DYLD_LIBRARY_PATH=/usr/local/lib:/usr/local/opt/openssl@1.1/lib pytest
run: pytest
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ the library and v1.0.0 release in particular, and also has some code examples.
- Python >= 3.6
- [libsecp256k1](https://github.com/bitcoin-core/secp256k1)
- [libbitcoinconsensus](https://github.com/bitcoin/bitcoin/blob/master/doc/shared-libraries.md) (optional, for consensus-compatible script verification)
- [openssl](https://github.com/openssl/openssl) (optional, only for historical signatures verification)

It is recommended to build the libsecp256k1 library by hand, using the following commit:

Expand Down
20 changes: 0 additions & 20 deletions bitcointx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,26 +356,6 @@ def get_custom_secp256k1_path() -> Optional[str]:
return bitcointx.util._secp256k1_library_path


def set_custom_openssl_path(path: str) -> None:
"""Set the custom path that will be used to load openssl library
by bitcointx.core.key module (used only for non-strict signature checking).
For the calling of this function to have any effect, it has to be called
before importing any other modules except 'bitcointx' and 'bitcointx.util'
"""

if not os.path.isfile(path):
raise ValueError('supplied path does not point to a file')

bitcointx.util._openssl_library_path = path


def get_custom_openssl_path() -> Optional[str]:
"""Return the path set earlier by set_custom_openssl_path().
If custom path was not set, None is returned."""

return bitcointx.util._openssl_library_path


class ChainParamsContextVar(bitcointx.util.ContextVarsCompat):
params: ChainParamsBase

Expand Down
5 changes: 3 additions & 2 deletions bitcointx/core/_ripemd160.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@
"""
Pure Python RIPEMD160 implementation.
The code is taken from Bitcoin Core's test framework.
This is needed because the openssl has deprecated ripemd160 algorithm
and it might not be available from hashlib anymore.
This is needed because ripemd160 algorithm might not be available
from hashlib anymore.
Runtime performance will be slow, but the alternative is having a compiled
dependency, which is too heavy.

IMPORTANT: code is not constant-time! This should NOT be used for working
with secret data, such as, for example building a MAC (message
authentication code), etc.
(btw, you cannot expect constant-time behavior from python code at all)
"""

from typing import Tuple
Expand Down
185 changes: 185 additions & 0 deletions bitcointx/core/ecdasig_parse_der_lax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Copyright (C) 2018 The python-bitcointx developers
#
# This file is part of python-bitcointx.
#
# It is subject to the license terms in the LICENSE file found in the top-level
# directory of this distribution.
#
# No part of python-bitcointx, including this file, may be copied, modified,
# propagated, or distributed except according to the terms contained in the
# LICENSE file.
#
# This code is ported from Bitcoin Core, and it was ported to there from
# libsecp256k1 library
# Original C code was Copyright (c) 2015 Pieter Wuille
# Original C code was licensed under MIT software license.

# This function is ported from the libsecp256k1 distribution and implements
# DER parsing for ECDSA signatures, while supporting an arbitrary subset of
# format violations.
#
# Supported violations include negative integers, excessive padding, garbage
# at the end, and overly long length descriptors. This is safe to use in
# Bitcoin because since the activation of BIP66, signatures are verified to be
# strict DER before being passed to this module, and we know it supports all
# violations present in the blockchain before that point.

import ctypes

from bitcointx.core.secp256k1 import (
_secp256k1, COMPACT_SIGNATURE_SIZE, secp256k1_context_verify
)


def ecdsa_signature_parse_der_lax(laxinput: bytes) -> bytes | None: # noqa
rpos: int
rlen: int
spos: int
slen: int
pos: int = 0
lenbyte: int
tmpsig = bytearray([0 for _ in range(64)])
overflow: int = 0

inputlen = len(laxinput)

sig = ctypes.create_string_buffer(COMPACT_SIGNATURE_SIZE)

# Hack to initialize sig with a correctly-parsed but invalid signature. */
_secp256k1.secp256k1_ecdsa_signature_parse_compact(
secp256k1_context_verify, sig, bytes(tmpsig))

# Sequence tag byte
if pos == inputlen or laxinput[pos] != 0x30:
return None

pos += 1

# Sequence length bytes
if pos == inputlen:
return None

lenbyte = laxinput[pos]
pos += 1

if lenbyte & 0x80:
lenbyte -= 0x80
if lenbyte > inputlen - pos:
return None

pos += lenbyte

# Integer tag byte for R
if pos == inputlen or laxinput[pos] != 0x02:
return None

pos += 1

# Integer length for R
if pos == inputlen:
return None

lenbyte = laxinput[pos]
pos += 1

if lenbyte & 0x80:
lenbyte -= 0x80
if lenbyte > inputlen - pos:
return None

while lenbyte > 0 and laxinput[pos] == 0:
pos += 1
lenbyte -= 1

if lenbyte >= 4:
return None

rlen = 0
while lenbyte > 0:
rlen = (rlen << 8) + laxinput[pos]
pos += 1
lenbyte -= 1

else:
rlen = lenbyte

if rlen > inputlen - pos:
return None

rpos = pos
pos += rlen

# Integer tag byte for S
if pos == inputlen or laxinput[pos] != 0x02:
return None

pos += 1

# Integer length for S
if pos == inputlen:
return None

lenbyte = laxinput[pos]
pos += 1

if lenbyte & 0x80:
lenbyte -= 0x80
if lenbyte > inputlen - pos:
return None

while lenbyte > 0 and laxinput[pos] == 0:
pos += 1
lenbyte -= 1

if lenbyte >= 4:
return None

slen = 0
while lenbyte > 0:
slen = (slen << 8) + laxinput[pos]
pos += 1
lenbyte -= 1

else:
slen = lenbyte

if slen > inputlen - pos:
return None

spos = pos

# Ignore leading zeroes in R
while rlen > 0 and laxinput[rpos] == 0:
rlen -= 1
rpos += 1

# Copy R value
if rlen > 32:
overflow = 1
else:
tmpsig[32-rlen:32] = laxinput[rpos:rpos+rlen]

# Ignore leading zeroes in S
while slen > 0 and laxinput[spos] == 0:
slen -= 1
spos += 1

# Copy S value
if slen > 32:
overflow = 1
else:
tmpsig[64-slen:64] = laxinput[spos:spos+slen]

if not overflow:
parse_result = _secp256k1.secp256k1_ecdsa_signature_parse_compact(
secp256k1_context_verify, sig, bytes(tmpsig))
overflow = int(not(parse_result))

if overflow:
# Overwrite the result again with a correctly-parsed but invalid
# signature if parsing failed.
tmpsig = bytearray([0 for _ in range(64)])
_secp256k1.secp256k1_ecdsa_signature_parse_compact(
secp256k1_context_verify, sig, bytes(tmpsig))

return sig.raw
Loading