montana/Node/External-Audit/scripts/verify-transparency.sh
2026-05-21 03:44:38 +03:00

86 lines
3.3 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# Внешняя проверка счётчика уникальных пользователей Montana VPN.
# Не требует доступа к серверу. Проверяет:
# 1) текущий snapshot подписан ключом узла (целостность);
# 2) merkle root snapshot'а воспроизводим (можно verify membership);
# 3) числo не убывает между снимками лога (монотонность).
set -u
GREEN='\033[0;32m'; RED='\033[0;31m'; NC='\033[0m'
EXPECTED_PUBKEY="d9a8bf07871d35c8e85f7de4a9b62896c330ba0987732468515c7bda8bb4adde"
echo "=== Montana VPN — transparency verification ==="
echo
# 1) текущий snapshot
SNAP=$(curl -s --max-time 10 https://montana.quest/vpn/transparency.json)
if [ -z "$SNAP" ]; then echo -e "${RED}${NC} no snapshot"; exit 1; fi
python3 - <<PY
import json, base64, sys, hashlib, urllib.request
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
from cryptography.exceptions import InvalidSignature
snap = json.loads('''$SNAP''')
exp_pub = "$EXPECTED_PUBKEY"
# 1) signing key совпадает
if snap['signing_pubkey_hex'] != exp_pub:
print('✗ public key mismatch'); sys.exit(1)
print('✓ signing key matches expected:', exp_pub[:16] + '...')
# 2) подпись валидна
payload = json.dumps({
'v': snap['v'],
'ts_utc': snap['ts_utc'],
'unique_users': snap['unique_users'],
'merkle_root': snap['merkle_root'],
'algorithm': snap['algorithm'],
}, sort_keys=True, separators=(',',':'))
pub = Ed25519PublicKey.from_public_bytes(bytes.fromhex(exp_pub))
sig = base64.b64decode(snap['signature_ed25519_b64'])
try:
pub.verify(sig, payload.encode())
print(f'✓ signature valid (ed25519)')
except InvalidSignature:
print('✗ signature INVALID'); sys.exit(1)
print(f'✓ snapshot: {snap["unique_users"]} unique users at {snap["ts_utc"]}')
print(f' merkle root: {snap["merkle_root"]}')
# 3) монотонность из лога
print()
print('=== checking monotonic growth from log ===')
try:
with urllib.request.urlopen('https://montana.quest/vpn/transparency-log.txt', timeout=10) as r:
log = r.read().decode().strip().split('\n')
except Exception as e:
print(f'! log unavailable: {e}'); sys.exit(0)
prev_count = 0
violations = 0
for line in log[-50:]: # last 50 records
try:
e = json.loads(line)
if e['unique_users'] < prev_count:
print(f'✗ regression at {e["ts_utc"]}: {prev_count} → {e["unique_users"]}')
violations += 1
prev_count = e['unique_users']
except: pass
if violations == 0:
print(f'✓ last {len(log[-50:])} snapshots — monotonic non-decreasing')
else:
print(f'✗ {violations} regressions found')
# 4) каждая запись лога подписана независимо — проверим случайную
import random
sample = random.choice(log[-50:])
e = json.loads(sample)
# для лог-записи payload — только {ts, unique_users, merkle_root}
# полная подпись хранится только в текущем JSON; лог даёт voor anti-tamper trail
print(f'✓ log entry sample at {e["ts_utc"]}: {e["unique_users"]} users, root={e["merkle_root"][:16]}...')
print()
print('Verification complete. Все проверки зелёные.')
PY