spdm

module
v0.0.0-...-a186c25 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 9, 2026 License: MIT

README

spdm

Go Reference Go Report Card

A pure-Go implementation of the SPDM (Security Protocol and Data Model) protocol, as defined in DMTF DSP0274.

WARNING: This library has NOT been tested against real hardware. It has only been validated through unit tests, end-to-end loopback tests, and reference interoperability tests against the DMTF spdm-emu (libspdm) reference implementation running in Docker. Do not use in production environments that depend on hardware-verified correctness without performing your own hardware validation first.

Architecture

The codebase follows the three-layer architecture defined by the SPDM specification (DSP0274 Section 6):

graph TB
    subgraph "Application Layer"
        spdm["pkg/spdm<br/><i>High-level facade</i>"]
        req["pkg/requester<br/><i>Requester (initiator) logic</i>"]
        rsp["pkg/responder<br/><i>Responder logic</i>"]
    end

    subgraph "Protocol Layer"
        msgs["pkg/gen/msgs<br/><i>Typed message structs</i>"]
        raw["pkg/gen/raw<br/><i>Generated C-to-Go structs</i>"]
        algo["pkg/gen/algo<br/><i>Algorithm identifiers</i>"]
        caps["pkg/gen/caps<br/><i>Capability flags</i>"]
        codes["pkg/gen/codes<br/><i>Request/Response/Error codes</i>"]
        status["pkg/gen/status<br/><i>Status codes</i>"]
        session["pkg/session<br/><i>Key derivation, encrypt/decrypt</i>"]
        crypto["pkg/crypto<br/><i>Crypto abstraction layer</i>"]
    end

    subgraph "Transport Layer"
        transport["pkg/transport<br/><i>Transport interface</i>"]
        tcpt["pkg/transport/tcp<br/><i>SPDM-over-TCP (DSP0287)</i>"]
        mctp["pkg/transport/mctp<br/><i>SPDM-over-MCTP (DSP0239)</i>"]
        pcidoe["pkg/transport/pcidoe<br/><i>SPDM-over-PCI DOE</i>"]
        storage["pkg/transport/storage<br/><i>SPDM-over-Storage (DSP0286)</i>"]
    end

    spdm --> req
    spdm --> rsp
    req --> msgs
    req --> session
    req --> crypto
    req --> transport
    rsp --> msgs
    rsp --> session
    rsp --> crypto
    rsp --> transport
    msgs --> raw
    msgs --> codes
    msgs --> algo
    session --> crypto
    session --> algo
    tcpt --> transport
    mctp --> transport
    pcidoe --> transport
    storage --> transport
Package Relationships
  • pkg/spdm is the consumer-facing facade. It wraps pkg/requester and pkg/responder with simplified types. Provider interfaces (e.g. CertProvider, MeasurementProvider) are type aliases to pkg/responder interfaces, so users only import pkg/spdm.

  • pkg/requester and pkg/responder implement the SPDM protocol state machines. They use pkg/gen/msgs for message serialization, pkg/crypto for cryptographic operations, pkg/session for secure session management, and pkg/transport for wire communication.

  • pkg/gen/msgs contains typed Go structs for every SPDM message (VERSION, CAPABILITIES, ALGORITHMS, CERTIFICATE, CHALLENGE, KEY_EXCHANGE, etc.) with Marshal()/Unmarshal() methods. The MessageHeader struct embeds raw.SPDMMessageHeader.

  • pkg/gen/raw contains machine-generated Go translations of every typedef struct in the libspdm C header. These are raw field-for-field translations used as embedded types by pkg/gen/msgs.

  • pkg/gen/algo, pkg/gen/caps, pkg/gen/codes contain generated constants mapping SPDM algorithm identifiers, capability flags, and request/response/error codes from the C #define values.

  • pkg/crypto defines interfaces (HashProvider, Verifier, KeyAgreement, AEAD, PSKProvider) and a Suite struct that groups them. pkg/crypto/stdlib provides a concrete implementation using Go's standard library.

  • pkg/session handles SPDM secure session lifecycle: HKDF key derivation, handshake/data key generation, AEAD encrypt/decrypt, and sequence number management.

  • pkg/transport defines the Transport interface (SendMessage/ReceiveMessage/HeaderSize). Sub-packages provide wire-format implementations for TCP, MCTP, PCI-DOE, and Storage bindings.

Code Generation

Type definitions are generated from the official DMTF libspdm C headers, pinned as a git submodule at spec/libspdm/ (v3.8.2).

flowchart LR
    subgraph "Source (git submodule)"
        header["spec/libspdm/include/<br/>industry_standard/spdm.h"]
    end

    subgraph "Parser"
        cheader["internal/cheader<br/><i>C99 AST parser<br/>modernc.org/cc/v4</i>"]
    end

    subgraph "Generator"
        spdmgen["tools/spdmgen<br/><i>Go code generator</i>"]
    end

    subgraph "Generated Output"
        raw2["pkg/gen/raw/types.go<br/><i>Go struct translations</i>"]
        algo2["pkg/gen/algo/*.go<br/><i>Algorithm constants</i>"]
        caps2["pkg/gen/caps/*.go<br/><i>Capability flags</i>"]
        codes2["pkg/gen/codes/*.go<br/><i>Request/Response/Error codes</i>"]
    end

    header --> cheader --> spdmgen
    spdmgen --> raw2
    spdmgen --> algo2
    spdmgen --> caps2
    spdmgen --> codes2
How It Works
  1. internal/cheader uses modernc.org/cc/v4 (a pure-Go C99 compiler frontend) to parse the libspdm header into an AST. It extracts:

    • All #define constants with integer values (via cc.Translate() + macro evaluation)
    • All typedef struct definitions with field names, types, and array lengths
  2. tools/spdmgen consumes the parsed result and generates Go source files through mapping tables that translate C define name prefixes to Go types. For example, SPDM_ALGORITHMS_BASE_HASH_ALGO_TPM_ALG_SHA_256 becomes algo.HashSHA256.

  3. The generator produces four packages:

    • raw: Field-for-field Go struct translations of every C typedef struct
    • algo: Typed constants for hash, asymmetric, DHE, AEAD, and measurement algorithms
    • caps: Bitmask constants for requester and responder capabilities
    • codes: Request codes, response codes, and error codes with string representations

To regenerate after updating the libspdm submodule:

go generate ./pkg/gen/...

Usage Examples

Requester: Connect and Authenticate
package main

import (
    "context"
    "fmt"
    "log"
    "net"

    "github.com/xaionaro-go/spdm/pkg/crypto/stdlib"
    "github.com/xaionaro-go/spdm/pkg/gen/algo"
    "github.com/xaionaro-go/spdm/pkg/gen/caps"
    "github.com/xaionaro-go/spdm/pkg/spdm"
    "github.com/xaionaro-go/spdm/pkg/transport/tcp"
)

func main() {
    ctx := context.Background()

    // Connect to an SPDM responder over TCP.
    conn, err := net.Dial("tcp", "127.0.0.1:2323")
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    // Create an SPDM requester with desired algorithms.
    req := spdm.NewRequester(spdm.RequesterConfig{
        Versions:         []algo.Version{algo.Version12},
        Transport:        tcp.New(conn),
        Crypto:           *stdlib.NewSuite(nil, nil),
        Caps:             caps.ReqCertCap | caps.ReqChalCap,
        BaseAsymAlgo:     algo.AsymECDSAP256,
        BaseHashAlgo:     algo.HashSHA256,
        DHEGroups:        algo.DHESECP256R1,
        AEADSuites:       algo.AEADAES128GCM,
        DataTransferSize: 4096,
        MaxSPDMmsgSize:   65536,
    })

    // Negotiate version, capabilities, and algorithms.
    ci, err := req.InitConnection(ctx)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Connected: version=%s, hash=%s, asym=%s\n",
        ci.Version, ci.HashAlgo, ci.AsymAlgo)

    // Retrieve certificate digests and chain.
    if _, err := req.GetDigests(ctx); err != nil {
        log.Fatal(err)
    }
    cert, err := req.GetCertificate(ctx, 0)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Certificate chain: %d bytes\n", len(cert.Chain))

    // Challenge the responder (proves identity).
    result, err := req.Challenge(ctx, 0)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Challenge succeeded for slot %d\n", result.SlotID)
}
Requester: Establish a Secure Session
    // After InitConnection + GetDigests + GetCertificate...

    // Perform DHE key exchange to establish a secure session.
    sess, err := req.KeyExchange(ctx, spdm.KeyExchangeOpts{
        SlotID:   0,
        HashType: 0xFF,
    })
    if err != nil {
        log.Fatal(err)
    }

    // Send/receive data within the encrypted session.
    response, err := sess.SendReceive(ctx, []byte("hello from requester"))
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Received: %s\n", response)

    // Send a heartbeat.
    if err := sess.Heartbeat(ctx); err != nil {
        log.Fatal(err)
    }

    // Close the session.
    if err := sess.Close(ctx); err != nil {
        log.Fatal(err)
    }
Responder: Serve SPDM Requests
package main

import (
    "context"
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "log"
    "net"

    "github.com/xaionaro-go/spdm/pkg/crypto/stdlib"
    "github.com/xaionaro-go/spdm/pkg/gen/algo"
    "github.com/xaionaro-go/spdm/pkg/gen/caps"
    "github.com/xaionaro-go/spdm/pkg/spdm"
    "github.com/xaionaro-go/spdm/pkg/transport/tcp"
)

func main() {
    ctx := context.Background()

    // Generate an ephemeral device identity key.
    key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)

    // Build the SPDM certificate chain (see cmd/spdm-responder for full example).
    certChainBytes, certPool := buildCertChain(key) // your implementation

    ln, _ := net.Listen("tcp", "127.0.0.1:2323")
    defer ln.Close()

    for {
        conn, _ := ln.Accept()

        rsp := spdm.NewResponder(spdm.ResponderConfig{
            Versions:         []algo.Version{algo.Version12},
            Transport:        tcp.New(conn),
            Crypto:           *stdlib.NewSuite(key, certPool),
            Caps:             caps.RspCertCap | caps.RspChalCap | caps.RspMeasCapNoSig,
            BaseAsymAlgo:     algo.AsymECDSAP256,
            BaseHashAlgo:     algo.HashSHA256,
            DHEGroups:        algo.DHESECP256R1,
            AEADSuites:       algo.AEADAES128GCM,
            DataTransferSize: 4096,
            MaxSPDMmsgSize:   65536,
            CertProvider:     yourCertProvider,  // implements spdm.CertProvider
            MeasProvider:     yourMeasProvider,   // implements spdm.MeasurementProvider
        })

        // Serve blocks, handling all incoming SPDM requests.
        if err := rsp.Serve(ctx); err != nil {
            log.Printf("session ended: %v", err)
        }
        conn.Close()
    }
}
Implementing Provider Interfaces

The responder requires provider implementations for device-specific data:

// CertProvider supplies certificate chains and digests.
type CertProvider interface {
    CertChain(ctx context.Context, slotID uint8) ([]byte, error)
    DigestForSlot(ctx context.Context, slotID uint8) ([]byte, error)
}

// MeasurementProvider supplies device measurements.
type MeasurementProvider interface {
    Collect(ctx context.Context, index uint8) ([]msgs.MeasurementBlock, error)
    SummaryHash(ctx context.Context, hashType uint8) ([]byte, error)
}

See cmd/spdm-responder/ for a complete working example with ephemeral certificates and static measurements.

Using Different Transports
// TCP (DSP0287)
import "github.com/xaionaro-go/spdm/pkg/transport/tcp"
t := tcp.New(conn)

// MCTP (DSP0239) — implement transport.Transport over your MCTP layer
import "github.com/xaionaro-go/spdm/pkg/transport/mctp"

// PCI DOE — implement transport.Transport over your DOE mailbox
import "github.com/xaionaro-go/spdm/pkg/transport/pcidoe"

// Storage (DSP0286) — implement transport.Transport over your storage interface
import "github.com/xaionaro-go/spdm/pkg/transport/storage"

All transports implement the same transport.Transport interface:

type Transport interface {
    SendMessage(ctx context.Context, sessionID *uint32, msg []byte) error
    ReceiveMessage(ctx context.Context) (sessionID *uint32, msg []byte, err error)
    HeaderSize() int
}

CLI Tools

The repository includes example CLI tools:

# Start a responder
go run ./cmd/spdm-responder -listen 127.0.0.1:2323 -v

# In another terminal, connect and authenticate
go run ./cmd/spdm-requester connect -addr 127.0.0.1:2323 -v
go run ./cmd/spdm-requester get-cert -addr 127.0.0.1:2323
go run ./cmd/spdm-requester challenge -addr 127.0.0.1:2323
go run ./cmd/spdm-requester get-meas -addr 127.0.0.1:2323

Tests

Unit Tests

Package-level tests covering message serialization, capability validation, algorithm mapping, error codes, crypto operations, session key derivation, and handler logic:

go test ./...
End-to-End Loopback Tests

SPDM handshake tests (test/e2e/) that connect a Go requester to a Go responder in-process, verifying the complete protocol flow including version negotiation, capabilities exchange, certificate retrieval, challenge-response authentication, and secure session establishment.

Reference Interoperability Tests

Tests against the DMTF spdm-emu reference implementation (libspdm) running in Docker. These validate that the Go implementation correctly interoperates with the official C reference:

cd tests/reference
make test

This builds a Docker image containing both the spdm-emu binaries and the Go test suite, then runs the interop tests. Requires Docker with BuildKit support.

The reference tests cover: transport-level communication, handshake flows, certificate exchange, measurements retrieval, and secured sessions.

License

MIT

Directories

Path Synopsis
cmd
spdm-requester command
spdm-responder command
internal
pkg
crypto
Package crypto defines interfaces for the SPDM cryptographic operations per DSP0274 Section 12.
Package crypto defines interfaces for the SPDM cryptographic operations per DSP0274 Section 12.
gen
gen/raw
Package raw contains machine-translated C struct definitions from the DMTF SPDM specification headers.
Package raw contains machine-translated C struct definitions from the DMTF SPDM specification headers.
gen/status
Package status defines SPDM library error types and sentinel errors matching the codes in libspdm spdm_return_status.h (DSP0274 Appendix A).
Package status defines SPDM library error types and sentinel errors matching the codes in libspdm spdm_return_status.h (DSP0274 Appendix A).
spdm
Package spdm provides the consumer-facing API for the spdm library per DSP0274.
Package spdm provides the consumer-facing API for the spdm library per DSP0274.
tools
spdmgen command
spdmgen parses C header files (DMTF SPDM spec headers) and generates Go code for the spdm library.
spdmgen parses C header files (DMTF SPDM spec headers) and generates Go code for the spdm library.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL