qrcrypt/compact_xchapoly.py

32 lines
1.7 KiB
Python

import secrets
u=lambda b:sum(b[i]<<(i*8)for i in range(len(b)))
unu=lambda n,l:bytes(n>>i&255 for i in range(0,l,8))
add=lambda x,y:x+y&0xffffffff
rotl=lambda x,n:x<<n&0xffffffff|x>>32-n
def qr(a,b,c,d):a=add(a,b);d^=a;d=rotl(d,16);c=add(c,d);b^=c;b=rotl(b,12);a=add(a,b);d^=a;d=rotl(d,8);c=add(c,d);b^=c;b=rotl(b,7);return a,b,c,d
def ccb(k,c,n,f):
if c>=1<<32: raise ValueError
s=[0x61707865,0x3320646e,0x79622d32,0x6b206574,u(k[:4]),u(k[4:8]),u(k[8:12]),u(k[12:16]),u(k[16:20]),u(k[20:24]),u(k[24:28]),u(k[28:]),c,u(n[:4]),u(n[4:8]),u(n[8:])]
o=s[:]
if f:o=[0]*16
def x(a,b,c,d):s[a],s[b],s[c],s[d]=qr(s[a],s[b],s[c],s[d])
for _ in range(10):x(0,4,8,12);x(1,5,9,13);x(2,6,10,14);x(3,7,11,15);x(0,5,10,15);x(1,6,11,12);x(2,7,8,13);x(3,4,9,14)
return b''.join(unu(add(o[i],s[i]),32)for i in range(len(s)))
cd=lambda a,b:a//b if a%b==0 else a//b+1
def chacha20(k,c,n,m):
for i in range(cd(len(m),64)):yield from(a^b for a,b in zip(m[i*64:i*64+64],ccb(k,c+i,n,0)))
def poly1305(m,k):
r=bytearray(k[:16]);r[3]&=15;r[7]&=15;r[11]&=15;r[15]&=15;r[4]&=252;r[8]&=252;r[12]&=252;r=u(r);s=u(k[16:]);P=2**130-5;a=0
for i in range(cd(len(m),16)):a=(a+u(m[i*16:i*16+16]+b'\x01'))*r%P
return unu(a+s,128)
p16=lambda x:b'\x00'*(-len(x)%16)
tag=lambda a,k,n,c:poly1305(b''.join((a,p16(a),c,p16(c),unu(len(a),64),unu(len(c),64))),ccb(k,0,n,0)[:32])
def cpe(a,k,n,p):c=bytes(chacha20(k,1,n,p));return c+tag(a,k,n,c)
def cpd(a,k,n,e):
c=e[:-16]
if not secrets.compare_digest(e[-16:],tag(a,k,n,c)):return None
return bytes(chacha20(k,1,n,c))
def hchacha20(k,n):s=ccb(k,u(n[:4]),n[4:],1);return s[:16]+s[48:]
def enc(a,k,n,p):k=hchacha20(k, n[:16]);n=b'\x00'*4+n[16:];return cpe(a,k,n,p)
def dec(a,k,n,c):k=hchacha20(k, n[:16]);n=b'\x00'*4+n[16:];return cpd(a,k,n,c)