├── .gitignore ├── CODE_OF_CONDUCT.md ├── Cargo.toml ├── LICENSE ├── README.md ├── SECURITY.md ├── SUPPORT.md ├── sample ├── .cargo │ └── config.toml ├── Cargo.toml ├── README.md ├── SampleHost │ ├── Main.cpp │ ├── SampleHost.sln │ ├── SampleHost.vcxproj │ └── packages.config ├── build.rs ├── rust-toolchain.toml └── src │ ├── bcrypt.rs │ ├── ffi.rs │ ├── lib.rs │ └── params.rs └── src ├── allocator.rs ├── enclaveapi.rs ├── error.rs ├── lib.rs ├── types.rs └── winenclave.rs /.gitignore: -------------------------------------------------------------------------------- 1 | **/target 2 | Cargo.lock 3 | .vs 4 | **/x64 5 | *.vcxproj.user 6 | **/packages 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vbs-enclave" 3 | version = "0.2.0" 4 | edition = "2021" 5 | license-file = "LICENSE" 6 | publish = false 7 | 8 | [lib] 9 | test = false 10 | 11 | [dependencies] 12 | thiserror = { version = "2.0", default-features = false } 13 | 14 | [dependencies.windows-sys] 15 | version = "0.59.0" 16 | features = [ 17 | "Win32_System_Environment", 18 | "Win32_Foundation" 19 | ] 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VBS Rust Enclave Example 2 | 3 | This proof-of-concept demonstrates how one can implement a Windows [Virtualization-based security (VBS) enclave](https://learn.microsoft.com/en-us/windows/win32/trusted-execution/vbs-enclaves) in Rust. 4 | 5 | This project is the result of a Microsoft Offensive Research & Security Engineering (MORSE) hackathon. 6 | 7 | ## Requirements 8 | 9 | - [Rustlang](https://www.rust-lang.org/tools/install) 1.86.0-nightly 10 | - Cargo 11 | - [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/) 12 | - [Windows 11 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/) (10.0.22621.3233 or later) 13 | 14 | It was tested on x86_64, but will probably build for arm64 with no issues. 15 | 16 | ## Building the `vbs-enclave` crate 17 | 18 | The `vbs-enclave` crate will build with `cargo build` just fine. However, this crate only builds an rlib and is not usable standalone, but there is a sample enclave using it provided. 19 | 20 | ## Building the Sample 21 | 22 | ### `SampleHost` 23 | 24 | The sample host executable, `SampleHost.exe` is a Visual Studio project. Ensure the configuration you select (Debug or Release) matches what you build the enclave DLL for. 25 | 26 | ### `sample-vbs-enclave-rs` 27 | 28 | #### Test Signing 29 | Prior to building, follow the steps in the [VBS Enclaves Development Guide](https://learn.microsoft.com/en-us/windows/win32/trusted-execution/vbs-enclaves-dev-guide#step-3-signing-vbs-enclave-dlls) for configuring test signing. Export your certificate to a file (in these instructions, we use `enclave.cer`) 30 | 31 | You will probably want to run the sample on a test system, since it requires test signing. When you set up your test system, ensure that VBS is enabled. The instructions below work for a Hyper-V VM: 32 | 33 | On your host system, in an administrator prompt, run: 34 | ```powershell 35 | Set-VMProcessor -VmName "My VM Name" -ExposeVirtualizationExtensions $true 36 | ``` 37 | 38 | On your test system VM, run: 39 | ```powershell 40 | bcdedit /set testsigning on 41 | reg add "HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard" /v "EnableVirtualizationBasedSecurity" /t REG_DWORD /d 1 /f 42 | Restart-Computer 43 | ``` 44 | 45 | Additionally, you will need to copy your test signing certificate to the test system and install it: 46 | 47 | ```powershell 48 | Set-Location -Path Cert:\CurrentUser\My 49 | Import-Certificate C:\enclave.cer 50 | ``` 51 | 52 | Once you have a test signing certificate created and have enabled test signing on the system that will run the example enclave, you can build the enclave itself from the Visual Studio command prompt: 53 | 54 | #### Debug build 55 | 56 | ```powershell 57 | cd sample 58 | cargo build 59 | veiid.exe .\target\debug\sample_vbs_enclave_rs.dll 60 | 61 | # Replace "MyTestEnclaveCert" with your test signing certificate's name 62 | signtool.exe sign /ph /fd SHA256 /n "MyTestEnclaveCert" target\debug\sample_vbs_enclave_rs.dll 63 | ``` 64 | 65 | #### Release build 66 | 67 | ```powershell 68 | cd sample 69 | cargo build -r 70 | veiid.exe .\target\release\sample_vbs_enclave_rs.dll 71 | 72 | # Replace "MyTestEnclaveCert" with your test signing certificate's name 73 | signtool.exe sign /ph /fd SHA256 /n "MyTestEnclaveCert" target\release\sample_vbs_enclave_rs.dll 74 | ``` 75 | 76 | ## Running the sample 77 | 78 | Once you have the sample host and enclave executables, you can launch it like so, with this example output: 79 | 80 | ``` 81 | > .\SampleHost.exe .\sample_vbs_enclave_rs.dll "Hello World" 82 | Rust enclave created and initialized! 83 | Creating host keypair... 84 | Creating new enclave key and providing host public key... 85 | New keypair created! 86 | Report generated! 1240 bytes! 87 | Beep boop beep, validating attestation report... (for pretend) 88 | Enclave is validated! 89 | Deriving shared key... 90 | Public key imported successfully! 91 | Successfully derived shared ephemeral key! 92 | Encrypting the message: 93 | 48 00 65 00 6c 00 6c 00 6f 00 20 00 57 00 6f 00 H.e.l.l.o...W.o. 94 | 72 00 6c 00 64 00 r.l.d. 95 | 96 | Data decrypted! Message is: 97 | 48 00 65 00 6c 00 6c 00 6f 00 20 00 57 00 6f 00 H.e.l.l.o...W.o. 98 | 72 00 6c 00 64 00 r.l.d. 99 | 100 | The message round-tripped! 101 | ``` 102 | 103 | ## Future 104 | - Develop safe wrappers for VTL0 pointers and other mechanisms to ensure secure pointer usage across the VTL0<->VTL1 trust boundary 105 | - Use bindgen to generate parameter structures used in the host process from its header files 106 | - Implement a Rust version of SampleHost too 107 | 108 | ## Contributing 109 | 110 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 111 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 112 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 113 | 114 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 115 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 116 | provided by the bot. You will only need to do this once across all repos using our CLA. 117 | 118 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 119 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 120 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 121 | 122 | ## Trademarks 123 | 124 | This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft 125 | trademarks or logos is subject to and must follow 126 | [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). 127 | Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. 128 | Any use of third-party trademarks or logos are subject to those third-party's policies. 129 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # Support 2 | 3 | ## How to file issues and get help 4 | 5 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing 6 | issues before filing new issues to avoid duplicates. For new issues, file your bug or 7 | feature request as a new Issue. 8 | 9 | For help and questions about using this project, please use the project's GitHub Discussions area. 10 | 11 | ## Microsoft Support Policy 12 | 13 | Support for this project is limited to the resources listed above. 14 | -------------------------------------------------------------------------------- /sample/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | rustflags = [ 2 | # Pre Link Args 3 | # -Z is unstable options, only available on nightly 4 | "-Z", "pre-link-arg=/NXCOMPAT", 5 | "-Z", "pre-link-arg=/NODEFAULTLIB", 6 | "-Z", "pre-link-arg=/SUBSYSTEM:NATIVE", 7 | "-Z", "pre-link-arg=/DYNAMICBASE", 8 | "-Z", "pre-link-arg=/MANIFEST:NO", 9 | 10 | # Post Link Args -- these are dynamically generated in build.rs 11 | # "-C", "link-arg=/OPT:REF,ICF", 12 | # "-C", "link-arg=/ENTRY:dllmain", 13 | # "-C", "link-arg=/MERGE:.edata=.rdata", 14 | # "-C", "link-arg=/MERGE:.rustc=.data", 15 | # "-C", "link-arg=/INTEGRITYCHECK", 16 | # "-C", "link-arg=/enclave", 17 | # "-C", "link-arg=/GUARD:MIXED", 18 | # "-C", "link-arg=/include:__enclave_config", 19 | # "-C", "link-arg=C:\\Debuggers\\Lib\\10.0.26100.0\\ucrt_enclave\\x64\\ucrt.lib", 20 | # # libvcruntime *must* come before vertdll in the link order, or it will error 21 | # "-C", "link-arg=C:\\Program Files\\Microsoft Visual Studio\\2022\\Preview\\VC\\Tools\\MSVC\\14.42.34321\\lib\\x64\\enclave\\libvcruntime.lib", 22 | # "-C", "link-arg=C:\\Program Files\\Microsoft Visual Studio\\2022\\Preview\\VC\\Tools\\MSVC\\14.42.34321\\lib\\x64\\enclave\\libcmt.lib", 23 | # "-C", "link-arg=vertdll.lib", 24 | # "-C", "link-arg=bcrypt.lib", 25 | ] -------------------------------------------------------------------------------- /sample/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sample-vbs-enclave-rs" 3 | version = "0.2.0" 4 | edition = "2021" 5 | license-file = "../LICENSE" 6 | publish = false 7 | 8 | [lib] 9 | crate-type = ["cdylib"] 10 | test = false 11 | 12 | [profile.release] 13 | panic = "abort" 14 | 15 | [profile.dev] 16 | panic = "abort" 17 | 18 | [dependencies] 19 | vbs-enclave = { path = ".." } 20 | hex-literal = "0.4.1" 21 | spin = "0.9.8" 22 | 23 | [dependencies.windows-sys] 24 | version = "0.59.0" 25 | features = [ 26 | "Win32_System_Environment", 27 | "Win32_Security_Cryptography", 28 | "Win32_Foundation" 29 | ] -------------------------------------------------------------------------------- /sample/README.md: -------------------------------------------------------------------------------- 1 | # VBS Rust Enclave Example 2 | 3 | This proof-of-concept demonstrates how one can implement a Windows [Virtualization-based security (VBS) enclave](https://learn.microsoft.com/en-us/windows/win32/trusted-execution/vbs-enclaves) in Rust. 4 | 5 | This project is the result of a Microsoft Offensive Research & Security Engineering (MORSE) hackathon. 6 | 7 | ## What does the sample do? 8 | 9 | This sample enclave simulates an enclave that would operate on an untrusted remote system to create a shared secret between the enclave and a trusted remote system. In a realistic scenario, the host process would not be the one generating and deriving secrets; this would occur on the trusted remote system that uses the [Microsoft Azure Attestation service](https://learn.microsoft.com/en-us/azure/attestation/overview) or another service to validate the enclave's attestation claims before negotiating a shared secret. 10 | 11 | The enclave has three exported routines: 12 | - `new_keypair`: This routine accepts an ECDH public key from the host process, and then generates its own ECDH keypair. Once it has both, it derives a shared secret that can be used as an AES-GCM encryption key. 13 | - `generate_report`: This routine accepts an allocation callback, which it uses to allocate space in the host process where it can write an attestation report containing its ECDH public key. 14 | - `decrypt_data`: This routine accepts an allocation callback and ciphertext, which it then decrypts. It uses the allocation callback to allocate space in the host process where it can write the decrypted data. 15 | 16 | Using these routines, the host process then performs the following actions: 17 | - Generate an ECDH keypair 18 | - Pass its public key to the enclave via `new_keypair` 19 | - Request an attestation report via `generate_report` 20 | - (Pretends to) validate the attestation report 21 | - Extract the enclave's public key from the report 22 | - Derive the shared secret using its private key and the enclave's public key 23 | - Use the shared secret as an AES-GCM key encrypt a string provided in the command line 24 | - Pass the resulting ciphertext to the enclave via `decrypt_data` and receive back the decrypted data 25 | - Verify that the decrypted data matches the original plaintext and therefore round-tripped 26 | 27 | ## Requirements 28 | 29 | - [Rustlang](https://www.rust-lang.org/tools/install) 1.86.0-nightly 30 | - Cargo 31 | - [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/) 32 | - [Windows 11 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/) (10.0.22621.3233 or later) 33 | 34 | It was tested on x86_64, but will probably build for arm64 with no issues. 35 | 36 | ## Building the `vbs-enclave` crate 37 | 38 | The `vbs-enclave` crate will build with `cargo build` just fine. However, this crate only builds an rlib and is not usable standalone, but there is a sample enclave using it provided. 39 | 40 | ## Building the Sample 41 | 42 | ### `SampleHost` 43 | 44 | The sample host executable, `SampleHost.exe` is a Visual Studio project. Ensure the configuration you select (Debug or Release) matches what you build the enclave DLL for. 45 | 46 | ### `sample-vbs-enclave-rs` 47 | 48 | #### Test Signing 49 | Prior to building, follow the steps in the [VBS Enclaves Development Guide](https://learn.microsoft.com/en-us/windows/win32/trusted-execution/vbs-enclaves-dev-guide#step-3-signing-vbs-enclave-dlls) for configuring test signing. Export your certificate to a file (in these instructions, we use `enclave.cer`) 50 | 51 | You will probably want to run the sample on a test system, since it requires test signing. When you set up your test system, ensure that VBS is enabled. The instructions below work for a Hyper-V VM: 52 | 53 | On your host system, in an administrator prompt, run: 54 | ```powershell 55 | Set-VMProcessor -VmName "My VM Name" -ExposeVirtualizationExtensions $true 56 | ``` 57 | 58 | On your test system VM, run: 59 | ```powershell 60 | bcdedit /set testsigning on 61 | reg add "HKLM\SYSTEM\CurrentControlSet\Control\DeviceGuard" /v "EnableVirtualizationBasedSecurity" /t REG_DWORD /d 1 /f 62 | Restart-Computer 63 | ``` 64 | 65 | Additionally, you will need to copy your test signing certificate to the test system and install it: 66 | 67 | ```powershell 68 | Set-Location -Path Cert:\CurrentUser\My 69 | Import-Certificate C:\enclave.cer 70 | ``` 71 | 72 | Once you have a test signing certificate created and have enabled test signing on the system that will run the example enclave, you can build the enclave itself from the Visual Studio command prompt: 73 | 74 | #### Debug build 75 | 76 | ``` 77 | cd sample 78 | cargo build 79 | veiid.exe .\target\debug\sample_vbs_enclave_rs.dll 80 | 81 | # Replace "MyTestEnclaveCert" with your test signing certificate's name 82 | signtool.exe sign /ph /fd SHA256 /n "MyTestEnclaveCert" target\debug\sample_vbs_enclave_rs.dll 83 | ``` 84 | 85 | #### Release build 86 | 87 | ``` 88 | cd sample 89 | cargo build -r 90 | veiid.exe .\target\release\sample_vbs_enclave_rs.dll 91 | 92 | # Replace "MyTestEnclaveCert" with your test signing certificate's name 93 | signtool.exe sign /ph /fd SHA256 /n "MyTestEnclaveCert" target\release\sample_vbs_enclave_rs.dll 94 | ``` 95 | 96 | ## Running the sample 97 | 98 | Once you have the sample host and enclave executables, you can launch it like so, with this example output: 99 | 100 | ``` 101 | > .\SampleHost.exe .\sample_vbs_enclave_rs.dll "Hello World" 102 | Rust enclave created and initialized! 103 | Creating host keypair... 104 | Creating new enclave key and providing host public key... 105 | New keypair created! 106 | Report generated! 1240 bytes! 107 | Beep boop beep, validating attestation report... (for pretend) 108 | Enclave is validated! 109 | Deriving shared key... 110 | Public key imported successfully! 111 | Successfully derived shared ephemeral key! 112 | Encrypting the message: 113 | 48 00 65 00 6c 00 6c 00 6f 00 20 00 57 00 6f 00 H.e.l.l.o...W.o. 114 | 72 00 6c 00 64 00 r.l.d. 115 | 116 | Data decrypted! Message is: 117 | 48 00 65 00 6c 00 6c 00 6f 00 20 00 57 00 6f 00 H.e.l.l.o...W.o. 118 | 72 00 6c 00 64 00 r.l.d. 119 | 120 | The message round-tripped! 121 | ``` 122 | 123 | ## Future 124 | - Develop safe wrappers for VTL0 pointers and other mechanisms to ensure secure pointer usage across the VTL0<->VTL1 trust boundary 125 | - Use bindgen to generate parameter structures used in the host process from its header files 126 | - Implement a Rust version of SampleHost too 127 | -------------------------------------------------------------------------------- /sample/SampleHost/Main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // These suppress some warnings for casting LPVOID to HRESULT 10 | // and other truncation type issues when passing parameters and 11 | // return values back from CallEnclave. 12 | #pragma warning (disable : 4311) 13 | #pragma warning (disable : 4302) 14 | #pragma warning (disable : 4267) 15 | 16 | void CleanupEnclave(void* enclave_base) { 17 | TerminateEnclave(enclave_base, TRUE); 18 | DeleteEnclave(enclave_base); 19 | } 20 | 21 | using Enclave = std::unique_ptr; 22 | 23 | const uint8_t OwnerId[IMAGE_ENCLAVE_LONG_ID_LENGTH] = {0x10, 0x20, 0x30, 0x40, 0x41, 0x31, 0x21, 0x11}; 24 | 25 | struct NewKeypairParams 26 | { 27 | uint32_t key_size; 28 | uint8_t *public_key; 29 | 30 | 31 | }; 32 | 33 | struct GenerateReportParams 34 | { 35 | uint8_t *(*allocate_callback)(size_t); 36 | size_t report_size; 37 | uint8_t *report; 38 | }; 39 | 40 | struct DecryptDataParams 41 | { 42 | uint8_t* (*allocate_callback)(size_t); 43 | size_t encrypted_size; 44 | uint8_t* encrypted_data; 45 | size_t iv_size; 46 | uint8_t* iv; 47 | size_t tag_size; 48 | uint8_t* tag; 49 | size_t decrypted_size; 50 | uint8_t *decrypted_data; 51 | }; 52 | 53 | const uint32_t KeySize = 256; 54 | 55 | struct AES256KeyBlob 56 | { 57 | BCRYPT_KEY_DATA_BLOB_HEADER header; 58 | uint8_t key[32]; 59 | }; 60 | 61 | uint8_t *AllocateCallback(size_t size) 62 | { 63 | auto allocation = std::make_unique(size); 64 | return allocation.release(); 65 | } 66 | 67 | char hexchars[] = "0123456789abcdef"; 68 | void DumpBytes(_In_ uint8_t* buffer, size_t allocation_size) { 69 | for (size_t line = 0; line * 16 < allocation_size; line++) { 70 | uint8_t count = 0; 71 | while (count < 16) { 72 | std::cout << hexchars[((buffer[line * 16 + count]) & 0xf0) >> 4] << hexchars[(buffer[line * 16 + count]) & 0xf] << " "; 73 | count++; 74 | if (line * 16 + count == allocation_size) { 75 | break; 76 | } 77 | } 78 | 79 | for (size_t i = 0; i < 16 - count; i++) { 80 | std::cout << " "; 81 | } 82 | 83 | for (size_t i = 0; i < count; i++) { 84 | if (buffer[line * 16 + i] >= 'A' && buffer[line * 16 + i] <= 'z') { 85 | std::cout << buffer[line * 16 + i]; 86 | } 87 | else { 88 | std::cout << '.'; 89 | } 90 | } 91 | 92 | std::cout << std::endl; 93 | } 94 | } 95 | 96 | void* StartEnclave(std::wstring enclave_name) { 97 | ENCLAVE_CREATE_INFO_VBS info{}; 98 | 99 | #ifdef _DEBUG 100 | info.Flags = ENCLAVE_VBS_FLAG_DEBUG; 101 | #endif 102 | 103 | CopyMemory(&info.OwnerID, OwnerId, IMAGE_ENCLAVE_LONG_ID_LENGTH); 104 | 105 | void* enclave_base = CreateEnclave( 106 | GetCurrentProcess(), 107 | nullptr, 108 | 0x10000000, 109 | 0, 110 | ENCLAVE_TYPE_VBS, 111 | &info, 112 | sizeof(ENCLAVE_CREATE_INFO_VBS), 113 | nullptr); 114 | 115 | if (enclave_base == nullptr) 116 | { 117 | std::cerr << "CreateEnclave failed: " << GetLastError() << std::endl; 118 | return nullptr; 119 | } 120 | 121 | if (!LoadEnclaveImage(enclave_base, enclave_name.c_str())) 122 | { 123 | std::cerr << "LoadEnclaveImage failed: " << GetLastError() << std::endl; 124 | return nullptr; 125 | } 126 | 127 | ENCLAVE_INIT_INFO_VBS init_info{}; 128 | init_info.Length = sizeof(init_info); 129 | init_info.ThreadCount = 16; 130 | 131 | if (!InitializeEnclave(GetCurrentProcess(), enclave_base, &init_info, sizeof(init_info), nullptr)) 132 | { 133 | std::cerr << "InitializeEnclave failed: " << GetLastError() << std::endl; 134 | return nullptr; 135 | } 136 | 137 | return enclave_base; 138 | } 139 | 140 | wil::unique_bcrypt_key GenerateECDHKeypair() { 141 | wil::unique_bcrypt_key keypair = nullptr; 142 | 143 | NTSTATUS status = BCryptGenerateKeyPair( 144 | BCRYPT_ECDH_P256_ALG_HANDLE, 145 | &keypair, 146 | 256, 147 | 0 148 | ); 149 | 150 | if (status != 0) { 151 | std::cerr << "BCryptGenerateKeyPair failed: " << std::hex << status << std::endl; 152 | return nullptr; 153 | } 154 | 155 | status = BCryptFinalizeKeyPair(keypair.get(), 0); 156 | 157 | if (status != 0) { 158 | std::cerr << "BCryptFinalizeKeyPair failed: " << std::hex << status << std::endl; 159 | return nullptr; 160 | } 161 | 162 | return keypair; 163 | } 164 | 165 | std::unique_ptr ExportPublicKey(BCRYPT_KEY_HANDLE keypair) { 166 | uint32_t blob_size = 0; 167 | 168 | NTSTATUS status = BCryptExportKey( 169 | keypair, 170 | nullptr, 171 | BCRYPT_ECCPUBLIC_BLOB, 172 | nullptr, 173 | 0, 174 | (ULONG*)&blob_size, 175 | 0 176 | ); 177 | 178 | if (status != 0) { 179 | std::cerr << "BCryptExportKey failed: " << std::hex << status << std::endl; 180 | return nullptr; 181 | } 182 | 183 | if (blob_size != sizeof(BCRYPT_ECCKEY_BLOB) + ENCLAVE_REPORT_DATA_LENGTH) { 184 | std::cerr << "Export blob size is " << std::dec << blob_size << " bytes, expected " << sizeof(BCRYPT_ECCKEY_BLOB) + ENCLAVE_REPORT_DATA_LENGTH << " bytes!" << std::endl; 185 | return nullptr; 186 | } 187 | 188 | auto my_public_key_blob = std::make_unique(blob_size); 189 | 190 | if (nullptr == my_public_key_blob) { 191 | std::cerr << "Failed to allocate " << std::dec << blob_size << " bytes for public key!" << std::endl; 192 | return nullptr; 193 | } 194 | 195 | status = BCryptExportKey( 196 | keypair, 197 | nullptr, 198 | BCRYPT_ECCPUBLIC_BLOB, 199 | my_public_key_blob.get(), 200 | blob_size, 201 | (ULONG*)&blob_size, 202 | 0 203 | ); 204 | 205 | if (status != 0) { 206 | std::cerr << "Second BCryptExportKey failed: " << std::hex << status << std::endl; 207 | return nullptr; 208 | } 209 | 210 | return my_public_key_blob; 211 | } 212 | 213 | bool CreateEnclaveKeypair(void* enclave_base, uint8_t* public_key_blob) { 214 | PENCLAVE_ROUTINE new_keypair = (PENCLAVE_ROUTINE)GetProcAddress((HMODULE)enclave_base, "new_keypair"); 215 | 216 | if (nullptr == new_keypair) { 217 | std::cerr << "Failed to get new_keypair entry point! Error: " << std::hex << HRESULT_FROM_WIN32(GetLastError()) << std::endl; 218 | return false; 219 | } 220 | 221 | NewKeypairParams new_keypair_params = NewKeypairParams{ KeySize, public_key_blob }; 222 | void* result = nullptr; 223 | 224 | if (!CallEnclave(new_keypair, &new_keypair_params, TRUE, &result) || (HRESULT)result != S_OK) 225 | { 226 | std::cerr << "new_keypair failed: " << std::hex << (HRESULT)result << std::endl; 227 | return false; 228 | } 229 | 230 | return true; 231 | } 232 | 233 | std::unique_ptr GetAttestationReport(void* enclave_base) { 234 | PENCLAVE_ROUTINE generate_report = (PENCLAVE_ROUTINE)GetProcAddress((HMODULE)enclave_base, "generate_report"); 235 | 236 | if (nullptr == generate_report) { 237 | std::cerr << "Failed to get generate_report entry point! Error: " << std::hex << HRESULT_FROM_WIN32(GetLastError()) << std::endl; 238 | return nullptr; 239 | } 240 | 241 | GenerateReportParams generate_report_params = GenerateReportParams{ AllocateCallback }; 242 | void* result = nullptr; 243 | 244 | if (!CallEnclave(generate_report, &generate_report_params, TRUE, &result) || (HRESULT)result != S_OK) 245 | { 246 | std::cerr << "generate_report failed: " << std::hex << (HRESULT)result << std::endl; 247 | return nullptr; 248 | } 249 | 250 | std::cout << "Report generated! " << std::dec << generate_report_params.report_size << " bytes!" << std::endl; 251 | 252 | auto report_buffer = std::unique_ptr(generate_report_params.report); 253 | return report_buffer; 254 | } 255 | 256 | wil::unique_bcrypt_key DeriveSharedKey(BCRYPT_KEY_HANDLE keypair, uint8_t* public_key_blob) { 257 | BCRYPT_KEY_HANDLE public_key_handle = nullptr; 258 | 259 | NTSTATUS status = BCryptImportKeyPair( 260 | BCRYPT_ECDH_P256_ALG_HANDLE, 261 | nullptr, 262 | BCRYPT_ECCPUBLIC_BLOB, 263 | &public_key_handle, 264 | (PBYTE)public_key_blob, 265 | sizeof(BCRYPT_ECCKEY_BLOB) + ENCLAVE_REPORT_DATA_LENGTH, 266 | 0 267 | ); 268 | 269 | if (status != 0) 270 | { 271 | std::cerr << "BCryptImportKeyPair failed: " << std::hex << status << std::endl; 272 | return nullptr; 273 | } 274 | 275 | std::cout << "Public key imported successfully!" << std::endl; 276 | 277 | wil::unique_bcrypt_key agreed_secret = nullptr; 278 | 279 | status = BCryptSecretAgreement(keypair, public_key_handle, &agreed_secret, 0); 280 | 281 | if (status != 0) { 282 | std::cerr << "BCryptSecretAgreement failed: " << std::hex << status << std::endl; 283 | return nullptr; 284 | } 285 | 286 | BCryptBuffer buffer = { 287 | sizeof(BCRYPT_SHA256_ALGORITHM), 288 | KDF_HASH_ALGORITHM, 289 | (PVOID)BCRYPT_SHA256_ALGORITHM 290 | }; 291 | 292 | BCryptBufferDesc parameter_list = { 293 | BCRYPTBUFFER_VERSION, 294 | 1, 295 | &buffer 296 | }; 297 | 298 | AES256KeyBlob derived_key_blob = { 299 | { 300 | BCRYPT_KEY_DATA_BLOB_MAGIC, 301 | BCRYPT_KEY_DATA_BLOB_VERSION1, 302 | 32 303 | }, 304 | {} 305 | }; 306 | uint32_t bytes_needed = 0; 307 | 308 | status = BCryptDeriveKey(agreed_secret.get(), BCRYPT_KDF_HASH, ¶meter_list, derived_key_blob.key, sizeof(derived_key_blob.key), (ULONG*)&bytes_needed, 0); 309 | 310 | if (status != 0) { 311 | std::cerr << "BCryptDeriveKey failed: " << std::hex << status << std::endl; 312 | return nullptr; 313 | } 314 | 315 | wil::unique_bcrypt_key ephemeral_key = nullptr; 316 | 317 | status = BCryptImportKey(BCRYPT_AES_GCM_ALG_HANDLE, nullptr, BCRYPT_KEY_DATA_BLOB, &ephemeral_key, nullptr, 0, (uint8_t*)&derived_key_blob, sizeof(derived_key_blob), 0); 318 | 319 | if (status != 0) { 320 | std::cerr << "BCryptImportKey failed: " << std::hex << status << std::endl; 321 | return nullptr; 322 | } 323 | 324 | return ephemeral_key; 325 | } 326 | 327 | using BufferInfo = std::pair; 328 | BufferInfo EncryptMessage(BCRYPT_HANDLE ephemeral_key, std::wstring message, BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO& mode_info) { 329 | DumpBytes((uint8_t*)message.c_str(), message.size() * sizeof(wchar_t)); 330 | std::cout << std::endl; 331 | 332 | uint32_t bytes_needed = 0; 333 | 334 | NTSTATUS status = BCryptEncrypt( 335 | ephemeral_key, 336 | (PUCHAR)message.c_str(), 337 | message.size() * sizeof(wchar_t), 338 | &mode_info, 339 | nullptr, 340 | 0, 341 | nullptr, 342 | 0, 343 | (ULONG*)&bytes_needed, 344 | 0 345 | ); 346 | 347 | if (status != 0) { 348 | std::cerr << "BCryptEncrypt failed to get size: " << std::hex << status << std::endl; 349 | return std::make_pair(nullptr, 0); 350 | } 351 | 352 | auto ciphertext = new uint8_t[bytes_needed]; 353 | 354 | if (ciphertext == nullptr) { 355 | std::cerr << "Failed to allocate buffer for ciphertext" << std::endl; 356 | return std::make_pair(nullptr, 0); 357 | } 358 | 359 | status = BCryptEncrypt( 360 | ephemeral_key, 361 | (PUCHAR)message.c_str(), 362 | message.size() * sizeof(WCHAR), 363 | &mode_info, 364 | nullptr, 365 | 0, 366 | ciphertext, 367 | bytes_needed, 368 | (ULONG*)&bytes_needed, 369 | 0 370 | ); 371 | 372 | if (status != 0) { 373 | std::cerr << "BCryptEncrypt failed to encrypt: " << std::hex << status << std::endl; 374 | return std::make_pair(nullptr, 0); 375 | } 376 | 377 | return std::make_pair(ciphertext, bytes_needed); 378 | } 379 | 380 | std::wstring DecryptMessage(void* enclave_base, DecryptDataParams &decrypt_data_params) { 381 | PENCLAVE_ROUTINE decrypt_data = (PENCLAVE_ROUTINE)GetProcAddress((HMODULE)enclave_base, "decrypt_data"); 382 | 383 | if (nullptr == decrypt_data) { 384 | std::cerr << "Could not get at least one of the enclave routines!" << std::endl; 385 | return nullptr; 386 | } 387 | 388 | void* result = nullptr; 389 | 390 | if (!CallEnclave(decrypt_data, &decrypt_data_params, TRUE, &result) || (HRESULT)result != S_OK) 391 | { 392 | std::cerr << "decrypt_data failed: " << std::hex << (HRESULT)result << std::endl; 393 | return nullptr; 394 | } 395 | 396 | std::wstring decrypted_data((const wchar_t*)decrypt_data_params.decrypted_data, decrypt_data_params.decrypted_size / sizeof(wchar_t)); 397 | 398 | delete[] decrypt_data_params.decrypted_data; 399 | decrypt_data_params.decrypted_data = nullptr; 400 | 401 | return decrypted_data; 402 | } 403 | 404 | int wmain(int argc, wchar_t **argv) 405 | { 406 | if (argc != 3) 407 | { 408 | std::cerr << "Usage: EnclaveLoader.exe " << std::endl; 409 | return -1; 410 | } 411 | 412 | std::wstring message = argv[2]; 413 | 414 | Enclave enclave_base(StartEnclave(argv[1]), &CleanupEnclave); 415 | 416 | if (nullptr == enclave_base) { 417 | return -1; 418 | } 419 | 420 | std::cout << "Rust enclave created and initialized!" << std::endl; 421 | 422 | std::cout << "Creating host keypair..." << std::endl; 423 | 424 | auto keypair = GenerateECDHKeypair(); 425 | 426 | if (!keypair.is_valid()) { 427 | return -1; 428 | } 429 | 430 | auto my_public_key_blob = ExportPublicKey(keypair.get()); 431 | 432 | std::cout << "Creating new enclave key and providing host public key..." << std::endl; 433 | 434 | if (!CreateEnclaveKeypair(enclave_base.get(), my_public_key_blob.get())) { 435 | return -1; 436 | } 437 | 438 | std::cout << "New keypair created!" << std::endl; 439 | 440 | auto report_buffer = GetAttestationReport(enclave_base.get()); 441 | 442 | VBS_ENCLAVE_REPORT *report = (VBS_ENCLAVE_REPORT *)((VBS_ENCLAVE_REPORT_PKG_HEADER *)report_buffer.get() + 1); 443 | 444 | /* This is where you would validate the attestation report before using the public key! */ 445 | 446 | std::cout << "Beep boop beep, validating attestation report... (for pretend)" << std::endl; 447 | 448 | Sleep(1000); 449 | 450 | std::cout << "Enclave is validated!" << std::endl; 451 | 452 | auto public_key_blob = std::make_unique(sizeof(BCRYPT_ECCKEY_BLOB) + ENCLAVE_REPORT_DATA_LENGTH); 453 | BCRYPT_ECCKEY_BLOB *public_key_blob_header = (BCRYPT_ECCKEY_BLOB *)public_key_blob.get(); 454 | 455 | public_key_blob_header->dwMagic = BCRYPT_ECDH_PUBLIC_P256_MAGIC; 456 | public_key_blob_header->cbKey = ENCLAVE_REPORT_DATA_LENGTH / 2; 457 | 458 | memcpy(public_key_blob_header + 1, report->EnclaveData, ENCLAVE_REPORT_DATA_LENGTH); 459 | 460 | std::cout << "Deriving shared key..." << std::endl; 461 | 462 | auto ephemeral_key = DeriveSharedKey(keypair.get(), public_key_blob.get()); 463 | 464 | std::cout << "Successfully derived shared ephemeral key!" << std::endl; 465 | 466 | std::cout << "Encrypting the message:" << std::endl; 467 | 468 | BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO mode_info{}; 469 | BCRYPT_INIT_AUTH_MODE_INFO(mode_info); 470 | 471 | uint8_t iv[12] = {}; 472 | 473 | // Never use a static IV for AES-GCM unless you only ever use the key once! 474 | if (BCryptGenRandom(BCRYPT_RNG_ALG_HANDLE, iv, sizeof(iv), 0) != 0) { 475 | std::cerr << "BCryptGenRandom failed!" << std::endl; 476 | return -1; 477 | } 478 | 479 | mode_info.pbNonce = iv; 480 | mode_info.cbNonce = sizeof(iv); 481 | 482 | uint8_t tag[16] = {}; 483 | mode_info.pbTag = tag; 484 | mode_info.cbTag = sizeof(tag); 485 | 486 | auto ciphertext_info = EncryptMessage(ephemeral_key.get(), message, mode_info); 487 | 488 | auto ciphertext = std::unique_ptr(ciphertext_info.first); 489 | 490 | size_t bytes_needed = ciphertext_info.second; 491 | 492 | if (ciphertext == nullptr) { 493 | return -1; 494 | } 495 | 496 | DecryptDataParams decrypt_data_params = { 497 | AllocateCallback, 498 | bytes_needed, 499 | ciphertext.get(), 500 | mode_info.cbNonce, 501 | mode_info.pbNonce, 502 | mode_info.cbTag, 503 | mode_info.pbTag 504 | }; 505 | auto decrypted_data = DecryptMessage(enclave_base.get(), decrypt_data_params); 506 | 507 | std::cout << "Data decrypted! Message is:" << std::endl; 508 | DumpBytes((uint8_t*)decrypted_data.c_str(), decrypted_data.size() * sizeof(wchar_t)); 509 | std::cout << std::endl; 510 | 511 | if (message.compare(decrypted_data) == 0) { 512 | std::cout << "The message round-tripped!" << std::endl; 513 | } 514 | else { 515 | std::cerr << "The message did not round-trip!" << std::endl; 516 | return -1; 517 | } 518 | 519 | return 0; 520 | } -------------------------------------------------------------------------------- /sample/SampleHost/SampleHost.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.13.35723.152 d17.13 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleHost", "SampleHost.vcxproj", "{FA00898C-AC64-49AC-9092-05D4520467A9}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {FA00898C-AC64-49AC-9092-05D4520467A9}.Debug|x64.ActiveCfg = Debug|x64 17 | {FA00898C-AC64-49AC-9092-05D4520467A9}.Debug|x64.Build.0 = Debug|x64 18 | {FA00898C-AC64-49AC-9092-05D4520467A9}.Debug|x86.ActiveCfg = Debug|Win32 19 | {FA00898C-AC64-49AC-9092-05D4520467A9}.Debug|x86.Build.0 = Debug|Win32 20 | {FA00898C-AC64-49AC-9092-05D4520467A9}.Release|x64.ActiveCfg = Release|x64 21 | {FA00898C-AC64-49AC-9092-05D4520467A9}.Release|x64.Build.0 = Release|x64 22 | {FA00898C-AC64-49AC-9092-05D4520467A9}.Release|x86.ActiveCfg = Release|Win32 23 | {FA00898C-AC64-49AC-9092-05D4520467A9}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {D61D0C64-C8B6-4047-97CF-77D648A4B20A} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /sample/SampleHost/SampleHost.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 17.0 23 | Win32Proj 24 | {fa00898c-ac64-49ac-9092-05d4520467a9} 25 | SampleHost 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Level3 76 | true 77 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 78 | true 79 | 80 | 81 | Console 82 | true 83 | 84 | 85 | 86 | 87 | Level3 88 | true 89 | true 90 | true 91 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | true 93 | 94 | 95 | Console 96 | true 97 | true 98 | true 99 | 100 | 101 | 102 | 103 | Level3 104 | true 105 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | true 107 | MultiThreadedDebug 108 | 109 | 110 | Console 111 | true 112 | onecore.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) 113 | 114 | 115 | 116 | 117 | Level3 118 | true 119 | true 120 | true 121 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 122 | true 123 | 124 | 125 | Console 126 | true 127 | true 128 | true 129 | onecore.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /sample/SampleHost/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /sample/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::io::Read; 3 | use std::path::Path; 4 | use std::process::Command; 5 | use std::str; 6 | 7 | const PROGRAM_FILES_X86: &str = "ProgramFiles(x86)"; 8 | const VCTOOLS_DEFAULT_PATH: &str = "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt"; 9 | const MSVC_PATH: &str = "VC\\Tools\\MSVC"; 10 | const ENCLAVE_LIB_PATH: &str = "lib\\x64\\enclave"; 11 | const UCRT_LIB_PATH: &str = "ucrt_enclave\\x64\\ucrt.lib"; 12 | 13 | const SDK_SCRIPT: &str = r#"& { 14 | $kits_root_10 = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows Kits\Installed Roots\" -Name KitsRoot10).KitsRoot10 15 | $sdk_version = (Get-ChildItem -Path "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows Kits\Installed Roots\" | Sort-Object -Descending)[0] | Split-Path -Leaf 16 | Write-Host "$($kits_root_10)Lib\$sdk_version" 17 | } 18 | "#; 19 | 20 | fn main() -> Result<(), Box> { 21 | println!("cargo::rustc-link-arg=/OPT:REF,ICF"); 22 | println!("cargo::rustc-link-arg=/ENTRY:dllmain"); 23 | println!("cargo::rustc-link-arg=/MERGE:.edata=.rdata"); 24 | println!("cargo::rustc-link-arg=/MERGE:.rustc=.data"); 25 | println!("cargo::rustc-link-arg=/INTEGRITYCHECK"); 26 | println!("cargo::rustc-link-arg=/enclave"); 27 | println!("cargo::rustc-link-arg=/GUARD:MIXED"); 28 | println!("cargo::rustc-link-arg=/include:__enclave_config"); 29 | 30 | let program_files_x86 = 31 | env::var(PROGRAM_FILES_X86).expect("Program Files (x86) path not in environment variables"); 32 | 33 | let powershell_output = Command::new("powershell.exe") 34 | .arg(SDK_SCRIPT) 35 | .output()? 36 | .stdout; 37 | let sdk_path = str::from_utf8(&powershell_output)?.trim(); 38 | 39 | println!("{}", sdk_path); 40 | 41 | let vswhere = 42 | Path::new(&program_files_x86).join("Microsoft Visual Studio\\Installer\\vswhere.exe"); 43 | 44 | let vswhere_output = Command::new(vswhere) 45 | .args([ 46 | "-latest", 47 | "-products", 48 | "*", 49 | "-requires", 50 | "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", 51 | "-property", 52 | "installationPath", 53 | ]) 54 | .output()? 55 | .stdout; 56 | 57 | let install_path = Path::new(str::from_utf8(&vswhere_output)?.trim()); 58 | 59 | let mut default_path = String::new(); 60 | std::fs::File::open(install_path.join(VCTOOLS_DEFAULT_PATH)) 61 | .expect("Could not open Microsoft.VCToolsVersion.default.txt") 62 | .read_to_string(&mut default_path)?; 63 | 64 | let msvc = install_path.join(MSVC_PATH).join(default_path.trim()); 65 | 66 | let enclave_lib_path = msvc.join(ENCLAVE_LIB_PATH); 67 | 68 | println!( 69 | "cargo::rustc-link-arg={}", 70 | Path::new(sdk_path) 71 | .join(UCRT_LIB_PATH) 72 | .to_str() 73 | .expect("Couldn't make string from ucrt.lib path") 74 | ); 75 | // libvcruntime must come before vertdll or there will be duplicate external errors 76 | println!( 77 | "cargo::rustc-link-arg={}", 78 | enclave_lib_path 79 | .join("libvcruntime.lib") 80 | .to_str() 81 | .expect("Couldn't make string from libvcruntime.lib path") 82 | ); 83 | println!( 84 | "cargo::rustc-link-arg={}", 85 | enclave_lib_path 86 | .join("libcmt.lib") 87 | .to_str() 88 | .expect("Couldn't make string from libcmt.lib path") 89 | ); 90 | println!("cargo::rustc-link-arg=vertdll.lib"); 91 | println!("cargo::rustc-link-arg=bcrypt.lib"); 92 | 93 | Ok(()) 94 | } 95 | -------------------------------------------------------------------------------- /sample/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" -------------------------------------------------------------------------------- /sample/src/bcrypt.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::c_void; 2 | 3 | use alloc::vec::Vec; 4 | use vbs_enclave::error::{hresult_from_nt, EnclaveError}; 5 | use windows_sys::Win32::{ 6 | Foundation::STATUS_SUCCESS, 7 | Security::Cryptography::{ 8 | BCryptBuffer, BCryptBufferDesc, BCryptDecrypt, BCryptDeriveKey, BCryptDestroyKey, 9 | BCryptExportKey, BCryptFinalizeKeyPair, BCryptGenerateKeyPair, BCryptImportKey, 10 | BCryptImportKeyPair, BCryptSecretAgreement, BCRYPTBUFFER_VERSION, BCRYPT_ALG_HANDLE, 11 | BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO, BCRYPT_KEY_DATA_BLOB_HEADER, BCRYPT_KEY_HANDLE, 12 | }, 13 | }; 14 | 15 | pub const AES256_KEY_SIZE: usize = 32; 16 | 17 | pub trait KeyBlob { 18 | fn size(&self) -> u32; 19 | } 20 | 21 | #[repr(C)] 22 | pub struct Aes256KeyBlob { 23 | pub header: BCRYPT_KEY_DATA_BLOB_HEADER, 24 | pub key_material: [u8; AES256_KEY_SIZE], 25 | } 26 | 27 | impl KeyBlob for Aes256KeyBlob { 28 | fn size(&self) -> u32 { 29 | size_of::() as u32 30 | } 31 | } 32 | 33 | pub fn generate_keypair( 34 | algorithm: BCRYPT_ALG_HANDLE, 35 | key_size: u32, 36 | ) -> Result { 37 | let mut key_handle = core::ptr::null_mut::(); 38 | let key_handle_ptr = &mut key_handle as *mut *mut c_void; 39 | 40 | unsafe { 41 | // No flags defined. dwflags is always 0. 42 | match BCryptGenerateKeyPair(algorithm, key_handle_ptr, key_size, 0u32) { 43 | 0 => Ok(key_handle), 44 | e => Err(EnclaveError { 45 | hresult: hresult_from_nt(e), 46 | }), 47 | } 48 | } 49 | } 50 | 51 | pub fn finalize_keypair(key: BCRYPT_KEY_HANDLE) -> Result<(), EnclaveError> { 52 | unsafe { 53 | // No flags defined. dwflags is always 0. 54 | match BCryptFinalizeKeyPair(key, 0) { 55 | 0 => Ok(()), 56 | e => Err(EnclaveError { 57 | hresult: hresult_from_nt(e), 58 | }), 59 | } 60 | } 61 | } 62 | 63 | pub fn import_keypair( 64 | algorithm: BCRYPT_ALG_HANDLE, 65 | import_key: Option, 66 | blob_type: *const u16, 67 | input: &[u8], 68 | ) -> Result { 69 | let Ok(input_size) = u32::try_from(input.len()) else { 70 | return Err(EnclaveError::invalid_arg()); 71 | }; 72 | 73 | let mut key_handle = core::ptr::null_mut::(); 74 | let key_handle_ptr = &mut key_handle as *mut *mut c_void; 75 | 76 | unsafe { 77 | // No flags defined, dwflags is always 0. 78 | match BCryptImportKeyPair( 79 | algorithm, 80 | if let Some(i) = import_key { 81 | i 82 | } else { 83 | core::ptr::null_mut() 84 | }, 85 | blob_type, 86 | key_handle_ptr, 87 | input.as_ptr() as *const _, 88 | input_size, 89 | 0, 90 | ) { 91 | STATUS_SUCCESS => Ok(key_handle), 92 | e => Err(EnclaveError { 93 | hresult: hresult_from_nt(e), 94 | }), 95 | } 96 | } 97 | } 98 | 99 | pub fn secret_agreement( 100 | private_key: BCRYPT_KEY_HANDLE, 101 | public_key: BCRYPT_KEY_HANDLE, 102 | ) -> Result { 103 | let mut key_handle = core::ptr::null_mut::(); 104 | let key_handle_ptr = &mut key_handle as *mut *mut c_void; 105 | 106 | unsafe { 107 | // No flags defined, dwflags is always 0. 108 | match BCryptSecretAgreement(private_key, public_key, key_handle_ptr, 0) { 109 | STATUS_SUCCESS => Ok(key_handle), 110 | e => Err(EnclaveError { 111 | hresult: hresult_from_nt(e), 112 | }), 113 | } 114 | } 115 | } 116 | 117 | pub fn derive_key( 118 | shared_secret: BCRYPT_KEY_HANDLE, 119 | kdf: *const u16, 120 | parameters: &mut [BCryptBuffer], 121 | ) -> Result, EnclaveError> { 122 | let mut derived_key: Vec = Vec::new(); 123 | 124 | let parameter_list = BCryptBufferDesc { 125 | ulVersion: BCRYPTBUFFER_VERSION, 126 | cBuffers: 1, 127 | pBuffers: parameters.as_mut_ptr() as *mut _, 128 | }; 129 | 130 | let mut size_needed = 0u32; 131 | 132 | unsafe { 133 | // No flags defined, dwflags is always 0. 134 | match BCryptDeriveKey( 135 | shared_secret, 136 | kdf, 137 | ¶meter_list as *const _, 138 | core::ptr::null_mut(), 139 | 0u32, 140 | &mut size_needed as *mut u32, 141 | 0, 142 | ) { 143 | STATUS_SUCCESS => {} 144 | e => { 145 | return Err(EnclaveError { 146 | hresult: hresult_from_nt(e), 147 | }) 148 | } 149 | } 150 | } 151 | 152 | derived_key.resize(size_needed as usize, 0u8); 153 | 154 | unsafe { 155 | // No flags defined, dwflags is always 0. 156 | match BCryptDeriveKey( 157 | shared_secret, 158 | kdf, 159 | ¶meter_list as *const _, 160 | derived_key.as_mut_ptr() as *mut _, 161 | derived_key.len() as u32, 162 | &mut size_needed as *mut u32, 163 | 0, 164 | ) { 165 | STATUS_SUCCESS => Ok(derived_key), 166 | e => Err(EnclaveError { 167 | hresult: hresult_from_nt(e), 168 | }), 169 | } 170 | } 171 | } 172 | 173 | pub fn import_key( 174 | algorithm: BCRYPT_ALG_HANDLE, 175 | import_key: Option, 176 | blob_type: *const u16, 177 | input: &mut dyn KeyBlob, 178 | ) -> Result { 179 | let mut key_handle = core::ptr::null_mut::(); 180 | let key_handle_ptr = &mut key_handle as *mut *mut c_void; 181 | 182 | unsafe { 183 | // No flags defined, dwflags is always 0. 184 | // Ignoring the key object parameters, this sample will never use them. 185 | match BCryptImportKey( 186 | algorithm, 187 | if let Some(i) = import_key { 188 | i 189 | } else { 190 | core::ptr::null_mut() 191 | }, 192 | blob_type, 193 | key_handle_ptr, 194 | core::ptr::null_mut(), 195 | 0, 196 | input as *mut _ as *mut _, 197 | input.size() as u32, 198 | 0, 199 | ) { 200 | STATUS_SUCCESS => Ok(key_handle), 201 | e => Err(EnclaveError { 202 | hresult: hresult_from_nt(e), 203 | }), 204 | } 205 | } 206 | } 207 | 208 | pub fn export_key( 209 | key: BCRYPT_KEY_HANDLE, 210 | export_key: Option, 211 | blob_type: *const u16, 212 | ) -> Result, EnclaveError> { 213 | let mut key_blob: Vec = Vec::new(); 214 | let mut bytes_needed = 0u32; 215 | 216 | unsafe { 217 | // No flags defined, dwflags is always 0. 218 | match BCryptExportKey( 219 | key, 220 | if let Some(e) = export_key { 221 | e 222 | } else { 223 | core::ptr::null_mut() 224 | }, 225 | blob_type, 226 | core::ptr::null_mut(), 227 | 0, 228 | &mut bytes_needed, 229 | 0, 230 | ) { 231 | STATUS_SUCCESS => {} 232 | e => { 233 | return Err(EnclaveError { 234 | hresult: hresult_from_nt(e), 235 | }) 236 | } 237 | } 238 | } 239 | 240 | key_blob.resize(bytes_needed as usize, 0u8); 241 | 242 | unsafe { 243 | match BCryptExportKey( 244 | key, 245 | if let Some(e) = export_key { 246 | e 247 | } else { 248 | core::ptr::null_mut() 249 | }, 250 | blob_type, 251 | key_blob.as_mut_ptr(), 252 | key_blob.len() as u32, 253 | &mut bytes_needed, 254 | 0, 255 | ) { 256 | STATUS_SUCCESS => Ok(key_blob), 257 | e => Err(EnclaveError { 258 | hresult: hresult_from_nt(e), 259 | }), 260 | } 261 | } 262 | } 263 | 264 | pub fn decrypt_aes_gcm( 265 | key: BCRYPT_KEY_HANDLE, 266 | encrypted_data: &[u8], 267 | padding_info: &BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO, 268 | ) -> Result, EnclaveError> { 269 | let mut decrypted_size = 0u32; 270 | 271 | unsafe { 272 | match BCryptDecrypt( 273 | key, 274 | encrypted_data.as_ptr(), 275 | encrypted_data.len() as u32, 276 | padding_info as *const _ as *const _, 277 | core::ptr::null_mut(), 278 | 0, 279 | core::ptr::null_mut(), 280 | 0, 281 | &mut decrypted_size, 282 | 0, 283 | ) { 284 | STATUS_SUCCESS => {} 285 | e => { 286 | return Err(EnclaveError { 287 | hresult: hresult_from_nt(e), 288 | }) 289 | } 290 | } 291 | } 292 | 293 | let mut decrypted_data: Vec = Vec::new(); 294 | decrypted_data.resize(decrypted_size as usize, 0u8); 295 | 296 | unsafe { 297 | match BCryptDecrypt( 298 | key, 299 | encrypted_data.as_ptr(), 300 | encrypted_data.len() as u32, 301 | padding_info as *const _ as *const _, 302 | core::ptr::null_mut(), 303 | 0, 304 | decrypted_data.as_mut_ptr() as *mut _, 305 | decrypted_data.len() as u32, 306 | &mut decrypted_size, 307 | 0, 308 | ) { 309 | STATUS_SUCCESS => Ok(decrypted_data), 310 | e => Err(EnclaveError { 311 | hresult: hresult_from_nt(e), 312 | }), 313 | } 314 | } 315 | } 316 | 317 | pub fn destroy_key(key: BCRYPT_KEY_HANDLE) -> Result<(), EnclaveError> { 318 | unsafe { 319 | match BCryptDestroyKey(key) { 320 | STATUS_SUCCESS => Ok(()), 321 | e => Err(EnclaveError { 322 | hresult: hresult_from_nt(e), 323 | }), 324 | } 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /sample/src/ffi.rs: -------------------------------------------------------------------------------- 1 | use core::ffi::c_void; 2 | 3 | use crate::params::{DecryptDataParams, GenerateReportParams, NewKeypairParams}; 4 | use crate::{ 5 | decrypt_data_internal, generate_report_internal, new_keypair_internal, 6 | ECDH_256_PUBLIC_BLOB_SIZE, 7 | }; 8 | use alloc::vec::Vec; 9 | use hex_literal::hex; 10 | use vbs_enclave::enclaveapi::{call_enclave, EnclaveRoutineInvocation}; 11 | use vbs_enclave::error::{EnclaveError, HRESULT, S_OK}; 12 | use vbs_enclave::is_valid_vtl0; 13 | use vbs_enclave::types::LPENCLAVE_ROUTINE; 14 | use vbs_enclave::winenclave::{ 15 | ImageEnclaveConfig, IMAGE_ENCLAVE_FLAG_PRIMARY_IMAGE, IMAGE_ENCLAVE_MINIMUM_CONFIG_SIZE, 16 | }; 17 | 18 | // You should only enable debug in debug builds, or it can allow someone to 19 | // access your enclave in VTL0. 20 | // See: https://learn.microsoft.com/en-us/windows/win32/trusted-execution/vbs-enclaves-dev-guide#step-4-debugging-vbs-enclaves 21 | #[cfg(debug_assertions)] 22 | const ENCLAVE_CONFIG_POLICY_FLAGS: u32 = vbs_enclave::winenclave::IMAGE_ENCLAVE_POLICY_DEBUGGABLE; 23 | #[cfg(not(debug_assertions))] 24 | const ENCLAVE_CONFIG_POLICY_FLAGS: u32 = 0; 25 | 26 | // This structure is necessary for the enclave to load correctly. 27 | #[no_mangle] 28 | #[allow( 29 | non_upper_case_globals, 30 | reason = "__enclave_config is a special name required for enclaves" 31 | )] 32 | pub static __enclave_config: ImageEnclaveConfig = ImageEnclaveConfig { 33 | size: size_of::() as u32, 34 | // This value just points to the the offset of enclave_flags 35 | minimum_required_config_size: IMAGE_ENCLAVE_MINIMUM_CONFIG_SIZE, 36 | policy_flags: ENCLAVE_CONFIG_POLICY_FLAGS, 37 | number_of_imports: 0, 38 | import_list: 0, 39 | import_entry_size: 0, 40 | family_id: hex!( 41 | "fefe0000 00000000" 42 | "00000000 00000000" 43 | ), 44 | image_id: hex!( 45 | "01010000 00000000" 46 | "00000000 00000000" 47 | ), 48 | image_version: 0, 49 | security_version: 0, 50 | enclave_size: 0x1000_0000, 51 | number_of_threads: 16, 52 | enclave_flags: IMAGE_ENCLAVE_FLAG_PRIMARY_IMAGE, 53 | }; 54 | 55 | #[no_mangle] 56 | extern "C" fn new_keypair(params_vtl0: *const NewKeypairParams) -> HRESULT { 57 | // The first thing that needs to happen is validating the NewKeypairParams 58 | // pointer is fully within vtl0 memory, then copy it into vtl1 memory. 59 | // This prevents time-of-check/time-of-use bugs that can arise if you 60 | // read values that are residing within vtl0. 61 | let params_vtl1 = if is_valid_vtl0(params_vtl0, size_of::()) { 62 | unsafe { *params_vtl0 } 63 | } else { 64 | return EnclaveError::invalid_arg().into(); 65 | }; 66 | 67 | // Next, the public key buffer also lives in vtl0, so it needs to be 68 | // validated and copied into vtl1 as well. 69 | let mut public_key_blob = Vec::new(); 70 | public_key_blob.resize(ECDH_256_PUBLIC_BLOB_SIZE, 0u8); 71 | 72 | if is_valid_vtl0( 73 | (¶ms_vtl1).public_key_blob as *const u8 as *const _, 74 | ECDH_256_PUBLIC_BLOB_SIZE, 75 | ) { 76 | unsafe { 77 | public_key_blob 78 | .as_mut_slice() 79 | .copy_from_slice(core::slice::from_raw_parts( 80 | params_vtl1.public_key_blob, 81 | ECDH_256_PUBLIC_BLOB_SIZE, 82 | )); 83 | } 84 | } else { 85 | return EnclaveError::invalid_arg().into(); 86 | } 87 | 88 | // Finally, we can call the internal function that only operates on 89 | // safe Rust objects. 90 | match new_keypair_internal(params_vtl1.key_size, &public_key_blob) { 91 | Ok(()) => S_OK, 92 | Err(e) => e.into(), 93 | } 94 | } 95 | 96 | #[no_mangle] 97 | extern "C" fn generate_report(params_vtl0: *mut GenerateReportParams) -> HRESULT { 98 | // The first thing that needs to happen is validating the GenerateReportParams 99 | // pointer is fully within vtl0 memory, then copy it into vtl1 memory. 100 | // This prevents time-of-check/time-of-use bugs that can arise if you 101 | // read values that are residing within vtl0. 102 | let params_vtl1 = unsafe { 103 | if is_valid_vtl0(params_vtl0, size_of::()) { 104 | *params_vtl0.clone() 105 | } else { 106 | return EnclaveError::invalid_arg().into(); 107 | } 108 | }; 109 | 110 | // Next, the callback pointer in the structure needs to be validated, otherwise 111 | // CallEnclave can call a function within our vtl1 enclave and that is bad. 112 | // Note that this pointer is checked in the vtl1 struct, not the vtl0 struct, 113 | // because if it were checked in the vtl0 struct, an attacker could change it 114 | // after it is checked but before it is used. 115 | if !is_valid_vtl0(params_vtl1.allocate_callback as *const c_void, 1) { 116 | return EnclaveError::invalid_arg().into(); 117 | } 118 | 119 | // The internal function operated only on safe Rust objects. Any unsafe code 120 | // is in the FFI function like this one, or in a wrapper function for bcrypt 121 | // or enclave APIs. 122 | let report = match generate_report_internal() { 123 | Ok(v) => v, 124 | Err(e) => return e.into(), 125 | }; 126 | 127 | // Once we have the report vector, we call the vtl0 allocation callback 128 | // and validate that the pointer we get back is enclave-external before copying the 129 | // data out to it. 130 | let invocation = unsafe { 131 | EnclaveRoutineInvocation::new( 132 | params_vtl1.allocate_callback as LPENCLAVE_ROUTINE, 133 | report.len() as *const _, 134 | ) 135 | }; 136 | let allocation: *mut u8 = match call_enclave(invocation, true) { 137 | Ok(v) => v as *mut u8, 138 | Err(e) => return e.into(), 139 | }; 140 | 141 | if is_valid_vtl0(allocation, report.len()) { 142 | unsafe { 143 | let data_vtl0: &mut [u8] = core::slice::from_raw_parts_mut(allocation, report.len()); 144 | data_vtl0.copy_from_slice(&report); 145 | } 146 | } else { 147 | return EnclaveError::invalid_arg().into(); 148 | } 149 | 150 | // Finally, now that we have copied the buffer out, we set the length and pointer 151 | // in the vtl0 structure (which we already know is a valid allocation) so that the 152 | // host process can continue. 153 | unsafe { 154 | (*params_vtl0).report_size = report.len(); 155 | (*params_vtl0).report = allocation; 156 | } 157 | 158 | S_OK 159 | } 160 | 161 | #[no_mangle] 162 | extern "C" fn decrypt_data(params_vtl0: *mut DecryptDataParams) -> HRESULT { 163 | // The first thing that needs to happen is validating the DecryptDataParams 164 | // pointer is fully within vtl0 memory, then copy it into vtl1 memory. 165 | // This prevents time-of-check/time-of-use bugs that can arise if you 166 | // read values that are residing within vtl0. 167 | let params_vtl1 = if is_valid_vtl0(params_vtl0, size_of::()) { 168 | unsafe { *params_vtl0.clone() } 169 | } else { 170 | return EnclaveError::invalid_arg().into(); 171 | }; 172 | 173 | // Next, the callback pointer in the structure needs to be validated, otherwise 174 | // CallEnclave can call a function within our vtl1 enclave and that is bad. 175 | // Note that this pointer is checked in the vtl1 struct, not the vtl0 struct, 176 | // because if it were checked in the vtl0 struct, an attacker could change it 177 | // after it is checked but before it is used. 178 | if !is_valid_vtl0(params_vtl1.allocate_callback as *const c_void, 1) { 179 | return EnclaveError::invalid_arg().into(); 180 | } 181 | 182 | // The encrypted data buffer also lives in vtl0, so it needs to be 183 | // validated and copied into vtl1 as well. 184 | let mut encrypted_data: Vec = Vec::new(); 185 | encrypted_data.resize(params_vtl1.encrypted_size, 0u8); 186 | 187 | if is_valid_vtl0((¶ms_vtl1).encrypted_data, (¶ms_vtl1).encrypted_size) { 188 | unsafe { 189 | encrypted_data 190 | .as_mut_slice() 191 | .copy_from_slice(core::slice::from_raw_parts( 192 | params_vtl1.encrypted_data, 193 | params_vtl1.encrypted_size, 194 | )); 195 | } 196 | } else { 197 | return EnclaveError::invalid_arg().into(); 198 | } 199 | 200 | // The initialization vector buffer also lives in vtl0, so it needs to be 201 | // validated and copied into vtl1 as well. 202 | let mut iv: Vec = Vec::new(); 203 | iv.resize(params_vtl1.iv_size, 0u8); 204 | 205 | if is_valid_vtl0((¶ms_vtl1).iv, (¶ms_vtl1).iv_size) { 206 | unsafe { 207 | iv.as_mut_slice() 208 | .copy_from_slice(core::slice::from_raw_parts( 209 | params_vtl1.iv, 210 | params_vtl1.iv_size, 211 | )); 212 | } 213 | } else { 214 | return EnclaveError::invalid_arg().into(); 215 | } 216 | 217 | // The authentication tag buffer also lives in vtl0, so it needs to be 218 | // validated and copied into vtl1 as well. 219 | let mut tag: Vec = Vec::new(); 220 | tag.resize(params_vtl1.tag_size, 0u8); 221 | 222 | if is_valid_vtl0((¶ms_vtl1).tag, (¶ms_vtl1).tag_size) { 223 | unsafe { 224 | tag.as_mut_slice() 225 | .copy_from_slice(core::slice::from_raw_parts( 226 | params_vtl1.tag, 227 | params_vtl1.tag_size, 228 | )); 229 | } 230 | } else { 231 | return EnclaveError::invalid_arg().into(); 232 | } 233 | 234 | // The internal function operated only on safe Rust objects. Any unsafe code 235 | // is in the FFI function like this one, or in a wrapper function for bcrypt 236 | // or enclave APIs. 237 | let decrypted_data = match decrypt_data_internal(&encrypted_data, &mut iv, &mut tag) { 238 | Ok(v) => v, 239 | Err(e) => return e.into(), 240 | }; 241 | 242 | // Once we have the plaintext vector, we call the vtl0 allocation callback 243 | // and validate that the pointer we get back is valid before copying the 244 | // data out to it. 245 | let invocation = unsafe { 246 | EnclaveRoutineInvocation::new( 247 | params_vtl1.allocate_callback as LPENCLAVE_ROUTINE, 248 | decrypted_data.len() as *const _, 249 | ) 250 | }; 251 | let allocation: *mut u8 = match call_enclave(invocation, true) { 252 | Ok(v) => v as *mut u8, 253 | Err(e) => return e.into(), 254 | }; 255 | 256 | if is_valid_vtl0(allocation, decrypted_data.len()) { 257 | unsafe { 258 | let data_vtl0: &mut [u8] = 259 | core::slice::from_raw_parts_mut(allocation, decrypted_data.len()); 260 | data_vtl0.copy_from_slice(&decrypted_data); 261 | } 262 | } else { 263 | return EnclaveError::invalid_arg().into(); 264 | } 265 | 266 | // Finally, now that we have copied the buffer out, we set the length and pointer 267 | // in the vtl0 structure (which we already know is a valid allocation) so that the 268 | // host process can continue. 269 | unsafe { 270 | (*params_vtl0).decrypted_size = decrypted_data.len(); 271 | (*params_vtl0).decrypted_data = allocation; 272 | } 273 | 274 | S_OK 275 | } 276 | -------------------------------------------------------------------------------- /sample/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | extern crate alloc; 3 | use alloc::vec::Vec; 4 | 5 | use spin::Mutex; 6 | use vbs_enclave::{error::EnclaveError, winenclave::get_attestation_report}; 7 | 8 | use windows_sys::Win32::{ 9 | Security::Cryptography::{ 10 | BCryptBuffer, BCRYPT_AES_GCM_ALG_HANDLE, BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO, 11 | BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION, BCRYPT_ECCKEY_BLOB, BCRYPT_ECCPUBLIC_BLOB, 12 | BCRYPT_ECDH_P256_ALG_HANDLE, BCRYPT_KDF_HASH, BCRYPT_KEY_DATA_BLOB, 13 | BCRYPT_KEY_DATA_BLOB_HEADER, BCRYPT_KEY_DATA_BLOB_MAGIC, BCRYPT_KEY_DATA_BLOB_VERSION1, 14 | BCRYPT_KEY_HANDLE, BCRYPT_SHA256_ALGORITHM, KDF_HASH_ALGORITHM, 15 | }, 16 | System::Environment::ENCLAVE_REPORT_DATA_LENGTH, 17 | }; 18 | 19 | mod bcrypt; 20 | mod ffi; 21 | mod params; 22 | 23 | // A key handle is actually an alias of *const c_void but that 24 | // isn't considered thread safe. Since it's an opaque handle, 25 | // it's fine to convert it to a usize when storing it. 26 | static KEYPAIR: Mutex = Mutex::new(0); 27 | static KEY: Mutex = Mutex::new(0); 28 | 29 | const ECDH_256_PUBLIC_BLOB_SIZE: usize = 30 | size_of::() + ENCLAVE_REPORT_DATA_LENGTH as usize; 31 | 32 | fn new_keypair_internal(key_size: u32, public_key_blob: &[u8]) -> Result<(), EnclaveError> { 33 | let mut key = KEY.lock(); 34 | let mut keypair = KEYPAIR.lock(); 35 | 36 | if *keypair != 0 || *key != 0 { 37 | return Err(EnclaveError::invalid_arg()); 38 | } 39 | 40 | let algorithm = match key_size { 41 | 256 => BCRYPT_ECDH_P256_ALG_HANDLE, 42 | _ => return Err(EnclaveError::invalid_arg()), 43 | }; 44 | 45 | *keypair = bcrypt::generate_keypair(algorithm, key_size)? as usize; 46 | 47 | bcrypt::finalize_keypair(*keypair as BCRYPT_KEY_HANDLE)?; 48 | 49 | let public_key = 50 | bcrypt::import_keypair(algorithm, None, BCRYPT_ECCPUBLIC_BLOB, public_key_blob)?; 51 | 52 | let secret = bcrypt::secret_agreement(*keypair as BCRYPT_KEY_HANDLE, public_key)?; 53 | 54 | // This really should only fail if the key handle is invalid 55 | // but since we already used the key, we know it exists. 56 | let _ = bcrypt::destroy_key(public_key); 57 | 58 | // `pvBuffer` has to be a pointer to a UTF-16 C-string literal 59 | // and window-sys exports a *const u16 for the string literal, 60 | // but in the process of the conversion, the static length 61 | // is lost. Therefore, we have to calculate it ourselves. 62 | let mut parameters = [BCryptBuffer { 63 | cbBuffer: (("SHA256".len() + 1) * 2) as u32, 64 | BufferType: KDF_HASH_ALGORITHM, 65 | pvBuffer: BCRYPT_SHA256_ALGORITHM as *mut _, 66 | }]; 67 | 68 | let derived_key = bcrypt::derive_key(secret, BCRYPT_KDF_HASH, &mut parameters)?; 69 | 70 | // This really should only fail if the key handle is invalid 71 | // but since we already used the key, we know it exists. 72 | let _ = bcrypt::destroy_key(secret); 73 | 74 | let mut key_blob = bcrypt::Aes256KeyBlob { 75 | header: BCRYPT_KEY_DATA_BLOB_HEADER { 76 | dwMagic: BCRYPT_KEY_DATA_BLOB_MAGIC, 77 | dwVersion: BCRYPT_KEY_DATA_BLOB_VERSION1, 78 | cbKeyData: bcrypt::AES256_KEY_SIZE as u32, 79 | }, 80 | key_material: derived_key.try_into().expect( 81 | "A successful derive_key will always result in a Vec of length AES256_KEY_SIZE", 82 | ), 83 | }; 84 | 85 | let aes_key = bcrypt::import_key( 86 | BCRYPT_AES_GCM_ALG_HANDLE, 87 | None, 88 | BCRYPT_KEY_DATA_BLOB, 89 | &mut key_blob, 90 | )?; 91 | 92 | *key = aes_key as usize; 93 | Ok(()) 94 | } 95 | 96 | fn generate_report_internal() -> Result, EnclaveError> { 97 | let keypair = *KEYPAIR.lock() as BCRYPT_KEY_HANDLE; 98 | let public_key_blob = bcrypt::export_key(keypair, None, BCRYPT_ECCPUBLIC_BLOB)?; 99 | 100 | // The key blob itself is a ECDH-256 public key, which means that the x and y are each 101 | // 32 bytes. The available buffer for data in the attestation report is 64 bytes, 102 | // which leaves no space for the BCRYPT_ECCKEY_BLOB header, so that will have to 103 | // be recreated on the host process side. 104 | if public_key_blob.len() != ECDH_256_PUBLIC_BLOB_SIZE { 105 | return Err(EnclaveError::insufficient_buffer()); 106 | } 107 | 108 | let mut data = [0u8; ENCLAVE_REPORT_DATA_LENGTH as usize]; 109 | data.copy_from_slice(public_key_blob.split_at(size_of::()).1); 110 | 111 | get_attestation_report(Some(&data)) 112 | } 113 | 114 | fn decrypt_data_internal( 115 | encrypted_data: &[u8], 116 | iv: &mut [u8], 117 | tag: &mut [u8], 118 | ) -> Result, EnclaveError> { 119 | let key = *KEY.lock() as BCRYPT_KEY_HANDLE; 120 | 121 | let mode_info = BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO { 122 | cbSize: size_of::() as u32, 123 | dwInfoVersion: BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION, 124 | pbNonce: iv.as_mut_ptr() as *mut _, 125 | cbNonce: iv.len() as u32, 126 | pbAuthData: core::ptr::null_mut(), 127 | cbAuthData: 0, 128 | pbTag: tag.as_mut_ptr() as *mut _, 129 | cbTag: tag.len() as u32, 130 | pbMacContext: core::ptr::null_mut(), 131 | cbMacContext: 0, 132 | cbAAD: 0, 133 | cbData: 0, 134 | dwFlags: 0, 135 | }; 136 | 137 | bcrypt::decrypt_aes_gcm(key, encrypted_data, &mode_info) 138 | } 139 | -------------------------------------------------------------------------------- /sample/src/params.rs: -------------------------------------------------------------------------------- 1 | #[repr(C)] 2 | #[derive(Clone, Copy)] 3 | pub struct NewKeypairParams { 4 | // Only 256 is supported, because it needs to fit into the report 5 | pub key_size: u32, 6 | pub public_key_blob: *const u8, 7 | } 8 | 9 | #[repr(C)] 10 | #[derive(Clone, Copy)] 11 | pub struct GenerateReportParams { 12 | pub allocate_callback: extern "C" fn(usize) -> *mut u8, 13 | pub report_size: usize, 14 | pub report: *const u8, 15 | } 16 | 17 | #[repr(C)] 18 | #[derive(Clone, Copy)] 19 | pub struct DecryptDataParams { 20 | pub allocate_callback: extern "C" fn(usize) -> *mut u8, 21 | pub encrypted_size: usize, 22 | pub encrypted_data: *const u8, 23 | pub iv_size: usize, 24 | pub iv: *const u8, 25 | pub tag_size: usize, 26 | pub tag: *const u8, 27 | pub decrypted_size: usize, 28 | pub decrypted_data: *const u8, 29 | } 30 | -------------------------------------------------------------------------------- /src/allocator.rs: -------------------------------------------------------------------------------- 1 | use core::alloc::{GlobalAlloc, Layout}; 2 | 3 | // Since this must be compiled as no_std, an allocator needs to be defined. 4 | // Nothing complicated is needed, so the enclave allocator is just a passthrough 5 | // of malloc and free. 6 | 7 | extern "C" { 8 | fn malloc(s: usize) -> *mut u8; 9 | fn free(p: *const u8); 10 | } 11 | 12 | #[repr(C)] 13 | pub struct EnclaveAllocator {} 14 | 15 | unsafe impl GlobalAlloc for EnclaveAllocator { 16 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 17 | let size = layout.size(); 18 | let _align = layout.align(); 19 | 20 | malloc(size) 21 | } 22 | 23 | unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { 24 | free(ptr); 25 | } 26 | } 27 | 28 | #[global_allocator] 29 | static ALLOCATOR: EnclaveAllocator = EnclaveAllocator {}; 30 | -------------------------------------------------------------------------------- /src/enclaveapi.rs: -------------------------------------------------------------------------------- 1 | use crate::error::EnclaveError; 2 | use crate::types::LPENCLAVE_ROUTINE; 3 | use core::ffi::c_void; 4 | use windows_sys::Win32::Foundation::TRUE; 5 | use windows_sys::Win32::System::Environment::CallEnclave; 6 | 7 | // The enclave routine and parameter cannot be known to 8 | // be valid memory across the trust boundary, so we use 9 | // a struct to hold these values and mark the creation 10 | // of this struct as unsafe as a point in which the unsafe 11 | // assumption occurs. 12 | pub struct EnclaveRoutineInvocation { 13 | routine: LPENCLAVE_ROUTINE, 14 | param: *const c_void, 15 | } 16 | 17 | impl EnclaveRoutineInvocation { 18 | /// SAFETY: `routine` must be a valid function pointer. 19 | /// `param` must be a valid parameter for the function pointer, 20 | /// either an integer value that is expected, or a valid allocation. 21 | /// 22 | /// The EnclaveRoutineInvocation object must not outlive the lifetime 23 | /// of either of these pointers. 24 | pub unsafe fn new(routine: LPENCLAVE_ROUTINE, param: *const c_void) -> Self { 25 | Self { routine, param } 26 | } 27 | } 28 | 29 | pub fn call_enclave( 30 | invocation: EnclaveRoutineInvocation, 31 | wait_for_thread: bool, 32 | ) -> Result<*mut c_void, EnclaveError> { 33 | // Things to add: 34 | // 1. checking that the routine pointer is in vtl0 -- can this be done with a template? 35 | // 2. validation on parameter (not a vtl1 pointer, mostly as info leak) 36 | // 3. (optional) validation and copying of return value 37 | // 4. (optional) typing of parameter passed using vtl1_clonable traits etc 38 | // 5. return the return value parameter in a Result instead of just Ok 39 | 40 | let mut return_value = core::ptr::null_mut(); 41 | 42 | let success = unsafe { 43 | CallEnclave( 44 | invocation.routine, 45 | invocation.param, 46 | wait_for_thread as _, 47 | &mut return_value, 48 | ) 49 | }; 50 | 51 | if success == TRUE { 52 | Ok(return_value) 53 | } else { 54 | // TODO: Use GetLastError() and convert to HRESULT? 55 | Err(EnclaveError::invalid_arg()) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | pub use windows_sys::core::HRESULT; 2 | pub use windows_sys::Win32::Foundation::S_OK; 3 | use windows_sys::Win32::Foundation::{ERROR_INSUFFICIENT_BUFFER, E_INVALIDARG}; 4 | 5 | pub(crate) fn check_hr(hresult: HRESULT) -> Result<(), EnclaveError> { 6 | if hresult == S_OK { 7 | Ok(()) 8 | } else { 9 | Err(EnclaveError { hresult }) 10 | } 11 | } 12 | 13 | pub fn hresult_from_win32(e: u32) -> HRESULT { 14 | let facility_win32 = 0x0007u32; 15 | 16 | if e > 0 { 17 | ((e & 0x0000_ffff) as u32 | (facility_win32 << 16) | 0x8000_0000u32) as HRESULT 18 | } else { 19 | e as HRESULT 20 | } 21 | } 22 | 23 | pub fn hresult_from_nt(e: i32) -> HRESULT { 24 | let facility_nt_bit = 0x1000_0000; 25 | 26 | (e | facility_nt_bit) as HRESULT 27 | } 28 | 29 | #[derive(Debug, thiserror::Error)] 30 | #[error("enclave error: {hresult:x}")] 31 | pub struct EnclaveError { 32 | pub hresult: HRESULT, 33 | } 34 | 35 | impl EnclaveError { 36 | pub fn invalid_arg() -> Self { 37 | Self { 38 | hresult: E_INVALIDARG, 39 | } 40 | } 41 | 42 | // HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) 43 | pub fn insufficient_buffer() -> Self { 44 | Self { 45 | hresult: hresult_from_win32(ERROR_INSUFFICIENT_BUFFER), 46 | } 47 | } 48 | } 49 | 50 | impl From for HRESULT { 51 | fn from(err: EnclaveError) -> Self { 52 | err.hresult 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | use core::ffi::c_void; 3 | use core::ptr; 4 | 5 | mod allocator; 6 | pub mod enclaveapi; 7 | pub mod error; 8 | pub mod types; 9 | pub mod winenclave; 10 | 11 | extern crate alloc; 12 | 13 | use core::sync::atomic::{AtomicPtr, Ordering}; 14 | 15 | use winenclave::get_enclave_information; 16 | 17 | use core::panic::PanicInfo; 18 | #[panic_handler] 19 | // this will get red squiggles in vscode unless you set 20 | // "rust-analyzer.check.allTargets": false 21 | fn panic(_panic: &PanicInfo<'_>) -> ! { 22 | loop {} 23 | } 24 | 25 | static ENCLAVE_BASE: AtomicPtr = AtomicPtr::new(ptr::null_mut()); 26 | static ENCLAVE_END: AtomicPtr = AtomicPtr::new(ptr::null_mut()); 27 | 28 | pub fn is_valid_vtl0(base: *const T, size: usize) -> bool { 29 | !base.is_null() && is_valid_vtl0_or_null(base, size) 30 | } 31 | 32 | pub fn is_valid_vtl0_or_null(base: *const T, size: usize) -> bool { 33 | let base = base as *const c_void; 34 | let enclave_base = ENCLAVE_BASE.load(Ordering::Relaxed) as *const _; 35 | let enclave_end = ENCLAVE_END.load(Ordering::Relaxed) as *const _; 36 | 37 | let end = base.wrapping_byte_add(size); 38 | 39 | (end < enclave_base) || (enclave_end <= base) 40 | } 41 | 42 | #[no_mangle] 43 | pub extern "system" fn dllmain() -> bool { 44 | // Calculate this enclave's start and end addresses, so VTL0 45 | // pointers can be validated during enclave functions. 46 | let info = match get_enclave_information() { 47 | Ok(i) => i, 48 | _ => return false, 49 | }; 50 | 51 | let end = info.BaseAddress.wrapping_byte_add(info.Size); 52 | 53 | ENCLAVE_BASE.store(info.BaseAddress, Ordering::Relaxed); 54 | ENCLAVE_END.store(end, Ordering::Relaxed); 55 | 56 | true 57 | } 58 | -------------------------------------------------------------------------------- /src/types.rs: -------------------------------------------------------------------------------- 1 | #[allow(non_camel_case_types)] 2 | pub type LPENCLAVE_ROUTINE = isize; 3 | -------------------------------------------------------------------------------- /src/winenclave.rs: -------------------------------------------------------------------------------- 1 | use core::mem::{offset_of, MaybeUninit}; 2 | 3 | use alloc::vec::Vec; 4 | 5 | use windows_sys::Win32::System::Environment::{ 6 | EnclaveGetAttestationReport, EnclaveGetEnclaveInformation, EnclaveSealData, EnclaveUnsealData, 7 | ENCLAVE_IDENTITY, ENCLAVE_INFORMATION, ENCLAVE_REPORT_DATA_LENGTH, 8 | }; 9 | 10 | use crate::error::{check_hr, EnclaveError}; 11 | 12 | pub const ENCLAVE_LONG_ID_LENGTH: usize = 32; 13 | pub const ENCLAVE_SHORT_ID_LENGTH: usize = 16; 14 | 15 | pub const IMAGE_ENCLAVE_LONG_ID_LENGTH: usize = ENCLAVE_LONG_ID_LENGTH; 16 | pub const IMAGE_ENCLAVE_SHORT_ID_LENGTH: usize = ENCLAVE_SHORT_ID_LENGTH; 17 | pub const IMAGE_ENCLAVE_POLICY_DEBUGGABLE: u32 = 0x0000_0001; 18 | pub const IMAGE_ENCLAVE_FLAG_PRIMARY_IMAGE: u32 = 0x0000_0001; 19 | 20 | pub const ENCLAVE_FLAG_FULL_DEBUG_ENABLED: u32 = 0x0000_0001; 21 | 22 | pub const ENCLAVE_FLAG_DYNAMIC_DEBUG_ENABLED: u32 = 0x0000_0002; 23 | 24 | pub const ENCLAVE_FLAG_DYNAMIC_DEBUG_ACTIVE: u32 = 0x0000_0004; 25 | 26 | // struct _IMAGE_ENCLAVE_CONFIG64 { 27 | // DWORD Size; 28 | // DWORD MinimumRequiredConfigSize; 29 | // DWORD PolicyFlags; 30 | // DWORD NumberOfImports; 31 | // DWORD ImportList; 32 | // DWORD ImportEntrySize; 33 | // BYTE FamilyID[IMAGE_ENCLAVE_SHORT_ID_LENGTH]; 34 | // BYTE ImageID[IMAGE_ENCLAVE_SHORT_ID_LENGTH]; 35 | // DWORD ImageVersion; 36 | // DWORD SecurityVersion; 37 | // ULONGLONG EnclaveSize; 38 | // DWORD NumberOfThreads; 39 | // DWORD EnclaveFlags; 40 | // } 41 | 42 | #[repr(C)] 43 | // #[allow(non_camel_case_types)] 44 | pub struct ImageEnclaveConfig { 45 | pub size: u32, 46 | pub minimum_required_config_size: u32, 47 | pub policy_flags: u32, 48 | pub number_of_imports: u32, 49 | pub import_list: u32, 50 | pub import_entry_size: u32, 51 | pub family_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], 52 | pub image_id: [u8; IMAGE_ENCLAVE_SHORT_ID_LENGTH], 53 | pub image_version: u32, 54 | pub security_version: u32, 55 | pub enclave_size: usize, 56 | pub number_of_threads: u32, 57 | pub enclave_flags: u32, 58 | } 59 | 60 | pub const IMAGE_ENCLAVE_MINIMUM_CONFIG_SIZE: u32 = 61 | offset_of!(ImageEnclaveConfig, enclave_flags) as u32; 62 | 63 | #[repr(u32)] 64 | pub enum HResultSuccess { 65 | Ok = 0, 66 | False = 1, 67 | } 68 | 69 | #[repr(u32)] 70 | #[derive(Clone, Copy)] 71 | pub enum SealingIdentityPolicy { 72 | Invalid = 0, 73 | ExactCode = 1, 74 | PrimaryCode = 2, 75 | SameImage = 3, 76 | SameFamily = 4, 77 | SameAuthor = 5, 78 | } 79 | 80 | #[repr(u32)] 81 | #[derive(Clone, Copy)] 82 | pub enum SealingRuntimePolicy { 83 | None = 0, 84 | AllowFullDebug = 1, 85 | AllowDynamicDebug = 2, 86 | } 87 | 88 | pub fn get_attestation_report( 89 | enclave_data: Option<&[u8; ENCLAVE_REPORT_DATA_LENGTH as usize]>, 90 | ) -> Result, EnclaveError> { 91 | let mut output_size: u32 = 0; 92 | let mut report: Vec = Vec::new(); 93 | let data = if let Some(v) = enclave_data { 94 | v as *const u8 95 | } else { 96 | core::ptr::null() 97 | }; 98 | 99 | let hr = 100 | unsafe { EnclaveGetAttestationReport(data, core::ptr::null_mut(), 0, &mut output_size) }; 101 | check_hr(hr)?; 102 | 103 | report.resize(output_size as usize, 0); 104 | 105 | let hr = unsafe { 106 | EnclaveGetAttestationReport( 107 | data, 108 | report.as_mut_ptr() as *mut _, 109 | report.len() as u32, 110 | &mut output_size, 111 | ) 112 | }; 113 | check_hr(hr)?; 114 | 115 | Ok(report) 116 | } 117 | 118 | pub fn get_enclave_information() -> Result { 119 | let mut info = MaybeUninit::zeroed(); 120 | let hr = unsafe { 121 | EnclaveGetEnclaveInformation(size_of::() as u32, info.as_mut_ptr()) 122 | }; 123 | check_hr(hr)?; 124 | 125 | let info = unsafe { 126 | // SAFETY: only reachable if above HResult check passes. 127 | info.assume_init() 128 | }; 129 | 130 | Ok(info) 131 | } 132 | 133 | pub fn seal_data( 134 | data: &[u8], 135 | identity_policy: SealingIdentityPolicy, 136 | runtime_policy: SealingRuntimePolicy, 137 | ) -> Result, EnclaveError> { 138 | let Ok(data_to_encrypt_size) = u32::try_from(data.len()) else { 139 | return Err(EnclaveError::invalid_arg()); 140 | }; 141 | 142 | let mut output_size: u32 = 0; 143 | 144 | let hr = unsafe { 145 | EnclaveSealData( 146 | data.as_ptr() as _, 147 | data_to_encrypt_size, 148 | identity_policy as i32, 149 | runtime_policy as u32, 150 | core::ptr::null_mut(), 151 | 0, 152 | &mut output_size, 153 | ) 154 | }; 155 | check_hr(hr)?; 156 | 157 | let mut sealed_data = Vec::new(); 158 | sealed_data.resize(output_size as usize, 0); 159 | 160 | let hr = unsafe { 161 | EnclaveSealData( 162 | data.as_ptr() as _, 163 | data_to_encrypt_size, 164 | identity_policy as i32, 165 | runtime_policy as u32, 166 | sealed_data.as_mut_ptr() as _, 167 | sealed_data.len() as u32, 168 | &mut output_size, 169 | ) 170 | }; 171 | check_hr(hr)?; 172 | 173 | Ok(sealed_data) 174 | } 175 | 176 | pub fn unseal_data( 177 | data: &[u8], 178 | sealing_identity: Option<&mut ENCLAVE_IDENTITY>, 179 | unsealing_flags: Option, 180 | ) -> Result, EnclaveError> { 181 | let Ok(data_to_decrypt_len) = u32::try_from(data.len()) else { 182 | return Err(EnclaveError::invalid_arg()); 183 | }; 184 | 185 | let sealingidentity = if let Some(v) = sealing_identity { 186 | v as *mut _ 187 | } else { 188 | core::ptr::null_mut() 189 | }; 190 | 191 | let unsealingflags = if let Some(v) = unsealing_flags { 192 | v as *mut _ 193 | } else { 194 | core::ptr::null_mut() 195 | }; 196 | 197 | let mut decrypted_data_size = 0u32; 198 | 199 | let hr = unsafe { 200 | EnclaveUnsealData( 201 | data.as_ptr() as _, 202 | data_to_decrypt_len, 203 | core::ptr::null_mut(), 204 | 0, 205 | &mut decrypted_data_size as _, 206 | sealingidentity, 207 | unsealingflags, 208 | ) 209 | }; 210 | check_hr(hr)?; 211 | 212 | let mut decrypted_data: Vec = Vec::new(); 213 | decrypted_data.resize(decrypted_data_size as usize, 0); 214 | 215 | let hr = unsafe { 216 | EnclaveUnsealData( 217 | data.as_ptr() as _, 218 | data_to_decrypt_len, 219 | decrypted_data.as_mut_ptr() as _, 220 | decrypted_data.len() as u32, 221 | &mut decrypted_data_size as _, 222 | sealingidentity, 223 | unsealingflags, 224 | ) 225 | }; 226 | check_hr(hr)?; 227 | 228 | Ok(decrypted_data) 229 | } 230 | --------------------------------------------------------------------------------