/* 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; }