├── .github ├── auto_assign-issues.yml ├── auto_assign.yml ├── dependabot.yml └── workflows │ ├── dco.yml │ ├── lint.yml │ └── test.yml ├── .gitignore ├── .rustfmt.toml ├── CODEOWNERS ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── build.rs ├── docs └── attestation │ ├── README.md │ ├── certchain.dot │ ├── certchain.dot.png │ ├── prerequisites.md │ ├── process.msc │ ├── process.msc.png │ └── protections.md ├── package-version.py ├── src ├── certs │ ├── mod.rs │ ├── sev │ │ ├── builtin │ │ │ ├── genoa │ │ │ │ ├── ark.cert │ │ │ │ ├── ask.cert │ │ │ │ └── mod.rs │ │ │ ├── milan │ │ │ │ ├── ark.cert │ │ │ │ ├── ask.cert │ │ │ │ └── mod.rs │ │ │ ├── mod.rs │ │ │ ├── naples │ │ │ │ ├── ark.cert │ │ │ │ ├── ask.cert │ │ │ │ └── mod.rs │ │ │ ├── rome │ │ │ │ ├── ark.cert │ │ │ │ ├── ask.cert │ │ │ │ └── mod.rs │ │ │ └── turin │ │ │ │ ├── ark.cert │ │ │ │ ├── ask.cert │ │ │ │ └── mod.rs │ │ ├── ca │ │ │ ├── cert │ │ │ │ ├── mod.rs │ │ │ │ └── v1.rs │ │ │ ├── chain.rs │ │ │ └── mod.rs │ │ ├── chain.rs │ │ ├── crypto.rs │ │ ├── mod.rs │ │ ├── sev │ │ │ ├── cert │ │ │ │ ├── mod.rs │ │ │ │ └── v1 │ │ │ │ │ ├── algo.rs │ │ │ │ │ ├── body │ │ │ │ │ ├── key │ │ │ │ │ │ ├── ecc │ │ │ │ │ │ │ ├── group.rs │ │ │ │ │ │ │ └── mod.rs │ │ │ │ │ │ ├── mod.rs │ │ │ │ │ │ └── rsa.rs │ │ │ │ │ └── mod.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── sig │ │ │ │ │ ├── ecdsa.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ └── rsa.rs │ │ │ ├── chain.rs │ │ │ └── mod.rs │ │ └── util.rs │ └── snp │ │ ├── builtin │ │ ├── genoa │ │ │ ├── ark.pem │ │ │ ├── ask.pem │ │ │ └── mod.rs │ │ ├── milan │ │ │ ├── ark.pem │ │ │ ├── ask.pem │ │ │ └── mod.rs │ │ ├── mod.rs │ │ └── turin │ │ │ ├── ark.pem │ │ │ ├── ask.pem │ │ │ └── mod.rs │ │ ├── ca │ │ └── mod.rs │ │ ├── cert.rs │ │ ├── cert_nossl.rs │ │ ├── chain.rs │ │ ├── ecdsa │ │ └── mod.rs │ │ └── mod.rs ├── error.rs ├── firmware │ ├── guest │ │ ├── mod.rs │ │ └── types │ │ │ ├── mod.rs │ │ │ └── snp.rs │ ├── host │ │ ├── mod.rs │ │ └── types │ │ │ ├── mod.rs │ │ │ ├── sev.rs │ │ │ └── snp.rs │ ├── linux │ │ ├── guest │ │ │ ├── ioctl.rs │ │ │ ├── mod.rs │ │ │ └── types.rs │ │ ├── host │ │ │ ├── ioctl.rs │ │ │ ├── mod.rs │ │ │ └── types │ │ │ │ ├── mod.rs │ │ │ │ ├── sev.rs │ │ │ │ └── snp.rs │ │ └── mod.rs │ └── mod.rs ├── launch │ ├── linux │ │ ├── ioctl.rs │ │ ├── mod.rs │ │ ├── sev.rs │ │ ├── shared.rs │ │ └── snp.rs │ ├── mod.rs │ ├── sev.rs │ └── snp.rs ├── lib.rs ├── measurement │ ├── gctx.rs │ ├── idblock.rs │ ├── idblock_types.rs │ ├── mod.rs │ ├── ovmf.rs │ ├── sev.rs │ ├── sev_hashes.rs │ ├── snp.rs │ ├── vcpu_types.rs │ └── vmsa.rs ├── session │ ├── key.rs │ └── mod.rs ├── util │ ├── array.rs │ ├── cached_chain.rs │ ├── impl_const_id.rs │ ├── mod.rs │ └── parser │ │ ├── byte_parser.rs │ │ ├── mod.rs │ │ ├── read_ext.rs │ │ └── write_ext.rs └── vmsa │ └── mod.rs └── tests ├── api.rs ├── certs.rs ├── certs_data ├── cert_chain_milan ├── cert_chain_turin ├── report_milan.hex ├── vcek_milan.der └── vcek_turin.der ├── guest.rs ├── id-block.rs ├── measurement.rs ├── measurement ├── ovmf_AmdSev_suffix.bin ├── ovmf_OvmfX64_suffix.bin ├── test_auth_block.bin ├── test_auth_key.pem ├── test_auth_sig.bin ├── test_id_key.pem └── test_id_sig.bin ├── naples ├── ark.cert.bad ├── ark.cert.sig ├── ark.rs ├── ask.rs ├── cek.cert ├── cek.rs ├── mod.rs ├── oca.cert ├── oca.rs ├── pdh.cert ├── pdh.rs ├── pek.cert └── pek.rs ├── rome ├── ark.rs ├── ask.rs ├── cek.cert ├── cek.rs ├── mod.rs ├── oca.cert ├── oca.rs ├── pdh.cert ├── pdh.rs ├── pek.cert └── pek.rs ├── session.rs ├── sev_launch.rs └── snp_launch.rs /.github/auto_assign-issues.yml: -------------------------------------------------------------------------------- 1 | addAssignees: true 2 | 3 | assignees: 4 | - tylerfanelli 5 | - larrydewey 6 | -------------------------------------------------------------------------------- /.github/auto_assign.yml: -------------------------------------------------------------------------------- 1 | # Set to true to add reviewers to pull requests 2 | addReviewers: true 3 | 4 | # Set to true to add assignees to pull requests 5 | addAssignees: true 6 | 7 | # A list of reviewers to be added to pull requests (GitHub user name) 8 | reviewers: 9 | - DGonzalezVillal 10 | - tylerfanelli 11 | - larrydewey 12 | - ryansavino 13 | 14 | # A list of keywords to be skipped the process that add reviewers if pull requests include it 15 | skipKeywords: 16 | - wip 17 | - WIP 18 | 19 | # A number of reviewers added to the pull request 20 | # Set 0 to add all the reviewers (default: 0) 21 | numberOfReviewers: 2 22 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/dco.yml: -------------------------------------------------------------------------------- 1 | name: Sign-off Check 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | check: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: KineticCafe/actions-dco@v1 11 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | name: lint 3 | jobs: 4 | fmt: 5 | name: cargo fmt 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | - uses: dtolnay/rust-toolchain@stable 10 | with: 11 | components: rustfmt 12 | toolchain: 1.80.0 13 | - run: cargo fmt --all -- --check 14 | 15 | clippy-openssl: 16 | name: cargo clippy openssl 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: dtolnay/rust-toolchain@stable 21 | with: 22 | components: clippy 23 | toolchain: 1.80.0 24 | - run: cargo clippy --features=openssl,hw_tests,dangerous_hw_tests --all-targets -- -D clippy::all -D unused_imports -D warnings -D clippy::style 25 | 26 | clippy-crypto_nossl: 27 | name: cargo clippy crypto_nossl 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: actions/checkout@v4 31 | - uses: dtolnay/rust-toolchain@stable 32 | with: 33 | components: clippy 34 | toolchain: 1.80.0 35 | - run: cargo clippy --features=crypto_nossl,hw_tests,dangerous_hw_tests --all-targets -- -D clippy::all -D unused_imports -D warnings -D clippy::style 36 | 37 | readme: 38 | name: cargo rdme 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: actions/checkout@v4 42 | - uses: dtolnay/rust-toolchain@stable 43 | with: 44 | toolchain: nightly 45 | - run: | 46 | cargo install cargo-rdme 47 | cargo rdme --check 48 | 49 | check-spdx-headers: 50 | runs-on: ubuntu-latest 51 | steps: 52 | - name: checkout 53 | uses: actions/checkout@v4 54 | - uses: enarx/spdx@master 55 | with: 56 | licenses: Apache-2.0 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .vscode/ -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2021" 2 | newline_style = "Unix" 3 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @tylerfanelli @larrydewey @DGonzalezVillal 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sev" 3 | version = "6.2.0" 4 | authors = [ 5 | "Nathaniel McCallum ", 6 | "The VirTEE Project Developers", 7 | ] 8 | license = "Apache-2.0" 9 | edition = "2018" 10 | homepage = "https://github.com/virtee/sev" 11 | repository = "https://github.com/virtee/sev" 12 | description = "Library for AMD SEV" 13 | readme = "README.md" 14 | keywords = ["amd", "sev"] 15 | categories = [ 16 | "os", 17 | "os::linux-apis", 18 | "parsing", 19 | "network-programming", 20 | "hardware-support", 21 | ] 22 | exclude = [".gitignore", ".github/*"] 23 | rust-version = "1.80.0" 24 | 25 | [badges] 26 | # See https://doc.rust-lang.org/cargo/reference/manifest.html#the-badges-section 27 | github = { repository = "virtee/sev", workflow = "test" } 28 | #github = { repository = "virtee/sev", workflow = "lint" } 29 | maintenance = { status = "actively-developed" } 30 | is-it-maintained-issue-resolution = { repository = "virtee/sev" } 31 | is-it-maintained-open-issues = { repository = "virtee/sev" } 32 | 33 | [lib] 34 | name = 'sev' 35 | path = "src/lib.rs" 36 | doc = false 37 | 38 | [features] 39 | default = ["sev", "snp"] 40 | openssl = ["dep:openssl", "dep:rdrand"] 41 | hw_tests = [] 42 | dangerous_hw_tests = ["hw_tests", "dep:reqwest", "dep:tokio"] 43 | sev = [] 44 | snp = [] 45 | crypto_nossl = ["dep:p384", "dep:rsa", "dep:sha2", "dep:x509-cert"] 46 | 47 | [target.'cfg(target_os = "linux")'.dependencies] 48 | iocuddle = "^0.1" 49 | 50 | [dependencies] 51 | openssl = { version = "0.10", optional = true, features = ["vendored"] } 52 | serde = { version = "1.0", features = ["derive"] } 53 | serde_bytes = "0.11" 54 | bitflags = "2.9.0" 55 | codicon = "3.0" 56 | dirs = "^6.0" 57 | serde-big-array = "0.5.1" 58 | static_assertions = "^1.1.0" 59 | bitfield = "^0.19" 60 | uuid = { version = "^1.11", features = ["serde"] } 61 | bincode = "^1.3" 62 | hex = "0.4.3" 63 | libc = "0.2.161" 64 | lazy_static = "1.4.0" 65 | p384 = { version = "0.13.0", optional = true } 66 | rsa = { version = "0.9.6", optional = true } 67 | sha2 = { version = "0.10.8", optional = true } 68 | x509-cert = { version = "0.2.5", optional = true } 69 | byteorder = "1.4.3" 70 | base64 = "0.22.1" 71 | rdrand = { version = "^0.8", optional = true } 72 | reqwest = { version = "^0.12", features = ["blocking"], optional = true } 73 | tokio = { version = "1.29.1", features = ["rt-multi-thread"], optional = true } 74 | 75 | [target.'cfg(target_os = "linux")'.dev-dependencies] 76 | kvm-ioctls = ">=0.16" 77 | kvm-bindings = "^0.11" 78 | 79 | [dev-dependencies] 80 | serial_test = "3.0" 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | The `sev` crate provides an implementation of the [AMD Secure Encrypted 4 | Virtualization (SEV)][SEV] APIs and the [SEV Secure Nested Paging 5 | Firmware (SNP)][SNP] ABIs. 6 | 7 | [SEV]: https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/programmer-references/55766_SEV-KM_API_Specification.pdf 8 | [SNP]: https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/56860.pdf 9 | 10 | ## SEV APIs 11 | 12 | The linux kernel exposes two technically distinct AMD SEV APIs: 13 | 14 | 1. An API for managing the SEV platform itself 15 | 2. An API for managing SEV-enabled KVM virtual machines 16 | 17 | This crate implements both of those APIs and offers them to client. 18 | code through a flexible and type-safe high-level interface. 19 | 20 | ## SNP ABIs 21 | 22 | Like SEV, the linux kernel exposes another two different AMD SEV-SNP ABIs: 23 | 24 | 1. An ABI for managing the SEV-SNP platform itself 25 | 2. An ABI for managing SEV-SNP enabled KVM virtual machines 26 | 27 | These new ABIs work only for **SEV-SNP** enabled hosts and guests. 28 | 29 | This crate implements APIs for both SEV and SEV-SNP management. 30 | 31 | ## SEV and SEV-SNP enablement 32 | 33 | By default, both the SEV and SEV-SNP libraries are compiled. 34 | Because many modules provide support to both legacy SEV and SEV-SNP, they have been split into individual sub-modules `sev.rs` and `snp.rs`, isolating generation specific behavior. 35 | If desired, you may opt to exclude either of the sub-modules by disabling its feature in your project's `Cargo.toml` 36 | 37 | For example, to include the SEV APIs only: 38 | `sev = { version = "1.2.1", default-features = false, features = ["sev"] }` 39 | 40 | To include the SEV-SNP APIs only: 41 | `sev = { version = "1.2.1", default-features = false, features = ["snp"] }` 42 | 43 | ## Platform Management 44 | 45 | Refer to the [firmware](https://docs.rs/sev/latest/sev/firmware/) module for more information. 46 | 47 | ## Guest Management 48 | 49 | Refer to the [launch](https://docs.rs/sev/latest/sev/launch/) module for more information. 50 | 51 | ## Cryptographic Verification 52 | 53 | To enable the cryptographic verification of certificate chains and 54 | attestation reports, either the `openssl` or `crypto_nossl` feature 55 | has to be enabled manually. With `openssl`, OpenSSL is used for the 56 | verification. With `crypto_nossl`, OpenSSL is _not_ used for the 57 | verification and instead pure-Rust libraries (e.g., `p384`, `rsa`, 58 | etc.) are used. `openssl` and `crypto_nossl` are mutually exclusive, 59 | and enabling both at the same time leads to a compiler error. 60 | 61 | ## Remarks 62 | 63 | Note that the linux kernel provides access to these APIs through a set 64 | of `ioctl`s that are meant to be called on device nodes (`/dev/kvm` and 65 | `/dev/sev`, to be specific). As a result, these `ioctl`s form the substrate 66 | of the `sev` crate. Binaries that result from consumers of this crate are 67 | expected to run as a process with the necessary privileges to interact 68 | with the device nodes. 69 | 70 | ## Using the C API 71 | 72 | Projects in C can take advantage of the C API for the SEV [launch] ioctls. 73 | To install the C API, users can use `cargo-c` with the features they would 74 | like to produce and install a `pkg-config` file, a static library, a dynamic 75 | library, and a C header: 76 | 77 | `cargo cinstall --prefix=/usr --libdir=/usr/lib64` 78 | 79 | [firmware]: ./src/firmware/ 80 | [launch]: ./src/launch/ 81 | 82 | 83 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | fn main() { 4 | use std::path::Path; 5 | 6 | // Add in configurations to be checked within the code. 7 | println!("cargo::rustc-check-cfg=cfg(host)"); 8 | println!("cargo::rustc-check-cfg=cfg(guest)"); 9 | 10 | if Path::new("/dev/sev").exists() { 11 | println!("cargo:rustc-cfg=host"); 12 | } 13 | 14 | if Path::new("/dev/sev-guest").exists() { 15 | println!("cargo:rustc-cfg=guest"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /docs/attestation/certchain.dot: -------------------------------------------------------------------------------- 1 | # Commits which modify this file MUST generate the new .png! 2 | digraph G { 3 | node [style=filled]; 4 | 5 | node [fillcolor=blue, fontcolor=white]; 6 | CEK; 7 | 8 | node [fillcolor=none, fontcolor=black]; 9 | ARK -> ASK; 10 | ASK -> CEK; 11 | CEK -> PEK; 12 | PEK -> PDH; 13 | OCA -> PEK; 14 | } 15 | -------------------------------------------------------------------------------- /docs/attestation/certchain.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/docs/attestation/certchain.dot.png -------------------------------------------------------------------------------- /docs/attestation/prerequisites.md: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | 3 | Attestation is built on a number of technical concepts. Readers should familiarize 4 | themselves with the following topics: 5 | 6 | * [Public-key Cryptography](https://en.wikipedia.org/wiki/Public-key_cryptography) 7 | -------------------------------------------------------------------------------- /docs/attestation/process.msc: -------------------------------------------------------------------------------- 1 | # Commits which modify this file MUST generate the new .png! 2 | msc { 3 | tenant [textbgcolor="green"], 4 | host [textbgcolor="red"], 5 | bios [textbgcolor="orange"], 6 | bootloader [textbgcolor="orange"], 7 | kernel [textbgcolor="orange"], 8 | psp [textbgcolor="yellow"]; 9 | 10 | tenant=>host [label="cert chain request"]; 11 | host=>psp [label="cert chain request"]; 12 | psp=>host [label="cert chain reply\n(w/ firmware version)"]; 13 | host=>tenant [label="cert chain reply\n(w/ firmware version)"]; 14 | 15 | ...; 16 | 17 | tenant box tenant [label="validate cert chain"]; 18 | tenant box tenant [label="validate fw version"]; 19 | tenant box tenant [label="craft exec policy"]; 20 | tenant box tenant [label="random cdh keypair"]; 21 | tenant box tenant [label="random tek/tik"]; 22 | 23 | 24 | tenant box tenant [label="DH(cdh, pdh) => z"]; 25 | tenant box tenant [label="KDF(z) => master"]; 26 | tenant box tenant [label="KDF(master) => kek"]; 27 | tenant box tenant [label="KDF(master) => kik"]; 28 | 29 | tenant => host [label="CDH, MAC(tik, policy),\nMAC(kik, ENC(kek, tek||tik))"]; 30 | host => psp [label="CDH, MAC(tik, policy),\nMAC(kik, ENC(kek, tek||tik))"]; 31 | 32 | psp box psp [label="DH(cdh, pdh) => z"]; 33 | psp box psp [label="KDF(z) => master"]; 34 | psp box psp [label="KDF(master) => kek"]; 35 | psp box psp [label="KDF(master) => kik"]; 36 | 37 | psp box psp [label="check MAC(kik, ...)"]; 38 | psp box psp [label="DEC(kek, tek||tik)"]; 39 | 40 | host => psp [label="guest pages"]; 41 | psp box psp [label="measure guest pages"]; 42 | 43 | psp => host [label="MAC(tik, measurement)"]; 44 | host => tenant [label="MAC(tik, measurement)"]; 45 | 46 | tenant box tenant [label="validate measure"]; 47 | tenant box tenant [label="prepare metadata"]; 48 | tenant => host [label="enc = ENC(tek, metadata)\nMAC(tik, enc)"]; 49 | host => psp [label="enc = ENC(tek, metadata)\nMAC(tik, enc)"]; 50 | 51 | psp => bios [label="decrypt metadata into guest memory"]; 52 | 53 | --- [label="VM START"]; 54 | 55 | bios => bootloader [label="metadata"]; 56 | bootloader => kernel [label="metadata"]; 57 | kernel box kernel [label="unlock volume"]; 58 | 59 | --- [label="BOOT COMPLETE"]; 60 | } 61 | -------------------------------------------------------------------------------- /docs/attestation/process.msc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/docs/attestation/process.msc.png -------------------------------------------------------------------------------- /docs/attestation/protections.md: -------------------------------------------------------------------------------- 1 | # Protections 2 | 3 | An SEV-enabled guest will be protected from a number of potential threats. 4 | 5 | These threats are broadly categorized like so: 6 | 7 | **Confidentiality**: Anything that could disclose and/or read the contents of the virtual machine without 8 | its explicit permission is a threat to the virtual machine's confidentiality. 9 | 10 | **Integrity**: The virtual machine must always see the data that it last wrote. If this invariant is broken, 11 | then the integrity of the virtual machine is compromised. 12 | 13 | **Physical Access Attacks**: An attacker with a substantial level of access to the physical hardware may use 14 | this access to conduct attacks on the system and virtual machines running on it. 15 | 16 | **Miscellaneous**: An attack which doesn't fit in as nicely to any of the other above categories. 17 | 18 | Table legend: 19 | 20 | * :heavy\_check\_mark:: indicates that this attack is thwarted by an SEV feature. 21 | 22 | * :star2:: indicates that this mitigation may be optionally enabled. 23 | 24 | * : an empty cell indicates that the attack is *not* mitigated by that technology. 25 | 26 | | **Confidentiality** | **SEV** | **SEV-ES** | **SEV-SNP** | 27 | | ------------------: | :-----: | :--------: | :---------: | 28 | | VM Memory (*ex: Hypervisor reads private VM memory*) | :heavy\_check\_mark: | :heavy\_check\_mark: | :heavy\_check\_mark: | 29 | | VM Register State (*ex: Hypervisor attempts to read VM register context*) | | :heavy\_check\_mark: | :heavy\_check\_mark: | 30 | | DMA Protection (*ex: Device attempts to read VM memory*) | :heavy\_check\_mark: | :heavy\_check\_mark: | :heavy\_check\_mark: | 31 | | **Integrity** | **SEV** | **SEV-ES** | **SEV-SNP** | 32 | | Replay Protection (*ex: VM memory is replaced with an old copy*) | | | :heavy\_check\_mark: | 33 | | Data Corruption (*ex: VM memory is replaced with junk data*) | | | :heavy\_check\_mark: | 34 | | Memory Aliasing (*ex: Hypervisor maps two guest pages to same DRAM page*) | | | :heavy\_check\_mark: | 35 | | Memory Re-mapping (*ex: Hypervisor switches DRAM page mapped to a guest page*) | | | :heavy\_check\_mark: | 36 | | **Availability** | **SEV** | **SEV-ES** | **SEV-SNP** | 37 | | Guest to Host Denial of Service (*ex: Guest refuses to yield/exit*) | :heavy\_check\_mark: | :heavy\_check\_mark: | :heavy\_check\_mark: | 38 | | Host to Guest Denial of Service (*ex: Host refuses to run guest*) | | | | 39 | | **Physical Access Attacks** | **SEV** | **SEV-ES** | **SEV-SNP** | 40 | | Offline DRAM analysis (*ex: Cold boot*) | :heavy\_check\_mark: | :heavy\_check\_mark: | :heavy\_check\_mark: | 41 | | Active DRAM corruption (*ex: Manipulate DDR bus while VM is running*) | | | | 42 | | **Miscellaneous Attacks** | **SEV** | **SEV-ES** | **SEV-SNP** | 43 | | TCB Rollback (*ex: AMD-SP firmware is reverted to older version*) | | | :heavy\_check\_mark: | 44 | | Malicious Interrupt/Exception Injection (*ex: interrupt injected while RFLAGS.IF=0*) | | | :star2: | 45 | | Indirect Branch Predictor Poisoning (*ex: Poison BTB from hypervisor*) | | | :star2: | 46 | | Secure Hardware Debug Registers (*ex: Breakpoints changed during debugging*) | | | :star2: | 47 | | Trusted CPUID Information (*ex: Hypervisor lies about platform capabilities*) | | | :star2: | 48 | | Architectural Side Channels (*ex: PRIME+PROBE to track VM accesses*) | | | | 49 | | Page-level Side Channels (*ex: Track VM access patterns through page tables*) | | | | 50 | | Performance Counter Tracking (*ex: Fingerprint VM workloads by performance data*) | | | | 51 | 52 | *The table above was taken directly from the [AMD SEV-SNP Whitepaper (Table 1: Threat Model)]( 53 | https://www.amd.com/system/files/TechDocs/SEV-SNP-strengthening-vm-isolation-with-integrity-protection-and-more.pdf).* 54 | -------------------------------------------------------------------------------- /package-version.py: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | #!/usr/bin/env python3 4 | import json 5 | import subprocess 6 | 7 | try: 8 | out = subprocess.check_output(["cargo", "read-manifest"]) 9 | print(json.loads(out)["version"]) 10 | except FileNotFoundError: 11 | print("unknown") 12 | -------------------------------------------------------------------------------- /src/certs/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | /// Legacy SEV certificates. 4 | #[cfg(feature = "sev")] 5 | pub mod sev; 6 | 7 | /// SEV-SNP certificates. 8 | #[cfg(feature = "snp")] 9 | pub mod snp; 10 | -------------------------------------------------------------------------------- /src/certs/sev/builtin/genoa/ark.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/src/certs/sev/builtin/genoa/ark.cert -------------------------------------------------------------------------------- /src/certs/sev/builtin/genoa/ask.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/src/certs/sev/builtin/genoa/ask.cert -------------------------------------------------------------------------------- /src/certs/sev/builtin/genoa/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! AMD's Naples certificates. 4 | //! 5 | //! Certificate provenance: 6 | //! 7 | //! For convenience, the certificate chain has been split into individual 8 | //! certificates and are embedded here as byte slices. 9 | 10 | /// The public Naples ARK certificate. 11 | pub const ARK: &[u8] = include_bytes!("ark.cert"); 12 | 13 | /// The public Naples ASK certificate. 14 | pub const ASK: &[u8] = include_bytes!("ask.cert"); 15 | -------------------------------------------------------------------------------- /src/certs/sev/builtin/milan/ark.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/src/certs/sev/builtin/milan/ark.cert -------------------------------------------------------------------------------- /src/certs/sev/builtin/milan/ask.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/src/certs/sev/builtin/milan/ask.cert -------------------------------------------------------------------------------- /src/certs/sev/builtin/milan/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! AMD's Naples certificates. 4 | //! 5 | //! Certificate provenance: 6 | //! 7 | //! For convenience, the certificate chain has been split into individual 8 | //! certificates and are embedded here as byte slices. 9 | 10 | /// The public Naples ARK certificate. 11 | pub const ARK: &[u8] = include_bytes!("ark.cert"); 12 | 13 | /// The public Naples ASK certificate. 14 | pub const ASK: &[u8] = include_bytes!("ask.cert"); 15 | -------------------------------------------------------------------------------- /src/certs/sev/builtin/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Provides access to "built-in" AMD SEV ARK and ASK certificates. 4 | //! 5 | //! These are primarily offered as a convenience measure to avoid making 6 | //! HTTP requests to AMD's servers. 7 | 8 | pub mod genoa; 9 | pub mod milan; 10 | pub mod naples; 11 | pub mod rome; 12 | pub mod turin; 13 | -------------------------------------------------------------------------------- /src/certs/sev/builtin/naples/ark.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/src/certs/sev/builtin/naples/ark.cert -------------------------------------------------------------------------------- /src/certs/sev/builtin/naples/ask.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/src/certs/sev/builtin/naples/ask.cert -------------------------------------------------------------------------------- /src/certs/sev/builtin/naples/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! AMD's Naples certificates. 4 | //! 5 | //! Certificate provenance: 6 | //! 7 | //! For convenience, the certificate chain has been split into individual 8 | //! certificates and are embedded here as byte slices. 9 | 10 | /// The public Naples ARK certificate. 11 | pub const ARK: &[u8] = include_bytes!("ark.cert"); 12 | 13 | /// The public Naples ASK certificate. 14 | pub const ASK: &[u8] = include_bytes!("ask.cert"); 15 | -------------------------------------------------------------------------------- /src/certs/sev/builtin/rome/ark.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/src/certs/sev/builtin/rome/ark.cert -------------------------------------------------------------------------------- /src/certs/sev/builtin/rome/ask.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/src/certs/sev/builtin/rome/ask.cert -------------------------------------------------------------------------------- /src/certs/sev/builtin/rome/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! AMD's Rome certificates. 4 | //! 5 | //! Certificate provenance: 6 | //! 7 | //! For convenience, the certificate chain has been split into individual 8 | //! certificates and are embedded here as byte slices. 9 | 10 | /// The public Rome ARK certificate. 11 | pub const ARK: &[u8] = include_bytes!("ark.cert"); 12 | 13 | /// The public Rome ASK certificate. 14 | pub const ASK: &[u8] = include_bytes!("ask.cert"); 15 | -------------------------------------------------------------------------------- /src/certs/sev/builtin/turin/ark.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/src/certs/sev/builtin/turin/ark.cert -------------------------------------------------------------------------------- /src/certs/sev/builtin/turin/ask.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/src/certs/sev/builtin/turin/ask.cert -------------------------------------------------------------------------------- /src/certs/sev/builtin/turin/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! AMD's TURIN certificates. 4 | //! 5 | //! Certificate provenance: 6 | //! 7 | //! For convenience, the certificate chain has been split into individual 8 | //! certificates and are embedded here as byte slices. 9 | 10 | /// The public Turin ARK certificate. 11 | pub const ARK: &[u8] = include_bytes!("ark.cert"); 12 | 13 | /// The public Turin ASK certificate. 14 | pub const ASK: &[u8] = include_bytes!("ask.cert"); 15 | -------------------------------------------------------------------------------- /src/certs/sev/ca/cert/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Operations that can be done on an OCA certificate. 4 | 5 | mod v1; 6 | 7 | use super::*; 8 | 9 | use std::mem::size_of; 10 | 11 | use serde::{de, ser}; 12 | use serde_bytes::{ByteBuf, Bytes}; 13 | 14 | /// An OCA certificate. 15 | #[derive(Clone, Copy)] 16 | #[repr(C)] 17 | pub union Certificate { 18 | /// The version of the CA certificate. 19 | version: u32, 20 | 21 | /// The contents of the CA certificate. 22 | v1: v1::Certificate, 23 | } 24 | 25 | impl Default for Certificate { 26 | fn default() -> Self { 27 | Self { version: 0 } 28 | } 29 | } 30 | 31 | impl std::fmt::Debug for Certificate { 32 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 33 | match self.version() { 34 | 1 => write!(f, "{:?}", unsafe { self.v1 }), 35 | v => write!(f, "Certificate {{ version: {v} }}"), 36 | } 37 | } 38 | } 39 | 40 | #[cfg(feature = "openssl")] 41 | impl std::fmt::Display for Certificate { 42 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 43 | use codicon::Encoder; 44 | use std::fmt::Error; 45 | 46 | let key = PublicKey::try_from(self).or(Err(Error))?; 47 | 48 | let mut hsh = hash::Hasher::new(key.hash)?; 49 | 50 | self.encode(&mut hsh, Body).or(Err(Error))?; 51 | 52 | write!(f, "{} {} ", crate::certs::sev::Usage::from(key.usage), key)?; 53 | for b in hsh.finish()?.iter() { 54 | write!(f, "{:02x}", *b)?; 55 | } 56 | 57 | Ok(()) 58 | } 59 | } 60 | 61 | impl Eq for Certificate {} 62 | impl PartialEq for Certificate { 63 | fn eq(&self, other: &Certificate) -> bool { 64 | if unsafe { self.version != other.version } { 65 | return false; 66 | } 67 | match self.version() { 68 | 1 => unsafe { self.v1 == other.v1 }, 69 | _ => false, 70 | } 71 | } 72 | } 73 | 74 | impl> PartialEq for Certificate { 75 | fn eq(&self, other: &U) -> bool { 76 | if let Ok(a) = Usage::try_from(self) { 77 | return a == (*other).into(); 78 | } 79 | 80 | false 81 | } 82 | } 83 | 84 | impl codicon::Decoder<()> for Certificate { 85 | type Error = Error; 86 | 87 | fn decode(mut reader: impl Read, params: ()) -> Result { 88 | Ok(match u32::from_le(reader.load()?) { 89 | 1 => Certificate { 90 | v1: v1::Certificate::decode(reader, params)?, 91 | }, 92 | _ => return Err(ErrorKind::InvalidData)?, 93 | }) 94 | } 95 | } 96 | 97 | impl codicon::Encoder<()> for Certificate { 98 | type Error = Error; 99 | 100 | fn encode(&self, writer: impl Write, _: ()) -> Result<()> { 101 | match self.version() { 102 | 1 => unsafe { self.v1.encode(writer, ()) }, 103 | _ => Err(ErrorKind::InvalidInput)?, 104 | } 105 | } 106 | } 107 | 108 | #[cfg(feature = "openssl")] 109 | impl codicon::Encoder for Certificate { 110 | type Error = Error; 111 | 112 | fn encode(&self, writer: impl Write, _: Body) -> Result<()> { 113 | match self.version() { 114 | 1 => unsafe { self.v1.encode(writer, Body) }, 115 | _ => Err(ErrorKind::InvalidInput)?, 116 | } 117 | } 118 | } 119 | 120 | impl<'de> de::Deserialize<'de> for Certificate { 121 | fn deserialize(deserializer: D) -> std::result::Result 122 | where 123 | D: de::Deserializer<'de>, 124 | { 125 | use codicon::Decoder; 126 | 127 | let bytes = ByteBuf::deserialize(deserializer)?; 128 | Self::decode(bytes.as_slice(), ()).map_err(serde::de::Error::custom) 129 | } 130 | } 131 | 132 | impl ser::Serialize for Certificate { 133 | fn serialize(&self, serializer: S) -> std::result::Result 134 | where 135 | S: ser::Serializer, 136 | { 137 | use std::slice::from_raw_parts; 138 | 139 | let bytes = unsafe { from_raw_parts(self as *const Self as *const u8, size_of::()) }; 140 | let bytes = Bytes::new(bytes); 141 | bytes.serialize(serializer) 142 | } 143 | } 144 | 145 | impl TryFrom<&Certificate> for Usage { 146 | type Error = Error; 147 | 148 | fn try_from(value: &Certificate) -> Result { 149 | match value.version() { 150 | 1 => Ok(unsafe { value.v1.preamble.data.usage }), 151 | _ => Err(ErrorKind::InvalidInput)?, 152 | } 153 | } 154 | } 155 | 156 | impl TryFrom<&Certificate> for crate::certs::sev::Usage { 157 | type Error = Error; 158 | 159 | fn try_from(value: &Certificate) -> Result { 160 | Ok(Usage::try_from(value)?.into()) 161 | } 162 | } 163 | 164 | #[cfg(feature = "openssl")] 165 | impl TryFrom<&Certificate> for PublicKey { 166 | type Error = Error; 167 | 168 | fn try_from(value: &Certificate) -> Result { 169 | match value.version() { 170 | 1 => unsafe { value.v1.try_into() }, 171 | _ => Err(ErrorKind::InvalidInput)?, 172 | } 173 | } 174 | } 175 | 176 | #[cfg(feature = "openssl")] 177 | impl TryFrom<&Certificate> for Signature { 178 | type Error = Error; 179 | 180 | #[inline] 181 | fn try_from(value: &Certificate) -> Result { 182 | match value.version() { 183 | 1 => unsafe { Ok(value.v1.try_into()?) }, 184 | _ => Err(ErrorKind::InvalidInput.into()), 185 | } 186 | } 187 | } 188 | 189 | #[cfg(feature = "openssl")] 190 | impl Verifiable for (&Certificate, &Certificate) { 191 | type Output = (); 192 | 193 | fn verify(self) -> Result<()> { 194 | let key: PublicKey = self.0.try_into()?; 195 | let sig: Signature = self.1.try_into()?; 196 | key.verify(self.1, &sig) 197 | } 198 | } 199 | 200 | impl Certificate { 201 | #[inline] 202 | fn version(&self) -> u32 { 203 | u32::from_le(unsafe { self.version }) 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/certs/sev/ca/chain.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! For operating on OCA certificate chains. 4 | 5 | use super::*; 6 | 7 | use serde::{Deserialize, Serialize}; 8 | 9 | /// A complete OCA certificate chain. 10 | #[repr(C)] 11 | #[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] 12 | pub struct Chain { 13 | /// The AMD Signing Key certificate. 14 | pub ask: Certificate, 15 | 16 | /// The AMD Root Key certificate. 17 | pub ark: Certificate, 18 | } 19 | 20 | impl codicon::Decoder<()> for Chain { 21 | type Error = Error; 22 | 23 | fn decode(mut reader: impl Read, _: ()) -> Result { 24 | let ask = Certificate::decode(&mut reader, ())?; 25 | if Usage::try_from(&ask)? != Usage::ASK { 26 | return Err(ErrorKind::InvalidInput)?; 27 | } 28 | 29 | let ark = Certificate::decode(&mut reader, ())?; 30 | if Usage::try_from(&ark)? != Usage::ARK { 31 | return Err(ErrorKind::InvalidInput)?; 32 | } 33 | 34 | Ok(Self { ask, ark }) 35 | } 36 | } 37 | 38 | impl codicon::Encoder<()> for Chain { 39 | type Error = Error; 40 | 41 | fn encode(&self, mut writer: impl Write, _: ()) -> Result<()> { 42 | self.ask.encode(&mut writer, ())?; 43 | self.ark.encode(&mut writer, ()) 44 | } 45 | } 46 | 47 | #[cfg(feature = "openssl")] 48 | impl<'a> Verifiable for &'a Chain { 49 | type Output = &'a Certificate; 50 | 51 | fn verify(self) -> Result { 52 | (&self.ark, &self.ark).verify()?; 53 | (&self.ark, &self.ask).verify()?; 54 | Ok(&self.ask) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/certs/sev/ca/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! For operating on OCA certificates. 4 | 5 | mod cert; 6 | mod chain; 7 | 8 | pub use cert::Certificate; 9 | pub use chain::Chain; 10 | 11 | use super::*; 12 | 13 | /// Denotes the usage of the certificate. 14 | #[repr(C)] 15 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 16 | pub struct Usage(u32); 17 | 18 | impl Usage { 19 | /// AMD Root Key. 20 | pub const ARK: Usage = Usage(super::Usage::ARK.0); 21 | 22 | /// AMD Signing Key. 23 | pub const ASK: Usage = Usage(super::Usage::ASK.0); 24 | } 25 | 26 | impl TryFrom for Usage { 27 | type Error = (); 28 | 29 | fn try_from(value: super::Usage) -> std::result::Result { 30 | Ok(match value { 31 | super::Usage::ARK => Usage::ARK, 32 | super::Usage::ASK => Usage::ASK, 33 | _ => return Err(()), 34 | }) 35 | } 36 | } 37 | 38 | impl From for super::Usage { 39 | fn from(value: Usage) -> Self { 40 | Self(value.0) 41 | } 42 | } 43 | 44 | impl PartialEq for Usage { 45 | fn eq(&self, other: &super::Usage) -> bool { 46 | self.0 == other.0 47 | } 48 | } 49 | 50 | impl PartialEq for super::Usage { 51 | fn eq(&self, other: &Usage) -> bool { 52 | self.0 == other.0 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/certs/sev/chain.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Utilities for operating on entire certificate chains. 4 | 5 | use super::*; 6 | 7 | use serde::{Deserialize, Serialize}; 8 | 9 | /// A complete certificate chain. 10 | #[repr(C)] 11 | #[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] 12 | pub struct Chain { 13 | /// The Certificate Authority chain. 14 | pub ca: ca::Chain, 15 | 16 | /// The SEV platform chain. 17 | pub sev: sev::Chain, 18 | } 19 | 20 | impl codicon::Decoder<()> for Chain { 21 | type Error = Error; 22 | 23 | fn decode(mut reader: impl Read, _: ()) -> Result { 24 | let sev = sev::Chain::decode(&mut reader, ())?; 25 | let ca = ca::Chain::decode(&mut reader, ())?; 26 | Ok(Self { ca, sev }) 27 | } 28 | } 29 | 30 | impl codicon::Encoder<()> for Chain { 31 | type Error = Error; 32 | 33 | fn encode(&self, mut writer: impl Write, _: ()) -> Result<()> { 34 | self.sev.encode(&mut writer, ())?; 35 | self.ca.encode(&mut writer, ()) 36 | } 37 | } 38 | 39 | #[cfg(feature = "openssl")] 40 | impl<'a> Verifiable for &'a Chain { 41 | type Output = &'a sev::Certificate; 42 | 43 | fn verify(self) -> Result { 44 | let ask = self.ca.verify()?; 45 | (ask, &self.sev.cek).verify()?; 46 | self.sev.verify() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/certs/sev/crypto.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | 5 | use std::fmt::{Debug, Formatter}; 6 | 7 | #[cfg(target_os = "linux")] 8 | impl PrivateKey { 9 | pub(crate) fn derive(&self, cert: &sev::Certificate) -> Result> { 10 | let key = PublicKey::try_from(cert)?; 11 | let mut der = derive::Deriver::new(&self.key)?; 12 | der.set_peer(&key.key)?; 13 | Ok(der.derive_to_vec()?) 14 | } 15 | } 16 | 17 | macro_rules! prv_decoder { 18 | ($($cert:path => $usage:path),+) => { 19 | $( 20 | impl codicon::Decoder<&$cert> for PrivateKey<$usage> { 21 | type Error = Error; 22 | 23 | fn decode(mut reader: impl Read, params: &$cert) -> Result { 24 | let mut buf = Vec::new(); 25 | reader.read_to_end(&mut buf)?; 26 | 27 | let prv = pkey::PKey::private_key_from_der(&buf)?; 28 | let key = PublicKey::try_from(params)?; 29 | if !prv.public_eq(&key.key) { 30 | return Err(ErrorKind::InvalidData)?; 31 | } 32 | 33 | Ok(PrivateKey { 34 | usage: key.usage, 35 | hash: key.hash, 36 | id: key.id, 37 | key: prv, 38 | }) 39 | } 40 | } 41 | )+ 42 | }; 43 | } 44 | 45 | prv_decoder! { 46 | sev::Certificate => sev::Usage, 47 | ca::Certificate => ca::Usage 48 | } 49 | 50 | impl codicon::Encoder<()> for PrivateKey { 51 | type Error = Error; 52 | 53 | fn encode(&self, mut writer: impl Write, _: ()) -> Result<()> { 54 | let buf = self.key.private_key_to_der()?; 55 | writer.write_all(&buf) 56 | } 57 | } 58 | 59 | impl> std::fmt::Display for PublicKey { 60 | fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { 61 | use std::fmt::Error; 62 | 63 | let sig = matches!( 64 | self.usage.into(), 65 | Usage::CEK | Usage::OCA | Usage::PEK | Usage::ARK | Usage::ASK 66 | ); 67 | 68 | match (sig, self.key.id()) { 69 | (true, pkey::Id::RSA) => write!( 70 | f, 71 | "R{} R{}", 72 | self.key.rsa()?.size() * 8, 73 | self.hash.size() * 8 74 | ), 75 | 76 | (true, pkey::Id::EC) => write!( 77 | f, 78 | "EP{} E{}", 79 | self.key.ec_key()?.group().degree(), 80 | self.hash.size() * 8 81 | ), 82 | 83 | (false, pkey::Id::EC) => write!( 84 | f, 85 | "EP{} D{}", 86 | self.key.ec_key()?.group().degree(), 87 | self.hash.size() * 8 88 | ), 89 | 90 | _ => Err(Error), 91 | } 92 | } 93 | } 94 | 95 | impl PublicKey 96 | where 97 | U: Debug, 98 | Usage: PartialEq, 99 | { 100 | /// Verifies the provided signatures signed or did not sign the message digest provided. 101 | pub fn verify( 102 | &self, 103 | msg: &impl codicon::Encoder, 104 | sig: &Signature, 105 | ) -> Result<()> { 106 | let usage = sig.usage == self.usage; 107 | let kind = sig.kind == self.key.id(); 108 | let hash = sig.hash == self.hash; 109 | let id = sig.id.is_none() || sig.id == self.id; 110 | if !usage || !kind || !hash || !id { 111 | return Err(ErrorKind::InvalidInput)?; 112 | } 113 | 114 | let mut ver = sign::Verifier::new(sig.hash, &self.key)?; 115 | if self.key.id() == pkey::Id::RSA { 116 | ver.set_rsa_padding(rsa::Padding::PKCS1_PSS)?; 117 | ver.set_rsa_pss_saltlen(sign::RsaPssSaltlen::DIGEST_LENGTH)?; 118 | } 119 | 120 | msg.encode(&mut ver, Body)?; 121 | ver.verify(&sig.sig).map(|ok| { 122 | // OpenSSL's verify will return Ok(true) if the signature 123 | // is verified and Ok(false) if not. This patches the result 124 | // to return Err if OpenSSL returns Ok(false). 125 | if ok { 126 | Ok(()) 127 | } else { 128 | Err(ErrorKind::NotFound)? 129 | } 130 | })? 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/certs/sev/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Everything needed for working with AMD SEV certificate chains. 4 | 5 | pub mod builtin; 6 | pub mod ca; 7 | mod chain; 8 | 9 | #[allow(clippy::module_inception)] 10 | pub mod sev; 11 | 12 | #[cfg(feature = "openssl")] 13 | mod util; 14 | 15 | #[cfg(feature = "openssl")] 16 | mod crypto; 17 | 18 | pub use chain::Chain; 19 | 20 | use crate::util::*; 21 | #[cfg(feature = "openssl")] 22 | use util::*; 23 | 24 | use std::{ 25 | convert::*, 26 | io::{Error, ErrorKind, Read, Result, Write}, 27 | }; 28 | 29 | #[cfg(feature = "openssl")] 30 | use openssl::*; 31 | 32 | /// OpenSSL body 33 | #[cfg(feature = "openssl")] 34 | pub(crate) struct Body; 35 | 36 | #[cfg(feature = "openssl")] 37 | /// An interface for types that may contain entities such as 38 | /// signatures that must be verified. 39 | pub trait Verifiable { 40 | /// An output type for successful verification. 41 | type Output; 42 | 43 | /// Self-verifies signatures. 44 | fn verify(self) -> Result; 45 | } 46 | 47 | #[cfg(feature = "openssl")] 48 | /// An interface for types that can sign another type (i.e., a certificate). 49 | pub trait Signer { 50 | /// The now-signed type. 51 | type Output; 52 | 53 | /// Signs the target. 54 | fn sign(&self, target: &mut T) -> Result; 55 | } 56 | 57 | /// OpenSSL related signature 58 | #[cfg(feature = "openssl")] 59 | pub(crate) struct Signature { 60 | id: Option<[u8; 16]>, 61 | sig: Vec, 62 | kind: pkey::Id, 63 | hash: hash::MessageDigest, 64 | usage: Usage, 65 | } 66 | 67 | #[cfg(feature = "openssl")] 68 | /// Represents a private key. 69 | pub struct PrivateKey { 70 | id: Option<[u8; 16]>, 71 | key: pkey::PKey, 72 | hash: hash::MessageDigest, 73 | usage: U, 74 | } 75 | 76 | /// Represents a public key. 77 | #[cfg(feature = "openssl")] 78 | pub(crate) struct PublicKey { 79 | id: Option<[u8; 16]>, 80 | key: pkey::PKey, 81 | hash: hash::MessageDigest, 82 | usage: U, 83 | } 84 | 85 | #[cfg(all(feature = "sev", feature = "openssl"))] 86 | impl PublicKey { 87 | /// Obtains the OpenSSL EcKey within. 88 | pub fn ec_key( 89 | &self, 90 | ) -> std::result::Result, openssl::error::ErrorStack> 91 | { 92 | self.key.ec_key() 93 | } 94 | } 95 | 96 | /// Denotes a certificate's usage. 97 | #[repr(C)] 98 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 99 | pub struct Usage(u32); 100 | 101 | impl Usage { 102 | /// Owner Certificate Authority. 103 | pub const OCA: Usage = Usage(0x1001u32.to_le()); 104 | 105 | /// AMD Root Key. 106 | pub const ARK: Usage = Usage(0x0000u32.to_le()); 107 | 108 | /// AMD Signing Key. 109 | pub const ASK: Usage = Usage(0x0013u32.to_le()); 110 | 111 | /// Chip Endorsement Key. 112 | pub const CEK: Usage = Usage(0x1004u32.to_le()); 113 | 114 | /// Platform Endorsement Key. 115 | pub const PEK: Usage = Usage(0x1002u32.to_le()); 116 | 117 | /// Platform Diffie-Hellman. 118 | pub const PDH: Usage = Usage(0x1003u32.to_le()); 119 | 120 | const INV: Usage = Usage(0x1000u32.to_le()); 121 | } 122 | 123 | impl std::fmt::Display for Usage { 124 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 125 | write!( 126 | f, 127 | "{}", 128 | match *self { 129 | Usage::OCA => "OCA", 130 | Usage::PEK => "PEK", 131 | Usage::PDH => "PDH", 132 | Usage::CEK => "CEK", 133 | Usage::ARK => "ARK", 134 | Usage::ASK => "ASK", 135 | Usage::INV => "INV", 136 | _ => return Err(std::fmt::Error), 137 | } 138 | ) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/certs/sev/sev/cert/v1/algo.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #[cfg(feature = "openssl")] 4 | use super::*; 5 | 6 | #[repr(C)] 7 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 8 | pub struct Algorithm(u32); 9 | 10 | #[allow(dead_code)] 11 | impl Algorithm { 12 | pub const RSA_SHA256: Algorithm = Algorithm(0x0001u32.to_le()); 13 | pub const ECDSA_SHA256: Algorithm = Algorithm(0x0002u32.to_le()); 14 | pub const ECDH_SHA256: Algorithm = Algorithm(0x0003u32.to_le()); 15 | pub const RSA_SHA384: Algorithm = Algorithm(0x0101u32.to_le()); 16 | pub const ECDSA_SHA384: Algorithm = Algorithm(0x0102u32.to_le()); 17 | pub const ECDH_SHA384: Algorithm = Algorithm(0x0103u32.to_le()); 18 | pub const NONE: Algorithm = Algorithm(0x0000u32.to_le()); 19 | } 20 | 21 | impl Default for Algorithm { 22 | fn default() -> Algorithm { 23 | Algorithm::NONE 24 | } 25 | } 26 | 27 | #[cfg(feature = "openssl")] 28 | impl TryFrom for pkey::Id { 29 | type Error = Error; 30 | 31 | fn try_from(value: Algorithm) -> Result { 32 | Ok(match value { 33 | Algorithm::RSA_SHA256 | Algorithm::RSA_SHA384 => pkey::Id::RSA, 34 | Algorithm::ECDSA_SHA256 | Algorithm::ECDSA_SHA384 => pkey::Id::EC, 35 | Algorithm::ECDH_SHA256 | Algorithm::ECDH_SHA384 => pkey::Id::EC, 36 | _ => return Err(ErrorKind::InvalidInput)?, 37 | }) 38 | } 39 | } 40 | 41 | #[cfg(feature = "openssl")] 42 | impl TryFrom for hash::MessageDigest { 43 | type Error = Error; 44 | 45 | fn try_from(value: Algorithm) -> Result { 46 | match value { 47 | Algorithm::RSA_SHA256 | Algorithm::ECDSA_SHA256 | Algorithm::ECDH_SHA256 => { 48 | Ok(hash::MessageDigest::sha256()) 49 | } 50 | 51 | Algorithm::RSA_SHA384 | Algorithm::ECDSA_SHA384 | Algorithm::ECDH_SHA384 => { 52 | Ok(hash::MessageDigest::sha384()) 53 | } 54 | 55 | _ => Err(ErrorKind::InvalidInput)?, 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/certs/sev/sev/cert/v1/body/key/ecc/group.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #[cfg(feature = "openssl")] 4 | use super::*; 5 | 6 | #[repr(C)] 7 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 8 | pub struct Group(u32); 9 | 10 | #[cfg(feature = "openssl")] 11 | impl Group { 12 | pub const P256: Group = Group(1u32.to_le()); 13 | pub const P384: Group = Group(2u32.to_le()); 14 | 15 | pub fn size(self) -> Result { 16 | Ok(match self { 17 | Group::P256 => 32, 18 | Group::P384 => 48, 19 | _ => return Err(ErrorKind::InvalidInput)?, 20 | }) 21 | } 22 | } 23 | 24 | #[cfg(feature = "openssl")] 25 | impl TryFrom for nid::Nid { 26 | type Error = Error; 27 | 28 | fn try_from(value: Group) -> Result { 29 | Ok(match value { 30 | Group::P256 => nid::Nid::X9_62_PRIME256V1, 31 | Group::P384 => nid::Nid::SECP384R1, 32 | _ => return Err(ErrorKind::InvalidInput)?, 33 | }) 34 | } 35 | } 36 | 37 | #[cfg(feature = "openssl")] 38 | impl TryFrom for Group { 39 | type Error = Error; 40 | 41 | fn try_from(value: nid::Nid) -> Result { 42 | Ok(match value { 43 | nid::Nid::X9_62_PRIME256V1 => Group::P256, 44 | nid::Nid::SECP384R1 => Group::P384, 45 | _ => return Err(ErrorKind::InvalidInput)?, 46 | }) 47 | } 48 | } 49 | 50 | #[cfg(feature = "openssl")] 51 | impl TryFrom for ec::EcGroup { 52 | type Error = Error; 53 | 54 | fn try_from(value: Group) -> Result { 55 | Ok(ec::EcGroup::from_curve_name(value.try_into()?)?) 56 | } 57 | } 58 | 59 | #[cfg(feature = "openssl")] 60 | impl TryFrom<&ec::EcGroupRef> for Group { 61 | type Error = Error; 62 | 63 | fn try_from(value: &ec::EcGroupRef) -> Result { 64 | value 65 | .curve_name() 66 | .ok_or(ErrorKind::InvalidInput)? 67 | .try_into() 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/certs/sev/sev/cert/v1/body/key/ecc/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | pub mod group; 4 | 5 | #[cfg(feature = "openssl")] 6 | use super::*; 7 | 8 | #[repr(C)] 9 | #[derive(Copy, Clone)] 10 | pub struct PubKey { 11 | g: group::Group, 12 | x: [u8; 72], 13 | y: [u8; 72], 14 | } 15 | 16 | impl std::fmt::Debug for PubKey { 17 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 18 | write!( 19 | f, 20 | "PubKey {{ group: {:?}, x: {:?}, y: {:?} }}", 21 | self.g, 22 | self.x.iter(), 23 | self.y.iter() 24 | ) 25 | } 26 | } 27 | 28 | impl Eq for PubKey {} 29 | impl PartialEq for PubKey { 30 | fn eq(&self, other: &PubKey) -> bool { 31 | self.g == other.g && self.x[..] == other.x[..] && self.y[..] == other.y[..] 32 | } 33 | } 34 | 35 | #[cfg(feature = "openssl")] 36 | impl TryFrom<&PubKey> for ec::EcKey { 37 | type Error = Error; 38 | 39 | fn try_from(value: &PubKey) -> Result { 40 | let s = value.g.size()?; 41 | Ok(ec::EcKey::from_public_key_affine_coordinates( 42 | &*ec::EcGroup::try_from(value.g)?, 43 | &*bn::BigNum::from_le(&value.x[..s])?, 44 | &*bn::BigNum::from_le(&value.y[..s])?, 45 | )?) 46 | } 47 | } 48 | 49 | #[cfg(feature = "openssl")] 50 | impl TryFrom<&PubKey> for pkey::PKey { 51 | type Error = Error; 52 | 53 | fn try_from(value: &PubKey) -> Result { 54 | Ok(pkey::PKey::from_ec_key(value.try_into()?)?) 55 | } 56 | } 57 | 58 | #[cfg(feature = "openssl")] 59 | impl TryFrom<&ec::EcKey> for PubKey { 60 | type Error = Error; 61 | 62 | fn try_from(value: &ec::EcKey) -> Result { 63 | let g = value.group(); 64 | let mut c = bn::BigNumContext::new()?; 65 | let mut x = bn::BigNum::new()?; 66 | let mut y = bn::BigNum::new()?; 67 | 68 | value 69 | .public_key() 70 | .affine_coordinates_gfp(g, &mut x, &mut y, &mut c)?; 71 | Ok(Self { 72 | g: group::Group::try_from(g)?, 73 | x: x.as_le_bytes(), 74 | y: y.as_le_bytes(), 75 | }) 76 | } 77 | } 78 | 79 | #[cfg(feature = "openssl")] 80 | impl PubKey { 81 | pub fn generate(group: group::Group) -> Result<(Self, ec::EcKey)> { 82 | let grp: ec::EcGroup = group.try_into()?; 83 | let prv = ec::EcKey::generate(&grp)?; 84 | Ok((Self::try_from(&prv)?, prv)) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/certs/sev/sev/cert/v1/body/key/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | pub mod ecc; 4 | pub mod rsa; 5 | 6 | use super::*; 7 | 8 | #[repr(C)] 9 | #[derive(Copy, Clone)] 10 | union PubKeys { 11 | ecc: ecc::PubKey, 12 | rsa: rsa::PubKey, 13 | } 14 | 15 | #[repr(C)] 16 | #[derive(Copy, Clone)] 17 | pub struct PubKey { 18 | pub usage: Usage, 19 | pub algo: Algorithm, 20 | key: PubKeys, 21 | } 22 | 23 | impl std::fmt::Debug for PubKey { 24 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 25 | match self.algo { 26 | Algorithm::RSA_SHA256 | Algorithm::RSA_SHA384 => write!( 27 | f, 28 | "PubKey {{ usage: {:?}, algo: {:?}, key: {:?} }}", 29 | self.usage, 30 | self.algo, 31 | unsafe { self.key.rsa } 32 | ), 33 | 34 | _ => write!( 35 | f, 36 | "PubKey {{ usage: {:?}, algo: {:?}, key: {:?} }}", 37 | self.usage, 38 | self.algo, 39 | unsafe { self.key.ecc } 40 | ), 41 | } 42 | } 43 | } 44 | 45 | impl Eq for PubKey {} 46 | impl PartialEq for PubKey { 47 | fn eq(&self, other: &PubKey) -> bool { 48 | self.usage == other.usage 49 | && self.algo == other.algo 50 | && match self.algo { 51 | Algorithm::RSA_SHA256 | Algorithm::RSA_SHA384 => unsafe { 52 | self.key.rsa == other.key.rsa 53 | }, 54 | _ => unsafe { self.key.ecc == other.key.ecc }, 55 | } 56 | } 57 | } 58 | 59 | #[cfg(feature = "openssl")] 60 | impl TryFrom<&PubKey> for pkey::PKey { 61 | type Error = Error; 62 | 63 | fn try_from(v: &PubKey) -> Result { 64 | match v.algo.try_into()? { 65 | pkey::Id::RSA => Ok(unsafe { &v.key.rsa }.try_into()?), 66 | pkey::Id::EC => Ok(unsafe { &v.key.ecc }.try_into()?), 67 | _ => Err(ErrorKind::InvalidInput)?, 68 | } 69 | } 70 | } 71 | 72 | #[cfg(feature = "openssl")] 73 | impl TryFrom<&PubKey> for PublicKey { 74 | type Error = Error; 75 | 76 | fn try_from(value: &PubKey) -> Result { 77 | let hash = value.algo.try_into()?; 78 | let key = value.try_into()?; 79 | Ok(Self { 80 | hash, 81 | key, 82 | id: None, 83 | usage: value.usage, 84 | }) 85 | } 86 | } 87 | 88 | #[cfg(feature = "openssl")] 89 | impl PubKey { 90 | pub fn generate(usage: Usage) -> Result<(PubKey, PrivateKey)> { 91 | let algo = match usage { 92 | Usage::OCA => Algorithm::ECDSA_SHA256, 93 | Usage::PEK => Algorithm::ECDSA_SHA256, 94 | Usage::CEK => Algorithm::ECDSA_SHA256, 95 | Usage::PDH => Algorithm::ECDH_SHA256, 96 | _ => return Err(ErrorKind::InvalidInput)?, 97 | }; 98 | 99 | let (key, prv) = ecc::PubKey::generate(ecc::group::Group::P384)?; 100 | let prv = pkey::PKey::from_ec_key(prv)?; 101 | 102 | Ok(( 103 | Self { 104 | usage, 105 | algo, 106 | key: PubKeys { ecc: key }, 107 | }, 108 | PrivateKey { 109 | usage, 110 | key: prv, 111 | id: None, 112 | hash: algo.try_into()?, 113 | }, 114 | )) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/certs/sev/sev/cert/v1/body/key/rsa.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | 5 | #[cfg(feature = "openssl")] 6 | use openssl::rsa; 7 | 8 | #[repr(C)] 9 | #[derive(Copy, Clone)] 10 | pub struct PubKey { 11 | modulus_size: u32, 12 | pubexp: [u8; 512], 13 | modulus: [u8; 512], 14 | } 15 | 16 | impl PubKey { 17 | fn bytes(&self) -> Result { 18 | match u32::from_le(self.modulus_size) { 19 | 2048 => Ok(256), 20 | 4096 => Ok(512), 21 | _ => Err(ErrorKind::InvalidInput)?, 22 | } 23 | } 24 | } 25 | 26 | impl std::fmt::Debug for PubKey { 27 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 28 | let size = self.bytes().or(Err(std::fmt::Error))?; 29 | let pdata = &self.pubexp[..size]; 30 | let mdata = &self.modulus[..size]; 31 | write!( 32 | f, 33 | "PubKey {{ modulus_size: {:?}, pubexp: {:?}, modulus: {:?} }}", 34 | self.modulus_size, pdata, mdata 35 | ) 36 | } 37 | } 38 | 39 | impl Eq for PubKey {} 40 | impl PartialEq for PubKey { 41 | fn eq(&self, other: &PubKey) -> bool { 42 | self.modulus_size == other.modulus_size 43 | && self.pubexp[..] == other.pubexp[..] 44 | && self.modulus[..] == other.modulus[..] 45 | } 46 | } 47 | 48 | #[cfg(feature = "openssl")] 49 | impl TryFrom<&PubKey> for rsa::Rsa { 50 | type Error = Error; 51 | 52 | fn try_from(value: &PubKey) -> Result { 53 | let s = value.bytes()?; 54 | Ok(rsa::Rsa::from_public_components( 55 | bn::BigNum::from_le(&value.modulus[..s])?, 56 | bn::BigNum::from_le(&value.pubexp[..s])?, 57 | )?) 58 | } 59 | } 60 | 61 | #[cfg(feature = "openssl")] 62 | impl TryFrom<&PubKey> for pkey::PKey { 63 | type Error = Error; 64 | 65 | fn try_from(value: &PubKey) -> Result { 66 | Ok(pkey::PKey::from_rsa(value.try_into()?)?) 67 | } 68 | } 69 | 70 | #[cfg(feature = "openssl")] 71 | impl PubKey { 72 | pub fn generate(bits: u32) -> Result<(Self, rsa::Rsa)> { 73 | let prv = rsa::Rsa::generate(bits)?; 74 | let n = prv.n().as_le_bytes(); 75 | let e = prv.e().as_le_bytes(); 76 | Ok(( 77 | Self { 78 | modulus_size: bits.to_le(), 79 | pubexp: e, 80 | modulus: n, 81 | }, 82 | prv, 83 | )) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/certs/sev/sev/cert/v1/body/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | pub mod key; 4 | 5 | use super::*; 6 | 7 | #[repr(C, packed)] 8 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 9 | pub struct Data { 10 | pub firmware: crate::firmware::host::Version, 11 | pub reserved: u16, 12 | pub key: key::PubKey, 13 | } 14 | 15 | #[repr(C, packed)] 16 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 17 | pub struct Body { 18 | pub ver: u32, 19 | pub data: Data, 20 | } 21 | 22 | #[cfg(feature = "openssl")] 23 | impl Body { 24 | pub fn generate(usage: Usage) -> Result<(Body, PrivateKey)> { 25 | let (key, prv) = key::PubKey::generate(usage)?; 26 | Ok(( 27 | Body { 28 | ver: 1u32.to_le(), 29 | data: Data { 30 | firmware: Default::default(), 31 | reserved: 0, 32 | key, 33 | }, 34 | }, 35 | prv, 36 | )) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/certs/sev/sev/cert/v1/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | mod algo; 4 | pub mod body; 5 | pub mod sig; 6 | 7 | use super::*; 8 | 9 | use algo::Algorithm; 10 | 11 | #[repr(C)] 12 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 13 | pub struct Certificate { 14 | pub body: body::Body, 15 | pub sigs: [sig::Signature; 2], 16 | } 17 | 18 | #[cfg(feature = "openssl")] 19 | impl Certificate { 20 | pub fn generate(usage: Usage) -> Result<(Self, PrivateKey)> { 21 | let (body, prv) = body::Body::generate(usage)?; 22 | Ok(( 23 | Self { 24 | body, 25 | sigs: [sig::Signature::default(), sig::Signature::default()], 26 | }, 27 | prv, 28 | )) 29 | } 30 | } 31 | 32 | impl codicon::Decoder<()> for Certificate { 33 | type Error = Error; 34 | 35 | fn decode(mut reader: impl Read, _: ()) -> Result { 36 | Ok(Self { 37 | body: body::Body { 38 | ver: 1u32.to_le(), 39 | data: reader.load()?, 40 | }, 41 | sigs: [reader.load()?, reader.load()?], 42 | }) 43 | } 44 | } 45 | 46 | #[cfg(feature = "openssl")] 47 | impl Signer for PrivateKey { 48 | type Output = (); 49 | 50 | fn sign(&self, target: &mut Certificate) -> Result<()> { 51 | let slot = if target.sigs[0].is_empty() { 52 | &mut target.sigs[0] 53 | } else if target.sigs[1].is_empty() { 54 | &mut target.sigs[1] 55 | } else { 56 | return Err(ErrorKind::InvalidInput)?; 57 | }; 58 | 59 | let mut sig = sign::Signer::new(self.hash, &self.key)?; 60 | if self.key.id() == pkey::Id::RSA { 61 | sig.set_rsa_padding(rsa::Padding::PKCS1_PSS)?; 62 | sig.set_rsa_pss_saltlen(sign::RsaPssSaltlen::DIGEST_LENGTH)?; 63 | } 64 | 65 | sig.save(&target.body)?; 66 | 67 | let sig = crate::certs::sev::Signature { 68 | usage: self.usage.into(), 69 | hash: self.hash, 70 | kind: self.key.id(), 71 | sig: sig.sign_to_vec()?, 72 | id: self.id, 73 | }; 74 | 75 | *slot = sig::Signature::try_from(sig)?; 76 | Ok(()) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/certs/sev/sev/cert/v1/sig/ecdsa.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #[cfg(feature = "openssl")] 4 | use {super::*, openssl::ecdsa}; 5 | 6 | use crate::util::array::Array; 7 | 8 | use serde::{Deserialize, Serialize}; 9 | 10 | const SIG_PIECE_SIZE: usize = std::mem::size_of::<[u8; 72]>(); 11 | 12 | /// An ECDSA Signature. 13 | #[repr(C)] 14 | #[derive(Default, Copy, Clone, Deserialize, Serialize)] 15 | pub struct Signature { 16 | r: Array, 17 | 18 | s: Array, 19 | 20 | _reserved: Array, 21 | } 22 | 23 | impl std::fmt::Debug for Signature { 24 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 25 | write!( 26 | f, 27 | "Signature {{ r: {:?}, s: {:?} }}", 28 | self.r.iter(), 29 | self.s.iter() 30 | ) 31 | } 32 | } 33 | 34 | impl Eq for Signature {} 35 | impl PartialEq for Signature { 36 | fn eq(&self, other: &Signature) -> bool { 37 | self.r[..] == other.r[..] && self.s[..] == other.s[..] 38 | } 39 | } 40 | 41 | impl std::fmt::Display for Signature { 42 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 43 | write!( 44 | f, 45 | r#" 46 | Signature: 47 | R: {} 48 | S: {} 49 | "#, 50 | self.r, self.s 51 | ) 52 | } 53 | } 54 | 55 | #[cfg(feature = "openssl")] 56 | impl From for Signature { 57 | #[inline] 58 | fn from(value: ecdsa::EcdsaSig) -> Self { 59 | Signature { 60 | r: Array(value.r().as_le_bytes()), 61 | s: Array(value.s().as_le_bytes()), 62 | _reserved: Array([0; 512 - (SIG_PIECE_SIZE * 2)]), 63 | } 64 | } 65 | } 66 | 67 | #[cfg(feature = "openssl")] 68 | impl TryFrom<&[u8]> for Signature { 69 | type Error = Error; 70 | 71 | #[inline] 72 | fn try_from(value: &[u8]) -> Result { 73 | Ok(ecdsa::EcdsaSig::from_der(value)?.into()) 74 | } 75 | } 76 | 77 | #[cfg(feature = "openssl")] 78 | impl TryFrom<&Array> for ecdsa::EcdsaSig { 79 | type Error = Error; 80 | 81 | #[inline] 82 | fn try_from(value: &Array) -> Result { 83 | let arr: &[u8] = value.as_ref(); 84 | let r = bn::BigNum::from_le(&arr[..SIG_PIECE_SIZE])?; 85 | let s = bn::BigNum::from_le(&arr[SIG_PIECE_SIZE..])?; 86 | Ok(ecdsa::EcdsaSig::from_private_components(r, s)?) 87 | } 88 | } 89 | 90 | #[cfg(feature = "openssl")] 91 | impl TryFrom<&Signature> for ecdsa::EcdsaSig { 92 | type Error = Error; 93 | 94 | #[inline] 95 | fn try_from(value: &Signature) -> Result { 96 | let r = bn::BigNum::from_le(&*value.r)?; 97 | let s = bn::BigNum::from_le(&*value.s)?; 98 | Ok(ecdsa::EcdsaSig::from_private_components(r, s)?) 99 | } 100 | } 101 | 102 | #[cfg(feature = "openssl")] 103 | impl TryFrom<&Signature> for Vec { 104 | type Error = Error; 105 | 106 | #[inline] 107 | fn try_from(value: &Signature) -> Result { 108 | Ok(ecdsa::EcdsaSig::try_from(value)?.to_der()?) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/certs/sev/sev/cert/v1/sig/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | pub(crate) mod ecdsa; 4 | mod rsa; 5 | 6 | use super::*; 7 | use crate::certs::sev::Usage; 8 | 9 | #[repr(C)] 10 | #[derive(Copy, Clone)] 11 | union Signatures { 12 | ecdsa: ecdsa::Signature, 13 | rsa: rsa::Signature, 14 | } 15 | 16 | #[allow(clippy::derivable_impls)] 17 | impl Default for Signatures { 18 | fn default() -> Self { 19 | Signatures { 20 | rsa: rsa::Signature::default(), 21 | } 22 | } 23 | } 24 | 25 | #[repr(C)] 26 | #[derive(Copy, Clone)] 27 | pub struct Signature { 28 | usage: Usage, 29 | algo: Algorithm, 30 | sig: Signatures, 31 | } 32 | 33 | impl Default for Signature { 34 | fn default() -> Self { 35 | Signature { 36 | usage: Usage::INV, 37 | algo: Algorithm::NONE, 38 | sig: Signatures::default(), 39 | } 40 | } 41 | } 42 | 43 | impl std::fmt::Debug for Signature { 44 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 45 | match self.algo { 46 | Algorithm::RSA_SHA256 | Algorithm::RSA_SHA384 => write!( 47 | f, 48 | "Signature {{ usage: {:?}, algo: {:?}, sig: {:?} }}", 49 | self.usage, 50 | self.algo, 51 | unsafe { self.sig.rsa } 52 | ), 53 | 54 | _ => write!( 55 | f, 56 | "Signature {{ usage: {:?}, algo: {:?}, sig: {:?} }}", 57 | self.usage, 58 | self.algo, 59 | unsafe { self.sig.ecdsa } 60 | ), 61 | } 62 | } 63 | } 64 | 65 | impl Eq for Signature {} 66 | impl PartialEq for Signature { 67 | fn eq(&self, other: &Signature) -> bool { 68 | self.usage == other.usage 69 | && self.algo == other.algo 70 | && match self.algo { 71 | Algorithm::RSA_SHA256 | Algorithm::RSA_SHA384 => unsafe { 72 | self.sig.rsa == other.sig.rsa 73 | }, 74 | 75 | _ => unsafe { self.sig.ecdsa == other.sig.ecdsa }, 76 | } 77 | } 78 | } 79 | 80 | #[cfg(feature = "openssl")] 81 | impl TryFrom for Signature { 82 | type Error = Error; 83 | 84 | fn try_from(value: crate::certs::sev::Signature) -> Result { 85 | if value.id.is_some() { 86 | return Err(ErrorKind::InvalidInput)?; 87 | } 88 | 89 | let (sig, algo) = match value.kind { 90 | pkey::Id::RSA => { 91 | let rsa = rsa::Signature::try_from(&value.sig[..])?; 92 | let sig = Signatures { rsa }; 93 | match value.hash.type_() { 94 | nid::Nid::SHA256 => (sig, Algorithm::RSA_SHA256), 95 | nid::Nid::SHA384 => (sig, Algorithm::RSA_SHA384), 96 | _ => return Err(ErrorKind::InvalidInput)?, 97 | } 98 | } 99 | 100 | pkey::Id::EC => { 101 | let ecdsa = ecdsa::Signature::try_from(&value.sig[..])?; 102 | let sig = Signatures { ecdsa }; 103 | match value.hash.type_() { 104 | nid::Nid::SHA256 => (sig, Algorithm::ECDSA_SHA256), 105 | nid::Nid::SHA384 => (sig, Algorithm::ECDSA_SHA384), 106 | _ => return Err(ErrorKind::InvalidInput)?, 107 | } 108 | } 109 | 110 | _ => return Err(ErrorKind::InvalidInput)?, 111 | }; 112 | 113 | Ok(Signature { 114 | usage: value.usage, 115 | algo, 116 | sig, 117 | }) 118 | } 119 | } 120 | 121 | #[cfg(feature = "openssl")] 122 | impl TryFrom<&Signature> for Option { 123 | type Error = Error; 124 | 125 | fn try_from(value: &Signature) -> Result { 126 | if value.is_empty() { 127 | return Ok(None); 128 | } 129 | 130 | let usage = value.usage; 131 | let hash = value.algo.try_into()?; 132 | let kind = value.algo.try_into()?; 133 | let sig = match kind { 134 | pkey::Id::RSA => Vec::try_from(unsafe { &value.sig.rsa })?, 135 | pkey::Id::EC => Vec::try_from(unsafe { &value.sig.ecdsa })?, 136 | _ => return Err(ErrorKind::InvalidInput)?, 137 | }; 138 | 139 | Ok(Some(crate::certs::sev::Signature { 140 | hash, 141 | kind, 142 | sig, 143 | usage, 144 | id: None, 145 | })) 146 | } 147 | } 148 | 149 | impl Signature { 150 | #[cfg(feature = "openssl")] 151 | pub fn is_empty(&self) -> bool { 152 | match self.usage { 153 | Usage::OCA | Usage::CEK | Usage::PEK | Usage::PDH | Usage::ARK | Usage::ASK => { 154 | !matches!( 155 | self.algo, 156 | Algorithm::RSA_SHA256 157 | | Algorithm::RSA_SHA384 158 | | Algorithm::ECDSA_SHA256 159 | | Algorithm::ECDSA_SHA384 160 | ) 161 | } 162 | _ => true, 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/certs/sev/sev/cert/v1/sig/rsa.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #[cfg(feature = "openssl")] 4 | use super::*; 5 | 6 | #[repr(C)] 7 | #[derive(Copy, Clone)] 8 | pub struct Signature([u8; 512]); 9 | 10 | impl std::fmt::Debug for Signature { 11 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 12 | write!(f, "Signature({:?})", self.0.iter()) 13 | } 14 | } 15 | 16 | impl Eq for Signature {} 17 | impl PartialEq for Signature { 18 | fn eq(&self, other: &Signature) -> bool { 19 | self.0[..] == other.0[..] 20 | } 21 | } 22 | 23 | #[allow(clippy::derivable_impls)] 24 | impl Default for Signature { 25 | fn default() -> Self { 26 | Signature([0u8; 512]) 27 | } 28 | } 29 | 30 | #[cfg(feature = "openssl")] 31 | impl From for Signature { 32 | #[inline] 33 | fn from(value: bn::BigNum) -> Self { 34 | Signature(value.as_le_bytes()) 35 | } 36 | } 37 | 38 | #[cfg(feature = "openssl")] 39 | impl TryFrom<&[u8]> for Signature { 40 | type Error = Error; 41 | 42 | #[inline] 43 | fn try_from(value: &[u8]) -> Result { 44 | Ok(bn::BigNum::from_slice(value)?.into()) 45 | } 46 | } 47 | 48 | #[cfg(feature = "openssl")] 49 | impl TryFrom<&Signature> for bn::BigNum { 50 | type Error = Error; 51 | 52 | #[inline] 53 | fn try_from(value: &Signature) -> Result { 54 | bn::BigNum::from_le(&value.0) 55 | } 56 | } 57 | 58 | #[cfg(feature = "openssl")] 59 | impl TryFrom<&Signature> for Vec { 60 | type Error = Error; 61 | 62 | #[inline] 63 | fn try_from(value: &Signature) -> Result { 64 | Ok(bn::BigNum::try_from(value)?.to_vec()) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/certs/sev/sev/chain.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! For operating on the SEV platform certificate chain. 4 | 5 | use super::*; 6 | 7 | use serde::{Deserialize, Serialize}; 8 | 9 | /// The SEV certificate chain. 10 | #[repr(C)] 11 | #[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] 12 | pub struct Chain { 13 | /// The Platform Diffie-Hellman certificate. 14 | pub pdh: Certificate, 15 | 16 | /// The certificate for the PEK. 17 | pub pek: Certificate, 18 | 19 | /// The certificate for the OCA. 20 | pub oca: Certificate, 21 | 22 | /// The certificate for the CEK. 23 | pub cek: Certificate, 24 | } 25 | 26 | impl codicon::Decoder<()> for Chain { 27 | type Error = Error; 28 | 29 | fn decode(mut reader: impl Read, _: ()) -> Result { 30 | let pdh = Certificate::decode(&mut reader, ())?; 31 | if Usage::try_from(&pdh)? != Usage::PDH { 32 | return Err(ErrorKind::InvalidInput)?; 33 | } 34 | 35 | let pek = Certificate::decode(&mut reader, ())?; 36 | if Usage::try_from(&pek)? != Usage::PEK { 37 | return Err(ErrorKind::InvalidInput)?; 38 | } 39 | 40 | let oca = Certificate::decode(&mut reader, ())?; 41 | if Usage::try_from(&oca)? != Usage::OCA { 42 | return Err(ErrorKind::InvalidInput)?; 43 | } 44 | 45 | let cek = Certificate::decode(&mut reader, ())?; 46 | if Usage::try_from(&cek)? != Usage::CEK { 47 | return Err(ErrorKind::InvalidInput)?; 48 | } 49 | 50 | Ok(Self { pdh, pek, oca, cek }) 51 | } 52 | } 53 | 54 | impl codicon::Encoder<()> for Chain { 55 | type Error = Error; 56 | 57 | fn encode(&self, mut writer: impl Write, _: ()) -> Result<()> { 58 | self.pdh.encode(&mut writer, ())?; 59 | self.pek.encode(&mut writer, ())?; 60 | self.oca.encode(&mut writer, ())?; 61 | self.cek.encode(&mut writer, ()) 62 | } 63 | } 64 | 65 | #[cfg(feature = "openssl")] 66 | impl<'a> Verifiable for &'a Chain { 67 | type Output = &'a Certificate; 68 | 69 | fn verify(self) -> Result { 70 | (&self.oca, &self.oca).verify()?; 71 | (&self.oca, &self.pek).verify()?; 72 | (&self.cek, &self.pek).verify()?; 73 | (&self.pek, &self.pdh).verify()?; 74 | Ok(&self.pdh) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/certs/sev/sev/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! For operating on SEV certificates. 4 | 5 | pub(crate) mod cert; 6 | mod chain; 7 | 8 | pub use cert::v1::sig::ecdsa::Signature as EcdsaSignature; 9 | pub use cert::Certificate; 10 | pub use chain::Chain; 11 | 12 | use super::*; 13 | 14 | /// Denotes the usage of a SEV certificate. 15 | #[repr(C)] 16 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 17 | pub struct Usage(u32); 18 | 19 | impl Usage { 20 | /// Owner Certificate Authority. 21 | pub const OCA: Usage = Usage(super::Usage::OCA.0); 22 | 23 | /// Chip Endorsement Key. 24 | pub const CEK: Usage = Usage(super::Usage::CEK.0); 25 | 26 | /// Platform Endorsement Key. 27 | pub const PEK: Usage = Usage(super::Usage::PEK.0); 28 | 29 | /// Platform Diffie-Hellman (PDH). 30 | pub const PDH: Usage = Usage(super::Usage::PDH.0); 31 | } 32 | 33 | impl TryFrom for Usage { 34 | type Error = (); 35 | 36 | fn try_from(value: super::Usage) -> std::result::Result { 37 | Ok(match value { 38 | super::Usage::OCA => Usage::OCA, 39 | super::Usage::CEK => Usage::CEK, 40 | super::Usage::PEK => Usage::PEK, 41 | super::Usage::PDH => Usage::PDH, 42 | _ => return Err(()), 43 | }) 44 | } 45 | } 46 | 47 | impl From for super::Usage { 48 | fn from(value: Usage) -> Self { 49 | Self(value.0) 50 | } 51 | } 52 | 53 | impl PartialEq for Usage { 54 | fn eq(&self, other: &super::Usage) -> bool { 55 | self.0 == other.0 56 | } 57 | } 58 | 59 | impl PartialEq for super::Usage { 60 | fn eq(&self, other: &Usage) -> bool { 61 | self.0 == other.0 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/certs/sev/util.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use std::io::Result; 4 | 5 | pub trait FromLe: Sized { 6 | fn from_le(value: &[u8]) -> Result; 7 | } 8 | 9 | pub trait AsLeBytes { 10 | fn as_le_bytes(&self) -> T; 11 | } 12 | 13 | impl FromLe for openssl::bn::BigNum { 14 | #[inline] 15 | fn from_le(value: &[u8]) -> Result { 16 | Ok(Self::from_slice( 17 | &value.iter().rev().cloned().collect::>(), 18 | )?) 19 | } 20 | } 21 | 22 | impl AsLeBytes<[u8; 72]> for openssl::bn::BigNumRef { 23 | fn as_le_bytes(&self) -> [u8; 72] { 24 | let mut buf = [0u8; 72]; 25 | 26 | for (i, b) in self.to_vec().iter().rev().cloned().enumerate() { 27 | buf[i] = b; 28 | } 29 | 30 | buf 31 | } 32 | } 33 | 34 | impl AsLeBytes<[u8; 512]> for openssl::bn::BigNumRef { 35 | fn as_le_bytes(&self) -> [u8; 512] { 36 | let mut buf = [0u8; 512]; 37 | 38 | for (i, b) in self.to_vec().iter().rev().cloned().enumerate() { 39 | buf[i] = b; 40 | } 41 | 42 | buf 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/certs/snp/builtin/genoa/ark.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 36 | /wMz1R1BHg== 37 | -----END CERTIFICATE----- 38 | -------------------------------------------------------------------------------- /src/certs/snp/builtin/genoa/ask.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 37 | -----END CERTIFICATE----- 38 | -------------------------------------------------------------------------------- /src/certs/snp/builtin/genoa/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | 5 | /// The public Genoa ARK certificate (PEM-encoded). 6 | pub const ARK: &[u8] = include_bytes!("ark.pem"); 7 | 8 | /// The public Genoa ASK certificate (PEM-encoded). 9 | pub const ASK: &[u8] = include_bytes!("ask.pem"); 10 | 11 | /// Get the Genoa ARK Certificate. 12 | pub fn ark() -> Result { 13 | Certificate::from_pem(ARK) 14 | } 15 | 16 | /// Get the Genoa ASK Certificate. 17 | pub fn ask() -> Result { 18 | Certificate::from_pem(ASK) 19 | } 20 | 21 | mod tests { 22 | #[test] 23 | fn ark_self_signed() { 24 | use super::*; 25 | 26 | let ark = ark().unwrap(); 27 | 28 | (&ark, &ark).verify().unwrap(); 29 | } 30 | 31 | #[test] 32 | fn ark_signs_ask() { 33 | use super::*; 34 | 35 | let ark = ark().unwrap(); 36 | let ask = ask().unwrap(); 37 | 38 | (&ark, &ask).verify().unwrap(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/certs/snp/builtin/milan/ark.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC 3 | BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS 4 | BgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg 5 | Q2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp 6 | Y2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy 7 | MTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS 8 | BgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j 9 | ZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG 10 | 9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg 11 | W41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta 12 | 1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2 13 | SzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0 14 | 60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05 15 | gmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg 16 | bKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs 17 | +gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi 18 | Qi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ 19 | eTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18 20 | fHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j 21 | WhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI 22 | rFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG 23 | KWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG 24 | SIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI 25 | AWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel 26 | ETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw 27 | STjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK 28 | dHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq 29 | zT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp 30 | KGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e 31 | pmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq 32 | HnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh 33 | 3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn 34 | JZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH 35 | CViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4 36 | AFZEAwoKCQ== 37 | -----END CERTIFICATE----- 38 | -------------------------------------------------------------------------------- /src/certs/snp/builtin/milan/ask.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 37 | -----END CERTIFICATE----- 38 | -------------------------------------------------------------------------------- /src/certs/snp/builtin/milan/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | 5 | /// The public Milan ARK certificate (PEM-encoded). 6 | pub const ARK: &[u8] = include_bytes!("ark.pem"); 7 | 8 | /// The public Milan ASK certificate (PEM-encoded). 9 | pub const ASK: &[u8] = include_bytes!("ask.pem"); 10 | 11 | /// Get the Milan ARK Certificate. 12 | pub fn ark() -> Result { 13 | Certificate::from_pem(ARK) 14 | } 15 | 16 | /// Get the Milan ASK Certificate. 17 | pub fn ask() -> Result { 18 | Certificate::from_pem(ASK) 19 | } 20 | 21 | mod tests { 22 | #[test] 23 | fn ark_self_signed() { 24 | use super::*; 25 | 26 | let ark = ark().unwrap(); 27 | 28 | (&ark, &ark).verify().unwrap(); 29 | } 30 | 31 | #[test] 32 | fn ark_signs_ask() { 33 | use super::*; 34 | 35 | let ark = ark().unwrap(); 36 | let ask = ask().unwrap(); 37 | 38 | (&ark, &ask).verify().unwrap(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/certs/snp/builtin/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Interfaces for retrieving builtin ARKs and ASKs for their respective generations. 4 | 5 | /// Genoa generation. 6 | pub mod genoa; 7 | 8 | /// Milan generation. 9 | pub mod milan; 10 | 11 | /// Turin generation 12 | pub mod turin; 13 | 14 | use super::*; 15 | -------------------------------------------------------------------------------- /src/certs/snp/builtin/turin/ark.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 36 | zpacMwFusA== 37 | -----END CERTIFICATE----- 38 | -------------------------------------------------------------------------------- /src/certs/snp/builtin/turin/ask.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 37 | -----END CERTIFICATE----- 38 | -------------------------------------------------------------------------------- /src/certs/snp/builtin/turin/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | 5 | /// The public TURIN ARK certificate (PEM-encoded). 6 | pub const ARK: &[u8] = include_bytes!("ark.pem"); 7 | 8 | /// The public TURIN ASK certificate (PEM-encoded). 9 | pub const ASK: &[u8] = include_bytes!("ask.pem"); 10 | 11 | /// Get the TURIN ARK Certificate. 12 | pub fn ark() -> Result { 13 | Certificate::from_pem(ARK) 14 | } 15 | 16 | /// Get the TURIN ASK Certificate. 17 | pub fn ask() -> Result { 18 | Certificate::from_pem(ASK) 19 | } 20 | 21 | mod tests { 22 | #[test] 23 | fn ark_self_signed() { 24 | use super::*; 25 | 26 | let ark = ark().unwrap(); 27 | 28 | (&ark, &ark).verify().unwrap(); 29 | } 30 | 31 | #[test] 32 | fn ark_signs_ask() { 33 | use super::*; 34 | 35 | let ark = ark().unwrap(); 36 | let ask = ask().unwrap(); 37 | 38 | (&ark, &ask).verify().unwrap(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/certs/snp/ca/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | //! Operations for a Certificate Authority (CA) chain. 3 | 4 | #[cfg(feature = "openssl")] 5 | use openssl::x509::X509; 6 | 7 | use super::*; 8 | 9 | /// A Certificate Authority (CA) chain. 10 | #[derive(Clone, Debug)] 11 | pub struct Chain { 12 | /// AMD Root Key certificate. 13 | pub ark: Certificate, 14 | 15 | /// AMD Signing Key certificate. 16 | pub ask: Certificate, 17 | } 18 | 19 | /// Verify if a CA chain's ARK is self-signed, along with if the ARK signs the ASK. 20 | impl<'a> Verifiable for &'a Chain { 21 | type Output = &'a Certificate; 22 | 23 | fn verify(self) -> Result { 24 | // Verify that ARK is self-signed. 25 | (&self.ark, &self.ark).verify()?; 26 | 27 | // Verify that ARK signs ASK. 28 | (&self.ark, &self.ask).verify()?; 29 | 30 | Ok(&self.ask) 31 | } 32 | } 33 | 34 | #[cfg(feature = "openssl")] 35 | impl From<(X509, X509)> for Chain { 36 | /// Assumes the structure of ASK/ARK or ASVK/ARK 37 | fn from(value: (X509, X509)) -> Self { 38 | Self { 39 | ark: value.1.into(), 40 | ask: value.0.into(), 41 | } 42 | } 43 | } 44 | 45 | #[cfg(feature = "openssl")] 46 | impl From<(&X509, &X509)> for Chain { 47 | /// Assumes the structure of &ASK/&ARK or &ASVK/&ARK 48 | fn from(value: (&X509, &X509)) -> Self { 49 | (value.0.clone(), value.1.clone()).into() 50 | } 51 | } 52 | 53 | #[cfg(feature = "openssl")] 54 | impl<'a: 'b, 'b> From<&'a Chain> for (&'b X509, &'b X509) { 55 | /// Will always assume the tuple type to be (&ASK, &ARK) or (&ASVK, &ARK). 56 | fn from(value: &'a Chain) -> Self { 57 | ((&value.ask).into(), (&value.ark).into()) 58 | } 59 | } 60 | 61 | #[cfg(feature = "openssl")] 62 | impl From<&[X509]> for Chain { 63 | /// Will only retrieve the first two certificates, ignoring the rest. Also 64 | /// assumes the structure to be (&ASK, &ARK) or (&ASVK, &ARK) 65 | fn from(value: &[X509]) -> Self { 66 | (&value[0], &value[1]).into() 67 | } 68 | } 69 | 70 | impl From<(Certificate, Certificate)> for Chain { 71 | /// Assumes the structure of ASK/ARK or ASVK/ARK 72 | fn from(value: (Certificate, Certificate)) -> Self { 73 | Self { 74 | ark: value.1, 75 | ask: value.0, 76 | } 77 | } 78 | } 79 | 80 | impl From<(&Certificate, &Certificate)> for Chain { 81 | /// Assumes the structure of &ASK/&ARK or &ASVK/&ARK 82 | fn from(value: (&Certificate, &Certificate)) -> Self { 83 | Self { 84 | ark: value.1.clone(), 85 | ask: value.0.clone(), 86 | } 87 | } 88 | } 89 | 90 | impl<'a: 'b, 'b> From<&'a Chain> for (&'b Certificate, &'b Certificate) { 91 | /// Will always assume the tuple type to be (&ASK, &ARK) or (&ASVK, &ARK). 92 | fn from(value: &'a Chain) -> Self { 93 | (&value.ask, &value.ark) 94 | } 95 | } 96 | 97 | impl From<&[Certificate]> for Chain { 98 | /// Will only retrieve the first two certificates, ignoring the rest. Also 99 | /// assumes the structure to be (&ASK, &ARK) or (&ASVK, &ARK) 100 | fn from(value: &[Certificate]) -> Self { 101 | (&value[0], &value[1]).into() 102 | } 103 | } 104 | 105 | impl Chain { 106 | /// Deserialize a PEM-encoded ARK and ASK pair to a CA chain. 107 | pub fn from_pem(ark: &[u8], ask: &[u8]) -> Result { 108 | Ok(Self { 109 | ark: Certificate::from_pem(ark)?, 110 | ask: Certificate::from_pem(ask)?, 111 | }) 112 | } 113 | 114 | /// Deserialize a DER-encoded ARK and ASK pair to a CA chain. 115 | pub fn from_der(ark: &[u8], ask: &[u8]) -> Result { 116 | Ok(Self { 117 | ark: Certificate::from_der(ark)?, 118 | ask: Certificate::from_der(ask)?, 119 | }) 120 | } 121 | 122 | #[cfg(feature = "openssl")] 123 | /// Deserialize the certificates from a PEM stack to a CA chain. 124 | pub fn from_pem_bytes(stack: &[u8]) -> Result { 125 | let certificates = X509::stack_from_pem(stack)?; 126 | let ark_cert = &certificates[1]; 127 | let ask_cert = &certificates[0]; 128 | Ok(Self { 129 | ark: ark_cert.into(), 130 | ask: ask_cert.into(), 131 | }) 132 | } 133 | } 134 | 135 | mod tests { 136 | #[test] 137 | fn milan_ca_chain_verifiable() { 138 | use crate::certs::snp::{builtin::milan, ca::*, Verifiable}; 139 | 140 | let chain = Chain { 141 | ark: milan::ark().unwrap(), 142 | ask: milan::ask().unwrap(), 143 | }; 144 | 145 | chain.verify().unwrap(); 146 | } 147 | 148 | #[test] 149 | fn genoa_ca_chain_verifiable() { 150 | use crate::certs::snp::{builtin::genoa, ca::*, Verifiable}; 151 | 152 | let chain = Chain { 153 | ark: genoa::ark().unwrap(), 154 | ask: genoa::ask().unwrap(), 155 | }; 156 | 157 | chain.verify().unwrap(); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/certs/snp/cert_nossl.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | 5 | use der::{referenced::OwnedToRef, Decode, DecodePem, Encode}; 6 | use rsa::signature; // re-export of signature crate 7 | use signature::Verifier; 8 | use spki::ObjectIdentifier; 9 | use std::convert::TryFrom; 10 | use std::io; 11 | use std::io::ErrorKind; 12 | use x509_cert::der; // re-export of der crate 13 | use x509_cert::spki; // re-export of spki crate 14 | 15 | /// Structures/interfaces for SEV-SNP certificates. 16 | 17 | #[derive(Clone, Debug, Eq, PartialEq)] 18 | pub struct Certificate(x509_cert::Certificate); 19 | 20 | const RSA_SSA_PSS_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10"); 21 | 22 | /// Verify if the public key of one Certificate signs another Certificate. 23 | impl Verifiable for (&Certificate, &Certificate) { 24 | type Output = (); 25 | 26 | fn verify(self) -> Result { 27 | let signer = &self.0 .0; 28 | let signee = &self.1 .0; 29 | 30 | if signee.signature_algorithm.oid != RSA_SSA_PSS_OID { 31 | return Err(io_error_other(format!( 32 | "unsupported signature algorithm: {:?}", 33 | signee.signature_algorithm 34 | ))); 35 | } 36 | 37 | let rsa_verifying_key = { 38 | let signer_spki_ref = signer 39 | .tbs_certificate 40 | .subject_public_key_info 41 | .owned_to_ref(); 42 | let signer_pubkey_rsa = rsa::RsaPublicKey::try_from(signer_spki_ref) 43 | .map_err(|e| io_error_other(format!("invalid RSA public key: {e:?}")))?; 44 | rsa::pss::VerifyingKey::::new(signer_pubkey_rsa) 45 | }; 46 | 47 | let message = signee.tbs_certificate.to_der().map_err(|e| { 48 | io_error_other(format!("failed to encode tbs_certificate as DER: {e:?}")) 49 | })?; 50 | 51 | let rsa_signature = rsa::pss::Signature::try_from(signee.signature.raw_bytes()) 52 | .map_err(|e| io_error_other(format!("invalid RSA signature: {e:?}")))?; 53 | 54 | rsa_verifying_key 55 | .verify(&message, &rsa_signature) 56 | .map_err(|e| { 57 | io_error_other(format!( 58 | "Signer certificate does not RSA sign signee certificate: {e}" 59 | )) 60 | }) 61 | } 62 | } 63 | 64 | impl Certificate { 65 | /// Create a Certificate from a PEM-encoded X509 structure. 66 | pub fn from_pem(pem: &[u8]) -> Result { 67 | let cert = x509_cert::Certificate::from_pem(pem) 68 | .map_err(|e| io::Error::new(ErrorKind::InvalidData, format!("invalid PEM: {}", e)))?; 69 | Ok(Self(cert)) 70 | } 71 | 72 | /// Serialize a Certificate struct to PEM. 73 | pub fn to_pem(&self) -> Result> { 74 | use der::EncodePem; 75 | Ok(self 76 | .0 77 | .to_pem(der::pem::LineEnding::default()) 78 | .map_err(|e| io_error_other(format!("PEM-encoding failed: {}", e)))? 79 | .into_bytes()) 80 | } 81 | 82 | /// Create a Certificate from a DER-encoded X509 structure. 83 | pub fn from_der(der: &[u8]) -> Result { 84 | let cert = x509_cert::Certificate::from_der(der) 85 | .map_err(|e| io::Error::new(ErrorKind::InvalidData, format!("invalid DER: {}", e)))?; 86 | Ok(Self(cert)) 87 | } 88 | 89 | /// Serialize a Certificate struct to DER. 90 | pub fn to_der(&self) -> Result> { 91 | self.0 92 | .to_der() 93 | .map_err(|e| io_error_other(format!("DER-encoding failed: {e:?}"))) 94 | } 95 | 96 | /// Retrieve the public key in SEC1 encoding. 97 | pub fn public_key_sec1(&self) -> &[u8] { 98 | self.0 99 | .tbs_certificate 100 | .subject_public_key_info 101 | .subject_public_key 102 | .raw_bytes() 103 | } 104 | } 105 | 106 | fn io_error_other>(error: S) -> io::Error { 107 | io::Error::new(ErrorKind::Other, error.into()) 108 | } 109 | -------------------------------------------------------------------------------- /src/certs/snp/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | /// ECDSA signatures. 4 | pub mod ecdsa; 5 | 6 | #[cfg(any(feature = "openssl", feature = "crypto_nossl"))] 7 | /// Certificate Authority (CA) certificates. 8 | pub mod ca; 9 | 10 | #[cfg(any(feature = "openssl", feature = "crypto_nossl"))] 11 | /// Built-in certificates for Milan and Genoa machines. 12 | pub mod builtin; 13 | 14 | #[cfg(feature = "openssl")] 15 | mod cert; 16 | #[cfg(feature = "crypto_nossl")] 17 | mod cert_nossl; 18 | 19 | #[cfg(any(feature = "openssl", feature = "crypto_nossl"))] 20 | mod chain; 21 | 22 | #[cfg(feature = "openssl")] 23 | pub use cert::Certificate; 24 | #[cfg(feature = "crypto_nossl")] 25 | pub use cert_nossl::Certificate; 26 | 27 | #[cfg(any(feature = "openssl", feature = "crypto_nossl"))] 28 | pub use chain::Chain; 29 | 30 | #[cfg(any(feature = "openssl", feature = "crypto_nossl"))] 31 | use std::io::Result; 32 | 33 | #[cfg(any(feature = "openssl", feature = "crypto_nossl"))] 34 | use std::io::{Error, ErrorKind}; 35 | 36 | #[cfg(feature = "openssl")] 37 | #[allow(dead_code)] 38 | struct Body; 39 | 40 | #[cfg(any(feature = "openssl", feature = "crypto_nossl"))] 41 | /// An interface for types that may contain entities such as 42 | /// signatures that must be verified. 43 | pub trait Verifiable { 44 | /// An output type for successful verification. 45 | type Output; 46 | 47 | /// Self-verifies signatures. 48 | fn verify(self) -> Result; 49 | } 50 | 51 | #[cfg(feature = "openssl")] 52 | /// An interface for types that can sign another type (i.e., a certificate). 53 | pub trait Signer { 54 | /// The now-signed type. 55 | type Output; 56 | 57 | /// Signs the target. 58 | fn sign(&self, target: &mut T) -> Result; 59 | } 60 | 61 | #[cfg(feature = "openssl")] 62 | pub(crate) trait FromLe: Sized { 63 | fn from_le(value: &[u8]) -> Result; 64 | } 65 | 66 | #[cfg(feature = "openssl")] 67 | pub(crate) trait AsLeBytes { 68 | fn as_le_bytes(&self) -> T; 69 | } 70 | 71 | #[cfg(feature = "openssl")] 72 | impl FromLe for openssl::bn::BigNum { 73 | #[inline] 74 | fn from_le(value: &[u8]) -> Result { 75 | Ok(Self::from_slice( 76 | &value.iter().rev().cloned().collect::>(), 77 | )?) 78 | } 79 | } 80 | 81 | #[cfg(feature = "openssl")] 82 | impl AsLeBytes<[u8; 72]> for openssl::bn::BigNumRef { 83 | fn as_le_bytes(&self) -> [u8; 72] { 84 | let mut buf = [0u8; 72]; 85 | 86 | for (i, b) in self.to_vec().into_iter().rev().enumerate() { 87 | buf[i] = b; 88 | } 89 | 90 | buf 91 | } 92 | } 93 | 94 | #[cfg(feature = "openssl")] 95 | impl AsLeBytes<[u8; 512]> for openssl::bn::BigNumRef { 96 | fn as_le_bytes(&self) -> [u8; 512] { 97 | let mut buf = [0u8; 512]; 98 | 99 | for (i, b) in self.to_vec().into_iter().rev().enumerate() { 100 | buf[i] = b; 101 | } 102 | 103 | buf 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/firmware/guest/types/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #[cfg(feature = "snp")] 4 | mod snp; 5 | 6 | #[cfg(feature = "snp")] 7 | pub use self::snp::*; 8 | -------------------------------------------------------------------------------- /src/firmware/host/types/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #[cfg(feature = "sev")] 4 | mod sev; 5 | 6 | #[cfg(feature = "snp")] 7 | mod snp; 8 | 9 | #[cfg(feature = "sev")] 10 | pub use self::sev::*; 11 | 12 | #[cfg(feature = "snp")] 13 | pub use self::snp::*; 14 | 15 | /// The platform state. 16 | /// 17 | /// The underlying SEV platform behaves like a state machine and can 18 | /// only perform certain actions while it is in certain states. 19 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 20 | #[repr(u8)] 21 | pub enum State { 22 | /// The platform is uninitialized. 23 | Uninitialized, 24 | 25 | /// The platform is initialized, but not currently managing any 26 | /// guests. 27 | Initialized, 28 | 29 | /// The platform is initialized and is overseeing execution 30 | /// of encrypted guests. 31 | Working, 32 | } 33 | 34 | impl std::fmt::Display for State { 35 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 36 | let state = match self { 37 | State::Uninitialized => "uninitialized", 38 | State::Initialized => "initialized", 39 | State::Working => "working", 40 | }; 41 | write!(f, "{state}") 42 | } 43 | } 44 | 45 | #[cfg(test)] 46 | mod tests { 47 | use super::*; 48 | 49 | #[test] 50 | fn test_state_display() { 51 | assert_eq!(State::Uninitialized.to_string(), "uninitialized"); 52 | assert_eq!(State::Initialized.to_string(), "initialized"); 53 | assert_eq!(State::Working.to_string(), "working"); 54 | } 55 | 56 | #[test] 57 | fn test_state_representation() { 58 | assert_eq!(State::Uninitialized as u8, 0); 59 | assert_eq!(State::Initialized as u8, 1); 60 | assert_eq!(State::Working as u8, 2); 61 | } 62 | 63 | #[test] 64 | fn test_state_debug() { 65 | assert_eq!(format!("{:?}", State::Uninitialized), "Uninitialized"); 66 | assert_eq!(format!("{:?}", State::Initialized), "Initialized"); 67 | assert_eq!(format!("{:?}", State::Working), "Working"); 68 | } 69 | 70 | #[test] 71 | fn test_state_equality() { 72 | assert_eq!(State::Uninitialized, State::Uninitialized); 73 | assert_eq!(State::Initialized, State::Initialized); 74 | assert_eq!(State::Working, State::Working); 75 | 76 | assert_ne!(State::Uninitialized, State::Initialized); 77 | assert_ne!(State::Initialized, State::Working); 78 | assert_ne!(State::Working, State::Uninitialized); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/firmware/host/types/sev.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Operations for managing the SEV platform. 4 | 5 | pub use crate::firmware::linux::host::types::PlatformStatusFlags; 6 | 7 | use crate::firmware::host::State; 8 | 9 | #[cfg(feature = "openssl")] 10 | use std::convert::TryInto; 11 | 12 | #[cfg(feature = "openssl")] 13 | use crate::certs::sev::{ 14 | sev::{Certificate, Usage}, 15 | PublicKey, Verifiable, 16 | }; 17 | 18 | #[cfg(feature = "openssl")] 19 | use openssl::{ec::EcKey, ecdsa::EcdsaSig, pkey::Public, sha::Sha256}; 20 | 21 | use crate::util::{TypeLoad, TypeSave}; 22 | 23 | use crate::util::array::Array; 24 | use serde::{Deserialize, Serialize}; 25 | 26 | use std::{ 27 | fmt::Debug, 28 | io::{Read, Write}, 29 | }; 30 | 31 | const MNONCE_SIZE: usize = 128 / 8; 32 | const DIGEST_SIZE: usize = 256 / 8; 33 | const POLICY_SIZE: usize = 32 / 8; 34 | const POLICY_OFFSET: usize = MNONCE_SIZE + DIGEST_SIZE; 35 | const MEASURABLE_BYTES: usize = MNONCE_SIZE + DIGEST_SIZE + POLICY_SIZE; 36 | 37 | /// Information about the SEV platform version. 38 | #[repr(C)] 39 | #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] 40 | pub struct Version { 41 | /// The major version number. 42 | pub major: u8, 43 | 44 | /// The minor version number. 45 | pub minor: u8, 46 | } 47 | 48 | impl std::fmt::Display for Version { 49 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 50 | write!(f, "{}.{}", self.major, self.minor) 51 | } 52 | } 53 | 54 | impl From for Version { 55 | fn from(v: u16) -> Self { 56 | Self { 57 | major: ((v & 0xF0) >> 4) as u8, 58 | minor: (v & 0x0F) as u8, 59 | } 60 | } 61 | } 62 | 63 | /// A description of the SEV platform's build information. 64 | #[repr(C)] 65 | #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] 66 | pub struct Build { 67 | /// The version information. 68 | pub version: Version, 69 | 70 | /// The build number. 71 | pub build: u8, 72 | } 73 | 74 | impl std::fmt::Display for Build { 75 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 76 | write!(f, "{}.{}", self.version, self.build) 77 | } 78 | } 79 | 80 | impl codicon::Decoder<()> for Build { 81 | type Error = std::io::Error; 82 | 83 | fn decode(mut reader: impl Read, _: ()) -> std::io::Result { 84 | reader.load() 85 | } 86 | } 87 | 88 | impl codicon::Encoder<()> for Build { 89 | type Error = std::io::Error; 90 | 91 | fn encode(&self, mut writer: impl Write, _: ()) -> std::io::Result<()> { 92 | writer.save(self) 93 | } 94 | } 95 | 96 | /// Information regarding the SEV platform's current status. 97 | #[derive(Clone, Debug, PartialEq, Eq)] 98 | pub struct Status { 99 | /// The build number. 100 | pub build: Build, 101 | 102 | /// The platform's current state. 103 | pub state: State, 104 | 105 | /// Additional platform information is encoded into flags. 106 | /// 107 | /// These could describe whether encrypted state functionality 108 | /// is enabled, or whether the platform is self-owned. 109 | pub flags: PlatformStatusFlags, 110 | 111 | /// The number of valid guests supervised by this platform. 112 | pub guests: u32, 113 | } 114 | 115 | /// An attestation report structure. 116 | #[derive(Default, Serialize, Deserialize)] 117 | #[repr(C)] 118 | pub struct LegacyAttestationReport { 119 | /// 128-bit Nonce from the Command Buffer. 120 | pub mnonce: [u8; MNONCE_SIZE], // 0x00 121 | /// SHA-256 digest of launched guest. 122 | pub launch_digest: [u8; DIGEST_SIZE], // 0x10 123 | /// Policy guest was launched with. 124 | pub policy: u32, // 0x30 125 | /// Key usage of SIG1 signing key. 126 | pub sig_usage: u32, // 0x34 127 | /// Signature Algorithm 128 | pub sig_algo: u32, // 0x38 129 | /// Reserved 130 | _reserved_0: u32, // 0x3C 131 | /// Signature of the report. 132 | pub signature: Array, // 0x40 - 0xCF 133 | } 134 | 135 | impl LegacyAttestationReport { 136 | /// Provides the measured bytes of the report. This should include bits 0x0 - 0x34 inclusively. 137 | pub fn measurable_bytes(&self) -> [u8; MEASURABLE_BYTES] { 138 | let mut bytes: [u8; MEASURABLE_BYTES] = [0; 52]; 139 | bytes[0..MNONCE_SIZE].copy_from_slice(&self.mnonce); 140 | bytes[MNONCE_SIZE..POLICY_OFFSET].copy_from_slice(&self.launch_digest); 141 | bytes[POLICY_OFFSET..].copy_from_slice(&self.policy.to_ne_bytes()); 142 | bytes 143 | } 144 | } 145 | 146 | #[cfg(feature = "openssl")] 147 | impl Verifiable for (&Certificate, &LegacyAttestationReport) { 148 | type Output = (); 149 | 150 | fn verify(self) -> std::io::Result { 151 | let sev_pub_key: PublicKey = self.0.try_into()?; 152 | let pub_key: &EcKey = &sev_pub_key.ec_key()?; 153 | 154 | let sig: EcdsaSig = (&self.1.signature).try_into()?; 155 | 156 | let mut hasher = Sha256::new(); 157 | hasher.update(&self.1.measurable_bytes()); 158 | let base_digest = hasher.finish(); 159 | 160 | let signed = sig.verify(&base_digest, pub_key)?; 161 | match signed { 162 | true => Ok(()), 163 | false => Err(std::io::Error::new( 164 | std::io::ErrorKind::Other, 165 | "PEK does not sign the attestation report", 166 | )), 167 | } 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/firmware/linux/guest/ioctl.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use crate::firmware::linux::guest::types::{ 4 | DerivedKeyReq, DerivedKeyRsp, ExtReportReq, ReportReq, ReportRsp, 5 | }; 6 | 7 | use std::marker::PhantomData; 8 | 9 | use iocuddle::{Group, Ioctl, WriteRead}; 10 | 11 | pub enum GuestIoctl { 12 | GetReport = 0x0, 13 | GetDerivedKey = 0x1, 14 | GetExtReport = 0x2, 15 | _Undefined, 16 | } 17 | 18 | const SEV: Group = Group::new(b'S'); 19 | 20 | pub const SNP_GET_REPORT: Ioctl> = 21 | unsafe { SEV.write_read(GuestIoctl::GetReport as u8) }; 22 | 23 | pub const SNP_GET_DERIVED_KEY: Ioctl> = 24 | unsafe { SEV.write_read(GuestIoctl::GetDerivedKey as u8) }; 25 | 26 | pub const SNP_GET_EXT_REPORT: Ioctl> = 27 | unsafe { SEV.write_read(GuestIoctl::GetExtReport as u8) }; 28 | 29 | /// The default structure used for making requests to the PSP as a guest owner. 30 | #[repr(C)] 31 | pub struct GuestRequest<'a, 'b, Req, Rsp> { 32 | /// Message version number (must be non-zero) 33 | pub message_version: u32, 34 | /// Request structure address. 35 | pub request_data: u64, 36 | /// Response structure address. 37 | pub response_data: u64, 38 | /// Firmware error address. 39 | pub fw_err: u64, 40 | 41 | _phantom_req: PhantomData<&'a mut Req>, 42 | _phantom_rsp: PhantomData<&'b mut Rsp>, 43 | } 44 | 45 | impl<'a, 'b, Req, Rsp> GuestRequest<'a, 'b, Req, Rsp> { 46 | /// Creates a new request from the addresses provided. 47 | /// 48 | /// # Arguments: 49 | /// 50 | /// * `ver` - Option - Version of the message. 51 | /// * `req` - &Req - The reference a Request object. 52 | /// * `rsp` - &Rsp - The reference a Response object. 53 | pub fn new(ver: Option, req: &'a mut Req, rsp: &'b mut Rsp) -> Self { 54 | Self { 55 | message_version: ver.unwrap_or(1), 56 | request_data: req as *mut Req as u64, 57 | response_data: rsp as *mut Rsp as u64, 58 | fw_err: Default::default(), 59 | _phantom_req: PhantomData, 60 | _phantom_rsp: PhantomData, 61 | } 62 | } 63 | } 64 | 65 | #[cfg(test)] 66 | mod tests { 67 | use super::*; 68 | 69 | #[test] 70 | fn test_guest_request_new() { 71 | let mut req = ReportReq::default(); 72 | let mut rsp = ReportRsp::default(); 73 | 74 | // Test with explicit version 75 | let guest_req = GuestRequest::new(Some(2), &mut req, &mut rsp); 76 | assert_eq!(guest_req.message_version, 2); 77 | assert_ne!(guest_req.request_data, 0); 78 | assert_ne!(guest_req.response_data, 0); 79 | assert_eq!(guest_req.fw_err, 0); 80 | 81 | // Test with default version 82 | let guest_req = GuestRequest::new(None, &mut req, &mut rsp); 83 | assert_eq!(guest_req.message_version, 1); 84 | assert_ne!(guest_req.request_data, 0); 85 | assert_ne!(guest_req.response_data, 0); 86 | assert_eq!(guest_req.fw_err, 0); 87 | } 88 | 89 | #[test] 90 | fn test_guest_ioctl_values() { 91 | assert_eq!(GuestIoctl::GetReport as u8, 0x0); 92 | assert_eq!(GuestIoctl::GetDerivedKey as u8, 0x1); 93 | assert_eq!(GuestIoctl::GetExtReport as u8, 0x2); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/firmware/linux/guest/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #[cfg(target_os = "linux")] 4 | pub(crate) mod ioctl; 5 | #[cfg(target_os = "linux")] 6 | pub(crate) mod types; 7 | -------------------------------------------------------------------------------- /src/firmware/linux/host/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Host FFI Wrappers for C Kernel APIs 4 | #[cfg(target_os = "linux")] 5 | pub(crate) mod ioctl; 6 | pub(crate) mod types; 7 | -------------------------------------------------------------------------------- /src/firmware/linux/host/types/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #[cfg(feature = "sev")] 4 | mod sev; 5 | 6 | #[cfg(feature = "snp")] 7 | mod snp; 8 | 9 | #[cfg(feature = "sev")] 10 | pub use self::sev::*; 11 | 12 | #[cfg(feature = "snp")] 13 | pub use self::snp::*; 14 | 15 | #[cfg(any(feature = "sev", feature = "snp"))] 16 | #[cfg(target_os = "linux")] 17 | use std::marker::PhantomData; 18 | 19 | /// Get the CPU's unique ID that can be used for getting 20 | /// a certificate for the CEK public key. 21 | #[cfg(target_os = "linux")] 22 | #[cfg(any(feature = "sev", feature = "snp"))] 23 | #[repr(C, packed)] 24 | pub struct GetId<'a> { 25 | id_addr: u64, 26 | id_len: u32, 27 | _phantom: PhantomData<&'a ()>, 28 | } 29 | 30 | #[cfg(any(feature = "sev", feature = "snp"))] 31 | #[cfg(target_os = "linux")] 32 | impl<'a> GetId<'a> { 33 | pub fn new(id: &'a mut [u8; 64]) -> Self { 34 | Self { 35 | id_addr: id.as_mut_ptr() as _, 36 | id_len: id.len() as _, 37 | _phantom: PhantomData, 38 | } 39 | } 40 | 41 | /// This method is only meaningful if called *after* the GET_ID2 ioctl is called because the 42 | /// kernel will write the length of the unique CPU ID to `GetId.id_len`. 43 | pub fn as_slice(&self) -> &[u8] { 44 | unsafe { std::slice::from_raw_parts(self.id_addr as *const u8, self.id_len as _) } 45 | } 46 | } 47 | 48 | /// Reset the platform's persistent state. 49 | /// 50 | /// (Chapter 5.5) 51 | #[cfg(feature = "sev")] 52 | #[cfg(target_os = "linux")] 53 | pub struct PlatformReset; 54 | 55 | #[cfg(target_os = "linux")] 56 | #[cfg(test)] 57 | mod tests { 58 | use super::*; 59 | 60 | #[test] 61 | fn test_get_id_new() { 62 | let mut id = [0u8; 64]; 63 | let get_id = GetId::new(&mut id); 64 | 65 | assert_eq!( 66 | unsafe { std::ptr::addr_of!(get_id.id_len).read_unaligned() }, 67 | 64 68 | ); 69 | assert_eq!(get_id.id_addr as *const u8, id.as_ptr()); 70 | } 71 | 72 | #[test] 73 | fn test_get_id_slice() { 74 | let mut id = [42u8; 64]; 75 | let get_id = GetId::new(&mut id); 76 | 77 | assert_eq!(get_id.as_slice(), &[42u8; 64]); 78 | } 79 | 80 | #[test] 81 | fn test_get_id_phantom() { 82 | let mut id = [0u8; 64]; 83 | let get_id = GetId::new(&mut id); 84 | 85 | // Verify PhantomData is working as expected 86 | assert_eq!(std::mem::size_of_val(&get_id._phantom), 0); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/firmware/linux/host/types/sev.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #[cfg(target_os = "linux")] 4 | use crate::certs::sev::sev; 5 | 6 | use crate::firmware::host::Version; 7 | 8 | #[cfg(target_os = "linux")] 9 | use std::marker::PhantomData; 10 | 11 | bitflags::bitflags! { 12 | /// The platform's status flags. 13 | #[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)] 14 | pub struct PlatformStatusFlags: u32 { 15 | /// If set, this platform is owned. Otherwise, it is self-owned. 16 | const OWNED = 1 << 0; 17 | 18 | /// If set, encrypted state functionality is present. 19 | const ENCRYPTED_STATE = 1 << 8; 20 | } 21 | } 22 | 23 | /// Query SEV platform status. 24 | /// 25 | /// (Chapter 5.6; Table 17) 26 | #[derive(Default)] 27 | #[repr(C, packed)] 28 | pub struct PlatformStatus { 29 | /// The firmware version (major.minor) 30 | pub version: Version, 31 | 32 | /// The Platform State. 33 | pub state: u8, 34 | 35 | /// Right now the only flag that is communicated in 36 | /// this single byte is whether the platform is self- 37 | /// owned or not. If the first bit is set then the 38 | /// platform is externally owned. If it is cleared, then 39 | /// the platform is self-owned. Self-owned is the default 40 | /// state. 41 | pub flags: PlatformStatusFlags, 42 | 43 | /// The firmware build ID for this API version. 44 | pub build: u8, 45 | 46 | /// The number of valid guests maintained by the SEV firmware. 47 | pub guest_count: u32, 48 | } 49 | 50 | /// Generate a new Platform Endorsement Key (PEK). 51 | /// 52 | /// (Chapter 5.7) 53 | #[cfg(target_os = "linux")] 54 | pub struct PekGen; 55 | 56 | /// Request certificate signing. 57 | /// 58 | /// (Chapter 5.8; Table 27) 59 | #[repr(C, packed)] 60 | #[cfg(target_os = "linux")] 61 | pub struct PekCsr<'a> { 62 | addr: u64, 63 | len: u32, 64 | _phantom: PhantomData<&'a ()>, 65 | } 66 | 67 | #[cfg(target_os = "linux")] 68 | impl<'a> PekCsr<'a> { 69 | pub fn new(cert: &'a mut sev::Certificate) -> Self { 70 | Self { 71 | addr: cert as *mut _ as _, 72 | len: std::mem::size_of_val(cert) as _, 73 | _phantom: PhantomData, 74 | } 75 | } 76 | } 77 | 78 | /// Join the platform to the domain. 79 | /// 80 | /// (Chapter 5.9; Table 29) 81 | #[cfg(target_os = "linux")] 82 | #[repr(C, packed)] 83 | pub struct PekCertImport<'a> { 84 | pek_addr: u64, 85 | pek_len: u32, 86 | oca_addr: u64, 87 | oca_len: u32, 88 | _phantom: PhantomData<&'a ()>, 89 | } 90 | 91 | #[cfg(target_os = "linux")] 92 | impl<'a> PekCertImport<'a> { 93 | pub fn new(pek: &'a sev::Certificate, oca: &'a sev::Certificate) -> Self { 94 | Self { 95 | pek_addr: pek as *const _ as _, 96 | pek_len: std::mem::size_of_val(pek) as _, 97 | oca_addr: oca as *const _ as _, 98 | oca_len: std::mem::size_of_val(oca) as _, 99 | _phantom: PhantomData, 100 | } 101 | } 102 | } 103 | 104 | /// (Re)generate the Platform Diffie-Hellman (PDH). 105 | /// 106 | /// (Chapter 5.10) 107 | #[cfg(target_os = "linux")] 108 | pub struct PdhGen; 109 | 110 | /// Retrieve the PDH and the platform certificate chain. 111 | /// 112 | /// (Chapter 5.11) 113 | #[cfg(target_os = "linux")] 114 | #[repr(C, packed)] 115 | pub struct PdhCertExport<'a> { 116 | pdh_addr: u64, 117 | pdh_len: u32, 118 | certs_addr: u64, 119 | certs_len: u32, 120 | _phantom: PhantomData<&'a ()>, 121 | } 122 | 123 | #[cfg(target_os = "linux")] 124 | impl<'a> PdhCertExport<'a> { 125 | pub fn new(pdh: &'a mut sev::Certificate, certs: &'a mut [sev::Certificate; 3]) -> Self { 126 | Self { 127 | pdh_addr: pdh as *mut _ as _, 128 | pdh_len: std::mem::size_of_val(pdh) as _, 129 | certs_addr: certs.as_mut_ptr() as _, 130 | certs_len: std::mem::size_of_val(certs) as _, 131 | _phantom: PhantomData, 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/firmware/linux/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | pub mod host; 4 | 5 | #[cfg(feature = "snp")] 6 | pub mod guest; 7 | 8 | pub(crate) const _4K_PAGE: usize = 4096; 9 | -------------------------------------------------------------------------------- /src/firmware/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Modules for interfacing with SEV firmware. 4 | //! Rust-friendly API wrappers to communicate with the FFI functions. 5 | 6 | #[cfg(any(feature = "sev", feature = "snp"))] 7 | pub mod host; 8 | 9 | #[cfg(feature = "snp")] 10 | pub mod guest; 11 | 12 | #[cfg(any(feature = "sev", feature = "snp"))] 13 | pub(crate) mod linux; 14 | -------------------------------------------------------------------------------- /src/launch/linux/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Operations and types for launching on Linux 4 | pub(crate) mod ioctl; 5 | 6 | #[cfg(feature = "sev")] 7 | pub(crate) mod sev; 8 | 9 | #[cfg(feature = "snp")] 10 | pub(crate) mod snp; 11 | 12 | #[cfg(any(feature = "sev", feature = "snp"))] 13 | pub(crate) mod shared; 14 | -------------------------------------------------------------------------------- /src/launch/linux/sev.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Types for interacting with the KVM SEV guest management API. 4 | 5 | use crate::{certs::sev::sev::Certificate, launch::sev::*}; 6 | 7 | use std::{ 8 | marker::PhantomData, 9 | mem::{size_of_val, MaybeUninit}, 10 | }; 11 | 12 | /// Initialize the SEV platform context. 13 | #[repr(C)] 14 | pub struct Init; 15 | 16 | /// Initialize the SEV-ES platform context. 17 | #[repr(C)] 18 | pub struct EsInit; 19 | 20 | #[derive(Clone, Copy)] 21 | #[repr(transparent)] 22 | pub struct Handle(u32); 23 | 24 | impl From> for Handle { 25 | fn from(ls: LaunchStart) -> Self { 26 | ls.handle 27 | } 28 | } 29 | 30 | /// Initiate SEV launch flow. 31 | #[repr(C)] 32 | pub struct LaunchStart<'a> { 33 | handle: Handle, 34 | policy: Policy, 35 | dh_addr: u64, 36 | dh_len: u32, 37 | session_addr: u64, 38 | session_len: u32, 39 | _phantom: PhantomData<&'a ()>, 40 | } 41 | 42 | impl<'a> LaunchStart<'a> { 43 | pub fn new(policy: &'a Policy, dh: &'a Certificate, session: &'a Session) -> Self { 44 | Self { 45 | handle: Handle(0), /* platform will generate one for us */ 46 | policy: *policy, 47 | dh_addr: dh as *const _ as _, 48 | dh_len: size_of_val(dh) as _, 49 | session_addr: session as *const _ as _, 50 | session_len: size_of_val(session) as _, 51 | _phantom: PhantomData, 52 | } 53 | } 54 | } 55 | 56 | /// Encrypt guest data with its VEK. 57 | #[repr(C)] 58 | pub struct LaunchUpdateData<'a> { 59 | addr: u64, 60 | len: u32, 61 | _phantom: PhantomData<&'a ()>, 62 | } 63 | 64 | impl<'a> LaunchUpdateData<'a> { 65 | pub fn new(data: &'a [u8]) -> Self { 66 | Self { 67 | addr: data.as_ptr() as _, 68 | len: data.len() as _, 69 | _phantom: PhantomData, 70 | } 71 | } 72 | } 73 | 74 | /// Update VMSA for setting up vCPUs on SEV-ES. 75 | #[repr(C)] 76 | pub struct LaunchUpdateVmsa; 77 | 78 | impl LaunchUpdateVmsa { 79 | pub fn new() -> Self { 80 | Self 81 | } 82 | } 83 | 84 | /// Inject a secret into the guest. 85 | #[repr(C)] 86 | pub struct LaunchSecret<'a> { 87 | hdr_addr: u64, 88 | hdr_len: u32, 89 | guest_addr: u64, 90 | guest_len: u32, 91 | trans_addr: u64, 92 | trans_len: u32, 93 | _phantom: PhantomData<&'a ()>, 94 | } 95 | 96 | impl<'a> LaunchSecret<'a> { 97 | pub fn new(header: &'a Header, guest: usize, trans: &'a [u8]) -> Self { 98 | Self { 99 | hdr_addr: header as *const _ as _, 100 | hdr_len: size_of_val(header) as _, 101 | guest_addr: guest as _, 102 | guest_len: trans.len() as _, 103 | trans_addr: trans.as_ptr() as _, 104 | trans_len: trans.len() as _, 105 | _phantom: PhantomData, 106 | } 107 | } 108 | } 109 | 110 | /// Get the guest's measurement. 111 | #[repr(C)] 112 | pub struct LaunchMeasure<'a> { 113 | addr: u64, 114 | len: u32, 115 | _phantom: PhantomData<&'a Measurement>, 116 | } 117 | 118 | impl<'a> LaunchMeasure<'a> { 119 | pub fn new(measurement: &'a mut MaybeUninit) -> Self { 120 | Self { 121 | addr: measurement.as_mut_ptr() as _, 122 | len: size_of_val(measurement) as _, 123 | _phantom: PhantomData, 124 | } 125 | } 126 | } 127 | 128 | /// Complete the SEV launch flow and transition guest into 129 | /// ready state. 130 | #[repr(C)] 131 | pub struct LaunchFinish; 132 | 133 | /// Fetch a finished guest's attestation report. 134 | #[derive(Default)] 135 | #[repr(C)] 136 | pub struct LaunchAttestation<'a> { 137 | mnonce: [u8; 16], 138 | addr: u64, 139 | pub len: u32, 140 | _phantom: PhantomData<&'a mut Vec>, 141 | } 142 | 143 | impl<'a> LaunchAttestation<'a> { 144 | pub fn new(mnonce: [u8; 16], bytes: &'a mut Vec) -> Self { 145 | Self { 146 | mnonce, 147 | addr: bytes.as_mut_ptr() as _, 148 | len: bytes.capacity() as _, 149 | _phantom: PhantomData, 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/launch/linux/shared.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! SEV and SEV-SNP shared types for interacting with the KVM SEV guest management API. 4 | 5 | /// Structure passed into KVM_SEV_INIT2 command. 6 | #[derive(Default)] 7 | #[repr(C, packed)] 8 | pub struct Init2 { 9 | /// Initial value of features field in VMSA. (Must be 0 for SEV) 10 | vmsa_features: u64, 11 | 12 | /// Always set to 0 13 | flags: u32, 14 | 15 | /// Maximum guest GHCB version allowed. (Currently 0 for SEV and 1 for SEV-ES and SEV-SNP) 16 | ghcb_version: u16, 17 | 18 | pad1: u16, 19 | 20 | pad2: [u32; 8], 21 | } 22 | 23 | impl Init2 { 24 | /// Default INIT2 values for SEV 25 | #[cfg(feature = "sev")] 26 | pub fn init_default_sev() -> Self { 27 | Self { 28 | vmsa_features: 0, 29 | flags: 0, 30 | ghcb_version: 0, 31 | pad1: Default::default(), 32 | pad2: Default::default(), 33 | } 34 | } 35 | 36 | /// Default INIT2 values for SEV-ES 37 | #[cfg(feature = "sev")] 38 | pub fn init_default_es() -> Self { 39 | Self { 40 | vmsa_features: 0x1, 41 | flags: 0, 42 | ghcb_version: 1, 43 | pad1: Default::default(), 44 | pad2: Default::default(), 45 | } 46 | } 47 | 48 | /// Default INIT2 values for SEV-SNP 49 | #[cfg(feature = "snp")] 50 | pub fn init_default_snp() -> Self { 51 | Self { 52 | vmsa_features: 0, 53 | flags: 0, 54 | ghcb_version: 2, 55 | pad1: Default::default(), 56 | pad2: Default::default(), 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/launch/linux/snp.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Types for interacting with the KVM SEV-SNP guest management API. 4 | 5 | use crate::launch::snp::*; 6 | 7 | use std::marker::PhantomData; 8 | 9 | /// Initialize the SEV-SNP platform in KVM. 10 | #[derive(Default)] 11 | #[repr(C, packed)] 12 | pub struct Init { 13 | /// Reserved space, must be always set to 0 when issuing the ioctl. 14 | flags: u64, 15 | } 16 | 17 | #[repr(C)] 18 | pub struct LaunchStart { 19 | /// Guest policy. See Table 7 of the AMD SEV-SNP Firmware 20 | /// specification for a description of the guest policy structure. 21 | policy: u64, 22 | 23 | /// Hypervisor provided value to indicate guest OS visible workarounds. 24 | /// The format is hypervisor defined. 25 | gosvw: [u8; 16], 26 | 27 | flags: u16, 28 | 29 | pad0: [u8; 6], 30 | 31 | pad1: [u64; 4], 32 | } 33 | 34 | impl From for LaunchStart { 35 | fn from(start: Start) -> Self { 36 | Self { 37 | policy: start.policy.into(), 38 | gosvw: start.gosvw, 39 | flags: 0, 40 | pad0: [0u8; 6], 41 | pad1: [0u64; 4], 42 | } 43 | } 44 | } 45 | 46 | /// Insert pages into the guest physical address space. 47 | #[repr(C)] 48 | pub struct LaunchUpdate<'a> { 49 | /// guest start frame number. 50 | pub start_gfn: u64, 51 | 52 | /// Userspace address of the page needed to be encrypted. 53 | pub uaddr: u64, 54 | 55 | /// Length of the page needed to be encrypted: 56 | /// (end encryption uaddr = uaddr + len). 57 | pub len: u64, 58 | 59 | /// Encoded page type. See Table 58 if the SNP Firmware specification. 60 | pub page_type: u8, 61 | 62 | pad0: u8, 63 | 64 | flags: u16, 65 | 66 | pad1: u32, 67 | 68 | pad2: [u64; 4], 69 | 70 | _phantom: PhantomData<&'a [u8]>, 71 | } 72 | 73 | impl From> for LaunchUpdate<'_> { 74 | fn from(update: Update) -> Self { 75 | Self { 76 | start_gfn: update.start_gfn, 77 | uaddr: update.uaddr.as_ptr() as _, 78 | len: update.uaddr.len() as _, 79 | page_type: update.page_type as _, 80 | pad0: 0, 81 | flags: 0, 82 | pad1: 0, 83 | pad2: [0u64; 4], 84 | _phantom: PhantomData, 85 | } 86 | } 87 | } 88 | 89 | pub const KVM_SEV_SNP_FINISH_DATA_SIZE: usize = 32; 90 | 91 | /// Complete the guest launch flow. 92 | #[repr(C)] 93 | pub struct LaunchFinish<'a> { 94 | /// Userspace address of the ID block. Ignored if ID_BLOCK_EN is 0. 95 | id_block_uaddr: u64, 96 | 97 | /// Userspace address of the authentication information of the ID block. Ignored if ID_BLOCK_EN is 0. 98 | id_auth_uaddr: u64, 99 | 100 | /// Indicates that the ID block is present. 101 | id_block_en: u8, 102 | 103 | /// Indicates that the author key is present in the ID authentication information structure. 104 | /// Ignored if ID_BLOCK_EN is 0. 105 | auth_key_en: u8, 106 | 107 | /// Opaque host-supplied data to describe the guest. The firmware does not interpret this value. 108 | host_data: [u8; KVM_SEV_SNP_FINISH_DATA_SIZE], 109 | 110 | pad: [u8; 6], 111 | 112 | _phantom: PhantomData<&'a [u8]>, 113 | } 114 | 115 | impl From> for LaunchFinish<'_> { 116 | fn from(finish: Finish) -> Self { 117 | let id_block = if let Some(addr) = finish.id_block { 118 | addr.as_ptr() as u64 119 | } else { 120 | 0 121 | }; 122 | 123 | let id_auth = if let Some(addr) = finish.id_auth { 124 | addr.as_ptr() as u64 125 | } else { 126 | 0 127 | }; 128 | 129 | Self { 130 | id_block_uaddr: id_block, 131 | id_auth_uaddr: id_auth, 132 | id_block_en: u8::from(finish.id_block.is_some()), 133 | auth_key_en: u8::from(finish.id_auth.is_some()), 134 | host_data: finish.host_data, 135 | pad: [0u8; 6], 136 | _phantom: PhantomData, 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/launch/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Everything one needs to launch an AMD SEV encrypted virtual machine. 4 | //! 5 | //! This module contains types for establishing a secure channel with the 6 | //! AMD Secure Processor for purposes of attestation as well as abstractions 7 | //! for navigating the AMD SEV launch process for a virtual machine. 8 | 9 | #[cfg(target_os = "linux")] 10 | #[cfg(any(feature = "sev", feature = "snp"))] 11 | mod linux; 12 | 13 | #[cfg(feature = "sev")] 14 | pub mod sev; 15 | 16 | #[cfg(feature = "snp")] 17 | pub mod snp; 18 | -------------------------------------------------------------------------------- /src/measurement/gctx.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | //! Operations to handle and create a Guest Context 3 | use std::convert::TryInto; 4 | 5 | use openssl::sha::sha384; 6 | 7 | use crate::error::*; 8 | 9 | #[cfg(target_os = "linux")] 10 | use crate::{ 11 | launch::snp::PageType, 12 | measurement::snp::{SnpLaunchDigest, LD_BYTES}, 13 | }; 14 | 15 | // VMSA page is recorded in the RMP table with GPA (u64)(-1). 16 | // However, the address is page-aligned, and also all the bits above 17 | // 51 are cleared. 18 | pub(crate) const VMSA_GPA: u64 = 0xFFFFFFFFF000; 19 | 20 | // Launch digest sized array with all zeros 21 | const ZEROS: [u8; LD_BYTES] = [0; LD_BYTES]; 22 | 23 | fn validate_block_size(length: usize) -> Result<(), GCTXError> { 24 | if (length % 4096) != 0 { 25 | Err(GCTXError::InvalidBlockSize) 26 | } else { 27 | Ok(()) 28 | } 29 | } 30 | 31 | pub(crate) struct Updating; 32 | pub(crate) struct Completed; 33 | 34 | /// Guest context field structure 35 | pub struct Gctx { 36 | /// 48 byte Launch Digest 37 | ld: SnpLaunchDigest, 38 | /// Current GCTX state 39 | _state: T, 40 | } 41 | 42 | /// Default init of GCTX, launch digest of all 0s 43 | impl Default for Gctx { 44 | fn default() -> Self { 45 | Self { 46 | ld: SnpLaunchDigest::default(), 47 | _state: Updating, 48 | } 49 | } 50 | } 51 | 52 | impl Gctx { 53 | /// Initialize a new guest context using existing data 54 | pub fn new(seed: &[u8]) -> Result { 55 | Ok(Self { 56 | ld: seed.try_into()?, 57 | _state: Updating, 58 | }) 59 | } 60 | 61 | /// Will update guest context launch digest with provided data from page 62 | fn update(&mut self, page_type: u8, gpa: u64, contents: &[u8]) -> Result<(), MeasurementError> { 63 | let page_info_len: u16 = 0x70; 64 | let is_imi: u8 = 0; 65 | let vmpl3_perms: u8 = 0; 66 | let vmpl2_perms: u8 = 0; 67 | let vmpl1_perms: u8 = 0; 68 | 69 | let mut page_info: Vec = self.ld.try_into()?; 70 | page_info.extend_from_slice(contents); 71 | 72 | page_info.extend_from_slice(&page_info_len.to_le_bytes()); 73 | page_info.extend_from_slice(&page_type.to_le_bytes()); 74 | page_info.extend_from_slice(&is_imi.to_le_bytes()); 75 | 76 | page_info.extend_from_slice(&vmpl3_perms.to_le_bytes()); 77 | page_info.extend_from_slice(&vmpl2_perms.to_le_bytes()); 78 | page_info.extend_from_slice(&vmpl1_perms.to_le_bytes()); 79 | page_info.extend_from_slice(&(0_u8).to_le_bytes()); 80 | 81 | page_info.extend_from_slice(&gpa.to_le_bytes()); 82 | 83 | if page_info.len() != (page_info_len as usize) { 84 | return Err(GCTXError::InvalidPageSize( 85 | page_info.len(), 86 | page_info_len as usize, 87 | ))?; 88 | } 89 | self.ld = sha384(&page_info).as_slice().try_into()?; 90 | 91 | Ok(()) 92 | } 93 | 94 | /// Update Lanunch digest type according to page type and guest physical address. 95 | /// Some Page types don't require data. Some page types just require size of the page. 96 | #[cfg(target_os = "linux")] 97 | pub fn update_page( 98 | &mut self, 99 | page_type: PageType, 100 | gpa: u64, 101 | contents: Option<&[u8]>, 102 | length_bytes: Option, 103 | ) -> Result<(), MeasurementError> { 104 | match page_type { 105 | PageType::Normal => { 106 | if let Some(data) = contents { 107 | validate_block_size(data.len())?; 108 | let mut offset = 0; 109 | while offset < data.len() { 110 | let page_data = &data[offset..offset + 4096]; 111 | self.update( 112 | page_type as u8, 113 | gpa + offset as u64, 114 | sha384(page_data).as_slice(), 115 | )?; 116 | offset += 4096; 117 | } 118 | Ok(()) 119 | } else { 120 | Err(GCTXError::MissingData)? 121 | } 122 | } 123 | 124 | PageType::Vmsa => { 125 | if let Some(data) = contents { 126 | validate_block_size(data.len())?; 127 | self.update(page_type as u8, VMSA_GPA, sha384(data).as_slice())?; 128 | Ok(()) 129 | } else { 130 | Err(GCTXError::MissingData)? 131 | } 132 | } 133 | 134 | PageType::Zero => { 135 | if let Some(length_bytes) = length_bytes { 136 | validate_block_size(length_bytes)?; 137 | let mut offset = 0; 138 | while offset < length_bytes { 139 | self.update(page_type as u8, gpa + offset as u64, &ZEROS)?; 140 | offset += 4096; 141 | } 142 | Ok(()) 143 | } else { 144 | Err(GCTXError::MissingBlockSize)? 145 | } 146 | } 147 | 148 | PageType::Unmeasured => { 149 | self.update(page_type as u8, gpa, &ZEROS)?; 150 | Ok(()) 151 | } 152 | 153 | PageType::Secrets => { 154 | self.update(page_type as u8, gpa, &ZEROS)?; 155 | Ok(()) 156 | } 157 | 158 | PageType::Cpuid => { 159 | self.update(page_type as u8, gpa, &ZEROS)?; 160 | Ok(()) 161 | } 162 | } 163 | } 164 | 165 | /// Change State to Completed 166 | pub(crate) fn finished(&self) -> Gctx { 167 | Gctx { 168 | ld: self.ld, 169 | _state: Completed, 170 | } 171 | } 172 | } 173 | 174 | impl Gctx { 175 | /// Get the launch digest bytes 176 | pub(crate) fn ld(&self) -> SnpLaunchDigest { 177 | self.ld 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/measurement/idblock.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Functions to use to calculate the ID-BLOCK and the AUTH-BLOCK. 4 | 5 | use bincode; 6 | use openssl::{ec::EcKey, pkey::Private, sha::sha384}; 7 | use std::{ 8 | convert::{TryFrom, TryInto}, 9 | fs::File, 10 | io::Read, 11 | path::PathBuf, 12 | }; 13 | 14 | use crate::{ 15 | error::IdBlockError, 16 | firmware::guest::GuestPolicy, 17 | measurement::{ 18 | idblock_types::{ 19 | FamilyId, IdAuth, IdBlock, IdMeasurements, ImageId, SevEcdsaPubKey, SevEcdsaSig, 20 | CURVE_P384_NID, 21 | }, 22 | snp::SnpLaunchDigest, 23 | }, 24 | }; 25 | 26 | /// Generate an AUTH-BLOCK using 2 EC P-384 keys and an already calculated ID-BlOCK 27 | pub fn gen_id_auth_block( 28 | id_block: &IdBlock, 29 | id_key_file: PathBuf, 30 | author_key_file: PathBuf, 31 | ) -> Result { 32 | let id_ec_priv_key = load_priv_key(id_key_file)?; 33 | let id_ec_pub_key = SevEcdsaPubKey::try_from(&id_ec_priv_key)?; 34 | let id_sig = SevEcdsaSig::try_from(( 35 | id_ec_priv_key, 36 | bincode::serialize(id_block) 37 | .map_err(|e| IdBlockError::BincodeError(*e))? 38 | .as_slice(), 39 | ))?; 40 | 41 | let author_ec_priv_key = load_priv_key(author_key_file)?; 42 | let author_pub_key = SevEcdsaPubKey::try_from(&author_ec_priv_key)?; 43 | let author_sig = SevEcdsaSig::try_from(( 44 | author_ec_priv_key, 45 | bincode::serialize(&id_ec_pub_key) 46 | .map_err(|e| IdBlockError::BincodeError(*e))? 47 | .as_slice(), 48 | ))?; 49 | 50 | Ok(IdAuth::new( 51 | None, 52 | None, 53 | id_sig, 54 | id_ec_pub_key, 55 | author_sig, 56 | author_pub_key, 57 | )) 58 | } 59 | 60 | enum KeyFormat { 61 | Pem, 62 | Der, 63 | } 64 | 65 | const PEM_PREFIXES: &[&[u8]] = &[ 66 | b"-----BEGIN PRIVATE KEY-----", // PKCS8 67 | b"-----BEGIN EC PRIVATE KEY-----", // legacy EC 68 | b"-----BEGIN ENCRYPTED PRIVATE KEY-----", // encrypted PKCS8 69 | ]; 70 | 71 | /// Identifies the format of a key based on the first line specified 72 | /// for the PEM. A non-PEM format assumes a DER format. 73 | fn identify_priv_key_format(bytes: &[u8]) -> KeyFormat { 74 | if PEM_PREFIXES.iter().any(|prefix| bytes.starts_with(prefix)) { 75 | KeyFormat::Pem 76 | } else { 77 | KeyFormat::Der 78 | } 79 | } 80 | ///Read a key file and return a private EcKey. 81 | /// Key has to be an EC P-384 key. 82 | pub fn load_priv_key(path: PathBuf) -> Result, IdBlockError> { 83 | let mut key_data = Vec::new(); 84 | let mut file = match File::open(path) { 85 | Ok(file) => file, 86 | Err(e) => return Err(IdBlockError::FileError(e)), 87 | }; 88 | 89 | file.read_to_end(&mut key_data) 90 | .map_err(IdBlockError::FileError)?; 91 | 92 | let pkey = match identify_priv_key_format(&key_data) { 93 | KeyFormat::Pem => { 94 | EcKey::private_key_from_pem(&key_data).map_err(IdBlockError::CryptoErrorStack)? 95 | } 96 | KeyFormat::Der => { 97 | EcKey::private_key_from_der(&key_data).map_err(IdBlockError::CryptoErrorStack)? 98 | } 99 | }; 100 | 101 | pkey.check_key().map_err(IdBlockError::CryptoErrorStack)?; 102 | 103 | if let Some(name) = pkey.group().curve_name() { 104 | if name != CURVE_P384_NID { 105 | return Err(IdBlockError::SevCurveError()); 106 | }; 107 | }; 108 | 109 | Ok(pkey) 110 | } 111 | 112 | /// Generate the sha384 digest of the provided pem key (same sized digest as SNP Launch Digest) 113 | pub fn generate_key_digest(key_path: PathBuf) -> Result { 114 | let ec_key = load_priv_key(key_path)?; 115 | 116 | let pub_key = SevEcdsaPubKey::try_from(&ec_key)?; 117 | 118 | Ok(SnpLaunchDigest::new( 119 | sha384( 120 | bincode::serialize(&pub_key) 121 | .map_err(|e| IdBlockError::BincodeError(*e))? 122 | .as_slice(), 123 | ) 124 | .try_into()?, 125 | )) 126 | } 127 | 128 | /// Calculate the different pieces needed for a complete pre-attestation. 129 | /// ID-BLOCK, AUTH-BLOCK, id-key digest and auth-key digest. 130 | pub fn snp_calculate_id( 131 | ld: Option, 132 | family_id: Option, 133 | image_id: Option, 134 | svn: Option, 135 | policy: Option, 136 | id_key_file: PathBuf, 137 | auth_key_file: PathBuf, 138 | ) -> Result { 139 | let id_block = IdBlock::new(ld, family_id, image_id, svn, policy)?; 140 | 141 | Ok(IdMeasurements { 142 | id_block, 143 | id_auth: gen_id_auth_block(&id_block, id_key_file.clone(), auth_key_file.clone())?, 144 | 145 | id_key_digest: generate_key_digest(id_key_file)?, 146 | 147 | auth_key_digest: generate_key_digest(auth_key_file)?, 148 | }) 149 | } 150 | -------------------------------------------------------------------------------- /src/measurement/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Everything one needs to calculate a launch measurement for a SEV encrypted confidential guest. 4 | //! This includes, GCTX, SEV-HASHES, VMSA and OVMF pages. 5 | 6 | #[cfg(all(target_os = "linux", feature = "snp", feature = "openssl"))] 7 | pub mod gctx; 8 | 9 | #[cfg(any(feature = "sev", feature = "snp"))] 10 | pub mod ovmf; 11 | 12 | #[cfg(any(feature = "sev", feature = "snp"))] 13 | pub mod vmsa; 14 | 15 | #[cfg(all(any(feature = "sev", feature = "snp"), feature = "openssl"))] 16 | pub mod sev_hashes; 17 | 18 | #[cfg(any(feature = "sev", feature = "snp"))] 19 | pub mod vcpu_types; 20 | 21 | #[cfg(all(feature = "snp", feature = "openssl"))] 22 | pub mod snp; 23 | 24 | #[cfg(all(feature = "sev", feature = "openssl"))] 25 | pub mod sev; 26 | 27 | #[cfg(all(feature = "snp", feature = "openssl"))] 28 | pub mod idblock; 29 | 30 | #[cfg(all(feature = "snp", feature = "openssl"))] 31 | pub mod idblock_types; 32 | -------------------------------------------------------------------------------- /src/measurement/sev.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Operations to calculate guest measurement for different SEV modes 4 | use crate::measurement::{ 5 | ovmf::OVMF, 6 | sev_hashes::SevHashes, 7 | vcpu_types::CpuType, 8 | vmsa::{GuestFeatures, VMMType, VMSA}, 9 | }; 10 | 11 | use std::path::PathBuf; 12 | 13 | use crate::error::*; 14 | 15 | use openssl::sha::Sha256; 16 | 17 | const _PAGE_MASK: u64 = 0xfff; 18 | 19 | /// Get the launch digest as a hex string 20 | pub fn get_hex_ld(ld: Vec) -> String { 21 | hex::encode(ld) 22 | } 23 | 24 | /// Arguments required to calculate the SEV-ES measurement 25 | pub struct SevEsMeasurementArgs<'a> { 26 | /// Number of vcpus 27 | pub vcpus: u32, 28 | /// vcpu type 29 | pub vcpu_type: CpuType, 30 | /// Path to OVMF file 31 | pub ovmf_file: PathBuf, 32 | /// Path to kernel file 33 | pub kernel_file: Option, 34 | /// Path to initrd file 35 | pub initrd_file: Option, 36 | /// Append arguments for kernel 37 | pub append: Option<&'a str>, 38 | /// vmm type 39 | pub vmm_type: Option, 40 | } 41 | 42 | /// Calculate an SEV-ES launch digest 43 | pub fn seves_calc_launch_digest( 44 | sev_es_measurement: SevEsMeasurementArgs, 45 | ) -> Result<[u8; 32], MeasurementError> { 46 | let ovmf = OVMF::new(sev_es_measurement.ovmf_file)?; 47 | let mut launch_hash = Sha256::new(); 48 | launch_hash.update(ovmf.data().as_slice()); 49 | 50 | if let Some(kernel) = sev_es_measurement.kernel_file { 51 | if !ovmf.is_sev_hashes_table_supported() { 52 | return Err(MeasurementError::InvalidOvmfKernelError); 53 | } 54 | let sev_hashes = SevHashes::new( 55 | kernel, 56 | sev_es_measurement.initrd_file, 57 | sev_es_measurement.append, 58 | )? 59 | .construct_table()?; 60 | launch_hash.update(sev_hashes.as_slice()); 61 | }; 62 | 63 | let official_vmm_type = match sev_es_measurement.vmm_type { 64 | Some(vmm) => vmm, 65 | None => VMMType::QEMU, 66 | }; 67 | 68 | let vmsa = VMSA::new( 69 | ovmf.sev_es_reset_eip()?.into(), 70 | sev_es_measurement.vcpu_type, 71 | official_vmm_type, 72 | Some(sev_es_measurement.vcpus as u64), 73 | GuestFeatures(0x0), 74 | ); 75 | 76 | for vmsa_page in vmsa.pages(sev_es_measurement.vcpus as usize)?.iter() { 77 | launch_hash.update(vmsa_page.as_slice()) 78 | } 79 | 80 | Ok(launch_hash.finish()) 81 | } 82 | 83 | /// Arguments required to calculate the SEV measurement 84 | pub struct SevMeasurementArgs<'a> { 85 | /// Path to OVMF file 86 | pub ovmf_file: PathBuf, 87 | /// Path to kernel file 88 | pub kernel_file: Option, 89 | /// Path to initrd file 90 | pub initrd_file: Option, 91 | /// Append arguments for kernel 92 | pub append: Option<&'a str>, 93 | } 94 | 95 | /// Calculate an SEV launch digest 96 | pub fn sev_calc_launch_digest( 97 | sev_measurement: SevMeasurementArgs, 98 | ) -> Result<[u8; 32], MeasurementError> { 99 | let ovmf = OVMF::new(sev_measurement.ovmf_file)?; 100 | let mut launch_hash = Sha256::new(); 101 | launch_hash.update(ovmf.data().as_slice()); 102 | 103 | if let Some(kernel) = sev_measurement.kernel_file { 104 | if !ovmf.is_sev_hashes_table_supported() { 105 | return Err(MeasurementError::InvalidOvmfKernelError); 106 | } 107 | let sev_hashes = 108 | SevHashes::new(kernel, sev_measurement.initrd_file, sev_measurement.append)? 109 | .construct_table()?; 110 | launch_hash.update(sev_hashes.as_slice()); 111 | }; 112 | 113 | Ok(launch_hash.finish()) 114 | } 115 | -------------------------------------------------------------------------------- /src/session/key.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | 5 | use std::{ 6 | convert::*, 7 | ops::{Deref, DerefMut}, 8 | ptr::write_volatile, 9 | }; 10 | 11 | use rdrand::{ErrorCode, RdRand}; 12 | 13 | use openssl::*; 14 | 15 | #[repr(transparent)] 16 | pub struct Key(Vec); 17 | 18 | impl Drop for Key { 19 | fn drop(&mut self) { 20 | for b in self.0.iter_mut() { 21 | unsafe { 22 | write_volatile(b as *mut u8, 0u8); 23 | } 24 | } 25 | } 26 | } 27 | 28 | impl Deref for Key { 29 | type Target = [u8]; 30 | 31 | fn deref(&self) -> &[u8] { 32 | &self.0 33 | } 34 | } 35 | 36 | impl DerefMut for Key { 37 | fn deref_mut(&mut self) -> &mut [u8] { 38 | &mut self.0 39 | } 40 | } 41 | 42 | impl codicon::Encoder<()> for Key { 43 | type Error = Error; 44 | fn encode(&self, mut writer: impl Write, _: ()) -> std::io::Result<()> { 45 | writer.write_all(&self.0)?; 46 | 47 | Ok(()) 48 | } 49 | } 50 | 51 | impl Key { 52 | pub fn new(key: Vec) -> Self { 53 | Self(key) 54 | } 55 | 56 | pub fn zeroed(size: usize) -> Self { 57 | Key(vec![0u8; size]) 58 | } 59 | 60 | /// Will attempt to create a random Key derived from the CPU RDRAND instruction. 61 | pub fn random(size: usize) -> std::result::Result { 62 | // Create a new empty key to store the pseudo-random bytes in. 63 | let mut key = Key::zeroed(size); 64 | 65 | // Instantiate a pseudo-random number generator instance to pull 66 | // random data from the CPU RDRAND instruction set. 67 | let mut rng = RdRand::new()?; 68 | 69 | // Attempt to generate N-number of bytes specified by the `size` 70 | // parameter, storing the bytes inside they key generated at the 71 | // start of the method. 72 | rng.try_fill_bytes(&mut key)?; 73 | 74 | // Return the key when successful. 75 | Ok(key) 76 | } 77 | } 78 | 79 | impl Key { 80 | // NIST 800-108 5.1 - KDF in Counter Mode 81 | pub fn derive(&self, size: usize, ctx: &[u8], label: &str) -> Result { 82 | let hsh = hash::MessageDigest::sha256(); 83 | let key = pkey::PKey::hmac(self)?; 84 | 85 | let hbytes = hsh.size(); 86 | let _ = u32::try_from(hbytes * 8).or(Err(ErrorKind::InvalidInput))?; 87 | let lbits = u32::try_from(size * 8).or(Err(ErrorKind::InvalidInput))?; 88 | 89 | let mut out = Key::zeroed(size.div_ceil(hbytes) * hbytes); 90 | let mut buf = &mut out[..]; 91 | 92 | for i in 1..=size.div_ceil(hbytes) as u32 { 93 | let mut sig = sign::Signer::new(hsh, &key)?; 94 | 95 | sig.update(&i.to_le_bytes())?; 96 | sig.update(label.as_bytes())?; 97 | sig.update(&[0u8])?; 98 | sig.update(ctx)?; 99 | sig.update(&lbits.to_le_bytes())?; 100 | 101 | sig.sign(buf)?; 102 | buf = &mut buf[hbytes..]; 103 | } 104 | 105 | out.0.truncate(size); 106 | Ok(out) 107 | } 108 | 109 | pub fn mac(&self, data: &[u8]) -> Result<[u8; 32]> { 110 | let mut mac = [0u8; 32]; 111 | let key = pkey::PKey::hmac(self)?; 112 | let mut sig = sign::Signer::new(hash::MessageDigest::sha256(), &key)?; 113 | 114 | sig.update(data)?; 115 | sig.sign(&mut mac)?; 116 | Ok(mac) 117 | } 118 | } 119 | 120 | #[cfg(test)] 121 | #[test] 122 | fn derive() { 123 | let master = Key::zeroed(16) 124 | .derive(16, &[0u8; 16], "sev-master-secret") 125 | .unwrap(); 126 | assert_eq!( 127 | master.0, 128 | vec![ 129 | 0xab, 0x4d, 0x26, 0x9f, 0xcc, 0x62, 0xbe, 0xdb, 0x45, 0x11, 0xd5, 0x6c, 0x38, 0x6c, 130 | 0xe7, 0x06, 131 | ] 132 | ) 133 | } 134 | 135 | #[cfg(test)] 136 | #[test] 137 | fn mac() { 138 | let mac = Key::zeroed(16).mac(&[0u8; 4]).unwrap(); 139 | assert_eq!( 140 | mac, 141 | [ 142 | 0xaa, 0x78, 0x55, 0xe1, 0x38, 0x39, 0xdd, 0x76, 0x7c, 0xd5, 0xda, 0x7c, 0x1f, 0xf5, 143 | 0x03, 0x65, 0x40, 0xc9, 0x26, 0x4b, 0x7a, 0x80, 0x30, 0x29, 0x31, 0x5e, 0x55, 0x37, 144 | 0x52, 0x87, 0xb4, 0xaf, 145 | ] 146 | ) 147 | } 148 | -------------------------------------------------------------------------------- /src/util/array.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Helpful structure to deal with arrays with a size larger than 32 bytes 4 | 5 | use crate::error::ArrayError; 6 | 7 | #[cfg(feature = "snp")] 8 | use crate::util::parser::ByteParser; 9 | 10 | use serde::{Deserialize, Serialize}; 11 | use serde_big_array::BigArray; 12 | use std::{ 13 | convert::{TryFrom, TryInto}, 14 | fmt::{Debug, LowerHex, UpperHex}, 15 | ops::{Deref, DerefMut}, 16 | }; 17 | 18 | /// Large array structure to serialize and default arrays larger than 32 bytes. 19 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] 20 | pub struct Array(#[serde(with = "BigArray")] pub [T; N]) 21 | where 22 | T: Serialize + for<'a> Deserialize<'a>; 23 | 24 | impl LowerHex for Array 25 | where 26 | T: std::marker::Copy 27 | + std::default::Default 28 | + for<'a> Deserialize<'a> 29 | + Serialize 30 | + Debug 31 | + LowerHex, 32 | { 33 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 34 | for byte in self.0.iter() { 35 | write!(f, "{byte:02x}")?; 36 | } 37 | Ok(()) 38 | } 39 | } 40 | 41 | impl UpperHex for Array 42 | where 43 | T: std::marker::Copy 44 | + std::default::Default 45 | + for<'a> Deserialize<'a> 46 | + Serialize 47 | + Debug 48 | + UpperHex, 49 | { 50 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 51 | for byte in self.0.iter() { 52 | write!(f, "{byte:02X}")?; 53 | } 54 | Ok(()) 55 | } 56 | } 57 | 58 | impl std::fmt::Display for Array 59 | where 60 | T: std::marker::Copy 61 | + std::default::Default 62 | + for<'a> Deserialize<'a> 63 | + Serialize 64 | + Debug 65 | + UpperHex, 66 | { 67 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 68 | writeln!(f)?; 69 | for (i, byte) in self.0.iter().enumerate() { 70 | if i > 0 && i % 16 == 0 { 71 | writeln!(f)?; 72 | } else if i > 0 { 73 | write!(f, " ")?; 74 | } 75 | write!(f, "{byte:02X}")?; 76 | } 77 | Ok(()) 78 | } 79 | } 80 | 81 | #[cfg(feature = "snp")] 82 | impl ByteParser for Array { 83 | type Bytes = [u8; N]; 84 | 85 | #[inline] 86 | fn from_bytes(bytes: Self::Bytes) -> Self { 87 | Self(bytes) 88 | } 89 | 90 | #[inline] 91 | fn to_bytes(&self) -> Self::Bytes { 92 | self.0 93 | } 94 | 95 | #[inline] 96 | fn default() -> Self { 97 | Self([0; N]) 98 | } 99 | } 100 | 101 | impl Default for Array 102 | where 103 | T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize, 104 | { 105 | fn default() -> Self { 106 | Self([T::default(); N]) 107 | } 108 | } 109 | 110 | impl TryFrom> for Array 111 | where 112 | T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize, 113 | { 114 | type Error = ArrayError; 115 | 116 | fn try_from(vec: Vec) -> Result { 117 | Ok(Array(vec.try_into().map_err(|_| { 118 | ArrayError::VectorError("Vector is the wrong size".to_string()) 119 | })?)) 120 | } 121 | } 122 | 123 | impl TryFrom<[T; N]> for Array 124 | where 125 | T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize, 126 | { 127 | type Error = ArrayError; 128 | 129 | fn try_from(array: [T; N]) -> Result { 130 | Ok(Array(array)) 131 | } 132 | } 133 | 134 | impl TryFrom<&[T]> for Array 135 | where 136 | T: std::marker::Copy 137 | + std::default::Default 138 | + for<'a> Deserialize<'a> 139 | + Serialize 140 | + Debug 141 | + LowerHex, 142 | { 143 | type Error = ArrayError; 144 | 145 | fn try_from(slice: &[T]) -> Result { 146 | Ok(Array(slice.try_into()?)) 147 | } 148 | } 149 | 150 | impl AsRef<[T]> for Array 151 | where 152 | T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize, 153 | { 154 | #[inline] 155 | fn as_ref(&self) -> &[T] { 156 | self.0.as_ref() 157 | } 158 | } 159 | 160 | impl AsMut<[T]> for Array 161 | where 162 | T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize, 163 | { 164 | #[inline] 165 | fn as_mut(&mut self) -> &mut [T] { 166 | self.0.as_mut() 167 | } 168 | } 169 | 170 | impl Deref for Array 171 | where 172 | T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize, 173 | { 174 | type Target = [T; N]; 175 | 176 | #[inline] 177 | fn deref(&self) -> &Self::Target { 178 | &self.0 179 | } 180 | } 181 | 182 | impl DerefMut for Array 183 | where 184 | T: std::marker::Copy + std::default::Default + for<'a> Deserialize<'a> + Serialize, 185 | { 186 | #[inline] 187 | fn deref_mut(&mut self) -> &mut Self::Target { 188 | &mut self.0 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/util/cached_chain.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Utilities for adhering to a cached SEV chain convention. 4 | //! 5 | //! The search path for the SEV chain is: 6 | //! 1. The path specified in the "SEV_CHAIN" environment variable 7 | //! (if present). 8 | //! 2. `$HOME/.cache/amd-sev/chain` 9 | //! 3. `/var/cache/amd-sev/chain` 10 | //! 11 | //! An entire certificate chain can be created using the `sevctl` 12 | //! utility. 13 | 14 | #![cfg(all(feature = "sev", feature = "dangerous_hw_tests"))] 15 | 16 | #[cfg(feature = "openssl")] 17 | use crate::{ 18 | certs::sev::{ca::Chain as CaChain, Chain as FullChain}, 19 | firmware::host::Firmware, 20 | sev::Certificate, 21 | Generation, 22 | }; 23 | 24 | #[cfg(feature = "openssl")] 25 | use reqwest::{ 26 | blocking::{get, Response}, 27 | StatusCode, 28 | }; 29 | 30 | use std::{ 31 | env, 32 | path::{Path, PathBuf}, 33 | }; 34 | 35 | #[cfg(feature = "openssl")] 36 | use std::io::Cursor; 37 | 38 | #[cfg(feature = "openssl")] 39 | use codicon::Decoder; 40 | 41 | fn append_rest>(path: P) -> PathBuf { 42 | let mut path = path.as_ref().to_path_buf(); 43 | path.push("amd-sev"); 44 | path.push("chain"); 45 | path 46 | } 47 | 48 | /// Returns the path stored in the optional `SEV_CHAIN` 49 | /// environment variable. 50 | pub fn env_var() -> Option { 51 | env::var("SEV_CHAIN").ok().map(PathBuf::from) 52 | } 53 | 54 | /// Returns the "user-level" search path for the SEV 55 | /// certificate chain (`$HOME/.cache/amd-sev/chain`). 56 | pub fn home() -> Option { 57 | dirs::cache_dir().map(append_rest) 58 | } 59 | 60 | /// Returns the "system-level" search path for the SEV 61 | /// certificate chain (`/var/cache/amd-sev/chain`). 62 | pub fn sys() -> Option { 63 | let sys = PathBuf::from("/var/cache"); 64 | if sys.exists() { 65 | Some(append_rest(sys)) 66 | } else { 67 | None 68 | } 69 | } 70 | 71 | /// Returns the list of search paths in the order that they 72 | /// will be searched for the SEV certificate chain. 73 | pub fn path() -> Vec { 74 | vec![env_var(), home(), sys()] 75 | .into_iter() 76 | .flatten() 77 | .collect() 78 | } 79 | 80 | /// Remove any certificates that may have been chached to reset 81 | /// testing for SEV APIS. 82 | pub fn rm_cached_chain() { 83 | let paths = path(); 84 | if let Some(path) = paths.first() { 85 | if path.exists() { 86 | std::fs::remove_file(path).unwrap(); 87 | } 88 | } 89 | } 90 | 91 | /// Request CEK certificate from AMD KDS and generate a full chain. 92 | #[cfg(all(feature = "sev", feature = "openssl"))] 93 | pub fn get_chain() -> FullChain { 94 | use std::convert::TryFrom; 95 | 96 | let mut firmware = Firmware::open().unwrap(); 97 | 98 | const CEK_SVC: &str = "https://kdsintf.amd.com/cek/id"; 99 | 100 | let mut sev_chain = firmware.pdh_cert_export().unwrap(); 101 | 102 | let id = firmware.get_identifier().unwrap(); 103 | 104 | let url = format!("{}/{}", CEK_SVC, id); 105 | 106 | // VCEK in DER format 107 | let vcek_rsp: Response = get(url).expect("Failed to get CEK certificate"); 108 | 109 | let cek_resp_bytes = match vcek_rsp.status() { 110 | StatusCode::OK => { 111 | let vcek_rsp_bytes: Vec = vcek_rsp.bytes().unwrap().to_vec(); 112 | vcek_rsp_bytes 113 | } 114 | _ => panic!("Cek request returned an error"), 115 | }; 116 | 117 | // Create a Cursor around the byte vector 118 | let cursor = Cursor::new(cek_resp_bytes); 119 | 120 | sev_chain.cek = Certificate::decode(cursor, ()).expect("Failed to decode CEK cert"); 121 | 122 | let ca_chain: CaChain = Generation::try_from(&sev_chain) 123 | .expect("Failed to generate SEV CA chain") 124 | .into(); 125 | 126 | FullChain { 127 | ca: ca_chain, 128 | sev: sev_chain, 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/util/impl_const_id.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! A simple const generics substitute. 4 | 5 | #[doc(hidden)] 6 | #[macro_export] 7 | macro_rules! impl_const_id { 8 | ( 9 | $(#[$outer:meta])* 10 | $visibility:vis $trait:ident => $id_ty:ty; 11 | $( 12 | $iocty:ty = $val:expr 13 | ),* $(,)* 14 | ) => { 15 | $(#[$outer])* 16 | $visibility trait $trait { 17 | $(#[$outer])* 18 | const ID: $id_ty; 19 | } 20 | 21 | $( 22 | impl $trait for $iocty { 23 | const ID: $id_ty = $val; 24 | } 25 | )* 26 | }; 27 | } 28 | 29 | #[cfg(test)] 30 | mod tests { 31 | struct A; 32 | struct B; 33 | struct C; 34 | 35 | impl_const_id! { 36 | Id => usize; 37 | A = 1, 38 | B = 2, 39 | C = 3, 40 | } 41 | 42 | #[test] 43 | fn test_const_id_macro() { 44 | assert_eq!(A::ID, 1); 45 | assert_eq!(B::ID, 2); 46 | assert_eq!(C::ID, 3); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/util/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | //! Helpful primitives for developing the crate. 4 | 5 | pub mod array; 6 | pub mod cached_chain; 7 | mod impl_const_id; 8 | #[cfg(feature = "snp")] 9 | pub mod parser; 10 | 11 | use std::{ 12 | io::{Read, Result, Write}, 13 | mem::{size_of, MaybeUninit}, 14 | slice::{from_raw_parts, from_raw_parts_mut}, 15 | }; 16 | 17 | pub trait TypeLoad: Read { 18 | fn load(&mut self) -> Result { 19 | #[allow(clippy::uninit_assumed_init)] 20 | let mut t = unsafe { MaybeUninit::uninit().assume_init() }; 21 | let p = &mut t as *mut T as *mut u8; 22 | let s = unsafe { from_raw_parts_mut(p, size_of::()) }; 23 | self.read_exact(s)?; 24 | Ok(t) 25 | } 26 | } 27 | 28 | pub trait TypeSave: Write { 29 | fn save(&mut self, value: &T) -> Result<()> { 30 | let p = value as *const T as *const u8; 31 | let s = unsafe { from_raw_parts(p, size_of::()) }; 32 | self.write_all(s) 33 | } 34 | } 35 | 36 | impl TypeLoad for T {} 37 | impl TypeSave for T {} 38 | -------------------------------------------------------------------------------- /src/util/parser/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | mod byte_parser; 4 | 5 | mod read_ext; 6 | 7 | mod write_ext; 8 | 9 | pub(crate) use byte_parser::ByteParser; 10 | 11 | pub(crate) use read_ext::ReadExt; 12 | 13 | pub(crate) use write_ext::WriteExt; 14 | -------------------------------------------------------------------------------- /src/util/parser/read_ext.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | use super::byte_parser::ByteParser; 3 | use std::io::Read; 4 | 5 | pub trait ReadExt: Read { 6 | fn parse_bytes(&mut self) -> Result 7 | where 8 | T: ByteParser>, 9 | { 10 | let mut bytes = T::default().to_bytes(); 11 | self.read_exact(bytes.as_mut())?; 12 | Ok(T::from_bytes(bytes)) 13 | } 14 | 15 | fn skip_bytes(mut self) -> Result 16 | where 17 | Self: Sized, 18 | { 19 | if SKIP != 0 { 20 | let mut skipped_bytes = [0u8; SKIP]; 21 | self.read_exact(&mut skipped_bytes)?; 22 | 23 | if skipped_bytes != [0; SKIP] { 24 | return Err(std::io::Error::new( 25 | std::io::ErrorKind::InvalidData, 26 | "Skipped bytes were expected to be zeroed.", 27 | )); 28 | } 29 | } 30 | Ok(self) 31 | } 32 | } 33 | 34 | impl ReadExt for R where R: Read {} 35 | 36 | #[cfg(test)] 37 | mod read_ext_tests { 38 | use super::*; 39 | use std::io::{self, Read}; 40 | 41 | // Mock Reader implementation 42 | #[derive(Debug)] 43 | struct MockReader { 44 | data: Vec, 45 | position: usize, 46 | } 47 | 48 | impl MockReader { 49 | fn new(data: Vec) -> Self { 50 | MockReader { data, position: 0 } 51 | } 52 | } 53 | 54 | impl Read for MockReader { 55 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 56 | let len = std::cmp::min(buf.len(), self.data.len() - self.position); 57 | if len > 0 { 58 | buf[..len].copy_from_slice(&self.data[self.position..self.position + len]); 59 | self.position += len; 60 | Ok(len) 61 | } else { 62 | Ok(0) // Indicate EOF 63 | } 64 | } 65 | } 66 | 67 | // Test case 1: No Skip, Valid Data 68 | #[test] 69 | fn test_no_skip_valid_data() { 70 | let data = vec![0x12, 0x34, 0x56, 0x78]; 71 | let mut reader = MockReader::new(data); 72 | let result: Result = reader.parse_bytes(); 73 | assert_eq!(result.unwrap(), 0x78563412); 74 | } 75 | 76 | // Test case 2: Skip, Valid Data 77 | #[test] 78 | fn test_skip_valid_data() { 79 | let data = vec![0, 0, 0, 0, 0x12, 0x34, 0x56, 0x78]; 80 | let reader = MockReader::new(data); 81 | let result: Result = reader.skip_bytes::<4>().unwrap().parse_bytes(); 82 | assert_eq!(result.unwrap(), 0x78563412); 83 | } 84 | 85 | // Test case 3: Skip, Invalid Data 86 | #[test] 87 | fn test_skip_invalid_data() { 88 | let data = vec![0, 0, 1, 0, 0x12, 0x34, 0x56, 0x78]; 89 | let reader = MockReader::new(data); 90 | let result = reader.skip_bytes::<4>(); 91 | assert!(result.is_err()); 92 | assert_eq!(result.unwrap_err().kind(), io::ErrorKind::InvalidData); 93 | } 94 | 95 | // Test case 4: Read Fails (UnexpectedEof) 96 | #[test] 97 | fn test_read_fails() { 98 | let data = vec![0x12, 0x34]; 99 | let mut reader = MockReader::new(data); 100 | let result: Result = reader.parse_bytes(); 101 | assert!(result.is_err()); 102 | assert_eq!(result.unwrap_err().kind(), io::ErrorKind::UnexpectedEof); 103 | } 104 | 105 | // Test case 5: Zero Length Read 106 | #[test] 107 | fn test_zero_length_read() { 108 | let data: Vec = vec![]; 109 | let mut reader = MockReader::new(data); 110 | let result: Result<[u8; 0], _> = reader.parse_bytes(); 111 | assert!(result.is_ok()); 112 | } 113 | 114 | // Test case 6: Empty Reader 115 | #[test] 116 | fn test_empty_reader() { 117 | let data: Vec = vec![]; 118 | let mut reader = MockReader::new(data); 119 | let result: Result = reader.parse_bytes(); 120 | assert!(result.is_err()); 121 | assert_eq!(result.unwrap_err().kind(), io::ErrorKind::UnexpectedEof); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/util/parser/write_ext.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | use super::byte_parser::ByteParser; 3 | use std::io::Write; 4 | 5 | pub trait WriteExt: Write { 6 | fn write_bytes(&mut self, value: T) -> Result<(), std::io::Error> { 7 | self.write_all(value.to_bytes().as_ref()) 8 | } 9 | 10 | fn skip_bytes(&mut self) -> Result<&mut Self, std::io::Error> 11 | where 12 | Self: Sized, 13 | { 14 | if SKIP != 0 { 15 | self.write_all(&[0; SKIP])?; 16 | } 17 | Ok(self) 18 | } 19 | } 20 | 21 | impl WriteExt for W where W: Write {} 22 | 23 | #[cfg(test)] 24 | mod write_ext_tests { 25 | use super::*; 26 | 27 | // Mock writer to capture written bytes 28 | #[derive(Debug, Default)] 29 | struct MockWriter { 30 | written: Vec, 31 | } 32 | 33 | impl Write for MockWriter { 34 | fn write(&mut self, buf: &[u8]) -> Result { 35 | self.written.extend_from_slice(buf); 36 | Ok(buf.len()) 37 | } 38 | 39 | fn flush(&mut self) -> Result<(), std::io::Error> { 40 | Ok(()) 41 | } 42 | } 43 | 44 | #[test] 45 | fn test_write_bytes_no_skip() -> Result<(), std::io::Error> { 46 | let mut writer = MockWriter::default(); 47 | let value: u32 = 0x12345678; 48 | writer.write_bytes(value)?; 49 | 50 | assert_eq!(writer.written, [0x78, 0x56, 0x34, 0x12]); 51 | Ok(()) 52 | } 53 | 54 | #[test] 55 | fn test_write_bytes_with_skip() -> Result<(), std::io::Error> { 56 | let mut writer = MockWriter::default(); 57 | let value: u16 = 0xABCD; 58 | writer.skip_bytes::<2>()?.write_bytes(value)?; 59 | 60 | assert_eq!(writer.written, [0, 0, 0xCD, 0xAB]); 61 | Ok(()) 62 | } 63 | 64 | #[test] 65 | fn test_mock_writer_flush() -> Result<(), std::io::Error> { 66 | let mut writer = MockWriter::default(); 67 | writer.flush()?; 68 | // Flush doesn't modify the written buffer, so we just check that it executes without error. 69 | assert_eq!(writer.written.len(), 0); 70 | Ok(()) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/certs.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #[cfg(feature = "sev")] 4 | mod naples; 5 | 6 | #[cfg(feature = "sev")] 7 | mod rome; 8 | 9 | #[cfg(all(feature = "openssl", feature = "sev"))] 10 | mod sev { 11 | use super::*; 12 | 13 | #[test] 14 | fn test_for_verify_false_positive() { 15 | use ::sev::certs::sev::*; 16 | use codicon::Decoder; 17 | 18 | // https://github.com/enarx/enarx/issues/520 19 | let naples_cek = sev::Certificate::decode(&mut &naples::CEK[..], ()).unwrap(); 20 | let rome_ask = ca::Certificate::decode(&mut &builtin::rome::ASK[..], ()).unwrap(); 21 | assert!((&rome_ask, &naples_cek).verify().is_err()); 22 | } 23 | } 24 | 25 | #[cfg(all(feature = "snp", any(feature = "openssl", feature = "crypto_nossl")))] 26 | mod snp { 27 | 28 | use sev::certs::snp::{builtin::milan, ca, Certificate, Chain, Verifiable}; 29 | 30 | const TEST_MILAN_VCEK_DER: &[u8] = include_bytes!("certs_data/vcek_milan.der"); 31 | 32 | #[cfg(feature = "openssl")] 33 | const TEST_TURIN_VCEK_DER: &[u8] = include_bytes!("certs_data/vcek_turin.der"); 34 | 35 | const TEST_MILAN_ATTESTATION_REPORT: &[u8] = include_bytes!("certs_data/report_milan.hex"); 36 | 37 | #[cfg(feature = "openssl")] 38 | const TEST_MILAN_CA: &[u8] = include_bytes!("certs_data/cert_chain_milan"); 39 | 40 | #[cfg(feature = "openssl")] 41 | const TEST_TURIN_CA: &[u8] = include_bytes!("certs_data/cert_chain_turin"); 42 | 43 | #[test] 44 | fn milan_chain() { 45 | let ark = milan::ark().unwrap(); 46 | let ask = milan::ask().unwrap(); 47 | let vcek = Certificate::from_der(TEST_MILAN_VCEK_DER).unwrap(); 48 | 49 | let ca = ca::Chain { ark, ask }; 50 | 51 | let chain = Chain { 52 | ca, 53 | vek: vcek.clone(), 54 | }; 55 | 56 | assert_eq!(chain.verify().ok(), Some(&vcek)); 57 | } 58 | 59 | #[test] 60 | fn milan_chain_invalid() { 61 | let ark = milan::ark().unwrap(); 62 | let ask = milan::ask().unwrap(); 63 | let vcek = { 64 | let mut buf = TEST_MILAN_VCEK_DER.to_vec(); 65 | buf[40] ^= 0xff; 66 | Certificate::from_der(&buf).unwrap() 67 | }; 68 | 69 | let ca = ca::Chain { ark, ask }; 70 | 71 | let chain = Chain { ca, vek: vcek }; 72 | 73 | assert_eq!(chain.verify().ok(), None); 74 | } 75 | 76 | #[test] 77 | fn milan_report() { 78 | use sev::firmware::guest::AttestationReport; 79 | 80 | let ark = milan::ark().unwrap(); 81 | let ask = milan::ask().unwrap(); 82 | let vcek = Certificate::from_der(TEST_MILAN_VCEK_DER).unwrap(); 83 | 84 | let ca = ca::Chain { ark, ask }; 85 | 86 | let chain = Chain { ca, vek: vcek }; 87 | 88 | let report_bytes = hex::decode(TEST_MILAN_ATTESTATION_REPORT).unwrap(); 89 | let report: AttestationReport = AttestationReport::from_bytes(&report_bytes).unwrap(); 90 | 91 | assert_eq!((&chain, &report).verify().ok(), Some(())); 92 | } 93 | 94 | #[test] 95 | fn milan_report_invalid() { 96 | use sev::firmware::guest::AttestationReport; 97 | 98 | let ark = milan::ark().unwrap(); 99 | let ask = milan::ask().unwrap(); 100 | let vcek = Certificate::from_der(TEST_MILAN_VCEK_DER).unwrap(); 101 | 102 | let ca = ca::Chain { ark, ask }; 103 | 104 | let chain = Chain { ca, vek: vcek }; 105 | 106 | let mut report_bytes = hex::decode(TEST_MILAN_ATTESTATION_REPORT).unwrap(); 107 | report_bytes[21] ^= 0x80; 108 | let report = AttestationReport::from_bytes(&report_bytes).unwrap(); 109 | 110 | assert_eq!((&chain, &report).verify().ok(), None); 111 | } 112 | 113 | #[cfg(feature = "openssl")] 114 | #[test] 115 | fn milan_ca_stack() { 116 | let vcek = Certificate::from_der(TEST_MILAN_VCEK_DER).unwrap(); 117 | 118 | let ca = ca::Chain::from_pem_bytes(TEST_MILAN_CA).unwrap(); 119 | 120 | let chain = Chain { 121 | ca, 122 | vek: vcek.clone(), 123 | }; 124 | 125 | assert_eq!(chain.verify().ok(), Some(&vcek)); 126 | } 127 | 128 | #[cfg(feature = "openssl")] 129 | #[test] 130 | fn turin_ca_stack() { 131 | let vcek = Certificate::from_der(TEST_TURIN_VCEK_DER).unwrap(); 132 | 133 | let ca = ca::Chain::from_pem_bytes(TEST_TURIN_CA).unwrap(); 134 | 135 | let chain = Chain { 136 | ca, 137 | vek: vcek.clone(), 138 | }; 139 | 140 | assert_eq!(chain.verify().ok(), Some(&vcek)); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /tests/certs_data/cert_chain_milan: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 37 | -----END CERTIFICATE----- 38 | -----BEGIN CERTIFICATE----- 39 | MIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC 40 | BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS 41 | BgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg 42 | Q2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp 43 | Y2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy 44 | MTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS 45 | BgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j 46 | ZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG 47 | 9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg 48 | W41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta 49 | 1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2 50 | SzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0 51 | 60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05 52 | gmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg 53 | bKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs 54 | +gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi 55 | Qi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ 56 | eTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18 57 | fHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j 58 | WhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI 59 | rFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG 60 | KWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG 61 | SIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI 62 | AWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel 63 | ETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw 64 | STjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK 65 | dHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq 66 | zT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp 67 | KGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e 68 | pmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq 69 | HnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh 70 | 3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn 71 | JZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH 72 | CViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4 73 | AFZEAwoKCQ== 74 | -----END CERTIFICATE----- 75 | -------------------------------------------------------------------------------- /tests/certs_data/cert_chain_turin: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | 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 37 | -----END CERTIFICATE----- 38 | -----BEGIN CERTIFICATE----- 39 | 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 73 | zpacMwFusA== 74 | -----END CERTIFICATE----- 75 | -------------------------------------------------------------------------------- /tests/certs_data/report_milan.hex: -------------------------------------------------------------------------------- 1 | 0200000000000000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000030000000000087301000000000000000000000000000000D447B55D197491BFE15CF298F9DE9986B7A7C4BE2468B4F6E2D53B71D7C645810B0F2CDFCA0040433BE063FC1A8293F0F3F8DAE7B79FECB3D1CD82BD6A93EBFD7A1E5C266C0108DBC9BB94FA926951320940915D0AAFB42464BD88B579EA158D3E1A0DC39B2C60BD95B9C480CD81841F000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000092B3B47D59F0A2A10A74C5678868A80238CF593C01A82F3CFFB878E904C28D5BFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0300000000000873000000000000000000000000000000000000000000000000D49554EC717F4E5B0FE6B143BCF0405BD7AE304727EDF46603F2A76AEF6A3ABC15D7AF38DB757039029F0EFACFD08E244324884738C72B082E2F87A44D541EB603000000000008730434010004340100030000000000087300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000061AB4F11AA661997625F233DF42A4AD54440EEB7A96EA63DE170CBC29C37C005CB54054881EC7D2BEE569B02D07F8272000000000000000000000000000000000000000000000000209D7EB9BE919A1D0BAF1D57FE6EBFEABBC53B778C6E977E40B15CA931BB6D44C5AB9E30CFDC7346CB41AC083B90BF490000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -------------------------------------------------------------------------------- /tests/certs_data/vcek_milan.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/tests/certs_data/vcek_milan.der -------------------------------------------------------------------------------- /tests/certs_data/vcek_turin.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/tests/certs_data/vcek_turin.der -------------------------------------------------------------------------------- /tests/guest.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #![cfg(all(feature = "snp", target_os = "linux"))] 4 | 5 | use sev::firmware::guest::*; 6 | 7 | #[cfg_attr(not(guest), ignore)] 8 | #[test] 9 | fn get_report() { 10 | let unique_data = [0u8; 64]; 11 | 12 | let mut fw = Firmware::open().unwrap(); 13 | 14 | fw.get_report(None, Some(unique_data), None).unwrap(); 15 | } 16 | 17 | #[cfg_attr(not(guest), ignore)] 18 | #[test] 19 | fn get_ext_report() { 20 | let unique_data = [0u8; 64]; 21 | 22 | let mut fw = Firmware::open().unwrap(); 23 | 24 | fw.get_ext_report(None, Some(unique_data), None).unwrap(); 25 | } 26 | 27 | #[cfg_attr(not(guest), ignore)] 28 | #[test] 29 | fn get_derived_key() { 30 | let derived_key = DerivedKey::new(false, GuestFieldSelect(1), 0, 0, 0); 31 | 32 | let mut fw = Firmware::open().unwrap(); 33 | 34 | fw.get_derived_key(None, derived_key).unwrap(); 35 | } 36 | 37 | #[cfg_attr(not(guest), ignore)] 38 | #[test] 39 | fn guest_fw_error() { 40 | let derived_key = DerivedKey::new( 41 | false, 42 | GuestFieldSelect(48), 43 | 0xFFFFFFFF, 44 | 0xFFFFFFFF, 45 | 0xFFFFFFFFFFFFFFFF, 46 | ); 47 | 48 | let mut fw = Firmware::open().unwrap(); 49 | 50 | let fw_err = fw 51 | .get_derived_key(None, derived_key) 52 | .unwrap_err() 53 | .to_string(); 54 | 55 | assert_eq!(fw_err, "Firmware Error Encountered: Known SEV FW Error: Status Code: 0x16: Given parameter is invalid.") 56 | } 57 | -------------------------------------------------------------------------------- /tests/measurement/ovmf_AmdSev_suffix.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/tests/measurement/ovmf_AmdSev_suffix.bin -------------------------------------------------------------------------------- /tests/measurement/ovmf_OvmfX64_suffix.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/tests/measurement/ovmf_OvmfX64_suffix.bin -------------------------------------------------------------------------------- /tests/measurement/test_auth_block.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/tests/measurement/test_auth_block.bin -------------------------------------------------------------------------------- /tests/measurement/test_auth_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAyp3rzsOsAMVIVbtmS 3 | QEU83YC5r0EOWZfSX3wwiBQytEjoVsXDBPTpvvyzM2aZ27ChZANiAARicMeL1pBM 4 | 0TNimaJbmHNqcc6S/vB0lNcssosHPIMPn1ZK96XBxv/Fo0lz1dpFhoyGPHkwosmE 5 | SKd6EAiBAuKpX5MIzT5WtVm3/TrMiLPMKhl3Q0j+ovLJAxGR4HGHrPU= 6 | -----END PRIVATE KEY----- 7 | -------------------------------------------------------------------------------- /tests/measurement/test_auth_sig.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/tests/measurement/test_auth_sig.bin -------------------------------------------------------------------------------- /tests/measurement/test_id_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDPZQkU2FP1ii90R+e7 3 | /ShQ/rzUzsS8X7kVJ0nJAxcrS1oMD8nKjfsKKfBWJ8nbCyqhZANiAAT2dj5NJgnP 4 | GyzyLyzIuMpM9V1HDDwjVSyTsWiHQ5Ddlcs/3uhH3fef8p77uIA8bR/M16jQR7nN 5 | wcdQsRkKvm0drTeo3ssUMnt2Zhn3EmB8/Q04hTxQ2pvNCAs1OJyTvj4= 6 | -----END PRIVATE KEY----- 7 | -------------------------------------------------------------------------------- /tests/measurement/test_id_sig.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/tests/measurement/test_id_sig.bin -------------------------------------------------------------------------------- /tests/naples/ark.cert.bad: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/tests/naples/ark.cert.bad -------------------------------------------------------------------------------- /tests/naples/ark.cert.sig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/tests/naples/ark.cert.sig -------------------------------------------------------------------------------- /tests/naples/ark.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | use ::sev::certs::sev::builtin::naples::*; 5 | 6 | #[test] 7 | fn decode() { 8 | let bad = ca::Certificate::decode(&mut &ARK_BAD[..], ()).unwrap(); 9 | let ark = ca::Certificate::decode(&mut &ARK[..], ()).unwrap(); 10 | assert_eq!(ark, bad); 11 | } 12 | 13 | #[test] 14 | fn encode() { 15 | let ark = ca::Certificate::decode(&mut &ARK_BAD[..], ()).unwrap(); 16 | 17 | let mut output = Vec::new(); 18 | ark.encode(&mut output, ()).unwrap(); 19 | assert_eq!(ARK.len(), output.len()); 20 | assert_eq!(ARK.to_vec(), output); 21 | 22 | let ark = ca::Certificate::decode(&mut &ARK[..], ()).unwrap(); 23 | 24 | let mut output = Vec::new(); 25 | ark.encode(&mut output, ()).unwrap(); 26 | assert_eq!(ARK.len(), output.len()); 27 | assert_eq!(ARK.to_vec(), output); 28 | } 29 | 30 | #[cfg(feature = "openssl")] 31 | #[test] 32 | fn verify() { 33 | let ark = ca::Certificate::decode(&mut &ARK_BAD[..], ()).unwrap(); 34 | (&ark, &ark).verify().unwrap(); 35 | 36 | let ark = ca::Certificate::decode(&mut &ARK[..], ()).unwrap(); 37 | (&ark, &ark).verify().unwrap(); 38 | } 39 | -------------------------------------------------------------------------------- /tests/naples/ask.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | use ::sev::certs::sev::builtin::naples::*; 5 | 6 | #[test] 7 | fn decode() { 8 | ca::Certificate::decode(&mut &ASK[..], ()).unwrap(); 9 | } 10 | 11 | #[test] 12 | fn encode() { 13 | let ask = ca::Certificate::decode(&mut &ASK[..], ()).unwrap(); 14 | 15 | let mut output = Vec::new(); 16 | ask.encode(&mut output, ()).unwrap(); 17 | assert_eq!(ASK.len(), output.len()); 18 | assert_eq!(ASK.to_vec(), output); 19 | } 20 | 21 | #[cfg(feature = "openssl")] 22 | #[test] 23 | fn verify() { 24 | let ark = ca::Certificate::decode(ARK, ()).unwrap(); 25 | let ask = ca::Certificate::decode(ASK, ()).unwrap(); 26 | 27 | (&ark, &ask).verify().unwrap(); 28 | assert!((&ask, &ark).verify().is_err()); 29 | } 30 | -------------------------------------------------------------------------------- /tests/naples/cek.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/tests/naples/cek.cert -------------------------------------------------------------------------------- /tests/naples/cek.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | 5 | #[test] 6 | fn decode() { 7 | sev::Certificate::decode(&mut &CEK[..], ()).unwrap(); 8 | } 9 | 10 | #[test] 11 | fn encode() { 12 | let cek = sev::Certificate::decode(&mut &CEK[..], ()).unwrap(); 13 | 14 | let mut output = Vec::new(); 15 | cek.encode(&mut output, ()).unwrap(); 16 | assert_eq!(CEK.len(), output.len()); 17 | assert_eq!(CEK.to_vec(), output); 18 | } 19 | 20 | #[cfg(feature = "openssl")] 21 | #[test] 22 | fn verify() { 23 | use ::sev::certs::sev::builtin::naples::ASK; 24 | 25 | let ask = ca::Certificate::decode(ASK, ()).unwrap(); 26 | let cek = sev::Certificate::decode(CEK, ()).unwrap(); 27 | 28 | (&ask, &cek).verify().unwrap(); 29 | //assert!((&cek, &ask).verify().is_err()); 30 | } 31 | -------------------------------------------------------------------------------- /tests/naples/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | mod ark; 4 | mod ask; 5 | mod cek; 6 | mod oca; 7 | mod pdh; 8 | mod pek; 9 | 10 | const ARK_BAD: &[u8] = include_bytes!("ark.cert.bad"); 11 | 12 | const OCA: &[u8] = include_bytes!("oca.cert"); 13 | pub const CEK: &[u8] = include_bytes!("cek.cert"); 14 | const PEK: &[u8] = include_bytes!("pek.cert"); 15 | const PDH: &[u8] = include_bytes!("pdh.cert"); 16 | 17 | use ::sev::certs::sev::*; 18 | 19 | #[allow(unused_imports)] 20 | use codicon::*; 21 | -------------------------------------------------------------------------------- /tests/naples/oca.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/tests/naples/oca.cert -------------------------------------------------------------------------------- /tests/naples/oca.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | 5 | #[test] 6 | fn decode() { 7 | sev::Certificate::decode(&mut &OCA[..], ()).unwrap(); 8 | } 9 | 10 | #[test] 11 | fn encode() { 12 | let oca = sev::Certificate::decode(&mut &OCA[..], ()).unwrap(); 13 | 14 | let mut output = Vec::new(); 15 | oca.encode(&mut output, ()).unwrap(); 16 | assert_eq!(OCA.len(), output.len()); 17 | assert_eq!(OCA.to_vec(), output); 18 | } 19 | 20 | #[cfg(feature = "openssl")] 21 | #[test] 22 | fn verify() { 23 | let oca = sev::Certificate::decode(OCA, ()).unwrap(); 24 | (&oca, &oca).verify().unwrap(); 25 | } 26 | 27 | #[cfg(feature = "openssl")] 28 | #[test] 29 | fn create() { 30 | let mut pdh = sev::Certificate::decode(&mut &PDH[..], ()).unwrap(); 31 | let (mut oca, key) = sev::Certificate::generate(sev::Usage::OCA).unwrap(); 32 | 33 | assert!((&pdh, &pdh).verify().is_err()); 34 | assert!((&oca, &pdh).verify().is_err()); 35 | assert!((&oca, &oca).verify().is_err()); 36 | 37 | key.sign(&mut oca).unwrap(); 38 | 39 | assert!((&pdh, &pdh).verify().is_err()); 40 | assert!((&oca, &pdh).verify().is_err()); 41 | (&oca, &oca).verify().unwrap(); 42 | 43 | key.sign(&mut pdh).unwrap(); 44 | (&oca, &pdh).verify().unwrap(); 45 | } 46 | -------------------------------------------------------------------------------- /tests/naples/pdh.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/tests/naples/pdh.cert -------------------------------------------------------------------------------- /tests/naples/pdh.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | 5 | #[test] 6 | fn decode() { 7 | sev::Certificate::decode(&mut &PDH[..], ()).unwrap(); 8 | } 9 | 10 | #[test] 11 | fn encode() { 12 | let pdh = sev::Certificate::decode(&mut &PDH[..], ()).unwrap(); 13 | 14 | let mut output = Vec::new(); 15 | pdh.encode(&mut output, ()).unwrap(); 16 | assert_eq!(PDH.len(), output.len()); 17 | assert_eq!(PDH.to_vec(), output); 18 | } 19 | 20 | #[cfg(feature = "openssl")] 21 | #[test] 22 | fn verify() { 23 | let pek = sev::Certificate::decode(PEK, ()).unwrap(); 24 | let pdh = sev::Certificate::decode(PDH, ()).unwrap(); 25 | 26 | (&pek, &pdh).verify().unwrap(); 27 | assert!((&pdh, &pek).verify().is_err()); 28 | } 29 | -------------------------------------------------------------------------------- /tests/naples/pek.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/tests/naples/pek.cert -------------------------------------------------------------------------------- /tests/naples/pek.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | 5 | #[test] 6 | fn decode() { 7 | sev::Certificate::decode(&mut &PEK[..], ()).unwrap(); 8 | } 9 | 10 | #[test] 11 | fn encode() { 12 | let pek = sev::Certificate::decode(&mut &PEK[..], ()).unwrap(); 13 | 14 | let mut output = Vec::new(); 15 | pek.encode(&mut output, ()).unwrap(); 16 | assert_eq!(PEK.len(), output.len()); 17 | assert_eq!(PEK.to_vec(), output); 18 | } 19 | 20 | #[cfg(feature = "openssl")] 21 | #[test] 22 | fn verify() { 23 | let cek = sev::Certificate::decode(CEK, ()).unwrap(); 24 | let oca = sev::Certificate::decode(OCA, ()).unwrap(); 25 | let pek = sev::Certificate::decode(PEK, ()).unwrap(); 26 | 27 | (&cek, &pek).verify().unwrap(); 28 | assert!((&pek, &cek).verify().is_err()); 29 | 30 | (&oca, &pek).verify().unwrap(); 31 | assert!((&pek, &oca).verify().is_err()); 32 | } 33 | -------------------------------------------------------------------------------- /tests/rome/ark.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | use ::sev::certs::sev::builtin::rome::*; 5 | 6 | #[test] 7 | fn decode() { 8 | ca::Certificate::decode(&mut &ARK[..], ()).unwrap(); 9 | } 10 | 11 | #[test] 12 | fn encode() { 13 | let ark = ca::Certificate::decode(&mut &ARK[..], ()).unwrap(); 14 | 15 | let mut output = Vec::new(); 16 | ark.encode(&mut output, ()).unwrap(); 17 | assert_eq!(ARK.len(), output.len()); 18 | assert_eq!(ARK.to_vec(), output); 19 | 20 | let ark = ca::Certificate::decode(&mut &ARK[..], ()).unwrap(); 21 | 22 | let mut output = Vec::new(); 23 | ark.encode(&mut output, ()).unwrap(); 24 | assert_eq!(ARK.len(), output.len()); 25 | assert_eq!(ARK.to_vec(), output); 26 | } 27 | 28 | #[cfg(feature = "openssl")] 29 | #[test] 30 | fn verify() { 31 | let ark = ca::Certificate::decode(&mut &ARK[..], ()).unwrap(); 32 | (&ark, &ark).verify().unwrap(); 33 | } 34 | -------------------------------------------------------------------------------- /tests/rome/ask.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | use ::sev::certs::sev::builtin::rome::*; 5 | 6 | #[test] 7 | fn decode() { 8 | ca::Certificate::decode(&mut &ASK[..], ()).unwrap(); 9 | } 10 | 11 | #[test] 12 | fn encode() { 13 | let ask = ca::Certificate::decode(&mut &ASK[..], ()).unwrap(); 14 | 15 | let mut output = Vec::new(); 16 | ask.encode(&mut output, ()).unwrap(); 17 | assert_eq!(ASK.len(), output.len()); 18 | assert_eq!(ASK.to_vec(), output); 19 | } 20 | 21 | #[cfg(feature = "openssl")] 22 | #[test] 23 | fn verify() { 24 | let ark = ca::Certificate::decode(ARK, ()).unwrap(); 25 | let ask = ca::Certificate::decode(ASK, ()).unwrap(); 26 | 27 | (&ark, &ask).verify().unwrap(); 28 | assert!((&ask, &ark).verify().is_err()); 29 | } 30 | -------------------------------------------------------------------------------- /tests/rome/cek.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/tests/rome/cek.cert -------------------------------------------------------------------------------- /tests/rome/cek.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | 5 | #[test] 6 | fn decode() { 7 | sev::Certificate::decode(&mut &CEK[..], ()).unwrap(); 8 | } 9 | 10 | #[test] 11 | fn encode() { 12 | let cek = sev::Certificate::decode(&mut &CEK[..], ()).unwrap(); 13 | 14 | let mut output = Vec::new(); 15 | cek.encode(&mut output, ()).unwrap(); 16 | assert_eq!(CEK.len(), output.len()); 17 | assert_eq!(CEK.to_vec(), output); 18 | } 19 | 20 | #[cfg(feature = "openssl")] 21 | #[test] 22 | fn verify() { 23 | use ::sev::certs::sev::builtin::rome::ASK; 24 | 25 | let ask = ca::Certificate::decode(ASK, ()).unwrap(); 26 | let cek = sev::Certificate::decode(CEK, ()).unwrap(); 27 | 28 | (&ask, &cek).verify().unwrap(); 29 | } 30 | -------------------------------------------------------------------------------- /tests/rome/mod.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | mod ark; 4 | mod ask; 5 | mod cek; 6 | mod oca; 7 | mod pdh; 8 | mod pek; 9 | 10 | const OCA: &[u8] = include_bytes!("oca.cert"); 11 | const CEK: &[u8] = include_bytes!("cek.cert"); 12 | const PEK: &[u8] = include_bytes!("pek.cert"); 13 | const PDH: &[u8] = include_bytes!("pdh.cert"); 14 | 15 | use ::sev::certs::sev::*; 16 | use codicon::{Decoder, Encoder}; 17 | -------------------------------------------------------------------------------- /tests/rome/oca.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/tests/rome/oca.cert -------------------------------------------------------------------------------- /tests/rome/oca.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | 5 | #[test] 6 | fn decode() { 7 | sev::Certificate::decode(&mut &OCA[..], ()).unwrap(); 8 | } 9 | 10 | #[test] 11 | fn encode() { 12 | let oca = sev::Certificate::decode(&mut &OCA[..], ()).unwrap(); 13 | 14 | let mut output = Vec::new(); 15 | oca.encode(&mut output, ()).unwrap(); 16 | assert_eq!(OCA.len(), output.len()); 17 | assert_eq!(OCA.to_vec(), output); 18 | } 19 | 20 | #[cfg(feature = "openssl")] 21 | #[test] 22 | fn verify() { 23 | let oca = sev::Certificate::decode(OCA, ()).unwrap(); 24 | (&oca, &oca).verify().unwrap(); 25 | } 26 | 27 | #[cfg(feature = "openssl")] 28 | #[test] 29 | fn create() { 30 | let mut pdh = sev::Certificate::decode(&mut &PDH[..], ()).unwrap(); 31 | let (mut oca, key) = sev::Certificate::generate(sev::Usage::OCA).unwrap(); 32 | 33 | assert!((&pdh, &pdh).verify().is_err()); 34 | assert!((&oca, &pdh).verify().is_err()); 35 | assert!((&oca, &oca).verify().is_err()); 36 | 37 | key.sign(&mut oca).unwrap(); 38 | 39 | assert!((&pdh, &pdh).verify().is_err()); 40 | assert!((&oca, &pdh).verify().is_err()); 41 | (&oca, &oca).verify().unwrap(); 42 | 43 | key.sign(&mut pdh).unwrap(); 44 | (&oca, &pdh).verify().unwrap(); 45 | } 46 | -------------------------------------------------------------------------------- /tests/rome/pdh.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/tests/rome/pdh.cert -------------------------------------------------------------------------------- /tests/rome/pdh.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | 5 | #[test] 6 | fn decode() { 7 | sev::Certificate::decode(&mut &PDH[..], ()).unwrap(); 8 | } 9 | 10 | #[test] 11 | fn encode() { 12 | let pdh = sev::Certificate::decode(&mut &PDH[..], ()).unwrap(); 13 | 14 | let mut output = Vec::new(); 15 | pdh.encode(&mut output, ()).unwrap(); 16 | assert_eq!(PDH.len(), output.len()); 17 | assert_eq!(PDH.to_vec(), output); 18 | } 19 | 20 | #[cfg(feature = "openssl")] 21 | #[test] 22 | fn verify() { 23 | let pek = sev::Certificate::decode(PEK, ()).unwrap(); 24 | let pdh = sev::Certificate::decode(PDH, ()).unwrap(); 25 | 26 | (&pek, &pdh).verify().unwrap(); 27 | assert!((&pdh, &pek).verify().is_err()); 28 | } 29 | -------------------------------------------------------------------------------- /tests/rome/pek.cert: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/virtee/sev/340c5c36f8753c44d198fe990293a5b63ab4a2a0/tests/rome/pek.cert -------------------------------------------------------------------------------- /tests/rome/pek.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | use super::*; 4 | 5 | #[test] 6 | fn decode() { 7 | sev::Certificate::decode(&mut &PEK[..], ()).unwrap(); 8 | } 9 | 10 | #[test] 11 | fn encode() { 12 | let pek = sev::Certificate::decode(&mut &PEK[..], ()).unwrap(); 13 | 14 | let mut output = Vec::new(); 15 | pek.encode(&mut output, ()).unwrap(); 16 | assert_eq!(PEK.len(), output.len()); 17 | assert_eq!(PEK.to_vec(), output); 18 | } 19 | 20 | #[cfg(feature = "openssl")] 21 | #[test] 22 | fn verify() { 23 | let cek = sev::Certificate::decode(CEK, ()).unwrap(); 24 | let oca = sev::Certificate::decode(OCA, ()).unwrap(); 25 | let pek = sev::Certificate::decode(PEK, ()).unwrap(); 26 | 27 | (&cek, &pek).verify().unwrap(); 28 | assert!((&pek, &cek).verify().is_err()); 29 | 30 | (&oca, &pek).verify().unwrap(); 31 | assert!((&pek, &oca).verify().is_err()); 32 | } 33 | -------------------------------------------------------------------------------- /tests/session.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #![cfg(feature = "openssl")] 4 | 5 | #[cfg(all(target_os = "linux", feature = "sev"))] 6 | mod initialized { 7 | use ::sev::{certs::sev::builtin::naples::*, certs::sev::*, launch, session::Session}; 8 | use codicon::Decoder; 9 | use std::convert::*; 10 | 11 | #[test] 12 | fn create() { 13 | Session::try_from(launch::sev::Policy::default()).unwrap(); 14 | } 15 | 16 | #[test] 17 | fn start() { 18 | const CEK: &[u8] = include_bytes!("naples/cek.cert"); 19 | const OCA: &[u8] = include_bytes!("naples/oca.cert"); 20 | const PEK: &[u8] = include_bytes!("naples/pek.cert"); 21 | const PDH: &[u8] = include_bytes!("naples/pdh.cert"); 22 | 23 | let session = Session::try_from(launch::sev::Policy::default()).unwrap(); 24 | session 25 | .start(Chain { 26 | ca: ca::Chain { 27 | ark: ca::Certificate::decode(&mut &ARK[..], ()).unwrap(), 28 | ask: ca::Certificate::decode(&mut &ASK[..], ()).unwrap(), 29 | }, 30 | sev: sev::Chain { 31 | cek: sev::Certificate::decode(&mut &CEK[..], ()).unwrap(), 32 | oca: sev::Certificate::decode(&mut &OCA[..], ()).unwrap(), 33 | pek: sev::Certificate::decode(&mut &PEK[..], ()).unwrap(), 34 | pdh: sev::Certificate::decode(&mut &PDH[..], ()).unwrap(), 35 | }, 36 | }) 37 | .unwrap(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/sev_launch.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #![cfg(all( 4 | feature = "openssl", 5 | target_os = "linux", 6 | feature = "sev", 7 | feature = "dangerous_hw_tests" 8 | ))] 9 | 10 | use kvm_bindings::kvm_userspace_memory_region; 11 | use kvm_ioctls::{Kvm, VcpuExit}; 12 | use serial_test::serial; 13 | use sev::certs::sev::sev::Usage; 14 | use sev::certs::sev::{sev::Certificate, Signer}; 15 | use sev::{cached_chain, firmware::host::Firmware, launch::sev::*, session::Session}; 16 | use std::slice::from_raw_parts; 17 | use std::{convert::TryFrom, os::unix::io::AsRawFd}; 18 | 19 | // Has to be a multiple of 16 20 | const CODE: &[u8; 16] = &[ 21 | 0xf4; 16 // hlt 22 | ]; 23 | 24 | #[cfg_attr(not(host), ignore)] 25 | #[test] 26 | #[serial] 27 | fn sev_launch_test() { 28 | // KVM SEV type 29 | const KVM_X86_SEV_VM: u64 = 2; 30 | 31 | let mut sev = Firmware::open().unwrap(); 32 | let build = sev.platform_status().unwrap().build; 33 | 34 | // Generating OCA cert and private key 35 | let (mut oca, prv) = Certificate::generate(Usage::OCA).expect("Generating OCA key pair"); 36 | prv.sign(&mut oca).expect("OCA key signing"); 37 | 38 | // Provisioning the PEK with the generated OCA key pair 39 | let mut pek = sev.pek_csr().expect("Cross signing request"); 40 | prv.sign(&mut pek).expect("Sign PEK with OCA private key"); 41 | sev.pek_cert_import(&pek, &oca) 42 | .expect("Import the newly-signed PEK"); 43 | 44 | // Export the full chain to launch SEV guest 45 | let chain = cached_chain::get_chain(); 46 | 47 | let policy = Policy::default(); 48 | let session = Session::try_from(policy).unwrap(); 49 | let start = session.start(chain).unwrap(); 50 | 51 | let kvm = Kvm::new().unwrap(); 52 | 53 | // Create VMft with SEV type 54 | let vm = kvm.create_vm_with_type(KVM_X86_SEV_VM).unwrap(); 55 | 56 | // Allocate a 1kB page of memory for the address space of the VM. 57 | const MEM_SIZE: usize = 0x1000; 58 | let address_space = unsafe { libc::mmap(0 as _, MEM_SIZE, 3, 34, -1, 0) }; 59 | 60 | if address_space == libc::MAP_FAILED { 61 | panic!("mmap() failed"); 62 | } 63 | 64 | let address_space: &[u8] = unsafe { from_raw_parts(address_space as *mut u8, MEM_SIZE) }; 65 | 66 | let mem_region = kvm_userspace_memory_region { 67 | slot: 0, 68 | guest_phys_addr: 0, 69 | memory_size: MEM_SIZE as _, 70 | userspace_addr: address_space.as_ptr() as _, 71 | flags: 0, 72 | }; 73 | 74 | unsafe { 75 | vm.set_user_memory_region(mem_region).unwrap(); 76 | } 77 | 78 | let mut session = session.measure().unwrap(); 79 | session.update_data(address_space.as_ref()).unwrap(); 80 | 81 | let (mut launcher, measurement) = { 82 | let launcher = Launcher::new(vm.as_raw_fd(), sev.as_raw_fd()).unwrap(); 83 | let mut launcher = launcher.start(start).unwrap(); 84 | launcher.update_data(address_space.as_ref()).unwrap(); 85 | let launcher = launcher.measure().unwrap(); 86 | let measurement = launcher.measurement(); 87 | (launcher, measurement) 88 | }; 89 | 90 | let session = session.verify(build, measurement).unwrap(); 91 | let secret = session.secret(HeaderFlags::default(), CODE).unwrap(); 92 | 93 | launcher 94 | .inject(&secret, address_space.as_ptr() as usize) 95 | .unwrap(); 96 | 97 | let _handle = launcher.finish().unwrap(); 98 | 99 | let mut vcpu = vm.create_vcpu(0).unwrap(); 100 | let mut sregs = vcpu.get_sregs().unwrap(); 101 | sregs.cs.base = 0; 102 | sregs.cs.selector = 0; 103 | vcpu.set_sregs(&sregs).unwrap(); 104 | 105 | let mut regs = vcpu.get_regs().unwrap(); 106 | regs.rip = std::ptr::null::() as u64; 107 | regs.rflags = 2; 108 | vcpu.set_regs(®s).unwrap(); 109 | 110 | match vcpu.run().unwrap() { 111 | VcpuExit::Hlt => (), 112 | exit_reason => panic!("unexpected exit reason: {:?}", exit_reason), 113 | } 114 | 115 | drop(vcpu); 116 | drop(vm); 117 | 118 | sev.platform_reset().unwrap(); 119 | cached_chain::rm_cached_chain(); 120 | } 121 | -------------------------------------------------------------------------------- /tests/snp_launch.rs: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | #![cfg(all(feature = "snp", target_os = "linux"))] 4 | 5 | use kvm_bindings::{kvm_create_guest_memfd, kvm_userspace_memory_region2, KVM_MEM_GUEST_MEMFD}; 6 | use kvm_ioctls::{Kvm, VcpuExit}; 7 | use sev::firmware::{guest::GuestPolicy, host::Firmware}; 8 | use sev::launch::snp::*; 9 | use std::os::fd::RawFd; 10 | use std::slice::from_raw_parts_mut; 11 | 12 | // one page of `hlt 13 | const CODE: &[u8; 4096] = &[ 14 | 0xf4; 4096 // hlt 15 | ]; 16 | 17 | const KVM_X86_SNP_VM: u64 = 4; 18 | 19 | #[cfg_attr(not(host), ignore)] 20 | #[test] 21 | fn snp_launch_test() { 22 | let kvm_fd = Kvm::new().unwrap(); 23 | 24 | // Create VM-fd with SEV-SNP type 25 | let vm_fd = kvm_fd.create_vm_with_type(KVM_X86_SNP_VM).unwrap(); 26 | 27 | const MEM_ADDR: u64 = 0x1000; 28 | 29 | // Allocate a 1kB page of memory for the address space of the VM. 30 | let address_space = unsafe { libc::mmap(0 as _, CODE.len(), 3, 34, -1, 0) }; 31 | 32 | if address_space == libc::MAP_FAILED { 33 | panic!("mmap() failed"); 34 | } 35 | 36 | let address_space: &mut [u8] = 37 | unsafe { from_raw_parts_mut(address_space as *mut u8, CODE.len()) }; 38 | 39 | address_space[..CODE.len()].copy_from_slice(&CODE[..]); 40 | 41 | let userspace_addr = address_space as *const [u8] as *const u8 as u64; 42 | 43 | // Create KVM guest_memfd struct 44 | let gmem = kvm_create_guest_memfd { 45 | size: 0x1000, 46 | flags: 0, 47 | reserved: [0; 6], 48 | }; 49 | 50 | // Create KVM guest_memfd 51 | let fd: RawFd = vm_fd.create_guest_memfd(gmem).unwrap(); 52 | 53 | // Create memory region 54 | let mem_region = kvm_userspace_memory_region2 { 55 | slot: 0, 56 | flags: KVM_MEM_GUEST_MEMFD, 57 | guest_phys_addr: 0x1000_u64, 58 | memory_size: 0x1000_u64, 59 | userspace_addr, 60 | guest_memfd_offset: 0, 61 | guest_memfd: fd as u32, 62 | pad1: 0, 63 | pad2: [0; 14], 64 | }; 65 | 66 | unsafe { 67 | vm_fd.set_user_memory_region2(mem_region).unwrap(); 68 | }; 69 | 70 | let sev = Firmware::open().unwrap(); 71 | let launcher = Launcher::new(vm_fd, sev).unwrap(); 72 | 73 | let mut policy = GuestPolicy(0); 74 | policy.set_smt_allowed(true); 75 | let start = Start::new(policy, [0; 16]); 76 | 77 | let mut launcher = launcher.start(start).unwrap(); 78 | 79 | let update = Update::new( 80 | mem_region.guest_phys_addr >> 12, 81 | address_space, 82 | PageType::Normal, 83 | ); 84 | 85 | launcher 86 | .update_data(update, mem_region.guest_phys_addr, mem_region.memory_size) 87 | .unwrap(); 88 | 89 | let finish = Finish::new(None, None, [0u8; 32]); 90 | 91 | let mut vcpu_fd = launcher.as_mut().create_vcpu(0).unwrap(); 92 | 93 | let mut regs = vcpu_fd.get_regs().unwrap(); 94 | regs.rip = MEM_ADDR; 95 | regs.rflags = 2; 96 | vcpu_fd.set_regs(®s).unwrap(); 97 | 98 | let mut sregs = vcpu_fd.get_sregs().unwrap(); 99 | sregs.cs.base = 0; 100 | sregs.cs.selector = 0; 101 | vcpu_fd.set_sregs(&sregs).unwrap(); 102 | 103 | let (_vm_fd, _sev) = launcher.finish(finish).unwrap(); 104 | 105 | let ret = vcpu_fd.run(); 106 | 107 | assert!(matches!(ret, Ok(VcpuExit::Hlt))); 108 | } 109 | --------------------------------------------------------------------------------