1
0
Fork 0
mirror of https://github.com/imjasonh/esp32 synced 2026-07-06 23:52:24 +00:00
esp32/Cargo.toml

56 lines
1.9 KiB
TOML
Raw Normal View History

[package]
name = "esp32-blinky"
version = "0.1.0"
edition = "2021"
resolver = "2"
rust-version = "1.77"
[[bin]]
name = "esp32-blinky"
harness = false
[profile.release]
opt-level = "s"
lto = true # link-time optimization across all crates
codegen-units = 1 # one CGU = more cross-fn opt at the cost of build speed
strip = true # drop ELF symbols not needed at runtime
panic = "abort" # smaller panic handler; matches build-std=panic_abort
[profile.dev]
debug = true
opt-level = "z"
[features]
default = ["std", "esp-idf-svc/native", "esp-idf-svc/binstart"]
pio = ["esp-idf-svc/pio"]
std = ["esp-idf-svc/std"]
[dependencies]
cloud_log: NVS-loaded config, tracing layer, queue, stub sender First commit of cloud-logging work (logs-plan.md). Foundation only: the actual NTP/JWT/RSA/HTTPS-POST sender is the next commit; this sender is a stub that writes 'would POST' to serial. src/cloud_log.rs: - GcpConfig::load reads the optional 'gcp' NVS namespace. If any required key (project_id / sa_email / sa_key_id / sa_key_pem) is missing, returns Ok(None) — cloud logging is opt-in per device. - LogQueue: Mutex<VecDeque>-backed bounded ring buffer (256 entries), drops oldest when full and surfaces the drop count on the next push. - CloudLogLayer: tracing_subscriber Layer that captures events, extracts structured fields via field::Visit, applies the configured min_severity filter, and pushes onto the queue. Uses wall-clock time when SystemTime::now() is past 2020 (NTP synced); else None so Cloud Logging assigns server-side timestamps. main.rs: - Take NVS first, before any tracing events fire. - If the gcp NVS namespace is populated, install CloudLogLayer as the global tracing subscriber and spawn the sender thread (32 KB stack). - tracing now uses the 'log-always' feature so events still emit log records even with a subscriber installed — keeps EspLogger writing to serial regardless. tools/provision/: - Optional [gcp] section in provisioning.toml. Tool emits the gcp namespace into the NVS CSV when present, validates min_severity spelling early. ota.md: gcp namespace added to NVS schema docs. provisioning.toml.example: commented [gcp] block. Firmware size: 1.52 MB -> 1.60 MB (tracing-subscriber + cloud_log code). Plenty of slot headroom remaining (1.94 MB). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 17:06:00 -04:00
# `log-always` makes tracing emit log records even when a Subscriber is
# set, so EspLogger (a log::Logger) keeps writing to the serial console
# in addition to whatever subscriber layers we install (cloud_log).
tracing = { version = "0.1", features = ["log-always"] }
tracing-subscriber = { version = "0.3", default-features = false, features = ["registry"] }
anyhow = "1"
esp-idf-svc = "0.52"
embedded-svc = "0.29"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sha2 = "0.11"
# `hex::encode` to render SHA digests; sha2 0.11's output type
# (`Array<u8, _>`) doesn't implement `LowerHex` like `GenericArray`
# did, so `format!("{:x}", ...)` no longer works.
hex = "0.4"
Add cosign signature verification (OTA phase 4a) Each OTA fetch is now gated on a Sigstore Bundle signature whose Fulcio cert identifies a member of an allowlist hardcoded in src/trust.rs (currently imjasonh@gmail.com / accounts.google.com). The allowlist cannot be changed via OTA -- only by editing source and reflashing over USB. Publisher (Makefile): make publish now runs cosign sign --yes after the publisher push. Cosign keyless OIDC pops a browser the first time; subsequent signs reuse the cached token within ~10min. Firmware: - src/trust.rs: TRUSTED_IDENTITIES list + bundled Sigstore root and intermediate CA PEMs (trust/fulcio_root.pem, fulcio_intermediate.pem). - src/sig.rs: parse Sigstore Bundle v0.3 (DSSE envelope), verify (a) Fulcio cert SAN email + OID 1.3.6.1.4.1.57264.1.1 issuer match the allowlist, (b) leaf cert chains to bundled Sigstore root via P-384 ECDSA-SHA384, (c) DSSE signature verifies via P-256 ECDSA-SHA256 over the PAE, (d) in-toto Statement subject digest binds to our manifest digest. - src/ota.rs: fetch_manifest now returns the manifest's own SHA256 (the digest cosign signed), not just the parsed body. New fetch_signature_bundle walks the OCI 1.1 referrers layout cosign uses (image index -> inner manifest -> bundle blob). Sig is fetched and verified before the firmware download starts. Crates: p256, p384, x509-cert (with pem feature), base64. Adds ~350 KB to the firmware -- repartitioned ota slots from 1.5MB to 1.75MB to fit (USB-only migration). Bumped OTA thread stack to 48KB for cert-parsing headroom. Verified end-to-end: Jason signed :latest with cosign keyless, device polled, all four verification steps passed (identity, chain, DSSE sig, in-toto binding), then downloaded and applied as before. Phase 4b (Rekor SET / transparency log inclusion proof) and 4c (operational hardening) remain. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 13:50:03 -04:00
# Phase 4a: cosign Sigstore Bundle verification.
p256 = { version = "0.13", features = ["ecdsa"] } # leaf signs DSSE
p384 = { version = "0.13", features = ["ecdsa"] } # intermediate signs leaf
x509-cert = { version = "0.2", features = ["pem"] }
base64 = "0.22"
cloud_log: real sender — JWT, RSA-SHA256, OAuth2, POST to logging.googleapis.com Replaces the stub eprintln sender with the real implementation: - mint_access_token: builds + signs a service-account JWT (RS256 over base64url(header).base64url(claims)), POSTs to oauth2.googleapis.com with the standard grant_type=...jwt-bearer form body, parses the access_token + expires_in. - CachedToken: held in the sender thread's local state. Refreshes 300 seconds before expiry (Google issues 1h tokens; we re-mint at T+55min). - post_batch: serializes the WriteEntriesRequest with logName, resource (generic_node + project + MAC node_id), and entries (severity + jsonPayload + optional timestamp), POSTs to logging.googleapis.com/v2/entries:write with the bearer. - Backoff on failure: sleep doubles on each consecutive failure (capped at 5 min). Batches dropped on POST failure rather than re-enqueued; loss is surfaced via dropped_before on the next entry. Crates added: rsa 0.9 (with sha2 feature for AssociatedOid; PKCS#1 v1.5 needs the DigestInfo prefix), time 0.3 (RFC3339 formatting). main.rs: SNTP startup is now gated on gcp.is_some(). The JWT auth needs a real wall-clock for / (Google rejects ~5min skew), so devices with cloud logging pay a one-time ~few-second sync at boot. Devices without [gcp] skip it. Log entry timestamps themselves are still optional — when omitted, GCP server-side assigns them. Cloud Logging severity mapping: tracing TRACE/DEBUG -> DEBUG, INFO -> INFO, WARN -> WARNING, ERROR -> ERROR (matches GCP's LogSeverity enum). device_mac() reads via esp_efuse_mac_get_default and uses the lower 6 bytes as the node_id resource label, so multi-device logs can be filtered by MAC. Firmware size: 1.60 MB -> 1.71 MB (+rsa, +time, +sender code). Still under the 1.94 MB slot. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 17:13:03 -04:00
# Cloud Logging: RSA-PKCS#1v1.5 SHA-256 signing for service-account JWTs.
rsa = { version = "0.9", default-features = false, features = ["std", "pem", "sha2"] }
# RFC 3339 timestamp formatting for Cloud Logging entries.
time = { version = "0.3", default-features = false, features = ["std", "formatting"] }
[build-dependencies]
embuild = "0.33"