SM Remote Vendor Adapter Contract

SM Remote production mode bridges to an operator-controlled vendor adapter through SM_REMOTE_HSM_URL. The adapter may wrap an SM HSM, SDF service, or PKCS#11 bridge, but it must expose this HTTP contract without shipping proprietary SDK code inside Stella Ops.

Production Rules

Endpoints

Health

GET /health

{
  "status": "ok",
  "isAvailable": true,
  "providerName": "vendor-sm-hsm",
  "contractVersion": "sm-remote-vendor-adapter.v2",
  "adapterId": "vendor-sm-hsm-primary",
  "adapterKind": "sm-hsm",
  "production": true,
  "testOnly": false
}

Status

GET /status

A v2 bridge advertises the SM4 modes it supports (SM4-CBC and/or SM4-GCM). A v1 bridge advertises only SM4-ECB.

{
  "isAvailable": true,
  "providerName": "vendor-sm-hsm",
  "supportedAlgorithms": ["SM2", "SM4-CBC", "SM4-GCM"],
  "contractVersion": "sm-remote-vendor-adapter.v2",
  "adapterId": "vendor-sm-hsm-primary",
  "adapterKind": "sm-hsm",
  "production": true,
  "testOnly": false
}

SM Remote requires the SM4 algorithm matching the configured SMREMOTE_SM4_MODE to be present in supportedAlgorithms; otherwise it fails the operation closed (algorithm_missing) at probe time.

Key Discovery

GET /keys

{
  "contractVersion": "sm-remote-vendor-adapter.v1",
  "adapterId": "vendor-sm-hsm-primary",
  "adapterKind": "sm-hsm",
  "production": true,
  "testOnly": false,
  "keys": [
    {
      "keyId": "vendor-sm2-key",
      "remoteKeyId": "vendor-sm2-key",
      "algorithmId": "SM2",
      "usages": ["sign", "verify"],
      "adapterId": "vendor-sm-hsm-primary",
      "adapterKind": "sm-hsm",
      "production": true,
      "testOnly": false
    },
    {
      "keyId": "vendor-sm4-key",
      "remoteKeyId": "vendor-sm4-key",
      "algorithmId": "SM4-CBC",
      "usages": ["encrypt", "decrypt"],
      "adapterId": "vendor-sm-hsm-primary",
      "adapterKind": "sm-hsm",
      "production": true,
      "testOnly": false
    }
  ]
}

A v1 bridge advertises algorithmId: "SM4-ECB" for its SM4 key; a v2 bridge advertises SM4-CBC or SM4-GCM. SM Remote accepts the whole SM4 family on the key descriptor (bare SM4, SM4-CBC, SM4-GCM, SM4-ECB); the exact-mode check happens against /status.supportedAlgorithms.

SM Remote verifies that every configured SMREMOTE_KEYS remote ID appears in /keys and that the key advertises the requested operation.

Sign

POST /sign

{
  "keyId": "vendor-sm2-key",
  "algorithmId": "SM2",
  "payloadBase64": "c21yZW1vdGUtcHJvZHVjdGlvbi1oc20tcGF5bG9hZA=="
}
{
  "signature": "dmVuZG9yLXNtMi1rZXl8U00yfGMyMXlaVzF2ZEdVdGNISnZaSFZqZEdsdmJpMW9jMjB0Y0dGNWJHOWhaQT09",
  "keyId": "vendor-sm2-key",
  "algorithmId": "SM2",
  "contractVersion": "sm-remote-vendor-adapter.v1",
  "adapterId": "vendor-sm-hsm-primary",
  "adapterKind": "sm-hsm",
  "production": true,
  "testOnly": false
}

Verify

POST /verify

{
  "keyId": "vendor-sm2-key",
  "algorithmId": "SM2",
  "payloadBase64": "c21yZW1vdGUtcHJvZHVjdGlvbi1oc20tcGF5bG9hZA==",
  "signature": "dmVuZG9yLXNtMi1rZXl8U00yfGMyMXlaVzF2ZEdVdGNISnZaSFZqZEdsdmJpMW9jMjB0Y0dGNWJHOWhaQT09"
}
{
  "valid": true,
  "keyId": "vendor-sm2-key",
  "algorithmId": "SM2",
  "contractVersion": "sm-remote-vendor-adapter.v1",
  "adapterId": "vendor-sm-hsm-primary",
  "adapterKind": "sm-hsm",
  "production": true,
  "testOnly": false
}

Encrypt And Decrypt

POST /encrypt (v2, SM4-GCM). ivBase64 is optional on the request — when omitted the vendor bridge/HSM generates the IV and returns it. For SM4-GCM the response also carries tagBase64; for SM4-CBC only ivBase64. A v1/SM4-ECB bridge omits both.

{
  "keyId": "vendor-sm4-key",
  "algorithmId": "SM4-GCM",
  "payloadBase64": "c21yZW1vdGUtcHJvZHVjdGlvbi1oc20tcGF5bG9hZA=="
}
{
  "ciphertextBase64": "Y2lwaGVydGV4dC1ieXRlcw==",
  "ivBase64": "AAABIzR4AAAAAACrzQ==",
  "tagBase64": "g95TQeTCtYF34GWpv3ti7A==",
  "keyId": "vendor-sm4-key",
  "algorithmId": "SM4-GCM",
  "contractVersion": "sm-remote-vendor-adapter.v2",
  "adapterId": "vendor-sm-hsm-primary",
  "adapterKind": "sm-hsm",
  "production": true,
  "testOnly": false
}

POST /decrypt uses the same keyId and algorithmId with ciphertextBase64, plus ivBase64 (SM4-CBC/SM4-GCM) and tagBase64 (SM4-GCM); the response returns payloadBase64 plus the same contract metadata. IV and tag are explicit wire fields, never prepended to the ciphertext.

Contract versioning & SM4 mode negotiation

ADR-037 Option 3 makes the SM4 symmetric mode negotiable, closing the last ECB-by-default symmetric surface:

Host SMREMOTE_SM4_MODESelected algorithm idBridge must advertisev1 (ECB-only) bridgev2 bridge
CBC (default)SM4-CBCSM4-CBCrejected (algorithm_missing)OK
GCMSM4-GCMSM4-GCMrejected (algorithm_missing)OK when GCM advertised
ECB-InteropSM4-ECBSM4-ECBOK (pin …v1)OK when ECB advertised

Failure Classification

Adapters should return non-2xx responses for transport-level failures and may return a failure envelope when the adapter can classify the failure:

{
  "contractVersion": "sm-remote-vendor-adapter.v1",
  "adapterId": "vendor-sm-hsm-primary",
  "adapterKind": "sm-hsm",
  "production": true,
  "testOnly": false,
  "failure": {
    "class": "key_mapping_missing",
    "code": "SDF_KEY_NOT_FOUND",
    "message": "configured key was not present in the HSM slot"
  }
}

Recommended failure classes: configuration_missing, adapter_unavailable, adapter_timeout, authentication_failed, key_mapping_missing, key_usage_mismatch, algorithm_missing, malformed_request, malformed_response, and adapter_failure.

Library boundary (in-process transport vs signed pack)

The Stella-side HTTP transport that talks to this vendor contract is host-owned and ships as a contracts-only library, not a signed crypto pack — it carries no in-process crypto material, it only forwards over HTTP. As of the SmRemote contracts-only split (ADR-024, d2e24267aa):

This means the wire contract above is the boundary between Stella’s host-owned cn.sm.remote.http forwarder and the operator-controlled vendor adapter; the forwarder never embeds vendor SDK code.

Placement

Vendor bridge service sources and packaging belong under devops/services/sm-remote/ or a future explicitly named bridge directory. Proprietary SDK packages, vendor images, or vendored headers are not allowed until the dependency license gate updates NOTICE.md, docs/legal/THIRD-PARTY-DEPENDENCIES.md, and any required license texts.