├── .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 | [![Automata DCAP Rust Library](https://img.shields.io/badge/Power%20By-Automata-orange.svg)](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 | --------------------------------------------------------------------------------