Skip to content
This repository was archived by the owner on Nov 22, 2024. It is now read-only.

Commit a1a3001

Browse files
authored
fix ECDSA signature encoding for CCF tree alg (#22)
1 parent af27fa9 commit a1a3001

File tree

1 file changed

+53
-6
lines changed

1 file changed

+53
-6
lines changed

scitt_emulator/ccf.py

+53-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Copyright (c) Microsoft Corporation.
22
# Licensed under the MIT License.
33

4-
from typing import Optional
4+
from typing import Optional, Tuple
55
from pathlib import Path
66
from hashlib import sha256
77
import datetime
@@ -103,8 +103,8 @@ def create_receipt_contents(self, countersign_tbi: bytes, entry_id: str):
103103
# Compute Merkle tree leaf hash
104104
countersign_tbi_hash = sha256(countersign_tbi).digest()
105105
internal_hash = sha256(b"dummy").digest()
106-
internal_data = f"{entry_id}"
107-
internal_data_hash = sha256(internal_data.encode("ascii")).digest()
106+
internal_data = f"{entry_id}".encode("ascii")
107+
internal_data_hash = sha256(internal_data).digest()
108108
leaf = sha256(
109109
internal_hash + internal_data_hash + countersign_tbi_hash
110110
).digest()
@@ -119,7 +119,9 @@ def create_receipt_contents(self, countersign_tbi: bytes, entry_id: str):
119119
print("Root: " + root.hex())
120120

121121
# Sign root
122-
signature = node_priv_key.sign(root, ec.ECDSA(utils.Prehashed(hashes.SHA256())))
122+
signature_dss = node_priv_key.sign(root, ec.ECDSA(utils.Prehashed(hashes.SHA256())))
123+
curve_size = node_priv_key.curve.key_size // 8
124+
signature = convert_dss_signature_to_p1363(signature_dss, curve_size)
123125

124126
# Compute Merkle tree proof
125127
# Simplification, since the tree has an even number of leaves
@@ -139,7 +141,7 @@ def verify_receipt_contents(self, receipt_contents: list, countersign_tbi: bytes
139141

140142
# Compute Merkle tree leaf hash
141143
countersign_tbi_hash = sha256(countersign_tbi).digest()
142-
internal_data_hash = sha256(internal_data.encode("ascii")).digest()
144+
internal_data_hash = sha256(internal_data).digest()
143145
leaf = sha256(
144146
internal_hash + internal_data_hash + countersign_tbi_hash
145147
).digest()
@@ -156,9 +158,10 @@ def verify_receipt_contents(self, receipt_contents: list, countersign_tbi: bytes
156158
print("Root: " + root.hex())
157159

158160
# Verify Merkle tree root signature
161+
signature_dss = convert_p1363_signature_to_dss(signature)
159162
node_cert = x509.load_der_x509_certificate(node_cert_der)
160163
node_cert.public_key().verify(
161-
signature, root, ec.ECDSA(utils.Prehashed(hashes.SHA256()))
164+
signature_dss, root, ec.ECDSA(utils.Prehashed(hashes.SHA256()))
162165
)
163166

164167
# Verify node certificate
@@ -168,6 +171,50 @@ def verify_receipt_contents(self, receipt_contents: list, countersign_tbi: bytes
168171
verify_certificate_is_issued_by(node_cert, service_cert)
169172

170173

174+
def decode_p1363_signature(signature: bytes) -> Tuple[int, int]:
175+
"""
176+
Decode an ECDSA signature from its IEEE P1363 encoding into its r and s
177+
components. The two integers are padded to the curve size and concatenated.
178+
179+
This is the format used throughout the COSE/JOSE ecosystem.
180+
"""
181+
# The two components are padded to the same size, so we can find the size
182+
# of each one by taking half the size of the signature.
183+
if len(signature) % 2 != 0:
184+
raise ValueError("Signature must be an even number of bytes")
185+
mid = len(signature) // 2
186+
r = int.from_bytes(signature[:mid], "big")
187+
s = int.from_bytes(signature[mid:], "big")
188+
return r, s
189+
190+
191+
def convert_p1363_signature_to_dss(signature: bytes) -> bytes:
192+
"""
193+
Convert an ECDSA signature from its IEEE P1363 encoding to an ASN1/DER
194+
encoding.
195+
196+
The former is the format used throughout the COSE/JOSE ecosystem.
197+
The latter is used by OpenSSL and the cryptography package.
198+
"""
199+
r, s = decode_p1363_signature(signature)
200+
return utils.encode_dss_signature(r, s)
201+
202+
203+
def convert_dss_signature_to_p1363(signature: bytes, curve_size: int) -> bytes:
204+
"""
205+
Convert an ECDSA signature from its ASN1/DER encoding to IEEE P1363
206+
encoding.
207+
208+
The former is used by OpenSSL and the cryptography package.
209+
The latter is the format used throughout the COSE/JOSE ecosystem.
210+
"""
211+
r, s = utils.decode_dss_signature(signature)
212+
try:
213+
return r.to_bytes(curve_size, "big") + s.to_bytes(curve_size, "big")
214+
except OverflowError:
215+
raise ValueError("Signature is too large for given curve size")
216+
217+
171218
def verify_certificate_is_issued_by(
172219
certificate: x509.Certificate, other: x509.Certificate
173220
):

0 commit comments

Comments
 (0)