/* lock_screen.c — MeowOS 锁屏 4 位 PIN(纯逻辑;宿主可单测 lock_screen.h)。 * * 与 meowpanel.c 的访问级 PIN(PANEL_ENHANCE_PLAN)**同源同格式**:`:`。 * 内联 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 #include #include #include #include #include #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 /* ---- 哈希基元(格式 `:`,与面板全等)---- */ 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; }