- 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。
354 lines
12 KiB
C
354 lines
12 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;
|
||
}
|