bump: python ver
fix: race condition in SecureMemory global vs. client instance - no more global state mutation
This commit is contained in:
parent
0746b50ee9
commit
1f0890300d
4 changed files with 13 additions and 20 deletions
|
|
@ -4,14 +4,13 @@ from cryptography.hazmat.primitives import serialization, hashes
|
||||||
from cryptography.hazmat.primitives.asymmetric import rsa, padding
|
from cryptography.hazmat.primitives.asymmetric import rsa, padding
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
|
|
||||||
|
|
||||||
# Setup module logger
|
# Setup module logger
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Import secure memory module
|
# Import secure memory module
|
||||||
try:
|
try:
|
||||||
from .SecureMemory import secure_bytearray, get_memory_protection_info, _get_secure_memory
|
from .SecureMemory import secure_bytearray, _get_secure_memory
|
||||||
_SECURE_MEMORY_AVAILABLE = True
|
_SECURE_MEMORY_AVAILABLE = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
_SECURE_MEMORY_AVAILABLE = False
|
_SECURE_MEMORY_AVAILABLE = False
|
||||||
|
|
@ -77,19 +76,21 @@ class SecureCompletionClient:
|
||||||
- Response parsing
|
- Response parsing
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, router_url: str = "https://api.nomyo.ai:12435", allow_http: bool = False):
|
def __init__(self, router_url: str = "https://api.nomyo.ai:12435", allow_http: bool = False, secure_memory: bool = True):
|
||||||
"""
|
"""
|
||||||
Initialize the secure completion client.
|
Initialize the secure completion client.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
router_url: Base URL of the NOMYO Router (must use HTTPS for production)
|
router_url: Base URL of the NOMYO Router (must use HTTPS for production)
|
||||||
allow_http: Allow HTTP connections (ONLY for local development, never in production)
|
allow_http: Allow HTTP connections (ONLY for local development, never in production)
|
||||||
|
secure_memory: Whether to use secure memory operations for this instance.
|
||||||
"""
|
"""
|
||||||
self.router_url = router_url.rstrip('/')
|
self.router_url = router_url.rstrip('/')
|
||||||
self.private_key = None
|
self.private_key = None
|
||||||
self.public_key_pem = None
|
self.public_key_pem = None
|
||||||
self.key_size = 4096 # RSA key size
|
self.key_size = 4096 # RSA key size
|
||||||
self.allow_http = allow_http # Store for use in fetch_server_public_key
|
self.allow_http = allow_http # Store for use in fetch_server_public_key
|
||||||
|
self._use_secure_memory = _SECURE_MEMORY_AVAILABLE and secure_memory
|
||||||
|
|
||||||
# Validate HTTPS for security
|
# Validate HTTPS for security
|
||||||
if not self.router_url.startswith("https://"):
|
if not self.router_url.startswith("https://"):
|
||||||
|
|
@ -122,7 +123,7 @@ class SecureCompletionClient:
|
||||||
- Rotate keys regularly
|
- Rotate keys regularly
|
||||||
- Store keys outside the project directory in production
|
- Store keys outside the project directory in production
|
||||||
"""
|
"""
|
||||||
if not _SECURE_MEMORY_AVAILABLE or not self.private_key:
|
if not self._use_secure_memory or not self.private_key:
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -416,7 +417,7 @@ class SecureCompletionClient:
|
||||||
|
|
||||||
aes_key = secrets.token_bytes(32) # 256-bit key
|
aes_key = secrets.token_bytes(32) # 256-bit key
|
||||||
try:
|
try:
|
||||||
if _SECURE_MEMORY_AVAILABLE:
|
if self._use_secure_memory:
|
||||||
with secure_bytearray(payload_json) as protected_payload:
|
with secure_bytearray(payload_json) as protected_payload:
|
||||||
with secure_bytearray(aes_key) as protected_aes_key:
|
with secure_bytearray(aes_key) as protected_aes_key:
|
||||||
return await self._do_encrypt(
|
return await self._do_encrypt(
|
||||||
|
|
@ -500,7 +501,7 @@ class SecureCompletionClient:
|
||||||
)
|
)
|
||||||
|
|
||||||
# Use secure memory to protect AES key and decrypted plaintext
|
# Use secure memory to protect AES key and decrypted plaintext
|
||||||
if _SECURE_MEMORY_AVAILABLE:
|
if self._use_secure_memory:
|
||||||
with secure_bytearray(aes_key) as protected_aes_key:
|
with secure_bytearray(aes_key) as protected_aes_key:
|
||||||
ciphertext = base64.b64decode(package["encrypted_payload"]["ciphertext"])
|
ciphertext = base64.b64decode(package["encrypted_payload"]["ciphertext"])
|
||||||
nonce = base64.b64decode(package["encrypted_payload"]["nonce"])
|
nonce = base64.b64decode(package["encrypted_payload"]["nonce"])
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,6 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
__version__ = "0.1.1"
|
__version__ = "0.2.1"
|
||||||
__author__ = "NOMYO AI"
|
__author__ = "NOMYO AI"
|
||||||
__license__ = "Apache-2.0"
|
__license__ = "Apache-2.0"
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,9 @@ import uuid
|
||||||
from typing import Dict, Any, List, Optional
|
from typing import Dict, Any, List, Optional
|
||||||
from .SecureCompletionClient import SecureCompletionClient
|
from .SecureCompletionClient import SecureCompletionClient
|
||||||
|
|
||||||
# Import secure memory module for configuration
|
# Check if secure memory module is available (used only for the user-facing warning)
|
||||||
try:
|
try:
|
||||||
from .SecureMemory import disable_secure_memory, enable_secure_memory
|
from . import SecureMemory as _ # noqa: F401
|
||||||
_SECURE_MEMORY_AVAILABLE = True
|
_SECURE_MEMORY_AVAILABLE = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
_SECURE_MEMORY_AVAILABLE = False
|
_SECURE_MEMORY_AVAILABLE = False
|
||||||
|
|
@ -69,20 +69,14 @@ class SecureChatCompletion:
|
||||||
key_dir: Directory to load/save RSA keys. If None, ephemeral keys are
|
key_dir: Directory to load/save RSA keys. If None, ephemeral keys are
|
||||||
generated in memory for this session only.
|
generated in memory for this session only.
|
||||||
"""
|
"""
|
||||||
self.client = SecureCompletionClient(router_url=base_url, allow_http=allow_http)
|
self.client = SecureCompletionClient(router_url=base_url, allow_http=allow_http, secure_memory=secure_memory)
|
||||||
self._keys_initialized = False
|
self._keys_initialized = False
|
||||||
self._keys_lock = asyncio.Lock()
|
self._keys_lock = asyncio.Lock()
|
||||||
self.api_key = api_key
|
self.api_key = api_key
|
||||||
self._key_dir = key_dir
|
self._key_dir = key_dir
|
||||||
self._secure_memory_enabled = secure_memory
|
self._secure_memory_enabled = secure_memory
|
||||||
|
|
||||||
# Configure secure memory if available
|
if secure_memory and not _SECURE_MEMORY_AVAILABLE:
|
||||||
if _SECURE_MEMORY_AVAILABLE:
|
|
||||||
if secure_memory:
|
|
||||||
enable_secure_memory()
|
|
||||||
else:
|
|
||||||
disable_secure_memory()
|
|
||||||
elif secure_memory:
|
|
||||||
import warnings
|
import warnings
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
"Secure memory requested but not available. "
|
"Secure memory requested but not available. "
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,6 @@ classifiers = [
|
||||||
"Intended Audience :: Information Technology",
|
"Intended Audience :: Information Technology",
|
||||||
"License :: OSI Approved :: Apache Software License",
|
"License :: OSI Approved :: Apache Software License",
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"Programming Language :: Python :: 3.8",
|
|
||||||
"Programming Language :: Python :: 3.9",
|
|
||||||
"Programming Language :: Python :: 3.10",
|
"Programming Language :: Python :: 3.10",
|
||||||
"Programming Language :: Python :: 3.11",
|
"Programming Language :: Python :: 3.11",
|
||||||
"Programming Language :: Python :: 3.12",
|
"Programming Language :: Python :: 3.12",
|
||||||
|
|
@ -27,7 +25,7 @@ classifiers = [
|
||||||
"Topic :: Communications :: Chat",
|
"Topic :: Communications :: Chat",
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
]
|
]
|
||||||
requires-python = ">=3.8"
|
requires-python = ">=3.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyio==4.12.0",
|
"anyio==4.12.0",
|
||||||
"certifi==2025.11.12",
|
"certifi==2025.11.12",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue