commit
cf25e28c28
156 files changed
+38669
No files matched your search
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate independent Laravel CBC envelopes and optionally verify using PHP OpenSSL.
|
||||
|
||||
Run: python tests/crypto_interop.py [--check-php]
|
||||
Requires Python cryptography. The optional PHP check requires PHP's openssl extension.
|
||||
Fixtures exercise Laravel's encryptString envelope, not the entire Laravel framework.
|
||||
"""
|
||||
import argparse
|
||||
import base64
|
||||
import hashlib
|
||||
import hmac
|
||||
import json
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
from cryptography.hazmat.primitives.padding import PKCS7
|
||||
|
||||
|
||||
def generate():
|
||||
fixtures = []
|
||||
for key in (b"0123456789abcdef", b"0123456789abcdef0123456789abcdef"):
|
||||
for plaintext in ("", "Laravel encrypted field", "Unicode 🔐 café"):
|
||||
iv = bytes(range(16))
|
||||
padder = PKCS7(128).padder()
|
||||
padded = padder.update(plaintext.encode()) + padder.finalize()
|
||||
encryptor = Cipher(algorithms.AES(key), modes.CBC(iv)).encryptor()
|
||||
value = base64.b64encode(encryptor.update(padded) + encryptor.finalize()).decode()
|
||||
encoded_iv = base64.b64encode(iv).decode()
|
||||
mac = hmac.new(key, (encoded_iv + value).encode(), hashlib.sha256).hexdigest()
|
||||
payload = dict(iv=encoded_iv, value=value, mac=mac, tag="")
|
||||
envelope = base64.b64encode(json.dumps(payload, separators=(",", ":")).encode()).decode()
|
||||
fixtures.append(dict(cipher=f"AES-{len(key)*8}-CBC", app_key="base64:" + base64.b64encode(key).decode(), plaintext=plaintext, envelope=envelope))
|
||||
return fixtures
|
||||
|
||||
|
||||
def check_php(fixtures):
|
||||
php = shutil.which("php")
|
||||
if not php:
|
||||
print("SKIP PHP OpenSSL: php executable unavailable")
|
||||
return
|
||||
code = r'''
|
||||
$rows = json_decode(stream_get_contents(STDIN), true, 512, JSON_THROW_ON_ERROR);
|
||||
foreach ($rows as $row) {
|
||||
$key = base64_decode(substr($row['app_key'], 7), true);
|
||||
$payload = json_decode(base64_decode($row['envelope']), true);
|
||||
$mac = hash_hmac('sha256', $payload['iv'].$payload['value'], $key);
|
||||
if (!hash_equals($mac, $payload['mac'])) { throw new Exception('MAC mismatch'); }
|
||||
$plain = openssl_decrypt($payload['value'], $row['cipher'], $key, 0, base64_decode($payload['iv']));
|
||||
if ($plain !== $row['plaintext']) { throw new Exception('decrypt mismatch'); }
|
||||
$encrypted = openssl_encrypt($row['plaintext'], $row['cipher'], $key, 0, base64_decode($payload['iv']));
|
||||
if ($encrypted !== $payload['value']) { throw new Exception('encrypt mismatch'); }
|
||||
}
|
||||
echo 'PASS PHP OpenSSL: '.count($rows).' fixtures'.PHP_EOL;
|
||||
'''
|
||||
subprocess.run([php, "-r", code], input=json.dumps(fixtures), text=True, check=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--check-php", action="store_true")
|
||||
args = parser.parse_args()
|
||||
rows = generate()
|
||||
target = Path(__file__).parent / "fixtures" / "laravel_cbc.json"
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
target.write_text(json.dumps(rows, indent=2, ensure_ascii=False) + "\n")
|
||||
print(f"Generated {len(rows)} independent Python AES CBC / HMAC fixtures: {target}")
|
||||
if args.check_php:
|
||||
check_php(rows)
|
||||
Reference in new issue
Block a user