Doridian / Doridian/OpenBambuAPI
Signing canonicalization corrections + live-verified project_file findings (LAN http url, S3 presigned rejection, clean_print_error, md5 semantics)
- Dominant language
- No language data
- Stars
- 680
- Forks
- 96
- PR merge metrics
- No merged PRs in 30d
Description
# Corrections + new findings from live 2026-firmware P2S testing (signed commands, project_file)
Verified against a **Bambu Lab P2S on 2026 firmware, cloud mode** (X.509 app-cert route). Posting corrections to `cloud-x509-auth.md` plus several undocumented behaviors we confirmed live. Full write-up + tooling: https://github.com/Snail3D/bamboo-sense/blob/main/docs/CRACK.md
## 1. The signing canonicalization in cloud-x509-auth.md is wrong (sorted keys)
The doc's sorted-keys recipe does not verify. What actually works (confirmed by byte-level variant testing against the printer's error codes, and consistent with the decompiled signer in Randomblock1/bambu_connect_disasm):
- **Sign `JSON.stringify(envelope_without_header)` in original insertion order** — `"print"` (or `"security"`/`"system"`) object first, `"user_id"` **trailing, outside** the command object
- `sequence_id` must be a **number**, not a string (string sequence_ids change the error signature)
- `header` is then **spliced in before the final `}`** of the signed bytes: `signed_bytes[:-1] + ',"header":{...}' + '}'`
- `payload_len` = UTF-8 byte length of the signed string
- sig = base64(RSA-SHA256 PKCS1v15 over the signed bytes)
- cert_id = 32-hex-char leaf serial + issuer CN, e.g. `5A5A…CN=GLOF3813734089.bambulab.com` (issuer CN, not the leaf CN)
Useful error-code map (from the `security` result path):
| reason / shape | meaning |
|---|---|
| no `header` at all | rejected before parsing |
| `0x05024009`-family | malformed envelope |
| `0x0502400A`-family | header present, signature doesn't verify (wrong canonical form, wrong cert_id, or cert not installed) |
## 2. `print.project_file` — fully working recipe + the URL gotcha nobody documents
```json
{"print":{"sequence_id":,"command":"project_file","param":"Metadata/plate_1.gcode",
"url":"","md5":"","project_id":"0","profile_id":"0",
"task_id":"0","subtask_id":"0","use_ams":true,"ams_mapping":[],
"bed_leveling":true,"flow_cali":false,"vibration_cali":true,"layer_inspect":true,
"timelapse":false},"user_id":""}
```
- `md5` is the **md5 of the whole 3mf file**, NOT the `Metadata/plate_1.gcode.md5` stored inside it. Wrong md5 ⇒ task aborts in PREPARE with `print_error` 83902527 (0x0500403F), before any heating.
- `url` works as **plaintext**; `url_enc` (RSA to printer_cert) is accepted but not required on this firmware.
- `use_ams:false` makes the printer pull from the **external spool holder**; if that's empty it fails with HMS_0300-0200 (filament ran out). `ams_mapping` = tray index list, one entry per filament.
### The big one: `url` can be a plain LAN `http://` URL on a cloud-mode printer
We started a real print on a cloud-mode (not LAN-only) P2S by handing `project_file` a bare `http://192.168.x.x:8477/model.gcode.3mf` served by a laptop. Printer did HEAD + GET and pulled 18.7 MB over LAN. **Cloud-mode printer, zero Bambu cloud involvement, no SD card, no LAN-only mode.**
### The trap: presigned S3 URLs are rejected in PREPARE
Upload via `v1/iot-service/api/user/upload` (PUT 200) and hand the returned `https://s3.us-west-2.amazonaws.com/or-cloud-upload-prod/...?AWSAccessKeyId=...&Signature=...` URL to `project_file`: command is **ACKed SUCCESS**, printer shows PREPARE, then fails ~75 s later with `print_error` 83902527, no heating, HMS_0100-0100. Reproduced 3×. The firmware evidently only fetches from allow-listed hosts. If you see "ACK SUCCESS → PREPARE → FAILED 0x0500403F": it's the URL, not your signature/md5/AMS.
## 3. Undocumented `print.clean_print_error` command
`{"print":{"command":"clean_print_error","sequence_id":N}}` (signed normally) is accepted and **clears the HMS alarm list** (verified: HMS 0x10001 entry vanished). `gcode_state` remains `FAILED` as a sticky last-task-outcome field (same way FINISH persists). Notably, a LAN-url `project_file` still **starts while `gcode_state` reads FAILED** — the "ERROR STATE" refusals we saw were tied to the S3-url attempts, not to the latch. `system.restart`/`system.reboot` over MQTT are silently ignored.
## 4. State machine notes
- Signed `stop` during RUNNING aborts instantly (verified at 238 °C nozzle).
- `stop` from FINISH returns SUCCESS but is a no-op; `resume` from FAILED returns FAIL.
- New `project_file` while FAILED: depends on URL validity — LAN urls launched fine, S3 urls got `{"result":"FAIL","reason":"ERROR STATE"}`.
Happy to turn any of this into PRs against the relevant docs if you want — the full recipe, scripts and the dongle implementation (ESP32-S3 signing vault that installs its own cert via `app_cert_install` and signs commands on-chip) are MIT-licensed at Snail3D/bamboo-sense.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with cloud-x509-auth.md and the live-testing write-up at docs/CRACK.md. Compare the canonicalization, project_file, clean_print_error, URL, md5, and state-machine findings against the existing documentation. Done means the corrections and verified behaviors are documented with the relevant recipes and caveats.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws
- Domain
- api, documentation, security
- Issue type
- Documentation
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 70/100