Skip to content

SPIKE #1083 ODCA Certificate Validation for AMT

ShradhaGupta31 edited this page May 11, 2026 · 2 revisions

1. Issue Explanation

Issue Link: https://github.com/device-management-toolkit/rpc-go/issues/1083

For AMT 19+ activation, users often rely on --skip-amt-cert-check to get through TLS setup. This bypasses ODCA certificate verification logic that should be enforced in normal secure flows.

The feature request is to ensure ODCA certificate chains presented by AMT are validated. This is needed because the Intel AMT ODCA certificate is used as an Intel AMT TLS Server certificate for mutual TLS provisioning, assuring that the configuration software is communicating with authentic Intel AMT firmware.

Goal: make activation succeed securely by default without requiring -n or --skip-amt-cert-check in standard environments.

ODCA in Simple Terms

  • ODCA is Intel's certificate chain used by AMT during provisioning TLS.
  • During TLS handshake, AMT presents a certificate chain.
  • Verification means: "Is this chain anchored to a trusted Intel ODCA root and structurally valid?"
  • If not verified, activation can still encrypt traffic, but identity trust is missing.

2. Current Behavior (Current Implementation)

  • --skip-amt-cert-check controls certificate validation for the local TLS hop to the LMS endpoint (the chain represents AMT identity).
  • For direct local TLS, rpc-go builds TLS config in internal/certs/lmsTls.go via GetTLSConfig.
  • In pre-provisioning mode (mode == 0), rpc-go callback invokes:
    • VerifyCertificates
    • VerifyLeafCertificate
    • VerifyROMODCACertificate
    • VerifyFullChain
  • VerifyFullChain currently uses embedded roots (internal/certs/trustedstore/*.cer) via LoadRootCAPool.
  • In cloud TLS tunnel mode, TLS to AMT is terminated by RPS TLSTunnelManager; rpc-go forwards bytes and does not validate ODCA in that hop.

Key Implementation Points

Concern File
TLS config and peer certificate checks internal/certs/lmsTls.go
Embedded trusted root loading internal/certs/embed.go
Insecure bypass flag definition internal/cli/cli.go
Activation path (consumes TLS config) internal/commands/activate/local.go

Observed Problem

  • Customers pass --skip-amt-cert-check during activation on AMT 19+ platforms.
  • This sets InsecureSkipVerify = true on the TLS config, disabling all certificate chain validation against trusted roots.
  • The transport (mTLS) remains secure, but the ODCA chain is never verified.

3. DMT Architecture — Console vs Cloud Paths

Is this feature for local (console) activation only, or also for cloud (RPS/MPS) flows? Answer: both paths are affected whenever LocalTlsEnforced is active or LMS is present on the device. The diagrams below show exactly where each TLS hop occurs and where ODCA verification is triggered.

3.1 Path A — Console / Local Activation

The operator runs rpc activate --local directly on the managed device. No RPS or MPS is involved.

+------------------------------------------------------------------+
|                        Managed Device                            |
|                                                                  |
|  +-----------+   (1) WSMAN over TLS    +--------------------+    |
|  |           | ----------------------> |                    |    |
|  |  rpc-go   |   localhost:16993       |  LMS (Local Mgmt   |    |
|  | (console) | <---------------------- |     Service)       |    |
|  |           | LMS presents chain      |                    |    |
|  +-----------+   <-- VERIFY HERE ---   +--------+-----------+    |
|                                                 |                |
|                                           (2) MEI/HECI           |
|                                           (in-kernel, no TLS)    |
|                                                 |                |
|                                        +--------v-----------+    |
|                                        |   AMT firmware     |    |
|                                        |     (CSME)         |    |
|                                        +--------------------+    |
+------------------------------------------------------------------+
Hop Protocol TLS? Certificate Presented
rpc-go → LMS WSMAN/TLS Yes LMS presents chain representing AMT identity — verify here
LMS → AMT MEI/HECI No
Code path: internal/commands/activate/local.goSetupWsmanClient(tlsConfig)

3.2 Path B — Cloud Activation via RPS (with LMS relay / TLS tunnel)

The operator runs rpc activate -u wss://rps.example.com --profile myprofile. RPS orchestrates activation; rpc-go relays commands between RPS and LMS/LME.

+-----------------------------------------------+    
|              Managed Device                   |    +--------------------------------------+
|                                               |    |              Cloud                   |                                    
|  +-----------+  (1) WSS (WebSocket + TLS)     |    |  +-------------------------------+   |
|  |           | ----------------------------->-|--->|  |  RPS                          |   |
|  |  rpc-go   |  wss://rps.example.com         |    |  | (Remote Provisioning Server)  |   |  
|  |           | <----------------------------- |    |  |                               |   |
|  |           |  provisioning commands         |    |  | TLSTunnelManager              |   |
|  |           |  [guarded by --skip-cert-check]|    +--------------------------------------+
|  |           |                                |    
|  |           |  (2a) if LocalTlsEnforced:     |
|  |           |  raw tunnel bytes to LMS TLS   |
|  |           | ----------------> +----------+ |
|  |           |  localhost:16993  |  LMS     | |
|  |           | <---------------- |          | |
|  |           |  (rpc-go relays) +----+-----+ |
|  |           |                       |        |
|  |           |                   MEI/HECI     |
|  |           |  (2b) if no TLS / LMS absent:  |
|  |           |  Direct MEI/HECI (LME)         |
|  |           | ----------------> +----------+ |
|  +-----------+  (no TLS, no      |  AMT fw  | |
|                  ODCA check)     |  (CSME)  | |
|                                  +----------+ |
+-----------------------------------------------+
Hop Protocol TLS? Certificate Presented Flag
rpc-go → RPS WSS Yes RPS server cert (standard CA) --skip-cert-check / -n
rpc-go → LMS Plain TCP relay to TLS port (16993) when LocalTlsEnforced No (at rpc-go layer)
RPS TLSTunnelManager ↔ AMT (via rpc-go relay) TLS tunnel (tls_data) Yes ODCA / configured CA chain — verify here RPS tunnel policy
rpc-go → LME MEI/HECI fallback (LMS absent) No

Code paths:

  • rpc-go/internal/rps/executor.golm.NewLMSConnection(... LocalTlsEnforced ...) for relay
  • rpc-go/internal/lm/service.goLMSConnection.Connect() dials plain TCP to LMS (16993 in TLS tunnel mode)
  • rpc-go/internal/rps/rps.goConnect(skipCertCheck) validates only the RPS WSS cert
  • rps/src/TLSTunnelManager.ts terminates TLS, captures peer chain, and verifies ODCA/custom CA
  • Falls back to lm.NewLMEConnection() (direct MEI, no TLS) when LMS is absent

3.3 TLS Handshake Detail — Cloud TLS Tunnel (RPS validates)

rpc-go                   RPS TLSTunnelManager                     LMS endpoint / AMT identity
  |                              |                                 |
  |-- WSS (TLS) ---------------->|                                 |
  |   (RPS cert checked here)    |                                 |
  |                              |-- TLS ClientHello ----------->  |
  |<====== tls_data bytes ======>|<-- ServerHello + cert chain --  |
  |   (byte relay only)          |   (ODCA/custom CA verified)     |
  |                              |-- TLS Finished -------------->  |
  |<====== tunneled WSMAN data ================================>   |

Notes:

  • In this mode, rpc-go is a transport relay for AMT TLS bytes.
  • Certificate chain verification for AMT TLS is performed in RPS (TLSTunnelManager), not in rpc-go GetTLSConfig.

3.4 Where ODCA Verification Is Needed — Summary

Activation Path Where TLS Terminates ODCA Check Needed? Controlled By
Console local (--local) rpc-go ↔ LMS (16993) YES --skip-amt-cert-check
Cloud via RPS + LocalTlsEnforced=true RPS TLSTunnelManager ↔ AMT (via rpc-go relay) YES RPS TLS tunnel policy
Cloud via RPS, plain LMS (no TLS enforced) No AMT TLS hop No
Cloud via RPS, LMS absent (LME fallback) No AMT TLS hop No
Post-activation CIRA to MPS AMT ↔ MPS (mTLS) Not part of this rpc-go ODCA check MPS/CIRA trust config

The ODCA feature targets the first two rows. On AMT 19+, the ODCA root CA in those chains changed, causing trust failures unless the correct root is available to the active verifier (rpc-go in local direct mode, RPS in cloud TLS tunnel mode).


3.5 TLS Handshake Detail — Local Direct Path (rpc-go validates)

rpc-go                                    LMS (localhost:16993)
   |                                            |
   |---- TLS ClientHello ---------------------->|
   |                                            |
   |<--- TLS ServerHello + Certificate chain ---|
   |     [0] leaf:  iAMT CSME IDevID RCFG       |
   |     [1] intermediate 1                     |
   |     [2] intermediate 2                     |
   |     [3] ODCA ROM CA        <- level 3      |
   |     [4] ODCA intermediate                  |
   |     [5] Intel root CA   <- CHANGED on 19+  |
   |                                            |
   |  VerifyPeerCertificate() fires             |
   |  in GetTLSConfig() (lmsTls.go)             |
   |    VerifyCertificates()                    |
   |      VerifyLeafCertificate()   [index 0]   |
   |      VerifyROMODCACertificate() [index 3]  |
   |      VerifyFullChain()                     |
   |        LoadRootCAPool()                    |
   |          needs AMT 19+ root in             |
   |          trustedstore/ to succeed          |
   |                                            |
   |---- TLS Finished ------------------------->|
   |---- WSMAN activation commands ------------>|

Root cause on AMT 19+: The Intel root CA at index 5 changed. If that root is absent from trustedstore/, VerifyFullChain() fails and customers fall back to --skip-amt-cert-check. This is what the feature must fix.

4. Sequence Diagram: Current Flow vs Target

This sequence applies to the local direct TLS validation path (rpc-go as TLS client). For cloud TLS tunnel mode, equivalent trust-source improvements must be applied in RPS TLSTunnelManager.

sequenceDiagram
    autonumber
    participant U as User/CLI
    participant C as internal/cli
    participant A as activate/local
    participant T as GetTLSConfig
    participant H as TLS Handshake
    participant V as VerifyPeerCertificate
    participant VC as VerifyCertificates
    participant L as VerifyLeafCertificate
    participant R as VerifyROMODCACertificate
    participant F as VerifyFullChain
    participant E as Embedded Roots (existing)
    participant B as BuildTrustPool (new)
    participant O as OS Trust Store (new)
    participant CF as Optional CA File (new optional)

    U->>C: rpc activate
    C->>A: SkipAMTCertCheck=false
    A->>T: GetTLSConfig(mode=0, amtCertInfo, false)
    T-->>A: tls.Config(callback enabled)
    A->>H: Open TLS to LMS endpoint
    H->>V: Peer cert chain
    V->>VC: VerifyCertificates(rawCerts,...)
    VC->>L: Leaf CN/hash checks (existing)
    L-->>VC: pass/fail
    VC->>R: ROM ODCA CN/OU checks (existing)
    R-->>VC: pass/fail

    Note over VC,F: Existing chain verification call remains
    VC->>F: VerifyFullChain(parsedCerts)

    Note over F,E: Current: embedded roots only
    F->>E: LoadRootCAPool()
    E-->>F: Root pool

    Note over F,B: Implement: replace/extend root loading with combined pool
    F->>B: BuildTrustPool() (new)
    B->>E: Load embedded roots
    B->>O: Merge OS roots
    opt enterprise override
      B->>CF: Add CA file roots
    end
    B-->>F: Combined trust pool

    F-->>VC: Valid chain or actionable trust error
    VC-->>V: success/failure
    V-->>H: handshake continue/fail
Loading

5. Implementation Tasks for #1083

5.1 Design and Contracts

  • Define trust-source policy for AMT 19+ default activation.
  • Decide precedence: embedded + OS + optional file.
  • Define error taxonomy/messages for trust failures.

5.2 Code Changes

  • Add BuildTrustPool abstraction in internal/certs.
  • Load and merge embedded root certs (existing source).
  • Load and merge OS trusted roots (new source).
  • Optionally support user-provided CA file roots.
  • Update VerifyFullChain to use combined trust pool.
  • Keep VerifyLeafCertificate and VerifyROMODCACertificate as supplemental checks.
  • Add CLI/config/env input for enterprise CA bundle (--amt-ca-file, AMT_CA_FILE) and thread into TLS validation path
  • Align cloud TLS tunnel verifier trust inputs in RPS (TLSTunnelManager) with the same trust policy, so local and cloud behavior match.
  • Keep skip flags functional as explicit insecure override.

5.3 Tests

  • Unit test: valid chain with embedded roots passes.
  • Unit test: valid chain with OS roots passes.
  • Unit test: untrusted chain fails closed.
  • Unit test: malformed/expired cert fails with clear error.
  • Command/integration test: activation works without skip flags in trusted setup.
  • Command/integration test: --skip-amt-cert-check still bypasses verification when explicitly used.

Clone this wiki locally