Es ist logisch trivial einzusehen, daᅵ IT-Sicherheit nicht von
proprietᅵren (black-box) Software der kommerziellen Firmen
abhᅵngig sein darf. Ja es ist in der Tat eine Ironie, daᅵ man,
um die eigene Privatsphᅵre zu schᅵtzen, sich je irgendwas
bedienen sollte, dessen Vertrauenswᅵrdigkeit vᅵllig unbekannt ist.
Zur Verwendung von gewᅵhnlichen Leuten fᅵr die Sicherung ihrer
gewᅵhnlichen Kommunikationen, d.h. Email etc., gegen Lauschangriffe
von Kriminellen und "Agencies" habe ich daher einen Python-Code
zur Verschlᅵsselung geschrieben, welcher von jedermann leicht
geprᅵft werden kann, um dessen Vertrauenswᅵrdigkeit festzustellen,
und welcher auch sehr effizient ist fᅵr dessen intendierten
Anwendungen in der Praxis.
Fᅵr Kommentare, Anregungen und Kritike wᅵre ich sehr dankbar.
M. K. Shen
--------------------------------------------------------------------
# JADE, a block cipher (with authentication) based on use of permutation
# polynomials mod 2**n.
# JADE is an encryption scheme having the following special features:
# 1. It is a block cipher that treats the bits of an entire block of
e.g. 256
# bits as a single integer value and directly performs certain arithmetic
# operations and bit rotations on it. See enc() and dec().
# 2. It uses a scheme of pseudo-random number generation that distinguishes
# itself from the common schemes, since it makes use not of a single PRNG
# but of a pool of constituent PRNGs that are pseudo-randomly
selected (by
# themselves) for activation, thus rendering its analysis inherently
hard.
# The outputs of these constituent PRNGs are post-processed in a way to
# further block any sequence prediction attempts of the analyist. See
# genrandom().
# 3. It does a dynamic encryption processing, since it employs pseudo-random
# numbers (also integers of the block size) to work with the blocks of
# plaintext bits (numbers) and there is a feedback from the value of a
# runtime internal variable such that the PRN sequence employed in any
# block is rendered dependent on the processing of the previous blocks.
# Another source of dynamics comes from the choice (depending also on
that
# internal variable) between two candidate operations for performing
# substitution (i.e. mapping between two integer values in our case) in
# the block. See enc() and dec() and genrandom().
# It should be remarked that our use of fairly big integers incurs of course
# trade-offs in terms of computing efficiency. Further, Python code is not
# compiled but interpreted. Benchmarking showed nevertheless that a
message of
# 10000 characters, which is sufficiently long for normal communications of
# common private persons (who naturally desire to have their personal
intimate
# spheres remain intact and who are our main targeted users!), can be
processed
# on a common modern PC (for the system parameters as given in the
examples and
# even with other possible values of logn and logpoolsize) in less than
1.3 sec.
# (for encryption or decryption), which is clearly acceptable as a price
to be
# paid for using our open-source and easily readable and verifiable code
instead
# of using commercial proprietary (black-box) IT-security software, which
# generally have very excellent computing efficiency but which are
absolutely
# unknown (since by definition un-knowable) of being free or not of dormant
# backdoors implanted by mafias or malicious agencies of certain mighty
# pseudo-righteous pseudo-humanitarian pseudo-peace-loving pseudo-democratic
# countries of the world and waiting to be exploited at really (optimal for
# them) critical time points.
# Detailed informations on the design of JADE are given in the two sections
# below. See in particular the comments that precede each function being
# defined.
# This software may be freely used:
# 1. for all personal purposes unconditionally and
# 2. for all other purposes under the condition that its name, version
number
# and authorship are explicitly mentioned and that the author is informed
# of all eventual code modifications done.
# Version 1.1, 22.08.2012.
# Update history:
# 19.08.2012: Release of Version 1.0.
# 22.08.2012: Release of Version 1.1. Variable flip is introduced in
genrandom()
# to provide more variability in, hence higher security for, the
underlying PRN
# generation scheme.
# 24.08.2012: Release of Version 1.1.1. Correction of a number in the
examples
# section and tiny modifications of comments.
# Author's email address:
mok-ko...@t-online.de
################################################################################
# The constituent PRNGs in the pool are all based on permutation polynomials
# mod 2**n (see reference [1] below) that generate PRN sequences having
# full-period length of 2**n. From an initializing PRNG (itself
consisting of
# 2 constituent PRNGs based on permuation polynomials) specified by
user-given
# data, the parameters of a user-chosen number of PRNGs of his desired
# polynomial degree are generated. See buildgeneratorpool(). The pool of
PRNGs
# has a dynamic pseudo-random index, nextprng, that points to the next
PRNG to
# be activated. This PRNG, besides generating its outputs as described,
can on
# occasions additionally serve to perform a bijective mapping on integer
values
# of the size of the block, since it is a permutation polynomial. see enc()
# which invokes either evalpermpoly() or invpermpoly().
# The number of bits, n, of a block can be specifed by the user via the
system
# variable logn, which is the log base 2 of n. Other basic system parameters
# to be defined by the user are logpoolsize, prngm, subkeylen and
# logfeedbacknum. logpoolsize is log base 2 of the size of the pool of
PRNGs.
# prngm is the number of coefficients of the permutation polynomials of the
# PRNGs in the pool (i.e. degree of polynomal + 1). subkeylen is similar to
# prngm but is for the initializing PRNG, see gensubkeyrandom().
logfeedbacknum
# is log base 2 of an (approximate) number of blocks processed after
which one
# PRN generated is to be discarded. (This is a feedback to the PRN
generation
# process.)
# Constraints: 7 <= logn <= 10
# 5 <= logpoolsize <= 11
# 3 <= prngm
# prngm <= subkeylen
# 1 <= logfeedbacknum (logfeedbacknum <= 4 recommended)
# The following is an example of user-chosen values of basic system
parameters
# of JADE. These will be used in the two examples given at the end of
the code.
# User-chosen block-length is 256 bits.
logn=8
# User-chosen size of PRNG pool is 128.
logpoolsize=7
# User-chosen type of PRNGs in the PRNG pool employs 2nd degree polynomials.
# (3 = 2 + 1)
prngm=3
# User-chosen initializing PRNG employs two 3rd degree polynomials. (4 =
3 + 1)
subkeylen=4
# User chooses to have as feedback to PRN generation to skip one PRN
generated
# for an average of every 4 blocks.
logfeedbacknum=2
# For each session in which JADE is used, the user has to supply
subkey1data,
# subkey2data, seed1data, seed2data as secret key materials and invoke the
# function initjade(). seed1data and seed2data are for security reasons
# preferrably to be variable (dynamic) accross all processing sessions, e.g.
# dependent on date, time and message numbers, even though subkey1data and
# subkey2data could in general remain constant for some comparatively longer
# periods of time. Each element in these secret key materials have an
effective
# length of n bits, i.e. longer data will be taken mod 2**n, while too short
# data have the disadvantage of not fully exploiting the available key
space.
# (It may be noted that such keys could also be systematically derived from
# certain master keys through an appropriate scheme of key management and
# there could even be a hierachy of master keys.)
# Note that, for the above example of user-chosen basic system
parameters with
# logn=8 and subkeylen=4, the entropy that could be specified in subkey1 and
# subkey2 can in the best case (i.e. with perfectly random bits in each
element
# having a width of 2**8=256 bits) attain a total of 2*4*256=2048 bits of
# entropy. (BTW this is evidently by far not the case with the data for the
# subkeys that are given in the examples section at the end of the
code.) This
# means on the other hand that, if the given key data are of rather low
entropy
# density, say 0.25, then there would nonetheless be a total of 512 bits of
# entropy (provided, as said, the width of 256 bits for each element is
fully
# utilized by the given data). The possibility of thus conveniently
employ key
# materials of low entropy density could under circumstances be
advantageously
# exploited. (One could then e.g. simply xor two pieces of common texts and
# obtain bit strings to serve as key materials, despite their
comparatively low
# entropy density.)
################################################################################
# The following are functions defined in JADE. User has to initialize
the system
# by invoking the function initjade() with the appropriate parameters.
# Modify user-supplied coefficients of the polynomial poly so as to
satisfy the
# full-period criteria of a permutation polynomial mod 2**n. The degree
of poly
# is required to be at least 2. m denotes the number of coefficients of the
# polynomial, i.e. degree of polynomial plus 1. For the criteria see
[1], p.283.
# [1] V. Anashin, A. Khrennikov, Applied Algebraic Dynamics, Berlin, 2009.
#
http://www.degruyter.com/view/supplement/9783110203011_Errata.pdf
# We use for simplification of coding a restricted form of the criteria,
namely:
# c_0 = c_1 = 1 mod 4
# c_i = 0 mod 4 for all other i
def fullperiodcriteria(poly,m):
global logn,n,np1,nm1,nh,tpn,tpnm1,chnum
if m<3:
print("error in fullperiodcriteria")
exit(1)
gg=(tpnm1<<2)&tpnm1
permpoly=poly[:]
for i in range(2):
permpoly[i]=(permpoly[i]&gg)|1
for i in range(2,m):
permpoly[i]&=gg
return(permpoly)
# Evaluate permpoly with argument x with Horner's method.
def evalpermpoly(permpoly,m,x):
global logn,n,np1,nm1,nh,tpn,tpnm1,chnum
y=permpoly[m-1]
for i in range(m-2,-1,-1):
y=(y*x+permpoly[i])%tpn
return(y)
# Inverse of evalpermpoly(). First derivative of permpoly(x) = 1 mod 2.
Hensel's
# lifting lemma leads to the iteration: x_(i+1)=x_i-g(x_i) mod 2**(i+1),
with
# g(x)=permpoly(x)-y. See [1] above, p.306.
def invpermpoly(permpoly,m,y):
global logn,n,np1,nm1,nh,tpn,tpnm1,chnum
if (((permpoly[0]-y)%tpn)&1)==0:
x=0
else:
x=1
for i in range(2,np1,1):
x=(x-(evalpermpoly(permpoly,m,x)-y))%tpn
return(x)
# A composite PRNG built via xor-ing of 2 constituent PRNGs with the
output of
# the 2nd PRNG rotated by one half of word-length. This composite PRNG
is used
# only at JADE initilization time to generate a pool of PRNGs for the proper
# generation of PRNs of our scheme. See buildgeneratorpool().
def gensubkeyrandom(veclen):
global logn,n,np1,nm1,nh,tpn,tpnm1,chnum
global subkeylen,subkey1,subkey2,seed1,seed2
if (len(subkey1)!=subkeylen) or (len(subkey2)!=subkeylen):
print("error in gensubkeyrandom")
exit(2)
randnum=[]
for i in range(veclen):
seed1=evalpermpoly(subkey1,subkeylen,seed1)
seed2=evalpermpoly(subkey2,subkeylen,seed2)
r=((seed2<<nh)&tpnm1)|(seed2>>nh)
randnum+=[seed1^r]
return(randnum)
# Build a pool of PRNGs and intitialize their seeds. Build and fill two
buffers
# and initialize their pointers. Initialize nextprng, feedback and flip.
def buildgeneratorpool():
global prngm,poolsize,maskpool,prngpool,prngseed,nextprng
global maskfeedback,feedback,flip
global buffer1,buffer2,buffer1pt,buffer2pt,prngsum,rot0,rot1,rot2
prngpool=[]
prngseed=[]
buffer1=[]
buffer2=[]
for i in range(poolsize):
randtmp=gensubkeyrandom(prngm+3)
permpoly=fullperiodcriteria(randtmp[:-3],prngm)
prngpool+=[permpoly]
prngseed+=[randtmp[-3]]
buffer1+=[randtmp[-2]]
buffer2+=[randtmp[-1]]
buffer1pt=buffer2pt=0
nextprng=0
feedback=0
flip=0
# Use the pool of PRNGs to generate and return a PRN sequence of length
veclen.
# The consituent PRNG currently indexed by nextprng is activated and
outputs rr.
# rr is combined with the value of an accumulator (prngsum) with xor or +
# (depending on the value of the variable flip) and is rotated by an
amount rot0
# to become the new value of prngsum, which is also designated as uu.
nextprng
# and rot0 are updated with bits from rr. uu is put into buffer1 and
output of
# it is put into buffer2. (Outputs from the two buffers are rotated by
rot1 and
# rot2 respectively.) Both buffer pointers and rot1 and rot2 are updated
with
# bits of uu. The sequence of outputs from buffer2 is finally returned
by the
# function. The pseudo-radnom nature of the activation of the
constituent PRNGs
# in the pool, together with the post-processing of their outputs, which
# introduces indeterminancies for the analyst, effectively thwart his
sequence
# prediction attempts. For, given an output value from this function, his is
# blocked all along the path that would lead backward to the one particular
# constituent PRNG that contributed to that value. Note also that there are
# moreover no statistical biases in the output sequences to be exploited in
# practice, as test results with Maurer's Universal Test have shown. If
feedback
# is 1, one PRN is discarded and feedback is reset. flip, which
introduces some
# variability into the PRN generation process, is updated with one bit
of rr.
def genrandom(veclen):
global logn,n,np1,nm1,nh,tpn,tpnm1,chnum
global prngm,poolsize,maskpool,prngpool,prngseed,nextprng
global maskfeedback,feedback,flip
global buffer1,buffer2,buffer1pt,buffer2pt,prngsum,rot0,rot1,rot2
global shfpg,shfr0,shfb1,shfr1,shfb2,shfr2
if feedback==1:
rr=prngseed[nextprng]\
=evalpermpoly(prngpool[nextprng],prngm,prngseed[nextprng])
nextprng=(rr>>shfpg)
rot0=(rr>>shfr0)&nm1
flip=rr>>nm1
feedback=0
randnum=[]
for i in range(veclen):
rr=prngseed[nextprng]\
=evalpermpoly(prngpool[nextprng],prngm,prngseed[nextprng])
if flip==0:
prngsum=prngsum^rr
else:
prngsum=prngsum+rr
uu=prngsum=((prngsum<<rot0)&tpnm1)|(prngsum>>(n-rot0))
nextprng=(rr>>shfpg)
rot0=(rr>>shfr0)&nm1
flip=rr>>nm1
bb1=buffer1[buffer1pt]
buffer1[buffer1pt]=uu
bb1=((bb1<<rot1)&tpnm1)|(bb1>>(n-rot1))
bb2=buffer2[buffer2pt]
buffer2[buffer2pt]=bb1
bb2=((bb2<<rot2)&tpnm1)|(bb2>>(n-rot2))
randnum+=[bb2]
buffer1pt=(uu>>shfb1)
rot1=(uu>>shfr1)&nm1
buffer2pt=(uu>>shfb2)&maskpool
rot1=(uu>>shfr2)&nm1
return(randnum)
# Perform checks of constraints on user-given values and initialize a
number of
# system variables. Let the pool of PRNGs be built and does a priming of the
# PRNGs in the pool. (Size of the buffers is chosen by design to be equal to
# that of the pool of the PRNGs.) Initialize sigma and finalvector.
(sigma is
# used in enc() and dec(). finalvector is used for authentication, see
encrypt()
# and decrypt().)
def initjade(subkey1data,subkey2data,seed1data,seed2data):
global logn,n,np1,nm1,nh,tpn,tpnm1,chnum
global logpoolsize,logfeedbacknum
global subkeylen,subkey1,subkey2,seed1,seed2
global prngm,poolsize,maskpool,prngpool,prngseed,nextprng
global maskfeedback,feedback,flip
global buffer1,buffer2,buffer1pt,buffer2pt,prngsum,rot0,rot1,rot2
global shfpg,shfr0,shfb1,shfr1,shfb2,shfr2
global sigma,finalvector,rots
n=2**logn
np1=n+1
nm1=n-1
nh=n//2
tpn=2**n
tpnm1=tpn-1
chnum=n//8
subkey1=subkey1data
subkey2=subkey2data
seed1=seed1data
seed2=seed2data
if prngm<3 or subkeylen<prngm or logn<7 or logn>10 or\
logpoolsize<5 or logpoolsize>11 or logfeedbacknum<1 or\
len(subkey1)!=subkeylen or len(subkey2)!=subkeylen:
print("error in initjade")
exit(3)
for i in range(subkeylen):
subkey1[i]%=tpn
subkey2[i]%=tpn
poolsize=2**logpoolsize
# The following 8 bit masks are needed in genrandom().
maskpool=poolsize-1
shfpg=n-logpoolsize
shfr0=shfpg-logn
shfb1=shfpg
shfr1=shfr0
shfb2=shfr1-logpoolsize
shfr2=shfb2-logn
maskfeedback=(2**logfeedbacknum)-1
prngsum=0
rot0=rot1=rot2=0
buildgeneratorpool()
# Priming of the PRNG pool. (This is a conservative measure. Note that the
# buffers are initialized with values from gensubkeyrandom().)
genrandom(2*poolsize)
# Initialize sigma and finalfector.
randtmp=genrandom(2)
sigma=randtmp[0]
finalvector=randtmp[1]
rots=0
# Rotate word w left by k bits.
def rotation(w,k):
global logn,n,np1,nm1,nh,tpn,tpnm1,chnum
if k>n:
print("error in rotation")
exit(4)
return((w<<k)&tpnm1)|(w>>(n-k))
# Inverse of x mod 2**n. Code taken from communication of Thomas Pornin,
2009.
def invmod(x):
global logn,n,np1,nm1,nh,tpn,tpnm1,chnum
if (x%2)==0:
print("error in invmod")
exit(5)
y=2-x
for i in range(logn):
y=(y*(2-x*y))%tpn
return(y)
# A function mod 2**n that is bijective in x, when y is constant, and vice
# versa. This is considered to be beneficial for combining x and y in
respect
# of entropy.
def hf(x,y):
global logn,n,np1,nm1,nh,tpn,tpnm1,chnum
return((2*x*y+x+y)%tpn)
# Inverse of hf().
def invhf(hf,y):
global logn,n,np1,nm1,nh,tpn,tpnm1,chnum
return(((hf-y)*invmod(2*y+1))%tpn)
# np and nc are integer values that result from byte-wise coding of
plaintext
# and ciphertext characters respectively in a block (of chnum
characters). Let
# sigma denote the accumulator and r0 and r1 be two PRNs. We compute:
# g = hf((sigma + np) xor r0, r1) and, depending on the value of one bit in
# sigma, either nc = permpoly(prng,g) or nc = invpermpoly(prng,g), where
prng
# denotes the permutation polynomial of the current (active) PRNG in the
pool
# (i.e. the one indexed by nextprng). sigma is updated by adding nc and
xor-ing
# with np, after which it is rotated by an amount rots, which is a value
taken
# from r1. The said runtime selection of mapping provides dynamics in the
# processing, which renders the analysis hard. It also renders the
processing
# time of encryption and decryption equal, since the inverse computation
takes
# more time. Note that sigma serves the function of block-chaining, its
value
# being dependent on all plaintext and ciphertext blocks that have been
# processed before the current block. If at begin of processing of a
block the
# last 2**logfeedbacknum bits of sigma are all 1, one PRN generated will be
# discarded via the variable feedback, see genrandom().
def enc(nplist):
global logn,n,np1,nm1,nh,tpn,tpnm1,chnum
global prngm,poolsize,maskpool,prngpool,prngseed,nextprng
global maskfeedback,feedback,flip
global buffer1,buffer2,buffer1pt,buffer2pt,prngsum,rot0,rot1,rot2
global sigma,finalvector,rots
nclist=[]
for i in range(len(nplist)):
np=nplist[i]
# Feedback to PRN generation process.
if (sigma&maskfeedback)==maskfeedback:
feedback=1
# Obtain r1 and r2.
randtmp=genrandom(2)
# Compute g.
g=hf(((sigma+np)%tpn)^randtmp[0],randtmp[1])
# If on encryption the middle bit of sigma is 0, i.e. with probability
of 1/2,
# use evalpermpoly() as mapping else use its inverse invpermpoly() as
mapping
# to obtain nc from np (via g).
if (sigma>>nh)&0x1==0:
nc=evalpermpoly(prngpool[nextprng],prngm,g)
else:
nc=invpermpoly(prngpool[nextprng],prngm,g)
nclist+=[nc]
sigma=rotation(((sigma+nc)%tpn)^np,rots)
# Use last logn bits of r1 as new value of rots.
rots=randtmp[1]&nm1
# nplist[:] (not simply nplist) is required in the statement below to
restore
# the nplist outside to its original value.
nplist[:]=nplist[:-1]
return(nclist)
# Inverse of enc().
def dec(nclist):
global logn,n,np1,nm1,nh,tpn,tpnm1,chnum
global prngm,poolsize,maskpool,prngpool,prngseed,nextprng
global maskfeedback,feedback,flip
global buffer1,buffer2,buffer1pt,buffer2pt,prngsum,rot0,rot1,rot2
global sigma,finalvector,rots
nplist=[]
for i in range(len(nclist)):
nc=nclist[i]
if (sigma&maskfeedback)==maskfeedback:
feedback=1
randtmp=genrandom(2)
if (sigma>>nh)&0x1==0:
g=invpermpoly(prngpool[nextprng],prngm,nc)
else:
g=evalpermpoly(prngpool[nextprng],prngm,nc)
np=((invhf(g,randtmp[1])^randtmp[0])-sigma)%tpn
nplist+=[np]
sigma=rotation(((sigma+nc)%tpn)^np,rots)
rots=randtmp[1]&nm1
return(nplist)
# pickle is a system module of Python.
import pickle
# Convert a string s to a binary integer representation.
def pack(s):
b=s.encode('latin1')
k=0
for i in range(len(b)):
k<<=8
k|=b[i]
return(k)
# The inverse of pack(). blen is the length of the original string that was
# previously processed by pack().
def unpack(k,blen):
b=bytearray(blen)
for i in range(blen-1,-1,-1):
b[i]=k&0xff
k>>=8
s=b.decode('latin1')
return(s)
# If the length of the string plaintext is not a multiple of block
length (chnum
# characters), a filling with the character 'z' is done. Divide the
plaintext
# string into a number of blocks. Each block is then converted into one
integer
# by pack(), resulting thus in a list of integers, nplist. The PRN
finalvector,
# generated in initjade(), is appended to nplist for purpose of
authentication
# in decrypt(). Encrypts nplist with enc() into nclist, which is also a
list of
# integers, and store it into ciphertextfile. This file is not printable in
# readable symbols and has to be sent to the recipient e.g. as email
attachment.
# In case this is undesirable, a conceivable simple alternative would be to
# directly output nclist, which is a list of big integers and whose elements
# could even be transmitted in digits on voice channels if necessary.
def encrypt(plaintext,ciphertextfile):
global logn,n,np1,nm1,nh,tpn,tpnm1,chnum
print("processing string plaintext")
k=len(plaintext)%chnum
if k!=0:
for i in range(chnum-k):
plaintext+="z"
print("plaintext is extended at end with",chnum-k,"'z' "\
"for processing requirement")
k=0
nplist=[]
for i in range(len(plaintext)//chnum):
nplist+=[pack(plaintext[k:k+chnum])]
k+=chnum
# Append finalvector.
nplist+=[finalvector]
nclist=enc(nplist)
fp=open(ciphertextfile,"wb")
pickle.dump(nclist,fp)
fp.close()
print("ciphertext has been written to file:",ciphertextfile)
print()
# Retrieve nclist from ciphertextfile and decrypt it with dec() into nplist,
# which is a list of integers, each of which corresponds to a block of
plaintext
# characters (chnum in number) previously encrypted by the sender with
# encrypt(). An integer to characters conversion is then done with
unpack(). The
# thus recovered string of plaintext characters should be identical
(excepting
# the fillers 'z' at end) to the string of plaintext characters that the
sender
# processed with encrypt(), if the authentication check based on the
encryption
# of finalvector (a PRN value appended to nplist in encryption()) is
found to be
# ok. For, the value of sigma that is used in the procesing of
finalvector is
# dependent on all plaintext and ciphertext blocks that have been processed
# before finalvector. The recovered plaintext string is returned.
def decrypt(ciphertextfile):
global logn,n,np1,nm1,nh,tpn,tpnm1,chnum
print("processing file",ciphertextfile)
fp=open(ciphertextfile,"rb")
nclist=pickle.load(fp)
fp.close()
nplist=dec(nclist)
# Check whether the last block of ciphertext decrypts to finalvector.
if nplist[-1]!=finalvector:
print("######### authentication failed #########")
exit(6)
# Remove finalvector.
nplist=nplist[:-1]
plaintextrecovered=""
for i in range(len(nplist)):
plaintextrecovered+=unpack(nplist[i],chnum)
print("plaintext has been recoverd from",ciphertextfile,"
authentication ok")
print()
return(plaintextrecovered)
################################################################################
# The following code does not belong to JADE in the proper sense but
serves only
# to illustrate with two examples further below how the user may employ it.
# Maurer's Universal Test, see [2].
# [2] J-S. Coron, D. Naccache, An Accurate Evaluation of Maurer's
Universal Test.
#
http://www.jscoron.fr/publications/universal.pdf
import math
qq=2560
qqp1=qq+1
kk=256000
qqkkp1=qq+kk+1
bbdim=259072
def maurertest(bb):
global qq,qqp1,kk,qqkkp1,bbdim
eftu=7.1836656
y1=2.5758
y2=3.2905
t=[0 for i in range(256)]
for i in range(1,qqp1,1):
t[bb[i]]=i
sum=0.0
for i in range(qqp1,qqkkp1,1):
sum+=math.log10(i-t[bb[i]])
t[bb[i]]=i
tu=(sum/kk)/math.log10(2.0)
c=math.sqrt(0.3732189+(0.3730195*256)/kk)
sigma=c*math.sqrt(3.2386622/kk)
t11=eftu-y1*sigma
t12=eftu+y1*sigma
t21=eftu-y2*sigma
t22=eftu+y2*sigma
return(tu,t11,t12,t21,t22)
def maurertestresult():
global logn,n,np1,nm1,nh,tpn,tpnm1,chnum
global qq,qqp1,kk,qqkkp1,bbdim
bb=[0 for k in range(bbdim)]
h=bbdim//chnum
gg=genrandom(h)
u=0
k1=-1
k2=chnum-1
for i in range(h):
g=gg[i]
for k in range(k2,k1,-1):
bb[u]=g&0xff
g>>=8
u+=1
k1+=chnum
k2+=chnum
tu,t11,t12,t21,t22 = maurertest(bb)
print("Maurer's Universal Test for L=8, rho=0.01 (middle value is the "\
"test statistic and should lie between the other two values):")
print(t11,tu,t12)
# Two examples of ways of using JADE:
# Values of basic system parameters have already been specified by the
user in
# the above.
# The secret key informations agreed upon by the communication paratners
are in
# our case assumed to be as follows. Note that each element below could, if
# desired, advantageously exploit the full range of n-bit binary values. The
# following values don't do so for our present case of logn=8. These values
# actually had been arbitrarily chosen as a common basis for testing the
code
# for different values of logn during the software development. (See for
this
# issue the comments following the user-chosen values of basic system
parameters
# further above.)
subkey1data=[ 0x11112222, 0x22223333, 0x33334444, 0x55555555 ]
subkey2data=[ 0xaaaaaaaa, 0xbbbbbbbb, 0x55556666, 0xeeeeeeee ]
seed1data=12345
seed2data=4567890
# Example 1:
print("Example 1:")
print()
# We suppose the sender has the following character string pt as plaintext:
pt="Was sich ueberhaupt sagen laesst, laesst sich klar sagen; und "\
"wovon man nicht reden kann, darueber muss man schweigen."
print("original plaintext:")
print(pt)
print()
# The sender intializes the system,
initjade(subkey1data,subkey2data,seed1data,seed2data)
# encrypts pt, obtaining a binary ciphertext file ciphertext.bin
encrypt(pt,"ciphertext.bin")
# and sends it to the recipient. (This file cannot be printed in readable
# symbols and must be sent as such.)
# The recipient initializes the system,
initjade(subkey1data,subkey2data,seed1data,seed2data)
# decrypts ciphertext.bin, assigns the result to pt1 and prints it out.
(Result
# of authentication check will be automatically reported.)
pt1=decrypt("ciphertext.bin")
print("plaintext recovered:")
print(pt1)
print()
# Example 2. Perform Maurer's Universal Test.
# User may employ our function genrandom() to obtain PRNs for other purposes
# than encpryption processing and may need to know their statistical
properties.
# Hence the present example.
print("Example 2:")
print()
# Note that theoretically there is a 0.01 chance for the test statistic
tu to go
# outside of the interval [t11, t12] (under the assumption that the sequence
# being tested is random).
initjade(subkey1data,subkey2data,seed1data,seed2data)
maurertestresult()