Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/lock_screen.c
T
AskaEth e948a0a44e 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。
2026-09-02 00:48:23 +08:00

354 lines
12 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.
/* 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;
}