136 lines
3.5 KiB
Markdown
136 lines
3.5 KiB
Markdown
# Client integration
|
|
|
|
Your current client already has a custom `PackLib` using `zstd` and
|
|
`XChaCha20`. This project should become the next iteration, not a parallel dead
|
|
tool.
|
|
|
|
## Suggested migration
|
|
|
|
1. Keep the current `PackManager` interface stable.
|
|
2. Add a new loader beside the current pack code, for example:
|
|
- `src/PackLib/M2PackArchive.h`
|
|
- `src/PackLib/M2PackArchive.cpp`
|
|
3. Route `CPackManager::AddPack()` by extension:
|
|
- legacy format for old `.pack`
|
|
- new format for `.m2p`
|
|
4. Embed only the signing public key in the client.
|
|
5. Resolve the content decryption key from runtime delivery:
|
|
- launcher-provided memory
|
|
- machine-bound cache
|
|
- or a derived release secret
|
|
|
|
## Client key header
|
|
|
|
For the current implementation, the client expects:
|
|
|
|
- `src/PackLib/M2PackKeys.h`
|
|
|
|
Generate it from the release key material with:
|
|
|
|
```bash
|
|
m2pack export-client-config \
|
|
--key keys/master.key \
|
|
--public-key keys/signing.pub \
|
|
--key-id 1 \
|
|
--output /path/to/m2dev-client-src/src/PackLib/M2PackKeys.h
|
|
```
|
|
|
|
That keeps loader constants aligned with the archive builder.
|
|
|
|
Important:
|
|
|
|
- the generated client header no longer embeds the real master key
|
|
- `.m2p` loading now requires a runtime master key
|
|
- the runtime master key must match the archive `key_id`
|
|
- if a `.m2p` file exists and fails validation or runtime key resolution, the client should not silently fall back to `.pck`
|
|
|
|
## Runtime validation
|
|
|
|
Minimum validation:
|
|
|
|
- verify header magic and version
|
|
- compare manifest hash
|
|
- verify manifest signature
|
|
- reject duplicate paths
|
|
- reject path traversal
|
|
- verify AEAD tag before decompression
|
|
- verify plaintext hash only in debug or explicit strict mode
|
|
|
|
## Launcher key delivery
|
|
|
|
The client now supports runtime key overrides and no longer has to rely only on
|
|
the compiled fallback key.
|
|
|
|
Supported inputs, highest priority last:
|
|
|
|
1. shared memory mapping
|
|
2. environment variables
|
|
3. command line arguments
|
|
|
|
### Command line
|
|
|
|
```text
|
|
--m2pack-key-hex <64-hex-master-key>
|
|
--m2pack-pubkey-hex <64-hex-public-key>
|
|
--m2pack-key-map <mapping-name>
|
|
--m2pack-key-id <integer>
|
|
--m2pack-strict-hash [0|1]
|
|
```
|
|
|
|
### Environment
|
|
|
|
```text
|
|
M2PACK_MASTER_KEY_HEX
|
|
M2PACK_SIGN_PUBKEY_HEX
|
|
M2PACK_KEY_MAP
|
|
M2PACK_KEY_ID
|
|
M2PACK_STRICT_HASH
|
|
```
|
|
|
|
### Shared memory
|
|
|
|
Default mapping name:
|
|
|
|
```text
|
|
Local\M2PackSharedKeys
|
|
```
|
|
|
|
Binary layout:
|
|
|
|
```c
|
|
struct M2PackSharedKeys {
|
|
char magic[8]; // "M2KEYS1\0"
|
|
uint32_t version; // 1
|
|
uint32_t flags; // reserved
|
|
uint32_t key_id; // runtime master key slot
|
|
uint8_t master_key[32];
|
|
uint8_t sign_public_key[32];
|
|
};
|
|
```
|
|
|
|
Recommended production path:
|
|
|
|
- launcher fetches or unwraps the current content key
|
|
- launcher creates the shared mapping
|
|
- launcher starts the client with `--m2pack-key-map Local\\M2PackSharedKeys`
|
|
- client reads runtime keys during startup
|
|
- client rejects `.m2p` loading if the runtime master key is missing
|
|
|
|
## Loader notes
|
|
|
|
- Use memory-mapped I/O for the archive.
|
|
- Keep a path-to-entry map in lowercase normalized form.
|
|
- Decrypt per request, not by unpacking the full archive.
|
|
- Keep a small decompression scratch buffer pool if the client reads in parallel.
|
|
- Skip plaintext hash verification in release hot paths unless `M2PACK_STRICT_HASH`
|
|
or `--m2pack-strict-hash` explicitly enables it.
|
|
|
|
## Release model
|
|
|
|
- `master.key`: private content encryption key
|
|
- `signing.key`: private release signing key
|
|
- `signing.pub`: embedded client verifier key
|
|
|
|
The signing key belongs in CI or a secured release box. The public key belongs
|
|
in the client binary or in a protected launcher manifest.
|