#!/usr/bin/env python3 systems = ( # p,q,sk,pk,ct,w (653,4621,1518,994,897,288), (761,4591,1763,1158,1039,286), (857,5167,1999,1322,1184,322), (953,6343,2254,1505,1349,396), (1013,7177,2417,1623,1455,448), (1277,7879,3059,2067,1847,492), ) apiall = {} topdir = 'crypto_kem' import shutil import os import re from math import floor,ceil import subprocess def readfile(fn): with open(fn) as f: return f.read() def writefile(fn,x): with open(fn,'w') as f: f.write(x) def writefiletime(fn,x,t): writefile(fn,x) os.utime(fn,(t,t)) build_subroutine_done = set() def build_subroutine(sub,macros): src = 'src/%s'%sub srctime = os.stat(src).st_mtime o = sub.split('/')[0] op = 'crypto_%s'%sub for m in macros: op = re.sub(m,str(macros[m]),op) if op in build_subroutine_done: return print('building %s'%op) shutil.rmtree(op,ignore_errors=True) os.makedirs(op) api = '' if sub.startswith('verify/'): api = '#define CRYPTO_BYTES %s\n'%macros['BYTES'] if sub.startswith('decode/') or sub.startswith('encode/'): api = '#define CRYPTO_STRBYTES %s\n'%macros['STRBYTES'] api += '#define CRYPTO_ITEMS %s\n'%macros['ITEMS'] api += '#define CRYPTO_ITEMBYTES %s\n'%macros['ITEMBYTES'] if sub.startswith('core/'): api = '#define CRYPTO_OUTPUTBYTES %s\n'%macros['OUTPUTBYTES'] api += '#define CRYPTO_INPUTBYTES %s\n'%macros['INPUTBYTES'] api += '#define CRYPTO_KEYBYTES %s\n'%macros['KEYBYTES'] api += '#define CRYPTO_CONSTBYTES %s\n'%macros['CONSTBYTES'] if o not in apiall: apiall[o] = '' apiall[o] += api.replace('CRYPTO_',op.replace('/','_')) for impl in sorted(os.listdir(src)): if impl == 'supercop': continue srci = '%s/%s'%(src,impl) srcitime = os.stat(srci).st_mtime opi = '%s/%s'%(op,impl) os.makedirs(opi) if impl != 'supercop': writefiletime('%s/api.h'%opi,api,srctime) for fn in sorted(os.listdir(srci)): if fn == 'mult768.c': if macros['P'] > 768: continue if fn in ('mult1024.c','precomp7681.inc','precomp10753.inc'): if macros['P'] < 768: continue if macros['P'] > 1024: continue if fn in ('mult1280.c'): if macros['P'] < 1024: continue if macros['P'] > 1280: continue srcif = '%s/%s'%(srci,fn) opif = '%s/%s'%(opi,fn) x = readfile(srcif) for m in macros: x = re.sub('{%s}'%m,'%s'%macros[m],x) writefiletime(opif,x,os.stat(srcif).st_mtime) if os.stat(srcif).st_mode&1: os.chmod(opif,0o755) if os.path.exists('%s/Makefile'%opi): subprocess.check_call('cd %s; make'%opi,shell=True) os.utime(opi,(srcitime,srcitime)) changedsrc = False for m in macros: newsrc = re.sub(m,'%s'%macros[m],src) if newsrc != src: changedsrc = True src = newsrc if changedsrc and os.path.exists(src): srctime = os.stat(src).st_mtime for impl in sorted(os.listdir(src)): if impl == 'supercop': continue srci = '%s/%s'%(src,impl) srcitime = os.stat(srci).st_mtime opi = '%s/%s'%(op,impl) os.makedirs(opi) writefiletime('%s/api.h'%opi,api,srctime) for fn in sorted(os.listdir(srci)): srcif = '%s/%s'%(srci,fn) opif = '%s/%s'%(opi,fn) x = readfile(srcif) writefiletime(opif,x,os.stat(srcif).st_mtime) if os.stat(srcif).st_mode&1: os.chmod(opif,0o755) os.utime(opi,(srcitime,srcitime)) os.utime(op,(srctime,srctime)) build_subroutine_done.add(op) # ----- sizes of encodings limit = 16384 def Encode(R,M): if len(M) == 0: return [] S = [] if len(M) == 1: r,m = R[0],M[0] while m > 1: S += [r%256] r,m = r//256,(m+255)//256 return S R2,M2 = [],[] for i in range(0,len(M)-1,2): m,r = M[i]*M[i+1],R[i]+M[i]*R[i+1] while m >= limit: S += [r%256] r,m = r//256,(m+255)//256 R2 += [r] M2 += [m] if len(M)&1: R2 += [R[-1]] M2 += [M[-1]] return S+Encode(R2,M2) def encodebytes(M): return len(Encode([0]*len(M),M)) # ----- main loop for p,q,sk,pk,ct,w in systems: q23 = (q+2)//3 q14 = round(2.0**14/q) q15 = floor(2.0**15/q) q18 = round(2.0**18/q) q27 = round(2.0**27/q) q31 = floor(2.0**31/q) qinv = pow(q,2**14-1,2**16) if qinv >= 2**15: qinv -= 2**16 q10753 = (10753<<16)%q if q10753 >= q/2: q10753 -= q ppadsort = p if p == 761: ppadsort = 768 if p == 953: ppadsort = 960 if p == 1013: ppadsort = 1024 if p == 1277: ppadsort = 1280 assert ppadsort >= p ppad = p while ppad%16 != 1: ppad += 1 ppad64 = p while ppad64%64 != 1: ppad64 += 1 # ----- parameter checks assert p > 0 assert p%4 == 1 assert q > 0 assert q%6 == 1 assert w > 0 assert 2*p >= 3*w assert q >= 16*w+1 assert p < 1280 assert q < 8192 smallbytes = ceil(p/4.0) roundedbytes = encodebytes([q23]*p) assert pk == encodebytes([q]*p) assert sk == 3*smallbytes+pk+32 assert ct == 32+roundedbytes # ----- build relevant subroutines first build_subroutine('verify/BYTES',{'BYTES':ct}) macros = {'STRBYTES':2,'ITEMS':1,'ITEMBYTES':2} build_subroutine('decode/int16',macros) build_subroutine('encode/int16',macros) macros = {'P':p,'STRBYTES':(p+3)//4,'ITEMS':p,'ITEMBYTES':1} macros['AVXLOOPS'] = ceil(p/128) macros['AVXOVERSHOOT'] = 32*ceil(p/128)-floor(p/4) build_subroutine('decode/Px3',macros) build_subroutine('encode/Px3',macros) macros = {'P':p,'STRBYTES':p*2,'ITEMS':p,'ITEMBYTES':2} build_subroutine('decode/Pxint16',macros) build_subroutine('encode/Pxint16',macros) macros = {'P':p,'STRBYTES':p*4,'ITEMS':p,'ITEMBYTES':4} build_subroutine('decode/Pxint32',macros) macros = {'P':p,'Q':q,'Q12':(q-1)//2,'STRBYTES':encodebytes([q]*p),'ITEMS':p,'ITEMBYTES':2} build_subroutine('decode/PxQ',macros) build_subroutine('encode/PxQ',macros) macros = {'P':p,'Q':q,'Q12':(q-1)//2,'R':q23,'STRBYTES':encodebytes([q23]*p),'ITEMS':p,'ITEMBYTES':2} build_subroutine('decode/PxR',macros) build_subroutine('encode/PxR',macros) build_subroutine('encode/PxRround',macros) macros = {'P':p,'STRBYTES':p,'ITEMS':p,'ITEMBYTES':2} build_subroutine('encode/Pxfreeze3',macros) macros = {'P':p,'PPAD64':ppad64,'OUTPUTBYTES':p+1,'INPUTBYTES':p,'KEYBYTES':0,'CONSTBYTES':0} build_subroutine('core/inv3sntrupP',macros) macros = {'P':p,'OUTPUTBYTES':p,'INPUTBYTES':p,'KEYBYTES':p,'CONSTBYTES':0} build_subroutine('core/mult3sntrupP',macros) macros = {'P':p,'Q':q,'QINV':qinv, 'Q18':q18,'15Q':q15,'Q27':q27,'Q31':q31,'Q10753':q10753, 'OUTPUTBYTES':2*p,'INPUTBYTES':2*p,'KEYBYTES':p,'CONSTBYTES':0} build_subroutine('core/multsntrupP',macros) macros = {'P':p,'PPAD':ppad,'Q':q,'QINV':qinv, 'Q14':q14,'15Q':q15,'Q18':q18,'Q27':q27,'Q31':q31, 'OUTPUTBYTES':2*p+1,'INPUTBYTES':p,'KEYBYTES':0,'CONSTBYTES':0} build_subroutine('core/invsntrupP',macros) macros = {'P':p,'Q':q,'OUTPUTBYTES':2*p,'INPUTBYTES':2*p,'KEYBYTES':0,'CONSTBYTES':0} build_subroutine('core/scale3sntrupP',macros) macros = {'P':p,'OUTPUTBYTES':2,'INPUTBYTES':p,'KEYBYTES':0,'CONSTBYTES':0} macros['AVXENDINGMASK'] = ','.join(['1']*(p%32)+['0']*(32-(p%32))) build_subroutine('core/weightsntrupP',macros) macros = {'P':p,'W':w,'OUTPUTBYTES':p,'INPUTBYTES':p,'KEYBYTES':0,'CONSTBYTES':0} build_subroutine('core/wforcesntrupP',macros) # ----- and now build the kem system = f'sntrup{p}' src = 'src/kem/sntrupP' srctime = os.stat(src).st_mtime op = '%s/%s'%(topdir,system) print('building %s'%op) shutil.rmtree(op,ignore_errors=True) os.makedirs(op) opreal = 'crypto_kem/%s'%system api = '#define CRYPTO_SECRETKEYBYTES %s\n'%sk api += '#define CRYPTO_PUBLICKEYBYTES %s\n'%pk api += '#define CRYPTO_CIPHERTEXTBYTES %s\n'%ct api += '#define CRYPTO_BYTES 32\n' if 'kem' not in apiall: apiall['kem'] = '' apiall['kem'] += api.replace('CRYPTO_',opreal.replace('/','_')) for impl in sorted(os.listdir(src)): srci = '%s/%s'%(src,impl) opi = '%s/%s'%(op,impl) os.makedirs(opi) target = '%s/api.h'%opi writefiletime(target,api,os.stat(srci).st_mtime) for fn in sorted(os.listdir(srci)): srcif = '%s/%s'%(srci,fn) opif = '%s/%s'%(opi,fn) if (impl,fn) in (('factored','params.h'),('avx','Makefile')): x = readfile(srcif) x = re.sub('{P}','%s'%p,x) x = re.sub('{PPADSORT}','%s'%ppadsort,x) x = re.sub('{Q}','%s'%q,x) x = re.sub('{R}','%d'%q23,x) x = re.sub('{Q18}','%d'%q18,x) x = re.sub('{Q27}','%d'%q27,x) x = re.sub('{Q31}','%d'%q31,x) x = re.sub('{W}','%s'%w,x) x = re.sub('{CIPHERTEXTBYTES}','%s'%ct,x) writefiletime(opif,x,os.stat(srcif).st_mtime) continue if p != 761 and fn == 'CHANGES': continue # XXX x = readfile(srcif) writefiletime(opif,x,os.stat(srcif).st_mtime) if os.stat(srcif).st_mode&1: os.chmod(opif,0o755) for impl in sorted(os.listdir('src/kem/sntrupP')): opi = '%s/%s'%(op,impl) if os.path.exists('%s/Makefile'%opi): subprocess.check_call('cd %s; make'%opi,shell=True) os.utime(op,(srctime,srctime)) for impl in sorted(os.listdir(src)): srci = '%s/%s'%(src,impl) srcitime = os.stat(srci).st_mtime opi = '%s/%s'%(op,impl) os.utime(opi,(srcitime,srcitime)) with open('api','w') as f: for sub in 'verify','decode','encode','core','kem': f.write(apiall[sub]) f.write('\n')