feat(assets): brouiller les blocs embarques dans l'exe

Le frontend etait lisible dans l'onglet Image de Process Explorer, avec
les commentaires. BuildAssets.ps1 brouille chaque fichier avant brcc32,
PM.EmbeddedAssets le debrouille au moment de servir.

C'est de l'opacite, pas du secret : la cle est dans l'exe. Ca supprime
strings et l'onglet Image ; la memoire, un debogueur et la reponse HTTP
rendent toujours le texte. D'ou XOR plutot qu'AES — a securite egale
(nulle contre qui a l'exe), dix lignes, et aucune force affichee qu'on
n'a pas.

Une magie PMA1 prefixe le clair : sans elle, deux constantes qui derivent
serviraient du binaire au navigateur, ecran blanc sans trace.

Verifie en comparant les flux des deux cotes bloc par bloc, puis par
aller-retour sur app.js, index.html et style.css. Le frontend ne se
trouve plus dans le binaire.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-09-05 17:17:15 +01:00
parent 8f0bbb580f
commit 427d294b22
4 changed files with 195 additions and 13 deletions
+38
View File
@@ -22,6 +22,44 @@ Toute modif `index.html` / `js/` / `css/` nécessite :
Sans étape 1, l'exe embarque l'ancienne version des assets — le bug le
plus courant après modif frontend.
### Blocs embarqués brouillés
Depuis 2026-09-05, `BuildAssets.ps1` **brouille** chaque fichier avant de le
donner à `brcc32` (dans `assets/obf/`, gitignoré ; les sources en clair ne sont
jamais touchées) et `PM.EmbeddedAssets.DeobfuscateAsset` le débrouille au moment
de servir.
```
bloc i = SHA256( secret(32) || nom de ressource UTF-8 || i en 4 octets LE )
sortie = XOR( magie 'PMA1' + contenu du fichier )
```
- **C'est de l'opacité, pas du secret.** La clé est dans l'exe : qui tient le
binaire retrouve le texte. Ce que ça supprime, c'est l'onglet *Image* de
Process Explorer et n'importe quel `strings` — le frontend n'est plus lisible
en survolant le fichier. L'onglet *Memory*, un débogueur ou la réponse HTTP le
rendent toujours : WebView2 doit recevoir du JavaScript en clair pour
l'exécuter. D'où **XOR plutôt qu'AES** : à sécurité rigoureusement égale
(nulle, contre qui a l'exe), dix lignes au lieu d'un appel CNG, et surtout
aucune force affichée qu'on n'a pas.
- Le flux dépend du **nom de la ressource** : tous les fichiers commencent par
les mêmes lignes de commentaire, un flux commun rendrait ces préfixes
identiques d'un bloc à l'autre.
- **La magie `PMA1` préfixe le clair.** Sans elle, une clé qui a dérivé entre le
script et l'exe servirait du binaire au navigateur : écran blanc, aucune
trace. Avec elle, `TryServeEmbedded` refuse. **Symptôme à reconnaître :
`/index.html` en 404 juste après avoir touché `AssetObfSecret` d'un seul
côté** — les deux constantes (`$ObfSecret` / `AssetObfSecret`) doivent rester
identiques.
- **Coût mesuré : 2,45 ms** pour les 820 Ko embarqués (AES chronométré comme
borne haute ; le XOR est plus rapide), une seule fois au chargement de la
page. À comparer aux ~650 ms d'Argon2id au déverrouillage.
- **Les tests frontend ne sont pas concernés** : ils lisent les `.js` sur le
disque, jamais le `.res`.
- Vérifié en comparant les flux des deux côtés bloc par bloc (ils doivent être
identiques au bit près) puis en refaisant l'aller-retour sur `app.js`,
`index.html` et `style.css`.
### Découpage frontend (§3.1, en cours)
`app.js` (~12k lignes) est **progressivement scindé** en fichiers classic-script