Skip to content

Commit f057d48

Browse files
committed
v2.4.0: Hidden Space, WiFi sharing, Auto-Send (Enter)
1 parent 663feb3 commit f057d48

47 files changed

Lines changed: 4693 additions & 342 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
# Changelog
22

3+
## [2.4.0] — May 2026
4+
### Added
5+
- **Hidden Space** — second independent encrypted vault selectable at boot via
6+
alternate PIN. Each space has isolated TOTP keys, passwords, web cabinet
7+
credentials, WiFi config, BLE PIN, Device BLE PIN, Duress PIN, display theme,
8+
startup mode, and HID mode. Spaces share only hardware-level settings: boot mode,
9+
RTC config, display settings, web server timeout, session duration, AP password,
10+
BLE device name, and mDNS hostname. Space B web cabinet hides all shared settings.
11+
- **WiFi sharing** — optional: share Space A WiFi credentials with Space B
12+
via re-encryption with a chip-derived key (configurable in web cabinet,
13+
Space A context only).
14+
- **Auto-Send (Enter)** — new per-password flag in Password Manager; when enabled, device automatically presses Enter via BLE/USB HID after typing the password. Configurable per entry in web cabinet and offline decrypt tool. ENT badge shown on device display.
15+
16+
### Fixed
17+
- `quickUpdateCategory()` — quick category change via badge pill now preserves `auto_send` flag (previously reset to false on category switch)/
18+
- Name of key file obfuscated
19+
- PROGMEM copy bug(first web server open wasnt loading, loaded after page refresh)
20+
321
## [2.3.0] — May 2026
422
### Security Enhancements
523
- **Duress PIN with memory zeroing** — multi-layer duress PIN triggers advanced memory

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,14 @@
182182
- PIN protection for BLE transmission
183183
- Encrypted export/import for backup and migration
184184

185+
### 🔒 Hidden Space
186+
- **Two independent vaults** — alternate PIN at boot unlocks a fully isolated
187+
second space with its own TOTP keys, passwords, and web cabinet account
188+
- **Optional WiFi sharing** — Space B can inherit Space A WiFi credentials
189+
with one toggle; disabled by default
190+
- **Full isolation** — separate BLE PIN, web admin credentials, and session
191+
per space; spaces cannot read each other's data
192+
185193
### 🌐 Web Management Interface
186194
- Runs on the device itself — no cloud, no external servers
187195
- Full TOTP and password management from any browser
@@ -216,6 +224,8 @@ All sensitive data is encrypted with AES-256 using a unique per-device key deriv
216224

217225
**Device security:** PIN with persistent lockout (5 attempts across reboots), secure memory wipe before deep sleep, encrypted BLE pairing.
218226

227+
**Hidden Space:** two-slot device key file; Space B unlocked only by its own PIN via independent PBKDF2 derivation; wipe zeroes slot and deletes all HMAC-derived files.
228+
219229
### Known Limitations
220230
- PBKDF2 iteration count (25,000) is below OWASP 2023 recommendations due to ESP32 hardware constraints
221231
- No hardware secure enclave or secure boot by default
@@ -297,6 +307,7 @@ pio run -e lilygo-t-display-s3 -t upload
297307
| [Logging System](docs/development/LOGGING_SYSTEM.md) | Debug and log configuration |
298308
| [Multi-Board Support](docs/development/multi-board.md) | Internal multi-board development rules (for maintainers) |
299309
| [Hardware Porting Guide](docs/development/PORTING.md) | Port SecureGen to your own ESP32 board |
310+
| [Abandoned Ideas](docs/development/abandoned-ideas.md) | Rejected features and architectural decisions |
300311

301312
---
302313

@@ -311,6 +322,7 @@ pio run -e lilygo-t-display-s3 -t upload
311322
device; ephemeral key derived on-device, never entered manually
312323
- Flash encryption and secure boot (optional hardening)
313324
- ATECC608 secure element support
325+
- SD Card Module Support for pin code + cryptokey unlock feature
314326

315327
### Cryptography
316328
- Migration ECDH P-256 → X25519 (~400ms → ~80ms key exchange)

decrypt_export.html

Lines changed: 13 additions & 5 deletions
Large diffs are not rendered by default.

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
- `url-obfuscation.md` — URL obfuscation layer internals, epoch rotation, endpoint registration checklist.
1515
- `multi-board.md` — Multi-board development rules. Three types of platform differences, scaling checklist, golden rule.
1616
- `PORTING.md` — Hardware porting guide. Step-by-step instructions for adapting SecureGen firmware to new ESP32/S3 boards. Covers hardware requirements, board header creation, platformio.ini configuration, capability flags (USB HID, PSRAM), deep sleep wake pins, display geometry, security checklist, and common mistakes to avoid.
17+
- `abandoned-ideas.md` — Rejected features and architectural decisions. Documents approaches that were considered but abandoned, with reasoning and alternatives.
1718

1819
#### development/boards/
1920
Hardware reference for supported targets. Each file covers: MCU, display, pinout, USB/flashing specifics, known quirks.

docs/development/ENDPOINTS.md

Lines changed: 112 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,26 +112,58 @@ Returns password list metadata. Passwords are not included — use `/api/passwor
112112
"name": "Gmail",
113113
"category": "web",
114114
"strength": 2,
115-
"pw_hash": "a1b2c3d4"
115+
"pw_hash": "a1b2c3d4",
116+
"auto_send": false
116117
}
117118
]
118119
}
119120
```
120121

121-
Fields: `name` (string), `category` ("web"|"app"|"local"|"key"|""), `strength` (0–3), `pw_hash` (first 8 bytes of SHA-256, hex, for duplicate detection). Passwords are never included in the list response — use `/api/passwords/get`.
122+
| Field | Type | Description |
123+
|-------|------|-------------|
124+
| name | string | Password entry name |
125+
| category | string | Category: "web", "app", "local", "key", or "" |
126+
| strength | int | Password strength: 0=unknown, 1=weak, 2=medium, 3=strong |
127+
| pw_hash | string | First 8 bytes of SHA-256 (hex) for duplicate detection |
128+
| auto_send | bool | Whether Enter is sent after password output |
129+
130+
Passwords are never included in the list response — use `/api/passwords/get`.
122131

123132
### POST /api/passwords/add 🔐 🛡️ 🔒
124-
Request: `{ "name": "...", "password": "...", "category": "web" }`
133+
Request: `{ "name": "...", "password": "...", "category": "web", "auto_send": false }`
134+
135+
| Parameter | Type | Default | Description |
136+
|-----------|------|---------|-------------|
137+
| name | string | required | Password entry name |
138+
| password | string | required | Password value |
139+
| category | string | "" | Category: "web", "app", "local", "key", or "" |
140+
| auto_send | bool | false | Automatically press Enter after HID output |
125141

126-
Note: `category` optional — omit or pass "" for no category.
142+
Note: `category` and `auto_send` are optional.
127143

128144
### POST /api/passwords/get 🔐 🛡️ 🔒
129145
Returns the plaintext password for one entry.
130146
Request: `{ "index": 0 }`
131-
Response: `{ "success": true, "password": "..." }`
147+
Response: `{ "success": true, "password": "...", "name": "...", "category": "web", "auto_send": false }`
148+
149+
| Response Field | Type | Description |
150+
|----------------|------|-------------|
151+
| success | bool | Operation status |
152+
| password | string | Plaintext password value |
153+
| name | string | Password entry name |
154+
| category | string | Category: "web", "app", "local", "key", or "" |
155+
| auto_send | bool | Whether Enter is sent after password output |
132156

133157
### POST /api/passwords/update 🔐 🛡️ 🔒
134-
Request: `{ "index": 0, "name": "...", "password": "...", "category": "web" }`
158+
Request: `{ "index": 0, "name": "...", "password": "...", "category": "web", "auto_send": false }`
159+
160+
| Parameter | Type | Default | Description |
161+
|-----------|------|---------|-------------|
162+
| index | int | required | Password entry index |
163+
| name | string | required | Password entry name |
164+
| password | string | required | Password value |
165+
| category | string | "" | Category: "web", "app", "local", "key", or "" |
166+
| auto_send | bool | false | Automatically press Enter after HID output |
135167

136168
### POST /api/passwords/delete 🔐 🛡️ 🔒
137169
Request: `{ "index": 0 }`
@@ -318,6 +350,80 @@ Request: `{ "new_password": "..." }`
318350

319351
---
320352

353+
## Hidden Space
354+
355+
### GET /api/hidden_space 🔐 🔒
356+
357+
Returns current hidden space status. Response differs by active space.
358+
359+
**Space A response:**
360+
```json
361+
{
362+
"hidden_space_enabled": true,
363+
"current_space": "A",
364+
"can_enable": true,
365+
"can_disable": true,
366+
"share_wifi": false
367+
}
368+
```
369+
370+
**Space B response:**
371+
```json
372+
{
373+
"hidden_space_enabled": true,
374+
"current_space": "B",
375+
"can_enable": false,
376+
"can_disable": true,
377+
"share_wifi": false
378+
}
379+
```
380+
381+
`hidden_space_enabled` reflects whether Space B is provisioned (sentinel
382+
file detected). `share_wifi` is only meaningful in Space A context.
383+
384+
### POST /api/hidden_space 🔐 🛡 🔒
385+
386+
Manages hidden space lifecycle. All actions require Space A context except
387+
`disable` (which requires Space B context).
388+
389+
**Action: enable**
390+
Writes `/.setup_hidden_space` trigger file and restarts the device.
391+
On next boot, PIN entry flow creates Space B slot.
392+
393+
```json
394+
{ "action": "enable" }
395+
```
396+
397+
**Action: disable**
398+
Wipes Space B: overwrites slot B with random bytes, deletes all
399+
HMAC-derived data files, removes sentinel and shared cache.
400+
Must be called from Space B context.
401+
402+
```json
403+
{ "action": "disable" }
404+
```
405+
406+
**Action: set_share_wifi**
407+
Copies Space A WiFi credentials re-encrypted with chip-derived key to
408+
`/.conn_cache`. Space B WifiManager uses this file as fallback if its own
409+
credentials are absent. Calling with `enabled: false` removes the cache file.
410+
Space A context only.
411+
412+
```json
413+
{ "action": "set_share_wifi", "enabled": true }
414+
```
415+
416+
Response (all actions on success):
417+
```json
418+
{ "status": "ok" }
419+
```
420+
421+
> **Registration:** this endpoint follows the 6-location checklist
422+
> (direct handler, two tunnel dispatchers, obfuscation manager,
423+
> `shouldTunnelEndpoint`, `shouldSecureEndpoint`).
424+
425+
---
426+
321427
## Utility
322428

323429
### GET /api/csrf_token 🔐 🔒

0 commit comments

Comments
 (0)