This commit is contained in:
user committed 2025-07-27 03:12:33 +03:00
1 parent 8b68b0f1e3
commit 274c8f1f09
8 files changed
+2126 -6

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
"""
MY Network v3.0 - Cryptographic Module for uploader-bot
Модуль криптографических операций для защиты inter-node коммуникаций.
"""
from .ed25519_manager import Ed25519Manager, get_ed25519_manager, init_ed25519_manager
__all__ = [
'Ed25519Manager',
'get_ed25519_manager',
'init_ed25519_manager'
]
+316
View File
@@ -0,0 +1,316 @@
"""
MY Network v3.0 - Ed25519 Cryptographic Manager for uploader-bot
Модуль для работы с ed25519 ключами и подписями.
Все inter-node сообщения должны быть подписаны и проверены.
"""
import os
import base64
import json
import hashlib
from typing import Dict, Any, Optional, Tuple
from pathlib import Path
import logging
import time
try:
import ed25519
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ed25519 as crypto_ed25519
except ImportError as e:
logging.error(f"Required cryptographic libraries not found: {e}")
raise ImportError("Please install: pip install ed25519 cryptography")
logger = logging.getLogger(__name__)
class Ed25519Manager:
"""Менеджер для ed25519 криптографических операций в uploader-bot"""
def __init__(self, private_key_path: Optional[str] = None, public_key_path: Optional[str] = None):
"""
Инициализация Ed25519Manager
Args:
private_key_path: Путь к приватному ключу
public_key_path: Путь к публичному ключу
"""
self.private_key_path = private_key_path or os.getenv('NODE_PRIVATE_KEY_PATH')
self.public_key_path = public_key_path or os.getenv('NODE_PUBLIC_KEY_PATH')
self._private_key = None
self._public_key = None
self._node_id = None
# Загружаем ключи при инициализации
self._load_keys()
def _load_keys(self) -> None:
"""Загрузка ключей из файлов"""
try:
# Загрузка приватного ключа
if self.private_key_path and os.path.exists(self.private_key_path):
with open(self.private_key_path, 'rb') as f:
private_key_data = f.read()
# Загружаем PEM ключ
self._private_key = serialization.load_pem_private_key(
private_key_data,
password=None
)
# Получаем публичный ключ из приватного
self._public_key = self._private_key.public_key()
# Генерируем NODE_ID из публичного ключа
self._node_id = self._generate_node_id()
logger.info(f"Ed25519 ключи загружены. Node ID: {self._node_id}")
else:
logger.warning(f"Private key file not found: {self.private_key_path}")
except Exception as e:
logger.error(f"Error loading Ed25519 keys: {e}")
raise
def _generate_node_id(self) -> str:
"""Генерация NODE_ID из публичного ключа"""
if not self._public_key:
raise ValueError("Public key not loaded")
# Получаем raw bytes публичного ключа
public_key_bytes = self._public_key.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw
)
# Создаем упрощенный base58-подобный NODE_ID
# В реальной реализации здесь должен быть полный base58
hex_key = public_key_bytes.hex()
return f"node-{hex_key[:16]}"
@property
def node_id(self) -> str:
"""Получить NODE_ID"""
if not self._node_id:
raise ValueError("Node ID not generated. Check if keys are loaded.")
return self._node_id
@property
def public_key_hex(self) -> str:
"""Получить публичный ключ в hex формате"""
if not self._public_key:
raise ValueError("Public key not loaded")
public_key_bytes = self._public_key.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw
)
return public_key_bytes.hex()
def sign_message(self, message: Dict[str, Any]) -> str:
"""
Подписать сообщение ed25519 ключом
Args:
message: Словарь с данными для подписи
Returns:
base64-encoded подпись
"""
if not self._private_key:
raise ValueError("Private key not loaded")
# Сериализуем сообщение в JSON для подписи
message_json = json.dumps(message, sort_keys=True, ensure_ascii=False)
message_bytes = message_json.encode('utf-8')
# Создаем хеш сообщения для подписи
message_hash = hashlib.sha256(message_bytes).digest()
# Подписываем хеш
signature = self._private_key.sign(message_hash)
# Возвращаем подпись в base64
return base64.b64encode(signature).decode('ascii')
def verify_signature(self, message: Dict[str, Any], signature: str, public_key_hex: str) -> bool:
"""
Проверить подпись сообщения
Args:
message: Словарь с данными
signature: base64-encoded подпись
public_key_hex: Публичный ключ в hex формате
Returns:
True если подпись валидна
"""
try:
# Восстанавливаем публичный ключ из hex
public_key_bytes = bytes.fromhex(public_key_hex)
public_key = crypto_ed25519.Ed25519PublicKey.from_public_bytes(public_key_bytes)
# Сериализуем сообщение так же как при подписи
message_json = json.dumps(message, sort_keys=True, ensure_ascii=False)
message_bytes = message_json.encode('utf-8')
message_hash = hashlib.sha256(message_bytes).digest()
# Декодируем подпись
signature_bytes = base64.b64decode(signature.encode('ascii'))
# Проверяем подпись
public_key.verify(signature_bytes, message_hash)
return True
except Exception as e:
logger.warning(f"Signature verification failed: {e}")
return False
def create_signed_message(self, message_type: str, data: Dict[str, Any]) -> Dict[str, Any]:
"""
Создать подписанное сообщение для отправки
Args:
message_type: Тип сообщения (handshake, sync_request, etc.)
data: Данные сообщения
Returns:
Подписанное сообщение
"""
# Основная структура сообщения
message = {
"type": message_type,
"node_id": self.node_id,
"public_key": self.public_key_hex,
"timestamp": int(time.time()),
"data": data
}
# Подписываем сообщение
signature = self.sign_message(message)
# Добавляем подпись
signed_message = message.copy()
signed_message["signature"] = signature
return signed_message
def verify_incoming_message(self, message: Dict[str, Any]) -> Tuple[bool, Optional[str]]:
"""
Проверить входящее подписанное сообщение
Args:
message: Входящее сообщение
Returns:
(is_valid, error_message)
"""
try:
# Проверяем обязательные поля
required_fields = ["type", "node_id", "public_key", "timestamp", "data", "signature"]
for field in required_fields:
if field not in message:
return False, f"Missing required field: {field}"
# Извлекаем подпись и создаем сообщение без подписи для проверки
signature = message.pop("signature")
# Проверяем подпись
is_valid = self.verify_signature(message, signature, message["public_key"])
if not is_valid:
return False, "Invalid signature"
# Проверяем временную метку (не старше 5 минут)
current_time = int(time.time())
if abs(current_time - message["timestamp"]) > 300:
return False, "Message timestamp too old"
return True, None
except Exception as e:
return False, f"Verification error: {str(e)}"
def create_handshake_message(self, target_node_id: str, additional_data: Optional[Dict] = None) -> Dict[str, Any]:
"""
Создать сообщение для handshake с другой нодой
Args:
target_node_id: ID целевой ноды
additional_data: Дополнительные данные
Returns:
Подписанное handshake сообщение
"""
handshake_data = {
"target_node_id": target_node_id,
"protocol_version": "3.0",
"node_type": os.getenv("NODE_TYPE", "uploader"),
"capabilities": ["upload", "content_streaming", "conversion", "storage"]
}
if additional_data:
handshake_data.update(additional_data)
return self.create_signed_message("handshake", handshake_data)
def create_upload_message(self, content_hash: str, metadata: Dict[str, Any]) -> Dict[str, Any]:
"""
Создать подписанное сообщение для загрузки контента
Args:
content_hash: Хеш контента
metadata: Метаданные файла
Returns:
Подписанное upload сообщение
"""
upload_data = {
"content_hash": content_hash,
"metadata": metadata,
"uploader_node": self.node_id,
"upload_timestamp": int(time.time())
}
return self.create_signed_message("content_upload", upload_data)
def create_sync_message(self, content_list: list, operation: str = "announce") -> Dict[str, Any]:
"""
Создать сообщение для синхронизации контента
Args:
content_list: Список контента для синхронизации
operation: Тип операции (announce, request, response)
Returns:
Подписанное sync сообщение
"""
sync_data = {
"operation": operation,
"content_list": content_list,
"sync_id": hashlib.sha256(
(self.node_id + str(int(time.time()))).encode()
).hexdigest()[:16]
}
return self.create_signed_message("content_sync", sync_data)
# Глобальный экземпляр менеджера
_ed25519_manager = None
def get_ed25519_manager() -> Ed25519Manager:
"""Получить глобальный экземпляр Ed25519Manager"""
global _ed25519_manager
if _ed25519_manager is None:
_ed25519_manager = Ed25519Manager()
return _ed25519_manager
def init_ed25519_manager(private_key_path: str, public_key_path: str) -> Ed25519Manager:
"""Инициализировать Ed25519Manager с путями к ключам"""
global _ed25519_manager
_ed25519_manager = Ed25519Manager(private_key_path, public_key_path)
return _ed25519_manager