dcap-qvl 0.3.2__cp38-abi3-musllinux_1_1_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dcap-qvl might be problematic. Click here for more details.
- dcap_qvl/__init__.py +142 -0
- dcap_qvl/__init__.pyi +355 -0
- dcap_qvl/dcap_qvl.abi3.so +0 -0
- dcap_qvl-0.3.2.dist-info/METADATA +389 -0
- dcap_qvl-0.3.2.dist-info/RECORD +6 -0
- dcap_qvl-0.3.2.dist-info/WHEEL +4 -0
dcap_qvl/__init__.py
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DCAP Quote Verification Library
|
|
3
|
+
|
|
4
|
+
This package provides Python bindings for the DCAP (Data Center Attestation Primitives)
|
|
5
|
+
quote verification library implemented in Rust.
|
|
6
|
+
|
|
7
|
+
Main classes:
|
|
8
|
+
- QuoteCollateralV3: Represents quote collateral data
|
|
9
|
+
- VerifiedReport: Contains verification results
|
|
10
|
+
|
|
11
|
+
Main functions:
|
|
12
|
+
- verify: Verify a quote with collateral data
|
|
13
|
+
- get_collateral: Get collateral from PCCS URL
|
|
14
|
+
- get_collateral_from_pcs: Get collateral from Intel PCS
|
|
15
|
+
- get_collateral_and_verify: Get collateral and verify quote
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import time
|
|
19
|
+
import json
|
|
20
|
+
from typing import Optional, Union
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
import requests
|
|
24
|
+
except ImportError:
|
|
25
|
+
requests = None
|
|
26
|
+
|
|
27
|
+
from .dcap_qvl import (
|
|
28
|
+
PyQuoteCollateralV3 as QuoteCollateralV3,
|
|
29
|
+
PyVerifiedReport as VerifiedReport,
|
|
30
|
+
PyQuote as Quote,
|
|
31
|
+
py_verify as verify,
|
|
32
|
+
parse_quote,
|
|
33
|
+
get_collateral_for_fmspc,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Default Intel PCS URL
|
|
37
|
+
PCS_URL = "https://api.trustedservices.intel.com"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# Note: These functions are now implemented in Rust and imported above
|
|
41
|
+
# The manual parsing logic has been replaced with proper Rust-based parsing
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _make_pcs_request(url: str) -> bytes:
|
|
45
|
+
"""Make HTTP request to PCS endpoint."""
|
|
46
|
+
if requests is None:
|
|
47
|
+
raise ImportError(
|
|
48
|
+
"requests library is required for collateral fetching. Install with: pip install requests")
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
response = requests.get(url, timeout=30, verify=False)
|
|
52
|
+
response.raise_for_status()
|
|
53
|
+
return response.content
|
|
54
|
+
except requests.RequestException as e:
|
|
55
|
+
raise RuntimeError(f"Failed to fetch from {url}: {e}")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
async def get_collateral(pccs_url: str, raw_quote: bytes) -> QuoteCollateralV3:
|
|
59
|
+
"""Get collateral from PCCS URL.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
pccs_url: PCCS server URL
|
|
63
|
+
raw_quote: Raw quote bytes
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
QuoteCollateralV3: Quote collateral data
|
|
67
|
+
|
|
68
|
+
Raises:
|
|
69
|
+
ValueError: If quote is invalid or FMSPC cannot be extracted
|
|
70
|
+
RuntimeError: If network request fails
|
|
71
|
+
ImportError: If requests library is not available
|
|
72
|
+
"""
|
|
73
|
+
if not isinstance(raw_quote, (bytes, bytearray)):
|
|
74
|
+
raise TypeError("raw_quote must be bytes")
|
|
75
|
+
|
|
76
|
+
quote = Quote.parse(raw_quote)
|
|
77
|
+
fmspc = quote.fmspc()
|
|
78
|
+
is_sgx = quote.is_sgx()
|
|
79
|
+
ca = quote.ca()
|
|
80
|
+
return await get_collateral_for_fmspc(pccs_url, fmspc, ca, is_sgx)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
async def get_collateral_from_pcs(raw_quote: bytes) -> QuoteCollateralV3:
|
|
84
|
+
"""Get collateral from Intel PCS.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
raw_quote: Raw quote bytes
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
QuoteCollateralV3: Quote collateral data
|
|
91
|
+
|
|
92
|
+
Raises:
|
|
93
|
+
ValueError: If quote is invalid or FMSPC cannot be extracted
|
|
94
|
+
RuntimeError: If network request fails
|
|
95
|
+
ImportError: If requests library is not available
|
|
96
|
+
"""
|
|
97
|
+
return await get_collateral(PCS_URL, raw_quote)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
async def get_collateral_and_verify(raw_quote: bytes, pccs_url: Optional[str] = None) -> VerifiedReport:
|
|
101
|
+
"""Get collateral and verify the quote.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
raw_quote: Raw quote bytes
|
|
105
|
+
pccs_url: Optional PCCS URL (defaults to Intel PCS)
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
VerifiedReport: Verification result
|
|
109
|
+
|
|
110
|
+
Raises:
|
|
111
|
+
ValueError: If quote is invalid or verification fails
|
|
112
|
+
RuntimeError: If network request fails
|
|
113
|
+
ImportError: If requests library is not available
|
|
114
|
+
"""
|
|
115
|
+
# Use provided PCCS URL or default to Intel PCS
|
|
116
|
+
url = pccs_url.strip() if pccs_url else PCS_URL
|
|
117
|
+
if not url:
|
|
118
|
+
url = PCS_URL
|
|
119
|
+
|
|
120
|
+
# Get collateral
|
|
121
|
+
collateral = await get_collateral(url, raw_quote)
|
|
122
|
+
|
|
123
|
+
# Get current time
|
|
124
|
+
now_secs = int(time.time())
|
|
125
|
+
|
|
126
|
+
print("Collateral:", collateral.to_json())
|
|
127
|
+
# Verify quote
|
|
128
|
+
return verify(raw_quote, collateral, now_secs)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
__all__ = [
|
|
132
|
+
"QuoteCollateralV3",
|
|
133
|
+
"VerifiedReport",
|
|
134
|
+
"Quote",
|
|
135
|
+
"verify",
|
|
136
|
+
"get_collateral",
|
|
137
|
+
"get_collateral_from_pcs",
|
|
138
|
+
"get_collateral_and_verify",
|
|
139
|
+
"get_collateral_for_fmspc",
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
__version__ = "0.3.2"
|
dcap_qvl/__init__.pyi
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Type stubs for dcap_qvl Python bindings.
|
|
3
|
+
|
|
4
|
+
This file provides type hints for the compiled Rust extension, enabling better
|
|
5
|
+
IDE support, type checking with mypy, and improved developer experience.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import List
|
|
9
|
+
|
|
10
|
+
__version__: str
|
|
11
|
+
__all__: List[str]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class QuoteCollateralV3:
|
|
15
|
+
"""
|
|
16
|
+
Represents quote collateral data required for DCAP quote verification.
|
|
17
|
+
|
|
18
|
+
This class contains all the necessary certificate chains, CRLs, and
|
|
19
|
+
attestation information needed to verify an SGX or TDX quote.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
pck_crl_issuer_chain: str,
|
|
25
|
+
root_ca_crl: bytes,
|
|
26
|
+
pck_crl: bytes,
|
|
27
|
+
tcb_info_issuer_chain: str,
|
|
28
|
+
tcb_info: str,
|
|
29
|
+
tcb_info_signature: bytes,
|
|
30
|
+
qe_identity_issuer_chain: str,
|
|
31
|
+
qe_identity: str,
|
|
32
|
+
qe_identity_signature: bytes,
|
|
33
|
+
) -> None:
|
|
34
|
+
"""
|
|
35
|
+
Create a new QuoteCollateralV3 instance.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
pck_crl_issuer_chain: PCK CRL issuer certificate chain (PEM format)
|
|
39
|
+
root_ca_crl: Root CA certificate revocation list
|
|
40
|
+
pck_crl: PCK certificate revocation list
|
|
41
|
+
tcb_info_issuer_chain: TCB info issuer certificate chain (PEM format)
|
|
42
|
+
tcb_info: TCB (Trusted Computing Base) information (JSON string)
|
|
43
|
+
tcb_info_signature: Signature for the TCB info
|
|
44
|
+
qe_identity_issuer_chain: QE identity issuer certificate chain (PEM format)
|
|
45
|
+
qe_identity: Quoting Enclave identity information (JSON string)
|
|
46
|
+
qe_identity_signature: Signature for the QE identity
|
|
47
|
+
"""
|
|
48
|
+
...
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def pck_crl_issuer_chain(self) -> str:
|
|
52
|
+
"""PCK CRL issuer certificate chain in PEM format."""
|
|
53
|
+
...
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def root_ca_crl(self) -> bytes:
|
|
57
|
+
"""Root CA certificate revocation list."""
|
|
58
|
+
...
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def pck_crl(self) -> bytes:
|
|
62
|
+
"""PCK certificate revocation list."""
|
|
63
|
+
...
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def tcb_info_issuer_chain(self) -> str:
|
|
67
|
+
"""TCB info issuer certificate chain in PEM format."""
|
|
68
|
+
...
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def tcb_info(self) -> str:
|
|
72
|
+
"""TCB (Trusted Computing Base) information as JSON string."""
|
|
73
|
+
...
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def tcb_info_signature(self) -> bytes:
|
|
77
|
+
"""Signature for the TCB info."""
|
|
78
|
+
...
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def qe_identity_issuer_chain(self) -> str:
|
|
82
|
+
"""QE identity issuer certificate chain in PEM format."""
|
|
83
|
+
...
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def qe_identity(self) -> str:
|
|
87
|
+
"""Quoting Enclave identity information as JSON string."""
|
|
88
|
+
...
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def qe_identity_signature(self) -> bytes:
|
|
92
|
+
"""Signature for the QE identity."""
|
|
93
|
+
...
|
|
94
|
+
|
|
95
|
+
def to_json(self) -> str:
|
|
96
|
+
"""
|
|
97
|
+
Serialize the collateral to a JSON string.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
JSON string representation of the collateral data
|
|
101
|
+
|
|
102
|
+
Raises:
|
|
103
|
+
ValueError: If serialization fails
|
|
104
|
+
"""
|
|
105
|
+
...
|
|
106
|
+
|
|
107
|
+
@staticmethod
|
|
108
|
+
def from_json(json_str: str) -> "QuoteCollateralV3":
|
|
109
|
+
"""
|
|
110
|
+
Create a QuoteCollateralV3 instance from a JSON string.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
json_str: JSON string containing collateral data
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
New QuoteCollateralV3 instance
|
|
117
|
+
|
|
118
|
+
Raises:
|
|
119
|
+
ValueError: If JSON parsing fails or data is invalid
|
|
120
|
+
"""
|
|
121
|
+
...
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class VerifiedReport:
|
|
125
|
+
"""
|
|
126
|
+
Contains the results of DCAP quote verification.
|
|
127
|
+
|
|
128
|
+
This class holds the verification status and any security advisories
|
|
129
|
+
that were found during the quote verification process.
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def status(self) -> str:
|
|
134
|
+
"""
|
|
135
|
+
Verification status string.
|
|
136
|
+
|
|
137
|
+
Common values include:
|
|
138
|
+
- "OK": Verification successful, no issues
|
|
139
|
+
- "SW_HARDENING_NEEDED": Software hardening recommended
|
|
140
|
+
- "CONFIGURATION_NEEDED": Platform configuration required
|
|
141
|
+
- "OUT_OF_DATE": TCB is out of date
|
|
142
|
+
- "REVOKED": Certificate or key has been revoked
|
|
143
|
+
"""
|
|
144
|
+
...
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def advisory_ids(self) -> List[str]:
|
|
148
|
+
"""
|
|
149
|
+
List of security advisory IDs that apply to this quote.
|
|
150
|
+
|
|
151
|
+
These are Intel security advisory identifiers (e.g., "INTEL-SA-00334")
|
|
152
|
+
that indicate known security issues affecting the attested platform.
|
|
153
|
+
"""
|
|
154
|
+
...
|
|
155
|
+
|
|
156
|
+
def to_json(self) -> str:
|
|
157
|
+
"""
|
|
158
|
+
Serialize the verification report to a JSON string.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
JSON string representation of the verification report
|
|
162
|
+
|
|
163
|
+
Raises:
|
|
164
|
+
ValueError: If serialization fails
|
|
165
|
+
"""
|
|
166
|
+
...
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class Quote:
|
|
170
|
+
"""
|
|
171
|
+
Represents a parsed SGX or TDX quote.
|
|
172
|
+
|
|
173
|
+
This class provides access to quote metadata and identifiers
|
|
174
|
+
without requiring collateral data for verification.
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
@staticmethod
|
|
178
|
+
def parse(raw_quote: bytes) -> "Quote":
|
|
179
|
+
"""
|
|
180
|
+
Parse a raw quote from bytes.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
raw_quote: Raw quote data as bytes (SGX or TDX format)
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
Quote instance with parsed quote data
|
|
187
|
+
|
|
188
|
+
Raises:
|
|
189
|
+
ValueError: If quote parsing fails due to invalid format or corrupted data
|
|
190
|
+
|
|
191
|
+
Example:
|
|
192
|
+
>>> import dcap_qvl
|
|
193
|
+
>>>
|
|
194
|
+
>>> with open("quote.bin", "rb") as f:
|
|
195
|
+
... quote_data = f.read()
|
|
196
|
+
>>>
|
|
197
|
+
>>> quote = dcap_qvl.Quote.parse(quote_data)
|
|
198
|
+
>>> print(f"Quote type: {quote.quote_type()}")
|
|
199
|
+
>>> print(f"FMSPC: {quote.fmspc()}")
|
|
200
|
+
"""
|
|
201
|
+
...
|
|
202
|
+
|
|
203
|
+
def fmspc(self) -> str:
|
|
204
|
+
"""
|
|
205
|
+
Extract the FMSPC (Family-Model-Stepping-Platform-CustomSKU) identifier.
|
|
206
|
+
|
|
207
|
+
The FMSPC is a 6-byte identifier that uniquely identifies the
|
|
208
|
+
platform's TCB level and is used for collateral retrieval.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
FMSPC as uppercase hexadecimal string (12 characters)
|
|
212
|
+
|
|
213
|
+
Raises:
|
|
214
|
+
ValueError: If FMSPC cannot be extracted from the quote
|
|
215
|
+
|
|
216
|
+
Example:
|
|
217
|
+
>>> quote = dcap_qvl.Quote.parse(quote_data)
|
|
218
|
+
>>> fmspc = quote.fmspc()
|
|
219
|
+
>>> print(f"FMSPC: {fmspc}") # e.g., "00606A000000"
|
|
220
|
+
"""
|
|
221
|
+
...
|
|
222
|
+
|
|
223
|
+
def ca(self) -> str:
|
|
224
|
+
"""
|
|
225
|
+
Extract the CA (Certificate Authority) identifier.
|
|
226
|
+
|
|
227
|
+
The CA identifier indicates which certificate authority
|
|
228
|
+
should be used for quote verification.
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
CA identifier as string
|
|
232
|
+
|
|
233
|
+
Raises:
|
|
234
|
+
ValueError: If CA identifier cannot be extracted from the quote
|
|
235
|
+
"""
|
|
236
|
+
...
|
|
237
|
+
|
|
238
|
+
def is_tdx(self) -> bool:
|
|
239
|
+
"""
|
|
240
|
+
Check if this is a TDX (Trust Domain Extensions) quote.
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
True if the quote is TDX format, False if SGX format
|
|
244
|
+
|
|
245
|
+
Example:
|
|
246
|
+
>>> quote = dcap_qvl.Quote.parse(quote_data)
|
|
247
|
+
>>> if quote.is_tdx():
|
|
248
|
+
... print("This is a TDX quote")
|
|
249
|
+
... else:
|
|
250
|
+
... print("This is an SGX quote")
|
|
251
|
+
"""
|
|
252
|
+
...
|
|
253
|
+
|
|
254
|
+
def is_sgx(self) -> bool:
|
|
255
|
+
"""
|
|
256
|
+
Check if this is an SGX quote.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
True if the quote is SGX format, False if TDX format
|
|
260
|
+
|
|
261
|
+
Example:
|
|
262
|
+
>>> quote = dcap_qvl.Quote.parse(quote_data)
|
|
263
|
+
>>> if quote.is_sgx():
|
|
264
|
+
... print("This is an SGX quote")
|
|
265
|
+
... else:
|
|
266
|
+
... print("This is a TDX quote")
|
|
267
|
+
"""
|
|
268
|
+
...
|
|
269
|
+
|
|
270
|
+
def quote_type(self) -> str:
|
|
271
|
+
"""
|
|
272
|
+
Get the quote type as a string.
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
"TDX" for TDX quotes, "SGX" for SGX quotes
|
|
276
|
+
|
|
277
|
+
Example:
|
|
278
|
+
>>> quote = dcap_qvl.Quote.parse(quote_data)
|
|
279
|
+
>>> print(f"Quote type: {quote.quote_type()}")
|
|
280
|
+
"""
|
|
281
|
+
...
|
|
282
|
+
|
|
283
|
+
# Synchronous functions
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def verify(
|
|
287
|
+
raw_quote: bytes,
|
|
288
|
+
collateral: QuoteCollateralV3,
|
|
289
|
+
now_secs: int
|
|
290
|
+
) -> VerifiedReport:
|
|
291
|
+
"""
|
|
292
|
+
Verify an SGX or TDX quote with the provided collateral data.
|
|
293
|
+
|
|
294
|
+
This function performs cryptographic verification of the quote against
|
|
295
|
+
the provided collateral information, checking certificates, signatures,
|
|
296
|
+
and revocation status.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
raw_quote: Raw quote data as bytes (SGX or TDX format)
|
|
300
|
+
collateral: Quote collateral containing certificates and attestation data
|
|
301
|
+
now_secs: Current timestamp in seconds since Unix epoch for time-based checks
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
VerifiedReport containing verification status and advisory information
|
|
305
|
+
|
|
306
|
+
Raises:
|
|
307
|
+
ValueError: If verification fails due to invalid data, expired certificates,
|
|
308
|
+
revoked keys, or other verification errors
|
|
309
|
+
|
|
310
|
+
Example:
|
|
311
|
+
>>> import dcap_qvl
|
|
312
|
+
>>> import time
|
|
313
|
+
>>>
|
|
314
|
+
>>> # Load quote and collateral data
|
|
315
|
+
>>> with open("quote.bin", "rb") as f:
|
|
316
|
+
... quote_data = f.read()
|
|
317
|
+
>>>
|
|
318
|
+
>>> collateral = dcap_qvl.QuoteCollateralV3.from_json(collateral_json)
|
|
319
|
+
>>> result = dcap_qvl.verify(quote_data, collateral, int(time.time()))
|
|
320
|
+
>>> print(f"Status: {result.status}")
|
|
321
|
+
"""
|
|
322
|
+
...
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def parse_quote(raw_quote: bytes) -> Quote:
|
|
326
|
+
"""
|
|
327
|
+
Parse a raw quote from bytes (convenience function).
|
|
328
|
+
|
|
329
|
+
This is a convenience function that calls Quote.parse() directly.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
raw_quote: Raw quote data as bytes (SGX or TDX format)
|
|
333
|
+
|
|
334
|
+
Returns:
|
|
335
|
+
Quote instance with parsed quote data
|
|
336
|
+
|
|
337
|
+
Raises:
|
|
338
|
+
ValueError: If quote parsing fails due to invalid format or corrupted data
|
|
339
|
+
|
|
340
|
+
Example:
|
|
341
|
+
>>> import dcap_qvl
|
|
342
|
+
>>>
|
|
343
|
+
>>> with open("quote.bin", "rb") as f:
|
|
344
|
+
... quote_data = f.read()
|
|
345
|
+
>>>
|
|
346
|
+
>>> quote = dcap_qvl.parse_quote(quote_data)
|
|
347
|
+
>>> print(f"Quote type: {quote.quote_type()}")
|
|
348
|
+
>>> print(f"FMSPC: {quote.fmspc()}")
|
|
349
|
+
"""
|
|
350
|
+
...
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
async def get_collateral_for_fmspc(pccs_url: str, fmspc: str, ca: str, is_sgx: bool) -> QuoteCollateralV3:
|
|
354
|
+
"""Get collateral for a specific FMSPC from PCCS URL."""
|
|
355
|
+
...
|
|
Binary file
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dcap-qvl
|
|
3
|
+
Version: 0.3.2
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Intended Audience :: Developers
|
|
6
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Rust
|
|
14
|
+
Classifier: Topic :: Security :: Cryptography
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
16
|
+
Summary: Python bindings for DCAP (Data Center Attestation Primitives) quote verification library
|
|
17
|
+
Keywords: sgx,tdx,dcap,attestation,verification,cryptography
|
|
18
|
+
Author-email: Kevin Wang <wy721@qq.com>
|
|
19
|
+
License: MIT
|
|
20
|
+
Requires-Python: >=3.8
|
|
21
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
22
|
+
Project-URL: Homepage, https://github.com/Phala-Network/dcap-qvl
|
|
23
|
+
Project-URL: Repository, https://github.com/Phala-Network/dcap-qvl
|
|
24
|
+
Project-URL: Issues, https://github.com/Phala-Network/dcap-qvl/issues
|
|
25
|
+
|
|
26
|
+
# Python Bindings for DCAP-QVL
|
|
27
|
+
|
|
28
|
+
This package provides Python bindings for the DCAP (Data Center Attestation Primitives) quote verification library implemented in Rust.
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Install from PyPI
|
|
34
|
+
pip install dcap-qvl
|
|
35
|
+
|
|
36
|
+
# Basic usage
|
|
37
|
+
python -c "
|
|
38
|
+
import dcap_qvl
|
|
39
|
+
print('DCAP-QVL Python bindings successfully installed!')
|
|
40
|
+
print(f'Available functions: {dcap_qvl.__all__}')
|
|
41
|
+
"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- Verify SGX and TDX quotes
|
|
47
|
+
- Handle quote collateral data
|
|
48
|
+
- Parse verification results
|
|
49
|
+
- Pure Rust implementation with Python bindings
|
|
50
|
+
- Cross-platform compatibility (Linux, macOS, Windows)
|
|
51
|
+
- Asynchronous collateral fetching from PCCS/PCS with async/await support
|
|
52
|
+
- Compatible with Python 3.8+
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
### From PyPI (recommended)
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install dcap-qvl
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Using uv
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
uv add dcap-qvl
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Usage
|
|
69
|
+
|
|
70
|
+
### Basic Quote Verification
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
import dcap_qvl
|
|
74
|
+
import json
|
|
75
|
+
import time
|
|
76
|
+
|
|
77
|
+
# Load quote data (binary)
|
|
78
|
+
with open("path/to/quote", "rb") as f:
|
|
79
|
+
quote_data = f.read()
|
|
80
|
+
|
|
81
|
+
# Load collateral data (JSON)
|
|
82
|
+
with open("path/to/collateral.json", "r") as f:
|
|
83
|
+
collateral_json = json.load(f)
|
|
84
|
+
|
|
85
|
+
# Create collateral object
|
|
86
|
+
collateral = dcap_qvl.QuoteCollateralV3.from_json(json.dumps(collateral_json))
|
|
87
|
+
|
|
88
|
+
# Verify the quote
|
|
89
|
+
now = int(time.time())
|
|
90
|
+
try:
|
|
91
|
+
result = dcap_qvl.verify(quote_data, collateral, now)
|
|
92
|
+
print(f"Verification successful! Status: {result.status}")
|
|
93
|
+
print(f"Advisory IDs: {result.advisory_ids}")
|
|
94
|
+
except ValueError as e:
|
|
95
|
+
print(f"Verification failed: {e}")
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Working with Collateral Data
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
# Create collateral manually
|
|
102
|
+
collateral = dcap_qvl.QuoteCollateralV3(
|
|
103
|
+
pck_crl_issuer_chain="...",
|
|
104
|
+
root_ca_crl=b"...", # bytes
|
|
105
|
+
pck_crl=b"...", # bytes
|
|
106
|
+
tcb_info_issuer_chain="...",
|
|
107
|
+
tcb_info="...", # JSON string
|
|
108
|
+
tcb_info_signature=b"...", # bytes
|
|
109
|
+
qe_identity_issuer_chain="...",
|
|
110
|
+
qe_identity="...", # JSON string
|
|
111
|
+
qe_identity_signature=b"...", # bytes
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Serialize to JSON
|
|
115
|
+
json_str = collateral.to_json()
|
|
116
|
+
|
|
117
|
+
# Deserialize from JSON
|
|
118
|
+
collateral = dcap_qvl.QuoteCollateralV3.from_json(json_str)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## API Reference
|
|
122
|
+
|
|
123
|
+
### Async Collateral Functions
|
|
124
|
+
|
|
125
|
+
All collateral functions are asynchronous and must be awaited. They use the Rust async runtime for optimal performance.
|
|
126
|
+
|
|
127
|
+
#### `async get_collateral_for_fmspc(pccs_url: str, fmspc: str, ca: str, is_sgx: bool) -> QuoteCollateralV3`
|
|
128
|
+
|
|
129
|
+
Get collateral for a specific FMSPC directly from PCCS URL (Rust async export).
|
|
130
|
+
|
|
131
|
+
**Parameters:**
|
|
132
|
+
- `pccs_url`: PCCS URL (e.g., "https://api.trustedservices.intel.com")
|
|
133
|
+
- `fmspc`: FMSPC value as hex string (e.g., "B0C06F000000")
|
|
134
|
+
- `ca`: Certificate Authority ("processor" or "platform")
|
|
135
|
+
- `is_sgx`: True for SGX quotes, False for TDX quotes
|
|
136
|
+
|
|
137
|
+
**Returns:**
|
|
138
|
+
- `QuoteCollateralV3`: Quote collateral data
|
|
139
|
+
|
|
140
|
+
**Raises:**
|
|
141
|
+
- `ValueError`: If FMSPC is invalid or collateral cannot be retrieved
|
|
142
|
+
- `RuntimeError`: If network request fails
|
|
143
|
+
|
|
144
|
+
**Example:**
|
|
145
|
+
```python
|
|
146
|
+
import asyncio
|
|
147
|
+
import dcap_qvl
|
|
148
|
+
|
|
149
|
+
async def main():
|
|
150
|
+
collateral = await dcap_qvl.get_collateral_for_fmspc(
|
|
151
|
+
pccs_url="https://api.trustedservices.intel.com",
|
|
152
|
+
fmspc="B0C06F000000",
|
|
153
|
+
ca="processor",
|
|
154
|
+
is_sgx=True
|
|
155
|
+
)
|
|
156
|
+
print(f"Got collateral: {len(collateral.tcb_info)} chars")
|
|
157
|
+
|
|
158
|
+
asyncio.run(main())
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### `async get_collateral(pccs_url: str, raw_quote: bytes) -> QuoteCollateralV3`
|
|
162
|
+
|
|
163
|
+
Get collateral from a custom PCCS URL by parsing the quote.
|
|
164
|
+
|
|
165
|
+
**Parameters:**
|
|
166
|
+
- `pccs_url`: PCCS URL (e.g., "https://api.trustedservices.intel.com")
|
|
167
|
+
- `raw_quote`: Raw quote data as bytes
|
|
168
|
+
|
|
169
|
+
**Returns:**
|
|
170
|
+
- `QuoteCollateralV3`: Quote collateral data
|
|
171
|
+
|
|
172
|
+
**Raises:**
|
|
173
|
+
- `ValueError`: If quote is invalid or FMSPC cannot be extracted
|
|
174
|
+
- `RuntimeError`: If network request fails
|
|
175
|
+
|
|
176
|
+
**Example:**
|
|
177
|
+
```python
|
|
178
|
+
import asyncio
|
|
179
|
+
import dcap_qvl
|
|
180
|
+
|
|
181
|
+
async def main():
|
|
182
|
+
pccs_url = "https://api.trustedservices.intel.com"
|
|
183
|
+
quote_data = open("quote.bin", "rb").read()
|
|
184
|
+
collateral = await dcap_qvl.get_collateral(pccs_url, quote_data)
|
|
185
|
+
print(f"Got collateral: {len(collateral.tcb_info)} chars")
|
|
186
|
+
|
|
187
|
+
asyncio.run(main())
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
#### `async get_collateral_from_pcs(raw_quote: bytes) -> QuoteCollateralV3`
|
|
191
|
+
|
|
192
|
+
Get collateral from Intel's PCS (default).
|
|
193
|
+
|
|
194
|
+
**Parameters:**
|
|
195
|
+
- `raw_quote`: Raw quote data as bytes
|
|
196
|
+
|
|
197
|
+
**Returns:**
|
|
198
|
+
- `QuoteCollateralV3`: Quote collateral data
|
|
199
|
+
|
|
200
|
+
**Raises:**
|
|
201
|
+
- `ValueError`: If quote is invalid or FMSPC cannot be extracted
|
|
202
|
+
- `RuntimeError`: If network request fails
|
|
203
|
+
|
|
204
|
+
**Example:**
|
|
205
|
+
```python
|
|
206
|
+
import asyncio
|
|
207
|
+
import dcap_qvl
|
|
208
|
+
|
|
209
|
+
async def main():
|
|
210
|
+
quote_data = open("quote.bin", "rb").read()
|
|
211
|
+
collateral = await dcap_qvl.get_collateral_from_pcs(quote_data)
|
|
212
|
+
print(f"Got collateral from Intel PCS")
|
|
213
|
+
|
|
214
|
+
asyncio.run(main())
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### `async get_collateral_and_verify(raw_quote: bytes, pccs_url: Optional[str] = None) -> VerifiedReport`
|
|
218
|
+
|
|
219
|
+
Get collateral and verify quote in one step.
|
|
220
|
+
|
|
221
|
+
**Parameters:**
|
|
222
|
+
- `raw_quote`: Raw quote data as bytes
|
|
223
|
+
- `pccs_url`: Optional PCCS URL (uses Intel PCS if None)
|
|
224
|
+
|
|
225
|
+
**Returns:**
|
|
226
|
+
- `VerifiedReport`: Verification results
|
|
227
|
+
|
|
228
|
+
**Raises:**
|
|
229
|
+
- `ValueError`: If quote is invalid or verification fails
|
|
230
|
+
- `RuntimeError`: If network request fails
|
|
231
|
+
|
|
232
|
+
**Example:**
|
|
233
|
+
```python
|
|
234
|
+
import asyncio
|
|
235
|
+
import dcap_qvl
|
|
236
|
+
|
|
237
|
+
async def main():
|
|
238
|
+
quote_data = open("quote.bin", "rb").read()
|
|
239
|
+
result = await dcap_qvl.get_collateral_and_verify(quote_data)
|
|
240
|
+
print(f"Status: {result.status}")
|
|
241
|
+
print(f"Advisory IDs: {result.advisory_ids}")
|
|
242
|
+
|
|
243
|
+
asyncio.run(main())
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Classes
|
|
247
|
+
|
|
248
|
+
#### `QuoteCollateralV3`
|
|
249
|
+
|
|
250
|
+
Represents quote collateral data required for verification.
|
|
251
|
+
|
|
252
|
+
**Constructor:**
|
|
253
|
+
```python
|
|
254
|
+
QuoteCollateralV3(
|
|
255
|
+
pck_crl_issuer_chain: str,
|
|
256
|
+
root_ca_crl: bytes,
|
|
257
|
+
pck_crl: bytes,
|
|
258
|
+
tcb_info_issuer_chain: str,
|
|
259
|
+
tcb_info: str,
|
|
260
|
+
tcb_info_signature: bytes,
|
|
261
|
+
qe_identity_issuer_chain: str,
|
|
262
|
+
qe_identity: str,
|
|
263
|
+
qe_identity_signature: bytes,
|
|
264
|
+
)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Methods:**
|
|
268
|
+
- `to_json() -> str`: Serialize to JSON string
|
|
269
|
+
- `from_json(json_str: str) -> QuoteCollateralV3`: Create from JSON string (static method)
|
|
270
|
+
|
|
271
|
+
**Properties:**
|
|
272
|
+
- `pck_crl_issuer_chain: str`
|
|
273
|
+
- `root_ca_crl: bytes`
|
|
274
|
+
- `pck_crl: bytes`
|
|
275
|
+
- `tcb_info_issuer_chain: str`
|
|
276
|
+
- `tcb_info: str`
|
|
277
|
+
- `tcb_info_signature: bytes`
|
|
278
|
+
- `qe_identity_issuer_chain: str`
|
|
279
|
+
- `qe_identity: str`
|
|
280
|
+
- `qe_identity_signature: bytes`
|
|
281
|
+
|
|
282
|
+
#### `VerifiedReport`
|
|
283
|
+
|
|
284
|
+
Contains the results of quote verification.
|
|
285
|
+
|
|
286
|
+
**Properties:**
|
|
287
|
+
- `status: str`: Verification status
|
|
288
|
+
- `advisory_ids: List[str]`: List of advisory IDs
|
|
289
|
+
|
|
290
|
+
**Methods:**
|
|
291
|
+
- `to_json() -> str`: Serialize to JSON string
|
|
292
|
+
|
|
293
|
+
### Functions
|
|
294
|
+
|
|
295
|
+
#### `verify(raw_quote: bytes, collateral: QuoteCollateralV3, now_secs: int) -> VerifiedReport`
|
|
296
|
+
|
|
297
|
+
Verify a quote with the provided collateral data.
|
|
298
|
+
|
|
299
|
+
**Parameters:**
|
|
300
|
+
- `raw_quote`: Raw quote data as bytes
|
|
301
|
+
- `collateral`: Quote collateral data
|
|
302
|
+
- `now_secs`: Current timestamp in seconds since Unix epoch
|
|
303
|
+
|
|
304
|
+
**Returns:**
|
|
305
|
+
- `VerifiedReport`: Verification results
|
|
306
|
+
|
|
307
|
+
**Raises:**
|
|
308
|
+
- `ValueError`: If verification fails
|
|
309
|
+
|
|
310
|
+
## Development
|
|
311
|
+
|
|
312
|
+
### Building from Source
|
|
313
|
+
|
|
314
|
+
If you want to build from source or contribute to development:
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
# Clone the repository
|
|
318
|
+
git clone https://github.com/Phala-Network/dcap-qvl.git
|
|
319
|
+
cd dcap-qvl/python-bindings
|
|
320
|
+
|
|
321
|
+
# Install development dependencies (including maturin)
|
|
322
|
+
uv sync
|
|
323
|
+
|
|
324
|
+
# Build and install the Python extension in development mode
|
|
325
|
+
uv run maturin develop --features python
|
|
326
|
+
|
|
327
|
+
# Run tests
|
|
328
|
+
uv run python -m pytest tests/test_python_bindings.py
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
**Note:** maturin is only required for building from source. Regular users installing from PyPI don't need maturin.
|
|
332
|
+
|
|
333
|
+
### Running Examples
|
|
334
|
+
|
|
335
|
+
After installing the package, you can run the examples:
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
# Download the examples from the repository
|
|
339
|
+
git clone https://github.com/Phala-Network/dcap-qvl.git
|
|
340
|
+
cd dcap-qvl/python-bindings
|
|
341
|
+
|
|
342
|
+
# Basic functionality test
|
|
343
|
+
python examples/basic_test.py
|
|
344
|
+
|
|
345
|
+
# Full example (requires sample data files)
|
|
346
|
+
python examples/python_example.py
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Or if you're using uv for development:
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
# Basic functionality test
|
|
353
|
+
uv run python examples/basic_test.py
|
|
354
|
+
|
|
355
|
+
# Full example (requires sample data files)
|
|
356
|
+
uv run python examples/python_example.py
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Testing Across Python Versions
|
|
360
|
+
|
|
361
|
+
The project includes comprehensive testing across all supported Python versions:
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
# Quick test across all Python versions
|
|
365
|
+
make test_python_versions
|
|
366
|
+
|
|
367
|
+
# Test current Python version only
|
|
368
|
+
make test_python
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
See [PYTHON_TESTING.md](PYTHON_TESTING.md) for detailed information about Python version compatibility testing.
|
|
372
|
+
|
|
373
|
+
## Requirements
|
|
374
|
+
|
|
375
|
+
### For regular usage (installing from PyPI):
|
|
376
|
+
- Python 3.8+
|
|
377
|
+
|
|
378
|
+
### For development (building from source):
|
|
379
|
+
- Python 3.8+
|
|
380
|
+
- Rust toolchain (rustc, cargo)
|
|
381
|
+
- maturin (automatically installed with `uv sync`)
|
|
382
|
+
|
|
383
|
+
## License
|
|
384
|
+
|
|
385
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
|
386
|
+
|
|
387
|
+
## Contributing
|
|
388
|
+
|
|
389
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
dcap_qvl-0.3.2.dist-info/METADATA,sha256=3N6XPbmZkdJvADf6A4b-V9uPiyFCqiJTZs1Ay82i8us,9879
|
|
2
|
+
dcap_qvl-0.3.2.dist-info/WHEEL,sha256=sxsdX9bP4DpKuoJA2T0GhMh-uehhS-WoJSjbdErb1wc,105
|
|
3
|
+
dcap_qvl/__init__.py,sha256=3c5mhP5xFUou8kcx8ykjMLA34RMPXiD5Q2ATiEsy4_w,3910
|
|
4
|
+
dcap_qvl/__init__.pyi,sha256=eXZBGDagPBOg2T34FFd1GA_xIj-5GxaQUkNWaUHyvMc,9960
|
|
5
|
+
dcap_qvl/dcap_qvl.abi3.so,sha256=kSjp4YhBnJD9pupBMmmC1wXnN3mpIDl656D5D5mvMaE,5938216
|
|
6
|
+
dcap_qvl-0.3.2.dist-info/RECORD,,
|