SPDM-Utils is an open source Linux application designed to support, test and develop SPDM requesters and responders. SPDM-Utils is written in Rust and uses libspdm as the backend.
It can be used as a requester CLI to interface with SPDM devices. It includes support for the PCIe Data Object Exchange (DOE) Capability and MCTP transport layers.
SPDM-Utils can also be used as a responder. It can be an embedded MCTP responder running on Tock. It can also be used as a responder running on Linux, and exposed to QEMU or other applications via sockets.
SPDM-Utils can use Unix sockets as well. So you can test it all locally as a requester and responder.
Copyright (c) 2022 Western Digital
SPDM-Utils source code is dual licensed under the Apache-2.0 license and MIT license. A copy of these licenses can be found either in the LICENSE-APACHE or LICENSE-MIT files. Versions are also available at http://www.apache.org/licenses/LICENSE-2.0 and http://opensource.org/licenses/MIT.
See LICENSE-APACHE, LICENSE-MIT, and COPYRIGHT for details.
First you need to install Rust, instructions for that are available at: https://rustup.rs/
You will also need a few host dependencies
Note: dnf
commands are for Fedora, and apt
is used for Debian/Ubuntu based
distributions.
$ sudo dnf install cmake clang-libs clang-devel pciutils-devel openssl openssl-devel python3-devel systemd-devel
or
$ sudo apt install cmake clang libclang-dev pciutils libpci-dev openssl libssl-dev libsystemd-dev python3-dev
spdm-utils
uses the cbor-diag ruby gem for
manifest encoding and decoding. Similar to the implementation of this CBOR parsing
online tool.
You will first need to have gem
installed, this is a the package manager for ruby.
For example, for Fedora you can install it with:
$ sudo dnf install gem
or
$ sudo apt install gem ruby-dev
After which, you can install cbor-diag
$ gem install cbor-diag
or
$ gem install -i <INSTALL_PATH> cbor-diag
The default binary path should (maybe different across distros) be,
$HOME/bin/
, which you may need to add to your PATH
. You can test that the
scripts are usable with
$ which cbor2diag.rb
home/<user>/bin/cbor2diag.rb
When building spdm-utils
it will generate a manifest.out.cbor
which contains
the serialised cbor manifest, and also a manifest.pretty
which is the pretty format
of the manifest (user friendly).
First clone spdm-utils
and it's submodules
$ git clone --recurse-submodules -j8 https://github.com/westerndigitalcorporation/spdm-utils.git
Initialise all sub-modules
cd third-party/
git submodule init; git submodule update --recursive
To build libspdm in the third-party directory
cd libspdm/
mkdir build; cd build
cmake -DARCH=x64 -DTOOLCHAIN=GCC -DTARGET=Debug -DCRYPTO=openssl -DENABLE_BINARY_BUILD=1 -DCOMPILED_LIBCRYPTO_PATH=/usr/lib/ -DCOMPILED_LIBSSL_PATH=/usr/lib/ -DDISABLE_TESTS=1 -DCMAKE_C_FLAGS="-DLIBSPDM_ENABLE_CAPABILITY_EVENT_CAP=0 -DLIBSPDM_ENABLE_CAPABILITY_MEL_CAP=0 -DLIBSPDM_HAL_PASS_SPDM_CONTEXT=1 -DLIBSPDM_ENABLE_CAPABILITY_GET_KEY_PAIR_INFO_CAP=0 -DLIBSPDM_ENABLE_CAPABILITY_SET_KEY_PAIR_INFO_CAP=0" ..
make -j8
Note that we build libspdm
with chunking enabled. Chunking allows us to keep the maximum data transferred
in a single burst down by chunking the SPDM message data into frames of digestible size(s).
For example, usb_i2c
communication with the tock-responder
requires it, so we enable it by default.
Then you can build SPDM-Utils with
cargo build --bin spdm_utils
This is currently a work in progress
cargo build --lib --features=no_std
SPDM-Utils supports logging. The following log levels are supported:
- trace
- debug
- info
- warn
- error
By default SPDM-Utils will build with trace
log level, meaning that the log
outputs are very verbose containing all logs. To change this, set the LOG_LEVEL
environment variable to the desired level when building. The logger also takes a
LOG_STYLE
parameter which may be used to set the character style. This
defaults to always
but can be changed to one of (see
here for more):
- always
- never
- auto
LOG_LEVEL=info LOG_STYLE=never cargo build
All changes should go through the Cargo formatter and tests, which can be run with
cargo fmt; cargo clippy; cargo test
Setup and build SPDM-Responder-Validator
in the third-party directory
cd third-party/
git submodule init; git submodule update --recursive
cd SPDM-Responder-Validator/
rm -rf libspdm/
# This assumes that `third-party/libspdm` is configured correctly as above
# The symlink here ensures that the tests are build against the same version of libspdm
ln -s ../libspdm/ libspdm
mkdir build; cd build
cmake -DARCH=x64 -DTOOLCHAIN=GCC -DTARGET=Debug -DCRYPTO=openssl ..
make -j8
We can now build SPDM-Utils with
cargo build --features libspdm_tests
You can run SPDM-Utils completely on the host using unix sockets. In this case you can run the server side with
cargo run -- --socket-server request get-digests
and the client side with
./target/debug/spdm_utils --socket-client response
Note that the server must be run first. You can also swap the server/client specification between the request or response side as well.
You can also run the libspdm tests by running tests on the socket server with:
cargo run -- --socket-server tests
spdm-utils
can issue a user defined list of SPDM request(s).
It is the responsibility of the user to ensure, the SPDM requests are ordered
in a specification compliant way. It is not checked
by spdm-utils
or libspdm
.
Usage is as follows, as demonstrated over the socket model. Ensure you have an SPDM responder server socket running in responder mode prior to issuing this command.
$./target/debug/spdm_utils --socket-client request get-version,get-capabilities,negotiate-algorithms
Request sub-commands can be specified as follows, refer to usage request --help
for available options.
$./target/debug/spdm_utils --socket-client request get-version,get-measurement[index=1]
spdm-utils
can send SPDM request(s) directly, without establishing a session.
This maybe useful for development, testing and CI. The --no-session
argument
shall be specified to indicate this. In this mode, it is the responsibility of
the user to ensure that the requests are ordered in an SPDM specification
compliant way. spdm-utils
or libspdm
do not check the request order, instead
directly issues them to the responder.
$ ./target/debug/spdm_utils --socket-client --no-session request get-version,get-capabilities,negotiate-algorithms,get-digests,get-certificate,challenge
This command with issue the requests listed in the order in which they are listed to the responder.
You can run SPDM-Utils on the host to interact with a real DOE device. To do that you can run the following example to get digest information
The pcie-vid
and pcie-devid
values can be found by using lspci
for the respective device. For example:
$ lspci -nnv
...
0c:00.1 Audio device [0403]: Advanced Micro Devices, Inc. [AMD/ATI] Navi 21/23 HDMI/DP Audio Controller [1002:ab28]
Subsystem: Advanced Micro Devices, Inc. [AMD/ATI] Navi 21/23 HDMI/DP Audio Controller [1002:ab28]
Flags: bus master, fast devsel, latency 0, IRQ 128, IOMMU group 28
Memory at fcd20000 (32-bit, non-prefetchable) [size=16K]
Capabilities: <access denied>
Kernel driver in use: snd_hda_intel
Kernel modules: snd_hda_intel
...
Where Vendor ID = 0x1002
and Device ID = 0xab28
. spdm-utils
can then be
invoked as below:
./target/debug/spdm_utils --pcie-vid <VendorID> --pcie-devid <DeviceID> --doe-pci-cfg request get-digests
From a host you can set the certificate of the device. As SPDM-Utils uses
the Alias cert model you can only set the root certificate to the device
certificate with the SET_CERTIFICATE
command (see section 117 on the
SPDM spec).
For example to set the certificate run:
spdm_utils --pcie-vid <VendorID> --pcie-devid <DeviceID> --doe-pci-cfg request --cert-path ./certs/alias/slot0/immutable.der set-certificate
You can additionally specify --cert-slot-id
to specify the target slot number, valid slot numbers range from
0-7.
A requester can get the Certificate Signing Request (CSR) from the device with a command similar to this:
spdm_utils --pcie-vid <VendorID> --pcie-devid <DeviceID> --doe-pci-cfg request get-csr
Which will save the file to csr_response.der
. You can then verify the CSR
with openssl
openssl req -text -noout -inform der -verify -in ./csr_response.der
Once you have a csr_response.der
from the responder, you first want to
convert it to a PEM format with
openssl req -inform der -in ./csr_response.der -out csr_response.req
You can now sign the CSR
openssl x509 -req -in csr_response.req -out csr_response.cert -CA ./certs/slot0/inter.der -sha384 -days 3650 -set_serial 2 -extensions device_ca -extfile ./certs/alias/openssl.cnf
Then convert the certificate back to DER
openssl asn1parse -in csr_response.cert -out csr_response.cert.der
Combine all of the immutable certs
cat ./certs/slot0/ca.cert.der ./certs/slot0/inter.cert.der ./csr_response.cert.der > set-cert.der
Now you can set the certificate of a slot
spdm_utils --pcie-vid <VendorID> --pcie-devid <DeviceID> --doe-pci-cfg request --cert-slot-id 1 --cert-path ./set-cert.der set-certificate
Then you request the certificate back
spdm_utils --pcie-vid <VendorID> --pcie-devid <DeviceID> --doe-pci-cfg request --cert-slot-id 1 get-certificate
If you are running the socket/client mode you will have to simulate a device reset and certificate re-gen. That can be done by running this
cd certs
./setup_certs.sh ../target/debug/spdm_utils
cd ../
SPDM-Utils supports binding to QEMU to implement an SPDM responder side to an emulated device in QEMU. SPDM support for QEMU is not upstream yet, however, this fork has the necessary changes required to emulated an NVMe device with SPDM support over DOE.
For example, this may be an emulated NVMe device in QEMU that binds to SPDM-Utils for the SPDM responder implementation.
With the current SPDM implementation in QEMU, the only transport layer supported is DOE. SPDM-Utils must be started before QEMU for this to work.
$ ./target/debug/spdm_utils --qemu-server response
[2023-08-29T06:21:47Z DEBUG SPDM-Utils] Logger initialisation [OK]
[2023-08-29T06:21:47Z DEBUG SPDM-Utils::qemu_server] Setting up a server on [port: 2323, ip: 127.0.0.1]
[2023-08-29T06:21:47Z INFO SPDM-Utils::qemu_server] Server started, waiting for qemu on port: 2323
Note: You can provide --qemu-port <QEMU_PORT>
to specify a port for the server
and also --spdm-transport-protocol <TRANSPORT>
to specify the transport layer.
This will start SPDM-Utils responder server on port 2323 (default). QEMU can now be started. Once QEMU starts, if the connection is successful, the following logs should show (ensure that INFO log level is enabled in SPDM-Utils).
[2023-08-29T06:22:01Z INFO SPDM-Utils::qemu_server] New connection: 127.0.0.1:40528
[2023-08-29T06:22:01Z INFO SPDM-Utils::responder] Running in a response loop
Now QEMU is ready to use SPDM-Utils as an SPDM responder for an emulated device.