feat(sec): 签名鉴权 2B (agent-221) — meow_verify 多公钥数组+v2签名块(含ts,未来宽限,兼容v1)+meow_sign verify子命令+产签README; svc_power 面板身份精确路径绑定(删启发式,3反例验证)
meow_verify→6c2e8ed2; svc_power→306df962; qemu+宿主全过
This commit is contained in:
1 parent
249b6224ab
commit
05a19a9ea3
13 files changed
+490
-71
No files matched your search
@@ -0,0 +1,132 @@
|
|||||||
|
# meow_sign — MeowOS `.mpk` 产签/验签工具(宿主端)
|
||||||
|
|
||||||
|
> 定位:**离线维护者侧工具**,给 `.mpk` 包打 Ed25519 签名,供设备端 `meow_verify`(固件 `/usr/bin/meow_verify`)
|
||||||
|
> 验签。本工具**不落设备、不入库私钥**。设计依据:`devdocs/plan/MPK_SIGNATURE_DESIGN.md`(§4.2 产签流程 / §3.1 块格式 / §5.1 挂点)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. 安全模型(先读,诚实标注)
|
||||||
|
|
||||||
|
| 项 | 说明 |
|
||||||
|
|---|---|
|
||||||
|
| 算法 | Ed25519(非对称)。**私钥仅在离线维护机**,设备只持**公钥**(固化进 `meow_sign` 的 `meow_signing_pub.h`)。 |
|
||||||
|
| 私钥 | `.keys/signing.key`(0600)——**绝不入库**(`.gitignore` 已忽略 `.keys/`)、**绝不落设备**;全网唯一,**务必单独备份**。 |
|
||||||
|
| 设备验证 | `meow_verify`(固件)只读内嵌公钥数组验签;`os.meow.*` 包必须有有效签名,否则 nyai/launcher **拒装**(防伪造系统包名)。 |
|
||||||
|
| 签名覆盖 | `payload_sha256`(整个 tar.gz 前缀,含二进制/资源/app.json)+ 显式 `package`(包名)。改内容/改包名 → 验签失败。 |
|
||||||
|
| 诚实天花板 | 签名 + IPC 鉴权是**防误装/防伪造包名/防沿途篡改**,**不是真安全边界**:有 shell 的 root 用户可直接建目录/换 `meow_verify`。它挡住第三方 App 的「无意识越权」,挡不住 root 有意攻击(见 `MPK_SIGNATURE_DESIGN §8`)。 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 用法
|
||||||
|
|
||||||
|
```sh
|
||||||
|
meow_sign keygen <priv_path> # 生成密钥对:priv(0600) + <priv>.pub(32B raw) + stdout 打印 pub_hex
|
||||||
|
meow_sign sign <pkg>.mpk <priv> [--v1] # 给纯 tar.gz 追加签名 footer(默认 v2 含时间戳;--v1=2A 旧版无 ts)
|
||||||
|
meow_sign verify <mpk> <pub|hex> # 用公钥(.pub 文件或 64 位 hex)验签指定 .mpk(产签后自测)
|
||||||
|
```
|
||||||
|
|
||||||
|
- `sign` 的 **`<pkg>.mpk` 文件名茎必须等于包名**(`app.json.package`),这是三重绑定①(`meow_verify` 会二次校验)。
|
||||||
|
- 默认产出 **v2** 块(184B):含**被签时间戳 `ts`**,验签端做「未来宽限」校验(超出 `now+86400` 拒)。
|
||||||
|
如需兼容旧固件(2A 的 `meow_verify` 只认 180B 的 v1)可加 `--v1` 产旧版。**新固件 `meow_verify` v1/v2 都读**。
|
||||||
|
- `verify` 是**宿主侧镜像**(产签后、分发前确认签名有效);**设备侧真正把关的是 `meow_verify`**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 产签流程(给新系统 App 签名)
|
||||||
|
|
||||||
|
以新增 `os.meow.xxx` 为例。
|
||||||
|
|
||||||
|
### 2.1 首次:生成/备份密钥(仅一次)
|
||||||
|
```sh
|
||||||
|
./patch_v2/meow_sign/meow_sign keygen .keys/signing.key
|
||||||
|
# 私钥 .keys/signing.key(0600) 请离线备份;公钥 .keys/signing.key.pub 可入库/固化进固件。
|
||||||
|
```
|
||||||
|
> 若已存在 `.keys/signing.key`(本仓库 2A 已生成),**直接复用,勿覆盖**(覆盖会令旧包全部失效)。
|
||||||
|
|
||||||
|
### 2.2 打包(不含签名;.mpk = 纯 tar.gz)
|
||||||
|
```sh
|
||||||
|
mkdir -p <build>/os.meow.xxx
|
||||||
|
# 放入 app.json + meow 二进制 + 资源;app.json 的 package 必须 == 目录名 == 文件名茎;version 单调递增(禁降级)
|
||||||
|
tar czf os.meow.xxx.mpk os.meow.xxx
|
||||||
|
```
|
||||||
|
> `app.json` 要点:`package`(= `os.meow.xxx`)、`exec`(指向 `MeowAPPs/os.meow.xxx/` 内二进制)、`version`(≥ 已装,禁止降级)。
|
||||||
|
|
||||||
|
### 2.3 签名(默认 v2)
|
||||||
|
```sh
|
||||||
|
./patch_v2/meow_sign/meow_sign sign os.meow.xxx.mpk .keys/signing.key
|
||||||
|
# => sign ok (v2): ... package=os.meow.xxx ts=<unix> block_len=184 ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.4 产签后自测(可选但推荐)
|
||||||
|
```sh
|
||||||
|
./patch_v2/meow_sign/meow_sign verify os.meow.xxx.mpk .keys/signing.key.pub # 用自己公钥确认
|
||||||
|
./patch_v2/meow_verify/meow_verify_host os.meow.xxx.mpk # 用固件内嵌公钥确认(与设备同口径)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.5 装到设备 / 分发
|
||||||
|
- 把 `os.meow.xxx.mpk` 放到卡上(如 `MeowOS/opt/nyai/` 或目标目录),用 `nyai install` 安装。
|
||||||
|
- 安装期由 nyai/launcher 调 `meow_verify`:验签通过 + 三重绑定(footer.package==文件名茎==app.json.package + `os.meow.*` 前缀)才落盘;否则拒装并回滚。
|
||||||
|
- **`os.meow.*` 无签名/验签失败 → 一律拒装**(防伪造系统包名);第三方包无签可直接装(但建议也签名防篡改)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 三重绑定(三方各司其职)
|
||||||
|
|
||||||
|
| 绑定 | 校验方 | 校验点 |
|
||||||
|
|---|---|---|
|
||||||
|
| ① footer.package == 文件名茎 | `meow_verify`(`--package`/推导) | 防「把 `os.meow.xxx.mpk` 改名为 `evil.mpk`」 |
|
||||||
|
| ② footer.package == 解包 `app.json.package` | nyai / launcher 安装流 | 防「改 app.json 的 package」 |
|
||||||
|
| ③ `os.meow.*` 前缀 + 必须验签过 | nyai / launcher 安装流 | 防「无签名冒充系统包」 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 多公钥 / 密钥轮换(2B)
|
||||||
|
|
||||||
|
固件 `meow_verify` 内嵌**公钥数组**(`meow_signing_pub.h`),验签时**任一公钥通过即放行**。轮换 = 加钥/去钥 + 重编 `meow_verify`(随固件更新):
|
||||||
|
|
||||||
|
```c
|
||||||
|
#define MEOW_PUB_KEY_COUNT 2
|
||||||
|
static const uint8_t g_meow_pub[MEOW_PUB_KEY_COUNT][32] = {
|
||||||
|
{ /* key #0:旧主钥(保留做跨代兼容) */ ... },
|
||||||
|
{ /* key #1:新钥 */ ... },
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
- **加钥**:新钥 `meow_sign keygen` 后追加到数组尾部、`MEOW_PUB_KEY_COUNT` +1、重编 `meow_verify` → 旧钥签的包(新固件仍内嵌旧钥)与新旧包都放行(平滑过渡)。
|
||||||
|
- **去钥**:从数组移除 + 重编 → 原钥签的包在新固件**拒装**。**去钥必须与包重签协同**(先用新钥重签所有在维护的系统包)。
|
||||||
|
- 私钥更新(新钥签新包)时,务必保证新私钥离线备份,且**旧私钥在轮换期内保留**(否则旧钥签的包失效)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. v1 / v2 块与兼容
|
||||||
|
|
||||||
|
| | v1(2A 旧版) | v2(2B 加固) |
|
||||||
|
|---|---|---|
|
||||||
|
| 总长 | 180B | 184B |
|
||||||
|
| 时间戳 | 无 | 有(`ts`,unix 秒,**被签名**) |
|
||||||
|
| 被签消息 | `"MEOWSIG1"‖alg‖pkg_len‖package‖sha256` | v1 msg ‖ `ts(4B,LE)` |
|
||||||
|
| `meow_verify` | 读取(不检 ts) | 读取(校验 ts 未来宽限,超 `now+86400` 拒;`ts==0` 视为畸形拒) |
|
||||||
|
|
||||||
|
- 新 `meow_verify`(2B 固件)**v1/v2 都读** → 旧 2A 签的包仍可装/可导出复验。
|
||||||
|
- **`--v1` 产出的包在旧 2A 固件可装;v2 包需 2B+ 固件**(旧固件 `meow_verify` 只认 180B,会把 184B 块误判为无签 → `os.meow.*` 拒装)。固件与工具链同版本发布即可避免此坑。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 明确不做(诚实)
|
||||||
|
|
||||||
|
- **防重放 / 密钥过期 / CRL / 在线验证**:需网络+后端,超出「单机 + 卡分发」模型(`MPK_SIGNATURE_DESIGN §8`)。
|
||||||
|
- **时间戳只做「未来异常」宽限拒绝**(防错误时钟/手工伪造未来 ts),不做「旧包过期拒装」。
|
||||||
|
- 签名是**防误装/防伪造包名/防篡改**,不是安全边界。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 相关文件
|
||||||
|
|
||||||
|
| 文件 | 作用 |
|
||||||
|
|---|---|
|
||||||
|
| `meow_sign.c` | 产签/验签工具源码(复用 `../meow_verify` 的 `tweetnacl.c` + `sha256.c` + `meow_sig.h`) |
|
||||||
|
| `build_sign.sh` | 宿主编译(`./build_sign.sh` / `./build_sign.sh static`) |
|
||||||
|
| `../meow_verify/meow_verify.c` | 设备侧校验器(固件 `/usr/bin/meow_verify`) |
|
||||||
|
| `../meow_verify/meow_signing_pub.h` | **固件内嵌公钥数组**(仅公钥,可入库) |
|
||||||
|
| `../meow_verify/meow_sig.h` | 签名块格式 + 被签消息构造(verifier/signer 共用) |
|
||||||
|
| `.keys/` | 私钥目录(**已 gitignore,绝不入库**) |
|
||||||
Binary file not shown.
+173
-20
@@ -1,10 +1,13 @@
|
|||||||
/* meow_sign.c — MeowOS .mpk 产签工具(宿主端,非设备;离线维护者侧使用)。
|
/* meow_sign.c — MeowOS .mpk 产签/验签工具(宿主端,非设备;离线维护者侧使用)。
|
||||||
*
|
*
|
||||||
* 功能(MPK_SIGNATURE_DESIGN §4.2):
|
* 功能(MPK_SIGNATURE_DESIGN §4.2 + 2B §7):
|
||||||
* - keygen <priv_path>:生成 Ed25519 密钥对。私钥(64B seed||pub,0600)写 <priv_path>;
|
* - keygen <priv_path>:生成 Ed25519 密钥对。私钥(64B seed||pub,0600)写 <priv_path>;
|
||||||
* 公钥(32B hex + raw)写 <priv_path>.pub,并在 stdout 打印 hex(便于固化进 meow_verify)。
|
* 公钥(32B hex + raw)写 <priv_path>.pub,并在 stdout 打印 hex(便于固化进 meow_verify)。
|
||||||
* - sign <pkg>.mpk <priv_path>:对 .mpk(当前无 footer = 纯 tar.gz)计算 payload_sha256、
|
* - sign <pkg>.mpk <priv_path> [--v1]:对 .mpk(当前无 footer = 纯 tar.gz)计算 payload_sha256、
|
||||||
* 组被签消息、Ed25519 签名,并**尾部追加** 180B 定长 footer(.mpk = tar.gz || meow_sig_block)。
|
* 组被签消息、Ed25519 签名,并**尾部追加**签名 footer。默认产出 **v2**(含签名时间戳 ts,
|
||||||
|
* 纳入被签消息,验签端做未来宽限校验,见 meow_verify);`--v1` 产 2A 旧版(无 ts,跨旧固件)。
|
||||||
|
* - verify <mpk> <pubkey>:宿主侧用任意**公钥**(hex 或 .pub 文件路径)验签指定 .mpk,输出通过/原因。
|
||||||
|
* 用于产签后、分发前自测(对应用户定案「先验签再装」的宿主侧镜像);固件侧统一走 meow_verify。
|
||||||
*
|
*
|
||||||
* 私钥绝不落设备、绝不入库(仓库 .gitignore 已忽略 .keys/)。
|
* 私钥绝不落设备、绝不入库(仓库 .gitignore 已忽略 .keys/)。
|
||||||
* 复用 ../meow_verify 的 tweetnacl.c(Ed25519)+ sha256.c。 */
|
* 复用 ../meow_verify 的 tweetnacl.c(Ed25519)+ sha256.c。 */
|
||||||
@@ -15,12 +18,13 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include "../meow_verify/tweetnacl.h"
|
#include "../meow_verify/tweetnacl.h"
|
||||||
#include "../meow_verify/sha256.h"
|
#include "../meow_verify/sha256.h"
|
||||||
#include "../meow_verify/meow_sig.h"
|
#include "../meow_verify/meow_sig.h"
|
||||||
|
|
||||||
/* keygen 需真随机(从 /dev/urandom 读);sign 不调用 keypair。 */
|
/* keygen 需真随机(从 /dev/urandom 读);sign/verify 不调用 keypair。 */
|
||||||
void randombytes(unsigned char *x, unsigned long long xlen)
|
void randombytes(unsigned char *x, unsigned long long xlen)
|
||||||
{
|
{
|
||||||
int fd = open("/dev/urandom", O_RDONLY);
|
int fd = open("/dev/urandom", O_RDONLY);
|
||||||
@@ -55,6 +59,27 @@ static void hex_print(const char *label, const uint8_t *b, int n)
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 十六进制串 → 字节。返回字节数(-1 非法)。 */
|
||||||
|
static int hex_decode(const char *s, uint8_t *out, int maxn)
|
||||||
|
{
|
||||||
|
int n = (int)strlen(s);
|
||||||
|
if ((n & 1) || n / 2 > maxn) return -1;
|
||||||
|
for (int i = 0; i < n; i += 2) {
|
||||||
|
int hi = s[i], lo = s[i + 1], v = 0;
|
||||||
|
if (hi >= '0' && hi <= '9') v = hi - '0';
|
||||||
|
else if (hi >= 'a' && hi <= 'f') v = hi - 'a' + 10;
|
||||||
|
else if (hi >= 'A' && hi <= 'F') v = hi - 'A' + 10;
|
||||||
|
else return -1;
|
||||||
|
v <<= 4;
|
||||||
|
if (lo >= '0' && lo <= '9') v += lo - '0';
|
||||||
|
else if (lo >= 'a' && lo <= 'f') v += lo - 'a' + 10;
|
||||||
|
else if (lo >= 'A' && lo <= 'F') v += lo - 'A' + 10;
|
||||||
|
else return -1;
|
||||||
|
out[i / 2] = (uint8_t)v;
|
||||||
|
}
|
||||||
|
return n / 2;
|
||||||
|
}
|
||||||
|
|
||||||
static int keygen(const char *priv_path)
|
static int keygen(const char *priv_path)
|
||||||
{
|
{
|
||||||
uint8_t pk[32], sk[64];
|
uint8_t pk[32], sk[64];
|
||||||
@@ -69,7 +94,7 @@ static int keygen(const char *priv_path)
|
|||||||
fprintf(stderr, "meow_sign: write %s fail: %s\n", pub_path, strerror(errno));
|
fprintf(stderr, "meow_sign: write %s fail: %s\n", pub_path, strerror(errno));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
printf("keygen ok:\n priv -> %s (0640? no: 0600)\n pub -> %s\n", priv_path, pub_path);
|
printf("keygen ok:\n priv -> %s (0600)\n pub -> %s\n", priv_path, pub_path);
|
||||||
hex_print("pub_hex", pk, 32);
|
hex_print("pub_hex", pk, 32);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -92,6 +117,27 @@ static int file_sha256(const char *path, uint8_t out[MEOW_SIG_SHA256_LEN])
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 分块 SHA-256 计算文件前 plen 字节(verify 用:payload = .mpk 去 footer 前缀)。 */
|
||||||
|
static int sha256_prefix(const char *path, long long plen, uint8_t out[MEOW_SIG_SHA256_LEN])
|
||||||
|
{
|
||||||
|
int fd = open(path, O_RDONLY);
|
||||||
|
if (fd < 0) return -1;
|
||||||
|
meow_sha256_ctx c;
|
||||||
|
meow_sha256_init(&c);
|
||||||
|
uint8_t buf[8192];
|
||||||
|
long long remaining = plen;
|
||||||
|
while (remaining > 0) {
|
||||||
|
size_t chunk = remaining > (long long)sizeof buf ? sizeof buf : (size_t)remaining;
|
||||||
|
ssize_t r = read(fd, buf, chunk);
|
||||||
|
if (r <= 0) { close(fd); return -1; }
|
||||||
|
meow_sha256_update(&c, buf, (size_t)r);
|
||||||
|
remaining -= (long long)r;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
meow_sha256_final(&c, out);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int derive_pkg(const char *mpk, char *out, int n)
|
static int derive_pkg(const char *mpk, char *out, int n)
|
||||||
{
|
{
|
||||||
const char *bn = strrchr(mpk, '/');
|
const char *bn = strrchr(mpk, '/');
|
||||||
@@ -104,7 +150,8 @@ static int derive_pkg(const char *mpk, char *out, int n)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sign(const char *mpk, const char *priv_path)
|
/* sign:默认 v2(含 ts);legacy_v1 非 0 则产 2A 旧版 footer(无 ts)。 */
|
||||||
|
static int sign(const char *mpk, const char *priv_path, int legacy_v1)
|
||||||
{
|
{
|
||||||
char pkg[MEOW_SIG_PKG_MAX];
|
char pkg[MEOW_SIG_PKG_MAX];
|
||||||
if (derive_pkg(mpk, pkg, sizeof pkg) != 0) {
|
if (derive_pkg(mpk, pkg, sizeof pkg) != 0) {
|
||||||
@@ -127,45 +174,145 @@ static int sign(const char *mpk, const char *priv_path)
|
|||||||
uint8_t sha[MEOW_SIG_SHA256_LEN];
|
uint8_t sha[MEOW_SIG_SHA256_LEN];
|
||||||
if (file_sha256(mpk, sha) != 0) { fprintf(stderr, "meow_sign: read %s fail\n", mpk); return 1; }
|
if (file_sha256(mpk, sha) != 0) { fprintf(stderr, "meow_sign: read %s fail\n", mpk); return 1; }
|
||||||
|
|
||||||
/* 组被签消息 + 签出 64B 分离签名 */
|
/* 组被签消息(v2 含 ts) + 签出 64B 分离签名 */
|
||||||
|
uint32_t ts = legacy_v1 ? 0u : (uint32_t)time(NULL);
|
||||||
char msg[MEOW_SIG_MSG_MAX];
|
char msg[MEOW_SIG_MSG_MAX];
|
||||||
unsigned mlen = meow_sig_msg_build(msg, pkg, sha);
|
unsigned mlen = legacy_v1 ? meow_sig_msg_build(msg, pkg, sha)
|
||||||
|
: meow_sig_msg_build_v2(msg, pkg, sha, ts);
|
||||||
uint8_t sm[MEOW_SIG_SIG_LEN + MEOW_SIG_MSG_MAX];
|
uint8_t sm[MEOW_SIG_SIG_LEN + MEOW_SIG_MSG_MAX];
|
||||||
unsigned long long smlen = 0;
|
unsigned long long smlen = 0;
|
||||||
crypto_sign(sm, &smlen, (const unsigned char *)msg, mlen, sk);
|
crypto_sign(sm, &smlen, (const unsigned char *)msg, mlen, sk);
|
||||||
if (smlen != mlen + MEOW_SIG_SIG_LEN) { fprintf(stderr, "meow_sign: sign len mismatch\n"); return 1; }
|
if (smlen != mlen + MEOW_SIG_SIG_LEN) { fprintf(stderr, "meow_sign: sign len mismatch\n"); return 1; }
|
||||||
|
|
||||||
/* 构造 footer 并尾部追加 */
|
if (legacy_v1) {
|
||||||
meow_sig_block_t blk;
|
meow_sig_block_t blk;
|
||||||
memset(&blk, 0, sizeof blk);
|
memset(&blk, 0, sizeof blk);
|
||||||
memcpy(blk.magic, MEOW_SIG_MAGIC, MEOW_SIG_MAGIC_LEN);
|
memcpy(blk.magic, MEOW_SIG_MAGIC, MEOW_SIG_MAGIC_LEN);
|
||||||
blk.alg = MEOW_SIG_ALG_ED25519;
|
blk.alg = MEOW_SIG_ALG_ED25519;
|
||||||
blk.flags = 0;
|
blk.flags = 0; /* 无 ts */
|
||||||
blk.pad = 0;
|
blk.pad = 0;
|
||||||
snprintf(blk.package, sizeof blk.package, "%s", pkg);
|
snprintf(blk.package, sizeof blk.package, "%s", pkg);
|
||||||
memcpy(blk.payload_sha256, sha, MEOW_SIG_SHA256_LEN);
|
memcpy(blk.payload_sha256, sha, MEOW_SIG_SHA256_LEN);
|
||||||
memcpy(blk.sig, sm, MEOW_SIG_SIG_LEN);
|
memcpy(blk.sig, sm, MEOW_SIG_SIG_LEN);
|
||||||
blk.msg_len = mlen;
|
blk.msg_len = mlen;
|
||||||
blk.block_len = MEOW_SIG_BLOCK_LEN;
|
blk.block_len = MEOW_SIG_BLOCK_LEN_V1;
|
||||||
|
|
||||||
int afd = open(mpk, O_WRONLY | O_APPEND);
|
int afd = open(mpk, O_WRONLY | O_APPEND);
|
||||||
if (afd < 0) { fprintf(stderr, "meow_sign: append %s fail: %s\n", mpk, strerror(errno)); return 1; }
|
if (afd < 0) { fprintf(stderr, "meow_sign: append %s fail: %s\n", mpk, strerror(errno)); return 1; }
|
||||||
ssize_t w = write(afd, &blk, MEOW_SIG_BLOCK_LEN);
|
ssize_t w = write(afd, &blk, MEOW_SIG_BLOCK_LEN_V1);
|
||||||
close(afd);
|
close(afd);
|
||||||
if (w != MEOW_SIG_BLOCK_LEN) { fprintf(stderr, "meow_sign: footer write fail\n"); return 1; }
|
if (w != (ssize_t)MEOW_SIG_BLOCK_LEN_V1) { fprintf(stderr, "meow_sign: footer write fail\n"); return 1; }
|
||||||
|
printf("sign ok (v1): %s package=%s msg_len=%u block_len=%u payload_sha256=",
|
||||||
printf("sign ok: %s package=%s msg_len=%u block_len=%u payload_sha256=",
|
mpk, pkg, mlen, (unsigned)MEOW_SIG_BLOCK_LEN_V1);
|
||||||
mpk, pkg, mlen, (unsigned)blk.block_len);
|
|
||||||
hex_print("", sha, 32);
|
hex_print("", sha, 32);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
meow_sig_block_v2_t blk;
|
||||||
|
memset(&blk, 0, sizeof blk);
|
||||||
|
memcpy(blk.magic, MEOW_SIG_MAGIC, MEOW_SIG_MAGIC_LEN);
|
||||||
|
blk.alg = MEOW_SIG_ALG_ED25519;
|
||||||
|
blk.flags = MEOW_SIG_FLAG_TS; /* 含 ts (v2) */
|
||||||
|
blk.pad = 0;
|
||||||
|
snprintf(blk.package, sizeof blk.package, "%s", pkg);
|
||||||
|
memcpy(blk.payload_sha256, sha, MEOW_SIG_SHA256_LEN);
|
||||||
|
memcpy(blk.sig, sm, MEOW_SIG_SIG_LEN);
|
||||||
|
blk.ts = ts;
|
||||||
|
blk.msg_len = mlen;
|
||||||
|
blk.block_len = MEOW_SIG_BLOCK_LEN_V2;
|
||||||
|
int afd = open(mpk, O_WRONLY | O_APPEND);
|
||||||
|
if (afd < 0) { fprintf(stderr, "meow_sign: append %s fail: %s\n", mpk, strerror(errno)); return 1; }
|
||||||
|
ssize_t w = write(afd, &blk, MEOW_SIG_BLOCK_LEN_V2);
|
||||||
|
close(afd);
|
||||||
|
if (w != (ssize_t)MEOW_SIG_BLOCK_LEN_V2) { fprintf(stderr, "meow_sign: footer write fail\n"); return 1; }
|
||||||
|
printf("sign ok (v2): %s package=%s ts=%u msg_len=%u block_len=%u payload_sha256=",
|
||||||
|
mpk, pkg, ts, mlen, (unsigned)MEOW_SIG_BLOCK_LEN_V2);
|
||||||
|
hex_print("", sha, 32);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 读文件尾部签名块(与 meow_verify 同逻辑)。返回块字节数;-2 太短/块长非法;-1 IO 错。 */
|
||||||
|
static int read_tail_block(const char *path, long long fsize, uint8_t *buf, int bufn)
|
||||||
|
{
|
||||||
|
int fd = open(path, O_RDONLY);
|
||||||
|
if (fd < 0) return -1;
|
||||||
|
uint8_t bl4[4];
|
||||||
|
if (lseek(fd, fsize - 4, SEEK_SET) < 0) { close(fd); return -1; }
|
||||||
|
if (read(fd, bl4, 4) != 4) { close(fd); return -1; }
|
||||||
|
uint32_t blen = meow_le32(bl4);
|
||||||
|
if (blen != MEOW_SIG_BLOCK_LEN_V1 && blen != MEOW_SIG_BLOCK_LEN_V2) { close(fd); return -2; }
|
||||||
|
if (fsize < (long long)blen || (int)blen > bufn) { close(fd); return -2; }
|
||||||
|
if (lseek(fd, fsize - (long long)blen, SEEK_SET) < 0) { close(fd); return -1; }
|
||||||
|
if (read(fd, buf, blen) != (ssize_t)blen) { close(fd); return -1; }
|
||||||
|
close(fd);
|
||||||
|
return (int)blen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* verify:宿主侧用任意公钥验签。pubkey 可为 32B 原始公钥文件路径、或 64 位 hex。 */
|
||||||
|
static int verify(const char *mpk, const char *pubkey)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
uint8_t pk[32];
|
||||||
|
if (stat(pubkey, &st) == 0) {
|
||||||
|
int fd = open(pubkey, O_RDONLY);
|
||||||
|
if (fd < 0) { fprintf(stderr, "meow_sign: open pub %s fail: %s\n", pubkey, strerror(errno)); return 1; }
|
||||||
|
if (read(fd, pk, sizeof pk) != (ssize_t)sizeof pk) { close(fd); fprintf(stderr, "meow_sign: pub file not 32B\n"); return 1; }
|
||||||
|
close(fd);
|
||||||
|
} else if (hex_decode(pubkey, pk, sizeof pk) != 32) {
|
||||||
|
fprintf(stderr, "meow_sign: pubkey 非法(需 32B .pub 文件路径,或 64 位 hex)\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stat(mpk, &st) != 0) { fprintf(stderr, "meow_sign: stat %s fail\n", mpk); return 1; }
|
||||||
|
long long fsize = (long long)st.st_size;
|
||||||
|
if (fsize < (long long)MEOW_SIG_BLOCK_LEN_V1) { fprintf(stderr, "meow_sign: no signature footer\n"); return 2; }
|
||||||
|
uint8_t buf[MEOW_SIG_BLOCK_MAX];
|
||||||
|
int blen = read_tail_block(mpk, fsize, buf, sizeof buf);
|
||||||
|
if (blen < 0) { fprintf(stderr, "meow_sign: no signature footer (or truncated)\n"); return 2; }
|
||||||
|
if (memcmp(buf, MEOW_SIG_MAGIC, MEOW_SIG_MAGIC_LEN) != 0) { fprintf(stderr, "meow_sign: no MEOWSIG1 footer\n"); return 2; }
|
||||||
|
if (buf[8] != MEOW_SIG_ALG_ED25519) { fprintf(stderr, "meow_sign: unknown alg\n"); return 1; }
|
||||||
|
int v2 = (blen == (int)MEOW_SIG_BLOCK_LEN_V2);
|
||||||
|
if (!v2 && blen != (int)MEOW_SIG_BLOCK_LEN_V1) { fprintf(stderr, "meow_sign: bad block_len\n"); return 1; }
|
||||||
|
uint8_t want_flags = v2 ? MEOW_SIG_FLAG_TS : 0;
|
||||||
|
if ((buf[9] & MEOW_SIG_FLAG_TS) != want_flags) { fprintf(stderr, "meow_sign: flags/version mismatch\n"); return 1; }
|
||||||
|
|
||||||
|
char pkgsig[MEOW_SIG_PKG_MAX + 1];
|
||||||
|
memcpy(pkgsig, buf + MEOW_SIG_OFF_PKG, MEOW_SIG_PKG_MAX);
|
||||||
|
pkgsig[MEOW_SIG_PKG_MAX] = 0;
|
||||||
|
|
||||||
|
long long plen = fsize - (long long)blen;
|
||||||
|
uint8_t sha[MEOW_SIG_SHA256_LEN];
|
||||||
|
if (sha256_prefix(mpk, plen, sha) != 0) { fprintf(stderr, "meow_sign: read payload fail\n"); return 3; }
|
||||||
|
if (memcmp(buf + MEOW_SIG_OFF_SHA, sha, MEOW_SIG_SHA256_LEN) != 0) {
|
||||||
|
fprintf(stderr, "meow_sign: payload sha256 mismatch\n"); return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ts = v2 ? meow_le32(buf + MEOW_SIG_OFF_TS) : 0;
|
||||||
|
char msg[MEOW_SIG_MSG_MAX];
|
||||||
|
unsigned mlen = v2 ? meow_sig_msg_build_v2(msg, pkgsig, sha, ts)
|
||||||
|
: meow_sig_msg_build(msg, pkgsig, sha);
|
||||||
|
uint32_t blk_msglen = meow_le32(buf + (v2 ? MEOW_SIG_OFF_MSGLEN_V2 : MEOW_SIG_OFF_MSGLEN_V1));
|
||||||
|
if (blk_msglen != mlen) { fprintf(stderr, "meow_sign: msg_len mismatch\n"); return 1; }
|
||||||
|
|
||||||
|
uint8_t sm[MEOW_SIG_SIG_LEN + MEOW_SIG_MSG_MAX];
|
||||||
|
memcpy(sm, buf + MEOW_SIG_OFF_SIG, MEOW_SIG_SIG_LEN);
|
||||||
|
memcpy(sm + MEOW_SIG_SIG_LEN, msg, mlen);
|
||||||
|
uint8_t out[MEOW_SIG_SIG_LEN + MEOW_SIG_MSG_MAX];
|
||||||
|
unsigned long long outlen = 0;
|
||||||
|
if (crypto_sign_open(out, &outlen, sm, (unsigned long long)(MEOW_SIG_SIG_LEN + mlen), pk) != 0) {
|
||||||
|
fprintf(stderr, "meow_sign: ed25519 signature invalid\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("verify ok: %s package=%s version=%s ts=%u\n", mpk, pkgsig, v2 ? "v2" : "v1", ts);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void usage(const char *a0)
|
static void usage(const char *a0)
|
||||||
{
|
{
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"usage:\n"
|
"usage:\n"
|
||||||
" %s keygen <priv_path> 生成密钥对(priv 0600 + .pub,明文、不破坏现有)\n"
|
" %s keygen <priv_path> 生成密钥对(priv 0600 + .pub,明文、不破坏现有)\n"
|
||||||
" %s sign <pkg>.mpk <priv_path> 对 .mpk 尾部追加 Ed25519 签名 footer\n", a0, a0);
|
" %s sign <pkg>.mpk <priv_path> [--v1] 对 .mpk 尾部追加 Ed25519 签名 footer(默认 v2 含时间戳;--v1=2A 旧版无 ts)\n"
|
||||||
|
" %s verify <mpk> <pub|hex> 用公钥(.pub 文件或 64 位 hex)验签指定 .mpk\n", a0, a0, a0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
@@ -177,7 +324,13 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
if (!strcmp(argv[1], "sign")) {
|
if (!strcmp(argv[1], "sign")) {
|
||||||
if (argc < 4) { usage(argv[0]); return 2; }
|
if (argc < 4) { usage(argv[0]); return 2; }
|
||||||
return sign(argv[2], argv[3]);
|
int legacy_v1 = 0;
|
||||||
|
for (int i = 4; i < argc; i++) if (!strcmp(argv[i], "--v1")) legacy_v1 = 1;
|
||||||
|
return sign(argv[2], argv[3], legacy_v1);
|
||||||
|
}
|
||||||
|
if (!strcmp(argv[1], "verify")) {
|
||||||
|
if (argc < 4) { usage(argv[0]); return 2; }
|
||||||
|
return verify(argv[2], argv[3]);
|
||||||
}
|
}
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
return 2;
|
return 2;
|
||||||
|
|||||||
Binary file not shown.
@@ -642,6 +642,25 @@ static int is_sys_identity(const caller_t *c)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* MeowPanel 精确绑定(2B):仅当 exe **精确命中登记路径** 才予 os.meow.panel 系统身份。
|
||||||
|
* 替代 2A 的启发式 `strstr(exe,"/MeowPanel/")||strstr(exe,"meowpanel")`——后者过宽(任何路径含该
|
||||||
|
* 子串即被放行,如 `/.../MeowPanel/evil` 或 `/usr/bin/foomeowpanel`)。面板本体为外置卡组件
|
||||||
|
* (无卡不 import),路径登记 = `meowpanel.conf` 的 `exec`(`MeowOS/opt/MeowPanel/meowpanel`)。
|
||||||
|
* 面板二进制更新(换 md5)不影响路径登记,无需重编本服务;「精确绑定」靠路径唯一性 + 唯一二进制位。
|
||||||
|
* 诚实标注:若攻击者以 root 在本路径放恶意二进制,仍获 os.meow.panel——但该情形 root 本可为所欲为
|
||||||
|
* (MPK_SIGNATURE_DESIGN §8 天花板);本绑定只把「启发式误放行」收紧为「明确路径登记」。
|
||||||
|
* 如需更强(绑定面板二进制 md5),可在此按路径读文件算 md5 比对登记表——首版以路径登记为准。 */
|
||||||
|
static const char *const k_panel_exe[] = {
|
||||||
|
"/usr/data/mnt/sd_0/MeowOS/opt/MeowPanel/meowpanel",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
static int exe_is_panel(const char *exe)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; k_panel_exe[i]; i++) if (strcmp(exe, k_panel_exe[i]) == 0) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* 由 exe 路径判身份(纯函数,便于宿主自测 + 生产共用)。前提:out 已 memset(kind 缺省 CALL_OTHER)。
|
/* 由 exe 路径判身份(纯函数,便于宿主自测 + 生产共用)。前提:out 已 memset(kind 缺省 CALL_OTHER)。
|
||||||
* - exe 前缀 /usr/data/mnt/sd_0/MeowAPPs/ → MeowAPP 包(kind=CALL_PKG,pkg=目录名)
|
* - exe 前缀 /usr/data/mnt/sd_0/MeowAPPs/ → MeowAPP 包(kind=CALL_PKG,pkg=目录名)
|
||||||
* - 否则命中系统二进制 allowlist → CALL_SYS
|
* - 否则命中系统二进制 allowlist → CALL_SYS
|
||||||
@@ -667,7 +686,7 @@ static void classify_exe(const char *exe, caller_t *out)
|
|||||||
char tmp[MEOW_APP_ID_MAX];
|
char tmp[MEOW_APP_ID_MAX];
|
||||||
if (strcmp(exe, "/usr/bin/meow_launcherd") == 0) {
|
if (strcmp(exe, "/usr/bin/meow_launcherd") == 0) {
|
||||||
snprintf(tmp, sizeof tmp, "%s", "os.meow.launcher");
|
snprintf(tmp, sizeof tmp, "%s", "os.meow.launcher");
|
||||||
} else if (strstr(exe, "/MeowPanel/") || strstr(exe, "meowpanel")) {
|
} else if (exe_is_panel(exe)) {
|
||||||
snprintf(tmp, sizeof tmp, "%s", "os.meow.panel");
|
snprintf(tmp, sizeof tmp, "%s", "os.meow.panel");
|
||||||
} else if (strncmp(exe, "/usr/bin/meow_", 14) == 0) {
|
} else if (strncmp(exe, "/usr/bin/meow_", 14) == 0) {
|
||||||
/* 截取 "meow_xyz"(去 "/usr/bin/" 前缀)作近似别名 */
|
/* 截取 "meow_xyz"(去 "/usr/bin/" 前缀)作近似别名 */
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -45,9 +45,22 @@ int main(void)
|
|||||||
classify_exe("/usr/bin/meow_svc_power", &c);
|
classify_exe("/usr/bin/meow_svc_power", &c);
|
||||||
CHECK(c.kind == CALL_SYS); /* /usr/bin/meow_* 前缀批准 */
|
CHECK(c.kind == CALL_SYS); /* /usr/bin/meow_* 前缀批准 */
|
||||||
|
|
||||||
|
/* 2B 精确绑定:仅精确命中登记路径(meowpanel.conf 的 exec)才予 os.meow.panel。
|
||||||
|
* 旧启发式(路径含 /MeowPanel/ 或 meowpanel 子串)会被过宽放行,今已收紧为精确路径登记。 */
|
||||||
|
memset(&c, 0, sizeof c);
|
||||||
|
classify_exe("/usr/data/mnt/sd_0/MeowOS/opt/MeowPanel/meowpanel", &c);
|
||||||
|
CHECK(c.kind == CALL_SYS && strcmp(c.pkg, "os.meow.panel") == 0);
|
||||||
|
|
||||||
|
/* 反例:路径含 /MeowPanel/ 或 meowpanel 子串但并非精确登记路径 → 不再放行(CALL_OTHER) */
|
||||||
memset(&c, 0, sizeof c);
|
memset(&c, 0, sizeof c);
|
||||||
classify_exe("/usr/data/mnt/sd_0/MeowOS/opt/MeowPanel/backend/meowpanel", &c);
|
classify_exe("/usr/data/mnt/sd_0/MeowOS/opt/MeowPanel/backend/meowpanel", &c);
|
||||||
CHECK(c.kind == CALL_SYS && strcmp(c.pkg, "os.meow.panel") == 0);
|
CHECK(c.kind == CALL_OTHER);
|
||||||
|
memset(&c, 0, sizeof c);
|
||||||
|
classify_exe("/usr/data/mnt/sd_0/MeowOS/opt/MeowPanel/evil", &c);
|
||||||
|
CHECK(c.kind == CALL_OTHER);
|
||||||
|
memset(&c, 0, sizeof c);
|
||||||
|
classify_exe("/usr/bin/meowpanel_fake", &c);
|
||||||
|
CHECK(c.kind == CALL_OTHER); /* 含 "meowpanel" 但非 /usr/bin/meow_* 精确前缀、非登记路径 */
|
||||||
|
|
||||||
memset(&c, 0, sizeof c);
|
memset(&c, 0, sizeof c);
|
||||||
classify_exe("/usr/data/meow/settings_tool", &c);
|
classify_exe("/usr/data/meow/settings_tool", &c);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
/* meow_sig.h — .mpk 签名块格式(MPK_SIGNATURE_DESIGN §3.1)+ 被签消息构造(§3.1)。
|
/* meow_sig.h — .mpk 签名块格式(MPK_SIGNATURE_DESIGN §3.1,2B 扩展 §7)+ 被签消息构造。
|
||||||
*
|
*
|
||||||
* .mpk = [ tar.gz bytes ......................... ] [ meow_sig_block(定长 LE)]
|
* .mpk = [ tar.gz bytes ......................... ] [ meow_sig_block(变长 LE,v1/v2)]
|
||||||
*
|
*
|
||||||
* meow_sig_block(packed,总长 MEOW_SIG_BLOCK_LEN):
|
* meow_sig_block v1(packed,总长 MEOW_SIG_BLOCK_LEN_V1=180):
|
||||||
* char magic[8]; "MEOWSIG1"
|
* char magic[8]; "MEOWSIG1"
|
||||||
* uint8_t alg; 1 = ed25519
|
* uint8_t alg; 1 = ed25519
|
||||||
* uint8_t flags; 0
|
* uint8_t flags; 0
|
||||||
@@ -11,10 +11,21 @@
|
|||||||
* uint8_t payload_sha256[32];sha256(整个 .mpk 去掉尾部 footer 的其余字节)
|
* uint8_t payload_sha256[32];sha256(整个 .mpk 去掉尾部 footer 的其余字节)
|
||||||
* uint8_t sig[64]; Ed25519(pk, msg)
|
* uint8_t sig[64]; Ed25519(pk, msg)
|
||||||
* uint32_t msg_len; 实际签名的 msg 字节数(LE)
|
* uint32_t msg_len; 实际签名的 msg 字节数(LE)
|
||||||
* uint32_t block_len; 本块总长(LE;= MEOW_SIG_BLOCK_LEN)
|
* uint32_t block_len; 本块总长(LE;= MEOW_SIG_BLOCK_LEN_V1)
|
||||||
*
|
*
|
||||||
* 被签消息 msg = "MEOWSIG1" || alg(1B) || package_len(4B,LE) || package || payload_sha256(32B)。
|
* meow_sig_block v2(packed,总长 MEOW_SIG_BLOCK_LEN_V2=184;2B 加固):
|
||||||
* 本文件由 meow_verify 与 meow_sign 共用(meow_sign 用相对路径 include)。 */
|
* 在 v1 的 sig 后、msg_len 前插入 uint32_t ts(unix 秒,LE,**纳入被签消息**)。
|
||||||
|
* 前 172 字节(magic/alg/flags/pad/package/payload_sha256/sig)与 v1 完全同偏移,
|
||||||
|
* 故验签端可据尾部 block_len(v1=180 / v2=184)判别版本后按偏移取字段。
|
||||||
|
* flags bit0 (MEOW_SIG_FLAG_TS)=1 标记含时间戳。
|
||||||
|
*
|
||||||
|
* 被签消息:
|
||||||
|
* v1 msg = "MEOWSIG1" || alg(1B) || package_len(4B,LE) || package || payload_sha256(32B)。
|
||||||
|
* v2 msg = v1 msg || ts(4B,LE)。 — 时间戳被签名绑定,防改时间戳。
|
||||||
|
*
|
||||||
|
* 本文件由 meow_verify 与 meow_sign 共用(meow_sign 用相对路径 include)。
|
||||||
|
* v1/v2 兼容:验签端读 v1(旧 2A 包)不检时间戳(无该字段),读 v2(2B 包)检 ts 未来宽限。
|
||||||
|
* (明确不做:防重放/过期——ts 只做「未来异常」宽限拒绝,见 MPK_SIGNATURE_DESIGN §8 诚实标注。) */
|
||||||
#ifndef MEOW_SIG_H
|
#ifndef MEOW_SIG_H
|
||||||
#define MEOW_SIG_H
|
#define MEOW_SIG_H
|
||||||
|
|
||||||
@@ -28,6 +39,9 @@
|
|||||||
#define MEOW_SIG_SHA256_LEN 32
|
#define MEOW_SIG_SHA256_LEN 32
|
||||||
#define MEOW_SIG_SIG_LEN 64
|
#define MEOW_SIG_SIG_LEN 64
|
||||||
|
|
||||||
|
/* flags 位(v2) */
|
||||||
|
#define MEOW_SIG_FLAG_TS 0x01u /* 含签名时间戳(v2) */
|
||||||
|
|
||||||
typedef struct __attribute__((packed)) {
|
typedef struct __attribute__((packed)) {
|
||||||
uint8_t magic[MEOW_SIG_MAGIC_LEN];
|
uint8_t magic[MEOW_SIG_MAGIC_LEN];
|
||||||
uint8_t alg;
|
uint8_t alg;
|
||||||
@@ -40,7 +54,34 @@ typedef struct __attribute__((packed)) {
|
|||||||
uint32_t block_len;
|
uint32_t block_len;
|
||||||
} meow_sig_block_t;
|
} meow_sig_block_t;
|
||||||
|
|
||||||
#define MEOW_SIG_BLOCK_LEN (sizeof(meow_sig_block_t)) /* 定长 180 */
|
typedef struct __attribute__((packed)) {
|
||||||
|
uint8_t magic[MEOW_SIG_MAGIC_LEN];
|
||||||
|
uint8_t alg;
|
||||||
|
uint8_t flags;
|
||||||
|
uint16_t pad;
|
||||||
|
char package[MEOW_SIG_PKG_MAX];
|
||||||
|
uint8_t payload_sha256[MEOW_SIG_SHA256_LEN];
|
||||||
|
uint8_t sig[MEOW_SIG_SIG_LEN];
|
||||||
|
uint32_t ts; /* v2: unix 时间戳(LE,被签名) */
|
||||||
|
uint32_t msg_len;
|
||||||
|
uint32_t block_len;
|
||||||
|
} meow_sig_block_v2_t;
|
||||||
|
|
||||||
|
/* 块版本:由尾部 block_len 判别(v1=180 无 ts;v2=184 含 ts) */
|
||||||
|
#define MEOW_SIG_BLOCK_LEN_V1 (sizeof(meow_sig_block_t)) /* 180 */
|
||||||
|
#define MEOW_SIG_BLOCK_LEN_V2 (sizeof(meow_sig_block_v2_t)) /* 184 = 180 + ts(4) */
|
||||||
|
#define MEOW_SIG_BLOCK_MAX MEOW_SIG_BLOCK_LEN_V2
|
||||||
|
|
||||||
|
/* 签名时间戳未来宽限(秒):ts 在 now+宽限内视为合法时钟误差,超出即拒。 */
|
||||||
|
#define MEOW_SIG_TS_GRACE 86400u /* 24h */
|
||||||
|
|
||||||
|
/* 字段偏移(前 172B 两版同偏移;ts/msg_len 因版本异,见下)。 */
|
||||||
|
#define MEOW_SIG_OFF_PKG 12
|
||||||
|
#define MEOW_SIG_OFF_SHA 76
|
||||||
|
#define MEOW_SIG_OFF_SIG 108
|
||||||
|
#define MEOW_SIG_OFF_TS 172 /* v2 only */
|
||||||
|
#define MEOW_SIG_OFF_MSGLEN_V1 (MEOW_SIG_BLOCK_LEN_V1 - 8) /* 172 */
|
||||||
|
#define MEOW_SIG_OFF_MSGLEN_V2 (MEOW_SIG_BLOCK_LEN_V2 - 8) /* 176 */
|
||||||
|
|
||||||
/* 按字节读取 LE 的 uint32(避免 packed 解引用对齐告警) */
|
/* 按字节读取 LE 的 uint32(避免 packed 解引用对齐告警) */
|
||||||
static inline uint32_t meow_le32(const uint8_t *p)
|
static inline uint32_t meow_le32(const uint8_t *p)
|
||||||
@@ -56,9 +97,10 @@ static inline void meow_le32_put(uint8_t *p, uint32_t v)
|
|||||||
p[3] = (uint8_t)((v >> 24) & 0xff);
|
p[3] = (uint8_t)((v >> 24) & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MEOW_SIG_MSG_MAX (MEOW_SIG_MAGIC_LEN + 1 + 4 + MEOW_SIG_PKG_MAX + MEOW_SIG_SHA256_LEN)
|
/* v1 或 v2 的 msg 均不超过此上限(v1 109 / v2 113)。 */
|
||||||
|
#define MEOW_SIG_MSG_MAX (MEOW_SIG_MAGIC_LEN + 1 + 4 + MEOW_SIG_PKG_MAX + MEOW_SIG_SHA256_LEN + 4)
|
||||||
|
|
||||||
/* 按设计构造被签消息 msg = "MEOWSIG1" || alg(1B) || package_len(4B,LE) || package || payload_sha256(32B)。
|
/* 按设计构造 v1 被签消息 msg = "MEOWSIG1" || alg(1B) || package_len(4B,LE) || package || payload_sha256(32B)。
|
||||||
* 返回 msg 字节数。签名端与验签端必须用同一构造(meow_sign / meow_verify 共用)。 */
|
* 返回 msg 字节数。签名端与验签端必须用同一构造(meow_sign / meow_verify 共用)。 */
|
||||||
static inline unsigned meow_sig_msg_build(char *out, const char *package,
|
static inline unsigned meow_sig_msg_build(char *out, const char *package,
|
||||||
const uint8_t payload_sha256[MEOW_SIG_SHA256_LEN])
|
const uint8_t payload_sha256[MEOW_SIG_SHA256_LEN])
|
||||||
@@ -74,4 +116,14 @@ static inline unsigned meow_sig_msg_build(char *out, const char *package,
|
|||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 构造 v2 被签消息 = v1 msg || ts(4B,LE)。返回 msg 字节数(= v1 msg + 4)。 */
|
||||||
|
static inline unsigned meow_sig_msg_build_v2(char *out, const char *package,
|
||||||
|
const uint8_t payload_sha256[MEOW_SIG_SHA256_LEN],
|
||||||
|
uint32_t ts)
|
||||||
|
{
|
||||||
|
unsigned o = meow_sig_msg_build(out, package, payload_sha256);
|
||||||
|
meow_le32_put((uint8_t *)out + o, ts); o += 4;
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* MEOW_SIG_H */
|
#endif /* MEOW_SIG_H */
|
||||||
@@ -1,17 +1,27 @@
|
|||||||
/* meow_signing_pub.h — 固件公钥(Ed25519 32B,编译期固化进 meow_verify)。
|
/* meow_signing_pub.h — 固件公钥(Ed25519 32B,编译期固化进 meow_verify)。
|
||||||
* 由 `meow_sign keygen` 生成(.keys/signing.key.pub),本头只含【公钥】,可入库;
|
* 由 `meow_sign keygen` 生成(.keys/signing.key.pub),本头只含【公钥】,可入库;
|
||||||
* 私钥仅在 .keys/(已加入 .gitignore,绝不入库/报告)。轮换公钥=重生成本头+重编 meow_verify。 */
|
* 私钥仅在 .keys/(已加入 .gitignore,绝不入库/报告)。
|
||||||
|
*
|
||||||
|
* 2B 多钥兼容(MPK_SIGNATURE_DESIGN §4.3 轮换铺路):固件内嵌**公钥数组**,验签时任一公钥通过即放行。
|
||||||
|
* 轮换/增补公钥=重写本头数组(g_meow_pub[MEOW_PUB_KEY_COUNT][])+重编 meow_verify(固件更新)。
|
||||||
|
* 保持旧钥在数组前部可作「跨代兼容」(旧钥签的包在新固件仍验过);从尾部追加新钥;
|
||||||
|
* 若某钥彻底作废,从数组移除即可(注意:会令旧钥签的包在新固件拒装——轮换需与包重签协同)。
|
||||||
|
* MEOW_PUB_KEY_COUNT 为当前内嵌公钥数(可增)。 */
|
||||||
#ifndef MEOW_SIGNING_PUB_H
|
#ifndef MEOW_SIGNING_PUB_H
|
||||||
#define MEOW_SIGNING_PUB_H
|
#define MEOW_SIGNING_PUB_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
static const uint8_t g_meow_pub[32] =
|
#define MEOW_PUB_KEY_COUNT 1 /* 当前固件内嵌公钥数;增补即在数组追加并 +1、重编 meow_verify */
|
||||||
|
|
||||||
|
static const uint8_t g_meow_pub[MEOW_PUB_KEY_COUNT][32] =
|
||||||
{
|
{
|
||||||
|
{ /* key #0:维护者主钥(.keys/signing.key.pub;与 2A 同钥,跨代兼容) */
|
||||||
0x22, 0xa7, 0x83, 0xac, 0x28, 0xd3, 0x25, 0x6d,
|
0x22, 0xa7, 0x83, 0xac, 0x28, 0xd3, 0x25, 0x6d,
|
||||||
0x3f, 0x84, 0x72, 0x72, 0x50, 0x5b, 0x2c, 0x61,
|
0x3f, 0x84, 0x72, 0x72, 0x50, 0x5b, 0x2c, 0x61,
|
||||||
0x60, 0x97, 0xff, 0xc3, 0x98, 0x78, 0x14, 0x9d,
|
0x60, 0x97, 0xff, 0xc3, 0x98, 0x78, 0x14, 0x9d,
|
||||||
0xc7, 0x37, 0x8c, 0x58, 0x72, 0x26, 0x37, 0xf2
|
0xc7, 0x37, 0x8c, 0x58, 0x72, 0x26, 0x37, 0xf2
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* MEOW_SIGNING_PUB_H */
|
#endif /* MEOW_SIGNING_PUB_H */
|
||||||
Binary file not shown.
@@ -1,12 +1,15 @@
|
|||||||
/* meow_verify.c — MeowOS .mpk 签名校验器(固件 /usr/bin/meow_verify,静态 mipsel)。
|
/* meow_verify.c — MeowOS .mpk 签名校验器(固件 /usr/bin/meow_verify,静态 mipsel)。
|
||||||
*
|
*
|
||||||
* 功能(MPK_SIGNATURE_DESIGN §5.1):
|
* 功能(MPK_SIGNATURE_DESIGN §5.1 + 2B §7):
|
||||||
* - Ed25519 验签(TweetNaCl crypto_sign_open,公钥编译期固化进 meow_signing_pub.h);
|
* - Ed25519 验签(TweetNaCl crypto_sign_open,公钥编译期固化进 meow_signing_pub.h,**多钥数组**);
|
||||||
* - payload_sha256 = SHA-256(.mpk 去掉尾部 footer 的其余字节,即 tar.gz 前缀,分块读、无需解包);
|
* - payload_sha256 = SHA-256(.mpk 去掉尾部 footer 的其余字节,即 tar.gz 前缀,分块读、无需解包);
|
||||||
* - 包名绑定①:footer.package == 期望包名(--package 或文件名茎);
|
* - 包名绑定①:footer.package == 期望包名(--package 或文件名茎);
|
||||||
* - 被签消息 msg = "MEOWSIG1" || alg(1B) || package_len(4B,LE) || package || payload_sha256(32B)。
|
* - 被签消息:v1 msg = "MEOWSIG1"||alg||pkg_len||package||payload_sha256;v2 msg = v1||ts(4B,LE)。
|
||||||
|
* 2B 加固:多公钥(任一通过即放行,为轮换铺路);v2 块含**签名时间戳 ts**,超未来宽限即拒
|
||||||
|
* (MEOW_SIG_TS_GRACE=24h;只做「未来异常」拒,防重放/过期明确不做)。
|
||||||
* 绑定②(app.json.package)与③(os.meow.* 前缀)由调用方(nyai / launcher)在安装流里执行,
|
* 绑定②(app.json.package)与③(os.meow.* 前缀)由调用方(nyai / launcher)在安装流里执行,
|
||||||
* 本器只保证「有签名就必须验签通过」。见 MPK_SIGNATURE_DESIGN §3.3(分工诚实标注)。
|
* 本器只保证「有签名就必须验签通过」。见 MPK_SIGNATURE_DESIGN §3.3(分工诚实标注)。
|
||||||
|
* 无签名第三方包安装放行由调用方决定,本器对无 footer 返回 2(调用方据分级处理)。
|
||||||
*
|
*
|
||||||
* 用法:
|
* 用法:
|
||||||
* meow_verify <mpk> # 验签(默认系统模式:固件公钥 + 绑定①)
|
* meow_verify <mpk> # 验签(默认系统模式:固件公钥 + 绑定①)
|
||||||
@@ -15,7 +18,7 @@
|
|||||||
* meow_verify --has-sig <mpk> # 只探测是否存在 MEOWSIG1 footer:输出 yes/no
|
* meow_verify --has-sig <mpk> # 只探测是否存在 MEOWSIG1 footer:输出 yes/no
|
||||||
* meow_verify --strip <mpk> # 输出去 footer 的干净 tar.gz 到 stdout(无 footer 则原样复制)
|
* meow_verify --strip <mpk> # 输出去 footer 的干净 tar.gz 到 stdout(无 footer 则原样复制)
|
||||||
*
|
*
|
||||||
* 退出码:0=通过;1=有签但不通过(篡改/包名不符/公钥不符/格式错);2=无签名 footer;3=用法/IO 错误。
|
* 退出码:0=通过;1=有签但不通过(篡改/包名不符/公钥不符/格式错/ts异常);2=无签名 footer;3=用法/IO 错误。
|
||||||
*/
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -23,12 +26,14 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include "tweetnacl.h"
|
#include "tweetnacl.h"
|
||||||
#include "sha256.h"
|
#include "sha256.h"
|
||||||
#include "meow_sig.h"
|
#include "meow_sig.h"
|
||||||
|
|
||||||
/* 固件公钥(编译期固化,32B Ed25519)。由 meow_sign keygen 生成后写死于此头。 */
|
/* 固件公钥数组(编译期固化,多钥 Ed25519)。由 meow_sign keygen 生成后写死于此头;
|
||||||
|
* 轮换/增补公钥=改数组+MEOW_PUB_KEY_COUNT+重编 meow_verify(见 meow_signing_pub.h)。 */
|
||||||
#include "meow_signing_pub.h"
|
#include "meow_signing_pub.h"
|
||||||
|
|
||||||
/* 验签侧绝不调用 keypair,但 tweetnacl.c 的 crypto_sign_keypair 引用了 randombytes;
|
/* 验签侧绝不调用 keypair,但 tweetnacl.c 的 crypto_sign_keypair 引用了 randombytes;
|
||||||
@@ -46,17 +51,22 @@ static int file_size(const char *path, long long *sz)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 读文件尾部 MEOW_SIG_BLOCK_LEN 字节作为 footer。返回 0 成功,-2 文件太短(无签名空间),-1 IO 错。 */
|
/* 读文件尾部签名块:先读最后 4 字节 block_len 判版本(v1=180 / v2=184),再整块读入 buf。
|
||||||
static int read_footer(const char *path, long long fsize, meow_sig_block_t *blk)
|
* 返回实际块字节数(>0);-2 太短/块长非法(无签名空间);-1 IO 错。magic 校验留给调用方。 */
|
||||||
|
static int read_block_bytes(const char *path, long long fsize, uint8_t *buf, int bufn)
|
||||||
{
|
{
|
||||||
if (fsize < (long long)MEOW_SIG_BLOCK_LEN) return -2;
|
|
||||||
int fd = open(path, O_RDONLY);
|
int fd = open(path, O_RDONLY);
|
||||||
if (fd < 0) return -1;
|
if (fd < 0) return -1;
|
||||||
if (lseek(fd, fsize - (long long)MEOW_SIG_BLOCK_LEN, SEEK_SET) < 0) { close(fd); return -1; }
|
uint8_t bl4[4];
|
||||||
ssize_t n = read(fd, blk, MEOW_SIG_BLOCK_LEN);
|
if (lseek(fd, fsize - 4, SEEK_SET) < 0) { close(fd); return -1; }
|
||||||
|
if (read(fd, bl4, 4) != 4) { close(fd); return -1; }
|
||||||
|
uint32_t blen = meow_le32(bl4);
|
||||||
|
if (blen != MEOW_SIG_BLOCK_LEN_V1 && blen != MEOW_SIG_BLOCK_LEN_V2) { close(fd); return -2; }
|
||||||
|
if (fsize < (long long)blen || (int)blen > bufn) { close(fd); return -2; }
|
||||||
|
if (lseek(fd, fsize - (long long)blen, SEEK_SET) < 0) { close(fd); return -1; }
|
||||||
|
if (read(fd, buf, blen) != (ssize_t)blen) { close(fd); return -1; }
|
||||||
close(fd);
|
close(fd);
|
||||||
if (n != (ssize_t)MEOW_SIG_BLOCK_LEN) return -1;
|
return (int)blen;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 分块 SHA-256 计算文件前 plen 字节。返回 0 成功,-1 IO/读错。 */
|
/* 分块 SHA-256 计算文件前 plen 字节。返回 0 成功,-1 IO/读错。 */
|
||||||
@@ -98,51 +108,80 @@ static int verify_mpk(const char *mpk, const char *pkg)
|
|||||||
{
|
{
|
||||||
long long fsize;
|
long long fsize;
|
||||||
if (file_size(mpk, &fsize) != 0) return 3;
|
if (file_size(mpk, &fsize) != 0) return 3;
|
||||||
if (fsize < (long long)MEOW_SIG_BLOCK_LEN) return 2; /* 无签名空间 */
|
if (fsize < (long long)MEOW_SIG_BLOCK_LEN_V1) return 2; /* 无签名空间 */
|
||||||
|
|
||||||
meow_sig_block_t blk;
|
uint8_t buf[MEOW_SIG_BLOCK_MAX];
|
||||||
if (read_footer(mpk, fsize, &blk) != 0) return 3;
|
int blen = read_block_bytes(mpk, fsize, buf, sizeof buf);
|
||||||
if (memcmp(blk.magic, MEOW_SIG_MAGIC, MEOW_SIG_MAGIC_LEN) != 0) return 2; /* 无 MEOWSIG1 footer */
|
if (blen < 0) return blen == -2 ? 2 : 3;
|
||||||
if (blk.alg != MEOW_SIG_ALG_ED25519 || blk.block_len != MEOW_SIG_BLOCK_LEN) return 1; /* 格式错 */
|
if (memcmp(buf, MEOW_SIG_MAGIC, MEOW_SIG_MAGIC_LEN) != 0) return 2; /* 无 MEOWSIG1 footer */
|
||||||
|
if (buf[8] != MEOW_SIG_ALG_ED25519) return 1; /* 算法不符 */
|
||||||
|
|
||||||
char pkgbuf[MEOW_SIG_PKG_MAX];
|
int v2 = (blen == (int)MEOW_SIG_BLOCK_LEN_V2);
|
||||||
|
if (!v2 && blen != (int)MEOW_SIG_BLOCK_LEN_V1) return 1; /* 块长未知:格式错 */
|
||||||
|
uint8_t want_flags = v2 ? MEOW_SIG_FLAG_TS : 0;
|
||||||
|
if ((buf[9] & MEOW_SIG_FLAG_TS) != want_flags) return 1; /* flags 与版本不符 */
|
||||||
|
|
||||||
|
char pkgsig[MEOW_SIG_PKG_MAX + 1];
|
||||||
|
memcpy(pkgsig, buf + MEOW_SIG_OFF_PKG, MEOW_SIG_PKG_MAX);
|
||||||
|
pkgsig[MEOW_SIG_PKG_MAX] = 0; /* 保证 NUL 终止 */
|
||||||
|
|
||||||
|
char pkgbuf[MEOW_SIG_PKG_MAX + 1];
|
||||||
if (pkg && *pkg) {
|
if (pkg && *pkg) {
|
||||||
snprintf(pkgbuf, sizeof pkgbuf, "%s", pkg);
|
snprintf(pkgbuf, sizeof pkgbuf, "%s", pkg);
|
||||||
} else {
|
} else {
|
||||||
if (derive_pkg(mpk, pkgbuf, sizeof pkgbuf) != 0) return 3;
|
if (derive_pkg(mpk, pkgbuf, sizeof pkgbuf) != 0) return 3;
|
||||||
}
|
}
|
||||||
blk.package[MEOW_SIG_PKG_MAX - 1] = 0; /* 保证 NUL 终止 */
|
|
||||||
/* 绑定①:期望包名 == footer.package */
|
/* 绑定①:期望包名 == footer.package */
|
||||||
if (strcmp(pkgbuf, blk.package) != 0) {
|
if (strcmp(pkgbuf, pkgsig) != 0) {
|
||||||
fprintf(stderr, "meow_verify: package mismatch (want %s, sig %s)\n", pkgbuf, blk.package);
|
fprintf(stderr, "meow_verify: package mismatch (want %s, sig %s)\n", pkgbuf, pkgsig);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* payload 哈希 */
|
/* payload 哈希 */
|
||||||
long long plen = fsize - (long long)MEOW_SIG_BLOCK_LEN;
|
long long plen = fsize - (long long)blen;
|
||||||
uint8_t sha[MEOW_SIG_SHA256_LEN];
|
uint8_t sha[MEOW_SIG_SHA256_LEN];
|
||||||
if (sha256_prefix(mpk, plen, sha) != 0) return 3;
|
if (sha256_prefix(mpk, plen, sha) != 0) return 3;
|
||||||
if (memcmp(sha, blk.payload_sha256, MEOW_SIG_SHA256_LEN) != 0) {
|
if (memcmp(buf + MEOW_SIG_OFF_SHA, sha, MEOW_SIG_SHA256_LEN) != 0) {
|
||||||
fprintf(stderr, "meow_verify: payload sha256 mismatch (content tampered)\n");
|
fprintf(stderr, "meow_verify: payload sha256 mismatch (content tampered)\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 重组被签消息 + 验签 */
|
/* 重组被签消息(v2 含 ts)+ 验签(多公钥,任一通过即放行) */
|
||||||
|
uint32_t ts = v2 ? meow_le32(buf + MEOW_SIG_OFF_TS) : 0;
|
||||||
char msg[MEOW_SIG_MSG_MAX];
|
char msg[MEOW_SIG_MSG_MAX];
|
||||||
unsigned mlen = meow_sig_msg_build(msg, blk.package, blk.payload_sha256);
|
unsigned mlen;
|
||||||
if (blk.msg_len != mlen) { fprintf(stderr, "meow_verify: msg_len field mismatch\n"); return 1; }
|
if (v2) mlen = meow_sig_msg_build_v2(msg, pkgsig, sha, ts);
|
||||||
|
else mlen = meow_sig_msg_build(msg, pkgsig, sha);
|
||||||
|
uint32_t blk_msglen = meow_le32(buf + (v2 ? MEOW_SIG_OFF_MSGLEN_V2 : MEOW_SIG_OFF_MSGLEN_V1));
|
||||||
|
if (blk_msglen != mlen) { fprintf(stderr, "meow_verify: msg_len field mismatch\n"); return 1; }
|
||||||
|
|
||||||
uint8_t sm[MEOW_SIG_SIG_LEN + MEOW_SIG_MSG_MAX];
|
uint8_t sm[MEOW_SIG_SIG_LEN + MEOW_SIG_MSG_MAX];
|
||||||
memcpy(sm, blk.sig, MEOW_SIG_SIG_LEN);
|
memcpy(sm, buf + MEOW_SIG_OFF_SIG, MEOW_SIG_SIG_LEN);
|
||||||
memcpy(sm + MEOW_SIG_SIG_LEN, msg, mlen);
|
memcpy(sm + MEOW_SIG_SIG_LEN, msg, mlen);
|
||||||
uint8_t out[MEOW_SIG_SIG_LEN + MEOW_SIG_MSG_MAX];
|
uint8_t out[MEOW_SIG_SIG_LEN + MEOW_SIG_MSG_MAX];
|
||||||
|
int ok = 0;
|
||||||
|
for (int i = 0; i < MEOW_PUB_KEY_COUNT; i++) {
|
||||||
unsigned long long outlen = 0;
|
unsigned long long outlen = 0;
|
||||||
if (crypto_sign_open(out, &outlen, sm, (unsigned long long)(MEOW_SIG_SIG_LEN + mlen), g_meow_pub) != 0) {
|
if (crypto_sign_open(out, &outlen, sm, (unsigned long long)(MEOW_SIG_SIG_LEN + mlen),
|
||||||
|
g_meow_pub[i]) == 0) { ok = 1; break; }
|
||||||
|
}
|
||||||
|
if (!ok) {
|
||||||
fprintf(stderr, "meow_verify: ed25519 signature invalid\n");
|
fprintf(stderr, "meow_verify: ed25519 signature invalid\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%s\n", blk.package);
|
/* 2B:v2 块签名时间戳未来宽限校验(仅防「未来异常」,防重放/过期不做) */
|
||||||
|
if (v2) {
|
||||||
|
if (ts == 0) { fprintf(stderr, "meow_verify: ts field is zero (malformed)\n"); return 1; }
|
||||||
|
time_t now = time(NULL);
|
||||||
|
if ((time_t)ts > now + (time_t)MEOW_SIG_TS_GRACE) {
|
||||||
|
fprintf(stderr, "meow_verify: signature ts in future beyond grace (ts=%u now=%ld)\n",
|
||||||
|
ts, (long)now);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s\n", pkgsig);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,11 +192,11 @@ static int strip_mpk(const char *mpk)
|
|||||||
if (file_size(mpk, &fsize) != 0) return 3;
|
if (file_size(mpk, &fsize) != 0) return 3;
|
||||||
long long payload = fsize;
|
long long payload = fsize;
|
||||||
int has_sig = 0;
|
int has_sig = 0;
|
||||||
if (fsize >= (long long)MEOW_SIG_BLOCK_LEN) {
|
if (fsize >= (long long)MEOW_SIG_BLOCK_LEN_V1) {
|
||||||
meow_sig_block_t blk;
|
uint8_t buf[MEOW_SIG_BLOCK_MAX];
|
||||||
if (read_footer(mpk, fsize, &blk) == 0 &&
|
int blen = read_block_bytes(mpk, fsize, buf, sizeof buf);
|
||||||
memcmp(blk.magic, MEOW_SIG_MAGIC, MEOW_SIG_MAGIC_LEN) == 0) {
|
if (blen >= 0 && memcmp(buf, MEOW_SIG_MAGIC, MEOW_SIG_MAGIC_LEN) == 0) {
|
||||||
payload = fsize - (long long)MEOW_SIG_BLOCK_LEN;
|
payload = fsize - (long long)blen;
|
||||||
has_sig = 1;
|
has_sig = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,10 +219,11 @@ static int has_sig(const char *mpk)
|
|||||||
{
|
{
|
||||||
long long fsize;
|
long long fsize;
|
||||||
if (file_size(mpk, &fsize) != 0) return 3;
|
if (file_size(mpk, &fsize) != 0) return 3;
|
||||||
if (fsize < (long long)MEOW_SIG_BLOCK_LEN) { printf("no\n"); return 2; }
|
if (fsize < (long long)MEOW_SIG_BLOCK_LEN_V1) { printf("no\n"); return 2; }
|
||||||
meow_sig_block_t blk;
|
uint8_t buf[MEOW_SIG_BLOCK_MAX];
|
||||||
if (read_footer(mpk, fsize, &blk) != 0) { printf("no\n"); return 2; }
|
int blen = read_block_bytes(mpk, fsize, buf, sizeof buf);
|
||||||
int h = (memcmp(blk.magic, MEOW_SIG_MAGIC, MEOW_SIG_MAGIC_LEN) == 0);
|
if (blen < 0) { printf("no\n"); return 2; }
|
||||||
|
int h = (memcmp(buf, MEOW_SIG_MAGIC, MEOW_SIG_MAGIC_LEN) == 0);
|
||||||
printf(h ? "yes\n" : "no\n");
|
printf(h ? "yes\n" : "no\n");
|
||||||
return h ? 0 : 2;
|
return h ? 0 : 2;
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Reference in new issue
Block a user