feat(sec): 二期签名鉴权 2A 最小闭环 (agent-185) — meow_verify(Ed25519验签CLI,公钥固化,TweetNaCl+SHA256单文件) + meow_sign(宿主keygen/sign,尾部180B footer签名块,私钥仅.keys/不入库) + nyai/meow_pkg_install 验签分级(有签必验/os.meow.*无签拒装/第三方直装告警/三重绑定/原签存.mpk.src导出保留) + svc_power IPC鉴权(SO_PEERCRED→/proc/pid/exe→包名/系统身份allowlist,系统动作仅os.meow.*或系统身份) + cmd_upgrade同步签名分级防绕过

meow_verify 37e50606; nyai f2938d28; svc_power e0494eb7; launcher 24c9ee5d(含热重载)
宿主验证12项全过(正例/篡负载/篡包名/无签系统包拒/第三方直装/降级拒/导出round-trip byte-identical/verify缺失宁缺勿装/footer容忍/E2E PERM/鉴权分类)
出入: TweetNaCl替orlp/ed25519(任务允许同等实现); 读级暂仍uid==0(用户随后定案放宽到任意调用者,待改); 系统身份allowlist为启发式
This commit is contained in:
MeowOS committed 2026-08-31 04:33:46 +08:00
1 parent f50abae02a
commit 3f57d7fd4f
22 files changed
+1978 -15

No files matched your search

+115 -1
View File
@@ -506,6 +506,82 @@ static int meow_app_prepare(const char *exec)
}
/* ---- II 期签名校验 / 导出保留签名(MPK_SIGNATURE_DESIGN §5.1/§5.4,shell-out meow_verify) ---- */
/* meow_verify --system <mpk> --package <pkg>。返回 0=有效签 /1=有签不通过 /2=无签名 /127=工具不可用。 */
static int pkg_verify_sig(const char *mpk, const char *pkg)
{
const char *vb = getenv("MEOW_VERIFY_BIN");
if (!vb || !*vb) vb = "/usr/bin/meow_verify";
char cmd[700];
snprintf(cmd, sizeof cmd, "'%s' --system '%s' --package '%s' >/dev/null 2>&1", vb, mpk, pkg);
int rc = system(cmd);
if (rc == -1) return 127;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return 127;
}
/* 读已解压 app.json 的某字符串字段(用 launcher 的 json_str;返回 0 成功)。 */
static int pkg_json_field(const char *appjs, const char *key, char *out, int n)
{
if (!appjs || !key || n <= 0) return -1;
FILE *f = fopen(appjs, "r");
if (!f) return -1;
static char j[16384];
size_t r = fread(j, 1, sizeof j - 1, f);
fclose(f);
j[r] = 0;
if (!r) return -1;
if (json_str(j, key, out, n) <= 0) { out[0] = 0; return -1; }
return 0;
}
/* 读 .mpk 内 app.json 的某字符串字段(gzip -dc | tar -xO app.json;返回 0 成功)。 */
static int pkg_json_field_mpk(const char *mpk, const char *key, char *out, int n)
{
char cmd[700];
snprintf(cmd, sizeof cmd, "gzip -dc '%s' 2>/dev/null | tar -xO app.json 2>/dev/null", mpk);
FILE *pf = popen(cmd, "r");
if (!pf) return -1;
static char j[16384];
size_t r = fread(j, 1, sizeof j - 1, pf);
pclose(pf);
if (!r) return -1;
j[r] = 0;
if (json_str(j, key, out, n) <= 0) { out[0] = 0; return -1; }
return 0;
}
/* 安装成功后,把原签 .mpk 另存 MeowData/<pkg>/data/<pkg>.mpk.src(导出保留签名)。 */
static void pkg_save_src(const char *mpk, const char *pkg)
{
char dst[320], cmd[720];
snprintf(dst, sizeof dst, "/usr/data/mnt/sd_0/MeowData/%s/data/%s.mpk.src", pkg, pkg);
snprintf(cmd, sizeof cmd, "mkdir -p '/usr/data/mnt/sd_0/MeowData/%s/data' 2>/dev/null && "
"cp '%s' '%s' 2>/dev/null", pkg, mpk, dst);
if (system(cmd) != 0) logmsg("pkg_install: save .mpk.src fail for %s", pkg);
}
/* 导出保留签名:缓存 .mpk.src 存在 + 验签通过 + 其 app.json 版本 == 已装 → 直接复制(不二次签名)。
* 成功 0;否则 -1(调用方现场重打包,无签名)。 */
static int pkg_export_preserve_sig(const char *pkg, const char *dst)
{
char srcp[320], instj[280], cmd[760], tmp[340];
snprintf(srcp, sizeof srcp, "/usr/data/mnt/sd_0/MeowData/%s/data/%s.mpk.src", pkg, pkg);
if (access(srcp, R_OK) != 0) return -1;
if (pkg_verify_sig(srcp, pkg) != 0) { logmsg("pkg_export: cached .mpk.src sig fail %s", pkg); return -1; }
char cv[64], iv[64];
snprintf(instj, sizeof instj, "/usr/data/mnt/sd_0/MeowAPPs/%s/app.json", pkg);
if (pkg_json_field(instj, "version", iv, sizeof iv) != 0) return -1;
if (pkg_json_field_mpk(srcp, "version", cv, sizeof cv) != 0) return -1;
if (strcmp(iv, cv) != 0) { logmsg("pkg_export: cached version %s != installed %s", cv, iv); return -1; }
snprintf(tmp, sizeof tmp, "%s.tmp", dst);
snprintf(cmd, sizeof cmd, "cp '%s' '%s' 2>/dev/null", srcp, tmp);
if (system(cmd) != 0 || access(tmp, R_OK) != 0) return -1;
if (rename(tmp, dst) != 0) { unlink(tmp); return -1; }
return 0;
}
/* .mpk 安装(本期接口/CLI + 文档;UI 后期):解压 .mpk → MeowAPPs/<pkg>/。
* 约定 .mpk = tar.gz,顶层即包目录(含 app.json);包名 = 文件名茎 <pkg>.mpk。
* 同名先备份;解压后校验 app.json;随后数据 init(MeowData/<pkg>/{data,cache})。
@@ -525,9 +601,25 @@ int meow_pkg_install(const char *mpk_path)
if (len <= 0 || len >= (int)sizeof pkg) { logmsg("pkg_install: bad pkg name"); return -1; }
memcpy(pkg, bn, len); pkg[len] = 0;
/* 目标 MeowAPPs/<pkg>/;同名先备份(.bak_<pid>),再解压,再校验 app.json */
char dst[256], bak[256], cmd[700];
char dst[256], bak[256], cmd[760];
snprintf(dst, sizeof dst, "/usr/data/mnt/sd_0/MeowAPPs/%s", pkg);
snprintf(bak, sizeof bak, "%s.bak_%d", dst, (int)getpid());
int replacing = (access(dst, F_OK) == 0);
/* II 期:签名分级先于解包(MPK_SIGNATURE_DESIGN §5.2:先验签再解包,失败不落盘)。
* 有签名必须验签(失败拒装);os.meow.* 必须有有效签名;无签名第三方放行(告警)。 */
int vr = pkg_verify_sig(mpk_path, pkg);
int sysp = (strncmp(pkg, "os.meow.", 8) == 0);
if (vr == 1) { logmsg("pkg_install: sig invalid, reject %s", pkg); return -3; }
else if (vr == 2) {
if (sysp) { logmsg("pkg_install: sys pkg %s must be signed, rejected", pkg); return -3; }
logmsg("pkg_install: %s unsigned (3rd party allowed)", pkg);
}
else if (vr != 0) {
if (sysp) { logmsg("pkg_install: meow_verify unavailable rc=%d, sys pkg reject", vr); return -3; }
logmsg("pkg_install: meow_verify unavailable rc=%d, %s unverified (3rd party)", vr, pkg);
}
/* BusyBox tar 无 -z:用 gzip -dc | tar -x 处理 tar.gz。 */
snprintf(cmd, sizeof cmd,
"rm -rf '%s' 2>/dev/null; "
@@ -535,10 +627,27 @@ int meow_pkg_install(const char *mpk_path)
"mkdir -p '%s' && gzip -dc '%s' | tar -x -C '%s' 2>/dev/null && "
"[ -f '%s/app.json' ]", bak, dst, dst, bak, dst, mpk_path, dst, dst);
if (system(cmd) != 0) { logmsg("pkg_install: extract/validate fail for %s", pkg); return -1; }
/* II 期:app.json.package 绑定(== 包名,禁改包名/目录改名;防第三方面具系统包)。 */
{
char appj[280], ph[64];
snprintf(appj, sizeof appj, "/usr/data/mnt/sd_0/MeowAPPs/%s/app.json", pkg);
if (pkg_json_field(appj, "package", ph, sizeof ph) == 0 && strcmp(ph, pkg) != 0) {
logmsg("pkg_install: app.json.package %s != %s, reject", ph, pkg);
char rb[760];
snprintf(rb, sizeof rb, "rm -rf '%s' 2>/dev/null; [ -d '%s' ] && mv '%s' '%s' 2>/dev/null",
dst, bak, bak, dst);
system(rb);
return -3;
}
}
/* 数据 init:MeowData/<pkg>/{data,cache} */
snprintf(cmd, sizeof cmd, "mkdir -p '/usr/data/mnt/sd_0/MeowData/%s/data' "
"'/usr/data/mnt/sd_0/MeowData/%s/cache' 2>/dev/null", pkg, pkg);
system(cmd);
/* 有效签 → 原签 .mpk 另存 MeowData/<pkg>/data/<pkg>.mpk.src(导出保留签名) */
if (vr == 0) pkg_save_src(mpk_path, pkg);
logmsg("pkg_install: %s installed (old backed up %s)", pkg, bak);
return 0;
}
@@ -556,6 +665,11 @@ int meow_pkg_export(const char *pkg, const char *out_path)
snprintf(src, sizeof src, "/usr/data/mnt/sd_0/MeowAPPs/%s", pkg);
if (access(src, R_OK) != 0) { logmsg("pkg_export: no such pkg"); return -1; }
snprintf(dst, sizeof dst, "%s", out_path);
/* II 期:导出保留签名 —— 若安装时存了原签 .mpk.src 且其版本==已装 → 直接复制(不二次签名)。 */
if (pkg_export_preserve_sig(pkg, dst) == 0) {
logmsg("pkg_export: %s -> %s (signed, preserved)", pkg, dst);
return 0;
}
/* BusyBox tar 无 -z:tar -c | gzip -c > out(先写 .tmp 再 mv,防半成品)。 */
char cmd[512];
snprintf(cmd, sizeof cmd, "tar -c -C '%s' . | gzip -c > '%s.tmp' 2>/dev/null && "
+28
View File
@@ -0,0 +1,28 @@
#!/bin/bash
# build_sign.sh — meow_sign 宿主编译(产签工具;非目标机二进制,无需 mipsel)。
#
# 用法:
# ./build_sign.sh # 宿主编译 → patch_v2/meow_sign/meow_sign
# ./build_sign.sh static # 静态宿主编译(可选,便于分发/一致验证)
set -e
cd "$(dirname "$0")"
ROOT="$HOME/Project/R3ProII"
HOST_CC="${CC_HOST:-cc}"
V="$ROOT/patch_v2/meow_verify"
# tweetnacl.c 为 vendor 代码:放宽 -Wno-sign-compare / -Wno-unused-function(其 u64/u32 与 int 混比)。
CFLAGS="-Os -Wall -Wextra"
if [ "$1" = "static" ]; then
"$HOST_CC" $CFLAGS -static meow_sign.c "$V/tweetnacl.c" "$V/sha256.c" -o meow_sign_static
echo "== meow_sign (static) =="
ls -la meow_sign_static; md5sum meow_sign_static; file meow_sign_static
exit 0
fi
# tweetnacl.c 单独编译并放宽警告;meow_sign.c 全警告编译
"$HOST_CC" -Os -Wno-sign-compare -Wno-unused-function -c "$V/tweetnacl.c" -o tweetnacl_meow.o
"$HOST_CC" -Os -Wall -Wextra -c "$V/sha256.c" -o sha256_meow.o
"$HOST_CC" $CFLAGS meow_sign.c tweetnacl_meow.o sha256_meow.o -o meow_sign
rm -f tweetnacl_meow.o sha256_meow.o
echo "== meow_sign (host) =="
ls -la meow_sign; md5sum meow_sign; file meow_sign
BIN
View File
Binary file not shown.
+184
View File
@@ -0,0 +1,184 @@
/* meow_sign.c — MeowOS .mpk 产签工具(宿主端,非设备;离线维护者侧使用)。
*
* 功能(MPK_SIGNATURE_DESIGN §4.2):
* - keygen <priv_path>:生成 Ed25519 密钥对。私钥(64B seed||pub,0600)写 <priv_path>;
* 公钥(32B hex + raw)写 <priv_path>.pub,并在 stdout 打印 hex(便于固化进 meow_verify)。
* - sign <pkg>.mpk <priv_path>:对 .mpk(当前无 footer = 纯 tar.gz)计算 payload_sha256、
* 组被签消息、Ed25519 签名,并**尾部追加** 180B 定长 footer(.mpk = tar.gz || meow_sig_block)。
*
* 私钥绝不落设备、绝不入库(仓库 .gitignore 已忽略 .keys/)。
* 复用 ../meow_verify 的 tweetnacl.c(Ed25519)+ sha256.c。 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include "../meow_verify/tweetnacl.h"
#include "../meow_verify/sha256.h"
#include "../meow_verify/meow_sig.h"
/* keygen 需真随机(从 /dev/urandom 读);sign 不调用 keypair。 */
void randombytes(unsigned char *x, unsigned long long xlen)
{
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) { fprintf(stderr, "meow_sign: /dev/urandom open fail: %s\n", strerror(errno)); exit(1); }
size_t off = 0;
while (off < (size_t)xlen) {
ssize_t r = read(fd, x + off, (size_t)xlen - off);
if (r <= 0) { close(fd); fprintf(stderr, "meow_sign: urandom read fail\n"); exit(1); }
off += (size_t)r;
}
close(fd);
}
static int write_file(const char *path, const void *buf, size_t n, mode_t mode)
{
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
if (fd < 0) return -1;
size_t off = 0;
while (off < n) {
ssize_t w = write(fd, (const char *)buf + off, n - off);
if (w <= 0) { close(fd); return -1; }
off += (size_t)w;
}
close(fd);
return 0;
}
static void hex_print(const char *label, const uint8_t *b, int n)
{
printf("%s=", label);
for (int i = 0; i < n; i++) printf("%02x", b[i]);
printf("\n");
}
static int keygen(const char *priv_path)
{
uint8_t pk[32], sk[64];
crypto_sign_keypair(pk, sk);
if (write_file(priv_path, sk, sizeof sk, 0600) != 0) {
fprintf(stderr, "meow_sign: write %s fail: %s\n", priv_path, strerror(errno));
return 1;
}
char pub_path[512];
snprintf(pub_path, sizeof pub_path, "%s.pub", priv_path);
if (write_file(pub_path, pk, sizeof pk, 0644) != 0) {
fprintf(stderr, "meow_sign: write %s fail: %s\n", pub_path, strerror(errno));
return 1;
}
printf("keygen ok:\n priv -> %s (0640? no: 0600)\n pub -> %s\n", priv_path, pub_path);
hex_print("pub_hex", pk, 32);
return 0;
}
/* 分块 SHA-256 计算整文件(其内容作为 payload;此时文件尚无 footer)。 */
static int file_sha256(const char *path, 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];
for (;;) {
ssize_t r = read(fd, buf, sizeof buf);
if (r <= 0) break;
meow_sha256_update(&c, buf, (size_t)r);
}
close(fd);
meow_sha256_final(&c, out);
return 0;
}
static int derive_pkg(const char *mpk, char *out, int n)
{
const char *bn = strrchr(mpk, '/');
bn = bn ? bn + 1 : mpk;
const char *dot = strrchr(bn, '.');
if (!dot || strcmp(dot, ".mpk") != 0) return -1;
int len = (int)(dot - bn);
if (len <= 0 || len >= n) return -1;
memcpy(out, bn, (size_t)len); out[len] = 0;
return 0;
}
static int sign(const char *mpk, const char *priv_path)
{
char pkg[MEOW_SIG_PKG_MAX];
if (derive_pkg(mpk, pkg, sizeof pkg) != 0) {
fprintf(stderr, "meow_sign: 需要 <pkg>.mpk(包名=文件名茎)\n");
return 1;
}
/* 读私钥(64B seed||pub) */
int fd = open(priv_path, O_RDONLY);
if (fd < 0) { fprintf(stderr, "meow_sign: open %s fail: %s\n", priv_path, strerror(errno)); return 1; }
uint8_t sk[64];
size_t off = 0;
while (off < sizeof sk) {
ssize_t r = read(fd, sk + off, sizeof sk - off);
if (r <= 0) { close(fd); fprintf(stderr, "meow_sign: priv key too short\n"); return 1; }
off += (size_t)r;
}
close(fd);
/* 计算 payload sha256(当前文件无 footer) */
uint8_t sha[MEOW_SIG_SHA256_LEN];
if (file_sha256(mpk, sha) != 0) { fprintf(stderr, "meow_sign: read %s fail\n", mpk); return 1; }
/* 组被签消息 + 签出 64B 分离签名 */
char msg[MEOW_SIG_MSG_MAX];
unsigned mlen = meow_sig_msg_build(msg, pkg, sha);
uint8_t sm[MEOW_SIG_SIG_LEN + MEOW_SIG_MSG_MAX];
unsigned long long smlen = 0;
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; }
/* 构造 footer 并尾部追加 */
meow_sig_block_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 = 0;
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.msg_len = mlen;
blk.block_len = MEOW_SIG_BLOCK_LEN;
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);
close(afd);
if (w != MEOW_SIG_BLOCK_LEN) { fprintf(stderr, "meow_sign: footer write fail\n"); return 1; }
printf("sign ok: %s package=%s msg_len=%u block_len=%u payload_sha256=",
mpk, pkg, mlen, (unsigned)blk.block_len);
hex_print("", sha, 32);
return 0;
}
static void usage(const char *a0)
{
fprintf(stderr,
"usage:\n"
" %s keygen <priv_path> 生成密钥对(priv 0600 + .pub,明文、不破坏现有)\n"
" %s sign <pkg>.mpk <priv_path> 对 .mpk 尾部追加 Ed25519 签名 footer\n", a0, a0);
}
int main(int argc, char **argv)
{
if (argc < 2) { usage(argv[0]); return 2; }
if (!strcmp(argv[1], "keygen")) {
if (argc < 3) { usage(argv[0]); return 2; }
return keygen(argv[2]);
}
if (!strcmp(argv[1], "sign")) {
if (argc < 4) { usage(argv[0]); return 2; }
return sign(argv[2], argv[3]);
}
usage(argv[0]);
return 2;
}
Binary file not shown.
+106 -10
View File
@@ -10,8 +10,12 @@
* 动作逻辑与 launcher `power_menu.c` 的 `sys_do_reboot`/`set_boot_default_hiby`/`bootmode.sh Recovery`
* **同源 0-diff 搬移**,仅入口(CLI vs IPC)与鉴权层不同。
*
* 鉴权(一期基础校验):IPC 服务端受理 MSG_POWER_ACTION 前以 `SO_PEERCRED` 校验调用方 `uid==0`(root)
* ——非 root 直接 `MEOW_ERR_PERM` 拒绝;完整 `os.meow.*` 包名鉴权(§5)留二期。CLI=root 免鉴权。
* 鉴权(II 期,MPK_SIGNATURE_DESIGN §6):IPC 服务端按【连接级身份】分级——`SO_PEERCRED` 取 pid →
* `readlink /proc/pid/exe` → 命中 `MeowAPPs/<pkg>/` 判定包名,或命中系统二进制 allowlist
* (/usr/bin/meow_*、/usr/data/meow/meow*、MeowPanel 后端)判定为系统身份;其余为 OTHER。
* 动作级策略:系统动作(shutdown/reboot/upgrade/back_hiby/set-default-*)仅放行 `os.meow.*` 包或系统身份;
* 读/状态类(status/battery/reboot_reason/upgrade_status/screen/dac)放行所有 root 调用方;非 root 一律拒。
* `MSG_HELLO` 自报 app_id 不在此作权威(仅交叉校验,见 §6.1)。CLI=root 免鉴权。
*
* 通知转储归属(§3.2/§3.3 已定案):通知存储为 launcher 进程内存(meow_notif.c g_slots[]),服务跨进程
* 不可达——**转储由调用方 launcher 先做**,服务对关机/重启/升级一律 `sync()` 落盘,不重实现转储。
@@ -593,15 +597,99 @@ static int send_power_status(int fd, uint32_t req_id, const m_power_status_t *st
return send_frame(fd, MSG_POWER_STATUS, req_id, &s, sizeof s);
}
/* ---------------- II 期:调用方身份解析(连接级,MPK_SIGNATURE_DESIGN §6.2) ---------------- */
typedef enum { CALL_OTHER, CALL_PKG, CALL_SYS } caller_kind_t;
typedef struct {
caller_kind_t kind;
char pkg[MEOW_APP_ID_MAX]; /* CALL_PKG=MeowAPPs/<pkg>/ 取包名;CALL_SYS=系统身份别名 */
uid_t uid;
} caller_t;
/* 系统动作(SYSTEM_FRAMEWORK_SERVICES §2.4 + 设计 §6.3 表):须 os.meow.* 包或系统身份。 */
static int is_system_action(uint32_t cmd)
{
switch (cmd) {
case POWER_SHUTDOWN: case POWER_REBOOT: case POWER_UPGRADE:
case POWER_BACK_HIBY: case POWER_SET_DEFAULT_MEOW: case POWER_SET_DEFAULT_HIBY:
return 1;
default:
return 0;
}
}
/* 是否可信系统身份:CALL_SYS(受信系统二进制)或 CALL_PKG 且包前缀 os.meow.*。 */
static int is_sys_identity(const caller_t *c)
{
if (c->kind == CALL_SYS) return 1;
if (c->kind == CALL_PKG && !strncmp(c->pkg, "os.meow.", 8)) return 1;
return 0;
}
/* 由 exe 路径判身份(纯函数,便于宿主自测 + 生产共用)。前提:out 已 memset(kind 缺省 CALL_OTHER)。
* - exe 前缀 /usr/data/mnt/sd_0/MeowAPPs/ → MeowAPP 包(kind=CALL_PKG,pkg=目录名)
* - 否则命中系统二进制 allowlist → CALL_SYS
* - 其余保持 CALL_OTHER
* msg 自报 app_id 仅作交叉校验(不在此处做权威判定,见设计 §6.1/§6.5)。 */
static void classify_exe(const char *exe, caller_t *out)
{
/* MeowAPPs/<pkg>/(与 app_launcher.c meow_pkg_name 同口径:去前缀取第一段目录名) */
static const char *mp = "/usr/data/mnt/sd_0/MeowAPPs/";
if (strncmp(exe, mp, strlen(mp)) == 0) {
const char *p = exe + strlen(mp);
if (*p == '/') p++;
const char *end = strchr(p, '/');
int plen = end ? (int)(end - p) : (int)strlen(p);
if (plen > 0 && plen < MEOW_APP_ID_MAX) {
memcpy(out->pkg, p, (size_t)plen); out->pkg[plen] = 0;
out->kind = CALL_PKG;
return;
}
}
/* 系统二进制 allowlist(/usr/bin/meow_*、/usr/data/meow_*、MeowPanel 后端) */
char tmp[MEOW_APP_ID_MAX];
if (strcmp(exe, "/usr/bin/meow_launcherd") == 0) {
snprintf(tmp, sizeof tmp, "%s", "os.meow.launcher");
} else if (strstr(exe, "/MeowPanel/") || strstr(exe, "meowpanel")) {
snprintf(tmp, sizeof tmp, "%s", "os.meow.panel");
} else if (strncmp(exe, "/usr/bin/meow_", 14) == 0) {
/* 截取 "meow_xyz"(去 "/usr/bin/" 前缀)作近似别名 */
size_t l = strlen(exe + 11);
if (l >= sizeof tmp) l = sizeof tmp - 1;
memcpy(tmp, exe + 11, l); tmp[l] = 0;
} else if (strncmp(exe, "/usr/data/meow/", 15) == 0) {
snprintf(tmp, sizeof tmp, "%s", "os.meow.core");
} else {
return; /* CALL_OTHER */
}
snprintf(out->pkg, sizeof out->pkg, "%s", tmp);
out->kind = CALL_SYS;
}
/* 由连接 SO_PEERCRED 取 pid → readlink /proc/<pid>/exe → classify_exe(连接创建时缓存,循环内复用)。 */
static void classify_caller(int fd, caller_t *out)
{
memset(out, 0, sizeof *out);
out->uid = (uid_t)-1;
struct ucred cred;
socklen_t cl = sizeof cred;
if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &cl) != 0) return;
out->uid = cred.uid;
char procpath[64], exe[256];
snprintf(procpath, sizeof procpath, "/proc/%d/exe", (int)cred.pid);
ssize_t n = readlink(procpath, exe, sizeof exe - 1);
if (n <= 0) return;
exe[n] = 0;
classify_exe(exe, out);
}
/* 处理单条连接(连接内循环读帧:客户端先 MSG_HELLO 握手,再 MSG_POWER_ACTION)。 */
static void handle_conn(int c)
{
/* 基础鉴权:调用方 uid==0(root)才受理动作。 */
struct ucred cred;
socklen_t cl = sizeof cred;
int uid0 = 0;
if (getsockopt(c, SOL_SOCKET, SO_PEERCRED, &cred, &cl) == 0 && cred.uid == 0)
uid0 = 1;
/* II 期鉴权升级(MPK_SIGNATURE_DESIGN §6.2):uid==0 判定升级为连接级身份解析(pid→exe→包名/系统身份)。 */
caller_t caller;
classify_caller(c, &caller);
for (;;) {
uint8_t hdr[MEOW_IPC_HDR_LEN];
@@ -629,12 +717,20 @@ static void handle_conn(int c)
memset(&a, 0, sizeof a);
if (rlen > sizeof a) rlen = sizeof a;
memcpy(&a, payload, rlen);
if (!uid0) {
/* 动作级策略(设计 §6.3):非 root 一律拒;系统动作须 os.meow.* 包或受信系统身份;
* 读/状态类(STATUS/BATTERY/REBOOT_REASON/UPGRADE_STATUS/SCREEN/DAC)放行所有 root 调用方。 */
int allow = 0;
if (caller.uid == 0) {
if (is_system_action(a.cmd)) allow = is_sys_identity(&caller);
else allow = 1;
}
if (!allow) {
m_power_status_t st;
memset(&st, 0, sizeof st);
st.code = MEOW_ERR_PERM;
st.cmd = a.cmd;
snprintf(st.msg, sizeof st.msg, "permission denied (uid!=0)");
snprintf(st.msg, sizeof st.msg, "permission denied (caller kind=%d uid=%d)",
(int)caller.kind, (int)caller.uid);
send_power_status(c, req_id, &st);
continue;
}
Binary file not shown.
@@ -0,0 +1,93 @@
/* svc_power_authtest.c — meow_svc_power II 期 IPC 鉴权逻辑 宿主自测(本程序非生产代码)。
*
* 通过 #include 生产源文件(meow_svc_power.c,用 #define main 改名避免入口冲突),在同一 TU 内直接
* 访问其 FILE-STATIC 函数 classify_exe / is_system_action / is_sys_identity,验证鉴别/分级策略的决策表。
* 覆盖「真机需特权路径 /proc/pid/exe 判定」之外的所有纯逻辑分支;真实 pid→exe 解析留真机验证。
*
* 用途:dev docs 复现级宿主自测;不进入固件/不部署。用法:
* cc -Wall -Wextra -I../meow_launcherd svc_power_authtest.c -o svc_power_authtest && ./svc_power_authtest
*/
#define main meow_svc_power_prod_main_unused
#include "meow_svc_power.c"
#undef main
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static int g_fail = 0;
#define CHECK(cond) do { if (!(cond)) { printf(" FAIL: %s (line %d)\n", #cond, __LINE__); g_fail++; } \
else { printf(" ok : %s\n", #cond); } } while (0)
int main(void)
{
caller_t c;
printf("== classify_exe ==\n");
memset(&c, 0, sizeof c);
classify_exe("/usr/data/mnt/sd_0/MeowAPPs/os.meow.tst/meow", &c);
CHECK(c.kind == CALL_PKG && strcmp(c.pkg, "os.meow.tst") == 0);
memset(&c, 0, sizeof c);
classify_exe("/usr/data/mnt/sd_0/MeowAPPs/com.example.tool/meow", &c);
CHECK(c.kind == CALL_PKG && strcmp(c.pkg, "com.example.tool") == 0);
memset(&c, 0, sizeof c);
classify_exe("/usr/data/mnt/sd_0/MeowAPPs/os.meow.tst/sub/meow", &c);
CHECK(c.kind == CALL_PKG && strcmp(c.pkg, "os.meow.tst") == 0); /* 取首段目录 */
memset(&c, 0, sizeof c);
classify_exe("/usr/bin/meow_launcherd", &c);
CHECK(c.kind == CALL_SYS && strcmp(c.pkg, "os.meow.launcher") == 0);
memset(&c, 0, sizeof c);
classify_exe("/usr/bin/meow_svc_power", &c);
CHECK(c.kind == CALL_SYS); /* /usr/bin/meow_* 前缀批准 */
memset(&c, 0, sizeof 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);
memset(&c, 0, sizeof c);
classify_exe("/usr/data/meow/settings_tool", &c);
CHECK(c.kind == CALL_SYS); /* /usr/data/meow/ 批准 */
memset(&c, 0, sizeof c);
classify_exe("/usr/bin/bash", &c);
CHECK(c.kind == CALL_OTHER);
memset(&c, 0, sizeof c);
classify_exe("/bin/sh", &c);
CHECK(c.kind == CALL_OTHER);
memset(&c, 0, sizeof c);
classify_exe("/usr/data/mnt/sd_0/MeowAPPs/justadirty/k", &c);
CHECK(c.kind == CALL_PKG && strcmp(c.pkg, "justadirty") == 0); /* 第三方包(非 os.meow.*) */
CHECK(is_sys_identity(&c) == 0); /* 非系统身份 → 系统动作被拒 */
printf("== is_sys_identity ==\n");
c.kind = CALL_PKG; snprintf(c.pkg, sizeof c.pkg, "%s", "os.meow.tst");
CHECK(is_sys_identity(&c) == 1);
c.kind = CALL_PKG; snprintf(c.pkg, sizeof c.pkg, "%s", "com.example.tool");
CHECK(is_sys_identity(&c) == 0);
c.kind = CALL_SYS; snprintf(c.pkg, sizeof c.pkg, "%s", "os.meow.launcher");
CHECK(is_sys_identity(&c) == 1);
c.kind = CALL_OTHER; c.pkg[0] = 0;
CHECK(is_sys_identity(&c) == 0);
printf("== is_system_action ==\n");
CHECK(is_system_action(POWER_SHUTDOWN) == 1);
CHECK(is_system_action(POWER_REBOOT) == 1);
CHECK(is_system_action(POWER_UPGRADE) == 1);
CHECK(is_system_action(POWER_BACK_HIBY) == 1);
CHECK(is_system_action(POWER_SET_DEFAULT_MEOW) == 1);
CHECK(is_system_action(POWER_SET_DEFAULT_HIBY) == 1);
CHECK(is_system_action(POWER_STATUS) == 0);
CHECK(is_system_action(POWER_BATTERY) == 0);
CHECK(is_system_action(POWER_REBOOT_REASON) == 0);
CHECK(is_system_action(POWER_UPGRADE_STATUS) == 0);
CHECK(is_system_action(POWER_SCREEN) == 0);
CHECK(is_system_action(POWER_DAC) == 0);
if (g_fail) { printf("\nAUTH TEST: %d FAIL\n", g_fail); return 1; }
printf("\nAUTH TEST: ALL PASS\n");
return 0;
}
+43
View File
@@ -0,0 +1,43 @@
#!/bin/bash
# build_verify.sh — meow_verify 交叉编译(mipsel 静态,固件 /usr/bin/meow_verify)+ 宿主编译可选。
#
# 目标:R3 Pro II / MeowOS(mipsel-linux-muslsf,静态)。纯 POSIX+Linux(无外部依赖,除 vendor
# tweetnacl.c/sha256.c 全内编)。公钥编译期固化:meow_signing_pub.h(由 meow_sign keygen 生成)。
#
# 警告策略(诚实标注):本项目自有代码为 0 warning;
# - meow_verify.c / sha256.c / meow_sig.h → -Wall -Wextra(0 warning)
# - tweetnacl.c(第三方 public-domain vendor 单文件)→ 单独对象 + -Wno-sign-compare
# -Wno-unterminated-string-initialization -Wno-unused-function(作者原写法:u64/u32 与 int 混比、
# "expand 32-byte k" 16B char 数组塞 17 字节。非本项目代码,局部放宽)。
# - 不启用 gc-sections:meow_verify.c 的 randombytes() stub 满足 crypto_sign_keypair 的引用,整链静态。
#
# 用法:
# ./build_verify.sh # 交叉编译 → patch_v2/meow_verify/meow_verify(mipsel 静态)
# ./build_verify.sh host # 宿主编译 → patch_v2/meow_verify/meow_verify_host
set -e
cd "$(dirname "$0")"
ROOT="$HOME/Project/R3ProII"
CC="$ROOT/.tools/zig-mipsel-cc"
TNFLAGS="-Os -Wno-sign-compare -Wno-unterminated-string-initialization -Wno-unused-function -static"
if [ "$1" = "host" ]; then
HOST_CC="${CC_HOST:-cc}"
"$HOST_CC" -Os -Wno-sign-compare -Wno-unterminated-string-initialization -Wno-unused-function -c tweetnacl.c -o tweetnacl_v.o
"$HOST_CC" -Os -Wall -Wextra -c sha256.c -o sha256_v.o
"$HOST_CC" -Os -Wall -Wextra -c meow_verify.c -o meow_verify_v.o
"$HOST_CC" meow_verify_v.o sha256_v.o tweetnacl_v.o -o meow_verify_host
rm -f tweetnacl_v.o sha256_v.o meow_verify_v.o
echo "== meow_verify (host) =="
ls -la meow_verify_host; md5sum meow_verify_host; file meow_verify_host
exit 0
fi
"$CC" $TNFLAGS -c tweetnacl.c -o tweetnacl_v.o
"$CC" -Os -Wall -Wextra -static -c sha256.c -o sha256_v.o
"$CC" -Os -Wall -Wextra -static -c meow_verify.c -o meow_verify_v.o
"$CC" -Os -static meow_verify_v.o sha256_v.o tweetnacl_v.o -o meow_verify
rm -f tweetnacl_v.o sha256_v.o meow_verify_v.o
echo "== meow_verify (mipsel static) =="
ls -la meow_verify
md5sum meow_verify
file meow_verify
+77
View File
@@ -0,0 +1,77 @@
/* meow_sig.h — .mpk 签名块格式(MPK_SIGNATURE_DESIGN §3.1)+ 被签消息构造(§3.1)。
*
* .mpk = [ tar.gz bytes ......................... ] [ meow_sig_block(定长 LE)]
*
* meow_sig_block(packed,总长 MEOW_SIG_BLOCK_LEN):
* char magic[8]; "MEOWSIG1"
* uint8_t alg; 1 = ed25519
* uint8_t flags; 0
* uint16_t pad; (0)
* char package[64]; 权威包名(== app.json.package == 文件名茎)
* uint8_t payload_sha256[32];sha256(整个 .mpk 去掉尾部 footer 的其余字节)
* uint8_t sig[64]; Ed25519(pk, msg)
* uint32_t msg_len; 实际签名的 msg 字节数(LE)
* uint32_t block_len; 本块总长(LE;= MEOW_SIG_BLOCK_LEN)
*
* 被签消息 msg = "MEOWSIG1" || alg(1B) || package_len(4B,LE) || package || payload_sha256(32B)。
* 本文件由 meow_verify 与 meow_sign 共用(meow_sign 用相对路径 include)。 */
#ifndef MEOW_SIG_H
#define MEOW_SIG_H
#include <stdint.h>
#include <string.h>
#define MEOW_SIG_MAGIC "MEOWSIG1"
#define MEOW_SIG_MAGIC_LEN 8
#define MEOW_SIG_ALG_ED25519 1
#define MEOW_SIG_PKG_MAX 64
#define MEOW_SIG_SHA256_LEN 32
#define MEOW_SIG_SIG_LEN 64
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 msg_len;
uint32_t block_len;
} meow_sig_block_t;
#define MEOW_SIG_BLOCK_LEN (sizeof(meow_sig_block_t)) /* 定长 180 */
/* 按字节读取 LE 的 uint32(避免 packed 解引用对齐告警) */
static inline uint32_t meow_le32(const uint8_t *p)
{
return (uint32_t)p[0] | ((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
}
static inline void meow_le32_put(uint8_t *p, uint32_t v)
{
p[0] = (uint8_t)(v & 0xff);
p[1] = (uint8_t)((v >> 8) & 0xff);
p[2] = (uint8_t)((v >> 16) & 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)
/* 按设计构造被签消息 msg = "MEOWSIG1" || alg(1B) || package_len(4B,LE) || package || payload_sha256(32B)。
* 返回 msg 字节数。签名端与验签端必须用同一构造(meow_sign / meow_verify 共用)。 */
static inline unsigned meow_sig_msg_build(char *out, const char *package,
const uint8_t payload_sha256[MEOW_SIG_SHA256_LEN])
{
unsigned pkg_len = (unsigned)strlen(package);
if (pkg_len > MEOW_SIG_PKG_MAX) pkg_len = MEOW_SIG_PKG_MAX;
unsigned o = 0;
memcpy(out + o, MEOW_SIG_MAGIC, MEOW_SIG_MAGIC_LEN); o += MEOW_SIG_MAGIC_LEN;
out[o++] = MEOW_SIG_ALG_ED25519;
meow_le32_put((uint8_t *)out + o, (uint32_t)pkg_len); o += 4;
memcpy(out + o, package, pkg_len); o += pkg_len;
memcpy(out + o, payload_sha256, MEOW_SIG_SHA256_LEN); o += MEOW_SIG_SHA256_LEN;
return o;
}
#endif /* MEOW_SIG_H */
+17
View File
@@ -0,0 +1,17 @@
/* meow_signing_pub.h — 固件公钥(Ed25519 32B,编译期固化进 meow_verify)。
* 由 `meow_sign keygen` 生成(.keys/signing.key.pub),本头只含【公钥】,可入库;
* 私钥仅在 .keys/(已加入 .gitignore,绝不入库/报告)。轮换公钥=重生成本头+重编 meow_verify。 */
#ifndef MEOW_SIGNING_PUB_H
#define MEOW_SIGNING_PUB_H
#include <stdint.h>
static const uint8_t g_meow_pub[32] =
{
0x22, 0xa7, 0x83, 0xac, 0x28, 0xd3, 0x25, 0x6d,
0x3f, 0x84, 0x72, 0x72, 0x50, 0x5b, 0x2c, 0x61,
0x60, 0x97, 0xff, 0xc3, 0x98, 0x78, 0x14, 0x9d,
0xc7, 0x37, 0x8c, 0x58, 0x72, 0x26, 0x37, 0xf2
};
#endif /* MEOW_SIGNING_PUB_H */
BIN
View File
Binary file not shown.
+215
View File
@@ -0,0 +1,215 @@
/* meow_verify.c — MeowOS .mpk 签名校验器(固件 /usr/bin/meow_verify,静态 mipsel)。
*
* 功能(MPK_SIGNATURE_DESIGN §5.1):
* - Ed25519 验签(TweetNaCl crypto_sign_open,公钥编译期固化进 meow_signing_pub.h);
* - payload_sha256 = SHA-256(.mpk 去掉尾部 footer 的其余字节,即 tar.gz 前缀,分块读、无需解包);
* - 包名绑定①:footer.package == 期望包名(--package 或文件名茎);
* - 被签消息 msg = "MEOWSIG1" || alg(1B) || package_len(4B,LE) || package || payload_sha256(32B)。
* 绑定②(app.json.package)与③(os.meow.* 前缀)由调用方(nyai / launcher)在安装流里执行,
* 本器只保证「有签名就必须验签通过」。见 MPK_SIGNATURE_DESIGN §3.3(分工诚实标注)。
*
* 用法:
* meow_verify <mpk> # 验签(默认系统模式:固件公钥 + 绑定①)
* meow_verify --system <mpk> # 同上(显式)
* meow_verify --package <pkg> <mpk> # 显式给期望包名(否则从文件名茎推导)
* meow_verify --has-sig <mpk> # 只探测是否存在 MEOWSIG1 footer:输出 yes/no
* meow_verify --strip <mpk> # 输出去 footer 的干净 tar.gz 到 stdout(无 footer 则原样复制)
*
* 退出码:0=通过;1=有签但不通过(篡改/包名不符/公钥不符/格式错);2=无签名 footer;3=用法/IO 错误。
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "tweetnacl.h"
#include "sha256.h"
#include "meow_sig.h"
/* 固件公钥(编译期固化,32B Ed25519)。由 meow_sign keygen 生成后写死于此头。 */
#include "meow_signing_pub.h"
/* 验签侧绝不调用 keypair,但 tweetnacl.c 的 crypto_sign_keypair 引用了 randombytes;
* 提供 stub 以满足链接(静态链接整对象,函数未调用)。 */
void randombytes(unsigned char *x, unsigned long long xlen)
{
(void)x; (void)xlen;
}
static int file_size(const char *path, long long *sz)
{
struct stat st;
if (stat(path, &st) != 0) return -1;
*sz = (long long)st.st_size;
return 0;
}
/* 读文件尾部 MEOW_SIG_BLOCK_LEN 字节作为 footer。返回 0 成功,-2 文件太短(无签名空间),-1 IO 错。 */
static int read_footer(const char *path, long long fsize, meow_sig_block_t *blk)
{
if (fsize < (long long)MEOW_SIG_BLOCK_LEN) return -2;
int fd = open(path, O_RDONLY);
if (fd < 0) return -1;
if (lseek(fd, fsize - (long long)MEOW_SIG_BLOCK_LEN, SEEK_SET) < 0) { close(fd); return -1; }
ssize_t n = read(fd, blk, MEOW_SIG_BLOCK_LEN);
close(fd);
if (n != (ssize_t)MEOW_SIG_BLOCK_LEN) return -1;
return 0;
}
/* 分块 SHA-256 计算文件前 plen 字节。返回 0 成功,-1 IO/读错。 */
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;
}
/* 从 .mpk 路径取名:去掉目录 + 去 .mpk 后缀。成功 0。 */
static int derive_pkg(const char *mpk, char *out, int n)
{
const char *bn = strrchr(mpk, '/');
bn = bn ? bn + 1 : mpk;
const char *dot = strrchr(bn, '.');
if (!dot || strcmp(dot, ".mpk") != 0) return -1;
int len = (int)(dot - bn);
if (len <= 0 || len >= n) return -1;
memcpy(out, bn, (size_t)len); out[len] = 0;
return 0;
}
/* 核心验签。pkg 为期望包名(nil 则从文件名推导)。返回 0/1/2/3。 */
static int verify_mpk(const char *mpk, const char *pkg)
{
long long fsize;
if (file_size(mpk, &fsize) != 0) return 3;
if (fsize < (long long)MEOW_SIG_BLOCK_LEN) return 2; /* 无签名空间 */
meow_sig_block_t blk;
if (read_footer(mpk, fsize, &blk) != 0) return 3;
if (memcmp(blk.magic, MEOW_SIG_MAGIC, MEOW_SIG_MAGIC_LEN) != 0) return 2; /* 无 MEOWSIG1 footer */
if (blk.alg != MEOW_SIG_ALG_ED25519 || blk.block_len != MEOW_SIG_BLOCK_LEN) return 1; /* 格式错 */
char pkgbuf[MEOW_SIG_PKG_MAX];
if (pkg && *pkg) {
snprintf(pkgbuf, sizeof pkgbuf, "%s", pkg);
} else {
if (derive_pkg(mpk, pkgbuf, sizeof pkgbuf) != 0) return 3;
}
blk.package[MEOW_SIG_PKG_MAX - 1] = 0; /* 保证 NUL 终止 */
/* 绑定①:期望包名 == footer.package */
if (strcmp(pkgbuf, blk.package) != 0) {
fprintf(stderr, "meow_verify: package mismatch (want %s, sig %s)\n", pkgbuf, blk.package);
return 1;
}
/* payload 哈希 */
long long plen = fsize - (long long)MEOW_SIG_BLOCK_LEN;
uint8_t sha[MEOW_SIG_SHA256_LEN];
if (sha256_prefix(mpk, plen, sha) != 0) return 3;
if (memcmp(sha, blk.payload_sha256, MEOW_SIG_SHA256_LEN) != 0) {
fprintf(stderr, "meow_verify: payload sha256 mismatch (content tampered)\n");
return 1;
}
/* 重组被签消息 + 验签 */
char msg[MEOW_SIG_MSG_MAX];
unsigned mlen = meow_sig_msg_build(msg, blk.package, blk.payload_sha256);
if (blk.msg_len != mlen) { fprintf(stderr, "meow_verify: msg_len field mismatch\n"); return 1; }
uint8_t sm[MEOW_SIG_SIG_LEN + MEOW_SIG_MSG_MAX];
memcpy(sm, blk.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), g_meow_pub) != 0) {
fprintf(stderr, "meow_verify: ed25519 signature invalid\n");
return 1;
}
printf("%s\n", blk.package);
return 0;
}
/* --strip:输出干净 tar.gz(去 footer;无 footer 原样复制到 stdout)。返回 0=有签,2=无签,3=IO 错。 */
static int strip_mpk(const char *mpk)
{
long long fsize;
if (file_size(mpk, &fsize) != 0) return 3;
long long payload = fsize;
int has_sig = 0;
if (fsize >= (long long)MEOW_SIG_BLOCK_LEN) {
meow_sig_block_t blk;
if (read_footer(mpk, fsize, &blk) == 0 &&
memcmp(blk.magic, MEOW_SIG_MAGIC, MEOW_SIG_MAGIC_LEN) == 0) {
payload = fsize - (long long)MEOW_SIG_BLOCK_LEN;
has_sig = 1;
}
}
int fd = open(mpk, O_RDONLY);
if (fd < 0) return 3;
uint8_t buf[16384];
long long remaining = payload;
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 3; }
if (fwrite(buf, 1, (size_t)r, stdout) != (size_t)r) { close(fd); return 3; }
remaining -= (long long)r;
}
close(fd);
return has_sig ? 0 : 2;
}
static int has_sig(const char *mpk)
{
long long fsize;
if (file_size(mpk, &fsize) != 0) return 3;
if (fsize < (long long)MEOW_SIG_BLOCK_LEN) { printf("no\n"); return 2; }
meow_sig_block_t blk;
if (read_footer(mpk, fsize, &blk) != 0) { printf("no\n"); return 2; }
int h = (memcmp(blk.magic, MEOW_SIG_MAGIC, MEOW_SIG_MAGIC_LEN) == 0);
printf(h ? "yes\n" : "no\n");
return h ? 0 : 2;
}
int main(int argc, char **argv)
{
if (argc < 2) {
fprintf(stderr, "usage: %s [--system|--has-sig|--strip] [--package <pkg>] <mpk>\n", argv[0]);
return 3;
}
int i = 1;
int mode = 0; /* 0=verify, 1=has-sig, 2=strip */
const char *pkg = NULL;
const char *mpk = NULL;
for (; i < argc; i++) {
if (!strcmp(argv[i], "--system")) { mode = 0; }
else if (!strcmp(argv[i], "--has-sig")) { mode = 1; }
else if (!strcmp(argv[i], "--strip")) { mode = 2; }
else if (!strcmp(argv[i], "--package")) {
if (i + 1 >= argc) { fprintf(stderr, "--package needs arg\n"); return 3; }
pkg = argv[++i];
} else { mpk = argv[i]; }
}
if (!mpk) { fprintf(stderr, "missing <mpk>\n"); return 3; }
if (mode == 1) return has_sig(mpk);
if (mode == 2) return strip_mpk(mpk);
return verify_mpk(mpk, pkg);
}
Binary file not shown.
+97
View File
@@ -0,0 +1,97 @@
/* sha256.c — 紧凑 SHA-256(FIPS 180-4),公开领域参考写法,-Wall -Wextra 0 warning。
* 流式 context;meow_verify / meow_sign 共用。 */
#include "sha256.h"
#include <string.h>
static const uint32_t K[64] = {
0x428a2f98u,0x71374491u,0xb5c0fbcfu,0xe9b5dba5u,0x3956c25bu,0x59f111f1u,0x923f82a4u,0xab1c5ed5u,
0xd807aa98u,0x12835b01u,0x243185beu,0x550c7dc3u,0x72be5d74u,0x80deb1feu,0x9bdc06a7u,0xc19bf174u,
0xe49b69c1u,0xefbe4786u,0x0fc19dc6u,0x240ca1ccu,0x2de92c6fu,0x4a7484aau,0x5cb0a9dcu,0x76f988dau,
0x983e5152u,0xa831c66du,0xb00327c8u,0xbf597fc7u,0xc6e00bf3u,0xd5a79147u,0x06ca6351u,0x14292967u,
0x27b70a85u,0x2e1b2138u,0x4d2c6dfcu,0x53380d13u,0x650a7354u,0x766a0abbu,0x81c2c92eu,0x92722c85u,
0xa2bfe8a1u,0xa81a664bu,0xc24b8b70u,0xc76c51a3u,0xd192e819u,0xd6990624u,0xf40e3585u,0x106aa070u,
0x19a4c116u,0x1e376c08u,0x2748774cu,0x34b0bcb5u,0x391c0cb3u,0x4ed8aa4au,0x5b9cca4fu,0x682e6ff3u,
0x748f82eeu,0x78a5636fu,0x84c87814u,0x8cc70208u,0x90befffau,0xa4506cebu,0xbef9a3f7u,0xc67178f2u
};
#define ROR(x,n) (((x) >> (n)) | ((x) << (32 - (n))))
#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define BSIG0(x) (ROR(x,2) ^ ROR(x,13) ^ ROR(x,22))
#define BSIG1(x) (ROR(x,6) ^ ROR(x,11) ^ ROR(x,25))
#define SSIG0(x) (ROR(x,7) ^ ROR(x,18) ^ ((x) >> 3))
#define SSIG1(x) (ROR(x,17) ^ ROR(x,19) ^ ((x) >> 10))
void meow_sha256_init(meow_sha256_ctx *c)
{
c->state[0] = 0x6a09e667u; c->state[1] = 0xbb67ae85u;
c->state[2] = 0x3c6ef372u; c->state[3] = 0xa54ff53au;
c->state[4] = 0x510e527fu; c->state[5] = 0x9b05688cu;
c->state[6] = 0x1f83d9abu; c->state[7] = 0x5be0cd19u;
c->count = 0; c->buflen = 0;
}
static void sha256_block(meow_sha256_ctx *c, const uint8_t *p)
{
uint32_t w[64];
int i;
for (i = 0; i < 16; i++) {
w[i] = ((uint32_t)p[i*4] << 24) | ((uint32_t)p[i*4+1] << 16) |
((uint32_t)p[i*4+2] << 8) | (uint32_t)p[i*4+3];
}
for (i = 16; i < 64; i++)
w[i] = SSIG1(w[i-2]) + w[i-7] + SSIG0(w[i-15]) + w[i-16];
uint32_t a = c->state[0], b = c->state[1], cc = c->state[2], d = c->state[3];
uint32_t e = c->state[4], f = c->state[5], g = c->state[6], h = c->state[7];
for (i = 0; i < 64; i++) {
uint32_t t1 = h + BSIG1(e) + CH(e,f,g) + K[i] + w[i];
uint32_t t2 = BSIG0(a) + MAJ(a,b,cc);
h = g; g = f; f = e; e = d + t1;
d = cc; cc = b; b = a; a = t1 + t2;
}
c->state[0] += a; c->state[1] += b; c->state[2] += cc; c->state[3] += d;
c->state[4] += e; c->state[5] += f; c->state[6] += g; c->state[7] += h;
}
void meow_sha256_update(meow_sha256_ctx *c, const uint8_t *data, size_t len)
{
c->count += (uint64_t)len;
/* 先补满残余块 */
if (c->buflen) {
size_t need = 64 - c->buflen;
size_t take = len < need ? len : need;
memcpy(c->buf + c->buflen, data, take);
c->buflen += take; data += take; len -= take;
if (c->buflen == 64) { sha256_block(c, c->buf); c->buflen = 0; }
}
while (len >= 64) { sha256_block(c, data); data += 64; len -= 64; }
if (len) { memcpy(c->buf, data, len); c->buflen = len; }
}
void meow_sha256_final(meow_sha256_ctx *c, uint8_t out[32])
{
uint64_t bitlen = c->count * 8;
uint8_t *b = c->buf;
size_t bl = c->buflen;
int i;
b[bl++] = 0x80; /* 填充:1 位 1 */
if (bl > 56) { /* 一块放不下长度 → 补齐到 64 后压一块 */
while (bl < 64) b[bl++] = 0;
sha256_block(c, b);
bl = 0;
}
while (bl < 56) b[bl++] = 0; /* 补到 56 */
for (i = 0; i < 8; i++) /* 大端 64 位 bit 长度 */
b[bl++] = (uint8_t)(bitlen >> (56 - i * 8));
sha256_block(c, b);
for (i = 0; i < 8; i++) {
out[i*4] = (uint8_t)(c->state[i] >> 24);
out[i*4+1] = (uint8_t)(c->state[i] >> 16);
out[i*4+2] = (uint8_t)(c->state[i] >> 8);
out[i*4+3] = (uint8_t)(c->state[i]);
}
}
+21
View File
@@ -0,0 +1,21 @@
/* sha256.h — minimal public-domain SHA-256 context/API for MeowOS .mpk payload hashing.
* Used by meow_verify (device) and meow_sign (host) to compute .mpk 的 payload_sha256
* (tar.gz 前缀哈希)。流式 API,避免整包载入内存。 */
#ifndef MEOW_SHA256_H
#define MEOW_SHA256_H
#include <stddef.h>
#include <stdint.h>
typedef struct {
uint32_t state[8];
uint64_t count; /* 已输入总字节数 */
uint8_t buf[64]; /* 未满一块的缓冲 */
size_t buflen;
} meow_sha256_ctx;
void meow_sha256_init(meow_sha256_ctx *c);
void meow_sha256_update(meow_sha256_ctx *c, const uint8_t *data, size_t len);
void meow_sha256_final(meow_sha256_ctx *c, uint8_t out[32]);
#endif /* MEOW_SHA256_H */
+816
View File
@@ -0,0 +1,816 @@
/*
* TweetNaCl — 公有领域(public domain)加密库,作者 Bernard van Gastel 等
* 原始来源:https://tweetnacl.cr.yp.to/20140427/tweetnacl.c (20140427)
* 本文件为原样 vendor(仅去掉尾部空行),供 MeowOS meow_verify/meow_sign 使用
* Ed25519(crypto_sign_*)与 SHA-512(crypto_hash)子集。
* 编译注意:与本项目其余代码不同,本文件用 `-Wno-sign-compare` 等放宽(vendor 代码),
* 见 build_verify.sh / build_sign.sh。 */
#include "tweetnacl.h"
#define FOR(i,n) for (i = 0;i < n;++i)
#define sv static void
typedef unsigned char u8;
typedef unsigned long u32;
typedef unsigned long long u64;
typedef long long i64;
typedef i64 gf[16];
extern void randombytes(u8 *,u64);
static const u8
_0[16],
_9[32] = {9};
static const gf
gf0,
gf1 = {1},
_121665 = {0xDB41,1},
D = {0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203},
D2 = {0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406},
X = {0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169},
Y = {0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666},
I = {0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83};
static u32 L32(u32 x,int c) { return (x << c) | ((x&0xffffffff) >> (32 - c)); }
static u32 ld32(const u8 *x)
{
u32 u = x[3];
u = (u<<8)|x[2];
u = (u<<8)|x[1];
return (u<<8)|x[0];
}
static u64 dl64(const u8 *x)
{
u64 i,u=0;
FOR(i,8) u=(u<<8)|x[i];
return u;
}
sv st32(u8 *x,u32 u)
{
int i;
FOR(i,4) { x[i] = u; u >>= 8; }
}
sv ts64(u8 *x,u64 u)
{
int i;
for (i = 7;i >= 0;--i) { x[i] = u; u >>= 8; }
}
static int vn(const u8 *x,const u8 *y,int n)
{
u32 i,d = 0;
FOR(i,n) d |= x[i]^y[i];
return (1 & ((d - 1) >> 8)) - 1;
}
int crypto_verify_16(const u8 *x,const u8 *y)
{
return vn(x,y,16);
}
int crypto_verify_32(const u8 *x,const u8 *y)
{
return vn(x,y,32);
}
sv core(u8 *out,const u8 *in,const u8 *k,const u8 *c,int h)
{
u32 w[16],x[16],y[16],t[4];
int i,j,m;
FOR(i,4) {
x[5*i] = ld32(c+4*i);
x[1+i] = ld32(k+4*i);
x[6+i] = ld32(in+4*i);
x[11+i] = ld32(k+16+4*i);
}
FOR(i,16) y[i] = x[i];
FOR(i,20) {
FOR(j,4) {
FOR(m,4) t[m] = x[(5*j+4*m)%16];
t[1] ^= L32(t[0]+t[3], 7);
t[2] ^= L32(t[1]+t[0], 9);
t[3] ^= L32(t[2]+t[1],13);
t[0] ^= L32(t[3]+t[2],18);
FOR(m,4) w[4*j+(j+m)%4] = t[m];
}
FOR(m,16) x[m] = w[m];
}
if (h) {
FOR(i,16) x[i] += y[i];
FOR(i,4) {
x[5*i] -= ld32(c+4*i);
x[6+i] -= ld32(in+4*i);
}
FOR(i,4) {
st32(out+4*i,x[5*i]);
st32(out+16+4*i,x[6+i]);
}
} else
FOR(i,16) st32(out + 4 * i,x[i] + y[i]);
}
int crypto_core_salsa20(u8 *out,const u8 *in,const u8 *k,const u8 *c)
{
core(out,in,k,c,0);
return 0;
}
int crypto_core_hsalsa20(u8 *out,const u8 *in,const u8 *k,const u8 *c)
{
core(out,in,k,c,1);
return 0;
}
static const u8 sigma[16] = "expand 32-byte k";
int crypto_stream_salsa20_xor(u8 *c,const u8 *m,u64 b,const u8 *n,const u8 *k)
{
u8 z[16],x[64];
u32 u,i;
if (!b) return 0;
FOR(i,16) z[i] = 0;
FOR(i,8) z[i] = n[i];
while (b >= 64) {
crypto_core_salsa20(x,z,k,sigma);
FOR(i,64) c[i] = (m?m[i]:0) ^ x[i];
u = 1;
for (i = 8;i < 16;++i) {
u += (u32) z[i];
z[i] = u;
u >>= 8;
}
b -= 64;
c += 64;
if (m) m += 64;
}
if (b) {
crypto_core_salsa20(x,z,k,sigma);
FOR(i,b) c[i] = (m?m[i]:0) ^ x[i];
}
return 0;
}
int crypto_stream_salsa20(u8 *c,u64 d,const u8 *n,const u8 *k)
{
return crypto_stream_salsa20_xor(c,0,d,n,k);
}
int crypto_stream(u8 *c,u64 d,const u8 *n,const u8 *k)
{
u8 s[32];
crypto_core_hsalsa20(s,n,k,sigma);
return crypto_stream_salsa20(c,d,n+16,s);
}
int crypto_stream_xor(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k)
{
u8 s[32];
crypto_core_hsalsa20(s,n,k,sigma);
return crypto_stream_salsa20_xor(c,m,d,n+16,s);
}
sv add1305(u32 *h,const u32 *c)
{
u32 j,u = 0;
FOR(j,17) {
u += h[j] + c[j];
h[j] = u & 255;
u >>= 8;
}
}
static const u32 minusp[17] = {
5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252
} ;
int crypto_onetimeauth(u8 *out,const u8 *m,u64 n,const u8 *k)
{
u32 s,i,j,u,x[17],r[17],h[17],c[17],g[17];
FOR(j,17) r[j]=h[j]=0;
FOR(j,16) r[j]=k[j];
r[3]&=15;
r[4]&=252;
r[7]&=15;
r[8]&=252;
r[11]&=15;
r[12]&=252;
r[15]&=15;
while (n > 0) {
FOR(j,17) c[j] = 0;
for (j = 0;(j < 16) && (j < n);++j) c[j] = m[j];
c[j] = 1;
m += j; n -= j;
add1305(h,c);
FOR(i,17) {
x[i] = 0;
FOR(j,17) x[i] += h[j] * ((j <= i) ? r[i - j] : 320 * r[i + 17 - j]);
}
FOR(i,17) h[i] = x[i];
u = 0;
FOR(j,16) {
u += h[j];
h[j] = u & 255;
u >>= 8;
}
u += h[16]; h[16] = u & 3;
u = 5 * (u >> 2);
FOR(j,16) {
u += h[j];
h[j] = u & 255;
u >>= 8;
}
u += h[16]; h[16] = u;
}
FOR(j,17) g[j] = h[j];
add1305(h,minusp);
s = -(h[16] >> 7);
FOR(j,17) h[j] ^= s & (g[j] ^ h[j]);
FOR(j,16) c[j] = k[j + 16];
c[16] = 0;
add1305(h,c);
FOR(j,16) out[j] = h[j];
return 0;
}
int crypto_onetimeauth_verify(const u8 *h,const u8 *m,u64 n,const u8 *k)
{
u8 x[16];
crypto_onetimeauth(x,m,n,k);
return crypto_verify_16(h,x);
}
int crypto_secretbox(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k)
{
int i;
if (d < 32) return -1;
crypto_stream_xor(c,m,d,n,k);
crypto_onetimeauth(c + 16,c + 32,d - 32,c);
FOR(i,16) c[i] = 0;
return 0;
}
int crypto_secretbox_open(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *k)
{
int i;
u8 x[32];
if (d < 32) return -1;
crypto_stream(x,32,n,k);
if (crypto_onetimeauth_verify(c + 16,c + 32,d - 32,x) != 0) return -1;
crypto_stream_xor(m,c,d,n,k);
FOR(i,32) m[i] = 0;
return 0;
}
sv set25519(gf r, const gf a)
{
int i;
FOR(i,16) r[i]=a[i];
}
sv car25519(gf o)
{
int i;
i64 c;
FOR(i,16) {
o[i]+=(1LL<<16);
c=o[i]>>16;
o[(i+1)*(i<15)]+=c-1+37*(c-1)*(i==15);
o[i]-=c<<16;
}
}
sv sel25519(gf p,gf q,int b)
{
i64 t,i,c=~(b-1);
FOR(i,16) {
t= c&(p[i]^q[i]);
p[i]^=t;
q[i]^=t;
}
}
sv pack25519(u8 *o,const gf n)
{
int i,j,b;
gf m,t;
FOR(i,16) t[i]=n[i];
car25519(t);
car25519(t);
car25519(t);
FOR(j,2) {
m[0]=t[0]-0xffed;
for(i=1;i<15;i++) {
m[i]=t[i]-0xffff-((m[i-1]>>16)&1);
m[i-1]&=0xffff;
}
m[15]=t[15]-0x7fff-((m[14]>>16)&1);
b=(m[15]>>16)&1;
m[14]&=0xffff;
sel25519(t,m,1-b);
}
FOR(i,16) {
o[2*i]=t[i]&0xff;
o[2*i+1]=t[i]>>8;
}
}
static int neq25519(const gf a, const gf b)
{
u8 c[32],d[32];
pack25519(c,a);
pack25519(d,b);
return crypto_verify_32(c,d);
}
static u8 par25519(const gf a)
{
u8 d[32];
pack25519(d,a);
return d[0]&1;
}
sv unpack25519(gf o, const u8 *n)
{
int i;
FOR(i,16) o[i]=n[2*i]+((i64)n[2*i+1]<<8);
o[15]&=0x7fff;
}
sv A(gf o,const gf a,const gf b)
{
int i;
FOR(i,16) o[i]=a[i]+b[i];
}
sv Z(gf o,const gf a,const gf b)
{
int i;
FOR(i,16) o[i]=a[i]-b[i];
}
sv M(gf o,const gf a,const gf b)
{
i64 i,j,t[31];
FOR(i,31) t[i]=0;
FOR(i,16) FOR(j,16) t[i+j]+=a[i]*b[j];
FOR(i,15) t[i]+=38*t[i+16];
FOR(i,16) o[i]=t[i];
car25519(o);
car25519(o);
}
sv S(gf o,const gf a)
{
M(o,a,a);
}
sv inv25519(gf o,const gf i)
{
gf c;
int a;
FOR(a,16) c[a]=i[a];
for(a=253;a>=0;a--) {
S(c,c);
if(a!=2&&a!=4) M(c,c,i);
}
FOR(a,16) o[a]=c[a];
}
sv pow2523(gf o,const gf i)
{
gf c;
int a;
FOR(a,16) c[a]=i[a];
for(a=250;a>=0;a--) {
S(c,c);
if(a!=1) M(c,c,i);
}
FOR(a,16) o[a]=c[a];
}
int crypto_scalarmult(u8 *q,const u8 *n,const u8 *p)
{
u8 z[32];
i64 x[80],r,i;
gf a,b,c,d,e,f;
FOR(i,31) z[i]=n[i];
z[31]=(n[31]&127)|64;
z[0]&=248;
unpack25519(x,p);
FOR(i,16) {
b[i]=x[i];
d[i]=a[i]=c[i]=0;
}
a[0]=d[0]=1;
for(i=254;i>=0;--i) {
r=(z[i>>3]>>(i&7))&1;
sel25519(a,b,r);
sel25519(c,d,r);
A(e,a,c);
Z(a,a,c);
A(c,b,d);
Z(b,b,d);
S(d,e);
S(f,a);
M(a,c,a);
M(c,b,e);
A(e,a,c);
Z(a,a,c);
S(b,a);
Z(c,d,f);
M(a,c,_121665);
A(a,a,d);
M(c,c,a);
M(a,d,f);
M(d,b,x);
S(b,e);
sel25519(a,b,r);
sel25519(c,d,r);
}
FOR(i,16) {
x[i+16]=a[i];
x[i+32]=c[i];
x[i+48]=b[i];
x[i+64]=d[i];
}
inv25519(x+32,x+32);
M(x+16,x+16,x+32);
pack25519(q,x+16);
return 0;
}
int crypto_scalarmult_base(u8 *q,const u8 *n)
{
return crypto_scalarmult(q,n,_9);
}
int crypto_box_keypair(u8 *y,u8 *x)
{
randombytes(x,32);
return crypto_scalarmult_base(y,x);
}
int crypto_box_beforenm(u8 *k,const u8 *y,const u8 *x)
{
u8 s[32];
crypto_scalarmult(s,x,y);
return crypto_core_hsalsa20(k,_0,s,sigma);
}
int crypto_box_afternm(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *k)
{
return crypto_secretbox(c,m,d,n,k);
}
int crypto_box_open_afternm(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *k)
{
return crypto_secretbox_open(m,c,d,n,k);
}
int crypto_box(u8 *c,const u8 *m,u64 d,const u8 *n,const u8 *y,const u8 *x)
{
u8 k[32];
crypto_box_beforenm(k,y,x);
return crypto_box_afternm(c,m,d,n,k);
}
int crypto_box_open(u8 *m,const u8 *c,u64 d,const u8 *n,const u8 *y,const u8 *x)
{
u8 k[32];
crypto_box_beforenm(k,y,x);
return crypto_box_open_afternm(m,c,d,n,k);
}
static u64 R(u64 x,int c) { return (x >> c) | (x << (64 - c)); }
static u64 Ch(u64 x,u64 y,u64 z) { return (x & y) ^ (~x & z); }
static u64 Maj(u64 x,u64 y,u64 z) { return (x & y) ^ (x & z) ^ (y & z); }
static u64 Sigma0(u64 x) { return R(x,28) ^ R(x,34) ^ R(x,39); }
static u64 Sigma1(u64 x) { return R(x,14) ^ R(x,18) ^ R(x,41); }
static u64 sigma0(u64 x) { return R(x, 1) ^ R(x, 8) ^ (x >> 7); }
static u64 sigma1(u64 x) { return R(x,19) ^ R(x,61) ^ (x >> 6); }
static const u64 K[80] =
{
0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
};
int crypto_hashblocks(u8 *x,const u8 *m,u64 n)
{
u64 z[8],b[8],a[8],w[16],t;
int i,j;
FOR(i,8) z[i] = a[i] = dl64(x + 8 * i);
while (n >= 128) {
FOR(i,16) w[i] = dl64(m + 8 * i);
FOR(i,80) {
FOR(j,8) b[j] = a[j];
t = a[7] + Sigma1(a[4]) + Ch(a[4],a[5],a[6]) + K[i] + w[i%16];
b[7] = t + Sigma0(a[0]) + Maj(a[0],a[1],a[2]);
b[3] += t;
FOR(j,8) a[(j+1)%8] = b[j];
if (i%16 == 15)
FOR(j,16)
w[j] += w[(j+9)%16] + sigma0(w[(j+1)%16]) + sigma1(w[(j+14)%16]);
}
FOR(i,8) { a[i] += z[i]; z[i] = a[i]; }
m += 128;
n -= 128;
}
FOR(i,8) ts64(x+8*i,z[i]);
return n;
}
static const u8 iv[64] = {
0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08,
0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b,
0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b,
0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1,
0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1,
0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f,
0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b,
0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79
} ;
int crypto_hash(u8 *out,const u8 *m,u64 n)
{
u8 h[64],x[256];
u64 i,b = n;
FOR(i,64) h[i] = iv[i];
crypto_hashblocks(h,m,n);
m += n;
n &= 127;
m -= n;
FOR(i,256) x[i] = 0;
FOR(i,n) x[i] = m[i];
x[n] = 128;
n = 256-128*(n<112);
x[n-9] = b >> 61;
ts64(x+n-8,b<<3);
crypto_hashblocks(h,x,n);
FOR(i,64) out[i] = h[i];
return 0;
}
sv add(gf p[4],gf q[4])
{
gf a,b,c,d,t,e,f,g,h;
Z(a, p[1], p[0]);
Z(t, q[1], q[0]);
M(a, a, t);
A(b, p[0], p[1]);
A(t, q[0], q[1]);
M(b, b, t);
M(c, p[3], q[3]);
M(c, c, D2);
M(d, p[2], q[2]);
A(d, d, d);
Z(e, b, a);
Z(f, d, c);
A(g, d, c);
A(h, b, a);
M(p[0], e, f);
M(p[1], h, g);
M(p[2], g, f);
M(p[3], e, h);
}
sv cswap(gf p[4],gf q[4],u8 b)
{
int i;
FOR(i,4)
sel25519(p[i],q[i],b);
}
sv pack(u8 *r,gf p[4])
{
gf tx, ty, zi;
inv25519(zi, p[2]);
M(tx, p[0], zi);
M(ty, p[1], zi);
pack25519(r, ty);
r[31] ^= par25519(tx) << 7;
}
sv scalarmult(gf p[4],gf q[4],const u8 *s)
{
int i;
set25519(p[0],gf0);
set25519(p[1],gf1);
set25519(p[2],gf1);
set25519(p[3],gf0);
for (i = 255;i >= 0;--i) {
u8 b = (s[i/8]>>(i&7))&1;
cswap(p,q,b);
add(q,p);
add(p,p);
cswap(p,q,b);
}
}
sv scalarbase(gf p[4],const u8 *s)
{
gf q[4];
set25519(q[0],X);
set25519(q[1],Y);
set25519(q[2],gf1);
M(q[3],X,Y);
scalarmult(p,q,s);
}
int crypto_sign_keypair(u8 *pk, u8 *sk)
{
u8 d[64];
gf p[4];
int i;
randombytes(sk, 32);
crypto_hash(d, sk, 32);
d[0] &= 248;
d[31] &= 127;
d[31] |= 64;
scalarbase(p,d);
pack(pk,p);
FOR(i,32) sk[32 + i] = pk[i];
return 0;
}
static const u64 L[32] = {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10};
sv modL(u8 *r,i64 x[64])
{
i64 carry,i,j;
for (i = 63;i >= 32;--i) {
carry = 0;
for (j = i - 32;j < i - 12;++j) {
x[j] += carry - 16 * x[i] * L[j - (i - 32)];
carry = (x[j] + 128) >> 8;
x[j] -= carry << 8;
}
x[j] += carry;
x[i] = 0;
}
carry = 0;
FOR(j,32) {
x[j] += carry - (x[31] >> 4) * L[j];
carry = x[j] >> 8;
x[j] &= 255;
}
FOR(j,32) x[j] -= carry * L[j];
FOR(i,32) {
x[i+1] += x[i] >> 8;
r[i] = x[i] & 255;
}
}
sv reduce(u8 *r)
{
i64 x[64],i;
FOR(i,64) x[i] = (u64) r[i];
FOR(i,64) r[i] = 0;
modL(r,x);
}
int crypto_sign(u8 *sm,u64 *smlen,const u8 *m,u64 n,const u8 *sk)
{
u8 d[64],h[64],r[64];
i64 i,j,x[64];
gf p[4];
crypto_hash(d, sk, 32);
d[0] &= 248;
d[31] &= 127;
d[31] |= 64;
*smlen = n+64;
FOR(i,n) sm[64 + i] = m[i];
FOR(i,32) sm[32 + i] = d[32 + i];
crypto_hash(r, sm+32, n+32);
reduce(r);
scalarbase(p,r);
pack(sm,p);
FOR(i,32) sm[i+32] = sk[i+32];
crypto_hash(h,sm,n + 64);
reduce(h);
FOR(i,64) x[i] = 0;
FOR(i,32) x[i] = (u64) r[i];
FOR(i,32) FOR(j,32) x[i+j] += h[i] * (u64) d[j];
modL(sm + 32,x);
return 0;
}
static int unpackneg(gf r[4],const u8 p[32])
{
gf t, chk, num, den, den2, den4, den6;
set25519(r[2],gf1);
unpack25519(r[1],p);
S(num,r[1]);
M(den,num,D);
Z(num,num,r[2]);
A(den,r[2],den);
S(den2,den);
S(den4,den2);
M(den6,den4,den2);
M(t,den6,num);
M(t,t,den);
pow2523(t,t);
M(t,t,num);
M(t,t,den);
M(t,t,den);
M(r[0],t,den);
S(chk,r[0]);
M(chk,chk,den);
if (neq25519(chk, num)) M(r[0],r[0],I);
S(chk,r[0]);
M(chk,chk,den);
if (neq25519(chk, num)) return -1;
if (par25519(r[0]) == (p[31]>>7)) Z(r[0],gf0,r[0]);
M(r[3],r[0],r[1]);
return 0;
}
int crypto_sign_open(u8 *m,u64 *mlen,const u8 *sm,u64 n,const u8 *pk)
{
int i;
u8 t[32],h[64];
gf p[4],q[4];
*mlen = -1;
if (n < 64) return -1;
if (unpackneg(q,pk)) return -1;
FOR(i,n) m[i] = sm[i];
FOR(i,32) m[i+32] = pk[i];
crypto_hash(h,m,n);
reduce(h);
scalarmult(p,q,h);
scalarbase(q,sm + 32);
add(p,q);
pack(t,p);
n -= 64;
if (crypto_verify_32(sm, t)) {
FOR(i,n) m[i] = 0;
return -1;
}
FOR(i,n) m[i] = sm[i + 64];
*mlen = n;
return 0;
}
+23
View File
@@ -0,0 +1,23 @@
/* tweetnacl.h — TweetNaCl 用于 MeowOS .mpk 签名/验签的最小声明头(Ed25519 + SHA-512 子集)。
* 实现:https://tweetnacl.cr.yp.to/20140427/tweetnacl.c (公有领域)。
* 仅暴露 meow_verify / meow_sign 需要的一小片 API;其余(salsa20/box/secretbox…)
* 在 tweetnacl.c 内保留但未声明,未引用即不进入最终链接。 */
#ifndef TWEETNACL_H
#define TWEETNACL_H
#define crypto_sign_BYTES 64
#define crypto_sign_PUBLICKEYBYTES 32
#define crypto_sign_SECRETKEYBYTES 64
#define crypto_hash_BYTES 64
int crypto_verify_16(const unsigned char *x, const unsigned char *y);
int crypto_verify_32(const unsigned char *x, const unsigned char *y);
int crypto_hash(unsigned char *out, const unsigned char *m, unsigned long long n);
int crypto_sign_keypair(unsigned char *public_key, unsigned char *secret_key);
int crypto_sign(unsigned char *sm, unsigned long long *smlen,
const unsigned char *m, unsigned long long n, const unsigned char *sk);
int crypto_sign_open(unsigned char *m, unsigned long long *mlen,
const unsigned char *sm, unsigned long long n, const unsigned char *pk);
#endif /* TWEETNACL_H */
Binary file not shown.
+136 -4
View File
@@ -23,6 +23,7 @@
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#define NYAI_VERSION "0.2.0"
#define BUF 1024
@@ -282,6 +283,7 @@ typedef struct {
char name[256];
char version[64];
char exec[512];
char package[64]; /* app.json "package"(权威包名,== 文件名茎;II 期绑定校验用) */
char author[128];
char routes[512];
char services[512];
@@ -305,6 +307,7 @@ static int load_app_meta(const char *path, appmeta *m)
if (bal != 0) return -1;
if (json_str(j,"name",m->name,sizeof m->name)!=0 || !m->name[0]) return -1;
if (json_str(j,"exec",m->exec,sizeof m->exec)!=0 || !m->exec[0]) return -1;
if (json_str(j,"package",m->package,sizeof m->package)!=0) m->package[0]=0;
if (json_str(j,"version",m->version,sizeof m->version)!=0) m->version[0]=0;
if (json_str(j,"author",m->author,sizeof m->author)!=0) m->author[0]=0;
if (json_str(j,"depends",m->depends,sizeof m->depends)!=0) m->depends[0]=0;
@@ -648,7 +651,10 @@ static int cmd_update(void)
return ok ? 0 : 1;
}
/* ---------------- index <dir> ---------------- */
/* ---------------- index <dir> ----------------
* 注意(MPK_SIGNATURE_DESIGN §5.5):索引记录的 SHA256 = 整包哈希(含尾部签名 footer 的 .mpk)。
* 必须先 `meow_sign sign <pkg>.mpk` 完成签名、再 `nyai index <dir>`,否则索引与装后的整包不一致,
* install 的 sha256_file 校验会失败。本命令不改,仅标注此顺序约束(签名分级见 install_mpk_file)。 */
static int cmd_index(const char *dir)
{
if (!dir || !*dir) { fprintf(stderr,"nyai: index: 缺目录\n"); return 2; }
@@ -746,6 +752,89 @@ static int cmd_list_upgradable(pkgrec *recs, int nrec)
return 0;
}
/* ---------------- II 期签名校验(MPK_SIGNATURE_DESIGN §5.1,shell-out meow_verify) ---------------- */
/* 系统包前缀:os.meow.*(防伪造系统包名;独立于 valid_pkg,不改变现有合法包名序列)。 */
static int is_system_pkg(const char *pkg)
{
return pkg && !strncmp(pkg, "os.meow.", 8);
}
/* meow_verify 二进制路径:环境变量 MEOW_VERIFY_BIN 覆盖(宿主测试用),缺省 /usr/bin/meow_verify。 */
static const char *verify_bin(void)
{
const char *v = getenv("MEOW_VERIFY_BIN");
return (v && *v) ? v : "/usr/bin/meow_verify";
}
/* 调用 meow_verify --system <mpk> --package <pkg> 验签。
* out_pkg 回传校验器输出的 footer.package(成功时)。返回校验器退出码:
* 0=有效签(三重绑定①通过) / 1=有签但不通过(改包名/负载/公钥) / 2=无签名 / 127=工具不可用。 */
static int verify_mpk_signature(const char *mpk, const char *pkg, char *out_pkg, int n)
{
if (!mpk || !pkg) return 127;
if (unsafe_path(mpk)) return 127;
char cmd[1200];
snprintf(cmd, sizeof cmd, "'%s' --system '%s' --package '%s' 2>/dev/null",
verify_bin(), mpk, pkg);
if (out_pkg && n > 0) out_pkg[0] = 0;
FILE *pf = popen(cmd, "r");
if (!pf) return 127;
char line[128];
if (fgets(line, sizeof line, pf) != NULL) {
char *e = line + strlen(line);
while (e > line && (e[-1]=='\n'||e[-1]=='\r'||e[-1]==' '||e[-1]=='\t')) *--e = 0;
if (out_pkg && n > 0) {
size_t cl = strlen(line);
if (cl >= (size_t)n) cl = (size_t)n - 1;
memcpy(out_pkg, line, cl); out_pkg[cl] = 0;
}
}
int rc = pclose(pf);
if (rc == -1) return 127;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return 127;
}
/* 安装期保存「原签 .mpk」到 MeowData/<pkg>/data/<pkg>.mpk.src(导出保留签名用,§5.4)。
* 仅当本包带有效签名时调用。失败只告警(导不出签名但安装成功)。 */
static void save_mpk_source(const char *mpk, const char *pkg)
{
char sp[BUF], d[BUF], cmd[1100];
path_data(sp, sizeof sp, pkg, "data");
mkdir_p(sp);
snprintf(d, sizeof d, "%s/%s.mpk.src", sp, pkg);
snprintf(cmd, sizeof cmd, "cp '%s' '%s' 2>/dev/null", mpk, d);
if (system(cmd) != 0)
fprintf(stderr, "nyai: 警告: 保存原签包 %s.mpk.src 失败(导出将无签名)\n", pkg);
}
/* 导出保留签名:MeowData/<pkg>/data/<pkg>.mpk.src 存在 + 验签通过 + 其 app.json 版本 == 已装版本
* → 直接复制它作为导出结果(不二次签名);否则返回 -1(调用方现场重打包,无签名)。 */
static int export_preserve_sig(const char *pkg, const char *dst)
{
char sp[BUF], srcp[BUF], instj[BUF], cmd[1100];
path_data(sp, sizeof sp, pkg, "data");
snprintf(srcp, sizeof srcp, "%s/%s.mpk.src", sp, pkg);
if (access(srcp, R_OK) != 0) return -1;
char op[64];
int rc = verify_mpk_signature(srcp, pkg, op, sizeof op);
if (rc != 0) { fprintf(stderr, "nyai: export: 缓存原签包验签失败(rc=%d),回退重打包\n", rc); return -1; }
appmeta cached, inst;
if (mpk_appinfo(srcp, &cached) != 0) return -1;
snprintf(instj, sizeof instj, "%s/MeowAPPs/%s/app.json", meow_root(), pkg);
if (load_app_meta(instj, &inst) != 0) return -1;
if (strcmp(cached.version, inst.version) != 0) {
fprintf(stderr, "nyai: export: 缓存原签包版本(%s) != 已装(%s),回退重打包\n",
cached.version[0]?cached.version:"(?)", inst.version[0]?inst.version:"(?)");
return -1;
}
char tmp[520]; snprintf(tmp, sizeof tmp, "%s.tmp", dst);
snprintf(cmd, sizeof cmd, "cp '%s' '%s' 2>/dev/null", srcp, tmp);
if (system(cmd) != 0 || access(tmp, F_OK) != 0) return -1;
if (rename(tmp, dst) != 0) { rm_rf(tmp); return -1; }
return 0;
}
/* ---------------- install(文件或名称) ---------------- */
static int install_mpk_file(const char *mpk_path)
{
@@ -769,8 +858,8 @@ static int install_mpk_file(const char *mpk_path)
snprintf(stg,sizeof stg,"%s/MeowAPPs/.stg_%d_%s", meow_root(), (int)getpid(), pkg);
int replacing = (access(dst,F_OK)==0);
/* 签名分级预留(未实现验签):os.meow.* 系统包二期须有有效签名,无签名/验签失败一律拒装 */
if (!strncmp(pkg,"os.meow.", 8)) printf("nyai: 系统包 %s 需签名(二期)\n", pkg);
/* 签名分级(II 期,MPK_SIGNATURE_DESIGN §5.2):有签名必须验签;os.meow.* 必须有有效签名;
* 无签名第三方放行(告警)。验签在 ① 解到 stg 后、② 防降级前执行(stg=临时目录,失败即 rm 不落盘)。 */
/* ① 先解到临时目录读元数据,再决定替换(防降级 + 校验 app.json) */
rm_rf(stg);
@@ -788,7 +877,34 @@ static int install_mpk_file(const char *mpk_path)
fprintf(stderr,"nyai: install: app.json 不合法(缺 name 或 exec)\n");
rm_rf(stg); return 1;
}
/* TODO(2A): meow_verify 验签挂点,见 MPK_SIGNATURE_DESIGN.md */
/* ①.5 签名分级 + 三重绑定(MPK_SIGNATURE_DESIGN §3.3/§5.2 失败行为表) */
int vr = verify_mpk_signature(mpk_path, pkg, NULL, 0);
int system_pkg = is_system_pkg(pkg);
int keep_src = 0; /* 有效签 → 安装后另存原签 .mpk.src */
if (vr == 0) {
keep_src = 1;
printf("nyai: 签名校验通过 (package=%s)\n", pkg);
} else if (vr == 1) {
fprintf(stderr,"nyai: install: 签名验证失败(内容被篡改/包名不符/公钥不符),拒绝安装\n");
rm_rf(stg); return 1;
} else if (vr == 2) {
if (system_pkg) {
fprintf(stderr,"nyai: install: 系统包 %s 必须签名(无签名),拒绝安装\n", pkg);
rm_rf(stg); return 1;
}
printf("nyai: 提示: %s 未签名(第三方包放行安装)\n", pkg);
} else {
if (system_pkg) {
fprintf(stderr,"nyai: install: meow_verify 不可用(rc=%d),os.meow.* 拒装(宁缺勿装)\n", vr);
rm_rf(stg); return 1;
}
printf("nyai: 警告: meow_verify 不可用(rc=%d),%s 未验签放行\n", vr, pkg);
}
/* 绑定(一般/②):解包 app.json.package == 文件名茎(防目录改名/改 app.json;meow_verify 已保证① footer.package==pkg) */
if (m.package[0] && strcmp(m.package, pkg) != 0) {
fprintf(stderr,"nyai: install: app.json.package(%s) != 包名(%s),拒绝\n", m.package, pkg);
rm_rf(stg); return 1;
}
/* ② 同包名替换前的防降级门禁:新版必须 >= 已装(相等=重装,允许;<即降级拒装) */
if (replacing) {
@@ -823,6 +939,9 @@ static int install_mpk_file(const char *mpk_path)
mkdir_p(d1); mkdir_p(d2);
printf("Setting up %s (%s) ...\n", pkg, m.version[0]?m.version:"?");
/* ④.5 II 期:原签 .mpk 另存 MeowData/<pkg>/data/<pkg>.mpk.src(导出保留签名) */
if (keep_src) save_mpk_source(mpk_path, pkg);
/* ⑤ 安装成功后清理本次创建的 .bak_<pid>(失败回滚路径不动) */
if (replacing && bak[0]) { rm_rf(bak); printf("Removed backup %s\n", bak); }
printf("OK\n");
@@ -1064,6 +1183,11 @@ static int cmd_export(const char *pkg, const char *out)
snprintf(outbuf,sizeof outbuf,"%s", out && *out ? out : pkg);
if (!strstr(outbuf,".mpk")) strncat(outbuf,".mpk", sizeof outbuf-strlen(outbuf)-1);
snprintf(dst,sizeof dst,"%s", outbuf);
/* II 期:导出保留签名 —— 若安装时存了原签 .mpk.src 且其版本==已装 → 直接复制(不二次签名)。 */
if (export_preserve_sig(pkg, dst) == 0) {
printf("OK (signed, preserved from .mpk.src)\n");
return 0;
}
printf("Exporting %s -> %s ...\n", pkg, dst);
char tmpp[BUF]; snprintf(tmpp,sizeof tmpp,"%s.tmp", dst);
int rc;
@@ -1115,6 +1239,14 @@ static int cmd_upgrade(const char *pkg, const char *file)
if (access(mpk,R_OK)!=0) { fprintf(stderr,"nyai: upgrade: 找不到包文件 %s\n", mpk); return 1; }
char dst[BUF]; snprintf(dst,sizeof dst,"%s/MeowAPPs/%s", meow_root(), pkg);
if (access(dst,F_OK)!=0) { fprintf(stderr,"nyai: upgrade: %s 未安装(如需全新安装请用 install)\n", pkg); return 1; }
/* 签名分级(与 install 同规范):os.meow.* 必须有有效签名;有签必须验签;无签第三方放行。 */
{
int vr = verify_mpk_signature(mpk, pkg, NULL, 0);
int sysp = is_system_pkg(pkg);
if (vr == 1) { fprintf(stderr,"nyai: upgrade: 签名验证失败,拒绝升级\n"); return 1; }
else if (vr == 2) { if (sysp) { fprintf(stderr,"nyai: upgrade: 系统包 %s 必须签名,拒绝升级\n", pkg); return 1; } printf("nyai: 提示: %s 未签名(第三方放行)\n", pkg); }
else if (vr != 0) { if (sysp) { fprintf(stderr,"nyai: upgrade: meow_verify 不可用(rc=%d),os.meow.* 拒升\n", vr); return 1; } printf("nyai: 警告: meow_verify 不可用(rc=%d),%s 未验签放行\n", vr, pkg); }
}
/* 防降级门禁(与 install 同规范):新版必须 >= 已装(相等=重装允许) */
{
char aj[BUF]; snprintf(aj,sizeof aj,"%s/app.json", dst);
Binary file not shown.