| 1 | |
| 2 | set -e |
| 3 | export PATH="$HOME/.cargo/bin:$PATH" |
| 4 | |
| 5 | WORKDIR=/tmp/oversight-conformance |
| 6 | REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "$0")/../.." && pwd)}" |
| 7 | RUST_CARGO="$REPO_ROOT/oversight-rust/Cargo.toml" |
| 8 | PYTHON_ROOT="$REPO_ROOT" |
| 9 | |
| 10 | rm -rf $WORKDIR |
| 11 | mkdir -p $WORKDIR |
| 12 | cd $WORKDIR |
| 13 | |
| 14 | echo "=== Setup: generate identities in Rust ===" |
| 15 | cargo run --manifest-path $RUST_CARGO --release -q -- keygen --out alice.json 2>&1 | tail -4 |
| 16 | cargo run --manifest-path $RUST_CARGO --release -q -- keygen --out issuer.json 2>&1 | tail -4 |
| 17 | |
| 18 | ALICE_X_PUB=$(python3 -c "import json; print(json.load(open('alice.json'))['x25519_pub'])") |
| 19 | ALICE_X_PRIV=$(python3 -c "import json; print(json.load(open('alice.json'))['x25519_priv'])") |
| 20 | ISSUER_ED_PRIV=$(python3 -c "import json; print(json.load(open('issuer.json'))['ed25519_priv'])") |
| 21 | ISSUER_ED_PUB=$(python3 -c "import json; print(json.load(open('issuer.json'))['ed25519_pub'])") |
| 22 | |
| 23 | echo "This is a cross-language conformance test." > plaintext.txt |
| 24 | EXPECTED_HASH=$(python3 -c " |
| 25 | import hashlib |
| 26 | data = open('plaintext.txt', 'rb').read() |
| 27 | print(hashlib.sha256(data).hexdigest()) |
| 28 | ") |
| 29 | echo "Expected hash: $EXPECTED_HASH" |
| 30 | |
| 31 | echo "" |
| 32 | echo "=== 1. Seal in RUST, open in PYTHON ===" |
| 33 | cargo run --manifest-path $RUST_CARGO --release -q -- seal \ |
| 34 | --input plaintext.txt --output rust-sealed.bin \ |
| 35 | --issuer issuer.json --recipient-pub "$ALICE_X_PUB" \ |
| 36 | --recipient-id "alice@test" --registry "https://reg.test" 2>&1 | tail -3 |
| 37 | |
| 38 | python3 <<PYEOF |
| 39 | import sys |
| 40 | sys.path.insert(0, '$PYTHON_ROOT') |
| 41 | from oversight_core.container import open_sealed |
| 42 | blob = open('rust-sealed.bin', 'rb').read() |
| 43 | priv = bytes.fromhex('$ALICE_X_PRIV') |
| 44 | plaintext, manifest = open_sealed(blob, priv) |
| 45 | expected = open('plaintext.txt', 'rb').read() |
| 46 | assert plaintext == expected, f"PLAINTEXT MISMATCH: got {plaintext!r}, expected {expected!r}" |
| 47 | assert manifest.content_hash == '$EXPECTED_HASH', f"HASH MISMATCH: {manifest.content_hash}" |
| 48 | print(f" ✓ Python read Rust-sealed file ({len(plaintext)} bytes)") |
| 49 | print(f" ✓ content_hash matches: {manifest.content_hash[:16]}...") |
| 50 | print(f" ✓ file_id from Rust manifest: {manifest.file_id}") |
| 51 | print(f" ✓ signature verified: {manifest.verify()}") |
| 52 | PYEOF |
| 53 | |
| 54 | echo "" |
| 55 | echo "=== 2. Seal in PYTHON, open in RUST ===" |
| 56 | python3 <<PYEOF |
| 57 | import sys |
| 58 | sys.path.insert(0, '$PYTHON_ROOT') |
| 59 | from oversight_core import ClassicIdentity, content_hash |
| 60 | from oversight_core.manifest import Manifest, Recipient |
| 61 | from oversight_core.container import seal |
| 62 | |
| 63 | alice_pub = bytes.fromhex('$ALICE_X_PUB') |
| 64 | issuer_priv = bytes.fromhex('$ISSUER_ED_PRIV') |
| 65 | issuer_pub = bytes.fromhex('$ISSUER_ED_PUB') |
| 66 | |
| 67 | plaintext = open('plaintext.txt', 'rb').read() |
| 68 | m = Manifest.new( |
| 69 | original_filename='plaintext.txt', |
| 70 | content_hash=content_hash(plaintext), |
| 71 | size_bytes=len(plaintext), |
| 72 | issuer_id='cross-test', |
| 73 | issuer_ed25519_pub_hex=issuer_pub.hex(), |
| 74 | recipient=Recipient(recipient_id='alice@test', x25519_pub=alice_pub.hex()), |
| 75 | registry_url='https://reg.test', |
| 76 | content_type='text/plain', |
| 77 | ) |
| 78 | blob = seal(plaintext, m, issuer_priv, alice_pub) |
| 79 | open('python-sealed.bin', 'wb').write(blob) |
| 80 | print(f" ✓ Python sealed ({len(blob)} bytes)") |
| 81 | PYEOF |
| 82 | |
| 83 | cargo run --manifest-path $RUST_CARGO --release -q -- open \ |
| 84 | --input python-sealed.bin --output rust-recovered.txt --recipient alice.json 2>&1 | tail -3 |
| 85 | |
| 86 | diff plaintext.txt rust-recovered.txt && echo " ✓ Rust read Python-sealed file, plaintext matches" |
| 87 | |
| 88 | echo "" |
| 89 | echo "=== 3. Inspect cross-format: Python can inspect Rust-sealed, Rust can inspect Python-sealed ===" |
| 90 | python3 <<PYEOF |
| 91 | import sys |
| 92 | sys.path.insert(0, '$PYTHON_ROOT') |
| 93 | from oversight_core.container import SealedFile |
| 94 | blob = open('rust-sealed.bin', 'rb').read() |
| 95 | sf = SealedFile.from_bytes(blob) |
| 96 | assert sf.manifest.verify(), "Python couldn't verify Rust signature!" |
| 97 | print(f" ✓ Python Manifest.verify() of Rust-sealed: True (suite={sf.manifest.suite})") |
| 98 | PYEOF |
| 99 | |
| 100 | cargo run --manifest-path $RUST_CARGO --release -q -- inspect \ |
| 101 | --input python-sealed.bin 2>&1 | grep -E "(signature valid|suite|OVERSIGHT)" | head -5 |
| 102 | |
| 103 | echo "" |
| 104 | echo "=== 4. Non-ASCII recipient_id round trip (the JCS divergence case) ===" |
| 105 | UNICODE_RECIPIENT='Zión@org' |
| 106 | |
| 107 | cargo run --manifest-path $RUST_CARGO --release -q -- seal \ |
| 108 | --input plaintext.txt --output rust-unicode-sealed.bin \ |
| 109 | --issuer issuer.json --recipient-pub "$ALICE_X_PUB" \ |
| 110 | --recipient-id "$UNICODE_RECIPIENT" --registry "https://reg.test" 2>&1 | tail -3 |
| 111 | |
| 112 | python3 <<PYEOF |
| 113 | import sys |
| 114 | sys.path.insert(0, '$PYTHON_ROOT') |
| 115 | from oversight_core.container import open_sealed, SealedFile |
| 116 | blob = open('rust-unicode-sealed.bin', 'rb').read() |
| 117 | priv = bytes.fromhex('$ALICE_X_PRIV') |
| 118 | plaintext, manifest = open_sealed(blob, priv) |
| 119 | assert manifest.verify(), ( |
| 120 | "Python Manifest.verify() of Rust-sealed file with non-ASCII recipient_id " |
| 121 | "FAILED. This is the JCS divergence: Python and Rust are computing " |
| 122 | "different canonical bytes for the same manifest." |
| 123 | ) |
| 124 | assert manifest.recipient.recipient_id == '$UNICODE_RECIPIENT', ( |
| 125 | f"recipient_id mismatch: got {manifest.recipient.recipient_id!r}" |
| 126 | ) |
| 127 | print(f" ✓ Python verifies Rust-sealed manifest with recipient_id={manifest.recipient.recipient_id!r}") |
| 128 | PYEOF |
| 129 | |
| 130 | python3 <<PYEOF |
| 131 | import sys |
| 132 | sys.path.insert(0, '$PYTHON_ROOT') |
| 133 | from oversight_core import ClassicIdentity, content_hash |
| 134 | from oversight_core.manifest import Manifest, Recipient |
| 135 | from oversight_core.container import seal |
| 136 | |
| 137 | alice_pub = bytes.fromhex('$ALICE_X_PUB') |
| 138 | issuer_priv = bytes.fromhex('$ISSUER_ED_PRIV') |
| 139 | issuer_pub = bytes.fromhex('$ISSUER_ED_PUB') |
| 140 | |
| 141 | plaintext = open('plaintext.txt', 'rb').read() |
| 142 | m = Manifest.new( |
| 143 | original_filename='plaintext.txt', |
| 144 | content_hash=content_hash(plaintext), |
| 145 | size_bytes=len(plaintext), |
| 146 | issuer_id='cross-test', |
| 147 | issuer_ed25519_pub_hex=issuer_pub.hex(), |
| 148 | recipient=Recipient(recipient_id='$UNICODE_RECIPIENT', x25519_pub=alice_pub.hex()), |
| 149 | registry_url='https://reg.test', |
| 150 | content_type='text/plain', |
| 151 | ) |
| 152 | blob = seal(plaintext, m, issuer_priv, alice_pub) |
| 153 | open('python-unicode-sealed.bin', 'wb').write(blob) |
| 154 | assert m.verify(), "Python cannot verify its own signature on a non-ASCII manifest" |
| 155 | print(f" ✓ Python signed manifest with non-ASCII recipient_id, self-verify OK") |
| 156 | PYEOF |
| 157 | |
| 158 | cargo run --manifest-path $RUST_CARGO --release -q -- open \ |
| 159 | --input python-unicode-sealed.bin --output rust-unicode-recovered.txt --recipient alice.json 2>&1 | tail -3 |
| 160 | |
| 161 | diff plaintext.txt rust-unicode-recovered.txt && echo " ✓ Rust opens Python-sealed non-ASCII manifest, plaintext matches" |
| 162 | |
| 163 | cargo run --manifest-path $RUST_CARGO --release -q -- inspect \ |
| 164 | --input python-unicode-sealed.bin 2>&1 | grep -E "signature valid" | head -1 |
| 165 | |
| 166 | echo "" |
| 167 | echo "=== 5. Hybrid (OSGT-HYBRID-v1) ML-KEM-768 KEM, Python <-> Rust ===" |
| 168 | PYTHONPATH="$REPO_ROOT:$PYTHONPATH" python3 "$REPO_ROOT/oversight-rust/tests/conformance_hybrid_kem.py" |
| 169 | |
| 170 | echo "" |
| 171 | echo "==========================================" |
| 172 | echo " CROSS-LANGUAGE CONFORMANCE: ALL PASS" |
| 173 | echo "==========================================" |