feat(launcher,lock): MeowOS 锁屏 4 位 PIN(system.pin 同源面板,错5锁5分钟持久化,唤醒/电源菜单手动锁)

- system.pin:4 位数字 PIN,sha256+salt 存 settings.conf(格式与面板 panel.pin 全等,绝无明文)。
  panel.pin 已设且 system.pin 未设时锁屏接受 panel.pin,首次验证成功后并入 system.pin。
- 锁屏时机:息屏唤醒后(默认开,system.pin_lock_on_wake=1 可关)+ 电源菜单「锁定」手动锁;
  开机不锁(boot 进主屏不拦)。
- 锁屏页:launcher 渲染系统级全屏页,App 不可见不可触;SDK 按钮画数字键盘(不调 IME),
  输入 4 位自动校验;● 遮罩不回显数字;错 5 次锁 5 分钟(持久化锁定戳/计数,重启不清)。
- 安全细节:锁屏页电源键=息屏(允许再睡),音量键不动,不响应左缘返回/通知中心手势。
  兜底:SSH root 删 system.pin 键即解锁(无其它后门)。HibyOS 不涉及。
- 宿主测试(test_lock_screen):设置/校验/错5锁定持久化(重载模拟重启)/唤醒触发/开机不锁
  /panel.pin 迁移,全 PASS;既有 settings_brightness/notif_panel/notif_tf/vol_ramp_jack 回归
  无碍;交叉 -Wall -Wextra 0 warning。meow_launcherd md5 670f978e71c1e81cfe86d47c21248c9d。
This commit is contained in:
AskaEth committed 2026-09-02 00:48:23 +08:00
1 parent 5a5e995ebd
commit e948a0a44e
14 files changed
+1011 -14

No files matched your search

+353
View File
@@ -0,0 +1,353 @@
/* lock_screen.c — MeowOS 锁屏 4 位 PIN(纯逻辑;宿主可单测 lock_screen.h)。
*
* 与 meowpanel.c 的访问级 PIN(PANEL_ENHANCE_PLAN)**同源同格式**:`<salt_hex>:<sha256_hex(salt||pin)>`。
* 内联 SHA-256 取自 meow_verify/sha256.c(FIPS 180-4,公有领域参考写法),前缀 lck_ 避免符号冲突;
* 算法与 meowpanel.c 的 mp_sha256 逐字一致 → 面板生成的 panel.pin 无需重设即可被本模块校验。
*
* 日志用 fprintf(stderr)(前缀 [lock]),不依赖 launcher 的 logmsg —— 宿主测试可直接 #include 本文件。
* 依赖 meow_settings_* API(同一 settings.conf),持久化走 meow_settings_save()(含 boot.default
* 同步,幂等)。
*/
#include "lock_screen.h"
#include "meow_settings.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#define LCK_LOG(...) fprintf(stderr, "[lock] " __VA_ARGS__)
/* ---- 最小 SHA-256(FIPS 180-4,公有领域参考写法;内联拷贝自 meow_verify/sha256.c,前缀 lck_) ---- */
static const uint32_t lck_sha256_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 LCK_ROR(x,n) (((x) >> (n)) | ((x) << (32 - (n))))
#define LCK_CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
#define LCK_MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define LCK_BSIG0(x) (LCK_ROR(x,2) ^ LCK_ROR(x,13) ^ LCK_ROR(x,22))
#define LCK_BSIG1(x) (LCK_ROR(x,6) ^ LCK_ROR(x,11) ^ LCK_ROR(x,25))
#define LCK_SSIG0(x) (LCK_ROR(x,7) ^ LCK_ROR(x,18) ^ ((x) >> 3))
#define LCK_SSIG1(x) (LCK_ROR(x,17) ^ LCK_ROR(x,19) ^ ((x) >> 10))
typedef struct lck_sha256_ctx {
uint32_t state[8];
uint64_t count;
uint8_t buf[64];
size_t buflen;
} lck_sha256_ctx;
static void lck_sha256_init(lck_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 lck_sha256_block(lck_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] = LCK_SSIG1(w[i-2]) + w[i-7] + LCK_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 + LCK_BSIG1(e) + LCK_CH(e,f,g) + lck_sha256_k[i] + w[i];
uint32_t t2 = LCK_BSIG0(a) + LCK_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;
}
static void lck_sha256_update(lck_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) { lck_sha256_block(c, c->buf); c->buflen = 0; }
}
while (len >= 64) { lck_sha256_block(c, data); data += 64; len -= 64; }
if (len) { memcpy(c->buf, data, len); c->buflen = len; }
}
static void lck_sha256_final(lck_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;
if (bl > 56) {
while (bl < 64) b[bl++] = 0;
lck_sha256_block(c, b);
bl = 0;
}
while (bl < 56) b[bl++] = 0;
for (i = 0; i < 8; i++)
b[bl++] = (uint8_t)(bitlen >> (56 - i * 8));
lck_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]);
}
}
#undef LCK_ROR
#undef LCK_CH
#undef LCK_MAJ
#undef LCK_BSIG0
#undef LCK_BSIG1
#undef LCK_SSIG0
#undef LCK_SSIG1
/* ---- 哈希基元(格式 `<salt_hex>:<sha256_hex(salt||pin)>`,与面板全等)---- */
static void lck_hex(const unsigned char *raw, size_t n, char *out)
{
static const char hex[] = "0123456789abcdef";
for (size_t i = 0; i < n; i++) {
out[i*2] = hex[raw[i] >> 4];
out[i*2+1] = hex[raw[i] & 0xf];
}
out[n*2] = 0;
}
static int lck_read_random(unsigned char *buf, size_t n)
{
int fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) return -1;
size_t got = 0;
while (got < n) {
ssize_t rd = read(fd, buf + got, n - got);
if (rd <= 0) { close(fd); return -1; }
got += (size_t)rd;
}
close(fd);
return 0;
}
static int lck_make_salt(char *out, int outcap) /* 16B → 32 hex 盐 */
{
unsigned char raw[16];
if (outcap < 33) return -1;
if (lck_read_random(raw, sizeof raw) < 0) return -1;
lck_hex(raw, sizeof raw, out);
return 0;
}
static int lck_pass_hash(const char *salt, const char *password, char *out, int outcap)
{
lck_sha256_ctx c;
uint8_t digest[32];
if (outcap < LCK_HASH_LEN + 1) return -1;
lck_sha256_init(&c);
lck_sha256_update(&c, (const uint8_t *)salt, strlen(salt));
lck_sha256_update(&c, (const uint8_t *)password, strlen(password));
lck_sha256_final(&c, digest);
lck_hex(digest, sizeof digest, out);
return 0;
}
static int lck_make_hash(const char *password, char *out, int outcap) /* salt:hex */
{
char salt[33], h[LCK_HASH_LEN + 1];
if (lck_make_salt(salt, sizeof salt) < 0) return -1;
if (lck_pass_hash(salt, password, h, sizeof h) < 0) return -1;
snprintf(out, (size_t)outcap, "%s:%s", salt, h);
return 0;
}
/* 校验 `salt:hex` 是否匹配明文(恒定比较,不反解)。stored 为 `salt:hex` 完整串。 */
static int lck_hash_matches(const char *stored, const char *password)
{
if (!stored || !stored[0] || !password) return 0;
char tmp[160];
snprintf(tmp, sizeof tmp, "%s", stored);
char *colon = strchr(tmp, ':');
if (!colon) return 0;
*colon = 0;
char want[LCK_HASH_LEN + 1];
if (lck_pass_hash(tmp, password, want, sizeof want) != 0) return 0;
if (strlen(colon + 1) != LCK_HASH_LEN) return 0;
return memcmp(want, colon + 1, LCK_HASH_LEN) == 0;
}
/* ---- 设置读取辅助(NULL 表示键缺失/空)---- */
static char *lck_get(const char *key)
{
char buf[MEOW_SET_MAX_LEN];
if (meow_settings_get_str(key, buf, sizeof buf) != 0) return NULL;
if (!buf[0]) return NULL; /* 空值=未设 */
char *r = strdup(buf);
return r;
}
static int lck_get_int(const char *key)
{
int v = 0;
if (meow_settings_get_int(key, &v) != 0) v = 0;
return v;
}
static int lck_set_int(const char *key, int v)
{
return meow_settings_set_int(key, v);
}
static int lck_persist(void)
{
return meow_settings_save();
}
/* ---- 公开接口 ---- */
int lck_pin_valid(const char *pin)
{
if (!pin) return 0;
size_t l = strlen(pin);
if (l != LCK_PIN_LEN) return 0;
for (size_t i = 0; i < l; i++)
if (pin[i] < '0' || pin[i] > '9') return 0;
return 1;
}
int lck_pin_configured(void)
{
char *sp = lck_get(LCK_KEY_PIN);
int s = (sp != NULL);
free(sp);
if (s) return 1;
char *pp = lck_get(LCK_KEY_PANEL_PIN);
int p = (pp != NULL);
free(pp);
return p;
}
int lck_lock_required(int allow_disable)
{
if (!lck_pin_configured()) return 0; /* 未设凭据:锁屏无鉴别物,不开 */
if (allow_disable) {
int wake = 1;
if (meow_settings_get_int(LCK_KEY_WAKE, &wake) != 0) wake = 1;
return wake ? 1 : 0;
}
return 1; /* 手动锁:不看 WAKE 键 */
}
int lck_pin_check(const char *pin)
{
if (!lck_pin_valid(pin)) return 0;
char *sp = lck_get(LCK_KEY_PIN);
if (sp) {
int ok = lck_hash_matches(sp, pin);
free(sp);
return ok;
}
/* system.pin 未设:迁移期回退校验 panel.pin */
char *pp = lck_get(LCK_KEY_PANEL_PIN);
if (pp) {
int ok = lck_hash_matches(pp, pin);
free(pp);
return ok;
}
return 0;
}
int lck_pin_set(const char *pin)
{
if (!lck_pin_valid(pin)) return MEOW_ERR_INVAL;
char hash[160]; /* 32(salt) + ':' + 64(sha256 hex) + NUL = 98 */
if (lck_make_hash(pin, hash, sizeof hash) != 0) return MEOW_ERR_IO;
if (meow_settings_set_str(LCK_KEY_PIN, hash) != MEOW_OK) return MEOW_ERR_INVAL;
lck_set_int(LCK_KEY_FAILS, 0);
lck_set_int(LCK_KEY_LOCK_UNTIL, 0);
lck_persist();
LCK_LOG("pin set (%d digits, hash stored salt:sha256)\n", LCK_PIN_LEN);
return MEOW_OK;
}
int lck_pin_clear(void)
{
if (meow_settings_set_str(LCK_KEY_PIN, "") != MEOW_OK) return MEOW_ERR_INVAL;
lck_set_int(LCK_KEY_FAILS, 0);
lck_set_int(LCK_KEY_LOCK_UNTIL, 0);
lck_persist();
LCK_LOG("pin cleared (unlock)\n");
return MEOW_OK;
}
int lck_pin_success(void)
{
char *sp = lck_get(LCK_KEY_PIN);
if (!sp) {
/* 首次成功:把 panel.pin 并入 system.pin(一次性迁移),此后 system.pin 为准 */
char *pp = lck_get(LCK_KEY_PANEL_PIN);
if (pp) {
meow_settings_set_str(LCK_KEY_PIN, pp);
LCK_LOG("success: migrated panel.pin -> system.pin\n");
free(pp);
} else {
LCK_LOG("success: no system.pin and no panel.pin?!\n");
}
}
free(sp);
lck_set_int(LCK_KEY_FAILS, 0);
lck_set_int(LCK_KEY_LOCK_UNTIL, 0);
lck_persist();
return MEOW_OK;
}
int lck_pin_fail(void)
{
time_t now = time(NULL);
int fails = lck_get_int(LCK_KEY_FAILS);
int until = lck_get_int(LCK_KEY_LOCK_UNTIL);
/* 已锁定:不再递增(锁屏页本应禁输入;防御) */
if (until > 0 && (time_t)until > now)
return (int)((time_t)until - now);
fails++;
if (fails >= LCK_PIN_MAX_FAILS) {
until = (int)now + LCK_PIN_LOCK_SEC;
fails = 0;
LCK_LOG("fail: %d consecutive -> LOCK %ds (until=%d)\n",
LCK_PIN_MAX_FAILS, LCK_PIN_LOCK_SEC, until);
} else {
until = 0;
LCK_LOG("fail: attempt %d/%d\n", fails, LCK_PIN_MAX_FAILS);
}
lck_set_int(LCK_KEY_FAILS, fails);
lck_set_int(LCK_KEY_LOCK_UNTIL, until);
lck_persist();
return (until > 0 && (time_t)until > now) ? (int)((time_t)until - now) : 0;
}
time_t lck_lock_remaining(void)
{
time_t now = time(NULL);
int until = lck_get_int(LCK_KEY_LOCK_UNTIL);
if (until > 0 && (time_t)until > now)
return (time_t)until - now;
if (until > 0) {
/* 截止已过:清锁定戳+计数(进程内解禁),落盘 */
LCK_LOG("lock expired (%d < now=%ld), clearing\n", until, (long)now);
lck_set_int(LCK_KEY_LOCK_UNTIL, 0);
lck_set_int(LCK_KEY_FAILS, 0);
lck_persist();
}
return 0;
}