Files
MeowOS-For-R3P2/patch_v2/meow_sign/meow_sign.c
T
MeowOS 3f57d7fd4f 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为启发式
2026-08-31 04:33:46 +08:00

185 lines
6.4 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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;
}