Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions Documentation/docs/developer/ATTESTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,12 @@ launching the proxy. The supported backend attestation protocols include:

SVSM communicates with the attestation proxy using one of two transport methods:

- **vsock**: When the `vsock` feature is enabled, SVSM will first try to use vsock for communication with the host proxy
using port `1995`. If it fails, SVSM will try again using the serial port.
- **vsock** (default): SVSM uses vsock for communication with the host proxy on
port `1995`.

- **Serial port**: If vsock is not available, SVSM uses the COM3 serial port for communication with the attestation proxy.
- **Serial port** (testing only): When the `attest-serial` feature is enabled,
SVSM falls back to the COM3 serial port if the vsock connection fails. This
is intended for testing purposes only.

## Try for yourself

Expand All @@ -267,8 +269,8 @@ SEV-SNP machine with an SVSM-enabled kernel.
```shell
git clone https://github.com/coconut-svsm/svsm.git
# ... build OVMF, qemu, SVSM IGVM, etc...
FW_FILE=... make FEATURES=attest # serial transport
FW_FILE=... make FEATURES=attest,vsock,virtio-drivers # vsock transport (with serial fallback)
FW_FILE=... make FEATURES=attest,vsock,virtio-drivers # vsock transport (default)
FW_FILE=... make FEATURES=attest,vsock,attest-serial,virtio-drivers # vsock with serial fallback (testing)
```

2. Clone and run the `kbs-test` server used for testing. Supply the following
Expand Down Expand Up @@ -327,10 +329,11 @@ SEV-SNP machine with an SVSM-enabled kernel.

4. Run a guest with SVSM

SVSM will use vsock for communication if the feature is enabled, otherwise it
falls back to the COM3 serial port (see [Transport Methods](#transport-methods)).
The attestation proxy will need to be configured correctly to ensure proper communication
according to the transport used.
SVSM uses vsock for communication by default. If the `attest-serial` feature
is enabled, it falls back to the COM3 serial port when vsock fails (see
[Transport Methods](#transport-methods)). The attestation proxy will need to
be configured correctly to ensure proper communication according to the
transport used.

* **vsock**
```shell
Expand Down
3 changes: 2 additions & 1 deletion kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ zeroize = { workspace = true, features = ["alloc", "derive"] }
test.workspace = true

[features]
attest = ["dep:aes-kw", "dep:cocoon-tpm-crypto", "dep:cocoon-tpm-tpm2-interface", "dep:cocoon-tpm-utils-common", "dep:concat-kdf", "dep:kbs-types", "dep:libaproxy", "dep:serde", "dep:serde_json"]
attest = ["vsock", "dep:aes-kw", "dep:cocoon-tpm-crypto", "dep:cocoon-tpm-tpm2-interface", "dep:cocoon-tpm-utils-common", "dep:concat-kdf", "dep:kbs-types", "dep:libaproxy", "dep:serde", "dep:serde_json"]
attest-serial = ["attest"]

default = []
enable-gdb = ["dep:gdbstub", "dep:gdbstub_arch"]
Expand Down
60 changes: 31 additions & 29 deletions kernel/src/attest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ use crate::{
crypto::SecretSlice,
error::SvsmError,
greq::{pld_report::*, services::get_regular_report},
io::{DEFAULT_IO_DRIVER, Read, Write},
serial::SerialPort,
io::{Read, Write},
utils::vec::{try_to_vec, vec_sized},
};
#[cfg(feature = "vsock")]
#[cfg(feature = "attest-serial")]
use crate::{io::DEFAULT_IO_DRIVER, serial::SerialPort};
use crate::{vsock::VMADDR_CID_HOST, vsock::stream::VsockStream};
use aes_gcm::{AeadInPlace, Aes256Gcm, KeyInit, Nonce, aead::generic_array::GenericArray};
use aes_kw::{KeyInit as _, KwAes256};
Expand All @@ -36,77 +36,79 @@ use serde::Serialize;
use sha2::{Digest, Sha512};
use zerocopy::{FromBytes, IntoBytes};

#[cfg(feature = "attest-serial")]
// TODO: Make the IO port configurable/discoverable or drop the support entirely.
const ATTEST_DEFAULT_SERIAL_IO_ADDR: u16 = 0x3e8; // COM3

enum Transport<'a> {
#[cfg(feature = "vsock")]
enum Transport {
Vsock(VsockStream),
Serial(SerialPort<'a>),
#[cfg(feature = "attest-serial")]
Serial(SerialPort<'static>),
}

impl Read for Transport<'_> {
impl Read for Transport {
type Err = SvsmError;

fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Err> {
match self {
#[cfg(feature = "vsock")]
Transport::Vsock(vsock) => vsock.read(buf),
#[cfg(feature = "attest-serial")]
Transport::Serial(serial) => serial.read(buf),
}
}
}

impl Write for Transport<'_> {
impl Write for Transport {
type Err = SvsmError;

fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Err> {
match self {
#[cfg(feature = "vsock")]
Transport::Vsock(vsock) => vsock.write(buf),
#[cfg(feature = "attest-serial")]
Transport::Serial(serial) => serial.write(buf),
}
}
}

impl Transport<'_> {
#[cfg(feature = "vsock")]
fn new() -> Self {
impl Transport {
fn new() -> Result<Self, SvsmError> {
match VsockStream::connect(ATTEST_DEFAULT_VSOCK_PORT, VMADDR_CID_HOST) {
Ok(value) => Transport::Vsock(value),
Ok(value) => Ok(Transport::Vsock(value)),
Err(e) => {
log::warn!(
"Failed to connect to attestation proxy on vsock port \
{ATTEST_DEFAULT_VSOCK_PORT}: {e:?}. \
Falling back to serial port transport.",
{ATTEST_DEFAULT_VSOCK_PORT}: {e:?}."
);
create_serial_transport()

#[cfg(feature = "attest-serial")]
{
log::warn!("Falling back to serial port transport.");
create_serial_transport()
}
#[cfg(not(feature = "attest-serial"))]
Err(e)
}
}
}

#[cfg(not(feature = "vsock"))]
fn new() -> Self {
create_serial_transport()
}
}

fn create_serial_transport<'a>() -> Transport<'a> {
#[cfg(feature = "attest-serial")]
fn create_serial_transport() -> Result<Transport, SvsmError> {
let sp = SerialPort::new(&DEFAULT_IO_DRIVER, ATTEST_DEFAULT_SERIAL_IO_ADDR);
sp.init();
Transport::Serial(sp)
Ok(Transport::Serial(sp))
}

/// The attestation driver that communicates with the proxy via some communication channel (serial
/// port, virtio-vsock, etc...).
#[allow(missing_debug_implementations)]
pub struct AttestationDriver<'a> {
transport: Transport<'a>,
pub struct AttestationDriver {
transport: Transport,
tee: Tee,
ecc: EccKey,
}

impl TryFrom<Tee> for AttestationDriver<'_> {
impl TryFrom<Tee> for AttestationDriver {
type Error = SvsmError;

fn try_from(tee: Tee) -> Result<Self, Self::Error> {
Expand All @@ -118,7 +120,7 @@ impl TryFrom<Tee> for AttestationDriver<'_> {
let curve = Curve::new(TpmEccCurve::NistP521).map_err(AttestationError::Crypto)?;
let ecc = sc_key_generate(&curve).map_err(AttestationError::Crypto)?;

let transport = Transport::new();
let transport = Transport::new()?;
Ok(Self {
transport,
tee,
Expand All @@ -127,7 +129,7 @@ impl TryFrom<Tee> for AttestationDriver<'_> {
}
}

impl AttestationDriver<'_> {
impl AttestationDriver {
/// Attest SVSM's launch state by communicating with the attestation proxy.
pub fn attest(&mut self) -> Result<SecretSlice, SvsmError> {
let negotiation = self.negotiation()?;
Expand Down