Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.programming > #3556
| From | Mok-Kong Shen <mok-kong.shen@t-online.de> |
|---|---|
| Newsgroups | comp.programming |
| Subject | JADE-S, a block cipher with authentication (integrity check) based on use of permutation polynomials mod 2**n |
| Date | 2013-06-24 20:35 +0200 |
| Organization | albasani.net |
| Message-ID | <kqa3i4$q0v$2@news.albasani.net> (permalink) |
JADE-S is a successor of JADE. It employs the same PRN generation
scheme as JADE but implements a novel idea of block encryption
processing which is inspired by the iterative solution of systems of
linear equations and is presented by me recently in a thread in this
group. For comments and critiques I should be very grateful.
M. K. Shen
-----------------------------------------------------------------------
# JADE-S, a block cipher with authentication (integrity check) based on
the use
# of permutation polynomials mod 2**n
# JADE-S is a block encryption scheme for securing the privacy of
communications
# of the common people via encrypting text messages (with automatically
# performed authentication (integrity check)) sent over the Internet.
Its novel
# design includes as a major component a sophistically designed
pseudo-random
# number (PRN) generation scheme, involving a pool of constituent PRN
generators
# (PRNGs) based on permutation polynomials mod 2**n that are pseudo-randomly
# activated (by themselves) with runtime dynamic renewal. Block lengths
ranging
# from 128 to 1024 bits can be chosen by the user. Blocks of n bits are
treated
# as n-bit integers with certain arithmetic operations being directly
performed
# upon them. The PRNGs in the pool serve further the purpose of
pseudo-random
# substitution (in the crypto sense) operations, since permutation
polynomials
# can be employed for bijective mappings. Particular attention is paid
to very
# thoroughly mixing together the entire set of blocks that constitute the
# user-given messages during encryption processing, thus resulting in
superior
# avalanche effects. The very good readability of its open-source code
enables
# the user to easily ensure the correctness of the code and that there
can be no
# backdoors as could otherwise potentially be the case where commercial
# proprietary (blackbox) encryption software are employed. Speed competition
# with commercial software in processing huge volumes of materials, e.g.
# graphics files, is intentionally excluded from the design goals of
JADE-S. On
# the other hand, extreme prudence is taken to ensure that the code is
# practically absolutely immune to all known attacks against block ciphers
# owing to the fact that their underlying assumptions simply don't hold for
# JADE-S from the very beginning. (JADE-S presumably will be secure as
well in
# the forthcoming so-called post-quantum era.) Despite the trade-offs hereby
# forcefully being taken, JADE-S's processing efficiency, as benchmarking
# clearly shows, is nonetheless entirely acceptable for its targeted
users (see
# result of running Ex. 2 in Sec.2, and Sec.3).
# JADE-S is a successor of JADE (latest Version 2.0.3 available from
# http://s13.zetaboards.com/Crypto/topic/6948465/1/#new). It employs the
same
# PRN generation scheme as JADE but implements a novel idea of block
encryption
# processing which is taken from the iterative method of solutions of
systems of
# equations in linear algegra and which is recently posted to a number
of Usenet
# groups by the present author. A breif description of the major design
features
# of JADE-S is as follows:
#
# 1. With the user-given key we build up in a bootstrap way from a PRNG
based on
# permutaton polynomial mod 2**n through an intermediate pool, i.e. a
large
# number, of PRNGs of the same kind to a final pool of such PRNGs. The
# outputs of the final pool of PRNGs (which are pseudo-randomly
chosen to be
# activated by their own outputs) are post-processed through appropriate
# summation and buffering such that the statistical qualities of the PRNs
# ultimately being delivered are greatly improved and any attacks of the
# analysts are rendered infeasible in practice.
#
# 2. Denoting the blocks of plainatext by x1, x2, ... xk, we perform the
# following assignments sequentially for encryption processing:
# x1 := f1(x1 + x2 ... + xk ^ r1)
# x2 := f2(x1 + x2 ... + xk ^ r2)
# ................
# xk := fk(x1 + x2 ... + xk ^ rk)
# where the f's are the permutation polynomials pseudo-randomly
chosen from
# those available in the final PRNG pool and the r's are PRNs
generated by
# the PRNG pool. Note that the f's are non-linear and that, in
obtaining the
# individual ciphertext blocks (the x's on the leftside of the
assignments)
# all blocks of the given message (some already processed, some not
yet) are
# involved, which clearly leads to a superior avalanche effect (compare
# however block-chaining that is commonly used in running the other known
# block ciphers).
#
# 3. For the purpose of authentication, we append a pseudo-random block
to the
# beginning of the given plaintext blocks such that it now takes the
position
# of x1 in the processing scheme sketched above. It is easily seen
that x1
# has strong influences on the results of all the other assignments. This
# implies, conversely, that any manipulation of the ciphertext blocks
will
# with very high probability be reflected in the changed value of x1
(with
# respect to the one on encryption) obtained on decryption, which
means that
# x1 can serve well as a MAC for authentication. (JADE-S actually
employs,
# as an enhancement, also an additional pseudo-random block at the
end of the
# given plaintext blocks for authentication and the user may further
specify
# that more than one such authentication blocks be used.)
# Contents of this document:
# Section 1: Python (http://www.python.org, Python 3.x) code of JADE-S with
# comments explaining the functionality of each function as well as the
# structuring of the entire program. Permissible values of system parameters
# and key materials are listed at the beginning of this section. Users
with some
# programming experience and basic knowledge of Python should be able to
read
# this section and understand how JADE-S exactly works without difficulties.
# Section 2: Some examples demonstrating how JADE-S can be actually
used for
# encryption/decryption as well as how Maurer's Universal Test can be
done for
# getting informations on the statistical quality of the results obtained.
# Section 3: Miscellaneous notes.
# Version 1.0, released on 24.06.2013.
# Version numbers and release dates do not reflect interim revisions of
comment
# lines. Code lines of documents with the same version number are
however always
# identical. The most recent document of JADE-S can be obtained from
# http://s13.zetaboards.com/Crypto/topic/7072208/1/#new
# 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.
# The author is indebted to TPS for review and suggestions throughout
JADE-S's
# development phase. Any remaining deficiencies of the software are
however the
# sole responsibilty of the author.
# Concrete comments and constructive critiques are sincerely solicited
either
# via the above thread or directly via email.
# Email address of the author: mok-kong.shen@t-online.de
################################################################################
################################################################################
######### Section 1. Python Code of JADE-S.
##### This section lists all functions defined in JADE-S. For each run
of JADE-S
##### the user has to provide the appropriate system parameters and key
##### materials and invoke initjades(). See Sec.2 for examples.
# Permissible values of system parameters and key materials (limits chosen
# based on practical considerations) are as follows (the first 5 parameters
# of the list and ciphertextfilemode are essential, the rest could be more
# or less arbitrarily chosen, their variability has albeit the
beneficial side
# effect of enhancing the the complexity facing the analyst):
# logn: log base 2 of the number of bits (n) in a block.
# 7 <= logn <= 10.
# baseprngm: Degree of polynomials + 1 of the PRNGs in baseprngpool.
# baseprngm >= prngm.
# logbasepoolsize: log base 2 of the size of baseprngpool.
# 7 <= logbasepoolsize <= 10
# prngm: Degree of polynomials + 1 of the PRNGs in prngpool.
# 3 <= prngm.
# logpoolsize: log base 2 of the size of prngpool.
# 7 <= logpoolsize <= 10
# maxprngusecount: Maxmal number of uses of a PRNG in prngpool to
generate PRNs
# before it is automatically renewed.
# 20 <= maxprngusecount
# initialrandompriming: Priming value of the PRNG geninitialrandom().
# 0 <= initialrandompriming.
# baserandompriming: Priming value of the PRNG genbaserandom().
# 0 <= baserandompriming.
# randompriming: Priming value of the PRNG genrandom().
# 0 <= randompriming.
# initvecblocknum: Number of authentication blocks at the begining of
# ciphertext.
# 1 <= initvecblocknum.
# finalvecblocknum: Number of authentication blocks at the end of
ciphertext.
# 0 <= finalvecblocknum.
# ciphertextfilemode: = 0: Outputfile is Python specific (not printable).
# = 1: Outputfile is in decimal integers (printable).
# = 2: Outputfile is in hexadecimal integers
(printable).
# keydata: A list of 2 elements of (maximal) n/2 bits each.
# Larger values given will be truncated mod 2**n.
# seeddata: 1 element of (maximal) n/2 bits.
# Larger values given will be truncated mod 2**n.
# Loading three system modules of Python.
import math,time,pickle
##### All pseudo-random number generators (PRNGs) of JADE-S are based on
##### permutation polynomials mod 2**n. The user-given key materials are
used to
##### establish the ultimate PRNG genrandom() through a bootstrapping
process
##### via gennhrandom(), geninitialrandom() and genbaserandom().
##### The function fullperiodcriteria(), evalpermpoly(), invpermpoly() are
##### general functions required for computating with permutation
polynomials
##### mod 2**n.
# 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
ref.[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,chnum
global tpwn,tpwnm1,shfnrot,tpwnh,tpwnhm1,shfnhrot
if m<3:
print("Error in fullperiodcriteria")
exit(1)
gg=(tpwnm1<<2)&tpwnm1
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,chnum
global tpwn,tpwnm1,shfnrot,tpwnh,tpwnhm1,shfnhrot
y=permpoly[m-1]
for i in range(m-2,-1,-1):
y=(y*x+permpoly[i])&tpwnm1
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 ref.[1], p.306.
def invpermpoly(permpoly,m,y):
global logn,n,np1,nm1,nh,chnum
global tpwn,tpwnm1,shfnrot,tpwnh,tpwnhm1,shfnhrot
if (((permpoly[0]-y)&tpwnm1)&1)==0:
x=0
else:
x=1
for i in range(2,np1,1):
x=(x-(evalpermpoly(permpoly,m,x)-y))&tpwnm1
return(x)
##### The user-given key (a list of 2 elements of n/2 bits) and seed (1
element
##### of n/2 bits) are used to build a PRNG gennhrandom(), delivering
PRNs of
##### n/2 bits, with the help of nhcombine() and buildinitialgenerator().
##### Concatenating 2 successive outputs gennhrandom() forms then the
output of
##### n bits of the PRNG geninitialrandom(), which will be used in the next
##### higher level code of PRN generation.
# Combining two n/2 bit words with reciprocal rotation and xor.
def nhcombine(x,y):
global logn,n,np1,nm1,nh,chnum
global tpwn,tpwnm1,shfnrot,tpwnh,tpwnhm1,shfnhrot
rotx=x>>shfnhrot
roty=y>>shfnhrot
rx=((x<<roty)&tpwnhm1)|(x>>(nh-roty))
ry=((y<<rotx)&tpwnhm1)|(y>>(nh-rotx))
r=rx^ry
return(r)
# Build a linear congruential PRNG mod 2**(n/2), gennhrandom(), from the
# user-given keydata and seeddata.
# Full period criteria: c0 = 1 mod 2, c1 = 1 mod 4, see ref.[1].
def buildinitialgenerator():
global keylen,keydata,seeddata,key,seed,basekeylen
global tpwn,tpwnm1,shfnrot,tpwnh,tpwnhm1,shfnhrot
key=keydata
seed=seeddata
if (len(key)!=2):
print("Error in buildinitialgeneator")
exit(2)
gg=(tpwnhm1<<2)&tpwnhm1
key[0]=(key[0]&tpwnhm1)|0x1
key[1]=((key[1]&tpwnhm1)&gg)|0x1
seed=seed&tpwnhm1
# Combine two outputs of the linear PRNG established in
buildinitialgenerator()
# with nhcombine() to obtain one output of n/2 bits of gennhrandom().
Function
# returns a list of veclen elements.
def gennhrandom(veclen):
global keylen,keydata,seeddata,key,seed,basekeylen
global tpwn,tpwnm1,shfnrot,tpwnh,tpwnhm1,shfnhrot
nhrandom=[]
for i in range(veclen):
r1=seed=(key[0]+key[1]*seed)&tpwnhm1
r2=seed=(key[0]+key[1]*seed)&tpwnhm1
nhrandom+=[nhcombine(r1,r2)]
return(nhrandom)
# Concatenate two outputs from gennhrandom() to be one of n bits. Function
# returns a list of veclen elements.
def geninitialrandom(veclen):
global logn,n,np1,nm1,nh,chnum
global keylen,keydata,seeddata,key,seed,basekeylen
global tpwn,tpwnm1,shfnrot,tpwnh,tpwnhm1,shfnhrot
random=[]
for i in range(veclen):
r=gennhrandom(2)
random+=[(r[0]<<nh)|r[1]]
return(random)
##### Outputs of geninitialrandom() are used to build a pool of PRNGs
based on
##### permutation polynomials mod 2**n of degree baseprngm-1, designated as
##### baseprngpool, with the help of ncombine() and
buildbasegeneratorpool().
##### genbaserandom() then delivers PRNs from this pool, which will be
used for
##### the next higher level code of PRN generation.
# Combining two n bit words with reciprocal rotation and xor.
def ncombine(x,y):
global logn,n,np1,nm1,nh,chnum
global tpwn,tpwnm1,shfnrot,tpwnh,tpwnhm1,shfnhrot
rotx=x>>shfnrot
roty=y>>shfnrot
rx=((x<<roty)&tpwnm1)|(x>>(n-roty))
ry=((y<<rotx)&tpwnm1)|(y>>(n-rotx))
r=rx^ry
return(r)
# Use geninitialrandom() to build a PRNG (consisting of two internal
PRNGs of
# degree baseprngm-1, with keys gbasekey1 and gbasekey2 and seeds
gbaseseed1 and
# gbaseseed2) to build a pool of PRNGs of degree baseprngm-1, designated as
# baseprngpool.
def buildbasegeneratorpool():
global baseprngm,logbasepoolsize,basepoolsize,baseprngpool,baseprngseed
global basebuffer,basebufpt
global baseprn,baseindex,baseprngsum,shfbaseindex,shfbasebegin
global initialrandompriming,baserandompriming,randompriming
buildinitialgenerator()
# Priming of geninitialrandom().
geninitialrandom(initialrandompriming)
rr1=geninitialrandom(baseprngm+1)
gbasekey1=rr1[:-1]
gbaseseed1=rr1[-1]
rr2=geninitialrandom(baseprngm+1)
gbasekey2=rr2[:-1]
gbaseseed2=rr2[-1]
gbasekey1=fullperiodcriteria(gbasekey1,baseprngm)
gbasekey2=fullperiodcriteria(gbasekey2,baseprngm)
baseprngpool=[]
baseprngseed=[]
basebuffer=[]
for i in range(basepoolsize):
randtmp=[]
for j in range(baseprngm+2):
r1=gbaseseed1=evalpermpoly(gbasekey1,baseprngm,gbaseseed1)
r2=gbaseseed2=evalpermpoly(gbasekey2,baseprngm,gbaseseed2)
randtmp+=[ncombine(r1,r2)]
permpoly=fullperiodcriteria(randtmp[:-2],prngm)
baseprngpool+=[permpoly]
baseprngseed+=[randtmp[-2]]
basebuffer+=[randtmp[-1]]
basebufpt=0
baseprn=0
baseindex=0
baseprngsum=0
# Generate a sequence of PRNs (a list of veclen elements) from the
baseprngpool.
# The consituent PRNG in the pool currently indexed by baseindex is
activated
# and outputs baseprn. baseprn is summed into an accumulator
(baseprngsum) and
# the sums are passed through a buffer (basebuffer) to result in the
elements
# of the desired sequence. 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 analysis, effectively
# thwart the analyst's sequence prediction attempts. For, given an
output value
# from this function, he is blocked all along the path that would lead
backwards
# to the one particular constituent PRNG that mainly contributed to that
value.
# Note also that there are moreover no statistical biases in the output
# sequences of JADE-S's genbaserandom() (this applies also to
genrandom() of the
# next higher final level of PRN generation further below) to be
exploited in
# practice, as test results with Maurer's Universal Test have shown.
(Decades
# ago someone published a PRN generation scheme in a journal of
statistics where
# a number of PRNGs are activated in a round-robin fashion in order to
obtain
# better statistical properties. The present author later independently
proposed
# in Internet forums to use a similar scheme where the activation of the
# consituent PRNGs is albeit not in a fixed serial order but pseudo-random,
# which is apparently to be preferred for crypto security purposes.)
Note that
# below both baseindex and basebufpt are obtained from bits of the
accumulator
# baseprngsum, which is pseudo-random. For the use of buffers, cf.
Algorithm B
# in ref.[2], p.34.
# [2] D. E. Knuth, The Art of Computer Programming, vol. 2, 3rd ed.,
Reading,
# 1998.
def genbaserandom(veclen):
global tpwn,tpwnm1,shfnrot,tpwnh,tpwnhm1,shfnhrot
global baseprngm,logbasepoolsize,basepoolsize,baseprngpool,baseprngseed
global basebuffer,basebufpt
global baseprn,baseindex,baseprngsum,shfbaseindex,shfbasebegin
random=[]
for i in range(veclen):
k=baseprngsum>>shfbasebegin
basebufpt=k&logbasepoolsize
baseindex=(k>>logbasepoolsize)
baseprn=baseprngseed[baseindex]\
=evalpermpoly(baseprngpool[baseindex],baseprngm,\
baseprngseed[baseindex])
baseprngsum=(baseprngsum+baseprn)&tpwnm1
basebf=basebuffer[basebufpt]
basebuffer[basebufpt]=basebf
random+=[basebf]
return(random)
##### Outputs of genbaserandom() are used to build a pool of PRNGs based on
##### permutation polynomials mod 2**n of degree prngm-1, designated as
##### prngpool, with the help of buildgeneratorpool(). genrandom() then
delivers
##### outputs from this pool, which will be used for the proper
##### encryption/decryption processing of JADE-S.
# Use genbaserandom() to build a pool of PRNGs, designated as prngpool.
def buildgeneratorpool():
global logn,n,np1,nm1,nh,chnum
global prngm,logpoolsize,poolsize,maskpool
global prngpool,prngseed,prngusecount,maxprngusecount
global prn,index,shfindex,shfbegin,prngfeedback
global initialrandompriming,baserandompriming,randompriming
global prngsum,buf1sum,buffer1,buffer2,buf1pt,buf2pt
buildbasegeneratorpool()
# Priming of genbaserandom().
genbaserandom(baserandompriming)
prngpool=[]
prngseed=[]
prngusecount=[]
buffer1=[]
buffer2=[]
for i in range(poolsize):
randtmp=genbaserandom(prngm+3)
permpoly=fullperiodcriteria(randtmp[:-3],prngm)
prngpool+=[permpoly]
prngseed+=[randtmp[-3]]
buffer1+=[randtmp[-2]]
buffer2+=[randtmp[-1]]
prngusecount+=[0]
prn=0
index=0
buf1pt=buf2pt=0
prngsum=buf1sum=0
prngfeedback=0
# Renew the PRNG in the prngpool currently indexed by index using
# genbaserandom(). This function is called by genrandom().
def renewal():
global prngm,logpoolsize,poolsize,maskpool
global prngpool,prngseed,prngusecount,maxprngusecount
global prn,index,shfindex,shfbegin,prngfeedback
randtmp=genbaserandom(prngm)
prngpool[index]=fullperiodcriteria(randtmp,prngm)
# Generate a sequence of PRNs (a list of veclen elements) from the prngpool,
# similar to genbaserandom() but employing two summations (prngsum and
buf1sum)
# and two buffers (buffer1 and buffer2). If prngfeedback is 1, index is
reset
# to a pseudo-random new value. If the usecount of the PRNG at index exceeds
# maxprngusecount, it is renewed with renewal(). User may employ
genrandom(),
# after calling initjades(), to generate PRNs for use in applications
# independent of JADE-S's encryption/decryption. In such cases benefits of
# dynamics/variability could be obtained via appropriate use of the variable
# prngfeedback and the function renewal().
def genrandom(veclen):
global prngm,logpoolsize,poolsize,maskpool
global prngpool,prngseed,prngusecount,maxprngusecount
global prn,index,shfindex,shfbegin,prngfeedback
global prngsum,buf1sum,buffer1,buffer2,buf1pt,buf2pt
random=[]
for i in range(veclen):
k=prngsum>>shfbegin
buf2pt=k&maskpool
k>>=logpoolsize
buf1pt=k&maskpool
if prngfeedback==1:
# index arbitrarily determined by rightmost logn bits of prngsum.
index=prngsum&maskpool
prngfeedback=0
else:
index=(k>>logpoolsize)
if prngusecount[index]>=maxprngusecount:
renewal()
prngusecount[index]=1
else:
prngusecount[index]+=1
prn=prngseed[index]\
=evalpermpoly(prngpool[index],prngm,prngseed[index])
prngsum=(prngsum+prn)&tpwnm1
bf1=buffer1[buf1pt]
buffer1[buf1pt]=prngsum
buf1sum=(buf1sum+bf1)&tpwnm1
bf2=buffer2[buf2pt]
buffer2[buf2pt]=buf1sum
random+=[bf2]
return(random)
##### The user has to initialize the system with initjades() for each run of
##### JADE-S.
# Check the user-given system parameters for conformance to constraints
given
# further above and compute the corresponding values of all system variables
# required for running JADE-S.
def initjades():
global logn,n,np1,nm1,nh,chnum
global tpwn,tpwnm1,shfnrot,tpwnh,tpwnhm1,shfnhrot
global baseprngm,logbasepoolsize,basepoolsize,baseprngpool,baseprngseed
global baseprn,baseindex,baseprngsum,shfbaseindex,shfbasebegin
global initialrandompriming,baserandompriming,randompriming
global prngm,logpoolsize,poolsize,maskpool
global prngpool,prngseed,prngusecount,maxprngusecount
global prn,index,shfindex,shfbegin,prngfeedback
global initvecblocknum,finalvecblocknum,initvector,finalvector
global ciphertextfilemode,formatstr,digitnum,hexnum,base,fileext
global shfchoice
if logn<7 or logn>10 or baseprngm<prngm or logbasepoolsize<7 or\
logbasepoolsize>10 or prngm<3 or logpoolsize<7 or logpoolsize>10 or\
maxprngusecount<20 or initialrandompriming<0 or
baserandompriming<0 or\
randompriming<0 or initvecblocknum<1 or finalvecblocknum<0 or\
ciphertextfilemode<0 or ciphertextfilemode>2:
print("Error in initjades")
exit(3)
n=2**logn
np1=n+1
nm1=n-1
tpwn=2**n
tpwnm1=tpwn-1
shfnrot=n-logn
nh=n//2
tpwnh=2**nh
tpwnhm1=tpwnh-1
shfnhrot=nh-(logn-1)
basepoolsize=2**logbasepoolsize
shfbaseindex=n-logbasepoolsize
shfbasebegin=n-(2*logbasepoolsize)
poolsize=2**logpoolsize
shfindex=n-logpoolsize
shfbegin=n-(3*logpoolsize)
maskpool=poolsize-1
shfchoice=shfindex-1
chnum=n//8
buildgeneratorpool()
# System priming of genrandom(), value arbitrarily determined.
genrandom(3*poolsize)
# User priming of genrandom().
genrandom(randompriming)
# Initialize initvector and finalvector with some arbitrary pseudo-random
# values.
initvector=genrandom(initvecblocknum)
finalvector=genrandom(finalvecblocknum)
if ciphertextfilemode==0:
formatstr="{0:0"+str(n)+"b}"
fileext=".bin"
elif ciphertextfilemode==1:
digitnum=int(math.log(tpwnm1,10))
if 10**digitnum<tpwnm1:
digitnum+=1
formatstr="{0:0"+str(digitnum)+"d}"
base=10
fileext=".txt"
else:
hexnum=2*chnum
formatstr="{0:#0"+str(hexnum+2)+"x}"
base=16
fileext=".txt"
##### The following functions do the proper work of
encryption/decryption using
##### the PRNG genrandom() developed above.
##### enc() and dec() are basic functions that will be used in the
higher level
##### functions encrypt() and decrypt().
##### Filler of user-given plaintext to block boundary is 'z'.
# The elements in nplist are integer values that result from byte-wise
encoding
# of plaintext characters in a block (of chnum characters). We compute
for the
# elements x1, x2, ... xk of the list the following, where the f's are
# invertible non-linear functions, the r's are pseudo-random numbers and the
# assignments are performed sequentially:
# x1 := f1(x1 + x2 ... + xk ^ r1)
# x2 := f2(x1 + x2 ... + xk ^ r2)
# ................
# xk := fk(x1 + x2 ... + xk ^ rk)
# (The scheme, suggested for block encryption processing by the present
author
# recently in a few Internet forums ,is inspired by the iterative
solution of
# systems of linear equations as given in V. N. Faddeeva, Computational
Methods
# of Linear Algebra, p.117, Dover Publ., 1959.) The f's are pseudo-randomly
# selected from the pool of permutation polynomials mod 2**n on which
# genrandom() is based and the r's are obtained from genrandom(). We
employ for
# an f either evalpermpoly() or invpermpoly() depending on the pseudo-random
# value choice. This renders the processing time of encryption and
decryption
# equal, since the inverse computation takes more time. Note that the
# block-chaining effect in the use of the other known common block
ciphers is
# intrinsically present in our scheme, as in every step all blocks are
(equally)
# involved in the argument of f, with the consequence that a far
superior mixing
# together and hence degree of interdependence of all blocks involved during
# processing is achieved. For the purpose of authentication, we append a
# pseudo-random block to the beginning of the sequence of given
plaintext blocks
# such that it now takes the position of x1 in the processing scheme
sketched
# above. It is easily seen that x1 has strong influences on the results
of all
# the other assignments. This implies, conversely, that any manipulation
of the
# ciphertext blocks will with very high probability be reflected in the
changed
# value of x1 (with respect to the one on encryption) obtained on
decryption,
# which means that x1 can serve well as a MAC for authentication. JADE-S
# actually employs, as an enhancement, also an additional pseudo-random
block at
# the end of the given plaintext blocks for authentication and the user may
# further specify that more than one such authentication blocks be used.
See the
# system parameters initvecblocknum and finalvecblocknum. Note that after
# execution of enc() nclist is also available globally (in particular, the
# encrypted finalvector can be obtained with nclist[-finalvecblocknum:]
for use
# as a kind of hash value).
def enc(nplist):
global logn,n,np1,nm1,nh,chnum
global tpwn,tpwnm1,shfnrot,tpwnh,tpwnhm1,shfnhrot
global prngm,logpoolsize,poolsize,maskpool
global prngpool,prngseed,prngusecount,maxprngusecount
global prn,index,shfindex,shfbegin,prngfeedback
global shfchoice
global nclist
nclist=nplist[:]
lenlist=len(nclist)
choice=[]
polyindex=[]
random1=genrandom(lenlist)
# random2 provides the PRNs in the scheme sketched above.
random2=genrandom(lenlist)
for i in range(lenlist):
# Leading bits of random1 provide polyindex and choice.
k=random1[i]>>shfchoice
choice+=[k&1]
polyindex+=[k>>1]
sigmax=0
for i in range(lenlist):
sigmax+=nclist[i]
sigmax=sigmax&tpwnm1
for i in range(lenlist):
if choice[i]==0:
tt=evalpermpoly(prngpool[polyindex[i]],prngm,sigmax^random2[i])
else:
tt=invpermpoly(prngpool[polyindex[i]],prngm,sigmax^random2[i])
sigmax=(sigmax-nclist[i]+tt)&tpwnm1
nclist[i]=tt
return(nclist)
# Inverse of enc().
def dec(nclist):
global logn,n,np1,nm1,nh,chnum
global tpwn,tpwnm1,shfnrot,tpwnh,tpwnhm1,shfnhrot
global prngm,logpoolsize,poolsize,maskpool
global prngpool,prngseed,prngusecount,maxprngusecount
global prn,index,shfindex,shfbegin,prngfeedback
global shfchoice
nplist=nclist[:]
lenlist=len(nclist)
choice=[]
polyindex=[]
random1=genrandom(lenlist)
random2=genrandom(lenlist)
for i in range(lenlist):
k=random1[i]>>shfchoice
choice+=[k&1]
polyindex+=[k>>1]
sigmax=0
for i in range(lenlist):
sigmax+=nplist[i]
sigmax=sigmax&tpwnm1
for i in range(lenlist-1,-1,-1):
if choice[i]==0:
tt=invpermpoly(prngpool[polyindex[i]],prngm,nplist[i])
else:
tt=evalpermpoly(prngpool[polyindex[i]],prngm,nplist[i])
vv=(sigmax-nplist[i])&tpwnm1
nplist[i]=((tt^random2[i])-vv)&tpwnm1
sigmax=(vv+nplist[i])&tpwnm1
return(nplist)
##### pack() and unpack() are auxiliary functions needed in encrypt() and
##### decrpyt().
# 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)
##### encrypt() encrpyts the user-given plaintext and writes the
ciphertext into
##### a file with name ciphertextname and file extension bin/txt
according to
##### ciphetextfilemode=0/1/2, i.e. whether the file is to be in Python
specific
##### format or consisting of a list of decimal or hexadecimal integers.
##### decrpyt() decrypts a file thus obtained back into the plaintext.
# If the length of the plaintext string 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
initvector
# and finalvector, generated in initjades(), are appended to the
beginning and
# and end of nplist respectively for purpose of authentication in decrypt().
# nplist is encrypted with enc() to nclist and is written to ciphertextfile
# according to the system parameter ciphertextfilemode. (If the plaintext is
# given in an external file, see comments in Ex.1 of Sec.2 on how to
read it in
# for processing.)
def encrypt(plaintext,ciphertextname):
global logn,n,np1,nm1,nh,chnum
global tpwn,tpwnm1,shfnrot,tpwnh,tpwnhm1,shfnhrot
global initvecblocknum,finalvecblocknum,initvector,finalvector
global ciphertextfilemode,formatstr,digitnum,hexnum,base,fileext
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 satisfying processing requirement")
k=0
nplist=[]
for i in range(len(plaintext)//chnum):
nplist+=[pack(plaintext[k:k+chnum])]
k+=chnum
# Append initvector and finalvector.
nplist=initvector+nplist+finalvector
lenlist=len(nplist)
# Encryption processing with the function enc().
nclist=enc(nplist)
# Output to ciphertextfile.
ciphertextfile=ciphertextname+fileext
if ciphertextfilemode==0:
fp=open(ciphertextfile,"wb")
pickle.dump(nclist,fp)
print("File is not printable and has to be sent as such")
else:
fp=open(ciphertextfile,"w")
for i in range(lenlist):
fp.write(formatstr.format(nclist[i])+"\n")
if ciphertextfilemode==1:
print("File consists of",lenlist,"integers (maximum size for each",\
"integer:",digitnum,"digits)")
else:
print("File consists of",lenlist,"integers (maximum size for each",\
"integer:",hexnum,"hexadecimal\ndigits with prefix '0x')")
fp.close()
print("Ciphertext has been written to file:",ciphertextfile)
print()
# Inverse of encrypt(). If the first initvecblocknum and last
finalvecblocknum
# elements of nplist obtained are identical to initvector and finalvector
# respectively, the authentication (integrity check) is ok. An integer to
# characters conversion is then done with unpack(). The recovered plaintext
# string is returned. Note that this may contain filling characters 'z'
added
# by encrypt(). (If the plaintext is to be written out to an external
file, see
# comments in Ex.1 of Sec. 2 on how to do that.)
def decrypt(ciphertextname):
global logn,n,np1,nm1,nh,chnum
global tpwn,tpwnm1,shfnrot,tpwnh,tpwnhm1,shfnhrot
global initvecblocknum,finalvecblocknum,initvector,finalvector
global ciphertextfilemode,formatstr,digitnum,hexnum,base,fileext
ciphertextfile=ciphertextname+fileext
print("Processing file",ciphertextfile)
if ciphertextfilemode==0:
fp=open(ciphertextfile,"rb")
nclist=pickle.load(fp)
else:
fp=open(ciphertextfile,"r")
ncliststrlist=fp.read().split()
nclist=[]
for i in range(len(ncliststrlist)):
nclist+=[int(ncliststrlist[i],base)]
fp.close()
lenlist=len(nclist)
# Decryption processing with the function dec().
nplist=dec(nclist)
# Check whether initvector and finalvecter are correctly recovered and
# exclude them from nplist.
authen1=nplist[:initvecblocknum]
if finalvecblocknum>0:
authen2=nplist[-finalvecblocknum:]
nplist=nplist[initvecblocknum:-finalvecblocknum]
else:
authen2=[]
nplist=nplist[initvecblocknum:]
if authen1!=initvector or authen2!=finalvector:
print("######### Authentication (intgegrity check) failed #########")
exit(5)
plaintextrecovered=""
for i in range(len(nplist)):
plaintextrecovered+=unpack(nplist[i],chnum)
print("Plaintext has been recoverd from",ciphertextfile)
print("Authentication (integrity check) ok")
print()
return(plaintextrecovered)
################################################################################
################################################################################
######### Section 2. Examples of Applications of JADE-S.
##### The following code does not belong to JADE-S in the proper sense
but serves
##### only to illustrate with examples further below how the user may apply
##### JADE-S.
# Maurer's Universal Test, see [3].
# [3] J-S. Coron, D. Naccache, An Accurate Evaluation of Maurer's
Universal Test.
# http://www.jscoron.fr/publications/universal.pdf
qq=2560
qqp1=qq+1
kk=256000
qqkkp1=qq+kk+1
def maurertest(bb):
global qq,qqp1,kk,qqkkp1
eftu=7.1836656
# y1 and y2 are for rho=0.01 and rho=0.001 respectively.
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(h,gg):
global logn,n,np1,nm1,nh,chnum
global qq,qqp1,kk,qqkkp1
if h*chnum<qqkkp1:
print("Error in maurertestresult")
exit(6)
bb=[0 for k in range(h*chnum)]
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\nand should lie between the other two values):")
print(t11,tu,t12)
################################################################################
# User specified system parameters and key materials are assumed to be the
# following for the examples below:
logn=8
baseprngm=4
logbasepoolsize=7
prngm=3
logpoolsize=8
maxprngusecount=50
initialrandompriming=10
baserandompriming=20
randompriming=50
initvecblocknum=1
finalvecblocknum=1
ciphertextfilemode=0
# keydata is a list of 2 elements of n/2 bits, seeddata 1 element of n/2
bits
# (here they are arbitrary apparently 'non-random' values chosen for the
# convenience of illustration only).
keydata=[ 0x11111111111111111111111111111111,\
0x22222222222222222222222222222222 ]
seeddata= 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
###################################################################################
# Example 1. Normal encryption and decryption.
print()
print("Example 1:")
print()
print("Normal encryption and decryption")
print()
# We assume the sender has the following character string pt as plaintext:
pt="Was sich ueberhaupt sagen laesst, laesst sich klar sagen; und "\
"wovon man\nnicht reden kann, darueber muss man schweigen."
print("Original plaintext:")
print(pt)
print()
# The sender intializes the system,
initjades()
# encrypts pt, obtaining a ciphertext file ciphermessage.bin
(ciphertextfilemode
# is chosen above to be 0, thus the file cannot be printed and has to be
sent as
# such to the recipient.)
encrypt(pt,"ciphermessage")
# and sends it to the recipient.
# The recipient initializes the system,
initjades()
# decrypts the received ciphertextfile ciphermessage.bin
(ciphertextfilemode is
# 0 as chosen by the sender), assigns the result to pt1 and prints it out.
# Result of authentication check will be automatically reported.
pt1=decrypt("ciphermessage")
print("Plaintext recovered:")
print(pt1)
# If the plaintext message to be encrypted is in an external file (last line
# preferably without 'return'), it can be read into pt with:
# f=open("messagetobesent.txt","r"))
# pt=f.read()
# f.close()
# Similarly the received plaintext message (after decryption is
performed) can
# be stored into an external file from pt1 with:
# f=open("messagereceived.txt","w")
# f.write(pt1)
# f.close()
# Example 2: Measuring time of encryption/decryption.
print()
print("Example 2:")
print()
nnumb=10000
pt=""
for i in range(nnumb):
pt+="a"
print("Measuring time of encryption/decryption of a message of",nnumb,\
"characters")
print()
start=time.clock()
initjades()
encrypt(pt,"ciphertext")
print("encrypt time:",time.clock()-start,"sec")
start=time.clock()
initjades()
pt1=decrypt("ciphertext")
print("decrypt time:",time.clock()-start,"sec")
# Example 3. Perform Maurer's Universal Test on genrandom().
# User may like to employ JADE-S's function genrandom() for other
usages, e.g.
# for common numerical computations or obtaining PRNs (eventually in
combination
# with other sources) to generate key streams for other encryption
schemes, and
# may want to know their statistical properties. Hence the present example.
print()
print("Example 3:")
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).
print("Testing pseudo-radnom numbers generated by genrandom()")
print()
# Initialize the system.
initjades()
# Determine number of PRNs (words) needed for the test.
h=qqkkp1//chnum
if h*chnum<qqkkp1:
h+=1
# Obtain the PRNs.
gg=genrandom(h)
#Perform the test.
maurertestresult(h,gg)
# Example 4. Perform Maurer's Universal Test on ciphertext obtained from
# a plaintext of the form "aaa........".
# This is intended to show the randomness of the result of encrypting an
# absolutely non-random plaintext. (A perfectly random plaintext on the
other
# hand would have had the same frequency, 1/26, of occurence for all
characters
# of the alphabet.)
print()
print("Example 4:")
print()
print("Testing the result of encrypting an absolutely non-random plaintext")
print()
# Since ciphertext in decimal integers will be needed, we have to reset the
# parameter ciphertextfilemode to 1 (it was chosen above to be 0).
ciphertextfilemode=1
# Initialize the system.
initjades()
# Determine number of words of the form "aaa..." needed for the test.
h=qqkkp1//chnum
if h*chnum<qqkkp1:
h+=1
# Obtain the plaintext.
r=""
for i in range(chnum):
r+="a"
pt=""
for i in range(h):
pt+=r
# Perform encryption.
encrypt(pt,"aaamessage")
# Obtain the corresponding ciphertext (decimal integers in nclist).
ciphertextfile="aaamessage.txt"
fp=open(ciphertextfile,"r")
ncliststrlist=fp.read().split()
nclist=[]
for i in range(len(ncliststrlist)):
nclist+=[int(ncliststrlist[i])]
# Perform the test.
maurertestresult(h,nclist)
################################################################################
################################################################################
######### Section 3. Miscellaneous Notes.
# The code of JADE-S assumes that the sender supplies his message in a
variable
# of Python and the recipient retrieves the same also from a variable of
Python.
# See commments in Sec.2. Ex.1 for situations where the sender has his
message
# in a file or the recepient needs to obtain the same in a file.
# In case arbitrary binary information is to be processed, this may be
supplied
# in n-bit blocks as elements of the list nplist to be encrypted to the list
# nclist by JADE-S. The code modification necessary for this is evident, see
# encrypt() and decrypt().
# Users employing non-English keyboards may obtain with input of non-English
# symbols a message from Python: "... IDLE will convert it to UTF-8".
Click on
# OK in this case. Though not tested, there seems to be no reason why JADE-S
# shouldn't work also with non-European languages. Note that JADE-S
needs Python
# 3.x.
# To obtain a binary file of m PRNs generated by genrandom() one could use:
# initjades()
# g=genrandom(m)
# fp=open("mfile.bin","wb")
# for i in range(m):
# fp.write(g[i].to_bytes(chnum,byteorder='big'))
# fp.close()
# For a chosen block size of n bits, the key material (keydata and
seeddata) has
# a total size 1.5*n bits. However, if keydata (of n bits) has enough
entropy to
# defeat brute-forcing, then seeddata evidently need not necessarily be
secret.
# (That for the larger values of n allowed in JADE-S the key material
generally
# needs only a correspondingly lower entropy density is clearly of
advantage in
# practice.) In fact it could be favourable in practice for the key
management
# work to have a well-guarded keydata of sufficient entropy and use it
for some
# longer time period, while the seeddata is always variable accross the
sessions
# and depends in some rather simple way on certain session specific data,
# including date, time, message serial number, etc. One could use JADE-S
and a
# master-keydata (with the corresponding seeddata e.g. involving a certain
# month, if the keydata to be used in that month is to be obtained) to
process
# an agreed upon text and use the value of nclist[-finalvecblocknum:]
(see enc()
# with the system parameter finalvecblocknum set to the desired length)
after
# execution of encrypt() to supply the keydata needed, since it is by
nature a
# good hash value of the plaintext for fixed values of system parameters and
# key materials employed. Further there could be a hierarchy of
master-keydata,
# depending on usage, validity time, etc. and stemming all from a grand
# master-keydata in the way described. A master-keydata could be
obtained by our
# targeted users, if necessary, through throwing 8-sided dice, this
being only a
# one-time drudgery, and exchanged with the communication partners via
# steganography, employing the present author's EMAILSTEGANO (available from
# http://s13.zetaboards.com/Crypto/topic/6939954/1/). (With 6-sided dice one
# could get 2 bits from [1,4] and 1 bit from [5,6].) For gathering
entropy from
# hardware, see
https://www.irisa.fr/caps/projects/hipsor/old/files/HAVEGE.pdf
# Evidently there can exist no single general recipe of best performing key
# management that is applicable in all different concrete situations in
# practice and the user must exercise his own prudence, vigilence and
ingenuity.
# JADE-S treats the bits of an entire block of e.g. 256 bits as a single
integer
# and directly performs certain arithmetic and bit operations on it. See
enc()
# and dec(). This incurs essential trade-offs in terms of efficiency.
Further,
# Python code is not compiled but interpreted and in the present coding
priority
# has always been given to readability rather than to runtime efficiency.
# Benchmarking shows nevertheless that a message of 10000 characters,
which is
# sufficiently long for normal communications of common private persons
(who are
# our targeted users), can be processed on a common modern PC (with all
variants
# of the system parameters logn and logpoolsize) in less than 0.5 sec.,
which is
# clearly acceptable.
# JADE-S uses a scheme of PRN generation that distinguishes itself from the
# common schemes in that 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 sophisticated way to further
block
# any sequence prediction attempts of the analyist, see genrandom().
genrandom()
# accepts feedback signals so that functions employing it could through
feedback
# (via setting the global variable prngfeedback) dynamically influence the
# sequence of PRNs obtained and thus indirectly achieve dynamics in the
results
# computed by the functions. (JADE-S doesn't make use of prngfeedback,
though.)
# Use-counts are kept so that one can limit the life-time of the PRNGs
in the
# pool to deliberately enhance the hardness of analysis. Note also that in
# JADE-S all PRNs are only indirectly used so that these are never directly
# accessible to the analyst to work on. (Contrast this with the common
stream
# encryption).
# The present author experimentally found that the use of an accumulator
# improves the statistical qualities of the output of the PRNGs of the
type used
# in JADE-S. The combination of an accumulator and a buffer (which is also
# beneficial statistically) has the additional effect of rendering the
analysis
# hard. See genbaserandom() and genrandom(). In genrandom(), which
ultimately
# delivers the PRNs for application use, a cascade of an accumulator and two
# buffers is employed for achieving superior performance.
# JADE-S is very richly parametrized, in particular the user can choose
block
# lengths ranging from 128 to 1024 bits. Check for authentication
(integrity) is
# natively integrated and always performed, with the number of
authentication
# blocks arbitrarily specifiable by the user. While the common block ciphers
# have a number of rounds in order to attain their desired strength,
JADE-S has
# only one round, iterations of our kind of computations (see enc()) being
# considered superfluous for gaining security.
# JADE-S shares with JADE 2.0.3 the same elaborate PRN generation scheme
but is
# simpler in design concepts in the proper encryption/decryption processing
# part. It is thus easier to be understood and consequently to be preferred.
# It is important to be always conscious of the fact that in general a
cipher is
# only one component in the entire chain of computer processing that is
# destined/meant to protect user's privacy and that unfortunately a chain is
# only as strong as its weakest link. In fact IT security risks are
omnipresent.
# Thus, depending on the actual environment, one may have to consider
also, for
# instance, human factors and potential backdoors in proprietary
# (non-opensource, blackbox) software that are involved in the
management and
# processing of digital signatures, sophisticated malware (e.g. Stuxnet and
# its descendents, trojans introduced by governmental agencies) that steal
# plaintext files, WLAN insecurities, key loggers, Tempest attacks, etc.
etc.
# The book Security Engineering of R. Anderson covers a lot of practical IT
# security issues. There is, among others, an entire chapter on emission
# security.
# Note that backdoors (in both software and hardware) implanted by mafias or
# foreign secret agencies may remain dormant (thus undectable from the
outside)
# until the moment arrives that is favourable for the bad guys to create a
# devasticating chaos (via either "time-bomb" or, more sophistically, remote
# commands) to e.g. the critical infrastructures of a nation. In his book
# Cyberwar (in German, ISBN 978-3-941841-23-9) S. Gayken convincingly
argues for
# "Entnetzung" ("de-networking") as a necessary defense measure for
protecting
# the computer networks of sensible critical systems. (He also writes:
# "Commercially produced components cannot be trusted. Especially not,
if that's
# from global enterprises or firms from abroad".) By simple logic, if
one's home
# network is "really" to be well protected, then the same rigorous measure
# certainly should be taken. That would imply in our understanding
having one
# extra computer that is permanently isolated from the Internet and
transferring
# data to and from it via printouts/scanning (since USB devices are
presumably
# hard to to be protected from malware infections) and taking care of
risks from
# eventually conceivable emission attacks from the environment as well.
# That information privacy needs active and careful protection measures
is once
# again stressed by recent news from The Guardian about Prism, Tempora, etc.
# Another encryption software of the present author is SHUFFLE2,
obtainable from
# http://s13.zetaboards.com/Crypto/topic/6925232/1/
Back to comp.programming | Previous | Next | Find similar | Unroll thread
JADE-S, a block cipher with authentication (integrity check) based on use of permutation polynomials mod 2**n Mok-Kong Shen <mok-kong.shen@t-online.de> - 2013-06-24 20:35 +0200
csiph-web