M1:镜生域后端闭环——灵胚/照心试演/启镜原子闸门/执镜关系/命台租约/魂籍签名导出/死亡冷静期(迁移 001,测试 7 用例)
This commit is contained in:
1 parent
c090893e63
commit
1cd0069d65
24 files changed
+1508
-9
No files matched your search
@@ -0,0 +1,82 @@
|
||||
"""魂籍(portable_identity_record):可验证、版本化的身份与状态包。
|
||||
|
||||
完整性模型:
|
||||
- payload 规范化序列化(排序键、紧凑分隔符)后取 SHA-256 摘要;
|
||||
- 签名 = HMAC-SHA256(密钥, "{character_id}|{version}|{digest}"),绑定人物与版本;
|
||||
- 校验:重算摘要与签名比对。篡改 payload 破坏摘要;把记录复制到别的人物/版本,
|
||||
签名不匹配——文件相同或人物 ID 相同不足以冒充原主体(data-model.md §1)。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import hmac
|
||||
import json
|
||||
|
||||
from app.core.config import settings
|
||||
from app.models.jingsheng import IdentityRecord
|
||||
|
||||
|
||||
def _canonical(payload: dict) -> str:
|
||||
return json.dumps(payload, ensure_ascii=False, sort_keys=True, separators=(",", ":"))
|
||||
|
||||
|
||||
def _digest(payload: dict) -> str:
|
||||
return hashlib.sha256(_canonical(payload).encode("utf-8")).hexdigest()
|
||||
|
||||
|
||||
def _signature(character_id: str, version: int, digest: str) -> str:
|
||||
secret = settings.identity_record_secret
|
||||
message = f"{character_id}|{version}|{digest}".encode("utf-8")
|
||||
return hmac.new(secret.encode("utf-8"), message, hashlib.sha256).hexdigest()
|
||||
|
||||
|
||||
def build_identity_record(
|
||||
*,
|
||||
character_id: str,
|
||||
version: int,
|
||||
identity: dict,
|
||||
runtime_state: dict,
|
||||
data_state: dict | None = None,
|
||||
) -> IdentityRecord:
|
||||
"""构建一条魂籍版本记录(调用方负责落库)。"""
|
||||
payload = {
|
||||
"spec": "hunjing.portable_identity_record.v1",
|
||||
"character_id": character_id,
|
||||
"version": version,
|
||||
"identity": identity,
|
||||
"data_state": data_state or {"note": "数据状态由各域自报(M1 占位)"},
|
||||
"runtime_state": runtime_state,
|
||||
}
|
||||
digest = _digest(payload)
|
||||
return IdentityRecord(
|
||||
character_id=character_id,
|
||||
version=version,
|
||||
payload_json=json.dumps(payload, ensure_ascii=False),
|
||||
digest=digest,
|
||||
signature=_signature(character_id, version, digest),
|
||||
)
|
||||
|
||||
|
||||
class IdentityVerificationError(Exception):
|
||||
"""校验失败:摘要或签名不匹配。"""
|
||||
|
||||
def __init__(self, reason: str) -> None:
|
||||
super().__init__(reason)
|
||||
self.reason = reason
|
||||
|
||||
|
||||
def verify_identity_record(record: IdentityRecord) -> dict:
|
||||
"""校验魂籍记录,返回规范化 payload;失败抛 IdentityVerificationError。"""
|
||||
try:
|
||||
payload = json.loads(record.payload_json)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise IdentityVerificationError("payload 无法解析") from exc
|
||||
if _digest(payload) != record.digest:
|
||||
raise IdentityVerificationError("摘要不匹配(payload 已被篡改)")
|
||||
expected = _signature(record.character_id, record.version, record.digest)
|
||||
if not hmac.compare_digest(expected, record.signature):
|
||||
raise IdentityVerificationError("签名不匹配(非本平台签发或被移植冒用)")
|
||||
if payload.get("character_id") != record.character_id or payload.get("version") != record.version:
|
||||
raise IdentityVerificationError("payload 声明的身份与记录不符")
|
||||
return payload
|
||||
Reference in new issue
Block a user