"""仅用标准库实现的认证加密盒（加密-then-MAC，HMAC-SHA256 计数器流）。

客户端与服务端必须逐字节一致。密钥由共享密钥 + 卡密 + 机器码派生，
所以一段密文只有"持有该卡密且在该机器上"才能解开，且服务端可随时吊销卡密。
"""

from __future__ import annotations

import hashlib
import hmac
import os


def _subkeys(secret: str, label: str, license_key: str, machine: str) -> tuple[bytes, bytes]:
    root = hmac.new(secret.encode("utf-8"), f"{label}|{license_key}|{machine}".encode("utf-8"), hashlib.sha256).digest()
    enc = hmac.new(root, b"enc", hashlib.sha256).digest()
    mac = hmac.new(root, b"mac", hashlib.sha256).digest()
    return enc, mac


def _keystream(enc_key: bytes, iv: bytes, n: int) -> bytes:
    out = bytearray()
    counter = 0
    while len(out) < n:
        out += hmac.new(enc_key, iv + counter.to_bytes(8, "big"), hashlib.sha256).digest()
        counter += 1
    return bytes(out[:n])


def seal(secret: str, label: str, license_key: str, machine: str, plaintext: bytes) -> bytes:
    enc, mac = _subkeys(secret, label, license_key, machine)
    iv = os.urandom(16)
    ks = _keystream(enc, iv, len(plaintext))
    ct = bytes(a ^ b for a, b in zip(plaintext, ks))
    tag = hmac.new(mac, iv + ct, hashlib.sha256).digest()
    return iv + tag + ct


def open_box(secret: str, label: str, license_key: str, machine: str, blob: bytes) -> bytes:
    if len(blob) < 48:
        raise ValueError("密文过短")
    iv, tag, ct = blob[:16], blob[16:48], blob[48:]
    enc, mac = _subkeys(secret, label, license_key, machine)
    if not hmac.compare_digest(hmac.new(mac, iv + ct, hashlib.sha256).digest(), tag):
        raise ValueError("认证失败")
    ks = _keystream(enc, iv, len(ct))
    return bytes(a ^ b for a, b in zip(ct, ks))
