import json,math,itertools,hmac,hashlib from fractions import Fraction SLUG='postquantum-protocol-composition';KIND='Constructed analytical model; not a measured benchmark' def extract(salt,ikm):return hmac.new(salt,ikm,hashlib.sha256).digest() def expand(prk,info,length): previous=b'';output=b'' for counter in range(1,math.ceil(length/32)+1): previous=hmac.new(prk,previous+info+bytes([counter]),hashlib.sha256).digest();output+=previous return output[:length] def frame(parts):return b''.join(len(p).to_bytes(4,'big')+p for p in parts) def chunks(length,maximum):return math.ceil(length/maximum) def run(): checks=[] prk=extract(bytes(range(13)),b'\x0b'*22) assert prk.hex()=='077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5' okm=expand(prk,bytes(range(240,250)),42) assert okm.hex()=='3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865' checks.append(dict(name='RFC 5869 SHA-256 test case one',passed=True,cases=2,detail='Published PRK and 42-byte OKM reproduced; helper is verification code, not a production cryptographic library.')) left=(b'ab',b'c');right=(b'a',b'bc') assert b''.join(left)==b''.join(right) assert frame(left)!=frame(right) checks.append(dict(name='Unframed ambiguity and framed distinction',passed=True,cases=2,detail='Constructed variable-length components collide before hashing without framing.')) count=0 for maximum in (600,1200,1500): for length in (32,288,1120,1216,1376,1472,4096): explicit=len([bytes(min(maximum,length-start)) for start in range(0,length,maximum)]) assert explicit==chunks(length,maximum);count+=1 checks.append(dict(name='Chunk formula versus explicit byte slicing',passed=True,cases=count,detail='Three assumed capacities and seven message lengths; not a real transport stack.')) for n in (2,4): for p in (Fraction(0),Fraction(1,100),Fraction(1,20),Fraction(1)): no_loss=sum((p**sum(mask)*(1-p)**(n-sum(mask)) for mask in itertools.product((0,1),repeat=n) if sum(mask)==0),Fraction(0)) assert no_loss==(1-p)**n checks.append(dict(name='Independent loss enumeration and endpoints',passed=True,cases=8,detail='Exact finite probability enumeration for two and four chunks.')) sets=[512,768,1024];pks=[800,1184,1568];cts=[768,1088,1568];loss=[0,0.001,0.01,0.02,0.05] n_hybrid=chunks(1184+32+256,1200)+chunks(1088+32+256,1200) n_traditional=2*chunks(32+256,1200) assert n_hybrid==4 and n_traditional==2 figs=[dict(id='raw-key-bytes',title='Raw key-establishment contribution ledger',x_label='ML-KEM parameter-set identifier',y_label='Total raw bytes in both directions',series=[dict(name='ML-KEM public key plus ciphertext',x=sets,y=[a+b for a,b in zip(pks,cts)]),dict(name='Plus assumed 32-byte traditional contribution each way',x=sets,y=[a+b+64 for a,b in zip(pks,cts)]),dict(name='Assumed traditional-only baseline',x=sets,y=[64]*3)],caption='ML-KEM sizes from FIPS 203 Table 3; parameter-set identifiers are categorical, not a continuous security or performance scale.',assumptions=['Raw bytes only','No certificates or record framing','Additional traditional contribution assumed 32 bytes each direction'],kind=KIND),dict(id='loss-sensitivity',title='Completion without retransmission in an ideal chunk model',x_label='Independent chunk-loss probability',y_label='Probability of no lost chunks',series=[dict(name='Four constructed hybrid chunks',x=loss,y=[(1-p)**4 for p in loss]),dict(name='Two constructed traditional chunks',x=loss,y=[(1-p)**2 for p in loss])],caption='256 framing bytes per direction, 1200-byte chunk capacity, independent losses; not a TLS/TCP/QUIC benchmark.',assumptions=['No retransmission','Independent loss','ML-KEM-768 and assumed traditional contribution sizes'],kind=KIND)] return dict(slug=SLUG,checks=checks,figures=figs,result_summary=dict(mlkem768_pk_bytes=1184,mlkem768_ciphertext_bytes=1088,constructed_client_bytes=1216,constructed_server_bytes=1120,constructed_total_bytes=2336,traditional_raw_total_bytes=64,constructed_hybrid_chunks=4,constructed_traditional_chunks=2,hybrid_no_loss_p01=float(Fraction(99,100)**4),traditional_no_loss_p01=float(Fraction(99,100)**2))) if __name__=='__main__':print(json.dumps(run(),indent=2))