[#3] Added generate proto script create container method
Signed-off-by: Ilyas Niyazov <i.niyazov@yadro.com>
This commit is contained in:
parent
19282f13cc
commit
3afa14db95
52 changed files with 1381 additions and 74 deletions
1
frostfs_sdk/cryptography/__init__.py
Normal file
1
frostfs_sdk/cryptography/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
# Cryptography directory package
|
100
frostfs_sdk/cryptography/key_extension.py
Normal file
100
frostfs_sdk/cryptography/key_extension.py
Normal file
|
@ -0,0 +1,100 @@
|
|||
import base58
|
||||
import ecdsa
|
||||
import hashlib
|
||||
from struct import pack, unpack
|
||||
from Crypto.Hash import RIPEMD160
|
||||
|
||||
|
||||
COMPRESSED_PUBLIC_KEY_LENGTH = 33
|
||||
NEO_ADDRESS_VERSION = 0x35
|
||||
UNCOMPRESSED_PUBLIC_KEY_LENGTH = 65
|
||||
DECODE_ADDRESS_LENGTH = 21
|
||||
PS_IN_HASH160 = 0x0C
|
||||
CHECK_SIG_DESCRIPTOR = int.from_bytes(
|
||||
hashlib.sha256("System.Crypto.CheckSig".encode('ascii')).digest()[:4], byteorder='little'
|
||||
)
|
||||
|
||||
|
||||
class KeyExtension:
|
||||
def get_private_key_from_wif(self, wif: str) -> bytes:
|
||||
"""
|
||||
Converts a WIF private key to a byte array.
|
||||
|
||||
:param wif: WIF private key in string format.
|
||||
:return: Private key in byte format (32 bytes).
|
||||
:raises ValueError: If the WIF key is incorrect.
|
||||
"""
|
||||
assert not self.is_empty(wif)
|
||||
|
||||
decoded = base58.b58decode_check(wif)
|
||||
if len(decoded) != 34 or decoded[0] != 0x80 or decoded[-1] != 0x01:
|
||||
raise ValueError("Incorrect WIF private key")
|
||||
|
||||
private_key = decoded[1:-1]
|
||||
return private_key
|
||||
|
||||
def get_public_key(self, private_key: bytes) -> bytes:
|
||||
"""
|
||||
Extract public key from Private key
|
||||
|
||||
:param private_key: Private key in byte format (32 bytes).
|
||||
:return: compressed public key in byte format (33 bytes).
|
||||
:raises ValueError: If the private_key key is empty or null.
|
||||
"""
|
||||
assert not self.is_empty(private_key)
|
||||
|
||||
if len(private_key) != 32:
|
||||
raise ValueError(f"Incorrect len of private key, Expected: 32, Actual: {len(private_key)}")
|
||||
|
||||
public_key = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.NIST256p).get_verifying_key()
|
||||
compressed_public_key = public_key.to_string("compressed")
|
||||
return compressed_public_key
|
||||
|
||||
def get_owner_id_by_public_key(self, public_key: bytes) -> str:
|
||||
if len(public_key) != COMPRESSED_PUBLIC_KEY_LENGTH:
|
||||
raise ValueError(f"Encoded compressed public key has wrong length. Expected {COMPRESSED_PUBLIC_KEY_LENGTH}, got {len(public_key)}")
|
||||
|
||||
script_hash = self.get_script_hash(public_key)
|
||||
data = bytearray(DECODE_ADDRESS_LENGTH)
|
||||
data[0] = NEO_ADDRESS_VERSION
|
||||
data[1:] = script_hash
|
||||
return base58.b58encode_check(data).decode('utf-8')
|
||||
|
||||
def get_script_hash(self, public_key: bytes):
|
||||
script = self.create_signature_redeem_script(public_key)
|
||||
sha256_hash = hashlib.sha256(script).digest()
|
||||
return self.get_ripemd160(sha256_hash)
|
||||
|
||||
@staticmethod
|
||||
def create_signature_redeem_script(public_key: bytes):
|
||||
if len(public_key) != COMPRESSED_PUBLIC_KEY_LENGTH:
|
||||
raise ValueError(f"Encoded compressed public key has wrong length. Expected {COMPRESSED_PUBLIC_KEY_LENGTH}, got {len(public_key)}")
|
||||
|
||||
script = bytearray([PS_IN_HASH160, COMPRESSED_PUBLIC_KEY_LENGTH])
|
||||
script.extend(public_key)
|
||||
script.append(UNCOMPRESSED_PUBLIC_KEY_LENGTH)
|
||||
script.extend(pack("<I", CHECK_SIG_DESCRIPTOR))
|
||||
return bytes(script)
|
||||
|
||||
@staticmethod
|
||||
def get_ripemd160(value):
|
||||
if value is None:
|
||||
raise ValueError("Input parameter is missing")
|
||||
|
||||
digest = RIPEMD160.new()
|
||||
digest.update(value)
|
||||
return digest.digest()
|
||||
|
||||
@staticmethod
|
||||
def is_empty(sequence_symbols: bytes | str):
|
||||
if len(sequence_symbols) == 0 or sequence_symbols is None:
|
||||
raise ValueError(f"Empty sequence symbols of key: {sequence_symbols}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def get_hex_string(value):
|
||||
if value is None or len(value) == 0:
|
||||
raise ValueError("Input parameter is missing")
|
||||
|
||||
# Convert byte array to hexadecimal string
|
||||
return f"{int.from_bytes(value, byteorder='big'):0{len(value) * 2}x}"
|
54
frostfs_sdk/cryptography/signer.py
Normal file
54
frostfs_sdk/cryptography/signer.py
Normal file
|
@ -0,0 +1,54 @@
|
|||
import ecdsa
|
||||
from hashlib import sha256, sha512
|
||||
|
||||
from google.protobuf.message import Message
|
||||
|
||||
from frostfs_sdk.client.models.ecdsa_model import ECDSA
|
||||
from frostfs_sdk.protos.models.refs.types_pb2 import SignatureRFC6979, Signature
|
||||
|
||||
|
||||
class Signer:
|
||||
@staticmethod
|
||||
def sign_message_rfc_6979(key: ECDSA, message: Message) -> SignatureRFC6979:
|
||||
return SignatureRFC6979(
|
||||
key=key.public_key,
|
||||
sign=Signer.sign_rfc6979(key.private_key, message.SerializeToString())
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def sign_rfc6979(private_key: bytes, message: bytes) -> bytes:
|
||||
if len(private_key) == 0 or private_key is None:
|
||||
raise ValueError(f"Incorrect private_key: {private_key}")
|
||||
|
||||
sk = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.NIST256p, hashfunc=sha256)
|
||||
|
||||
signature = sk.sign_deterministic(message)
|
||||
|
||||
return signature
|
||||
|
||||
@staticmethod
|
||||
def sign_message(key: ECDSA, message: Message) -> SignatureRFC6979:
|
||||
return SignatureRFC6979(
|
||||
key=key.public_key,
|
||||
sign=Signer.sign(key.private_key, message.SerializeToString())
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def sign(private_key: bytes, message: bytes) -> bytes:
|
||||
if len(private_key) == 0 or private_key is None:
|
||||
raise ValueError(f"Incorrect private key: {private_key}")
|
||||
|
||||
sk = ecdsa.SigningKey.from_string(private_key, curve=ecdsa.NIST256p, hashfunc=sha512)
|
||||
signature = sk.sign(message)
|
||||
|
||||
# the first byte indicates the node version marker
|
||||
signature_with_marker = bytes([0x04]) + signature
|
||||
|
||||
return signature_with_marker
|
||||
|
||||
@staticmethod
|
||||
def _sign_message_part(key: ECDSA, data: Message) -> Signature:
|
||||
return Signature(
|
||||
key=key.public_key,
|
||||
sign=Signature.sign(key.private_key, data.SerializeToString())
|
||||
)
|
50
frostfs_sdk/cryptography/verifier.py
Normal file
50
frostfs_sdk/cryptography/verifier.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
import ecdsa
|
||||
from hashlib import sha256, sha512
|
||||
|
||||
|
||||
class Verifier:
|
||||
def verify_rfc6979(self, public_key: bytes, message: bytes, signature: bytes) -> bool:
|
||||
"""
|
||||
Verify a signature using the public key.
|
||||
|
||||
:param public_key: Public key in byte format.
|
||||
:param message: Signature verification message in byte format.
|
||||
:param signature: Signature in byte format.
|
||||
:return: True if the signature is correct, otherwise False.
|
||||
:raises: ValueError: If the public_key key is incorrect.
|
||||
"""
|
||||
if len(public_key) == 0 or public_key is None:
|
||||
raise ValueError(f"Incorrect public key: {public_key}")
|
||||
|
||||
if message is None or signature is None:
|
||||
return False
|
||||
|
||||
vk = ecdsa.VerifyingKey.from_string(public_key, curve=ecdsa.NIST256p, hashfunc=sha256)
|
||||
|
||||
try:
|
||||
return vk.verify(signature, message)
|
||||
except ecdsa.BadSignatureError:
|
||||
return False
|
||||
|
||||
def verify(self, public_key: bytes, message: bytes, signature: bytes) -> bool:
|
||||
"""
|
||||
Verify a signature using the public key.
|
||||
|
||||
:param public_key: Public key in byte format.
|
||||
:param message: Signature verification message in byte format.
|
||||
:param signature: Signature in byte format.
|
||||
:return: True if the signature is correct, otherwise False.
|
||||
:raises: ValueError: If the public_key key is incorrect.
|
||||
"""
|
||||
if len(public_key) == 0 or public_key is None:
|
||||
raise ValueError(f"Incorrect public key: {public_key}")
|
||||
|
||||
if message is None or signature is None:
|
||||
return False
|
||||
|
||||
vk = ecdsa.VerifyingKey.from_string(public_key, curve=ecdsa.NIST256p, hashfunc=sha512)
|
||||
|
||||
try:
|
||||
return vk.verify(signature, message)
|
||||
except ecdsa.BadSignatureError:
|
||||
return False
|
Loading…
Add table
Add a link
Reference in a new issue