Skip to content

Commit

Permalink
fix(client): rsa and eddsa encoding (#1481)
Browse files Browse the repository at this point in the history
* fix(client): rsa and eddsa encoding

* fix tests

* properly url encode in the async client

* protect code

* add recvwindow
  • Loading branch information
carlosmiei authored Nov 27, 2024
1 parent 4b8d1ed commit 81c2e10
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 6 deletions.
9 changes: 8 additions & 1 deletion binance/async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from urllib.parse import urlencode
import time
import aiohttp
import yarl

from binance.enums import HistoricalKlinesType
from binance.exceptions import (
Expand Down Expand Up @@ -103,8 +104,14 @@ async def _request(
):
kwargs = self._get_request_kwargs(method, signed, force_params, **kwargs)

if method == 'get':
# url encode the query string
if 'params' in kwargs:
uri = f"{uri}?{kwargs['params']}"
kwargs.pop('params')

async with getattr(self.session, method)(
uri, proxy=self.https_proxy, **kwargs
yarl.URL(uri, encoded=True), proxy=self.https_proxy, **kwargs
) as response:
self.response = response
return await self._handle_response(response)
Expand Down
22 changes: 18 additions & 4 deletions binance/base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from Crypto.PublicKey import RSA, ECC
from Crypto.Hash import SHA256
from Crypto.Signature import pkcs1_15, eddsa
import urllib.parse as _urlencode
from operator import itemgetter
from urllib.parse import urlencode

Expand Down Expand Up @@ -59,6 +60,8 @@ class BaseClient:

REQUEST_TIMEOUT: float = 10

REQUEST_RECVWINDOW: int = 10000 # 10 seconds

SYMBOL_TYPE_SPOT = "SPOT"

ORDER_STATUS_NEW = "NEW"
Expand Down Expand Up @@ -300,13 +303,21 @@ def _rsa_signature(self, query_string: str):
assert self.PRIVATE_KEY
h = SHA256.new(query_string.encode("utf-8"))
signature = pkcs1_15.new(self.PRIVATE_KEY).sign(h) # type: ignore
return b64encode(signature).decode()
res = b64encode(signature).decode()
# return self.encode_uri_component(res)
return res

@staticmethod
def encode_uri_component(uri, safe="~()*!.'"):
return _urlencode.quote(uri, safe=safe)

def _ed25519_signature(self, query_string: str):
assert self.PRIVATE_KEY
return b64encode(
res = b64encode(
eddsa.new(self.PRIVATE_KEY, "rfc8032").sign(query_string.encode())
).decode() # type: ignore
# return self.encode_uri_component(res)
return res

def _hmac_signature(self, query_string: str) -> str:
assert self.API_SECRET, "API Secret required for private endpoints"
Expand All @@ -317,15 +328,16 @@ def _hmac_signature(self, query_string: str) -> str:
)
return m.hexdigest()

def _generate_signature(self, data: Dict) -> str:
def _generate_signature(self, data: Dict, uri_encode=True) -> str:
sig_func = self._hmac_signature
if self.PRIVATE_KEY:
if self._is_rsa:
sig_func = self._rsa_signature
else:
sig_func = self._ed25519_signature
query_string = "&".join([f"{d[0]}={d[1]}" for d in self._order_params(data)])
return sig_func(query_string)
res = sig_func(query_string)
return self.encode_uri_component(res) if uri_encode else res

def _sign_ws_params(self, params, signature_func):
if "signature" in params:
Expand Down Expand Up @@ -445,6 +457,8 @@ def _get_request_kwargs(
kwargs["data"]["timestamp"] = int(
time.time() * 1000 + self.timestamp_offset
)
if self.REQUEST_RECVWINDOW:
kwargs["data"]["recvWindow"] = self.REQUEST_RECVWINDOW
kwargs["data"]["signature"] = self._generate_signature(kwargs["data"])

# sort get and post params to match signature order
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cryptography.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def test_encryption():
private_key_pass=case["password"],
ping=False,
)
signature = client._generate_signature(data)
signature = client._generate_signature(data, False)
assert signature == case["expected_signature"], (
f"Test failed: {case['description']}"
)

0 comments on commit 81c2e10

Please sign in to comment.