← Files AgentBrokoARCHIVED FILE
.venv/Lib/site-packages/eth_keys/utils/der.py
3.41 KB · Sep 30, 2026 · 23:15 UTC
# Non-recoverable signatures are encoded using a DER sequence of two integers
# We locally implement serialization and deserialization for this specific spec
# with constrained inputs.
# This is done locally to avoid importing a 3rd-party library, in this very sensitive
# project. asn1tools and pyasn1 were used as reference APIs, see how in
# tests/core/test_utils_asn1.py
#
# See more about DER encodings, and ASN.1 in general, here:
# http://luca.ntop.org/Teaching/Appunti/asn1.html
#
# These methods are NOT intended for external use outside of this project. They do not
# fully validate inputs and make assumptions that are not *generally* true.
from typing import (
Iterator,
Tuple,
)
from eth_utils import (
apply_to_return_value,
big_endian_to_int,
int_to_big_endian,
)
@apply_to_return_value(bytes)
def two_int_sequence_encoder(signature_r: int, signature_s: int) -> Iterator[int]:
"""
Encode two integers using DER, defined as:
::
ECDSASpec DEFINITIONS ::= BEGIN
ECDSASignature ::= SEQUENCE {
r INTEGER,
s INTEGER
}
END
Only a subset of integers are supported: positive, 32-byte ints.
See: https://docs.microsoft.com/en-us/windows/desktop/seccertenroll/about-sequence
"""
# Sequence tag
yield 0x30
encoded1 = _encode_int(signature_r)
encoded2 = _encode_int(signature_s)
# Sequence length
yield len(encoded1) + len(encoded2)
yield from encoded1
yield from encoded2
def two_int_sequence_decoder(encoded: bytes) -> Tuple[int, int]:
"""
Decode bytes to two integers using DER, defined as:
::
ECDSASpec DEFINITIONS ::= BEGIN
ECDSASignature ::= SEQUENCE {
r INTEGER,
s INTEGER
}
END
Only a subset of integers are supported: positive, 32-byte ints.
r is returned first, and s is returned second
See: https://docs.microsoft.com/en-us/windows/desktop/seccertenroll/about-sequence
"""
if encoded[0] != 0x30:
raise ValueError(
f"Encoded sequence must start with 0x30 byte, but got {encoded[0]}"
)
# skip sequence length
int1, rest = _decode_int(encoded[2:])
int2, empty = _decode_int(rest)
if len(empty) != 0:
raise ValueError(
"Encoded sequence must not contain any trailing data, but had "
f"{repr(empty)}"
)
return int1, int2
@apply_to_return_value(bytes)
def _encode_int(primitive: int) -> Iterator[int]:
# See: https://docs.microsoft.com/en-us/windows/desktop/seccertenroll/about-integer
# Integer tag
yield 0x02
encoded = int_to_big_endian(primitive)
if encoded[0] >= 128:
# Indicate that integer is positive
# (it always is, but doesn't always need the flag)
yield len(encoded) + 1
yield 0x00
else:
yield len(encoded)
yield from encoded
def _decode_int(encoded: bytes) -> Tuple[int, bytes]:
# See: https://docs.microsoft.com/en-us/windows/desktop/seccertenroll/about-integer
if encoded[0] != 0x02:
raise ValueError(
"Encoded value must be an integer, starting with on 0x02 byte, but got "
f"{encoded[0]}"
)
length = encoded[1]
# to_int can handle leading zeros
decoded_int = big_endian_to_int(encoded[2 : 2 + length])
return decoded_int, encoded[2 + length :]
SHA-256: a3f0dbd69d6712d03a97180a8f77372fdc4dc049c11fa39abef937c3b31fc2bc