- 面板一次性解锁恢复口:/tmp/meow_pin_unlock_once 存在即被锁屏页消费删除并放行一次 (不校验 PIN、不清 system.pin、不重置失败计数;重启标志自然消失;lck_unlock_once_consume, launcher 侧只做检查/消费,panel 端点由主会话另实现)。 - 系统设置「安全 → 锁屏 PIN」页(lock_settings.c,subpage.c 脚手架):状态(未设置/已设置/ 锁定中+剩余)/ 设置(4 位双输确认)/ 修改(需旧 PIN 一次)/ 清除(**免旧 PIN**,仅二次确认弹窗) / system.pin_lock_on_wake 开关;数字输入用 SDK 按钮键盘(lck_input_pin_modal)。 - 电源菜单「锁定」主动锁强化:立即熄屏 + 写 /tmp/meow_pin_locked(主动锁标志,重启消失, 唤醒时无论 pin_lock_on_wake 是否开都必锁屏)+ rm /tmp/meow_pin_unlock_once 作废挂起绕过; lck_wake_should_lock() 作为唤醒门取代 lck_lock_required(1)(主菜单 2 处 + App 会话 2 处)。 - 测试:test_lock_screen 扩 51 ok——bypass 存在即消费/不存在不影响/消费后删除;清除免旧 PIN; 主动锁标志唤醒必锁/清除后回退;全回归 settings_brightness/notif_panel/notif_tf/vol_ramp_jack 无碍; 交叉 -Wall -Wextra 0 warning。meow_launcherd md5 bdf884756ef8a8353aa8e5dfb6e10a0a。 - SYSTEM_SETTINGS.md 同步锁屏行为段。
390 lines
13 KiB
C
390 lines
13 KiB
C
/* 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;
|
||
}
|
||
|
||
/* ---- 面板一次性解锁恢复口(tmpfs 标志)---- */
|
||
int lck_unlock_once_consume(void)
|
||
{
|
||
if (access(LCK_UNLOCK_ONCE_FILE, F_OK) != 0) return 0; /* 不存在:不影响 */
|
||
unlink(LCK_UNLOCK_ONCE_FILE);
|
||
LCK_LOG("unlock-once: bypass file present -> consume + release once\n");
|
||
return 1;
|
||
}
|
||
|
||
int lck_manual_lock_set(void)
|
||
{
|
||
int fd = open(LCK_MANUAL_LOCK_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||
if (fd < 0) { LCK_LOG("manual-lock: open %s fail\n", LCK_MANUAL_LOCK_FILE); return MEOW_ERR_IO; }
|
||
write(fd, "1", 1); close(fd);
|
||
LCK_LOG("manual-lock: set %s\n", LCK_MANUAL_LOCK_FILE);
|
||
return MEOW_OK;
|
||
}
|
||
|
||
void lck_manual_lock_clear(void)
|
||
{
|
||
if (access(LCK_MANUAL_LOCK_FILE, F_OK) == 0) {
|
||
unlink(LCK_MANUAL_LOCK_FILE);
|
||
LCK_LOG("manual-lock: cleared\n");
|
||
}
|
||
}
|
||
|
||
int lck_wake_should_lock(void)
|
||
{
|
||
if (!lck_pin_configured()) return 0;
|
||
int wake = 1;
|
||
if (meow_settings_get_int(LCK_KEY_WAKE, &wake) != 0) wake = 1;
|
||
if (wake) return 1;
|
||
/* 唤醒锁已关,但存在「主动锁」标志(电源菜单「锁定」)→ 仍必锁屏 */
|
||
return access(LCK_MANUAL_LOCK_FILE, F_OK) == 0;
|
||
}
|