├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── data
├── Intel_SGX_Provisioning_Certification_RootCA.cer
├── Intel_SGX_Provisioning_Certification_RootCA.pem
├── identityv2_0.json
├── identityv2_1.json
├── intel_root_ca_crl.der
├── pck_certchain.pem
├── pck_platform_crl.der
├── qeidentityv2.json
├── qeidentityv2_apiv4.json
├── quote_tdx_00806f050000.dat
├── signing_cert.pem
├── tcb_info_v3_tdx_0.json
├── tcb_info_v3_tdx_1.json
├── tcb_signing_cert.pem
├── tcbinfov2.json
└── tcbinfov3_00806f050000.json
└── src
├── constants.rs
├── lib.rs
├── types
├── cert.rs
├── collaterals.rs
├── enclave_identity.rs
├── mod.rs
├── quotes
│ ├── body.rs
│ ├── mod.rs
│ ├── version_3.rs
│ └── version_4.rs
└── tcbinfo.rs
└── utils
├── cert.rs
├── crypto.rs
├── enclave_identity.rs
├── hash.rs
├── mod.rs
├── quotes
├── mod.rs
├── version_3.rs
└── version_4.rs
├── tcbinfo.rs
└── tdx_module.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 |
3 |
4 | # Added by cargo
5 | #
6 | # already existing elements were commented out
7 |
8 | #/target
9 | Cargo.lock
10 |
11 | **/.DS_Store
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "dcap-rs"
3 | license = "Apache-2.0"
4 | version = "0.1.0"
5 | edition = "2021"
6 | description = "Intel Data Center Attestation Primitives (DCAP) Quote Verification Library (QVL) implemented in pure Rust."
7 | repository = "https://github.com/automata-network/dcap-rs"
8 | keywords = ["dcap", "intel", "quote", "attestation", "sgx", "rust", "library"]
9 |
10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
11 |
12 | [lib]
13 | crate-type = ["lib"]
14 |
15 | [dependencies]
16 | hex = { version = "0.4" }
17 | x509-parser = { version = "0.15.1" }
18 | serde = { version = "1.0", features = ["derive"] }
19 | serde_json = { version = "1.0" }
20 | chrono = { version = "0.4" }
21 | time = { version = "0.3.36" }
22 |
23 | p256 = { version = "0.13.2" }
24 | sha2 = { version = "0.10.8" }
25 | sha3 = { version = "0.10.8" }
26 | alloy-sol-types = { version = "0.8.12" }
27 |
28 | [features]
29 | default = []
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | # Automata DCAP Rust Library
10 | [](https://github.com/automata-network)
11 |
12 | Intel Data Center Attestation Primitives Quote Verification Library (DCAP QVL) implemented in pure Rust.
13 |
14 | This library can be integrated into zkVM programs that provide users the ability to attest DCAP quotes directly on-chain, by publishing and verifying ZK SNARK proofs in the [AutomataDCAPAttestation](https://github.com/automata-network/automata-dcap-attestation) contract.
15 |
16 | This library supports verification of the following quotes:
17 | - V3 SGX Quotes
18 | - V4 TDX and SGX Quotes
19 |
20 | ## Usage
21 |
22 | To use dcap-rs, add the following to `Cargo.toml`:
23 |
24 | ```
25 | [dependencies]
26 | dcap-rs = { git = "https://github.com/automata-network/dcap-rs.git" }
27 | ```
28 |
29 | ### zkVM Patches
30 |
31 | zkVM programs provide patches, which are simply modified Rust crates that can help reducing execution cycle costs in the VM.
32 |
33 | We have tested `dcap-rs` with both RiscZero and SP1 zkVMs, and we would happily work with more zkVMs in the future.
34 |
35 | Read the section(s) below to learn about how patches can be applied towards corresponding zkVM programs.
36 |
37 | #### RiscZero Accelerators
38 |
39 | Patches applied:
40 | - `crypto-bigint`
41 | - `sha2`
42 | - Our attempt at accelerating [`p256`](https://github.com/automata-network/RustCrypto-elliptic-curves/tree/risczero/p256).
43 |
44 | Make sure to include the following patches into your Guest's `cargo.toml`.
45 |
46 | ```
47 | [patch.crates-io]
48 | sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.6-risczero.0" }
49 | crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.2-risczero.0" }
50 | p256 = { git = "https://github.com/automata-network/RustCrypto-elliptic-curves.git" }
51 | ```
52 |
53 | Click [here](https://dev.risczero.com/api/zkvm/acceleration) to learn more about RiscZero accelerators.
54 |
55 | #### SP1 Precompiles
56 |
57 | Patches applied:
58 | - `crypto-bigint`
59 | - `sha2`
60 |
61 | Make sure to include the following patches into your workspace `cargo.toml`.
62 |
63 | ```
64 | [patch.crates-io]
65 | sha2 = { git = "https://github.com/sp1-patches/RustCrypto-hashes", branch = "patch-sha2-v0.10.8" }
66 | crypto-bigint = { git = "https://github.com/sp1-patches/RustCrypto-bigint", branch = "patch-v0.5.5" }
67 | ```
68 |
69 | Click [here](https://docs.succinct.xyz/writing-programs/precompiles.html) to learn more about SP1 Precompiles.
70 |
71 | ---
72 |
73 | ## Contributing
74 |
75 | **Before You Contribute**:
76 | * **Raise an Issue**: If you find a bug or wish to suggest a feature, please open an issue first to discuss it. Detail the bug or feature so we understand your intention.
77 | * **Pull Requests (PR)**: Before submitting a PR, ensure:
78 | * Your contribution successfully builds.
79 | * It includes tests, if applicable.
80 |
81 | ## License
82 |
83 | Apache License
84 |
--------------------------------------------------------------------------------
/data/Intel_SGX_Provisioning_Certification_RootCA.cer:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/automata-network/dcap-rs/d847b8f75a493640c4881bdf67775250b6baefab/data/Intel_SGX_Provisioning_Certification_RootCA.cer
--------------------------------------------------------------------------------
/data/Intel_SGX_Provisioning_Certification_RootCA.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw
3 | aDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv
4 | cnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ
5 | BgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG
6 | A1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0
7 | aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT
8 | AlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7
9 | 1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB
10 | uzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ
11 | MEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50
12 | ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV
13 | Ur9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI
14 | KoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg
15 | AiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=
16 | -----END CERTIFICATE-----
17 |
--------------------------------------------------------------------------------
/data/identityv2_0.json:
--------------------------------------------------------------------------------
1 | {"enclaveIdentity":{"id":"QE","version":2,"issueDate":"2025-02-12T12:33:04Z","nextUpdate":"2025-03-14T12:33:04Z","tcbEvaluationDataNumber":17,"miscselect":"00000000","miscselectMask":"FFFFFFFF","attributes":"11000000000000000000000000000000","attributesMask":"FBFFFFFFFFFFFFFF0000000000000000","mrsigner":"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF","isvprodid":1,"tcbLevels":[{"tcb":{"isvsvn":8},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"isvsvn":6},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"isvsvn":5},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"isvsvn":4},"tcbDate":"2019-11-13T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"isvsvn":2},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"isvsvn":1},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate"}]},"signature":"39abd09a8e8562598774c2e453e62c977adf4082a70fef08ceb45a92cf50ed0a08b7a907def7ab628c5005492b8cb20ab6864219320c40cd9eca7d900e4dd0a3"}
--------------------------------------------------------------------------------
/data/identityv2_1.json:
--------------------------------------------------------------------------------
1 | {"enclaveIdentity":{"id":"QE","version":2,"issueDate":"2025-02-12T13:33:05Z","nextUpdate":"2025-03-14T13:33:05Z","tcbEvaluationDataNumber":17,"miscselect":"00000000","miscselectMask":"FFFFFFFF","attributes":"11000000000000000000000000000000","attributesMask":"FBFFFFFFFFFFFFFF0000000000000000","mrsigner":"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF","isvprodid":1,"tcbLevels":[{"tcb":{"isvsvn":8},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"isvsvn":6},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"isvsvn":5},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"isvsvn":4},"tcbDate":"2019-11-13T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"isvsvn":2},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"isvsvn":1},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate"}]},"signature":"b49e5ae0a18de3a800d8199a3dbd6fe715dfbec28ff50931f541f72662fb17734c64bbbb2b1e60938ef42d4145f4e0c40fe4d8f2a9ca6fcdc0e671f60b01a0a6"}
--------------------------------------------------------------------------------
/data/intel_root_ca_crl.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/automata-network/dcap-rs/d847b8f75a493640c4881bdf67775250b6baefab/data/intel_root_ca_crl.der
--------------------------------------------------------------------------------
/data/pck_certchain.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIE8jCCBJmgAwIBAgIVAKwPvbp7zozPuADdky+oRn5o6pMuMAoGCCqGSM49BAMC
3 | MHAxIjAgBgNVBAMMGUludGVsIFNHWCBQQ0sgUGxhdGZvcm0gQ0ExGjAYBgNVBAoM
4 | EUludGVsIENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UE
5 | CAwCQ0ExCzAJBgNVBAYTAlVTMB4XDTIzMDgyNDIxMzUzMloXDTMwMDgyNDIxMzUz
6 | MlowcDEiMCAGA1UEAwwZSW50ZWwgU0dYIFBDSyBDZXJ0aWZpY2F0ZTEaMBgGA1UE
7 | CgwRSW50ZWwgQ29ycG9yYXRpb24xFDASBgNVBAcMC1NhbnRhIENsYXJhMQswCQYD
8 | VQQIDAJDQTELMAkGA1UEBhMCVVMwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATE
9 | vKjuKf7iir82hm+MZAQE+hGd3IqmS9b5cNcHJuKzZDYpbo5Ij4LzqvpOP80poARs
10 | YPB3YN5U7pM7wdI61Kfqo4IDDjCCAwowHwYDVR0jBBgwFoAUlW9dzb0b4elAScnU
11 | 9DPOAVcL3lQwawYDVR0fBGQwYjBgoF6gXIZaaHR0cHM6Ly9hcGkudHJ1c3RlZHNl
12 | cnZpY2VzLmludGVsLmNvbS9zZ3gvY2VydGlmaWNhdGlvbi92My9wY2tjcmw/Y2E9
13 | cGxhdGZvcm0mZW5jb2Rpbmc9ZGVyMB0GA1UdDgQWBBQiZvg7901z1qUM8tSLuCXX
14 | eq1LoTAOBgNVHQ8BAf8EBAMCBsAwDAYDVR0TAQH/BAIwADCCAjsGCSqGSIb4TQEN
15 | AQSCAiwwggIoMB4GCiqGSIb4TQENAQEEECX44dpP6CLQTw/xUCWTH0kwggFlBgoq
16 | hkiG+E0BDQECMIIBVTAQBgsqhkiG+E0BDQECAQIBDDAQBgsqhkiG+E0BDQECAgIB
17 | DDAQBgsqhkiG+E0BDQECAwIBAzAQBgsqhkiG+E0BDQECBAIBAzARBgsqhkiG+E0B
18 | DQECBQICAP8wEQYLKoZIhvhNAQ0BAgYCAgD/MBAGCyqGSIb4TQENAQIHAgEBMBAG
19 | CyqGSIb4TQENAQIIAgEAMBAGCyqGSIb4TQENAQIJAgEAMBAGCyqGSIb4TQENAQIK
20 | AgEAMBAGCyqGSIb4TQENAQILAgEAMBAGCyqGSIb4TQENAQIMAgEAMBAGCyqGSIb4
21 | TQENAQINAgEAMBAGCyqGSIb4TQENAQIOAgEAMBAGCyqGSIb4TQENAQIPAgEAMBAG
22 | CyqGSIb4TQENAQIQAgEAMBAGCyqGSIb4TQENAQIRAgENMB8GCyqGSIb4TQENAQIS
23 | BBAMDAMD//8BAAAAAAAAAAAAMBAGCiqGSIb4TQENAQMEAgAAMBQGCiqGSIb4TQEN
24 | AQQEBgBgagAAADAPBgoqhkiG+E0BDQEFCgEBMB4GCiqGSIb4TQENAQYEEBE1xAiQ
25 | rt9E624C1YQkIp4wRAYKKoZIhvhNAQ0BBzA2MBAGCyqGSIb4TQENAQcBAQH/MBAG
26 | CyqGSIb4TQENAQcCAQEAMBAGCyqGSIb4TQENAQcDAQEAMAoGCCqGSM49BAMCA0cA
27 | MEQCIDZocQLdx6+O+XmOkvokeA34Zara4+e9SNXw4Kh9mXvWAiATyiZnIY2GO4fg
28 | I8g3BflNCOVDnBPRpPuY7rtHNw3TpQ==
29 | -----END CERTIFICATE-----
30 | -----BEGIN CERTIFICATE-----
31 | MIICljCCAj2gAwIBAgIVAJVvXc29G+HpQEnJ1PQzzgFXC95UMAoGCCqGSM49BAMC
32 | MGgxGjAYBgNVBAMMEUludGVsIFNHWCBSb290IENBMRowGAYDVQQKDBFJbnRlbCBD
33 | b3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQsw
34 | CQYDVQQGEwJVUzAeFw0xODA1MjExMDUwMTBaFw0zMzA1MjExMDUwMTBaMHAxIjAg
35 | BgNVBAMMGUludGVsIFNHWCBQQ0sgUGxhdGZvcm0gQ0ExGjAYBgNVBAoMEUludGVs
36 | IENvcnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0Ex
37 | CzAJBgNVBAYTAlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENSB/7t21lXSO
38 | 2Cuzpxw74eJB72EyDGgW5rXCtx2tVTLq6hKk6z+UiRZCnqR7psOvgqFeSxlmTlJl
39 | eTmi2WYz3qOBuzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBS
40 | BgNVHR8ESzBJMEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2Vy
41 | dmljZXMuaW50ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUlW9d
42 | zb0b4elAScnU9DPOAVcL3lQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYB
43 | Af8CAQAwCgYIKoZIzj0EAwIDRwAwRAIgXsVki0w+i6VYGW3UF/22uaXe0YJDj1Ue
44 | nA+TjD1ai5cCICYb1SAmD5xkfTVpvo4UoyiSYxrDWLmUR4CI9NKyfPN+
45 | -----END CERTIFICATE-----
46 | -----BEGIN CERTIFICATE-----
47 | MIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw
48 | aDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv
49 | cnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ
50 | BgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG
51 | A1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0
52 | aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT
53 | AlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7
54 | 1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB
55 | uzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ
56 | MEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50
57 | ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV
58 | Ur9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI
59 | KoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg
60 | AiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=
61 | -----END CERTIFICATE-----
62 |
--------------------------------------------------------------------------------
/data/pck_platform_crl.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/automata-network/dcap-rs/d847b8f75a493640c4881bdf67775250b6baefab/data/pck_platform_crl.der
--------------------------------------------------------------------------------
/data/qeidentityv2.json:
--------------------------------------------------------------------------------
1 | {"enclaveIdentity":{"id":"QE","version":2,"issueDate":"2025-02-13T03:33:12Z","nextUpdate":"2025-03-15T03:33:12Z","tcbEvaluationDataNumber":17,"miscselect":"00000000","miscselectMask":"FFFFFFFF","attributes":"11000000000000000000000000000000","attributesMask":"FBFFFFFFFFFFFFFF0000000000000000","mrsigner":"8C4F5775D796503E96137F77C68A829A0056AC8DED70140B081B094490C57BFF","isvprodid":1,"tcbLevels":[{"tcb":{"isvsvn":8},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"isvsvn":6},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"isvsvn":5},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"isvsvn":4},"tcbDate":"2019-11-13T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"isvsvn":2},"tcbDate":"2019-05-15T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"isvsvn":1},"tcbDate":"2018-08-15T00:00:00Z","tcbStatus":"OutOfDate"}]},"signature":"7cdd92c48255c1006054e1b821aafd6e497f5b9e42c5f6e922ae4002b3f57ab8cf4dc8c865a6f0c322efc6bc1db4c1eb2eb88f3370baac091c540c3e5166458c"}
--------------------------------------------------------------------------------
/data/qeidentityv2_apiv4.json:
--------------------------------------------------------------------------------
1 | {"enclaveIdentity":{"id":"TD_QE","version":2,"issueDate":"2025-02-13T03:39:00Z","nextUpdate":"2025-03-15T03:39:00Z","tcbEvaluationDataNumber":17,"miscselect":"00000000","miscselectMask":"FFFFFFFF","attributes":"11000000000000000000000000000000","attributesMask":"FBFFFFFFFFFFFFFF0000000000000000","mrsigner":"DC9E2A7C6F948F17474E34A7FC43ED030F7C1563F1BABDDF6340C82E0E54A8C5","isvprodid":2,"tcbLevels":[{"tcb":{"isvsvn":4},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"UpToDate"}]},"signature":"806c264115a883173594035b667358449d2278e95e865f97e51f39b63cf1ab41fb052859ae37d169ef4e4f9d09ddbf60f26cebb6012eed689eeae2c5230d7a22"}
--------------------------------------------------------------------------------
/data/quote_tdx_00806f050000.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/automata-network/dcap-rs/d847b8f75a493640c4881bdf67775250b6baefab/data/quote_tdx_00806f050000.dat
--------------------------------------------------------------------------------
/data/signing_cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICizCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw
3 | aDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv
4 | cnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ
5 | BgNVBAYTAlVTMB4XDTE4MDUyMTEwNTAxMFoXDTI1MDUyMTEwNTAxMFowbDEeMBwG
6 | A1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw
7 | b3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD
8 | VQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv
9 | P+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju
10 | ypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f
11 | BEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz
12 | LmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK
13 | QEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG
14 | SM49BAMCA0cAMEQCIB9C8wOAN/ImxDtGACV246KcqjagZOR0kyctyBrsGGJVAiAj
15 | ftbrNGsGU8YH211dRiYNoPPu19Zp/ze8JmhujB0oBw==
16 | -----END CERTIFICATE-----
17 |
--------------------------------------------------------------------------------
/data/tcb_info_v3_tdx_0.json:
--------------------------------------------------------------------------------
1 | {"tcbInfo":{"id":"TDX","version":3,"issueDate":"2025-02-12T10:38:59Z","nextUpdate":"2025-03-14T10:38:59Z","fmspc":"90c06f000000","pceId":"0000","tcbType":0,"tcbEvaluationDataNumber":17,"tdxModule":{"mrsigner":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","attributes":"0000000000000000","attributesMask":"FFFFFFFFFFFFFFFF"},"tdxModuleIdentities":[{"id":"TDX_03","mrsigner":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","attributes":"0000000000000000","attributesMask":"FFFFFFFFFFFFFFFF","tcbLevels":[{"tcb":{"isvsvn":3},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"UpToDate"}]},{"id":"TDX_01","mrsigner":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","attributes":"0000000000000000","attributesMask":"FFFFFFFFFFFFFFFF","tcbLevels":[{"tcb":{"isvsvn":4},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"isvsvn":2},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"OutOfDate"}]}],"tcbLevels":[{"tcb":{"sgxtcbcomponents":[{"svn":2,"category":"BIOS","type":"Early Microcode Update"},{"svn":2,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":5,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":13,"tdxtcbcomponents":[{"svn":5,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":2,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"sgxtcbcomponents":[{"svn":2,"category":"BIOS","type":"Early Microcode Update"},{"svn":2,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":5,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":5,"tdxtcbcomponents":[{"svn":5,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":2,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2018-01-04T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00233","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00320","INTEL-SA-00329","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00477","INTEL-SA-00837"]}]},"signature":"80ee91b992045c78855d915513c6ac47b7bfdb4301210cf328f9250cd4885481d4986cc4d43c585acb2ce3f588715912c7de605d57dcfa0a4210dc8d008f3d60"}
--------------------------------------------------------------------------------
/data/tcb_info_v3_tdx_1.json:
--------------------------------------------------------------------------------
1 | {"tcbInfo":{"id":"TDX","version":3,"issueDate":"2025-02-12T11:39:33Z","nextUpdate":"2025-03-14T11:39:33Z","fmspc":"90c06f000000","pceId":"0000","tcbType":0,"tcbEvaluationDataNumber":17,"tdxModule":{"mrsigner":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","attributes":"0000000000000000","attributesMask":"FFFFFFFFFFFFFFFF"},"tdxModuleIdentities":[{"id":"TDX_03","mrsigner":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","attributes":"0000000000000000","attributesMask":"FFFFFFFFFFFFFFFF","tcbLevels":[{"tcb":{"isvsvn":3},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"UpToDate"}]},{"id":"TDX_01","mrsigner":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","attributes":"0000000000000000","attributesMask":"FFFFFFFFFFFFFFFF","tcbLevels":[{"tcb":{"isvsvn":4},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"isvsvn":2},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"OutOfDate"}]}],"tcbLevels":[{"tcb":{"sgxtcbcomponents":[{"svn":2,"category":"BIOS","type":"Early Microcode Update"},{"svn":2,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":5,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":13,"tdxtcbcomponents":[{"svn":5,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":2,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"sgxtcbcomponents":[{"svn":2,"category":"BIOS","type":"Early Microcode Update"},{"svn":2,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":5,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":5,"tdxtcbcomponents":[{"svn":5,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":2,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2018-01-04T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00233","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00320","INTEL-SA-00329","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00477","INTEL-SA-00837"]}]},"signature":"62d866fbddb83317e0e1ba00ebed622253abf10980c38d70220ad4de529a5dec08b2724e49968ba34f4594cdd1e6d2ccd5300a9dc93c14de53663d4f6bc3526d"}
--------------------------------------------------------------------------------
/data/tcb_signing_cert.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICizCCAjKgAwIBAgIUfjiC1ftVKUpASY5FhAPpFJG99FUwCgYIKoZIzj0EAwIw
3 | aDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv
4 | cnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ
5 | BgNVBAYTAlVTMB4XDTE4MDUyMTEwNTAxMFoXDTI1MDUyMTEwNTAxMFowbDEeMBwG
6 | A1UEAwwVSW50ZWwgU0dYIFRDQiBTaWduaW5nMRowGAYDVQQKDBFJbnRlbCBDb3Jw
7 | b3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYD
8 | VQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABENFG8xzydWRfK92bmGv
9 | P+mAh91PEyV7Jh6FGJd5ndE9aBH7R3E4A7ubrlh/zN3C4xvpoouGlirMba+W2lju
10 | ypajgbUwgbIwHwYDVR0jBBgwFoAUImUM1lqdNInzg7SVUr9QGzknBqwwUgYDVR0f
11 | BEswSTBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2Vz
12 | LmludGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5kZXIwHQYDVR0OBBYEFH44gtX7VSlK
13 | QEmORYQD6RSRvfRVMA4GA1UdDwEB/wQEAwIGwDAMBgNVHRMBAf8EAjAAMAoGCCqG
14 | SM49BAMCA0cAMEQCIB9C8wOAN/ImxDtGACV246KcqjagZOR0kyctyBrsGGJVAiAj
15 | ftbrNGsGU8YH211dRiYNoPPu19Zp/ze8JmhujB0oBw==
16 | -----END CERTIFICATE-----
17 | -----BEGIN CERTIFICATE-----
18 | MIICjzCCAjSgAwIBAgIUImUM1lqdNInzg7SVUr9QGzknBqwwCgYIKoZIzj0EAwIw
19 | aDEaMBgGA1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENv
20 | cnBvcmF0aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJ
21 | BgNVBAYTAlVTMB4XDTE4MDUyMTEwNDUxMFoXDTQ5MTIzMTIzNTk1OVowaDEaMBgG
22 | A1UEAwwRSW50ZWwgU0dYIFJvb3QgQ0ExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0
23 | aW9uMRQwEgYDVQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExCzAJBgNVBAYT
24 | AlVTMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEC6nEwMDIYZOj/iPWsCzaEKi7
25 | 1OiOSLRFhWGjbnBVJfVnkY4u3IjkDYYL0MxO4mqsyYjlBalTVYxFP2sJBK5zlKOB
26 | uzCBuDAfBgNVHSMEGDAWgBQiZQzWWp00ifODtJVSv1AbOScGrDBSBgNVHR8ESzBJ
27 | MEegRaBDhkFodHRwczovL2NlcnRpZmljYXRlcy50cnVzdGVkc2VydmljZXMuaW50
28 | ZWwuY29tL0ludGVsU0dYUm9vdENBLmRlcjAdBgNVHQ4EFgQUImUM1lqdNInzg7SV
29 | Ur9QGzknBqwwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwCgYI
30 | KoZIzj0EAwIDSQAwRgIhAOW/5QkR+S9CiSDcNoowLuPRLsWGf/Yi7GSX94BgwTwg
31 | AiEA4J0lrHoMs+Xo5o/sX6O9QWxHRAvZUGOdRQ7cvqRXaqI=
32 | -----END CERTIFICATE-----
--------------------------------------------------------------------------------
/data/tcbinfov2.json:
--------------------------------------------------------------------------------
1 | {"tcbInfo":{"version":2,"issueDate":"2025-02-13T03:29:42Z","nextUpdate":"2025-03-15T03:29:42Z","fmspc":"00606a000000","pceId":"0000","tcbType":0,"tcbEvaluationDataNumber":17,"tcbLevels":[{"tcb":{"sgxtcbcomp01svn":14,"sgxtcbcomp02svn":14,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":1,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":13},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"SWHardeningNeeded"},{"tcb":{"sgxtcbcomp01svn":14,"sgxtcbcomp02svn":14,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":0,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":13},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"ConfigurationAndSWHardeningNeeded"},{"tcb":{"sgxtcbcomp01svn":12,"sgxtcbcomp02svn":12,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":1,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":13},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"sgxtcbcomp01svn":12,"sgxtcbcomp02svn":12,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":0,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":13},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"OutOfDateConfigurationNeeded"},{"tcb":{"sgxtcbcomp01svn":11,"sgxtcbcomp02svn":11,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":1,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":13},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"sgxtcbcomp01svn":11,"sgxtcbcomp02svn":11,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":0,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":13},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"OutOfDateConfigurationNeeded"},{"tcb":{"sgxtcbcomp01svn":7,"sgxtcbcomp02svn":9,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":1,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":13},"tcbDate":"2022-08-10T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"sgxtcbcomp01svn":7,"sgxtcbcomp02svn":9,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":0,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":13},"tcbDate":"2022-08-10T00:00:00Z","tcbStatus":"OutOfDateConfigurationNeeded"},{"tcb":{"sgxtcbcomp01svn":4,"sgxtcbcomp02svn":4,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":0,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":11},"tcbDate":"2021-11-10T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"sgxtcbcomp01svn":4,"sgxtcbcomp02svn":4,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":0,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":10},"tcbDate":"2020-11-11T00:00:00Z","tcbStatus":"OutOfDate"},{"tcb":{"sgxtcbcomp01svn":4,"sgxtcbcomp02svn":4,"sgxtcbcomp03svn":3,"sgxtcbcomp04svn":3,"sgxtcbcomp05svn":255,"sgxtcbcomp06svn":255,"sgxtcbcomp07svn":0,"sgxtcbcomp08svn":0,"sgxtcbcomp09svn":0,"sgxtcbcomp10svn":0,"sgxtcbcomp11svn":0,"sgxtcbcomp12svn":0,"sgxtcbcomp13svn":0,"sgxtcbcomp14svn":0,"sgxtcbcomp15svn":0,"sgxtcbcomp16svn":0,"pcesvn":5},"tcbDate":"2018-01-04T00:00:00Z","tcbStatus":"OutOfDate"}]},"signature":"b73111e67a20491507dd03490f3d128ca9af7d8d12d5dde5120cd67c236b3ad5620372add044a3e0ea65401a6bc7c2c212900828b4a57b8f0895d9946e848029"}
--------------------------------------------------------------------------------
/data/tcbinfov3_00806f050000.json:
--------------------------------------------------------------------------------
1 | {"tcbInfo":{"id":"TDX","version":3,"issueDate":"2025-02-13T03:50:41Z","nextUpdate":"2025-03-15T03:50:41Z","fmspc":"00806f050000","pceId":"0000","tcbType":0,"tcbEvaluationDataNumber":17,"tdxModule":{"mrsigner":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","attributes":"0000000000000000","attributesMask":"FFFFFFFFFFFFFFFF"},"tdxModuleIdentities":[{"id":"TDX_03","mrsigner":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","attributes":"0000000000000000","attributesMask":"FFFFFFFFFFFFFFFF","tcbLevels":[{"tcb":{"isvsvn":3},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"UpToDate"}]},{"id":"TDX_01","mrsigner":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","attributes":"0000000000000000","attributesMask":"FFFFFFFFFFFFFFFF","tcbLevels":[{"tcb":{"isvsvn":4},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"isvsvn":2},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"OutOfDate"}]}],"tcbLevels":[{"tcb":{"sgxtcbcomponents":[{"svn":7,"category":"BIOS","type":"Early Microcode Update"},{"svn":7,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":3,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11,"tdxtcbcomponents":[{"svn":5,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":7,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2024-03-13T00:00:00Z","tcbStatus":"UpToDate"},{"tcb":{"sgxtcbcomponents":[{"svn":6,"category":"BIOS","type":"Early Microcode Update"},{"svn":6,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":3,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11,"tdxtcbcomponents":[{"svn":3,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":6,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2023-08-09T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00960","INTEL-SA-00982","INTEL-SA-00986"]},{"tcb":{"sgxtcbcomponents":[{"svn":5,"category":"BIOS","type":"Early Microcode Update"},{"svn":5,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":3,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":11,"tdxtcbcomponents":[{"svn":3,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":5,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2023-02-15T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00837","INTEL-SA-00960","INTEL-SA-00982","INTEL-SA-00986"]},{"tcb":{"sgxtcbcomponents":[{"svn":5,"category":"BIOS","type":"Early Microcode Update"},{"svn":5,"category":"OS/VMM","type":"SGX Late Microcode Update"},{"svn":2,"category":"OS/VMM","type":"TXT SINIT"},{"svn":2,"category":"BIOS"},{"svn":3,"category":"BIOS"},{"svn":1,"category":"BIOS"},{"svn":0},{"svn":3,"category":"OS/VMM","type":"SEAMLDR ACM"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}],"pcesvn":5,"tdxtcbcomponents":[{"svn":3,"category":"OS/VMM","type":"TDX Module"},{"svn":0,"category":"OS/VMM","type":"TDX Module"},{"svn":5,"category":"OS/VMM","type":"TDX Late Microcode Update"},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0},{"svn":0}]},"tcbDate":"2018-01-04T00:00:00Z","tcbStatus":"OutOfDate","advisoryIDs":["INTEL-SA-00106","INTEL-SA-00115","INTEL-SA-00135","INTEL-SA-00203","INTEL-SA-00220","INTEL-SA-00233","INTEL-SA-00270","INTEL-SA-00293","INTEL-SA-00320","INTEL-SA-00329","INTEL-SA-00381","INTEL-SA-00389","INTEL-SA-00477","INTEL-SA-00837","INTEL-SA-00960","INTEL-SA-00982","INTEL-SA-00986"]}]},"signature":"efd79a5c3445d3af81068fc0bc6ac013a87c6aea91088c4373eac8de0b9a99ee27de6eaf3e28a6ad77f1199fb283edbb99ed8940b4dac568d78102e590a7dc71"}
--------------------------------------------------------------------------------
/src/constants.rs:
--------------------------------------------------------------------------------
1 | // https://github.com/intel/SGX-TDX-DCAP-QuoteVerificationLibrary/blob/16b7291a7a86e486fdfcf1dfb4be885c0cc00b4e/Src/AttestationLibrary/src/QuoteVerification/QuoteConstants.h
2 |
3 | pub const SGX_TEE_TYPE: u32 = 0x00000000;
4 | pub const TDX_TEE_TYPE: u32 = 0x00000081;
5 |
6 | pub const ECDSA_256_WITH_P256_CURVE: u16 = 2;
7 | pub const ECDSA_384_WITH_P384_CURVE: u16 = 3;
8 |
9 | pub const HEADER_LEN: usize = 48;
10 |
11 | pub const ENCLAVE_REPORT_LEN: usize = 384;
12 | pub const TD10_REPORT_LEN: usize = 584;
13 | pub const TD15_REPORT_LEN: usize = 684;
14 |
15 | pub const INTEL_QE_VENDOR_ID: [u8; 16] = [0x93, 0x9A, 0x72, 0x33, 0xF7, 0x9C, 0x4C, 0xA9, 0x94, 0x0A, 0x0D, 0xB3, 0x95, 0x7F, 0x06, 0x07];
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod types;
2 | pub mod utils;
3 | pub mod constants;
4 |
5 | use x509_parser::certificate::X509Certificate;
6 |
7 | #[cfg(test)]
8 | mod tests {
9 | use crate::types::tcbinfo::{TcbInfoV2, TcbInfoV3};
10 | use crate::types::enclave_identity::EnclaveIdentityV2;
11 | use crate::types::quotes::{version_4::QuoteV4, version_3::QuoteV3};
12 | use crate::types::collaterals::IntelCollateral;
13 |
14 | use crate::utils::cert::{hash_crl_keccak256, hash_x509_keccak256, parse_crl_der, parse_pem, parse_x509_der, verify_crl};
15 | use crate::utils::tcbinfo::{validate_tcbinfov2, validate_tcbinfov3, get_tcbinfov3_content_hash};
16 | use crate::utils::enclave_identity::get_enclave_identityv2_content_hash;
17 | use crate::utils::quotes::{
18 | version_3::verify_quote_dcapv3,
19 | version_4::verify_quote_dcapv4
20 | };
21 |
22 | // Pinned September 10th, 2024, 6:49am GMT
23 | // there's no need for constant sample collateral updates
24 | const PINNED_TIME: u64 = 1739419232;
25 |
26 | #[test]
27 | fn test_root_crl_verify() {
28 | let intel_sgx_root_ca = parse_x509_der(include_bytes!("../data/Intel_SGX_Provisioning_Certification_RootCA.cer"));
29 | let intel_sgx_root_ca_crl = parse_crl_der(include_bytes!("../data/intel_root_ca_crl.der"));
30 |
31 | assert!(verify_crl(&intel_sgx_root_ca_crl, &intel_sgx_root_ca, PINNED_TIME));
32 | }
33 |
34 | #[test]
35 | fn test_tcbinfov3() {
36 | // let current_time = chrono::Utc::now().timestamp() as u64;
37 |
38 | let tcbinfov3_json = include_str!("../data/tcbinfov3_00806f050000.json");
39 | let tcbinfov3: TcbInfoV3 = serde_json::from_str(tcbinfov3_json).unwrap();
40 | let tcbinfov3_serialize = serde_json::to_string(&tcbinfov3).unwrap();
41 | assert!(tcbinfov3_serialize == tcbinfov3_json);
42 |
43 | let sgx_signing_cert_pem = &parse_pem(include_bytes!("../data/signing_cert.pem")).unwrap()[0];
44 | let sgx_signing_cert = parse_x509_der(&sgx_signing_cert_pem.contents);
45 |
46 | assert!(validate_tcbinfov3(&tcbinfov3, &sgx_signing_cert, PINNED_TIME));
47 | }
48 |
49 | #[test]
50 | fn test_tcbinfov2() {
51 | // let current_time = chrono::Utc::now().timestamp() as u64;
52 |
53 | let tcbinfov2_json = include_str!("../data/tcbinfov2.json");
54 | let tcbinfov2: TcbInfoV2 = serde_json::from_str(tcbinfov2_json).unwrap();
55 | let tcbinfov2_serialize = serde_json::to_string(&tcbinfov2).unwrap();
56 | assert!(tcbinfov2_serialize == tcbinfov2_json);
57 |
58 | let sgx_signing_cert_pem = &parse_pem(include_bytes!("../data/signing_cert.pem")).unwrap()[0];
59 | let sgx_signing_cert = parse_x509_der(&sgx_signing_cert_pem.contents);
60 |
61 | assert!(validate_tcbinfov2(&tcbinfov2, &sgx_signing_cert, PINNED_TIME));
62 | }
63 |
64 | #[test]
65 | fn test_quotev4() {
66 | let quotev4_slice = include_bytes!("../data/quote_tdx_00806f050000.dat");
67 | let quotev4 = QuoteV4::from_bytes(quotev4_slice);
68 | assert_eq!(quotev4.header.version, 4);
69 | }
70 |
71 | #[test]
72 | fn test_verifyv3() {
73 | // let current_time = chrono::Utc::now().timestamp() as u64;
74 |
75 | let mut collaterals = IntelCollateral::new();
76 | collaterals.set_tcbinfo_bytes(include_bytes!("../data/tcbinfov2.json"));
77 | collaterals.set_qeidentity_bytes(include_bytes!("../data/qeidentityv2.json"));
78 | collaterals.set_intel_root_ca_der(include_bytes!("../data/Intel_SGX_Provisioning_Certification_RootCA.cer"));
79 | collaterals.set_sgx_tcb_signing_pem(include_bytes!("../data/signing_cert.pem"));
80 | collaterals.set_sgx_intel_root_ca_crl_der(include_bytes!("../data/intel_root_ca_crl.der"));
81 | collaterals.set_sgx_platform_crl_der(include_bytes!("../data/pck_platform_crl.der"));
82 | // collaterals.set_sgx_processor_crl_der(include_bytes!("../data/pck_processor_crl.der"));
83 |
84 |
85 | let dcap_quote_bytes = hex::decode("030002000000000009000e00939a7233f79c4ca9940a0db3957f0607ad04024c9dfb382baf51ca3e5d6cb6e6000000000c0c100fffff0100000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000e700000000000000a4f45c39dac622cb1dd32ddb35a52ec92db41d0fa88a1c911c49e59c534f61cd00000000000000000000000000000000000000000000000000000000000000001bda23eb3a807dfe735ddcebbfa2eac05e04a00df2804296612f770b594180ba0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ca100000e78d2532cbef391dea9a477119bc505b47e187f6f045636cce8bcf41604a099232eee31b3ef3827c442eb5d5981610480deb0625ed4b01c1ac2b0fb43e05efdeab8af342a611fb608193d9a47b8111654172adf2dabd2d428d28ebe094b9baa1f8f7e240b015af174d4f58a6b201946eee2097af02ed554909779ea2d9f3c1020c0c100fffff0100000000000000000000000000000000000000000000000000000000000000000000000000000000001500000000000000e700000000000000192aa50ce1c0cef03ccf89e7b5b16b0d7978f5c2b1edcf774d87702e8154d8bf00000000000000000000000000000000000000000000000000000000000000008c4f5775d796503e96137f77c68a829a0056ac8ded70140b081b094490c57bff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a82754acc7010b3c087c6425ccf47033f711fa44776c6df3cf744864a063657b00000000000000000000000000000000000000000000000000000000000000006cf7ecfde138b32bbf6aec5e260f8bb6277cc2876ea144c3995d2afc0e6baa3525d91884672bf2832c23a6ebf85a165b45af53c836a31168ff7deaec0dd9c82c2000000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f0500620e00002d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d494945386a4343424a6d674177494241674956414b7750766270377a6f7a50754144646b792b6f526e356f36704d754d416f4743437147534d343942414d430a4d484178496a416742674e5642414d4d47556c756447567349464e4857434251513073675547786864475a76636d306751304578476a415942674e5642416f4d0a45556c756447567349454e76636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155450a4341774351304578437a414a42674e5642415954416c56544d4234584454497a4d4467794e4449784d7a557a4d6c6f5844544d774d4467794e4449784d7a557a0a4d6c6f77634445694d434147413155454177775a535735305a5777675530645949464244537942445a584a3061575a70593246305a5445614d426747413155450a43677752535735305a577767513239796347397959585270623234784644415342674e564241634d43314e68626e526849454e7359584a684d517377435159440a5651514944414a445154454c4d416b474131554542684d4356564d775754415442676371686b6a4f5051494242676771686b6a4f50514d4242774e43414154450a764b6a754b66376969723832686d2b4d5a4151452b6847643349716d53396235634e63484a754b7a5a445970626f35496a344c7a7176704f503830706f4152730a59504233594e355537704d3777644936314b66716f344944446a434341776f77487759445652306a42426777466f41556c5739647a62306234656c4153636e550a3944504f4156634c336c5177617759445652306642475177596a42676f46366758495a616148523063484d364c79396863476b7564484a316333526c5a484e6c0a636e5a705932567a4c6d6c75644756734c6d4e766253397a5a3367765932567964476c6d61574e6864476c76626939324d7939775932746a636d772f593245390a6347786864475a76636d306d5a57356a62325270626d63395a4756794d4230474131556444675157424251695a7667373930317a3171554d3874534c754358580a6571314c6f54414f42674e56485138424166384542414d434273417744415944565230544151482f4241497741444343416a734743537147534962345451454e0a41515343416977776767496f4d42344743697147534962345451454e41514545454358343464705036434c5154772f785543575448306b776767466c42676f710a686b69472b453042445145434d4949425654415142677371686b69472b45304244514543415149424444415142677371686b69472b45304244514543416749420a4444415142677371686b69472b4530424451454341774942417a415142677371686b69472b4530424451454342414942417a415242677371686b69472b4530420a4451454342514943415038774551594c4b6f5a496876684e41513042416759434167442f4d42414743797147534962345451454e41514948416745424d4241470a43797147534962345451454e41514949416745414d42414743797147534962345451454e4151494a416745414d42414743797147534962345451454e4151494b0a416745414d42414743797147534962345451454e4151494c416745414d42414743797147534962345451454e4151494d416745414d42414743797147534962340a5451454e4151494e416745414d42414743797147534962345451454e4151494f416745414d42414743797147534962345451454e41514950416745414d4241470a43797147534962345451454e41514951416745414d42414743797147534962345451454e415149524167454e4d42384743797147534962345451454e415149530a4242414d44414d442f2f38424141414141414141414141414d42414743697147534962345451454e41514d45416741414d42514743697147534962345451454e0a4151514542674267616741414144415042676f71686b69472b45304244514546436745424d42344743697147534962345451454e4151594545424531784169510a72743945363234433159516b497034775241594b4b6f5a496876684e41513042427a41324d42414743797147534962345451454e415163424151482f4d4241470a43797147534962345451454e41516343415145414d42414743797147534962345451454e41516344415145414d416f4743437147534d343942414d43413063410a4d45514349445a6f63514c6478362b4f2b586d4f6b766f6b654133345a617261342b6539534e5877344b68396d5876574169415479695a6e495932474f3466670a4938673342666c4e434f56446e42505270507559377274484e77335470513d3d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436c6a4343416a32674177494241674956414a567658633239472b487051456e4a3150517a7a674658433935554d416f4743437147534d343942414d430a4d476778476a415942674e5642414d4d45556c756447567349464e48574342536232393049454e424d526f77474159445651514b4442464a626e526c624342440a62334a7762334a6864476c76626a45554d424947413155454277774c553246756447456751327868636d4578437a414a42674e564241674d416b4e424d5173770a435159445651514745774a56557a4165467730784f4441314d6a45784d4455774d5442614677307a4d7a41314d6a45784d4455774d5442614d484178496a41670a42674e5642414d4d47556c756447567349464e4857434251513073675547786864475a76636d306751304578476a415942674e5642416f4d45556c75644756730a49454e76636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b474131554543417743513045780a437a414a42674e5642415954416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a304441516344516741454e53422f377432316c58534f0a3243757a7078773734654a423732457944476757357258437478327456544c7136684b6b367a2b5569525a436e71523770734f766771466553786c6d546c4a6c0a65546d693257597a33714f42757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f536347724442530a42674e5648523845537a424a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b633256790a646d6c6a5a584d75615735305a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e5648513445466751556c5739640a7a62306234656c4153636e553944504f4156634c336c517744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159420a4166384341514177436759494b6f5a497a6a30454177494452774177524149675873566b6930772b6936565947573355462f32327561586530594a446a3155650a6e412b546a44316169356343494359623153416d4435786b66545670766f34556f79695359787244574c6d5552344349394e4b7966504e2b0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a2d2d2d2d2d424547494e2043455254494649434154452d2d2d2d2d0a4d4949436a7a4343416a53674177494241674955496d554d316c71644e496e7a6737535655723951477a6b6e42717777436759494b6f5a497a6a3045417749770a614445614d4267474131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e760a636e4276636d4630615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a0a42674e5642415954416c56544d423458445445344d4455794d5445774e4455784d466f58445451354d54497a4d54497a4e546b314f566f77614445614d4267470a4131554541777752535735305a5777675530645949464a766233516751304578476a415942674e5642416f4d45556c756447567349454e76636e4276636d46300a615739754d5251774567594456515148444174545957353059534244624746795954454c4d416b47413155454341774351304578437a414a42674e56424159540a416c56544d466b77457759484b6f5a497a6a3043415159494b6f5a497a6a3044415163445167414543366e45774d4449595a4f6a2f69505773437a61454b69370a314f694f534c52466857476a626e42564a66566e6b59347533496a6b4459594c304d784f346d717379596a6c42616c54565978465032734a424b357a6c4b4f420a757a43427544416642674e5648534d4547444157674251695a517a575770303069664f44744a5653763141624f5363477244425342674e5648523845537a424a0a4d45656752614244686b466f64485277637a6f764c324e6c636e52705a6d6c6a5958526c63793530636e567a6447566b63325679646d6c6a5a584d75615735300a5a577775593239744c306c756447567355306459556d397664454e424c6d526c636a416442674e564851344546675155496d554d316c71644e496e7a673753560a55723951477a6b6e4271777744675944565230504151482f42415144416745474d42494741315564457745422f7751494d4159424166384341514577436759490a4b6f5a497a6a3045417749445351417752674968414f572f35516b522b533943695344634e6f6f774c7550524c735747662f59693747535839344267775477670a41694541344a306c72486f4d732b586f356f2f7358364f39515778485241765a55474f6452513763767152586171493d0a2d2d2d2d2d454e442043455254494649434154452d2d2d2d2d0a00").unwrap();
86 | let dcap_quote = QuoteV3::from_bytes(&dcap_quote_bytes);
87 |
88 | let verified_output = verify_quote_dcapv3(&dcap_quote, &collaterals, PINNED_TIME);
89 |
90 | println!("{:?}", verified_output);
91 | let root_hash = hash_x509_keccak256(&collaterals.get_sgx_intel_root_ca());
92 | let sign_hash = hash_x509_keccak256(&collaterals.get_sgx_tcb_signing());
93 | let crl_hash = hash_crl_keccak256(&collaterals.get_sgx_intel_root_ca_crl().unwrap());
94 | println!("{:?}", root_hash);
95 | println!("{:?}", sign_hash);
96 | println!("{:?}", crl_hash);
97 | }
98 |
99 | #[test]
100 | fn test_verifyv4() {
101 | // let current_time = chrono::Utc::now().timestamp() as u64;
102 |
103 | let mut collaterals = IntelCollateral::new();
104 | collaterals.set_tcbinfo_bytes(include_bytes!("../data/tcbinfov3_00806f050000.json"));
105 | collaterals.set_qeidentity_bytes(include_bytes!("../data/qeidentityv2_apiv4.json"));
106 | collaterals.set_intel_root_ca_der(include_bytes!("../data/Intel_SGX_Provisioning_Certification_RootCA.cer"));
107 | collaterals.set_sgx_tcb_signing_pem(include_bytes!("../data/signing_cert.pem"));
108 | collaterals.set_sgx_intel_root_ca_crl_der(include_bytes!("../data/intel_root_ca_crl.der"));
109 | collaterals.set_sgx_platform_crl_der(include_bytes!("../data/pck_platform_crl.der"));
110 | // collaterals.set_sgx_processor_crl_der(include_bytes!("../data/pck_processor_crl.der"));
111 |
112 |
113 | let dcap_quote = QuoteV4::from_bytes(include_bytes!("../data/quote_tdx_00806f050000.dat"));
114 |
115 | let verified_output = verify_quote_dcapv4(&dcap_quote, &collaterals, PINNED_TIME);
116 |
117 | println!("{:?}", verified_output);
118 | let root_hash = hash_x509_keccak256(&collaterals.get_sgx_intel_root_ca());
119 | let sign_hash = hash_x509_keccak256(&collaterals.get_sgx_tcb_signing());
120 | let crl_hash = hash_crl_keccak256(&collaterals.get_sgx_intel_root_ca_crl().unwrap());
121 | println!("{:?}", root_hash);
122 | println!("{:?}", sign_hash);
123 | println!("{:?}", crl_hash);
124 | }
125 |
126 | #[test]
127 | fn test_tcb_v3_tdx_content_hash() {
128 | let tcb_v3_tdx_0_json_string = include_str!("../data/tcb_info_v3_tdx_0.json");
129 | let tcb_v3_tdx_1_json_string = include_str!("../data/tcb_info_v3_tdx_1.json");
130 |
131 | let tcb_v3_tdx_0: TcbInfoV3 = serde_json::from_str(&tcb_v3_tdx_0_json_string).unwrap();
132 | let tcb_v3_tdx_1: TcbInfoV3 = serde_json::from_str(&tcb_v3_tdx_1_json_string).unwrap();
133 |
134 | let tcb_v3_tdx_0_content_hash = get_tcbinfov3_content_hash(&tcb_v3_tdx_0);
135 | let tcb_v3_tdx_1_content_hash = get_tcbinfov3_content_hash(&tcb_v3_tdx_1);
136 |
137 | assert_eq!(tcb_v3_tdx_0_content_hash, tcb_v3_tdx_1_content_hash);
138 | }
139 |
140 | #[test]
141 | fn test_identity_v2_content_hash() {
142 | let identity_v2_0_json_string = include_str!("../data/identityv2_0.json");
143 | let identity_v2_1_json_string = include_str!("../data/identityv2_1.json");
144 |
145 | let identity_v2_0: EnclaveIdentityV2 = serde_json::from_str(&identity_v2_0_json_string).unwrap();
146 | let identity_v2_1: EnclaveIdentityV2 = serde_json::from_str(&identity_v2_1_json_string).unwrap();
147 |
148 | let identity_v2_0_content_hash = get_enclave_identityv2_content_hash(&identity_v2_0);
149 | let identity_v2_1_content_hash = get_enclave_identityv2_content_hash(&identity_v2_1);
150 |
151 | assert_eq!(identity_v2_0_content_hash, identity_v2_1_content_hash);
152 | }
153 | }
--------------------------------------------------------------------------------
/src/types/cert.rs:
--------------------------------------------------------------------------------
1 | use serde::{Serialize, Deserialize};
2 | use x509_parser::{certificate::X509Certificate, revocation_list::CertificateRevocationList};
3 |
4 | use crate::utils::cert::{get_crl_uri, is_cert_revoked, parse_x509_der_multi, pem_to_der};
5 |
6 | use super::collaterals::IntelCollateral;
7 |
8 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
9 | #[serde(rename_all = "camelCase")]
10 | pub struct SgxExtensionTcbLevel {
11 | pub sgxtcbcomp01svn: u8,
12 | pub sgxtcbcomp02svn: u8,
13 | pub sgxtcbcomp03svn: u8,
14 | pub sgxtcbcomp04svn: u8,
15 | pub sgxtcbcomp05svn: u8,
16 | pub sgxtcbcomp06svn: u8,
17 | pub sgxtcbcomp07svn: u8,
18 | pub sgxtcbcomp08svn: u8,
19 | pub sgxtcbcomp09svn: u8,
20 | pub sgxtcbcomp10svn: u8,
21 | pub sgxtcbcomp11svn: u8,
22 | pub sgxtcbcomp12svn: u8,
23 | pub sgxtcbcomp13svn: u8,
24 | pub sgxtcbcomp14svn: u8,
25 | pub sgxtcbcomp15svn: u8,
26 | pub sgxtcbcomp16svn: u8,
27 | pub pcesvn: u16,
28 | pub cpusvn: [u8; 16]
29 | }
30 |
31 |
32 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
33 | pub struct SgxExtensions {
34 | pub ppid: [u8; 16],
35 | pub tcb: SgxExtensionTcbLevel,
36 | pub pceid: [u8; 2],
37 | pub fmspc: [u8; 6],
38 | pub sgx_type: u32,
39 | pub platform_instance_id: Option<[u8; 16]>,
40 | pub configuration: Option,
41 | }
42 |
43 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
44 | pub struct PckPlatformConfiguration {
45 | pub dynamic_platform: Option,
46 | pub cached_keys: Option,
47 | pub smt_enabled: Option,
48 | }
49 |
50 | #[derive(Debug)]
51 | pub struct IntelSgxCrls<'a> {
52 | pub sgx_root_ca_crl: Option>,
53 | pub sgx_pck_processor_crl: Option>,
54 | pub sgx_pck_platform_crl: Option>,
55 | }
56 |
57 | impl<'a> IntelSgxCrls<'a> {
58 | pub fn new(sgx_root_ca_crl: Option>, sgx_pck_processor_crl: Option>, sgx_pck_platform_crl: Option>) -> Self {
59 | Self {
60 | sgx_root_ca_crl,
61 | sgx_pck_processor_crl,
62 | sgx_pck_platform_crl,
63 | }
64 | }
65 |
66 | pub fn from_collaterals(collaterals: &'a IntelCollateral) -> Self {
67 | let sgx_root_ca_crl = collaterals.get_sgx_intel_root_ca_crl();
68 | let sgx_pck_processor_crl = collaterals.get_sgx_pck_processor_crl();
69 | let sgx_pck_platform_crl = collaterals.get_sgx_pck_platform_crl();
70 |
71 | Self::new(sgx_root_ca_crl, sgx_pck_processor_crl, sgx_pck_platform_crl)
72 | }
73 |
74 | pub fn is_cert_revoked(&self, cert: &X509Certificate) -> bool {
75 | let crl = match get_crl_uri(cert) {
76 | Some(crl_uri) => {
77 | if crl_uri.contains("https://api.trustedservices.intel.com/sgx/certification/v3/pckcrl?ca=platform")
78 | || crl_uri.contains("https://api.trustedservices.intel.com/sgx/certification/v4/pckcrl?ca=platform") {
79 | self.sgx_pck_platform_crl.as_ref()
80 | } else if crl_uri.contains("https://api.trustedservices.intel.com/sgx/certification/v3/pckcrl?ca=processor")
81 | || crl_uri.contains("https://api.trustedservices.intel.com/sgx/certification/v4/pckcrl?ca=processor") {
82 | self.sgx_pck_processor_crl.as_ref()
83 | } else if crl_uri.contains("https://certificates.trustedservices.intel.com/IntelSGXRootCA.der") {
84 | self.sgx_root_ca_crl.as_ref()
85 | } else {
86 | panic!("Unknown CRL URI: {}", crl_uri);
87 | }
88 | },
89 | None => {
90 | panic!("No CRL URI found in certificate");
91 | }
92 | }.unwrap();
93 |
94 | // check if the cert is revoked given the crl
95 | is_cert_revoked(cert, crl)
96 | }
97 | }
98 |
99 | #[derive(Debug, Clone)]
100 | pub struct Certificates {
101 | pub certs_der: Vec,
102 | }
103 |
104 | impl Certificates {
105 | pub fn from_der(certs_der: &[u8]) -> Self {
106 | Self {
107 | certs_der: certs_der.to_vec(),
108 | }
109 | }
110 |
111 | pub fn from_pem(pem_bytes: &[u8]) -> Self {
112 | let certs_der = pem_to_der(pem_bytes);
113 | Self::from_der(&certs_der)
114 | }
115 |
116 | pub fn get_certs(&self) -> Vec {
117 | let certs = parse_x509_der_multi(&self.certs_der);
118 | certs
119 | }
120 | }
--------------------------------------------------------------------------------
/src/types/collaterals.rs:
--------------------------------------------------------------------------------
1 | use x509_parser::{certificate::X509Certificate, revocation_list::CertificateRevocationList};
2 |
3 | use super::enclave_identity::EnclaveIdentityV2;
4 | use super::tcbinfo::{TcbInfoV2, TcbInfoV3};
5 |
6 | use crate::utils::cert::{parse_crl_der, parse_x509_der, parse_x509_der_multi, pem_to_der};
7 |
8 | #[derive(Clone, Debug)]
9 | pub struct IntelCollateral {
10 | pub tcbinfo_bytes: Option>,
11 | pub qeidentity_bytes: Option>,
12 | pub sgx_intel_root_ca_der: Option>,
13 | pub sgx_tcb_signing_der: Option>,
14 | pub sgx_pck_certchain_der: Option>,
15 | pub sgx_intel_root_ca_crl_der: Option>,
16 | pub sgx_pck_processor_crl_der: Option>,
17 | pub sgx_pck_platform_crl_der: Option>,
18 | }
19 |
20 | // builder pattern for IntelCollateralV3
21 | impl IntelCollateral {
22 | pub fn new() -> IntelCollateral {
23 | IntelCollateral {
24 | tcbinfo_bytes: None,
25 | qeidentity_bytes: None,
26 | sgx_intel_root_ca_der: None,
27 | sgx_tcb_signing_der: None,
28 | sgx_pck_certchain_der: None,
29 | sgx_intel_root_ca_crl_der: None,
30 | sgx_pck_processor_crl_der: None,
31 | sgx_pck_platform_crl_der: None,
32 | }
33 | }
34 |
35 | pub fn to_bytes(&self) -> Vec {
36 | // serialization scheme is simple: the bytestream is made of 2 parts
37 | // the first contains a u32 length for each of the members
38 | // the second contains the actual data
39 | // [lengths of each of the member][data segment]
40 |
41 | let tcbinfo_bytes = match self.tcbinfo_bytes {
42 | Some(ref tcbinfo) => tcbinfo.as_slice(),
43 | None => &[],
44 | };
45 |
46 | let qeidentity_bytes = match self.qeidentity_bytes {
47 | Some(ref qeidentity) => qeidentity.as_slice(),
48 | None => &[],
49 | };
50 |
51 | let sgx_intel_root_ca_der_bytes = match &self.sgx_intel_root_ca_der {
52 | Some(der) => der.as_slice(),
53 | None => &[],
54 | };
55 |
56 | let sgx_tcb_signing_der_bytes = match &self.sgx_tcb_signing_der {
57 | Some(der) => der.as_slice(),
58 | None => &[],
59 | };
60 |
61 | let sgx_pck_certchain_der_bytes = match &self.sgx_pck_certchain_der {
62 | Some(der) => der.as_slice(),
63 | None => &[],
64 | };
65 |
66 | let sgx_intel_root_ca_crl_der_bytes = match &self.sgx_intel_root_ca_crl_der {
67 | Some(der) => der.as_slice(),
68 | None => &[],
69 | };
70 |
71 | let sgx_pck_processor_crl_der_bytes = match &self.sgx_pck_processor_crl_der {
72 | Some(der) => der.as_slice(),
73 | None => &[],
74 | };
75 |
76 | let sgx_pck_platform_crl_der_bytes = match &self.sgx_pck_platform_crl_der {
77 | Some(der) => der.as_slice(),
78 | None => &[],
79 | };
80 |
81 | // get the total length
82 | let total_length = 4 * 8 + tcbinfo_bytes.len() + qeidentity_bytes.len() + sgx_intel_root_ca_der_bytes.len() + sgx_tcb_signing_der_bytes.len() + sgx_pck_certchain_der_bytes.len() + sgx_intel_root_ca_crl_der_bytes.len() + sgx_pck_processor_crl_der_bytes.len() + sgx_pck_platform_crl_der_bytes.len();
83 |
84 | // create the vec and copy the data
85 | let mut data = Vec::with_capacity(total_length);
86 | data.extend_from_slice(&(tcbinfo_bytes.len() as u32).to_le_bytes());
87 | data.extend_from_slice(&(qeidentity_bytes.len() as u32).to_le_bytes());
88 | data.extend_from_slice(&(sgx_intel_root_ca_der_bytes.len() as u32).to_le_bytes());
89 | data.extend_from_slice(&(sgx_tcb_signing_der_bytes.len() as u32).to_le_bytes());
90 | data.extend_from_slice(&(sgx_pck_certchain_der_bytes.len() as u32).to_le_bytes());
91 | data.extend_from_slice(&(sgx_intel_root_ca_crl_der_bytes.len() as u32).to_le_bytes());
92 | data.extend_from_slice(&(sgx_pck_processor_crl_der_bytes.len() as u32).to_le_bytes());
93 | data.extend_from_slice(&(sgx_pck_platform_crl_der_bytes.len() as u32).to_le_bytes());
94 |
95 | data.extend_from_slice(&tcbinfo_bytes);
96 | data.extend_from_slice(&qeidentity_bytes);
97 | data.extend_from_slice(&sgx_intel_root_ca_der_bytes);
98 | data.extend_from_slice(&sgx_tcb_signing_der_bytes);
99 | data.extend_from_slice(&sgx_pck_certchain_der_bytes);
100 | data.extend_from_slice(&sgx_intel_root_ca_crl_der_bytes);
101 | data.extend_from_slice(&sgx_pck_processor_crl_der_bytes);
102 | data.extend_from_slice(&sgx_pck_platform_crl_der_bytes);
103 |
104 | data
105 | }
106 |
107 | pub fn from_bytes(slice: &[u8]) -> Self {
108 | // reverse the serialization process
109 | // each length is 4 bytes long, we have a total of 8 members
110 | let tcbinfo_bytes_len = u32::from_le_bytes(slice[0..4].try_into().unwrap()) as usize;
111 | let qeidentity_bytes_len = u32::from_le_bytes(slice[4..8].try_into().unwrap()) as usize;
112 | let sgx_intel_root_ca_der_len = u32::from_le_bytes(slice[8..12].try_into().unwrap()) as usize;
113 | let sgx_tcb_signing_der_len = u32::from_le_bytes(slice[12..16].try_into().unwrap()) as usize;
114 | let sgx_pck_certchain_der_len = u32::from_le_bytes(slice[16..20].try_into().unwrap()) as usize;
115 | let sgx_intel_root_ca_crl_der_len = u32::from_le_bytes(slice[20..24].try_into().unwrap()) as usize;
116 | let sgx_pck_processor_crl_der_len = u32::from_le_bytes(slice[24..28].try_into().unwrap()) as usize;
117 | let sgx_pck_platform_crl_der_len = u32::from_le_bytes(slice[28..32].try_into().unwrap()) as usize;
118 |
119 | let mut offset = 4 * 8 as usize;
120 | let tcbinfo_bytes: Option> = match tcbinfo_bytes_len {
121 | 0 => None,
122 | len => Some(slice[offset..offset + len].to_vec())
123 | };
124 | offset += tcbinfo_bytes_len;
125 |
126 | let qeidentity_bytes: Option> = match qeidentity_bytes_len {
127 | 0 => None,
128 | len => Some(slice[offset..offset + len].to_vec())
129 | };
130 | offset += qeidentity_bytes_len;
131 |
132 | let sgx_intel_root_ca_der: Option> = match sgx_intel_root_ca_der_len {
133 | 0 => None,
134 | len => Some(slice[offset..offset + len].to_vec())
135 | };
136 | offset += sgx_intel_root_ca_der_len;
137 |
138 | let sgx_tcb_signing_der: Option> = match sgx_tcb_signing_der_len {
139 | 0 => None,
140 | len => Some(slice[offset..offset + len].to_vec())
141 | };
142 | offset += sgx_tcb_signing_der_len;
143 |
144 | let sgx_pck_certchain_der: Option> = match sgx_pck_certchain_der_len {
145 | 0 => None,
146 | len => Some(slice[offset..offset + len].to_vec())
147 | };
148 | offset += sgx_pck_certchain_der_len;
149 |
150 | let sgx_intel_root_ca_crl_der: Option> = match sgx_intel_root_ca_crl_der_len {
151 | 0 => None,
152 | len => Some(slice[offset..offset + len].to_vec())
153 | };
154 | offset += sgx_intel_root_ca_crl_der_len;
155 |
156 | let sgx_pck_processor_crl_der: Option> = match sgx_pck_processor_crl_der_len {
157 | 0 => None,
158 | len => Some(slice[offset..offset + len].to_vec())
159 | };
160 | offset += sgx_pck_processor_crl_der_len;
161 |
162 | let sgx_pck_platform_crl_der: Option> = match sgx_pck_platform_crl_der_len {
163 | 0 => None,
164 | len => Some(slice[offset..offset + len].to_vec())
165 | };
166 | offset += sgx_pck_platform_crl_der_len;
167 |
168 | assert!(offset == slice.len());
169 |
170 | IntelCollateral {
171 | tcbinfo_bytes: tcbinfo_bytes,
172 | qeidentity_bytes: qeidentity_bytes,
173 | sgx_intel_root_ca_der,
174 | sgx_tcb_signing_der,
175 | sgx_pck_certchain_der,
176 | sgx_intel_root_ca_crl_der,
177 | sgx_pck_processor_crl_der,
178 | sgx_pck_platform_crl_der,
179 | }
180 | }
181 |
182 | pub fn get_tcbinfov2(&self) -> TcbInfoV2 {
183 | match &self.tcbinfo_bytes {
184 | Some(tcbinfov2) => {
185 | let tcbinfo: TcbInfoV2 = serde_json::from_slice(tcbinfov2).unwrap();
186 | assert_eq!(tcbinfo.tcb_info.version, 2);
187 | tcbinfo
188 | },
189 | None => panic!("TCB Info V2 not set"),
190 | }
191 | }
192 |
193 | pub fn get_tcbinfov3(&self) -> TcbInfoV3 {
194 | match &self.tcbinfo_bytes {
195 | Some(tcbinfov3) => {
196 | let tcbinfo: TcbInfoV3 = serde_json::from_slice(tcbinfov3).unwrap();
197 | assert_eq!(tcbinfo.tcb_info.version, 3);
198 | tcbinfo
199 | },
200 | None => panic!("TCB Info V3 not set"),
201 | }
202 | }
203 |
204 | pub fn set_tcbinfo_bytes(&mut self, tcbinfo_slice: &[u8]) {
205 | self.tcbinfo_bytes = Some(tcbinfo_slice.to_vec());
206 | }
207 |
208 | pub fn get_qeidentityv2(&self) -> EnclaveIdentityV2 {
209 | match &self.qeidentity_bytes {
210 | Some(qeidentityv2) => {
211 | let qeidentity = serde_json::from_slice(qeidentityv2).unwrap();
212 | qeidentity
213 | },
214 | None => panic!("QE Identity V2 not set"),
215 | }
216 | }
217 |
218 | pub fn set_qeidentity_bytes(&mut self, qeidentity_slice: &[u8]) {
219 | self.qeidentity_bytes = Some(qeidentity_slice.to_vec());
220 | }
221 |
222 | pub fn get_sgx_intel_root_ca<'a>(&'a self) -> X509Certificate<'a> {
223 | match self.sgx_intel_root_ca_der {
224 | Some(ref der) => {
225 | let cert = parse_x509_der(der);
226 | cert
227 | },
228 | None => panic!("Intel Root CA not set"),
229 | }
230 | }
231 |
232 | pub fn set_intel_root_ca_der(&mut self, intel_root_ca_der: &[u8]) {
233 | self.sgx_intel_root_ca_der = Some(intel_root_ca_der.to_vec());
234 | }
235 |
236 | pub fn get_sgx_tcb_signing<'a>(&'a self) -> X509Certificate<'a> {
237 | match self.sgx_tcb_signing_der {
238 | Some(ref der) => {
239 | let cert = parse_x509_der(der);
240 | cert
241 | },
242 | None => panic!("SGX TCB Signing Cert not set"),
243 | }
244 | }
245 |
246 | pub fn set_sgx_tcb_signing_der(&mut self, sgx_tcb_signing_der: &[u8]) {
247 | self.sgx_tcb_signing_der = Some(sgx_tcb_signing_der.to_vec());
248 | }
249 |
250 | pub fn set_sgx_tcb_signing_pem(&mut self, sgx_tcb_signing_pem: &[u8]) {
251 | // convert pem to der
252 | let sgx_tcb_signing_der = pem_to_der(sgx_tcb_signing_pem);
253 | self.sgx_tcb_signing_der = Some(sgx_tcb_signing_der);
254 | }
255 |
256 | pub fn get_sgx_pck_certchain<'a>(&'a self) -> Option>> {
257 | match &self.sgx_pck_certchain_der {
258 | Some(certchain_der) => {
259 | let certchain = parse_x509_der_multi(certchain_der);
260 | Some(certchain)
261 | },
262 | None => None,
263 | }
264 | }
265 |
266 | pub fn set_sgx_pck_certchain_der(&mut self, sgx_pck_certchain_der: Option<&[u8]>) {
267 | match sgx_pck_certchain_der {
268 | Some(certchain_der) => {
269 | self.sgx_pck_certchain_der = Some(certchain_der.to_vec());
270 | },
271 | None => {
272 | self.sgx_pck_certchain_der = None;
273 | },
274 | }
275 | }
276 |
277 | pub fn set_sgx_pck_certchain_pem(&mut self, sgx_pck_certchain_pem: Option<&[u8]>) {
278 | match sgx_pck_certchain_pem {
279 | Some(certchain_pem) => {
280 | // convert pem to der
281 | let sgx_pck_certchain_der = pem_to_der(certchain_pem);
282 | self.sgx_pck_certchain_der = Some(sgx_pck_certchain_der);
283 | },
284 | None => {
285 | self.sgx_pck_certchain_der = None;
286 | },
287 | }
288 | }
289 |
290 | pub fn get_sgx_intel_root_ca_crl<'a>(&'a self) -> Option> {
291 | match &self.sgx_intel_root_ca_crl_der {
292 | Some(crl_der) => {
293 | let crl = parse_crl_der(crl_der);
294 | Some(crl)
295 | },
296 | None => None,
297 | }
298 | }
299 |
300 | pub fn set_sgx_intel_root_ca_crl_der(&mut self, sgx_intel_root_ca_crl_der: &[u8]) {
301 | self.sgx_intel_root_ca_crl_der = Some(sgx_intel_root_ca_crl_der.to_vec());
302 | }
303 |
304 | pub fn set_sgx_intel_root_ca_crl_pem(&mut self, sgx_intel_root_ca_crl_pem: &[u8]) {
305 | // convert pem to der
306 | let sgx_intel_root_ca_crl_der = pem_to_der(sgx_intel_root_ca_crl_pem);
307 | self.sgx_intel_root_ca_crl_der = Some(sgx_intel_root_ca_crl_der);
308 | }
309 |
310 | pub fn get_sgx_pck_processor_crl<'a>(&'a self) -> Option> {
311 | match &self.sgx_pck_processor_crl_der {
312 | Some(crl_der) => {
313 | let crl = parse_crl_der(crl_der);
314 | Some(crl)
315 | },
316 | None => None,
317 | }
318 | }
319 |
320 | pub fn set_sgx_processor_crl_der(&mut self, sgx_pck_processor_crl_der: &[u8]) {
321 | self.sgx_pck_processor_crl_der = Some(sgx_pck_processor_crl_der.to_vec());
322 | }
323 |
324 | pub fn set_sgx_processor_crl_der_pem(&mut self, sgx_pck_processor_crl_pem: &[u8]) {
325 | // convert pem to der
326 | let sgx_pck_processor_crl_der = pem_to_der(sgx_pck_processor_crl_pem);
327 | self.sgx_pck_processor_crl_der = Some(sgx_pck_processor_crl_der);
328 | }
329 |
330 | pub fn get_sgx_pck_platform_crl<'a>(&'a self) -> Option> {
331 | match &self.sgx_pck_platform_crl_der {
332 | Some(crl_der) => {
333 | let crl = parse_crl_der(crl_der);
334 | Some(crl)
335 | },
336 | None => None,
337 | }
338 | }
339 |
340 | pub fn set_sgx_platform_crl_der(&mut self, sgx_pck_platform_crl_der: &[u8]) {
341 | self.sgx_pck_platform_crl_der = Some(sgx_pck_platform_crl_der.to_vec());
342 | }
343 |
344 | pub fn set_sgx_platform_crl_der_pem(&mut self, sgx_pck_platform_crl_pem: &[u8]) {
345 | // convert pem to der
346 | let sgx_pck_platform_crl_der = pem_to_der(sgx_pck_platform_crl_pem);
347 | self.sgx_pck_platform_crl_der = Some(sgx_pck_platform_crl_der);
348 | }
349 | }
--------------------------------------------------------------------------------
/src/types/enclave_identity.rs:
--------------------------------------------------------------------------------
1 | use serde::{Deserialize, Serialize};
2 |
3 | // EnclaveIdentityV2:
4 | // type: object
5 | // description: SGX Enclave Identity data structure encoded as JSON string in case of success
6 | // (200 HTTP status code)
7 | // properties:
8 | // enclaveIdentity:
9 | // type: object
10 | // properties:
11 | // id:
12 | // type: string
13 | // description: Identifier of the SGX Enclave issued by Intel. Supported values are QE, QVE and TD_QE
14 | // version:
15 | // type: integer
16 | // example: 2
17 | // description: Version of the structure
18 | // issueDate:
19 | // type: string
20 | // format: date-time
21 | // description: >-
22 | // Representation of date and time the Enclave Identity information
23 | // was created. The time shall be in UTC and the encoding shall
24 | // be compliant to ISO 8601 standard (YYYY-MM-DDThh:mm:ssZ)
25 | // nextUpdate:
26 | // type: string
27 | // format: date-time
28 | // description: >-
29 | // Representation of date and time by which next Enclave Identity
30 | // information will be issued. The time shall be in
31 | // UTC and the encoding shall be compliant to ISO 8601 standard
32 | // (YYYY-MM-DDThh:mm:ssZ)
33 | // tcbEvaluationDataNumber:
34 | // type: integer
35 | // example: 2
36 | // description: >-
37 | // A monotonically increasing sequence number changed
38 | // when Intel updates the content of the TCB evaluation data
39 | // set: TCB Info, QE Idenity and QVE Identity. The tcbEvaluationDataNumber
40 | // update is synchronized across TCB Info for all flavors of
41 | // SGX CPUs (Family-Model-Stepping-Platform-CustomSKU) and QE/QVE
42 | // Identity. This sequence number allows users to easily determine
43 | // when a particular TCB Info/QE Idenity/QVE Identiy superseedes
44 | // another TCB Info/QE Identity/QVE Identity (value: current
45 | // TCB Recovery event number stored in the database).
46 | // miscselect:
47 | // type: string
48 | // pattern: ^[0-9a-fA-F]{8}$
49 | // example: '00000000'
50 | // description: Base 16-encoded string representing miscselect "golden" value (upon applying mask).
51 | // miscselectMask:
52 | // type: string
53 | // pattern: ^[0-9a-fA-F]{8}$
54 | // example: '00000000'
55 | // description: Base 16-encoded string representing mask to be applied to miscselect value retrieved from the platform.
56 | // attributes:
57 | // type: string
58 | // pattern: ^[0-9a-fA-F]{32}$
59 | // example: '00000000000000000000000000000000'
60 | // description: Base 16-encoded string representing attributes "golden" value (upon applying mask).
61 | // attributesMask:
62 | // type: string
63 | // pattern: ^[0-9a-fA-F]{32}$
64 | // example: '00000000000000000000000000000000'
65 | // description: Base 16-encoded string representing mask to be applied to attributes value retrieved from the platform.
66 | // mrsigner:
67 | // type: string
68 | // pattern: ^[0-9a-fA-F]{64}$
69 | // example: '0000000000000000000000000000000000000000000000000000000000000000'
70 | // description: Base 16-encoded string representing mrsigner hash.
71 | // isvprodid:
72 | // type: integer
73 | // example: 0
74 | // minimum: 0
75 | // maximum: 65535
76 | // description: Enclave Product ID.
77 | // tcbLevels:
78 | // description: >-
79 | // Sorted list of supported Enclave TCB levels for given
80 | // QVE encoded as a JSON array of Enclave TCB level objects.
81 | // type: array
82 | // items:
83 | // type: object
84 | // properties:
85 | // tcb:
86 | // type: object
87 | // properties:
88 | // isvsvn:
89 | // description: SGX Enclave's ISV SVN
90 | // type: integer
91 | // tcbDate:
92 | // type: string
93 | // format: date-time
94 | // description: >-
95 | // If there are security advisories published by Intel after tcbDate
96 | // that are for issues whose mitigations are currently enforced* by SGX attestation,
97 | // then the value of tcbStatus for the TCB level will not be UpToDate.
98 | // Otherwise (i.e., either no advisories after or not currently enforced),
99 | // the value of tcbStatus for the TCB level will not be OutOfDate.
100 | //
101 | // The time shall be in UTC and the encoding shall
102 | // be compliant to ISO 8601 standard (YYYY-MM-DDThh:mm:ssZ).
103 | // tcbStatus:
104 | // type: string
105 | // enum:
106 | // - UpToDate
107 | // - OutOfDate
108 | // - Revoked
109 | // description: >-
110 | // TCB level status. One of the following values:
111 | //
112 | // "UpToDate" - TCB level of the SGX platform is up-to-date.
113 | //
114 | // "OutOfDate" - TCB level of SGX platform is outdated.
115 | //
116 | // "Revoked" - TCB level of SGX platform is revoked.
117 | // The platform is not trustworthy.
118 | // advisoryIDs:
119 | // type: array
120 | // description: >-
121 | // Array of Advisory IDs referring to Intel security advisories that
122 | // provide insight into the reason(s) for the value of tcbStatus for
123 | // this TCB level when the value is not UpToDate.
124 | //
125 | // This field is optional. It will be present only
126 | // if the list of Advisory IDs is not empty.
127 | // items:
128 | // type: string
129 | // signature:
130 | // type: string
131 | // description: Hex-encoded string representation of a signature calculated
132 | // over qeIdentity body (without whitespaces) using TCB Info Signing Key.
133 |
134 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
135 | #[serde(rename_all = "camelCase")]
136 | pub struct EnclaveIdentityV2 {
137 | pub enclave_identity: EnclaveIdentityV2Inner,
138 | pub signature: String,
139 | }
140 |
141 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
142 | #[serde(rename_all = "camelCase")]
143 | pub struct EnclaveIdentityV2Inner {
144 | pub id: String,
145 | pub version: u32,
146 | pub issue_date: String,
147 | pub next_update: String,
148 | pub tcb_evaluation_data_number: u32,
149 | pub miscselect: String,
150 | pub miscselect_mask: String,
151 | pub attributes: String,
152 | pub attributes_mask: String,
153 | pub mrsigner: String,
154 | pub isvprodid: u16,
155 | pub tcb_levels: Vec,
156 | }
157 |
158 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
159 | #[serde(rename_all = "camelCase")]
160 | pub struct EnclaveIdentityV2TcbLevelItem {
161 | pub tcb: EnclaveIdentityV2TcbLevel,
162 | pub tcb_date: String,
163 | pub tcb_status: String,
164 | #[serde(rename(serialize = "advisoryIDs", deserialize = "advisoryIDs"))]
165 | #[serde(skip_serializing_if = "Option::is_none")]
166 | pub advisory_ids: Option>,
167 | }
168 |
169 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
170 | #[serde(rename_all = "camelCase")]
171 | pub struct EnclaveIdentityV2TcbLevel {
172 | pub isvsvn: u16,
173 | }
174 |
--------------------------------------------------------------------------------
/src/types/mod.rs:
--------------------------------------------------------------------------------
1 | use self::quotes::body::*;
2 | use crate::constants::{ENCLAVE_REPORT_LEN, SGX_TEE_TYPE, TD10_REPORT_LEN, TDX_TEE_TYPE};
3 | use alloy_sol_types::SolValue;
4 | use serde::{Deserialize, Serialize};
5 |
6 | pub mod cert;
7 | pub mod collaterals;
8 | pub mod enclave_identity;
9 | pub mod quotes;
10 | pub mod tcbinfo;
11 |
12 | #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13 | pub enum TcbStatus {
14 | OK,
15 | TcbSwHardeningNeeded,
16 | TcbConfigurationAndSwHardeningNeeded,
17 | TcbConfigurationNeeded,
18 | TcbOutOfDate,
19 | TcbOutOfDateConfigurationNeeded,
20 | TcbRevoked,
21 | TcbUnrecognized,
22 | }
23 |
24 | impl TcbStatus {
25 | pub fn from_str(s: &str) -> Self {
26 | return match s {
27 | "UpToDate" => TcbStatus::OK,
28 | "SWHardeningNeeded" => TcbStatus::TcbSwHardeningNeeded,
29 | "ConfigurationAndSWHardeningNeeded" => TcbStatus::TcbConfigurationAndSwHardeningNeeded,
30 | "ConfigurationNeeded" => TcbStatus::TcbConfigurationNeeded,
31 | "OutOfDate" => TcbStatus::TcbOutOfDate,
32 | "OutOfDateConfigurationNeeded" => TcbStatus::TcbOutOfDateConfigurationNeeded,
33 | "Revoked" => TcbStatus::TcbRevoked,
34 | _ => TcbStatus::TcbUnrecognized,
35 | };
36 | }
37 | }
38 |
39 | // serialization:
40 | // [quote_vesion][tee_type][tcb_status][fmspc][quote_body_raw_bytes][abi-encoded string array of tcb_advisory_ids]
41 | // 2 bytes + 4 bytes + 1 byte + 6 bytes + var (SGX_ENCLAVE_REPORT = 384; TD10_REPORT = 584) + var
42 | // total: 13 + (384 or 584) + var bytes
43 | #[derive(Debug)]
44 | pub struct VerifiedOutput {
45 | pub quote_version: u16,
46 | pub tee_type: u32,
47 | pub tcb_status: TcbStatus,
48 | pub fmspc: [u8; 6],
49 | pub quote_body: QuoteBody,
50 | pub advisory_ids: Option>,
51 | }
52 |
53 | impl VerifiedOutput {
54 | pub fn to_bytes(&self) -> Vec {
55 | let mut output_vec = Vec::new();
56 |
57 | // big-endian encoded to ensure on-chain compatibility
58 | output_vec.extend_from_slice(&self.quote_version.to_be_bytes());
59 |
60 | output_vec.extend_from_slice(&self.tee_type.to_le_bytes());
61 | output_vec.push(match self.tcb_status {
62 | TcbStatus::OK => 0,
63 | TcbStatus::TcbSwHardeningNeeded => 1,
64 | TcbStatus::TcbConfigurationAndSwHardeningNeeded => 2,
65 | TcbStatus::TcbConfigurationNeeded => 3,
66 | TcbStatus::TcbOutOfDate => 4,
67 | TcbStatus::TcbOutOfDateConfigurationNeeded => 5,
68 | TcbStatus::TcbRevoked => 6,
69 | TcbStatus::TcbUnrecognized => 7,
70 | });
71 | output_vec.extend_from_slice(&self.fmspc);
72 |
73 | match self.quote_body {
74 | QuoteBody::SGXQuoteBody(body) => {
75 | output_vec.extend_from_slice(&body.to_bytes());
76 | }
77 | QuoteBody::TD10QuoteBody(body) => {
78 | output_vec.extend_from_slice(&body.to_bytes());
79 | }
80 | }
81 |
82 | if let Some(advisory_ids) = self.advisory_ids.as_ref() {
83 | let encoded = advisory_ids.abi_encode();
84 | output_vec.extend_from_slice(encoded.as_slice());
85 | }
86 |
87 | output_vec
88 | }
89 |
90 | pub fn from_bytes(slice: &[u8]) -> VerifiedOutput {
91 | let mut quote_version = [0; 2];
92 | quote_version.copy_from_slice(&slice[0..2]);
93 | let mut tee_type = [0; 4];
94 | tee_type.copy_from_slice(&slice[2..6]);
95 | let tcb_status = match slice[6] {
96 | 0 => TcbStatus::OK,
97 | 1 => TcbStatus::TcbSwHardeningNeeded,
98 | 2 => TcbStatus::TcbConfigurationAndSwHardeningNeeded,
99 | 3 => TcbStatus::TcbConfigurationNeeded,
100 | 4 => TcbStatus::TcbOutOfDate,
101 | 5 => TcbStatus::TcbOutOfDateConfigurationNeeded,
102 | 6 => TcbStatus::TcbRevoked,
103 | 7 => TcbStatus::TcbUnrecognized,
104 | _ => panic!("Invalid TCB Status"),
105 | };
106 | let mut fmspc = [0; 6];
107 | fmspc.copy_from_slice(&slice[7..13]);
108 |
109 | let mut offset = 13usize;
110 | let quote_body = match u32::from_le_bytes(tee_type) {
111 | SGX_TEE_TYPE => {
112 | let raw_quote_body = &slice[offset..offset + ENCLAVE_REPORT_LEN];
113 | offset += ENCLAVE_REPORT_LEN;
114 | QuoteBody::SGXQuoteBody(EnclaveReport::from_bytes(raw_quote_body))
115 | }
116 | TDX_TEE_TYPE => {
117 | let raw_quote_body = &slice[offset..offset + TD10_REPORT_LEN];
118 | offset += TD10_REPORT_LEN;
119 | QuoteBody::TD10QuoteBody(TD10ReportBody::from_bytes(raw_quote_body))
120 | }
121 | _ => panic!("unknown TEE type"),
122 | };
123 |
124 | let mut advisory_ids = None;
125 | if offset < slice.len() {
126 | let advisory_ids_slice = &slice[offset..];
127 | advisory_ids = Some(>::abi_decode(advisory_ids_slice, true).unwrap());
128 | }
129 |
130 | VerifiedOutput {
131 | quote_version: u16::from_be_bytes(quote_version),
132 | tee_type: u32::from_le_bytes(tee_type),
133 | tcb_status,
134 | fmspc,
135 | quote_body,
136 | advisory_ids,
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/types/quotes/body.rs:
--------------------------------------------------------------------------------
1 | #[derive(Debug, Copy, Clone)]
2 | pub enum QuoteBody {
3 | SGXQuoteBody(EnclaveReport),
4 | TD10QuoteBody(TD10ReportBody)
5 | }
6 |
7 | #[derive(Clone, Copy, Debug, PartialEq, Eq)]
8 | pub struct EnclaveReport {
9 | pub cpu_svn: [u8; 16], // [16 bytes]
10 | // Security Version of the CPU (raw value)
11 | pub misc_select: [u8; 4], // [4 bytes]
12 | // SSA Frame extended feature set.
13 | // Reports what SECS.MISCSELECT settings are used in the enclave. You can limit the
14 | // allowed MISCSELECT settings in the sigstruct using MISCSELECT/MISCMASK.
15 | pub reserved_1: [u8; 28], // [28 bytes]
16 | // Reserved for future use - 0
17 | pub attributes: [u8; 16], // [16 bytes]
18 | // Set of flags describing attributes of the enclave.
19 | // Reports what SECS.ATTRIBUTES settings are used in the enclave. The ISV can limit what
20 | // SECS.ATTRIBUTES can be used when loading the enclave through parameters to the SGX Signtool.
21 | // The Signtool will produce a SIGSTRUCT with ATTRIBUTES and ATTRIBUTESMASK
22 | // which determine allowed ATTRIBUTES.
23 | // - For each SIGSTRUCT.ATTRIBUTESMASK bit that is set, then corresponding bit in the
24 | // SECS.ATTRIBUTES must match the same bit in SIGSTRUCT.ATTRIBUTES.
25 | pub mrenclave: [u8; 32], // [32 bytes]
26 | // Measurement of the enclave.
27 | // The MRENCLAVE value is the SHA256 hash of the ENCLAVEHASH field in the SIGSTRUCT.
28 | pub reserved_2: [u8; 32], // [32 bytes]
29 | // Reserved for future use - 0
30 | pub mrsigner: [u8; 32], // [32 bytes]
31 | // Measurement of the enclave signer.
32 | // The MRSIGNER value is the SHA256 hash of the MODULUS field in the SIGSTRUCT.
33 | pub reserved_3: [u8; 96], // [96 bytes]
34 | // Reserved for future use - 0
35 | pub isv_prod_id: u16, // [2 bytes]
36 | // Product ID of the enclave.
37 | // The ISV should configure a unique ISVProdID for each product which may
38 | // want to share sealed data between enclaves signed with a specific MRSIGNER. The ISV
39 | // may want to supply different data to identical enclaves signed for different products.
40 | pub isv_svn: u16, // [2 bytes]
41 | // Security Version of the enclave
42 | pub reserved_4: [u8; 60], // [60 bytes]
43 | // Reserved for future use - 0
44 | pub report_data: [u8; 64], // [64 bytes]
45 | // Additional report data.
46 | // The enclave is free to provide 64 bytes of custom data to the REPORT.
47 | // This can be used to provide specific data from the enclave or it can be used to hold
48 | // a hash of a larger block of data which is provided with the quote.
49 | // The verification of the quote signature confirms the integrity of the
50 | // report data (and the rest of the REPORT body).
51 | }
52 |
53 | impl EnclaveReport {
54 | pub fn from_bytes(raw_bytes: &[u8]) -> EnclaveReport{
55 | assert_eq!(raw_bytes.len(), 384);
56 | let mut obj = EnclaveReport {
57 | cpu_svn: [0; 16],
58 | misc_select: [0; 4],
59 | reserved_1: [0; 28],
60 | attributes: [0; 16],
61 | mrenclave: [0; 32],
62 | reserved_2: [0; 32],
63 | mrsigner: [0; 32],
64 | reserved_3: [0; 96],
65 | isv_prod_id: 0,
66 | isv_svn: 0,
67 | reserved_4: [0; 60],
68 | report_data: [0; 64],
69 | };
70 |
71 | // parse raw bytes into obj
72 | obj.cpu_svn.copy_from_slice(&raw_bytes[0..16]);
73 | obj.misc_select.copy_from_slice(&raw_bytes[16..20]);
74 | obj.reserved_1.copy_from_slice(&raw_bytes[20..48]);
75 | obj.attributes.copy_from_slice(&raw_bytes[48..64]);
76 | obj.mrenclave.copy_from_slice(&raw_bytes[64..96]);
77 | obj.reserved_2.copy_from_slice(&raw_bytes[96..128]);
78 | obj.mrsigner.copy_from_slice(&raw_bytes[128..160]);
79 | obj.reserved_3.copy_from_slice(&raw_bytes[160..256]);
80 | obj.isv_prod_id = u16::from_le_bytes([raw_bytes[256], raw_bytes[257]]);
81 | obj.isv_svn = u16::from_le_bytes([raw_bytes[258], raw_bytes[259]]);
82 | obj.reserved_4.copy_from_slice(&raw_bytes[260..320]);
83 | obj.report_data.copy_from_slice(&raw_bytes[320..384]);
84 |
85 | return obj;
86 | }
87 |
88 | pub fn to_bytes(&self) -> [u8; 384] {
89 | // convert the struct into raw bytes
90 | let mut raw_bytes = [0; 384];
91 | // copy the fields into the raw bytes
92 | raw_bytes[0..16].copy_from_slice(&self.cpu_svn);
93 | raw_bytes[16..20].copy_from_slice(&self.misc_select);
94 | raw_bytes[20..48].copy_from_slice(&self.reserved_1);
95 | raw_bytes[48..64].copy_from_slice(&self.attributes);
96 | raw_bytes[64..96].copy_from_slice(&self.mrenclave);
97 | raw_bytes[96..128].copy_from_slice(&self.reserved_2);
98 | raw_bytes[128..160].copy_from_slice(&self.mrsigner);
99 | raw_bytes[160..256].copy_from_slice(&self.reserved_3);
100 | raw_bytes[256..258].copy_from_slice(&self.isv_prod_id.to_le_bytes());
101 | raw_bytes[258..260].copy_from_slice(&self.isv_svn.to_le_bytes());
102 | raw_bytes[260..320].copy_from_slice(&self.reserved_4);
103 | raw_bytes[320..384].copy_from_slice(&self.report_data);
104 |
105 | raw_bytes
106 | }
107 | }
108 |
109 | // TD Attributes:
110 | // [bits] : [description]
111 | // [0:7] : (TUD) TD Under Debug flags.
112 | // If any of the bits in this group are set to 1, the TD is untrusted.
113 | // [0] - (DEBUG) Defines whether the TD runs in TD debug mode (set to 1) or not (set to 0).
114 | // In TD debug mode, the CPU state and private memory are accessible by the host VMM.
115 | // [1:7] - (RESERVED) Reserved for future TUD flags, must be 0.
116 | // [8:31] : (SEC) Attributes that may impact the security of the TD
117 | // [8:27] - (RESERVED) Reserved for future SEC flags, must be 0.
118 | // [28] - (SEPT_VE_DISABLE) Disable EPT violation conversion to #VE on TD access of PENDING pages
119 | // [29] - (RESERVED) Reserved for future SEC flags, must be 0.
120 | // [30] - (PKS) TD is allowed to use Supervisor Protection Keys.
121 | // [31] - (KL) TD is allowed to use Key Locker.
122 | // [32:63] : (OTHER) Attributes that do not impact the security of the TD
123 | // [32:62] - (RESERVED) Reserved for future OTHER flags, must be 0.
124 | // [63] - (PERFMON) TD is allowed to use Perfmon and PERF_METRICS capabilities.
125 |
126 | // TEE_TCB_SVN:
127 | // [bytes] : [Name] : [description]
128 | // [0] : Tdxtcbcomp01 : QVL compares with TCBInfo.TCBLevels.tcb.tdxtcbcomponents.svn[0]
129 | // [1] : Tdxtcbcomp02 : QVL compares with TCBInfo.TCBLevels.tcb.tdxtcbcomponents.svn[1]
130 | // [2] : Tdxtcbcomp03 : QVL compares with TCBInfo.TCBLevels.tcb.tdxtcbcomponents.svn[2
131 | // [3] : Tdxtcbcomp04 : QVL compares with TCBInfo.TCBLevels.tcb.tdxtcbcomponents.svn[3]
132 | // [4] : Tdxtcbcomp05 : QVL compares with TCBInfo.TCBLevels.tcb.tdxtcbcomponents.svn[4]
133 | // [5] : Tdxtcbcomp06 : QVL compares with TCBInfo.TCBLevels.tcb.tdxtcbcomponents.svn[5]
134 | // [6] : Tdxtcbcomp07 : QVL compares with TCBInfo.TCBLevels.tcb.tdxtcbcomponents.svn[6]
135 | // [7] : Tdxtcbcomp08 : QVL compares with TCBInfo.TCBLevels.tcb.tdxtcbcomponents.svn[7]
136 | // [8] : Tdxtcbcomp09 : QVL compares with TCBInfo.TCBLevels.tcb.tdxtcbcomponents.svn[8]
137 | // [9] : Tdxtcbcomp10 : QVL compares with TCBInfo.TCBLevels.tcb.tdxtcbcomponents.svn[9]
138 | // [10] : Tdxtcbcomp11 : QVL compares with TCBInfo.TCBLevels.tcb.tdxtcbcomponents.svn[10]
139 | // [11] : Tdxtcbcomp12 : QVL compares with TCBInfo.TCBLevels.tcb.tdxtcbcomponents.svn[11]
140 | // [12] : Tdxtcbcomp13 : QVL compares with TCBInfo.TCBLevels.tcb.tdxtcbcomponents.svn[12]
141 | // [13] : Tdxtcbcomp14 : QVL compares with TCBInfo.TCBLevels.tcb.tdxtcbcomponents.svn[13]
142 | // [14] : Tdxtcbcomp15 : QVL compares with TCBInfo.TCBLevels.tcb.tdxtcbcomponents.svn[14]
143 | // [15] : Tdxtcbcomp16 : QVL compares with TCBInfo.TCBLevels.tcb.tdxtcbcomponents.svn[15]
144 |
145 | #[derive(Copy, Clone, Debug)]
146 | pub struct TD10ReportBody {
147 | pub tee_tcb_svn: [u8; 16], // [16 bytes]
148 | // Describes the TCB of TDX. (Refer to above)
149 | pub mrseam: [u8; 48], // [48 bytes]
150 | // Measurement of the TDX Module.
151 | pub mrsignerseam: [u8; 48], // [48 bytes]
152 | // Zero for Intel TDX Module
153 | pub seam_attributes: u64, // [8 bytes]
154 | // Must be zero for TDX 1.0
155 | pub td_attributes: u64, // [8 bytes]
156 | // TD Attributes (Refer to above)
157 | pub xfam: u64, // [8 bytes]
158 | // XFAM (eXtended Features Available Mask) is defined as a 64b bitmap, which has the same format as XCR0 or IA32_XSS MSR.
159 | pub mrtd: [u8; 48], // [48 bytes]
160 | // (SHA384) Measurement of the initial contents of the TD.
161 | pub mrconfigid: [u8; 48], // [48 bytes]
162 | // Software-defined ID for non-owner-defined configuration of the TD, e.g., runtime or OS configuration.
163 | pub mrowner: [u8; 48], // [48 bytes]
164 | // Software-defined ID for the TD’s owner
165 | pub mrownerconfig: [u8; 48], // [48 bytes]
166 | // Software-defined ID for owner-defined configuration of the TD,
167 | // e.g., specific to the workload rather than the runtime or OS.
168 | pub rtmr0: [u8; 48], // [48 bytes]
169 | // (SHA384) Root of Trust for Measurement (RTM) for the TD.
170 | pub rtmr1: [u8; 48], // [48 bytes]
171 | // (SHA384) Root of Trust for Measurement (RTM) for the TD.
172 | pub rtmr2: [u8; 48], // [48 bytes]
173 | // (SHA384) Root of Trust for Measurement (RTM) for the TD.
174 | pub rtmr3: [u8; 48], // [48 bytes]
175 | // (SHA384) Root of Trust for Measurement (RTM) for the TD.
176 | pub report_data: [u8; 64], // [64 bytes]
177 | // Additional report data.
178 | // The TD is free to provide 64 bytes of custom data to the REPORT.
179 | // This can be used to provide specific data from the TD or it can be used to hold a hash of a larger block of data which is provided with the quote.
180 | // Note that the signature of a TD Quote covers the REPORTDATA field. As a result, the integrity is protected with a key rooted in an Intel CA.
181 | }
182 |
183 | impl TD10ReportBody {
184 | pub fn from_bytes(raw_bytes: &[u8]) -> Self {
185 | // copy the bytes into the struct
186 | let mut tee_tcb_svn = [0; 16];
187 | tee_tcb_svn.copy_from_slice(&raw_bytes[0..16]);
188 | let mut mrseam = [0; 48];
189 | mrseam.copy_from_slice(&raw_bytes[16..64]);
190 | let mut mrsignerseam = [0; 48];
191 | mrsignerseam.copy_from_slice(&raw_bytes[64..112]);
192 | let seam_attributes = u64::from_le_bytes([raw_bytes[112], raw_bytes[113], raw_bytes[114], raw_bytes[115], raw_bytes[116], raw_bytes[117], raw_bytes[118], raw_bytes[119]]);
193 | let td_attributes = u64::from_le_bytes([raw_bytes[120], raw_bytes[121], raw_bytes[122], raw_bytes[123], raw_bytes[124], raw_bytes[125], raw_bytes[126], raw_bytes[127]]);
194 | let xfam = u64::from_le_bytes([raw_bytes[128], raw_bytes[129], raw_bytes[130], raw_bytes[131], raw_bytes[132], raw_bytes[133], raw_bytes[134], raw_bytes[135]]);
195 | let mut mrtd = [0; 48];
196 | mrtd.copy_from_slice(&raw_bytes[136..184]);
197 | let mut mrconfigid = [0; 48];
198 | mrconfigid.copy_from_slice(&raw_bytes[184..232]);
199 | let mut mrowner = [0; 48];
200 | mrowner.copy_from_slice(&raw_bytes[232..280]);
201 | let mut mrownerconfig = [0; 48];
202 | mrownerconfig.copy_from_slice(&raw_bytes[280..328]);
203 | let mut rtmr0 = [0; 48];
204 | rtmr0.copy_from_slice(&raw_bytes[328..376]);
205 | let mut rtmr1 = [0; 48];
206 | rtmr1.copy_from_slice(&raw_bytes[376..424]);
207 | let mut rtmr2 = [0; 48];
208 | rtmr2.copy_from_slice(&raw_bytes[424..472]);
209 | let mut rtmr3 = [0; 48];
210 | rtmr3.copy_from_slice(&raw_bytes[472..520]);
211 | let mut report_data = [0; 64];
212 | report_data.copy_from_slice(&raw_bytes[520..584]);
213 |
214 | TD10ReportBody {
215 | tee_tcb_svn,
216 | mrseam,
217 | mrsignerseam,
218 | seam_attributes,
219 | td_attributes,
220 | xfam,
221 | mrtd,
222 | mrconfigid,
223 | mrowner,
224 | mrownerconfig,
225 | rtmr0,
226 | rtmr1,
227 | rtmr2,
228 | rtmr3,
229 | report_data,
230 | }
231 | }
232 |
233 | pub fn to_bytes(&self) -> [u8; 584] {
234 | let mut raw_bytes = [0; 584];
235 | raw_bytes[0..16].copy_from_slice(&self.tee_tcb_svn);
236 | raw_bytes[16..64].copy_from_slice(&self.mrseam);
237 | raw_bytes[64..112].copy_from_slice(&self.mrsignerseam);
238 | raw_bytes[112..120].copy_from_slice(&self.seam_attributes.to_le_bytes());
239 | raw_bytes[120..128].copy_from_slice(&self.td_attributes.to_le_bytes());
240 | raw_bytes[128..136].copy_from_slice(&self.xfam.to_le_bytes());
241 | raw_bytes[136..184].copy_from_slice(&self.mrtd);
242 | raw_bytes[184..232].copy_from_slice(&self.mrconfigid);
243 | raw_bytes[232..280].copy_from_slice(&self.mrowner);
244 | raw_bytes[280..328].copy_from_slice(&self.mrownerconfig);
245 | raw_bytes[328..376].copy_from_slice(&self.rtmr0);
246 | raw_bytes[376..424].copy_from_slice(&self.rtmr1);
247 | raw_bytes[424..472].copy_from_slice(&self.rtmr2);
248 | raw_bytes[472..520].copy_from_slice(&self.rtmr3);
249 | raw_bytes[520..584].copy_from_slice(&self.report_data);
250 |
251 | raw_bytes
252 | }
253 | }
--------------------------------------------------------------------------------
/src/types/quotes/mod.rs:
--------------------------------------------------------------------------------
1 | use super::cert::Certificates;
2 |
3 | pub mod version_3;
4 | pub mod version_4;
5 | pub mod body;
6 |
7 | use body::EnclaveReport;
8 |
9 | #[derive(Clone, Debug, PartialEq, Eq)]
10 | pub struct QuoteHeader {
11 | pub version: u16, // [2 bytes]
12 | // Version of the quote data structure - 4, 5
13 | pub att_key_type: u16, // [2 bytes]
14 | // Type of the Attestation Key used by the Quoting Enclave -
15 | // 2 (ECDSA-256-with-P-256 curve)
16 | // 3 (ECDSA-384-with-P-384 curve)
17 | pub tee_type: u32, // [4 bytes]
18 | // TEE for this Attestation
19 | // 0x00000000: SGX
20 | // 0x00000081: TDX
21 | pub qe_svn: [u8; 2], // [2 bytes]
22 | // Security Version of the Quoting Enclave - 1 (only applicable for SGX Quotes)
23 | pub pce_svn: [u8; 2], // [2 bytes]
24 | // Security Version of the PCE - 0 (only applicable for SGX Quotes)
25 | pub qe_vendor_id: [u8; 16], // [16 bytes]
26 | // Unique identifier of the QE Vendor.
27 | // Value: 939A7233F79C4CA9940A0DB3957F0607 (Intel® SGX QE Vendor)
28 | // Note: Each vendor that decides to provide a customized Quote data structure should have
29 | // unique ID.
30 | pub user_data: [u8; 20], // [20 bytes]
31 | // Custom user-defined data. For the Intel® SGX and TDX DCAP Quote Generation Libraries,
32 | // the first 16 bytes contain a Platform Identifier that is used to link a PCK Certificate to an Enc(PPID).
33 | }
34 |
35 | impl QuoteHeader {
36 | pub fn from_bytes(raw_bytes: &[u8]) -> Self {
37 | let version = u16::from_le_bytes([raw_bytes[0], raw_bytes[1]]);
38 | let att_key_type = u16::from_le_bytes([raw_bytes[2], raw_bytes[3]]);
39 | let tee_type = u32::from_le_bytes([raw_bytes[4], raw_bytes[5], raw_bytes[6], raw_bytes[7]]);
40 | let mut qe_svn = [0; 2];
41 | qe_svn.copy_from_slice(&raw_bytes[8..10]);
42 | let mut pce_svn = [0; 2];
43 | pce_svn.copy_from_slice(&raw_bytes[10..12]);
44 | let mut qe_vendor_id = [0; 16];
45 | qe_vendor_id.copy_from_slice(&raw_bytes[12..28]);
46 | let mut user_data = [0; 20];
47 | user_data.copy_from_slice(&raw_bytes[28..48]);
48 |
49 | QuoteHeader {
50 | version,
51 | att_key_type,
52 | tee_type,
53 | qe_svn,
54 | pce_svn,
55 | qe_vendor_id,
56 | user_data,
57 | }
58 | }
59 |
60 | pub fn to_bytes(&self) -> [u8; 48] {
61 | let mut raw_bytes = [0; 48];
62 | raw_bytes[0..2].copy_from_slice(&self.version.to_le_bytes());
63 | raw_bytes[2..4].copy_from_slice(&self.att_key_type.to_le_bytes());
64 | raw_bytes[4..8].copy_from_slice(&self.tee_type.to_le_bytes());
65 | raw_bytes[8..10].copy_from_slice(&self.qe_svn);
66 | raw_bytes[10..12].copy_from_slice(&self.pce_svn);
67 | raw_bytes[12..28].copy_from_slice(&self.qe_vendor_id);
68 | raw_bytes[28..48].copy_from_slice(&self.user_data);
69 |
70 | raw_bytes
71 | }
72 | }
73 |
74 | #[derive(Clone, Debug, PartialEq, Eq)]
75 | pub struct QeAuthData {
76 | pub size: u16,
77 | pub data: Vec,
78 | }
79 |
80 | impl QeAuthData {
81 | pub fn from_bytes(raw_bytes: &[u8]) -> QeAuthData {
82 | let size = u16::from_le_bytes([raw_bytes[0], raw_bytes[1]]);
83 | let data = raw_bytes[2..2+size as usize].to_vec();
84 | QeAuthData {
85 | size,
86 | data,
87 | }
88 | }
89 | }
90 |
91 | #[derive(Clone, Debug, PartialEq, Eq)]
92 | pub struct CertData {
93 | pub cert_data_type: u16, // [2 bytes]
94 | // Determines type of data required to verify the QE Report Signature in the Quote Signature Data structure.
95 | // 1 - (PCK identifier: PPID in plain text, CPUSVN, and PCESVN)
96 | // 2 - (PCK identifier: PPID encrypted using RSA-2048-OAEP, CPUSVN, and PCESVN)
97 | // 3 - (PCK identifier: PPID encrypted using RSA-2048-OAEP, CPUSVN, PCESVN, and QEID)
98 | // 4 - (PCK Leaf Certificate in plain text; currently not supported)
99 | // 5 - (Concatenated PCK Cert Chain)
100 | // 6 - (QE Report Certification Data)
101 | // 7 - (PLATFORM_MANIFEST; currently not supported)
102 | pub cert_data_size: u32, // [4 bytes]
103 | // Size of Certification Data field.
104 | pub cert_data: Vec, // [variable bytes]
105 | // Data required to verify the QE Report Signature depending on the value of the Certification Data Type:
106 | // 1: Byte array that contains concatenation of PPID, CPUSVN, PCESVN (LE), PCEID (LE).
107 | // 2: Byte array that contains concatenation of PPID encrypted using RSA-2048-OAEP, CPUSVN, PCESVN (LE), PCEID (LE).
108 | // 3: Byte array that contains concatenation of PPID encrypted using RSA-3072-OAEP, CPUSVN, PCESVN (LE), PCEID (LE).
109 | // 4: PCK Leaf Certificate
110 | // 5: Concatenated PCK Cert Chain (PEM formatted). PCK Leaf Cert || Intermediate CA Cert || Root CA Cert
111 | // 6: QE Report Certification Data
112 | // 7: PLATFORM_MANIFEST
113 | }
114 |
115 | impl CertData {
116 | pub fn from_bytes(raw_bytes: &[u8]) -> Self {
117 | let cert_data_type = u16::from_le_bytes([raw_bytes[0], raw_bytes[1]]);
118 | let cert_data_size = u32::from_le_bytes([raw_bytes[2], raw_bytes[3], raw_bytes[4], raw_bytes[5]]);
119 | let cert_data = raw_bytes[6..6+cert_data_size as usize].to_vec();
120 |
121 | CertData {
122 | cert_data_type,
123 | cert_data_size,
124 | cert_data,
125 | }
126 | }
127 |
128 | pub fn get_cert_data(&self) -> CertDataType {
129 | match self.cert_data_type {
130 | 1 => CertDataType::Type1(self.cert_data.clone()),
131 | 2 => CertDataType::Type2(self.cert_data.clone()),
132 | 3 => CertDataType::Type3(self.cert_data.clone()),
133 | 4 => CertDataType::Type4(self.cert_data.clone()),
134 | 5 => CertDataType::CertChain(Certificates::from_pem(&self.cert_data)),
135 | 6 => CertDataType::QeReportCertData(QeReportCertData::from_bytes(&self.cert_data)),
136 | 7 => CertDataType::Type7(self.cert_data.clone()),
137 | _ => CertDataType::Unused,
138 | }
139 | }
140 | }
141 |
142 | pub enum CertDataType {
143 | Unused,
144 | Type1(Vec),
145 | Type2(Vec),
146 | Type3(Vec),
147 | Type4(Vec),
148 | CertChain(Certificates),
149 | QeReportCertData(QeReportCertData),
150 | Type7(Vec),
151 | }
152 |
153 | #[derive(Clone, Debug)]
154 | pub struct QeReportCertData {
155 | pub qe_report: EnclaveReport,
156 | pub qe_report_signature: [u8; 64],
157 | pub qe_auth_data: QeAuthData,
158 | pub qe_cert_data: CertData,
159 | }
160 |
161 | impl QeReportCertData {
162 | pub fn from_bytes(raw_bytes: &[u8]) -> Self {
163 | // 384 bytes for qe_report
164 | let qe_report = EnclaveReport::from_bytes(&raw_bytes[0..384]);
165 | // 64 bytes for qe_report_signature
166 | let mut qe_report_signature = [0; 64];
167 | qe_report_signature.copy_from_slice(&raw_bytes[384..448]);
168 | // qe auth data is variable length, we'll pass remaining bytes to the from_bytes method
169 | let qe_auth_data = QeAuthData::from_bytes(&raw_bytes[448..]);
170 | // get the length of qe_auth_data
171 | let qe_auth_data_size = 2 + qe_auth_data.size as usize;
172 | // finish off with the parsing of qe_cert_data
173 | let qe_cert_data_start = 448 + qe_auth_data_size;
174 | let qe_cert_data = CertData::from_bytes(&raw_bytes[qe_cert_data_start..]);
175 |
176 | QeReportCertData {
177 | qe_report,
178 | qe_report_signature,
179 | qe_auth_data,
180 | qe_cert_data,
181 | }
182 | }
183 | }
--------------------------------------------------------------------------------
/src/types/quotes/version_3.rs:
--------------------------------------------------------------------------------
1 | // https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_SGX_ECDSA_QuoteLibReference_DCAP_API.pdf
2 |
3 | use super::{QuoteHeader, QeAuthData, CertData, body::EnclaveReport};
4 |
5 | // high level sgx quote structure
6 | // [48 - header] [384 - isv enclave report] [4 - quote signature length] [var - quote signature]
7 | #[derive(Clone, Debug, PartialEq, Eq)]
8 | pub struct QuoteV3 {
9 | pub header: QuoteHeader, // [48 bytes]
10 | // Header of Quote data structure. This field is transparent (the user knows
11 | // its internal structure). Rest of the Quote data structure can be
12 | // treated as opaque (hidden from the user).
13 | pub isv_enclave_report: EnclaveReport, // [384 bytes]
14 | // Report of the attested ISV Enclave.
15 | // The CPUSVN and ISVSVN is the TCB when the quote is generated.
16 | // The REPORT.ReportData is defined by the ISV but should provide quote replay
17 | // protection if required.
18 | pub signature_len: u32, // [4 bytes]
19 | // Size of the Quote Signature Data structure in bytes.
20 | pub signature: QuoteSignatureDataV3, // [variable bytes]
21 | // Variable-length data containing the signature and supporting data.
22 | // E.g. ECDSA 256-bit Quote Signature Data Structure (SgxQuoteSignatureData)
23 | }
24 |
25 | impl QuoteV3 {
26 | pub fn from_bytes(raw_bytes: &[u8]) -> QuoteV3 {
27 | let header = QuoteHeader::from_bytes(&raw_bytes[0..48]);
28 | let isv_enclave_report = EnclaveReport::from_bytes(&raw_bytes[48..432]);
29 | let signature_len = u32::from_le_bytes([raw_bytes[432], raw_bytes[433], raw_bytes[434], raw_bytes[435]]);
30 | // allocate and create a buffer for signature
31 | let signature_slice = &raw_bytes[436..436 + signature_len as usize];
32 | let signature = QuoteSignatureDataV3::from_bytes(signature_slice);
33 |
34 | QuoteV3 {
35 | header,
36 | isv_enclave_report,
37 | signature_len,
38 | signature,
39 | }
40 | }
41 | }
42 |
43 | #[derive(Clone, Debug, PartialEq, Eq)]
44 | pub struct QuoteSignatureDataV3 {
45 | pub isv_enclave_report_signature: [u8; 64], // ECDSA signature, the r component followed by the s component, 2 x 32 bytes.
46 | pub ecdsa_attestation_key: [u8; 64], // EC KT-I Public Key, the x-coordinate followed by the y-coordinate
47 | // (on the RFC 6090 P-256 curve), 2 x 32 bytes.
48 | pub qe_report: EnclaveReport,
49 | pub qe_report_signature: [u8; 64],
50 | pub qe_auth_data: QeAuthData,
51 | pub qe_cert_data: CertData,
52 | }
53 |
54 | impl QuoteSignatureDataV3 {
55 | pub fn from_bytes(raw_bytes: &[u8]) -> QuoteSignatureDataV3 {
56 | let mut isv_enclave_report_signature = [0u8; 64];
57 | let mut ecdsa_attestation_key = [0u8; 64];
58 | let mut qe_report_signature = [0u8; 64];
59 |
60 | isv_enclave_report_signature.copy_from_slice(&raw_bytes[0..64]);
61 | ecdsa_attestation_key.copy_from_slice(&raw_bytes[64..128]);
62 | let qe_report = EnclaveReport::from_bytes(&raw_bytes[128..512]);
63 | qe_report_signature.copy_from_slice(&raw_bytes[512..576]);
64 | let qe_auth_data = QeAuthData::from_bytes(&raw_bytes[576..]);
65 | let qe_cert_data_start = 576 + 2 + qe_auth_data.size as usize;
66 | let qe_cert_data = CertData::from_bytes(&raw_bytes[qe_cert_data_start..]);
67 |
68 | QuoteSignatureDataV3 {
69 | isv_enclave_report_signature,
70 | ecdsa_attestation_key,
71 | qe_report,
72 | qe_report_signature,
73 | qe_auth_data,
74 | qe_cert_data,
75 | }
76 | }
77 | }
--------------------------------------------------------------------------------
/src/types/quotes/version_4.rs:
--------------------------------------------------------------------------------
1 | use super::{CertData, QuoteHeader, body::*};
2 | use crate::constants::{ENCLAVE_REPORT_LEN, SGX_TEE_TYPE, TD10_REPORT_LEN, TDX_TEE_TYPE};
3 |
4 | #[derive(Clone, Debug)]
5 | pub struct QuoteV4 {
6 | pub header: QuoteHeader, // [48 bytes]
7 | // Header of Quote data structure.
8 | // This field is transparent (the user knows its internal structure).
9 | // Rest of the Quote data structure can be treated as opaque (hidden from the user).
10 | pub quote_body: QuoteBody, // May either contain a SGX Enclave Report (384 bytes) or TD10 Report (584 bytes)
11 | pub signature_len: u32, // [4 bytes]
12 | // Size of the Quote Signature Data structure in bytes.
13 | pub signature: QuoteSignatureDataV4, // [variable bytes]
14 | }
15 |
16 | impl QuoteV4 {
17 | pub fn from_bytes(raw_bytes: &[u8]) -> Self {
18 | let header = QuoteHeader::from_bytes(&raw_bytes[0..48]);
19 | let quote_body;
20 | let mut offset: usize = 48;
21 | match header.tee_type {
22 | SGX_TEE_TYPE => {
23 | offset += ENCLAVE_REPORT_LEN;
24 | quote_body = QuoteBody::SGXQuoteBody(EnclaveReport::from_bytes(&raw_bytes[48..offset]));
25 | },
26 | TDX_TEE_TYPE => {
27 | offset += TD10_REPORT_LEN;
28 | quote_body = QuoteBody::TD10QuoteBody(TD10ReportBody::from_bytes(&raw_bytes[48..offset]));
29 | },
30 | _ => {
31 | panic!("Unknown TEE type")
32 | }
33 | }
34 | let signature_len = u32::from_le_bytes([
35 | raw_bytes[offset],
36 | raw_bytes[offset + 1],
37 | raw_bytes[offset + 2],
38 | raw_bytes[offset + 3]
39 | ]);
40 | offset += 4;
41 | let signature_slice = &raw_bytes[offset..offset+signature_len as usize];
42 | let signature = QuoteSignatureDataV4::from_bytes(signature_slice);
43 |
44 | QuoteV4 {
45 | header,
46 | quote_body,
47 | signature_len,
48 | signature,
49 | }
50 | }
51 | }
52 |
53 | #[derive(Clone, Debug)]
54 | pub struct QuoteSignatureDataV4 {
55 | pub quote_signature: [u8; 64], // [64 bytes]
56 | // ECDSA signature, the r component followed by the s component, 2 x 32 bytes.
57 | // Public part of the Attestation Key generated by the Quoting Enclave.
58 | pub ecdsa_attestation_key: [u8; 64],// [64 bytes]
59 | // EC KT-I Public Key, the x-coordinate followed by the y-coordinate (on the RFC 6090 P-256 curve), 2 x 32 bytes.
60 | // Public part of the Attestation Key generated by the Quoting Enclave.
61 | pub qe_cert_data: CertData, // [variable bytes]
62 | // QE Cert Data
63 | }
64 |
65 | impl QuoteSignatureDataV4 {
66 | pub fn from_bytes(raw_bytes: &[u8]) -> Self {
67 | let mut quote_signature = [0; 64];
68 | quote_signature.copy_from_slice(&raw_bytes[0..64]);
69 | let mut ecdsa_attestation_key = [0; 64];
70 | ecdsa_attestation_key.copy_from_slice(&raw_bytes[64..128]);
71 | let qe_cert_data = CertData::from_bytes(&raw_bytes[128..]);
72 |
73 | QuoteSignatureDataV4 {
74 | quote_signature,
75 | ecdsa_attestation_key,
76 | qe_cert_data,
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/types/tcbinfo.rs:
--------------------------------------------------------------------------------
1 | use serde::{Deserialize, Serialize};
2 |
3 | pub enum TcbInfo {
4 | V2(TcbInfoV2),
5 | V3(TcbInfoV3)
6 | }
7 |
8 | // TcbInfoV2:
9 | // type: object
10 | // description: >-
11 | // SGX TCB Info encoded as JSON string in case of success (200 HTTP
12 | // status code)
13 | // properties:
14 | // tcbInfo:
15 | // type: object
16 | // properties:
17 | // version:
18 | // type: integer
19 | // example: 2
20 | // description: Version of the structure
21 | // issueDate:
22 | // type: string
23 | // format: date-time
24 | // description: >-
25 | // Representation of date and time the TCB information
26 | // was created. The time shall be in UTC and the
27 | // encoding shall be compliant to ISO 8601 standard
28 | // (YYYY-MM-DDThh:mm:ssZ)
29 | // nextUpdate:
30 | // type: string
31 | // format: date-time
32 | // description: >-
33 | // Representation of date and time by which next TCB
34 | // information will be issued. The time shall be in UTC
35 | // and the encoding shall be compliant to ISO 8601
36 | // standard (YYYY-MM-DDThh:mm:ssZ)
37 | // fmspc:
38 | // type: string
39 | // pattern: ^[0-9a-fA-F]{12}$
40 | // example: '000000000000'
41 | // description: >-
42 | // Base 16-encoded string representation of FMSPC
43 | // (Family-Model-Stepping-Platform-CustomSKU)
44 | // pceId:
45 | // type: string
46 | // pattern: ^[0-9a-fA-F]{4}$
47 | // example: '0000'
48 | // description: Base 16-encoded string representation of PCE identifier
49 | // tcbType:
50 | // type: integer
51 | // example: 0
52 | // description: >-
53 | // Type of TCB level composition that determines TCB
54 | // level comparison logic
55 | // tcbEvaluationDataNumber:
56 | // type: integer
57 | // example: 2
58 | // description: >-
59 | // A monotonically increasing sequence number changed
60 | // when Intel updates the content of the TCB evaluation data
61 | // set: TCB Info, QE Idenity and QVE Identity. The tcbEvaluationDataNumber
62 | // update is synchronized across TCB Info for all flavors of
63 | // SGX CPUs (Family-Model-Stepping-Platform-CustomSKU) and QE/QVE
64 | // Identity. This sequence number allows users to easily determine
65 | // when a particular TCB Info/QE Idenity/QVE Identiy superseedes
66 | // another TCB Info/QE Identity/QVE Identity (value: current
67 | // TCB Recovery event number stored in the database).
68 | // tcbLevels:
69 | // type: array
70 | // description: >-
71 | // Sorted list of supported TCB levels for given FMSPC
72 | // encoded as a JSON array of TCB level objects
73 | // items:
74 | // type: object
75 | // properties:
76 | // tcb:
77 | // type: object
78 | // properties:
79 | // pcesvn:
80 | // type: integer
81 | // example: 0
82 | // minimum: 0
83 | // maximum: 65535
84 | // sgxtcbcomp01svn:
85 | // type: integer
86 | // example: 0
87 | // minimum: 0
88 | // maximum: 255
89 | // sgxtcbcomp02svn:
90 | // type: integer
91 | // example: 0
92 | // minimum: 0
93 | // maximum: 255
94 | // sgxtcbcomp03svn:
95 | // type: integer
96 | // example: 0
97 | // minimum: 0
98 | // maximum: 255
99 | // sgxtcbcomp04svn:
100 | // type: integer
101 | // example: 0
102 | // minimum: 0
103 | // maximum: 255
104 | // sgxtcbcomp05svn:
105 | // type: integer
106 | // example: 0
107 | // minimum: 0
108 | // maximum: 255
109 | // sgxtcbcomp06svn:
110 | // type: integer
111 | // example: 0
112 | // minimum: 0
113 | // maximum: 255
114 | // sgxtcbcomp07svn:
115 | // type: integer
116 | // example: 0
117 | // minimum: 0
118 | // maximum: 255
119 | // sgxtcbcomp08svn:
120 | // type: integer
121 | // example: 0
122 | // minimum: 0
123 | // maximum: 255
124 | // sgxtcbcomp09svn:
125 | // type: integer
126 | // example: 0
127 | // minimum: 0
128 | // maximum: 255
129 | // sgxtcbcomp10svn:
130 | // type: integer
131 | // example: 0
132 | // minimum: 0
133 | // maximum: 255
134 | // sgxtcbcomp11svn:
135 | // type: integer
136 | // example: 0
137 | // minimum: 0
138 | // maximum: 255
139 | // sgxtcbcomp12svn:
140 | // type: integer
141 | // example: 0
142 | // minimum: 0
143 | // maximum: 255
144 | // sgxtcbcomp13svn:
145 | // type: integer
146 | // example: 0
147 | // minimum: 0
148 | // maximum: 255
149 | // sgxtcbcomp14svn:
150 | // type: integer
151 | // example: 0
152 | // minimum: 0
153 | // maximum: 255
154 | // sgxtcbcomp15svn:
155 | // type: integer
156 | // example: 0
157 | // minimum: 0
158 | // maximum: 255
159 | // sgxtcbcomp16svn:
160 | // type: integer
161 | // example: 0
162 | // minimum: 0
163 | // maximum: 255
164 | // tcbDate:
165 | // type: string
166 | // format: date-time
167 | // description: >-
168 | // If there are security advisories published by Intel after tcbDate
169 | // that are for issues whose mitigations are currently enforced* by SGX attestation,
170 | // then the value of tcbStatus for the TCB level will not be UpToDate.
171 | // Otherwise (i.e., either no advisories after or not currently enforced),
172 | // the value of tcbStatus for the TCB level will not be OutOfDate.
173 | //
174 | // The time shall be in UTC and the encoding shall
175 | // be compliant to ISO 8601 standard (YYYY-MM-DDThh:mm:ssZ).
176 | // tcbStatus:
177 | // type: string
178 | // enum:
179 | // - UpToDate
180 | // - SWHardeningNeeded
181 | // - ConfigurationNeeded
182 | // - ConfigurationAndSWHardeningNeeded
183 | // - OutOfDate
184 | // - OutOfDateConfigurationNeeded
185 | // - Revoked
186 | // description: >-
187 | // TCB level status. One of the following values:
188 | //
189 | // "UpToDate" - TCB level of the SGX platform is up-to-date.
190 | //
191 | // "SWHardeningNeeded" - TCB level of the SGX platform
192 | // is up-to-date but due to certain issues affecting the
193 | // platform, additional SW Hardening in the attesting
194 | // SGX enclaves may be needed.
195 | //
196 | // "ConfigurationNeeded" - TCB level of the SGX platform
197 | // is up-to-date but additional configuration of SGX
198 | // platform may be needed.
199 | //
200 | // "ConfigurationAndSWHardeningNeeded" - TCB level of the
201 | // SGX platform is up-to-date but additional configuration
202 | // for the platform and SW Hardening in the attesting SGX
203 | // enclaves may be needed.
204 | //
205 | // "OutOfDate" - TCB level of SGX platform is outdated.
206 | //
207 | // "OutOfDateConfigurationNeeded" - TCB level of SGX
208 | // platform is outdated and additional configuration
209 | // of SGX platform may be needed.
210 | //
211 | // "Revoked" - TCB level of SGX platform is revoked.
212 | // The platform is not trustworthy.
213 | // ZL: This new field is added for v3, seems like a mistake in Intel's documentation.
214 | // Going to keep it here for now.
215 | // advisoryIDs:
216 | // type: array
217 | // description: >-
218 | // Array of Advisory IDs referring to Intel security advisories that
219 | // provide insight into the reason(s) for the value of tcbStatus for
220 | // this TCB level when the value is not UpToDate.
221 | //
222 | // Note: The value can be different for different
223 | // FMSPCs.
224 | //
225 | // This field is optional. It will be present only
226 | // if the list of Advisory IDs is not empty.
227 | // items:
228 | // type: string
229 | // signature:
230 | // type: string
231 | // description: >-
232 | // Base 16-encoded string representation of signature calculated over tcbInfo
233 | // body without whitespaces using TCB Signing Key
234 | // i.e:
235 | // {"version":2,"issueDate":"2019-07-30T12:00:00Z","nextUpdate":"2019-08-30T12:00:00Z",...}
236 |
237 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
238 | #[serde(rename_all = "camelCase")]
239 | pub struct TcbInfoV2 {
240 | pub tcb_info: TcbInfoV2Inner,
241 | pub signature: String,
242 | }
243 |
244 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
245 | #[serde(rename_all = "camelCase")]
246 | pub struct TcbInfoV2Inner {
247 | pub version: u32,
248 | pub issue_date: String,
249 | pub next_update: String,
250 | pub fmspc: String,
251 | pub pce_id: String,
252 | pub tcb_type: u8,
253 | pub tcb_evaluation_data_number: u32,
254 | pub tcb_levels: Vec,
255 | }
256 |
257 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
258 | #[serde(rename_all = "camelCase")]
259 | pub struct TcbInfoV2TcbLevelItem {
260 | pub tcb: TcbInfoV2TcbLevel,
261 | pub tcb_date: String,
262 | pub tcb_status: String,
263 | }
264 |
265 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
266 | #[serde(rename_all = "camelCase")]
267 | pub struct TcbInfoV2TcbLevel {
268 | pub sgxtcbcomp01svn: u8,
269 | pub sgxtcbcomp02svn: u8,
270 | pub sgxtcbcomp03svn: u8,
271 | pub sgxtcbcomp04svn: u8,
272 | pub sgxtcbcomp05svn: u8,
273 | pub sgxtcbcomp06svn: u8,
274 | pub sgxtcbcomp07svn: u8,
275 | pub sgxtcbcomp08svn: u8,
276 | pub sgxtcbcomp09svn: u8,
277 | pub sgxtcbcomp10svn: u8,
278 | pub sgxtcbcomp11svn: u8,
279 | pub sgxtcbcomp12svn: u8,
280 | pub sgxtcbcomp13svn: u8,
281 | pub sgxtcbcomp14svn: u8,
282 | pub sgxtcbcomp15svn: u8,
283 | pub sgxtcbcomp16svn: u8,
284 | pub pcesvn: u16,
285 | }
286 | // TcbInfoV3:
287 | // type: object
288 | // description: >-
289 | // SGX TCB Info encoded as JSON string in case of success (200 HTTP
290 | // status code)
291 | // properties:
292 | // tcbInfo:
293 | // type: object
294 | // properties:
295 | // id:
296 | // type: string
297 | // description: Identifier of the TCB Info issued by Intel. Supported values are SGX or TDX.
298 | // version:
299 | // type: integer
300 | // example: 2
301 | // description: Version of the structure
302 | // issueDate:
303 | // type: string
304 | // format: date-time
305 | // description: >-
306 | // Representation of date and time the TCB information
307 | // was created. The time shall be in UTC and the
308 | // encoding shall be compliant to ISO 8601 standard
309 | // (YYYY-MM-DDThh:mm:ssZ)
310 | // nextUpdate:
311 | // type: string
312 | // format: date-time
313 | // description: >-
314 | // Representation of date and time by which next TCB
315 | // information will be issued. The time shall be in UTC
316 | // and the encoding shall be compliant to ISO 8601
317 | // standard (YYYY-MM-DDThh:mm:ssZ)
318 | // fmspc:
319 | // type: string
320 | // pattern: ^[0-9a-fA-F]{12}$
321 | // example: '000000000000'
322 | // description: >-
323 | // Base 16-encoded string representation of FMSPC
324 | // (Family-Model-Stepping-Platform-CustomSKU)
325 | // pceId:
326 | // type: string
327 | // pattern: ^[0-9a-fA-F]{4}$
328 | // example: '0000'
329 | // description: Base 16-encoded string representation of PCE identifier
330 | // tcbType:
331 | // type: integer
332 | // example: 0
333 | // description: >-
334 | // Type of TCB level composition that determines TCB
335 | // level comparison logic
336 | // tcbEvaluationDataNumber:
337 | // type: integer
338 | // example: 2
339 | // description: >-
340 | // A monotonically increasing sequence number changed
341 | // when Intel updates the content of the TCB evaluation data
342 | // set: TCB Info, QE Idenity and QVE Identity. The tcbEvaluationDataNumber
343 | // update is synchronized across TCB Info for all flavors of
344 | // SGX CPUs (Family-Model-Stepping-Platform-CustomSKU) and QE/QVE
345 | // Identity. This sequence number allows users to easily determine
346 | // when a particular TCB Info/QE Idenity/QVE Identiy superseedes
347 | // another TCB Info/QE Identity/QVE Identity (value: current
348 | // TCB Recovery event number stored in the database).
349 | // tdxModule:
350 | // type: object
351 | // description: >-
352 | // This field is optional. It will be present only
353 | // in context of TDX TCB Info.
354 | // properties:
355 | // mrsigner:
356 | // type: string
357 | // pattern: ^[0-9a-fA-F]{96}$
358 | // example: '0000000000000000000000000000000000000000000000000000000000000000'
359 | // description: Base 16-encoded string representation of the measurement of a TDX SEAM module's signer.
360 | // attributes:
361 | // type: string
362 | // pattern: ^[0-9a-fA-F]{16}$
363 | // example: '0000000000000000'
364 | // description: Hex-encoded byte array (8 bytes) representing attributes "golden" value (upon applying mask) for TDX SEAM module.
365 | // attributesMask:
366 | // type: string
367 | // pattern: ^[0-9a-fA-F]{16}$
368 | // example: 'FFFFFFFFFFFFFFFF'
369 | // description: Hex-encoded byte array (8 bytes) representing mask to be applied to TDX SEAM module's attributes value retrieved from the platform.
370 | // tdxModuleIdentities:
371 | // type: array
372 | // description: >-
373 | // This field is optional. It will be present only in context of TDX TCB Info when the platform supports more than one TDX SEAM Module.
374 | // items:
375 | // type: object
376 | // properties:
377 | // id:
378 | // type: string
379 | // description: Identifier of TDX Module
380 | // mrsigner:
381 | // type: string
382 | // pattern: ^[0-9a-fA-F]{96}$
383 | // example: '0000000000000000000000000000000000000000000000000000000000000000'
384 | // description: Base 16-encoded string representation of the measurement of a TDX SEAM module's signer.
385 | // attributes:
386 | // type: string
387 | // pattern: ^[0-9a-fA-F]{16}$
388 | // example: '0000000000000000'
389 | // description: Base 16-encoded string representation of the byte array (8 bytes) representing attributes "golden" value (upon applying mask) for TDX SEAM module.
390 | // attributesMask:
391 | // type: string
392 | // pattern: ^[0-9a-fA-F]{16}$
393 | // example: 'FFFFFFFFFFFFFFFF'
394 | // description: Base 16-encoded string representation of the byte array (8 bytes) representing mask to be applied to TDX SEAM module's attributes value retrieved from the platform.
395 | // tcbLevels:
396 | // type: array
397 | // description: >-
398 | // Sorted list of supported TCB levels for given TDX SEAM module encoded as a JSON array of TCB level objects.
399 | // items:
400 | // type: object
401 | // properties:
402 | // tcb:
403 | // type: object
404 | // properties:
405 | // isvnsvn:
406 | // description: TDX SEAM module's ISV SVN
407 | // type: integer
408 | // tcbDate:
409 | // type: string
410 | // format: date-time
411 | // description: >-
412 | // If there are security advisories published by Intel after tcbDate
413 | // that are for issues whose mitigations are currently enforced* by SGX/TDX attestation,
414 | // then the value of tcbStatus for the TCB level will not be UpToDate.
415 | // Otherwise (i.e., either no advisories after or not currently enforced),
416 | // the value of tcbStatus for the TCB level will not be OutOfDate.
417 | //
418 | // The time shall be in UTC and the encoding shall
419 | // be compliant to ISO 8601 standard (YYYY-MM-DDThh:mm:ssZ).
420 | // tcbStatus:
421 | // type: string
422 | // enum:
423 | // - UpToDate
424 | // - OutOfDate
425 | // - Revoked
426 | // description: >-
427 | // TCB level status. One of the following values:
428 | //
429 | // "UpToDate" - TCB level of the TDX SEAM Module is up-to-date.
430 | //
431 | // "OutOfDate" - TCB level of TDX SEAM Module is outdated.
432 | //
433 | // "Revoked" - TCB level of TDX SEAM Module is revoked.
434 | // The platform is not trustworthy.
435 | // advisoryIDs:
436 | // type: array
437 | // description: >-
438 | // Array of Advisory IDs referring to Intel security advisories that
439 | // provide insight into the reason(s) for the value of tcbStatus for
440 | // this TCB level when the value is not UpToDate.
441 | //
442 | // This field is optional. It will be present only
443 | // if the list of Advisory IDs is not empty.
444 | // items:
445 | // type: string
446 | // tcbLevels:
447 | // type: array
448 | // description: >-
449 | // Sorted list of supported TCB levels for given FMSPC
450 | // encoded as a JSON array of TCB level objects
451 | // items:
452 | // type: object
453 | // properties:
454 | // tcb:
455 | // type: object
456 | // properties:
457 | // sgxtcbcomponents:
458 | // description: >-
459 | // Array of 16 SGX TCB Components (as in CPUSVN) encoded as a JSON array of TCB Component objects.
460 | // items:
461 | // properties:
462 | // svn:
463 | // type: "integer"
464 | // description: SVN of TCB Component. This field is mandatory.
465 | // category:
466 | // type: "string"
467 | // description: Category of TCB Component (e.g. ucode, BIOS, SW). This field is optional and will be present only for selected TCB Components.
468 | // type:
469 | // type: "string"
470 | // description: Type of TCB Component (e.g. Patch@Reset, Late Patch). This field is optional and will be present only for selected TCB Components.
471 | // pcesvn:
472 | // type: integer
473 | // example: 0
474 | // minimum: 0
475 | // maximum: 65535
476 | // tdxtcbcomponents:
477 | // description: >-
478 | // Array of 16 TDX TCB Components (as in TEE TCB SVN array in TD Report) encoded as a JSON array of TCB Component objects.
479 | //
480 | // This field is optional and only present in TDX TCB Info.
481 | // items:
482 | // properties:
483 | // svn:
484 | // type: "integer"
485 | // description: SVN of TCB Component. This field is mandatory.
486 | // category:
487 | // type: "string"
488 | // description: Category of TCB Component (e.g. ucode, BIOS, SW). This field is optional and will be present only for selected TCB Components.
489 | // type:
490 | // type: "string"
491 | // description: Type of TCB Component (e.g. Patch@Reset, Late Patch). This field is optional and will be present only for selected TCB Components.
492 | // tcbDate:
493 | // type: string
494 | // format: date-time
495 | // description: >-
496 | // If there are security advisories published by Intel after tcbDate
497 | // that are for issues whose mitigations are currently enforced* by SGX attestation,
498 | // then the value of tcbStatus for the TCB level will not be UpToDate.
499 | // Otherwise (i.e., either no advisories after or not currently enforced),
500 | // the value of tcbStatus for the TCB level will not be OutOfDate.
501 | //
502 | // The time shall be in UTC and the encoding shall
503 | // be compliant to ISO 8601 standard (YYYY-MM-DDThh:mm:ssZ).
504 | // tcbStatus:
505 | // type: string
506 | // enum:
507 | // - UpToDate
508 | // - SWHardeningNeeded
509 | // - ConfigurationNeeded
510 | // - ConfigurationAndSWHardeningNeeded
511 | // - OutOfDate
512 | // - OutOfDateConfigurationNeeded
513 | // - Revoked
514 | // description: >-
515 | // TCB level status. One of the following values:
516 | //
517 | // "UpToDate" - TCB level of the SGX platform is up-to-date.
518 | //
519 | // "SWHardeningNeeded" - TCB level of the SGX platform
520 | // is up-to-date but due to certain issues affecting the
521 | // platform, additional SW Hardening in the attesting
522 | // SGX enclaves may be needed.
523 | //
524 | // "ConfigurationNeeded" - TCB level of the SGX platform
525 | // is up-to-date but additional configuration of SGX
526 | // platform may be needed.
527 | //
528 | // "ConfigurationAndSWHardeningNeeded" - TCB level of the
529 | // SGX platform is up-to-date but additional configuration
530 | // for the platform and SW Hardening in the attesting SGX
531 | // enclaves may be needed.
532 | //
533 | // "OutOfDate" - TCB level of SGX platform is outdated.
534 | //
535 | // "OutOfDateConfigurationNeeded" - TCB level of SGX
536 | // platform is outdated and additional configuration
537 | // of SGX platform may be needed.
538 | //
539 | // "Revoked" - TCB level of SGX platform is revoked.
540 | // The platform is not trustworthy.
541 | // advisoryIDs:
542 | // type: array
543 | // description: >-
544 | // Array of Advisory IDs referring to Intel security advisories that
545 | // provide insight into the reason(s) for the value of tcbStatus for
546 | // this TCB level when the value is not UpToDate.
547 | //
548 | // Note: The value can be different for different
549 | // FMSPCs.
550 | //
551 | // This field is optional. It will be present only
552 | // if the list of Advisory IDs is not empty.
553 | // items:
554 | // type: string
555 | // signature:
556 | // type: string
557 | // description: >-
558 | // Base 16-encoded string representation of signature calculated over tcbInfo
559 | // body without whitespaces using TCB Signing Key
560 | // i.e:
561 | // {"version":2,"issueDate":"2019-07-30T12:00:00Z","nextUpdate":"2019-08-30T12:00:00Z",...}
562 |
563 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
564 | #[serde(rename_all = "camelCase")]
565 | pub struct TcbInfoV3 {
566 | pub tcb_info: TcbInfoV3Inner,
567 | pub signature: String,
568 | }
569 |
570 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
571 | #[serde(rename_all = "camelCase")]
572 | pub struct TcbInfoV3Inner {
573 | pub id: String,
574 | pub version: u32,
575 | pub issue_date: String,
576 | pub next_update: String,
577 | pub fmspc: String,
578 | pub pce_id: String,
579 | pub tcb_type: u8,
580 | pub tcb_evaluation_data_number: u32,
581 | #[serde(skip_serializing_if = "Option::is_none")]
582 | pub tdx_module: Option,
583 | #[serde(skip_serializing_if = "Option::is_none")]
584 | pub tdx_module_identities: Option>,
585 | pub tcb_levels: Vec,
586 | }
587 |
588 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
589 | #[serde(rename_all = "camelCase")]
590 | pub struct TdxModule {
591 | pub mrsigner: String, // Base 16-encoded string representation of the measurement of a TDX SEAM module’s signer.
592 | pub attributes: String, // Hex-encoded byte array (8 bytes) representing attributes "golden" value.
593 | pub attributes_mask: String, // Hex-encoded byte array (8 bytes) representing mask to be applied to TDX SEAM module’s
594 | // attributes value retrieved from the platform
595 | }
596 |
597 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
598 | #[serde(rename_all = "camelCase")]
599 | pub struct TdxModuleIdentities {
600 | pub id: String, // Identifier of TDX Module
601 | pub mrsigner: String, // Base 16-encoded string representation of the measurement of a TDX SEAM module’s signer.
602 | pub attributes: String, // Base 16-encoded string representation of the byte array (8 bytes) representing attributes "golden" value.
603 | pub attributes_mask: String, // Base 16-encoded string representation of the byte array (8 bytes) representing mask to be applied to TDX SEAM module’s
604 | // attributes value retrieved from the platform
605 | pub tcb_levels: Vec,
606 | }
607 |
608 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
609 | #[serde(rename_all = "camelCase")]
610 | pub struct TdxModuleIdentitiesTcbLevelItem {
611 | pub tcb: TdxModuleIdentitiesTcbLevel,
612 | pub tcb_date: String,
613 | pub tcb_status: String,
614 | #[serde(rename(serialize = "advisoryIDs", deserialize = "advisoryIDs"))]
615 | #[serde(skip_serializing_if = "Option::is_none")]
616 | pub advisory_ids: Option>,
617 |
618 | }
619 |
620 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
621 | #[serde(rename_all = "camelCase")]
622 | pub struct TdxModuleIdentitiesTcbLevel {
623 | pub isvsvn: u8, // TDX SEAM module’s ISV SVN
624 | }
625 |
626 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
627 | #[serde(rename_all = "camelCase")]
628 | pub struct TcbInfoV3TcbLevelItem {
629 | pub tcb: TcbInfoV3TcbLevel,
630 | pub tcb_date: String,
631 | pub tcb_status: String,
632 | #[serde(rename(serialize = "advisoryIDs", deserialize = "advisoryIDs"))]
633 | #[serde(skip_serializing_if = "Option::is_none")]
634 | pub advisory_ids: Option>,
635 | }
636 |
637 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
638 | #[serde(rename_all = "camelCase")]
639 | pub struct TcbInfoV3TcbLevel {
640 | pub sgxtcbcomponents: Vec,
641 | pub pcesvn: u16,
642 | #[serde(skip_serializing_if = "Option::is_none")]
643 | pub tdxtcbcomponents: Option>,
644 | }
645 |
646 | #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
647 | #[serde(rename_all = "camelCase")]
648 | pub struct TcbComponent {
649 | pub svn: u8, // SVN of TCB Component.
650 | #[serde(skip_serializing_if = "Option::is_none")]
651 | pub category: Option, // Category of TCB Component (e.g. BIOS, OS/VMM).
652 | #[serde(skip_serializing_if = "Option::is_none")]
653 | #[serde(rename(serialize = "type", deserialize = "type"))]
654 | pub type_: Option, // Type of TCB Component (e.g. SGX Late Microcode Update, TXT SINIT).
655 | }
--------------------------------------------------------------------------------
/src/utils/cert.rs:
--------------------------------------------------------------------------------
1 | use x509_parser::oid_registry::asn1_rs::{
2 | oid, Boolean, Enumerated, FromDer, Integer, OctetString, Oid, Sequence,
3 | };
4 | use x509_parser::oid_registry::OID_X509_EXT_CRL_DISTRIBUTION_POINTS;
5 | use x509_parser::prelude::*;
6 |
7 | use crate::constants::{SGX_TEE_TYPE, TDX_TEE_TYPE};
8 | use crate::types::cert::{PckPlatformConfiguration, SgxExtensionTcbLevel, SgxExtensions};
9 | use crate::types::tcbinfo::{TcbComponent, TcbInfoV2, TcbInfoV3};
10 | use crate::types::TcbStatus;
11 | use crate::utils::crypto::verify_p256_signature_der;
12 | use crate::utils::hash::{keccak256sum, sha256sum};
13 |
14 | pub fn hash_x509_keccak256(cert: &X509Certificate) -> [u8; 32] {
15 | keccak256sum(cert.tbs_certificate.as_ref())
16 | }
17 |
18 | pub fn hash_x509_sha256(cert: &X509Certificate) -> [u8; 32] {
19 | sha256sum(cert.tbs_certificate.as_ref())
20 | }
21 |
22 | pub fn hash_crl_keccak256(cert: &CertificateRevocationList) -> [u8; 32] {
23 | keccak256sum(cert.tbs_cert_list.as_ref())
24 | }
25 |
26 | pub fn hash_crl_sha256(cert: &CertificateRevocationList) -> [u8; 32] {
27 | sha256sum(cert.tbs_cert_list.as_ref())
28 | }
29 |
30 | pub fn pem_to_der(pem_bytes: &[u8]) -> Vec {
31 | // convert from raw pem bytes to pem objects
32 | let pems = parse_pem(pem_bytes).unwrap();
33 | // convert from pem objects to der bytes
34 | // to make it more optimize, we'll read get all the lengths of the der bytes
35 | // and then allocate the buffer once
36 | let der_bytes_len: usize = pems.iter().map(|pem| pem.contents.len()).sum();
37 | let mut der_bytes = Vec::with_capacity(der_bytes_len);
38 | for pem in pems {
39 | der_bytes.extend_from_slice(&pem.contents);
40 | }
41 | der_bytes
42 | }
43 |
44 | pub fn parse_pem(raw_bytes: &[u8]) -> Result, PEMError> {
45 | Pem::iter_from_buffer(raw_bytes).collect()
46 | }
47 |
48 | pub fn parse_crl_der<'a>(raw_bytes: &'a [u8]) -> CertificateRevocationList<'a> {
49 | let (_, crl) = CertificateRevocationList::from_der(raw_bytes).unwrap();
50 | crl
51 | }
52 |
53 | pub fn parse_x509_der<'a>(raw_bytes: &'a [u8]) -> X509Certificate<'a> {
54 | let (_, cert) = X509Certificate::from_der(raw_bytes).unwrap();
55 | cert
56 | }
57 |
58 | pub fn parse_x509_der_multi<'a>(raw_bytes: &'a [u8]) -> Vec> {
59 | let mut certs = Vec::new();
60 | let mut i = raw_bytes;
61 | while i.len() > 0 {
62 | let (j, cert) = X509Certificate::from_der(i).unwrap();
63 | certs.push(cert);
64 | i = j;
65 | }
66 | certs
67 | }
68 |
69 | pub fn parse_certchain<'a>(pem_certs: &'a [Pem]) -> Vec> {
70 | pem_certs
71 | .iter()
72 | .map(|pem| pem.parse_x509().unwrap())
73 | .collect()
74 | }
75 |
76 | pub fn verify_certificate(cert: &X509Certificate, signer_cert: &X509Certificate, current_time: u64) -> bool {
77 | // verifies that the certificate is unexpired
78 | let issue_date = cert.validity().not_before.timestamp() as u64;
79 | let expiry_date = cert.validity().not_after.timestamp() as u64;
80 | if (current_time < issue_date) || (current_time > expiry_date) {
81 | return false;
82 | }
83 |
84 | // verifies that the certificate is valid
85 | let data = cert.tbs_certificate.as_ref();
86 | let signature = cert.signature_value.as_ref();
87 | let public_key = signer_cert.public_key().subject_public_key.as_ref();
88 |
89 | // make sure that the issuer is the signer
90 | if cert.issuer() != signer_cert.subject() {
91 | return false;
92 | }
93 |
94 | verify_p256_signature_der(data, signature, public_key)
95 | }
96 |
97 | pub fn verify_crl(crl: &CertificateRevocationList, signer_cert: &X509Certificate, current_time: u64) -> bool {
98 | // verifies that the crl is unexpired
99 | let issue_date = crl.last_update().timestamp() as u64;
100 | let expiry_date = if let Some(next_update) = crl.next_update() {
101 | next_update.timestamp() as u64
102 | } else {
103 | // next update field is optional
104 | u64::max_value()
105 | };
106 |
107 | if (current_time < issue_date) || (current_time > expiry_date) {
108 | return false;
109 | }
110 |
111 | // verifies that the crl is valid
112 | let data = crl.tbs_cert_list.as_ref();
113 | let signature = crl.signature_value.as_ref();
114 | let public_key = signer_cert.public_key().subject_public_key.as_ref();
115 | // make sure that the issuer is the signer
116 | if crl.issuer() != signer_cert.subject() {
117 | return false;
118 | }
119 | verify_p256_signature_der(data, signature, public_key)
120 | }
121 |
122 | // we'll just verify that the certchain signature matches, any other checks will be done by the caller
123 | pub fn verify_certchain_signature<'a, 'b>(
124 | certs: &[X509Certificate<'a>],
125 | root_cert: &X509Certificate<'b>,
126 | current_time: u64
127 | ) -> bool {
128 | // verify that the cert chain is valid
129 | let mut iter = certs.iter();
130 | let mut prev_cert = iter.next().unwrap();
131 | for cert in iter {
132 | // verify that the previous cert signed the current cert
133 | if !verify_certificate(prev_cert, cert, current_time) {
134 | return false;
135 | }
136 | prev_cert = cert;
137 | }
138 | // verify that the root cert signed the last cert
139 | verify_certificate(prev_cert, root_cert, current_time)
140 | }
141 |
142 | pub fn is_cert_revoked<'a, 'b>(
143 | cert: &X509Certificate<'a>,
144 | crl: &CertificateRevocationList<'b>,
145 | ) -> bool {
146 | crl.iter_revoked_certificates()
147 | .any(|entry| entry.user_certificate == cert.tbs_certificate.serial)
148 | }
149 |
150 | pub fn get_x509_subject_cn(cert: &X509Certificate) -> String {
151 | let subject = cert.subject();
152 | let cn = subject.iter_common_name().next().unwrap();
153 | cn.as_str().unwrap().to_string()
154 | }
155 |
156 | pub fn get_x509_issuer_cn(cert: &X509Certificate) -> String {
157 | let issuer = cert.issuer();
158 | let cn = issuer.iter_common_name().next().unwrap();
159 | cn.as_str().unwrap().to_string()
160 | }
161 |
162 | pub fn get_crl_uri(cert: &X509Certificate) -> Option {
163 | let crl_ext = cert
164 | .get_extension_unique(&OID_X509_EXT_CRL_DISTRIBUTION_POINTS)
165 | .unwrap()
166 | .unwrap();
167 | let crl_uri = match crl_ext.parsed_extension() {
168 | ParsedExtension::CRLDistributionPoints(crls) => {
169 | match &crls.iter().next().unwrap().distribution_point {
170 | Some(DistributionPointName::FullName(uri)) => {
171 | let uri = &uri[0];
172 | match uri {
173 | GeneralName::URI(uri) => Some(uri.to_string()),
174 | _ => None,
175 | }
176 | }
177 | _ => None,
178 | }
179 | }
180 | _ => {
181 | unreachable!();
182 | }
183 | };
184 | crl_uri
185 | }
186 |
187 | pub fn get_asn1_bool<'a>(bytes: &'a [u8], oid_str: &str) -> (&'a [u8], bool) {
188 | let (k, asn1_seq) = Sequence::from_der(bytes).unwrap();
189 | let (l, asn1_oid) = Oid::from_der(asn1_seq.content.as_ref()).unwrap();
190 | assert!(oid_str.eq(&asn1_oid.to_id_string()));
191 | let (l, asn1_bool) = Boolean::from_der(l).unwrap();
192 | assert_eq!(l.len(), 0);
193 | (k, asn1_bool.bool())
194 | }
195 |
196 | pub fn get_asn1_uint64<'a>(bytes: &'a [u8], oid_str: &str) -> (&'a [u8], u64) {
197 | let (k, asn1_seq) = Sequence::from_der(bytes).unwrap();
198 | let (l, asn1_oid) = Oid::from_der(asn1_seq.content.as_ref()).unwrap();
199 | assert!(oid_str.eq(&asn1_oid.to_id_string()));
200 | let (l, asn1_int) = Integer::from_der(l).unwrap();
201 | assert_eq!(l.len(), 0);
202 | (k, asn1_int.as_u64().unwrap())
203 | }
204 |
205 | pub fn get_asn1_bytes<'a>(bytes: &'a [u8], oid_str: &str) -> (&'a [u8], Vec) {
206 | let (k, asn1_seq) = Sequence::from_der(bytes).unwrap();
207 | let (l, asn1_oid) = Oid::from_der(asn1_seq.content.as_ref()).unwrap();
208 | assert!(oid_str.eq(&asn1_oid.to_id_string()));
209 | let (l, asn1_bytes) = OctetString::from_der(l).unwrap();
210 | assert_eq!(l.len(), 0);
211 | (k, asn1_bytes.into_cow().to_vec())
212 | }
213 |
214 | pub fn extract_sgx_extension<'a>(cert: &'a X509Certificate<'a>) -> SgxExtensions {
215 | // https://download.01.org/intel-sgx/sgx-dcap/1.20/linux/docs/SGX_PCK_Certificate_CRL_Spec-1.4.pdf
216 |
217 | // :
218 | // :
219 | // :
220 | // :
221 | // :
222 | // …
223 | // :
224 | // :
225 | // :
226 | // :
227 | // :
228 | // :
229 | // :
230 | // :
231 | // :
232 | // :
233 | // :
234 |
235 | // SGX Extensions | 1.2.840.113741.1.13.1 | mandatory | ASN.1 Sequence
236 | // PPID | 1.2.840.113741.1.13.1.1 | mandatory | ASN.1 Octet String
237 | // TCB | 1.2.840.113741.1.13.1.2 | mandatory | ASN.1 Sequence
238 | // SGX TCB Comp01 SVN | 1.2.840.113741.1.13.1.2.1 | mandatory | ASN.1 Integer
239 | // SGX TCB Comp02 SVN | 1.2.840.113741.1.13.1.2.2 | mandatory | ASN.1 Integer
240 | // ...
241 | // SGX TCB Comp16 SVN | 1.2.840.113741.1.13.1.2.16 | mandatory | ASN.1 Integer
242 | // PCESVN | 1.2.840.113741.1.13.1.2.17 | mandatory | ASN.1 Integer
243 | // CPUSVN | 1.2.840.113741.1.13.1.2.18 | mandatory | ASN.1 Integer
244 | // PCE-ID | 1.2.840.113741.1.13.1.3 | mandatory | ASN.1 Octet String
245 | // FMSPC | 1.2.840.113741.1.13.1.4 | mandatory | ASN.1 Octet String
246 | // SGX Type | 1.2.840.113741.1.13.1.5 | mandatory | ASN.1 Enumerated
247 | // Platform Instance ID | 1.2.840.113741.1.13.1.6 | optional | ASN.1 Octet String
248 | // Configuration | 1.2.840.113741.1.13.1.7 | optional | ASN.1 Sequence
249 | // Dynamic Platform | 1.2.840.113741.1.13.1.7.1 | optional | ASN.1 Boolean
250 | // Cached Keys | 1.2.840.113741.1.13.1.7.2 | optional | ASN.1 Boolean
251 | // SMT Enabled | 1.2.840.113741.1.13.1.7.3 | optional | ASN.1 Boolean
252 |
253 | let sgx_extensions_bytes = cert
254 | .get_extension_unique(&oid!(1.2.840 .113741 .1 .13 .1))
255 | .unwrap()
256 | .unwrap()
257 | .value;
258 |
259 | let (_, sgx_extensions) = Sequence::from_der(sgx_extensions_bytes).unwrap();
260 |
261 | // we'll process the sgx extensions here...
262 | let mut i = sgx_extensions.content.as_ref();
263 |
264 | // let's define the required information to create the SgxExtensions struct
265 | let mut ppid = [0; 16];
266 | let mut tcb = SgxExtensionTcbLevel {
267 | sgxtcbcomp01svn: 0,
268 | sgxtcbcomp02svn: 0,
269 | sgxtcbcomp03svn: 0,
270 | sgxtcbcomp04svn: 0,
271 | sgxtcbcomp05svn: 0,
272 | sgxtcbcomp06svn: 0,
273 | sgxtcbcomp07svn: 0,
274 | sgxtcbcomp08svn: 0,
275 | sgxtcbcomp09svn: 0,
276 | sgxtcbcomp10svn: 0,
277 | sgxtcbcomp11svn: 0,
278 | sgxtcbcomp12svn: 0,
279 | sgxtcbcomp13svn: 0,
280 | sgxtcbcomp14svn: 0,
281 | sgxtcbcomp15svn: 0,
282 | sgxtcbcomp16svn: 0,
283 | pcesvn: 0,
284 | cpusvn: [0; 16],
285 | };
286 | let mut pceid = [0; 2];
287 | let mut fmspc = [0; 6];
288 | let mut sgx_type = 0;
289 | let mut platform_instance_id: Option<[u8; 16]> = None;
290 | let mut configuration: Option = None;
291 |
292 | while i.len() > 0 {
293 | let (j, current_sequence) = Sequence::from_der(i).unwrap();
294 | i = j;
295 | let (j, current_oid) = Oid::from_der(current_sequence.content.as_ref()).unwrap();
296 | match current_oid.to_id_string().as_str() {
297 | "1.2.840.113741.1.13.1.1" => {
298 | let (k, ppid_bytes) = OctetString::from_der(j).unwrap();
299 | assert_eq!(k.len(), 0);
300 | ppid.copy_from_slice(ppid_bytes.as_ref());
301 | }
302 | "1.2.840.113741.1.13.1.2" => {
303 | let (k, tcb_sequence) = Sequence::from_der(j).unwrap();
304 | assert_eq!(k.len(), 0);
305 | // iterate through from 1 - 18
306 | let (k, sgxtcbcomp01svn) =
307 | get_asn1_uint64(tcb_sequence.content.as_ref(), "1.2.840.113741.1.13.1.2.1");
308 | let (k, sgxtcbcomp02svn) = get_asn1_uint64(k, "1.2.840.113741.1.13.1.2.2");
309 | let (k, sgxtcbcomp03svn) = get_asn1_uint64(k, "1.2.840.113741.1.13.1.2.3");
310 | let (k, sgxtcbcomp04svn) = get_asn1_uint64(k, "1.2.840.113741.1.13.1.2.4");
311 | let (k, sgxtcbcomp05svn) = get_asn1_uint64(k, "1.2.840.113741.1.13.1.2.5");
312 | let (k, sgxtcbcomp06svn) = get_asn1_uint64(k, "1.2.840.113741.1.13.1.2.6");
313 | let (k, sgxtcbcomp07svn) = get_asn1_uint64(k, "1.2.840.113741.1.13.1.2.7");
314 | let (k, sgxtcbcomp08svn) = get_asn1_uint64(k, "1.2.840.113741.1.13.1.2.8");
315 | let (k, sgxtcbcomp09svn) = get_asn1_uint64(k, "1.2.840.113741.1.13.1.2.9");
316 | let (k, sgxtcbcomp10svn) = get_asn1_uint64(k, "1.2.840.113741.1.13.1.2.10");
317 | let (k, sgxtcbcomp11svn) = get_asn1_uint64(k, "1.2.840.113741.1.13.1.2.11");
318 | let (k, sgxtcbcomp12svn) = get_asn1_uint64(k, "1.2.840.113741.1.13.1.2.12");
319 | let (k, sgxtcbcomp13svn) = get_asn1_uint64(k, "1.2.840.113741.1.13.1.2.13");
320 | let (k, sgxtcbcomp14svn) = get_asn1_uint64(k, "1.2.840.113741.1.13.1.2.14");
321 | let (k, sgxtcbcomp15svn) = get_asn1_uint64(k, "1.2.840.113741.1.13.1.2.15");
322 | let (k, sgxtcbcomp16svn) = get_asn1_uint64(k, "1.2.840.113741.1.13.1.2.16");
323 | let (k, pcesvn) = get_asn1_uint64(k, "1.2.840.113741.1.13.1.2.17");
324 | let (k, cpusvn) = get_asn1_bytes(k, "1.2.840.113741.1.13.1.2.18");
325 |
326 | assert_eq!(k.len(), 0);
327 | // copy the bytes into the tcb struct
328 | tcb.sgxtcbcomp01svn = sgxtcbcomp01svn as u8;
329 | tcb.sgxtcbcomp02svn = sgxtcbcomp02svn as u8;
330 | tcb.sgxtcbcomp03svn = sgxtcbcomp03svn as u8;
331 | tcb.sgxtcbcomp04svn = sgxtcbcomp04svn as u8;
332 | tcb.sgxtcbcomp05svn = sgxtcbcomp05svn as u8;
333 | tcb.sgxtcbcomp06svn = sgxtcbcomp06svn as u8;
334 | tcb.sgxtcbcomp07svn = sgxtcbcomp07svn as u8;
335 | tcb.sgxtcbcomp08svn = sgxtcbcomp08svn as u8;
336 | tcb.sgxtcbcomp09svn = sgxtcbcomp09svn as u8;
337 | tcb.sgxtcbcomp10svn = sgxtcbcomp10svn as u8;
338 | tcb.sgxtcbcomp11svn = sgxtcbcomp11svn as u8;
339 | tcb.sgxtcbcomp12svn = sgxtcbcomp12svn as u8;
340 | tcb.sgxtcbcomp13svn = sgxtcbcomp13svn as u8;
341 | tcb.sgxtcbcomp14svn = sgxtcbcomp14svn as u8;
342 | tcb.sgxtcbcomp15svn = sgxtcbcomp15svn as u8;
343 | tcb.sgxtcbcomp16svn = sgxtcbcomp16svn as u8;
344 | tcb.pcesvn = pcesvn as u16;
345 | tcb.cpusvn.copy_from_slice(cpusvn.as_ref());
346 | }
347 | "1.2.840.113741.1.13.1.3" => {
348 | let (k, pceid_bytes) = OctetString::from_der(j).unwrap();
349 | assert_eq!(k.len(), 0);
350 | pceid.copy_from_slice(pceid_bytes.as_ref());
351 | }
352 | "1.2.840.113741.1.13.1.4" => {
353 | let (k, fmspc_bytes) = OctetString::from_der(j).unwrap();
354 | assert_eq!(k.len(), 0);
355 | fmspc.copy_from_slice(fmspc_bytes.as_ref());
356 | }
357 | "1.2.840.113741.1.13.1.5" => {
358 | let (k, sgx_type_enum) = Enumerated::from_der(j).unwrap();
359 | assert_eq!(k.len(), 0);
360 | sgx_type = sgx_type_enum.0;
361 | }
362 | "1.2.840.113741.1.13.1.6" => {
363 | let (k, platform_instance_id_bytes) = OctetString::from_der(j).unwrap();
364 | assert_eq!(k.len(), 0);
365 | let mut temp = [0; 16];
366 | temp.copy_from_slice(platform_instance_id_bytes.as_ref());
367 | platform_instance_id = Some(temp);
368 | }
369 | "1.2.840.113741.1.13.1.7" => {
370 | let (k, configuration_seq) = Sequence::from_der(j).unwrap();
371 | assert_eq!(k.len(), 0);
372 | let mut configuration_temp = PckPlatformConfiguration {
373 | dynamic_platform: None,
374 | cached_keys: None,
375 | smt_enabled: None,
376 | };
377 | // iterate through from 1 - 3, note that some of them might be optional.
378 | let mut k = configuration_seq.content.as_ref();
379 | while k.len() > 0 {
380 | let (l, asn1_seq) = Sequence::from_der(k).unwrap();
381 | k = l;
382 | let (l, current_oid) = Oid::from_der(asn1_seq.content.as_ref()).unwrap();
383 | match current_oid.to_id_string().as_str() {
384 | "1.2.840.113741.1.13.1.7.1" => {
385 | let (l, dynamic_platform_bool) = Boolean::from_der(l).unwrap();
386 | assert_eq!(l.len(), 0);
387 | configuration_temp.dynamic_platform =
388 | Some(dynamic_platform_bool.bool());
389 | }
390 | "1.2.840.113741.1.13.1.7.2" => {
391 | let (l, cached_keys_bool) = Boolean::from_der(l).unwrap();
392 | assert_eq!(l.len(), 0);
393 | configuration_temp.cached_keys = Some(cached_keys_bool.bool());
394 | }
395 | "1.2.840.113741.1.13.1.7.3" => {
396 | let (l, smt_enabled_bool) = Boolean::from_der(l).unwrap();
397 | assert_eq!(l.len(), 0);
398 | configuration_temp.smt_enabled = Some(smt_enabled_bool.bool());
399 | }
400 | _ => {
401 | unreachable!("Unknown OID: {}", current_oid.to_id_string());
402 | }
403 | }
404 | }
405 | // done parsing...
406 | configuration = Some(configuration_temp);
407 | }
408 | _ => {
409 | unreachable!("Unknown OID: {}", current_oid.to_id_string());
410 | }
411 | }
412 | }
413 |
414 | SgxExtensions {
415 | ppid,
416 | tcb,
417 | pceid,
418 | fmspc,
419 | sgx_type,
420 | platform_instance_id,
421 | configuration,
422 | }
423 | }
424 |
425 | pub fn get_sgx_fmspc_tcbstatus_v2(
426 | sgx_extensions: &SgxExtensions,
427 | tcb_info_root: &TcbInfoV2,
428 | ) -> TcbStatus {
429 | // we'll make sure the tcbinforoot is valid
430 | // check that fmspc is valid
431 | // check that pceid is valid
432 |
433 | // convert tcbinfo fmspc and pceid from string to bytes for comparison
434 | assert!(sgx_extensions.fmspc.to_vec() == hex::decode(&tcb_info_root.tcb_info.fmspc).unwrap());
435 | assert!(sgx_extensions.pceid.to_vec() == hex::decode(&tcb_info_root.tcb_info.pce_id).unwrap());
436 |
437 | // now that we are sure that fmspc and pceid is the same, we'll iterate through and find the tcbstatus
438 | // we assume that the tcb_levels are sorted in descending svn order
439 | // println!("sgx_extensions tcb: {:?}", sgx_extensions.tcb);
440 | for tcb_level in tcb_info_root.tcb_info.tcb_levels.iter() {
441 | let tcb = &tcb_level.tcb;
442 | // println!("tcb: {:?}", tcb);
443 | if tcb.sgxtcbcomp01svn <= sgx_extensions.tcb.sgxtcbcomp01svn
444 | && tcb.sgxtcbcomp02svn <= sgx_extensions.tcb.sgxtcbcomp02svn
445 | && tcb.sgxtcbcomp03svn <= sgx_extensions.tcb.sgxtcbcomp03svn
446 | && tcb.sgxtcbcomp04svn <= sgx_extensions.tcb.sgxtcbcomp04svn
447 | && tcb.sgxtcbcomp05svn <= sgx_extensions.tcb.sgxtcbcomp05svn
448 | && tcb.sgxtcbcomp06svn <= sgx_extensions.tcb.sgxtcbcomp06svn
449 | && tcb.sgxtcbcomp07svn <= sgx_extensions.tcb.sgxtcbcomp07svn
450 | && tcb.sgxtcbcomp08svn <= sgx_extensions.tcb.sgxtcbcomp08svn
451 | && tcb.sgxtcbcomp09svn <= sgx_extensions.tcb.sgxtcbcomp09svn
452 | && tcb.sgxtcbcomp10svn <= sgx_extensions.tcb.sgxtcbcomp10svn
453 | && tcb.sgxtcbcomp11svn <= sgx_extensions.tcb.sgxtcbcomp11svn
454 | && tcb.sgxtcbcomp12svn <= sgx_extensions.tcb.sgxtcbcomp12svn
455 | && tcb.sgxtcbcomp13svn <= sgx_extensions.tcb.sgxtcbcomp13svn
456 | && tcb.sgxtcbcomp14svn <= sgx_extensions.tcb.sgxtcbcomp14svn
457 | && tcb.sgxtcbcomp15svn <= sgx_extensions.tcb.sgxtcbcomp15svn
458 | && tcb.sgxtcbcomp16svn <= sgx_extensions.tcb.sgxtcbcomp16svn
459 | && tcb.pcesvn <= sgx_extensions.tcb.pcesvn
460 | {
461 | // println!("tcb_status: {:?}", tcb_level.tcb_status);
462 | return TcbStatus::from_str(tcb_level.tcb_status.as_str());
463 | }
464 | }
465 | // we went through all the tcblevels and didn't find a match
466 | // shouldn't happen so we'll toggle an exception
467 | unreachable!();
468 | }
469 |
470 | // Slightly modified from https://github.com/intel/SGX-TDX-DCAP-QuoteVerificationLibrary/blob/7e5b2a13ca5472de8d97dd7d7024c2ea5af9a6ba/Src/AttestationLibrary/src/Verifiers/Checks/TcbLevelCheck.cpp#L129-L181
471 | pub fn get_sgx_tdx_fmspc_tcbstatus_v3(
472 | tee_type: u32,
473 | sgx_extensions: &SgxExtensions,
474 | tee_tcb_svn: &[u8; 16],
475 | tcbinfov3: &TcbInfoV3,
476 | ) -> (TcbStatus, TcbStatus, Option>) {
477 | // we'll make sure the tcbinforoot is valid
478 | // check that fmspc is valid
479 | // check that pceid is valid
480 |
481 | // convert tcbinfo fmspc and pceid from string to bytes for comparison
482 | assert!(sgx_extensions.fmspc.to_vec() == hex::decode(&tcbinfov3.tcb_info.fmspc).unwrap());
483 | assert!(sgx_extensions.pceid.to_vec() == hex::decode(&tcbinfov3.tcb_info.pce_id).unwrap());
484 |
485 | let mut sgx_tcb_status = TcbStatus::TcbUnrecognized;
486 | let mut tdx_tcb_status = TcbStatus::TcbUnrecognized;
487 |
488 | let extension_pcesvn = sgx_extensions.tcb.pcesvn;
489 | let mut advisory_ids = None;
490 |
491 | for tcb_level in tcbinfov3.tcb_info.tcb_levels.iter() {
492 | if sgx_tcb_status == TcbStatus::TcbUnrecognized {
493 | let sgxtcbcomponents_ok =
494 | match_sgxtcbcomp(sgx_extensions, &tcb_level.tcb.sgxtcbcomponents);
495 | let pcesvn_ok = extension_pcesvn >= tcb_level.tcb.pcesvn;
496 | if sgxtcbcomponents_ok && pcesvn_ok {
497 | sgx_tcb_status = TcbStatus::from_str(tcb_level.tcb_status.as_str());
498 | if tee_type == SGX_TEE_TYPE {
499 | advisory_ids = tcb_level.advisory_ids.clone();
500 | }
501 | }
502 | }
503 | if sgx_tcb_status != TcbStatus::TcbUnrecognized || sgx_tcb_status != TcbStatus::TcbRevoked {
504 | if !is_empty(tee_tcb_svn) {
505 | let tdxtcbcomponents_ok = match tcb_level.tcb.tdxtcbcomponents.as_ref() {
506 | Some(tdxtcbcomponents) => tdxtcbcomponents
507 | .iter()
508 | .zip(tee_tcb_svn.iter())
509 | .all(|(tcb, tee)| *tee >= tcb.svn as u8),
510 | None => true,
511 | };
512 | if tdxtcbcomponents_ok {
513 | tdx_tcb_status = TcbStatus::from_str(tcb_level.tcb_status.as_str());
514 | if tee_type == TDX_TEE_TYPE {
515 | advisory_ids = tcb_level.advisory_ids.clone();
516 | }
517 | break;
518 | }
519 | }
520 | }
521 | }
522 | (sgx_tcb_status, tdx_tcb_status, advisory_ids)
523 | }
524 |
525 | fn is_empty(slice: &[u8]) -> bool {
526 | slice.iter().all(|&x| x == 0)
527 | }
528 |
529 | fn match_sgxtcbcomp(sgx_extensions: &SgxExtensions, sgxtcbcomponents: &[TcbComponent]) -> bool {
530 | let extension_tcbcomponents = extension_to_tcbcomponents(&sgx_extensions.tcb);
531 | // Compare all of the SGX TCB Comp SVNs retrieved from the SGX PCK Certificate (from 01 to 16) with the corresponding values of SVNs in sgxtcbcomponents array of TCB Level.
532 | // If all SGX TCB Comp SVNs in the certificate are greater or equal to the corresponding values in TCB Level, then return true.
533 | // Otherwise, return false.
534 | extension_tcbcomponents
535 | .iter()
536 | .zip(sgxtcbcomponents.iter())
537 | .all(|(ext, tcb)| ext.svn >= tcb.svn)
538 | }
539 |
540 | fn extension_to_tcbcomponents(extension: &SgxExtensionTcbLevel) -> Vec {
541 | let mut tcbcomponents = Vec::with_capacity(16);
542 | tcbcomponents.push(TcbComponent {
543 | svn: extension.sgxtcbcomp01svn,
544 | category: None,
545 | type_: None,
546 | });
547 | tcbcomponents.push(TcbComponent {
548 | svn: extension.sgxtcbcomp02svn,
549 | category: None,
550 | type_: None,
551 | });
552 | tcbcomponents.push(TcbComponent {
553 | svn: extension.sgxtcbcomp03svn,
554 | category: None,
555 | type_: None,
556 | });
557 | tcbcomponents.push(TcbComponent {
558 | svn: extension.sgxtcbcomp04svn,
559 | category: None,
560 | type_: None,
561 | });
562 | tcbcomponents.push(TcbComponent {
563 | svn: extension.sgxtcbcomp05svn,
564 | category: None,
565 | type_: None,
566 | });
567 | tcbcomponents.push(TcbComponent {
568 | svn: extension.sgxtcbcomp06svn,
569 | category: None,
570 | type_: None,
571 | });
572 | tcbcomponents.push(TcbComponent {
573 | svn: extension.sgxtcbcomp07svn,
574 | category: None,
575 | type_: None,
576 | });
577 | tcbcomponents.push(TcbComponent {
578 | svn: extension.sgxtcbcomp08svn,
579 | category: None,
580 | type_: None,
581 | });
582 | tcbcomponents.push(TcbComponent {
583 | svn: extension.sgxtcbcomp09svn,
584 | category: None,
585 | type_: None,
586 | });
587 | tcbcomponents.push(TcbComponent {
588 | svn: extension.sgxtcbcomp10svn,
589 | category: None,
590 | type_: None,
591 | });
592 | tcbcomponents.push(TcbComponent {
593 | svn: extension.sgxtcbcomp11svn,
594 | category: None,
595 | type_: None,
596 | });
597 | tcbcomponents.push(TcbComponent {
598 | svn: extension.sgxtcbcomp12svn,
599 | category: None,
600 | type_: None,
601 | });
602 | tcbcomponents.push(TcbComponent {
603 | svn: extension.sgxtcbcomp13svn,
604 | category: None,
605 | type_: None,
606 | });
607 | tcbcomponents.push(TcbComponent {
608 | svn: extension.sgxtcbcomp14svn,
609 | category: None,
610 | type_: None,
611 | });
612 | tcbcomponents.push(TcbComponent {
613 | svn: extension.sgxtcbcomp15svn,
614 | category: None,
615 | type_: None,
616 | });
617 | tcbcomponents.push(TcbComponent {
618 | svn: extension.sgxtcbcomp16svn,
619 | category: None,
620 | type_: None,
621 | });
622 |
623 | tcbcomponents
624 | }
625 |
--------------------------------------------------------------------------------
/src/utils/crypto.rs:
--------------------------------------------------------------------------------
1 | use p256::ecdsa::{VerifyingKey, signature::Verifier, Signature};
2 |
3 | // verify_p256_signature_bytes verifies a P256 ECDSA signature
4 | // using the provided data, signature, and public key.
5 | // The data is the message that was signed as a byte slice.
6 | // The signature is the signature (in raw form [r][s]) of the data as a byte slice. (64 bytes)
7 | // The public_key is the public key (in uncompressed form [4][x][y]) of the entity that signed the data. (65 bytes)
8 | // Returns true if the signature is valid, false otherwise.
9 | pub fn verify_p256_signature_bytes(data: &[u8], signature: &[u8], public_key: &[u8]) -> bool {
10 | let signature = Signature::from_bytes(signature.try_into().unwrap()).unwrap();
11 | let verifying_key = VerifyingKey::from_sec1_bytes(public_key).unwrap();
12 | verifying_key.verify(data, &signature).is_ok()
13 | }
14 |
15 | pub fn verify_p256_signature_der(data: &[u8], signature: &[u8], public_key: &[u8]) -> bool {
16 | let signature = Signature::from_der(signature).unwrap();
17 | let verifying_key = VerifyingKey::from_sec1_bytes(public_key).unwrap();
18 | verifying_key.verify(data, &signature).is_ok()
19 | }
--------------------------------------------------------------------------------
/src/utils/enclave_identity.rs:
--------------------------------------------------------------------------------
1 | use crate::types::{enclave_identity::EnclaveIdentityV2, quotes::body::EnclaveReport, TcbStatus};
2 | use crate::utils::crypto::verify_p256_signature_bytes;
3 | use sha3::{Digest, Keccak256};
4 |
5 | use crate::X509Certificate;
6 |
7 | pub fn validate_enclave_identityv2(
8 | enclave_identityv2: &EnclaveIdentityV2,
9 | sgx_signing_pubkey: &X509Certificate,
10 | current_time: u64,
11 | ) -> bool {
12 | // get tcb_info_root time
13 | let issue_date =
14 | chrono::DateTime::parse_from_rfc3339(&enclave_identityv2.enclave_identity.issue_date)
15 | .unwrap();
16 | let next_update_date =
17 | chrono::DateTime::parse_from_rfc3339(&enclave_identityv2.enclave_identity.next_update)
18 | .unwrap();
19 |
20 | // convert the issue_date and next_update_date to seconds since epoch
21 | let issue_date_seconds = issue_date.timestamp() as u64;
22 | let next_update_seconds = next_update_date.timestamp() as u64;
23 |
24 | // check that the current time is between the issue_date and next_update_date
25 | if current_time < issue_date_seconds || current_time > next_update_seconds {
26 | return false;
27 | }
28 |
29 | // signature is a hex string, we'll convert it to bytes
30 | // ZL: we'll assume that the signature is a P256 ECDSA signature
31 | let enclave_identityv2_signature_bytes = hex::decode(&enclave_identityv2.signature).unwrap();
32 |
33 | // verify that the enclave_identity_root is signed by the root cert
34 | let enclave_identityv2_signature_data =
35 | serde_json::to_vec(&enclave_identityv2.enclave_identity).unwrap();
36 | verify_p256_signature_bytes(
37 | &enclave_identityv2_signature_data,
38 | &enclave_identityv2_signature_bytes,
39 | sgx_signing_pubkey.public_key().subject_public_key.as_ref(),
40 | )
41 | }
42 |
43 | pub fn get_qe_tcbstatus(
44 | enclave_report: &EnclaveReport,
45 | qeidentityv2: &EnclaveIdentityV2,
46 | ) -> TcbStatus {
47 | for tcb_level in qeidentityv2.enclave_identity.tcb_levels.iter() {
48 | if tcb_level.tcb.isvsvn <= enclave_report.isv_svn {
49 | let tcb_status = match &tcb_level.tcb_status[..] {
50 | "UpToDate" => TcbStatus::OK,
51 | "SWHardeningNeeded" => TcbStatus::TcbSwHardeningNeeded,
52 | "ConfigurationAndSWHardeningNeeded" => {
53 | TcbStatus::TcbConfigurationAndSwHardeningNeeded
54 | }
55 | "ConfigurationNeeded" => TcbStatus::TcbConfigurationNeeded,
56 | "OutOfDate" => TcbStatus::TcbOutOfDate,
57 | "OutOfDateConfigurationNeeded" => TcbStatus::TcbOutOfDateConfigurationNeeded,
58 | "Revoked" => TcbStatus::TcbRevoked,
59 | _ => TcbStatus::TcbUnrecognized,
60 | };
61 | return tcb_status;
62 | }
63 | }
64 |
65 | TcbStatus::TcbUnrecognized
66 | }
67 |
68 | // A content_hash is the hash representation of the enclave_identity
69 | // excluding both "issue_date" and "next_update" fields
70 | // integers are big-endian encoded
71 | pub fn get_enclave_identityv2_content_hash(enclave_identityv2: &EnclaveIdentityV2) -> [u8; 32] {
72 | let mut pre_image: Vec = vec![];
73 | pre_image.extend_from_slice(&[convert_enclave_identity_id_string_to_u8(
74 | &enclave_identityv2.enclave_identity.id,
75 | )]);
76 | pre_image.extend_from_slice(&enclave_identityv2.enclave_identity.version.to_be_bytes());
77 | pre_image.extend_from_slice(
78 | &enclave_identityv2
79 | .enclave_identity
80 | .tcb_evaluation_data_number
81 | .to_be_bytes(),
82 | );
83 | pre_image.extend_from_slice(
84 | hex::decode(&enclave_identityv2.enclave_identity.miscselect)
85 | .unwrap()
86 | .as_slice(),
87 | );
88 | pre_image.extend_from_slice(
89 | hex::decode(&enclave_identityv2.enclave_identity.miscselect_mask)
90 | .unwrap()
91 | .as_slice(),
92 | );
93 | pre_image.extend_from_slice(
94 | hex::decode(&enclave_identityv2.enclave_identity.attributes)
95 | .unwrap()
96 | .as_slice(),
97 | );
98 | pre_image.extend_from_slice(
99 | hex::decode(&enclave_identityv2.enclave_identity.attributes_mask)
100 | .unwrap()
101 | .as_slice(),
102 | );
103 | pre_image.extend_from_slice(
104 | hex::decode(&enclave_identityv2.enclave_identity.mrsigner)
105 | .unwrap()
106 | .as_slice(),
107 | );
108 | pre_image.extend_from_slice(&enclave_identityv2.enclave_identity.isvprodid.to_be_bytes());
109 | pre_image.extend_from_slice(
110 | serde_json::to_vec(&enclave_identityv2.enclave_identity.tcb_levels)
111 | .unwrap()
112 | .as_slice(),
113 | );
114 | Keccak256::digest(&pre_image).try_into().unwrap()
115 | }
116 |
117 | fn convert_enclave_identity_id_string_to_u8(id_str: &str) -> u8 {
118 | match id_str {
119 | "QE" => 0,
120 | "QVE" => 1,
121 | "TD_QE" => 2,
122 | _ => panic!("Unknown enclave_identity id string"),
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/utils/hash.rs:
--------------------------------------------------------------------------------
1 | use sha2::{Sha256, Digest};
2 | use sha3::Keccak256;
3 |
4 | pub fn sha256sum(data: &[u8]) -> [u8; 32] {
5 | let mut hasher = Sha256::new();
6 | hasher.update(data);
7 | let result = hasher.finalize();
8 | let mut output = [0; 32];
9 | output.copy_from_slice(&result);
10 | output
11 | }
12 |
13 | pub fn keccak256sum(data: &[u8]) -> [u8; 32] {
14 | let mut hasher = Keccak256::new();
15 | hasher.update(data);
16 | let result = hasher.finalize();
17 | let mut output = [0; 32];
18 | output.copy_from_slice(&result);
19 | output
20 | }
21 |
--------------------------------------------------------------------------------
/src/utils/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod hash;
2 | pub mod cert;
3 | pub mod crypto;
4 | pub mod tcbinfo;
5 | pub mod enclave_identity;
6 | pub mod quotes;
7 | pub mod tdx_module;
8 |
--------------------------------------------------------------------------------
/src/utils/quotes/mod.rs:
--------------------------------------------------------------------------------
1 | pub mod version_3;
2 | pub mod version_4;
3 |
4 | use x509_parser::certificate::X509Certificate;
5 |
6 | use crate::constants::{ECDSA_256_WITH_P256_CURVE, INTEL_QE_VENDOR_ID};
7 | use crate::types::enclave_identity::EnclaveIdentityV2;
8 | use crate::utils::hash::sha256sum;
9 |
10 | use crate::types::cert::{IntelSgxCrls, SgxExtensions};
11 | use crate::types::collaterals::IntelCollateral;
12 | use crate::types::quotes::{
13 | body::{EnclaveReport, QuoteBody},
14 | CertData, QuoteHeader,
15 | };
16 | use crate::types::tcbinfo::TcbInfo;
17 | use crate::types::TcbStatus;
18 | use crate::utils::enclave_identity::get_qe_tcbstatus;
19 |
20 | use crate::utils::cert::{
21 | extract_sgx_extension, get_x509_issuer_cn, get_x509_subject_cn, parse_certchain, parse_pem,
22 | verify_certchain_signature, verify_certificate, verify_crl,
23 | };
24 | use crate::utils::crypto::verify_p256_signature_bytes;
25 | use crate::utils::enclave_identity::validate_enclave_identityv2;
26 | use crate::utils::tcbinfo::{validate_tcbinfov2, validate_tcbinfov3};
27 |
28 | fn check_quote_header(quote_header: &QuoteHeader, quote_version: u16) -> bool {
29 | let quote_version_is_valid = quote_header.version == quote_version;
30 | let att_key_type_is_supported = quote_header.att_key_type == ECDSA_256_WITH_P256_CURVE;
31 | let qe_vendor_id_is_valid = quote_header.qe_vendor_id == INTEL_QE_VENDOR_ID;
32 |
33 | quote_version_is_valid && att_key_type_is_supported && qe_vendor_id_is_valid
34 | }
35 |
36 | // verification steps that are required for both SGX and TDX quotes
37 | // Checks:
38 | // - valid qeidentity
39 | // - valid tcbinfo
40 | // - valid pck certificate chain
41 | // - qe report content
42 | // - ecdsa verification on qe report data and quote body data
43 | // Returns:
44 | // - QEIdentity TCB Status
45 | // - SGX Extension
46 | // - TCBInfo (v2 or v3)
47 | fn common_verify_and_fetch_tcb(
48 | quote_header: &QuoteHeader,
49 | quote_body: &QuoteBody,
50 | ecdsa_attestation_signature: &[u8],
51 | ecdsa_attestation_pubkey: &[u8],
52 | qe_report: &EnclaveReport,
53 | qe_report_signature: &[u8],
54 | qe_auth_data: &[u8],
55 | qe_cert_data: &CertData,
56 | collaterals: &IntelCollateral,
57 | current_time: u64,
58 | ) -> (TcbStatus, SgxExtensions, TcbInfo) {
59 | let signing_cert = collaterals.get_sgx_tcb_signing();
60 | let intel_sgx_root_cert = collaterals.get_sgx_intel_root_ca();
61 |
62 | // verify that signing_verifying_key is not revoked and signed by the root cert
63 | let intel_crls = IntelSgxCrls::from_collaterals(collaterals);
64 |
65 | // ZL: If collaterals are checked by the caller, then these can be removed
66 | // check that CRLs are valid
67 | match &intel_crls.sgx_root_ca_crl {
68 | Some(crl) => {
69 | assert!(verify_crl(crl, &intel_sgx_root_cert, current_time));
70 | }
71 | None => {
72 | panic!("No SGX Root CA CRL found");
73 | }
74 | }
75 |
76 | let signing_cert_revoked = intel_crls.is_cert_revoked(&signing_cert);
77 | assert!(!signing_cert_revoked, "TCB Signing Cert revoked");
78 | assert!(
79 | verify_certificate(&signing_cert, &intel_sgx_root_cert, current_time),
80 | "TCB Signing Cert is not signed by Intel SGX Root CA"
81 | );
82 |
83 | // validate QEIdentity
84 | let qeidentityv2 = collaterals.get_qeidentityv2();
85 | assert!(validate_enclave_identityv2(
86 | &qeidentityv2,
87 | &signing_cert,
88 | current_time
89 | ));
90 |
91 | // verify QEReport then get TCB Status
92 | assert!(
93 | verify_qe_report_data(
94 | &qe_report.report_data,
95 | &ecdsa_attestation_pubkey,
96 | qe_auth_data
97 | ),
98 | "QE Report Data is incorrect"
99 | );
100 | assert!(
101 | validate_qe_report(qe_report, &qeidentityv2),
102 | "QE Report values do not match with the provided QEIdentity"
103 | );
104 | let qe_tcb_status = get_qe_tcbstatus(qe_report, &qeidentityv2);
105 | assert!(
106 | qe_tcb_status != TcbStatus::TcbRevoked,
107 | "QEIdentity TCB Revoked"
108 | );
109 |
110 | // get the certchain embedded in the ecda quote signature data
111 | // this can be one of 5 types
112 | // we only handle type 5 for now...
113 | // TODO: Add support for all other types
114 | assert_eq!(qe_cert_data.cert_data_type, 5, "QE Cert Type must be 5");
115 | let certchain_pems = parse_pem(&qe_cert_data.cert_data).unwrap();
116 | let certchain = parse_certchain(&certchain_pems);
117 | // checks that the certificates used in the certchain are not revoked
118 | for cert in certchain.iter() {
119 | assert!(!intel_crls.is_cert_revoked(cert));
120 | }
121 |
122 | // get the pck certificate, and check whether issuer common name is valid
123 | let pck_cert = &certchain[0];
124 | let pck_cert_issuer = &certchain[1];
125 | assert!(
126 | check_pck_issuer_and_crl(pck_cert, pck_cert_issuer, &intel_crls, current_time),
127 | "Invalid PCK Issuer or CRL"
128 | );
129 |
130 | // verify that the cert chain signatures are valid
131 | assert!(
132 | verify_certchain_signature(&certchain, &intel_sgx_root_cert, current_time),
133 | "Invalid PCK Chain"
134 | );
135 |
136 | // verify the signature for qe report data
137 | let qe_report_bytes = qe_report.to_bytes();
138 |
139 | let qe_report_public_key = pck_cert.public_key().subject_public_key.as_ref();
140 | assert!(
141 | verify_p256_signature_bytes(&qe_report_bytes, qe_report_signature, qe_report_public_key),
142 | "Invalid qe signature"
143 | );
144 |
145 | // get the SGX extension
146 | let sgx_extensions = extract_sgx_extension(&pck_cert);
147 |
148 | // verify the signature for attestation body
149 | let mut data = Vec::new();
150 | data.extend_from_slice("e_header.to_bytes());
151 | match quote_body {
152 | QuoteBody::SGXQuoteBody(body) => data.extend_from_slice(&body.to_bytes()),
153 | QuoteBody::TD10QuoteBody(body) => data.extend_from_slice(&body.to_bytes()),
154 | };
155 |
156 | // prefix pub key
157 | let mut prefixed_pub_key = [4; 65];
158 | prefixed_pub_key[1..65].copy_from_slice(ecdsa_attestation_pubkey);
159 | assert!(
160 | verify_p256_signature_bytes(&data, ecdsa_attestation_signature, &prefixed_pub_key),
161 | "Invalid attestation signature"
162 | );
163 |
164 | // validate tcbinfo v2 or v3, depending on the quote version
165 | let tcb_info: TcbInfo;
166 | if quote_header.version >= 4 {
167 | let tcb_info_v3 = collaterals.get_tcbinfov3();
168 | assert!(
169 | validate_tcbinfov3(&tcb_info_v3, &signing_cert, current_time),
170 | "Invalid TCBInfoV3"
171 | );
172 | tcb_info = TcbInfo::V3(tcb_info_v3);
173 | } else {
174 | let tcb_info_v2 = collaterals.get_tcbinfov2();
175 | assert!(
176 | validate_tcbinfov2(&tcb_info_v2, &signing_cert, current_time),
177 | "Invalid TCBInfoV2"
178 | );
179 | tcb_info = TcbInfo::V2(tcb_info_v2);
180 | }
181 |
182 | (qe_tcb_status, sgx_extensions, tcb_info)
183 | }
184 |
185 | fn check_pck_issuer_and_crl(
186 | pck_cert: &X509Certificate,
187 | pck_issuer_cert: &X509Certificate,
188 | intel_crls: &IntelSgxCrls,
189 | current_time: u64
190 | ) -> bool {
191 | // we'll check what kind of cert is it, and validate the appropriate CRL
192 | let pck_cert_subject_cn = get_x509_issuer_cn(pck_cert);
193 | let pck_cert_issuer_cn = get_x509_subject_cn(pck_issuer_cert);
194 |
195 | assert!(
196 | pck_cert_issuer_cn == pck_cert_subject_cn,
197 | "PCK Issuer CN does not match with PCK Intermediate Subject CN"
198 | );
199 |
200 | match pck_cert_issuer_cn.as_str() {
201 | "Intel SGX PCK Platform CA" => verify_crl(
202 | intel_crls.sgx_pck_platform_crl.as_ref().unwrap(),
203 | pck_issuer_cert,
204 | current_time
205 | ),
206 | "Intel SGX PCK Processor CA" => verify_crl(
207 | &intel_crls.sgx_pck_processor_crl.as_ref().unwrap(),
208 | pck_issuer_cert,
209 | current_time
210 | ),
211 | _ => {
212 | panic!("Unknown PCK Cert Subject CN: {}", pck_cert_subject_cn);
213 | }
214 | }
215 | }
216 |
217 | fn validate_qe_report(enclave_report: &EnclaveReport, qeidentityv2: &EnclaveIdentityV2) -> bool {
218 | // make sure that the enclave_identityv2 is a qeidentityv2
219 | // check that id is "QE", "TD_QE" or "QVE" and version is 2
220 | if !((qeidentityv2.enclave_identity.id == "QE"
221 | || qeidentityv2.enclave_identity.id == "TD_QE"
222 | || qeidentityv2.enclave_identity.id == "QVE")
223 | && qeidentityv2.enclave_identity.version == 2)
224 | {
225 | return false;
226 | }
227 |
228 | let mrsigner_ok = enclave_report.mrsigner
229 | == hex::decode(&qeidentityv2.enclave_identity.mrsigner)
230 | .unwrap()
231 | .as_slice();
232 | let isvprodid_ok = enclave_report.isv_prod_id == qeidentityv2.enclave_identity.isvprodid;
233 |
234 | let attributes = hex::decode(&qeidentityv2.enclave_identity.attributes).unwrap();
235 | let attributes_mask = hex::decode(&qeidentityv2.enclave_identity.attributes_mask).unwrap();
236 | let masked_attributes = attributes
237 | .iter()
238 | .zip(attributes_mask.iter())
239 | .map(|(a, m)| a & m)
240 | .collect::>();
241 | let masked_enclave_attributes = enclave_report
242 | .attributes
243 | .iter()
244 | .zip(attributes_mask.iter())
245 | .map(|(a, m)| a & m)
246 | .collect::>();
247 | let enclave_attributes_ok = masked_enclave_attributes == masked_attributes;
248 |
249 | let miscselect = hex::decode(&qeidentityv2.enclave_identity.miscselect).unwrap();
250 | let miscselect_mask = hex::decode(&qeidentityv2.enclave_identity.miscselect_mask).unwrap();
251 | let masked_miscselect = miscselect
252 | .iter()
253 | .zip(miscselect_mask.iter())
254 | .map(|(a, m)| a & m)
255 | .collect::>();
256 | let masked_enclave_miscselect = enclave_report
257 | .misc_select
258 | .iter()
259 | .zip(miscselect_mask.iter())
260 | .map(|(a, m)| a & m)
261 | .collect::>();
262 | let enclave_miscselect_ok = masked_enclave_miscselect == masked_miscselect;
263 |
264 | mrsigner_ok && isvprodid_ok && enclave_attributes_ok && enclave_miscselect_ok
265 | }
266 |
267 | fn verify_qe_report_data(
268 | report_data: &[u8],
269 | ecdsa_attestation_key: &[u8],
270 | qe_auth_data: &[u8],
271 | ) -> bool {
272 | let mut verification_data = Vec::new();
273 | verification_data.extend_from_slice(ecdsa_attestation_key);
274 | verification_data.extend_from_slice(qe_auth_data);
275 | let mut recomputed_report_data = [0u8; 64];
276 | recomputed_report_data[..32].copy_from_slice(&sha256sum(&verification_data));
277 | recomputed_report_data == report_data
278 | }
279 |
280 | // https://github.com/intel/SGX-TDX-DCAP-QuoteVerificationLibrary/blob/16b7291a7a86e486fdfcf1dfb4be885c0cc00b4e/Src/AttestationLibrary/src/Verifiers/QuoteVerifier.cpp#L271-L312
281 | fn converge_tcb_status_with_qe_tcb(tcb_status: TcbStatus, qe_tcb_status: TcbStatus) -> TcbStatus {
282 | let converged_tcb_status: TcbStatus;
283 | match qe_tcb_status {
284 | TcbStatus::TcbOutOfDate => {
285 | if tcb_status == TcbStatus::OK || tcb_status == TcbStatus::TcbSwHardeningNeeded {
286 | converged_tcb_status = TcbStatus::TcbOutOfDate;
287 | } else if tcb_status == TcbStatus::TcbConfigurationNeeded
288 | || tcb_status == TcbStatus::TcbConfigurationAndSwHardeningNeeded
289 | {
290 | converged_tcb_status = TcbStatus::TcbOutOfDateConfigurationNeeded;
291 | } else {
292 | converged_tcb_status = tcb_status;
293 | }
294 | },
295 | _ => {
296 | converged_tcb_status = tcb_status;
297 | }
298 | }
299 | converged_tcb_status
300 | }
301 |
--------------------------------------------------------------------------------
/src/utils/quotes/version_3.rs:
--------------------------------------------------------------------------------
1 | use crate::types::quotes::{body::QuoteBody, version_3::QuoteV3};
2 | use crate::types::{
3 | collaterals::IntelCollateral,
4 | tcbinfo::{TcbInfo, TcbInfoV2},
5 | TcbStatus, VerifiedOutput,
6 | };
7 | use crate::utils::cert::get_sgx_fmspc_tcbstatus_v2;
8 |
9 | use super::{check_quote_header, common_verify_and_fetch_tcb, converge_tcb_status_with_qe_tcb};
10 |
11 | pub fn verify_quote_dcapv3(
12 | quote: &QuoteV3,
13 | collaterals: &IntelCollateral,
14 | current_time: u64,
15 | ) -> VerifiedOutput {
16 | assert!(check_quote_header("e.header, 3), "invalid quote header");
17 |
18 | let quote_body = QuoteBody::SGXQuoteBody(quote.isv_enclave_report);
19 | let (qe_tcb_status, sgx_extensions, tcb_info) = common_verify_and_fetch_tcb(
20 | "e.header,
21 | "e_body,
22 | "e.signature.isv_enclave_report_signature,
23 | "e.signature.ecdsa_attestation_key,
24 | "e.signature.qe_report,
25 | "e.signature.qe_report_signature,
26 | "e.signature.qe_auth_data.data,
27 | "e.signature.qe_cert_data,
28 | collaterals,
29 | current_time,
30 | );
31 |
32 | let tcb_info_v2: TcbInfoV2;
33 | if let TcbInfo::V2(tcb) = tcb_info {
34 | tcb_info_v2 = tcb;
35 | } else {
36 | panic!("TcbInfo must be V2!");
37 | }
38 | let mut tcb_status = get_sgx_fmspc_tcbstatus_v2(&sgx_extensions, &tcb_info_v2);
39 |
40 | assert!(tcb_status != TcbStatus::TcbRevoked, "FMSPC TCB Revoked");
41 |
42 | tcb_status = converge_tcb_status_with_qe_tcb(tcb_status, qe_tcb_status);
43 |
44 | VerifiedOutput {
45 | quote_version: quote.header.version,
46 | tee_type: quote.header.tee_type,
47 | tcb_status,
48 | fmspc: sgx_extensions.fmspc,
49 | quote_body: quote_body,
50 | advisory_ids: None,
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/utils/quotes/version_4.rs:
--------------------------------------------------------------------------------
1 | use crate::constants::SGX_TEE_TYPE;
2 | use crate::types::quotes::body::QuoteBody;
3 | use crate::types::quotes::{version_4::QuoteV4, CertDataType};
4 | use crate::types::TcbStatus;
5 | use crate::types::{
6 | tcbinfo::{TcbInfo, TcbInfoV3},
7 | collaterals::IntelCollateral, VerifiedOutput,
8 | };
9 | use crate::utils::cert::get_sgx_tdx_fmspc_tcbstatus_v3;
10 | use crate::utils::tdx_module::{
11 | converge_tcb_status_with_tdx_module_tcb, get_tdx_module_identity_and_tcb,
12 | };
13 |
14 | use super::{check_quote_header, common_verify_and_fetch_tcb, converge_tcb_status_with_qe_tcb};
15 |
16 | pub fn verify_quote_dcapv4(
17 | quote: &QuoteV4,
18 | collaterals: &IntelCollateral,
19 | current_time: u64,
20 | ) -> VerifiedOutput {
21 | assert!(check_quote_header("e.header, 4), "invalid quote header");
22 |
23 | // we'll now proceed to verify the qe
24 | let qe_cert_data_v4 = "e.signature.qe_cert_data;
25 |
26 | // right now we just handle type 6, which contains the QEReport, QEReportSignature, QEAuthData and another CertData
27 | let qe_report_cert_data = if let CertDataType::QeReportCertData(qe_report_cert_data) =
28 | qe_cert_data_v4.get_cert_data()
29 | {
30 | qe_report_cert_data
31 | } else {
32 | panic!("Unsupported CertDataType in QuoteSignatureDataV4");
33 | };
34 |
35 | let (qe_tcb_status, sgx_extensions, tcb_info) = common_verify_and_fetch_tcb(
36 | "e.header,
37 | "e.quote_body,
38 | "e.signature.quote_signature,
39 | "e.signature.ecdsa_attestation_key,
40 | &qe_report_cert_data.qe_report,
41 | &qe_report_cert_data.qe_report_signature,
42 | &qe_report_cert_data.qe_auth_data.data,
43 | &qe_report_cert_data.qe_cert_data,
44 | collaterals,
45 | current_time,
46 | );
47 |
48 | let tcb_info_v3: TcbInfoV3;
49 | if let TcbInfo::V3(tcb) = tcb_info {
50 | tcb_info_v3 = tcb;
51 | } else {
52 | panic!("TcbInfo must be V3!");
53 | }
54 |
55 | let (quote_tdx_body, tee_tcb_svn) = if let QuoteBody::TD10QuoteBody(body) = "e.quote_body {
56 | (Some(body), body.tee_tcb_svn)
57 | } else {
58 | // SGX does not produce tee_tcb_svns
59 | (None, [0; 16])
60 | };
61 |
62 | let tee_type = quote.header.tee_type;
63 | let (sgx_tcb_status, tdx_tcb_status, advisory_ids) =
64 | get_sgx_tdx_fmspc_tcbstatus_v3(tee_type, &sgx_extensions, &tee_tcb_svn, &tcb_info_v3);
65 |
66 | assert!(
67 | sgx_tcb_status != TcbStatus::TcbRevoked || tdx_tcb_status != TcbStatus::TcbRevoked,
68 | "FMSPC TCB Revoked"
69 | );
70 |
71 | let mut tcb_status: TcbStatus;
72 | if quote.header.tee_type == SGX_TEE_TYPE {
73 | tcb_status = sgx_tcb_status;
74 | } else {
75 | tcb_status = tdx_tcb_status;
76 |
77 | // Fetch TDXModule TCB and TDXModule Identity
78 | let (tdx_module_tcb_status, tdx_module_mrsigner, tdx_module_attributes) =
79 | get_tdx_module_identity_and_tcb(&tee_tcb_svn, &tcb_info_v3);
80 |
81 | assert!(
82 | tdx_module_tcb_status != TcbStatus::TcbRevoked,
83 | "TDX Module TCB Revoked"
84 | );
85 |
86 | // check TDX module
87 | let (tdx_report_mrsigner, tdx_report_attributes) = if let Some(tdx_body) = quote_tdx_body {
88 | (tdx_body.mrsignerseam, tdx_body.seam_attributes)
89 | } else {
90 | unreachable!();
91 | };
92 |
93 | let mr_signer_matched = tdx_module_mrsigner == tdx_report_mrsigner;
94 | let attributes_matched = tdx_module_attributes == tdx_report_attributes;
95 | assert!(
96 | mr_signer_matched && attributes_matched,
97 | "TDX module values mismatch"
98 | );
99 |
100 | tcb_status = converge_tcb_status_with_tdx_module_tcb(tcb_status, tdx_module_tcb_status)
101 | }
102 |
103 | tcb_status = converge_tcb_status_with_qe_tcb(tcb_status, qe_tcb_status);
104 |
105 | VerifiedOutput {
106 | quote_version: quote.header.version,
107 | tee_type: quote.header.tee_type,
108 | tcb_status,
109 | fmspc: sgx_extensions.fmspc,
110 | quote_body: quote.quote_body,
111 | advisory_ids: advisory_ids
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/utils/tcbinfo.rs:
--------------------------------------------------------------------------------
1 | use crate::types::tcbinfo::{TcbInfoV2, TcbInfoV3};
2 | use crate::utils::crypto::verify_p256_signature_bytes;
3 | use crate::X509Certificate;
4 |
5 | use sha3::{Digest, Keccak256};
6 |
7 | pub fn validate_tcbinfov2(
8 | tcbinfov2: &TcbInfoV2,
9 | sgx_signing_cert: &X509Certificate,
10 | current_time: u64,
11 | ) -> bool {
12 | // get tcb_info_root time
13 | let issue_date = chrono::DateTime::parse_from_rfc3339(&tcbinfov2.tcb_info.issue_date).unwrap();
14 | let next_update_date =
15 | chrono::DateTime::parse_from_rfc3339(&tcbinfov2.tcb_info.next_update).unwrap();
16 |
17 | // convert the issue_date and next_update_date to seconds since epoch
18 | let issue_date_seconds = issue_date.timestamp() as u64;
19 | let next_update_seconds = next_update_date.timestamp() as u64;
20 |
21 | // check that the current time is between the issue_date and next_update_date
22 | if current_time < issue_date_seconds || current_time > next_update_seconds {
23 | return false;
24 | }
25 |
26 | // signature is a hex string, we'll convert it to bytes
27 | // ZL: we'll assume that the signature is a P256 ECDSA signature
28 | let tcbinfov2_signature_bytes = hex::decode(&tcbinfov2.signature).unwrap();
29 |
30 | // verify that the tcb_info_root is signed by the root cert
31 | let tcbinfov2_signature_data = serde_json::to_vec(&tcbinfov2.tcb_info).unwrap();
32 | verify_p256_signature_bytes(
33 | &tcbinfov2_signature_data,
34 | &tcbinfov2_signature_bytes,
35 | sgx_signing_cert.public_key().subject_public_key.as_ref(),
36 | )
37 | }
38 |
39 | pub fn validate_tcbinfov3(
40 | tcbinfov3: &TcbInfoV3,
41 | sgx_signing_cert: &X509Certificate,
42 | current_time: u64,
43 | ) -> bool {
44 | // get tcb_info_root time
45 | let issue_date = chrono::DateTime::parse_from_rfc3339(&tcbinfov3.tcb_info.issue_date).unwrap();
46 | let next_update_date =
47 | chrono::DateTime::parse_from_rfc3339(&tcbinfov3.tcb_info.next_update).unwrap();
48 |
49 | // convert the issue_date and next_update_date to seconds since epoch
50 | let issue_date_seconds = issue_date.timestamp() as u64;
51 | let next_update_seconds = next_update_date.timestamp() as u64;
52 |
53 | // check that the current time is between the issue_date and next_update_date
54 | if current_time < issue_date_seconds || current_time > next_update_seconds {
55 | assert!(false);
56 | return false;
57 | }
58 |
59 | // signature is a hex string, we'll convert it to bytes
60 | // ZL: we'll assume that the signature is a P256 ECDSA signature
61 | let tcbinfov3_signature_bytes = hex::decode(&tcbinfov3.signature).unwrap();
62 |
63 | // verify that the tcb_info_root is signed by the root cert
64 | let tcbinfov3_signature_data = serde_json::to_vec(&tcbinfov3.tcb_info).unwrap();
65 | verify_p256_signature_bytes(
66 | &tcbinfov3_signature_data,
67 | &tcbinfov3_signature_bytes,
68 | sgx_signing_cert.public_key().subject_public_key.as_ref(),
69 | )
70 | }
71 |
72 | // A content_hash is the hash representation of tcb_info
73 | // excluding both "issue_date" and "next_update" fields
74 | // integers are big-endian encoded
75 |
76 | pub fn get_tcbinfov2_content_hash(tcbinfov2: &TcbInfoV2) -> [u8; 32] {
77 | let mut pre_image: Vec = vec![];
78 |
79 | pre_image.extend_from_slice(&[tcbinfov2.tcb_info.tcb_type]);
80 | pre_image.extend_from_slice(&[tcb_id_string_to_u8("SGX")]); // SGX by default for V2
81 | pre_image.extend_from_slice(&tcbinfov2.tcb_info.version.to_be_bytes());
82 | pre_image.extend_from_slice(&tcbinfov2.tcb_info.tcb_evaluation_data_number.to_be_bytes());
83 | pre_image.extend_from_slice(hex::decode(&tcbinfov2.tcb_info.fmspc).unwrap().as_slice());
84 | pre_image.extend_from_slice(hex::decode(&tcbinfov2.tcb_info.pce_id).unwrap().as_slice());
85 | pre_image.extend_from_slice(
86 | serde_json::to_vec(&tcbinfov2.tcb_info.tcb_levels)
87 | .unwrap()
88 | .as_slice(),
89 | );
90 |
91 | Keccak256::digest(&pre_image).try_into().unwrap()
92 | }
93 |
94 | pub fn get_tcbinfov3_content_hash(tcbinfov3: &TcbInfoV3) -> [u8; 32] {
95 | let mut pre_image: Vec = vec![];
96 |
97 | pre_image.extend_from_slice(&[tcbinfov3.tcb_info.tcb_type]);
98 | pre_image.extend_from_slice(&[tcb_id_string_to_u8(&tcbinfov3.tcb_info.id)]); // SGX by default for V2
99 | pre_image.extend_from_slice(&tcbinfov3.tcb_info.version.to_be_bytes());
100 | pre_image.extend_from_slice(&tcbinfov3.tcb_info.tcb_evaluation_data_number.to_be_bytes());
101 | pre_image.extend_from_slice(hex::decode(&tcbinfov3.tcb_info.fmspc).unwrap().as_slice());
102 | pre_image.extend_from_slice(hex::decode(&tcbinfov3.tcb_info.pce_id).unwrap().as_slice());
103 | pre_image.extend_from_slice(
104 | serde_json::to_vec(&tcbinfov3.tcb_info.tcb_levels)
105 | .unwrap()
106 | .as_slice(),
107 | );
108 |
109 | if let Some(tdx_module) = &tcbinfov3.tcb_info.tdx_module {
110 | pre_image.extend_from_slice(serde_json::to_vec(tdx_module).unwrap().as_slice());
111 | }
112 |
113 | if let Some(tdx_module_identities) = &tcbinfov3.tcb_info.tdx_module_identities {
114 | pre_image.extend_from_slice(
115 | serde_json::to_vec(tdx_module_identities)
116 | .unwrap()
117 | .as_slice(),
118 | );
119 | }
120 |
121 | Keccak256::digest(&pre_image).try_into().unwrap()
122 | }
123 |
124 | fn tcb_id_string_to_u8(tcb_id: &str) -> u8 {
125 | match tcb_id {
126 | "SGX" => 0,
127 | "TDX" => 1,
128 | _ => panic!("Unknown TCB_ID"),
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/utils/tdx_module.rs:
--------------------------------------------------------------------------------
1 | use crate::types::tcbinfo::TcbInfoV3;
2 | use crate::types::TcbStatus;
3 |
4 | // https://github.com/intel/SGX-TDX-DCAP-QuoteVerificationLibrary/blob/7e5b2a13ca5472de8d97dd7d7024c2ea5af9a6ba/Src/AttestationLibrary/src/Verifiers/Checks/TdxModuleCheck.cpp#L62-L97
5 | pub fn get_tdx_module_identity_and_tcb(
6 | tee_tcb_svn: &[u8; 16],
7 | tcb_info_v3: &TcbInfoV3,
8 | ) -> (TcbStatus, [u8; 48], u64) {
9 | let tdx_module = if let Some(tdx_module_obj) = &tcb_info_v3.tcb_info.tdx_module {
10 | tdx_module_obj
11 | } else {
12 | panic!("TDX module not found");
13 | };
14 |
15 | let tdx_module_isv_svn = tee_tcb_svn[0];
16 | let tdx_module_version = tee_tcb_svn[1];
17 |
18 | if tdx_module_version == 0 {
19 | let mut mrsigner: [u8; 48] = [0; 48];
20 | mrsigner.copy_from_slice(&hex::decode(&tdx_module.mrsigner).unwrap());
21 |
22 | return (
23 | TcbStatus::OK,
24 | mrsigner,
25 | from_str_to_u64(tdx_module.attributes.as_str()),
26 | );
27 | }
28 |
29 | let tdx_module_identity_id = format!("TDX_{:02x}", tdx_module_version);
30 | if let Some(tdx_module_identities) = &tcb_info_v3.tcb_info.tdx_module_identities {
31 | for tdx_module_identity in tdx_module_identities.iter() {
32 | if tdx_module_identity.id == tdx_module_identity_id {
33 | for tcb_level in &tdx_module_identity.tcb_levels {
34 | if tdx_module_isv_svn >= tcb_level.tcb.isvsvn {
35 | let mut mrsigner: [u8; 48] = [0; 48];
36 | mrsigner
37 | .copy_from_slice(&hex::decode(&tdx_module_identity.mrsigner).unwrap());
38 | let attributes = &tdx_module_identity.attributes;
39 | let tcb_status = TcbStatus::from_str(tcb_level.tcb_status.as_str());
40 | return (tcb_status, mrsigner, from_str_to_u64(attributes.as_str()));
41 | }
42 | }
43 | }
44 | }
45 | } else {
46 | panic!("TDX module identities not found");
47 | }
48 |
49 | panic!("TDX Module could not match to any TCB Level for TSX Module ISVSN: {}", tdx_module_isv_svn);
50 | }
51 |
52 | // https://github.com/intel/SGX-TDX-DCAP-QuoteVerificationLibrary/blob/7e5b2a13ca5472de8d97dd7d7024c2ea5af9a6ba/Src/AttestationLibrary/src/Verifiers/Checks/TdxModuleCheck.cpp#L99-L137
53 | pub fn converge_tcb_status_with_tdx_module_tcb(
54 | tcb_status: TcbStatus,
55 | tdx_module_tcb_status: TcbStatus,
56 | ) -> TcbStatus {
57 | let converged_tcb_status: TcbStatus;
58 | match tdx_module_tcb_status {
59 | TcbStatus::TcbOutOfDate => {
60 | if tcb_status == TcbStatus::OK || tcb_status == TcbStatus::TcbSwHardeningNeeded {
61 | converged_tcb_status = TcbStatus::TcbOutOfDate;
62 | } else if tcb_status == TcbStatus::TcbConfigurationNeeded
63 | || tcb_status == TcbStatus::TcbConfigurationAndSwHardeningNeeded
64 | {
65 | converged_tcb_status = TcbStatus::TcbOutOfDateConfigurationNeeded;
66 | } else {
67 | converged_tcb_status = tcb_status;
68 | }
69 | },
70 | _ => {
71 | converged_tcb_status = tcb_status;
72 | }
73 | }
74 | converged_tcb_status
75 | }
76 |
77 | fn from_str_to_u64(str: &str) -> u64 {
78 | assert!(str.len() == 16, "invalid u64 str length");
79 |
80 | match u64::from_str_radix(str, 16) {
81 | Ok(ret) => ret,
82 | Err(_) => panic!("Invalid hex character found"),
83 | }
84 | }
85 |
--------------------------------------------------------------------------------