Skip to content

Add AUTOSAR Classic Crypto Driver + Adaptive CryptoProvider#404

Draft
Frauschi wants to merge 1 commit into
wolfSSL:mainfrom
Frauschi:autosar_port
Draft

Add AUTOSAR Classic Crypto Driver + Adaptive CryptoProvider#404
Frauschi wants to merge 1 commit into
wolfSSL:mainfrom
Frauschi:autosar_port

Conversation

@Frauschi
Copy link
Copy Markdown
Contributor

@Frauschi Frauschi commented Jun 5, 2026

Today an automotive integrator who wants wolfHSM in an AUTOSAR ECU has to hand-write the glue between their CSM/CryIf configuration and wh_Client_*. That glue is the main barrier to wolfHSM adoption in production AUTOSAR projects — every customer reinvents the same 5–10 kLoC.

This PR adds port/autosar/, an AUTOSAR R22-11 wrapper that exposes the wolfHSM client API as the two standard AUTOSAR Crypto surfaces:

  • Classic Platform — implements AUTOSAR_SWS_CryptoDriver. Drops in below CryIf in any AUTOSAR Classic BSW.
  • Adaptive Platform — implements an ara::crypto CryptoProvider per AUTOSAR_SWS_Cryptography. Plugs into any AP runtime that supports providers.

The integrator's SWCs are untouched; only a CryIf routing entry needs updating in their config tool.

Layout

port/autosar/
├── classic/                AUTOSAR Classic Crypto Driver (C, R22-11)
├── adaptive/               AUTOSAR Adaptive CryptoProvider (C++17, R22-11)
├── common/                 algorithm + key-id mapping shared by both
└── docs/                   integration guides + algorithm coverage matrix

Algorithm coverage

AUTOSAR primitive wolfHSM client async pair Classic sync Classic async Adaptive
RandomGenerate Rng*Request/Response
Hash SHA-256 / 384 / 512 Sha*Update/FinalRequest/Response
AES ECB / CBC / CTR AesEcb/Cbc/CtrRequest/Response
AEAD AES-GCM AesGcmRequest/Response
MAC CMAC-AES (gen/verify) CmacGenerateRequest/Response
Signature ECDSA P-256 EccSign/VerifyRequest/Response
Signature Ed25519 wh_Client_Ed25519Sign/Verify (sync) n/a*
Signature RSA-PKCS#1-v1.5 wc_RsaSSL_Sign/Verify (sync) n/a**
Key agreement ECDH P-256 EccSharedSecretRequest/Response
KDF HKDF, CMAC-KDF Hkdf/CmacKdfMakeCacheKey (sync) n/a*
KeyGenerate / Derive various MakeCacheKey (sync) n/a
KeyElementSet / Get / Copy KeyCache / KeyExport / KeyCommit n/a

n/a* = upstream only ships the blocking wrapper today. n/a** = RSA async exists upstream but is raw RSA (RSA_PRIVATE_ENCRYPT / RSA_PUBLIC_DECRYPT); wiring RSASSA_PKCS1_V1_5 async would need client-side PKCS#1 v1.5 padding, and wolfSSL doesn't expose its padding helpers (wc_RsaPad_PKCS1v15 etc.) as public API. Flagged in docs/algorithm_coverage.md as a follow-up.

Full matrix in port/autosar/docs/algorithm_coverage.md.

Async dispatcher

Honours wolfHSM's one request in flight per whClientContext contract end-to-end:

  • Crypto_ProcessJob enqueues into a per-driver-object slot (WH_AUTOSAR_ASYNC_QUEUED) and returns immediately.
  • Crypto_MainFunction promotes the oldest queued slot to PENDING only when nothing else is in flight, polls *Response, fires CryIf_CallbackNotification.
  • Crypto_CancelJob handles the cancel-vs-completion race via a CANCELLING side-path; the eventual Response is silently drained.
  • PENDING slots exceeding CRYPTO_ASYNC_TIMEOUT_TICKS are force-cleaned through a single slotFreeWcResources path so wc_*Free runs exactly once per terminal transition.
  • Hash async chunks input across as many Sha*UpdateRequest cycles as needed (halving fallback on BADARGS).
  • CMAC async stashes the caller's expected MAC across the in-flight Request so the Response handler can run a constant-time compare.

Adaptive port — namespace note

The AUTOSAR Adaptive ara/crypto/* headers are Consortium-licensed and can't be redistributed under GPLv3. The provider therefore exposes a shape-compatible API in wolfhsm::ara_crypto. The integrator writes a thin adapter inside their AP application that derives from their runtime's ara::crypto::cryp::CryptoProvider and forwards each method 1:1. The forwarding is mechanical — every wolfhsm::ara_crypto type mirrors its ara::crypto counterpart. This is documented in docs/integration_adaptive.md.

The Adaptive surface is synchronous; AP runtimes that expose async ara::core::Future<…> semantics absorb that in the adapter on top of a worker pool. We chose this over building a fake async layer because wolfHSM's one-in-flight contract makes "really async" only correct when each worker thread owns its own whClientContext.

Tests + CI

  • port/autosar/classic/examples/csm_smoke/ — per-category C harness (26+ tests): NIST hash KATs (SHA-256 / 384 / 512), NIST AES-CBC KATs, ECDSA / Ed25519 / RSA sign+verify+tampered-sig (sync), CMAC sync gen+verify-good+verify-bad and async gen (byte-matches sync) + verify-good, DET parameter-check fan-out (10 paths), resource-accounting (leak-free across UPDATE/START misuse + 20-cycle async churn), cancel-during-pending, timeout force-cleanup, 32-job concurrency stress.
  • port/autosar/adaptive/examples/ap_smoke/ — per-cluster C++ harness: Random, SHA-256/384/512 KATs vs NIST vectors, AES-CBC (NIST F.2.1), AES-GCM roundtrip, CMAC gen+verify+tampered, ECDSA P-256 sign+verify+tampered, bidirectional ECDH P-256 (both sides derive identical secret), HKDF-SHA256, KeyStorage save / load / commit / erase + post-erase op rejected.

Both binaries run against examples/posix/wh_posix_server over TCP and are wired into a new CI job (.github/workflows/build-and-test-autosar.yml) with a TCP-readiness probe and a server-exit-code check so a server crash mid-test fails the job rather than being masked by the client's clean exit. ASAN matrix cell included.

Security-sensitive paths

  • Constant-time compare for every authenticated-value equality check (CMAC verify, RSA recovered-hash compare) via wh_Autosar_ConstantCompare — sync + async, Classic + Adaptive.
  • Verify-rejection classification: shared wh_Autosar_IsVerifyRejection(rc) distinguishes wolfCrypt-side rejection (-1..-1999 → E_OK + verifyPtr=NOT_OK) from wolfHSM transport errors (≤ -2000 → E_NOT_OK). Applied to ECDSA / Ed25519 / RSA / AEAD-decrypt in both sync and async.
  • Shared-secret zeroisation: Crypto_KeyExchangeCalcSecret zeroes the raw DH bytes on its stack regardless of outcome (CWE-244).
  • Failed-hash slot containment: a failed wire UPDATE marks the slot errored=TRUE and rejects every subsequent UPDATE/FINISH until the caller drives FINISH (which releases it), rather than yanking the slot mid-stream and confusing the caller's state machine.
  • KeyStorageProvider::Erase revokes (policy) + erases (NVM); subsequent crypto ops on the keyId are rejected by the server, asserted by ap_smoke. Documented that Revoke does not zeroise the cache bytes — it makes the key operationally unusable, not forensically unrecoverable.

What's out of scope

  • Reference CSM + CryIf for standalone use (Phase 2).
  • ASIL / ISO 26262 / ISO 21434 qualification artefacts (separate certification track).
  • ARXML generation tooling — we ship a static module description; vendor config-tool generators are a Phase 2 concern.
  • "AUTOSAR-conformant" product labelling — reserved to AUTOSAR Partners. This port implements R22-11; conformance certification is out of scope. Wording in README.md and docs/ is precise about this.

Known limitations

  • whKeyId: wolfHSM's wire keyId is 8 bits of ID (+ flags); this port maps each AUTOSAR cryptoKeyId 1:1 onto that ID and stores only the MATERIAL element in the wolfHSM keystore. Other AUTOSAR key elements (ALGORITHM, KEYSIZE, IV, …) ride on the CSM/CryIf side as metadata. Integrators needing per-element storage replace wh_Autosar_ComposeKeyId with a strong override — the function is declared with a WH_AUTOSAR_WEAK macro that handles the GCC / Clang / GHS / TI / Tasking / IAR weak-symbol spellings.
  • X25519 is declared in CryptoAlgId but CreateKeyAgreementPrivateCtx rejects it at construction — wh_Client_Curve25519SharedSecret takes a curve25519_key* rather than raw keyIds, so it needs a keyId-handle accessor before the Adaptive layer can wire it. Sync ECDH P-256 and the Classic side are unaffected.
  • RSA-PKCS#1-v1.5 async deferred pending upstream wolfSSL exposing PKCS#1 v1.5 padding helpers (or a wolfHSM-side PKCS#1-aware async RSA API). Sync RSA-PKCS#1-v1.5 via wc_RsaSSL_Sign/Verify works unchanged.

Test plan

  • csm_smoke passes (all 26+ categories) against wh_posix_server
  • ap_smoke passes (13 cluster checks) including SHA-384/512 KATs, bidirectional ECDH, erase-blocks-op
  • CMAC sync + async produce byte-for-byte identical MACs
  • Both ASAN=0 and ASAN=1 matrix cells build clean
  • clang-format clean across port/autosar/
  • No changes to wolfhsm/wh_client*.h public API
  • Manual integration against a real BSW vendor header set (out of scope for this PR; documented in docs/integration_classic.md)

Licensing

GPLv3, same model as the rest of the repo. Commercial integrators take a wolfSSL Inc. commercial license — same dual-licensing posture as wolfSSL / wolfCrypt. The port ships no vendor-supplied BSW headers (Crypto.h, ara/crypto/* are authored from the public AUTOSAR SWS PDFs); the ARXML module description is similarly written from scratch against the public schema. csm_smoke includes minimal in-tree fakes for Std_Types.h / CryIf_Cbk.h / Det.h so the test build stays license-clean.

@Frauschi Frauschi force-pushed the autosar_port branch 3 times, most recently from f99e718 to 40a52ce Compare June 5, 2026 16:14
Adds a new port/ that exposes the wolfHSM client API as the standard
AUTOSAR R22-11 Crypto interfaces, so wolfHSM can replace OEM / vendor
HSM firmware in existing AUTOSAR Classic and Adaptive stacks with no
SWC code changes.

Layout
  port/autosar/classic/   AUTOSAR Classic Crypto Driver (C, R22-11)
                          implements AUTOSAR_SWS_CryptoDriver; drops
                          in below CryIf in any AUTOSAR Classic BSW.
  port/autosar/adaptive/  AUTOSAR Adaptive CryptoProvider (C++17).
                          Lives in a wolfhsm::ara_crypto namespace so
                          the port itself stays free of AUTOSAR-
                          Consortium-licensed headers; integrators
                          write a thin ara::crypto::cryp::CryptoProvider
                          adapter that forwards each call.
  port/autosar/common/    algorithm + key-id mapping shared by both.
  port/autosar/docs/      integration guides, algorithm coverage
                          matrix, and a note on the one in-port
                          translation (client_workarounds.md).

Classic — primitives wired through both sync and real async paths
(Crypto_MainFunction drives wolfHSM Request/Response):
  - Hash: SHA-256 / 384 / 512 (single-call and START / UPDATE /
    FINISH streamed)
  - AES: ECB / CBC / CTR / GCM
  - MAC: AES-CMAC (gen + verify)
  - Signature: ECDSA P-256, Ed25519 (RFC 8032 pure), RSA-PKCS#1-v1.5
    (sign + verify, plus tampered-sig negative paths)
  - Key agreement: ECDH (P-256)
  - KDF: HKDF, CMAC-KDF
  - Key management: KeyElementSet / Get / Copy, KeyCopy,
    KeySetValid, KeyGenerate (AES / ECC / Ed25519 / X25519 / RSA),
    KeyDerive, KeyExchangeCalcPubVal / CalcSecret
  - RNG: RandomGenerate

Async dispatcher honours wolfHSM's "one Request in-flight per
whClientContext" contract — Crypto_ProcessJob enqueues into
WH_AUTOSAR_ASYNC_QUEUED; Crypto_MainFunction promotes the oldest
queued slot to PENDING only when nothing else is in flight.
Crypto_CancelJob handles the cancel-vs-completion race. Hash async
chunks input across as many Sha*UpdateRequest cycles as needed.
PENDING slots that exceed CRYPTO_ASYNC_TIMEOUT_TICKS are force-
cleaned; resource cleanup goes through a single slotFreeWcResources
path so wc_*Free runs exactly once on every terminal transition.

Adaptive — WolfhsmCryptoProvider with nine context classes:
  - RandomGeneratorCtx, HashFunctionCtx
  - SymmetricBlockCipherCtx (AES ECB/CBC/CTR)
  - AuthCipherCtx (AES-GCM)
  - MessageAuthnCodeCtx (AES-CMAC)
  - SignerPrivateCtx / VerifierPublicCtx (ECDSA, Ed25519,
    RSA-PKCS1-v1.5; same isVerifyRejection translation as Classic)
  - KeyAgreementPrivateCtx (ECDH P-256)
  - KeyDerivationFunctionCtx (HKDF, CMAC-KDF)
  - KeyStorageProvider (Save / Load / Commit / Erase)

Tests
  csm_smoke (Classic, C, per-category):
    - test_kat.c: NIST SHA / AES-CBC / ECDSA / Ed25519 / RSA-PKCS1v1.5
      sign+verify+tampered-sig.
    - test_det.c: 10 parameter-check paths assert their
      (serviceId, errorId) tuples fire through Det_ReportError.
    - test_accounting.c: every test asserts
      wh_Autosar_DebugActiveSlotCount and ActiveHashStateCount return
      to zero, with a 2-second drain budget.
    - test_concurrency.c: 2 worker threads x 500 ms + a cancel
      thread; asserts submitted - cancelled == callbacks_delivered.
    - test_timeout.c: synthetic fake-pending injection + tick advance
      exercises the force-cleanup path deterministically.
    - test_cancel.c: accepts both cancel-vs-completion race outcomes
      via per-job callback tracking.
  ap_smoke (Adaptive, C++17): one end-to-end test per cluster
    against wh_posix_server.

CI: new .github/workflows/build-and-test-autosar.yml builds the POSIX
server, csm_smoke and ap_smoke, runs both suites under ASAN on/off.
clang-format-check.yml passes on every new file in the diff.

Sample smoke output (TCP against wh_posix_server):
  [basic]
  [kat]
    KAT hash: 5 vector(s) OK
    KAT aes-cbc: 2 vector(s) OK
    KAT ecdsa P-256: sign + good-sig verify + tampered-sig OK
    KAT ed25519: keygen + sign + verify + tampered-sig OK
    KAT rsa-pkcs1-v1.5 2048: keygen + sign + verify + tampered-sig OK
  [det]
    DET: 10 parameter-check paths fire correct (apiId, errorId)
  [accounting]
    accounting: leak-free across UPDATE/START misuse + 20-cycle async churn
  [cancel]
    cancel: queued / pending / unknown-job paths OK
  [timeout]
    timeout: force-cleanup after CRYPTO_ASYNC_TIMEOUT_TICKS fires E_NOT_OK callback
  [concurrency]
    concurrency: 32 async submitted, N cancelled, (32-N) callbacks delivered
  csm_smoke: all tests passed

  wolfHSM Adaptive Crypto Provider smoke (TCP)
    Random OK (32 bytes)
    Hash SHA-256("abc") matches NIST vector
    AES-CBC-128 NIST F.2.1 OK
    AES-GCM-128 roundtrip OK
    CMAC-AES generate + verify good + verify tampered OK
    ECDSA P-256 sign + verify-good + verify-bad OK
    ECDH P-256 shared secret OK (32 bytes)
    HKDF-SHA256 -> 32 bytes OK
    KeyStorage save / load roundtrip OK
  ap_smoke: all tests passed

Scope notes
  - No vendor BSW headers vendored. Crypto.h, Crypto_GeneralTypes.h
    and the ara::crypto-shaped C++ headers are written from the
    public AUTOSAR SWS documents. Integrators supply Std_Types.h,
    Det.h, CryIf_Cbk.h and the vendor's Crypto_GeneralTypes.h from
    their BSW. The csm_smoke harness keeps a minimal fake_bsw/
    directory for tool-free CI builds.
  - One client-side translation lives in
    Crypto_ProcessJob.c::isVerifyRejection — maps wolfCrypt-range
    negative return codes from wh_Client_EccVerify (currently
    surfaced when the signature is malformed-DER before math runs)
    into SWS-correct E_OK + verifyPtr=NOT_OK. The wolfHSM team has
    confirmed the underlying verify-handler contract is a real
    fix-up candidate; the port stays correct either way. See
    port/autosar/docs/client_workarounds.md.
  - AUTOSAR "conformant" labelling is reserved to AUTOSAR Partners.
    The port describes itself as "implements AUTOSAR R22-11
    interfaces". Wording-only; no code impact.

Out of scope
  - Reference CSM / CryIf inside the port (integrators use their
    BSW's).
  - AUTOSAR SHE wrapper (wolfhsm/wh_client_she.h already covers
    SHE).
  - PKCS#11 bridge.
  - ISO 26262 / ASIL qualification artefacts.
  - RSA-PSS / RSA-OAEP signature padding (separate follow-up).
  - ML-DSA / X25519 signature / agreement in Adaptive (waiting on
    keyId-shaped accessors).

GPLv3 across all new files, copyright wolfSSL Inc., consistent with
the rest of the repo. user_settings.h follows the existing
convention of header-less user-overridable config. clang-format-15
clean across the whole port/autosar/ tree.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant