mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-25 00:51:20 +00:00
- Refactor gateway/platforms/qqbot.py into gateway/platforms/qqbot/ package: - adapter.py: core QQAdapter (unchanged logic, constants from shared module) - constants.py: shared constants (API URLs, timeouts, message types) - crypto.py: AES-256-GCM key generation and secret decryption - onboard.py: QR-code scan-to-configure API (create_bind_task, poll_bind_result) - utils.py: User-Agent builder, HTTP headers, config helpers - __init__.py: re-exports all public symbols for backward compatibility - Add interactive QR-code setup flow in hermes_cli/gateway.py: - Terminal QR rendering via qrcode package (graceful fallback to URL) - Auto-refresh on QR expiry (up to 3 times) - AES-256-GCM encrypted credential exchange - DM security policy selection (pairing/allowlist/open) - Update hermes_cli/setup.py to delegate to gateway's _setup_qqbot() - Add qrcode>=7.4 dependency to pyproject.toml and requirements.txt
45 lines
1.4 KiB
Python
45 lines
1.4 KiB
Python
"""AES-256-GCM utilities for QQBot scan-to-configure credential decryption."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import base64
|
|
import os
|
|
|
|
|
|
def generate_bind_key() -> str:
|
|
"""Generate a 256-bit random AES key and return it as base64.
|
|
|
|
The key is passed to ``create_bind_task`` so the server can encrypt
|
|
the bot's *client_secret* before returning it. Only this CLI holds
|
|
the key, ensuring the secret never travels in plaintext.
|
|
"""
|
|
return base64.b64encode(os.urandom(32)).decode()
|
|
|
|
|
|
def decrypt_secret(encrypted_base64: str, key_base64: str) -> str:
|
|
"""Decrypt a base64-encoded AES-256-GCM ciphertext.
|
|
|
|
Ciphertext layout (after base64-decoding)::
|
|
|
|
IV (12 bytes) ‖ ciphertext (N bytes) ‖ AuthTag (16 bytes)
|
|
|
|
Args:
|
|
encrypted_base64: The ``bot_encrypt_secret`` value from
|
|
``poll_bind_result``.
|
|
key_base64: The base64 AES key generated by
|
|
:func:`generate_bind_key`.
|
|
|
|
Returns:
|
|
The decrypted *client_secret* as a UTF-8 string.
|
|
"""
|
|
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
|
|
key = base64.b64decode(key_base64)
|
|
raw = base64.b64decode(encrypted_base64)
|
|
|
|
iv = raw[:12]
|
|
ciphertext_with_tag = raw[12:] # AESGCM expects ciphertext + tag concatenated
|
|
|
|
aesgcm = AESGCM(key)
|
|
plaintext = aesgcm.decrypt(iv, ciphertext_with_tag, None)
|
|
return plaintext.decode("utf-8")
|