(Vorwerk “TeaCaps” for the Temial brewing machine)
Last update: 2025‑05‑02
This is a work in progress to give owners of Temial machines more control over their devices.
There is no affiliation with Vorwerk.
- Printable tag always starts with
VORWERK2…
→ remove that 8‑byte ASCII prefix. - Remainder is Crockford Base‑32 (digits 2–7, letters A‑Z, no
I,O,U,0,1
).
After decoding you get 52 bytes payload + 4 bytes trailer = 56 bytes. - Byte map (little‑endian indices):
off | len | value | status | notes |
---|---|---|---|---|
0 | 2 | 01 38 |
const | format version |
2 | 1 | tea‑type id | ✅ decoded | see §2.2 |
3‑4 | 2 | raw steep‑time | ||
5 | 1 | max‑infusions (low nibble) | ||
6 | 1 | flags | bit 0 = pre‑rinse (“wake the tea”) | |
7‑10 | 4 | reserved? | const 00 25 2B 24 in all samples | |
11‑50 | 40 | trailer | ❓ capsule‑unique nonce + MAC | |
51‑55 | (―) | Base‑32 ECC bits | handled by QR layer |
- Trailer is not a simple CRC; forging requires the secret key in the brewer’s MCU.
import base64, struct
PREFIX = "VORWERK2"
FMT = b"\x01\x38"
TEA_ID = {
0x7F: "Schwarztee",
0xC4: "Früchtetee",
0xE4: "Gelbtee",
0xAD: "Grüntee",
0xF2: "Kräutertee",
0xEC: "Oolongtee",
0x05: "Rote Kraeuter",
}
def _b32(tag):
body = tag[len(PREFIX):]
body += "=" * ((8 - len(body) % 8) % 8)
return base64.b32decode(body, casefold=True)
def decode(tag):
raw = _b32(tag)
if raw[:2] != FMT:
raise ValueError("bad header")
return {
"tea" : TEA_ID.get(raw[2], f"0x{raw[2]:02X}"),
"steep_raw" : struct.unpack("<H", raw[3:5])[0],
"infusions" : raw[5] & 0x0F,
"wake" : bool(raw[6] & 0x01),
"trailer" : raw[11:].hex(),
}
### 2.1 Fixed Part (01 38
)
Two‑byte little‑endian constant found in all observed capsules.
Speculative meaning: 0x3801
= format→0x01, origin→0x38 (Vorwerk internal code).
### 2.2 Tea‑Type Byte Confirmed mapping from eight genuine tags:
id (hex) | tea |
---|---|
7F | Schwarztee |
C4 | Früchte |
E4 | Gelb |
AD | Grün |
F2 | Kräuter |
EC | Oolong |
05 | (unlabelled yellow‑tea batch) |
### 2.3 Steep‑Time (raw)
Two bytes, little‑endian.
Scaling factor still unknown; empirical pairs suggest raw ≈ seconds × 520 / 5 but more data needed.
### 2.4 Max‑Infusions
Low nibble (0x0–0xF
) matches pack text (1–4).
High nibble always 0x0
.
### 2.5 Flags (offset 6)
Bit 0 → “wake the tea / pre‑rinse” (TRUE on Gelb, Grün, Oolong).
Bits 1‑7 constant 0 so far.
### 2.6 Trailer (40 bytes)
Different for every capsule, even when the recipe bytes (0‑10) are identical.
Probable structure:
11‑14 per‑capsule nonce / counter
15‑? truncated MAC( header || recipe || nonce, secret_key )
Cryptanalysis impossible without brewer firmware or hundreds of same‑machine tags.
- Length – 40 bytes ≫ 4‑byte CRC.
- No common seed – six‑equation GF(2) solver finds no constant.
- Same recipe, different trailer – CRC would stay constant.
Route | Hardware needed | Effort | Notes |
---|---|---|---|
MCU flash dump | 1 × SWD/J‑Link, hot‑air access | ★★★ | Temial uses STM32F4 (unconfirmed) → non‑secure read‑out if RDP level 0. |
Voltage glitch + SWD unlock | Crowbar / ChipWhisperer | ★★★★ | If RDP level 1 set. |
Black‑box brute force | QR printer + camera | ☆☆☆ | Infeasible (2³²–2⁶⁴ space) |
Side‑channel power analysis | high‑speed probe | ★★★★★ | Last resort. |
German “Reparatur‑Klausel” (§ 69 e UrhG) legitimises firmware reverse‑engineering for interoperability.
- 2025‑05‑02 – Corrected header to 2 bytes
01 38
; added batch pair analysis; new byte map. - 2025‑05‑01 – Initial guess (7‑byte header + 4‑byte CRC) – now obsolete but kept here for history.
- Exact scaling of
steep_raw
and water volume bytes. - Full TEA_ID table (need more capsule types).
- Trailer format: locate nonce length, MAC algorithm, key storage.
- Can the brewer be convinced to accept a tag with empty trailer for DIY mode?
Pull requests with new dumps or scope‑pics are very welcome.