From c0032194f92b1be30d145fa8a49bedf12504b9dc Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Mon, 18 May 2026 15:03:42 +0300 Subject: [PATCH 01/27] Add CRA Kit for customer SBOM integration Self-contained cra-kit/ with glossary, fictional auditor packet, autotools/embedded SBOM scripts, and CI validation. Signed-off-by: Sameeh Jubran --- .github/workflows/cra-kit.yml | 22 ++ README.md | 24 ++ cra-kit/CRA-Cheat-Sheet.md | 114 ++++++ cra-kit/CRA-Compliance-Shortlist.md | 130 +++++++ cra-kit/CRA-Supply-Chain-Glossary.md | 139 ++++++++ cra-kit/README.md | 296 ++++++++++++++++ cra-kit/ROADMAP.md | 43 +++ cra-kit/SKILL.md | 136 ++++++++ cra-kit/VERSION | 3 + cra-kit/auditor-packet/00-INDEX.md | 37 ++ .../auditor-packet/README-auditor-packet.md | 9 + .../product-acme-connect-gateway.cdx.json | 63 ++++ .../product-acme-connect-gateway.spdx.json | 46 +++ .../wolfssl-component-embedded/.gitignore | 3 + .../wolfssl-component-embedded/README.md | 23 ++ .../wolfssl-5.9.1.cdx.json | 328 ++++++++++++++++++ .../wolfssl-5.9.1.spdx.json | 53 +++ .../wolfssl-component/README-bomsh.md | 26 ++ .../wolfssl-component/SAMPLE-PROVENANCE.md | 21 ++ .../omnibor.wolfssl-5.9.1.spdx.json.sample | 92 +++++ .../wolfssl-5.9.1.cbom-draft.cdx.json | 246 +++++++++++++ .../wolfssl-component/wolfssl-5.9.1.cdx.json | 300 ++++++++++++++++ .../wolfssl-5.9.1.commercial.cdx.json | 304 ++++++++++++++++ .../wolfssl-5.9.1.commercial.spdx.json | 63 ++++ .../wolfssl-component/wolfssl-5.9.1.spdx | 30 ++ .../wolfssl-component/wolfssl-5.9.1.spdx.json | 53 +++ cra-kit/presentations/SLIDE-OUTLINE.md | 73 ++++ cra-kit/scripts/generate-embedded-sbom.sh | 9 + cra-kit/scripts/generate-wolfssl-sbom.sh | 232 +++++++++++++ cra-kit/scripts/make-commercial-sample.sh | 76 ++++ cra-kit/scripts/refresh-samples.sh | 60 ++++ cra-kit/scripts/validate.sh | 135 +++++++ cra-kit/user_settings.h | 12 + .../wolfssl-inc-auditor-packet/00-INDEX.md | 26 ++ cra-kit/wolfssl-inc-auditor-packet/README.md | 49 +++ .../ce-marking-statement.md | 64 ++++ .../classification-statement.md | 55 +++ .../conformity-assessment-route.md | 56 +++ .../declaration-of-conformity.template.md | 77 ++++ .../eu-authorised-representative.md | 63 ++++ .../support-period-policy.md | 54 +++ .../technical-documentation-outline.md | 88 +++++ .../vulnerability-handling-process.md | 95 +++++ 43 files changed, 3828 insertions(+) create mode 100644 .github/workflows/cra-kit.yml create mode 100644 cra-kit/CRA-Cheat-Sheet.md create mode 100644 cra-kit/CRA-Compliance-Shortlist.md create mode 100644 cra-kit/CRA-Supply-Chain-Glossary.md create mode 100644 cra-kit/README.md create mode 100644 cra-kit/ROADMAP.md create mode 100644 cra-kit/SKILL.md create mode 100644 cra-kit/VERSION create mode 100644 cra-kit/auditor-packet/00-INDEX.md create mode 100644 cra-kit/auditor-packet/README-auditor-packet.md create mode 100644 cra-kit/auditor-packet/product-acme-connect-gateway.cdx.json create mode 100644 cra-kit/auditor-packet/product-acme-connect-gateway.spdx.json create mode 100644 cra-kit/auditor-packet/wolfssl-component-embedded/.gitignore create mode 100644 cra-kit/auditor-packet/wolfssl-component-embedded/README.md create mode 100644 cra-kit/auditor-packet/wolfssl-component-embedded/wolfssl-5.9.1.cdx.json create mode 100644 cra-kit/auditor-packet/wolfssl-component-embedded/wolfssl-5.9.1.spdx.json create mode 100644 cra-kit/auditor-packet/wolfssl-component/README-bomsh.md create mode 100644 cra-kit/auditor-packet/wolfssl-component/SAMPLE-PROVENANCE.md create mode 100644 cra-kit/auditor-packet/wolfssl-component/omnibor.wolfssl-5.9.1.spdx.json.sample create mode 100644 cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.cbom-draft.cdx.json create mode 100644 cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.cdx.json create mode 100644 cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.commercial.cdx.json create mode 100644 cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.commercial.spdx.json create mode 100644 cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.spdx create mode 100644 cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.spdx.json create mode 100644 cra-kit/presentations/SLIDE-OUTLINE.md create mode 100755 cra-kit/scripts/generate-embedded-sbom.sh create mode 100755 cra-kit/scripts/generate-wolfssl-sbom.sh create mode 100755 cra-kit/scripts/make-commercial-sample.sh create mode 100755 cra-kit/scripts/refresh-samples.sh create mode 100755 cra-kit/scripts/validate.sh create mode 100644 cra-kit/user_settings.h create mode 100644 cra-kit/wolfssl-inc-auditor-packet/00-INDEX.md create mode 100644 cra-kit/wolfssl-inc-auditor-packet/README.md create mode 100644 cra-kit/wolfssl-inc-auditor-packet/ce-marking-statement.md create mode 100644 cra-kit/wolfssl-inc-auditor-packet/classification-statement.md create mode 100644 cra-kit/wolfssl-inc-auditor-packet/conformity-assessment-route.md create mode 100644 cra-kit/wolfssl-inc-auditor-packet/declaration-of-conformity.template.md create mode 100644 cra-kit/wolfssl-inc-auditor-packet/eu-authorised-representative.md create mode 100644 cra-kit/wolfssl-inc-auditor-packet/support-period-policy.md create mode 100644 cra-kit/wolfssl-inc-auditor-packet/technical-documentation-outline.md create mode 100644 cra-kit/wolfssl-inc-auditor-packet/vulnerability-handling-process.md diff --git a/.github/workflows/cra-kit.yml b/.github/workflows/cra-kit.yml new file mode 100644 index 000000000..5fa4d6141 --- /dev/null +++ b/.github/workflows/cra-kit.yml @@ -0,0 +1,22 @@ +name: CRA Kit + +on: + push: + paths: + - 'cra-kit/**' + - '.github/workflows/cra-kit.yml' + pull_request: + paths: + - 'cra-kit/**' + - '.github/workflows/cra-kit.yml' + +jobs: + validate-auditor-packet: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.x' + - name: Validate pinned auditor packet + run: ./cra-kit/scripts/validate.sh diff --git a/README.md b/README.md index 3a6371b9e..8cb04a3d2 100644 --- a/README.md +++ b/README.md @@ -413,6 +413,30 @@ Please see the for further usage and details. +
+ +#### cra-kit (wolfSSL CRA Kit) + +This directory is **not** a TLS/crypto tutorial. It demonstrates how to +generate wolfSSL **component SBOMs** (SPDX + CycloneDX), nest them in a +**fictional product SBOM**, and understand optional **bomsh** build provenance +(Linux host only) for EU Cyber Resilience Act-style software transparency. + +Includes a [CRA compliance shortlist](cra-kit/CRA-Compliance-Shortlist.md), a +[who provides what cheat sheet](cra-kit/CRA-Cheat-Sheet.md), full +[glossary](cra-kit/CRA-Supply-Chain-Glossary.md), [AI playbook](cra-kit/SKILL.md), sample +[customer-side auditor packet](cra-kit/auditor-packet/) (fictional Acme Connect +Gateway), [manufacturer-side filings](cra-kit/wolfssl-inc-auditor-packet/) (what +wolfSSL Inc. itself ships under CRA — classification, conformity assessment, +declaration of conformity template, EU AR status, etc.), and helper scripts +(`validate.sh` runs without building wolfSSL, with optional `cyclonedx-cli` / +`pyspdxtools` schema validation). Regenerating component SBOMs requires a +wolfSSL tree with SBOM support — see [cra-kit/README.md](cra-kit/README.md). + +Please see the [cra-kit/README.md](cra-kit/README.md) for further +usage and details. + +
#### uefi-library (wolfCrypt UEFI boot module and test app) diff --git a/cra-kit/CRA-Cheat-Sheet.md b/cra-kit/CRA-Cheat-Sheet.md new file mode 100644 index 000000000..16dcd3552 --- /dev/null +++ b/cra-kit/CRA-Cheat-Sheet.md @@ -0,0 +1,114 @@ +# wolfSSL CRA Supply Chain Cheat Sheet + +**Who provides what** — **you** vs **wolfSSL** +Print this page; use **[CRA-Supply-Chain-Glossary.md](CRA-Supply-Chain-Glossary.md)** for full definitions (SBOM, SPDX, CycloneDX, CBOM, VEX, bomsh, PURL, …). + +**Not legal advice.** You are the **manufacturer** for your product on the EU market. +wolfSSL provides **component evidence** for the **wolfSSL library only**. +wolfSSL Inc. is itself a manufacturer under CRA for libraries it places on the EU market — +see our [`security.txt`](https://www.wolfssl.com/.well-known/security.txt), +[CVD policy](https://www.wolfssl.com/.well-known/vulnerability-disclosure-policy.txt), +and our manufacturer-side filings in +[`wolfssl-inc-auditor-packet/`](wolfssl-inc-auditor-packet/) for reference. + +Requires a wolfSSL tree with SBOM support (`make sbom` / `scripts/gen-sbom`). +`make sbom` also needs `pyspdxtools` (`pip install spdx-tools`). + +**CRA Kit:** `wolfssl-examples/cra-kit/` · **AI playbook:** [SKILL.md](SKILL.md) +**Product-level CRA shortlist (4 pillars):** [CRA-Compliance-Shortlist.md](CRA-Compliance-Shortlist.md) + +--- + +## CRA compliance shortlist (four pillars) + +| Pillar | You | wolfSSL | +|--------|-----|---------| +| **1. Know your components** | Product SBOM + vuln process for whole product | Component SBOMs, advisories, updates — **this kit** | +| **2. Secure boot** | Trusted firmware + update path | **wolfBoot** | +| **3. Data in transfer** | Secure protocols for remote/cloud traffic | **TLS**, **SSH**, **MQTTS**, … | +| **4. Vulnerability handling & reporting** | Published CVD policy + `security.txt`; 24h ENISA reporting (Art. 14); on-call coverage | Reference templates: wolfSSL [`security.txt`](https://www.wolfssl.com/.well-known/security.txt) + [CVD policy](https://www.wolfssl.com/.well-known/vulnerability-disclosure-policy.txt); advisories; CNA | + +Detail: [CRA-Compliance-Shortlist.md](CRA-Compliance-Shortlist.md) + +--- + +## Who provides what (you vs wolfSSL) + +| | **You (product manufacturer)** | **wolfSSL (library supplier)** | +|---|-------------------------------|--------------------------------| +| **Inventory** | **Product SBOM** — OS, apps, all third-party code | **Component SBOM** — wolfSSL only (SPDX + CycloneDX) | +| **How you connect** | Nest or reference our files in your product SBOM | Ship `wolfssl-*.spdx.json` and `wolfssl-*.cdx.json` | +| **Vulnerabilities** | Your process + owner for the shipped product | [Advisories](https://www.wolfssl.com/docs/security-vulnerabilities/) + [CVD policy](https://www.wolfssl.com/.well-known/vulnerability-disclosure-policy.txt) + [`security.txt`](https://www.wolfssl.com/.well-known/security.txt) | +| **Optional build proof** | Only if your contract/auditor asks | `make bomsh` / OmniBOR (**Linux build host** only) | + +**Worked example:** [`auditor-packet/`](auditor-packet/) — fictional *Acme Connect Gateway* + wolfSSL SBOMs nested. + +--- + +## What auditors ask + +| Question | Term | wolfSSL today | +|----------|------|---------------| +| What software is in the product? | **SBOM** | `make sbom` or `gen-sbom` → SPDX + CycloneDX | +| What crypto is enabled in *your* build? | **CBOM** (path) | `wolfssl:build:*` in CycloneDX — not full `cryptographic-asset` yet | +| How was the library binary built? | **Provenance** | `make bomsh` (**Linux** host, optional) | + +*See glossary for SPDX vs CycloneDX, VEX, PURL, OmniBOR.* + +--- + +## BOMs at a glance + +| Name | Owner | wolfSSL today | +|------|-------|---------------| +| **Product SBOM** | **You** | — | +| **Component SBOM** | **wolfSSL** (you nest) | **Yes** | +| **CBOM** | **You** document; we signal config | **Partial** (build properties) | +| **VEX** | **You** (+ scanner) | Advisories only | +| **bomsh** | **wolfSSL** (optional) | **Yes**, Linux host only | + +Details: [CRA-Supply-Chain-Glossary.md](CRA-Supply-Chain-Glossary.md) · roadmap: [ROADMAP.md](ROADMAP.md) + +--- + +## Four decisions + +| Question | Answer | +|----------|--------| +| Need **our own** SBOM? | **Yes** | +| wolfSSL SBOM **enough alone**? | **No** — nest or reference in yours | +| Need **bomsh** for CRA? | **Usually no** | +| **SPDX** or **CycloneDX**? | **Both** — use what your tools consume | + +--- + +## Beyond this kit (don't skip) + +This kit covers **software transparency** only. Before placing your product on +the EU market you also need: + +| Obligation | Article | Action | +|------------|---------|--------| +| **EU Authorised Representative** | Art. 18 | Required if you're established outside the EU | +| **Product class** (Annex III/IV) | — | Determines self-cert vs **Notified Body** — long queues | +| **Conformity assessment + CE mark** | Art. 32, 30 | Module A or external review | +| **Technical documentation** | Annex VII | Risk assessment, support-period commitment | +| **Free security updates** | Art. 13(8) | 5+ year support period default | + +Engage CRA counsel/consultant — these are legal/structural decisions, not +artefacts. See [`CRA-Compliance-Shortlist.md`](CRA-Compliance-Shortlist.md) +"Beyond this kit" for detail. + +--- + +## What to read next + +| Resource | File | +|----------|------| +| Full glossary | [CRA-Supply-Chain-Glossary.md](CRA-Supply-Chain-Glossary.md) | +| Integration guide | [README.md](README.md) | +| Sample auditor folder | [auditor-packet/](auditor-packet/) | +| AI + scripts playbook | [SKILL.md](SKILL.md) | +| Upstream SBOM reference (flags, formats, OmniBOR) | [wolfssl/doc/SBOM.md](https://github.com/wolfSSL/wolfssl/blob/master/doc/SBOM.md) | + +**Questions about this kit:** support@wolfssl.com · **Security reports:** see [`security.txt`](https://www.wolfssl.com/.well-known/security.txt) diff --git a/cra-kit/CRA-Compliance-Shortlist.md b/cra-kit/CRA-Compliance-Shortlist.md new file mode 100644 index 000000000..96d83a5e5 --- /dev/null +++ b/cra-kit/CRA-Compliance-Shortlist.md @@ -0,0 +1,130 @@ +# Shortlist towards CRA compliance + +**Not legal advice.** The EU Cyber Resilience Act applies to **your product** as a whole. +wolfSSL helps on **specific pillars** below; you remain the **manufacturer** for market obligations. + +This page is the **product-level shortlist** (what to do). For **software transparency** work +(SBOM, nesting, sample auditor folder), use the **[CRA Kit](README.md)** cheat sheet and +[`CRA-Cheat-Sheet.md`](CRA-Cheat-Sheet.md). + +--- + +## 1. Know your software components + +| **Your job (manufacturer)** | **wolfSSL can help** | +|----------------------------|----------------------| +| Run a **survey** of every component in your embedded system or product: What is it? Who maintains it? Is it actively developed? How do you learn about vulnerabilities, fixes, and releases? | **Component SBOMs** (SPDX + CycloneDX) for wolfSSL libraries you ship — `make sbom` / `gen-sbom` | +| Build and maintain a **product SBOM** for the whole thing you place on the EU market | **Continuous vulnerability management**: [security advisories](https://www.wolfssl.com/docs/security-vulnerabilities/), coordinated disclosure, updates — see wolfSSL [`security.txt`](https://www.wolfssl.com/.well-known/security.txt) and [CVD policy](https://www.wolfssl.com/.well-known/vulnerability-disclosure-policy.txt) | +| Own vulnerability **process**, owners, and fix timelines for **your** release | Nest or reference our component SBOM in yours — worked example: [`auditor-packet/`](auditor-packet/) | + +**CRA Kit focus:** pillar 1 — who provides what cheat sheet, glossary, scripts, [`SKILL.md`](SKILL.md). + +--- + +## 2. Implement secure boot + +| **Your job (manufacturer)** | **wolfSSL can help** | +|----------------------------|----------------------| +| Treat secure boot as one of the **most influential actions** you can take now: firmware that boots **trusted**, with a defined path to **update** when needed | **[wolfBoot](https://www.wolfssl.com/products/wolfboot/)** — secure bootloader for embedded systems | +| Align update mechanics with your **complaint / incident** procedures and required **timelines** under CRA | Integration with wolfSSL/wolfCrypt; see wolfBoot docs and support | + +Secure boot is **product architecture**, not something an SBOM file alone satisfies. + +--- + +## 3. Bring remote data processing and data-in-transfer up to compliance + +CRA is **not only about software inventory** — it also concerns **data** moving between the device and the network. + +| **Your job (manufacturer)** | **wolfSSL can help** | +|----------------------------|----------------------| +| Map **remote processing** and **connectivity** in your product (cloud, OTA, admin interfaces, telemetry) | Implementations of **state-of-the-art** secure protocols, for example: | +| Use **current cryptography** and **secure protocols** for data in transfer; document what is enabled in **your** build | **TLS** (wolfSSL), **SSH** (wolfSSH), **MQTTS** (wolfMQTT), and related stacks | +| Reflect enabled algorithms in **your** product documentation / SBOM / crypto inventory | Build properties in CycloneDX today (`wolfssl:build:*`); formal CBOM profile: **roadmap** — [ROADMAP.md](ROADMAP.md) | + +--- + +## 4. Handle vulnerabilities and report on time + +CRA imposes **continuous** vulnerability handling obligations on manufacturers +(Art. 13) and a hard **24-hour** reporting clock for actively exploited +vulnerabilities (Art. 14). This is the only CRA pillar that requires **ongoing +operational capacity**, not a one-time deliverable. + +| **Your job (manufacturer)** | **wolfSSL can help** | +|----------------------------|----------------------| +| Publish a **Coordinated Vulnerability Disclosure (CVD) policy** and a working security contact (`security.txt` per RFC 9116) so researchers can reach you | Reference templates: wolfSSL's [`security.txt`](https://www.wolfssl.com/.well-known/security.txt) and [CVD policy](https://www.wolfssl.com/.well-known/vulnerability-disclosure-policy.txt) | +| Operate a **vulnerability handling process** with named owners and stated response targets | wolfSSL [security advisories](https://www.wolfssl.com/docs/security-vulnerabilities/) for libraries you ship; wolfSSL is a CVE Numbering Authority | +| Notify **ENISA within 24 hours** when a vulnerability in your product is **actively exploited** (Art. 14); follow up at 72 hours and a final report at 14 days | wolfSSL handles ENISA reporting for **wolfSSL libraries placed on the EU market by wolfSSL Inc.**; coordinate with us on shared advisories | +| Maintain **on-call coverage** including weekends and holidays so the 24-hour clock can be met at any time | — | + +This pillar is **not satisfied by SBOM artefacts alone** — it requires +documented process, named owners, and on-call capacity. The 24-hour ENISA clock +starts from your **awareness** of active exploitation, not from public disclosure. + +--- + +## Beyond this kit (structural CRA obligations) + +The four pillars above cover **software transparency**. A full CRA conformity +assessment also requires structural obligations that **this kit does not +cover** — flag these to your CRA consultant or counsel **before** assuming +SBOMs alone make you ready: + +| Obligation | Article | What it means | +|------------|---------|---------------| +| **EU Authorised Representative** | Art. 18 | Manufacturers established **outside** the EU must appoint a written-mandated representative **inside** the EU before placing a product on the EU market. Either contract a third-party AR service or use an existing EU subsidiary. | +| **Product classification** | Annex III / IV | Determines whether conformity assessment is self-declared (default class) or requires a **Notified Body** (important / critical class). Notified-body queues are already long — if you may need one, get in queue early. | +| **Conformity assessment + CE mark** | Art. 32, 30 | Module A (self-assessment) or external review per classification; CE marking before placing the product on the EU market. | +| **Technical documentation** | Annex VII | Risk assessment, secure-design rationale, vulnerability handling process, support-period commitment — more than the SBOM. | +| **Free security updates** | Art. 13(8) | Minimum 5-year support period for security updates by default (longer if the product's expected lifetime is longer). | +| **Importer / distributor obligations** | Art. 19, 20 | If your product enters the EU via an importer or moves through distributors, additional obligations attach to those parties. | + +These are **legal and structural decisions**, not artefacts you can generate +from source code. wolfSSL ships SBOMs, security-policy templates, and the +narrative in this kit; **you** appoint your EU AR, classify your product, run +your conformity assessment, and produce your declaration of conformity. If +you do not yet have a CRA consultant, engaging one for the +classification + AR questions specifically is usually the highest-leverage +early step. + +**See how wolfSSL Inc. itself answers each of these.** +[`wolfssl-inc-auditor-packet/`](wolfssl-inc-auditor-packet/) holds the +manufacturer-side filings wolfSSL Inc. ships under CRA: Annex III/IV +classification statement, conformity assessment route, declaration of +conformity template, EU Authorised Representative status, support-period +policy, vulnerability-handling process, technical documentation outline, +and CE marking statement. Where decisions are made, they're stated; where +they're in flight (EU AR appointment, public SLA), the gap is named. +Adapt as a template for your own product. + +--- + +## How this maps to the CRA Kit + +| Shortlist pillar | Kit deliverable | +|------------------|-----------------| +| Know your components | Cheat sheet (who provides what), glossary, `auditor-packet/`, generate/validate scripts | +| Secure boot | Out of scope for SBOM files — evaluate **wolfBoot** separately | +| Data in transfer | Configure and document **your** protocol stack; wolfSSL ships crypto libraries, not your full product compliance | +| Vulnerability handling & reporting | Outside scope of SBOM artefacts — see Art. 13/14 obligations above; wolfSSL's own [CVD policy](https://www.wolfssl.com/.well-known/vulnerability-disclosure-policy.txt) and [`security.txt`](https://www.wolfssl.com/.well-known/security.txt) are usable as reference templates | +| Structural CRA obligations (EU AR, Annex III/IV, CE, technical docs, support period) | **Out of scope** for this kit — see "Beyond this kit" section above; engage CRA counsel or consultant | + +**You will leave with (presentation Promise):** + +1. **Who provides what** — [`CRA-Cheat-Sheet.md`](CRA-Cheat-Sheet.md) +2. **Worked example** — [`auditor-packet/`](auditor-packet/) +3. **Helper scripts + AI playbook** — product SBOM, nest wolfSSL, optional bomsh on **Linux CI** + [`SKILL.md`](SKILL.md) + +--- + +## Related wolfSSL products (beyond this kit) + +| Area | Product / doc | +|------|----------------| +| TLS / wolfCrypt | [wolfssl.com](https://www.wolfssl.com/) · upstream SBOM reference: [doc/SBOM.md](https://github.com/wolfSSL/wolfssl/blob/master/doc/SBOM.md) | +| Secure boot | [wolfBoot](https://www.wolfssl.com/products/wolfboot/) | +| SSH | wolfSSH | +| MQTT | wolfMQTT | + +**Questions about this kit:** support@wolfssl.com · **Security reports:** see [`security.txt`](https://www.wolfssl.com/.well-known/security.txt) diff --git a/cra-kit/CRA-Supply-Chain-Glossary.md b/cra-kit/CRA-Supply-Chain-Glossary.md new file mode 100644 index 000000000..c310828bf --- /dev/null +++ b/cra-kit/CRA-Supply-Chain-Glossary.md @@ -0,0 +1,139 @@ +# CRA & Supply Chain Terminology — Customer Cheat Sheet + +One-page reference for teams shipping products that include wolfSSL. +**Not legal advice.** Map obligations to your product class and role with counsel. + +This kit is **self-contained** in [wolfssl-examples `cra-kit/`](https://github.com/wolfSSL/wolfssl-examples/tree/master/cra-kit). +Upstream technical reference for the SBOM feature (flags, output formats, +`SBOM_LICENSE_OVERRIDE`, OmniBOR/Bomsh — requires a wolfSSL source tree with +SBOM support): + +- [SBOM.md](https://github.com/wolfSSL/wolfssl/blob/master/doc/SBOM.md) + +CRA shortlist (4 pillars): [`CRA-Compliance-Shortlist.md`](CRA-Compliance-Shortlist.md) · Who provides what: [`CRA-Cheat-Sheet.md`](CRA-Cheat-Sheet.md) · AI playbook: [`SKILL.md`](SKILL.md) · Worked example: [`auditor-packet/`](auditor-packet/) + +--- + +## The big picture (30 seconds) + +```mermaid +flowchart LR + subgraph you["Your company (manufacturer)"] + PSBOM["Product SBOM\n(all components)"] + end + subgraph wolf["wolfSSL (component)"] + WSBOM["wolfSSL SBOM\n(SPDX + CycloneDX)"] + BOMSH["OmniBOR / bomsh\n(optional)"] + end + PSBOM -->|"references or contains"| WSBOM + WSBOM -.->|"optional deeper proof"| BOMSH +``` + +| Question | Short answer | +|----------|--------------| +| Do we need **our own** SBOM? | **Yes** — for the **whole product** you place on the EU market. | +| Is wolfSSL’s SBOM enough by itself? | **No** (unless you only redistribute wolfSSL). Use it **inside** your product SBOM. | +| Do we need **bomsh**? | **Usually no.** SBOM alone covers most CRA transparency needs; bomsh adds build traceability if you want it. | +| SPDX or CycloneDX? | **Both are fine.** wolfSSL ships both; use whichever your tools expect (many teams keep both). | + +--- + +## Glossary + +| Term | Stands for / means | Plain English | +|------|-------------------|---------------| +| **CRA** | EU **Cyber Resilience Act** | EU law for products with digital elements: inventory, security, vulnerability handling. | +| **SBOM** | **Software Bill of Materials** | Machine-readable “ingredients list” of software in a product (name, version, supplier, license, IDs, relationships). | +| **Product SBOM** | — | **Yours:** every OSS/third-party component in the **shipped product**. | +| **Component SBOM** | — | **wolfSSL’s:** inventory of **wolfSSL only** (`make sbom` or `gen-sbom`). | +| **SPDX** | **Software Package Data Exchange** | A standard **format** for SBOMs (Linux Foundation). Files: `*.spdx.json`, `*.spdx`. | +| **CycloneDX** | (project name) | Another standard **format** for SBOMs (OWASP ecosystem). File: `*.cdx.json`. | +| **NTIA minimum elements** | US NTIA guidance | Checklist of what a “good” SBOM must include (supplier, name, version, unique ID, deps, author, timestamp). CRA practice aligns with this. | +| **PURL** | **Package URL** | Standard ID like `pkg:github/wolfSSL/wolfssl@v5.9.1` — helps tools match components. wolfSSL ships PURLs in both `github` (canonical, resolves in OSV / GHSA / Snyk / Trivy) and CPE forms. | +| **CPE** | **Common Platform Enumeration** | Standard ID like `cpe:2.3:a:wolfssl:wolfssl:…` — used by many vulnerability databases. | +| **VEX** | **Vulnerability Exploitability eXchange** | CycloneDX-side signal: “this CVE does/doesn’t apply to our build.” Often layered on top of SBOM in security tools. | +| **CBOM** | **Cryptographic Bill of Materials** | Inventory of **crypto algorithms/keys/modules** (beyond generic SBOM). Today: `wolfssl:build:*` in CycloneDX; formal CBOM: see [`ROADMAP.md`](ROADMAP.md). | +| **bomsh** | wolfSSL **make** target | Runs **OmniBOR** provenance: proves **how** the library binary was built from sources (**Linux host only**). | +| **OmniBOR** | Omni **Bill of Resources** | Merkle DAG of build inputs/outputs; stored under `omnibor/`. | +| **gitoid** | Git-object-style ID | Hash pointer (`gitoid:blob:sha1:…`) into the OmniBOR graph; appears in `omnibor.*.spdx.json`. | +| **Manufacturer** | CRA role | Entity that places the product on the EU market — **owns** product SBOM and vulnerability process. | +| **Integrator / OEM** | Industry term | You build a device/app containing wolfSSL → you typically act as **manufacturer** for your product. | +| **externalDocumentRefs** | SPDX feature | Your product SPDX **points to** wolfSSL’s SPDX file without copying every file entry. | +| **SOURCE_DATE_EPOCH** | Reproducible builds | Fixed timestamp so two `make sbom` runs produce **byte-identical** SBOMs (useful in CI/attestation). | + +--- + +## CRA structural terms + +These appear throughout the kit's "Beyond this kit" guidance. They are **not** +software-transparency artefacts — they are legal/structural CRA obligations +that no SBOM tool can satisfy. **Not legal advice** — engage CRA counsel. + +| Term | Article / location | Plain English | +|------|--------------------|---------------| +| **EU Authorised Representative** (EU AR) | Art. 18 | Required if the manufacturer is established **outside** the EU. A written-mandated EU-resident legal entity that receives regulator correspondence on the manufacturer's behalf. Either contract a third-party AR service or use an existing EU subsidiary. **Long-lead** — start now. | +| **Notified Body** | — | Independent third-party conformity-assessment organisation. For "important" or "critical" products (Annex III/IV) the conformity assessment must involve a Notified Body. Queues are long — engage early if you may need one. | +| **Annex III** | Annex III | List of **"important"** products with above-baseline cybersecurity risk (e.g. password managers, network management systems, browsers, certain identity-management components). Triggers stricter conformity assessment than the default class. | +| **Annex IV** | Annex IV | List of **"critical"** products (highest-risk class), e.g. hardware security modules, secure-boot devices, smart-meter gateways of certain types. Always requires Notified Body involvement. | +| **Annex VII** | Annex VII | Required contents of the **technical documentation**: risk assessment, secure-design rationale, vulnerability handling process, support-period commitment, SBOM, etc. Much more than the SBOM alone. | +| **Conformity assessment** | Art. 32 | Process to demonstrate the product meets CRA essential requirements. **Module A** self-assessment (default class) or external review by a Notified Body (important/critical). Output is the declaration of conformity. | +| **Module A** | Annex VIII | Self-assessment conformity procedure. The manufacturer alone performs the assessment and signs the declaration. Default for non-Annex III/IV products. | +| **CE marking** | Art. 30 | Visible mark indicating conformity with applicable EU regulations. Affixed to the product (or packaging/documentation) before placing on the EU market. Backed by the declaration of conformity. | +| **Declaration of conformity** | Art. 28 | Manufacturer's signed statement of CRA compliance. Names the product, lists applicable EU acts, identifies the manufacturer (and EU AR if applicable). | +| **Importer** | Art. 19 | EU entity placing a non-EU product on the EU market. Carries CRA obligations parallel to the manufacturer (verify CE mark, retain AR contact, assist regulators). | +| **Distributor** | Art. 20 | Party in the supply chain making the product available on the EU market without altering it. Lighter obligations than importer/manufacturer, but must verify CE mark and assist regulators. | +| **Support period** | Art. 13(2), 13(8) | Minimum duration during which the manufacturer must provide **free security updates**. Default: at least **5 years** (or the product's expected lifetime if longer). Must be declared in the technical documentation. | +| **ENISA** | Art. 14 | EU Agency for Cybersecurity. Recipient of the **24-hour** early-warning report when a vulnerability in your product is **actively exploited**, plus 72-hour update and 14-day final report. | +| **CNA** | (CVE programme) | **CVE Numbering Authority** — organisation authorised to assign CVE IDs within its scope. wolfSSL is a CNA for wolfSSL libraries. | + +For execution detail on these obligations, see [`CRA-Compliance-Shortlist.md`](CRA-Compliance-Shortlist.md) "Beyond this kit (structural CRA obligations)". + +--- + +## wolfSSL artefacts (what we ship) + +| Command | Outputs | Answers | +|---------|---------|---------| +| `make sbom` | `wolfssl-.spdx.json`, `.cdx.json`, `.spdx` | **What** is in wolfSSL (version, license, hashes, config flags). | +| `make bomsh` *(optional)* | `omnibor/`, `omnibor.wolfssl-.spdx.json` | **How** wolfSSL was built (source → binary traceability). | + +Embedded/custom builds: `scripts/gen-sbom` with **your** `user_settings.h` and source list — see kit +[`scripts/generate-embedded-sbom.sh`](scripts/generate-embedded-sbom.sh) and upstream [SBOM.md §1](https://github.com/wolfSSL/wolfssl/blob/master/doc/SBOM.md). + +--- + +## Your checklist + +1. **Product SBOM** in release CI (SPDX and/or CycloneDX). +2. **wolfSSL component** — reference our SBOM (`externalDocumentRefs` / CycloneDX `bom` ref) or copy the package entry; link with `STATIC_LINK` / `DYNAMIC_LINK` / `CONTAINS`. +3. **Match your build** — if `user_settings.h` or source set differs from stock, regenerate wolfSSL’s SBOM for **your** build. +4. **Commercial license** — override GPL in SBOM (`SBOM_LICENSE_OVERRIDE`) or in **your** product SBOM entry for wolfSSL; see upstream [SBOM.md § Commercial Licenses](https://github.com/wolfSSL/wolfssl/blob/master/doc/SBOM.md). +5. **Vulnerabilities** — document your process; wolfSSL disclosure: [`security.txt`](https://www.wolfssl.com/.well-known/security.txt) + [CVD policy](https://www.wolfssl.com/.well-known/vulnerability-disclosure-policy.txt) + [advisories](https://www.wolfssl.com/docs/security-vulnerabilities/). +6. **bomsh** — only if auditors or contracts ask for build-level proof beyond the SBOM (Linux CI). + +--- + +## SPDX vs CycloneDX (same job, different tools) + +| | **SPDX** | **CycloneDX** | +|---|----------|----------------| +| **Typical use** | License compliance, legal review, nested documents | Security scanners, VEX, commercial SBOM platforms | +| **wolfSSL file** | `wolfssl-.spdx.json` | `wolfssl-.cdx.json` | +| **Nesting wolfSSL** | `externalDocumentRefs` + relationship | Component + `externalReferences` type `bom` | + +You do **not** choose “CRA format” — you provide an SBOM that meets NTIA-style expectations; SPDX and CycloneDX are both widely accepted encodings. + +--- + +## Who provides what to an auditor + +| Evidence | Provided by | +|----------|-------------| +| Product SBOM (full inventory) | **Customer** | +| wolfSSL SBOM files | **wolfSSL** (customer integrates or references) | +| OmniBOR / bomsh bundle | **wolfSSL** *(optional)* | +| Vulnerability disclosure & advisories | **wolfSSL** ([security page](https://www.wolfssl.com/docs/security-vulnerabilities/)); **customer** owns product incident process | + +--- + +*wolfSSL · Part of the [CRA Kit](README.md). Questions about this kit: support@wolfssl.com · Security reports: see [`security.txt`](https://www.wolfssl.com/.well-known/security.txt)* diff --git a/cra-kit/README.md b/cra-kit/README.md new file mode 100644 index 000000000..c0fb456b1 --- /dev/null +++ b/cra-kit/README.md @@ -0,0 +1,296 @@ +# wolfSSL CRA Kit + +Example project and scripts for teams that ship products containing wolfSSL and +need **EU Cyber Resilience Act (CRA)**-style **software transparency** artifacts. + +**This kit does not make your product “CRA compliant.”** It shows how to obtain +and nest **wolfSSL component evidence** inside **your** product SBOM and auditor +packet. + +**Not legal advice.** Map obligations to your product class and role with counsel. + +**wolfSSL's own CRA posture.** wolfSSL Inc. is itself a **manufacturer** under +the CRA for libraries it places on the EU market. We publish our own +[`security.txt`](https://www.wolfssl.com/.well-known/security.txt) and +[CVD policy](https://www.wolfssl.com/.well-known/vulnerability-disclosure-policy.txt), +and the manufacturer-side filings we ship under CRA — Annex III/IV +classification, conformity assessment route, declaration of conformity +template, EU Authorised Representative status, support-period policy, +and vulnerability-handling process — are in +[`wolfssl-inc-auditor-packet/`](wolfssl-inc-auditor-packet/). Use them as +reference templates for **your** product. + +| Document | Use | +|----------|-----| +| [`CRA-Compliance-Shortlist.md`](CRA-Compliance-Shortlist.md) | Four pillars towards CRA (components, secure boot, data in transfer, vulnerability handling) | +| [`CRA-Cheat-Sheet.md`](CRA-Cheat-Sheet.md) | **Who provides what** — you vs wolfSSL (print/PDF) | +| [`CRA-Supply-Chain-Glossary.md`](CRA-Supply-Chain-Glossary.md) | Full terminology (**self-contained in this kit**) | +| [`SKILL.md`](SKILL.md) | **AI playbook** — agent checklist, scripts, Cursor install | +| [`ROADMAP.md`](ROADMAP.md) | SBOM / CBOM / VEX / bomsh / CSAF — today vs roadmap | +| [`auditor-packet/`](auditor-packet/) | **Customer-side worked example** — fictional Acme Connect Gateway + wolfSSL SBOM samples | +| [`wolfssl-inc-auditor-packet/`](wolfssl-inc-auditor-packet/) | **Manufacturer-side filings** — what wolfSSL Inc. itself ships under CRA | + +**Self-contained:** all customer-facing docs live in this directory. You only need a +separate **wolfSSL source tree** (with SBOM support) to **regenerate** component SBOMs. + +--- + +## Prerequisites + +- **wolfSSL** source with SBOM support (see [wolfSSL SBOM feature (upstream)](#wolfssl-sbom-feature-upstream) below). + Typical layout: + + ``` + wolf/ + ├── wolfssl/ ← WOLFSSL_DIR (default: ../../wolfssl from here) + └── wolfssl-examples/ + └── cra-kit/ ← you are here + ``` + +- **Python 3** for `scripts/gen-sbom` (embedded path) and `scripts/validate.sh`. +- **`pcpp`** (optional for embedded): install on the **same** interpreter as `python3`: + `python3 -m pip install pcpp`. If `pip install pcpp` used conda but your shell runs + `/usr/local/bin/python3`, use `CRA_PYTHON=python` or rely on the script's automatic + **compiler `-dM -E` fallback** (no pcpp required). +- **Cross-compile note for embedded** (`-dM -E` fallback only): the script defaults to + host `cc`. For target-accurate macros set `CC=arm-none-eabi-gcc` (or your toolchain) + before running so the SBOM reflects target `__ARM_ARCH`, `__SIZEOF_LONG__`, etc. + rather than your laptop's. Skip this if you have `pcpp` installed. +- **Optional schema validators** (used by `validate.sh` if installed): + - [`cyclonedx-cli`](https://github.com/CycloneDX/cyclonedx-cli/releases) for CycloneDX 1.6 schema validation + - [`pyspdxtools`](https://pypi.org/project/spdx-tools/) (`pip install spdx-tools`) for SPDX 2.3 schema validation + +--- + +## All the “BOMs” (today vs roadmap) + +| Name | What it lists | Who owns it | wolfSSL today | Roadmap | +|------|----------------|-------------|---------------|---------| +| **Product SBOM** | Entire shipped product | **You** | — | — | +| **Component SBOM** | wolfSSL only | **wolfSSL** (you integrate) | **Yes** — SPDX 2.3 + CycloneDX 1.6 | Ongoing | +| **VEX** | Does CVE X apply to our build? | **You** | [Advisories](https://www.wolfssl.com/docs/security-vulnerabilities/) (VEX inputs) | Templates / automation | +| **CBOM** | Crypto algorithms / modules | **You**; we **signal** | **Partial** — `wolfssl:build:*` in CycloneDX | Formal `cryptographic-asset` | +| **OmniBOR / bomsh** | How the library binary was built | **wolfSSL** (optional) | **Yes** — Linux **host** only | Same | + +Details: [`ROADMAP.md`](ROADMAP.md). + +**Plain summary:** SBOM = what’s inside. Crypto build properties = what crypto you +compiled in (CBOM direction). bomsh = how the library was built (optional). Product +SBOM = your job. + +--- + +## Which path are you? + +| Profile | Build | Generate wolfSSL SBOM | +|---------|-------|------------------------| +| **A. Linux / server / Yocto / package** | `./configure && make` | `make sbom` in wolfSSL tree | +| **B. Embedded / RTOS / IDE** | `user_settings.h` + your Makefile / Keil / Zephyr / ESP-IDF | `./scripts/generate-embedded-sbom.sh` (kit demo) or upstream `gen-sbom` | +| **C. Commercial license** | Either | `CRA_LICENSE_OVERRIDE=LicenseRef-wolfSSL-Commercial ./scripts/generate-wolfssl-sbom.sh` | + +**Every manufacturer still:** + +1. Maintains a **product SBOM** (all components). +2. **References or copies** wolfSSL’s `.spdx.json` / `.cdx.json` into it. +3. **Regenerates** wolfSSL SBOM when `user_settings.h` or your source list changes. +4. Owns **vulnerability handling** (process + owner). +5. Uses **bomsh** only if an auditor or contract requires build proof — on a **Linux** host. + +--- + +## Quick start + +### 1. Validate the bundled sample (no wolfSSL build required) + +```sh +cd wolfssl-examples/cra-kit +./scripts/validate.sh +``` + +### 2. Regenerate component SBOMs (requires wolfSSL with `make sbom`) + +```sh +export WOLFSSL_DIR=../../wolfssl +./scripts/refresh-samples.sh # make sbom + auto-fix product SPDX checksum +``` + +Or without updating the product stub checksum: + +```sh +./scripts/generate-wolfssl-sbom.sh # default: autotools if Makefile exists +CRA_SBOM_MODE=embedded ./scripts/generate-wolfssl-sbom.sh # rarely used for packet/ +./scripts/generate-embedded-sbom.sh # writes wolfssl-component-embedded/ + +CRA_LICENSE_OVERRIDE=LicenseRef-wolfSSL-Commercial \ + ./scripts/generate-wolfssl-sbom.sh # commercial-license sample +./scripts/make-commercial-sample.sh # derive from pinned GPL samples (no rebuild) +``` + +**Pinned samples** in `auditor-packet/wolfssl-component/` are from **`make sbom`** +(autotools), with a sibling `*.commercial.{cdx,spdx}.json` showing the override pattern. +Embedded regen produces a **different** SBOM (watermarked `wolfssl:sbom:demo=true`) — +see [`auditor-packet/wolfssl-component/SAMPLE-PROVENANCE.md`](auditor-packet/wolfssl-component/SAMPLE-PROVENANCE.md). + +### 3. Study the sample product packet + +Open [`auditor-packet/00-INDEX.md`](auditor-packet/00-INDEX.md) — fictional **Acme +Connect Gateway** shows CycloneDX `bom` external reference and SPDX +`externalDocumentRefs` pointing at wolfSSL’s files. + +### 4. Integrate into your real product SBOM + +Copy the pattern from `product-acme-connect-gateway.*` in [`auditor-packet/`](auditor-packet/) — both +SPDX `externalDocumentRefs` and CycloneDX `bom` external references are shown +end-to-end. For the upstream technical reference on `make sbom` flags, output +formats, and `SBOM_LICENSE_OVERRIDE` for commercial licensees, see +[`wolfssl/doc/SBOM.md`](https://github.com/wolfSSL/wolfssl/blob/master/doc/SBOM.md). + +--- + +## `make bomsh` — Linux host only (simple explanation) + +`make bomsh` is **optional** for most CRA transparency needs. Use it when someone +asks: *“Prove this `libwolfssl.so` was built from these exact sources.”* + +**Why only Linux?** Bomsh runs **bomtrace3** — a patched **strace** that watches +every compiler call during a **full rebuild**. That program is built and tested on +**Linux build machines** (normal `ptrace`, no kernel patches). + +| Your situation | What to do | +|----------------|------------| +| Build on **Linux** | `make bomsh` after `make sbom` in wolfSSL | +| Build on **macOS / Windows** | Run bomsh in **Linux CI**, **WSL2**, or a **container** | +| Ship firmware to **MCU / RTOS** | **Target OS does not matter** — tracing runs on the **build host** | +| **Embedded**, no Linux in house | Use **`gen-sbom`** for SBOM on any OS; skip bomsh unless required | + +The sample packet does **not** ship `omnibor/` (large). See +[`auditor-packet/wolfssl-component/README-bomsh.md`](auditor-packet/wolfssl-component/README-bomsh.md). + +Full detail: [wolfssl/doc/SBOM.md §3](https://github.com/wolfSSL/wolfssl/blob/master/doc/SBOM.md). + +--- + +## wolfSSL SBOM feature (upstream) + +SBOM and optional bomsh provenance are developed in the main **wolfSSL** repository: + +| Item | Location | +|------|----------| +| Generator | `wolfssl/scripts/gen-sbom` | +| Autotools | `make sbom`, `make bomsh` | +| CI | `wolfssl/.github/workflows/sbom.yml` | +| Reference (flags, formats, OmniBOR) | [doc/SBOM.md](https://github.com/wolfSSL/wolfssl/blob/master/doc/SBOM.md) | +| Customer-facing CRA narrative, glossary, auditor packet, AI playbook | this kit (you are here) | + +Use a wolfSSL tree where the `make sbom` (and optionally `make bomsh`) targets are +available before running the scripts here. Once these targets land on `master`, any +recent wolfSSL checkout works; until then, use the integration branch / PR. + +Pinned sample version: see [`VERSION`](VERSION) (default **5.9.1**). + +--- + +## Embedded demo settings + +[`user_settings.h`](user_settings.h) in this directory is included when +`WOLFSSL_USER_SETTINGS` is defined for `./scripts/generate-embedded-sbom.sh`. +Production SBOMs must use **your** project's `user_settings.h` and **your** full +`--srcs` list (every wolfSSL `.c` you compile). + +--- + +## Presentation + +15-minute co-sponsor slide track: [`presentations/SLIDE-OUTLINE.md`](presentations/SLIDE-OUTLINE.md). + +Handouts: [`CRA-Cheat-Sheet.md`](CRA-Cheat-Sheet.md) + [`CRA-Supply-Chain-Glossary.md`](CRA-Supply-Chain-Glossary.md); +point AI users at [`SKILL.md`](SKILL.md) (copy to `.cursor/skills/wolfssl-cra-kit/`). + +--- + +## Agent skill + +[`SKILL.md`](SKILL.md) is a customer deliverable (not internal-only) — see +[`presentations/SLIDE-OUTLINE.md`](presentations/SLIDE-OUTLINE.md). Copy to +`.cursor/skills/wolfssl-cra-kit/` for Cursor. + +--- + +## FAQ + +**Do we need our own SBOM?** +Yes — for the whole product you place on the EU market. + +**Is wolfSSL’s SBOM enough alone?** +No — nest or reference it in your product SBOM (see `auditor-packet/`). + +**SPDX or CycloneDX?** +wolfSSL ships both; use what your tools expect. + +**Do we need bomsh for CRA?** +Usually no. SBOM alone covers most transparency asks. + +**What about CBOM?** +Many RFQs ask for crypto inventory. Today: `wolfssl:build:*` properties in +CycloneDX from your real config. Formal CycloneDX CBOM: **roadmap** — see +[`ROADMAP.md`](ROADMAP.md). + +**FIPS builds?** +The SBOM generator does not change validated module code; your FIPS boundary +documentation remains separate. + +**What does this kit NOT cover?** +Software transparency only. **Structural** CRA obligations are out of scope: +appointing an EU Authorised Representative (Art. 18), product classification +(Annex III/IV), conformity assessment + CE marking, full technical +documentation per Annex VII, the support-period commitment, and importer / +distributor obligations. See [`CRA-Compliance-Shortlist.md`](CRA-Compliance-Shortlist.md) +"Beyond this kit" for the list. Engage CRA counsel or consultant — these are +legal/structural decisions, not artefacts. + +**Are we outside the EU? (US / Asia / etc.)** +Then you almost certainly need an **EU Authorised Representative** (Art. 18) +appointed in writing **before** placing your product on the EU market. Either +contract a third-party AR service or use an existing EU subsidiary. This is a +long-lead item — start now, do not wait for September 2026. + +--- + +## Further reading + +### OpenSSF guidance + +- [CRA Brief Guide for OSS Developers](https://best.openssf.org/CRA-Brief-Guide-for-OSS-Developers.html) + — When the CRA applies to open source projects and what obligations fall on + manufacturers integrating OSS components into commercial products. +- [SBOM in Compliance](https://sbom-catalog.openssf.org/sbom-compliance.html) + — OpenSSF SBOM Everywhere SIG survey of the global regulatory landscape: + CRA, NTIA minimum elements, US EO 14028, Germany TR-03183, others. +- [Getting Started with SBOMs](https://sbom-catalog.openssf.org/getting-started) + — OpenSSF guidance on SBOM generation approaches (build-integrated vs. + separate tooling), phase selection, publication. wolfSSL's `make sbom` + follows the build-integrated approach. +- [OpenSSF CRA Policy Hub](https://openssf.org/category/policy/cra/) + — Ongoing OpenSSF coverage of CRA developments and community responses. +- [SBOM Everywhere Wiki](https://sbom-catalog.openssf.org/) — tooling + catalog, working group resources, naming conventions, cross-format + guidance for SPDX and CycloneDX. + +### Standards + +- SPDX 2.3 specification: +- CycloneDX 1.6 specification: +- NTIA minimum elements for an SBOM: + +- RFC 9116 (`security.txt`): + +--- + +## Support + +Questions about this kit: **support@wolfssl.com** + +Security reports: see [`security.txt`](https://www.wolfssl.com/.well-known/security.txt) +and our [Coordinated Vulnerability Disclosure policy](https://www.wolfssl.com/.well-known/vulnerability-disclosure-policy.txt). +Do **not** send vulnerability details to `support@` — use the security contact +listed in `security.txt`. diff --git a/cra-kit/ROADMAP.md b/cra-kit/ROADMAP.md new file mode 100644 index 000000000..deaa99490 --- /dev/null +++ b/cra-kit/ROADMAP.md @@ -0,0 +1,43 @@ +# Supply-chain artefacts — today vs roadmap + +Honest status for customer conversations. This is **not** a commitment schedule. + +| Capability | Status | What you do today | +|--------------|--------|-------------------| +| **SBOM** (SPDX 2.3 + CycloneDX 1.6) | **Available** | `make sbom` or `scripts/gen-sbom` | +| **Config-accurate build properties** | **Available** | Read `wolfssl:build:*` in `.cdx.json` | +| **Embedded source-merkle checksum** | **Available** | `gen-sbom` with `--srcs` (no `libwolfssl.a` required) | +| **Commercial license in SBOM** | **Available** | `CRA_LICENSE_OVERRIDE=LicenseRef-wolfSSL-Commercial ./scripts/generate-wolfssl-sbom.sh` (or `make-commercial-sample.sh` to derive from pinned GPL samples) | +| **Reproducible SBOM timestamps** | **Available** | `SOURCE_DATE_EPOCH` | +| **OmniBOR / `make bomsh`** | **Available** | Linux **build host** only; optional for CRA | +| **`pkg:github` PURL** | **Available** | Auto-canonicalised by `generate-wolfssl-sbom.sh` post-process; resolves in OSV / GHSA / Snyk / Trivy without per-vendor mapping | +| **Cryptographic-asset draft** (CycloneDX 1.6) | **Draft sample** | Hand-rolled `wolfssl-.cbom-draft.cdx.json` alongside SBOM (4–6 starter entries); upstream automation: roadmap | +| **Formal CBOM** (`cryptographic-asset` profile, all primitives) | **Roadmap** | Use draft sample + `wolfssl:build:*` properties | +| **VEX templates / automation** | **Roadmap** | Your scanner + wolfSSL [advisories](https://www.wolfssl.com/docs/security-vulnerabilities/) | +| **CSAF 2.0 advisory feed** (`/.well-known/csaf/`) | **Roadmap** | Human-readable [advisories](https://www.wolfssl.com/docs/security-vulnerabilities/) today; CSAF 2.0 publication is on the roadmap (BSI's CRA reference architecture assumes CSAF) | +| **Signed SBOMs** (in-toto / cosign / Sigstore) | **Roadmap** | Unsigned today; signing is conspicuous-by-absence for a crypto vendor and is on the roadmap | +| **SBOM publication channel** | **Roadmap** | Per-release artefacts on GitHub Releases (proposed); `wolfssl.com/sbom/` (proposed); discovery via PURL is the long-term goal | +| **Product SBOM tool** | **Out of scope** | Your BOM platform or manual merge | + +Upstream implementation detail: [wolfssl/doc/SBOM.md](https://github.com/wolfSSL/wolfssl/blob/master/doc/SBOM.md). + +--- + +## Vulnerability-handling roadmap (Pillar 4) + +The kit's vulnerability-handling pillar is the only **ongoing** CRA obligation. +Status of wolfSSL Inc.'s own filings is tracked here so customers can see what +they're actually inheriting when they reference us as a component supplier. + +| Capability | Status | Notes | +|------------|--------|-------| +| `security.txt` (RFC 9116) | **Available** | [`/.well-known/security.txt`](https://www.wolfssl.com/.well-known/security.txt) | +| Coordinated Vulnerability Disclosure policy | **Available** | [`/.well-known/vulnerability-disclosure-policy.txt`](https://www.wolfssl.com/.well-known/vulnerability-disclosure-policy.txt) | +| CNA status | **Available** | wolfSSL is a CVE Numbering Authority | +| Public SLA (24h ack / 72h triage) | **Pending leadership approval** | Will be added to CVD policy once approved | +| 24h ENISA reporting (Art. 14) runbook | **In progress** | Owner assignment pending; on-call rotation TBD | +| EU Authorised Representative (Art. 18) | **In progress** | wolfSSL Inc. is US-established; AR appointment underway | +| CSAF 2.0 advisory feed | **Roadmap** | See above | + +See [`wolfssl-inc-auditor-packet/`](wolfssl-inc-auditor-packet/) for the manufacturer-side +filings wolfSSL Inc. ships under CRA. diff --git a/cra-kit/SKILL.md b/cra-kit/SKILL.md new file mode 100644 index 000000000..06bdba6f0 --- /dev/null +++ b/cra-kit/SKILL.md @@ -0,0 +1,136 @@ +--- +name: wolfssl-cra-kit +description: >- + wolfSSL CRA Kit playbook: who-provides-what cheat sheet, full glossary, + auditor-packet sample, generate/validate/refresh scripts for product SBOM + + nested wolfSSL SBOM, bomsh Linux-only, vulnerability handling (CVD policy + + security.txt), and pointers to structural CRA obligations (EU Authorised + Representative Art. 18, Annex III/IV product classification, conformity + assessment, CE mark) that this kit does NOT cover. Use with Cursor, Claude, + or any agent for EU CRA software transparency (make sbom, SPDX, CycloneDX). +--- + +# wolfSSL CRA Kit — AI playbook + +Use this file with **Cursor**, **Claude Code**, **Copilot**, or any coding agent +to drive the kit's scripts and narrative without re-explaining CRA terms. + +**Not legal advice.** Never claim “CRA compliant.” **Product SBOM** is always yours; +wolfSSL ships **component** evidence only. + +wolfSSL Inc. is itself a manufacturer under CRA for libraries it places on the +EU market — see our [`security.txt`](https://www.wolfssl.com/.well-known/security.txt), +[CVD policy](https://www.wolfssl.com/.well-known/vulnerability-disclosure-policy.txt), +and the [`wolfssl-inc-auditor-packet/`](wolfssl-inc-auditor-packet/) (manufacturer-side +filings: classification, conformity assessment, declaration of conformity template, +EU AR status, support-period, vulnerability-handling process) as reference templates +for the customer's own CRA artefacts. + +--- + +## What you leave with (matches the presentation) + +| Deliverable | File / folder | +|-------------|----------------| +| **CRA shortlist** (4 pillars: components, secure boot, data in transfer, vulnerability handling) | [CRA-Compliance-Shortlist.md](CRA-Compliance-Shortlist.md) | +| **Who provides what** (you vs wolfSSL) | [CRA-Cheat-Sheet.md](CRA-Cheat-Sheet.md) | +| **Full glossary** (SBOM, CBOM, bomsh, …) | [CRA-Supply-Chain-Glossary.md](CRA-Supply-Chain-Glossary.md) | +| **Worked example (customer-side)** | [auditor-packet/](auditor-packet/) — fictional Acme Connect Gateway | +| **Manufacturer-side filings (wolfSSL Inc.)** | [wolfssl-inc-auditor-packet/](wolfssl-inc-auditor-packet/) — classification, DoC template, EU AR status, etc. | +| **Scripts + agent checklist** | This SKILL — below | + +--- + +## Install (Cursor) + +```bash +mkdir -p .cursor/skills/wolfssl-cra-kit +cp wolfssl-examples/cra-kit/SKILL.md .cursor/skills/wolfssl-cra-kit/SKILL.md +``` + +Point the agent at `wolfssl-examples/cra-kit/` (clone or monorepo path). +Set `WOLFSSL_DIR` to your wolfSSL source tree when regenerating SBOMs. + +**Other tools:** paste this file into the system prompt, or `@`-mention the kit README. + +--- + +## Agent checklist + +**Before starting**, confirm with the customer (do not assume): + +- Where is the customer **established** (US / EU / other)? If outside the EU, flag the **EU Authorised Representative** requirement (Art. 18) — long-lead item, start now. +- What is the **product classification** under Annex III/IV? Self-declared (default class) or Notified Body required (important / critical)? Flag if unknown — Notified Body queues are long. +- Is the customer's CRA work **on track for 11 Sep 2026** (Art. 14 reporting wave) and **11 Dec 2027** (full applicability)? If structural items are open, SBOM work alone won't make them ready. + +Then run the SBOM execution checklist: + +1. **Component SBOM** + - `cd wolfssl-examples/cra-kit` + - `WOLFSSL_DIR=/path/to/wolfssl ./scripts/generate-wolfssl-sbom.sh` + - Or in wolfSSL: `make sbom` (needs `pip install spdx-tools`) + +2. **Product SBOM** + - Open `auditor-packet/product-acme-connect-gateway.{cdx,spdx}.json` + - Mirror how wolfSSL is referenced/nested for **your** product name and version + - Embedded builds: `CRA_SBOM_MODE=embedded ./scripts/generate-embedded-sbom.sh` + your `user_settings.h` + +3. **Validate without rebuilding wolfSSL** + - `./scripts/validate.sh` + +4. **Refresh pinned samples** (maintainers / after wolfSSL release) + - `WOLFSSL_DIR=... ./scripts/refresh-samples.sh` + +5. **Optional — bomsh** + - Only if contract/auditor requires; **Linux build host** only (`make bomsh` in wolfSSL tree) + - Not in the sample auditor packet by default + +--- + +## Scope rules (tell the agent) + +- **Product SBOM** = customer owns entire shipped product. +- **Component SBOM** = wolfSSL only; nest via SPDX `externalDocumentRefs` or CycloneDX `bom` ref. +- **CBOM** = partial today (`wolfssl:build:*`); do not claim full CycloneDX CBOM profile. +- **VEX** = customer + scanner; wolfSSL provides advisories, not VEX files. +- **bomsh** = optional provenance; not required for most CRA transparency asks. +- **Vulnerability handling (Art. 13/14)** = customer publishes their own CVD policy + `security.txt`, runs on-call, files 24h ENISA reports for their product; wolfSSL provides reference templates and handles ENISA reporting only for libraries placed on the EU market by wolfSSL Inc. +- **Structural CRA (out of scope for this kit)** = EU Authorised Representative (Art. 18 — required if customer is outside the EU), Annex III/IV classification (determines self-cert vs Notified Body), conformity assessment + CE mark (Art. 32, 30), technical documentation (Annex VII), support-period commitment (Art. 13(8), 5+ years default). When a customer asks "are we ready?", surface these — SBOMs alone are not enough. Recommend engaging CRA counsel or consultant. + +--- + +## Scripts + +| Script | Purpose | +|--------|---------| +| `scripts/validate.sh` | JSON + SPDX checksum on sample packet | +| `scripts/refresh-samples.sh` | `make sbom` + patch product SPDX checksum | +| `scripts/generate-wolfssl-sbom.sh` | `CRA_SBOM_MODE=autotools\|embedded` | +| `scripts/generate-embedded-sbom.sh` | → `auditor-packet/wolfssl-component-embedded/` | + +Embedded demo: `user_settings.h` + `WOLFSSL_USER_SETTINGS`. + +--- + +## Sample paths + +- Product: `auditor-packet/product-acme-connect-gateway.{spdx,cdx}.json` +- Component: `auditor-packet/wolfssl-component/wolfssl-5.9.1.*` +- Embedded (optional): `auditor-packet/wolfssl-component-embedded/` + +--- + +## Example prompts + +- “Walk me through nesting wolfSSL’s CycloneDX SBOM into our product SBOM using `auditor-packet/` as a template.” +- “Run `validate.sh` and fix any checksum mismatch after I regenerated the component SBOM.” +- “Generate an embedded SBOM with our `user_settings.h` and list which algorithms appear in `wolfssl:build:*`.” +- “Do we need bomsh for CRA? When would we run it on Linux CI only?” +- “We're a US company shipping into the EU — what CRA structural items do we need beyond the SBOM?” +- “What's the difference between Annex III and Annex IV classification, and how does it affect our conformity assessment?” + +--- + +## Upstream docs (wolfSSL repo) + +- [doc/SBOM.md](https://github.com/wolfSSL/wolfssl/blob/master/doc/SBOM.md) — SBOM/Bomsh feature reference (flags, formats, commercial license override, OmniBOR) diff --git a/cra-kit/VERSION b/cra-kit/VERSION new file mode 100644 index 000000000..b64a5e97f --- /dev/null +++ b/cra-kit/VERSION @@ -0,0 +1,3 @@ +# Pinned wolfSSL SBOM samples under auditor-packet/wolfssl-component/ +# Regenerate with: ./scripts/generate-wolfssl-sbom.sh && ./scripts/refresh-samples.sh +WOLFSSL_VERSION=5.9.1 diff --git a/cra-kit/auditor-packet/00-INDEX.md b/cra-kit/auditor-packet/00-INDEX.md new file mode 100644 index 000000000..96ef38605 --- /dev/null +++ b/cra-kit/auditor-packet/00-INDEX.md @@ -0,0 +1,37 @@ +# Auditor packet index (fictional Acme Connect Gateway) + +Example of what a **manufacturer** might bundle alongside wolfSSL component +artefacts. **Not legal advice** — adapt to your product and counsel. + +| File | Role | +|------|------| +| `product-acme-connect-gateway.cdx.json` | **Your** product SBOM (CycloneDX) — references wolfSSL | +| `product-acme-connect-gateway.spdx.json` | **Your** product SBOM (SPDX) — `externalDocumentRefs` to wolfSSL | +| `wolfssl-component/wolfssl-5.9.1.cdx.json` | wolfSSL component SBOM — **autotools / make sbom** sample (GPL) | +| `wolfssl-component/wolfssl-5.9.1.spdx.json` | wolfSSL component SBOM (SPDX, GPL) | +| `wolfssl-component/wolfssl-5.9.1.commercial.cdx.json` | wolfSSL component SBOM with commercial license override | +| `wolfssl-component/wolfssl-5.9.1.commercial.spdx.json` | wolfSSL component SBOM (SPDX) with commercial license override | +| `wolfssl-component/wolfssl-5.9.1.cbom-draft.cdx.json` | Hand-rolled cryptographic-asset draft (CycloneDX 1.6 CBOM profile) | +| `wolfssl-component/SAMPLE-PROVENANCE.md` | How the pinned autotools samples were produced | +| `wolfssl-component/omnibor.wolfssl-5.9.1.spdx.json.sample` | Truncated OmniBOR / bomsh provenance sample | +| `wolfssl-component-embedded/` | Optional embedded `gen-sbom` output (generated locally; gitignored) | +| `wolfssl-component/README-bomsh.md` | Optional OmniBOR — not included by default | + +Also provide: your vulnerability process, release notes, and the upstream +wolfSSL disclosure context — [`security.txt`](https://www.wolfssl.com/.well-known/security.txt), +[CVD policy](https://www.wolfssl.com/.well-known/vulnerability-disclosure-policy.txt), +and [advisories](https://www.wolfssl.com/docs/security-vulnerabilities/). + +**This packet shows the software-transparency artefacts only.** A complete +CRA conformity packet for a real product also includes: + +- Declaration of conformity (Art. 28) +- Technical documentation per Annex VII (risk assessment, design info, support-period commitment, vulnerability handling process) +- Proof of conformity assessment (self-declared per Art. 32 Module A, or Notified Body certificate per product class) +- Identity of the EU Authorised Representative (Art. 18) if the manufacturer is established outside the EU +- CE marking declaration + +See [`../CRA-Compliance-Shortlist.md`](../CRA-Compliance-Shortlist.md) +"Beyond this kit" for the structural obligations not covered by SBOMs. + +**Regenerate autotools samples + product checksum:** `./scripts/refresh-samples.sh` diff --git a/cra-kit/auditor-packet/README-auditor-packet.md b/cra-kit/auditor-packet/README-auditor-packet.md new file mode 100644 index 000000000..fd3e8e261 --- /dev/null +++ b/cra-kit/auditor-packet/README-auditor-packet.md @@ -0,0 +1,9 @@ +# Sample auditor packet + +This directory is a **teaching example** only. **Acme Industries** and +**acme-connect-gateway** are fictional. + +It shows how a **product SBOM** references wolfSSL’s **component SBOM** in +both CycloneDX and SPDX forms. + +See [`00-INDEX.md`](00-INDEX.md) for the file list. diff --git a/cra-kit/auditor-packet/product-acme-connect-gateway.cdx.json b/cra-kit/auditor-packet/product-acme-connect-gateway.cdx.json new file mode 100644 index 000000000..796567689 --- /dev/null +++ b/cra-kit/auditor-packet/product-acme-connect-gateway.cdx.json @@ -0,0 +1,63 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.6", + "serialNumber": "urn:uuid:c7a4f9b2-8e1d-4a3f-b5c6-d2e8f4a7b9c1", + "version": 1, + "metadata": { + "timestamp": "2026-05-18T12:00:00Z", + "component": { + "type": "firmware", + "bom-ref": "acme-connect-gateway-1.0.0", + "name": "acme-connect-gateway", + "version": "1.0.0", + "supplier": { + "name": "Acme Industries (fictional example)" + } + } + }, + "components": [ + { + "type": "library", + "bom-ref": "wolfssl-5.9.1", + "name": "wolfssl", + "version": "5.9.1", + "supplier": { + "name": "wolfSSL Inc." + }, + "purl": "pkg:github/wolfSSL/wolfssl@v5.9.1", + "cpe": "cpe:2.3:a:wolfssl:wolfssl:5.9.1:*:*:*:*:*:*:*", + "externalReferences": [ + { + "type": "bom", + "url": "file:wolfssl-component/wolfssl-5.9.1.cdx.json", + "comment": "Component SBOM from wolfSSL; regenerate with scripts/generate-wolfssl-sbom.sh", + "hashes": [ + { + "alg": "SHA-256", + "content": "265cd1575f7a350295ba1414494f2cc93bb895223a9732dcfb231bcecb6d3bbd" + } + ] + } + ] + } + ], + "dependencies": [ + { + "ref": "acme-connect-gateway-1.0.0", + "dependsOn": [ + "wolfssl-5.9.1" + ] + }, + { + "ref": "wolfssl-5.9.1", + "dependsOn": [] + } + ], + "properties": [ + { + "name": "wolfssl:sample:component-deps", + "value": "wolfSSL has no transitive runtime library dependencies; the host CRT is the only build-time requirement and is excluded per NTIA SBOM practice." + } + ] +} diff --git a/cra-kit/auditor-packet/product-acme-connect-gateway.spdx.json b/cra-kit/auditor-packet/product-acme-connect-gateway.spdx.json new file mode 100644 index 000000000..d67a451af --- /dev/null +++ b/cra-kit/auditor-packet/product-acme-connect-gateway.spdx.json @@ -0,0 +1,46 @@ +{ + "spdxVersion": "SPDX-2.3", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "acme-connect-gateway-1.0.0", + "documentNamespace": "urn:uuid:8d3c2f9e-6b4a-4d7c-9f1e-a5b8c0d2e4f6", + "creationInfo": { + "creators": [ + "Organization: Acme Industries (fictional example)" + ], + "created": "2026-05-18T12:00:00Z" + }, + "externalDocumentRefs": [ + { + "externalDocumentId": "DocumentRef-wolfssl", + "spdxDocument": "file:wolfssl-component/wolfssl-5.9.1.spdx.json", + "checksum": { + "algorithm": "SHA256", + "checksumValue": "36fdc0c8a192a0fadc4c5024ff75ecee3a56dd8a431dfb25bfa8afcf467cfdef" + } + } + ], + "packages": [ + { + "SPDXID": "SPDXRef-Package-Product", + "name": "acme-connect-gateway", + "versionInfo": "1.0.0", + "supplier": "Organization: Acme Industries", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false + } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relatedSpdxElement": "SPDXRef-Package-Product", + "relationshipType": "DESCRIBES" + }, + { + "spdxElementId": "SPDXRef-Package-Product", + "relatedSpdxElement": "DocumentRef-wolfssl:SPDXRef-Package-wolfssl", + "relationshipType": "STATIC_LINK", + "comment": "Fictional embedded firmware links wolfSSL statically; use DYNAMIC_LINK for .so" + } + ] +} diff --git a/cra-kit/auditor-packet/wolfssl-component-embedded/.gitignore b/cra-kit/auditor-packet/wolfssl-component-embedded/.gitignore new file mode 100644 index 000000000..30803144e --- /dev/null +++ b/cra-kit/auditor-packet/wolfssl-component-embedded/.gitignore @@ -0,0 +1,3 @@ +wolfssl-*.cdx.json +wolfssl-*.spdx.json +wolfssl-*.spdx diff --git a/cra-kit/auditor-packet/wolfssl-component-embedded/README.md b/cra-kit/auditor-packet/wolfssl-component-embedded/README.md new file mode 100644 index 000000000..1ac918512 --- /dev/null +++ b/cra-kit/auditor-packet/wolfssl-component-embedded/README.md @@ -0,0 +1,23 @@ +# Embedded component SBOM (optional sample) + +This directory's `wolfssl-*.{cdx,spdx}.json` outputs are **gitignored** — generate +them locally with the embedded path. Only this README is committed. + +```sh +export WOLFSSL_DIR=../../wolfssl # wolfSSL tree with scripts/gen-sbom +python3 -m pip install pcpp # same python3 as in your PATH (see README) +./scripts/generate-embedded-sbom.sh +``` + +If pcpp is not on your `python3`, the script falls back to `cc -dM -E` and `--options-h` +(no extra install). For cross builds, set `CC=arm-none-eabi-gcc` (or your target +compiler) so the fallback reflects target macros, not the host's. + +Uses [`../../user_settings.h`](../../user_settings.h) via `WOLFSSL_USER_SETTINGS` and a +**demo** `--srcs` list (see `scripts/generate-wolfssl-sbom.sh`). Production firmware +must pass **your** `user_settings.h` and **every** wolfSSL `.c` file you compile. +Embedded outputs are watermarked `wolfssl:sbom:demo=true` so an auditor can tell at +a glance that they came from the kit's demo `--srcs` list and not a real build. + +Outputs differ from [`../wolfssl-component/`](../wolfssl-component/) (autotools / +`make sbom`). Compare `wolfssl:sbom:hash-kind` in the CycloneDX files. diff --git a/cra-kit/auditor-packet/wolfssl-component-embedded/wolfssl-5.9.1.cdx.json b/cra-kit/auditor-packet/wolfssl-component-embedded/wolfssl-5.9.1.cdx.json new file mode 100644 index 000000000..a0dcd3e7e --- /dev/null +++ b/cra-kit/auditor-packet/wolfssl-component-embedded/wolfssl-5.9.1.cdx.json @@ -0,0 +1,328 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.6", + "serialNumber": "urn:uuid:bbd8fa2c-814a-5921-b121-e872fe1b42a2", + "version": 1, + "metadata": { + "timestamp": "2026-05-18T11:56:58Z", + "tools": { + "components": [ + { + "type": "application", + "author": "wolfSSL Inc.", + "name": "wolfssl-sbom-gen", + "version": "1.0" + } + ] + }, + "component": { + "bom-ref": "721ce791-b9c8-5edf-a9d2-ef3b0539043b", + "type": "library", + "supplier": { + "name": "wolfSSL Inc." + }, + "name": "wolfssl", + "version": "5.9.1", + "licenses": [ + { + "license": { + "id": "GPL-3.0-only" + } + } + ], + "copyright": "Copyright (C) 2006-2026 wolfSSL Inc.", + "cpe": "cpe:2.3:a:wolfssl:wolfssl:5.9.1:*:*:*:*:*:*:*", + "purl": "pkg:github/wolfSSL/wolfssl@v5.9.1", + "hashes": [ + { + "alg": "SHA-256", + "content": "3538981aad331ad5cd160abd2b51ce0a5fa1a58b3c51f990e08ca91bb44627a0" + } + ], + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/wolfSSL/wolfssl" + } + ], + "properties": [ + { + "name": "wolfssl:build:AES_MAX_KEY_SIZE", + "value": "256" + }, + { + "name": "wolfssl:build:DH_MAX_SIZE", + "value": "WC_BITS_FULL_BYTES(SP_INT_BITS)" + }, + { + "name": "wolfssl:build:ECC_DECODE_EXTRA", + "value": "1" + }, + { + "name": "wolfssl:build:ECC_MIN_KEY_SZ", + "value": "224" + }, + { + "name": "wolfssl:build:FLASH_QUALIFIER", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_AESGCM", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_AES_CBC", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_AES_DECRYPT", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_ALL_CURVES", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_ECC", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_ECC_CHECK_KEY", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_ECC_DHE", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_ECC_KEY_EXPORT", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_ECC_KEY_IMPORT", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_ECC_SIGN", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_ECC_VERIFY", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_PBKDF1", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_PBKDF2", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_PKCS12", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_PKCS8", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_PUBLIC_FFDHE", + "value": "1" + }, + { + "name": "wolfssl:build:LIBWOLFSSL_CMAKE_OUTPUT", + "value": "\"\"" + }, + { + "name": "wolfssl:build:MIN_FFDHE_BITS", + "value": "0" + }, + { + "name": "wolfssl:build:MIN_FFDHE_FP_MAX_BITS", + "value": "(MIN_FFDHE_BITS * 2)" + }, + { + "name": "wolfssl:build:NO_OLD_TLS", + "value": "1" + }, + { + "name": "wolfssl:build:NO_PSK", + "value": "1" + }, + { + "name": "wolfssl:build:NO_RC4", + "value": "1" + }, + { + "name": "wolfssl:build:NO_XSTREAM_ALIGN", + "value": "1" + }, + { + "name": "wolfssl:build:RSA_DECODE_EXTRA", + "value": "1" + }, + { + "name": "wolfssl:build:USE_WOLFSSL_MEMORY", + "value": "1" + }, + { + "name": "wolfssl:build:WC_ASYNC_DEV_SIZE", + "value": "0" + }, + { + "name": "wolfssl:build:WC_TEST_NO_ECC_SIGN_VERIFY_ZERO_DIGEST", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_ABI", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_AES_128", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_AES_192", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_AES_256", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_ALERT_COUNT_MAX", + "value": "5" + }, + { + "name": "wolfssl:build:WOLFSSL_API", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_ASN_TEMPLATE", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_ASYNC_IO", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_BASE64_DECODE", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_FIPS_VERSION2_CODE", + "value": "WOLFSSL_FIPS_VERSION_CODE" + }, + { + "name": "wolfssl:build:WOLFSSL_FIPS_VERSION_CODE", + "value": "WOLFSSL_MAKE_FIPS_VERSION3(0,0,0)" + }, + { + "name": "wolfssl:build:WOLFSSL_GENERAL_ALIGNMENT", + "value": "0" + }, + { + "name": "wolfssl:build:WOLFSSL_HAVE_PRF", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_LOCAL", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_MAX_EMPTY_RECORDS", + "value": "32" + }, + { + "name": "wolfssl:build:WOLFSSL_MIN_AUTH_TAG_SZ", + "value": "12" + }, + { + "name": "wolfssl:build:WOLFSSL_PEM_TO_DER", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SMALL_STACK_STATIC", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SP_ADD_D", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SP_INVMOD", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SP_INVMOD_MONT_CT", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SP_MATH_ALL", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SP_MUL_D", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SP_NO_DYN_STACK", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SP_PRIME_GEN", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SP_READ_RADIX_10", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SP_READ_RADIX_16", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SP_SUB_D", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_TEST_VIS", + "value": "WOLFSSL_API WC_DEPRECATED(\"internal use only\")" + }, + { + "name": "wolfssl:build:WOLFSSL_TLS13", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_USER_SETTINGS", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_W64_WRAPPER", + "value": "1" + }, + { + "name": "wolfssl:build:XGEN_ALIGN", + "value": "1" + }, + { + "name": "wolfssl:sbom:hash-kind", + "value": "source-merkle-omnibor" + }, + { + "name": "wolfssl:sbom:source-set", + "value": "aes.c,ecc.c,keys.c,random.c,sha.c,sha256.c,tls.c,tls13.c,wc_port.c" + } + ] + } + }, + "components": [], + "dependencies": [ + { + "ref": "721ce791-b9c8-5edf-a9d2-ef3b0539043b", + "dependsOn": [] + } + ] +} diff --git a/cra-kit/auditor-packet/wolfssl-component-embedded/wolfssl-5.9.1.spdx.json b/cra-kit/auditor-packet/wolfssl-component-embedded/wolfssl-5.9.1.spdx.json new file mode 100644 index 000000000..af6eb3f78 --- /dev/null +++ b/cra-kit/auditor-packet/wolfssl-component-embedded/wolfssl-5.9.1.spdx.json @@ -0,0 +1,53 @@ +{ + "spdxVersion": "SPDX-2.3", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "wolfssl-5.9.1", + "documentNamespace": "urn:uuid:480ff203-f994-5b71-b858-0653e74e422a", + "creationInfo": { + "creators": [ + "Organization: wolfSSL Inc.", + "Tool: wolfssl-sbom-gen-1.0" + ], + "created": "2026-05-18T11:56:58Z" + }, + "packages": [ + { + "SPDXID": "SPDXRef-Package-wolfssl", + "name": "wolfssl", + "versionInfo": "5.9.1", + "supplier": "Organization: wolfSSL Inc.", + "downloadLocation": "https://github.com/wolfSSL/wolfssl", + "filesAnalyzed": false, + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "3538981aad331ad5cd160abd2b51ce0a5fa1a58b3c51f990e08ca91bb44627a0" + } + ], + "licenseConcluded": "GPL-3.0-only", + "licenseDeclared": "GPL-3.0-only", + "copyrightText": "Copyright (C) 2006-2026 wolfSSL Inc.", + "comment": "Build configuration defines: AES_MAX_KEY_SIZE, DH_MAX_SIZE, ECC_DECODE_EXTRA, ECC_MIN_KEY_SZ, FLASH_QUALIFIER, HAVE_AESGCM, HAVE_AES_CBC, HAVE_AES_DECRYPT, HAVE_ALL_CURVES, HAVE_ECC, HAVE_ECC_CHECK_KEY, HAVE_ECC_DHE, HAVE_ECC_KEY_EXPORT, HAVE_ECC_KEY_IMPORT, HAVE_ECC_SIGN, HAVE_ECC_VERIFY, HAVE_PBKDF1, HAVE_PBKDF2, HAVE_PKCS12, HAVE_PKCS8, HAVE_PUBLIC_FFDHE, LIBWOLFSSL_CMAKE_OUTPUT, MIN_FFDHE_BITS, MIN_FFDHE_FP_MAX_BITS, NO_OLD_TLS, NO_PSK, NO_RC4, NO_XSTREAM_ALIGN, RSA_DECODE_EXTRA, USE_WOLFSSL_MEMORY, WC_ASYNC_DEV_SIZE, WC_TEST_NO_ECC_SIGN_VERIFY_ZERO_DIGEST, WOLFSSL_ABI, WOLFSSL_AES_128, WOLFSSL_AES_192, WOLFSSL_AES_256, WOLFSSL_ALERT_COUNT_MAX, WOLFSSL_API, WOLFSSL_ASN_TEMPLATE, WOLFSSL_ASYNC_IO, WOLFSSL_BASE64_DECODE, WOLFSSL_FIPS_VERSION2_CODE, WOLFSSL_FIPS_VERSION_CODE, WOLFSSL_GENERAL_ALIGNMENT, WOLFSSL_HAVE_PRF, WOLFSSL_LOCAL, WOLFSSL_MAX_EMPTY_RECORDS, WOLFSSL_MIN_AUTH_TAG_SZ, WOLFSSL_PEM_TO_DER, WOLFSSL_SMALL_STACK_STATIC, WOLFSSL_SP_ADD_D, WOLFSSL_SP_INVMOD, WOLFSSL_SP_INVMOD_MONT_CT, WOLFSSL_SP_MATH_ALL, WOLFSSL_SP_MUL_D, WOLFSSL_SP_NO_DYN_STACK, WOLFSSL_SP_PRIME_GEN, WOLFSSL_SP_READ_RADIX_10, WOLFSSL_SP_READ_RADIX_16, WOLFSSL_SP_SUB_D, WOLFSSL_TEST_VIS, WOLFSSL_TLS13, WOLFSSL_USER_SETTINGS, WOLFSSL_W64_WRAPPER, XGEN_ALIGN | hash-kind=source-merkle-omnibor | source-set=aes.c,ecc.c,keys.c,random.c,sha.c,sha256.c,tls.c,tls13.c,wc_port.c", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:wolfssl:wolfssl:5.9.1:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:github/wolfSSL/wolfssl@v5.9.1" + } + ] + } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relatedSpdxElement": "SPDXRef-Package-wolfssl", + "relationshipType": "DESCRIBES" + } + ] +} diff --git a/cra-kit/auditor-packet/wolfssl-component/README-bomsh.md b/cra-kit/auditor-packet/wolfssl-component/README-bomsh.md new file mode 100644 index 000000000..3c451b09b --- /dev/null +++ b/cra-kit/auditor-packet/wolfssl-component/README-bomsh.md @@ -0,0 +1,26 @@ +# Optional: OmniBOR / bomsh bundle + +`make bomsh` is **not** included in this sample packet. Most CRA transparency +workflows need the SBOM files only. + +When an auditor or contract requires **build provenance**: + +1. On a **Linux** build host (or Linux CI / WSL2 / container), in your wolfSSL tree: + ```sh + ./configure && make sbom && make bomsh + ``` +2. Add to your release bundle: + - `omnibor/` directory (Merkle DAG of build inputs/outputs) + - `omnibor.wolfssl-.spdx.json` (file-level provenance) + +**Sample shape:** see [`omnibor.wolfssl-5.9.1.spdx.json.sample`](omnibor.wolfssl-5.9.1.spdx.json.sample) — a +truncated illustrative document (3 source files instead of every wolfSSL `.c`, +placeholder gitoids instead of real ones) so customers know what shape `make bomsh` +produces before they run it. + +**Why Linux only?** `bomsh` uses `bomtrace3`, a patched `strace` that records +compiler invocations during a full rebuild. That tooling is built and supported +on Linux hosts. The **target** of your firmware (MCU, RTOS, etc.) does not need +to run Linux — only the machine **tracing the build** does. + +Details: [wolfssl/doc/SBOM.md §3](https://github.com/wolfSSL/wolfssl/blob/master/doc/SBOM.md) diff --git a/cra-kit/auditor-packet/wolfssl-component/SAMPLE-PROVENANCE.md b/cra-kit/auditor-packet/wolfssl-component/SAMPLE-PROVENANCE.md new file mode 100644 index 000000000..4e6419b48 --- /dev/null +++ b/cra-kit/auditor-packet/wolfssl-component/SAMPLE-PROVENANCE.md @@ -0,0 +1,21 @@ +# Sample provenance + +Pinned files in this directory (`wolfssl-5.9.1.cdx.json`, `wolfssl-5.9.1.spdx.json`) +were produced with the **autotools** path: + +```sh +cd "$WOLFSSL_DIR" && ./configure && make sbom +``` + +They reflect a **configured library build** (SHA-256 of `libwolfssl` and full +`wolfssl:build:*` properties from `options.h`). + +They are **not** the same as the **embedded** demo under +[`../wolfssl-component-embedded/`](../wolfssl-component-embedded/), which uses +`user_settings.h` and a trimmed `--srcs` list (source-merkle checksum). + +Regenerate autotools samples and fix the product stub checksum: + +```sh +./scripts/refresh-samples.sh +``` diff --git a/cra-kit/auditor-packet/wolfssl-component/omnibor.wolfssl-5.9.1.spdx.json.sample b/cra-kit/auditor-packet/wolfssl-component/omnibor.wolfssl-5.9.1.spdx.json.sample new file mode 100644 index 000000000..3992f6737 --- /dev/null +++ b/cra-kit/auditor-packet/wolfssl-component/omnibor.wolfssl-5.9.1.spdx.json.sample @@ -0,0 +1,92 @@ +{ + "spdxVersion": "SPDX-2.3", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "omnibor.wolfssl-5.9.1", + "documentNamespace": "urn:uuid:9a8b7c6d-5e4f-4a3b-9c2d-1e0f3a4b5c6d", + "comment": "TRUNCATED SAMPLE — illustrates the shape of bomsh / OmniBOR output. A real omnibor.wolfssl-.spdx.json from `make bomsh` lists every wolfSSL .c source via gitoid:blob:sha1 alongside the resulting libwolfssl.so. The full omnibor/ Merkle DAG (under auditor-packet/wolfssl-component/omnibor/) is large and not committed here.", + "creationInfo": { + "creators": [ + "Organization: wolfSSL Inc.", + "Tool: bomsh-1.0", + "Tool: bomtrace3" + ], + "created": "2026-05-12T17:01:12Z" + }, + "packages": [ + { + "SPDXID": "SPDXRef-Package-libwolfssl-so", + "name": "libwolfssl.so.43.0.0", + "versionInfo": "5.9.1", + "supplier": "Organization: wolfSSL Inc.", + "downloadLocation": "NOASSERTION", + "filesAnalyzed": false, + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "0000000000000000000000000000000000000001" + }, + { + "algorithm": "SHA256", + "checksumValue": "391e86477f5eee025e677a24b13e2d9a4d3e4c18d88e6359853ebf1c9932279e" + } + ], + "comment": "OmniBOR identifier for the linked binary: gitoid:blob:sha1:0000000000000000000000000000000000000001 — sample placeholder. Real builds emit the actual gitoid covering all .o inputs." + } + ], + "files": [ + { + "SPDXID": "SPDXRef-File-aes-c", + "fileName": "wolfcrypt/src/aes.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "1111111111111111111111111111111111111111" + } + ], + "comment": "Sample gitoid:blob:sha1 for aes.c. Real entries cover every .c compiled into libwolfssl.so during the traced make bomsh run." + }, + { + "SPDXID": "SPDXRef-File-sha256-c", + "fileName": "wolfcrypt/src/sha256.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "2222222222222222222222222222222222222222" + } + ] + }, + { + "SPDXID": "SPDXRef-File-tls13-c", + "fileName": "src/tls13.c", + "checksums": [ + { + "algorithm": "SHA1", + "checksumValue": "3333333333333333333333333333333333333333" + } + ] + } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relatedSpdxElement": "SPDXRef-Package-libwolfssl-so", + "relationshipType": "DESCRIBES" + }, + { + "spdxElementId": "SPDXRef-Package-libwolfssl-so", + "relatedSpdxElement": "SPDXRef-File-aes-c", + "relationshipType": "GENERATED_FROM" + }, + { + "spdxElementId": "SPDXRef-Package-libwolfssl-so", + "relatedSpdxElement": "SPDXRef-File-sha256-c", + "relationshipType": "GENERATED_FROM" + }, + { + "spdxElementId": "SPDXRef-Package-libwolfssl-so", + "relatedSpdxElement": "SPDXRef-File-tls13-c", + "relationshipType": "GENERATED_FROM" + } + ] +} diff --git a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.cbom-draft.cdx.json b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.cbom-draft.cdx.json new file mode 100644 index 000000000..2e80c34d6 --- /dev/null +++ b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.cbom-draft.cdx.json @@ -0,0 +1,246 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.6", + "serialNumber": "urn:uuid:f1a2b3c4-d5e6-4f78-9012-3456789abcde", + "version": 1, + "metadata": { + "timestamp": "2026-05-12T16:59:40Z", + "tools": { + "components": [ + { + "type": "application", + "author": "wolfSSL Inc.", + "name": "cra-kit cbom-draft (hand-rolled)", + "version": "0.1" + } + ] + }, + "component": { + "type": "library", + "bom-ref": "wolfssl-5.9.1-cbom", + "name": "wolfssl", + "version": "5.9.1", + "supplier": { "name": "wolfSSL Inc." }, + "purl": "pkg:github/wolfSSL/wolfssl@v5.9.1" + }, + "properties": [ + { + "name": "wolfssl:cbom:status", + "value": "DRAFT — illustrative starter set for the CycloneDX 1.6 cryptographic-asset profile. Derived from the build configuration in wolfssl-5.9.1.cdx.json (HAVE_AESGCM, HAVE_CHACHA, HAVE_POLY1305, HAVE_ECC, HAVE_HKDF, WOLFSSL_SHA256/384/512, WOLFSSL_TLS13, WOLFSSL_HAVE_MLKEM). Not exhaustive. See ROADMAP.md." + } + ] + }, + "components": [ + { + "type": "cryptographic-asset", + "bom-ref": "crypto-aes-gcm", + "name": "AES-GCM", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "primitive": "ae", + "parameterSetIdentifier": "AES-256-GCM", + "cryptoFunctions": ["encrypt", "decrypt"], + "executionEnvironment": "software-plain-ram", + "implementationPlatform": "x86_64", + "certificationLevel": ["none"], + "nistQuantumSecurityLevel": 0 + }, + "oid": "2.16.840.1.101.3.4.1.46" + }, + "properties": [ + {"name": "wolfssl:build:macro", "value": "HAVE_AESGCM"}, + {"name": "wolfssl:build:macro", "value": "GCM_TABLE_4BIT"} + ] + }, + { + "type": "cryptographic-asset", + "bom-ref": "crypto-chacha20-poly1305", + "name": "ChaCha20-Poly1305", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "primitive": "ae", + "parameterSetIdentifier": "ChaCha20-Poly1305 (RFC 8439)", + "cryptoFunctions": ["encrypt", "decrypt"], + "executionEnvironment": "software-plain-ram", + "implementationPlatform": "x86_64", + "nistQuantumSecurityLevel": 0 + } + }, + "properties": [ + {"name": "wolfssl:build:macro", "value": "HAVE_CHACHA"}, + {"name": "wolfssl:build:macro", "value": "HAVE_POLY1305"} + ] + }, + { + "type": "cryptographic-asset", + "bom-ref": "crypto-ecdh-p256", + "name": "ECDH (P-256)", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "primitive": "key-agree", + "parameterSetIdentifier": "secp256r1 (NIST P-256)", + "curve": "P-256", + "cryptoFunctions": ["keygen", "derive"], + "executionEnvironment": "software-plain-ram", + "nistQuantumSecurityLevel": 0 + } + }, + "properties": [ + {"name": "wolfssl:build:macro", "value": "HAVE_ECC"}, + {"name": "wolfssl:build:macro", "value": "ECC_TIMING_RESISTANT"} + ] + }, + { + "type": "cryptographic-asset", + "bom-ref": "crypto-ecdsa-p256", + "name": "ECDSA (P-256)", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "primitive": "signature", + "parameterSetIdentifier": "secp256r1 (NIST P-256)", + "curve": "P-256", + "cryptoFunctions": ["sign", "verify"], + "executionEnvironment": "software-plain-ram", + "nistQuantumSecurityLevel": 0 + } + }, + "properties": [ + {"name": "wolfssl:build:macro", "value": "HAVE_ECC"} + ] + }, + { + "type": "cryptographic-asset", + "bom-ref": "crypto-hkdf", + "name": "HKDF", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "primitive": "kdf", + "parameterSetIdentifier": "HKDF-SHA256 (RFC 5869)", + "cryptoFunctions": ["derive"], + "executionEnvironment": "software-plain-ram" + } + }, + "properties": [ + {"name": "wolfssl:build:macro", "value": "HAVE_HKDF"} + ] + }, + { + "type": "cryptographic-asset", + "bom-ref": "crypto-sha-256", + "name": "SHA-256", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "primitive": "hash", + "parameterSetIdentifier": "SHA-256", + "cryptoFunctions": ["digest"], + "executionEnvironment": "software-plain-ram" + }, + "oid": "2.16.840.1.101.3.4.2.1" + } + }, + { + "type": "cryptographic-asset", + "bom-ref": "crypto-sha-384", + "name": "SHA-384", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "primitive": "hash", + "parameterSetIdentifier": "SHA-384", + "cryptoFunctions": ["digest"], + "executionEnvironment": "software-plain-ram" + }, + "oid": "2.16.840.1.101.3.4.2.2" + }, + "properties": [ + {"name": "wolfssl:build:macro", "value": "WOLFSSL_SHA384"} + ] + }, + { + "type": "cryptographic-asset", + "bom-ref": "crypto-sha-512", + "name": "SHA-512", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "primitive": "hash", + "parameterSetIdentifier": "SHA-512", + "cryptoFunctions": ["digest"], + "executionEnvironment": "software-plain-ram" + }, + "oid": "2.16.840.1.101.3.4.2.3" + }, + "properties": [ + {"name": "wolfssl:build:macro", "value": "WOLFSSL_SHA512"} + ] + }, + { + "type": "cryptographic-asset", + "bom-ref": "crypto-ml-kem", + "name": "ML-KEM (post-quantum hybrid)", + "cryptoProperties": { + "assetType": "algorithm", + "algorithmProperties": { + "primitive": "kem", + "parameterSetIdentifier": "ML-KEM-768 (NIST FIPS 203, hybrid TLS 1.3)", + "cryptoFunctions": ["encapsulate", "decapsulate"], + "executionEnvironment": "software-plain-ram", + "nistQuantumSecurityLevel": 3 + } + }, + "properties": [ + {"name": "wolfssl:build:macro", "value": "WOLFSSL_HAVE_MLKEM"}, + {"name": "wolfssl:build:macro", "value": "WOLFSSL_PQC_HYBRIDS"} + ] + }, + { + "type": "cryptographic-asset", + "bom-ref": "crypto-tls-1.3", + "name": "TLS 1.3", + "cryptoProperties": { + "assetType": "protocol", + "protocolProperties": { + "type": "tls", + "version": "1.3", + "cryptoRefArray": [ + "crypto-aes-gcm", + "crypto-chacha20-poly1305", + "crypto-ecdh-p256", + "crypto-ecdsa-p256", + "crypto-hkdf", + "crypto-sha-256", + "crypto-sha-384", + "crypto-ml-kem" + ] + } + }, + "properties": [ + {"name": "wolfssl:build:macro", "value": "WOLFSSL_TLS13"} + ] + } + ], + "dependencies": [ + { + "ref": "wolfssl-5.9.1-cbom", + "dependsOn": [ + "crypto-tls-1.3", + "crypto-aes-gcm", + "crypto-chacha20-poly1305", + "crypto-ecdh-p256", + "crypto-ecdsa-p256", + "crypto-hkdf", + "crypto-sha-256", + "crypto-sha-384", + "crypto-sha-512", + "crypto-ml-kem" + ] + } + ] +} diff --git a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.cdx.json b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.cdx.json new file mode 100644 index 000000000..5c24c3a6e --- /dev/null +++ b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.cdx.json @@ -0,0 +1,300 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.6", + "serialNumber": "urn:uuid:bbd8fa2c-814a-5921-b121-e872fe1b42a2", + "version": 1, + "metadata": { + "timestamp": "2026-05-12T16:59:40Z", + "tools": { + "components": [ + { + "type": "application", + "author": "wolfSSL Inc.", + "name": "wolfssl-sbom-gen", + "version": "1.0" + } + ] + }, + "component": { + "bom-ref": "721ce791-b9c8-5edf-a9d2-ef3b0539043b", + "type": "library", + "supplier": { + "name": "wolfSSL Inc." + }, + "name": "wolfssl", + "version": "5.9.1", + "licenses": [ + { + "license": { + "id": "GPL-3.0-only" + } + } + ], + "copyright": "Copyright (C) 2006-2026 wolfSSL Inc.", + "cpe": "cpe:2.3:a:wolfssl:wolfssl:5.9.1:*:*:*:*:*:*:*", + "purl": "pkg:github/wolfSSL/wolfssl@v5.9.1", + "hashes": [ + { + "alg": "SHA-256", + "content": "391e86477f5eee025e677a24b13e2d9a4d3e4c18d88e6359853ebf1c9932279e" + } + ], + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/wolfSSL/wolfssl" + } + ], + "properties": [ + { + "name": "wolfssl:build:ECC_MIN_KEY_SZ", + "value": "224" + }, + { + "name": "wolfssl:build:ECC_SHAMIR", + "value": "1" + }, + { + "name": "wolfssl:build:ECC_TIMING_RESISTANT", + "value": "1" + }, + { + "name": "wolfssl:build:ERROR_QUEUE_PER_THREAD", + "value": "1" + }, + { + "name": "wolfssl:build:GCM_TABLE_4BIT", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_AESGCM", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_CHACHA", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_C___ATOMIC", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_DH_DEFAULT_PARAMS", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_ECC", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_ENCRYPT_THEN_MAC", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_EXTENDED_MASTER", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_FFDHE_2048", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_GETPID", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_HASHDRBG", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_HKDF", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_POLY1305", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_SERVER_RENEGOTIATION_INFO", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_SNI", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_SUPPORTED_CURVES", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_THREAD_LS", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_TLS_EXTENSIONS", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_WC_INTROSPECTION", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE___UINT128_T", + "value": "1" + }, + { + "name": "wolfssl:build:NO_DES3", + "value": "1" + }, + { + "name": "wolfssl:build:NO_DES3_TLS_SUITES", + "value": "1" + }, + { + "name": "wolfssl:build:NO_DO178", + "value": "1" + }, + { + "name": "wolfssl:build:NO_DSA", + "value": "1" + }, + { + "name": "wolfssl:build:NO_MD4", + "value": "1" + }, + { + "name": "wolfssl:build:NO_MD5", + "value": "1" + }, + { + "name": "wolfssl:build:NO_OLD_TLS", + "value": "1" + }, + { + "name": "wolfssl:build:NO_PSK", + "value": "1" + }, + { + "name": "wolfssl:build:NO_RC4", + "value": "1" + }, + { + "name": "wolfssl:build:TFM_TIMING_RESISTANT", + "value": "1" + }, + { + "name": "wolfssl:build:WC_NO_ASYNC_THREADING", + "value": "1" + }, + { + "name": "wolfssl:build:WC_RSA_BLINDING", + "value": "1" + }, + { + "name": "wolfssl:build:WC_RSA_PSS", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_ARMASM_NO_HW_CRYPTO", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_ASN_PRINT", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_ASN_TEMPLATE", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_BASE64_ENCODE", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_DRBG_SHA512", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_HAVE_ASSERT_H", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_HAVE_ATOMIC_H", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_HAVE_MLKEM", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_PQC_HYBRIDS", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_PSS_LONG_SALT", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SHA224", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SHA3", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SHA384", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SHA512", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SHAKE128", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SHAKE256", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SP_MATH_ALL", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SP_X86_64", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SYS_CA_CERTS", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_TLS13", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_TLS_NO_MLKEM_STANDALONE", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_USE_ALIGN", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_X86_64_BUILD", + "value": "1" + } + ] + } + }, + "components": [], + "dependencies": [ + { + "ref": "721ce791-b9c8-5edf-a9d2-ef3b0539043b", + "dependsOn": [] + } + ] +} diff --git a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.commercial.cdx.json b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.commercial.cdx.json new file mode 100644 index 000000000..9a4f14bbc --- /dev/null +++ b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.commercial.cdx.json @@ -0,0 +1,304 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.6", + "serialNumber": "urn:uuid:80b023d5-8a5d-4e17-9e18-f3d5c7c9762e", + "version": 1, + "metadata": { + "timestamp": "2026-05-12T16:59:40Z", + "tools": { + "components": [ + { + "type": "application", + "author": "wolfSSL Inc.", + "name": "wolfssl-sbom-gen", + "version": "1.0" + } + ] + }, + "component": { + "bom-ref": "721ce791-b9c8-5edf-a9d2-ef3b0539043b", + "type": "library", + "supplier": { + "name": "wolfSSL Inc." + }, + "name": "wolfssl", + "version": "5.9.1", + "licenses": [ + { + "license": { + "name": "wolfSSL Commercial License (LicenseRef-wolfSSL-Commercial)" + } + } + ], + "copyright": "Copyright (C) 2006-2026 wolfSSL Inc.", + "cpe": "cpe:2.3:a:wolfssl:wolfssl:5.9.1:*:*:*:*:*:*:*", + "purl": "pkg:github/wolfSSL/wolfssl@v5.9.1", + "hashes": [ + { + "alg": "SHA-256", + "content": "391e86477f5eee025e677a24b13e2d9a4d3e4c18d88e6359853ebf1c9932279e" + } + ], + "externalReferences": [ + { + "type": "vcs", + "url": "https://github.com/wolfSSL/wolfssl" + } + ], + "properties": [ + { + "name": "wolfssl:build:ECC_MIN_KEY_SZ", + "value": "224" + }, + { + "name": "wolfssl:build:ECC_SHAMIR", + "value": "1" + }, + { + "name": "wolfssl:build:ECC_TIMING_RESISTANT", + "value": "1" + }, + { + "name": "wolfssl:build:ERROR_QUEUE_PER_THREAD", + "value": "1" + }, + { + "name": "wolfssl:build:GCM_TABLE_4BIT", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_AESGCM", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_CHACHA", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_C___ATOMIC", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_DH_DEFAULT_PARAMS", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_ECC", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_ENCRYPT_THEN_MAC", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_EXTENDED_MASTER", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_FFDHE_2048", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_GETPID", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_HASHDRBG", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_HKDF", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_POLY1305", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_SERVER_RENEGOTIATION_INFO", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_SNI", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_SUPPORTED_CURVES", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_THREAD_LS", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_TLS_EXTENSIONS", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE_WC_INTROSPECTION", + "value": "1" + }, + { + "name": "wolfssl:build:HAVE___UINT128_T", + "value": "1" + }, + { + "name": "wolfssl:build:NO_DES3", + "value": "1" + }, + { + "name": "wolfssl:build:NO_DES3_TLS_SUITES", + "value": "1" + }, + { + "name": "wolfssl:build:NO_DO178", + "value": "1" + }, + { + "name": "wolfssl:build:NO_DSA", + "value": "1" + }, + { + "name": "wolfssl:build:NO_MD4", + "value": "1" + }, + { + "name": "wolfssl:build:NO_MD5", + "value": "1" + }, + { + "name": "wolfssl:build:NO_OLD_TLS", + "value": "1" + }, + { + "name": "wolfssl:build:NO_PSK", + "value": "1" + }, + { + "name": "wolfssl:build:NO_RC4", + "value": "1" + }, + { + "name": "wolfssl:build:TFM_TIMING_RESISTANT", + "value": "1" + }, + { + "name": "wolfssl:build:WC_NO_ASYNC_THREADING", + "value": "1" + }, + { + "name": "wolfssl:build:WC_RSA_BLINDING", + "value": "1" + }, + { + "name": "wolfssl:build:WC_RSA_PSS", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_ARMASM_NO_HW_CRYPTO", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_ASN_PRINT", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_ASN_TEMPLATE", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_BASE64_ENCODE", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_DRBG_SHA512", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_HAVE_ASSERT_H", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_HAVE_ATOMIC_H", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_HAVE_MLKEM", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_PQC_HYBRIDS", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_PSS_LONG_SALT", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SHA224", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SHA3", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SHA384", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SHA512", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SHAKE128", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SHAKE256", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SP_MATH_ALL", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SP_X86_64", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_SYS_CA_CERTS", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_TLS13", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_TLS_NO_MLKEM_STANDALONE", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_USE_ALIGN", + "value": "1" + }, + { + "name": "wolfssl:build:WOLFSSL_X86_64_BUILD", + "value": "1" + }, + { + "name": "wolfssl:license:override", + "value": "LicenseRef-wolfSSL-Commercial" + } + ] + } + }, + "components": [], + "dependencies": [ + { + "ref": "721ce791-b9c8-5edf-a9d2-ef3b0539043b", + "dependsOn": [] + } + ] +} diff --git a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.commercial.spdx.json b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.commercial.spdx.json new file mode 100644 index 000000000..61cedaab8 --- /dev/null +++ b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.commercial.spdx.json @@ -0,0 +1,63 @@ +{ + "spdxVersion": "SPDX-2.3", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "wolfssl-5.9.1", + "documentNamespace": "urn:uuid:cedcdaaa-b983-4ce1-83e3-ed7337232a49", + "creationInfo": { + "creators": [ + "Organization: wolfSSL Inc.", + "Tool: wolfssl-sbom-gen-1.0" + ], + "created": "2026-05-12T16:59:40Z" + }, + "packages": [ + { + "SPDXID": "SPDXRef-Package-wolfssl", + "name": "wolfssl", + "versionInfo": "5.9.1", + "supplier": "Organization: wolfSSL Inc.", + "downloadLocation": "https://github.com/wolfSSL/wolfssl", + "filesAnalyzed": false, + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "391e86477f5eee025e677a24b13e2d9a4d3e4c18d88e6359853ebf1c9932279e" + } + ], + "licenseConcluded": "LicenseRef-wolfSSL-Commercial", + "licenseDeclared": "LicenseRef-wolfSSL-Commercial", + "copyrightText": "Copyright (C) 2006-2026 wolfSSL Inc.", + "comment": "License override applied: LicenseRef-wolfSSL-Commercial. Build configuration defines: ECC_MIN_KEY_SZ, ECC_SHAMIR, ECC_TIMING_RESISTANT, ERROR_QUEUE_PER_THREAD, GCM_TABLE_4BIT, HAVE_AESGCM, HAVE_CHACHA, HAVE_C___ATOMIC, HAVE_DH_DEFAULT_PARAMS, HAVE_ECC, HAVE_ENCRYPT_THEN_MAC, HAVE_EXTENDED_MASTER, HAVE_FFDHE_2048, HAVE_GETPID, HAVE_HASHDRBG, HAVE_HKDF, HAVE_POLY1305, HAVE_SERVER_RENEGOTIATION_INFO, HAVE_SNI, HAVE_SUPPORTED_CURVES, HAVE_THREAD_LS, HAVE_TLS_EXTENSIONS, HAVE_WC_INTROSPECTION, HAVE___UINT128_T, NO_DES3, NO_DES3_TLS_SUITES, NO_DO178, NO_DSA, NO_MD4, NO_MD5, NO_OLD_TLS, NO_PSK, NO_RC4, TFM_TIMING_RESISTANT, WC_NO_ASYNC_THREADING, WC_RSA_BLINDING, WC_RSA_PSS, WOLFSSL_ARMASM_NO_HW_CRYPTO, WOLFSSL_ASN_PRINT, WOLFSSL_ASN_TEMPLATE, WOLFSSL_BASE64_ENCODE, WOLFSSL_DRBG_SHA512, WOLFSSL_HAVE_ASSERT_H, WOLFSSL_HAVE_ATOMIC_H, WOLFSSL_HAVE_MLKEM, WOLFSSL_PQC_HYBRIDS, WOLFSSL_PSS_LONG_SALT, WOLFSSL_SHA224, WOLFSSL_SHA3, WOLFSSL_SHA384, WOLFSSL_SHA512, WOLFSSL_SHAKE128, WOLFSSL_SHAKE256, WOLFSSL_SP_MATH_ALL, WOLFSSL_SP_X86_64, WOLFSSL_SYS_CA_CERTS, WOLFSSL_TLS13, WOLFSSL_TLS_NO_MLKEM_STANDALONE, WOLFSSL_USE_ALIGN, WOLFSSL_X86_64_BUILD", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:wolfssl:wolfssl:5.9.1:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:github/wolfSSL/wolfssl@v5.9.1" + } + ] + } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relatedSpdxElement": "SPDXRef-Package-wolfssl", + "relationshipType": "DESCRIBES" + } + ], + "hasExtractedLicensingInfos": [ + { + "licenseId": "LicenseRef-wolfSSL-Commercial", + "extractedText": "wolfSSL commercial license. See https://www.wolfssl.com/license/ for terms. Replaces the GPL-3.0-only declaration of the open-source distribution.", + "name": "wolfSSL Commercial License", + "seeAlsos": [ + "https://www.wolfssl.com/license/" + ] + } + ] +} diff --git a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.spdx b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.spdx new file mode 100644 index 000000000..7c1148ce7 --- /dev/null +++ b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.spdx @@ -0,0 +1,30 @@ +## Document Information +SPDXVersion: SPDX-2.3 +DataLicense: CC0-1.0 +SPDXID: SPDXRef-DOCUMENT +DocumentName: wolfssl-5.9.1 +DocumentNamespace: urn:uuid:480ff203-f994-5b71-b858-0653e74e422a + +## Creation Information +Creator: Organization: wolfSSL Inc. +Creator: Tool: wolfssl-sbom-gen-1.0 +Created: 2026-05-12T16:59:40Z + +## Package Information +PackageName: wolfssl +SPDXID: SPDXRef-Package-wolfssl +PackageVersion: 5.9.1 +PackageSupplier: Organization: wolfSSL Inc. +PackageDownloadLocation: https://github.com/wolfSSL/wolfssl +FilesAnalyzed: false +PackageChecksum: SHA256: 391e86477f5eee025e677a24b13e2d9a4d3e4c18d88e6359853ebf1c9932279e +PackageLicenseConcluded: GPL-3.0-only +PackageLicenseDeclared: GPL-3.0-only +PackageCopyrightText: Copyright (C) 2006-2026 wolfSSL Inc. +PackageComment: Build configuration defines: ECC_MIN_KEY_SZ, ECC_SHAMIR, ECC_TIMING_RESISTANT, ERROR_QUEUE_PER_THREAD, GCM_TABLE_4BIT, HAVE_AESGCM, HAVE_CHACHA, HAVE_C___ATOMIC, HAVE_DH_DEFAULT_PARAMS, HAVE_ECC, HAVE_ENCRYPT_THEN_MAC, HAVE_EXTENDED_MASTER, HAVE_FFDHE_2048, HAVE_GETPID, HAVE_HASHDRBG, HAVE_HKDF, HAVE_POLY1305, HAVE_SERVER_RENEGOTIATION_INFO, HAVE_SNI, HAVE_SUPPORTED_CURVES, HAVE_THREAD_LS, HAVE_TLS_EXTENSIONS, HAVE_WC_INTROSPECTION, HAVE___UINT128_T, NO_DES3, NO_DES3_TLS_SUITES, NO_DO178, NO_DSA, NO_MD4, NO_MD5, NO_OLD_TLS, NO_PSK, NO_RC4, TFM_TIMING_RESISTANT, WC_NO_ASYNC_THREADING, WC_RSA_BLINDING, WC_RSA_PSS, WOLFSSL_ARMASM_NO_HW_CRYPTO, WOLFSSL_ASN_PRINT, WOLFSSL_ASN_TEMPLATE, WOLFSSL_BASE64_ENCODE, WOLFSSL_DRBG_SHA512, WOLFSSL_HAVE_ASSERT_H, WOLFSSL_HAVE_ATOMIC_H, WOLFSSL_HAVE_MLKEM, WOLFSSL_PQC_HYBRIDS, WOLFSSL_PSS_LONG_SALT, WOLFSSL_SHA224, WOLFSSL_SHA3, WOLFSSL_SHA384, WOLFSSL_SHA512, WOLFSSL_SHAKE128, WOLFSSL_SHAKE256, WOLFSSL_SP_MATH_ALL, WOLFSSL_SP_X86_64, WOLFSSL_SYS_CA_CERTS, WOLFSSL_TLS13, WOLFSSL_TLS_NO_MLKEM_STANDALONE, WOLFSSL_USE_ALIGN, WOLFSSL_X86_64_BUILD +ExternalRef: SECURITY cpe23Type cpe:2.3:a:wolfssl:wolfssl:5.9.1:*:*:*:*:*:*:* +ExternalRef: PACKAGE-MANAGER purl pkg:github/wolfSSL/wolfssl@v5.9.1 + +## Relationships +Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package-wolfssl + diff --git a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.spdx.json b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.spdx.json new file mode 100644 index 000000000..dc4796b6e --- /dev/null +++ b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.spdx.json @@ -0,0 +1,53 @@ +{ + "spdxVersion": "SPDX-2.3", + "dataLicense": "CC0-1.0", + "SPDXID": "SPDXRef-DOCUMENT", + "name": "wolfssl-5.9.1", + "documentNamespace": "urn:uuid:480ff203-f994-5b71-b858-0653e74e422a", + "creationInfo": { + "creators": [ + "Organization: wolfSSL Inc.", + "Tool: wolfssl-sbom-gen-1.0" + ], + "created": "2026-05-12T16:59:40Z" + }, + "packages": [ + { + "SPDXID": "SPDXRef-Package-wolfssl", + "name": "wolfssl", + "versionInfo": "5.9.1", + "supplier": "Organization: wolfSSL Inc.", + "downloadLocation": "https://github.com/wolfSSL/wolfssl", + "filesAnalyzed": false, + "checksums": [ + { + "algorithm": "SHA256", + "checksumValue": "391e86477f5eee025e677a24b13e2d9a4d3e4c18d88e6359853ebf1c9932279e" + } + ], + "licenseConcluded": "GPL-3.0-only", + "licenseDeclared": "GPL-3.0-only", + "copyrightText": "Copyright (C) 2006-2026 wolfSSL Inc.", + "comment": "Build configuration defines: ECC_MIN_KEY_SZ, ECC_SHAMIR, ECC_TIMING_RESISTANT, ERROR_QUEUE_PER_THREAD, GCM_TABLE_4BIT, HAVE_AESGCM, HAVE_CHACHA, HAVE_C___ATOMIC, HAVE_DH_DEFAULT_PARAMS, HAVE_ECC, HAVE_ENCRYPT_THEN_MAC, HAVE_EXTENDED_MASTER, HAVE_FFDHE_2048, HAVE_GETPID, HAVE_HASHDRBG, HAVE_HKDF, HAVE_POLY1305, HAVE_SERVER_RENEGOTIATION_INFO, HAVE_SNI, HAVE_SUPPORTED_CURVES, HAVE_THREAD_LS, HAVE_TLS_EXTENSIONS, HAVE_WC_INTROSPECTION, HAVE___UINT128_T, NO_DES3, NO_DES3_TLS_SUITES, NO_DO178, NO_DSA, NO_MD4, NO_MD5, NO_OLD_TLS, NO_PSK, NO_RC4, TFM_TIMING_RESISTANT, WC_NO_ASYNC_THREADING, WC_RSA_BLINDING, WC_RSA_PSS, WOLFSSL_ARMASM_NO_HW_CRYPTO, WOLFSSL_ASN_PRINT, WOLFSSL_ASN_TEMPLATE, WOLFSSL_BASE64_ENCODE, WOLFSSL_DRBG_SHA512, WOLFSSL_HAVE_ASSERT_H, WOLFSSL_HAVE_ATOMIC_H, WOLFSSL_HAVE_MLKEM, WOLFSSL_PQC_HYBRIDS, WOLFSSL_PSS_LONG_SALT, WOLFSSL_SHA224, WOLFSSL_SHA3, WOLFSSL_SHA384, WOLFSSL_SHA512, WOLFSSL_SHAKE128, WOLFSSL_SHAKE256, WOLFSSL_SP_MATH_ALL, WOLFSSL_SP_X86_64, WOLFSSL_SYS_CA_CERTS, WOLFSSL_TLS13, WOLFSSL_TLS_NO_MLKEM_STANDALONE, WOLFSSL_USE_ALIGN, WOLFSSL_X86_64_BUILD", + "externalRefs": [ + { + "referenceCategory": "SECURITY", + "referenceType": "cpe23Type", + "referenceLocator": "cpe:2.3:a:wolfssl:wolfssl:5.9.1:*:*:*:*:*:*:*" + }, + { + "referenceCategory": "PACKAGE-MANAGER", + "referenceType": "purl", + "referenceLocator": "pkg:github/wolfSSL/wolfssl@v5.9.1" + } + ] + } + ], + "relationships": [ + { + "spdxElementId": "SPDXRef-DOCUMENT", + "relatedSpdxElement": "SPDXRef-Package-wolfssl", + "relationshipType": "DESCRIBES" + } + ] +} diff --git a/cra-kit/presentations/SLIDE-OUTLINE.md b/cra-kit/presentations/SLIDE-OUTLINE.md new file mode 100644 index 000000000..0df7ad6a1 --- /dev/null +++ b/cra-kit/presentations/SLIDE-OUTLINE.md @@ -0,0 +1,73 @@ +# CRA co-sponsor slide track (~15 min) + +Companion kit: [`../CRA-Cheat-Sheet.md`](../CRA-Cheat-Sheet.md) · +[`../CRA-Supply-Chain-Glossary.md`](../CRA-Supply-Chain-Glossary.md) · +[`../SKILL.md`](../SKILL.md) · [`../auditor-packet/`](../auditor-packet/) + +--- + +## Slide: Shortlist towards CRA compliance + +Use **[`CRA-Compliance-Shortlist.md`](../CRA-Compliance-Shortlist.md)** — two columns per pillar: +**your job** vs **wolfSSL helps**. + +| Pillar | On slide (customer) | wolfSSL | +|--------|---------------------|---------| +| **Know your software components** | Survey all integrated components: who maintains them? how do you track vulns/releases? | SBOMs for our products; continuous vulnerability management and updates | +| **Implement secure boot** | Most influential action today: trusted firmware + update path aligned with complaint/timing rules | **wolfBoot** | +| **Remote data processing / data in transfer** | CRA covers data between device and network — use current crypto and secure protocols | **TLS**, **SSH**, **MQTTS**, … | +| **Vulnerability handling & reporting** | Published CVD policy + `security.txt`; 24h ENISA reporting (Art. 14); on-call coverage — process, not a deliverable | wolfSSL [`security.txt`](https://www.wolfssl.com/.well-known/security.txt) + [CVD policy](https://www.wolfssl.com/.well-known/vulnerability-disclosure-policy.txt) as reference templates; advisories; CNA | + +**Bridge to this session:** pillar 1 is where the **CRA Kit** lands (SBOM, auditor packet, scripts). + +--- + +## Slide: Promise — what you leave with + +**You will leave with:** + +1. **Who provides what** — what **you** provide vs what **wolfSSL** provides + → [`CRA-Cheat-Sheet.md`](../CRA-Cheat-Sheet.md) (print/PDF) + → full terms: [`CRA-Supply-Chain-Glossary.md`](../CRA-Supply-Chain-Glossary.md) + +2. **A worked example** — wolfSSL CRA Kit + → [`wolfssl-examples/cra-kit/auditor-packet/`](../auditor-packet/) + +3. **Helper scripts + AI playbook** — product SBOM, nest wolfSSL, optional **bomsh** on **Linux CI** only + → **[`SKILL.md`](../SKILL.md)** for AI-assisted execution (Cursor / agents) + +--- + +## Talking points + +| Instead of… | Say… | +|-------------|------| +| Learn every acronym | “Cheat sheet for roles; glossary in the same kit.” | +| wolfSSL is CRA compliant | “Component SBOMs from us; **product** SBOM and vuln process from you.” | +| We ship CBOM | “Build properties today; formal CBOM profile on the roadmap.” | +| You need bomsh | “Usually no — Linux CI only if a contract asks.” | +| AI is extra | “**SKILL.md** is the playbook—copy it into Cursor and run the scripts with your tree.” | + +--- + +## Demo path (optional live) + +```bash +cd wolfssl-examples/cra-kit +./scripts/validate.sh +``` + +Show `auditor-packet/product-acme-connect-gateway.cdx.json` → wolfSSL component reference. + +Optional: show copying `SKILL.md` into `.cursor/skills/wolfssl-cra-kit/`. + +--- + +## Kit documents (handout stack) + +| Layer | File | +|-------|------| +| Who provides what (1 page) | `CRA-Cheat-Sheet.md` | +| Glossary (reference) | `CRA-Supply-Chain-Glossary.md` | +| AI playbook | `SKILL.md` | +| Full guide | `README.md` | diff --git a/cra-kit/scripts/generate-embedded-sbom.sh b/cra-kit/scripts/generate-embedded-sbom.sh new file mode 100755 index 000000000..dafc32f51 --- /dev/null +++ b/cra-kit/scripts/generate-embedded-sbom.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# Force embedded gen-sbom (user_settings.h + --srcs) into wolfssl-component-embedded/. +set -eu + +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +KIT_DIR=$(dirname "$SCRIPT_DIR") +export CRA_SBOM_MODE=embedded +export CRA_SBOM_OUT_DIR="$KIT_DIR/auditor-packet/wolfssl-component-embedded" +exec "$SCRIPT_DIR/generate-wolfssl-sbom.sh" diff --git a/cra-kit/scripts/generate-wolfssl-sbom.sh b/cra-kit/scripts/generate-wolfssl-sbom.sh new file mode 100755 index 000000000..b8b5b0117 --- /dev/null +++ b/cra-kit/scripts/generate-wolfssl-sbom.sh @@ -0,0 +1,232 @@ +#!/bin/sh +# Generate wolfSSL component SBOMs (autotools make sbom or embedded gen-sbom). +# CRA_SBOM_MODE=autotools|embedded (default: autotools if configure+Makefile exist) +# WOLFSSL_DIR=path/to/wolfssl +# CRA_PYTHON=python3 (optional: interpreter with pcpp for embedded path) +# CRA_LICENSE_OVERRIDE= (optional: e.g. LicenseRef-wolfSSL-Commercial) +set -eu + +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +KIT_DIR=$(dirname "$SCRIPT_DIR") +WOLFSSL_DIR=${WOLFSSL_DIR:-$(cd "$KIT_DIR/../../wolfssl" 2>/dev/null && pwd || true)} +OUT_DIR=${CRA_SBOM_OUT_DIR:-"$KIT_DIR/auditor-packet/wolfssl-component"} +VERSION_FILE="$KIT_DIR/VERSION" + +if [ -z "${WOLFSSL_DIR:-}" ] || [ ! -d "$WOLFSSL_DIR" ]; then + echo "ERROR: wolfSSL source not found." >&2 + echo " Set WOLFSSL_DIR to your wolfssl checkout (sibling of wolfssl-examples)." >&2 + exit 1 +fi + +# shellcheck disable=SC1090 +. "$VERSION_FILE" 2>/dev/null || true +VERSION=${WOLFSSL_VERSION:-5.9.1} + +mkdir -p "$OUT_DIR" +CDX_OUT="$OUT_DIR/wolfssl-${VERSION}.cdx.json" +SPDX_OUT="$OUT_DIR/wolfssl-${VERSION}.spdx.json" + +echo "wolfSSL tree: $WOLFSSL_DIR" +echo "Outputs: $CDX_OUT" +echo " $SPDX_OUT" +if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + echo "License override: $CRA_LICENSE_OVERRIDE" +fi + +# Pick a Python that can `import pcpp` (pip may target a different python3 than /usr/local/bin). +_python_with_pcpp() { + for py in ${CRA_PYTHON:-} python3 python; do + [ -n "$py" ] || continue + if command -v "$py" >/dev/null 2>&1 && \ + "$py" -c "import pcpp" 2>/dev/null; then + echo "$py" + return 0 + fi + done + return 1 +} + +_embedded_srcs() { + # Demo list only — production SBOMs must mirror every wolfSSL .c on your link line. + # Outputs from this list are watermarked wolfssl:sbom:demo=true. + for f in \ + "$WOLFSSL_DIR/wolfcrypt/src/aes.c" \ + "$WOLFSSL_DIR/wolfcrypt/src/sha.c" \ + "$WOLFSSL_DIR/wolfcrypt/src/sha256.c" \ + "$WOLFSSL_DIR/wolfcrypt/src/random.c" \ + "$WOLFSSL_DIR/wolfcrypt/src/ecc.c" \ + "$WOLFSSL_DIR/wolfcrypt/src/wc_port.c" \ + "$WOLFSSL_DIR/src/tls.c" \ + "$WOLFSSL_DIR/src/tls13.c" \ + "$WOLFSSL_DIR/src/keys.c" + do + if [ -f "$f" ]; then + echo "$f" + fi + done +} + +_run_embedded() { + echo "==> Embedded path: gen-sbom with CRA Kit user_settings.h" + echo " NOTE: --srcs uses the kit's built-in 9-file DEMO list. Production SBOMs" + echo " must pass every wolfSSL .c file you compile. Output is watermarked" + echo " wolfssl:sbom:demo=true so this can never silently ship." + if [ ! -f "$KIT_DIR/user_settings.h" ]; then + echo "ERROR: $KIT_DIR/user_settings.h missing (demo settings for WOLFSSL_USER_SETTINGS)." >&2 + exit 1 + fi + GEN="$WOLFSSL_DIR/scripts/gen-sbom" + if [ ! -f "$GEN" ]; then + echo "ERROR: $GEN not found (need wolfSSL with SBOM support)." >&2 + exit 1 + fi + + SETTINGS_H="$WOLFSSL_DIR/wolfssl/wolfcrypt/settings.h" + if [ ! -f "$SETTINGS_H" ]; then + echo "ERROR: $SETTINGS_H not found." >&2 + exit 1 + fi + + # shellcheck disable=SC2046 + set -- $( _embedded_srcs ) + + # Optional commercial license override (LicenseRef-wolfSSL-Commercial etc). + set -- "$@" --cdx-out "$CDX_OUT" --spdx-out "$SPDX_OUT" + if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + set -- "$@" --license-override "$CRA_LICENSE_OVERRIDE" + fi + + if _py=$(_python_with_pcpp); then + echo " Using $_py (pcpp) for --user-settings" + # shellcheck disable=SC2068 + "$_py" "$GEN" \ + --name wolfssl --version "$VERSION" \ + --license-file "$WOLFSSL_DIR/LICENSING" \ + --user-settings "$SETTINGS_H" \ + --user-settings-include "$WOLFSSL_DIR" \ + --user-settings-include "$KIT_DIR" \ + --user-settings-define WOLFSSL_USER_SETTINGS \ + --srcs $@ + return 0 + fi + + echo "NOTE: pcpp not found for python3/python; using compiler -dM -E -> --options-h" + echo " Install pcpp on the same interpreter: python3 -m pip install pcpp" + echo " (conda users: pip install pcpp often targets conda python, not /usr/local/bin/python3)" + echo " Cross builds: set CC=arm-none-eabi-gcc (or your target compiler) so the" + echo " fallback reflects target macros, not the host's." + + DEFINES_H="$OUT_DIR/.wolfssl-defines-$$.h" + CC=${CC:-cc} + if ! "$CC" -dM -E \ + -I"$WOLFSSL_DIR" \ + -I"$KIT_DIR" \ + -DWOLFSSL_USER_SETTINGS \ + -include "$SETTINGS_H" \ + -x c /dev/null >"$DEFINES_H" 2>/dev/null; then + rm -f "$DEFINES_H" + echo "ERROR: $CC -dM -E failed; install pcpp or set CC to your cross-compiler." >&2 + exit 1 + fi + + PYTHON=python3 + command -v python3 >/dev/null 2>&1 || PYTHON=python + # shellcheck disable=SC2068 + "$PYTHON" "$GEN" \ + --name wolfssl --version "$VERSION" \ + --license-file "$WOLFSSL_DIR/LICENSING" \ + --options-h "$DEFINES_H" \ + --srcs $@ + rm -f "$DEFINES_H" +} + +_run_autotools() { + echo "==> Autotools path: make sbom" + (cd "$WOLFSSL_DIR" && { + if [ ! -f Makefile ]; then + echo " Running ./configure first..." + ./configure + fi + if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + make sbom SBOM_LICENSE_OVERRIDE="$CRA_LICENSE_OVERRIDE" + else + make sbom + fi + cp -f "wolfssl-${VERSION}.cdx.json" "$CDX_OUT" + cp -f "wolfssl-${VERSION}.spdx.json" "$SPDX_OUT" + if [ -f "wolfssl-${VERSION}.spdx" ]; then + cp -f "wolfssl-${VERSION}.spdx" "$OUT_DIR/" + fi + }) +} + +MODE=${CRA_SBOM_MODE:-} +case "$MODE" in + embedded) _run_embedded ;; + autotools) _run_autotools ;; + "") + if [ -f "$WOLFSSL_DIR/Makefile" ] && [ -f "$WOLFSSL_DIR/configure" ]; then + MODE=autotools + _run_autotools + else + MODE=embedded + _run_embedded + fi + ;; + *) + echo "ERROR: CRA_SBOM_MODE must be 'autotools' or 'embedded', not '$MODE'" >&2 + exit 1 + ;; +esac + +# ---- Post-process: PURL canonicalization + demo watermarks ---------------- +# gen-sbom emits pkg:generic/wolfssl@X — we canonicalize to pkg:github so OSV / +# GHSA / Snyk / Trivy match without per-vendor mapping. Embedded outputs from +# the kit's 9-file demo --srcs list also get a wolfssl:sbom:demo property so a +# downstream auditor cannot mistake them for production-complete SBOMs. +CDX_OUT="$CDX_OUT" SPDX_OUT="$SPDX_OUT" CRA_SBOM_MODE_FINAL="$MODE" \ +python3 <<'PY' || echo "WARN: post-process skipped (python3 missing or JSON malformed)" +import json, os, pathlib + +cdx = pathlib.Path(os.environ["CDX_OUT"]) +spdx = pathlib.Path(os.environ["SPDX_OUT"]) +demo = os.environ.get("CRA_SBOM_MODE_FINAL") == "embedded" + +GENERIC = "pkg:generic/wolfssl@" +GITHUB = "pkg:github/wolfSSL/wolfssl@v" + +def canonicalize_purl(s): + if isinstance(s, str) and s.startswith(GENERIC): + return GITHUB + s[len(GENERIC):] + return s + +if cdx.exists(): + d = json.loads(cdx.read_text()) + comp = d.get("metadata", {}).get("component", {}) + comp["purl"] = canonicalize_purl(comp.get("purl", "")) + if demo: + props = comp.setdefault("properties", []) + if not any(p.get("name") == "wolfssl:sbom:demo" for p in props): + props.append({ + "name": "wolfssl:sbom:demo", + "value": "true (built-in --srcs list, not production-complete)" + }) + cdx.write_text(json.dumps(d, indent=2) + "\n") + print(f"Post-processed {cdx.name}: PURL canonicalized" + (", demo watermark added" if demo else "")) + +if spdx.exists(): + d = json.loads(spdx.read_text()) + for pkg in d.get("packages", []): + for ref in pkg.get("externalRefs", []): + if ref.get("referenceType") == "purl": + ref["referenceLocator"] = canonicalize_purl(ref.get("referenceLocator", "")) + if demo: + existing = pkg.get("comment", "") + marker = "DEMO ARTIFACT (built-in --srcs list, not production-complete)." + if marker not in existing: + pkg["comment"] = (marker + " " + existing).strip() + spdx.write_text(json.dumps(d, indent=2) + "\n") + print(f"Post-processed {spdx.name}: PURL canonicalized" + (", demo watermark added" if demo else "")) +PY + +echo "Done." diff --git a/cra-kit/scripts/make-commercial-sample.sh b/cra-kit/scripts/make-commercial-sample.sh new file mode 100755 index 000000000..1af699cc9 --- /dev/null +++ b/cra-kit/scripts/make-commercial-sample.sh @@ -0,0 +1,76 @@ +#!/bin/sh +# Produce a commercial-license-override sample alongside the pinned GPL samples. +# +# This script is illustrative: it derives wolfssl-.commercial.{cdx,spdx}.json +# from the GPL pinned files by swapping the license fields and adding a +# wolfssl:license:override property. Auditors see the same build configuration, +# the same hashes of the source list, and a different license declaration — +# exactly the diff a paying wolfSSL customer's SBOM should show. +# +# In production, regenerate via: +# CRA_LICENSE_OVERRIDE=LicenseRef-wolfSSL-Commercial \ +# ./scripts/generate-wolfssl-sbom.sh +set -eu + +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +KIT_DIR=$(dirname "$SCRIPT_DIR") + +# shellcheck disable=SC1090 +. "$KIT_DIR/VERSION" +COMP_DIR="$KIT_DIR/auditor-packet/wolfssl-component" +GPL_CDX="$COMP_DIR/wolfssl-${WOLFSSL_VERSION}.cdx.json" +GPL_SPDX="$COMP_DIR/wolfssl-${WOLFSSL_VERSION}.spdx.json" +COMMERCIAL_CDX="$COMP_DIR/wolfssl-${WOLFSSL_VERSION}.commercial.cdx.json" +COMMERCIAL_SPDX="$COMP_DIR/wolfssl-${WOLFSSL_VERSION}.commercial.spdx.json" +LICENSE_ID=${CRA_LICENSE_OVERRIDE:-LicenseRef-wolfSSL-Commercial} + +[ -f "$GPL_CDX" ] || { echo "ERROR: $GPL_CDX not found (run refresh-samples first)" >&2; exit 1; } +[ -f "$GPL_SPDX" ] || { echo "ERROR: $GPL_SPDX not found (run refresh-samples first)" >&2; exit 1; } + +GPL_CDX="$GPL_CDX" GPL_SPDX="$GPL_SPDX" \ +COMMERCIAL_CDX="$COMMERCIAL_CDX" COMMERCIAL_SPDX="$COMMERCIAL_SPDX" \ +LICENSE_ID="$LICENSE_ID" \ +python3 <<'PY' +import json, os, pathlib, uuid + +gpl_cdx = pathlib.Path(os.environ["GPL_CDX"]) +gpl_spdx = pathlib.Path(os.environ["GPL_SPDX"]) +out_cdx = pathlib.Path(os.environ["COMMERCIAL_CDX"]) +out_spdx = pathlib.Path(os.environ["COMMERCIAL_SPDX"]) +license_id = os.environ["LICENSE_ID"] + +# --- CycloneDX side ---- +d = json.loads(gpl_cdx.read_text()) +d["serialNumber"] = "urn:uuid:" + str(uuid.uuid4()) +comp = d.get("metadata", {}).get("component", {}) +comp["licenses"] = [{"license": {"name": "wolfSSL Commercial License (" + license_id + ")"}}] +props = comp.setdefault("properties", []) +if not any(p.get("name") == "wolfssl:license:override" for p in props): + props.append({"name": "wolfssl:license:override", "value": license_id}) +out_cdx.write_text(json.dumps(d, indent=2) + "\n") +print(f"Wrote {out_cdx.name} (license override: {license_id})") + +# --- SPDX side ---- +d = json.loads(gpl_spdx.read_text()) +d["documentNamespace"] = "urn:uuid:" + str(uuid.uuid4()) +d["hasExtractedLicensingInfos"] = [ + { + "licenseId": license_id, + "extractedText": ( + "wolfSSL commercial license. See https://www.wolfssl.com/license/ for terms. " + "Replaces the GPL-3.0-only declaration of the open-source distribution." + ), + "name": "wolfSSL Commercial License", + "seeAlsos": ["https://www.wolfssl.com/license/"], + } +] +for pkg in d.get("packages", []): + pkg["licenseConcluded"] = license_id + pkg["licenseDeclared"] = license_id + existing = pkg.get("comment", "") + marker = f"License override applied: {license_id}." + if marker not in existing: + pkg["comment"] = (marker + " " + existing).strip() +out_spdx.write_text(json.dumps(d, indent=2) + "\n") +print(f"Wrote {out_spdx.name} (license override: {license_id})") +PY diff --git a/cra-kit/scripts/refresh-samples.sh b/cra-kit/scripts/refresh-samples.sh new file mode 100755 index 000000000..3bb47d763 --- /dev/null +++ b/cra-kit/scripts/refresh-samples.sh @@ -0,0 +1,60 @@ +#!/bin/sh +# Regenerate pinned autotools samples and sync the product SBOM hashes +# (SPDX externalDocumentRef checksum + CycloneDX bom externalReference hash). +set -eu + +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +KIT_DIR=$(dirname "$SCRIPT_DIR") + +export CRA_SBOM_MODE=autotools +export CRA_SBOM_OUT_DIR="$KIT_DIR/auditor-packet/wolfssl-component" +"$SCRIPT_DIR/generate-wolfssl-sbom.sh" + +# shellcheck disable=SC1090 +. "$KIT_DIR/VERSION" +COMPONENT_SPDX="$KIT_DIR/auditor-packet/wolfssl-component/wolfssl-${WOLFSSL_VERSION}.spdx.json" +COMPONENT_CDX="$KIT_DIR/auditor-packet/wolfssl-component/wolfssl-${WOLFSSL_VERSION}.cdx.json" +PRODUCT_SPDX="$KIT_DIR/auditor-packet/product-acme-connect-gateway.spdx.json" +PRODUCT_CDX="$KIT_DIR/auditor-packet/product-acme-connect-gateway.cdx.json" + +COMPONENT_SPDX="$COMPONENT_SPDX" COMPONENT_CDX="$COMPONENT_CDX" \ +PRODUCT_SPDX="$PRODUCT_SPDX" PRODUCT_CDX="$PRODUCT_CDX" \ +python3 <<'PY' +import hashlib, json, os, pathlib + +component_spdx = pathlib.Path(os.environ["COMPONENT_SPDX"]) +component_cdx = pathlib.Path(os.environ["COMPONENT_CDX"]) +product_spdx = pathlib.Path(os.environ["PRODUCT_SPDX"]) +product_cdx = pathlib.Path(os.environ["PRODUCT_CDX"]) + +# --- SPDX side: pin externalDocumentRef checksum --------------------------- +spdx_digest = hashlib.sha256(component_spdx.read_bytes()).hexdigest() +doc = json.loads(product_spdx.read_text()) +refs = doc.get("externalDocumentRefs") or [] +if not refs: + raise SystemExit("product SPDX has no externalDocumentRefs") +refs[0].setdefault("checksum", {})["algorithm"] = "SHA256" +refs[0]["checksum"]["checksumValue"] = spdx_digest +product_spdx.write_text(json.dumps(doc, indent=2) + "\n") +print(f"Updated {product_spdx.name} externalDocumentRef checksum -> {spdx_digest}") + +# --- CycloneDX side: pin component externalReference hash ------------------ +cdx_digest = hashlib.sha256(component_cdx.read_bytes()).hexdigest() +prod = json.loads(product_cdx.read_text()) +patched = False +for comp in prod.get("components", []): + if comp.get("name") == "wolfssl": + for ref in comp.get("externalReferences", []): + if ref.get("type") == "bom": + ref["hashes"] = [{"alg": "SHA-256", "content": cdx_digest}] + patched = True + break + if patched: + break +if not patched: + raise SystemExit("product CDX has no wolfssl bom externalReference to pin") +product_cdx.write_text(json.dumps(prod, indent=2) + "\n") +print(f"Updated {product_cdx.name} CycloneDX bom hash -> {cdx_digest}") +PY + +"$SCRIPT_DIR/validate.sh" diff --git a/cra-kit/scripts/validate.sh b/cra-kit/scripts/validate.sh new file mode 100755 index 000000000..492599521 --- /dev/null +++ b/cra-kit/scripts/validate.sh @@ -0,0 +1,135 @@ +#!/bin/sh +# Sanity checks on the example auditor packet. +# +# Mandatory: JSON parse, SPDX externalDocumentRef checksum, CycloneDX bom hash (if pinned). +# Best-effort: CycloneDX 1.6 schema (cyclonedx-cli) and SPDX 2.3 schema (pyspdxtools) +# validation, when those tools are installed locally. +set -eu + +SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +KIT_DIR=$(dirname "$SCRIPT_DIR") +AP="$KIT_DIR/auditor-packet" +PRODUCT_CDX="$AP/product-acme-connect-gateway.cdx.json" +PRODUCT_SPDX="$AP/product-acme-connect-gateway.spdx.json" + +fail() { echo "FAIL: $*" >&2; exit 1; } +ok() { echo "OK: $*"; } + +command -v python3 >/dev/null 2>&1 || fail "python3 required" + +# shellcheck disable=SC1090 +. "$KIT_DIR/VERSION" 2>/dev/null || WOLFSSL_VERSION=5.9.1 +WOLF_CDX="$AP/wolfssl-component/wolfssl-${WOLFSSL_VERSION}.cdx.json" +WOLF_SPDX="$AP/wolfssl-component/wolfssl-${WOLFSSL_VERSION}.spdx.json" + +for f in "$PRODUCT_CDX" "$PRODUCT_SPDX" "$WOLF_CDX" "$WOLF_SPDX"; do + [ -f "$f" ] || fail "missing $f" + python3 -c "import json; json.load(open('$f'))" || fail "invalid JSON: $f" + ok "$(basename "$f") parses" +done + +# CycloneDX 1.6 serialNumber must match urn:uuid:; auditors with strict +# validators (cyclonedx-cli) reject anything else. Catch this even when the tool +# isn't installed. +PRODUCT_CDX="$PRODUCT_CDX" WOLF_CDX="$WOLF_CDX" python3 <<'PY' +import json, os, re, sys +UUID = re.compile(r"^urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", re.I) +errors = [] +for env in ("PRODUCT_CDX", "WOLF_CDX"): + path = os.environ[env] + sn = json.load(open(path)).get("serialNumber", "") + if not UUID.match(sn): + errors.append(f"{os.path.basename(path)}: serialNumber {sn!r} is not urn:uuid:") +if errors: + sys.exit("CycloneDX serialNumber violation(s):\n " + "\n ".join(errors)) +print("OK: CycloneDX serialNumbers are valid urn:uuid:") +PY + +PRODUCT_SPDX="$PRODUCT_SPDX" WOLF_SPDX="$WOLF_SPDX" python3 <<'PY' +import hashlib, json, os, sys + +product = json.load(open(os.environ["PRODUCT_SPDX"])) +wolf = open(os.environ["WOLF_SPDX"], "rb").read() +digest = hashlib.sha256(wolf).hexdigest() +refs = product.get("externalDocumentRefs") or [] +if not refs: + sys.exit("product SPDX has no externalDocumentRefs") +chk = refs[0].get("checksum", {}).get("checksumValue", "") +if chk.lower() != digest.lower(): + sys.exit( + f"SPDX checksum mismatch:\n embedded={chk}\n actual ={digest}\n" + "Run scripts/refresh-samples.sh after regenerating wolfSSL SBOM." + ) +print("OK: product SPDX checksum matches wolfssl-component SBOM") +PY + +PRODUCT_CDX="$PRODUCT_CDX" WOLF_CDX="$WOLF_CDX" python3 <<'PY' +import hashlib, json, os, sys + +prod = json.load(open(os.environ["PRODUCT_CDX"])) +wolf_bytes = open(os.environ["WOLF_CDX"], "rb").read() +digest = hashlib.sha256(wolf_bytes).hexdigest() +comps = prod.get("components") or [] +wolf = next((c for c in comps if c.get("name") == "wolfssl"), None) +if not wolf: + sys.exit("product CDX has no wolfssl component") +if not wolf.get("supplier", {}).get("name"): + sys.exit("product CDX wolfssl component has no supplier (NTIA min-elements gap)") +refs = wolf.get("externalReferences") or [] +bom = next((r for r in refs if r.get("type") == "bom"), None) +if not bom: + sys.exit("wolfssl component has no bom externalReference") +hashes = bom.get("hashes") or [] +if not hashes: + sys.exit("wolfssl component bom externalReference has no hashes (run refresh-samples.sh)") +got = hashes[0].get("content", "").lower() +if got == "to_be_pinned_by_refresh_samples": + sys.exit("wolfssl component bom hash is the unpinned placeholder; run refresh-samples.sh") +if got != digest.lower(): + sys.exit( + f"CycloneDX bom hash mismatch:\n embedded={got}\n actual ={digest}\n" + "Run scripts/refresh-samples.sh after regenerating wolfSSL SBOM." + ) +print("OK: product CycloneDX bom hash matches wolfssl-component CDX") +print("OK: product CycloneDX wolfssl component has supplier") +PY + +# ---- Optional: cyclonedx-cli schema validation ---------------------------- +CDX_TOOL= +if command -v cyclonedx-cli >/dev/null 2>&1; then + CDX_TOOL=cyclonedx-cli +elif command -v cyclonedx >/dev/null 2>&1; then + CDX_TOOL=cyclonedx +fi +if [ -n "$CDX_TOOL" ]; then + for cdx in "$PRODUCT_CDX" "$WOLF_CDX"; do + if "$CDX_TOOL" validate \ + --input-file "$cdx" \ + --input-format json \ + --input-version v1_6 \ + --fail-on-errors >/dev/null 2>&1; then + ok "$(basename "$cdx") passes CycloneDX 1.6 schema validation ($CDX_TOOL)" + else + fail "$(basename "$cdx") fails CycloneDX 1.6 schema validation ($CDX_TOOL)" + fi + done +else + echo "NOTE: cyclonedx-cli not installed; skipping CycloneDX 1.6 schema validation." + echo " Install: https://github.com/CycloneDX/cyclonedx-cli/releases" +fi + +# ---- Optional: pyspdxtools schema validation ------------------------------ +if command -v pyspdxtools >/dev/null 2>&1; then + for spdx in "$PRODUCT_SPDX" "$WOLF_SPDX"; do + if pyspdxtools -i "$spdx" >/dev/null 2>&1; then + ok "$(basename "$spdx") passes SPDX 2.3 schema validation (pyspdxtools)" + else + fail "$(basename "$spdx") fails SPDX 2.3 schema validation (pyspdxtools)" + fi + done +else + echo "NOTE: pyspdxtools not installed; skipping SPDX 2.3 schema validation." + echo " Install: pip install spdx-tools" +fi + +ok "auditor packet validation passed" diff --git a/cra-kit/user_settings.h b/cra-kit/user_settings.h new file mode 100644 index 000000000..c8cb8b0b5 --- /dev/null +++ b/cra-kit/user_settings.h @@ -0,0 +1,12 @@ +/* Demo user_settings.h for CRA Kit embedded SBOM generation. + * Production: replace with your project's user_settings.h (or point gen-sbom at it). */ +#ifndef CRA_EVIDENCE_USER_SETTINGS_H +#define CRA_EVIDENCE_USER_SETTINGS_H + +#define WOLFSSL_TLS13 +#define HAVE_AESGCM +#define HAVE_ECC +#define NO_PSK +#define NO_OLD_TLS + +#endif /* CRA_EVIDENCE_USER_SETTINGS_H */ diff --git a/cra-kit/wolfssl-inc-auditor-packet/00-INDEX.md b/cra-kit/wolfssl-inc-auditor-packet/00-INDEX.md new file mode 100644 index 000000000..d8611a417 --- /dev/null +++ b/cra-kit/wolfssl-inc-auditor-packet/00-INDEX.md @@ -0,0 +1,26 @@ +# wolfSSL Inc. CRA filings — index + +| File | CRA reference | Status | +|------|---------------|--------| +| [`classification-statement.md`](classification-statement.md) | Annex III / IV | ✅ Decided — Class I (default), self-certification | +| [`conformity-assessment-route.md`](conformity-assessment-route.md) | Art. 32, Annex VIII | ✅ Module A self-assessment | +| [`declaration-of-conformity.template.md`](declaration-of-conformity.template.md) | Art. 28 | 🟡 Template ready; signature pending product release alignment | +| [`eu-authorised-representative.md`](eu-authorised-representative.md) | Art. 18 | 🟠 In progress — appointment underway | +| [`support-period-policy.md`](support-period-policy.md) | Art. 13(2), 13(8) | ✅ Decided — 5-year minimum, longer for LTS lines | +| [`vulnerability-handling-process.md`](vulnerability-handling-process.md) | Art. 13, 14 | 🟡 Process documented; public SLA pending leadership approval | +| [`technical-documentation-outline.md`](technical-documentation-outline.md) | Annex VII | 🟠 In progress — outline complete; per-release packet on roadmap | +| [`ce-marking-statement.md`](ce-marking-statement.md) | Art. 30 | 🟡 Will affix on first CRA-applicable release after 11 Dec 2027 | + +## Reading order for new customers + +1. **`classification-statement.md`** — what wolfSSL is (and isn't) under Annex III/IV +2. **`conformity-assessment-route.md`** — why Module A self-assessment fits this classification +3. **`vulnerability-handling-process.md`** — the only continuous obligation +4. **`support-period-policy.md`** — what we commit to maintain, for how long +5. **`eu-authorised-representative.md`** — how a US-established manufacturer satisfies Art. 18 +6. **`declaration-of-conformity.template.md`** + **`technical-documentation-outline.md`** + **`ce-marking-statement.md`** — the formal output + +## CRA timeline anchors + +- **11 Sep 2026** — Art. 14 vulnerability reporting obligations start (24h ENISA early-warning, 72h follow-up, 14-day final report). +- **11 Dec 2027** — Full CRA applicability; conformity assessment, CE marking, declaration of conformity, technical documentation, and support-period commitments all in force for products placed on the EU market from this date. diff --git a/cra-kit/wolfssl-inc-auditor-packet/README.md b/cra-kit/wolfssl-inc-auditor-packet/README.md new file mode 100644 index 000000000..f038652cc --- /dev/null +++ b/cra-kit/wolfssl-inc-auditor-packet/README.md @@ -0,0 +1,49 @@ +# wolfSSL Inc. — manufacturer-side CRA filings + +This directory shows what wolfSSL Inc. itself ships **as the manufacturer** +for libraries it places on the EU market under the Cyber Resilience Act +(Regulation (EU) 2024/2847). The customer-facing +[`auditor-packet/`](../auditor-packet/) shows what **a customer** assembles +when they ship a product containing wolfSSL; this packet is its mirror +image — what we file ourselves. + +**Why this exists.** Earlier versions of the kit told customers to declare +themselves manufacturers, appoint EU Authorised Representatives, classify +their products under Annex III/IV, and run ENISA reporting rotations — +without showing what wolfSSL had done on any of those fronts. The kit's +audience reasonably read that as *"do as we say, not as we do."* This +directory closes that gap. Where a decision is made, it is stated. +Where a decision is in flight, the placeholder names what is missing +and why, so customers can see the work in progress rather than a polished +fiction. + +**Status conventions used below:** + +- ✅ **Decided & published** — wolfSSL Inc. has made and published this decision. +- 🟡 **Decided internally, publication pending** — internal sign-off; awaits final review. +- 🟠 **In progress** — actively being worked on; target dates given where known. +- ⏳ **Pending leadership decision** — the call has not yet been made. + +**Not legal advice.** These artefacts are templates and statements of position; +they are not, and do not replace, the actual signed legal documents wolfSSL Inc. +files with EU regulators or its EU Authorised Representative. + +--- + +## Contents + +See [`00-INDEX.md`](00-INDEX.md) for the file list and CRA article mapping. + +## Use as a template + +Customers shipping their own products into the EU can copy the structure here, +fill in their own product details, and adapt the placeholders. Where wolfSSL +Inc.'s position is firm (e.g. Class I self-certification per Art. 32 Module A +for the wolfSSL library), the supporting reasoning is included so customers can +calibrate their own decisions. + +## Customer-facing analogue + +If you are looking for the customer-side worked example (a fictional product, +*Acme Connect Gateway*, that includes wolfSSL), see +[`../auditor-packet/`](../auditor-packet/). diff --git a/cra-kit/wolfssl-inc-auditor-packet/ce-marking-statement.md b/cra-kit/wolfssl-inc-auditor-packet/ce-marking-statement.md new file mode 100644 index 000000000..3b5556ecf --- /dev/null +++ b/cra-kit/wolfssl-inc-auditor-packet/ce-marking-statement.md @@ -0,0 +1,64 @@ +# CE marking — wolfSSL libraries + +**Status:** 🟡 Will affix from first CRA-applicable release after 11 Dec 2027 +**CRA reference:** Art. 30 (rules and conditions for affixing the CE marking) + +## Decision + +wolfSSL Inc. will affix the CE marking to wolfSSL libraries placed on the EU +market from **11 Dec 2027** (full CRA applicability date) onwards, having +completed the Annex VIII Module A self-assessment per +[`conformity-assessment-route.md`](conformity-assessment-route.md). + +## How CE marking is affixed for software products + +CRA Art. 30 specifies that the CE marking shall be affixed visibly, legibly, +and indelibly. For software products that lack a physical surface, the +established practice (per the Blue Guide on the implementation of EU product +rules) is to affix the marking: + +1. **In the documentation** that accompanies the product (release notes, README, or a dedicated `LEGAL/` directory in the release tarball). +2. **On the website** where the product is downloaded or distributed (`wolfssl.com` product page). +3. **In a machine-readable form**, where applicable (e.g. as a property in the SBOM). + +wolfSSL will use all three locations. + +## What CE marking represents + +The CE marking is the manufacturer's declaration that: + +- The product conforms to all applicable Union harmonisation legislation (here, the CRA and any other EU acts that apply, e.g. RED if shipped as part of radio equipment). +- The conformity assessment procedure has been completed (Module A self-assessment). +- A declaration of conformity (Art. 28) has been drawn up and signed. +- Technical documentation (Annex VII) is held and available to authorities on request. + +It is **not** a quality mark, a certification, or a guarantee. It is a +manufacturer's self-declaration of regulatory conformity. + +## Where the CE mark will appear in wolfSSL releases + +- `LEGAL/CE-marking.txt` — text statement plus the CE logo (PDF) in the release tarball +- `wolfssl-.cdx.json` — `metadata.properties[].name = "wolfssl:ce-marking"`, value "applied" with date +- Release notes — visible CE statement section +- wolfssl.com release page — CE marking image alongside download link + +## What this means for customers + +If you ship a finished product into the EU containing wolfSSL, you affix CE +marking to **your finished product**, not to the wolfSSL component. Your CE +marking is backed by **your** declaration of conformity, **your** technical +documentation, and **your** conformity assessment. wolfSSL's component-level +CE marking does not transfer to your product. + +If your finished product is also subject to other CE-required directives +(e.g. the Radio Equipment Directive, Machinery Regulation), the CE marking +covers all applicable acts collectively — list each in your declaration of +conformity. + +## References + +- CRA Art. 30 (CE marking) +- CRA Art. 28 (Declaration of conformity) +- Commission Notice "The Blue Guide on the implementation of EU product rules" +- [`conformity-assessment-route.md`](conformity-assessment-route.md) +- [`declaration-of-conformity.template.md`](declaration-of-conformity.template.md) diff --git a/cra-kit/wolfssl-inc-auditor-packet/classification-statement.md b/cra-kit/wolfssl-inc-auditor-packet/classification-statement.md new file mode 100644 index 000000000..0276710d3 --- /dev/null +++ b/cra-kit/wolfssl-inc-auditor-packet/classification-statement.md @@ -0,0 +1,55 @@ +# Classification statement — wolfSSL libraries (Annex III / IV) + +**Status:** ✅ Decided & published +**CRA reference:** Annex III, Annex IV; Art. 6 (classes of products with digital elements) + +## Decision + +wolfSSL Inc. classifies the following products as **default class** ("Class I") +for CRA purposes: + +| Product | Classification | Rationale | +|---------|----------------|-----------| +| **wolfSSL** (TLS library) | **Default class** (not Annex III, not Annex IV) | A general-purpose TLS / cryptographic library is not a finished product type listed in Annex III or Annex IV. The library is integrated by manufacturers into their own products; those manufacturers carry the classification of their finished product. | +| **wolfCrypt** (cryptographic library) | **Default class** | Same reasoning. FIPS 140-3 validation of wolfCrypt does not change CRA classification — FIPS validates the cryptographic module against US/Canadian government standards, not against EU CRA Annex III/IV criteria. | +| **wolfBoot** (secure bootloader) | **Default class** | Bootloader software shipped as a library or reference image is integrated into a hardware product whose manufacturer classifies the finished device. | +| **wolfSSH** (SSH library) | **Default class** | Library, not a finished SSH server product. | +| **wolfMQTT** (MQTT library) | **Default class** | Library, not a finished broker/client product. | + +## Reasoning + +Annex III and Annex IV list **finished product categories** (password managers, +network management systems, browsers, hardware security modules, smart meters +of certain types, etc.). wolfSSL Inc. does not ship any such finished product +on the EU market. Customers integrate our libraries into their own products +and place those finished products on the EU market under their own brand — +those customers carry the Annex III/IV classification of the finished product +they ship. + +If a customer's product falls into Annex III or IV, the customer's conformity +assessment route is determined by **their** product's classification, not by +the classification of the library they integrate. wolfSSL provides component +SBOMs, security advisories, CVD policy, vulnerability handling, and technical +support that customers can incorporate into their own conformity assessment. + +## Counter-example + +Were wolfSSL Inc. to ship, for example, a turnkey **password manager** product +under its own brand on the EU market, that product would be Annex III ("important") +and would require Notified Body involvement in conformity assessment. We do not +ship such a product. + +## What this means for customers + +If you ship a product on the EU market that contains wolfSSL, classify your +**finished product** under Annex III/IV — not the wolfSSL library inside it. +If your finished product is default class, you can self-assess (Module A); if +it's Annex III or IV, your route may require a Notified Body. wolfSSL's +classification doesn't determine yours. + +## References + +- [`../CRA-Compliance-Shortlist.md`](../CRA-Compliance-Shortlist.md) — "Beyond this kit (structural CRA obligations)" +- [`../CRA-Supply-Chain-Glossary.md`](../CRA-Supply-Chain-Glossary.md) — Annex III, Annex IV, Notified Body definitions +- CRA text Annex III: list of important products +- CRA text Annex IV: list of critical products diff --git a/cra-kit/wolfssl-inc-auditor-packet/conformity-assessment-route.md b/cra-kit/wolfssl-inc-auditor-packet/conformity-assessment-route.md new file mode 100644 index 000000000..e86cef153 --- /dev/null +++ b/cra-kit/wolfssl-inc-auditor-packet/conformity-assessment-route.md @@ -0,0 +1,56 @@ +# Conformity assessment route — wolfSSL libraries + +**Status:** ✅ Decided & published (route only; per-release execution begins 11 Dec 2027) +**CRA reference:** Art. 32, Annex VIII + +## Decision + +wolfSSL Inc. follows **Annex VIII Module A — internal control (self-assessment)** +for libraries it places on the EU market. + +## Why Module A + +Module A is the appropriate route when: + +- The product is **default class** under Annex III/IV (see [`classification-statement.md`](classification-statement.md)). +- The manufacturer maintains internal documentation of design, risk assessment, and conformity testing. +- No Notified Body involvement is required. + +All three apply to wolfSSL libraries. + +## What Module A requires + +Module A obligates wolfSSL Inc. to: + +1. **Maintain technical documentation** per Annex VII covering each released library version. See [`technical-documentation-outline.md`](technical-documentation-outline.md). +2. **Take all necessary measures** so each library version conforms to CRA essential requirements (Annex I). +3. **Affix the CE marking** to each conformant version (or, for software products, include it in the documentation that accompanies the product). See [`ce-marking-statement.md`](ce-marking-statement.md). +4. **Draw up and sign a written declaration of conformity** (Art. 28). See [`declaration-of-conformity.template.md`](declaration-of-conformity.template.md). +5. **Keep technical documentation and the declaration** for **10 years** after the product is placed on the EU market (or for the duration of the support period, whichever is longer). + +## Notified Body engagement — not used + +Notified Body involvement is required when a product is classified as +**Annex III "important class II"** or **Annex IV "critical"**. wolfSSL libraries +are neither. We have evaluated TÜV Süd as a Notified Body candidate (per +internal correspondence with our DACH team and a customer recommendation in +May 2026) and concluded that engagement is not required for the libraries +themselves. Customers whose finished products fall into Annex III/IV may +engage a Notified Body for **their own** product; wolfSSL provides component +SBOMs, advisories, and CVD documentation that the customer's Notified Body +can incorporate. + +## What this means for customers + +If your finished product is default class, you follow Module A like we do. +If your finished product is Annex III or IV, you may need a Notified Body +for your product — wolfSSL's component artefacts (SBOMs, CVD policy, +advisories, support-period statement) feed into your Notified Body +submission as supplier evidence. + +## References + +- CRA Art. 32: conformity assessment procedures +- CRA Annex VIII: conformity assessment modules (Module A is internal control) +- CRA Annex I: essential cybersecurity requirements +- [`../CRA-Supply-Chain-Glossary.md`](../CRA-Supply-Chain-Glossary.md) — Module A, Conformity assessment, Notified Body diff --git a/cra-kit/wolfssl-inc-auditor-packet/declaration-of-conformity.template.md b/cra-kit/wolfssl-inc-auditor-packet/declaration-of-conformity.template.md new file mode 100644 index 000000000..5fdf88150 --- /dev/null +++ b/cra-kit/wolfssl-inc-auditor-packet/declaration-of-conformity.template.md @@ -0,0 +1,77 @@ +# Declaration of conformity — template + +**Status:** 🟡 Template ready; per-release signed declarations begin 11 Dec 2027 +**CRA reference:** Art. 28, Annex V (declaration of conformity contents) + +This template will be customised and signed for each conformant wolfSSL release +placed on the EU market from 11 Dec 2027 onwards. Customers may adapt this +template for their own products. + +--- + +## EU Declaration of Conformity + +**1. Product identification** + +- Name: [PRODUCT NAME, e.g. wolfSSL] +- Version: [VERSION, e.g. 5.9.1] +- Type: [TYPE, e.g. cryptographic / TLS library, software product placed on the market] +- Unique identifier: [PURL, e.g. `pkg:github/wolfSSL/wolfssl@v5.9.1`] + +**2. Manufacturer** + +- Name: wolfSSL Inc. +- Postal address: [WOLFSSL INC. REGISTERED OFFICE — to be filled] +- Email: [TO BE FILLED — kept synchronised with `/.well-known/security.txt` once wolfSSL Inc.'s security alias is provisioned] +- Website: https://www.wolfssl.com/ + +**3. EU Authorised Representative** (Art. 18, required for non-EU manufacturers) + +- Name: [TO BE FILLED — see `eu-authorised-representative.md`] +- Postal address: [TO BE FILLED] +- Mandate effective date: [TO BE FILLED] + +**4. Object of the declaration** + +This declaration of conformity is issued under the sole responsibility of the +manufacturer and applies to the object of the declaration described in +section 1. + +**5. Conformity statement** + +The object of the declaration described above is in conformity with the +relevant Union harmonisation legislation: + +- **Regulation (EU) 2024/2847 (Cyber Resilience Act)** — essential requirements set out in Annex I. + +**6. References to relevant standards or specifications** + +- [HARMONISED STANDARDS USED, once published — likely candidates: EN 18031 series, ETSI EN 303 645 for IoT-relevant deployments] +- Or, where harmonised standards are not yet available: a description of the technical specifications applied (see Annex VII technical documentation). + +**7. Conformity assessment procedure** + +Annex VIII **Module A — internal control** (see [`conformity-assessment-route.md`](conformity-assessment-route.md)). +No Notified Body involvement required for default-class products. + +**8. Additional information** + +- Software bill of materials: see corresponding `wolfssl-.cdx.json` and `.spdx.json` (released alongside the binary). +- Vulnerability handling process: [`vulnerability-handling-process.md`](vulnerability-handling-process.md) and [https://www.wolfssl.com/.well-known/vulnerability-disclosure-policy.txt](https://www.wolfssl.com/.well-known/vulnerability-disclosure-policy.txt). +- Support period: [`support-period-policy.md`](support-period-policy.md). + +**9. Signature** + +- Place: [LOCATION OF ISSUE] +- Date: [DATE OF ISSUE] +- Name and function: [SIGNATORY NAME, e.g. Larry Stefonic, CEO, wolfSSL Inc.] +- Signature: ___________________ + +--- + +## Notes for customers adapting this template + +1. Fields in `[BRACKETS]` must be filled before signature. +2. The declaration must be drawn up in **at least one of the official languages** of the Member State where the product is placed on the market. English is generally accepted but verify with your EU Authorised Representative. +3. The signed declaration is part of the **technical documentation** (Annex VII) and must be retained for **10 years**. +4. The declaration accompanies the product. For software products, this typically means including it in the release tarball, in a `LEGAL/` directory, or alongside the SBOMs. diff --git a/cra-kit/wolfssl-inc-auditor-packet/eu-authorised-representative.md b/cra-kit/wolfssl-inc-auditor-packet/eu-authorised-representative.md new file mode 100644 index 000000000..36c1fe234 --- /dev/null +++ b/cra-kit/wolfssl-inc-auditor-packet/eu-authorised-representative.md @@ -0,0 +1,63 @@ +# EU Authorised Representative — wolfSSL Inc. + +**Status:** 🟠 In progress — appointment underway; target completion before 11 Sep 2026 +**CRA reference:** Art. 18 + +## Why an EU AR is required + +wolfSSL Inc. is established in the **United States** (Edmonds, Washington). CRA +Art. 18 requires manufacturers established outside the EU to appoint, **in +writing**, an Authorised Representative inside the EU before placing a product +on the EU market. The AR: + +- Receives correspondence from EU market surveillance authorities and ENISA on the manufacturer's behalf. +- Holds the technical documentation (Annex VII) and declaration of conformity (Art. 28) for **10 years** post-placement, available to authorities on request. +- Cooperates with authorities on corrective action where the product presents a cybersecurity risk. + +The AR does **not** transfer manufacturer obligations — wolfSSL Inc. remains +the manufacturer and bears the substantive obligations. The AR is a single +point of contact in the EU. + +## Current state + +🟠 **wolfSSL Inc. is finalising the EU AR appointment.** Two paths were evaluated: + +1. **Use an existing wolfSSL EU presence.** wolfSSL has business operations in + the DACH region (Germany / Austria / Switzerland). Nominating an existing + EU-resident wolfSSL legal entity as the AR is the simplest path if such an + entity exists with the appropriate legal capacity to act as AR. +2. **Contract a third-party AR service.** Several vendors (e.g. Obelis, Authrep, + Casa Group) offer AR-as-a-service across CE-marking regulations. Cost is + typically EUR 1500–4000/year per regulation; lead time 4–6 weeks. + +The internal call was made by wolfSSL leadership in [DATE TO BE CONFIRMED]. The +written mandate will be in place before 11 Sep 2026 (Art. 14 vulnerability +reporting onset) and certainly before 11 Dec 2027 (full CRA applicability). + +## Placeholder identity + +Once the appointment is signed: + +- **Name:** [TO BE FILLED] +- **Address:** [TO BE FILLED] +- **Email:** [TO BE FILLED] +- **Mandate effective date:** [TO BE FILLED] +- **Mandate scope:** all wolfSSL libraries placed on the EU market by wolfSSL Inc. under CRA. + +## What this means for customers + +If your company is established **outside the EU** (US / UK post-Brexit / Asia / +elsewhere), you face the same Art. 18 obligation. wolfSSL's choice of AR does +not satisfy your obligation — you appoint your own. + +The single-most-important advice we can give: **start now**. AR appointments +take weeks to months including legal review on both sides; the lead time +compounds with conformity assessment timelines and is the most common +last-minute blocker for non-EU manufacturers. + +## References + +- CRA Art. 18 (Authorised Representative) +- CRA Art. 19 (Importer obligations) — what an EU importer carries if no AR is in place +- [`../CRA-Compliance-Shortlist.md`](../CRA-Compliance-Shortlist.md) — "Beyond this kit" +- [`../CRA-Supply-Chain-Glossary.md`](../CRA-Supply-Chain-Glossary.md) — EU Authorised Representative diff --git a/cra-kit/wolfssl-inc-auditor-packet/support-period-policy.md b/cra-kit/wolfssl-inc-auditor-packet/support-period-policy.md new file mode 100644 index 000000000..9d83c9a8c --- /dev/null +++ b/cra-kit/wolfssl-inc-auditor-packet/support-period-policy.md @@ -0,0 +1,54 @@ +# Support-period policy — wolfSSL libraries + +**Status:** ✅ Decided & published +**CRA reference:** Art. 13(2), Art. 13(8) + +## Commitment + +wolfSSL Inc. commits to providing **free security updates** for wolfSSL +libraries for a **minimum of 5 years** from the release date of each version +placed on the EU market under CRA, in accordance with Art. 13(2) and 13(8). + +For versions designated **Long-Term Support (LTS)**, the support period is +extended to match the LTS commitment, which is currently up to **10 years** for +specific releases (e.g. those certified to FIPS 140-3 or covered by commercial +LTS contracts). + +## Scope of "security update" + +A security update under this policy is any release that: + +- Addresses a vulnerability disclosed via wolfSSL's [Coordinated Vulnerability Disclosure policy](https://www.wolfssl.com/.well-known/vulnerability-disclosure-policy.txt) or assigned a CVE by wolfSSL as a CNA; +- Is published as a tagged GitHub release with accompanying SBOM (`*.cdx.json`, `*.spdx.json`) and security advisory; +- Carries the same conformity assessment as the original release (Module A self-assessment, see [`conformity-assessment-route.md`](conformity-assessment-route.md)). + +Feature updates are not security updates and are not in scope of this commitment. + +## Release line policy + +| Release line | Support period | Notes | +|--------------|----------------|-------| +| Mainline releases | **5 years** from release date | Default per Art. 13(8) | +| LTS releases | **10 years** from release date | Designated explicitly at release time | +| FIPS 140-3-certified versions | Bound to FIPS certificate validity | May extend or shorten depending on NIST recertification | +| Commercial-license customers | Per commercial agreement | Often extends past CRA minimum; never less than CRA minimum | + +## Where this is published + +- This policy file (committed to [github.com/wolfSSL/wolfssl-examples](https://github.com/wolfSSL/wolfssl-examples)). +- Each per-release declaration of conformity references the support period applicable to that release. +- Customer-visible: [wolfSSL release notes](https://github.com/wolfSSL/wolfssl/releases) note the support window. + +## What this means for customers + +If you embed a wolfSSL release in your product: + +- **Match or exceed** wolfSSL's support window in your own product's support-period commitment. CRA does not allow a customer to commit to a shorter support window than they can actually deliver — if your product's commitment is 7 years, you cannot rely on a wolfSSL version with only 5 years of remaining support. +- **Plan upgrades** before wolfSSL's support window for your embedded version expires. +- **Consider an LTS version** if your product's support window is 7+ years, or **a commercial-license LTS contract** if you need supplier-side support beyond the public commitment. + +## References + +- CRA Art. 13(2): support period default 5 years (or product expected lifetime if longer) +- CRA Art. 13(8): vulnerability handling effectiveness during support period +- [`../CRA-Compliance-Shortlist.md`](../CRA-Compliance-Shortlist.md) — pillar 4 + "Beyond this kit" diff --git a/cra-kit/wolfssl-inc-auditor-packet/technical-documentation-outline.md b/cra-kit/wolfssl-inc-auditor-packet/technical-documentation-outline.md new file mode 100644 index 000000000..98d0409f5 --- /dev/null +++ b/cra-kit/wolfssl-inc-auditor-packet/technical-documentation-outline.md @@ -0,0 +1,88 @@ +# Technical documentation outline — Annex VII + +**Status:** 🟠 In progress — outline complete; per-release populated documents on roadmap +**CRA reference:** Annex VII (technical documentation contents) + +CRA Annex VII enumerates the contents of the technical documentation file that +manufacturers must maintain (and retain for **10 years** after market placement) +for each conformant product. This file is not made public; it is held by the +manufacturer (and the EU AR) and produced to authorities on request. + +## Outline of wolfSSL Inc.'s per-release technical documentation file + +For each wolfSSL library version placed on the EU market under CRA, the +following sections are populated: + +### 1. General description + +- Product name, version, intended purpose +- Variants and configurations (e.g. FIPS-validated build, embedded build, commercial-license build) +- Identification of integrated components (the wolfSSL SBOM itself) + +### 2. Design and manufacturing + +- Architectural description (TLS state machine, cryptographic API surfaces, build system) +- Source-tree organisation (where to find what) +- Build instructions and reproducibility settings (`SOURCE_DATE_EPOCH`, `make sbom`, `make bomsh`) +- Reference to the SBOM: `wolfssl-.cdx.json`, `.spdx.json` + +### 3. Cybersecurity risk assessment + +- Threat model: what wolfSSL is designed to protect, what it is not +- Attack surface analysis (network-facing TLS handshake, parser surfaces, key management) +- Risk-mitigation choices (timing-resistance flags, side-channel hardening, deprecated algorithm exclusions) +- Reference to relevant external assessments (FIPS 140-3 Cryptographic Module Validation Program reports, third-party penetration tests where commissioned) + +### 4. List of harmonised standards applied + +- [TO BE FILLED once CRA harmonised standards are published] +- Where standards are not available: technical specifications applied (e.g. RFC 5246, RFC 8446 for TLS; FIPS 140-3 for the FIPS-validated build) + +### 5. Conformity assessment route + +- Annex VIII Module A (self-assessment) — see [`conformity-assessment-route.md`](conformity-assessment-route.md) + +### 6. Vulnerability handling + +- CVD policy (link to `/.well-known/vulnerability-disclosure-policy.txt`) +- Process narrative (see [`vulnerability-handling-process.md`](vulnerability-handling-process.md)) +- Per-release: any open advisories at time of release, with their CVE IDs + +### 7. Support-period commitment + +- See [`support-period-policy.md`](support-period-policy.md) +- Per-release: explicit support window dates + +### 8. Declaration of conformity + +- Signed declaration per Art. 28 — see [`declaration-of-conformity.template.md`](declaration-of-conformity.template.md) + +### 9. Software bill of materials + +- `wolfssl-.cdx.json` (CycloneDX 1.6) +- `wolfssl-.spdx.json` (SPDX 2.3) +- Optional: `omnibor.wolfssl-.spdx.json` (build provenance via `make bomsh`) +- Optional: `wolfssl-.cbom-draft.cdx.json` (cryptographic-asset draft) + +### 10. CE marking + +- See [`ce-marking-statement.md`](ce-marking-statement.md) + +## Retention + +- **10 years** from the date the product is placed on the EU market, or for the duration of the support period (whichever is longer). +- Held by wolfSSL Inc. **and** the EU Authorised Representative ([`eu-authorised-representative.md`](eu-authorised-representative.md)). + +## What this means for customers + +You maintain a parallel Annex VII file for **your** finished product. wolfSSL's +component artefacts (SBOMs, advisories, CVD policy, support-period commitment) +populate the **upstream component** sections of your file; you populate the +finished-product sections (architecture, threat model, conformity assessment). +Our file is not yours; yours integrates ours. + +## References + +- CRA Annex VII (technical documentation) +- CRA Art. 31 (technical documentation retention) +- [`../CRA-Compliance-Shortlist.md`](../CRA-Compliance-Shortlist.md) — Annex VII row in "Beyond this kit" diff --git a/cra-kit/wolfssl-inc-auditor-packet/vulnerability-handling-process.md b/cra-kit/wolfssl-inc-auditor-packet/vulnerability-handling-process.md new file mode 100644 index 000000000..c6b1644a0 --- /dev/null +++ b/cra-kit/wolfssl-inc-auditor-packet/vulnerability-handling-process.md @@ -0,0 +1,95 @@ +# Vulnerability handling process — wolfSSL Inc. + +**Status:** 🟡 Process documented; public SLA pending leadership approval +**CRA reference:** Art. 13 (vulnerability handling), Art. 14 (active-exploitation reporting) + +## Discovery → report → triage → fix → disclosure + +``` + ┌────────────────────────┐ + │ External report │ + │ · security.txt │ + │ · GitHub Security tab │ + │ · Customer support │ + └──────────┬─────────────┘ + │ + ▼ + ┌────────────────────────┐ + │ wolfSSL PSIRT (rotating│ + │ on-call, target 24h │ + │ acknowledgement) │ + └──────────┬─────────────┘ + │ + ┌──────────┴─────────────┐ + ▼ ▼ + ┌────────────────┐ ┌──────────────────┐ + │ Triage (72h): │ │ Active exploit? │ + │ severity, CVSS,│ ────▶ │ Yes ─▶ ENISA 24h │ + │ scope, fix plan│ │ No ─▶ standard │ + └────────┬───────┘ └──────────────────┘ + │ + ▼ + ┌────────────────┐ + │ Fix + advisory │ + │ (CVE assigned │ + │ as CNA) │ + └────────┬───────┘ + │ + ▼ + ┌────────────────┐ + │ Coordinated │ + │ disclosure + │ + │ release │ + └────────────────┘ +``` + +## Public-facing artefacts + +| Artefact | Location | Purpose | +|----------|----------|---------| +| `security.txt` (RFC 9116) | [`/.well-known/security.txt`](https://www.wolfssl.com/.well-known/security.txt) | Single canonical contact entry; researchers reach the right inbox without guessing | +| Coordinated Vulnerability Disclosure policy | [`/.well-known/vulnerability-disclosure-policy.txt`](https://www.wolfssl.com/.well-known/vulnerability-disclosure-policy.txt) | How wolfSSL handles reports: scope, expectations, safe-harbor | +| Security advisories | [https://www.wolfssl.com/docs/security-vulnerabilities/](https://www.wolfssl.com/docs/security-vulnerabilities/) | Per-CVE narrative, affected versions, mitigations | +| CVE Numbering Authority | wolfSSL is a [CNA](https://www.cve.org/PartnerInformation/ListofPartners) | wolfSSL assigns CVE IDs within the wolfSSL libraries scope | + +## Service-level targets (proposed; pending leadership approval) + +| Stage | Target | Notes | +|-------|--------|-------| +| Acknowledgement of receipt | **24 hours** | From any channel listed in `security.txt`. Pending public approval to commit. | +| Initial triage (severity, validity, fix plan) | **72 hours** | Pending public approval to commit. | +| ENISA early-warning notification | **24 hours from awareness of active exploitation** (Art. 14(1)) | Hard regulatory deadline — not negotiable. | +| ENISA follow-up report | **72 hours from awareness** (Art. 14(2)) | Hard regulatory deadline. | +| ENISA final report | **14 days from CSIRT notification of CVE-published or vendor-published advisory** (Art. 14(3)) | Hard regulatory deadline. | +| Coordinated public disclosure | Typically 90 days from triage; case-by-case | Negotiable with reporter. | + +These targets are not yet publicly committed in the CVD policy. Once the +leadership decision is taken, the CVD policy at `/.well-known/vulnerability-disclosure-policy.txt` +will be updated to include them. + +## On-call coverage + +🟠 **In progress.** Continuous 24/7/365 coverage including weekends and +holidays is the only Art. 14 obligation that requires sustained staffing, +not a one-time deliverable. Owner assignment and rotation policy are +under leadership discussion. + +The current interim arrangement is a single primary contact during business +hours plus a documented escalation path; this does not satisfy the 24h ENISA +clock for incidents reported overnight or on holidays. Closing this gap +before 11 Sep 2026 is the highest-priority action item in this packet. + +## What this means for customers + +When you ship a product containing wolfSSL: + +- **Your own pillar-4 obligation is independent of ours.** You publish your own `security.txt`, your own CVD policy, run your own on-call. Our process does not satisfy yours. +- **Coordinate on shared advisories.** When wolfSSL issues an advisory affecting versions you ship, we will (where possible) coordinate with downstream manufacturers via the CNA process. Subscribe to wolfSSL release notes / advisories so you see them promptly. +- **ENISA reporting is split.** wolfSSL Inc. files for libraries it places on the EU market by name; **you** file for your finished product. The 24h clock starts from each manufacturer's awareness independently. + +## References + +- CRA Art. 13: vulnerability handling, support period, security updates +- CRA Art. 14: notification obligations (24h, 72h, 14 days) +- [`../CRA-Compliance-Shortlist.md`](../CRA-Compliance-Shortlist.md) — pillar 4 +- [`../CRA-Supply-Chain-Glossary.md`](../CRA-Supply-Chain-Glossary.md) — ENISA, CNA, Conformity assessment From 04f19f8b2e0b53ec4d8769f237fa137a575900ec Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Mon, 15 Jun 2026 13:54:11 +0300 Subject: [PATCH 02/27] cra-kit: address Skoll review findings Fail early when the wolfSSL tree version differs from the kit's pinned VERSION instead of a cryptic cp error, trap-clean the embedded temp defines file on all exit paths, add sh -n + shellcheck CI coverage for the scripts, and use the standard file:./ relative form in the product SBOM external references. Signed-off-by: Sameeh Jubran --- .github/workflows/cra-kit.yml | 8 ++++++++ .../product-acme-connect-gateway.cdx.json | 2 +- .../product-acme-connect-gateway.spdx.json | 2 +- cra-kit/scripts/generate-wolfssl-sbom.sh | 20 +++++++++++++++++-- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cra-kit.yml b/.github/workflows/cra-kit.yml index 5fa4d6141..0b10c2f23 100644 --- a/.github/workflows/cra-kit.yml +++ b/.github/workflows/cra-kit.yml @@ -20,3 +20,11 @@ jobs: python-version: '3.x' - name: Validate pinned auditor packet run: ./cra-kit/scripts/validate.sh + - name: Shell syntax check (sh -n) + run: | + for s in cra-kit/scripts/*.sh; do + echo "sh -n $s" + sh -n "$s" + done + - name: ShellCheck scripts + run: shellcheck cra-kit/scripts/*.sh diff --git a/cra-kit/auditor-packet/product-acme-connect-gateway.cdx.json b/cra-kit/auditor-packet/product-acme-connect-gateway.cdx.json index 796567689..f8c02effd 100644 --- a/cra-kit/auditor-packet/product-acme-connect-gateway.cdx.json +++ b/cra-kit/auditor-packet/product-acme-connect-gateway.cdx.json @@ -30,7 +30,7 @@ "externalReferences": [ { "type": "bom", - "url": "file:wolfssl-component/wolfssl-5.9.1.cdx.json", + "url": "file:./wolfssl-component/wolfssl-5.9.1.cdx.json", "comment": "Component SBOM from wolfSSL; regenerate with scripts/generate-wolfssl-sbom.sh", "hashes": [ { diff --git a/cra-kit/auditor-packet/product-acme-connect-gateway.spdx.json b/cra-kit/auditor-packet/product-acme-connect-gateway.spdx.json index d67a451af..73b0b39ca 100644 --- a/cra-kit/auditor-packet/product-acme-connect-gateway.spdx.json +++ b/cra-kit/auditor-packet/product-acme-connect-gateway.spdx.json @@ -13,7 +13,7 @@ "externalDocumentRefs": [ { "externalDocumentId": "DocumentRef-wolfssl", - "spdxDocument": "file:wolfssl-component/wolfssl-5.9.1.spdx.json", + "spdxDocument": "file:./wolfssl-component/wolfssl-5.9.1.spdx.json", "checksum": { "algorithm": "SHA256", "checksumValue": "36fdc0c8a192a0fadc4c5024ff75ecee3a56dd8a431dfb25bfa8afcf467cfdef" diff --git a/cra-kit/scripts/generate-wolfssl-sbom.sh b/cra-kit/scripts/generate-wolfssl-sbom.sh index b8b5b0117..6a27005fc 100755 --- a/cra-kit/scripts/generate-wolfssl-sbom.sh +++ b/cra-kit/scripts/generate-wolfssl-sbom.sh @@ -117,6 +117,10 @@ _run_embedded() { echo " fallback reflects target macros, not the host's." DEFINES_H="$OUT_DIR/.wolfssl-defines-$$.h" + # Clean up the temp defines file on every exit path, including a failing + # generator run (it previously leaked the dotfile under `set -e` if the + # final gen-sbom invocation failed before the manual `rm -f`). + trap 'rm -f "$DEFINES_H"' EXIT CC=${CC:-cc} if ! "$CC" -dM -E \ -I"$WOLFSSL_DIR" \ @@ -124,7 +128,6 @@ _run_embedded() { -DWOLFSSL_USER_SETTINGS \ -include "$SETTINGS_H" \ -x c /dev/null >"$DEFINES_H" 2>/dev/null; then - rm -f "$DEFINES_H" echo "ERROR: $CC -dM -E failed; install pcpp or set CC to your cross-compiler." >&2 exit 1 fi @@ -137,11 +140,24 @@ _run_embedded() { --license-file "$WOLFSSL_DIR/LICENSING" \ --options-h "$DEFINES_H" \ --srcs $@ - rm -f "$DEFINES_H" } _run_autotools() { echo "==> Autotools path: make sbom" + # `make sbom` names its output after the wolfSSL TREE's version + # (PACKAGE_VERSION), not the kit's pinned VERSION. If they differ, the + # `cp` below would otherwise fail with a cryptic "No such file or + # directory" under `set -eu`. Detect the mismatch early and explain it. + _tree_ver=$(sed -n \ + 's/.*LIBWOLFSSL_VERSION_STRING[[:space:]]*"\([^"]*\)".*/\1/p' \ + "$WOLFSSL_DIR/wolfssl/version.h" 2>/dev/null || true) + if [ -n "$_tree_ver" ] && [ "$_tree_ver" != "$VERSION" ]; then + echo "ERROR: wolfSSL tree is version $_tree_ver but the kit is pinned to $VERSION." >&2 + echo " 'make sbom' emits wolfssl-${_tree_ver}.* while the pinned auditor" >&2 + echo " packet references wolfssl-${VERSION}.*. Check out a wolfSSL $VERSION" >&2 + echo " tree, or update cra-kit/VERSION (and the pinned sample references)." >&2 + exit 1 + fi (cd "$WOLFSSL_DIR" && { if [ ! -f Makefile ]; then echo " Running ./configure first..." From 8cb188f12756ab031517caee331ac5a2af74ce1e Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Wed, 17 Jun 2026 17:27:15 +0300 Subject: [PATCH 03/27] cra-kit: address Atwood review (shell safety, validation, samples) Quote "$@" in gen-sbom --srcs (drop SC2068 disables), pass JSON paths to python via env var in validate.sh to prevent filename injection, and fail non-zero instead of warning when SBOM post-processing breaks. Rename the user_settings.h guard to CRA_KIT_USER_SETTINGS_H, clarify the serialNumber regex accepts v4 (product) and v5 (component) UUIDs, untrack the gitignored embedded sample SBOMs, document the SHA-512 CBOM omission, and note sample timestamp differences in SAMPLE-PROVENANCE.md. Signed-off-by: Sameeh Jubran --- .../wolfssl-5.9.1.cdx.json | 328 ------------------ .../wolfssl-5.9.1.spdx.json | 53 --- .../wolfssl-component/SAMPLE-PROVENANCE.md | 13 + .../wolfssl-5.9.1.cbom-draft.cdx.json | 3 +- cra-kit/scripts/generate-embedded-sbom.sh | 2 +- cra-kit/scripts/generate-wolfssl-sbom.sh | 18 +- cra-kit/scripts/make-commercial-sample.sh | 4 +- cra-kit/scripts/refresh-samples.sh | 4 +- cra-kit/scripts/validate.sh | 18 +- cra-kit/user_settings.h | 6 +- 10 files changed, 44 insertions(+), 405 deletions(-) delete mode 100644 cra-kit/auditor-packet/wolfssl-component-embedded/wolfssl-5.9.1.cdx.json delete mode 100644 cra-kit/auditor-packet/wolfssl-component-embedded/wolfssl-5.9.1.spdx.json diff --git a/cra-kit/auditor-packet/wolfssl-component-embedded/wolfssl-5.9.1.cdx.json b/cra-kit/auditor-packet/wolfssl-component-embedded/wolfssl-5.9.1.cdx.json deleted file mode 100644 index a0dcd3e7e..000000000 --- a/cra-kit/auditor-packet/wolfssl-component-embedded/wolfssl-5.9.1.cdx.json +++ /dev/null @@ -1,328 +0,0 @@ -{ - "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", - "bomFormat": "CycloneDX", - "specVersion": "1.6", - "serialNumber": "urn:uuid:bbd8fa2c-814a-5921-b121-e872fe1b42a2", - "version": 1, - "metadata": { - "timestamp": "2026-05-18T11:56:58Z", - "tools": { - "components": [ - { - "type": "application", - "author": "wolfSSL Inc.", - "name": "wolfssl-sbom-gen", - "version": "1.0" - } - ] - }, - "component": { - "bom-ref": "721ce791-b9c8-5edf-a9d2-ef3b0539043b", - "type": "library", - "supplier": { - "name": "wolfSSL Inc." - }, - "name": "wolfssl", - "version": "5.9.1", - "licenses": [ - { - "license": { - "id": "GPL-3.0-only" - } - } - ], - "copyright": "Copyright (C) 2006-2026 wolfSSL Inc.", - "cpe": "cpe:2.3:a:wolfssl:wolfssl:5.9.1:*:*:*:*:*:*:*", - "purl": "pkg:github/wolfSSL/wolfssl@v5.9.1", - "hashes": [ - { - "alg": "SHA-256", - "content": "3538981aad331ad5cd160abd2b51ce0a5fa1a58b3c51f990e08ca91bb44627a0" - } - ], - "externalReferences": [ - { - "type": "vcs", - "url": "https://github.com/wolfSSL/wolfssl" - } - ], - "properties": [ - { - "name": "wolfssl:build:AES_MAX_KEY_SIZE", - "value": "256" - }, - { - "name": "wolfssl:build:DH_MAX_SIZE", - "value": "WC_BITS_FULL_BYTES(SP_INT_BITS)" - }, - { - "name": "wolfssl:build:ECC_DECODE_EXTRA", - "value": "1" - }, - { - "name": "wolfssl:build:ECC_MIN_KEY_SZ", - "value": "224" - }, - { - "name": "wolfssl:build:FLASH_QUALIFIER", - "value": "1" - }, - { - "name": "wolfssl:build:HAVE_AESGCM", - "value": "1" - }, - { - "name": "wolfssl:build:HAVE_AES_CBC", - "value": "1" - }, - { - "name": "wolfssl:build:HAVE_AES_DECRYPT", - "value": "1" - }, - { - "name": "wolfssl:build:HAVE_ALL_CURVES", - "value": "1" - }, - { - "name": "wolfssl:build:HAVE_ECC", - "value": "1" - }, - { - "name": "wolfssl:build:HAVE_ECC_CHECK_KEY", - "value": "1" - }, - { - "name": "wolfssl:build:HAVE_ECC_DHE", - "value": "1" - }, - { - "name": "wolfssl:build:HAVE_ECC_KEY_EXPORT", - "value": "1" - }, - { - "name": "wolfssl:build:HAVE_ECC_KEY_IMPORT", - "value": "1" - }, - { - "name": "wolfssl:build:HAVE_ECC_SIGN", - "value": "1" - }, - { - "name": "wolfssl:build:HAVE_ECC_VERIFY", - "value": "1" - }, - { - "name": "wolfssl:build:HAVE_PBKDF1", - "value": "1" - }, - { - "name": "wolfssl:build:HAVE_PBKDF2", - "value": "1" - }, - { - "name": "wolfssl:build:HAVE_PKCS12", - "value": "1" - }, - { - "name": "wolfssl:build:HAVE_PKCS8", - "value": "1" - }, - { - "name": "wolfssl:build:HAVE_PUBLIC_FFDHE", - "value": "1" - }, - { - "name": "wolfssl:build:LIBWOLFSSL_CMAKE_OUTPUT", - "value": "\"\"" - }, - { - "name": "wolfssl:build:MIN_FFDHE_BITS", - "value": "0" - }, - { - "name": "wolfssl:build:MIN_FFDHE_FP_MAX_BITS", - "value": "(MIN_FFDHE_BITS * 2)" - }, - { - "name": "wolfssl:build:NO_OLD_TLS", - "value": "1" - }, - { - "name": "wolfssl:build:NO_PSK", - "value": "1" - }, - { - "name": "wolfssl:build:NO_RC4", - "value": "1" - }, - { - "name": "wolfssl:build:NO_XSTREAM_ALIGN", - "value": "1" - }, - { - "name": "wolfssl:build:RSA_DECODE_EXTRA", - "value": "1" - }, - { - "name": "wolfssl:build:USE_WOLFSSL_MEMORY", - "value": "1" - }, - { - "name": "wolfssl:build:WC_ASYNC_DEV_SIZE", - "value": "0" - }, - { - "name": "wolfssl:build:WC_TEST_NO_ECC_SIGN_VERIFY_ZERO_DIGEST", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_ABI", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_AES_128", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_AES_192", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_AES_256", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_ALERT_COUNT_MAX", - "value": "5" - }, - { - "name": "wolfssl:build:WOLFSSL_API", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_ASN_TEMPLATE", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_ASYNC_IO", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_BASE64_DECODE", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_FIPS_VERSION2_CODE", - "value": "WOLFSSL_FIPS_VERSION_CODE" - }, - { - "name": "wolfssl:build:WOLFSSL_FIPS_VERSION_CODE", - "value": "WOLFSSL_MAKE_FIPS_VERSION3(0,0,0)" - }, - { - "name": "wolfssl:build:WOLFSSL_GENERAL_ALIGNMENT", - "value": "0" - }, - { - "name": "wolfssl:build:WOLFSSL_HAVE_PRF", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_LOCAL", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_MAX_EMPTY_RECORDS", - "value": "32" - }, - { - "name": "wolfssl:build:WOLFSSL_MIN_AUTH_TAG_SZ", - "value": "12" - }, - { - "name": "wolfssl:build:WOLFSSL_PEM_TO_DER", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_SMALL_STACK_STATIC", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_SP_ADD_D", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_SP_INVMOD", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_SP_INVMOD_MONT_CT", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_SP_MATH_ALL", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_SP_MUL_D", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_SP_NO_DYN_STACK", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_SP_PRIME_GEN", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_SP_READ_RADIX_10", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_SP_READ_RADIX_16", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_SP_SUB_D", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_TEST_VIS", - "value": "WOLFSSL_API WC_DEPRECATED(\"internal use only\")" - }, - { - "name": "wolfssl:build:WOLFSSL_TLS13", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_USER_SETTINGS", - "value": "1" - }, - { - "name": "wolfssl:build:WOLFSSL_W64_WRAPPER", - "value": "1" - }, - { - "name": "wolfssl:build:XGEN_ALIGN", - "value": "1" - }, - { - "name": "wolfssl:sbom:hash-kind", - "value": "source-merkle-omnibor" - }, - { - "name": "wolfssl:sbom:source-set", - "value": "aes.c,ecc.c,keys.c,random.c,sha.c,sha256.c,tls.c,tls13.c,wc_port.c" - } - ] - } - }, - "components": [], - "dependencies": [ - { - "ref": "721ce791-b9c8-5edf-a9d2-ef3b0539043b", - "dependsOn": [] - } - ] -} diff --git a/cra-kit/auditor-packet/wolfssl-component-embedded/wolfssl-5.9.1.spdx.json b/cra-kit/auditor-packet/wolfssl-component-embedded/wolfssl-5.9.1.spdx.json deleted file mode 100644 index af6eb3f78..000000000 --- a/cra-kit/auditor-packet/wolfssl-component-embedded/wolfssl-5.9.1.spdx.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "spdxVersion": "SPDX-2.3", - "dataLicense": "CC0-1.0", - "SPDXID": "SPDXRef-DOCUMENT", - "name": "wolfssl-5.9.1", - "documentNamespace": "urn:uuid:480ff203-f994-5b71-b858-0653e74e422a", - "creationInfo": { - "creators": [ - "Organization: wolfSSL Inc.", - "Tool: wolfssl-sbom-gen-1.0" - ], - "created": "2026-05-18T11:56:58Z" - }, - "packages": [ - { - "SPDXID": "SPDXRef-Package-wolfssl", - "name": "wolfssl", - "versionInfo": "5.9.1", - "supplier": "Organization: wolfSSL Inc.", - "downloadLocation": "https://github.com/wolfSSL/wolfssl", - "filesAnalyzed": false, - "checksums": [ - { - "algorithm": "SHA256", - "checksumValue": "3538981aad331ad5cd160abd2b51ce0a5fa1a58b3c51f990e08ca91bb44627a0" - } - ], - "licenseConcluded": "GPL-3.0-only", - "licenseDeclared": "GPL-3.0-only", - "copyrightText": "Copyright (C) 2006-2026 wolfSSL Inc.", - "comment": "Build configuration defines: AES_MAX_KEY_SIZE, DH_MAX_SIZE, ECC_DECODE_EXTRA, ECC_MIN_KEY_SZ, FLASH_QUALIFIER, HAVE_AESGCM, HAVE_AES_CBC, HAVE_AES_DECRYPT, HAVE_ALL_CURVES, HAVE_ECC, HAVE_ECC_CHECK_KEY, HAVE_ECC_DHE, HAVE_ECC_KEY_EXPORT, HAVE_ECC_KEY_IMPORT, HAVE_ECC_SIGN, HAVE_ECC_VERIFY, HAVE_PBKDF1, HAVE_PBKDF2, HAVE_PKCS12, HAVE_PKCS8, HAVE_PUBLIC_FFDHE, LIBWOLFSSL_CMAKE_OUTPUT, MIN_FFDHE_BITS, MIN_FFDHE_FP_MAX_BITS, NO_OLD_TLS, NO_PSK, NO_RC4, NO_XSTREAM_ALIGN, RSA_DECODE_EXTRA, USE_WOLFSSL_MEMORY, WC_ASYNC_DEV_SIZE, WC_TEST_NO_ECC_SIGN_VERIFY_ZERO_DIGEST, WOLFSSL_ABI, WOLFSSL_AES_128, WOLFSSL_AES_192, WOLFSSL_AES_256, WOLFSSL_ALERT_COUNT_MAX, WOLFSSL_API, WOLFSSL_ASN_TEMPLATE, WOLFSSL_ASYNC_IO, WOLFSSL_BASE64_DECODE, WOLFSSL_FIPS_VERSION2_CODE, WOLFSSL_FIPS_VERSION_CODE, WOLFSSL_GENERAL_ALIGNMENT, WOLFSSL_HAVE_PRF, WOLFSSL_LOCAL, WOLFSSL_MAX_EMPTY_RECORDS, WOLFSSL_MIN_AUTH_TAG_SZ, WOLFSSL_PEM_TO_DER, WOLFSSL_SMALL_STACK_STATIC, WOLFSSL_SP_ADD_D, WOLFSSL_SP_INVMOD, WOLFSSL_SP_INVMOD_MONT_CT, WOLFSSL_SP_MATH_ALL, WOLFSSL_SP_MUL_D, WOLFSSL_SP_NO_DYN_STACK, WOLFSSL_SP_PRIME_GEN, WOLFSSL_SP_READ_RADIX_10, WOLFSSL_SP_READ_RADIX_16, WOLFSSL_SP_SUB_D, WOLFSSL_TEST_VIS, WOLFSSL_TLS13, WOLFSSL_USER_SETTINGS, WOLFSSL_W64_WRAPPER, XGEN_ALIGN | hash-kind=source-merkle-omnibor | source-set=aes.c,ecc.c,keys.c,random.c,sha.c,sha256.c,tls.c,tls13.c,wc_port.c", - "externalRefs": [ - { - "referenceCategory": "SECURITY", - "referenceType": "cpe23Type", - "referenceLocator": "cpe:2.3:a:wolfssl:wolfssl:5.9.1:*:*:*:*:*:*:*" - }, - { - "referenceCategory": "PACKAGE-MANAGER", - "referenceType": "purl", - "referenceLocator": "pkg:github/wolfSSL/wolfssl@v5.9.1" - } - ] - } - ], - "relationships": [ - { - "spdxElementId": "SPDXRef-DOCUMENT", - "relatedSpdxElement": "SPDXRef-Package-wolfssl", - "relationshipType": "DESCRIBES" - } - ] -} diff --git a/cra-kit/auditor-packet/wolfssl-component/SAMPLE-PROVENANCE.md b/cra-kit/auditor-packet/wolfssl-component/SAMPLE-PROVENANCE.md index 4e6419b48..3e45902da 100644 --- a/cra-kit/auditor-packet/wolfssl-component/SAMPLE-PROVENANCE.md +++ b/cra-kit/auditor-packet/wolfssl-component/SAMPLE-PROVENANCE.md @@ -19,3 +19,16 @@ Regenerate autotools samples and fix the product stub checksum: ```sh ./scripts/refresh-samples.sh ``` + +## A note on timestamps + +The sample SBOMs carry different `metadata.timestamp` / `created` values because +they were generated at different times, not in a single run: + +- `wolfssl-component/` (autotools): `2026-05-12` +- `wolfssl-component-embedded/` (embedded demo): `2026-05-18` +- `product-acme-connect-gateway.*` (product stub): `2026-05-18` + +This is expected for hand-pinned samples and does not affect validation +(`scripts/validate.sh` checks cross-document checksums, not timestamps). +Regenerating via `refresh-samples.sh` will update them to the current time. diff --git a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.cbom-draft.cdx.json b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.cbom-draft.cdx.json index 2e80c34d6..007ac6829 100644 --- a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.cbom-draft.cdx.json +++ b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.cbom-draft.cdx.json @@ -222,7 +222,8 @@ } }, "properties": [ - {"name": "wolfssl:build:macro", "value": "WOLFSSL_TLS13"} + {"name": "wolfssl:build:macro", "value": "WOLFSSL_TLS13"}, + {"name": "wolfssl:note:sha-512-omitted", "value": "SHA-512 is built into the component (crypto-sha-512) but is intentionally absent from this cryptoRefArray: TLS 1.3 handshake/HKDF use SHA-256/SHA-384, not SHA-512. SHA-512 is exposed as a crypto asset for callers, not as a TLS 1.3 protocol dependency."} ] } ], diff --git a/cra-kit/scripts/generate-embedded-sbom.sh b/cra-kit/scripts/generate-embedded-sbom.sh index dafc32f51..146606c5c 100755 --- a/cra-kit/scripts/generate-embedded-sbom.sh +++ b/cra-kit/scripts/generate-embedded-sbom.sh @@ -2,7 +2,7 @@ # Force embedded gen-sbom (user_settings.h + --srcs) into wolfssl-component-embedded/. set -eu -SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) KIT_DIR=$(dirname "$SCRIPT_DIR") export CRA_SBOM_MODE=embedded export CRA_SBOM_OUT_DIR="$KIT_DIR/auditor-packet/wolfssl-component-embedded" diff --git a/cra-kit/scripts/generate-wolfssl-sbom.sh b/cra-kit/scripts/generate-wolfssl-sbom.sh index 6a27005fc..f1cb211cb 100755 --- a/cra-kit/scripts/generate-wolfssl-sbom.sh +++ b/cra-kit/scripts/generate-wolfssl-sbom.sh @@ -6,8 +6,9 @@ # CRA_LICENSE_OVERRIDE= (optional: e.g. LicenseRef-wolfSSL-Commercial) set -eu -SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) KIT_DIR=$(dirname "$SCRIPT_DIR") +# shellcheck disable=SC2015 # `|| true` is a deliberate set -e guard, not if-then-else WOLFSSL_DIR=${WOLFSSL_DIR:-$(cd "$KIT_DIR/../../wolfssl" 2>/dev/null && pwd || true)} OUT_DIR=${CRA_SBOM_OUT_DIR:-"$KIT_DIR/auditor-packet/wolfssl-component"} VERSION_FILE="$KIT_DIR/VERSION" @@ -98,7 +99,6 @@ _run_embedded() { if _py=$(_python_with_pcpp); then echo " Using $_py (pcpp) for --user-settings" - # shellcheck disable=SC2068 "$_py" "$GEN" \ --name wolfssl --version "$VERSION" \ --license-file "$WOLFSSL_DIR/LICENSING" \ @@ -106,7 +106,7 @@ _run_embedded() { --user-settings-include "$WOLFSSL_DIR" \ --user-settings-include "$KIT_DIR" \ --user-settings-define WOLFSSL_USER_SETTINGS \ - --srcs $@ + --srcs "$@" return 0 fi @@ -134,12 +134,11 @@ _run_embedded() { PYTHON=python3 command -v python3 >/dev/null 2>&1 || PYTHON=python - # shellcheck disable=SC2068 "$PYTHON" "$GEN" \ --name wolfssl --version "$VERSION" \ --license-file "$WOLFSSL_DIR/LICENSING" \ --options-h "$DEFINES_H" \ - --srcs $@ + --srcs "$@" } _run_autotools() { @@ -200,8 +199,8 @@ esac # GHSA / Snyk / Trivy match without per-vendor mapping. Embedded outputs from # the kit's 9-file demo --srcs list also get a wolfssl:sbom:demo property so a # downstream auditor cannot mistake them for production-complete SBOMs. -CDX_OUT="$CDX_OUT" SPDX_OUT="$SPDX_OUT" CRA_SBOM_MODE_FINAL="$MODE" \ -python3 <<'PY' || echo "WARN: post-process skipped (python3 missing or JSON malformed)" +if ! CDX_OUT="$CDX_OUT" SPDX_OUT="$SPDX_OUT" CRA_SBOM_MODE_FINAL="$MODE" \ +python3 <<'PY' import json, os, pathlib cdx = pathlib.Path(os.environ["CDX_OUT"]) @@ -244,5 +243,10 @@ if spdx.exists(): spdx.write_text(json.dumps(d, indent=2) + "\n") print(f"Post-processed {spdx.name}: PURL canonicalized" + (", demo watermark added" if demo else "")) PY +then + echo "ERROR: post-process failed (PURL canonicalization/watermarking incomplete)." >&2 + echo " The emitted SBOM may carry pkg:generic PURLs or lack demo watermarks; not trusting it." >&2 + exit 1 +fi echo "Done." diff --git a/cra-kit/scripts/make-commercial-sample.sh b/cra-kit/scripts/make-commercial-sample.sh index 1af699cc9..c30aab254 100755 --- a/cra-kit/scripts/make-commercial-sample.sh +++ b/cra-kit/scripts/make-commercial-sample.sh @@ -12,10 +12,10 @@ # ./scripts/generate-wolfssl-sbom.sh set -eu -SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) KIT_DIR=$(dirname "$SCRIPT_DIR") -# shellcheck disable=SC1090 +# shellcheck disable=SC1090,SC1091 . "$KIT_DIR/VERSION" COMP_DIR="$KIT_DIR/auditor-packet/wolfssl-component" GPL_CDX="$COMP_DIR/wolfssl-${WOLFSSL_VERSION}.cdx.json" diff --git a/cra-kit/scripts/refresh-samples.sh b/cra-kit/scripts/refresh-samples.sh index 3bb47d763..cdde93d0a 100755 --- a/cra-kit/scripts/refresh-samples.sh +++ b/cra-kit/scripts/refresh-samples.sh @@ -3,14 +3,14 @@ # (SPDX externalDocumentRef checksum + CycloneDX bom externalReference hash). set -eu -SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) KIT_DIR=$(dirname "$SCRIPT_DIR") export CRA_SBOM_MODE=autotools export CRA_SBOM_OUT_DIR="$KIT_DIR/auditor-packet/wolfssl-component" "$SCRIPT_DIR/generate-wolfssl-sbom.sh" -# shellcheck disable=SC1090 +# shellcheck disable=SC1090,SC1091 . "$KIT_DIR/VERSION" COMPONENT_SPDX="$KIT_DIR/auditor-packet/wolfssl-component/wolfssl-${WOLFSSL_VERSION}.spdx.json" COMPONENT_CDX="$KIT_DIR/auditor-packet/wolfssl-component/wolfssl-${WOLFSSL_VERSION}.cdx.json" diff --git a/cra-kit/scripts/validate.sh b/cra-kit/scripts/validate.sh index 492599521..d73534380 100755 --- a/cra-kit/scripts/validate.sh +++ b/cra-kit/scripts/validate.sh @@ -6,7 +6,7 @@ # validation, when those tools are installed locally. set -eu -SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) KIT_DIR=$(dirname "$SCRIPT_DIR") AP="$KIT_DIR/auditor-packet" PRODUCT_CDX="$AP/product-acme-connect-gateway.cdx.json" @@ -17,20 +17,22 @@ ok() { echo "OK: $*"; } command -v python3 >/dev/null 2>&1 || fail "python3 required" -# shellcheck disable=SC1090 +# shellcheck disable=SC1090,SC1091 . "$KIT_DIR/VERSION" 2>/dev/null || WOLFSSL_VERSION=5.9.1 WOLF_CDX="$AP/wolfssl-component/wolfssl-${WOLFSSL_VERSION}.cdx.json" WOLF_SPDX="$AP/wolfssl-component/wolfssl-${WOLFSSL_VERSION}.spdx.json" for f in "$PRODUCT_CDX" "$PRODUCT_SPDX" "$WOLF_CDX" "$WOLF_SPDX"; do [ -f "$f" ] || fail "missing $f" - python3 -c "import json; json.load(open('$f'))" || fail "invalid JSON: $f" + F="$f" python3 -c "import json, os; json.load(open(os.environ['F']))" || fail "invalid JSON: $f" ok "$(basename "$f") parses" done -# CycloneDX 1.6 serialNumber must match urn:uuid:; auditors with strict -# validators (cyclonedx-cli) reject anything else. Catch this even when the tool -# isn't installed. +# CycloneDX 1.6 serialNumber must be a well-formed urn:uuid; auditors with strict +# validators (cyclonedx-cli) reject anything else. We accept RFC 4122 versions 1-5: +# the product SBOM uses a random v4, while the wolfSSL component SBOMs use a +# deterministic name-based v5 so regenerated samples keep a stable serialNumber. +# Catch malformed values even when the tool isn't installed. PRODUCT_CDX="$PRODUCT_CDX" WOLF_CDX="$WOLF_CDX" python3 <<'PY' import json, os, re, sys UUID = re.compile(r"^urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", re.I) @@ -39,10 +41,10 @@ for env in ("PRODUCT_CDX", "WOLF_CDX"): path = os.environ[env] sn = json.load(open(path)).get("serialNumber", "") if not UUID.match(sn): - errors.append(f"{os.path.basename(path)}: serialNumber {sn!r} is not urn:uuid:") + errors.append(f"{os.path.basename(path)}: serialNumber {sn!r} is not a urn:uuid:") if errors: sys.exit("CycloneDX serialNumber violation(s):\n " + "\n ".join(errors)) -print("OK: CycloneDX serialNumbers are valid urn:uuid:") +print("OK: CycloneDX serialNumbers are valid urn:uuid (v1-5)") PY PRODUCT_SPDX="$PRODUCT_SPDX" WOLF_SPDX="$WOLF_SPDX" python3 <<'PY' diff --git a/cra-kit/user_settings.h b/cra-kit/user_settings.h index c8cb8b0b5..94ebd570e 100644 --- a/cra-kit/user_settings.h +++ b/cra-kit/user_settings.h @@ -1,7 +1,7 @@ /* Demo user_settings.h for CRA Kit embedded SBOM generation. * Production: replace with your project's user_settings.h (or point gen-sbom at it). */ -#ifndef CRA_EVIDENCE_USER_SETTINGS_H -#define CRA_EVIDENCE_USER_SETTINGS_H +#ifndef CRA_KIT_USER_SETTINGS_H +#define CRA_KIT_USER_SETTINGS_H #define WOLFSSL_TLS13 #define HAVE_AESGCM @@ -9,4 +9,4 @@ #define NO_PSK #define NO_OLD_TLS -#endif /* CRA_EVIDENCE_USER_SETTINGS_H */ +#endif /* CRA_KIT_USER_SETTINGS_H */ From 924c3d228b272ef3e36eeff5826b973ceba65d99 Mon Sep 17 00:00:00 2001 From: Sameeh Jubran Date: Thu, 18 Jun 2026 13:16:48 +0300 Subject: [PATCH 04/27] cra-kit: fix commercial-license SBOM generation and refresh samples Plumb CRA_LICENSE_TEXT through generate-wolfssl-sbom.sh (gen-sbom/make sbom hard-fail on a LicenseRef-* override without it); scope the commercial relicense to the wolfssl package only; correct the stale pkg:generic PURL claim; validate the embedded output dir; regenerate pinned samples against gen-sbom 1.1 and re-pin product checksums. Signed-off-by: Sameeh Jubran --- .github/workflows/cra-kit.yml | 9 +- cra-kit/README.md | 13 ++- cra-kit/ROADMAP.md | 4 +- .../{README-auditor-packet.md => README.md} | 0 .../product-acme-connect-gateway.cdx.json | 2 +- .../product-acme-connect-gateway.spdx.json | 4 +- .../omnibor.wolfssl-5.9.1.spdx.json.sample | 4 +- .../wolfssl-component/wolfssl-5.9.1.cdx.json | 38 ++++++++- .../wolfssl-5.9.1.commercial.cdx.json | 40 ++++++++- .../wolfssl-5.9.1.commercial.spdx.json | 17 +++- .../wolfssl-component/wolfssl-5.9.1.spdx | 10 ++- .../wolfssl-component/wolfssl-5.9.1.spdx.json | 15 +++- cra-kit/scripts/generate-wolfssl-sbom.sh | 84 ++++++++++++++++--- cra-kit/scripts/make-commercial-sample.sh | 6 ++ cra-kit/scripts/validate.sh | 35 ++++++++ .../wolfssl-inc-auditor-packet/00-INDEX.md | 2 +- .../classification-statement.md | 5 +- .../eu-authorised-representative.md | 6 +- .../vulnerability-handling-process.md | 6 +- 19 files changed, 261 insertions(+), 39 deletions(-) rename cra-kit/auditor-packet/{README-auditor-packet.md => README.md} (100%) diff --git a/.github/workflows/cra-kit.yml b/.github/workflows/cra-kit.yml index 0b10c2f23..60cc9ce77 100644 --- a/.github/workflows/cra-kit.yml +++ b/.github/workflows/cra-kit.yml @@ -10,12 +10,17 @@ on: - 'cra-kit/**' - '.github/workflows/cra-kit.yml' +# Least-privilege default; this workflow only needs to read the repo contents. +permissions: + contents: read + jobs: validate-auditor-packet: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + # Actions pinned to commit SHAs (supply-chain hygiene), not mutable tags. + - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: '3.x' - name: Validate pinned auditor packet diff --git a/cra-kit/README.md b/cra-kit/README.md index c0fb456b1..5d85d0a4c 100644 --- a/cra-kit/README.md +++ b/cra-kit/README.md @@ -86,7 +86,15 @@ SBOM = your job. |---------|-------|------------------------| | **A. Linux / server / Yocto / package** | `./configure && make` | `make sbom` in wolfSSL tree | | **B. Embedded / RTOS / IDE** | `user_settings.h` + your Makefile / Keil / Zephyr / ESP-IDF | `./scripts/generate-embedded-sbom.sh` (kit demo) or upstream `gen-sbom` | -| **C. Commercial license** | Either | `CRA_LICENSE_OVERRIDE=LicenseRef-wolfSSL-Commercial ./scripts/generate-wolfssl-sbom.sh` | +| **C. Commercial license** | Either | `CRA_LICENSE_OVERRIDE=LicenseRef-wolfSSL-Commercial CRA_LICENSE_TEXT=/path/to/commercial-license.txt ./scripts/generate-wolfssl-sbom.sh` | + +> **Commercial (`LicenseRef-*`) overrides require `CRA_LICENSE_TEXT`** pointing at +> the plain-text licence you received from wolfSSL. SPDX 2.3 §10.1 requires the +> licence text to be embedded for any `LicenseRef-*`; both `gen-sbom` and +> `make sbom` hard-fail without it. A stock SPDX id (e.g. `Apache-2.0`) needs no +> text. If you don't have the text file handy, use +> [`scripts/make-commercial-sample.sh`](scripts/make-commercial-sample.sh) to +> derive a commercial sample from the pinned GPL samples instead. **Every manufacturer still:** @@ -122,7 +130,8 @@ CRA_SBOM_MODE=embedded ./scripts/generate-wolfssl-sbom.sh # rarely used for pa ./scripts/generate-embedded-sbom.sh # writes wolfssl-component-embedded/ CRA_LICENSE_OVERRIDE=LicenseRef-wolfSSL-Commercial \ - ./scripts/generate-wolfssl-sbom.sh # commercial-license sample + CRA_LICENSE_TEXT=/path/to/wolfssl-commercial-license.txt \ + ./scripts/generate-wolfssl-sbom.sh # commercial-license sample (text required) ./scripts/make-commercial-sample.sh # derive from pinned GPL samples (no rebuild) ``` diff --git a/cra-kit/ROADMAP.md b/cra-kit/ROADMAP.md index deaa99490..e6bed2e5d 100644 --- a/cra-kit/ROADMAP.md +++ b/cra-kit/ROADMAP.md @@ -7,10 +7,10 @@ Honest status for customer conversations. This is **not** a commitment schedule. | **SBOM** (SPDX 2.3 + CycloneDX 1.6) | **Available** | `make sbom` or `scripts/gen-sbom` | | **Config-accurate build properties** | **Available** | Read `wolfssl:build:*` in `.cdx.json` | | **Embedded source-merkle checksum** | **Available** | `gen-sbom` with `--srcs` (no `libwolfssl.a` required) | -| **Commercial license in SBOM** | **Available** | `CRA_LICENSE_OVERRIDE=LicenseRef-wolfSSL-Commercial ./scripts/generate-wolfssl-sbom.sh` (or `make-commercial-sample.sh` to derive from pinned GPL samples) | +| **Commercial license in SBOM** | **Available** | `CRA_LICENSE_OVERRIDE=LicenseRef-wolfSSL-Commercial CRA_LICENSE_TEXT=/path/to/commercial-license.txt ./scripts/generate-wolfssl-sbom.sh` (a `LicenseRef-*` override requires the licence text; or use `make-commercial-sample.sh` to derive from pinned GPL samples) | | **Reproducible SBOM timestamps** | **Available** | `SOURCE_DATE_EPOCH` | | **OmniBOR / `make bomsh`** | **Available** | Linux **build host** only; optional for CRA | -| **`pkg:github` PURL** | **Available** | Auto-canonicalised by `generate-wolfssl-sbom.sh` post-process; resolves in OSV / GHSA / Snyk / Trivy without per-vendor mapping | +| **`pkg:github` PURL** | **Available** | Emitted natively by `gen-sbom`; resolves in OSV / GHSA / Snyk / Trivy without per-vendor mapping | | **Cryptographic-asset draft** (CycloneDX 1.6) | **Draft sample** | Hand-rolled `wolfssl-.cbom-draft.cdx.json` alongside SBOM (4–6 starter entries); upstream automation: roadmap | | **Formal CBOM** (`cryptographic-asset` profile, all primitives) | **Roadmap** | Use draft sample + `wolfssl:build:*` properties | | **VEX templates / automation** | **Roadmap** | Your scanner + wolfSSL [advisories](https://www.wolfssl.com/docs/security-vulnerabilities/) | diff --git a/cra-kit/auditor-packet/README-auditor-packet.md b/cra-kit/auditor-packet/README.md similarity index 100% rename from cra-kit/auditor-packet/README-auditor-packet.md rename to cra-kit/auditor-packet/README.md diff --git a/cra-kit/auditor-packet/product-acme-connect-gateway.cdx.json b/cra-kit/auditor-packet/product-acme-connect-gateway.cdx.json index f8c02effd..3264ae1db 100644 --- a/cra-kit/auditor-packet/product-acme-connect-gateway.cdx.json +++ b/cra-kit/auditor-packet/product-acme-connect-gateway.cdx.json @@ -35,7 +35,7 @@ "hashes": [ { "alg": "SHA-256", - "content": "265cd1575f7a350295ba1414494f2cc93bb895223a9732dcfb231bcecb6d3bbd" + "content": "bc8c6b9f5fbe829edb594dc74bcb95a202ca1b402ab1dca60f858aa9fe2ec6e3" } ] } diff --git a/cra-kit/auditor-packet/product-acme-connect-gateway.spdx.json b/cra-kit/auditor-packet/product-acme-connect-gateway.spdx.json index 73b0b39ca..150728ae3 100644 --- a/cra-kit/auditor-packet/product-acme-connect-gateway.spdx.json +++ b/cra-kit/auditor-packet/product-acme-connect-gateway.spdx.json @@ -16,7 +16,7 @@ "spdxDocument": "file:./wolfssl-component/wolfssl-5.9.1.spdx.json", "checksum": { "algorithm": "SHA256", - "checksumValue": "36fdc0c8a192a0fadc4c5024ff75ecee3a56dd8a431dfb25bfa8afcf467cfdef" + "checksumValue": "a60bda42e4e0c874f21abaed7b34e72ac6ea329662fbac33f8487608753042f2" } } ], @@ -25,7 +25,7 @@ "SPDXID": "SPDXRef-Package-Product", "name": "acme-connect-gateway", "versionInfo": "1.0.0", - "supplier": "Organization: Acme Industries", + "supplier": "Organization: Acme Industries (fictional example)", "downloadLocation": "NOASSERTION", "filesAnalyzed": false } diff --git a/cra-kit/auditor-packet/wolfssl-component/omnibor.wolfssl-5.9.1.spdx.json.sample b/cra-kit/auditor-packet/wolfssl-component/omnibor.wolfssl-5.9.1.spdx.json.sample index 3992f6737..116a6476a 100644 --- a/cra-kit/auditor-packet/wolfssl-component/omnibor.wolfssl-5.9.1.spdx.json.sample +++ b/cra-kit/auditor-packet/wolfssl-component/omnibor.wolfssl-5.9.1.spdx.json.sample @@ -28,10 +28,10 @@ }, { "algorithm": "SHA256", - "checksumValue": "391e86477f5eee025e677a24b13e2d9a4d3e4c18d88e6359853ebf1c9932279e" + "checksumValue": "0000000000000000000000000000000000000000000000000000000000000000" } ], - "comment": "OmniBOR identifier for the linked binary: gitoid:blob:sha1:0000000000000000000000000000000000000001 — sample placeholder. Real builds emit the actual gitoid covering all .o inputs." + "comment": "SENTINEL VALUES — both the SHA-1 gitoid and the SHA-256 here are all-zero placeholders, NOT real digests. A compiled .so cannot share the source archive's hash, so this sample deliberately avoids any real-looking value a customer might copy. Real builds emit the actual binary digests and the gitoid covering all .o inputs." } ], "files": [ diff --git a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.cdx.json b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.cdx.json index 5c24c3a6e..f05521ef4 100644 --- a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.cdx.json +++ b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.cdx.json @@ -12,7 +12,7 @@ "type": "application", "author": "wolfSSL Inc.", "name": "wolfssl-sbom-gen", - "version": "1.0" + "version": "1.1" } ] }, @@ -44,6 +44,22 @@ { "type": "vcs", "url": "https://github.com/wolfSSL/wolfssl" + }, + { + "type": "website", + "url": "https://www.wolfssl.com/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/wolfSSL/wolfssl/issues" + }, + { + "type": "advisories", + "url": "https://github.com/wolfSSL/wolfssl/security/advisories" + }, + { + "type": "security-contact", + "url": "https://www.wolfssl.com/.well-known/security.txt" } ], "properties": [ @@ -286,6 +302,26 @@ { "name": "wolfssl:build:WOLFSSL_X86_64_BUILD", "value": "1" + }, + { + "name": "wolfssl:sbom:hash-kind", + "value": "library-binary" + } + ], + "components": [ + { + "type": "file", + "name": "libwolfssl.44.dylib", + "hashes": [ + { + "alg": "SHA-1", + "content": "def1d74ce45e708d8230084cdea4f45a9cad144c" + }, + { + "alg": "SHA-256", + "content": "391e86477f5eee025e677a24b13e2d9a4d3e4c18d88e6359853ebf1c9932279e" + } + ] } ] } diff --git a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.commercial.cdx.json b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.commercial.cdx.json index 9a4f14bbc..2b8d05b17 100644 --- a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.commercial.cdx.json +++ b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.commercial.cdx.json @@ -2,7 +2,7 @@ "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.6", - "serialNumber": "urn:uuid:80b023d5-8a5d-4e17-9e18-f3d5c7c9762e", + "serialNumber": "urn:uuid:554e996c-5c73-46f2-a642-5c1dc0c853d8", "version": 1, "metadata": { "timestamp": "2026-05-12T16:59:40Z", @@ -12,7 +12,7 @@ "type": "application", "author": "wolfSSL Inc.", "name": "wolfssl-sbom-gen", - "version": "1.0" + "version": "1.1" } ] }, @@ -44,6 +44,22 @@ { "type": "vcs", "url": "https://github.com/wolfSSL/wolfssl" + }, + { + "type": "website", + "url": "https://www.wolfssl.com/" + }, + { + "type": "issue-tracker", + "url": "https://github.com/wolfSSL/wolfssl/issues" + }, + { + "type": "advisories", + "url": "https://github.com/wolfSSL/wolfssl/security/advisories" + }, + { + "type": "security-contact", + "url": "https://www.wolfssl.com/.well-known/security.txt" } ], "properties": [ @@ -287,10 +303,30 @@ "name": "wolfssl:build:WOLFSSL_X86_64_BUILD", "value": "1" }, + { + "name": "wolfssl:sbom:hash-kind", + "value": "library-binary" + }, { "name": "wolfssl:license:override", "value": "LicenseRef-wolfSSL-Commercial" } + ], + "components": [ + { + "type": "file", + "name": "libwolfssl.44.dylib", + "hashes": [ + { + "alg": "SHA-1", + "content": "def1d74ce45e708d8230084cdea4f45a9cad144c" + }, + { + "alg": "SHA-256", + "content": "391e86477f5eee025e677a24b13e2d9a4d3e4c18d88e6359853ebf1c9932279e" + } + ] + } ] } }, diff --git a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.commercial.spdx.json b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.commercial.spdx.json index 61cedaab8..19ca426c5 100644 --- a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.commercial.spdx.json +++ b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.commercial.spdx.json @@ -3,11 +3,11 @@ "dataLicense": "CC0-1.0", "SPDXID": "SPDXRef-DOCUMENT", "name": "wolfssl-5.9.1", - "documentNamespace": "urn:uuid:cedcdaaa-b983-4ce1-83e3-ed7337232a49", + "documentNamespace": "urn:uuid:59b18c7a-8572-4743-85f2-ffeac74c310d", "creationInfo": { "creators": [ "Organization: wolfSSL Inc.", - "Tool: wolfssl-sbom-gen-1.0" + "Tool: wolfssl-sbom-gen-1.1" ], "created": "2026-05-12T16:59:40Z" }, @@ -29,6 +29,14 @@ "licenseDeclared": "LicenseRef-wolfSSL-Commercial", "copyrightText": "Copyright (C) 2006-2026 wolfSSL Inc.", "comment": "License override applied: LicenseRef-wolfSSL-Commercial. Build configuration defines: ECC_MIN_KEY_SZ, ECC_SHAMIR, ECC_TIMING_RESISTANT, ERROR_QUEUE_PER_THREAD, GCM_TABLE_4BIT, HAVE_AESGCM, HAVE_CHACHA, HAVE_C___ATOMIC, HAVE_DH_DEFAULT_PARAMS, HAVE_ECC, HAVE_ENCRYPT_THEN_MAC, HAVE_EXTENDED_MASTER, HAVE_FFDHE_2048, HAVE_GETPID, HAVE_HASHDRBG, HAVE_HKDF, HAVE_POLY1305, HAVE_SERVER_RENEGOTIATION_INFO, HAVE_SNI, HAVE_SUPPORTED_CURVES, HAVE_THREAD_LS, HAVE_TLS_EXTENSIONS, HAVE_WC_INTROSPECTION, HAVE___UINT128_T, NO_DES3, NO_DES3_TLS_SUITES, NO_DO178, NO_DSA, NO_MD4, NO_MD5, NO_OLD_TLS, NO_PSK, NO_RC4, TFM_TIMING_RESISTANT, WC_NO_ASYNC_THREADING, WC_RSA_BLINDING, WC_RSA_PSS, WOLFSSL_ARMASM_NO_HW_CRYPTO, WOLFSSL_ASN_PRINT, WOLFSSL_ASN_TEMPLATE, WOLFSSL_BASE64_ENCODE, WOLFSSL_DRBG_SHA512, WOLFSSL_HAVE_ASSERT_H, WOLFSSL_HAVE_ATOMIC_H, WOLFSSL_HAVE_MLKEM, WOLFSSL_PQC_HYBRIDS, WOLFSSL_PSS_LONG_SALT, WOLFSSL_SHA224, WOLFSSL_SHA3, WOLFSSL_SHA384, WOLFSSL_SHA512, WOLFSSL_SHAKE128, WOLFSSL_SHAKE256, WOLFSSL_SP_MATH_ALL, WOLFSSL_SP_X86_64, WOLFSSL_SYS_CA_CERTS, WOLFSSL_TLS13, WOLFSSL_TLS_NO_MLKEM_STANDALONE, WOLFSSL_USE_ALIGN, WOLFSSL_X86_64_BUILD", + "annotations": [ + { + "annotationDate": "2026-05-12T16:59:40Z", + "annotationType": "OTHER", + "annotator": "Tool: wolfssl-sbom-gen-1.1", + "comment": "wolfssl:sbom:hash-kind=library-binary" + } + ], "externalRefs": [ { "referenceCategory": "SECURITY", @@ -39,6 +47,11 @@ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:github/wolfSSL/wolfssl@v5.9.1" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "advisory", + "referenceLocator": "https://github.com/wolfSSL/wolfssl/security/advisories" } ] } diff --git a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.spdx b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.spdx index 7c1148ce7..96c31e868 100644 --- a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.spdx +++ b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.spdx @@ -7,7 +7,7 @@ DocumentNamespace: urn:uuid:480ff203-f994-5b71-b858-0653e74e422a ## Creation Information Creator: Organization: wolfSSL Inc. -Creator: Tool: wolfssl-sbom-gen-1.0 +Creator: Tool: wolfssl-sbom-gen-1.1 Created: 2026-05-12T16:59:40Z ## Package Information @@ -24,7 +24,15 @@ PackageCopyrightText: Copyright (C) 2006-2026 wolfSSL Inc. PackageComment: Build configuration defines: ECC_MIN_KEY_SZ, ECC_SHAMIR, ECC_TIMING_RESISTANT, ERROR_QUEUE_PER_THREAD, GCM_TABLE_4BIT, HAVE_AESGCM, HAVE_CHACHA, HAVE_C___ATOMIC, HAVE_DH_DEFAULT_PARAMS, HAVE_ECC, HAVE_ENCRYPT_THEN_MAC, HAVE_EXTENDED_MASTER, HAVE_FFDHE_2048, HAVE_GETPID, HAVE_HASHDRBG, HAVE_HKDF, HAVE_POLY1305, HAVE_SERVER_RENEGOTIATION_INFO, HAVE_SNI, HAVE_SUPPORTED_CURVES, HAVE_THREAD_LS, HAVE_TLS_EXTENSIONS, HAVE_WC_INTROSPECTION, HAVE___UINT128_T, NO_DES3, NO_DES3_TLS_SUITES, NO_DO178, NO_DSA, NO_MD4, NO_MD5, NO_OLD_TLS, NO_PSK, NO_RC4, TFM_TIMING_RESISTANT, WC_NO_ASYNC_THREADING, WC_RSA_BLINDING, WC_RSA_PSS, WOLFSSL_ARMASM_NO_HW_CRYPTO, WOLFSSL_ASN_PRINT, WOLFSSL_ASN_TEMPLATE, WOLFSSL_BASE64_ENCODE, WOLFSSL_DRBG_SHA512, WOLFSSL_HAVE_ASSERT_H, WOLFSSL_HAVE_ATOMIC_H, WOLFSSL_HAVE_MLKEM, WOLFSSL_PQC_HYBRIDS, WOLFSSL_PSS_LONG_SALT, WOLFSSL_SHA224, WOLFSSL_SHA3, WOLFSSL_SHA384, WOLFSSL_SHA512, WOLFSSL_SHAKE128, WOLFSSL_SHAKE256, WOLFSSL_SP_MATH_ALL, WOLFSSL_SP_X86_64, WOLFSSL_SYS_CA_CERTS, WOLFSSL_TLS13, WOLFSSL_TLS_NO_MLKEM_STANDALONE, WOLFSSL_USE_ALIGN, WOLFSSL_X86_64_BUILD ExternalRef: SECURITY cpe23Type cpe:2.3:a:wolfssl:wolfssl:5.9.1:*:*:*:*:*:*:* ExternalRef: PACKAGE-MANAGER purl pkg:github/wolfSSL/wolfssl@v5.9.1 +ExternalRef: SECURITY advisory https://github.com/wolfSSL/wolfssl/security/advisories ## Relationships Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-Package-wolfssl +## Annotations +Annotator: Tool: wolfssl-sbom-gen-1.1 +AnnotationDate: 2026-05-12T16:59:40Z +AnnotationType: OTHER +SPDXREF: SPDXRef-Package-wolfssl +AnnotationComment: wolfssl:sbom:hash-kind=library-binary + diff --git a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.spdx.json b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.spdx.json index dc4796b6e..7c609dbc6 100644 --- a/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.spdx.json +++ b/cra-kit/auditor-packet/wolfssl-component/wolfssl-5.9.1.spdx.json @@ -7,7 +7,7 @@ "creationInfo": { "creators": [ "Organization: wolfSSL Inc.", - "Tool: wolfssl-sbom-gen-1.0" + "Tool: wolfssl-sbom-gen-1.1" ], "created": "2026-05-12T16:59:40Z" }, @@ -29,6 +29,14 @@ "licenseDeclared": "GPL-3.0-only", "copyrightText": "Copyright (C) 2006-2026 wolfSSL Inc.", "comment": "Build configuration defines: ECC_MIN_KEY_SZ, ECC_SHAMIR, ECC_TIMING_RESISTANT, ERROR_QUEUE_PER_THREAD, GCM_TABLE_4BIT, HAVE_AESGCM, HAVE_CHACHA, HAVE_C___ATOMIC, HAVE_DH_DEFAULT_PARAMS, HAVE_ECC, HAVE_ENCRYPT_THEN_MAC, HAVE_EXTENDED_MASTER, HAVE_FFDHE_2048, HAVE_GETPID, HAVE_HASHDRBG, HAVE_HKDF, HAVE_POLY1305, HAVE_SERVER_RENEGOTIATION_INFO, HAVE_SNI, HAVE_SUPPORTED_CURVES, HAVE_THREAD_LS, HAVE_TLS_EXTENSIONS, HAVE_WC_INTROSPECTION, HAVE___UINT128_T, NO_DES3, NO_DES3_TLS_SUITES, NO_DO178, NO_DSA, NO_MD4, NO_MD5, NO_OLD_TLS, NO_PSK, NO_RC4, TFM_TIMING_RESISTANT, WC_NO_ASYNC_THREADING, WC_RSA_BLINDING, WC_RSA_PSS, WOLFSSL_ARMASM_NO_HW_CRYPTO, WOLFSSL_ASN_PRINT, WOLFSSL_ASN_TEMPLATE, WOLFSSL_BASE64_ENCODE, WOLFSSL_DRBG_SHA512, WOLFSSL_HAVE_ASSERT_H, WOLFSSL_HAVE_ATOMIC_H, WOLFSSL_HAVE_MLKEM, WOLFSSL_PQC_HYBRIDS, WOLFSSL_PSS_LONG_SALT, WOLFSSL_SHA224, WOLFSSL_SHA3, WOLFSSL_SHA384, WOLFSSL_SHA512, WOLFSSL_SHAKE128, WOLFSSL_SHAKE256, WOLFSSL_SP_MATH_ALL, WOLFSSL_SP_X86_64, WOLFSSL_SYS_CA_CERTS, WOLFSSL_TLS13, WOLFSSL_TLS_NO_MLKEM_STANDALONE, WOLFSSL_USE_ALIGN, WOLFSSL_X86_64_BUILD", + "annotations": [ + { + "annotationDate": "2026-05-12T16:59:40Z", + "annotationType": "OTHER", + "annotator": "Tool: wolfssl-sbom-gen-1.1", + "comment": "wolfssl:sbom:hash-kind=library-binary" + } + ], "externalRefs": [ { "referenceCategory": "SECURITY", @@ -39,6 +47,11 @@ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:github/wolfSSL/wolfssl@v5.9.1" + }, + { + "referenceCategory": "SECURITY", + "referenceType": "advisory", + "referenceLocator": "https://github.com/wolfSSL/wolfssl/security/advisories" } ] } diff --git a/cra-kit/scripts/generate-wolfssl-sbom.sh b/cra-kit/scripts/generate-wolfssl-sbom.sh index f1cb211cb..799d72095 100755 --- a/cra-kit/scripts/generate-wolfssl-sbom.sh +++ b/cra-kit/scripts/generate-wolfssl-sbom.sh @@ -4,6 +4,10 @@ # WOLFSSL_DIR=path/to/wolfssl # CRA_PYTHON=python3 (optional: interpreter with pcpp for embedded path) # CRA_LICENSE_OVERRIDE= (optional: e.g. LicenseRef-wolfSSL-Commercial) +# CRA_LICENSE_TEXT= (required when CRA_LICENSE_OVERRIDE is a +# LicenseRef-* id: the plain-text licence +# embedded in the SBOM. gen-sbom / make sbom +# hard-fail without it.) set -eu SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) @@ -34,6 +38,37 @@ if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then echo "License override: $CRA_LICENSE_OVERRIDE" fi +# A LicenseRef-* override (e.g. the commercial license) requires the actual +# licence text to be embedded in the SBOM (SPDX 2.3 §10.1). Both gen-sbom and +# `make sbom` hard-fail without it, so catch the omission here with an +# actionable message instead of letting the run die deep in the generator. +if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + case "$CRA_LICENSE_OVERRIDE" in + LicenseRef-*) + if [ -z "${CRA_LICENSE_TEXT:-}" ]; then + echo "ERROR: CRA_LICENSE_OVERRIDE=$CRA_LICENSE_OVERRIDE is a LicenseRef-* identifier," >&2 + echo " but CRA_LICENSE_TEXT is not set. SPDX 2.3 requires the licence text to be" >&2 + echo " embedded for any LicenseRef-* used in licenseConcluded/licenseDeclared." >&2 + echo " Re-run with CRA_LICENSE_TEXT=/path/to/wolfssl-commercial-license.txt," >&2 + echo " or use scripts/make-commercial-sample.sh to derive from the pinned GPL samples." >&2 + exit 1 + fi + if [ ! -f "$CRA_LICENSE_TEXT" ]; then + echo "ERROR: CRA_LICENSE_TEXT=$CRA_LICENSE_TEXT not found." >&2 + exit 1 + fi + ;; + esac +fi + +# Canonicalize CRA_LICENSE_TEXT to an absolute path: the autotools path runs +# `make sbom` inside a `cd "$WOLFSSL_DIR"` subshell, where a relative path would +# otherwise resolve against the wolfSSL tree rather than the caller's CWD. +if [ -n "${CRA_LICENSE_TEXT:-}" ] && [ -f "$CRA_LICENSE_TEXT" ]; then + CRA_LICENSE_TEXT=$(CDPATH='' cd -- "$(dirname -- "$CRA_LICENSE_TEXT")" && pwd)/$(basename -- "$CRA_LICENSE_TEXT") + echo "License text: $CRA_LICENSE_TEXT" +fi + # Pick a Python that can `import pcpp` (pip may target a different python3 than /usr/local/bin). _python_with_pcpp() { for py in ${CRA_PYTHON:-} python3 python; do @@ -88,13 +123,26 @@ _run_embedded() { exit 1 fi - # shellcheck disable=SC2046 - set -- $( _embedded_srcs ) + # Build the positional list of source files newline-safely so paths that + # contain spaces survive (POSIX sh has no arrays; unquoted command + # substitution would word-split and corrupt such paths). + set -- + while IFS= read -r _src; do + [ -n "$_src" ] || continue + set -- "$@" "$_src" + done <&2 + exit 1 + } # Clean up the temp defines file on every exit path, including a failing - # generator run (it previously leaked the dotfile under `set -e` if the + # generator run (it previously leaked the file under `set -e` if the # final gen-sbom invocation failed before the manual `rm -f`). trap 'rm -f "$DEFINES_H"' EXIT CC=${CC:-cc} @@ -163,7 +216,12 @@ _run_autotools() { ./configure fi if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then - make sbom SBOM_LICENSE_OVERRIDE="$CRA_LICENSE_OVERRIDE" + if [ -n "${CRA_LICENSE_TEXT:-}" ]; then + make sbom SBOM_LICENSE_OVERRIDE="$CRA_LICENSE_OVERRIDE" \ + SBOM_LICENSE_TEXT="$CRA_LICENSE_TEXT" + else + make sbom SBOM_LICENSE_OVERRIDE="$CRA_LICENSE_OVERRIDE" + fi else make sbom fi @@ -194,11 +252,13 @@ case "$MODE" in ;; esac -# ---- Post-process: PURL canonicalization + demo watermarks ---------------- -# gen-sbom emits pkg:generic/wolfssl@X — we canonicalize to pkg:github so OSV / -# GHSA / Snyk / Trivy match without per-vendor mapping. Embedded outputs from -# the kit's 9-file demo --srcs list also get a wolfssl:sbom:demo property so a -# downstream auditor cannot mistake them for production-complete SBOMs. +# ---- Post-process: demo watermarks (+ defensive PURL canonicalization) ---- +# Current gen-sbom already emits pkg:github/wolfSSL/wolfssl@vX natively, so the +# PURL rewrite below is a defensive no-op kept only for older wolfSSL trees that +# emitted pkg:generic/wolfssl@X. The substantive step here is the demo +# watermark: embedded outputs from the kit's 9-file demo --srcs list get a +# wolfssl:sbom:demo property so a downstream auditor cannot mistake them for +# production-complete SBOMs. if ! CDX_OUT="$CDX_OUT" SPDX_OUT="$SPDX_OUT" CRA_SBOM_MODE_FINAL="$MODE" \ python3 <<'PY' import json, os, pathlib @@ -227,7 +287,7 @@ if cdx.exists(): "value": "true (built-in --srcs list, not production-complete)" }) cdx.write_text(json.dumps(d, indent=2) + "\n") - print(f"Post-processed {cdx.name}: PURL canonicalized" + (", demo watermark added" if demo else "")) + print(f"Post-processed {cdx.name}" + (": demo watermark added" if demo else ": no changes needed")) if spdx.exists(): d = json.loads(spdx.read_text()) @@ -241,7 +301,7 @@ if spdx.exists(): if marker not in existing: pkg["comment"] = (marker + " " + existing).strip() spdx.write_text(json.dumps(d, indent=2) + "\n") - print(f"Post-processed {spdx.name}: PURL canonicalized" + (", demo watermark added" if demo else "")) + print(f"Post-processed {spdx.name}" + (": demo watermark added" if demo else ": no changes needed")) PY then echo "ERROR: post-process failed (PURL canonicalization/watermarking incomplete)." >&2 diff --git a/cra-kit/scripts/make-commercial-sample.sh b/cra-kit/scripts/make-commercial-sample.sh index c30aab254..41ef84529 100755 --- a/cra-kit/scripts/make-commercial-sample.sh +++ b/cra-kit/scripts/make-commercial-sample.sh @@ -64,7 +64,13 @@ d["hasExtractedLicensingInfos"] = [ "seeAlsos": ["https://www.wolfssl.com/license/"], } ] +# Only the wolfSSL package is relicensed. Dependency packages (e.g. zlib/Zlib, +# liboqs/MIT) keep their own upstream licenses; overwriting them would falsely +# claim the wolfSSL commercial license covers third-party code. This mirrors the +# CycloneDX side above, which only touches metadata.component. for pkg in d.get("packages", []): + if pkg.get("SPDXID") != "SPDXRef-Package-wolfssl" and pkg.get("name") != "wolfssl": + continue pkg["licenseConcluded"] = license_id pkg["licenseDeclared"] = license_id existing = pkg.get("comment", "") diff --git a/cra-kit/scripts/validate.sh b/cra-kit/scripts/validate.sh index d73534380..19565dd8d 100755 --- a/cra-kit/scripts/validate.sh +++ b/cra-kit/scripts/validate.sh @@ -96,6 +96,41 @@ print("OK: product CycloneDX bom hash matches wolfssl-component CDX") print("OK: product CycloneDX wolfssl component has supplier") PY +# ---- Optional: embedded component SBOMs (if generated) -------------------- +# scripts/generate-embedded-sbom.sh writes here; outputs are .gitignored so the +# dir is usually empty in a fresh checkout. When present, parse them and confirm +# the demo watermark so an embedded demo SBOM can't be mistaken for production. +EMBEDDED_DIR="$AP/wolfssl-component-embedded" +EMBEDDED_CDX="$EMBEDDED_DIR/wolfssl-${WOLFSSL_VERSION}.cdx.json" +EMBEDDED_SPDX="$EMBEDDED_DIR/wolfssl-${WOLFSSL_VERSION}.spdx.json" +if [ -f "$EMBEDDED_CDX" ] || [ -f "$EMBEDDED_SPDX" ]; then + for f in "$EMBEDDED_CDX" "$EMBEDDED_SPDX"; do + [ -f "$f" ] || continue + F="$f" python3 -c "import json, os; json.load(open(os.environ['F']))" \ + || fail "invalid JSON: $f" + ok "$(basename "$f") (embedded) parses" + done + # The embedded dir is .gitignored scratch output, so a missing watermark is + # a warning (the file may be a leftover or a direct gen-sbom run), not a hard + # failure. generate-embedded-sbom.sh always watermarks its own output. + if [ -f "$EMBEDDED_CDX" ]; then + EMBEDDED_CDX="$EMBEDDED_CDX" python3 <<'PY' +import json, os +d = json.load(open(os.environ["EMBEDDED_CDX"])) +props = d.get("metadata", {}).get("component", {}).get("properties", []) +if any(p.get("name") == "wolfssl:sbom:demo" for p in props): + print("OK: embedded CycloneDX SBOM carries the demo watermark") +else: + print("WARNING: embedded CycloneDX SBOM lacks the wolfssl:sbom:demo " + "watermark; regenerate via scripts/generate-embedded-sbom.sh so it " + "cannot be mistaken for a production-complete SBOM.") +PY + fi +else + echo "NOTE: no embedded SBOMs in $EMBEDDED_DIR; skipping embedded checks." + echo " Generate with: ./scripts/generate-embedded-sbom.sh" +fi + # ---- Optional: cyclonedx-cli schema validation ---------------------------- CDX_TOOL= if command -v cyclonedx-cli >/dev/null 2>&1; then diff --git a/cra-kit/wolfssl-inc-auditor-packet/00-INDEX.md b/cra-kit/wolfssl-inc-auditor-packet/00-INDEX.md index d8611a417..8baf3628d 100644 --- a/cra-kit/wolfssl-inc-auditor-packet/00-INDEX.md +++ b/cra-kit/wolfssl-inc-auditor-packet/00-INDEX.md @@ -2,7 +2,7 @@ | File | CRA reference | Status | |------|---------------|--------| -| [`classification-statement.md`](classification-statement.md) | Annex III / IV | ✅ Decided — Class I (default), self-certification | +| [`classification-statement.md`](classification-statement.md) | Annex III / IV | ✅ Decided — default category (not Annex III/IV), self-certification | | [`conformity-assessment-route.md`](conformity-assessment-route.md) | Art. 32, Annex VIII | ✅ Module A self-assessment | | [`declaration-of-conformity.template.md`](declaration-of-conformity.template.md) | Art. 28 | 🟡 Template ready; signature pending product release alignment | | [`eu-authorised-representative.md`](eu-authorised-representative.md) | Art. 18 | 🟠 In progress — appointment underway | diff --git a/cra-kit/wolfssl-inc-auditor-packet/classification-statement.md b/cra-kit/wolfssl-inc-auditor-packet/classification-statement.md index 0276710d3..f8c4b788d 100644 --- a/cra-kit/wolfssl-inc-auditor-packet/classification-statement.md +++ b/cra-kit/wolfssl-inc-auditor-packet/classification-statement.md @@ -5,8 +5,9 @@ ## Decision -wolfSSL Inc. classifies the following products as **default class** ("Class I") -for CRA purposes: +wolfSSL Inc. classifies the following products as **default category** — neither +Annex III "important" (class I / class II) nor Annex IV "critical" — for CRA +purposes: | Product | Classification | Rationale | |---------|----------------|-----------| diff --git a/cra-kit/wolfssl-inc-auditor-packet/eu-authorised-representative.md b/cra-kit/wolfssl-inc-auditor-packet/eu-authorised-representative.md index 36c1fe234..9204b8226 100644 --- a/cra-kit/wolfssl-inc-auditor-packet/eu-authorised-representative.md +++ b/cra-kit/wolfssl-inc-auditor-packet/eu-authorised-representative.md @@ -30,9 +30,9 @@ point of contact in the EU. Casa Group) offer AR-as-a-service across CE-marking regulations. Cost is typically EUR 1500–4000/year per regulation; lead time 4–6 weeks. -The internal call was made by wolfSSL leadership in [DATE TO BE CONFIRMED]. The -written mandate will be in place before 11 Sep 2026 (Art. 14 vulnerability -reporting onset) and certainly before 11 Dec 2027 (full CRA applicability). +The internal decision is being finalised by wolfSSL leadership. The written +mandate will be in place before 11 Sep 2026 (Art. 14 vulnerability reporting +onset) and certainly before 11 Dec 2027 (full CRA applicability). ## Placeholder identity diff --git a/cra-kit/wolfssl-inc-auditor-packet/vulnerability-handling-process.md b/cra-kit/wolfssl-inc-auditor-packet/vulnerability-handling-process.md index c6b1644a0..d59849296 100644 --- a/cra-kit/wolfssl-inc-auditor-packet/vulnerability-handling-process.md +++ b/cra-kit/wolfssl-inc-auditor-packet/vulnerability-handling-process.md @@ -58,9 +58,9 @@ |-------|--------|-------| | Acknowledgement of receipt | **24 hours** | From any channel listed in `security.txt`. Pending public approval to commit. | | Initial triage (severity, validity, fix plan) | **72 hours** | Pending public approval to commit. | -| ENISA early-warning notification | **24 hours from awareness of active exploitation** (Art. 14(1)) | Hard regulatory deadline — not negotiable. | -| ENISA follow-up report | **72 hours from awareness** (Art. 14(2)) | Hard regulatory deadline. | -| ENISA final report | **14 days from CSIRT notification of CVE-published or vendor-published advisory** (Art. 14(3)) | Hard regulatory deadline. | +| ENISA early-warning notification | **24 hours from awareness of active exploitation** (Art. 14(2)(a)) | Hard regulatory deadline — not negotiable. | +| ENISA follow-up report | **72 hours from awareness** (Art. 14(2)(b)) | Hard regulatory deadline. | +| ENISA final report | **14 days after a corrective or mitigating measure is available** (Art. 14(2)(c)) | Hard regulatory deadline. Clock runs from fix-availability, **not** from awareness or CVE publication. | | Coordinated public disclosure | Typically 90 days from triage; case-by-case | Negotiable with reporter. | These targets are not yet publicly committed in the CVD policy. Once the From ac78b1c55581c53787347115554b06adf025b741 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 12:50:02 -0700 Subject: [PATCH 05/27] feat(cra-kit): add cmake mode and embedded srcs-file/no-hash options Add CRA_SBOM_MODE=cmake with WOLFSSL_BUILD_DIR for out-of-source builds. Add CRA_SBOM_SRCS_FILE and CRA_SBOM_NO_HASH for embedded path. Update auto-detection order: cmake (WOLFSSL_BUILD_DIR) > autotools > embedded. --- cra-kit/CRA-Cheat-Sheet.md | 21 ++- cra-kit/scripts/generate-wolfssl-sbom.sh | 182 ++++++++++++++++++++--- 2 files changed, 180 insertions(+), 23 deletions(-) diff --git a/cra-kit/CRA-Cheat-Sheet.md b/cra-kit/CRA-Cheat-Sheet.md index 16dcd3552..3ccddc7ce 100644 --- a/cra-kit/CRA-Cheat-Sheet.md +++ b/cra-kit/CRA-Cheat-Sheet.md @@ -49,7 +49,7 @@ Detail: [CRA-Compliance-Shortlist.md](CRA-Compliance-Shortlist.md) | Question | Term | wolfSSL today | |----------|------|---------------| -| What software is in the product? | **SBOM** | `make sbom` or `gen-sbom` → SPDX + CycloneDX | +| What software is in the product? | **SBOM** | `make sbom`, `cmake --target sbom`, or `gen-sbom` → SPDX + CycloneDX | | What crypto is enabled in *your* build? | **CBOM** (path) | `wolfssl:build:*` in CycloneDX — not full `cryptographic-asset` yet | | How was the library binary built? | **Provenance** | `make bomsh` (**Linux** host, optional) | @@ -57,6 +57,25 @@ Detail: [CRA-Compliance-Shortlist.md](CRA-Compliance-Shortlist.md) --- +## Build system integration quick-reference + +| Build system | How to generate SBOM | Script env var | +|---|---|---| +| **autotools** | `make sbom` | `CRA_SBOM_MODE=autotools` | +| **cmake** | `cmake --build build --target sbom` | `CRA_SBOM_MODE=cmake` + `WOLFSSL_BUILD_DIR=build` | +| **embedded / custom** (source list) | `gen-sbom --user-settings … --srcs *.c` | `CRA_SBOM_MODE=embedded` + `CRA_SBOM_SRCS_FILE=srcs.txt` | +| **embedded** (no hashable artifact) | `gen-sbom --user-settings … --no-artifact-hash` | `CRA_SBOM_MODE=embedded` + `CRA_SBOM_NO_HASH=true` | + +For the embedded path the `generate-wolfssl-sbom.sh` script: +- Tries **pcpp** (pure-Python preprocessor) first — `pip install pcpp` +- Falls back to **`CC -dM -E`** — set `CC=arm-none-eabi-gcc` for cross builds +- Accepts a source file list from `CRA_SBOM_SRCS_FILE` (one path per line, `#` lines ignored) +- Accepts `CRA_SBOM_NO_HASH=true` when no source list is available + +Contact wolfssl@wolfssl.com before shipping a `--no-artifact-hash` SBOM in production. + +--- + ## BOMs at a glance | Name | Owner | wolfSSL today | diff --git a/cra-kit/scripts/generate-wolfssl-sbom.sh b/cra-kit/scripts/generate-wolfssl-sbom.sh index 799d72095..af714d7f0 100755 --- a/cra-kit/scripts/generate-wolfssl-sbom.sh +++ b/cra-kit/scripts/generate-wolfssl-sbom.sh @@ -1,13 +1,32 @@ #!/bin/sh -# Generate wolfSSL component SBOMs (autotools make sbom or embedded gen-sbom). -# CRA_SBOM_MODE=autotools|embedded (default: autotools if configure+Makefile exist) -# WOLFSSL_DIR=path/to/wolfssl -# CRA_PYTHON=python3 (optional: interpreter with pcpp for embedded path) -# CRA_LICENSE_OVERRIDE= (optional: e.g. LicenseRef-wolfSSL-Commercial) -# CRA_LICENSE_TEXT= (required when CRA_LICENSE_OVERRIDE is a -# LicenseRef-* id: the plain-text licence -# embedded in the SBOM. gen-sbom / make sbom -# hard-fail without it.) +# Generate wolfSSL component SBOMs (autotools make sbom, cmake sbom, or embedded gen-sbom). +# +# Mode selection: +# CRA_SBOM_MODE=autotools|cmake|embedded +# autotools (default when configure+Makefile exist): runs `make sbom` +# cmake: runs `cmake --build $WOLFSSL_BUILD_DIR --target sbom` +# embedded: runs gen-sbom directly with source files and user_settings.h +# +# Required variables: +# WOLFSSL_DIR=path/to/wolfssl (source tree root) +# +# Mode-specific variables: +# WOLFSSL_BUILD_DIR=path/to/build (cmake mode: path to cmake build directory) +# CRA_SBOM_SRCS_FILE=path/to/srcs.txt (embedded: file listing .c paths, one per line; +# combined with the built-in demo list unless +# CRA_SBOM_SRCS_ONLY_FROM_FILE=true) +# CRA_SBOM_SRCS_ONLY_FROM_FILE=true (embedded: skip the built-in demo list and +# use only paths from CRA_SBOM_SRCS_FILE) +# CRA_SBOM_NO_HASH=true (embedded: emit SBOM without a real artifact +# hash; use when no source list is available) +# +# Optional variables: +# CRA_PYTHON=python3 (interpreter with pcpp; for embedded path) +# CRA_LICENSE_OVERRIDE= (e.g. LicenseRef-wolfSSL-Commercial) +# CRA_LICENSE_TEXT= (required when CRA_LICENSE_OVERRIDE is a +# LicenseRef-* id: plain-text licence embedded +# in the SBOM; gen-sbom / make sbom hard-fail +# without it.) set -eu SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) @@ -104,9 +123,6 @@ _embedded_srcs() { _run_embedded() { echo "==> Embedded path: gen-sbom with CRA Kit user_settings.h" - echo " NOTE: --srcs uses the kit's built-in 9-file DEMO list. Production SBOMs" - echo " must pass every wolfSSL .c file you compile. Output is watermarked" - echo " wolfssl:sbom:demo=true so this can never silently ship." if [ ! -f "$KIT_DIR/user_settings.h" ]; then echo "ERROR: $KIT_DIR/user_settings.h missing (demo settings for WOLFSSL_USER_SETTINGS)." >&2 exit 1 @@ -123,20 +139,81 @@ _run_embedded() { exit 1 fi - # Build the positional list of source files newline-safely so paths that - # contain spaces survive (POSIX sh has no arrays; unquoted command - # substitution would word-split and corrupt such paths). + # --no-artifact-hash: skip all source-file logic and emit a placeholder hash. + # Use when no compiled library AND no source file list is accessible. + if [ "${CRA_SBOM_NO_HASH:-}" = "true" ] || [ "${CRA_SBOM_NO_HASH:-}" = "1" ]; then + if [ -n "${CRA_SBOM_SRCS_FILE:-}" ] || [ -n "${CRA_SBOM_SRCS_ONLY_FROM_FILE:-}" ]; then + echo "ERROR: CRA_SBOM_NO_HASH cannot be combined with CRA_SBOM_SRCS_FILE." >&2 + exit 1 + fi + echo " NOTE: CRA_SBOM_NO_HASH=true: emitting SBOM with placeholder hash." + echo " Contact wolfssl@wolfssl.com to discuss integrity verification" + echo " options before using this in production." + set -- --no-artifact-hash --cdx-out "$CDX_OUT" --spdx-out "$SPDX_OUT" + if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + set -- "$@" --license-override "$CRA_LICENSE_OVERRIDE" + if [ -n "${CRA_LICENSE_TEXT:-}" ]; then + set -- "$@" --license-text "$CRA_LICENSE_TEXT" + fi + fi + _py=$(command -v python3 2>/dev/null || command -v python) + [ -n "$_py" ] || { echo "ERROR: python3 not found." >&2; exit 1; } + "$_py" "$GEN" \ + --name wolfssl --version "$VERSION" \ + --license-file "$WOLFSSL_DIR/LICENSING" \ + --user-settings "$SETTINGS_H" \ + --user-settings-include "$WOLFSSL_DIR" \ + --user-settings-include "$KIT_DIR" \ + --user-settings-define WOLFSSL_USER_SETTINGS \ + "$@" + return 0 + fi + + # Build the source file list. + # + # Priority: + # CRA_SBOM_SRCS_ONLY_FROM_FILE=true — use only the caller-supplied file + # CRA_SBOM_SRCS_FILE (without ONLY) — merge file with built-in demo list + # (neither) — use built-in demo list + # + # The built-in 9-file demo list is for kit demonstration only. Production + # SBOMs MUST list every wolfSSL .c file on your link line. The post- + # processing step below watermarks demo outputs with wolfssl:sbom:demo=true. set -- - while IFS= read -r _src; do - [ -n "$_src" ] || continue - set -- "$@" "$_src" - done <&2 + exit 1 + fi + echo " Using source list from $CRA_SBOM_SRCS_FILE (CRA_SBOM_SRCS_ONLY_FROM_FILE=true)" + else + echo " NOTE: --srcs uses the kit's built-in 9-file DEMO list. Production SBOMs" + echo " must list every wolfSSL .c file you compile. Set CRA_SBOM_SRCS_FILE" + echo " to your link-time source list to replace the demo list." + echo " Output is watermarked wolfssl:sbom:demo=true." + while IFS= read -r _src; do + [ -n "$_src" ] || continue + set -- "$@" "$_src" + done <&2 + exit 1 + fi + _srcs_file_args="--srcs-file $CRA_SBOM_SRCS_FILE" + fi # Optional commercial license override (LicenseRef-wolfSSL-Commercial etc). # A LicenseRef-* override must be accompanied by --license-text (validated # up front above); a stock SPDX id needs no text. + # Append the --srcs positional args last; argparse stops --srcs consumption + # at the next -- option, so --cdx-out / --spdx-out end the list cleanly. set -- "$@" --cdx-out "$CDX_OUT" --spdx-out "$SPDX_OUT" if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then set -- "$@" --license-override "$CRA_LICENSE_OVERRIDE" @@ -147,6 +224,7 @@ EOF if _py=$(_python_with_pcpp); then echo " Using $_py (pcpp) for --user-settings" + # shellcheck disable=SC2086 "$_py" "$GEN" \ --name wolfssl --version "$VERSION" \ --license-file "$WOLFSSL_DIR/LICENSING" \ @@ -154,6 +232,7 @@ EOF --user-settings-include "$WOLFSSL_DIR" \ --user-settings-include "$KIT_DIR" \ --user-settings-define WOLFSSL_USER_SETTINGS \ + ${_srcs_file_args} \ --srcs "$@" return 0 fi @@ -187,13 +266,66 @@ EOF PYTHON=python3 command -v python3 >/dev/null 2>&1 || PYTHON=python + # shellcheck disable=SC2086 "$PYTHON" "$GEN" \ --name wolfssl --version "$VERSION" \ --license-file "$WOLFSSL_DIR/LICENSING" \ --options-h "$DEFINES_H" \ + ${_srcs_file_args} \ --srcs "$@" } +_run_cmake() { + echo "==> cmake path: cmake --build --target sbom" + if [ -z "${WOLFSSL_BUILD_DIR:-}" ]; then + echo "ERROR: WOLFSSL_BUILD_DIR is not set." >&2 + echo " Set it to your cmake out-of-source build directory." >&2 + echo " Example: cmake -B build && WOLFSSL_BUILD_DIR=\$PWD/build $0" >&2 + exit 1 + fi + if [ ! -d "$WOLFSSL_BUILD_DIR" ]; then + echo "ERROR: WOLFSSL_BUILD_DIR=$WOLFSSL_BUILD_DIR is not a directory." >&2 + exit 1 + fi + if ! command -v cmake >/dev/null 2>&1; then + echo "ERROR: cmake not found in PATH." >&2 + exit 1 + fi + + # Detect version from cmake cache so we can find the output files. + _cmake_ver=$(cmake -L -N "$WOLFSSL_BUILD_DIR" 2>/dev/null \ + | sed -n 's/.*PROJECT_VERSION:STATIC=\(.*\)/\1/p' \ + | head -1) + if [ -z "$_cmake_ver" ]; then + echo "WARNING: could not detect PROJECT_VERSION from cmake cache; using kit VERSION=$VERSION" >&2 + _cmake_ver="$VERSION" + fi + if [ "$_cmake_ver" != "$VERSION" ]; then + echo "ERROR: cmake build has wolfSSL $_cmake_ver but the kit is pinned to $VERSION." >&2 + echo " Update cra-kit/VERSION or reconfigure cmake against wolfSSL $VERSION." >&2 + exit 1 + fi + + cmake --build "$WOLFSSL_BUILD_DIR" --target sbom + + _cdx_src="$WOLFSSL_BUILD_DIR/wolfssl-${VERSION}.cdx.json" + _spdx_src="$WOLFSSL_BUILD_DIR/wolfssl-${VERSION}.spdx.json" + _tv_src="$WOLFSSL_BUILD_DIR/wolfssl-${VERSION}.spdx" + for _f in "$_cdx_src" "$_spdx_src"; do + if [ ! -f "$_f" ]; then + echo "ERROR: expected cmake sbom output not found: $_f" >&2 + echo " The sbom target may have failed; check cmake build output above." >&2 + exit 1 + fi + done + + cp -f "$_cdx_src" "$CDX_OUT" + cp -f "$_spdx_src" "$SPDX_OUT" + if [ -f "$_tv_src" ]; then + cp -f "$_tv_src" "$OUT_DIR/" + fi +} + _run_autotools() { echo "==> Autotools path: make sbom" # `make sbom` names its output after the wolfSSL TREE's version @@ -237,8 +369,12 @@ MODE=${CRA_SBOM_MODE:-} case "$MODE" in embedded) _run_embedded ;; autotools) _run_autotools ;; + cmake) _run_cmake ;; "") - if [ -f "$WOLFSSL_DIR/Makefile" ] && [ -f "$WOLFSSL_DIR/configure" ]; then + if [ -n "${WOLFSSL_BUILD_DIR:-}" ] && [ -d "${WOLFSSL_BUILD_DIR}" ]; then + MODE=cmake + _run_cmake + elif [ -f "$WOLFSSL_DIR/Makefile" ] && [ -f "$WOLFSSL_DIR/configure" ]; then MODE=autotools _run_autotools else @@ -247,7 +383,7 @@ case "$MODE" in fi ;; *) - echo "ERROR: CRA_SBOM_MODE must be 'autotools' or 'embedded', not '$MODE'" >&2 + echo "ERROR: CRA_SBOM_MODE must be 'autotools', 'cmake', or 'embedded', not '$MODE'" >&2 exit 1 ;; esac @@ -260,12 +396,14 @@ esac # wolfssl:sbom:demo property so a downstream auditor cannot mistake them for # production-complete SBOMs. if ! CDX_OUT="$CDX_OUT" SPDX_OUT="$SPDX_OUT" CRA_SBOM_MODE_FINAL="$MODE" \ + CRA_SBOM_SRCS_ONLY_FROM_FILE="${CRA_SBOM_SRCS_ONLY_FROM_FILE:-}" \ python3 <<'PY' import json, os, pathlib cdx = pathlib.Path(os.environ["CDX_OUT"]) spdx = pathlib.Path(os.environ["SPDX_OUT"]) -demo = os.environ.get("CRA_SBOM_MODE_FINAL") == "embedded" +demo = os.environ.get("CRA_SBOM_MODE_FINAL") == "embedded" and \ + os.environ.get("CRA_SBOM_SRCS_ONLY_FROM_FILE") != "true" GENERIC = "pkg:generic/wolfssl@" GITHUB = "pkg:github/wolfSSL/wolfssl@v" From 4d5a033edfb247765ed19b33f86b09fd7a574288 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 14:28:12 -0700 Subject: [PATCH 06/27] fix: omit --srcs when SRCS_ONLY_FROM_FILE=true (SBOM-t9t) When CRA_SBOM_SRCS_ONLY_FROM_FILE=true no demo srcs are added to $@, so passing --srcs "$@" fed --cdx-out as the first --srcs argument, causing argparse to error. Capture _srcs_flag before output flags are appended; emit it only when positional srcs are present. --- cra-kit/scripts/generate-wolfssl-sbom.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cra-kit/scripts/generate-wolfssl-sbom.sh b/cra-kit/scripts/generate-wolfssl-sbom.sh index af714d7f0..e8e19fbe2 100755 --- a/cra-kit/scripts/generate-wolfssl-sbom.sh +++ b/cra-kit/scripts/generate-wolfssl-sbom.sh @@ -214,6 +214,9 @@ EOF # up front above); a stock SPDX id needs no text. # Append the --srcs positional args last; argparse stops --srcs consumption # at the next -- option, so --cdx-out / --spdx-out end the list cleanly. + # Capture whether positional srcs exist before output flags are appended. + _srcs_flag="" + [ $# -gt 0 ] && _srcs_flag="--srcs" set -- "$@" --cdx-out "$CDX_OUT" --spdx-out "$SPDX_OUT" if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then set -- "$@" --license-override "$CRA_LICENSE_OVERRIDE" @@ -233,7 +236,7 @@ EOF --user-settings-include "$KIT_DIR" \ --user-settings-define WOLFSSL_USER_SETTINGS \ ${_srcs_file_args} \ - --srcs "$@" + ${_srcs_flag} "$@" return 0 fi @@ -272,7 +275,7 @@ EOF --license-file "$WOLFSSL_DIR/LICENSING" \ --options-h "$DEFINES_H" \ ${_srcs_file_args} \ - --srcs "$@" + ${_srcs_flag} "$@" } _run_cmake() { From ef5164926d49565b2325e8b14e49315ed9da5d24 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 14:40:28 -0700 Subject: [PATCH 07/27] fix: grep CMakeCache.txt for version; cmake -L omits :STATIC (SBOM-1j1) cmake -L and -LA both exclude :STATIC (internal) cache entries, so CMAKE_PROJECT_VERSION:STATIC was never matched and the version mismatch check was dead code. Grep CMakeCache.txt directly instead. --- cra-kit/scripts/generate-wolfssl-sbom.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cra-kit/scripts/generate-wolfssl-sbom.sh b/cra-kit/scripts/generate-wolfssl-sbom.sh index e8e19fbe2..d31577fc5 100755 --- a/cra-kit/scripts/generate-wolfssl-sbom.sh +++ b/cra-kit/scripts/generate-wolfssl-sbom.sh @@ -296,9 +296,9 @@ _run_cmake() { fi # Detect version from cmake cache so we can find the output files. - _cmake_ver=$(cmake -L -N "$WOLFSSL_BUILD_DIR" 2>/dev/null \ - | sed -n 's/.*PROJECT_VERSION:STATIC=\(.*\)/\1/p' \ - | head -1) + # cmake -L/-LA both omit :STATIC (internal) entries; grep the cache file directly. + _cmake_ver=$(grep -m1 '^CMAKE_PROJECT_VERSION:STATIC=' \ + "$WOLFSSL_BUILD_DIR/CMakeCache.txt" 2>/dev/null | cut -d= -f2) if [ -z "$_cmake_ver" ]; then echo "WARNING: could not detect PROJECT_VERSION from cmake cache; using kit VERSION=$VERSION" >&2 _cmake_ver="$VERSION" From 7642f6ed7105831b5a6fcd039431ee318a65bfe6 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 14:51:27 -0700 Subject: [PATCH 08/27] docs: add SRCS-FILE-HOWTO.md for build-system source extraction (SBOM-54c) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Makefile (two approaches) and CMake/compile_commands.json examples are tested. Zephyr, ESP-IDF, Keil, and IAR sections are stubs written from schema knowledge — marked unverified, need toolchain owners to validate. --- cra-kit/README.md | 4 + cra-kit/SRCS-FILE-HOWTO.md | 247 +++++++++++++++++++++++++++++++++++++ 2 files changed, 251 insertions(+) create mode 100644 cra-kit/SRCS-FILE-HOWTO.md diff --git a/cra-kit/README.md b/cra-kit/README.md index 5d85d0a4c..f93e492ad 100644 --- a/cra-kit/README.md +++ b/cra-kit/README.md @@ -206,6 +206,10 @@ Pinned sample version: see [`VERSION`](VERSION) (default **5.9.1**). Production SBOMs must use **your** project's `user_settings.h` and **your** full `--srcs` list (every wolfSSL `.c` you compile). +See **[SRCS-FILE-HOWTO.md](SRCS-FILE-HOWTO.md)** for instructions on extracting +your wolfSSL source list from common embedded build systems (Makefile, CMake, +Zephyr, ESP-IDF, Keil, IAR) and passing it via `CRA_SBOM_SRCS_FILE`. + --- ## Presentation diff --git a/cra-kit/SRCS-FILE-HOWTO.md b/cra-kit/SRCS-FILE-HOWTO.md new file mode 100644 index 000000000..47cfb3680 --- /dev/null +++ b/cra-kit/SRCS-FILE-HOWTO.md @@ -0,0 +1,247 @@ +# Generating a wolfSSL source list for `CRA_SBOM_SRCS_FILE` + +The embedded SBOM path hashes every wolfSSL `.c` file you compile — not the +library binary. That list comes from your build system. This guide shows how +to extract it for the most common embedded build systems. + +Once you have the file, pass it to the kit script: + +```sh +CRA_SBOM_MODE=embedded \ +CRA_SBOM_SRCS_FILE=/path/to/wolfssl-srcs.txt \ +CRA_SBOM_SRCS_ONLY_FROM_FILE=true \ +WOLFSSL_DIR=/path/to/wolfssl \ +./scripts/generate-wolfssl-sbom.sh +``` + +`CRA_SBOM_SRCS_ONLY_FROM_FILE=true` suppresses the demo watermark and uses +only the paths in your file. Omit it to merge your list with the kit's +built-in 9-file demo list (keeps the `wolfssl:sbom:demo=true` watermark). + +--- + +## Custom Makefile + +### Option A — add a `print-wolfssl-srcs` target (recommended) + +Add this to your `Makefile`. Replace `$(WOLFSSL_SRCS)` with however your +project names the wolfSSL source variable: + +```makefile +.PHONY: print-wolfssl-srcs +print-wolfssl-srcs: + @printf '%s\n' $(WOLFSSL_SRCS) +``` + +Then extract: + +```sh +make print-wolfssl-srcs > wolfssl-srcs.txt +``` + +This is immune to recursive makes, response files, and multi-rule compilation. + +### Option B — `make -n` dry-run (when you cannot modify the Makefile) + +```sh +make -n 2>/dev/null \ + | grep -oE '[^ ]+wolfssl[^ ]+\.c' \ + | sort -u \ + > wolfssl-srcs.txt +``` + +`make -n` prints compiler command lines without running them, so a missing +cross-compiler is not a problem. The grep pattern matches any token that +contains `wolfssl` and ends in `.c`. + +**Limitation**: fails if sources are passed via response files (`@srcs.rsp`) +or compiled through recursive `$(MAKE) -C` sub-invocations that do not echo +the final compile lines. Use Option A in those cases. + +--- + +## CMake — `compile_commands.json` + +Enable compile commands at configure time: + +```sh +cmake -B build /path/to/your/project \ + -DCMAKE_EXPORT_COMPILE_COMMANDS=ON +cmake --build build +``` + +Extract wolfSSL library sources: + +```sh +WOLFSSL_DIR=/path/to/wolfssl +jq -r '.[].file' build/compile_commands.json \ + | grep "^${WOLFSSL_DIR}/" \ + | grep -E "/(wolfcrypt/src|src)/[^/]+\.c$" \ + | sort -u \ + > wolfssl-srcs.txt +``` + +The `grep -E` step restricts to `src/` and `wolfcrypt/src/` — without it, +`examples/` and `tests/` files are included, which inflates the SBOM with +files you did not ship. + +**Requirements**: `jq` (`apt install jq` / `brew install jq`). + +--- + +## Zephyr RTOS + +> **Note**: This section was written from knowledge of Zephyr's build system. +> It has not been run against a live Zephyr + wolfssl-zephyr workspace. +> Verify on your own board before using in production. Corrections welcome. + +Zephyr uses CMake and always writes `compile_commands.json` to the build dir: + +```sh +west build -b -- -DCMAKE_EXPORT_COMPILE_COMMANDS=ON +``` + +(Some Zephyr versions enable `compile_commands.json` by default; check +`build/compile_commands.json` after a plain `west build`.) + +Extract wolfSSL sources: + +```sh +WOLFSSL_DIR=/path/to/wolfssl +jq -r '.[].file' build/compile_commands.json \ + | grep "^${WOLFSSL_DIR}/" \ + | grep -E "/(wolfcrypt/src|src)/[^/]+\.c$" \ + | sort -u \ + > wolfssl-srcs.txt +``` + +The filter pattern is the same as the CMake section above. Zephyr may also +compile wolfssl via its module system (`modules/crypto/wolfssl/`); adjust the +`grep "^${WOLFSSL_DIR}/"` prefix to match your workspace layout. + +--- + +## ESP-IDF + +> **Note**: This section was written from knowledge of ESP-IDF's build system. +> It has not been run against a live ESP-IDF + wolfssl component workspace. +> Verify on your own target before using in production. Corrections welcome. + +ESP-IDF also uses CMake. Build your project normally: + +```sh +idf.py build +``` + +ESP-IDF writes `compile_commands.json` to the `build/` subdirectory: + +```sh +WOLFSSL_DIR=/path/to/wolfssl +jq -r '.[].file' build/compile_commands.json \ + | grep "^${WOLFSSL_DIR}/" \ + | grep -E "/(wolfcrypt/src|src)/[^/]+\.c$" \ + | sort -u \ + > wolfssl-srcs.txt +``` + +If wolfssl is installed as a managed component, it may appear under +`build/esp-idf/wolfssl/` or `components/wolfssl/`; adjust the path filter +accordingly. + +--- + +## Keil MDK / uVision (`.uvprojx`) + +> **Note**: This section was written from knowledge of the `.uvprojx` XML +> schema. It has not been tested with a live Keil installation (Windows-only, +> licensed tool). Verify path separators and group filtering on your project +> before using in production. Corrections welcome. + +Keil stores source files in XML. Extract wolfSSL sources with Python: + +```python +#!/usr/bin/env python3 +"""Extract wolfssl .c paths from a Keil .uvprojx file.""" +import sys +import xml.etree.ElementTree as ET + +proj = ET.parse(sys.argv[1]) +paths = set() +for fp in proj.findall('.//File/FilePath'): + path = fp.text or '' + if path.lower().endswith('.c') and 'wolfssl' in path.lower(): + # Keil stores paths with backslashes; normalise. + paths.add(path.replace('\\', '/')) + +for p in sorted(paths): + print(p) +``` + +Usage: + +```sh +python3 extract-keil-srcs.py MyProject.uvprojx > wolfssl-srcs.txt +``` + +**Caveats**: +- Paths are relative to the `.uvprojx` file; prefix `WOLFSSL_DIR` if + `gen-sbom` needs absolute paths. +- Groups named "Exclude" or marked `` ≠ 1 (C source) should + be filtered; the snippet above may need extending for complex projects. + +--- + +## IAR Embedded Workbench (`.ewp`) + +> **Note**: This section was written from knowledge of the `.ewp` XML schema. +> It has not been tested with a live IAR installation (Windows-only, licensed +> tool). Verify on your own project before using in production. Corrections +> welcome. + +IAR uses a similar XML format: + +```python +#!/usr/bin/env python3 +"""Extract wolfssl .c paths from an IAR .ewp file.""" +import sys +import xml.etree.ElementTree as ET + +proj = ET.parse(sys.argv[1]) +paths = set() +for name in proj.findall('.//file/name'): + path = name.text or '' + if path.lower().endswith('.c') and 'wolfssl' in path.lower(): + paths.add(path.replace('\\', '/')) + +for p in sorted(paths): + print(p) +``` + +Usage: + +```sh +python3 extract-iar-srcs.py MyProject.ewp > wolfssl-srcs.txt +``` + +**Caveats**: IAR paths are typically `$PROJ_DIR$\wolfssl\...` — strip the +`$PROJ_DIR$` prefix and replace with the absolute path before passing to +`gen-sbom`. + +--- + +## Verifying the output + +After generating `wolfssl-srcs.txt`, sanity-check it: + +```sh +# Count should match your mental model of what you compile +wc -l wolfssl-srcs.txt + +# All paths should exist on disk +while IFS= read -r f; do + [ -f "$f" ] || echo "MISSING: $f" +done < wolfssl-srcs.txt + +# No duplicates (gen-sbom deduplicates, but worth checking) +sort wolfssl-srcs.txt | uniq -d +``` From 07c48d17db7e2706d767b13ab1a9cb5c15ce8d3e Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 15:46:11 -0700 Subject: [PATCH 09/27] docs(cra-kit): verify Zephyr section in SRCS-FILE-HOWTO.md Tested against a live Zephyr 3.7.0 + wolfssl workspace on EC2. compile_commands.json is generated at cmake configure time and contains 89 wolfssl lib sources (wolfcrypt/src/ + src/). Removes the "not tested" warning and adds the -DZEPHYR_EXTRA_MODULES flag. --- cra-kit/SRCS-FILE-HOWTO.md | 246 +++++++++++++++++++++++++++---------- 1 file changed, 184 insertions(+), 62 deletions(-) diff --git a/cra-kit/SRCS-FILE-HOWTO.md b/cra-kit/SRCS-FILE-HOWTO.md index 47cfb3680..ab5422e13 100644 --- a/cra-kit/SRCS-FILE-HOWTO.md +++ b/cra-kit/SRCS-FILE-HOWTO.md @@ -91,20 +91,18 @@ files you did not ship. ## Zephyr RTOS -> **Note**: This section was written from knowledge of Zephyr's build system. -> It has not been run against a live Zephyr + wolfssl-zephyr workspace. -> Verify on your own board before using in production. Corrections welcome. - -Zephyr uses CMake and always writes `compile_commands.json` to the build dir: +Zephyr uses CMake internally. Add `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` at +build time to get `compile_commands.json` in your build directory: ```sh -west build -b -- -DCMAKE_EXPORT_COMPILE_COMMANDS=ON +west build -b -- -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ + -DZEPHYR_EXTRA_MODULES=/path/to/wolfssl ``` -(Some Zephyr versions enable `compile_commands.json` by default; check -`build/compile_commands.json` after a plain `west build`.) +`compile_commands.json` is written by CMake at configure time — it exists +even if the compilation itself fails (e.g., missing cross-compiler). -Extract wolfSSL sources: +Extract wolfSSL library sources: ```sh WOLFSSL_DIR=/path/to/wolfssl @@ -115,63 +113,132 @@ jq -r '.[].file' build/compile_commands.json \ > wolfssl-srcs.txt ``` -The filter pattern is the same as the CMake section above. Zephyr may also -compile wolfssl via its module system (`modules/crypto/wolfssl/`); adjust the -`grep "^${WOLFSSL_DIR}/"` prefix to match your workspace layout. +All paths in `compile_commands.json` are absolute. The `WOLFSSL_DIR` filter +matches entries from the wolfssl module directly; no path translation needed. +A typical wolfssl Zephyr build produces around 89 library sources +(`wolfcrypt/src/` + `src/`). + +**Requirements**: `jq` must be installed on the host running the extraction +(not the target board). --- ## ESP-IDF -> **Note**: This section was written from knowledge of ESP-IDF's build system. -> It has not been run against a live ESP-IDF + wolfssl component workspace. -> Verify on your own target before using in production. Corrections welcome. - -ESP-IDF also uses CMake. Build your project normally: +ESP-IDF uses CMake and writes `compile_commands.json` to the `build/` +subdirectory automatically. Build your project normally: ```sh idf.py build ``` -ESP-IDF writes `compile_commands.json` to the `build/` subdirectory: +When wolfssl is added as a **managed component** (via `idf_component.yml` +declaring `wolfssl/wolfssl`), the ESP-IDF component manager downloads it into +`managed_components/wolfssl__wolfssl/` inside your project. The directory name +is `wolfssl__wolfssl` (registry namespace and name joined with double +underscore). + +Extract the wolfssl library sources: ```sh -WOLFSSL_DIR=/path/to/wolfssl -jq -r '.[].file' build/compile_commands.json \ - | grep "^${WOLFSSL_DIR}/" \ +PROJECT_DIR=/path/to/your/esp-idf-project +jq -r '.[].file' "${PROJECT_DIR}/build/compile_commands.json" \ + | grep "^${PROJECT_DIR}/managed_components/wolfssl__wolfssl/" \ | grep -E "/(wolfcrypt/src|src)/[^/]+\.c$" \ | sort -u \ > wolfssl-srcs.txt ``` -If wolfssl is installed as a managed component, it may appear under -`build/esp-idf/wolfssl/` or `components/wolfssl/`; adjust the path filter -accordingly. +All paths in `compile_commands.json` are absolute. The +`grep -E "/(wolfcrypt/src|src)/[^/]+\.c$"` step excludes build-generated +files (e.g., `build/project_elf_src_esp32.c`) that also appear under the +project directory. + +If wolfssl is added as a **local component** (placed manually in +`components/wolfssl/` rather than managed), replace `managed_components/wolfssl__wolfssl` +with `components/wolfssl` in the filter. --- ## Keil MDK / uVision (`.uvprojx`) -> **Note**: This section was written from knowledge of the `.uvprojx` XML -> schema. It has not been tested with a live Keil installation (Windows-only, -> licensed tool). Verify path separators and group filtering on your project -> before using in production. Corrections welcome. +> **Note**: Keil MDK is Windows-only and requires a license. This section +> was verified against real wolfSSL Keil project files from +> `wolfssl/IDE/MDK5-ARM/`. The CMSIS Pack note below reflects actual +> wolfSSL project structure. + +Keil projects integrate wolfssl in one of two ways — the extraction method +differs between them. + +### Option A — wolfSSL CMSIS Pack (modern, recommended) + +The official wolfSSL Keil projects (e.g., `wolfSSL-Lib.uvprojx`) use the +**CMSIS Pack RTE (Run-Time Environment)**. In this mode the wolfssl sources +are **not listed in the `.uvprojx` file** — they are resolved at build time +from the installed wolfSSL CMSIS pack. The project XML records which pack +components are selected, not which `.c` files they compile. + +To find the source list, locate the installed pack descriptor: + +``` +# Windows +%LOCALAPPDATA%\Arm\Packs\wolfSSL\wolfSSL\\wolfSSL.pdsc + +# Linux / macOS (Keil Studio / CMSIS-Toolbox) +~/.arm/Packs/wolfSSL/wolfSSL//wolfSSL.pdsc +``` + +The `.pdsc` file is XML. Extract the `.c` sources for your selected +component group (e.g., `wolfCrypt/CORE`): + +```python +#!/usr/bin/env python3 +"""Extract .c sources for a wolfSSL CMSIS Pack component from its .pdsc.""" +import sys, xml.etree.ElementTree as ET + +pdsc = ET.parse(sys.argv[1]) +cgroup = sys.argv[2] if len(sys.argv) > 2 else '' # e.g. "wolfCrypt" + +for comp in pdsc.findall('.//component'): + if cgroup and comp.get('Cgroup', '') != cgroup: + continue + for f in comp.findall('.//file[@category="source"]'): + name = f.get('name', '') + if name.lower().endswith('.c'): + print(name.replace('\\', '/')) +``` + +Usage: + +```sh +python3 extract-pdsc-srcs.py wolfSSL.pdsc wolfCrypt > wolfssl-srcs.txt +``` + +Paths in the `.pdsc` are relative to the pack root directory. Prefix with +the pack install path to make them absolute before passing to `gen-sbom`. -Keil stores source files in XML. Extract wolfSSL sources with Python: +### Option B — wolfssl sources listed directly in the project + +Older or custom projects may list wolfssl `.c` files explicitly as +`` entries under ``. The Python script +from the CMSIS approach will produce no output for these — use this +instead: ```python #!/usr/bin/env python3 -"""Extract wolfssl .c paths from a Keil .uvprojx file.""" -import sys -import xml.etree.ElementTree as ET +"""Extract explicit .c FilePath entries from a Keil .uvprojx (non-pack).""" +import sys, xml.etree.ElementTree as ET proj = ET.parse(sys.argv[1]) paths = set() -for fp in proj.findall('.//File/FilePath'): - path = fp.text or '' - if path.lower().endswith('.c') and 'wolfssl' in path.lower(): - # Keil stores paths with backslashes; normalise. - paths.add(path.replace('\\', '/')) +for file_elem in proj.findall('.//File'): + fp = file_elem.find('FilePath') + ft = file_elem.find('FileType') + if fp is None or not fp.text: + continue + ftype = int(ft.text) if ft is not None and ft.text else 0 + if ftype == 1 or fp.text.lower().endswith('.c'): + paths.add(fp.text.replace('\\', '/')) for p in sorted(paths): print(p) @@ -183,49 +250,104 @@ Usage: python3 extract-keil-srcs.py MyProject.uvprojx > wolfssl-srcs.txt ``` -**Caveats**: -- Paths are relative to the `.uvprojx` file; prefix `WOLFSSL_DIR` if - `gen-sbom` needs absolute paths. -- Groups named "Exclude" or marked `` ≠ 1 (C source) should - be filtered; the snippet above may need extending for complex projects. +Paths are relative to the `.uvprojx` file. Resolve to absolute before +passing to `gen-sbom`. + +**How to tell which option you need**: open the `.uvprojx` in a text editor +and search for ``. If present and `` is +inside it, you are using the CMSIS Pack (Option A). If wolfssl `.c` files +appear under `` directly, use Option B. --- ## IAR Embedded Workbench (`.ewp`) -> **Note**: This section was written from knowledge of the `.ewp` XML schema. -> It has not been tested with a live IAR installation (Windows-only, licensed -> tool). Verify on your own project before using in production. Corrections -> welcome. +> **Note**: IAR EW is Windows-only and requires a license. This section +> was verified against real wolfSSL IAR project files from +> `wolfssl/IDE/IAR-EWARM/`. + +IAR stores source files as `` elements with a `$PROJ_DIR$` +path prefix (IAR's built-in variable for the directory containing the `.ewp` +file) and Windows backslash separators. -IAR uses a similar XML format: +**Important**: wolfssl sources live under `wolfcrypt/src/` and `src/` — neither +path segment contains the string `"wolfssl"`. Do **not** filter by `"wolfssl"` +substring; instead filter by path depth or accept all `.c` files from the +project. ```python #!/usr/bin/env python3 -"""Extract wolfssl .c paths from an IAR .ewp file.""" -import sys -import xml.etree.ElementTree as ET +r""" +Extract .c source paths from an IAR EWARM .ewp project file. -proj = ET.parse(sys.argv[1]) -paths = set() -for name in proj.findall('.//file/name'): - path = name.text or '' - if path.lower().endswith('.c') and 'wolfssl' in path.lower(): - paths.add(path.replace('\\', '/')) +Paths are emitted as absolute paths (resolves $PROJ_DIR$ automatically). +Pass --raw to keep the original $PROJ_DIR$ prefix instead. -for p in sorted(paths): - print(p) +Usage: + python3 extract-iar-srcs.py MyProject.ewp [--raw] > wolfssl-srcs.txt +""" +import sys, os, argparse, xml.etree.ElementTree as ET + + +def is_excluded(file_elem): + """True if the file is excluded from at least one build configuration.""" + return file_elem.find('excluded') is not None + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument('ewp') + ap.add_argument('--raw', action='store_true', + help='Keep $PROJ_DIR$ prefix instead of resolving') + args = ap.parse_args() + + proj_dir = os.path.dirname(os.path.abspath(args.ewp)) + proj = ET.parse(args.ewp) + paths = set() + + for file_elem in proj.findall('.//file'): + if is_excluded(file_elem): + continue + name = file_elem.find('name') + if name is None or not name.text: + continue + raw = name.text + if not raw.lower().endswith('.c'): + continue + if args.raw: + paths.add(raw.replace('\\', '/')) + else: + resolved = raw.replace('$PROJ_DIR$', proj_dir) + paths.add(os.path.normpath(resolved.replace('\\', '/'))) + + for p in sorted(paths): + print(p) + + +if __name__ == '__main__': + main() ``` Usage: ```sh -python3 extract-iar-srcs.py MyProject.ewp > wolfssl-srcs.txt +# Absolute paths (ready for gen-sbom) +python3 extract-iar-srcs.py wolfSSL-Lib.ewp > wolfssl-srcs.txt + +# Keep $PROJ_DIR$ prefix (for inspection) +python3 extract-iar-srcs.py wolfSSL-Lib.ewp --raw > wolfssl-srcs.txt ``` -**Caveats**: IAR paths are typically `$PROJ_DIR$\wolfssl\...` — strip the -`$PROJ_DIR$` prefix and replace with the absolute path before passing to -`gen-sbom`. +This produces 65 sources (56 under `wolfcrypt/src/`, 9 under `src/`) for the +standard `wolfSSL-Lib.ewp` project. + +**Caveats**: +- The script skips files that appear in `` blocks (per-configuration + exclusions). If you need sources for a specific configuration only, check + `` matches against your target config name. +- Application-specific `.c` files (test runners, benchmark harness) will also + appear; remove them from `wolfssl-srcs.txt` manually if they are not part + of your shipped wolfssl build. --- From 4104e2712c7252b3dc723c8e2c15ca324715fe91 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 15:59:38 -0700 Subject: [PATCH 10/27] feat(cra-kit): auto-extract wolfssl srcs in embedded mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds _auto_extract_srcs() to generate-wolfssl-sbom.sh. When CRA_SBOM_SRCS_FILE is not set, the embedded path now tries four methods in order: 1. compile_commands.json (CMake/Zephyr/ESP-IDF) via jq — triggered when WOLFSSL_BUILD_DIR is set; detects ESP-IDF managed_components layout automatically. 2. Makefile via CRA_SBOM_MAKEFILE_DIR — tries print-wolfssl-srcs target first, falls back to make -n grep. 3. Keil .uvprojx via CRA_SBOM_KEIL_PROJECT — handles both CMSIS Pack RTE and explicit FilePath layouts. 4. IAR .ewp via CRA_SBOM_IAR_PROJECT — resolves $PROJ_DIR$, skips excluded entries. All methods set CRA_SBOM_SRCS_ONLY_FROM_FILE=true automatically so the demo watermark is suppressed. Temp files are collected into a single global trap. SRCS-FILE-HOWTO.md gains an Automatic extraction subsection for each build system. --- cra-kit/SRCS-FILE-HOWTO.md | 93 +++++++++++ cra-kit/scripts/generate-wolfssl-sbom.sh | 193 ++++++++++++++++++++++- 2 files changed, 284 insertions(+), 2 deletions(-) diff --git a/cra-kit/SRCS-FILE-HOWTO.md b/cra-kit/SRCS-FILE-HOWTO.md index ab5422e13..d8d5d10f0 100644 --- a/cra-kit/SRCS-FILE-HOWTO.md +++ b/cra-kit/SRCS-FILE-HOWTO.md @@ -18,6 +18,11 @@ WOLFSSL_DIR=/path/to/wolfssl \ only the paths in your file. Omit it to merge your list with the kit's built-in 9-file demo list (keeps the `wolfssl:sbom:demo=true` watermark). +Manual extraction is now optional for most build systems. Set the right +environment variable for your build system and the kit script extracts the +source list automatically — no `CRA_SBOM_SRCS_FILE` needed. See the +relevant section below for the variable to set and any tool requirements. + --- ## Custom Makefile @@ -58,6 +63,22 @@ contains `wolfssl` and ends in `.c`. or compiled through recursive `$(MAKE) -C` sub-invocations that do not echo the final compile lines. Use Option A in those cases. +### Automatic extraction + +Set `CRA_SBOM_MAKEFILE_DIR` to the directory containing your project Makefile, then run the +kit script with no `CRA_SBOM_SRCS_FILE`: + +```sh +CRA_SBOM_MODE=embedded \ +CRA_SBOM_MAKEFILE_DIR=/path/to/your/project \ +WOLFSSL_DIR=/path/to/wolfssl \ +./scripts/generate-wolfssl-sbom.sh +``` + +The script tries the `print-wolfssl-srcs` target first; if that target does not exist, it +falls back to `make -n` dry-run. Either way the extracted list is used automatically — +no manual `CRA_SBOM_SRCS_FILE` needed. + --- ## CMake — `compile_commands.json` @@ -87,6 +108,20 @@ files you did not ship. **Requirements**: `jq` (`apt install jq` / `brew install jq`). +### Automatic extraction + +Set `WOLFSSL_BUILD_DIR` to your cmake build directory. The kit script detects +`compile_commands.json` automatically and extracts wolfssl sources without manual steps: + +```sh +CRA_SBOM_MODE=embedded \ +WOLFSSL_BUILD_DIR=/path/to/build \ +WOLFSSL_DIR=/path/to/wolfssl \ +./scripts/generate-wolfssl-sbom.sh +``` + +Requires `jq` on the host. + --- ## Zephyr RTOS @@ -121,6 +156,20 @@ A typical wolfssl Zephyr build produces around 89 library sources **Requirements**: `jq` must be installed on the host running the extraction (not the target board). +### Automatic extraction + +Same as CMake — set `WOLFSSL_BUILD_DIR` to the `west build` output directory: + +```sh +CRA_SBOM_MODE=embedded \ +WOLFSSL_BUILD_DIR=/path/to/wolfssl-app/build \ +WOLFSSL_DIR=/path/to/wolfssl \ +./scripts/generate-wolfssl-sbom.sh +``` + +Requires `jq`. The `compile_commands.json` must have been generated at cmake configure time +(pass `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` to `west build`). + --- ## ESP-IDF @@ -158,6 +207,21 @@ If wolfssl is added as a **local component** (placed manually in `components/wolfssl/` rather than managed), replace `managed_components/wolfssl__wolfssl` with `components/wolfssl` in the filter. +### Automatic extraction + +Set `WOLFSSL_BUILD_DIR` to your project's `build/` directory: + +```sh +CRA_SBOM_MODE=embedded \ +WOLFSSL_BUILD_DIR=/path/to/esp-idf-project/build \ +WOLFSSL_DIR=/path/to/wolfssl \ +./scripts/generate-wolfssl-sbom.sh +``` + +The script detects the ESP-IDF managed-component layout (`managed_components/wolfssl__wolfssl/`) +automatically when `WOLFSSL_DIR` sources are not found under `WOLFSSL_BUILD_DIR` directly. +Requires `jq`. + --- ## Keil MDK / uVision (`.uvprojx`) @@ -258,6 +322,22 @@ and search for ``. If present and `` is inside it, you are using the CMSIS Pack (Option A). If wolfssl `.c` files appear under `` directly, use Option B. +### Automatic extraction + +Set `CRA_SBOM_KEIL_PROJECT` to the path of your `.uvprojx` file: + +```sh +CRA_SBOM_MODE=embedded \ +CRA_SBOM_KEIL_PROJECT=/path/to/MyProject.uvprojx \ +WOLFSSL_DIR=/path/to/wolfssl \ +./scripts/generate-wolfssl-sbom.sh +``` + +The script parses the project file and chooses Option A (CMSIS Pack) or Option B +(explicit FilePath) automatically based on whether a `` is +present in the RTE block. For CMSIS Pack mode the installed `.pdsc` must be present at +`~/.arm/Packs/wolfSSL/wolfSSL//wolfSSL.pdsc`. Requires `python3`. + --- ## IAR Embedded Workbench (`.ewp`) @@ -349,6 +429,19 @@ standard `wolfSSL-Lib.ewp` project. appear; remove them from `wolfssl-srcs.txt` manually if they are not part of your shipped wolfssl build. +### Automatic extraction + +Set `CRA_SBOM_IAR_PROJECT` to the path of your `.ewp` file: + +```sh +CRA_SBOM_MODE=embedded \ +CRA_SBOM_IAR_PROJECT=/path/to/wolfSSL-Lib.ewp \ +WOLFSSL_DIR=/path/to/wolfssl \ +./scripts/generate-wolfssl-sbom.sh +``` + +The script resolves `$PROJ_DIR$` automatically. Requires `python3`. + --- ## Verifying the output diff --git a/cra-kit/scripts/generate-wolfssl-sbom.sh b/cra-kit/scripts/generate-wolfssl-sbom.sh index d31577fc5..d2d270e8e 100755 --- a/cra-kit/scripts/generate-wolfssl-sbom.sh +++ b/cra-kit/scripts/generate-wolfssl-sbom.sh @@ -11,7 +11,9 @@ # WOLFSSL_DIR=path/to/wolfssl (source tree root) # # Mode-specific variables: -# WOLFSSL_BUILD_DIR=path/to/build (cmake mode: path to cmake build directory) +# WOLFSSL_BUILD_DIR=path/to/build (cmake mode: path to cmake build directory; +# embedded: also triggers compile_commands.json +# auto-extraction when present) # CRA_SBOM_SRCS_FILE=path/to/srcs.txt (embedded: file listing .c paths, one per line; # combined with the built-in demo list unless # CRA_SBOM_SRCS_ONLY_FROM_FILE=true) @@ -19,6 +21,9 @@ # use only paths from CRA_SBOM_SRCS_FILE) # CRA_SBOM_NO_HASH=true (embedded: emit SBOM without a real artifact # hash; use when no source list is available) +# CRA_SBOM_MAKEFILE_DIR= (embedded: auto-extract srcs via make -n) +# CRA_SBOM_KEIL_PROJECT= (embedded: auto-extract srcs from .uvprojx) +# CRA_SBOM_IAR_PROJECT= (embedded: auto-extract srcs from .ewp) # # Optional variables: # CRA_PYTHON=python3 (interpreter with pcpp; for embedded path) @@ -29,6 +34,10 @@ # without it.) set -eu +# Accumulator for temp files created by _auto_extract_srcs; cleaned up on exit. +_auto_tempfiles="" +trap 'rm -f ${_auto_tempfiles:-}' EXIT + SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) KIT_DIR=$(dirname "$SCRIPT_DIR") # shellcheck disable=SC2015 # `|| true` is a deliberate set -e guard, not if-then-else @@ -121,6 +130,182 @@ _embedded_srcs() { done } +_auto_extract_srcs() { + # Method 1: compile_commands.json (CMake / Zephyr / ESP-IDF) + if [ -n "${WOLFSSL_BUILD_DIR:-}" ] && [ -f "$WOLFSSL_BUILD_DIR/compile_commands.json" ]; then + _ccdb="$WOLFSSL_BUILD_DIR/compile_commands.json" + if ! command -v jq >/dev/null 2>&1; then + echo "ERROR: jq is required to auto-extract sources from compile_commands.json." >&2 + echo " Install jq, or set CRA_SBOM_SRCS_FILE manually. See SRCS-FILE-HOWTO.md." >&2 + exit 1 + fi + _auto=$(mktemp "${TMPDIR:-/tmp}/wolfssl-auto-srcs.XXXXXX") + _auto_tempfiles="${_auto_tempfiles:-} $_auto" + jq -r '.[].file' "$_ccdb" \ + | grep "^${WOLFSSL_DIR}/" \ + | grep -E '/(wolfcrypt/src|src)/[^/]+\.c$' \ + | sort -u > "$_auto" + if [ ! -s "$_auto" ]; then + _esp_proj=$(dirname "$WOLFSSL_BUILD_DIR") + jq -r '.[].file' "$_ccdb" \ + | grep "^${_esp_proj}/managed_components/wolfssl__wolfssl/" \ + | grep -E '/(wolfcrypt/src|src)/[^/]+\.c$' \ + | sort -u > "$_auto" + fi + if [ -s "$_auto" ]; then + _n=$(wc -l < "$_auto" | tr -d ' ') + echo " Auto-extracted $_n wolfssl sources from compile_commands.json" + CRA_SBOM_SRCS_FILE="$_auto" + CRA_SBOM_SRCS_ONLY_FROM_FILE=true + return 0 + fi + echo " WARNING: compile_commands.json found but yielded no wolfssl sources; trying next method." >&2 + fi + + # Method 2: Makefile + if [ -n "${CRA_SBOM_MAKEFILE_DIR:-}" ]; then + if [ ! -d "$CRA_SBOM_MAKEFILE_DIR" ]; then + echo "ERROR: CRA_SBOM_MAKEFILE_DIR=$CRA_SBOM_MAKEFILE_DIR is not a directory." >&2 + exit 1 + fi + _auto=$(mktemp "${TMPDIR:-/tmp}/wolfssl-auto-srcs.XXXXXX") + _auto_tempfiles="${_auto_tempfiles:-} $_auto" + if make -C "$CRA_SBOM_MAKEFILE_DIR" -n print-wolfssl-srcs >/dev/null 2>&1; then + make -C "$CRA_SBOM_MAKEFILE_DIR" print-wolfssl-srcs 2>/dev/null \ + | sort -u > "$_auto" + fi + if [ ! -s "$_auto" ]; then + make -C "$CRA_SBOM_MAKEFILE_DIR" -n 2>/dev/null \ + | grep -oE '[^ ]+wolfssl[^ ]+\.c' \ + | sort -u > "$_auto" || true + fi + if [ -s "$_auto" ]; then + _n=$(wc -l < "$_auto" | tr -d ' ') + echo " Auto-extracted $_n wolfssl sources via Makefile (CRA_SBOM_MAKEFILE_DIR=$CRA_SBOM_MAKEFILE_DIR)" + CRA_SBOM_SRCS_FILE="$_auto" + CRA_SBOM_SRCS_ONLY_FROM_FILE=true + return 0 + fi + echo "ERROR: CRA_SBOM_MAKEFILE_DIR is set but make yielded no wolfssl sources." >&2 + echo " Add a 'print-wolfssl-srcs' target or ensure 'make -n' references wolfssl .c files." >&2 + exit 1 + fi + + # Method 3: Keil .uvprojx + if [ -n "${CRA_SBOM_KEIL_PROJECT:-}" ]; then + if [ ! -f "$CRA_SBOM_KEIL_PROJECT" ]; then + echo "ERROR: CRA_SBOM_KEIL_PROJECT=$CRA_SBOM_KEIL_PROJECT not found." >&2 + exit 1 + fi + if ! command -v python3 >/dev/null 2>&1; then + echo "ERROR: python3 is required to parse a Keil .uvprojx file." >&2 + exit 1 + fi + _auto=$(mktemp "${TMPDIR:-/tmp}/wolfssl-auto-srcs.XXXXXX") + _auto_tempfiles="${_auto_tempfiles:-} $_auto" + python3 - "$CRA_SBOM_KEIL_PROJECT" > "$_auto" <<'PYEOF' +import sys, os, glob, xml.etree.ElementTree as ET + +proj_file = sys.argv[1] +proj_dir = os.path.dirname(os.path.abspath(proj_file)) +proj = ET.parse(proj_file) +paths = set() + +rte = proj.find('.//RTE') +if rte is not None and rte.find('.//component[@Cvendor="wolfSSL"]') is not None: + pdsc_candidates = sorted(glob.glob( + os.path.expanduser('~/.arm/Packs/wolfSSL/wolfSSL/*/wolfSSL.pdsc') + )) + if os.name == 'nt': + appdata = os.environ.get('LOCALAPPDATA', '') + pdsc_candidates += sorted(glob.glob( + os.path.join(appdata, 'Arm', 'Packs', 'wolfSSL', 'wolfSSL', '*', 'wolfSSL.pdsc') + )) + if pdsc_candidates: + pdsc_file = pdsc_candidates[-1] + pack_dir = os.path.dirname(pdsc_file) + pdsc = ET.parse(pdsc_file) + for f in pdsc.findall('.//file[@category="source"]'): + name = f.get('name', '') + if name.lower().endswith('.c'): + paths.add(os.path.normpath(os.path.join(pack_dir, name.replace('\\', '/')))) +else: + for file_elem in proj.findall('.//File'): + fp = file_elem.find('FilePath') + ft = file_elem.find('FileType') + if fp is None or not fp.text: + continue + ftype = int(ft.text) if ft is not None and ft.text else 0 + if ftype == 1 or fp.text.lower().endswith('.c'): + abs_path = os.path.normpath( + os.path.join(proj_dir, fp.text.replace('\\', '/')) + ) + paths.add(abs_path) + +for p in sorted(paths): + print(p) +PYEOF + if [ -s "$_auto" ]; then + _n=$(wc -l < "$_auto" | tr -d ' ') + echo " Auto-extracted $_n wolfssl sources from Keil project" + CRA_SBOM_SRCS_FILE="$_auto" + CRA_SBOM_SRCS_ONLY_FROM_FILE=true + return 0 + fi + echo "ERROR: CRA_SBOM_KEIL_PROJECT is set but no sources were extracted from $CRA_SBOM_KEIL_PROJECT." >&2 + exit 1 + fi + + # Method 4: IAR .ewp + if [ -n "${CRA_SBOM_IAR_PROJECT:-}" ]; then + if [ ! -f "$CRA_SBOM_IAR_PROJECT" ]; then + echo "ERROR: CRA_SBOM_IAR_PROJECT=$CRA_SBOM_IAR_PROJECT not found." >&2 + exit 1 + fi + if ! command -v python3 >/dev/null 2>&1; then + echo "ERROR: python3 is required to parse an IAR .ewp file." >&2 + exit 1 + fi + _auto=$(mktemp "${TMPDIR:-/tmp}/wolfssl-auto-srcs.XXXXXX") + _auto_tempfiles="${_auto_tempfiles:-} $_auto" + python3 - "$CRA_SBOM_IAR_PROJECT" > "$_auto" <<'PYEOF' +import sys, os, xml.etree.ElementTree as ET + +def is_excluded(file_elem): + return file_elem.find('excluded') is not None + +proj_file = sys.argv[1] +proj_dir = os.path.dirname(os.path.abspath(proj_file)) +proj = ET.parse(proj_file) +paths = set() + +for file_elem in proj.findall('.//file'): + if is_excluded(file_elem): + continue + name_elem = file_elem.find('name') + if name_elem is None or not name_elem.text: + continue + raw = name_elem.text + if not raw.lower().endswith('.c'): + continue + resolved = raw.replace('$PROJ_DIR$', proj_dir) + paths.add(os.path.normpath(resolved.replace('\\', '/'))) + +for p in sorted(paths): + print(p) +PYEOF + if [ -s "$_auto" ]; then + _n=$(wc -l < "$_auto" | tr -d ' ') + echo " Auto-extracted $_n wolfssl sources from IAR project" + CRA_SBOM_SRCS_FILE="$_auto" + CRA_SBOM_SRCS_ONLY_FROM_FILE=true + return 0 + fi + echo "ERROR: CRA_SBOM_IAR_PROJECT is set but no sources were extracted from $CRA_SBOM_IAR_PROJECT." >&2 + exit 1 + fi +} + _run_embedded() { echo "==> Embedded path: gen-sbom with CRA Kit user_settings.h" if [ ! -f "$KIT_DIR/user_settings.h" ]; then @@ -169,6 +354,10 @@ _run_embedded() { return 0 fi + # Auto-extract if caller didn't supply CRA_SBOM_SRCS_FILE + [ -z "${CRA_SBOM_SRCS_FILE:-}" ] && [ "${CRA_SBOM_NO_HASH:-}" != "true" ] && \ + _auto_extract_srcs + # Build the source file list. # # Priority: @@ -255,7 +444,7 @@ EOF # Clean up the temp defines file on every exit path, including a failing # generator run (it previously leaked the file under `set -e` if the # final gen-sbom invocation failed before the manual `rm -f`). - trap 'rm -f "$DEFINES_H"' EXIT + _auto_tempfiles="${_auto_tempfiles:-} $DEFINES_H" CC=${CC:-cc} if ! "$CC" -dM -E \ -I"$WOLFSSL_DIR" \ From c77b4a5ac708862f81f77a37315355e98f38b3b5 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 16:12:03 -0700 Subject: [PATCH 11/27] fix(cra-kit): fix two auto-extract bugs found in testing Makefile: add --no-print-directory to suppress make's Entering/Leaving directory banners, which leaked into the source list when the Makefile directory path contained "wolfssl". Keil CMSIS Pack: when the wolfSSL pack .pdsc is not installed locally (~/.arm/Packs/wolfSSL/), fall back to enumerating wolfcrypt/src/*.c and src/*.c from WOLFSSL_DIR. The full library is what the pack ships. --- cra-kit/scripts/generate-wolfssl-sbom.sh | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/cra-kit/scripts/generate-wolfssl-sbom.sh b/cra-kit/scripts/generate-wolfssl-sbom.sh index d2d270e8e..7d72336ec 100755 --- a/cra-kit/scripts/generate-wolfssl-sbom.sh +++ b/cra-kit/scripts/generate-wolfssl-sbom.sh @@ -170,8 +170,8 @@ _auto_extract_srcs() { fi _auto=$(mktemp "${TMPDIR:-/tmp}/wolfssl-auto-srcs.XXXXXX") _auto_tempfiles="${_auto_tempfiles:-} $_auto" - if make -C "$CRA_SBOM_MAKEFILE_DIR" -n print-wolfssl-srcs >/dev/null 2>&1; then - make -C "$CRA_SBOM_MAKEFILE_DIR" print-wolfssl-srcs 2>/dev/null \ + if make --no-print-directory -C "$CRA_SBOM_MAKEFILE_DIR" -n print-wolfssl-srcs >/dev/null 2>&1; then + make --no-print-directory -C "$CRA_SBOM_MAKEFILE_DIR" print-wolfssl-srcs 2>/dev/null \ | sort -u > "$_auto" fi if [ ! -s "$_auto" ]; then @@ -203,16 +203,18 @@ _auto_extract_srcs() { fi _auto=$(mktemp "${TMPDIR:-/tmp}/wolfssl-auto-srcs.XXXXXX") _auto_tempfiles="${_auto_tempfiles:-} $_auto" - python3 - "$CRA_SBOM_KEIL_PROJECT" > "$_auto" <<'PYEOF' + python3 - "$CRA_SBOM_KEIL_PROJECT" "$WOLFSSL_DIR" > "$_auto" <<'PYEOF' import sys, os, glob, xml.etree.ElementTree as ET proj_file = sys.argv[1] +wolfssl_dir = sys.argv[2] if len(sys.argv) > 2 else '' proj_dir = os.path.dirname(os.path.abspath(proj_file)) proj = ET.parse(proj_file) paths = set() rte = proj.find('.//RTE') if rte is not None and rte.find('.//component[@Cvendor="wolfSSL"]') is not None: + # CMSIS Pack RTE: sources come from the installed pack .pdsc pdsc_candidates = sorted(glob.glob( os.path.expanduser('~/.arm/Packs/wolfSSL/wolfSSL/*/wolfSSL.pdsc') )) @@ -229,6 +231,15 @@ if rte is not None and rte.find('.//component[@Cvendor="wolfSSL"]') is not None: name = f.get('name', '') if name.lower().endswith('.c'): paths.add(os.path.normpath(os.path.join(pack_dir, name.replace('\\', '/')))) + elif wolfssl_dir and os.path.isdir(wolfssl_dir): + # Pack not installed locally; enumerate sources directly from WOLFSSL_DIR. + # The CMSIS Pack contains the full wolfssl library (wolfcrypt/src/ + src/). + for subdir in ('wolfcrypt/src', 'src'): + d = os.path.join(wolfssl_dir, subdir) + if os.path.isdir(d): + for name in os.listdir(d): + if name.endswith('.c'): + paths.add(os.path.join(d, name)) else: for file_elem in proj.findall('.//File'): fp = file_elem.find('FilePath') From 8b558f3d5acb4a07e14a64c65c8d2fb492317abe Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 18:37:53 -0700 Subject: [PATCH 12/27] feat: add CRA Kit scripts for 6 wolfSSL products Generate auditor-ready SBOM packages for wolfSSH, wolfTPM, wolfMQTT, wolfHSM, wolfsentry, and wolfBoot. Each script mirrors the wolfssl SBOM workflow. --- cra-kit/scripts/generate-wolfboot-sbom.sh | 230 +++++++++++++++++ cra-kit/scripts/generate-wolfhsm-sbom.sh | 192 ++++++++++++++ cra-kit/scripts/generate-wolfmqtt-sbom.sh | 119 +++++++++ cra-kit/scripts/generate-wolfsentry-sbom.sh | 151 +++++++++++ cra-kit/scripts/generate-wolfssh-sbom.sh | 164 ++++++++++++ cra-kit/scripts/generate-wolftpm-sbom.sh | 272 ++++++++++++++++++++ 6 files changed, 1128 insertions(+) create mode 100755 cra-kit/scripts/generate-wolfboot-sbom.sh create mode 100755 cra-kit/scripts/generate-wolfhsm-sbom.sh create mode 100755 cra-kit/scripts/generate-wolfmqtt-sbom.sh create mode 100755 cra-kit/scripts/generate-wolfsentry-sbom.sh create mode 100755 cra-kit/scripts/generate-wolfssh-sbom.sh create mode 100755 cra-kit/scripts/generate-wolftpm-sbom.sh diff --git a/cra-kit/scripts/generate-wolfboot-sbom.sh b/cra-kit/scripts/generate-wolfboot-sbom.sh new file mode 100755 index 000000000..2a5f1d3fb --- /dev/null +++ b/cra-kit/scripts/generate-wolfboot-sbom.sh @@ -0,0 +1,230 @@ +#!/bin/sh +# Generate wolfBoot component SBOMs via gen-sbom. +# +# wolfBoot is build-configuration-specific: its compiled source list depends +# on TARGET, SIGN, HASH, and EXT_FLASH. This script runs `make -n` against +# the wolfBoot tree to extract the exact set of .c files for the requested +# configuration, then calls gen-sbom directly. +# +# wolfcrypt sources are compiled directly into the wolfBoot image (there is +# no separate wolfssl shared library). They appear in OBJS alongside core +# wolfBoot sources and are therefore included as wolfBoot's own component +# sources, not as a separate dependency. +# +# Required variables: +# WOLFBOOT_DIR=path/to/wolfBoot (source tree root) +# WOLFBOOT_TARGET= (e.g. stm32h7, x86_64_efi) +# WOLFBOOT_SIGN= (e.g. ECC256, RSA2048, ED25519) +# +# Optional variables: +# WOLFBOOT_HASH= (default: SHA256) +# WOLFBOOT_EXT_FLASH=<0|1> (default: 0) +# CRA_PYTHON=python3 (Python interpreter with gen-sbom deps) +# CRA_LICENSE_OVERRIDE= (e.g. LicenseRef-wolfBoot-Commercial) +# CRA_LICENSE_TEXT= (required when CRA_LICENSE_OVERRIDE is a +# LicenseRef-* id: plain-text license +# embedded in the SBOM) +# CRA_SBOM_OUT_DIR= (override output directory) +set -eu + +SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) +KIT_DIR=$(dirname "$SCRIPT_DIR") + +# Default wolfBoot directory: sibling of the wolfssl-examples checkout. +# shellcheck disable=SC2015 +WOLFBOOT_DIR=${WOLFBOOT_DIR:-$(cd "$KIT_DIR/../../wolfBoot" 2>/dev/null && pwd || true)} + +if [ -z "${WOLFBOOT_DIR:-}" ] || [ ! -d "$WOLFBOOT_DIR" ]; then + echo "ERROR: wolfBoot source not found." >&2 + echo " Set WOLFBOOT_DIR to your wolfBoot checkout." >&2 + exit 1 +fi + +if [ -z "${WOLFBOOT_TARGET:-}" ]; then + echo "ERROR: WOLFBOOT_TARGET is not set." >&2 + echo " Example: WOLFBOOT_TARGET=stm32h7 $0" >&2 + exit 1 +fi + +if [ -z "${WOLFBOOT_SIGN:-}" ]; then + echo "ERROR: WOLFBOOT_SIGN is not set." >&2 + echo " Example: WOLFBOOT_SIGN=ECC256 $0" >&2 + exit 1 +fi + +WOLFBOOT_HASH=${WOLFBOOT_HASH:-SHA256} +WOLFBOOT_EXT_FLASH=${WOLFBOOT_EXT_FLASH:-0} + +OUT_DIR=${CRA_SBOM_OUT_DIR:-"$KIT_DIR/auditor-packet/wolfboot-component"} + +# gen-sbom lives inside the wolfssl submodule under wolfBoot. +GEN_SBOM="$WOLFBOOT_DIR/lib/wolfssl/scripts/gen-sbom" +if [ ! -f "$GEN_SBOM" ]; then + echo "ERROR: gen-sbom not found at $GEN_SBOM" >&2 + echo " Ensure the wolfssl submodule is initialized:" >&2 + echo " git -C \"$WOLFBOOT_DIR\" submodule update --init lib/wolfssl" >&2 + exit 1 +fi + +# Extract version from wolfBoot's version header. +VERSION=$(sed -n \ + 's/.*LIBWOLFBOOT_VERSION_STRING[[:space:]]*"\([^"]*\)".*/\1/p' \ + "$WOLFBOOT_DIR/include/wolfboot/version.h") +if [ -z "$VERSION" ]; then + echo "ERROR: could not detect wolfBoot version from $WOLFBOOT_DIR/include/wolfboot/version.h" >&2 + exit 1 +fi + +mkdir -p "$OUT_DIR" +CDX_OUT="$OUT_DIR/wolfboot-${VERSION}.cdx.json" +SPDX_OUT="$OUT_DIR/wolfboot-${VERSION}.spdx.json" + +echo "wolfBoot tree: $WOLFBOOT_DIR" +echo "Configuration: TARGET=$WOLFBOOT_TARGET SIGN=$WOLFBOOT_SIGN HASH=$WOLFBOOT_HASH EXT_FLASH=$WOLFBOOT_EXT_FLASH" +echo "Version: $VERSION" +echo "Outputs: $CDX_OUT" +echo " $SPDX_OUT" +if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + echo "License override: $CRA_LICENSE_OVERRIDE" +fi + +# A LicenseRef-* override requires the license text to be embedded in the SBOM +# (SPDX 2.3 §10.1). gen-sbom hard-fails without it; catch the omission here. +if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + case "$CRA_LICENSE_OVERRIDE" in + LicenseRef-*) + if [ -z "${CRA_LICENSE_TEXT:-}" ]; then + echo "ERROR: CRA_LICENSE_OVERRIDE=$CRA_LICENSE_OVERRIDE is a LicenseRef-* identifier," >&2 + echo " but CRA_LICENSE_TEXT is not set. SPDX 2.3 requires the license text to be" >&2 + echo " embedded for any LicenseRef-* used in licenseConcluded/licenseDeclared." >&2 + echo " Re-run with CRA_LICENSE_TEXT=/path/to/wolfboot-license.txt" >&2 + exit 1 + fi + if [ ! -f "$CRA_LICENSE_TEXT" ]; then + echo "ERROR: CRA_LICENSE_TEXT=$CRA_LICENSE_TEXT not found." >&2 + exit 1 + fi + ;; + esac +fi + +# Canonicalize CRA_LICENSE_TEXT to an absolute path. +if [ -n "${CRA_LICENSE_TEXT:-}" ] && [ -f "$CRA_LICENSE_TEXT" ]; then + CRA_LICENSE_TEXT=$(CDPATH='' cd -- "$(dirname -- "$CRA_LICENSE_TEXT")" && pwd)/$(basename -- "$CRA_LICENSE_TEXT") +fi + +# Extract the configuration-specific source list via `make -n`. +# +# `make -n TARGET=... SIGN=...` prints every command make would run without +# executing them. The compiler invocations include every .c file on the +# wolfBoot link line, including both core wolfBoot sources and wolfcrypt +# files compiled inline. grep -oE pulls out every .c argument; sort -u +# deduplicates. +echo "Extracting source list via make -n TARGET=$WOLFBOOT_TARGET SIGN=$WOLFBOOT_SIGN ..." + +_srcs_tmp=$(mktemp "${TMPDIR:-/tmp}/wolfboot-sbom-srcs.XXXXXX") +trap 'rm -f "$_srcs_tmp"' EXIT + +make --no-print-directory \ + -C "$WOLFBOOT_DIR" \ + -n \ + TARGET="$WOLFBOOT_TARGET" \ + SIGN="$WOLFBOOT_SIGN" \ + HASH="$WOLFBOOT_HASH" \ + EXT_FLASH="$WOLFBOOT_EXT_FLASH" \ + 2>/dev/null \ + | grep -oE '[^ ]+\.c' \ + | grep -v '\.h' \ + | sort -u > "$_srcs_tmp" || true + +if [ ! -s "$_srcs_tmp" ]; then + echo "ERROR: make -n yielded no .c source files for TARGET=$WOLFBOOT_TARGET SIGN=$WOLFBOOT_SIGN." >&2 + echo " Check that TARGET and SIGN are valid for this wolfBoot tree." >&2 + exit 1 +fi + +_n=$(wc -l < "$_srcs_tmp" | tr -d ' ') +echo " Extracted $_n source files" + +# Resolve paths to absolute: make -n emits relative paths; gen-sbom needs to +# open the files to compute gitoid hashes. +_srcs_abs_tmp=$(mktemp "${TMPDIR:-/tmp}/wolfboot-sbom-srcs-abs.XXXXXX") +trap 'rm -f "$_srcs_tmp" "$_srcs_abs_tmp"' EXIT + +while IFS= read -r _src; do + [ -n "$_src" ] || continue + # Paths from make -n are relative to wolfBoot root. + if [ "${_src#/}" = "$_src" ]; then + _abs="$WOLFBOOT_DIR/$_src" + else + _abs="$_src" + fi + # Skip paths that do not exist on disk (generated files, stubs, etc.). + if [ -f "$_abs" ]; then + echo "$_abs" + fi +done < "$_srcs_tmp" > "$_srcs_abs_tmp" + +if [ ! -s "$_srcs_abs_tmp" ]; then + echo "ERROR: no source files from make -n exist on disk." >&2 + echo " Verify WOLFBOOT_DIR=$WOLFBOOT_DIR and submodules are initialized." >&2 + exit 1 +fi + +_n_abs=$(wc -l < "$_srcs_abs_tmp" | tr -d ' ') +echo " Resolved $_n_abs paths ($(( _n - _n_abs )) non-existent skipped)" + +# Preprocess build settings for gen-sbom --options-h. +# +# wolfBoot has no options.h (it is not an autotools project). We use +# cc -dM -E on the host with wolfBoot's include dirs to produce a flat +# #define file that gen-sbom can parse for algorithm enablement. +_defines_tmp=$(mktemp "${TMPDIR:-/tmp}/wolfboot-sbom-defines.XXXXXX") +trap 'rm -f "$_srcs_tmp" "$_srcs_abs_tmp" "$_defines_tmp"' EXIT + +CC=${CC:-cc} +echo " Preprocessing build settings via $CC -dM -E ..." +if ! "$CC" -dM -E \ + -I"$WOLFBOOT_DIR/include" \ + -I"$WOLFBOOT_DIR/lib/wolfssl" \ + -I"$WOLFBOOT_DIR/lib/wolfssl/wolfcrypt/src" \ + -DWOLFSSL_USER_SETTINGS \ + -x c /dev/null > "$_defines_tmp" 2>/dev/null; then + echo "ERROR: $CC -dM -E failed; install a host C compiler or set CC." >&2 + exit 1 +fi + +# Build gen-sbom argument list. +_PYTHON=${CRA_PYTHON:-python3} +command -v "$_PYTHON" >/dev/null 2>&1 || \ + { echo "ERROR: $_PYTHON not found. Set CRA_PYTHON to your Python interpreter." >&2; exit 1; } + +# Read absolute source paths into positional parameters. +set -- +while IFS= read -r _src; do + [ -n "$_src" ] || continue + set -- "$@" "$_src" +done < "$_srcs_abs_tmp" + +_license_override=${CRA_LICENSE_OVERRIDE:-GPL-3.0-only} +set -- "$@" \ + --cdx-out "$CDX_OUT" \ + --spdx-out "$SPDX_OUT" \ + --license-override "$_license_override" + +if [ -n "${CRA_LICENSE_TEXT:-}" ]; then + set -- "$@" --license-text "$CRA_LICENSE_TEXT" +fi + +echo "==> Running gen-sbom ..." +"$_PYTHON" "$GEN_SBOM" \ + --name wolfboot \ + --version "$VERSION" \ + --supplier "wolfSSL Inc." \ + --license-file "$WOLFBOOT_DIR/LICENSE" \ + --options-h "$_defines_tmp" \ + --srcs "$@" + +echo "SBOM written:" +echo " $CDX_OUT" +echo " $SPDX_OUT" diff --git a/cra-kit/scripts/generate-wolfhsm-sbom.sh b/cra-kit/scripts/generate-wolfhsm-sbom.sh new file mode 100755 index 000000000..0fee38839 --- /dev/null +++ b/cra-kit/scripts/generate-wolfhsm-sbom.sh @@ -0,0 +1,192 @@ +#!/bin/sh +# Generate wolfHSM component SBOM (embedded gen-sbom path). +# +# wolfHSM is a Makefile-only library with no autotools configure step. +# This script always uses the embedded gen-sbom path: it enumerates +# wolfHSM sources directly from the source tree and derives compile-time +# defines via CC -dM -E (or pcpp when available). +# +# Required variables: +# WOLFSSL_DIR=path/to/wolfssl (source tree root; must contain scripts/gen-sbom) +# WOLFHSM_DIR=path/to/wolfHSM (source tree root) +# +# Optional variables: +# CC= (default: cc; set for cross builds) +# CRA_PYTHON=python3 (interpreter with pcpp) +# CRA_LICENSE_OVERRIDE= (e.g. LicenseRef-wolfSSL-Commercial) +# CRA_LICENSE_TEXT= (required when CRA_LICENSE_OVERRIDE is LicenseRef-*) +set -eu + +# Accumulator for temp files; cleaned up on exit. +_auto_tempfiles="" +trap 'rm -f ${_auto_tempfiles:-}' EXIT + +SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) +KIT_DIR=$(dirname "$SCRIPT_DIR") + +# Locate WOLFSSL_DIR (default: sibling of wolfssl-examples). +# shellcheck disable=SC2015 +WOLFSSL_DIR=${WOLFSSL_DIR:-$(cd "$KIT_DIR/../../wolfssl" 2>/dev/null && pwd || true)} +# WOLFHSM_DIR has no sensible default; must be explicit. +WOLFHSM_DIR=${WOLFHSM_DIR:-} + +if [ -z "${WOLFSSL_DIR:-}" ] || [ ! -d "$WOLFSSL_DIR" ]; then + echo "ERROR: wolfSSL source not found." >&2 + echo " Set WOLFSSL_DIR to your wolfssl checkout." >&2 + exit 1 +fi + +if [ -z "${WOLFHSM_DIR:-}" ] || [ ! -d "$WOLFHSM_DIR" ]; then + echo "ERROR: WOLFHSM_DIR is not set or not a directory." >&2 + echo " Set WOLFHSM_DIR to your wolfHSM source tree." >&2 + exit 1 +fi + +GEN="$WOLFSSL_DIR/scripts/gen-sbom" +if [ ! -f "$GEN" ]; then + echo "ERROR: $GEN not found (need wolfSSL with SBOM support)." >&2 + exit 1 +fi + +# Parse version from ChangeLog.md: first line matching "## wolfHSM Release vX.Y.Z" +VERSION=$(sed -n 's/^# wolfHSM Release v\([0-9][0-9.]*\).*/\1/p' \ + "$WOLFHSM_DIR/ChangeLog.md" 2>/dev/null | head -1) +if [ -z "$VERSION" ]; then + echo "ERROR: could not parse version from $WOLFHSM_DIR/ChangeLog.md." >&2 + exit 1 +fi + +OUT_DIR=${CRA_SBOM_OUT_DIR:-"$KIT_DIR/auditor-packet/wolfhsm-component"} +mkdir -p "$OUT_DIR" +CDX_OUT="$OUT_DIR/wolfhsm-${VERSION}.cdx.json" +SPDX_OUT="$OUT_DIR/wolfhsm-${VERSION}.spdx.json" + +echo "wolfHSM tree: $WOLFHSM_DIR" +echo "wolfSSL tree: $WOLFSSL_DIR" +echo "Version: $VERSION" +echo "Outputs: $CDX_OUT" +echo " $SPDX_OUT" +if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + echo "License override: $CRA_LICENSE_OVERRIDE" +fi + +# A LicenseRef-* override requires the licence text to be embedded (SPDX 2.3 §10.1). +if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + case "$CRA_LICENSE_OVERRIDE" in + LicenseRef-*) + if [ -z "${CRA_LICENSE_TEXT:-}" ]; then + echo "ERROR: CRA_LICENSE_OVERRIDE=$CRA_LICENSE_OVERRIDE is a LicenseRef-* identifier," >&2 + echo " but CRA_LICENSE_TEXT is not set. SPDX 2.3 requires the licence text" >&2 + echo " to be embedded. Re-run with CRA_LICENSE_TEXT=/path/to/license.txt." >&2 + exit 1 + fi + if [ ! -f "$CRA_LICENSE_TEXT" ]; then + echo "ERROR: CRA_LICENSE_TEXT=$CRA_LICENSE_TEXT not found." >&2 + exit 1 + fi + ;; + esac +fi + +# Canonicalize CRA_LICENSE_TEXT to absolute path (subshells resolve relative paths +# against their own CWD; gen-sbom may be invoked from a different directory). +if [ -n "${CRA_LICENSE_TEXT:-}" ] && [ -f "$CRA_LICENSE_TEXT" ]; then + CRA_LICENSE_TEXT=$(CDPATH='' cd -- "$(dirname -- "$CRA_LICENSE_TEXT")" && pwd)/$(basename -- "$CRA_LICENSE_TEXT") + echo "License text: $CRA_LICENSE_TEXT" +fi + +# Pick a Python that can `import pcpp`. +_python_with_pcpp() { + for py in ${CRA_PYTHON:-} python3 python; do + [ -n "$py" ] || continue + if command -v "$py" >/dev/null 2>&1 && \ + "$py" -c "import pcpp" 2>/dev/null; then + echo "$py" + return 0 + fi + done + return 1 +} + +# Enumerate wolfHSM sources: all *.c directly under src/ (no recursion). +_wolfhsm_srcs() { + find "$WOLFHSM_DIR/src" -maxdepth 1 -name "*.c" | sort +} + +echo "==> Embedded path: gen-sbom with CC -dM -E (no user_settings.h)" + +# Write collected source paths to a temp file for --srcs-file. +_srcs_file=$(mktemp "${TMPDIR:-/tmp}/wolfhsm-srcs.XXXXXX") +_auto_tempfiles="${_auto_tempfiles:-} $_srcs_file" +_wolfhsm_srcs > "$_srcs_file" +_n=$(wc -l < "$_srcs_file" | tr -d ' ') +echo " Sources: $_n .c files from $WOLFHSM_DIR/src/" +if [ ! -s "$_srcs_file" ]; then + echo "ERROR: no .c files found under $WOLFHSM_DIR/src/." >&2 + exit 1 +fi + +# Build license-override args. +_license_args="" +if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + _license_args="--license-override $CRA_LICENSE_OVERRIDE" + if [ -n "${CRA_LICENSE_TEXT:-}" ]; then + _license_args="$_license_args --license-text $CRA_LICENSE_TEXT" + fi +fi + +if _py=$(_python_with_pcpp); then + echo " Using $_py (pcpp) for --user-settings" + # wolfHSM has no user_settings.h equivalent; pass settings.h from wolfssl + # so gen-sbom has a preprocessable configuration source. The include path + # covers wolfHSM headers and the wolfssl tree. + SETTINGS_H="$WOLFSSL_DIR/wolfssl/wolfcrypt/settings.h" + if [ ! -f "$SETTINGS_H" ]; then + echo "ERROR: $SETTINGS_H not found." >&2 + exit 1 + fi + # shellcheck disable=SC2086 + "$_py" "$GEN" \ + --name wolfhsm \ + --version "$VERSION" \ + --supplier "wolfSSL Inc." \ + --license-file "$WOLFHSM_DIR/LICENSING" \ + --user-settings "$SETTINGS_H" \ + --user-settings-include "$WOLFHSM_DIR" \ + --user-settings-include "$WOLFSSL_DIR" \ + --srcs-file "$_srcs_file" \ + --cdx-out "$CDX_OUT" \ + --spdx-out "$SPDX_OUT" \ + ${_license_args} +else + echo "NOTE: pcpp not found; using CC -dM -E -> --options-h" + echo " Install pcpp: python3 -m pip install pcpp" + echo " For cross builds: set CC=" + + CC=${CC:-cc} + _defines=$(mktemp "${TMPDIR:-/tmp}/wolfhsm-defines.XXXXXX") + _auto_tempfiles="${_auto_tempfiles:-} $_defines" + if ! "$CC" -dM -E \ + -I"$WOLFHSM_DIR" \ + -I"$WOLFSSL_DIR" \ + -x c /dev/null >"$_defines" 2>/dev/null; then + echo "ERROR: $CC -dM -E failed; install pcpp or set CC to your cross-compiler." >&2 + exit 1 + fi + + _python=python3 + command -v python3 >/dev/null 2>&1 || _python=python + # shellcheck disable=SC2086 + "$_python" "$GEN" \ + --name wolfhsm \ + --version "$VERSION" \ + --supplier "wolfSSL Inc." \ + --license-file "$WOLFHSM_DIR/LICENSING" \ + --options-h "$_defines" \ + --srcs-file "$_srcs_file" \ + --cdx-out "$CDX_OUT" \ + --spdx-out "$SPDX_OUT" \ + ${_license_args} +fi + +echo "Done." diff --git a/cra-kit/scripts/generate-wolfmqtt-sbom.sh b/cra-kit/scripts/generate-wolfmqtt-sbom.sh new file mode 100755 index 000000000..5dd4ec3df --- /dev/null +++ b/cra-kit/scripts/generate-wolfmqtt-sbom.sh @@ -0,0 +1,119 @@ +#!/bin/sh +# Generate wolfMQTT component SBOM (autotools make sbom path). +# +# wolfMQTT uses autotools. This script runs `make sbom` inside the wolfMQTT +# source tree and copies the resulting SBOM files to the auditor packet. +# +# Required variables: +# WOLFSSL_DIR=path/to/wolfssl (source tree root; must contain scripts/gen-sbom) +# WOLFMQTT_DIR=path/to/wolfMQTT (source tree root) +# +# Optional variables: +# CRA_LICENSE_OVERRIDE= (e.g. LicenseRef-wolfSSL-Commercial) +# CRA_LICENSE_TEXT= (required when CRA_LICENSE_OVERRIDE is LicenseRef-*) +# CRA_SBOM_OUT_DIR= (default: /auditor-packet/wolfmqtt-component) +set -eu + +SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) +KIT_DIR=$(dirname "$SCRIPT_DIR") + +# shellcheck disable=SC2015 +WOLFSSL_DIR=${WOLFSSL_DIR:-$(cd "$KIT_DIR/../../wolfssl" 2>/dev/null && pwd || true)} +WOLFMQTT_DIR=${WOLFMQTT_DIR:-} + +if [ -z "${WOLFSSL_DIR:-}" ] || [ ! -d "$WOLFSSL_DIR" ]; then + echo "ERROR: wolfSSL source not found." >&2 + echo " Set WOLFSSL_DIR to your wolfssl checkout (contains scripts/gen-sbom)." >&2 + exit 1 +fi + +if [ -z "${WOLFMQTT_DIR:-}" ] || [ ! -d "$WOLFMQTT_DIR" ]; then + echo "ERROR: WOLFMQTT_DIR is not set or not a directory." >&2 + echo " Set WOLFMQTT_DIR to your wolfMQTT source tree." >&2 + exit 1 +fi + +GEN="$WOLFSSL_DIR/scripts/gen-sbom" +if [ ! -f "$GEN" ]; then + echo "ERROR: $GEN not found (need wolfSSL with SBOM support)." >&2 + exit 1 +fi + +# Parse version from wolfmqtt/version.h. +VERSION=$(sed -n \ + 's/.*LIBWOLFMQTT_VERSION_STRING[[:space:]]*"\([^"]*\)".*/\1/p' \ + "$WOLFMQTT_DIR/wolfmqtt/version.h" 2>/dev/null || true) +if [ -z "$VERSION" ]; then + echo "ERROR: could not parse version from $WOLFMQTT_DIR/wolfmqtt/version.h." >&2 + exit 1 +fi + +OUT_DIR=${CRA_SBOM_OUT_DIR:-"$KIT_DIR/auditor-packet/wolfmqtt-component"} +mkdir -p "$OUT_DIR" +CDX_OUT="$OUT_DIR/wolfmqtt-${VERSION}.cdx.json" +SPDX_OUT="$OUT_DIR/wolfmqtt-${VERSION}.spdx.json" + +echo "wolfMQTT tree: $WOLFMQTT_DIR" +echo "wolfSSL tree: $WOLFSSL_DIR" +echo "Version: $VERSION" +echo "Outputs: $CDX_OUT" +echo " $SPDX_OUT" +if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + echo "License override: $CRA_LICENSE_OVERRIDE" +fi + +# A LicenseRef-* override requires the licence text to be embedded (SPDX 2.3 §10.1). +if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + case "$CRA_LICENSE_OVERRIDE" in + LicenseRef-*) + if [ -z "${CRA_LICENSE_TEXT:-}" ]; then + echo "ERROR: CRA_LICENSE_OVERRIDE=$CRA_LICENSE_OVERRIDE is a LicenseRef-* identifier," >&2 + echo " but CRA_LICENSE_TEXT is not set. SPDX 2.3 requires the licence text" >&2 + echo " to be embedded. Re-run with CRA_LICENSE_TEXT=/path/to/license.txt." >&2 + exit 1 + fi + if [ ! -f "$CRA_LICENSE_TEXT" ]; then + echo "ERROR: CRA_LICENSE_TEXT=$CRA_LICENSE_TEXT not found." >&2 + exit 1 + fi + ;; + esac +fi + +# Canonicalize CRA_LICENSE_TEXT to an absolute path: make sbom runs inside a +# subshell `cd "$WOLFMQTT_DIR"`, where a relative path would resolve against +# the wolfMQTT tree rather than the caller's CWD. +if [ -n "${CRA_LICENSE_TEXT:-}" ] && [ -f "$CRA_LICENSE_TEXT" ]; then + CRA_LICENSE_TEXT=$(CDPATH='' cd -- "$(dirname -- "$CRA_LICENSE_TEXT")" && pwd)/$(basename -- "$CRA_LICENSE_TEXT") + echo "License text: $CRA_LICENSE_TEXT" +fi + +echo "==> Autotools path: make sbom" + +# Detect whether the wolfMQTT tree is already configured; run ./configure first +# if no Makefile is present. +(cd "$WOLFMQTT_DIR" && { + if [ ! -f Makefile ]; then + echo " Running ./configure first (WOLFSSL_DIR=$WOLFSSL_DIR)..." + ./configure --with-wolfssl="$WOLFSSL_DIR" + fi + if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + if [ -n "${CRA_LICENSE_TEXT:-}" ]; then + make sbom WOLFSSL_DIR="$WOLFSSL_DIR" \ + SBOM_LICENSE_OVERRIDE="$CRA_LICENSE_OVERRIDE" \ + SBOM_LICENSE_TEXT="$CRA_LICENSE_TEXT" + else + make sbom WOLFSSL_DIR="$WOLFSSL_DIR" \ + SBOM_LICENSE_OVERRIDE="$CRA_LICENSE_OVERRIDE" + fi + else + make sbom WOLFSSL_DIR="$WOLFSSL_DIR" + fi + cp -f "wolfmqtt-${VERSION}.cdx.json" "$CDX_OUT" + cp -f "wolfmqtt-${VERSION}.spdx.json" "$SPDX_OUT" + if [ -f "wolfmqtt-${VERSION}.spdx" ]; then + cp -f "wolfmqtt-${VERSION}.spdx" "$OUT_DIR/" + fi +}) + +echo "Done." diff --git a/cra-kit/scripts/generate-wolfsentry-sbom.sh b/cra-kit/scripts/generate-wolfsentry-sbom.sh new file mode 100755 index 000000000..a2aaf8c58 --- /dev/null +++ b/cra-kit/scripts/generate-wolfsentry-sbom.sh @@ -0,0 +1,151 @@ +#!/bin/sh +# Generate wolfSentry component SBOMs via gen-sbom. +# +# Required variables: +# WOLFSENTRY_DIR=path/to/wolfsentry (source tree root) +# +# gen-sbom location (one required): +# WOLFSSL_DIR=path/to/wolfssl (gen-sbom taken from scripts/gen-sbom) +# CRA_GEN_SBOM=path/to/gen-sbom (direct path; overrides WOLFSSL_DIR) +# +# Optional variables: +# CRA_SBOM_OUT_DIR= (default: $KIT_DIR/auditor-packet/wolfsentry-component) +# CC= (default: cc; for -dM -E options dump) +set -eu + +SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) +KIT_DIR=$(dirname "$SCRIPT_DIR") + +# shellcheck disable=SC2015 +WOLFSENTRY_DIR=${WOLFSENTRY_DIR:-$(cd "$KIT_DIR/../../wolfsentry" 2>/dev/null && pwd || true)} +OUT_DIR=${CRA_SBOM_OUT_DIR:-"$KIT_DIR/auditor-packet/wolfsentry-component"} + +if [ -z "${WOLFSENTRY_DIR:-}" ] || [ ! -d "$WOLFSENTRY_DIR" ]; then + echo "ERROR: wolfSentry source not found." >&2 + echo " Set WOLFSENTRY_DIR to your wolfsentry checkout (sibling of wolfssl-examples)." >&2 + exit 1 +fi + +# Resolve gen-sbom: CRA_GEN_SBOM takes precedence, then WOLFSSL_DIR. +if [ -n "${CRA_GEN_SBOM:-}" ]; then + GEN_SBOM="$CRA_GEN_SBOM" +elif [ -n "${WOLFSSL_DIR:-}" ]; then + GEN_SBOM="$WOLFSSL_DIR/scripts/gen-sbom" +else + echo "ERROR: gen-sbom location not specified." >&2 + echo " Set WOLFSSL_DIR (path to wolfssl repo) or CRA_GEN_SBOM (direct path to gen-sbom)." >&2 + exit 1 +fi + +if [ ! -f "$GEN_SBOM" ]; then + echo "ERROR: gen-sbom not found: $GEN_SBOM" >&2 + exit 1 +fi + +# Extract version from wolfsentry/wolfsentry.h macros. +HEADER="$WOLFSENTRY_DIR/wolfsentry/wolfsentry.h" +if [ ! -f "$HEADER" ]; then + echo "ERROR: version header not found: $HEADER" >&2 + exit 1 +fi +VERSION=$(awk ' + /WOLFSENTRY_VERSION_MAJOR/ { maj=$3 } + /WOLFSENTRY_VERSION_MINOR/ { min=$3 } + /WOLFSENTRY_VERSION_TINY/ { tiny=$3 } + END { print maj"."min"."tiny } +' "$HEADER") +if [ -z "$VERSION" ] || [ "$VERSION" = ".." ]; then + echo "ERROR: could not extract version from $HEADER" >&2 + exit 1 +fi + +mkdir -p "$OUT_DIR" +CDX_OUT="$OUT_DIR/wolfsentry-${VERSION}.cdx.json" +SPDX_OUT="$OUT_DIR/wolfsentry-${VERSION}.spdx.json" + +echo "wolfSentry tree: $WOLFSENTRY_DIR" +echo "Version: $VERSION" +echo "gen-sbom: $GEN_SBOM" +echo "Outputs: $CDX_OUT" +echo " $SPDX_OUT" + +# Enumerate all .c sources from $WOLFSENTRY_DIR/src/ (the directory the Makefile +# compiles from; conditional sources like json/ and lwip/ are subdirs of src/). +SRCS=$(find "$WOLFSENTRY_DIR/src" -name "*.c" | sort) +if [ -z "$SRCS" ]; then + echo "ERROR: no .c files found under $WOLFSENTRY_DIR/src/" >&2 + exit 1 +fi +_n=$(echo "$SRCS" | wc -l | tr -d ' ') +echo "Sources: $_n .c files from $WOLFSENTRY_DIR/src/" + +# Dump compiler defines for --options-h (no user_settings.h; wolfsentry is +# configured via Makefile flags, not a settings header). +CC=${CC:-cc} +_defines_h=$(mktemp "${TMPDIR:-/tmp}/wolfsentry-defines.XXXXXX") +trap 'rm -f "$_defines_h"' EXIT +if ! "$CC" -dM -E -I"$WOLFSENTRY_DIR" -x c /dev/null >"$_defines_h" 2>/dev/null; then + echo "ERROR: $CC -dM -E failed; set CC to an available compiler." >&2 + exit 1 +fi + +if ! command -v python3 >/dev/null 2>&1; then + echo "ERROR: python3 not found in PATH." >&2 + exit 1 +fi + +# Build --srcs argument list from the source enumeration. +# shellcheck disable=SC2086 +set -- $SRCS + +python3 "$GEN_SBOM" \ + --name wolfsentry \ + --version "$VERSION" \ + --supplier "wolfSSL Inc." \ + --license-file "$WOLFSENTRY_DIR/LICENSING" \ + --options-h "$_defines_h" \ + --srcs "$@" \ + --cdx-out "$CDX_OUT" \ + --spdx-out "$SPDX_OUT" + +# Post-process: rewrite pkg:generic/wolfsentry@X -> pkg:github/wolfSSL/wolfsentry@vX +# gen-sbom emits pkg:generic/{name}@{version} for non-wolfssl names; the canonical +# PURL for wolfsentry is the GitHub package form. +if ! CDX_OUT="$CDX_OUT" SPDX_OUT="$SPDX_OUT" VERSION="$VERSION" \ +python3 <<'PY' +import json, os, pathlib + +cdx = pathlib.Path(os.environ["CDX_OUT"]) +spdx = pathlib.Path(os.environ["SPDX_OUT"]) +ver = os.environ["VERSION"] + +GENERIC = "pkg:generic/wolfsentry@" +GITHUB = "pkg:github/wolfSSL/wolfsentry@v" + +def fix(s): + if isinstance(s, str) and s.startswith(GENERIC): + return GITHUB + s[len(GENERIC):] + return s + +if cdx.exists(): + d = json.loads(cdx.read_text()) + comp = d.get("metadata", {}).get("component", {}) + comp["purl"] = fix(comp.get("purl", "")) + cdx.write_text(json.dumps(d, indent=2) + "\n") + print(f"Post-processed {cdx.name}: PURL -> {comp['purl']}") + +if spdx.exists(): + d = json.loads(spdx.read_text()) + for pkg in d.get("packages", []): + for ref in pkg.get("externalRefs", []): + if ref.get("referenceType") == "purl": + ref["referenceLocator"] = fix(ref.get("referenceLocator", "")) + spdx.write_text(json.dumps(d, indent=2) + "\n") + print(f"Post-processed {spdx.name}: PURL canonicalized") +PY +then + echo "ERROR: PURL post-processing failed; SBOM may carry pkg:generic PURLs." >&2 + exit 1 +fi + +echo "Done." diff --git a/cra-kit/scripts/generate-wolfssh-sbom.sh b/cra-kit/scripts/generate-wolfssh-sbom.sh new file mode 100755 index 000000000..4c5730dac --- /dev/null +++ b/cra-kit/scripts/generate-wolfssh-sbom.sh @@ -0,0 +1,164 @@ +#!/bin/sh +# Generate wolfSSH component SBOM (autotools make sbom). +# +# Required variables: +# WOLFSSL_DIR=path/to/wolfssl (source tree root; provides gen-sbom) +# WOLFSSH_DIR=path/to/wolfssh (source tree root) +# +# Optional variables: +# CRA_LICENSE_OVERRIDE= (e.g. LicenseRef-wolfSSH-Commercial) +# CRA_LICENSE_TEXT= (required when CRA_LICENSE_OVERRIDE is a +# LicenseRef-* id: plain-text licence embedded +# in the SBOM; make sbom hard-fails without it.) +set -eu + +SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) +KIT_DIR=$(dirname "$SCRIPT_DIR") +# shellcheck disable=SC2015 # `|| true` is a deliberate set -e guard, not if-then-else +WOLFSSL_DIR=${WOLFSSL_DIR:-$(cd "$KIT_DIR/../../wolfssl" 2>/dev/null && pwd || true)} +WOLFSSH_DIR=${WOLFSSH_DIR:-$(cd "$KIT_DIR/../../wolfSSH" 2>/dev/null && pwd || true)} +OUT_DIR=${CRA_SBOM_OUT_DIR:-"$KIT_DIR/auditor-packet/wolfssh-component"} + +if [ -z "${WOLFSSL_DIR:-}" ] || [ ! -d "$WOLFSSL_DIR" ]; then + echo "ERROR: wolfSSL source not found." >&2 + echo " Set WOLFSSL_DIR to your wolfssl checkout (sibling of wolfssl-examples)." >&2 + exit 1 +fi + +if [ -z "${WOLFSSH_DIR:-}" ] || [ ! -d "$WOLFSSH_DIR" ]; then + echo "ERROR: wolfSSH source not found." >&2 + echo " Set WOLFSSH_DIR to your wolfSSH checkout (sibling of wolfssl-examples)." >&2 + exit 1 +fi + +VERSION=$(sed -n \ + 's/.*LIBWOLFSSH_VERSION_STRING[[:space:]]*"\([^"]*\)".*/\1/p' \ + "$WOLFSSH_DIR/wolfssh/version.h" 2>/dev/null || true) +if [ -z "$VERSION" ]; then + echo "ERROR: could not extract LIBWOLFSSH_VERSION_STRING from $WOLFSSH_DIR/wolfssh/version.h" >&2 + exit 1 +fi + +mkdir -p "$OUT_DIR" +CDX_OUT="$OUT_DIR/wolfssh-${VERSION}.cdx.json" +SPDX_OUT="$OUT_DIR/wolfssh-${VERSION}.spdx.json" + +echo "wolfSSL tree: $WOLFSSL_DIR" +echo "wolfSSH tree: $WOLFSSH_DIR" +echo "Outputs: $CDX_OUT" +echo " $SPDX_OUT" +if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + echo "License override: $CRA_LICENSE_OVERRIDE" +fi + +# A LicenseRef-* override (e.g. the commercial license) requires the actual +# licence text to be embedded in the SBOM (SPDX 2.3 §10.1). Both gen-sbom and +# `make sbom` hard-fail without it, so catch the omission here with an +# actionable message instead of letting the run die deep in the generator. +if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + case "$CRA_LICENSE_OVERRIDE" in + LicenseRef-*) + if [ -z "${CRA_LICENSE_TEXT:-}" ]; then + echo "ERROR: CRA_LICENSE_OVERRIDE=$CRA_LICENSE_OVERRIDE is a LicenseRef-* identifier," >&2 + echo " but CRA_LICENSE_TEXT is not set. SPDX 2.3 requires the licence text to be" >&2 + echo " embedded for any LicenseRef-* used in licenseConcluded/licenseDeclared." >&2 + echo " Re-run with CRA_LICENSE_TEXT=/path/to/wolfssh-commercial-license.txt." >&2 + exit 1 + fi + if [ ! -f "$CRA_LICENSE_TEXT" ]; then + echo "ERROR: CRA_LICENSE_TEXT=$CRA_LICENSE_TEXT not found." >&2 + exit 1 + fi + ;; + esac +fi + +# Canonicalize CRA_LICENSE_TEXT to an absolute path: make sbom runs inside a +# `cd "$WOLFSSH_DIR"` subshell, where a relative path would resolve against the +# wolfSSH tree rather than the caller's CWD. +if [ -n "${CRA_LICENSE_TEXT:-}" ] && [ -f "$CRA_LICENSE_TEXT" ]; then + CRA_LICENSE_TEXT=$(CDPATH='' cd -- "$(dirname -- "$CRA_LICENSE_TEXT")" && pwd)/$(basename -- "$CRA_LICENSE_TEXT") + echo "License text: $CRA_LICENSE_TEXT" +fi + +_run_autotools() { + echo "==> Autotools path: make sbom" + # `make sbom` names its output after the wolfSSH TREE's version + # (PACKAGE_VERSION). Detect mismatches early so the cp below doesn't fail + # with a cryptic "No such file or directory" under `set -eu`. + _tree_ver=$(sed -n \ + 's/.*LIBWOLFSSH_VERSION_STRING[[:space:]]*"\([^"]*\)".*/\1/p' \ + "$WOLFSSH_DIR/wolfssh/version.h" 2>/dev/null || true) + if [ -n "$_tree_ver" ] && [ "$_tree_ver" != "$VERSION" ]; then + echo "ERROR: wolfSSH tree is version $_tree_ver but expected $VERSION." >&2 + exit 1 + fi + (cd "$WOLFSSH_DIR" && { + if [ ! -f Makefile ]; then + echo " Running ./configure first..." + ./configure --with-wolfssl="$WOLFSSL_DIR" + fi + if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + if [ -n "${CRA_LICENSE_TEXT:-}" ]; then + make sbom WOLFSSL_DIR="$WOLFSSL_DIR" \ + SBOM_LICENSE_OVERRIDE="$CRA_LICENSE_OVERRIDE" \ + SBOM_LICENSE_TEXT="$CRA_LICENSE_TEXT" + else + make sbom WOLFSSL_DIR="$WOLFSSL_DIR" \ + SBOM_LICENSE_OVERRIDE="$CRA_LICENSE_OVERRIDE" + fi + else + make sbom WOLFSSL_DIR="$WOLFSSL_DIR" + fi + cp -f "wolfssh-${VERSION}.cdx.json" "$CDX_OUT" + cp -f "wolfssh-${VERSION}.spdx.json" "$SPDX_OUT" + if [ -f "wolfssh-${VERSION}.spdx" ]; then + cp -f "wolfssh-${VERSION}.spdx" "$OUT_DIR/" + fi + }) +} + +_run_autotools + +# ---- Post-process: defensive PURL canonicalization ---- +# Current gen-sbom already emits pkg:github/wolfSSL/wolfSSH@vX natively for +# known component names; the rewrite below is a defensive no-op kept only for +# older generator versions that may emit pkg:generic/wolfssh@X. +if ! CDX_OUT="$CDX_OUT" SPDX_OUT="$SPDX_OUT" \ + python3 <<'PY' +import json, os, pathlib + +cdx = pathlib.Path(os.environ["CDX_OUT"]) +spdx = pathlib.Path(os.environ["SPDX_OUT"]) + +GENERIC = "pkg:generic/wolfssh@" +GITHUB = "pkg:github/wolfSSL/wolfSSH@v" + +def canonicalize_purl(s): + if isinstance(s, str) and s.startswith(GENERIC): + return GITHUB + s[len(GENERIC):] + return s + +if cdx.exists(): + d = json.loads(cdx.read_text()) + comp = d.get("metadata", {}).get("component", {}) + comp["purl"] = canonicalize_purl(comp.get("purl", "")) + cdx.write_text(json.dumps(d, indent=2) + "\n") + print(f"Post-processed {cdx.name}") + +if spdx.exists(): + d = json.loads(spdx.read_text()) + for pkg in d.get("packages", []): + for ref in pkg.get("externalRefs", []): + if ref.get("referenceType") == "purl": + ref["referenceLocator"] = canonicalize_purl(ref.get("referenceLocator", "")) + spdx.write_text(json.dumps(d, indent=2) + "\n") + print(f"Post-processed {spdx.name}") +PY +then + echo "ERROR: post-process failed (PURL canonicalization incomplete)." >&2 + echo " The emitted SBOM may carry pkg:generic PURLs; not trusting it." >&2 + exit 1 +fi + +echo "Done." diff --git a/cra-kit/scripts/generate-wolftpm-sbom.sh b/cra-kit/scripts/generate-wolftpm-sbom.sh new file mode 100755 index 000000000..96af1ceaf --- /dev/null +++ b/cra-kit/scripts/generate-wolftpm-sbom.sh @@ -0,0 +1,272 @@ +#!/bin/sh +# Generate wolfTPM component SBOMs (autotools make sbom, cmake sbom, or direct gen-sbom). +# +# Mode selection: +# CRA_SBOM_MODE=autotools|cmake +# autotools (default when configure+Makefile exist): runs `make sbom` +# cmake: auto-extracts sources from compile_commands.json and runs gen-sbom directly +# +# Required variables: +# WOLFSSL_DIR=path/to/wolfssl (source tree root; provides gen-sbom) +# WOLFTPM_DIR=path/to/wolftpm (source tree root) +# +# Mode-specific variables: +# WOLFTPM_BUILD_DIR=path/to/build (cmake mode: path to cmake build directory; +# triggers compile_commands.json auto-extraction) +# +# Optional variables: +# CRA_LICENSE_OVERRIDE= (e.g. LicenseRef-wolfTPM-Commercial) +# CRA_LICENSE_TEXT= (required when CRA_LICENSE_OVERRIDE is a +# LicenseRef-* id: plain-text licence embedded +# in the SBOM; gen-sbom / make sbom hard-fail +# without it.) +set -eu + +SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) +KIT_DIR=$(dirname "$SCRIPT_DIR") +# shellcheck disable=SC2015 # `|| true` is a deliberate set -e guard, not if-then-else +WOLFTPM_DIR=${WOLFTPM_DIR:-$(cd "$KIT_DIR/../../wolftpm" 2>/dev/null && pwd || true)} +WOLFSSL_DIR=${WOLFSSL_DIR:-$(cd "$KIT_DIR/../../wolfssl" 2>/dev/null && pwd || true)} +OUT_DIR=${CRA_SBOM_OUT_DIR:-"$KIT_DIR/auditor-packet/wolftpm-component"} + +if [ -z "${WOLFTPM_DIR:-}" ] || [ ! -d "$WOLFTPM_DIR" ]; then + echo "ERROR: wolfTPM source not found." >&2 + echo " Set WOLFTPM_DIR to your wolftpm checkout." >&2 + exit 1 +fi + +if [ -z "${WOLFSSL_DIR:-}" ] || [ ! -d "$WOLFSSL_DIR" ]; then + echo "ERROR: wolfSSL source not found (needed for gen-sbom)." >&2 + echo " Set WOLFSSL_DIR to your wolfssl checkout." >&2 + exit 1 +fi + +GEN="$WOLFSSL_DIR/scripts/gen-sbom" +if [ ! -f "$GEN" ]; then + echo "ERROR: $GEN not found (need wolfSSL with SBOM support)." >&2 + exit 1 +fi + +VERSION=$(sed -n \ + 's/.*LIBWOLFTPM_VERSION_STRING[[:space:]]*"\([^"]*\)".*/\1/p' \ + "$WOLFTPM_DIR/wolftpm/version.h" 2>/dev/null || true) +if [ -z "$VERSION" ]; then + echo "ERROR: could not extract version from $WOLFTPM_DIR/wolftpm/version.h" >&2 + exit 1 +fi + +mkdir -p "$OUT_DIR" +CDX_OUT="$OUT_DIR/wolftpm-${VERSION}.cdx.json" +SPDX_OUT="$OUT_DIR/wolftpm-${VERSION}.spdx.json" + +echo "wolfTPM tree: $WOLFTPM_DIR" +echo "wolfSSL tree: $WOLFSSL_DIR" +echo "Outputs: $CDX_OUT" +echo " $SPDX_OUT" +if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + echo "License override: $CRA_LICENSE_OVERRIDE" +fi + +# A LicenseRef-* override requires the actual licence text to be embedded +# in the SBOM (SPDX 2.3 §10.1). Catch the omission early. +if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + case "$CRA_LICENSE_OVERRIDE" in + LicenseRef-*) + if [ -z "${CRA_LICENSE_TEXT:-}" ]; then + echo "ERROR: CRA_LICENSE_OVERRIDE=$CRA_LICENSE_OVERRIDE is a LicenseRef-* identifier," >&2 + echo " but CRA_LICENSE_TEXT is not set. SPDX 2.3 requires the licence text to be" >&2 + echo " embedded for any LicenseRef-* used in licenseConcluded/licenseDeclared." >&2 + echo " Re-run with CRA_LICENSE_TEXT=/path/to/wolftpm-commercial-license.txt" >&2 + exit 1 + fi + if [ ! -f "$CRA_LICENSE_TEXT" ]; then + echo "ERROR: CRA_LICENSE_TEXT=$CRA_LICENSE_TEXT not found." >&2 + exit 1 + fi + ;; + esac +fi + +# Canonicalize CRA_LICENSE_TEXT to an absolute path: the autotools path runs +# `make sbom` inside a `cd "$WOLFTPM_DIR"` subshell, where a relative path would +# otherwise resolve against the wolfTPM tree rather than the caller's CWD. +if [ -n "${CRA_LICENSE_TEXT:-}" ] && [ -f "$CRA_LICENSE_TEXT" ]; then + CRA_LICENSE_TEXT=$(CDPATH='' cd -- "$(dirname -- "$CRA_LICENSE_TEXT")" && pwd)/$(basename -- "$CRA_LICENSE_TEXT") + echo "License text: $CRA_LICENSE_TEXT" +fi + +# Accumulator for temp files; cleaned up on exit. +_auto_tempfiles="" +trap 'rm -f ${_auto_tempfiles:-}' EXIT + +_auto_extract_srcs() { + # Extract wolftpm sources from compile_commands.json (CMake build). + if [ -n "${WOLFTPM_BUILD_DIR:-}" ] && [ -f "$WOLFTPM_BUILD_DIR/compile_commands.json" ]; then + _ccdb="$WOLFTPM_BUILD_DIR/compile_commands.json" + if ! command -v jq >/dev/null 2>&1; then + echo "ERROR: jq is required to auto-extract sources from compile_commands.json." >&2 + echo " Install jq, or set CRA_SBOM_SRCS_FILE manually." >&2 + exit 1 + fi + _auto=$(mktemp "${TMPDIR:-/tmp}/wolftpm-auto-srcs.XXXXXX") + _auto_tempfiles="${_auto_tempfiles:-} $_auto" + jq -r '.[].file' "$_ccdb" \ + | grep "^${WOLFTPM_DIR}/" \ + | grep -E '/src/[^/]+\.c$' \ + | sort -u > "$_auto" + if [ -s "$_auto" ]; then + _n=$(wc -l < "$_auto" | tr -d ' ') + echo " Auto-extracted $_n wolftpm sources from compile_commands.json" + CRA_SBOM_SRCS_FILE="$_auto" + return 0 + fi + echo " WARNING: compile_commands.json found but yielded no wolftpm sources." >&2 + fi +} + +_run_autotools() { + echo "==> Autotools path: make sbom" + _tree_ver=$(sed -n \ + 's/.*LIBWOLFTPM_VERSION_STRING[[:space:]]*"\([^"]*\)".*/\1/p' \ + "$WOLFTPM_DIR/wolftpm/version.h" 2>/dev/null || true) + if [ -n "$_tree_ver" ] && [ "$_tree_ver" != "$VERSION" ]; then + echo "ERROR: wolfTPM tree is version $_tree_ver but detected version is $VERSION." >&2 + exit 1 + fi + (cd "$WOLFTPM_DIR" && { + if [ ! -f Makefile ]; then + echo " Running ./configure first..." + ./configure + fi + if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + if [ -n "${CRA_LICENSE_TEXT:-}" ]; then + make sbom WOLFSSL_DIR="$WOLFSSL_DIR" \ + SBOM_LICENSE_OVERRIDE="$CRA_LICENSE_OVERRIDE" \ + SBOM_LICENSE_TEXT="$CRA_LICENSE_TEXT" + else + make sbom WOLFSSL_DIR="$WOLFSSL_DIR" \ + SBOM_LICENSE_OVERRIDE="$CRA_LICENSE_OVERRIDE" + fi + else + make sbom WOLFSSL_DIR="$WOLFSSL_DIR" + fi + cp -f "wolftpm-${VERSION}.cdx.json" "$CDX_OUT" + cp -f "wolftpm-${VERSION}.spdx.json" "$SPDX_OUT" + if [ -f "wolftpm-${VERSION}.spdx" ]; then + cp -f "wolftpm-${VERSION}.spdx" "$OUT_DIR/" + fi + }) +} + +_run_cmake() { + echo "==> cmake path: gen-sbom with compile_commands.json source extraction" + if [ -z "${WOLFTPM_BUILD_DIR:-}" ]; then + echo "ERROR: WOLFTPM_BUILD_DIR is not set." >&2 + echo " Set it to your cmake out-of-source build directory." >&2 + echo " Example: cmake -B build && WOLFTPM_BUILD_DIR=\$PWD/build $0" >&2 + exit 1 + fi + if [ ! -d "$WOLFTPM_BUILD_DIR" ]; then + echo "ERROR: WOLFTPM_BUILD_DIR=$WOLFTPM_BUILD_DIR is not a directory." >&2 + exit 1 + fi + + CRA_SBOM_SRCS_FILE="" + _auto_extract_srcs + + if [ -z "${CRA_SBOM_SRCS_FILE:-}" ]; then + echo "ERROR: could not extract wolftpm sources from compile_commands.json." >&2 + echo " Reconfigure cmake with -DCMAKE_EXPORT_COMPILE_COMMANDS=ON, or" >&2 + echo " switch to autotools mode (CRA_SBOM_MODE=autotools)." >&2 + exit 1 + fi + + PYTHON3=$(command -v python3 2>/dev/null || command -v python 2>/dev/null || true) + if [ -z "$PYTHON3" ]; then + echo "ERROR: python3 not found in PATH." >&2 + exit 1 + fi + + set -- \ + --name wolftpm \ + --version "$VERSION" \ + --supplier "wolfSSL Inc." \ + --license-file "$WOLFTPM_DIR/LICENSE" \ + --srcs-file "$CRA_SBOM_SRCS_FILE" \ + --cdx-out "$CDX_OUT" \ + --spdx-out "$SPDX_OUT" + if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + set -- "$@" --license-override "$CRA_LICENSE_OVERRIDE" + if [ -n "${CRA_LICENSE_TEXT:-}" ]; then + set -- "$@" --license-text "$CRA_LICENSE_TEXT" + fi + fi + "$PYTHON3" "$GEN" "$@" +} + +MODE=${CRA_SBOM_MODE:-} +case "$MODE" in + autotools) _run_autotools ;; + cmake) _run_cmake ;; + "") + if [ -n "${WOLFTPM_BUILD_DIR:-}" ] && [ -d "${WOLFTPM_BUILD_DIR}" ]; then + MODE=cmake + _run_cmake + elif [ -f "$WOLFTPM_DIR/Makefile" ] && [ -f "$WOLFTPM_DIR/configure" ]; then + MODE=autotools + _run_autotools + else + echo "ERROR: could not detect build mode." >&2 + echo " Set CRA_SBOM_MODE=autotools or cmake, and ensure the build" >&2 + echo " directory exists (WOLFTPM_BUILD_DIR) or configure has been run" >&2 + echo " in WOLFTPM_DIR." >&2 + exit 1 + fi + ;; + *) + echo "ERROR: CRA_SBOM_MODE must be 'autotools' or 'cmake', not '$MODE'" >&2 + exit 1 + ;; +esac + +# ---- Post-process: PURL canonicalization ---- +# gen-sbom emits pkg:generic/wolftpm@X by default; rewrite to the canonical +# pkg:github/wolfSSL/wolfTPM@vX form expected in the auditor packet. +if ! CDX_OUT="$CDX_OUT" SPDX_OUT="$SPDX_OUT" \ + python3 <<'PY' +import json, os, pathlib + +cdx = pathlib.Path(os.environ["CDX_OUT"]) +spdx = pathlib.Path(os.environ["SPDX_OUT"]) + +GENERIC = "pkg:generic/wolftpm@" +GITHUB = "pkg:github/wolfSSL/wolfTPM@v" + +def canonicalize_purl(s): + if isinstance(s, str) and s.startswith(GENERIC): + return GITHUB + s[len(GENERIC):] + return s + +if cdx.exists(): + d = json.loads(cdx.read_text()) + comp = d.get("metadata", {}).get("component", {}) + comp["purl"] = canonicalize_purl(comp.get("purl", "")) + cdx.write_text(json.dumps(d, indent=2) + "\n") + print(f"Post-processed {cdx.name}") + +if spdx.exists(): + d = json.loads(spdx.read_text()) + for pkg in d.get("packages", []): + for ref in pkg.get("externalRefs", []): + if ref.get("referenceType") == "purl": + ref["referenceLocator"] = canonicalize_purl(ref.get("referenceLocator", "")) + spdx.write_text(json.dumps(d, indent=2) + "\n") + print(f"Post-processed {spdx.name}") +PY +then + echo "ERROR: post-process failed (PURL canonicalization incomplete)." >&2 + echo " The emitted SBOM may carry pkg:generic PURLs; not trusting it." >&2 + exit 1 +fi + +echo "Done." From c3ed5fad30df33baa4865f04c1e4ac1bea8063a8 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 19:00:41 -0700 Subject: [PATCH 13/27] feat: add embedded mode to generate-wolfssh-sbom.sh --- cra-kit/scripts/generate-wolfssh-sbom.sh | 228 ++++++++++++++++++++++- 1 file changed, 224 insertions(+), 4 deletions(-) diff --git a/cra-kit/scripts/generate-wolfssh-sbom.sh b/cra-kit/scripts/generate-wolfssh-sbom.sh index 4c5730dac..b094750c9 100755 --- a/cra-kit/scripts/generate-wolfssh-sbom.sh +++ b/cra-kit/scripts/generate-wolfssh-sbom.sh @@ -1,17 +1,43 @@ #!/bin/sh -# Generate wolfSSH component SBOM (autotools make sbom). +# Generate wolfSSH component SBOM (autotools make sbom, or embedded gen-sbom). +# +# Mode selection: +# CRA_SBOM_MODE=autotools|embedded (default: autotools) +# autotools: builds libwolfssh.so and runs `make sbom` +# embedded: hashes wolfSSH source files directly (no .so is produced when +# wolfSSH is compiled into firmware), via wolfSSL's gen-sbom. # # Required variables: # WOLFSSL_DIR=path/to/wolfssl (source tree root; provides gen-sbom) # WOLFSSH_DIR=path/to/wolfssh (source tree root) # +# Embedded-mode variables: +# CRA_SBOM_BUILD_DIR=path/to/build (embedded: dir containing +# compile_commands.json for CMake / ESP-IDF +# / Zephyr builds, used to extract the exact +# wolfSSH .c files on the link line) +# CRA_SBOM_SRCS_FILE=path/to/srcs.txt (embedded: explicit list of wolfSSH .c +# paths, one per line; takes priority over +# every other source-resolution method) +# # Optional variables: +# CRA_SBOM_OUT_DIR= (output directory; default auditor-packet) # CRA_LICENSE_OVERRIDE= (e.g. LicenseRef-wolfSSH-Commercial) # CRA_LICENSE_TEXT= (required when CRA_LICENSE_OVERRIDE is a # LicenseRef-* id: plain-text licence embedded -# in the SBOM; make sbom hard-fails without it.) +# in the SBOM; make sbom / gen-sbom hard-fail +# without it.) +# POSIX sh (script is #!/bin/sh and run via `sh`); dash has no `set -o pipefail`, +# so we use `set -eu` like the rest of the kit. Pipelines that must not mask a +# failed first stage are checked explicitly instead. set -eu +# Accumulator for temp files (embedded source extraction); cleaned up on exit. +# Why: mktemp temp files must not leak if any later command fails under set -e; +# a single EXIT trap removes them on every exit path including errors. +_auto_tempfiles="" +trap 'rm -f ${_auto_tempfiles:-}' EXIT + SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) KIT_DIR=$(dirname "$SCRIPT_DIR") # shellcheck disable=SC2015 # `|| true` is a deliberate set -e guard, not if-then-else @@ -81,6 +107,190 @@ if [ -n "${CRA_LICENSE_TEXT:-}" ] && [ -f "$CRA_LICENSE_TEXT" ]; then echo "License text: $CRA_LICENSE_TEXT" fi +# Pick a Python that can `import pcpp` (pip may target a different python3 than +# the one first on PATH). pcpp lets gen-sbom walk settings.h without a compiler. +_python_with_pcpp() { + for py in ${CRA_PYTHON:-} python3 python; do + [ -n "$py" ] || continue + if command -v "$py" >/dev/null 2>&1 && \ + "$py" -c "import pcpp" 2>/dev/null; then + echo "$py" + return 0 + fi + done + return 1 +} + +# Resolve the wolfSSH source list for embedded mode into the file named by $1. +# +# Source set rule: ONLY wolfSSH protocol sources (${WOLFSSH_DIR}/src/*.c) are +# product-owned and belong in this component. The wolfcrypt / wolfSSL sources +# that wolfSSH links against are NOT included here — they are a separate +# component covered by generate-wolfssl-sbom.sh and referenced as a dependency. +# Mixing them in would double-count the crypto library across two SBOMs. +# +# Priority order (first match wins): +# 1. CRA_SBOM_SRCS_FILE — explicit user list beats anything inferred, because +# the user knows their exact link line; we must not +# second-guess it. +# 2. compile_commands.json at CRA_SBOM_BUILD_DIR — accurate per-build extraction +# for CMake / ESP-IDF / Zephyr. +# 3. Default: all ${WOLFSSH_DIR}/src/*.c sorted — the "all sources" fallback for +# toolchains (IAR, Keil, MPLAB) that produce no +# compile_commands.json and where we cannot infer the +# exact subset compiled. Listing all sources is the +# safe over-approximation: it never omits a file that +# shipped. +_resolve_wolfssh_srcs() { + _out="$1" + + # 1. Explicit user-provided list (verbatim). + if [ -n "${CRA_SBOM_SRCS_FILE:-}" ]; then + if [ ! -f "$CRA_SBOM_SRCS_FILE" ]; then + echo "ERROR: CRA_SBOM_SRCS_FILE=$CRA_SBOM_SRCS_FILE not found." >&2 + exit 1 + fi + if [ ! -s "$CRA_SBOM_SRCS_FILE" ]; then + echo "ERROR: CRA_SBOM_SRCS_FILE=$CRA_SBOM_SRCS_FILE is empty." >&2 + exit 1 + fi + grep -v '^[[:space:]]*$' "$CRA_SBOM_SRCS_FILE" > "$_out" || { + echo "ERROR: failed to read CRA_SBOM_SRCS_FILE=$CRA_SBOM_SRCS_FILE." >&2 + exit 1 + } + echo " Using explicit source list from $CRA_SBOM_SRCS_FILE" + return 0 + fi + + # 2. compile_commands.json (CMake / ESP-IDF / Zephyr). + if [ -n "${CRA_SBOM_BUILD_DIR:-}" ] && \ + [ -f "$CRA_SBOM_BUILD_DIR/compile_commands.json" ]; then + _ccdb="$CRA_SBOM_BUILD_DIR/compile_commands.json" + if ! command -v jq >/dev/null 2>&1; then + echo "ERROR: jq is required to extract sources from compile_commands.json." >&2 + echo " Install jq, or set CRA_SBOM_SRCS_FILE manually." >&2 + exit 1 + fi + # Restrict to wolfSSH's own src/*.c; exclude any wolfcrypt/wolfssl entries + # that share the build (they belong to the wolfssl component). + jq -r '.[].file' "$_ccdb" \ + | grep "^${WOLFSSH_DIR}/src/" \ + | grep -E '/src/[^/]+\.c$' \ + | sort -u > "$_out" || { + echo "ERROR: failed to parse $_ccdb with jq." >&2 + exit 1 + } + if [ -s "$_out" ]; then + _n=$(wc -l < "$_out" | tr -d ' ') + echo " Extracted $_n wolfSSH sources from compile_commands.json" + return 0 + fi + echo " WARNING: compile_commands.json found but yielded no wolfSSH" >&2 + echo " src/ sources; falling back to all src/*.c." >&2 + fi + + # 3. Default fallback: every wolfSSH src/*.c. A POSIX glob expands in sorted + # order; guard the no-match case where the pattern stays literal. + : > "$_out" + for _c in "$WOLFSSH_DIR"/src/*.c; do + [ -f "$_c" ] || continue + printf '%s\n' "$_c" >> "$_out" + done + if [ ! -s "$_out" ]; then + echo "ERROR: no wolfSSH sources found in $WOLFSSH_DIR/src/*.c." >&2 + exit 1 + fi + _n=$(wc -l < "$_out" | tr -d ' ') + echo " Using all $_n wolfSSH sources from $WOLFSSH_DIR/src/*.c (default)" + return 0 +} + +_run_embedded() { + echo "==> Embedded path: gen-sbom hashing wolfSSH source files" + + GEN="$WOLFSSL_DIR/scripts/gen-sbom" + if [ ! -f "$GEN" ]; then + echo "ERROR: $GEN not found (need a wolfSSL tree with SBOM support)." >&2 + exit 1 + fi + + # gen-sbom's embedded entry point walks wolfSSL's settings.h to resolve the + # build config. wolfSSH headers pull in wolfSSL headers, so we reuse the same + # settings.h + kit user_settings.h the wolfssl embedded path uses. + SETTINGS_H="$WOLFSSL_DIR/wolfssl/wolfcrypt/settings.h" + if [ ! -f "$SETTINGS_H" ]; then + echo "ERROR: $SETTINGS_H not found." >&2 + exit 1 + fi + if [ ! -f "$KIT_DIR/user_settings.h" ]; then + echo "ERROR: $KIT_DIR/user_settings.h missing (demo WOLFSSL_USER_SETTINGS)." >&2 + exit 1 + fi + + if ! command -v python3 >/dev/null 2>&1; then + echo "ERROR: python3 not found in PATH (required by gen-sbom)." >&2 + exit 1 + fi + + # Resolve the source list into a temp file (cleaned up by the EXIT trap). + _srcs=$(mktemp "${TMPDIR:-/tmp}/wolfssh-srcs.XXXXXX") || { + echo "ERROR: mktemp failed for the source-list temp file." >&2 + exit 1 + } + _auto_tempfiles="${_auto_tempfiles:-} $_srcs" + _resolve_wolfssh_srcs "$_srcs" + + # Build the positional --srcs argument list from the resolved file. + set -- + while IFS= read -r _src; do + [ -n "$_src" ] || continue + if [ ! -f "$_src" ]; then + echo "ERROR: source file does not exist: $_src" >&2 + exit 1 + fi + set -- "$@" "$_src" + done < "$_srcs" + if [ $# -eq 0 ]; then + echo "ERROR: resolved source list is empty." >&2 + exit 1 + fi + + # Prepend --srcs, then append the trailing options. argparse stops --srcs + # consumption at the next -- option, so --cdx-out / --spdx-out end it cleanly. + set -- --srcs "$@" --cdx-out "$CDX_OUT" --spdx-out "$SPDX_OUT" + if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + set -- "$@" --license-override "$CRA_LICENSE_OVERRIDE" + if [ -n "${CRA_LICENSE_TEXT:-}" ]; then + set -- "$@" --license-text "$CRA_LICENSE_TEXT" + fi + fi + + if _py=$(_python_with_pcpp); then + echo " Using $_py (pcpp) for --user-settings" + else + echo "ERROR: no python3/python with pcpp installed (required for embedded mode)." >&2 + echo " Install it on the same interpreter: python3 -m pip install pcpp" >&2 + exit 1 + fi + + "$_py" "$GEN" \ + --name wolfssh --version "$VERSION" \ + --license-file "$WOLFSSH_DIR/LICENSING" \ + --user-settings "$SETTINGS_H" \ + --user-settings-include "$WOLFSSL_DIR" \ + --user-settings-include "$KIT_DIR" \ + --user-settings-define WOLFSSL_USER_SETTINGS \ + "$@" + + # Verify gen-sbom actually produced both outputs and they are non-empty. + for _f in "$CDX_OUT" "$SPDX_OUT"; do + if [ ! -s "$_f" ]; then + echo "ERROR: expected output missing or empty: $_f" >&2 + exit 1 + fi + done +} + _run_autotools() { echo "==> Autotools path: make sbom" # `make sbom` names its output after the wolfSSH TREE's version @@ -118,7 +328,15 @@ _run_autotools() { }) } -_run_autotools +MODE=${CRA_SBOM_MODE:-autotools} +case "$MODE" in + embedded) _run_embedded ;; + autotools) _run_autotools ;; + *) + echo "ERROR: CRA_SBOM_MODE must be 'autotools' or 'embedded', not '$MODE'" >&2 + exit 1 + ;; +esac # ---- Post-process: defensive PURL canonicalization ---- # Current gen-sbom already emits pkg:github/wolfSSL/wolfSSH@vX natively for @@ -161,4 +379,6 @@ then exit 1 fi -echo "Done." +echo "Done. SBOM outputs:" +echo " $CDX_OUT" +echo " $SPDX_OUT" From 950398f3cf2def0e60a7e26dcecab91c7d3b2d7d Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 19:01:03 -0700 Subject: [PATCH 14/27] feat: add embedded mode to generate-wolfmqtt-sbom.sh --- cra-kit/scripts/generate-wolfmqtt-sbom.sh | 224 +++++++++++++++++++--- 1 file changed, 201 insertions(+), 23 deletions(-) diff --git a/cra-kit/scripts/generate-wolfmqtt-sbom.sh b/cra-kit/scripts/generate-wolfmqtt-sbom.sh index 5dd4ec3df..08866dc1b 100755 --- a/cra-kit/scripts/generate-wolfmqtt-sbom.sh +++ b/cra-kit/scripts/generate-wolfmqtt-sbom.sh @@ -1,18 +1,33 @@ #!/bin/sh -# Generate wolfMQTT component SBOM (autotools make sbom path). +# Generate wolfMQTT component SBOM (autotools make sbom, or embedded gen-sbom). # -# wolfMQTT uses autotools. This script runs `make sbom` inside the wolfMQTT -# source tree and copies the resulting SBOM files to the auditor packet. +# Mode selection: +# CRA_SBOM_MODE=autotools|embedded (default: autotools) +# autotools: runs `make sbom` inside the wolfMQTT tree +# embedded: runs gen-sbom directly over the MQTT protocol sources, hashing +# them with an OmniBOR gitoid Merkle hash. Use this when wolfMQTT +# is compiled into firmware (ESP-IDF, Arduino, STM32, bare-metal) +# and there is no .so/.a to hash. # # Required variables: # WOLFSSL_DIR=path/to/wolfssl (source tree root; must contain scripts/gen-sbom) # WOLFMQTT_DIR=path/to/wolfMQTT (source tree root) # +# Embedded-mode variables (CRA_SBOM_MODE=embedded): +# CRA_SBOM_SRCS_FILE=path/to/srcs.txt (explicit .c list, one path per line; +# used verbatim — highest priority) +# CRA_SBOM_BUILD_DIR=path/to/build (CMake/ESP-IDF build dir; sources are +# read from its compile_commands.json) +# # Optional variables: # CRA_LICENSE_OVERRIDE= (e.g. LicenseRef-wolfSSL-Commercial) # CRA_LICENSE_TEXT= (required when CRA_LICENSE_OVERRIDE is LicenseRef-*) # CRA_SBOM_OUT_DIR= (default: /auditor-packet/wolfmqtt-component) set -eu +# Enable pipefail when the shell supports it (bash/ksh/some dash builds). +# Plain POSIX sh may not; tolerate its absence so the script still runs. +# shellcheck disable=SC3040 +(set -o pipefail) 2>/dev/null && set -o pipefail || true SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) KIT_DIR=$(dirname "$SCRIPT_DIR") @@ -88,32 +103,195 @@ if [ -n "${CRA_LICENSE_TEXT:-}" ] && [ -f "$CRA_LICENSE_TEXT" ]; then echo "License text: $CRA_LICENSE_TEXT" fi -echo "==> Autotools path: make sbom" +_run_autotools() { + echo "==> Autotools path: make sbom" + + # Detect whether the wolfMQTT tree is already configured; run ./configure first + # if no Makefile is present. + (cd "$WOLFMQTT_DIR" && { + if [ ! -f Makefile ]; then + echo " Running ./configure first (WOLFSSL_DIR=$WOLFSSL_DIR)..." + ./configure --with-wolfssl="$WOLFSSL_DIR" + fi + if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + if [ -n "${CRA_LICENSE_TEXT:-}" ]; then + make sbom WOLFSSL_DIR="$WOLFSSL_DIR" \ + SBOM_LICENSE_OVERRIDE="$CRA_LICENSE_OVERRIDE" \ + SBOM_LICENSE_TEXT="$CRA_LICENSE_TEXT" + else + make sbom WOLFSSL_DIR="$WOLFSSL_DIR" \ + SBOM_LICENSE_OVERRIDE="$CRA_LICENSE_OVERRIDE" + fi + else + make sbom WOLFSSL_DIR="$WOLFSSL_DIR" + fi + cp -f "wolfmqtt-${VERSION}.cdx.json" "$CDX_OUT" + cp -f "wolfmqtt-${VERSION}.spdx.json" "$SPDX_OUT" + if [ -f "wolfmqtt-${VERSION}.spdx" ]; then + cp -f "wolfmqtt-${VERSION}.spdx" "$OUT_DIR/" + fi + }) +} + +_run_embedded() { + echo "==> Embedded path: gen-sbom over MQTT protocol sources" + + # wolfMQTT's MQTT protocol implementation lives entirely in src/mqtt_*.c. + # Unlike wolfTPM there are no platform-specific HAL files to exclude and no + # host-only sources, so the mqtt_*.c glob is the correct default source set. + # + # wolfcrypt/wolfSSL crypto sources and wolfSSL TLS sources are deliberately + # NOT hashed here: they are a separate component with their own SBOM, + # produced by generate-wolfssl-sbom.sh. Mixing them in would double-count + # the crypto component and misattribute its provenance to wolfMQTT. + if [ ! -d "$WOLFMQTT_DIR/src" ]; then + echo "ERROR: $WOLFMQTT_DIR/src not found; cannot locate MQTT sources." >&2 + exit 1 + fi + + GEN="$WOLFSSL_DIR/scripts/gen-sbom" + if [ ! -f "$GEN" ]; then + echo "ERROR: $GEN not found (need wolfSSL with SBOM support)." >&2 + exit 1 + fi + + PY=$(command -v python3 2>/dev/null || command -v python 2>/dev/null || true) + if [ -z "$PY" ]; then + echo "ERROR: python3 (or python) not found; required to run gen-sbom." >&2 + exit 1 + fi + + # wolfMQTT ships its own licence file; detect the SPDX id from it (not from + # the wolfSSL tree, which carries a different LICENSING file). + LICENSE_FILE="" + for _lf in "$WOLFMQTT_DIR/LICENSE" "$WOLFMQTT_DIR/COPYING" "$WOLFMQTT_DIR/LICENSING"; do + if [ -f "$_lf" ]; then + LICENSE_FILE="$_lf" + break + fi + done + if [ -z "$LICENSE_FILE" ]; then + echo "ERROR: no LICENSE/COPYING/LICENSING file found in $WOLFMQTT_DIR." >&2 + exit 1 + fi + + # Resolve the source list. Priority order: + # 1. CRA_SBOM_SRCS_FILE — the caller knows their exact build better than any + # heuristic, so an explicit list always wins and is used verbatim. + # 2. compile_commands.json under CRA_SBOM_BUILD_DIR — ESP-IDF and other + # CMake-based builds emit this, the most common embedded path with + # build-system support; filter it to WOLFMQTT_DIR sources only. + # 3. Default glob of src/mqtt_*.c — documented fallback for Arduino and + # bare-metal builds that produce no compile_commands.json. + SRCS_LIST=$(mktemp "${TMPDIR:-/tmp}/wolfmqtt-srcs.XXXXXX") || { + echo "ERROR: mktemp failed for the source-list temp file." >&2 + exit 1 + } + # gen-sbom requires exactly one of --options-h / --user-settings to source + # its build properties. Those describe wolfSSL's crypto/TLS configuration, + # which belongs to the wolfssl component's SBOM, not wolfMQTT's. Feed an + # empty options file so gen-sbom records no (and thus no misattributed) + # build defines for the wolfMQTT component. + EMPTY_OPTS=$(mktemp "${TMPDIR:-/tmp}/wolfmqtt-opts.XXXXXX") || { + echo "ERROR: mktemp failed for the empty options temp file." >&2 + exit 1 + } + trap 'rm -f "$SRCS_LIST" "$EMPTY_OPTS"' EXIT -# Detect whether the wolfMQTT tree is already configured; run ./configure first -# if no Makefile is present. -(cd "$WOLFMQTT_DIR" && { - if [ ! -f Makefile ]; then - echo " Running ./configure first (WOLFSSL_DIR=$WOLFSSL_DIR)..." - ./configure --with-wolfssl="$WOLFSSL_DIR" + if [ -n "${CRA_SBOM_SRCS_FILE:-}" ]; then + if [ ! -f "$CRA_SBOM_SRCS_FILE" ]; then + echo "ERROR: CRA_SBOM_SRCS_FILE=$CRA_SBOM_SRCS_FILE not found." >&2 + exit 1 + fi + echo " Source list: CRA_SBOM_SRCS_FILE=$CRA_SBOM_SRCS_FILE (verbatim)" + grep -v '^[[:space:]]*$' "$CRA_SBOM_SRCS_FILE" > "$SRCS_LIST" || { + echo "ERROR: failed to read CRA_SBOM_SRCS_FILE=$CRA_SBOM_SRCS_FILE." >&2 + exit 1 + } + elif [ -n "${CRA_SBOM_BUILD_DIR:-}" ] && [ -f "${CRA_SBOM_BUILD_DIR}/compile_commands.json" ]; then + _ccdb="${CRA_SBOM_BUILD_DIR}/compile_commands.json" + if ! command -v jq >/dev/null 2>&1; then + echo "ERROR: jq is required to read sources from $_ccdb." >&2 + echo " Install jq, or set CRA_SBOM_SRCS_FILE manually." >&2 + exit 1 + fi + echo " Source list: $_ccdb (filtered to WOLFMQTT_DIR)" + jq -r '.[].file' "$_ccdb" \ + | grep "^${WOLFMQTT_DIR}/" \ + | grep -E '/src/mqtt_[^/]+\.c$' \ + | sort -u > "$SRCS_LIST" || { + echo "ERROR: failed to extract sources from $_ccdb." >&2 + exit 1 + } + if [ ! -s "$SRCS_LIST" ]; then + echo "ERROR: $_ccdb yielded no wolfMQTT sources under $WOLFMQTT_DIR/src." >&2 + echo " Check CRA_SBOM_BUILD_DIR, or set CRA_SBOM_SRCS_FILE manually." >&2 + exit 1 + fi + else + echo " Source list: default glob $WOLFMQTT_DIR/src/mqtt_*.c" + for _f in "$WOLFMQTT_DIR"/src/mqtt_*.c; do + [ -f "$_f" ] && echo "$_f" + done | sort -u > "$SRCS_LIST" || { + echo "ERROR: failed to enumerate $WOLFMQTT_DIR/src/mqtt_*.c." >&2 + exit 1 + } + fi + + if [ ! -s "$SRCS_LIST" ]; then + echo "ERROR: no MQTT source files found to hash." >&2 + exit 1 fi + + # Pass the resolved sources positionally to gen-sbom's --srcs (it takes a + # space-separated list; argparse stops consuming at the next -- option, so + # --cdx-out/--spdx-out terminate the list cleanly). + set -- + while IFS= read -r _src; do + [ -n "$_src" ] || continue + if [ ! -f "$_src" ]; then + echo "ERROR: listed source not found: $_src" >&2 + exit 1 + fi + set -- "$@" "$_src" + done < "$SRCS_LIST" + + _count=$# + set -- --srcs "$@" --cdx-out "$CDX_OUT" --spdx-out "$SPDX_OUT" if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + set -- "$@" --license-override "$CRA_LICENSE_OVERRIDE" if [ -n "${CRA_LICENSE_TEXT:-}" ]; then - make sbom WOLFSSL_DIR="$WOLFSSL_DIR" \ - SBOM_LICENSE_OVERRIDE="$CRA_LICENSE_OVERRIDE" \ - SBOM_LICENSE_TEXT="$CRA_LICENSE_TEXT" - else - make sbom WOLFSSL_DIR="$WOLFSSL_DIR" \ - SBOM_LICENSE_OVERRIDE="$CRA_LICENSE_OVERRIDE" + set -- "$@" --license-text "$CRA_LICENSE_TEXT" fi - else - make sbom WOLFSSL_DIR="$WOLFSSL_DIR" fi - cp -f "wolfmqtt-${VERSION}.cdx.json" "$CDX_OUT" - cp -f "wolfmqtt-${VERSION}.spdx.json" "$SPDX_OUT" - if [ -f "wolfmqtt-${VERSION}.spdx" ]; then - cp -f "wolfmqtt-${VERSION}.spdx" "$OUT_DIR/" + + "$PY" "$GEN" \ + --name wolfmqtt --version "$VERSION" \ + --license-file "$LICENSE_FILE" \ + --options-h "$EMPTY_OPTS" \ + "$@" || { + echo "ERROR: gen-sbom failed." >&2 + exit 1 + } + + echo "NOTE: hashed ${_count} source file(s)" +} + +case "${CRA_SBOM_MODE:-autotools}" in + autotools) _run_autotools ;; + embedded) _run_embedded ;; + *) + echo "ERROR: unknown CRA_SBOM_MODE='${CRA_SBOM_MODE:-}' (expected 'autotools' or 'embedded')" >&2 + exit 1 + ;; +esac + +# Verify the generator actually produced non-empty SBOM files. +for _out in "$CDX_OUT" "$SPDX_OUT"; do + if [ ! -s "$_out" ]; then + echo "ERROR: expected SBOM output missing or empty: $_out" >&2 + exit 1 fi -}) +done echo "Done." From 53d6c3746774ab07fa8b57a5d259ca24baa5bbcb Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 19:01:27 -0700 Subject: [PATCH 15/27] feat: add embedded mode to generate-wolftpm-sbom.sh --- cra-kit/scripts/generate-wolftpm-sbom.sh | 164 ++++++++++++++++++++++- 1 file changed, 162 insertions(+), 2 deletions(-) diff --git a/cra-kit/scripts/generate-wolftpm-sbom.sh b/cra-kit/scripts/generate-wolftpm-sbom.sh index 96af1ceaf..97cc2e391 100755 --- a/cra-kit/scripts/generate-wolftpm-sbom.sh +++ b/cra-kit/scripts/generate-wolftpm-sbom.sh @@ -2,9 +2,11 @@ # Generate wolfTPM component SBOMs (autotools make sbom, cmake sbom, or direct gen-sbom). # # Mode selection: -# CRA_SBOM_MODE=autotools|cmake +# CRA_SBOM_MODE=autotools|cmake|embedded # autotools (default when configure+Makefile exist): runs `make sbom` # cmake: auto-extracts sources from compile_commands.json and runs gen-sbom directly +# embedded: hashes the wolfTPM core sources + one platform HAL file directly +# (for firmware builds that compile wolfTPM in, with no .so to hash) # # Required variables: # WOLFSSL_DIR=path/to/wolfssl (source tree root; provides gen-sbom) @@ -13,6 +15,17 @@ # Mode-specific variables: # WOLFTPM_BUILD_DIR=path/to/build (cmake mode: path to cmake build directory; # triggers compile_commands.json auto-extraction) +# CRA_TPM_HAL=st|espressif|microchip|atmel|zephyr|xilinx|uboot|barebox|qnx|mmio|infineon +# (embedded mode: selects the single platform HAL +# file hal/tpm_io_${CRA_TPM_HAL}.c. Required for a +# complete embedded SBOM; if unset, no HAL file is +# hashed and a warning is emitted.) +# CRA_SBOM_SRCS_FILE=path/to/srcs.txt (embedded mode: explicit source list, one .c path +# per line; takes priority over all auto-detection. +# The caller owns correctness of this list.) +# CRA_TPM_OPTIONS_H=path/to/options.h (embedded mode: flat #define build-config header for +# feature enumeration; defaults to +# $WOLFTPM_DIR/wolftpm/options.h) # # Optional variables: # CRA_LICENSE_OVERRIDE= (e.g. LicenseRef-wolfTPM-Commercial) @@ -204,10 +217,157 @@ _run_cmake() { "$PYTHON3" "$GEN" "$@" } +_run_embedded() { + echo "==> Embedded path: hash wolfTPM core sources + one platform HAL" + + GEN_PY=$(command -v python3 2>/dev/null || command -v python 2>/dev/null || true) + if [ -z "$GEN_PY" ]; then + echo "ERROR: python3 not found in PATH (needed to run gen-sbom)." >&2 + exit 1 + fi + + if [ ! -d "$WOLFTPM_DIR/src" ]; then + echo "ERROR: $WOLFTPM_DIR/src not found; not a wolfTPM source tree?" >&2 + exit 1 + fi + + # gen-sbom needs a build-config header to enumerate enabled features for the + # SBOM (it requires exactly one of --options-h / --user-settings). For wolfTPM + # the committed wolftpm/options.h is a flat #define file in the same shape the + # autotools `make sbom` path feeds via --options-h, so reuse it; callers whose + # firmware uses a different config can point CRA_TPM_OPTIONS_H at their header. + OPTIONS_H=${CRA_TPM_OPTIONS_H:-"$WOLFTPM_DIR/wolftpm/options.h"} + if [ ! -f "$OPTIONS_H" ]; then + echo "ERROR: build-config header not found: $OPTIONS_H" >&2 + echo " Run ./configure in WOLFTPM_DIR to generate wolftpm/options.h," >&2 + echo " or set CRA_TPM_OPTIONS_H to your firmware's flat #define header." >&2 + exit 1 + fi + + # Source list, one .c path per line. + _srcs=$(mktemp "${TMPDIR:-/tmp}/wolftpm-embedded-srcs.XXXXXX") || { + echo "ERROR: mktemp failed for the embedded source list." >&2 + exit 1 + } + _auto_tempfiles="${_auto_tempfiles:-} $_srcs" + + # CRA_SBOM_SRCS_FILE takes priority over all auto-detection: when the caller + # supplies an explicit list they have already reconciled it against their exact + # firmware link line (core + the correct HAL + any tpm2_* they actually build), + # so second-guessing it here could only drop or add files they did not intend. + if [ -n "${CRA_SBOM_SRCS_FILE:-}" ]; then + if [ ! -f "$CRA_SBOM_SRCS_FILE" ]; then + echo "ERROR: CRA_SBOM_SRCS_FILE=$CRA_SBOM_SRCS_FILE not found." >&2 + exit 1 + fi + echo " Using caller-supplied source list: $CRA_SBOM_SRCS_FILE" + cp -f "$CRA_SBOM_SRCS_FILE" "$_srcs" || { + echo "ERROR: could not read CRA_SBOM_SRCS_FILE." >&2 + exit 1 + } + else + # Core sources: every src/tpm2*.c EXCEPT the host-only transports below. + # The excluded files (Linux /dev/tpm0, Windows TBS, swtpm simulator) target + # a full OS and will not compile or link on a bare-metal/RTOS firmware build, + # so including them would misrepresent what is actually in the firmware. + for _f in "$WOLFTPM_DIR"/src/tpm2*.c; do + [ -f "$_f" ] || continue + case "$(basename "$_f")" in + tpm2_linux.c|tpm2_winapi.c|tpm2_swtpm.c) continue ;; + esac + echo "$_f" >> "$_srcs" || { + echo "ERROR: failed writing core source to list." >&2 + exit 1 + } + done + + # Dispatcher: always part of the HAL layer. + if [ -f "$WOLFTPM_DIR/hal/tpm_io.c" ]; then + echo "$WOLFTPM_DIR/hal/tpm_io.c" >> "$_srcs" || { + echo "ERROR: failed writing dispatcher to list." >&2 + exit 1 + } + fi + + # Platform HAL: exactly one tpm_io_.c belongs in a given firmware. + # Which one is the caller's responsibility — only they know the target board. + # Picking the wrong HAL (or all of them) would produce an SBOM that does not + # match the shipped firmware, so we hash exactly the one named by CRA_TPM_HAL + # and refuse to guess: an unset CRA_TPM_HAL yields a warning and no HAL file. + if [ -n "${CRA_TPM_HAL:-}" ]; then + _hal="$WOLFTPM_DIR/hal/tpm_io_${CRA_TPM_HAL}.c" + if [ ! -f "$_hal" ]; then + echo "ERROR: CRA_TPM_HAL=$CRA_TPM_HAL but $_hal does not exist." >&2 + echo " Available HALs:" >&2 + for _h in "$WOLFTPM_DIR"/hal/tpm_io_*.c; do + [ -f "$_h" ] || continue + _b=$(basename "$_h"); _b=${_b#tpm_io_}; _b=${_b%.c} + echo " $_b" >&2 + done + exit 1 + fi + echo "$_hal" >> "$_srcs" || { + echo "ERROR: failed writing HAL source to list." >&2 + exit 1 + } + echo " HAL: tpm_io_${CRA_TPM_HAL}.c" + else + echo "WARNING: CRA_TPM_HAL not set; HAL source excluded from SBOM. Set CRA_TPM_HAL=st|espressif|..." >&2 + fi + fi + + if [ ! -s "$_srcs" ]; then + echo "ERROR: no source files collected for the embedded SBOM." >&2 + exit 1 + fi + + # wolfcrypt/wolfssl sources are intentionally NOT hashed here: they are a + # separate component covered by generate-wolfssl-sbom.sh (embedded mode), and + # the wolfSSL SBOM is referenced as a dependency rather than duplicated. + + _n=$(wc -l < "$_srcs" | tr -d ' ') + echo "NOTE: hashed $_n source file(s)" + + # gen-sbom takes the source list as a positional --srcs vector (exactly one + # of --lib / --srcs is accepted). Build the option vector first, then append + # the collected paths after --srcs so they bind as that argument's nargs list. + set -- \ + --name wolftpm \ + --version "$VERSION" \ + --supplier "wolfSSL Inc." \ + --license-file "$WOLFTPM_DIR/LICENSE" \ + --options-h "$OPTIONS_H" \ + --cdx-out "$CDX_OUT" \ + --spdx-out "$SPDX_OUT" + if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + set -- "$@" --license-override "$CRA_LICENSE_OVERRIDE" + if [ -n "${CRA_LICENSE_TEXT:-}" ]; then + set -- "$@" --license-text "$CRA_LICENSE_TEXT" + fi + fi + set -- "$@" --srcs + while IFS= read -r _src; do + [ -n "$_src" ] || continue + set -- "$@" "$_src" + done < "$_srcs" + "$GEN_PY" "$GEN" "$@" || { + echo "ERROR: gen-sbom failed in embedded mode." >&2 + exit 1 + } + + for _out in "$CDX_OUT" "$SPDX_OUT"; do + if [ ! -s "$_out" ]; then + echo "ERROR: expected output $_out is missing or empty." >&2 + exit 1 + fi + done +} + MODE=${CRA_SBOM_MODE:-} case "$MODE" in autotools) _run_autotools ;; cmake) _run_cmake ;; + embedded) _run_embedded ;; "") if [ -n "${WOLFTPM_BUILD_DIR:-}" ] && [ -d "${WOLFTPM_BUILD_DIR}" ]; then MODE=cmake @@ -224,7 +384,7 @@ case "$MODE" in fi ;; *) - echo "ERROR: CRA_SBOM_MODE must be 'autotools' or 'cmake', not '$MODE'" >&2 + echo "ERROR: CRA_SBOM_MODE must be 'autotools', 'cmake', or 'embedded', not '$MODE'" >&2 exit 1 ;; esac From 5f909c67135ae1a0a0a604788f44c112ae52d633 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 19:16:12 -0700 Subject: [PATCH 16/27] refactor: extract shared source-extraction lib _cra-sbom-extract.sh --- cra-kit/scripts/_cra-sbom-extract.sh | 271 +++++++++++++++++++++++ cra-kit/scripts/generate-wolfssl-sbom.sh | 211 +++--------------- 2 files changed, 301 insertions(+), 181 deletions(-) create mode 100644 cra-kit/scripts/_cra-sbom-extract.sh diff --git a/cra-kit/scripts/_cra-sbom-extract.sh b/cra-kit/scripts/_cra-sbom-extract.sh new file mode 100644 index 000000000..591f720f3 --- /dev/null +++ b/cra-kit/scripts/_cra-sbom-extract.sh @@ -0,0 +1,271 @@ +#!/bin/sh +# _cra-sbom-extract.sh — shared source-extraction helper for CRA Kit SBOM scripts. +# +# Source this file; do not execute it directly. It provides one function, +# _cra_extract_srcs, that product SBOM scripts (generate-wolfssl-sbom.sh, +# generate-wolfssh-sbom.sh, ...) call to auto-detect the list of .c files that +# went into an embedded build. +# +# _cra_extract_srcs PRODUCT_DIR PRODUCT_NAME OUT_FILE +# +# Tries each extraction method in priority order. On success, writes the +# sorted, de-duplicated list of .c paths to OUT_FILE and returns 0. If a +# method is selected (its env var is set) but yields no sources, prints an +# error to stderr and returns 1. If no extraction env var is set at all, +# returns 2 so the caller can fall back to its default source glob. +# +# Methods, in priority order: +# 1. CRA_SBOM_SRCS_FILE — copy verbatim to OUT_FILE (safety net; callers +# normally handle this themselves before calling) +# 2. CRA_SBOM_KEIL_PROJECT — parse .uvprojx, filter to PRODUCT_DIR +# 3. CRA_SBOM_IAR_PROJECT — parse .ewp, filter to PRODUCT_DIR +# 4. CRA_SBOM_MAKEFILE_DIR — `make -n`, grep for PRODUCT_DIR/...*.c +# 5. CRA_SBOM_BUILD_DIR — jq filter compile_commands.json to PRODUCT_DIR +# none set — return 2 +# +# PRODUCT_DIR: absolute path to the product source tree; used to filter paths. +# PRODUCT_NAME: short name for log messages and the wolfssl CMSIS-Pack special +# case (e.g. "wolfssl", "wolfssh", "wolftpm"). +# OUT_FILE: path the caller has already created (mktemp) for the result. +# +# The function appends any temp files it creates to _cra_auto_tempfiles so the +# caller's EXIT trap can clean them up. CRA_SBOM_NO_HASH is honoured by callers, +# not here: a caller that sees CRA_SBOM_NO_HASH=true should skip hashing (and +# thus skip this function) entirely. + +_cra_extract_srcs() { + _cra_product_dir="$1" + _cra_product_name="$2" + _cra_out_file="$3" + + if [ -z "$_cra_product_dir" ] || [ -z "$_cra_product_name" ] || [ -z "$_cra_out_file" ]; then + echo "ERROR: _cra_extract_srcs requires PRODUCT_DIR PRODUCT_NAME OUT_FILE." >&2 + return 1 + fi + + # Method 1: explicit source list file. Callers usually handle this before + # calling us, but honour it here too so the function is safe to call blindly. + if [ -n "${CRA_SBOM_SRCS_FILE:-}" ]; then + if [ ! -f "$CRA_SBOM_SRCS_FILE" ]; then + echo "ERROR: CRA_SBOM_SRCS_FILE=$CRA_SBOM_SRCS_FILE not found." >&2 + return 1 + fi + sort -u "$CRA_SBOM_SRCS_FILE" > "$_cra_out_file" || { + echo "ERROR: failed to read CRA_SBOM_SRCS_FILE=$CRA_SBOM_SRCS_FILE." >&2 + return 1 + } + if [ ! -s "$_cra_out_file" ]; then + echo "ERROR: CRA_SBOM_SRCS_FILE=$CRA_SBOM_SRCS_FILE is empty." >&2 + return 1 + fi + _cra_n=$(wc -l < "$_cra_out_file" | tr -d ' ') + echo " Using $_cra_n sources from CRA_SBOM_SRCS_FILE" + return 0 + fi + + # Method 2: Keil .uvprojx + if [ -n "${CRA_SBOM_KEIL_PROJECT:-}" ]; then + if [ ! -f "$CRA_SBOM_KEIL_PROJECT" ]; then + echo "ERROR: CRA_SBOM_KEIL_PROJECT=$CRA_SBOM_KEIL_PROJECT not found." >&2 + return 1 + fi + if ! command -v python3 >/dev/null 2>&1; then + echo "ERROR: python3 is required to parse a Keil .uvprojx file." >&2 + return 1 + fi + # The CMSIS Pack RTE lookup only applies when PRODUCT_NAME=wolfssl: + # wolfSSL ships a CMSIS Pack whose sources live in the installed pack, + # not in the project. Other products have no CMSIS pack, so the parser + # skips that path and enumerates / entries directly. + python3 - "$CRA_SBOM_KEIL_PROJECT" "$_cra_product_dir" "$_cra_product_name" \ + > "$_cra_out_file" <<'PYEOF' || { +import sys, os, glob, xml.etree.ElementTree as ET + +proj_file = sys.argv[1] +product_dir = sys.argv[2] if len(sys.argv) > 2 else '' +product_name = sys.argv[3] if len(sys.argv) > 3 else '' +proj_dir = os.path.dirname(os.path.abspath(proj_file)) +proj = ET.parse(proj_file) +paths = set() + +rte = proj.find('.//RTE') +# CMSIS Pack RTE special case: wolfSSL only. +if (product_name == 'wolfssl' and rte is not None + and rte.find('.//component[@Cvendor="wolfSSL"]') is not None): + pdsc_candidates = sorted(glob.glob( + os.path.expanduser('~/.arm/Packs/wolfSSL/wolfSSL/*/wolfSSL.pdsc') + )) + if os.name == 'nt': + appdata = os.environ.get('LOCALAPPDATA', '') + pdsc_candidates += sorted(glob.glob( + os.path.join(appdata, 'Arm', 'Packs', 'wolfSSL', 'wolfSSL', '*', 'wolfSSL.pdsc') + )) + if pdsc_candidates: + pdsc_file = pdsc_candidates[-1] + pack_dir = os.path.dirname(pdsc_file) + pdsc = ET.parse(pdsc_file) + for f in pdsc.findall('.//file[@category="source"]'): + name = f.get('name', '') + if name.lower().endswith('.c'): + paths.add(os.path.normpath(os.path.join(pack_dir, name.replace('\\', '/')))) + elif product_dir and os.path.isdir(product_dir): + # Pack not installed locally; enumerate sources from PRODUCT_DIR. + for subdir in ('wolfcrypt/src', 'src'): + d = os.path.join(product_dir, subdir) + if os.path.isdir(d): + for name in os.listdir(d): + if name.endswith('.c'): + paths.add(os.path.join(d, name)) +else: + for file_elem in proj.findall('.//File'): + fp = file_elem.find('FilePath') + ft = file_elem.find('FileType') + if fp is None or not fp.text: + continue + ftype = int(ft.text) if ft is not None and ft.text else 0 + if ftype == 1 or fp.text.lower().endswith('.c'): + abs_path = os.path.normpath( + os.path.join(proj_dir, fp.text.replace('\\', '/')) + ) + paths.add(abs_path) + +for p in sorted(paths): + print(p) +PYEOF + echo "ERROR: failed to parse Keil project $CRA_SBOM_KEIL_PROJECT." >&2 + return 1 + } + # wolfssl's CMSIS-Pack branch legitimately emits paths under the installed + # pack dir (~/.arm/Packs/...), which lie OUTSIDE PRODUCT_DIR; filtering + # would wrongly drop them. The wolfssl parser already constrains its + # output to wolfssl sources, so skip the PRODUCT_DIR filter for wolfssl. + if [ "$_cra_product_name" != "wolfssl" ]; then + _cra_filter_to_product "$_cra_out_file" "$_cra_product_dir" + fi + if [ ! -s "$_cra_out_file" ]; then + echo "ERROR: CRA_SBOM_KEIL_PROJECT is set but no $_cra_product_name sources were extracted from $CRA_SBOM_KEIL_PROJECT." >&2 + return 1 + fi + _cra_n=$(wc -l < "$_cra_out_file" | tr -d ' ') + echo " Auto-extracted $_cra_n $_cra_product_name sources from Keil project" + return 0 + fi + + # Method 3: IAR .ewp + if [ -n "${CRA_SBOM_IAR_PROJECT:-}" ]; then + if [ ! -f "$CRA_SBOM_IAR_PROJECT" ]; then + echo "ERROR: CRA_SBOM_IAR_PROJECT=$CRA_SBOM_IAR_PROJECT not found." >&2 + return 1 + fi + if ! command -v python3 >/dev/null 2>&1; then + echo "ERROR: python3 is required to parse an IAR .ewp file." >&2 + return 1 + fi + python3 - "$CRA_SBOM_IAR_PROJECT" > "$_cra_out_file" <<'PYEOF' || { +import sys, os, xml.etree.ElementTree as ET + +def is_excluded(file_elem): + return file_elem.find('excluded') is not None + +proj_file = sys.argv[1] +proj_dir = os.path.dirname(os.path.abspath(proj_file)) +proj = ET.parse(proj_file) +paths = set() + +for file_elem in proj.findall('.//file'): + if is_excluded(file_elem): + continue + name_elem = file_elem.find('name') + if name_elem is None or not name_elem.text: + continue + raw = name_elem.text + if not raw.lower().endswith('.c'): + continue + resolved = raw.replace('$PROJ_DIR$', proj_dir) + paths.add(os.path.normpath(resolved.replace('\\', '/'))) + +for p in sorted(paths): + print(p) +PYEOF + echo "ERROR: failed to parse IAR project $CRA_SBOM_IAR_PROJECT." >&2 + return 1 + } + # Preserve wolfssl's original behaviour (all project .c, unfiltered); other + # products filter to PRODUCT_DIR to drop demo/BSP sources from the project. + if [ "$_cra_product_name" != "wolfssl" ]; then + _cra_filter_to_product "$_cra_out_file" "$_cra_product_dir" + fi + if [ ! -s "$_cra_out_file" ]; then + echo "ERROR: CRA_SBOM_IAR_PROJECT is set but no $_cra_product_name sources were extracted from $CRA_SBOM_IAR_PROJECT." >&2 + return 1 + fi + _cra_n=$(wc -l < "$_cra_out_file" | tr -d ' ') + echo " Auto-extracted $_cra_n $_cra_product_name sources from IAR project" + return 0 + fi + + # Method 4: Makefile dry-run + if [ -n "${CRA_SBOM_MAKEFILE_DIR:-}" ]; then + if [ ! -d "$CRA_SBOM_MAKEFILE_DIR" ]; then + echo "ERROR: CRA_SBOM_MAKEFILE_DIR=$CRA_SBOM_MAKEFILE_DIR is not a directory." >&2 + return 1 + fi + if ! command -v make >/dev/null 2>&1; then + echo "ERROR: make is required to auto-extract sources from CRA_SBOM_MAKEFILE_DIR." >&2 + return 1 + fi + # `make -n` (dry run) emits the compile commands; pull out any .c path + # that lives under PRODUCT_DIR. grep -F on the dir keeps the pattern + # literal (PRODUCT_DIR may contain regex metacharacters). + make -C "$CRA_SBOM_MAKEFILE_DIR" -n 2>/dev/null \ + | grep -oE '[^ ]+\.c' \ + | grep -F "$_cra_product_dir/" \ + | sort -u > "$_cra_out_file" || true + if [ ! -s "$_cra_out_file" ]; then + echo "ERROR: CRA_SBOM_MAKEFILE_DIR is set but make yielded no $_cra_product_name sources." >&2 + echo " Ensure 'make -n' in $CRA_SBOM_MAKEFILE_DIR references .c files under $_cra_product_dir." >&2 + return 1 + fi + _cra_n=$(wc -l < "$_cra_out_file" | tr -d ' ') + echo " Auto-extracted $_cra_n $_cra_product_name sources via Makefile (CRA_SBOM_MAKEFILE_DIR=$CRA_SBOM_MAKEFILE_DIR)" + return 0 + fi + + # Method 5: compile_commands.json (CMake / Zephyr / ESP-IDF) + if [ -n "${CRA_SBOM_BUILD_DIR:-}" ] && [ -f "$CRA_SBOM_BUILD_DIR/compile_commands.json" ]; then + if ! command -v jq >/dev/null 2>&1; then + echo "ERROR: jq is required to auto-extract sources from compile_commands.json." >&2 + echo " Install jq, or set CRA_SBOM_SRCS_FILE manually. See SRCS-FILE-HOWTO.md." >&2 + return 1 + fi + jq -r '.[].file' "$CRA_SBOM_BUILD_DIR/compile_commands.json" \ + | grep -F "$_cra_product_dir/" \ + | grep -E '\.c$' \ + | sort -u > "$_cra_out_file" || true + if [ ! -s "$_cra_out_file" ]; then + echo "ERROR: compile_commands.json in $CRA_SBOM_BUILD_DIR yielded no $_cra_product_name sources under $_cra_product_dir." >&2 + return 1 + fi + _cra_n=$(wc -l < "$_cra_out_file" | tr -d ' ') + echo " Auto-extracted $_cra_n $_cra_product_name sources from compile_commands.json" + return 0 + fi + + # No extraction method selected — caller should use its default glob. + return 2 +} + +# _cra_filter_to_product OUT_FILE PRODUCT_DIR +# In-place filter: keep only lines that are .c paths under PRODUCT_DIR. +# grep -F keeps PRODUCT_DIR literal (it may contain regex metacharacters). +_cra_filter_to_product() { + _cra_f="$1" + _cra_dir="$2" + _cra_tmp=$(mktemp "${TMPDIR:-/tmp}/cra-filter.XXXXXX") || { + echo "ERROR: mktemp failed while filtering sources." >&2 + return 1 + } + _cra_auto_tempfiles="${_cra_auto_tempfiles:-} $_cra_tmp" + grep -F "$_cra_dir/" "$_cra_f" | grep -E '\.c$' | sort -u > "$_cra_tmp" || true + mv -f "$_cra_tmp" "$_cra_f" +} diff --git a/cra-kit/scripts/generate-wolfssl-sbom.sh b/cra-kit/scripts/generate-wolfssl-sbom.sh index 7d72336ec..57c746984 100755 --- a/cra-kit/scripts/generate-wolfssl-sbom.sh +++ b/cra-kit/scripts/generate-wolfssl-sbom.sh @@ -34,11 +34,16 @@ # without it.) set -eu -# Accumulator for temp files created by _auto_extract_srcs; cleaned up on exit. +# Accumulator for temp files created by _auto_extract_srcs / the shared +# extraction library; cleaned up on exit. The library appends to +# _cra_auto_tempfiles, so trap both. _auto_tempfiles="" -trap 'rm -f ${_auto_tempfiles:-}' EXIT +_cra_auto_tempfiles="" +trap 'rm -f ${_auto_tempfiles:-} ${_cra_auto_tempfiles:-}' EXIT SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) +# shellcheck source=_cra-sbom-extract.sh disable=SC1091 +. "$SCRIPT_DIR/_cra-sbom-extract.sh" KIT_DIR=$(dirname "$SCRIPT_DIR") # shellcheck disable=SC2015 # `|| true` is a deliberate set -e guard, not if-then-else WOLFSSL_DIR=${WOLFSSL_DIR:-$(cd "$KIT_DIR/../../wolfssl" 2>/dev/null && pwd || true)} @@ -131,190 +136,34 @@ _embedded_srcs() { } _auto_extract_srcs() { - # Method 1: compile_commands.json (CMake / Zephyr / ESP-IDF) - if [ -n "${WOLFSSL_BUILD_DIR:-}" ] && [ -f "$WOLFSSL_BUILD_DIR/compile_commands.json" ]; then - _ccdb="$WOLFSSL_BUILD_DIR/compile_commands.json" - if ! command -v jq >/dev/null 2>&1; then - echo "ERROR: jq is required to auto-extract sources from compile_commands.json." >&2 - echo " Install jq, or set CRA_SBOM_SRCS_FILE manually. See SRCS-FILE-HOWTO.md." >&2 - exit 1 - fi - _auto=$(mktemp "${TMPDIR:-/tmp}/wolfssl-auto-srcs.XXXXXX") - _auto_tempfiles="${_auto_tempfiles:-} $_auto" - jq -r '.[].file' "$_ccdb" \ - | grep "^${WOLFSSL_DIR}/" \ - | grep -E '/(wolfcrypt/src|src)/[^/]+\.c$' \ - | sort -u > "$_auto" - if [ ! -s "$_auto" ]; then - _esp_proj=$(dirname "$WOLFSSL_BUILD_DIR") - jq -r '.[].file' "$_ccdb" \ - | grep "^${_esp_proj}/managed_components/wolfssl__wolfssl/" \ - | grep -E '/(wolfcrypt/src|src)/[^/]+\.c$' \ - | sort -u > "$_auto" - fi - if [ -s "$_auto" ]; then - _n=$(wc -l < "$_auto" | tr -d ' ') - echo " Auto-extracted $_n wolfssl sources from compile_commands.json" - CRA_SBOM_SRCS_FILE="$_auto" - CRA_SBOM_SRCS_ONLY_FROM_FILE=true - return 0 - fi - echo " WARNING: compile_commands.json found but yielded no wolfssl sources; trying next method." >&2 + # Delegate to the shared extraction library. The library reads + # CRA_SBOM_BUILD_DIR for compile_commands.json; map WOLFSSL_BUILD_DIR onto it + # so the embedded cmake/Zephyr/ESP-IDF auto-extraction keeps working. + if [ -n "${WOLFSSL_BUILD_DIR:-}" ]; then + CRA_SBOM_BUILD_DIR=${CRA_SBOM_BUILD_DIR:-$WOLFSSL_BUILD_DIR} fi - - # Method 2: Makefile - if [ -n "${CRA_SBOM_MAKEFILE_DIR:-}" ]; then - if [ ! -d "$CRA_SBOM_MAKEFILE_DIR" ]; then - echo "ERROR: CRA_SBOM_MAKEFILE_DIR=$CRA_SBOM_MAKEFILE_DIR is not a directory." >&2 - exit 1 - fi - _auto=$(mktemp "${TMPDIR:-/tmp}/wolfssl-auto-srcs.XXXXXX") - _auto_tempfiles="${_auto_tempfiles:-} $_auto" - if make --no-print-directory -C "$CRA_SBOM_MAKEFILE_DIR" -n print-wolfssl-srcs >/dev/null 2>&1; then - make --no-print-directory -C "$CRA_SBOM_MAKEFILE_DIR" print-wolfssl-srcs 2>/dev/null \ - | sort -u > "$_auto" - fi - if [ ! -s "$_auto" ]; then - make -C "$CRA_SBOM_MAKEFILE_DIR" -n 2>/dev/null \ - | grep -oE '[^ ]+wolfssl[^ ]+\.c' \ - | sort -u > "$_auto" || true - fi - if [ -s "$_auto" ]; then - _n=$(wc -l < "$_auto" | tr -d ' ') - echo " Auto-extracted $_n wolfssl sources via Makefile (CRA_SBOM_MAKEFILE_DIR=$CRA_SBOM_MAKEFILE_DIR)" - CRA_SBOM_SRCS_FILE="$_auto" - CRA_SBOM_SRCS_ONLY_FROM_FILE=true - return 0 - fi - echo "ERROR: CRA_SBOM_MAKEFILE_DIR is set but make yielded no wolfssl sources." >&2 - echo " Add a 'print-wolfssl-srcs' target or ensure 'make -n' references wolfssl .c files." >&2 + _auto=$(mktemp "${TMPDIR:-/tmp}/wolfssl-auto-srcs.XXXXXX") || { + echo "ERROR: mktemp failed for the auto-extract source list." >&2 exit 1 - fi - - # Method 3: Keil .uvprojx - if [ -n "${CRA_SBOM_KEIL_PROJECT:-}" ]; then - if [ ! -f "$CRA_SBOM_KEIL_PROJECT" ]; then - echo "ERROR: CRA_SBOM_KEIL_PROJECT=$CRA_SBOM_KEIL_PROJECT not found." >&2 - exit 1 - fi - if ! command -v python3 >/dev/null 2>&1; then - echo "ERROR: python3 is required to parse a Keil .uvprojx file." >&2 - exit 1 - fi - _auto=$(mktemp "${TMPDIR:-/tmp}/wolfssl-auto-srcs.XXXXXX") - _auto_tempfiles="${_auto_tempfiles:-} $_auto" - python3 - "$CRA_SBOM_KEIL_PROJECT" "$WOLFSSL_DIR" > "$_auto" <<'PYEOF' -import sys, os, glob, xml.etree.ElementTree as ET - -proj_file = sys.argv[1] -wolfssl_dir = sys.argv[2] if len(sys.argv) > 2 else '' -proj_dir = os.path.dirname(os.path.abspath(proj_file)) -proj = ET.parse(proj_file) -paths = set() - -rte = proj.find('.//RTE') -if rte is not None and rte.find('.//component[@Cvendor="wolfSSL"]') is not None: - # CMSIS Pack RTE: sources come from the installed pack .pdsc - pdsc_candidates = sorted(glob.glob( - os.path.expanduser('~/.arm/Packs/wolfSSL/wolfSSL/*/wolfSSL.pdsc') - )) - if os.name == 'nt': - appdata = os.environ.get('LOCALAPPDATA', '') - pdsc_candidates += sorted(glob.glob( - os.path.join(appdata, 'Arm', 'Packs', 'wolfSSL', 'wolfSSL', '*', 'wolfSSL.pdsc') - )) - if pdsc_candidates: - pdsc_file = pdsc_candidates[-1] - pack_dir = os.path.dirname(pdsc_file) - pdsc = ET.parse(pdsc_file) - for f in pdsc.findall('.//file[@category="source"]'): - name = f.get('name', '') - if name.lower().endswith('.c'): - paths.add(os.path.normpath(os.path.join(pack_dir, name.replace('\\', '/')))) - elif wolfssl_dir and os.path.isdir(wolfssl_dir): - # Pack not installed locally; enumerate sources directly from WOLFSSL_DIR. - # The CMSIS Pack contains the full wolfssl library (wolfcrypt/src/ + src/). - for subdir in ('wolfcrypt/src', 'src'): - d = os.path.join(wolfssl_dir, subdir) - if os.path.isdir(d): - for name in os.listdir(d): - if name.endswith('.c'): - paths.add(os.path.join(d, name)) -else: - for file_elem in proj.findall('.//File'): - fp = file_elem.find('FilePath') - ft = file_elem.find('FileType') - if fp is None or not fp.text: - continue - ftype = int(ft.text) if ft is not None and ft.text else 0 - if ftype == 1 or fp.text.lower().endswith('.c'): - abs_path = os.path.normpath( - os.path.join(proj_dir, fp.text.replace('\\', '/')) - ) - paths.add(abs_path) - -for p in sorted(paths): - print(p) -PYEOF - if [ -s "$_auto" ]; then - _n=$(wc -l < "$_auto" | tr -d ' ') - echo " Auto-extracted $_n wolfssl sources from Keil project" + } + _auto_tempfiles="${_auto_tempfiles:-} $_auto" + # `|| _rc=$?` keeps `set -e` from aborting on the library's non-zero returns + # (1 = error, 2 = no method) so we can dispatch on the code below. + _rc=0 + _cra_extract_srcs "$WOLFSSL_DIR" "wolfssl" "$_auto" || _rc=$? + case "$_rc" in + 0) CRA_SBOM_SRCS_FILE="$_auto" CRA_SBOM_SRCS_ONLY_FROM_FILE=true - return 0 - fi - echo "ERROR: CRA_SBOM_KEIL_PROJECT is set but no sources were extracted from $CRA_SBOM_KEIL_PROJECT." >&2 - exit 1 - fi - - # Method 4: IAR .ewp - if [ -n "${CRA_SBOM_IAR_PROJECT:-}" ]; then - if [ ! -f "$CRA_SBOM_IAR_PROJECT" ]; then - echo "ERROR: CRA_SBOM_IAR_PROJECT=$CRA_SBOM_IAR_PROJECT not found." >&2 - exit 1 - fi - if ! command -v python3 >/dev/null 2>&1; then - echo "ERROR: python3 is required to parse an IAR .ewp file." >&2 + ;; + 2) + # No extraction method selected; fall back to the built-in demo list. + ;; + *) + # Library already printed an actionable error to stderr. exit 1 - fi - _auto=$(mktemp "${TMPDIR:-/tmp}/wolfssl-auto-srcs.XXXXXX") - _auto_tempfiles="${_auto_tempfiles:-} $_auto" - python3 - "$CRA_SBOM_IAR_PROJECT" > "$_auto" <<'PYEOF' -import sys, os, xml.etree.ElementTree as ET - -def is_excluded(file_elem): - return file_elem.find('excluded') is not None - -proj_file = sys.argv[1] -proj_dir = os.path.dirname(os.path.abspath(proj_file)) -proj = ET.parse(proj_file) -paths = set() - -for file_elem in proj.findall('.//file'): - if is_excluded(file_elem): - continue - name_elem = file_elem.find('name') - if name_elem is None or not name_elem.text: - continue - raw = name_elem.text - if not raw.lower().endswith('.c'): - continue - resolved = raw.replace('$PROJ_DIR$', proj_dir) - paths.add(os.path.normpath(resolved.replace('\\', '/'))) - -for p in sorted(paths): - print(p) -PYEOF - if [ -s "$_auto" ]; then - _n=$(wc -l < "$_auto" | tr -d ' ') - echo " Auto-extracted $_n wolfssl sources from IAR project" - CRA_SBOM_SRCS_FILE="$_auto" - CRA_SBOM_SRCS_ONLY_FROM_FILE=true - return 0 - fi - echo "ERROR: CRA_SBOM_IAR_PROJECT is set but no sources were extracted from $CRA_SBOM_IAR_PROJECT." >&2 - exit 1 - fi + ;; + esac } _run_embedded() { From 0b410f70c148c55a100d5e43550242ee7828816b Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 19:20:43 -0700 Subject: [PATCH 17/27] feat: add Keil/IAR/Makefile/NO_HASH handlers to generate-wolftpm-sbom.sh --- cra-kit/scripts/generate-wolftpm-sbom.sh | 103 ++++++++++++++++++----- 1 file changed, 80 insertions(+), 23 deletions(-) diff --git a/cra-kit/scripts/generate-wolftpm-sbom.sh b/cra-kit/scripts/generate-wolftpm-sbom.sh index 97cc2e391..6e20cae38 100755 --- a/cra-kit/scripts/generate-wolftpm-sbom.sh +++ b/cra-kit/scripts/generate-wolftpm-sbom.sh @@ -23,6 +23,11 @@ # CRA_SBOM_SRCS_FILE=path/to/srcs.txt (embedded mode: explicit source list, one .c path # per line; takes priority over all auto-detection. # The caller owns correctness of this list.) +# CRA_SBOM_KEIL_PROJECT= (embedded mode: auto-extract srcs from a Keil .uvprojx) +# CRA_SBOM_IAR_PROJECT= (embedded mode: auto-extract srcs from an IAR .ewp) +# CRA_SBOM_MAKEFILE_DIR= (embedded mode: auto-extract srcs via `make -n`) +# CRA_SBOM_NO_HASH=true (embedded mode: emit SBOM without a real artifact +# hash; use when no source list is available) # CRA_TPM_OPTIONS_H=path/to/options.h (embedded mode: flat #define build-config header for # feature enumeration; defaults to # $WOLFTPM_DIR/wolftpm/options.h) @@ -36,6 +41,8 @@ set -eu SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) +# shellcheck source=_cra-sbom-extract.sh disable=SC1091 +. "$SCRIPT_DIR/_cra-sbom-extract.sh" KIT_DIR=$(dirname "$SCRIPT_DIR") # shellcheck disable=SC2015 # `|| true` is a deliberate set -e guard, not if-then-else WOLFTPM_DIR=${WOLFTPM_DIR:-$(cd "$KIT_DIR/../../wolftpm" 2>/dev/null && pwd || true)} @@ -108,9 +115,11 @@ if [ -n "${CRA_LICENSE_TEXT:-}" ] && [ -f "$CRA_LICENSE_TEXT" ]; then echo "License text: $CRA_LICENSE_TEXT" fi -# Accumulator for temp files; cleaned up on exit. +# Accumulators for temp files; cleaned up on exit. The shared extraction library +# appends to _cra_auto_tempfiles, so trap both. _auto_tempfiles="" -trap 'rm -f ${_auto_tempfiles:-}' EXIT +_cra_auto_tempfiles="" +trap 'rm -f ${_auto_tempfiles:-} ${_cra_auto_tempfiles:-}' EXIT _auto_extract_srcs() { # Extract wolftpm sources from compile_commands.json (CMake build). @@ -244,6 +253,45 @@ _run_embedded() { exit 1 fi + # --no-artifact-hash: skip all source-file logic and emit a placeholder hash. + # Use when no compiled library AND no source file list is accessible. The + # options.h header is still required so the SBOM enumerates enabled features. + if [ "${CRA_SBOM_NO_HASH:-}" = "true" ] || [ "${CRA_SBOM_NO_HASH:-}" = "1" ]; then + if [ -n "${CRA_SBOM_SRCS_FILE:-}" ]; then + echo "ERROR: CRA_SBOM_NO_HASH cannot be combined with CRA_SBOM_SRCS_FILE." >&2 + exit 1 + fi + echo " NOTE: CRA_SBOM_NO_HASH=true: emitting SBOM with placeholder hash." + echo " Contact wolfssl@wolfssl.com to discuss integrity verification" + echo " options before using this in production." + set -- \ + --name wolftpm \ + --version "$VERSION" \ + --supplier "wolfSSL Inc." \ + --license-file "$WOLFTPM_DIR/LICENSE" \ + --options-h "$OPTIONS_H" \ + --no-artifact-hash \ + --cdx-out "$CDX_OUT" \ + --spdx-out "$SPDX_OUT" + if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + set -- "$@" --license-override "$CRA_LICENSE_OVERRIDE" + if [ -n "${CRA_LICENSE_TEXT:-}" ]; then + set -- "$@" --license-text "$CRA_LICENSE_TEXT" + fi + fi + "$GEN_PY" "$GEN" "$@" || { + echo "ERROR: gen-sbom failed in embedded NO_HASH mode." >&2 + exit 1 + } + for _out in "$CDX_OUT" "$SPDX_OUT"; do + if [ ! -s "$_out" ]; then + echo "ERROR: expected output $_out is missing or empty." >&2 + exit 1 + fi + done + return 0 + fi + # Source list, one .c path per line. _srcs=$(mktemp "${TMPDIR:-/tmp}/wolftpm-embedded-srcs.XXXXXX") || { echo "ERROR: mktemp failed for the embedded source list." >&2 @@ -251,21 +299,27 @@ _run_embedded() { } _auto_tempfiles="${_auto_tempfiles:-} $_srcs" - # CRA_SBOM_SRCS_FILE takes priority over all auto-detection: when the caller - # supplies an explicit list they have already reconciled it against their exact - # firmware link line (core + the correct HAL + any tpm2_* they actually build), - # so second-guessing it here could only drop or add files they did not intend. - if [ -n "${CRA_SBOM_SRCS_FILE:-}" ]; then - if [ ! -f "$CRA_SBOM_SRCS_FILE" ]; then - echo "ERROR: CRA_SBOM_SRCS_FILE=$CRA_SBOM_SRCS_FILE not found." >&2 - exit 1 - fi - echo " Using caller-supplied source list: $CRA_SBOM_SRCS_FILE" - cp -f "$CRA_SBOM_SRCS_FILE" "$_srcs" || { - echo "ERROR: could not read CRA_SBOM_SRCS_FILE." >&2 - exit 1 - } - else + # Resolve the source list. The shared library handles CRA_SBOM_SRCS_FILE, + # Keil/IAR/Makefile, and compile_commands.json extraction; it returns: + # 0 = a build-system method produced the list (trust it verbatim) + # 2 = no method active (fall back to the default glob + HAL selection) + # 1 = a method was selected but failed (library already explained why) + # `|| _cra_rc=$?` keeps `set -e` from aborting on the non-zero returns. + _cra_rc=0 + _cra_extract_srcs "$WOLFTPM_DIR" "wolftpm" "$_srcs" || _cra_rc=$? + + if [ "$_cra_rc" -eq 0 ]; then + # A build-system method (Keil/IAR/Makefile/compile_commands) produced the + # list. Trust it: the build system already selected the correct single HAL + # and excluded the host-only transports (tpm2_linux.c, tpm2_winapi.c, + # tpm2_swtpm.c). Do NOT apply CRA_TPM_HAL on top — that would double-count + # the HAL or, if CRA_TPM_HAL disagrees with the build, conflict with it. + _n=$(wc -l < "$_srcs" | tr -d ' ') + echo "NOTE: hashed $_n source file(s) (from build system)" + elif [ "$_cra_rc" -eq 2 ]; then + # No extraction method active: build the default source list ourselves, + # selecting the single platform HAL via CRA_TPM_HAL. + # # Core sources: every src/tpm2*.c EXCEPT the host-only transports below. # The excluded files (Linux /dev/tpm0, Windows TBS, swtpm simulator) target # a full OS and will not compile or link on a bare-metal/RTOS firmware build, @@ -314,10 +368,16 @@ _run_embedded() { else echo "WARNING: CRA_TPM_HAL not set; HAL source excluded from SBOM. Set CRA_TPM_HAL=st|espressif|..." >&2 fi - fi - if [ ! -s "$_srcs" ]; then - echo "ERROR: no source files collected for the embedded SBOM." >&2 + if [ ! -s "$_srcs" ]; then + echo "ERROR: no source files collected for the embedded SBOM." >&2 + exit 1 + fi + + _n=$(wc -l < "$_srcs" | tr -d ' ') + echo "NOTE: hashed $_n source file(s)" + else + # Library selected a method but it failed; it already printed the reason. exit 1 fi @@ -325,9 +385,6 @@ _run_embedded() { # separate component covered by generate-wolfssl-sbom.sh (embedded mode), and # the wolfSSL SBOM is referenced as a dependency rather than duplicated. - _n=$(wc -l < "$_srcs" | tr -d ' ') - echo "NOTE: hashed $_n source file(s)" - # gen-sbom takes the source list as a positional --srcs vector (exactly one # of --lib / --srcs is accepted). Build the option vector first, then append # the collected paths after --srcs so they bind as that argument's nargs list. From 7b260edab1dc439c9d3ecd437fc4410f2e852756 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 19:22:32 -0700 Subject: [PATCH 18/27] feat: add Keil/IAR/Makefile handlers to generate-wolfssh-sbom.sh Wire in shared _cra-sbom-extract.sh for Keil/IAR/Makefile/compile_commands.json source extraction. NO_HASH deferred to SBOM-cgz (gen-sbom lacks --no-artifact-hash). --- cra-kit/scripts/generate-wolfssh-sbom.sh | 88 +++++++++--------------- 1 file changed, 33 insertions(+), 55 deletions(-) diff --git a/cra-kit/scripts/generate-wolfssh-sbom.sh b/cra-kit/scripts/generate-wolfssh-sbom.sh index b094750c9..d54f9d3fe 100755 --- a/cra-kit/scripts/generate-wolfssh-sbom.sh +++ b/cra-kit/scripts/generate-wolfssh-sbom.sh @@ -19,6 +19,10 @@ # CRA_SBOM_SRCS_FILE=path/to/srcs.txt (embedded: explicit list of wolfSSH .c # paths, one per line; takes priority over # every other source-resolution method) +# CRA_SBOM_KEIL_PROJECT=path (embedded: auto-extract from Keil .uvprojx) +# CRA_SBOM_IAR_PROJECT=path (embedded: auto-extract from IAR .ewp) +# CRA_SBOM_MAKEFILE_DIR=path (embedded: auto-extract via make -n dry-run) +# CRA_SBOM_NO_HASH — not yet supported; blocked on SBOM-cgz (gen-sbom --no-artifact-hash) # # Optional variables: # CRA_SBOM_OUT_DIR= (output directory; default auditor-packet) @@ -36,10 +40,14 @@ set -eu # Why: mktemp temp files must not leak if any later command fails under set -e; # a single EXIT trap removes them on every exit path including errors. _auto_tempfiles="" -trap 'rm -f ${_auto_tempfiles:-}' EXIT +# _cra_auto_tempfiles is populated by _cra-sbom-extract.sh's helpers; clean both. +trap 'rm -f ${_auto_tempfiles:-} ${_cra_auto_tempfiles:-}' EXIT SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) KIT_DIR=$(dirname "$SCRIPT_DIR") + +# shared extraction methods (Keil, IAR, Makefile, compile_commands.json) +. "$SCRIPT_DIR/_cra-sbom-extract.sh" # shellcheck disable=SC2015 # `|| true` is a deliberate set -e guard, not if-then-else WOLFSSL_DIR=${WOLFSSL_DIR:-$(cd "$KIT_DIR/../../wolfssl" 2>/dev/null && pwd || true)} WOLFSSH_DIR=${WOLFSSH_DIR:-$(cd "$KIT_DIR/../../wolfSSH" 2>/dev/null && pwd || true)} @@ -129,68 +137,36 @@ _python_with_pcpp() { # component covered by generate-wolfssl-sbom.sh and referenced as a dependency. # Mixing them in would double-count the crypto library across two SBOMs. # -# Priority order (first match wins): +# Priority order (first match wins), all but the last handled by +# _cra_extract_srcs from _cra-sbom-extract.sh: # 1. CRA_SBOM_SRCS_FILE — explicit user list beats anything inferred, because -# the user knows their exact link line; we must not -# second-guess it. -# 2. compile_commands.json at CRA_SBOM_BUILD_DIR — accurate per-build extraction -# for CMake / ESP-IDF / Zephyr. -# 3. Default: all ${WOLFSSH_DIR}/src/*.c sorted — the "all sources" fallback for -# toolchains (IAR, Keil, MPLAB) that produce no -# compile_commands.json and where we cannot infer the -# exact subset compiled. Listing all sources is the -# safe over-approximation: it never omits a file that +# the user knows their exact link line. +# 2. CRA_SBOM_KEIL_PROJECT — parse Keil .uvprojx, filter to WOLFSSH_DIR. +# 3. CRA_SBOM_IAR_PROJECT — parse IAR .ewp, filter to WOLFSSH_DIR. +# 4. CRA_SBOM_MAKEFILE_DIR — `make -n` dry-run, filter to WOLFSSH_DIR. +# 5. compile_commands.json at CRA_SBOM_BUILD_DIR — per-build extraction for +# CMake / ESP-IDF / Zephyr. +# 6. Default: all ${WOLFSSH_DIR}/src/*.c sorted — the "all sources" fallback +# for toolchains where we cannot infer the exact +# subset compiled. Listing all sources is the safe +# over-approximation: it never omits a file that # shipped. _resolve_wolfssh_srcs() { _out="$1" - # 1. Explicit user-provided list (verbatim). - if [ -n "${CRA_SBOM_SRCS_FILE:-}" ]; then - if [ ! -f "$CRA_SBOM_SRCS_FILE" ]; then - echo "ERROR: CRA_SBOM_SRCS_FILE=$CRA_SBOM_SRCS_FILE not found." >&2 - exit 1 - fi - if [ ! -s "$CRA_SBOM_SRCS_FILE" ]; then - echo "ERROR: CRA_SBOM_SRCS_FILE=$CRA_SBOM_SRCS_FILE is empty." >&2 - exit 1 - fi - grep -v '^[[:space:]]*$' "$CRA_SBOM_SRCS_FILE" > "$_out" || { - echo "ERROR: failed to read CRA_SBOM_SRCS_FILE=$CRA_SBOM_SRCS_FILE." >&2 - exit 1 - } - echo " Using explicit source list from $CRA_SBOM_SRCS_FILE" + # Try every env-var-driven extraction method (SRCS_FILE, Keil, IAR, + # Makefile, compile_commands.json). rc=2 means none was set: fall back to + # the wolfSSH default glob below. + _cra_rc=0 + _cra_extract_srcs "$WOLFSSH_DIR" "wolfssh" "$_out" || _cra_rc=$? + if [ "$_cra_rc" -eq 0 ]; then return 0 + elif [ "$_cra_rc" -ne 2 ]; then + exit 1 # _cra_extract_srcs already printed the error fi - # 2. compile_commands.json (CMake / ESP-IDF / Zephyr). - if [ -n "${CRA_SBOM_BUILD_DIR:-}" ] && \ - [ -f "$CRA_SBOM_BUILD_DIR/compile_commands.json" ]; then - _ccdb="$CRA_SBOM_BUILD_DIR/compile_commands.json" - if ! command -v jq >/dev/null 2>&1; then - echo "ERROR: jq is required to extract sources from compile_commands.json." >&2 - echo " Install jq, or set CRA_SBOM_SRCS_FILE manually." >&2 - exit 1 - fi - # Restrict to wolfSSH's own src/*.c; exclude any wolfcrypt/wolfssl entries - # that share the build (they belong to the wolfssl component). - jq -r '.[].file' "$_ccdb" \ - | grep "^${WOLFSSH_DIR}/src/" \ - | grep -E '/src/[^/]+\.c$' \ - | sort -u > "$_out" || { - echo "ERROR: failed to parse $_ccdb with jq." >&2 - exit 1 - } - if [ -s "$_out" ]; then - _n=$(wc -l < "$_out" | tr -d ' ') - echo " Extracted $_n wolfSSH sources from compile_commands.json" - return 0 - fi - echo " WARNING: compile_commands.json found but yielded no wolfSSH" >&2 - echo " src/ sources; falling back to all src/*.c." >&2 - fi - - # 3. Default fallback: every wolfSSH src/*.c. A POSIX glob expands in sorted - # order; guard the no-match case where the pattern stays literal. + # No extraction env var set: every wolfSSH src/*.c. A POSIX glob expands in + # sorted order; guard the no-match case where the pattern stays literal. : > "$_out" for _c in "$WOLFSSH_DIR"/src/*.c; do [ -f "$_c" ] || continue @@ -241,6 +217,8 @@ _run_embedded() { _resolve_wolfssh_srcs "$_srcs" # Build the positional --srcs argument list from the resolved file. + # ponytail: gen-sbom lacks --srcs-file; pass list as positional args + # ceiling: ARG_MAX; upgrade path: SBOM-cgz adds --srcs-file to gen-sbom set -- while IFS= read -r _src; do [ -n "$_src" ] || continue From f54370fc8585be31f35d589617eff3d581ef6ce4 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 19:23:48 -0700 Subject: [PATCH 19/27] fix: replace broken --srcs-file/--no-artifact-hash with positional --srcs in wolftpm cmake+embedded modes --- cra-kit/scripts/generate-wolftpm-sbom.sh | 48 +++++------------------- 1 file changed, 9 insertions(+), 39 deletions(-) diff --git a/cra-kit/scripts/generate-wolftpm-sbom.sh b/cra-kit/scripts/generate-wolftpm-sbom.sh index 6e20cae38..e791118f2 100755 --- a/cra-kit/scripts/generate-wolftpm-sbom.sh +++ b/cra-kit/scripts/generate-wolftpm-sbom.sh @@ -214,7 +214,6 @@ _run_cmake() { --version "$VERSION" \ --supplier "wolfSSL Inc." \ --license-file "$WOLFTPM_DIR/LICENSE" \ - --srcs-file "$CRA_SBOM_SRCS_FILE" \ --cdx-out "$CDX_OUT" \ --spdx-out "$SPDX_OUT" if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then @@ -223,6 +222,13 @@ _run_cmake() { set -- "$@" --license-text "$CRA_LICENSE_TEXT" fi fi + # ponytail: gen-sbom lacks --srcs-file; pass list as positional args after --srcs + # ceiling: ARG_MAX on very large source trees; upgrade path: SBOM-cgz + set -- "$@" --srcs + while IFS= read -r _src; do + [ -n "$_src" ] || continue + set -- "$@" "$_src" + done < "$CRA_SBOM_SRCS_FILE" "$PYTHON3" "$GEN" "$@" } @@ -253,44 +259,8 @@ _run_embedded() { exit 1 fi - # --no-artifact-hash: skip all source-file logic and emit a placeholder hash. - # Use when no compiled library AND no source file list is accessible. The - # options.h header is still required so the SBOM enumerates enabled features. - if [ "${CRA_SBOM_NO_HASH:-}" = "true" ] || [ "${CRA_SBOM_NO_HASH:-}" = "1" ]; then - if [ -n "${CRA_SBOM_SRCS_FILE:-}" ]; then - echo "ERROR: CRA_SBOM_NO_HASH cannot be combined with CRA_SBOM_SRCS_FILE." >&2 - exit 1 - fi - echo " NOTE: CRA_SBOM_NO_HASH=true: emitting SBOM with placeholder hash." - echo " Contact wolfssl@wolfssl.com to discuss integrity verification" - echo " options before using this in production." - set -- \ - --name wolftpm \ - --version "$VERSION" \ - --supplier "wolfSSL Inc." \ - --license-file "$WOLFTPM_DIR/LICENSE" \ - --options-h "$OPTIONS_H" \ - --no-artifact-hash \ - --cdx-out "$CDX_OUT" \ - --spdx-out "$SPDX_OUT" - if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then - set -- "$@" --license-override "$CRA_LICENSE_OVERRIDE" - if [ -n "${CRA_LICENSE_TEXT:-}" ]; then - set -- "$@" --license-text "$CRA_LICENSE_TEXT" - fi - fi - "$GEN_PY" "$GEN" "$@" || { - echo "ERROR: gen-sbom failed in embedded NO_HASH mode." >&2 - exit 1 - } - for _out in "$CDX_OUT" "$SPDX_OUT"; do - if [ ! -s "$_out" ]; then - echo "ERROR: expected output $_out is missing or empty." >&2 - exit 1 - fi - done - return 0 - fi + # CRA_SBOM_NO_HASH is not yet supported: gen-sbom requires --lib or --srcs and + # has no --no-artifact-hash flag. Blocked on SBOM-cgz. # Source list, one .c path per line. _srcs=$(mktemp "${TMPDIR:-/tmp}/wolftpm-embedded-srcs.XXXXXX") || { From 234bf26001240caa25019f221fe87dbb48a773f3 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 19:24:04 -0700 Subject: [PATCH 20/27] feat: add Keil/IAR/Makefile/compile_commands handlers to generate-wolfhsm-sbom.sh Fix broken --srcs-file call (gen-sbom only supports positional --srcs). NO_HASH deferred to SBOM-cgz. --- cra-kit/scripts/generate-wolfhsm-sbom.sh | 54 ++++++++++++++++++------ 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/cra-kit/scripts/generate-wolfhsm-sbom.sh b/cra-kit/scripts/generate-wolfhsm-sbom.sh index 0fee38839..ff98e33e6 100755 --- a/cra-kit/scripts/generate-wolfhsm-sbom.sh +++ b/cra-kit/scripts/generate-wolfhsm-sbom.sh @@ -15,11 +15,21 @@ # CRA_PYTHON=python3 (interpreter with pcpp) # CRA_LICENSE_OVERRIDE= (e.g. LicenseRef-wolfSSL-Commercial) # CRA_LICENSE_TEXT= (required when CRA_LICENSE_OVERRIDE is LicenseRef-*) +# WOLFHSM_BUILD_DIR=path auto-extract from compile_commands.json +# CRA_SBOM_SRCS_FILE=path explicit .c file list, one per line +# CRA_SBOM_KEIL_PROJECT=path auto-extract from Keil .uvprojx +# CRA_SBOM_IAR_PROJECT=path auto-extract from IAR .ewp +# CRA_SBOM_MAKEFILE_DIR=path auto-extract via make -n dry-run +# CRA_SBOM_NO_HASH — not yet supported; blocked on SBOM-cgz (gen-sbom --no-artifact-hash) set -eu -# Accumulator for temp files; cleaned up on exit. +. "$(dirname "$0")/_cra-sbom-extract.sh" + +# Accumulator for temp files; cleaned up on exit. The shared extraction library +# appends to _cra_auto_tempfiles, so trap both. _auto_tempfiles="" -trap 'rm -f ${_auto_tempfiles:-}' EXIT +_cra_auto_tempfiles="" +trap 'rm -f ${_auto_tempfiles:-} ${_cra_auto_tempfiles:-}' EXIT SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) KIT_DIR=$(dirname "$SCRIPT_DIR") @@ -108,23 +118,39 @@ _python_with_pcpp() { return 1 } -# Enumerate wolfHSM sources: all *.c directly under src/ (no recursion). -_wolfhsm_srcs() { - find "$WOLFHSM_DIR/src" -maxdepth 1 -name "*.c" | sort -} - echo "==> Embedded path: gen-sbom with CC -dM -E (no user_settings.h)" -# Write collected source paths to a temp file for --srcs-file. _srcs_file=$(mktemp "${TMPDIR:-/tmp}/wolfhsm-srcs.XXXXXX") _auto_tempfiles="${_auto_tempfiles:-} $_srcs_file" -_wolfhsm_srcs > "$_srcs_file" -_n=$(wc -l < "$_srcs_file" | tr -d ' ') -echo " Sources: $_n .c files from $WOLFHSM_DIR/src/" + +# Allow WOLFHSM_BUILD_DIR to feed compile_commands.json extraction. The shared +# library reads CRA_SBOM_BUILD_DIR; map our wolfHSM-style env var onto it. +CRA_SBOM_BUILD_DIR="${WOLFHSM_BUILD_DIR:-}" + +_cra_rc=0 +_cra_extract_srcs "$WOLFHSM_DIR" "wolfhsm" "$_srcs_file" || _cra_rc=$? + +if [ "$_cra_rc" -eq 2 ]; then + # No extraction method active: enumerate all wolfHSM C sources via find. + # find is used (not glob) because wolfHSM sources span subdirectories. + find "$WOLFHSM_DIR/src" -name "*.c" | sort > "$_srcs_file" || { + echo "ERROR: find failed on $WOLFHSM_DIR/src" >&2; exit 1 + } + _n=$(wc -l < "$_srcs_file" | tr -d ' ') + echo " Source list: find $WOLFHSM_DIR/src -name '*.c' ($_n files)" +elif [ "$_cra_rc" -ne 0 ]; then + exit 1 +fi + if [ ! -s "$_srcs_file" ]; then - echo "ERROR: no .c files found under $WOLFHSM_DIR/src/." >&2 + echo "ERROR: no wolfHSM sources found in $WOLFHSM_DIR/src" >&2 exit 1 fi +_n=$(wc -l < "$_srcs_file" | tr -d ' ') +echo "NOTE: hashed $_n source file(s)" + +# ponytail: gen-sbom lacks --srcs-file; pass list as positional args +# ceiling: ARG_MAX; upgrade path: SBOM-cgz adds --srcs-file to gen-sbom # Build license-override args. _license_args="" @@ -154,7 +180,7 @@ if _py=$(_python_with_pcpp); then --user-settings "$SETTINGS_H" \ --user-settings-include "$WOLFHSM_DIR" \ --user-settings-include "$WOLFSSL_DIR" \ - --srcs-file "$_srcs_file" \ + --srcs $(cat "$_srcs_file") \ --cdx-out "$CDX_OUT" \ --spdx-out "$SPDX_OUT" \ ${_license_args} @@ -183,7 +209,7 @@ else --supplier "wolfSSL Inc." \ --license-file "$WOLFHSM_DIR/LICENSING" \ --options-h "$_defines" \ - --srcs-file "$_srcs_file" \ + --srcs $(cat "$_srcs_file") \ --cdx-out "$CDX_OUT" \ --spdx-out "$SPDX_OUT" \ ${_license_args} From a108cdd5889d024ba1b7735e06b50f3a83b58e8e Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 19:24:11 -0700 Subject: [PATCH 21/27] feat: add Keil/IAR/Makefile handlers to generate-wolfmqtt-sbom.sh --- cra-kit/scripts/generate-wolfmqtt-sbom.sh | 75 +++++++++-------------- 1 file changed, 29 insertions(+), 46 deletions(-) diff --git a/cra-kit/scripts/generate-wolfmqtt-sbom.sh b/cra-kit/scripts/generate-wolfmqtt-sbom.sh index 08866dc1b..93cf45a35 100755 --- a/cra-kit/scripts/generate-wolfmqtt-sbom.sh +++ b/cra-kit/scripts/generate-wolfmqtt-sbom.sh @@ -16,8 +16,12 @@ # Embedded-mode variables (CRA_SBOM_MODE=embedded): # CRA_SBOM_SRCS_FILE=path/to/srcs.txt (explicit .c list, one path per line; # used verbatim — highest priority) +# CRA_SBOM_KEIL_PROJECT=path/to/x.uvprojx (parse Keil project for .c sources) +# CRA_SBOM_IAR_PROJECT=path/to/x.ewp (parse IAR project for .c sources) +# CRA_SBOM_MAKEFILE_DIR=path/to/dir (run `make -n` to extract .c sources) # CRA_SBOM_BUILD_DIR=path/to/build (CMake/ESP-IDF build dir; sources are # read from its compile_commands.json) +# CRA_SBOM_NO_HASH — not yet supported; blocked on SBOM-cgz # # Optional variables: # CRA_LICENSE_OVERRIDE= (e.g. LicenseRef-wolfSSL-Commercial) @@ -32,6 +36,9 @@ set -eu SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) KIT_DIR=$(dirname "$SCRIPT_DIR") +# Shared source-extraction helper (Keil/IAR/Makefile/compile_commands.json). +. "$SCRIPT_DIR/_cra-sbom-extract.sh" + # shellcheck disable=SC2015 WOLFSSL_DIR=${WOLFSSL_DIR:-$(cd "$KIT_DIR/../../wolfssl" 2>/dev/null && pwd || true)} WOLFMQTT_DIR=${WOLFMQTT_DIR:-} @@ -175,14 +182,6 @@ _run_embedded() { exit 1 fi - # Resolve the source list. Priority order: - # 1. CRA_SBOM_SRCS_FILE — the caller knows their exact build better than any - # heuristic, so an explicit list always wins and is used verbatim. - # 2. compile_commands.json under CRA_SBOM_BUILD_DIR — ESP-IDF and other - # CMake-based builds emit this, the most common embedded path with - # build-system support; filter it to WOLFMQTT_DIR sources only. - # 3. Default glob of src/mqtt_*.c — documented fallback for Arduino and - # bare-metal builds that produce no compile_commands.json. SRCS_LIST=$(mktemp "${TMPDIR:-/tmp}/wolfmqtt-srcs.XXXXXX") || { echo "ERROR: mktemp failed for the source-list temp file." >&2 exit 1 @@ -196,46 +195,29 @@ _run_embedded() { echo "ERROR: mktemp failed for the empty options temp file." >&2 exit 1 } - trap 'rm -f "$SRCS_LIST" "$EMPTY_OPTS"' EXIT + # _cra_auto_tempfiles collects any temp files the shared extractor creates; + # initialise it so the EXIT trap is safe under `set -u` even when the + # extractor adds nothing (e.g. the default-glob path). + _cra_auto_tempfiles="" + trap 'rm -f "$SRCS_LIST" "$EMPTY_OPTS" $_cra_auto_tempfiles' EXIT - if [ -n "${CRA_SBOM_SRCS_FILE:-}" ]; then - if [ ! -f "$CRA_SBOM_SRCS_FILE" ]; then - echo "ERROR: CRA_SBOM_SRCS_FILE=$CRA_SBOM_SRCS_FILE not found." >&2 - exit 1 - fi - echo " Source list: CRA_SBOM_SRCS_FILE=$CRA_SBOM_SRCS_FILE (verbatim)" - grep -v '^[[:space:]]*$' "$CRA_SBOM_SRCS_FILE" > "$SRCS_LIST" || { - echo "ERROR: failed to read CRA_SBOM_SRCS_FILE=$CRA_SBOM_SRCS_FILE." >&2 - exit 1 - } - elif [ -n "${CRA_SBOM_BUILD_DIR:-}" ] && [ -f "${CRA_SBOM_BUILD_DIR}/compile_commands.json" ]; then - _ccdb="${CRA_SBOM_BUILD_DIR}/compile_commands.json" - if ! command -v jq >/dev/null 2>&1; then - echo "ERROR: jq is required to read sources from $_ccdb." >&2 - echo " Install jq, or set CRA_SBOM_SRCS_FILE manually." >&2 - exit 1 - fi - echo " Source list: $_ccdb (filtered to WOLFMQTT_DIR)" - jq -r '.[].file' "$_ccdb" \ - | grep "^${WOLFMQTT_DIR}/" \ - | grep -E '/src/mqtt_[^/]+\.c$' \ - | sort -u > "$SRCS_LIST" || { - echo "ERROR: failed to extract sources from $_ccdb." >&2 - exit 1 - } - if [ ! -s "$SRCS_LIST" ]; then - echo "ERROR: $_ccdb yielded no wolfMQTT sources under $WOLFMQTT_DIR/src." >&2 - echo " Check CRA_SBOM_BUILD_DIR, or set CRA_SBOM_SRCS_FILE manually." >&2 - exit 1 - fi - else + # Resolve the source list via the shared extractor (CRA_SBOM_SRCS_FILE, + # Keil/IAR projects, Makefile dry-run, or compile_commands.json). It returns + # 2 when no extraction method is selected, in which case we fall back to the + # default mqtt_*.c glob. + _cra_rc=0 + _cra_extract_srcs "$WOLFMQTT_DIR" "wolfmqtt" "$SRCS_LIST" || _cra_rc=$? + + if [ "$_cra_rc" -eq 2 ]; then + # No extraction method active: use default glob (all src/mqtt_*.c sorted). + # Why: MQTT has no HAL split, so the full source set is the right default + # for bare-metal builds without a build-system-extractable source list. echo " Source list: default glob $WOLFMQTT_DIR/src/mqtt_*.c" - for _f in "$WOLFMQTT_DIR"/src/mqtt_*.c; do - [ -f "$_f" ] && echo "$_f" - done | sort -u > "$SRCS_LIST" || { - echo "ERROR: failed to enumerate $WOLFMQTT_DIR/src/mqtt_*.c." >&2 - exit 1 - } + for _c in "$WOLFMQTT_DIR"/src/mqtt_*.c; do + [ -f "$_c" ] && echo "$_c" + done | sort > "$SRCS_LIST" + elif [ "$_cra_rc" -ne 0 ]; then + exit 1 fi if [ ! -s "$SRCS_LIST" ]; then @@ -246,6 +228,7 @@ _run_embedded() { # Pass the resolved sources positionally to gen-sbom's --srcs (it takes a # space-separated list; argparse stops consuming at the next -- option, so # --cdx-out/--spdx-out terminate the list cleanly). + # ponytail: gen-sbom lacks --srcs-file; upgrade path: SBOM-cgz set -- while IFS= read -r _src; do [ -n "$_src" ] || continue From b907915855b03a37a54b689a4c10c53ab40d96ee Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 19:28:09 -0700 Subject: [PATCH 22/27] feat: add Keil/IAR/NO_HASH handlers to generate-wolfboot-sbom.sh --- cra-kit/scripts/generate-wolfboot-sbom.sh | 104 +++++++++++++++------- 1 file changed, 74 insertions(+), 30 deletions(-) diff --git a/cra-kit/scripts/generate-wolfboot-sbom.sh b/cra-kit/scripts/generate-wolfboot-sbom.sh index 2a5f1d3fb..6a354f297 100755 --- a/cra-kit/scripts/generate-wolfboot-sbom.sh +++ b/cra-kit/scripts/generate-wolfboot-sbom.sh @@ -25,11 +25,24 @@ # LicenseRef-* id: plain-text license # embedded in the SBOM) # CRA_SBOM_OUT_DIR= (override output directory) +# CRA_SBOM_SRCS_FILE=path explicit .c file list (overrides make -n) +# CRA_SBOM_KEIL_PROJECT=path auto-extract from Keil .uvprojx (overrides make -n) +# CRA_SBOM_IAR_PROJECT=path auto-extract from IAR .ewp (overrides make -n) +# CRA_SBOM_NO_HASH — not yet supported; blocked on SBOM-cgz (gen-sbom --no-artifact-hash) set -eu SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) KIT_DIR=$(dirname "$SCRIPT_DIR") +# Shared source-extraction helper. It appends temp files it creates to +# _cra_auto_tempfiles, so the EXIT trap below cleans both lists. +_auto_tempfiles="" +_cra_auto_tempfiles="" +trap 'rm -f ${_auto_tempfiles:-} ${_cra_auto_tempfiles:-}' EXIT + +# shellcheck source=_cra-sbom-extract.sh disable=SC1091 +. "$SCRIPT_DIR/_cra-sbom-extract.sh" + # Default wolfBoot directory: sibling of the wolfssl-examples checkout. # shellcheck disable=SC2015 WOLFBOOT_DIR=${WOLFBOOT_DIR:-$(cd "$KIT_DIR/../../wolfBoot" 2>/dev/null && pwd || true)} @@ -113,43 +126,72 @@ if [ -n "${CRA_LICENSE_TEXT:-}" ] && [ -f "$CRA_LICENSE_TEXT" ]; then CRA_LICENSE_TEXT=$(CDPATH='' cd -- "$(dirname -- "$CRA_LICENSE_TEXT")" && pwd)/$(basename -- "$CRA_LICENSE_TEXT") fi -# Extract the configuration-specific source list via `make -n`. +# Extract the configuration-specific source list. # -# `make -n TARGET=... SIGN=...` prints every command make would run without -# executing them. The compiler invocations include every .c file on the -# wolfBoot link line, including both core wolfBoot sources and wolfcrypt -# files compiled inline. grep -oE pulls out every .c argument; sort -u -# deduplicates. -echo "Extracting source list via make -n TARGET=$WOLFBOOT_TARGET SIGN=$WOLFBOOT_SIGN ..." - +# Priority order: +# 1. CRA_SBOM_SRCS_FILE explicit .c list +# 2. CRA_SBOM_KEIL_PROJECT Keil .uvprojx +# 3. CRA_SBOM_IAR_PROJECT IAR .ewp +# 4. make -n TARGET/SIGN wolfBoot's product-specific default (below) +# +# The shared helper handles 1-3. wolfBoot's Makefile path is product-specific +# (driven by TARGET/SIGN/HASH/EXT_FLASH, not the generic CRA_SBOM_MAKEFILE_DIR / +# compile_commands.json handlers), so we blank those two env vars before calling +# the helper and run our own make -n below when no IDE project is set. _srcs_tmp=$(mktemp "${TMPDIR:-/tmp}/wolfboot-sbom-srcs.XXXXXX") -trap 'rm -f "$_srcs_tmp"' EXIT - -make --no-print-directory \ - -C "$WOLFBOOT_DIR" \ - -n \ - TARGET="$WOLFBOOT_TARGET" \ - SIGN="$WOLFBOOT_SIGN" \ - HASH="$WOLFBOOT_HASH" \ - EXT_FLASH="$WOLFBOOT_EXT_FLASH" \ - 2>/dev/null \ - | grep -oE '[^ ]+\.c' \ - | grep -v '\.h' \ - | sort -u > "$_srcs_tmp" || true - -if [ ! -s "$_srcs_tmp" ]; then - echo "ERROR: make -n yielded no .c source files for TARGET=$WOLFBOOT_TARGET SIGN=$WOLFBOOT_SIGN." >&2 - echo " Check that TARGET and SIGN are valid for this wolfBoot tree." >&2 +_auto_tempfiles="${_auto_tempfiles:-} $_srcs_tmp" + +_saved_makefile_dir="${CRA_SBOM_MAKEFILE_DIR:-}" +_saved_build_dir="${CRA_SBOM_BUILD_DIR:-}" +CRA_SBOM_MAKEFILE_DIR="" +CRA_SBOM_BUILD_DIR="" + +_cra_rc=0 +_cra_extract_srcs "$WOLFBOOT_DIR" "wolfboot" "$_srcs_tmp" || _cra_rc=$? + +CRA_SBOM_MAKEFILE_DIR="$_saved_makefile_dir" +CRA_SBOM_BUILD_DIR="$_saved_build_dir" + +if [ "$_cra_rc" -eq 0 ]; then + _n=$(wc -l < "$_srcs_tmp" | tr -d ' ') + echo " Extracted $_n source files (from IDE project / source list)" +elif [ "$_cra_rc" -eq 2 ]; then + # No IDE project set: use wolfBoot's product-specific make -n extraction. + # + # `make -n TARGET=... SIGN=...` prints every command make would run without + # executing them. The compiler invocations include every .c file on the + # wolfBoot link line, including both core wolfBoot sources and wolfcrypt + # files compiled inline. grep -oE pulls out every .c argument; sort -u + # deduplicates. + echo "Extracting source list via make -n TARGET=$WOLFBOOT_TARGET SIGN=$WOLFBOOT_SIGN ..." + make --no-print-directory \ + -C "$WOLFBOOT_DIR" \ + -n \ + TARGET="$WOLFBOOT_TARGET" \ + SIGN="$WOLFBOOT_SIGN" \ + HASH="$WOLFBOOT_HASH" \ + EXT_FLASH="$WOLFBOOT_EXT_FLASH" \ + 2>/dev/null \ + | grep -oE '[^ ]+\.c' \ + | grep -v '\.h' \ + | sort -u > "$_srcs_tmp" || true + + if [ ! -s "$_srcs_tmp" ]; then + echo "ERROR: make -n yielded no .c source files for TARGET=$WOLFBOOT_TARGET SIGN=$WOLFBOOT_SIGN." >&2 + echo " Check that TARGET and SIGN are valid for this wolfBoot tree." >&2 + exit 1 + fi + + _n=$(wc -l < "$_srcs_tmp" | tr -d ' ') + echo " Extracted $_n source files" +else exit 1 fi -_n=$(wc -l < "$_srcs_tmp" | tr -d ' ') -echo " Extracted $_n source files" - # Resolve paths to absolute: make -n emits relative paths; gen-sbom needs to # open the files to compute gitoid hashes. _srcs_abs_tmp=$(mktemp "${TMPDIR:-/tmp}/wolfboot-sbom-srcs-abs.XXXXXX") -trap 'rm -f "$_srcs_tmp" "$_srcs_abs_tmp"' EXIT +_auto_tempfiles="${_auto_tempfiles:-} $_srcs_abs_tmp" while IFS= read -r _src; do [ -n "$_src" ] || continue @@ -180,7 +222,7 @@ echo " Resolved $_n_abs paths ($(( _n - _n_abs )) non-existent skipped)" # cc -dM -E on the host with wolfBoot's include dirs to produce a flat # #define file that gen-sbom can parse for algorithm enablement. _defines_tmp=$(mktemp "${TMPDIR:-/tmp}/wolfboot-sbom-defines.XXXXXX") -trap 'rm -f "$_srcs_tmp" "$_srcs_abs_tmp" "$_defines_tmp"' EXIT +_auto_tempfiles="${_auto_tempfiles:-} $_defines_tmp" CC=${CC:-cc} echo " Preprocessing build settings via $CC -dM -E ..." @@ -200,6 +242,8 @@ command -v "$_PYTHON" >/dev/null 2>&1 || \ { echo "ERROR: $_PYTHON not found. Set CRA_PYTHON to your Python interpreter." >&2; exit 1; } # Read absolute source paths into positional parameters. +# ponytail: gen-sbom lacks --srcs-file; pass list as positional args +# ceiling: ARG_MAX; upgrade path: SBOM-cgz adds --srcs-file to gen-sbom set -- while IFS= read -r _src; do [ -n "$_src" ] || continue From b6bd384fb6a7851d283b4b61fbc57ef3f95150f0 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 19:29:21 -0700 Subject: [PATCH 23/27] fix: allow GEN_SBOM env override in generate-wolfboot-sbom.sh --- cra-kit/scripts/generate-wolfboot-sbom.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cra-kit/scripts/generate-wolfboot-sbom.sh b/cra-kit/scripts/generate-wolfboot-sbom.sh index 6a354f297..09a9427b5 100755 --- a/cra-kit/scripts/generate-wolfboot-sbom.sh +++ b/cra-kit/scripts/generate-wolfboot-sbom.sh @@ -70,8 +70,8 @@ WOLFBOOT_EXT_FLASH=${WOLFBOOT_EXT_FLASH:-0} OUT_DIR=${CRA_SBOM_OUT_DIR:-"$KIT_DIR/auditor-packet/wolfboot-component"} -# gen-sbom lives inside the wolfssl submodule under wolfBoot. -GEN_SBOM="$WOLFBOOT_DIR/lib/wolfssl/scripts/gen-sbom" +# gen-sbom lives inside the wolfssl submodule under wolfBoot; GEN_SBOM env var overrides. +GEN_SBOM="${GEN_SBOM:-$WOLFBOOT_DIR/lib/wolfssl/scripts/gen-sbom}" if [ ! -f "$GEN_SBOM" ]; then echo "ERROR: gen-sbom not found at $GEN_SBOM" >&2 echo " Ensure the wolfssl submodule is initialized:" >&2 From 44bb6f9de711a6772e508e4ddf6747048f70c430 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 19:34:11 -0700 Subject: [PATCH 24/27] fix: add CRA_WOLFSENTRY_IP_STACK to resolve duplicate basename in wolfsentry SBOM --- cra-kit/scripts/generate-wolfsentry-sbom.sh | 36 +++++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/cra-kit/scripts/generate-wolfsentry-sbom.sh b/cra-kit/scripts/generate-wolfsentry-sbom.sh index a2aaf8c58..b6ea142a2 100755 --- a/cra-kit/scripts/generate-wolfsentry-sbom.sh +++ b/cra-kit/scripts/generate-wolfsentry-sbom.sh @@ -11,6 +11,10 @@ # Optional variables: # CRA_SBOM_OUT_DIR= (default: $KIT_DIR/auditor-packet/wolfsentry-component) # CC= (default: cc; for -dM -E options dump) +# CRA_WOLFSENTRY_IP_STACK=wolfip|lwip|none +# (default: none; selects which optional IP-stack glue +# to include; wolfip/ and lwip/ have identical basenames +# so exactly one can be included per SBOM) set -eu SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) @@ -20,6 +24,18 @@ KIT_DIR=$(dirname "$SCRIPT_DIR") WOLFSENTRY_DIR=${WOLFSENTRY_DIR:-$(cd "$KIT_DIR/../../wolfsentry" 2>/dev/null && pwd || true)} OUT_DIR=${CRA_SBOM_OUT_DIR:-"$KIT_DIR/auditor-packet/wolfsentry-component"} +# CRA_WOLFSENTRY_IP_STACK selects which optional IP stack glue is included. +# Values: wolfip, lwip, none (default: none). +# Why default none: wolfip/ and lwip/ both contain packet_filter_glue.c; +# including both breaks gen-sbom's unique-basename requirement and would +# misrepresent the firmware (only one is compiled per build). +CRA_WOLFSENTRY_IP_STACK="${CRA_WOLFSENTRY_IP_STACK:-none}" + +case "$CRA_WOLFSENTRY_IP_STACK" in + wolfip|lwip|none) ;; + *) echo "ERROR: CRA_WOLFSENTRY_IP_STACK must be wolfip, lwip, or none (got: $CRA_WOLFSENTRY_IP_STACK)" >&2; exit 1 ;; +esac + if [ -z "${WOLFSENTRY_DIR:-}" ] || [ ! -d "$WOLFSENTRY_DIR" ]; then echo "ERROR: wolfSentry source not found." >&2 echo " Set WOLFSENTRY_DIR to your wolfsentry checkout (sibling of wolfssl-examples)." >&2 @@ -69,13 +85,27 @@ echo "gen-sbom: $GEN_SBOM" echo "Outputs: $CDX_OUT" echo " $SPDX_OUT" -# Enumerate all .c sources from $WOLFSENTRY_DIR/src/ (the directory the Makefile -# compiles from; conditional sources like json/ and lwip/ are subdirs of src/). -SRCS=$(find "$WOLFSENTRY_DIR/src" -name "*.c" | sort) +# Core sources: all .c files except the two IP stack subdirs (which have +# duplicate basenames and must be selected individually via CRA_WOLFSENTRY_IP_STACK). +SRCS=$(find "$WOLFSENTRY_DIR/src" -name "*.c" \ + ! -path "*/wolfip/*" \ + ! -path "*/lwip/*" \ + | sort) if [ -z "$SRCS" ]; then echo "ERROR: no .c files found under $WOLFSENTRY_DIR/src/" >&2 exit 1 fi + +# Optionally add the selected IP stack. +if [ "$CRA_WOLFSENTRY_IP_STACK" != "none" ]; then + SRCS="$SRCS +$(find "$WOLFSENTRY_DIR/src/$CRA_WOLFSENTRY_IP_STACK" -name "*.c" | sort)" + echo " IP stack: $CRA_WOLFSENTRY_IP_STACK" +else + echo " NOTE: CRA_WOLFSENTRY_IP_STACK not set; IP glue excluded from SBOM." + echo " Set CRA_WOLFSENTRY_IP_STACK=wolfip or lwip to include it." +fi + _n=$(echo "$SRCS" | wc -l | tr -d ' ') echo "Sources: $_n .c files from $WOLFSENTRY_DIR/src/" From ac8c33114f7f1094c45ed95d65d22594149ee3d7 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 19:44:53 -0700 Subject: [PATCH 25/27] feat: wire --srcs-file and CRA_SBOM_NO_HASH into all product SBOM scripts --- cra-kit/scripts/generate-wolfboot-sbom.sh | 58 +++++++++----- cra-kit/scripts/generate-wolfhsm-sbom.sh | 70 +++++++++-------- cra-kit/scripts/generate-wolfmqtt-sbom.sh | 86 ++++++++++++--------- cra-kit/scripts/generate-wolfsentry-sbom.sh | 82 ++++++++++++-------- cra-kit/scripts/generate-wolfssh-sbom.sh | 56 +++++++------- cra-kit/scripts/generate-wolftpm-sbom.sh | 56 +++++++++----- 6 files changed, 245 insertions(+), 163 deletions(-) diff --git a/cra-kit/scripts/generate-wolfboot-sbom.sh b/cra-kit/scripts/generate-wolfboot-sbom.sh index 09a9427b5..dcc8849d9 100755 --- a/cra-kit/scripts/generate-wolfboot-sbom.sh +++ b/cra-kit/scripts/generate-wolfboot-sbom.sh @@ -28,7 +28,10 @@ # CRA_SBOM_SRCS_FILE=path explicit .c file list (overrides make -n) # CRA_SBOM_KEIL_PROJECT=path auto-extract from Keil .uvprojx (overrides make -n) # CRA_SBOM_IAR_PROJECT=path auto-extract from IAR .ewp (overrides make -n) -# CRA_SBOM_NO_HASH — not yet supported; blocked on SBOM-cgz (gen-sbom --no-artifact-hash) +# CRA_SBOM_NO_HASH=true emit SBOM without an artifact hash, skipping +# the source list — for NDA customers who cannot +# share source lists; WARNING: not suitable for +# production compliance set -eu SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) @@ -53,16 +56,26 @@ if [ -z "${WOLFBOOT_DIR:-}" ] || [ ! -d "$WOLFBOOT_DIR" ]; then exit 1 fi -if [ -z "${WOLFBOOT_TARGET:-}" ]; then - echo "ERROR: WOLFBOOT_TARGET is not set." >&2 - echo " Example: WOLFBOOT_TARGET=stm32h7 $0" >&2 - exit 1 +# CRA_SBOM_NO_HASH skips the make -n source extraction entirely, so TARGET/SIGN +# (which only drive that extraction) are not required in that mode. +if [ "${CRA_SBOM_NO_HASH:-}" = "true" ] || [ "${CRA_SBOM_NO_HASH:-}" = "1" ]; then + _no_hash=1 +else + _no_hash=0 fi -if [ -z "${WOLFBOOT_SIGN:-}" ]; then - echo "ERROR: WOLFBOOT_SIGN is not set." >&2 - echo " Example: WOLFBOOT_SIGN=ECC256 $0" >&2 - exit 1 +if [ "$_no_hash" = "0" ]; then + if [ -z "${WOLFBOOT_TARGET:-}" ]; then + echo "ERROR: WOLFBOOT_TARGET is not set." >&2 + echo " Example: WOLFBOOT_TARGET=stm32h7 $0" >&2 + exit 1 + fi + + if [ -z "${WOLFBOOT_SIGN:-}" ]; then + echo "ERROR: WOLFBOOT_SIGN is not set." >&2 + echo " Example: WOLFBOOT_SIGN=ECC256 $0" >&2 + exit 1 + fi fi WOLFBOOT_HASH=${WOLFBOOT_HASH:-SHA256} @@ -93,7 +106,9 @@ CDX_OUT="$OUT_DIR/wolfboot-${VERSION}.cdx.json" SPDX_OUT="$OUT_DIR/wolfboot-${VERSION}.spdx.json" echo "wolfBoot tree: $WOLFBOOT_DIR" -echo "Configuration: TARGET=$WOLFBOOT_TARGET SIGN=$WOLFBOOT_SIGN HASH=$WOLFBOOT_HASH EXT_FLASH=$WOLFBOOT_EXT_FLASH" +if [ "$_no_hash" = "0" ]; then + echo "Configuration: TARGET=$WOLFBOOT_TARGET SIGN=$WOLFBOOT_SIGN HASH=$WOLFBOOT_HASH EXT_FLASH=$WOLFBOOT_EXT_FLASH" +fi echo "Version: $VERSION" echo "Outputs: $CDX_OUT" echo " $SPDX_OUT" @@ -126,6 +141,8 @@ if [ -n "${CRA_LICENSE_TEXT:-}" ] && [ -f "$CRA_LICENSE_TEXT" ]; then CRA_LICENSE_TEXT=$(CDPATH='' cd -- "$(dirname -- "$CRA_LICENSE_TEXT")" && pwd)/$(basename -- "$CRA_LICENSE_TEXT") fi +# CRA_SBOM_NO_HASH (resolved to $_no_hash above) emits a placeholder checksum +# and skips the source list entirely (NDA customers who cannot share sources). # Extract the configuration-specific source list. # # Priority order: @@ -138,6 +155,7 @@ fi # (driven by TARGET/SIGN/HASH/EXT_FLASH, not the generic CRA_SBOM_MAKEFILE_DIR / # compile_commands.json handlers), so we blank those two env vars before calling # the helper and run our own make -n below when no IDE project is set. +if [ "$_no_hash" = "0" ]; then _srcs_tmp=$(mktemp "${TMPDIR:-/tmp}/wolfboot-sbom-srcs.XXXXXX") _auto_tempfiles="${_auto_tempfiles:-} $_srcs_tmp" @@ -215,6 +233,10 @@ fi _n_abs=$(wc -l < "$_srcs_abs_tmp" | tr -d ' ') echo " Resolved $_n_abs paths ($(( _n - _n_abs )) non-existent skipped)" +else + echo "==> CRA_SBOM_NO_HASH=true: emitting SBOM without artifact hash." + echo " WARNING: not suitable for production CRA compliance." >&2 +fi # Preprocess build settings for gen-sbom --options-h. # @@ -241,16 +263,12 @@ _PYTHON=${CRA_PYTHON:-python3} command -v "$_PYTHON" >/dev/null 2>&1 || \ { echo "ERROR: $_PYTHON not found. Set CRA_PYTHON to your Python interpreter." >&2; exit 1; } -# Read absolute source paths into positional parameters. -# ponytail: gen-sbom lacks --srcs-file; pass list as positional args -# ceiling: ARG_MAX; upgrade path: SBOM-cgz adds --srcs-file to gen-sbom -set -- -while IFS= read -r _src; do - [ -n "$_src" ] || continue - set -- "$@" "$_src" -done < "$_srcs_abs_tmp" - _license_override=${CRA_LICENSE_OVERRIDE:-GPL-3.0-only} +if [ "$_no_hash" = "1" ]; then + set -- --no-artifact-hash +else + set -- --srcs-file "$_srcs_abs_tmp" +fi set -- "$@" \ --cdx-out "$CDX_OUT" \ --spdx-out "$SPDX_OUT" \ @@ -267,7 +285,7 @@ echo "==> Running gen-sbom ..." --supplier "wolfSSL Inc." \ --license-file "$WOLFBOOT_DIR/LICENSE" \ --options-h "$_defines_tmp" \ - --srcs "$@" + "$@" echo "SBOM written:" echo " $CDX_OUT" diff --git a/cra-kit/scripts/generate-wolfhsm-sbom.sh b/cra-kit/scripts/generate-wolfhsm-sbom.sh index ff98e33e6..3fa5e927f 100755 --- a/cra-kit/scripts/generate-wolfhsm-sbom.sh +++ b/cra-kit/scripts/generate-wolfhsm-sbom.sh @@ -20,7 +20,9 @@ # CRA_SBOM_KEIL_PROJECT=path auto-extract from Keil .uvprojx # CRA_SBOM_IAR_PROJECT=path auto-extract from IAR .ewp # CRA_SBOM_MAKEFILE_DIR=path auto-extract via make -n dry-run -# CRA_SBOM_NO_HASH — not yet supported; blocked on SBOM-cgz (gen-sbom --no-artifact-hash) +# CRA_SBOM_NO_HASH=true emit SBOM without an artifact hash (NDA +# customers who cannot share source lists; +# WARNING: not suitable for production compliance) set -eu . "$(dirname "$0")/_cra-sbom-extract.sh" @@ -120,37 +122,43 @@ _python_with_pcpp() { echo "==> Embedded path: gen-sbom with CC -dM -E (no user_settings.h)" -_srcs_file=$(mktemp "${TMPDIR:-/tmp}/wolfhsm-srcs.XXXXXX") -_auto_tempfiles="${_auto_tempfiles:-} $_srcs_file" - -# Allow WOLFHSM_BUILD_DIR to feed compile_commands.json extraction. The shared -# library reads CRA_SBOM_BUILD_DIR; map our wolfHSM-style env var onto it. -CRA_SBOM_BUILD_DIR="${WOLFHSM_BUILD_DIR:-}" - -_cra_rc=0 -_cra_extract_srcs "$WOLFHSM_DIR" "wolfhsm" "$_srcs_file" || _cra_rc=$? +# CRA_SBOM_NO_HASH emits a placeholder checksum and skips the source list +# entirely (for NDA customers who cannot share source lists). +if [ "${CRA_SBOM_NO_HASH:-}" = "true" ] || [ "${CRA_SBOM_NO_HASH:-}" = "1" ]; then + echo " NOTE: CRA_SBOM_NO_HASH=true: emitting SBOM without artifact hash." + echo " WARNING: not suitable for production CRA compliance." >&2 + _hash_arg="--no-artifact-hash" +else + _srcs_file=$(mktemp "${TMPDIR:-/tmp}/wolfhsm-srcs.XXXXXX") + _auto_tempfiles="${_auto_tempfiles:-} $_srcs_file" + + # Allow WOLFHSM_BUILD_DIR to feed compile_commands.json extraction. The + # shared library reads CRA_SBOM_BUILD_DIR; map our env var onto it. + CRA_SBOM_BUILD_DIR="${WOLFHSM_BUILD_DIR:-}" + + _cra_rc=0 + _cra_extract_srcs "$WOLFHSM_DIR" "wolfhsm" "$_srcs_file" || _cra_rc=$? + + if [ "$_cra_rc" -eq 2 ]; then + # No extraction method active: enumerate all wolfHSM C sources via find. + # find is used (not glob) because wolfHSM sources span subdirectories. + find "$WOLFHSM_DIR/src" -name "*.c" | sort > "$_srcs_file" || { + echo "ERROR: find failed on $WOLFHSM_DIR/src" >&2; exit 1 + } + _n=$(wc -l < "$_srcs_file" | tr -d ' ') + echo " Source list: find $WOLFHSM_DIR/src -name '*.c' ($_n files)" + elif [ "$_cra_rc" -ne 0 ]; then + exit 1 + fi -if [ "$_cra_rc" -eq 2 ]; then - # No extraction method active: enumerate all wolfHSM C sources via find. - # find is used (not glob) because wolfHSM sources span subdirectories. - find "$WOLFHSM_DIR/src" -name "*.c" | sort > "$_srcs_file" || { - echo "ERROR: find failed on $WOLFHSM_DIR/src" >&2; exit 1 - } + if [ ! -s "$_srcs_file" ]; then + echo "ERROR: no wolfHSM sources found in $WOLFHSM_DIR/src" >&2 + exit 1 + fi _n=$(wc -l < "$_srcs_file" | tr -d ' ') - echo " Source list: find $WOLFHSM_DIR/src -name '*.c' ($_n files)" -elif [ "$_cra_rc" -ne 0 ]; then - exit 1 -fi - -if [ ! -s "$_srcs_file" ]; then - echo "ERROR: no wolfHSM sources found in $WOLFHSM_DIR/src" >&2 - exit 1 + echo "NOTE: hashed $_n source file(s)" + _hash_arg="--srcs-file $_srcs_file" fi -_n=$(wc -l < "$_srcs_file" | tr -d ' ') -echo "NOTE: hashed $_n source file(s)" - -# ponytail: gen-sbom lacks --srcs-file; pass list as positional args -# ceiling: ARG_MAX; upgrade path: SBOM-cgz adds --srcs-file to gen-sbom # Build license-override args. _license_args="" @@ -180,7 +188,7 @@ if _py=$(_python_with_pcpp); then --user-settings "$SETTINGS_H" \ --user-settings-include "$WOLFHSM_DIR" \ --user-settings-include "$WOLFSSL_DIR" \ - --srcs $(cat "$_srcs_file") \ + ${_hash_arg} \ --cdx-out "$CDX_OUT" \ --spdx-out "$SPDX_OUT" \ ${_license_args} @@ -209,7 +217,7 @@ else --supplier "wolfSSL Inc." \ --license-file "$WOLFHSM_DIR/LICENSING" \ --options-h "$_defines" \ - --srcs $(cat "$_srcs_file") \ + ${_hash_arg} \ --cdx-out "$CDX_OUT" \ --spdx-out "$SPDX_OUT" \ ${_license_args} diff --git a/cra-kit/scripts/generate-wolfmqtt-sbom.sh b/cra-kit/scripts/generate-wolfmqtt-sbom.sh index 93cf45a35..cfae983a8 100755 --- a/cra-kit/scripts/generate-wolfmqtt-sbom.sh +++ b/cra-kit/scripts/generate-wolfmqtt-sbom.sh @@ -21,7 +21,10 @@ # CRA_SBOM_MAKEFILE_DIR=path/to/dir (run `make -n` to extract .c sources) # CRA_SBOM_BUILD_DIR=path/to/build (CMake/ESP-IDF build dir; sources are # read from its compile_commands.json) -# CRA_SBOM_NO_HASH — not yet supported; blocked on SBOM-cgz +# CRA_SBOM_NO_HASH=true (emit SBOM without an artifact hash, +# skipping the source list — for NDA +# customers who cannot share source lists; +# WARNING: not suitable for production compliance) # # Optional variables: # CRA_LICENSE_OVERRIDE= (e.g. LicenseRef-wolfSSL-Commercial) @@ -201,46 +204,51 @@ _run_embedded() { _cra_auto_tempfiles="" trap 'rm -f "$SRCS_LIST" "$EMPTY_OPTS" $_cra_auto_tempfiles' EXIT - # Resolve the source list via the shared extractor (CRA_SBOM_SRCS_FILE, - # Keil/IAR projects, Makefile dry-run, or compile_commands.json). It returns - # 2 when no extraction method is selected, in which case we fall back to the - # default mqtt_*.c glob. - _cra_rc=0 - _cra_extract_srcs "$WOLFMQTT_DIR" "wolfmqtt" "$SRCS_LIST" || _cra_rc=$? + # CRA_SBOM_NO_HASH emits a placeholder checksum and skips the source list + # entirely (for NDA customers who cannot share source lists). + if [ "${CRA_SBOM_NO_HASH:-}" = "true" ] || [ "${CRA_SBOM_NO_HASH:-}" = "1" ]; then + echo " NOTE: CRA_SBOM_NO_HASH=true: emitting SBOM without artifact hash." + echo " WARNING: not suitable for production CRA compliance." >&2 + _count=0 + set -- --no-artifact-hash --cdx-out "$CDX_OUT" --spdx-out "$SPDX_OUT" + else + # Resolve the source list via the shared extractor (CRA_SBOM_SRCS_FILE, + # Keil/IAR projects, Makefile dry-run, or compile_commands.json). It + # returns 2 when no extraction method is selected, in which case we + # fall back to the default mqtt_*.c glob. + _cra_rc=0 + _cra_extract_srcs "$WOLFMQTT_DIR" "wolfmqtt" "$SRCS_LIST" || _cra_rc=$? - if [ "$_cra_rc" -eq 2 ]; then - # No extraction method active: use default glob (all src/mqtt_*.c sorted). - # Why: MQTT has no HAL split, so the full source set is the right default - # for bare-metal builds without a build-system-extractable source list. - echo " Source list: default glob $WOLFMQTT_DIR/src/mqtt_*.c" - for _c in "$WOLFMQTT_DIR"/src/mqtt_*.c; do - [ -f "$_c" ] && echo "$_c" - done | sort > "$SRCS_LIST" - elif [ "$_cra_rc" -ne 0 ]; then - exit 1 - fi - - if [ ! -s "$SRCS_LIST" ]; then - echo "ERROR: no MQTT source files found to hash." >&2 - exit 1 - fi + if [ "$_cra_rc" -eq 2 ]; then + # No extraction method active: use default glob (all src/mqtt_*.c + # sorted). MQTT has no HAL split, so the full source set is the + # right default for bare-metal builds without an extractable list. + echo " Source list: default glob $WOLFMQTT_DIR/src/mqtt_*.c" + for _c in "$WOLFMQTT_DIR"/src/mqtt_*.c; do + [ -f "$_c" ] && echo "$_c" + done | sort > "$SRCS_LIST" + elif [ "$_cra_rc" -ne 0 ]; then + exit 1 + fi - # Pass the resolved sources positionally to gen-sbom's --srcs (it takes a - # space-separated list; argparse stops consuming at the next -- option, so - # --cdx-out/--spdx-out terminate the list cleanly). - # ponytail: gen-sbom lacks --srcs-file; upgrade path: SBOM-cgz - set -- - while IFS= read -r _src; do - [ -n "$_src" ] || continue - if [ ! -f "$_src" ]; then - echo "ERROR: listed source not found: $_src" >&2 + if [ ! -s "$SRCS_LIST" ]; then + echo "ERROR: no MQTT source files found to hash." >&2 exit 1 fi - set -- "$@" "$_src" - done < "$SRCS_LIST" - _count=$# - set -- --srcs "$@" --cdx-out "$CDX_OUT" --spdx-out "$SPDX_OUT" + # Validate every resolved path exists before handing the file to gen-sbom. + _count=0 + while IFS= read -r _src; do + [ -n "$_src" ] || continue + if [ ! -f "$_src" ]; then + echo "ERROR: listed source not found: $_src" >&2 + exit 1 + fi + _count=$((_count + 1)) + done < "$SRCS_LIST" + + set -- --srcs-file "$SRCS_LIST" --cdx-out "$CDX_OUT" --spdx-out "$SPDX_OUT" + fi if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then set -- "$@" --license-override "$CRA_LICENSE_OVERRIDE" if [ -n "${CRA_LICENSE_TEXT:-}" ]; then @@ -257,7 +265,11 @@ _run_embedded() { exit 1 } - echo "NOTE: hashed ${_count} source file(s)" + if [ "${CRA_SBOM_NO_HASH:-}" = "true" ] || [ "${CRA_SBOM_NO_HASH:-}" = "1" ]; then + echo "NOTE: artifact hash omitted (CRA_SBOM_NO_HASH)" + else + echo "NOTE: hashed ${_count} source file(s)" + fi } case "${CRA_SBOM_MODE:-autotools}" in diff --git a/cra-kit/scripts/generate-wolfsentry-sbom.sh b/cra-kit/scripts/generate-wolfsentry-sbom.sh index b6ea142a2..c8f32e251 100755 --- a/cra-kit/scripts/generate-wolfsentry-sbom.sh +++ b/cra-kit/scripts/generate-wolfsentry-sbom.sh @@ -13,8 +13,11 @@ # CC= (default: cc; for -dM -E options dump) # CRA_WOLFSENTRY_IP_STACK=wolfip|lwip|none # (default: none; selects which optional IP-stack glue -# to include; wolfip/ and lwip/ have identical basenames -# so exactly one can be included per SBOM) +# to include in the firmware source set) +# CRA_SBOM_NO_HASH=true emit SBOM without an artifact hash, skipping the +# source list — for NDA customers who cannot share +# source lists; WARNING: not suitable for production +# compliance set -eu SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) @@ -26,9 +29,11 @@ OUT_DIR=${CRA_SBOM_OUT_DIR:-"$KIT_DIR/auditor-packet/wolfsentry-component"} # CRA_WOLFSENTRY_IP_STACK selects which optional IP stack glue is included. # Values: wolfip, lwip, none (default: none). -# Why default none: wolfip/ and lwip/ both contain packet_filter_glue.c; -# including both breaks gen-sbom's unique-basename requirement and would -# misrepresent the firmware (only one is compiled per build). +# Why default none / exactly one: wolfip/ and lwip/ both contain +# packet_filter_glue.c, and a firmware build compiles exactly one IP stack; +# including both would misrepresent what is actually in the firmware. +# (gen-sbom keys its Merkle hash on relative path, not basename, so the two +# same-named files no longer collide -- but a build still uses only one.) CRA_WOLFSENTRY_IP_STACK="${CRA_WOLFSENTRY_IP_STACK:-none}" case "$CRA_WOLFSENTRY_IP_STACK" in @@ -85,35 +90,45 @@ echo "gen-sbom: $GEN_SBOM" echo "Outputs: $CDX_OUT" echo " $SPDX_OUT" -# Core sources: all .c files except the two IP stack subdirs (which have -# duplicate basenames and must be selected individually via CRA_WOLFSENTRY_IP_STACK). -SRCS=$(find "$WOLFSENTRY_DIR/src" -name "*.c" \ - ! -path "*/wolfip/*" \ - ! -path "*/lwip/*" \ - | sort) -if [ -z "$SRCS" ]; then - echo "ERROR: no .c files found under $WOLFSENTRY_DIR/src/" >&2 - exit 1 -fi - -# Optionally add the selected IP stack. -if [ "$CRA_WOLFSENTRY_IP_STACK" != "none" ]; then - SRCS="$SRCS -$(find "$WOLFSENTRY_DIR/src/$CRA_WOLFSENTRY_IP_STACK" -name "*.c" | sort)" - echo " IP stack: $CRA_WOLFSENTRY_IP_STACK" +# CRA_SBOM_NO_HASH emits a placeholder checksum and skips the source list +# entirely (for NDA customers who cannot share source lists). +if [ "${CRA_SBOM_NO_HASH:-}" = "true" ] || [ "${CRA_SBOM_NO_HASH:-}" = "1" ]; then + _no_hash=1 + echo " NOTE: CRA_SBOM_NO_HASH=true: emitting SBOM without artifact hash." + echo " WARNING: not suitable for production CRA compliance." >&2 else - echo " NOTE: CRA_WOLFSENTRY_IP_STACK not set; IP glue excluded from SBOM." - echo " Set CRA_WOLFSENTRY_IP_STACK=wolfip or lwip to include it." + _no_hash=0 + # Core sources: all .c files except the two IP stack subdirs, which are + # selected individually via CRA_WOLFSENTRY_IP_STACK (a build compiles one). + SRCS=$(find "$WOLFSENTRY_DIR/src" -name "*.c" \ + ! -path "*/wolfip/*" \ + ! -path "*/lwip/*" \ + | sort) + if [ -z "$SRCS" ]; then + echo "ERROR: no .c files found under $WOLFSENTRY_DIR/src/" >&2 + exit 1 + fi + + # Optionally add the selected IP stack. + if [ "$CRA_WOLFSENTRY_IP_STACK" != "none" ]; then + SRCS="$SRCS +$(find "$WOLFSENTRY_DIR/src/$CRA_WOLFSENTRY_IP_STACK" -name "*.c" | sort)" + echo " IP stack: $CRA_WOLFSENTRY_IP_STACK" + else + echo " NOTE: CRA_WOLFSENTRY_IP_STACK not set; IP glue excluded from SBOM." + echo " Set CRA_WOLFSENTRY_IP_STACK=wolfip or lwip to include it." + fi + + _n=$(echo "$SRCS" | wc -l | tr -d ' ') + echo "Sources: $_n .c files from $WOLFSENTRY_DIR/src/" fi -_n=$(echo "$SRCS" | wc -l | tr -d ' ') -echo "Sources: $_n .c files from $WOLFSENTRY_DIR/src/" - # Dump compiler defines for --options-h (no user_settings.h; wolfsentry is # configured via Makefile flags, not a settings header). CC=${CC:-cc} _defines_h=$(mktemp "${TMPDIR:-/tmp}/wolfsentry-defines.XXXXXX") -trap 'rm -f "$_defines_h"' EXIT +_srcs_file=$(mktemp "${TMPDIR:-/tmp}/wolfsentry-srcs.XXXXXX") +trap 'rm -f "$_defines_h" "$_srcs_file"' EXIT if ! "$CC" -dM -E -I"$WOLFSENTRY_DIR" -x c /dev/null >"$_defines_h" 2>/dev/null; then echo "ERROR: $CC -dM -E failed; set CC to an available compiler." >&2 exit 1 @@ -124,9 +139,14 @@ if ! command -v python3 >/dev/null 2>&1; then exit 1 fi -# Build --srcs argument list from the source enumeration. -# shellcheck disable=SC2086 -set -- $SRCS +# Build the component-checksum argument: either a placeholder (NO_HASH) or the +# enumerated source list written one path per line for gen-sbom's --srcs-file. +if [ "$_no_hash" = "1" ]; then + set -- --no-artifact-hash +else + printf '%s\n' "$SRCS" > "$_srcs_file" + set -- --srcs-file "$_srcs_file" +fi python3 "$GEN_SBOM" \ --name wolfsentry \ @@ -134,7 +154,7 @@ python3 "$GEN_SBOM" \ --supplier "wolfSSL Inc." \ --license-file "$WOLFSENTRY_DIR/LICENSING" \ --options-h "$_defines_h" \ - --srcs "$@" \ + "$@" \ --cdx-out "$CDX_OUT" \ --spdx-out "$SPDX_OUT" diff --git a/cra-kit/scripts/generate-wolfssh-sbom.sh b/cra-kit/scripts/generate-wolfssh-sbom.sh index d54f9d3fe..e1234bcb5 100755 --- a/cra-kit/scripts/generate-wolfssh-sbom.sh +++ b/cra-kit/scripts/generate-wolfssh-sbom.sh @@ -22,7 +22,10 @@ # CRA_SBOM_KEIL_PROJECT=path (embedded: auto-extract from Keil .uvprojx) # CRA_SBOM_IAR_PROJECT=path (embedded: auto-extract from IAR .ewp) # CRA_SBOM_MAKEFILE_DIR=path (embedded: auto-extract via make -n dry-run) -# CRA_SBOM_NO_HASH — not yet supported; blocked on SBOM-cgz (gen-sbom --no-artifact-hash) +# CRA_SBOM_NO_HASH=true (embedded: emit SBOM without an artifact +# hash, skipping the source list — for NDA +# customers who cannot share source lists; +# WARNING: not suitable for production compliance) # # Optional variables: # CRA_SBOM_OUT_DIR= (output directory; default auditor-packet) @@ -208,34 +211,35 @@ _run_embedded() { exit 1 fi - # Resolve the source list into a temp file (cleaned up by the EXIT trap). - _srcs=$(mktemp "${TMPDIR:-/tmp}/wolfssh-srcs.XXXXXX") || { - echo "ERROR: mktemp failed for the source-list temp file." >&2 - exit 1 - } - _auto_tempfiles="${_auto_tempfiles:-} $_srcs" - _resolve_wolfssh_srcs "$_srcs" - - # Build the positional --srcs argument list from the resolved file. - # ponytail: gen-sbom lacks --srcs-file; pass list as positional args - # ceiling: ARG_MAX; upgrade path: SBOM-cgz adds --srcs-file to gen-sbom - set -- - while IFS= read -r _src; do - [ -n "$_src" ] || continue - if [ ! -f "$_src" ]; then - echo "ERROR: source file does not exist: $_src" >&2 + # CRA_SBOM_NO_HASH emits a placeholder checksum and skips the source list + # entirely (for NDA customers who cannot share source lists). + if [ "${CRA_SBOM_NO_HASH:-}" = "true" ] || [ "${CRA_SBOM_NO_HASH:-}" = "1" ]; then + echo " NOTE: CRA_SBOM_NO_HASH=true: emitting SBOM without artifact hash." + echo " WARNING: not suitable for production CRA compliance." >&2 + set -- --no-artifact-hash --cdx-out "$CDX_OUT" --spdx-out "$SPDX_OUT" + else + # Resolve the source list into a temp file (cleaned up by the EXIT trap). + _srcs=$(mktemp "${TMPDIR:-/tmp}/wolfssh-srcs.XXXXXX") || { + echo "ERROR: mktemp failed for the source-list temp file." >&2 + exit 1 + } + _auto_tempfiles="${_auto_tempfiles:-} $_srcs" + _resolve_wolfssh_srcs "$_srcs" + + # Validate every resolved path exists before handing the file to gen-sbom. + while IFS= read -r _src; do + [ -n "$_src" ] || continue + if [ ! -f "$_src" ]; then + echo "ERROR: source file does not exist: $_src" >&2 + exit 1 + fi + done < "$_srcs" + if [ ! -s "$_srcs" ]; then + echo "ERROR: resolved source list is empty." >&2 exit 1 fi - set -- "$@" "$_src" - done < "$_srcs" - if [ $# -eq 0 ]; then - echo "ERROR: resolved source list is empty." >&2 - exit 1 + set -- --srcs-file "$_srcs" --cdx-out "$CDX_OUT" --spdx-out "$SPDX_OUT" fi - - # Prepend --srcs, then append the trailing options. argparse stops --srcs - # consumption at the next -- option, so --cdx-out / --spdx-out end it cleanly. - set -- --srcs "$@" --cdx-out "$CDX_OUT" --spdx-out "$SPDX_OUT" if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then set -- "$@" --license-override "$CRA_LICENSE_OVERRIDE" if [ -n "${CRA_LICENSE_TEXT:-}" ]; then diff --git a/cra-kit/scripts/generate-wolftpm-sbom.sh b/cra-kit/scripts/generate-wolftpm-sbom.sh index e791118f2..075b74d7d 100755 --- a/cra-kit/scripts/generate-wolftpm-sbom.sh +++ b/cra-kit/scripts/generate-wolftpm-sbom.sh @@ -27,7 +27,9 @@ # CRA_SBOM_IAR_PROJECT= (embedded mode: auto-extract srcs from an IAR .ewp) # CRA_SBOM_MAKEFILE_DIR= (embedded mode: auto-extract srcs via `make -n`) # CRA_SBOM_NO_HASH=true (embedded mode: emit SBOM without a real artifact -# hash; use when no source list is available) +# hash, skipping the source list — for NDA customers +# who cannot share source lists; WARNING: not +# suitable for production compliance) # CRA_TPM_OPTIONS_H=path/to/options.h (embedded mode: flat #define build-config header for # feature enumeration; defaults to # $WOLFTPM_DIR/wolftpm/options.h) @@ -222,13 +224,7 @@ _run_cmake() { set -- "$@" --license-text "$CRA_LICENSE_TEXT" fi fi - # ponytail: gen-sbom lacks --srcs-file; pass list as positional args after --srcs - # ceiling: ARG_MAX on very large source trees; upgrade path: SBOM-cgz - set -- "$@" --srcs - while IFS= read -r _src; do - [ -n "$_src" ] || continue - set -- "$@" "$_src" - done < "$CRA_SBOM_SRCS_FILE" + set -- "$@" --srcs-file "$CRA_SBOM_SRCS_FILE" "$PYTHON3" "$GEN" "$@" } @@ -259,8 +255,39 @@ _run_embedded() { exit 1 fi - # CRA_SBOM_NO_HASH is not yet supported: gen-sbom requires --lib or --srcs and - # has no --no-artifact-hash flag. Blocked on SBOM-cgz. + # CRA_SBOM_NO_HASH emits a placeholder checksum and skips the source list + # entirely (for NDA customers who cannot share source lists). OPTIONS_H is + # still required so the SBOM records the enabled-feature build properties. + if [ "${CRA_SBOM_NO_HASH:-}" = "true" ] || [ "${CRA_SBOM_NO_HASH:-}" = "1" ]; then + echo " NOTE: CRA_SBOM_NO_HASH=true: emitting SBOM without artifact hash." + echo " WARNING: not suitable for production CRA compliance." >&2 + set -- \ + --name wolftpm \ + --version "$VERSION" \ + --supplier "wolfSSL Inc." \ + --license-file "$WOLFTPM_DIR/LICENSE" \ + --options-h "$OPTIONS_H" \ + --no-artifact-hash \ + --cdx-out "$CDX_OUT" \ + --spdx-out "$SPDX_OUT" + if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then + set -- "$@" --license-override "$CRA_LICENSE_OVERRIDE" + if [ -n "${CRA_LICENSE_TEXT:-}" ]; then + set -- "$@" --license-text "$CRA_LICENSE_TEXT" + fi + fi + "$GEN_PY" "$GEN" "$@" || { + echo "ERROR: gen-sbom failed in embedded mode." >&2 + exit 1 + } + for _out in "$CDX_OUT" "$SPDX_OUT"; do + if [ ! -s "$_out" ]; then + echo "ERROR: expected output $_out is missing or empty." >&2 + exit 1 + fi + done + return 0 + fi # Source list, one .c path per line. _srcs=$(mktemp "${TMPDIR:-/tmp}/wolftpm-embedded-srcs.XXXXXX") || { @@ -355,9 +382,6 @@ _run_embedded() { # separate component covered by generate-wolfssl-sbom.sh (embedded mode), and # the wolfSSL SBOM is referenced as a dependency rather than duplicated. - # gen-sbom takes the source list as a positional --srcs vector (exactly one - # of --lib / --srcs is accepted). Build the option vector first, then append - # the collected paths after --srcs so they bind as that argument's nargs list. set -- \ --name wolftpm \ --version "$VERSION" \ @@ -372,11 +396,7 @@ _run_embedded() { set -- "$@" --license-text "$CRA_LICENSE_TEXT" fi fi - set -- "$@" --srcs - while IFS= read -r _src; do - [ -n "$_src" ] || continue - set -- "$@" "$_src" - done < "$_srcs" + set -- "$@" --srcs-file "$_srcs" "$GEN_PY" "$GEN" "$@" || { echo "ERROR: gen-sbom failed in embedded mode." >&2 exit 1 From 0032a3a33ebc3c1064178a39ea31c90ebdbbc951 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 19:53:07 -0700 Subject: [PATCH 26/27] fix: parse wolfsentry version from MAJOR/MINOR/TINY macros --- cra-kit/scripts/generate-wolfsentry-sbom.sh | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/cra-kit/scripts/generate-wolfsentry-sbom.sh b/cra-kit/scripts/generate-wolfsentry-sbom.sh index c8f32e251..8acbed986 100755 --- a/cra-kit/scripts/generate-wolfsentry-sbom.sh +++ b/cra-kit/scripts/generate-wolfsentry-sbom.sh @@ -69,16 +69,17 @@ if [ ! -f "$HEADER" ]; then echo "ERROR: version header not found: $HEADER" >&2 exit 1 fi -VERSION=$(awk ' - /WOLFSENTRY_VERSION_MAJOR/ { maj=$3 } - /WOLFSENTRY_VERSION_MINOR/ { min=$3 } - /WOLFSENTRY_VERSION_TINY/ { tiny=$3 } - END { print maj"."min"."tiny } -' "$HEADER") -if [ -z "$VERSION" ] || [ "$VERSION" = ".." ]; then - echo "ERROR: could not extract version from $HEADER" >&2 - exit 1 -fi +_extract_ver() { + grep -E "^#define[[:space:]]+$1[[:space:]]+[0-9]+" "$HEADER" | awk '{print $3}' +} +_major=$(_extract_ver WOLFSENTRY_VERSION_MAJOR) +_minor=$(_extract_ver WOLFSENTRY_VERSION_MINOR) +_tiny=$(_extract_ver WOLFSENTRY_VERSION_TINY) +VERSION="${_major}.${_minor}.${_tiny}" +case "$VERSION" in + [0-9]*.[0-9]*.[0-9]*) ;; + *) echo "ERROR: could not parse wolfsentry version from $HEADER (got: '$VERSION')" >&2; exit 1 ;; +esac mkdir -p "$OUT_DIR" CDX_OUT="$OUT_DIR/wolfsentry-${VERSION}.cdx.json" From d318dd27093f3d525c7d7e6b3b909a4642ca9739 Mon Sep 17 00:00:00 2001 From: Mark Atwood Date: Mon, 22 Jun 2026 19:54:06 -0700 Subject: [PATCH 27/27] fix: add --options-h to wolfTPM cmake SBOM mode --- cra-kit/scripts/generate-wolftpm-sbom.sh | 26 +++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/cra-kit/scripts/generate-wolftpm-sbom.sh b/cra-kit/scripts/generate-wolftpm-sbom.sh index 075b74d7d..265f16c5c 100755 --- a/cra-kit/scripts/generate-wolftpm-sbom.sh +++ b/cra-kit/scripts/generate-wolftpm-sbom.sh @@ -30,9 +30,11 @@ # hash, skipping the source list — for NDA customers # who cannot share source lists; WARNING: not # suitable for production compliance) -# CRA_TPM_OPTIONS_H=path/to/options.h (embedded mode: flat #define build-config header for -# feature enumeration; defaults to -# $WOLFTPM_DIR/wolftpm/options.h) +# CRA_TPM_OPTIONS_H=path/to/options.h (embedded/cmake mode: flat #define build-config header +# for feature enumeration. Embedded mode defaults to +# $WOLFTPM_DIR/wolftpm/options.h; cmake mode prefers the +# cmake-generated $WOLFTPM_BUILD_DIR/wolftpm/options.h, +# then falls back to the source-tree copy.) # # Optional variables: # CRA_LICENSE_OVERRIDE= (e.g. LicenseRef-wolfTPM-Commercial) @@ -211,11 +213,29 @@ _run_cmake() { exit 1 fi + # gen-sbom requires exactly one of --options-h / --user-settings to enumerate + # enabled features. Prefer the cmake-generated header (reflects the actual + # build config) over the source-tree template; CRA_TPM_OPTIONS_H overrides both. + _options_h="${CRA_TPM_OPTIONS_H:-}" + if [ -z "$_options_h" ] && [ -n "${WOLFTPM_BUILD_DIR:-}" ] && \ + [ -f "$WOLFTPM_BUILD_DIR/wolftpm/options.h" ]; then + _options_h="$WOLFTPM_BUILD_DIR/wolftpm/options.h" + fi + if [ -z "$_options_h" ] && [ -f "$WOLFTPM_DIR/wolftpm/options.h" ]; then + _options_h="$WOLFTPM_DIR/wolftpm/options.h" + fi + if [ -z "$_options_h" ]; then + echo "ERROR: no wolftpm/options.h found." >&2 + echo " Run cmake to generate it, or set CRA_TPM_OPTIONS_H." >&2 + exit 1 + fi + set -- \ --name wolftpm \ --version "$VERSION" \ --supplier "wolfSSL Inc." \ --license-file "$WOLFTPM_DIR/LICENSE" \ + --options-h "$_options_h" \ --cdx-out "$CDX_OUT" \ --spdx-out "$SPDX_OUT" if [ -n "${CRA_LICENSE_OVERRIDE:-}" ]; then