From 3f57d7fd4f331f5c61152c34a9699edf774e2d9b Mon Sep 17 00:00:00 2001 From: MeowOS Date: Mon, 31 Aug 2026 04:33:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(sec):=20=E4=BA=8C=E6=9C=9F=E7=AD=BE?= =?UTF-8?q?=E5=90=8D=E9=89=B4=E6=9D=83=202A=20=E6=9C=80=E5=B0=8F=E9=97=AD?= =?UTF-8?q?=E7=8E=AF=20(agent-185)=20=E2=80=94=20meow=5Fverify(Ed25519?= =?UTF-8?q?=E9=AA=8C=E7=AD=BECLI,=E5=85=AC=E9=92=A5=E5=9B=BA=E5=8C=96,Twee?= =?UTF-8?q?tNaCl+SHA256=E5=8D=95=E6=96=87=E4=BB=B6)=20+=20meow=5Fsign(?= =?UTF-8?q?=E5=AE=BF=E4=B8=BBkeygen/sign,=E5=B0=BE=E9=83=A8180B=20footer?= =?UTF-8?q?=E7=AD=BE=E5=90=8D=E5=9D=97,=E7=A7=81=E9=92=A5=E4=BB=85.keys/?= =?UTF-8?q?=E4=B8=8D=E5=85=A5=E5=BA=93)=20+=20nyai/meow=5Fpkg=5Finstall=20?= =?UTF-8?q?=E9=AA=8C=E7=AD=BE=E5=88=86=E7=BA=A7(=E6=9C=89=E7=AD=BE?= =?UTF-8?q?=E5=BF=85=E9=AA=8C/os.meow.*=E6=97=A0=E7=AD=BE=E6=8B=92?= =?UTF-8?q?=E8=A3=85/=E7=AC=AC=E4=B8=89=E6=96=B9=E7=9B=B4=E8=A3=85?= =?UTF-8?q?=E5=91=8A=E8=AD=A6/=E4=B8=89=E9=87=8D=E7=BB=91=E5=AE=9A/?= =?UTF-8?q?=E5=8E=9F=E7=AD=BE=E5=AD=98.mpk.src=E5=AF=BC=E5=87=BA=E4=BF=9D?= =?UTF-8?q?=E7=95=99)=20+=20svc=5Fpower=20IPC=E9=89=B4=E6=9D=83(SO=5FPEERC?= =?UTF-8?q?RED=E2=86=92/proc/pid/exe=E2=86=92=E5=8C=85=E5=90=8D/=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E8=BA=AB=E4=BB=BDallowlist,=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E5=8A=A8=E4=BD=9C=E4=BB=85os.meow.*=E6=88=96=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E8=BA=AB=E4=BB=BD)=20+=20cmd=5Fupgrade=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E7=AD=BE=E5=90=8D=E5=88=86=E7=BA=A7=E9=98=B2=E7=BB=95=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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为启发式 --- .gitignore | 7 + patch_v2/meow_launcherd/app_launcher.c | 116 ++- patch_v2/meow_sign/build_sign.sh | 28 + patch_v2/meow_sign/meow_sign | Bin 0 -> 74064 bytes patch_v2/meow_sign/meow_sign.c | 184 +++++ patch_v2/meow_svc_power/meow_svc_power | Bin 90636 -> 91820 bytes patch_v2/meow_svc_power/meow_svc_power.c | 116 ++- patch_v2/meow_svc_power/meow_svc_power_host | Bin 73456 -> 73544 bytes patch_v2/meow_svc_power/svc_power_authtest.c | 93 +++ patch_v2/meow_verify/build_verify.sh | 43 + patch_v2/meow_verify/meow_sig.h | 77 ++ patch_v2/meow_verify/meow_signing_pub.h | 17 + patch_v2/meow_verify/meow_verify | Bin 0 -> 80416 bytes patch_v2/meow_verify/meow_verify.c | 215 +++++ patch_v2/meow_verify/meow_verify_host | Bin 0 -> 73928 bytes patch_v2/meow_verify/sha256.c | 97 +++ patch_v2/meow_verify/sha256.h | 21 + patch_v2/meow_verify/tweetnacl.c | 816 +++++++++++++++++++ patch_v2/meow_verify/tweetnacl.h | 23 + patch_v2/meowpkg/nyai | Bin 109596 -> 112428 bytes patch_v2/meowpkg/nyai.c | 140 +++- patch_v2/meowpkg/nyai_host | Bin 73848 -> 73896 bytes 22 files changed, 1978 insertions(+), 15 deletions(-) create mode 100755 patch_v2/meow_sign/build_sign.sh create mode 100755 patch_v2/meow_sign/meow_sign create mode 100644 patch_v2/meow_sign/meow_sign.c create mode 100644 patch_v2/meow_svc_power/svc_power_authtest.c create mode 100755 patch_v2/meow_verify/build_verify.sh create mode 100644 patch_v2/meow_verify/meow_sig.h create mode 100644 patch_v2/meow_verify/meow_signing_pub.h create mode 100755 patch_v2/meow_verify/meow_verify create mode 100644 patch_v2/meow_verify/meow_verify.c create mode 100755 patch_v2/meow_verify/meow_verify_host create mode 100644 patch_v2/meow_verify/sha256.c create mode 100644 patch_v2/meow_verify/sha256.h create mode 100644 patch_v2/meow_verify/tweetnacl.c create mode 100644 patch_v2/meow_verify/tweetnacl.h diff --git a/.gitignore b/.gitignore index 1230bf2..6bee47a 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,8 @@ __pycache__/ font/atlas/ # SSH 私钥/认证(绝不入库;.pub 公钥可留但这里一并忽略,按需手动 add) ssh_keys/ +# .mpk 签名私钥(Ed25519,离线维护者侧;绝不入库) +.keys/ # 二进制备份(*.bak 由 build/升级流程生成,勿入库) *.bak *.orig @@ -65,3 +67,8 @@ ssh_keys/ devdocs/ scripts/build/ fonts_lib/ + +# 字体源文件(体积大,不进库;产物在 TF 卡) +font/src/**/*.otf +font/src/**/*.ttf +font/src/**/*.ttc diff --git a/patch_v2/meow_launcherd/app_launcher.c b/patch_v2/meow_launcherd/app_launcher.c index 719f25e..0d0ae05 100644 --- a/patch_v2/meow_launcherd/app_launcher.c +++ b/patch_v2/meow_launcherd/app_launcher.c @@ -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 --package 。返回 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//data/.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//。 * 约定 .mpk = tar.gz,顶层即包目录(含 app.json);包名 = 文件名茎 .mpk。 * 同名先备份;解压后校验 app.json;随后数据 init(MeowData//{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//;同名先备份(.bak_),再解压,再校验 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//{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//data/.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 && " diff --git a/patch_v2/meow_sign/build_sign.sh b/patch_v2/meow_sign/build_sign.sh new file mode 100755 index 0000000..4be6f78 --- /dev/null +++ b/patch_v2/meow_sign/build_sign.sh @@ -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 diff --git a/patch_v2/meow_sign/meow_sign b/patch_v2/meow_sign/meow_sign new file mode 100755 index 0000000000000000000000000000000000000000..ced32c45f3444e0187b8c87eee94297f787916b7 GIT binary patch literal 74064 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 WE~&RO+dcXglu literal 0 HcmV?d00001 diff --git a/patch_v2/meow_sign/meow_sign.c b/patch_v2/meow_sign/meow_sign.c new file mode 100644 index 0000000..0603539 --- /dev/null +++ b/patch_v2/meow_sign/meow_sign.c @@ -0,0 +1,184 @@ +/* meow_sign.c — MeowOS .mpk 产签工具(宿主端,非设备;离线维护者侧使用)。 + * + * 功能(MPK_SIGNATURE_DESIGN §4.2): + * - keygen :生成 Ed25519 密钥对。私钥(64B seed||pub,0600)写 ; + * 公钥(32B hex + raw)写 .pub,并在 stdout 打印 hex(便于固化进 meow_verify)。 + * - sign .mpk :对 .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 +#include +#include +#include +#include +#include +#include + +#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: 需要 .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 0600 + .pub,明文、不破坏现有)\n" + " %s sign .mpk 对 .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; +} diff --git a/patch_v2/meow_svc_power/meow_svc_power b/patch_v2/meow_svc_power/meow_svc_power index bc576abb06f35b2bdcd84f1381307600110ba392..27457ca12a1cdb498d975c7d910ff81461fcb7ac 100755 GIT binary patch delta 20440 zcma)k0bEp7_Wyl%24)Tp<3>C|akkHI^U1v0Z z#<~RPMIkZ-L`5Y)P(d?AbIn@U%(Pr<&DyPX%MAIu<Xn5d+xdSp8I&Ehl85`98?i*GRJZ@jxjblf3+*a8xqSHJ$hgmlkkr*GxAi69(EK6 zJV8_(DDOdek4||epuwr}j0J1u20*h?j`lrhj~~$vJlS6^$Zx)lvF6((dV<+L)Y-TC zpRe8Fd0x*d?&s_bp2qt)iS)7R*o@j9~-vg$u^a z&wnyu%tN4*o}E1(g_O)DvBm^Ai?PNV(M7Y1pL=Hh{AcDBI`U^1J~KPt5j$%}LBZ@| z$Ft8A=KnG}-|@mT`66ymWJms>?NWz%aGS~Nna!EwDQF1r3ZBV${5So=F;1UWL2ETX zeg0nk)BMflls_iaI@8d89MY5zJSg`n$bk*3U*#+omFM6&Jb=MI;Hlms4N{r}$VR;r zZ*bOz=P;f|Jaw;d_9@^6JVmYOSQ}>rcpC9sM}EQ%&Z_a8X@hlML?RmnU4Z9N7=d&? z(%ndxAYBR=wxi=K>ladD^G6Ww3}+#D!th*0zVc)Bi3Ox5LBs2j)_>e_E&PV$?Sg^k z!h)}aNbCgC=PeTR=oniuOk#O}EKFiPz!QMy0rRaA3xT;e4wqO0U~0I;RsbHhNvsX9 z8kT@_ZB_@Nxg4-q52pADB zvBQ8pVZcXuva#=n!Do zCW%c0oCdfEuo|#3*bB*#Xahpt42FOnz%zh8z$<_k0mCry!?&QvfK7mzfENJs0Nb`o ztQ2s{Hq3dzY1<`s3h*f41wc=;#EeGN1C#;7-;h``;5fi+FA~#`C;+?w=mR{~3Jn0V z9T14h0nbyuU1HY(Wx%jOUQa3^3T;88&HA&Gqo_$goy z;DtY82nI7Y;Rpl-EO<|1lL1SQW9R_80V@DMJuaoNCM2rgm)JhQtAHl}i%v-F?^FTs zDq!9RFuD-NmH^6tMIWNaV$;Y-UKcBljC2oKp9;%I0m}@%DvNr@ZNUY|G))jTyN#E| zizK_9CyWuf0L^2>Lc5DQ9uPIi9eqITMU^E9;v;(;Hzx`n=}O7I1CtQ#K`mj@25B{~ z(6Ml2E1|db4`S}AWw8@BNENVLrK}BQ@+2`m(k5LHzlj{hi;~3VNITEGOB?|>dYAYr z(#4yTg~?&($#;u5hmF_WEyg?Ic+VtJ>~KkE#9GHFd)q^BBr1JJfPNVA3787L{oTTT5nXFbZW^Ry}8Rd4+8t>LS6 z6{w{2{t6QMBXDGD?-IlZBZr9tu0dAgJ8-EQS&}0G&SAkj;+)IQFTNvgfbq1WaN=4G z(yb3Q4j&cqQP#xsD2k<`0sW_r?L-yhyKuQwhMxn^QQ#`y6$_(We9@moO%!bFnAjU- zn``_t96Hs4(gXHGd@|upno%aJW#KenyXi-@O z$_m~WX|j#)JRzpaakhOYe@dCS^q|)HlcH93+wxCgF#(EA3RSFz7?V5Oc}kpN!59BaurI0M)#y5iBS zBFL79g38bPt(CO10Jtlki%_>~(0U}pzv9dxhPrPP-7dYCJa6*X+Mu8^>FW$|1;8oh z4GRg(z8>I9fZvBY^ds@~@U)rV^fM$}IB<=?Euu>J5so}~#KnG-C7fpv8^cO~TtT() zBj%G)G347m<_==+0KNeD*l$H}jC~M!{fO_ds}N&jhMUgp{G~_Sf{LKD`n1j>ea0K<@`#~9+Q=`O0bZP23Zbf9uft=bhtl0RR4g`p+

K4OL+O~3nmSlkw0#(541A~z_Jjd$ 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 delta 19123 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 diff --git a/patch_v2/meow_svc_power/meow_svc_power.c b/patch_v2/meow_svc_power/meow_svc_power.c index b4f20d8..0a4a586 100644 --- a/patch_v2/meow_svc_power/meow_svc_power.c +++ b/patch_v2/meow_svc_power/meow_svc_power.c @@ -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//` 判定包名,或命中系统二进制 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// 取包名;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//(与 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//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; } diff --git a/patch_v2/meow_svc_power/meow_svc_power_host b/patch_v2/meow_svc_power/meow_svc_power_host index 9148c36aea11e9f4feb4c8f52be0968850d0664e..e47498fc975da63dd3b5000b69246d6b3e98bf6b 100755 GIT binary patch delta 9609 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 UAl;p$OINE5O?pjzcNES2e+$WllmGw# delta 8718 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 IUd8AAA3Q;ae*gdg diff --git a/patch_v2/meow_svc_power/svc_power_authtest.c b/patch_v2/meow_svc_power/svc_power_authtest.c new file mode 100644 index 0000000..ed4b96d --- /dev/null +++ b/patch_v2/meow_svc_power/svc_power_authtest.c @@ -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 +#include +#include + +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; +} diff --git a/patch_v2/meow_verify/build_verify.sh b/patch_v2/meow_verify/build_verify.sh new file mode 100755 index 0000000..9b154dd --- /dev/null +++ b/patch_v2/meow_verify/build_verify.sh @@ -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 diff --git a/patch_v2/meow_verify/meow_sig.h b/patch_v2/meow_verify/meow_sig.h new file mode 100644 index 0000000..e094de3 --- /dev/null +++ b/patch_v2/meow_verify/meow_sig.h @@ -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 +#include + +#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 */ diff --git a/patch_v2/meow_verify/meow_signing_pub.h b/patch_v2/meow_verify/meow_signing_pub.h new file mode 100644 index 0000000..3ce23ec --- /dev/null +++ b/patch_v2/meow_verify/meow_signing_pub.h @@ -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 + +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 */ diff --git a/patch_v2/meow_verify/meow_verify b/patch_v2/meow_verify/meow_verify new file mode 100755 index 0000000000000000000000000000000000000000..2dcc4705c37a889f2888f6afb79cd7ee9adffab6 GIT binary patch literal 80416 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

RYuExEKHnE%jPfeo3uhX}@OWf1IW;{i;`2#% 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 # 验签(默认系统模式:固件公钥 + 绑定①) + * meow_verify --system # 同上(显式) + * meow_verify --package # 显式给期望包名(否则从文件名茎推导) + * meow_verify --has-sig # 只探测是否存在 MEOWSIG1 footer:输出 yes/no + * meow_verify --strip # 输出去 footer 的干净 tar.gz 到 stdout(无 footer 则原样复制) + * + * 退出码:0=通过;1=有签但不通过(篡改/包名不符/公钥不符/格式错);2=无签名 footer;3=用法/IO 错误。 + */ +#include +#include +#include +#include +#include +#include + +#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 ] \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 \n"); return 3; } + + if (mode == 1) return has_sig(mpk); + if (mode == 2) return strip_mpk(mpk); + return verify_mpk(mpk, pkg); +} diff --git a/patch_v2/meow_verify/meow_verify_host b/patch_v2/meow_verify/meow_verify_host new file mode 100755 index 0000000000000000000000000000000000000000..b6e23e49bc733c0eb68a4a0e99735126e5cc2845 GIT binary patch literal 73928 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 literal 0 HcmV?d00001 diff --git a/patch_v2/meow_verify/sha256.c b/patch_v2/meow_verify/sha256.c new file mode 100644 index 0000000..b6d9e9c --- /dev/null +++ b/patch_v2/meow_verify/sha256.c @@ -0,0 +1,97 @@ +/* sha256.c — 紧凑 SHA-256(FIPS 180-4),公开领域参考写法,-Wall -Wextra 0 warning。 + * 流式 context;meow_verify / meow_sign 共用。 */ +#include "sha256.h" +#include + +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]); + } +} diff --git a/patch_v2/meow_verify/sha256.h b/patch_v2/meow_verify/sha256.h new file mode 100644 index 0000000..2ba51bd --- /dev/null +++ b/patch_v2/meow_verify/sha256.h @@ -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 +#include + +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 */ diff --git a/patch_v2/meow_verify/tweetnacl.c b/patch_v2/meow_verify/tweetnacl.c new file mode 100644 index 0000000..1a6099f --- /dev/null +++ b/patch_v2/meow_verify/tweetnacl.c @@ -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; +} diff --git a/patch_v2/meow_verify/tweetnacl.h b/patch_v2/meow_verify/tweetnacl.h new file mode 100644 index 0000000..b9f85f6 --- /dev/null +++ b/patch_v2/meow_verify/tweetnacl.h @@ -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 */ diff --git a/patch_v2/meowpkg/nyai b/patch_v2/meowpkg/nyai index f234b6cfe3eba1090ba72c240a96324f85ad7cc5..e9869ca291a68ebda0bcf2d0fe8a9b630eeb915c 100755 GIT binary patch delta 33888 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 Q33Y#q!hWSL->mffU(2Q0{Qv*} delta 31020 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

nob{HMnG3uV| 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 WK3L4$pr0Ys%`b8GOPM~f@BamJi&S|4 diff --git a/patch_v2/meowpkg/nyai.c b/patch_v2/meowpkg/nyai.c index 277dfaf..a35e348 100644 --- a/patch_v2/meowpkg/nyai.c +++ b/patch_v2/meowpkg/nyai.c @@ -23,6 +23,7 @@ #include #include #include +#include #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 ---------------- */ +/* ---------------- index ---------------- + * 注意(MPK_SIGNATURE_DESIGN §5.5):索引记录的 SHA256 = 整包哈希(含尾部签名 footer 的 .mpk)。 + * 必须先 `meow_sign sign .mpk` 完成签名、再 `nyai index `,否则索引与装后的整包不一致, + * 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 --package 验签。 + * 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//data/.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//data/.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//data/.mpk.src(导出保留签名) */ + if (keep_src) save_mpk_source(mpk_path, pkg); + /* ⑤ 安装成功后清理本次创建的 .bak_(失败回滚路径不动) */ 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); diff --git a/patch_v2/meowpkg/nyai_host b/patch_v2/meowpkg/nyai_host index e65753997ec761ee2a587e62ec3abffc48be865f..67ba96712f3f2e85d69a73bf99f34396bded61e8 100755 GIT binary patch delta 18620 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 delta 16087 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 z zKLeO1bcP+Hh(f_-<*V^kK|2Q0JFU738+!i>y?^9m(=jQECRYn^>15?v5T@NBjX4O| z0l@t2efTNhP?6$>lxWLB`k#%ljdIbNa9rB$-3=hb+K`F%APh{39QSCEL{S)2V5Q_b zFdAfB7mhPvA4`&ieS8l|MN7fS7*ChS3?KWzmb&N@!$jY1(WhfVmGtjFmLzmaZq^T- zWMFi2^ZD0wL`wT)moXghQ`T1mhsnjji0uOD5nml(4^GU|#_X?DXsE>*!BuoKpI^ z;CRI038=B&6)8PrZL%t0Jdf($8JRItQ+VYG-9IBIZ-S77YCM<3IP9mMQUqOHk}*=O z51uexddSgFe{M!;3d#qR4b$ml8_d+R&J_LI8N<{SC-khFGSrDD^wBqs$e#@MBDNGL z+QPy&N%9sN$FJ9+9>Q6}t0(lwZYoMR@;#yUHPTM#AKp}?&g#%F-c+c4NYHl}SJ%i| zBLh>WfoL-nRS_4Oeg{p^VQ!@}ax%fUoov(uoND6ZKD?(H6?ksJ)mgNEBJcx{^#Sw= zx?o+Q(>&C9Ux>NnVb~AI+YP3vxmu7iiSQ^4giCv5+RfiFuFt|wgY zm{dNP7(>Xr9N)$jk38psQYCI6m3#jyZX8#V^!%F>N8?*ACot+jjg}N9Hm_1U9_>D3 zO)BsGV=DTBo1IR4JfTw8_hxry^e1k9SfjH^$||Jnsz~bPd!A>~`4UVe7{xP{v>}>W zZu#m-qWf-la2LkX$2O7iNt{$_0*8z&-GfFAAf636JO308Y=ua4X>6nQ1)RO?Th; zzQ)NKC?CuMP9wPv6$K-;Ntaaxb-h{TQXm|_N?K{s>yTYQs2DJ6=h#t|;@OAQ!H>%c zhI1-N$X8C4Ld!YYcsq?%EYv&}c!4-p7Qn~34+3XS&(L(jsRf))$r~ZXr!@d;0AP4Y z#|;P_?X(@IkcVSn@t$EE!@J0{AVLHJ*bCLQnWeNj5llidk>QNTg3jfzc&-2@)Q5iW z3l822#@v9_K?RYYGVc7GCc^_wCUPkdANgJ{nN_4UqHATBg`uEd;@~?m{vzN3SYjZ9 z@x9&qYaIN~=s%18I_yycPR8Gh_S5{k<^0FdA3^Nt;_IkGln6%wXV?;uwmvmYegCYr zJ-a;fndAEXx83N)KA*Vs14Wh}4&(Pk4)K&q$ghv<|GBL|vuE%x0R0q&Vo>cNOb6MM zwyXG(k=2vqZp(M_`+(Cro{kz|yXfzWJq13M=vWRo7O?dnI9(#mc0%Zios~8tk~AXs zs95AiRh_>Ucb6N1L;iClI7)CvS511&&y&+W&g4h1)w_kb!@iEVd-1rw{pVRFf2C;w z@S*g4xMpE`5;;Pr}c$g^uvL|oXwaLiE9QDA2)(Som}8{`_$ZiT)%IQv1P&S8|_=1zgVsmEm&Oi%XtePC}Q{i zdd^*=Jx8{@apaASht@vq>A%?X&4$pCm;QdJ=J(&f`N+Z7*B;)mQLn$-vE}JI;}rdk zyL)WW=i8MnalbsQZaKN&aI9YQfIGJT;-V$LT)5@=2hOTne!c8bMWg@SfqOEx4m-(R z*qME{(=^8bHtHSIoQC#Jv~$rui*_m6cC>Y9*)G$34ee00pP^lXwjJ$Jw3!f&`yZf# zcKN%exe#sBKTUHp+P2-Mc@XX34@}czLv^(d4-@D{3GF>-U)y7v8_*8Mr_b;bmh_=% z9;5eCjPC(pKquB{zF;nw4M5Y z&@9=hCulk9uAO?3mZg5YQ(vVO#ZHB2?Ys0-+KAXk@R<1zy(mq#d;v-sk`+xp`6&scwD|)|68cAa48-gh^I?{%=~>kPuicd`E|8c znep{k|N6KO8vyVI;LLqbk6oD`yBm+0@9QNi3uD{y82^EO`^v(E0Z_;oD5zc09|O`% zyxKp~cdjgnt-<4)AL&1=%pd(G9v^8k%~>>4MV~rl{FCZa%Ji?k?jxo3CHi-vUvL5x z5B^R7mbX{GWmQ(J1Iiq;SAS?#jy495FF@}kOHdm6Wb9Khe%(G59D|mWdjL3}0p~TS z5IBJ+dp*^2a-XJ!dO~fFKgB;57mi&Q^SFJz?FrR(o6;vR>Fck)`263We)h>eS~Py7 zjAub(D(pk(Z&IHm;unay0)Z5~3kY`tfdo%KAO1cAZ0)D|#MNVCGcotG|JK*69vwRi zkITQ(-(Nj8p$?CG)urec0yz3QuIw|HO4UeiBQH3cFi}4L7&Z{|nX) z%xo-0+7n~pY*pE-MgMPE6sebZNMdKaj z1^>)~@3!F2TJXAY5)fVS%NE?>lkkJGVS6ojy9Lj*7WNInHR8xGG3QzgU&O0efzJZq zjeaT`TES9F_LN@emt?^gPL%H_WFtq}j0hVbqx?O*H(tqBM=c4TqSqQrWDL%~P`Zt85x&^Dn`Ta!9^ikWFHk_W}<& zvKcO#^odAkNz4g0FJJDB5^J7pekGgV$>vGfG{|O8cx7*p&6RIogkQm1dOOG_t4@5^ zcRAUZa0_)iQHdpYOL$nf`1Kdv%Fm=sN6NQpvbkM0%Ve`wHqXgsdw6djki{n~$a1|{ zG_{plWZ#h8?_k#drrM7Lk@OaH)98>x4vY87_OP6zT{a0}o`|V3 z;5>HUFK6F(2m9s1`LpLNTzJ>LcPwCc7Y%*Yo2*pDzxqPD5>Zvhr0|+dr6?h4U&x|6 zGnEl3(fT6Vj&X$(`YA=p(fT6V=7p#9Q;L(LO4X1gOK|SuIu#Vq5;VB+vq%tVH(xVjoKi^xwZ~y=R