mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
virtio_crypto_transform.py:
- VirtioCryptoSession: HASH session (SHA-256, SHA-512, MD5)
- VirtioCryptoHashTransform: encode as HASH request, produce receipt
- Receipt: {schema, transform_type, algo, payload_bytes, result_hex, witness_hash}
- Wire-format structs: CtrlHdr(20B), HashSessionPara(8B), HashDataReq(28B)
- RFC 6234 test vectors: all pass
ivshmem_client.py:
- IvshmemClient: mmap /dev/shm/ivshmem_bar0
- IvshmemRing: doorbell notification
- IvshmemTransform: write payload, ring doorbell, produce receipt
- Receipt: {schema, transform_type: shared_memory, offset, length, witness_hash}
- Memory layout: registers 0x0000, metadata 0x10000, data 0x20000
- /dev/shm fallback test: verified
608 lines
21 KiB
Python
608 lines
21 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Virtio-Crypto Transform — Use virtio-crypto sessions as a hash computation pipeline.
|
|
|
|
Implements the HASH session primitive from the virtio-crypto spec:
|
|
1. VirtioCryptoSession — create a HASH session (SHA-256, SHA-512, MD5)
|
|
2. VirtioCryptoHashTransform — encode data as a HASH request, produce receipt
|
|
3. VirtioCryptoTransformReceipt — machine-readable result with witness
|
|
|
|
This is NOT a crypto driver. It is a computation substrate that models the
|
|
virtio-crypto control queue and data queue protocol. The host QEMU backend
|
|
(gcrypt/AES-NI) processes hash requests without knowing it is being used
|
|
for general computation.
|
|
|
|
Shim boundary: I/O only. No decision logic. All computation mapping decisions
|
|
belong in Lean (future VirtioCrypto modules).
|
|
|
|
Reference: virtio/virtio_crypto.h (Linux kernel headers), RFC 6234 (SHA test vectors)
|
|
|
|
Usage:
|
|
python3 virtio_crypto_transform.py --example
|
|
python3 virtio_crypto_transform.py --analyze <payload.bin>
|
|
python3 virtio_crypto_transform.py hash <payload.bin> [--algo sha256|sha512|md5]
|
|
python3 virtio_crypto_transform.py session-info [--algo sha256]
|
|
python3 virtio_crypto_transform.py receipt <result.json>
|
|
python3 virtio_crypto_transform.py --test-vectors
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
import struct
|
|
import sys
|
|
from dataclasses import dataclass, field
|
|
from typing import Dict, Any, List, Optional, Tuple
|
|
from pathlib import Path
|
|
|
|
# ── Virtio-Crypto Constants ──────────────────────────────────────────────────
|
|
|
|
# Opcodes (from linux/virtio_crypto.h)
|
|
VIRTIO_CRYPTO_OPCODE_HASH: int = 0x06
|
|
VIRTIO_CRYPTO_OPCODE_SYMCIPHER: int = 0x07
|
|
VIRTIO_CRYPTO_OPCODE_AKCIPHER: int = 0x08
|
|
|
|
# Session types
|
|
VIRTIO_CRYPTO_OP_SESSION_CREATE: int = 0x01
|
|
VIRTIO_CRYPTO_OP_SESSION_DESTROY: int = 0x02
|
|
|
|
# Hash algorithms (from linux/virtio_crypto.h)
|
|
VIRTIO_CRYPTO_HASH_SHA1: int = 0
|
|
VIRTIO_CRYPTO_HASH_SHA224: int = 1
|
|
VIRTIO_CRYPTO_HASH_SHA256: int = 2
|
|
VIRTIO_CRYPTO_HASH_SHA384: int = 3
|
|
VIRTIO_CRYPTO_HASH_SHA512: int = 4
|
|
VIRTIO_CRYPTO_HASH_MD5: int = 5
|
|
VIRTIO_CRYPTO_HASH_SHA3_224: int = 6
|
|
VIRTIO_CRYPTO_HASH_SHA3_256: int = 7
|
|
VIRTIO_CRYPTO_HASH_SHA3_384: int = 8
|
|
VIRTIO_CRYPTO_HASH_SHA3_512: int = 9
|
|
|
|
# Status codes
|
|
VIRTIO_CRYPTO_OK: int = 0
|
|
VIRTIO_CRYPTO_ERR: int = 1
|
|
VIRTIO_CRYPTO_BADMSG: int = 2
|
|
VIRTIO_CRYPTO_NOTSUPP: int = 3
|
|
VIRTIO_CRYPTO_INVSESS: int = 4
|
|
|
|
# Feature bits
|
|
VIRTIO_CRYPTO_F_CIPHER_STATELESS: int = 0
|
|
VIRTIO_CRYPTO_F_HASH_STATELESS: int = 1
|
|
|
|
# Algorithm name mapping
|
|
ALGO_MAP: Dict[str, int] = {
|
|
"md5": VIRTIO_CRYPTO_HASH_MD5,
|
|
"sha1": VIRTIO_CRYPTO_HASH_SHA1,
|
|
"sha224": VIRTIO_CRYPTO_HASH_SHA224,
|
|
"sha256": VIRTIO_CRYPTO_HASH_SHA256,
|
|
"sha384": VIRTIO_CRYPTO_HASH_SHA384,
|
|
"sha512": VIRTIO_CRYPTO_HASH_SHA512,
|
|
"sha3_224": VIRTIO_CRYPTO_HASH_SHA3_224,
|
|
"sha3_256": VIRTIO_CRYPTO_HASH_SHA3_256,
|
|
"sha3_384": VIRTIO_CRYPTO_HASH_SHA3_384,
|
|
"sha3_512": VIRTIO_CRYPTO_HASH_SHA3_512,
|
|
}
|
|
|
|
ALGO_DIGEST_SIZE: Dict[str, int] = {
|
|
"md5": 16,
|
|
"sha1": 20,
|
|
"sha224": 28,
|
|
"sha256": 32,
|
|
"sha384": 48,
|
|
"sha512": 64,
|
|
"sha3_224": 28,
|
|
"sha3_256": 32,
|
|
"sha3_384": 48,
|
|
"sha3_512": 64,
|
|
}
|
|
|
|
ALGO_VIRTIO_CODE: Dict[str, int] = {
|
|
"md5": VIRTIO_CRYPTO_HASH_MD5,
|
|
"sha1": VIRTIO_CRYPTO_HASH_SHA1,
|
|
"sha256": VIRTIO_CRYPTO_HASH_SHA256,
|
|
"sha384": VIRTIO_CRYPTO_HASH_SHA384,
|
|
"sha512": VIRTIO_CRYPTO_HASH_SHA512,
|
|
}
|
|
|
|
# ── Virtio-Crypto Header Structures ─────────────────────────────────────────
|
|
|
|
|
|
@dataclass
|
|
class VirtioCryptoCtrlHdr:
|
|
"""struct virtio_crypto_ctrl_header — control queue header.
|
|
|
|
Layout (from linux/virtio_crypto.h):
|
|
__le32 opcode
|
|
__le32 algo
|
|
__le32 flag
|
|
__le32 session_id
|
|
__u8 padding[4]
|
|
Total: 20 bytes
|
|
"""
|
|
opcode: int = 0
|
|
algo: int = 0
|
|
flag: int = 0
|
|
session_id: int = 0
|
|
|
|
CTRL_SIZE: int = 20 # 4 + 4 + 4 + 4 + 4 padding
|
|
|
|
def pack(self) -> bytes:
|
|
return struct.pack(
|
|
"!IIIII",
|
|
self.opcode,
|
|
self.algo,
|
|
self.flag,
|
|
self.session_id,
|
|
0, # padding
|
|
)
|
|
|
|
@staticmethod
|
|
def unpack(data: bytes) -> "VirtioCryptoCtrlHdr":
|
|
opcode, algo, flag, session_id, _ = struct.unpack("!IIIII", data[:20])
|
|
return VirtioCryptoCtrlHdr(
|
|
opcode=opcode,
|
|
algo=algo,
|
|
flag=flag,
|
|
session_id=session_id,
|
|
)
|
|
|
|
|
|
@dataclass
|
|
class VirtioCryptoSessionInput:
|
|
"""struct virtio_crypto_session_input — session creation result.
|
|
|
|
Layout:
|
|
__le32 session_id
|
|
__le32 status
|
|
Total: 8 bytes
|
|
"""
|
|
session_id: int = 0
|
|
status: int = VIRTIO_CRYPTO_OK
|
|
|
|
def pack(self) -> bytes:
|
|
return struct.pack("!II", self.session_id, self.status)
|
|
|
|
@staticmethod
|
|
def unpack(data: bytes) -> "VirtioCryptoSessionInput":
|
|
session_id, status = struct.unpack("!II", data[:8])
|
|
return VirtioCryptoSessionInput(session_id=session_id, status=status)
|
|
|
|
|
|
@dataclass
|
|
class VirtioCryptoHashSessionPara:
|
|
"""struct virtio_crypto_hash_session_para — hash session parameters.
|
|
|
|
Layout:
|
|
__le32 algo
|
|
__le32 hash_result_len
|
|
Total: 8 bytes
|
|
"""
|
|
algo: int = VIRTIO_CRYPTO_HASH_SHA256
|
|
hash_result_len: int = 32
|
|
|
|
def pack(self) -> bytes:
|
|
return struct.pack("!II", self.algo, self.hash_result_len)
|
|
|
|
@staticmethod
|
|
def unpack(data: bytes) -> "VirtioCryptoHashSessionPara":
|
|
algo, hash_result_len = struct.unpack("!II", data[:8])
|
|
return VirtioCryptoHashSessionPara(algo=algo, hash_result_len=hash_result_len)
|
|
|
|
|
|
@dataclass
|
|
class VirtioCryptoHashDataReq:
|
|
"""struct virtio_crypto_hash_data_req — data queue request for hash.
|
|
|
|
Layout:
|
|
struct virtio_crypto_ctrl_hdr header
|
|
__le32 src_data_len
|
|
__le32 dst_data_len
|
|
Total: 28 bytes (20 + 4 + 4)
|
|
"""
|
|
header: VirtioCryptoCtrlHdr = field(default_factory=VirtioCryptoCtrlHdr)
|
|
src_data_len: int = 0
|
|
dst_data_len: int = 0
|
|
|
|
HASH_REQ_SIZE: int = 28
|
|
|
|
def pack(self) -> bytes:
|
|
return self.header.pack() + struct.pack(
|
|
"!II", self.src_data_len, self.dst_data_len
|
|
)
|
|
|
|
@staticmethod
|
|
def unpack(data: bytes) -> "VirtioCryptoHashDataReq":
|
|
header = VirtioCryptoCtrlHdr.unpack(data[:20])
|
|
src_data_len, dst_data_len = struct.unpack("!II", data[20:28])
|
|
return VirtioCryptoHashDataReq(
|
|
header=header,
|
|
src_data_len=src_data_len,
|
|
dst_data_len=dst_data_len,
|
|
)
|
|
|
|
|
|
# ── Session and Transform ────────────────────────────────────────────────────
|
|
|
|
|
|
@dataclass
|
|
class VirtioCryptoSession:
|
|
"""A HASH session in virtio-crypto.
|
|
|
|
Models the session creation handshake:
|
|
Guest → control queue: VirtioCryptoCtrlHdr + VirtioCryptoHashSessionPara
|
|
Host → control queue: VirtioCryptoSessionInput (session_id, status)
|
|
|
|
The session_id is then used for all subsequent hash requests on this session.
|
|
"""
|
|
algo: str = "sha256"
|
|
algo_code: int = VIRTIO_CRYPTO_HASH_SHA256
|
|
digest_size: int = 32
|
|
session_id: int = 0
|
|
status: int = VIRTIO_CRYPTO_OK
|
|
|
|
@staticmethod
|
|
def create(algo: str = "sha256") -> "VirtioCryptoSession":
|
|
"""Create a new hash session for the given algorithm."""
|
|
algo_lower = algo.lower()
|
|
if algo_lower not in ALGO_MAP:
|
|
raise ValueError(f"Unknown algorithm: {algo}. Supported: {list(ALGO_MAP.keys())}")
|
|
algo_code = ALGO_MAP[algo_lower]
|
|
digest_size = ALGO_DIGEST_SIZE[algo_lower]
|
|
return VirtioCryptoSession(
|
|
algo=algo_lower,
|
|
algo_code=algo_code,
|
|
digest_size=digest_size,
|
|
)
|
|
|
|
def session_create_request(self) -> bytes:
|
|
"""Encode the control queue session creation request."""
|
|
ctrl = VirtioCryptoCtrlHdr(
|
|
opcode=VIRTIO_CRYPTO_OP_SESSION_CREATE,
|
|
algo=self.algo_code,
|
|
flag=VIRTIO_CRYPTO_F_HASH_STATELESS,
|
|
session_id=0,
|
|
)
|
|
para = VirtioCryptoHashSessionPara(
|
|
algo=self.algo_code,
|
|
hash_result_len=self.digest_size,
|
|
)
|
|
return ctrl.pack() + para.pack()
|
|
|
|
def session_create_response(self) -> bytes:
|
|
"""Encode the host's session creation response."""
|
|
resp = VirtioCryptoSessionInput(
|
|
session_id=self.session_id,
|
|
status=self.status,
|
|
)
|
|
return resp.pack()
|
|
|
|
def destroy_request(self) -> bytes:
|
|
"""Encode the session destroy request."""
|
|
ctrl = VirtioCryptoCtrlHdr(
|
|
opcode=VIRTIO_CRYPTO_OP_SESSION_DESTROY,
|
|
algo=self.algo_code,
|
|
flag=0,
|
|
session_id=self.session_id,
|
|
)
|
|
return ctrl.pack()
|
|
|
|
|
|
@dataclass
|
|
class VirtioCryptoTransformReceipt:
|
|
"""Machine-readable receipt for a virtio-crypto hash transform."""
|
|
schema: str = "virtio_crypto_transform_receipt_v1"
|
|
transform_type: str = "hash"
|
|
algo: str = "sha256"
|
|
algo_code: int = VIRTIO_CRYPTO_HASH_SHA256
|
|
payload_bytes: int = 0
|
|
result_hex: str = ""
|
|
result_bytes: int = 0
|
|
session_id: int = 0
|
|
witness_hash: str = ""
|
|
request_header_hex: str = ""
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
return {
|
|
"schema": self.schema,
|
|
"transform_type": self.transform_type,
|
|
"algo": self.algo,
|
|
"algo_code": self.algo_code,
|
|
"payload_bytes": self.payload_bytes,
|
|
"result_hex": self.result_hex,
|
|
"result_bytes": self.result_bytes,
|
|
"session_id": self.session_id,
|
|
"witness_hash": self.witness_hash,
|
|
"request_header_hex": self.request_header_hex,
|
|
}
|
|
|
|
|
|
def _compute_hash(algo: str, data: bytes) -> bytes:
|
|
"""Compute a hash using Python's hashlib (models QEMU's crypto backend)."""
|
|
import hashlib
|
|
h = hashlib.new(algo)
|
|
h.update(data)
|
|
return h.digest()
|
|
|
|
|
|
def _witness(data: bytes) -> str:
|
|
"""SHA-256 of the full request+result bundle for receipt verification."""
|
|
import hashlib
|
|
return hashlib.sha256(data).hexdigest()
|
|
|
|
|
|
# ── Transform Implementation ─────────────────────────────────────────────────
|
|
|
|
|
|
def transform_hash(
|
|
payload: bytes,
|
|
algo: str = "sha256",
|
|
session_id: int = 1,
|
|
) -> Tuple[bytes, VirtioCryptoTransformReceipt]:
|
|
"""HASH transform: encode data as a virtio-crypto HASH request, compute hash.
|
|
|
|
Protocol flow:
|
|
1. Guest creates HASH session on control queue → session_id
|
|
2. Guest posts VirtioCryptoHashDataReq + payload on data queue
|
|
3. Host (QEMU crypto backend) computes hash
|
|
4. Host writes hash result to dst buffer on data queue
|
|
|
|
This function models the full round-trip. The hash is computed locally
|
|
(matching what QEMU's gcrypt backend would produce).
|
|
|
|
Returns (result_digest, receipt).
|
|
"""
|
|
session = VirtioCryptoSession.create(algo)
|
|
session.session_id = session_id
|
|
|
|
# Encode the data queue request header
|
|
data_req = VirtioCryptoHashDataReq(
|
|
header=VirtioCryptoCtrlHdr(
|
|
opcode=VIRTIO_CRYPTO_OPCODE_HASH,
|
|
algo=session.algo_code,
|
|
flag=VIRTIO_CRYPTO_F_HASH_STATELESS,
|
|
session_id=session_id,
|
|
),
|
|
src_data_len=len(payload),
|
|
dst_data_len=session.digest_size,
|
|
)
|
|
|
|
# Compute hash (models QEMU crypto backend)
|
|
digest = _compute_hash(algo, payload)
|
|
|
|
# Build receipt
|
|
request_bytes = data_req.pack() + payload
|
|
receipt = VirtioCryptoTransformReceipt(
|
|
transform_type="hash",
|
|
algo=algo,
|
|
algo_code=session.algo_code,
|
|
payload_bytes=len(payload),
|
|
result_hex=digest.hex(),
|
|
result_bytes=len(digest),
|
|
session_id=session_id,
|
|
witness_hash=_witness(request_bytes + digest),
|
|
request_header_hex=data_req.pack().hex(),
|
|
)
|
|
|
|
return digest, receipt
|
|
|
|
|
|
# ── RFC 6234 Test Vectors ────────────────────────────────────────────────────
|
|
|
|
SHA256_TEST_VECTORS: List[Tuple[str, str]] = [
|
|
# (input, expected_sha256_hex)
|
|
("", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"),
|
|
("abc", "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"),
|
|
("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
|
|
"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"),
|
|
("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
|
|
"cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1"),
|
|
]
|
|
|
|
SHA512_TEST_VECTORS: List[Tuple[str, str]] = [
|
|
("", "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"),
|
|
("abc", "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"),
|
|
]
|
|
|
|
MD5_TEST_VECTORS: List[Tuple[str, str]] = [
|
|
("", "d41d8cd98f00b204e9800998ecf8427e"),
|
|
("abc", "900150983cd24fb0d6963f7d28e17f72"),
|
|
("message digest", "f96b697d7cb7938d525a2f31aaf161d0"),
|
|
("abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b"),
|
|
]
|
|
|
|
|
|
# ── CLI ──────────────────────────────────────────────────────────────────────
|
|
|
|
|
|
def main():
|
|
args = sys.argv[1:]
|
|
|
|
if not args or args[0] == "--example":
|
|
print("=== Virtio-Crypto Transform ===")
|
|
print()
|
|
print("HASH computation via virtio-crypto sessions:")
|
|
print()
|
|
print(" payload → HASH session (SHA-256/SHA-512/MD5)")
|
|
print(" → QEMU crypto backend computes hash")
|
|
print(" → result digest + receipt")
|
|
print()
|
|
print("=== Protocol ===")
|
|
print()
|
|
print(" 1. Control queue: CREATE_SESSION(algo) → session_id")
|
|
print(" 2. Data queue: HASH_REQUEST(session_id, payload) → digest")
|
|
print(" 3. Control queue: DESTROY_SESSION(session_id)")
|
|
print()
|
|
print("=== Demo ===")
|
|
print()
|
|
|
|
# SHA-256
|
|
payload = b"abc"
|
|
digest, receipt = transform_hash(payload, algo="sha256")
|
|
print(f"SHA-256({payload!r}):")
|
|
print(f" result: {digest.hex()}")
|
|
print(f" receipt: {json.dumps(receipt.to_dict(), indent=2)}")
|
|
print()
|
|
|
|
# SHA-512
|
|
digest, receipt = transform_hash(payload, algo="sha512")
|
|
print(f"SHA-512({payload!r}):")
|
|
print(f" result: {digest.hex()}")
|
|
print()
|
|
|
|
# MD5
|
|
digest, receipt = transform_hash(payload, algo="md5")
|
|
print(f"MD5({payload!r}):")
|
|
print(f" result: {digest.hex()}")
|
|
print()
|
|
|
|
# Session creation wire format
|
|
session = VirtioCryptoSession.create("sha256")
|
|
session.session_id = 1
|
|
req = session.session_create_request()
|
|
print(f"Session create request ({len(req)} bytes): {req.hex()}")
|
|
resp = session.session_create_response()
|
|
print(f"Session create response ({len(resp)} bytes): {resp.hex()}")
|
|
print()
|
|
|
|
# Data request wire format
|
|
data_req = VirtioCryptoHashDataReq(
|
|
header=VirtioCryptoCtrlHdr(
|
|
opcode=VIRTIO_CRYPTO_OPCODE_HASH,
|
|
algo=VIRTIO_CRYPTO_HASH_SHA256,
|
|
flag=VIRTIO_CRYPTO_F_HASH_STATELESS,
|
|
session_id=1,
|
|
),
|
|
src_data_len=3,
|
|
dst_data_len=32,
|
|
)
|
|
print(f"Data request header ({len(data_req.pack())} bytes): {data_req.pack().hex()}")
|
|
return
|
|
|
|
if args[0] == "--analyze":
|
|
if len(args) < 2:
|
|
print("Usage: virtio_crypto_transform.py --analyze <payload.bin>", file=sys.stderr)
|
|
sys.exit(1)
|
|
payload = Path(args[1]).read_bytes()
|
|
print(f"=== {args[1]} ===")
|
|
print(f" Size: {len(payload)} bytes")
|
|
print(f" SHA-256: {hashlib.sha256(payload).hexdigest()}")
|
|
print(f" SHA-512: {hashlib.sha512(payload).hexdigest()}")
|
|
print(f" MD5: {hashlib.md5(payload).hexdigest()}")
|
|
print(f" Digest sizes: sha256=32B, sha512=64B, md5=16B")
|
|
return
|
|
|
|
if args[0] == "--test-vectors":
|
|
print("=== RFC 6234 Test Vector Verification ===")
|
|
print()
|
|
all_pass = True
|
|
|
|
# SHA-256
|
|
print("--- SHA-256 ---")
|
|
for i, (inp, expected) in enumerate(SHA256_TEST_VECTORS):
|
|
digest, receipt = transform_hash(inp.encode(), algo="sha256")
|
|
actual = digest.hex()
|
|
ok = actual == expected
|
|
status = "PASS" if ok else "FAIL"
|
|
print(f" [{status}] vector {i}: input={inp[:40]!r}{'...' if len(inp) > 40 else ''}")
|
|
if not ok:
|
|
print(f" expected: {expected}")
|
|
print(f" actual: {actual}")
|
|
all_pass = False
|
|
print()
|
|
|
|
# SHA-512
|
|
print("--- SHA-512 ---")
|
|
for i, (inp, expected) in enumerate(SHA512_TEST_VECTORS):
|
|
digest, receipt = transform_hash(inp.encode(), algo="sha512")
|
|
actual = digest.hex()
|
|
ok = actual == expected
|
|
status = "PASS" if ok else "FAIL"
|
|
print(f" [{status}] vector {i}: input={inp[:40]!r}{'...' if len(inp) > 40 else ''}")
|
|
if not ok:
|
|
print(f" expected: {expected}")
|
|
print(f" actual: {actual}")
|
|
all_pass = False
|
|
print()
|
|
|
|
# MD5
|
|
print("--- MD5 ---")
|
|
for i, (inp, expected) in enumerate(MD5_TEST_VECTORS):
|
|
digest, receipt = transform_hash(inp.encode(), algo="md5")
|
|
actual = digest.hex()
|
|
ok = actual == expected
|
|
status = "PASS" if ok else "FAIL"
|
|
print(f" [{status}] vector {i}: input={inp[:40]!r}{'...' if len(inp) > 40 else ''}")
|
|
if not ok:
|
|
print(f" expected: {expected}")
|
|
print(f" actual: {actual}")
|
|
all_pass = False
|
|
print()
|
|
|
|
# Receipt schema check
|
|
print("--- Receipt Schema ---")
|
|
_, receipt = transform_hash(b"abc", algo="sha256")
|
|
d = receipt.to_dict()
|
|
required_keys = {"schema", "transform_type", "algo", "payload_bytes",
|
|
"result_hex", "witness_hash"}
|
|
missing = required_keys - set(d.keys())
|
|
if missing:
|
|
print(f" [FAIL] Missing keys in receipt: {missing}")
|
|
all_pass = False
|
|
else:
|
|
print(f" [PASS] Receipt has all required keys: {sorted(required_keys)}")
|
|
print(f" schema={d['schema']}, transform_type={d['transform_type']}")
|
|
print()
|
|
|
|
if all_pass:
|
|
print("ALL TEST VECTORS PASSED")
|
|
else:
|
|
print("SOME TESTS FAILED")
|
|
sys.exit(1)
|
|
return
|
|
|
|
if args[0] == "hash":
|
|
if len(args) < 2:
|
|
print("Usage: virtio_crypto_transform.py hash <payload.bin> [--algo sha256|sha512|md5]", file=sys.stderr)
|
|
sys.exit(1)
|
|
payload = Path(args[1]).read_bytes()
|
|
algo = "sha256"
|
|
if "--algo" in args:
|
|
ai = args.index("--algo")
|
|
algo = args[ai + 1]
|
|
digest, receipt = transform_hash(payload, algo=algo)
|
|
print(json.dumps(receipt.to_dict(), indent=2))
|
|
return
|
|
|
|
if args[0] == "session-info":
|
|
algo = "sha256"
|
|
if "--algo" in args:
|
|
ai = args.index("--algo")
|
|
algo = args[ai + 1]
|
|
session = VirtioCryptoSession.create(algo)
|
|
info = {
|
|
"algo": session.algo,
|
|
"algo_code": session.algo_code,
|
|
"digest_size": session.digest_size,
|
|
"supported_algorithms": list(ALGO_MAP.keys()),
|
|
"session_create_request_size": len(session.session_create_request()),
|
|
"session_create_response_size": len(session.session_create_response()),
|
|
}
|
|
print(json.dumps(info, indent=2))
|
|
return
|
|
|
|
if args[0] == "receipt":
|
|
if len(args) < 2:
|
|
print("Usage: virtio_crypto_transform.py receipt <result.json>", file=sys.stderr)
|
|
sys.exit(1)
|
|
data = json.loads(Path(args[1]).read_text())
|
|
print(json.dumps(data, indent=2))
|
|
return
|
|
|
|
print("Usage: virtio_crypto_transform.py <command> [args]", file=sys.stderr)
|
|
print("Commands: --example, --analyze, --test-vectors, hash, session-info, receipt", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|