Add archive key id support
This commit is contained in:
@@ -119,6 +119,7 @@ Build an archive from a client asset directory:
|
||||
--output out/client.m2p \
|
||||
--key keys/master.key \
|
||||
--sign-secret-key keys/signing.key \
|
||||
--key-id 1 \
|
||||
--json
|
||||
```
|
||||
|
||||
@@ -147,6 +148,7 @@ Export a client config header for `m2dev-client-src/src/PackLib/M2PackKeys.h`:
|
||||
./build/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
|
||||
```
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ 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
|
||||
|
||||
@@ -43,6 +43,7 @@ Inputs:
|
||||
- `output_archive`
|
||||
- `key_file`
|
||||
- `signing_secret_key_file`
|
||||
- `key_id` optional
|
||||
|
||||
### `pack_diff`
|
||||
|
||||
@@ -80,6 +81,7 @@ Inputs:
|
||||
- `key_file`
|
||||
- `public_key_file`
|
||||
- `output_header`
|
||||
- `key_id` optional
|
||||
|
||||
### `pack_binary_info`
|
||||
|
||||
|
||||
@@ -79,8 +79,9 @@ server.tool(
|
||||
output_archive: z.string(),
|
||||
key_file: z.string(),
|
||||
signing_secret_key_file: z.string(),
|
||||
key_id: z.number().int().positive().optional(),
|
||||
},
|
||||
async ({ input_dir, output_archive, key_file, signing_secret_key_file }) =>
|
||||
async ({ input_dir, output_archive, key_file, signing_secret_key_file, key_id }) =>
|
||||
toolResult(
|
||||
runCli([
|
||||
"build",
|
||||
@@ -92,6 +93,8 @@ server.tool(
|
||||
key_file,
|
||||
"--sign-secret-key",
|
||||
signing_secret_key_file,
|
||||
"--key-id",
|
||||
String(key_id ?? 1),
|
||||
]),
|
||||
),
|
||||
);
|
||||
@@ -164,8 +167,9 @@ server.tool(
|
||||
key_file: z.string(),
|
||||
public_key_file: z.string(),
|
||||
output_header: z.string(),
|
||||
key_id: z.number().int().positive().optional(),
|
||||
},
|
||||
async ({ key_file, public_key_file, output_header }) =>
|
||||
async ({ key_file, public_key_file, output_header, key_id }) =>
|
||||
toolResult(
|
||||
runCli([
|
||||
"export-client-config",
|
||||
@@ -173,6 +177,8 @@ server.tool(
|
||||
key_file,
|
||||
"--public-key",
|
||||
public_key_file,
|
||||
"--key-id",
|
||||
String(key_id ?? 1),
|
||||
"--output",
|
||||
output_header,
|
||||
]),
|
||||
|
||||
@@ -74,6 +74,7 @@ def pack_build(
|
||||
output_archive: str,
|
||||
key_file: str,
|
||||
signing_secret_key_file: str,
|
||||
key_id: int = 1,
|
||||
) -> dict[str, Any]:
|
||||
"""Build an .m2p archive from a source directory."""
|
||||
return _run_cli(
|
||||
@@ -86,6 +87,8 @@ def pack_build(
|
||||
key_file,
|
||||
"--sign-secret-key",
|
||||
signing_secret_key_file,
|
||||
"--key-id",
|
||||
str(key_id),
|
||||
)
|
||||
|
||||
|
||||
@@ -139,6 +142,7 @@ def pack_export_client_config(
|
||||
key_file: str,
|
||||
public_key_file: str,
|
||||
output_header: str,
|
||||
key_id: int = 1,
|
||||
) -> dict[str, Any]:
|
||||
"""Generate M2PackKeys.h for the Windows client tree."""
|
||||
return _run_cli(
|
||||
@@ -147,6 +151,8 @@ def pack_export_client_config(
|
||||
key_file,
|
||||
"--public-key",
|
||||
public_key_file,
|
||||
"--key-id",
|
||||
str(key_id),
|
||||
"--output",
|
||||
output_header,
|
||||
)
|
||||
|
||||
@@ -168,6 +168,7 @@ BuildResult build_archive(
|
||||
std::memcpy(header.magic, kArchiveMagic, sizeof(kArchiveMagic));
|
||||
header.version = kArchiveVersion;
|
||||
header.flags = 0;
|
||||
header.key_id = keys.key_id;
|
||||
header.manifest_offset = sizeof(ArchiveHeader) + payload_bytes.size();
|
||||
header.manifest_size = manifest_bytes.size();
|
||||
std::memcpy(header.manifest_hash, manifest_hash.data(), manifest_hash.size());
|
||||
|
||||
@@ -29,11 +29,12 @@ struct ArchiveHeader
|
||||
char magic[kMagicSize];
|
||||
std::uint32_t version;
|
||||
std::uint32_t flags;
|
||||
std::uint32_t key_id;
|
||||
std::uint64_t manifest_offset;
|
||||
std::uint64_t manifest_size;
|
||||
std::uint8_t manifest_hash[kHashSize];
|
||||
std::uint8_t manifest_signature[kSignatureSize];
|
||||
std::uint8_t reserved[64];
|
||||
std::uint8_t reserved[60];
|
||||
};
|
||||
|
||||
struct ManifestFixedHeader
|
||||
@@ -76,6 +77,7 @@ struct LoadedArchive
|
||||
|
||||
struct KeyMaterial
|
||||
{
|
||||
std::uint32_t key_id = 1;
|
||||
std::array<std::uint8_t, kAeadKeySize> master_key {};
|
||||
std::optional<std::vector<std::uint8_t>> signing_secret_key;
|
||||
std::optional<std::vector<std::uint8_t>> signing_public_key;
|
||||
|
||||
33
src/cli.cpp
33
src/cli.cpp
@@ -95,6 +95,23 @@ std::array<std::uint8_t, kAeadKeySize> load_master_key(const std::string& path)
|
||||
return key;
|
||||
}
|
||||
|
||||
std::uint32_t load_key_id(const ParsedArgs& args)
|
||||
{
|
||||
const auto raw = optional_option(args, "key-id");
|
||||
if (!raw.has_value())
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
const auto value = std::stoul(*raw);
|
||||
if (value == 0)
|
||||
{
|
||||
fail("key-id must be greater than zero");
|
||||
}
|
||||
|
||||
return static_cast<std::uint32_t>(value);
|
||||
}
|
||||
|
||||
struct SnapshotEntry
|
||||
{
|
||||
std::uint64_t size = 0;
|
||||
@@ -238,7 +255,7 @@ void print_usage()
|
||||
std::cout
|
||||
<< "m2pack commands:\n"
|
||||
<< " keygen --out-dir <dir> [--json]\n"
|
||||
<< " build --input <dir> --output <file> --key <hex-file> --sign-secret-key <hex-file> [--json]\n"
|
||||
<< " build --input <dir> --output <file> --key <hex-file> --sign-secret-key <hex-file> [--key-id <n>] [--json]\n"
|
||||
<< " diff --left <dir|archive.m2p> --right <dir|archive.m2p> [--json]\n"
|
||||
<< " list --archive <file> [--json]\n"
|
||||
<< " verify --archive <file> [--public-key <hex-file>] [--key <hex-file>] [--json]\n"
|
||||
@@ -286,6 +303,7 @@ void command_keygen(const ParsedArgs& args)
|
||||
void command_build(const ParsedArgs& args)
|
||||
{
|
||||
KeyMaterial keys;
|
||||
keys.key_id = load_key_id(args);
|
||||
keys.master_key = load_master_key(require_option(args, "key"));
|
||||
keys.signing_secret_key = load_hex_file(require_option(args, "sign-secret-key"));
|
||||
|
||||
@@ -300,6 +318,7 @@ void command_build(const ParsedArgs& args)
|
||||
<< "{"
|
||||
<< "\"ok\":true,"
|
||||
<< "\"archive\":\"" << json_escape(result.archive_path.string()) << "\","
|
||||
<< "\"key_id\":" << keys.key_id << ","
|
||||
<< "\"file_count\":" << result.file_count << ","
|
||||
<< "\"input_bytes\":" << result.total_input_bytes << ","
|
||||
<< "\"stored_bytes\":" << result.total_stored_bytes
|
||||
@@ -318,7 +337,7 @@ void command_list(const ParsedArgs& args)
|
||||
|
||||
if (args.json)
|
||||
{
|
||||
std::cout << "{\"ok\":true,\"entries\":[";
|
||||
std::cout << "{\"ok\":true,\"key_id\":" << archive.header.key_id << ",\"entries\":[";
|
||||
for (std::size_t i = 0; i < archive.entries.size(); ++i)
|
||||
{
|
||||
const auto& entry = archive.entries[i];
|
||||
@@ -364,6 +383,7 @@ void command_verify(const ParsedArgs& args)
|
||||
std::cout
|
||||
<< "{"
|
||||
<< "\"ok\":" << (ok ? "true" : "false") << ","
|
||||
<< "\"key_id\":" << archive.header.key_id << ","
|
||||
<< "\"entry_count\":" << archive.entries.size();
|
||||
if (!ok)
|
||||
{
|
||||
@@ -447,12 +467,17 @@ void command_export_client_config(const ParsedArgs& args)
|
||||
<< "// Do not edit manually.\n"
|
||||
<< "// Runtime master key delivery is required for .m2p loading.\n\n"
|
||||
<< "constexpr bool M2PACK_RUNTIME_MASTER_KEY_REQUIRED = true;\n\n"
|
||||
<< "constexpr uint32_t M2PACK_KEY_SLOT_COUNT = 1;\n"
|
||||
<< "constexpr std::array<uint32_t, M2PACK_KEY_SLOT_COUNT> M2PACK_SIGN_KEY_IDS = { "
|
||||
<< load_key_id(args)
|
||||
<< " };\n\n"
|
||||
<< "constexpr std::array<uint8_t, M2PACK_KEY_SIZE> M2PACK_MASTER_KEY = {"
|
||||
<< render_array(zero_master)
|
||||
<< "};\n\n"
|
||||
<< "constexpr std::array<uint8_t, M2PACK_PUBLIC_KEY_SIZE> M2PACK_SIGN_PUBLIC_KEY = {"
|
||||
<< "constexpr std::array<std::array<uint8_t, M2PACK_PUBLIC_KEY_SIZE>, M2PACK_KEY_SLOT_COUNT> M2PACK_SIGN_PUBLIC_KEYS = {{"
|
||||
<< "\n\t{"
|
||||
<< render_array(public_key)
|
||||
<< "};\n";
|
||||
<< "\t}\n}};\n";
|
||||
|
||||
const auto text = header.str();
|
||||
write_file(output_path, std::vector<std::uint8_t>(text.begin(), text.end()));
|
||||
|
||||
Reference in New Issue
Block a user