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
- Contract version:
sm-remote-vendor-adapter.v2(current default).v1remains valid and means SM4-ECB-only — pin it viaSM_REMOTE_HSM_CONTRACT_VERSION=sm-remote-vendor-adapter.v1for a legacy ECB-only bridge (see Contract versioning & SM4 mode negotiation). - Supported
adapterKindvalues:sm-hsm,sdf,pkcs11. - Every production response must include
contractVersion,adapterId,adapterKind,production: true, andtestOnly: false. - Responses that look like
sim.crypto.remote, contain simulator metadata, omit contract metadata, or setproduction: falsefail closed. - Production SM Remote requires
SM_REMOTE_HSM_URL, explicitSMREMOTE_KEYS, and vendor contract validation.SM_REMOTE_HSM_API_KEYis forwarded as a bearer token when configured;SM_REMOTE_HSM_REQUIRE_API_KEY=1makes it mandatory at startup. SMREMOTE_KEYSmaps public Stella key IDs to vendor key IDs:release-sm2=vendor-sm2-key,content-sm4=vendor-sm4-key.- SM4 mode is negotiable (ADR-037 Option 3).
SMREMOTE_SM4_MODEselectsCBC(default),GCM, or the explicit legacy waiverECB-Interop. The service selects the SM4 algorithm accordingly (SM4-CBC/SM4-GCM/SM4-ECB) and — at probe time — requires the bridge to advertise that algorithm in/status.supportedAlgorithms, failing closed withalgorithm_missingrather than downgrading to ECB. A bareSM4maps to the configured mode’s default.
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
v1bridge advertisesalgorithmId: "SM4-ECB"for its SM4 key; av2bridge advertisesSM4-CBCorSM4-GCM. SM Remote accepts the whole SM4 family on the key descriptor (bareSM4,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:
sm-remote-vendor-adapter.v2(current default) — adds explicit IV/tag transport and theSM4-CBC/SM4-GCMalgorithm ids. Selected by default; a v2 bridge advertisesSM4-CBCand/orSM4-GCMin/status.supportedAlgorithms.sm-remote-vendor-adapter.v1(legacy, ECB-only) — still valid. Pin it withSM_REMOTE_HSM_CONTRACT_VERSION=sm-remote-vendor-adapter.v1, which keeps an ECB-only bridge working. Because v1 means ECB, the host must also opt intoSMREMOTE_SM4_MODE=ECB-Interopto reach SM4-ECB (see below).SMREMOTE_SM4_MODE—CBC(default),GCM, orECB-Interop. Parallels the GOST plugin’sSymmetricMode/ECB-Interopwaiver: selectingECB-Interopemits a loud startup warning that SM4-ECB is not semantically secure and is enabled only for legacy vendor-bridge interop. There is no bareECBtoken — the-Interopsuffix makes the security waiver self-documenting.- Fail-closed negotiation. When the host is configured for
CBC/GCM(orECB-Interop), it requires the correspondingSM4-CBC/SM4-GCM/SM4-ECBalgorithm to be present in the bridge/status.supportedAlgorithms. A CBC/GCM-configured host against an ECB-only bridge is rejected at probe time withalgorithm_missing— it never silently downgrades to ECB (that would be a downgrade-attack surface).
Host SMREMOTE_SM4_MODE | Selected algorithm id | Bridge must advertise | v1 (ECB-only) bridge | v2 bridge |
|---|---|---|---|---|
CBC (default) | SM4-CBC | SM4-CBC | rejected (algorithm_missing) | OK |
GCM | SM4-GCM | SM4-GCM | rejected (algorithm_missing) | OK when GCM advertised |
ECB-Interop | SM4-ECB | SM4-ECB | OK (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):
StellaOps.Cryptography.Sm.Contracts(hosted undersrc/Cryptography/StellaOps.Cryptography.Plugin.SmRemote/) holdsSmRemoteHttpClient, the request/response DTOs,SmRemoteHttpProvider(cn.sm.remote.http),SmRemoteProviderOptions, andSmRemoteSigner. The SmRemote service host wires it directly into itsProgram.csendpoints.- The SmRemote service image does not compile-time reference the in-process SM provider plug-ins. The software SM2/SM3 provider (
cn.sm.soft) is mounted as a signed crypto pack on profilecrypto-sminstead (see Cryptography architecture §4.3b for the full decomposition, theMountedSmCryptoRuntimePluginLoader, and the production fail-closed guard).
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.
