1
1
# Copyright (c) Microsoft Corporation.
2
2
# Licensed under the MIT License.
3
3
4
- from typing import Optional
4
+ from typing import Optional , Tuple
5
5
from pathlib import Path
6
6
from hashlib import sha256
7
7
import datetime
@@ -103,8 +103,8 @@ def create_receipt_contents(self, countersign_tbi: bytes, entry_id: str):
103
103
# Compute Merkle tree leaf hash
104
104
countersign_tbi_hash = sha256 (countersign_tbi ).digest ()
105
105
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 ()
108
108
leaf = sha256 (
109
109
internal_hash + internal_data_hash + countersign_tbi_hash
110
110
).digest ()
@@ -119,7 +119,9 @@ def create_receipt_contents(self, countersign_tbi: bytes, entry_id: str):
119
119
print ("Root: " + root .hex ())
120
120
121
121
# 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 )
123
125
124
126
# Compute Merkle tree proof
125
127
# 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
139
141
140
142
# Compute Merkle tree leaf hash
141
143
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 ()
143
145
leaf = sha256 (
144
146
internal_hash + internal_data_hash + countersign_tbi_hash
145
147
).digest ()
@@ -156,9 +158,10 @@ def verify_receipt_contents(self, receipt_contents: list, countersign_tbi: bytes
156
158
print ("Root: " + root .hex ())
157
159
158
160
# Verify Merkle tree root signature
161
+ signature_dss = convert_p1363_signature_to_dss (signature )
159
162
node_cert = x509 .load_der_x509_certificate (node_cert_der )
160
163
node_cert .public_key ().verify (
161
- signature , root , ec .ECDSA (utils .Prehashed (hashes .SHA256 ()))
164
+ signature_dss , root , ec .ECDSA (utils .Prehashed (hashes .SHA256 ()))
162
165
)
163
166
164
167
# Verify node certificate
@@ -168,6 +171,50 @@ def verify_receipt_contents(self, receipt_contents: list, countersign_tbi: bytes
168
171
verify_certificate_is_issued_by (node_cert , service_cert )
169
172
170
173
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
+
171
218
def verify_certificate_is_issued_by (
172
219
certificate : x509 .Certificate , other : x509 .Certificate
173
220
):
0 commit comments