Coverage for ledger/signature.py: 96%
26 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-07-10 13:43 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-07-10 13:43 +0000
1# The MIT License (MIT)
2#
3# Copyright (c) 2021 RSK Labs Ltd
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy of
6# this software and associated documentation files (the "Software"), to deal in
7# the Software without restriction, including without limitation the rights to
8# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9# of the Software, and to permit persons to whom the Software is furnished to do
10# so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in all
13# copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21# SOFTWARE.
23# Parses a signature received from a powHSM dongle
25class HSM2DongleSignature:
26 def __init__(self, signature_bytes):
27 def error():
28 raise ValueError("Invalid DER-encoded signature: %s" % signature_bytes.hex())
30 # Decode signature_bytes, which should be in DER format
31 # Format:
32 #
33 # 0x30 TOTAL_LENGTH
34 # 0x02 R_LENGTH [R bytes]
35 # 0x02 S_LENGTH [S bytes]
36 # [potential rubbish]
37 #
38 # IMPORTANT: due to a bug, sometimes the first byte is 0x31 and not 0x30.
39 # Deal with it.
40 if (
41 len(signature_bytes) < 2
42 or signature_bytes[0] not in [0x30, 0x31]
43 or len(signature_bytes[2:]) < signature_bytes[1]
44 ):
45 error()
47 # R
48 if (
49 len(signature_bytes[2:]) < 2
50 or signature_bytes[2] != 0x02
51 or len(signature_bytes[4:]) < signature_bytes[3]
52 ):
53 error()
54 r_len = signature_bytes[3]
55 rbytes = signature_bytes[4:4 + r_len]
57 # S
58 if (
59 len(signature_bytes[4 + r_len:]) < 2
60 or signature_bytes[4 + r_len] != 0x02
61 or len(signature_bytes[6 + r_len:]) < signature_bytes[5 + r_len]
62 ):
63 error()
64 s_len = signature_bytes[5 + r_len]
65 sbytes = signature_bytes[6 + r_len:6 + r_len + s_len]
67 self._r = rbytes.hex()
68 self._s = sbytes.hex()
70 @property
71 def r(self):
72 return self._r
74 @property
75 def s(self):
76 return self._s
78 def __repr__(self):
79 return f"{type(self).__name__}<0x{self.r}, 0x{self.s}>"
81 # Self explanatory
82 def __eq__(self, other):
83 return (
84 type(self) == type(other)
85 and self.r == other.r
86 and self.s == other.s
87 )