mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-04-19 00:29:03 +00:00
refactor(core): introduce signature encoding and decoding
[no changelog]
This commit is contained in:
parent
07e90986aa
commit
8c807a16b4
2
core/src/all_modules.py
generated
2
core/src/all_modules.py
generated
@ -91,6 +91,8 @@ trezor.crypto.rlp
|
||||
import trezor.crypto.rlp
|
||||
trezor.crypto.scripts
|
||||
import trezor.crypto.scripts
|
||||
trezor.crypto.signature
|
||||
import trezor.crypto.signature
|
||||
trezor.crypto.slip39
|
||||
import trezor.crypto.slip39
|
||||
trezor.enums.AmountUnit
|
||||
|
@ -13,6 +13,7 @@ async def sign_tx(envelope: BinanceSignTx, keychain: Keychain) -> BinanceSignedT
|
||||
from trezor import wire
|
||||
from trezor.crypto.curve import secp256k1
|
||||
from trezor.crypto.hashlib import sha256
|
||||
from trezor.crypto.signature import encode_raw_signature
|
||||
from trezor.enums import MessageType
|
||||
from trezor.messages import (
|
||||
BinanceCancelMsg,
|
||||
@ -59,6 +60,8 @@ async def sign_tx(envelope: BinanceSignTx, keychain: Keychain) -> BinanceSignedT
|
||||
|
||||
# generate_content_signature
|
||||
msghash = sha256(msg_json.encode()).digest()
|
||||
signature_bytes = secp256k1.sign_recoverable(node.private_key(), msghash)[1:65]
|
||||
signature_bytes = encode_raw_signature(
|
||||
secp256k1.sign_recoverable(node.private_key(), msghash)
|
||||
)
|
||||
|
||||
return BinanceSignedTx(signature=signature_bytes, public_key=node.public_key())
|
||||
|
@ -105,11 +105,11 @@ NONSEGWIT_INPUT_SCRIPT_TYPES = (
|
||||
|
||||
|
||||
def ecdsa_sign(node: bip32.HDNode, digest: bytes) -> bytes:
|
||||
from trezor.crypto import der
|
||||
from trezor.crypto.curve import secp256k1
|
||||
from trezor.crypto.signature import encode_der_signature
|
||||
|
||||
sig = secp256k1.sign_recoverable(node.private_key(), digest)
|
||||
sigder = der.encode_seq((sig[1:33], sig[33:65]))
|
||||
sigder = encode_der_signature(sig)
|
||||
return sigder
|
||||
|
||||
|
||||
|
@ -8,12 +8,13 @@ if TYPE_CHECKING:
|
||||
from apps.common.coininfo import CoinInfo
|
||||
from apps.common.keychain import Keychain
|
||||
|
||||
from trezor.crypto.signature import encode_bip137_signature
|
||||
|
||||
|
||||
@with_keychain
|
||||
async def sign_message(
|
||||
msg: SignMessage, keychain: Keychain, coin: CoinInfo
|
||||
) -> MessageSignature:
|
||||
from trezor import wire
|
||||
from trezor.crypto.curve import secp256k1
|
||||
from trezor.enums import InputScriptType
|
||||
from trezor.messages import MessageSignature
|
||||
@ -52,19 +53,9 @@ async def sign_message(
|
||||
seckey = node.private_key()
|
||||
|
||||
digest = message_digest(coin, message)
|
||||
signature = secp256k1.sign_recoverable(seckey, digest)
|
||||
|
||||
if script_type == InputScriptType.SPENDADDRESS:
|
||||
script_type_info = 0
|
||||
elif script_type == InputScriptType.SPENDP2SHWITNESS:
|
||||
script_type_info = 4
|
||||
elif script_type == InputScriptType.SPENDWITNESS:
|
||||
script_type_info = 8
|
||||
else:
|
||||
raise wire.ProcessError("Unsupported script type")
|
||||
|
||||
# Add script type information to the recovery byte.
|
||||
if script_type_info != 0 and not msg.no_script_type:
|
||||
signature = bytes([signature[0] + script_type_info]) + signature[1:]
|
||||
signature = encode_bip137_signature(
|
||||
secp256k1.sign_recoverable(seckey, digest, False),
|
||||
script_type if not msg.no_script_type else InputScriptType.SPENDADDRESS,
|
||||
)
|
||||
|
||||
return MessageSignature(address=address, signature=signature)
|
||||
|
@ -53,6 +53,7 @@ def _address_to_script_type(address: str, coin: CoinInfo) -> InputScriptType:
|
||||
async def verify_message(msg: VerifyMessage) -> Success:
|
||||
from trezor import TR, utils
|
||||
from trezor.crypto.curve import secp256k1
|
||||
from trezor.crypto.signature import decode_bip137_signature
|
||||
from trezor.enums import InputScriptType
|
||||
from trezor.messages import Success
|
||||
from trezor.ui.layouts import confirm_signverify, show_success
|
||||
@ -77,32 +78,40 @@ async def verify_message(msg: VerifyMessage) -> Success:
|
||||
|
||||
digest = message_digest(coin, message)
|
||||
|
||||
script_type = _address_to_script_type(address, coin)
|
||||
recid = signature[0]
|
||||
if 27 <= recid <= 34:
|
||||
# p2pkh or no script type provided
|
||||
pass # use the script type from the address
|
||||
elif 35 <= recid <= 38 and script_type == InputScriptType.SPENDP2SHWITNESS:
|
||||
# segwit-in-p2sh
|
||||
signature = bytes([signature[0] - 4]) + signature[1:]
|
||||
elif 39 <= recid <= 42 and script_type == InputScriptType.SPENDWITNESS:
|
||||
# native segwit
|
||||
signature = bytes([signature[0] - 8]) + signature[1:]
|
||||
else:
|
||||
raise ProcessError("Invalid signature")
|
||||
address_script_type = _address_to_script_type(address, coin)
|
||||
signature_script_type, recoverable_signature = decode_bip137_signature(signature)
|
||||
|
||||
pubkey = secp256k1.verify_recover(signature, digest)
|
||||
if signature_script_type not in (
|
||||
InputScriptType.SPENDADDRESS,
|
||||
InputScriptType.SPENDADDRESS_UNCOMPRESSED,
|
||||
):
|
||||
if signature_script_type != address_script_type:
|
||||
raise ProcessError("Invalid signature")
|
||||
|
||||
if signature_script_type == InputScriptType.SPENDP2SHWITNESS:
|
||||
recoverable_signature = (
|
||||
bytes([recoverable_signature[0] - 4]) + recoverable_signature[1:]
|
||||
)
|
||||
if signature_script_type == InputScriptType.SPENDWITNESS:
|
||||
recoverable_signature = (
|
||||
bytes([recoverable_signature[0] - 8]) + recoverable_signature[1:]
|
||||
)
|
||||
|
||||
pubkey = secp256k1.verify_recover(
|
||||
recoverable_signature,
|
||||
digest,
|
||||
)
|
||||
|
||||
if not pubkey:
|
||||
raise ProcessError("Invalid signature")
|
||||
|
||||
if script_type == InputScriptType.SPENDADDRESS:
|
||||
if address_script_type == InputScriptType.SPENDADDRESS:
|
||||
addr = address_pkh(pubkey, coin)
|
||||
if not utils.BITCOIN_ONLY and coin.cashaddr_prefix is not None:
|
||||
addr = address_to_cashaddr(addr, coin)
|
||||
elif script_type == InputScriptType.SPENDP2SHWITNESS:
|
||||
elif address_script_type == InputScriptType.SPENDP2SHWITNESS:
|
||||
addr = address_p2wpkh_in_p2sh(pubkey, coin)
|
||||
elif script_type == InputScriptType.SPENDWITNESS:
|
||||
elif address_script_type == InputScriptType.SPENDWITNESS:
|
||||
addr = address_p2wpkh(pubkey, coin)
|
||||
else:
|
||||
raise ProcessError("Invalid signature")
|
||||
|
@ -14,6 +14,17 @@ def base58_encode(prefix: str, sig_prefix: str, data: bytes) -> str:
|
||||
return prefix + b58
|
||||
|
||||
|
||||
def encode_signature(recoverable_signature: bytes) -> str:
|
||||
from trezor.crypto.signature import encode_bip137_signature
|
||||
from trezor.enums import InputScriptType
|
||||
|
||||
return base58_encode(
|
||||
"SIG_",
|
||||
"K1",
|
||||
encode_bip137_signature(recoverable_signature, InputScriptType.SPENDADDRESS),
|
||||
)
|
||||
|
||||
|
||||
def eos_name_to_string(value: int) -> str:
|
||||
charmap = ".12345abcdefghijklmnopqrstuvwxyz"
|
||||
tmp = value
|
||||
|
@ -20,7 +20,7 @@ async def sign_tx(msg: EosSignTx, keychain: Keychain) -> EosSignedTx:
|
||||
from apps.common import paths
|
||||
|
||||
from .actions import process_action
|
||||
from .helpers import base58_encode
|
||||
from .helpers import encode_signature
|
||||
from .layout import require_sign_tx
|
||||
from .writers import write_bytes_fixed, write_header, write_uvarint
|
||||
|
||||
@ -52,7 +52,7 @@ async def sign_tx(msg: EosSignTx, keychain: Keychain) -> EosSignedTx:
|
||||
|
||||
digest = sha.get_digest()
|
||||
signature = secp256k1.sign_recoverable(
|
||||
node.private_key(), digest, True, secp256k1.CANONICAL_SIG_EOS
|
||||
node.private_key(), digest, False, secp256k1.CANONICAL_SIG_EOS
|
||||
)
|
||||
|
||||
return EosSignedTx(signature=base58_encode("SIG_", "K1", signature))
|
||||
return EosSignedTx(signature=encode_signature(signature))
|
||||
|
@ -221,3 +221,25 @@ def _from_bytes_bigendian_signed(b: bytes) -> int:
|
||||
return -result - 1
|
||||
else:
|
||||
return int.from_bytes(b, "big")
|
||||
|
||||
|
||||
def encode_signature(recoverable_signature: bytes) -> bytes:
|
||||
from trezor.crypto.signature import encode_bip137_signature
|
||||
from trezor.enums import InputScriptType
|
||||
|
||||
signature = encode_bip137_signature(
|
||||
recoverable_signature, InputScriptType.SPENDADDRESS_UNCOMPRESSED
|
||||
)
|
||||
return signature[1:] + signature[0:1]
|
||||
|
||||
|
||||
def decode_signature(signature: bytes) -> bytes:
|
||||
from trezor.crypto.signature import decode_bip137_signature
|
||||
from trezor.enums import InputScriptType
|
||||
|
||||
script_type, recoverable_signature = decode_bip137_signature(
|
||||
signature[-1:] + signature[:-1]
|
||||
)
|
||||
if script_type != InputScriptType.SPENDADDRESS_UNCOMPRESSED:
|
||||
raise ValueError("Unsupported script type")
|
||||
return recoverable_signature
|
||||
|
@ -35,7 +35,7 @@ async def sign_message(
|
||||
from apps.common import paths
|
||||
from apps.common.signverify import decode_message
|
||||
|
||||
from .helpers import address_from_bytes
|
||||
from .helpers import address_from_bytes, encode_signature
|
||||
|
||||
await paths.validate_path(keychain, msg.address_n)
|
||||
|
||||
@ -55,5 +55,5 @@ async def sign_message(
|
||||
|
||||
return EthereumMessageSignature(
|
||||
address=address,
|
||||
signature=signature[1:] + bytearray([signature[0]]),
|
||||
signature=encode_signature(signature),
|
||||
)
|
||||
|
@ -33,7 +33,7 @@ async def sign_typed_data(
|
||||
|
||||
from apps.common import paths
|
||||
|
||||
from .helpers import address_from_bytes
|
||||
from .helpers import address_from_bytes, encode_signature
|
||||
from .layout import require_confirm_address
|
||||
|
||||
await paths.validate_path(keychain, msg.address_n)
|
||||
@ -54,7 +54,7 @@ async def sign_typed_data(
|
||||
|
||||
return EthereumTypedDataSignature(
|
||||
address=address_from_bytes(address_bytes, defs.network),
|
||||
signature=signature[1:] + signature[0:1],
|
||||
signature=encode_signature(signature),
|
||||
)
|
||||
|
||||
|
||||
|
@ -14,13 +14,13 @@ async def verify_message(msg: EthereumVerifyMessage) -> Success:
|
||||
|
||||
from apps.common.signverify import decode_message
|
||||
|
||||
from .helpers import address_from_bytes, bytes_from_address
|
||||
from .helpers import address_from_bytes, bytes_from_address, decode_signature
|
||||
from .sign_message import message_digest
|
||||
|
||||
digest = message_digest(msg.message)
|
||||
if len(msg.signature) != 65:
|
||||
raise DataError("Invalid signature")
|
||||
sig = bytearray([msg.signature[64]]) + msg.signature[:64]
|
||||
sig = decode_signature(msg.signature)
|
||||
|
||||
pubkey = secp256k1.verify_recover(sig, digest)
|
||||
|
||||
|
@ -11,9 +11,9 @@ if TYPE_CHECKING:
|
||||
# NOTE: it is one big function because that way it is the most flash-space-efficient
|
||||
@auto_keychain(__name__)
|
||||
async def sign_tx(msg: RippleSignTx, keychain: Keychain) -> RippleSignedTx:
|
||||
from trezor.crypto import der
|
||||
from trezor.crypto.curve import secp256k1
|
||||
from trezor.crypto.hashlib import sha512
|
||||
from trezor.crypto.signature import encode_der_signature
|
||||
from trezor.messages import RippleSignedTx
|
||||
from trezor.wire import ProcessError
|
||||
|
||||
@ -55,7 +55,7 @@ async def sign_tx(msg: RippleSignTx, keychain: Keychain) -> RippleSignedTx:
|
||||
# Signs and encodes signature into DER format
|
||||
first_half_of_sha512 = sha512(to_sign).digest()[:32]
|
||||
sig = secp256k1.sign_recoverable(node.private_key(), first_half_of_sha512)
|
||||
sig_encoded = der.encode_seq((sig[1:33], sig[33:65]))
|
||||
sig_encoded = encode_der_signature(sig)
|
||||
|
||||
tx = serialize(msg, source_address, node.public_key(), sig_encoded)
|
||||
return RippleSignedTx(signature=sig_encoded, serialized_tx=tx)
|
||||
|
@ -7,6 +7,7 @@ import storage.device as storage_device
|
||||
from trezor import utils
|
||||
from trezor.crypto import chacha20poly1305, der, hashlib, hmac, random
|
||||
from trezor.crypto.curve import ed25519, nist256p1
|
||||
from trezor.crypto.signature import encode_der_signature
|
||||
|
||||
from apps.common import cbor, seed
|
||||
from apps.common.paths import HARDENED
|
||||
@ -95,7 +96,7 @@ class Credential:
|
||||
for segment in data:
|
||||
dig.update(segment)
|
||||
sig = nist256p1.sign_recoverable(self._private_key(), dig.digest(), False)
|
||||
return der.encode_seq((sig[1:33], sig[33:]))
|
||||
return encode_der_signature(sig)
|
||||
|
||||
def bogus_signature(self) -> bytes:
|
||||
raise NotImplementedError
|
||||
|
@ -1304,13 +1304,13 @@ def _msg_register(req: Msg, dialog_mgr: DialogManager) -> Cmd:
|
||||
|
||||
|
||||
def basic_attestation_sign(data: Iterable[bytes]) -> bytes:
|
||||
from trezor.crypto import der
|
||||
from trezor.crypto.signature import encode_der_signature
|
||||
|
||||
dig = hashlib.sha256()
|
||||
for segment in data:
|
||||
dig.update(segment)
|
||||
sig = nist256p1.sign_recoverable(_FIDO_ATT_PRIV_KEY, dig.digest(), False)
|
||||
return der.encode_seq((sig[1:33], sig[33:]))
|
||||
return encode_der_signature(sig)
|
||||
|
||||
|
||||
def _msg_register_sign(challenge: bytes, cred: U2fCredential) -> bytes:
|
||||
|
50
core/src/trezor/crypto/signature.py
Normal file
50
core/src/trezor/crypto/signature.py
Normal file
@ -0,0 +1,50 @@
|
||||
from trezor.crypto.der import encode_seq
|
||||
from trezor.enums import InputScriptType
|
||||
|
||||
|
||||
def encode_der_signature(recoverable_signature: bytes) -> bytes:
|
||||
assert len(recoverable_signature) == 65
|
||||
return encode_seq((recoverable_signature[1:33], recoverable_signature[33:65]))
|
||||
|
||||
|
||||
def encode_raw_signature(recoverable_signature: bytes) -> bytes:
|
||||
assert len(recoverable_signature) == 65
|
||||
return recoverable_signature[1:65]
|
||||
|
||||
|
||||
def encode_bip137_signature(
|
||||
recoverable_signature: bytes, script_type: InputScriptType
|
||||
) -> bytes:
|
||||
def get_script_type_number(script_type: InputScriptType) -> int:
|
||||
if script_type == InputScriptType.SPENDADDRESS_UNCOMPRESSED:
|
||||
return 0
|
||||
if script_type == InputScriptType.SPENDADDRESS:
|
||||
return 4
|
||||
elif script_type == InputScriptType.SPENDP2SHWITNESS:
|
||||
return 8
|
||||
elif script_type == InputScriptType.SPENDWITNESS:
|
||||
return 12
|
||||
else:
|
||||
raise ValueError("Unsupported script type")
|
||||
|
||||
assert len(recoverable_signature) == 65
|
||||
header = get_script_type_number(script_type) + recoverable_signature[0]
|
||||
return bytes([header]) + recoverable_signature[1:]
|
||||
|
||||
|
||||
def decode_bip137_signature(signature: bytes) -> tuple[InputScriptType, bytes]:
|
||||
def get_script_type(header: int) -> InputScriptType:
|
||||
if 27 <= header <= 30:
|
||||
return InputScriptType.SPENDADDRESS_UNCOMPRESSED
|
||||
elif 31 <= header <= 34:
|
||||
return InputScriptType.SPENDADDRESS
|
||||
elif 35 <= header <= 38:
|
||||
return InputScriptType.SPENDP2SHWITNESS
|
||||
elif 39 <= header <= 42:
|
||||
return InputScriptType.SPENDWITNESS
|
||||
else:
|
||||
raise ValueError("Unsupported script type")
|
||||
|
||||
assert len(signature) == 65
|
||||
header = signature[0]
|
||||
return get_script_type(header), bytes([header]) + signature[1:]
|
Loading…
Reference in New Issue
Block a user