der.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. # Non-recoverable signatures are encoded using a DER sequence of two integers
  2. # We locally implement serialization and deserialization for this specific spec
  3. # with constrained inputs.
  4. # This is done locally to avoid importing a 3rd-party library, in this very sensitive
  5. # project. asn1tools and pyasn1 were used as reference APIs, see how in
  6. # tests/core/test_utils_asn1.py
  7. #
  8. # See more about DER encodings, and ASN.1 in general, here:
  9. # http://luca.ntop.org/Teaching/Appunti/asn1.html
  10. #
  11. # These methods are NOT intended for external use outside of this project. They do not
  12. # fully validate inputs and make assumptions that are not *generally* true.
  13. from typing import (
  14. Iterator,
  15. Tuple,
  16. )
  17. from eth_utils import (
  18. apply_to_return_value,
  19. big_endian_to_int,
  20. int_to_big_endian,
  21. )
  22. @apply_to_return_value(bytes)
  23. def two_int_sequence_encoder(signature_r: int, signature_s: int) -> Iterator[int]:
  24. """
  25. Encode two integers using DER, defined as:
  26. ::
  27. ECDSASpec DEFINITIONS ::= BEGIN
  28. ECDSASignature ::= SEQUENCE {
  29. r INTEGER,
  30. s INTEGER
  31. }
  32. END
  33. Only a subset of integers are supported: positive, 32-byte ints.
  34. See: https://docs.microsoft.com/en-us/windows/desktop/seccertenroll/about-sequence
  35. """
  36. # Sequence tag
  37. yield 0x30
  38. encoded1 = _encode_int(signature_r)
  39. encoded2 = _encode_int(signature_s)
  40. # Sequence length
  41. yield len(encoded1) + len(encoded2)
  42. yield from encoded1
  43. yield from encoded2
  44. def two_int_sequence_decoder(encoded: bytes) -> Tuple[int, int]:
  45. """
  46. Decode bytes to two integers using DER, defined as:
  47. ::
  48. ECDSASpec DEFINITIONS ::= BEGIN
  49. ECDSASignature ::= SEQUENCE {
  50. r INTEGER,
  51. s INTEGER
  52. }
  53. END
  54. Only a subset of integers are supported: positive, 32-byte ints.
  55. r is returned first, and s is returned second
  56. See: https://docs.microsoft.com/en-us/windows/desktop/seccertenroll/about-sequence
  57. """
  58. if encoded[0] != 0x30:
  59. raise ValueError(
  60. f"Encoded sequence must start with 0x30 byte, but got {encoded[0]}"
  61. )
  62. # skip sequence length
  63. int1, rest = _decode_int(encoded[2:])
  64. int2, empty = _decode_int(rest)
  65. if len(empty) != 0:
  66. raise ValueError(
  67. "Encoded sequence must not contain any trailing data, but had "
  68. f"{repr(empty)}"
  69. )
  70. return int1, int2
  71. @apply_to_return_value(bytes)
  72. def _encode_int(primitive: int) -> Iterator[int]:
  73. # See: https://docs.microsoft.com/en-us/windows/desktop/seccertenroll/about-integer
  74. # Integer tag
  75. yield 0x02
  76. encoded = int_to_big_endian(primitive)
  77. if encoded[0] >= 128:
  78. # Indicate that integer is positive
  79. # (it always is, but doesn't always need the flag)
  80. yield len(encoded) + 1
  81. yield 0x00
  82. else:
  83. yield len(encoded)
  84. yield from encoded
  85. def _decode_int(encoded: bytes) -> Tuple[int, bytes]:
  86. # See: https://docs.microsoft.com/en-us/windows/desktop/seccertenroll/about-integer
  87. if encoded[0] != 0x02:
  88. raise ValueError(
  89. "Encoded value must be an integer, starting with on 0x02 byte, but got "
  90. f"{encoded[0]}"
  91. )
  92. length = encoded[1]
  93. # to_int can handle leading zeros
  94. decoded_int = big_endian_to_int(encoded[2 : 2 + length])
  95. return decoded_int, encoded[2 + length :]