Date: Jul 23, 2013 2:36 PM
Author: Mok-Kong Shen
Subject: NEOCLASSIC, An encryption scheme based largely on use of well-known<br> classical crypto techniques


Attached below is the code of my encryption software NEOCLASSIC
Version 1.1.1. For comments and critiques I should be very grateful.
Currently there is a challenge competition for it with a prize to
be won, see:

http://s13.zetaboards.com/Crypto/topic/7078836/1/

M. K. Shen

---------------------------------------------------------------------

# NEOCLASSIC, A simple versatile encryption scheme (with authentication)
based
# largely on the straightforward use of well-known classical crypto
techniques.



# Prologue:

# Having during the years designed a small number of encryption
algorithms, I
# am increasingly convinced that no practical encryption algorithm could be
# "absolutely" secure in the "strict" sense of the word and hence the
quest for
# such is futile from the very beginning. In fact, there exist rather strong
# critiques [1] on the so-called proofs of crypto-security. Thus what
seems to
# be desirable in practice will be the availability of a sufficiently large
# number of diverse kinds of sufficiently strong (preferably also easy for
# the common users to understand, verify and use) encryption algorithms
that,
# when appropriately employed (eventually in combinations, i.e. with
different
# ciphers working in tandem), are intuitively safe with a satisfactory
"factor
# of safety" in relationship to the power of attacks that one's opponent
# "presumably" has in hand. One is IMHO therefore defacto always doing some
# gambling in the crypto practice. But this is actually also the case
everywhere
# in real life, e.g. in the practice of medicine. In releasing the present
# software to the public, I am certainly conscious that it is definitely not
# absolutely secure but can only be hopefully practically secure, namely
when
# the user chooses sufficiently conservative values for its system
parameters.
# A deplorable dilemma I am facing as designer is however that, in
distinction
# to e.g. pharmacy, there are in crypto barely any practical tests of the
# nature of those pertaining to drugs to determine the right dosage for
# administration to the patients. Since I could hardly provide any sensible,
# ubiquitously valid, guidances to the selection of the system parameters, I
# am obliged to warn the potential users of my software at the very
beginning:
# The user is himslef responsible for the appropriate choice of the system
# parameters of this software in any actual applications. On the other hand,
# it is intuitively clear that higher values of the system parameters chosen
# should, in general, imply higher security, assuming that the user also
take
# sufficient care to ensure the fulfillment of all other security measures
# that are required in practice of the applications in which NEOCLASSIC is
# selected to be utilised.

# [1] N. Koblitz, The uneasy relationship between mathematics and
cryptology.
# Notices AMS, vol.54, 2007, pp.972-979, 1454-1456, vol.55, 2008, pp.6-7.


# NEOCLASSIC is a Python code for encryption processing that performs an
# arbitrary user-chosen number of rounds of pseudo-random transposition and
# substitution determined by a user-given session-key utilizing Python's
# built-in pseudo-random number generator (PRNG). Excepting the use of
PRNG (and
# the use of computer), everything in NEOCLASSIC is straightforward
application
# of well-known techniques in classical cryptography, hence the name of the
# scheme. It is the latest member of a set of (to date) 4 members of
encryption
# algorithms first published by the present author in
# http://s13.zetaboards.com/Crypto/index/ (the other 3 are JADE-S, JADE,
# SHUFFLE2). With appropriate setting of parameters, its strength is
considered
# to be comparable to the other members of the set. It may however be
preferred
# by the user because of the comparatively smaller number of its code
lines (95
# Python code lines) and consequently presumably also less time required to
# understand its design and verify the coding for correctness and
freedom from
# malicious backdoors. In fact, NEOCLASSIC has been developed primarily
in view
# of the evidently enhanced needs of the common people to choose and employ
# sufficiently strong encryption software to protect the privacy of their
# innocent communications against the appallingly huge-scale international
# secret surveillance done by certain mighty countries of the world as
recently
# revealed by the apparently highly convincing reports of the British
newspaper
# The Guardian.

# NEOCLASSIC highly exploits the benefits of dynamics/variability, which the
# present author repeatedly stressed in a number of Usenet groups and which
# seem to be largely ignored in the design of most modern ciphers.

# Details of how the code parts work are given in the comment lines
preceding
# the respective functions further below. Here we present for the
convenience of
# those users who may prefer to postpone a detailed examination of the
code for
# a later time a brief sketch of what is in essence done in one round on
# encryption, namely in the function process(), neglecting, initially,
for ease
# of explanation, the authentication block:
#
# Perform a pseudo-random transposition of the given plaintext characters.
#
# With a variable sigma (initialized pseudo-randomly) do to each plaintext
# character the following:
#
# 1. Find the index (idx) of that plaintext character in the alphabet.
# 2. Generate a pseudo-random number (rr).
# 3. Determine the "intermediate" ciphertext character corresponding
to the
# index gg in the alphabet, with gg=(idx+sigma+rr) (modulo alphabet
# size), i.e. one does a "shift" of amount sigma+rr as is done in the
# ancient Caesar cipher (our shift is albeit not a constant value but
# pseudo-random).
# 4. Update sigma according to sigma=sigma+idx (modulo alphabet
size), i.e.
# sigma gets an addend idx, which is a value (dynamically)
dependent on
# the plaintext character that has just been processed.
#
# Perform a mono-alphabetical substitution on all the intermediate
ciphertext
# characters, yielding the ciphertext characters (to be similarly
processed in
# the next following round, if there is one).
#
# For purpose of authentication we extend the user-given plaintext with
a number
# of characters that are pseudo-randomly generated (termed the
authentication
# block). It is the thus extended plaintext that is actually subjected
to the
# kind of processing described above. Due to sigma, the processing of one
# character has influence on the processing of the next following one. Thus,
# additionally owing to the transpositions done in the number of rounds, the
# final ciphertext resulted is such that each character is highly correlated
# (the very opposite of being independent) with all the others. It is not
# difficult to see that, conversely, in case the ciphertext characters that
# correspond to the user-given plaintext are somehow manipulated/altered
by the
# opponent, the authentication block recovered on decryption by the
recipient
# will (with a very high probability) not be identical to the authentication
# block that the sender generates on encryption. In this way we achieve our
# intended goal of authentication (integrity check), the necessity of which,
# we note, is often neglected/underestimated in practical applications of
# cryptography.

# The function to be invoked by the user for encryption/decryption is
# neoclassic(). An example given further below illustrates its usage.

# Competition with established modern ciphers in processing speed is
from the
# very beginning not a design goal of NEOCLASSIC (since message volumes of
# communications of common people (who are our targeted users) are as a rule
# fairly limited anyway and hence the difference in processing times
would be
# entirely negligible) but rather in ease of code understanding and
verification
# and in flexibility (of specification of system parameters, note also
that the
# length of sessionkey is not fixed) and ease of use to perform
# encryption/decryption of intuitively/presumably (since we can offer no
# crypto-"proof") equivalent, if not superior, strength (depending on
entropy
# of sessionkey and choice of system parameters).

# Although the author has only tested (with Python 3.3.2) the software
on texts
# in English, there seems to be no reason why NEOCLASSIC shouldn't well
function
# on texts in other languages that have an alphabet. For Chinese, use of the
# Unicode or telegraphic codes to transcribe the logograms into decimal
digits
# and an alphabet for English appears to be a viable possibility for
employing
# NEOCLASSIC in practical applications.

# It may be noted that outputs of Python't built-in PRNG are only used
# indirectly and not directly as would have been the case for stream
encryption
# processing. (In stream encryption, outputs of a PRNG are xor-ed with the
# plaintext characters to result in the ciphertext characters, such
that, if a
# segment of the plaintext happens to be known to the analyst via some
means, he
# would know the corresponding outputs of the PRNG with which he could then
# eventually manage to determine the parameters of the PRNG and thus to
decrypt
# the rest of the ciphertext.) Further, there is processing-dependent
# dynamics/variability provided by the feedback operations (skipping of PRNs
# output from the PRNG, see the function process()). Thus the question
# concerning extremely high quality of the PRNG being used isn't a
critical one
# to be examined for applications of NEOCLASSIC in practice.

# Communication partners should ensure having installed the same version of
# Python, since NEOCLASSIC depends on Python's built-in PRNG. We like to
mention
# that, besides the evident requirement of keeping session-keys (and
preferably
# also the chosen system parameters) secret, it may be under circumstances
# prudent (because of possible malware infections) to do
encryption/decryption
# processing exclusively on physically secure computers isolated from the
# Internet and transfer the processed materials manually, i.e. on papers, to
# computers connected to the Internet, noting possible insecurity of USB
sticks.
# Risks of electromagnetic emanations may eventually have to be
considered as
# well (see R. Anderson, Security Engineering, chap. 15, Emission Security).


# Version 1.1.1, released on 15.07.2013. Comment lines updated on
18.07.2013.


# Code lines of documents with the same version number are always identical.
# There may be interim modifications of comment lines. The most recent
document
# of NEOCLASSIC can be obtained from
# http://s13.zetaboards.com/Crypto/topic/7077275/1/ or from the author.


# 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
# NEOCLASSIC'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



# Loading a system module of Python that provides functionalities of its
# built-in PRNG.

import random


# The following string defines the alphabet to be used by NEOCLASSIC and
may be
# arbitrarily modified (altered, shortened or lengthened) by the user, if
# needed. Plaintext may only use a subset of alphabet, but ciphertext
will as
# a rule nonetheless involve the entire set of characters in alphabet.
If space
# is included in alphabet, user should note that the last character of the
# ciphertext obtained may happen to be a space and consequently could be
easily
# overlooked on subsequent handling of the ciphertext.

alphabetstr="ABCDEFGHIJKLMNOPQRSTUVWXYZ"\
"abcdefghijklmnopqrstuvwxyz"\
"0123456789()-?/=%&"

# Alphabet defined.
alphabet=list(alphabetstr)

# Alphabet size.
lenalpha=len(alphabet)


# Perform one round of pseudo-random transposition and substitution on
# textstring with Python's built-in PRNG seeded by roundkey.
Transposition is
# done in the straightforward classical sense by Python's function
shuffle() on
# textstring. Substitution is done in two stages. First, each
character's index
# in alphabet is added by sigma and a pseudo-random value from Python's
built-in
# PRNG (modulo alphabet size). This kind of addition goes back to the
ancient
# Caesar cipher in classical cryptography. The variable sigma is initialized
# with a pseudo-random value from Python's built-in PRNG and updated
with the
# index of the character being processed as the addend, thus resulting in
# processing-dependent dynamics/variability which is beneficial for security
# (albeit lacking in most other modern ciphers). Then there is a global
# substitution with Python's function translate(), with a substitution
alphabet
# determined by Python's function shuffle(). This is mono-alphabetical
# substitution in the straightforward classical sense. Note that the varying
# value of sigma has the effect of block-chaining known in modern block
# encryption and is thus of particular significance to the performance of
# authentication achieved via the authentication block (i.e. the MAC in
modern
# crypto terminology). If the updated sigma equals feedbackval, then the
next
# PRN from the PRNG is skipped. This feedback to the PRNG provides further
# dynamics/variability that renders the cryptanalysis by the opponent hard.
# process() is called by neocalssic() (which is directly invoked by the
user)
# numround times.

def process(roundkey,textstring,feedbackval,kn):
global alphabet,lenalpha,lentext
lenalpham1=lenalpha-1
# Seed the built-in PRNG with roundkey.
random.seed(roundkey)
# Generate cipheralphabet.
cipheralphabet=alphabet[:]
random.shuffle(cipheralphabet)
cipheralphabetstr="".join(cipheralphabet)
# Generate index sequence for performing transposition.
ciphersequence=[i for i in range(lentext)]
random.shuffle(ciphersequence)
# Initialize sigma.
sigma=random.randint(0,lenalpham1)
if kn==0:
# Begin of encryption processing for this round:
newstring=""
for i in range(lentext):
# This does transposition of the characters of textstring.
tt=textstring[ciphersequence[i]]
# This does a substitution of the individual character.
idx=alphabet.index(tt)
rr=random.randint(0,lenalpham1)
gg=(idx+sigma+rr)%lenalpha
newstring+=alphabet[gg]
# Update sigma.
sigma=(sigma+idx)%lenalpha
# Skip one PRN pseudo-randomly if sigma=feedbackval (this has a
probability of
# 1/lenalpha in case the characters in textstring are uniformly
distributed).
if sigma==feedbackval:
skipped=random.randint(0,lenalpham1)
# This does a global substitution (here from plaintext alphabet to
ciphertext
# alphabet, a mono-alphabetical substitution).
transtab=newstring.maketrans(alphabetstr,cipheralphabetstr)
newstring=newstring.translate(transtab)
else:
# Begin of decryption processing for this round (reversal of encryption
# processing):
# This reverses the global substitution.
tmpstring=textstring[:]
transtab=tmpstring.maketrans(cipheralphabetstr,alphabetstr)
tmpstring=tmpstring.translate(transtab)
newlist=["" for i in range(lentext)]
for i in range(lentext):
# This reverses the substitution of the individual character.
tt=tmpstring[i]
gg=alphabet.index(tt)
rr=random.randint(0,lenalpham1)
idx=(gg-sigma-rr)%lenalpha
newlist[ciphersequence[i]]=alphabet[idx]
# Update sigma.
sigma=(sigma+idx)%lenalpha
# Skip one PRN pseudo-randomly if sigma=feedbackval.
if sigma==feedbackval:
skipped=random.randint(0,lenalpham1)
newstring="".join(newlist)
# Return the result of processing of this round.
return(newstring)


# sessionkey may be a decimal or hexadecimal integer or a character
string of
# sufficient entropy. sessionkey is used as a seed for Python's built-in
PRNG to
# generate pseudo-random roundkeys that each has roundkeybits number of
bits for
# use as seeds for Python's built-in PRNG in the numround rounds of
# transposition and substitution done on inputstring by the function
process().
# It also used to generate feedbackvals, an array of PRNs used for feedback
# operations done in the function process(). sessionkey is preferably to be
# chosen different for different sessions (it may consist e.g. of a
fixed secret
# part of sufficient entropy and a variable session-dependent part (not
# necessarily to be guarded secret) that is determined from e.g. date, time,
# message number etc.). roundkeybits is to be appropriately chosen in
relation
# to entropy of sessionkey, i.e. too large a value for roundkeybits
relative to
# entropy of sessionkey doesn't make much sense in practice.
authenblocklen is
# the length of authenblock, which is a block of pseudo-random characters
# automatically generated by NEOCLASSIC to be processed together with
# inputstring to serve the purpose of authentication (integrity check),
see also
# comment to the function process() above. In general, the larger the
value of
# authenblocklen chosen, the higher will be the effectiveness of
authentication.
#
# neoclassic() is the function to be directly invoked by the user for
encryption
# and decryption. On encryption neoclassic() generates materials that
are needed
# to call process() numround times to treat textstring, which is the
inputstring
# extended by authentication block, and performs a final transposition
of the
# characters (which renders the scheme more symmetrical with respect to
# transpositions and should also enhance its strength). On decryption these
# steps are reversed in the opposite order, with check of correctness of the
# decrypted authentication block for ensuring the integrity of the
ciphertext
# during transmission from the sender to the recipient. Note that, as
stated in
# the prologue, the appropriate choice of the parameter values is user's
# responsibility and so there is no check excepting the trivial one for
# authenblocklen.
#
# Encryption: set kn=0.
# Decryption: set kn=1.

def
neoclassic(sessionkey,numround,roundkeybits,authenblocklen,inputstring,kn):
global alphabet,lenalpha,lentext
if authenblocklen<=0:
print("Error: No authentication block")
exit(1)
lenalpham1=lenalpha-1
for i in range(len(inputstring)):
if inputstring[i] not in alphabet:
print("Error: inputstring contains character",\
inputstring[i],"which is not in the defined alphabet")
exit(1)
if kn==0:
lentext=len(inputstring)+authenblocklen
else:
lentext=len(inputstring)
# Seed the built-in PRNG with sessionkey.
random.seed(sessionkey)
# Generate the arrays roundkeys and feedbackvals.
roundkeys=[]
feedbackvals=[]
for i in range(numround):
roundkeys+=[random.getrandbits(roundkeybits)]
feedbackvals+=[random.randint(0,lenalpham1)]
# Generate authentication block.
authenblock=""
for i in range(authenblocklen):
authenblock+=alphabet[random.randint(0,lenalpham1)]
# Generate index sequence for performing the final transposition (on
# encryption).
finaltranspsequence=[i for i in range(lentext)]
random.shuffle(finaltranspsequence)
if kn==0:
# Encryption processing.
# Append authentication block.
textstring=inputstring+authenblock
# Perform numround rounds of encryption processing.
for i in range(numround):
textstring=process(roundkeys[i],textstring,feedbackvals[i],kn)
# Perform final transposition.
tmpstring=textstring[:]
textstring=""
for i in range(lentext):
textstring+=tmpstring[finaltranspsequence[i]]
else:
# Decryption processing:
# This reverses the final transposition done on encryption.
tmpstring=inputstring[:]
newlist=["" for i in range(lentext)]
for i in range(lentext):
newlist[finaltranspsequence[i]]=tmpstring[i]
textstring="".join(newlist)
# Perform numround rounds of decryption processing.
for i in range(numround-1,-1,-1):
textstring=process(roundkeys[i],textstring,feedbackvals[i],kn)
# Separate out the (recovered) authentication block from the (recovered)
# plaintext.
lastblock=textstring[-authenblocklen:]
textstring=textstring[:-authenblocklen]
# The recovered authentication block (lastblock) should be identical to the
# authentication block (authenblock) that is obtained from computing with
# sessionkey above (similar to what sender does on encryption).
if lastblock==authenblock:
print("Authentication (integrity check) o.k.")
else:
print("Authentication (integrity check) failed ***************")
exit(2)
# Return the processing result.
return(textstring)



# The following is an (abitrarily chosen, toy) example illustrating how
to use
# NEOCLASSIC to do encryption/decryption.

# The alphabet (same for sender and recipient) has been defined further
above.

# Sender defines the session-key and the other parameters and encrypts the
# plaintext pt to ciphertext ct for transmission to the recipient. (pt
chosen
# here uses only a subset of the alphabet defined further above. This
limitation
# is arbitrarily done by the sender in this particular example case and
is not
# a necessary one.) Concerning choice of system parameters, see prologue
at the
# beginning of the document.

sessionkey="sodifreruvsfuvherudf0607052"
numround=3
roundkeybits=128
authenblocklen=15

print("Sender side:")
pt="nowisthetimeforallmentocometotheaidoftheircountry"
print("pt: ",pt)
ct=neoclassic(sessionkey,numround,roundkeybits,authenblocklen,pt,0)
print("ct: ",ct)
print()

# Recipient defines the same (agreed-upon with sender) session-key and the
# other parameters and decrypts the received ciphertext ct to plaintext pt1.
# NEOCLASSIC automatically verifies the correctness of the
authentication block
# recovered from ct and tells whether the authentication is o.k., see
# the function neoclassic().

sessionkey="sodifreruvsfuvherudf0607052"
numround=3
roundkeybits=128
authenblocklen=15

print("Recipient side:")
print("ct: ",ct)
pt1=neoclassic(sessionkey,numround,roundkeybits,authenblocklen,ct,1)
print("pt1:",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()



# Epilogue:

# In order to maximize the ease of examination as well as undestanding
of the
# code by the majority of our targeted users, we have opted to reduce the
# complexity of the design as far as feasible, relying on the fact that
there
# is in any case always the possiblity available to the user to arbitrarily
# tune to his desire the strength of protection by NEOCLASSIC throgh a
# corresponding appropriate choice of the system parameters, e.g. the
value of
# numrounds, without thereby seriously effecting its acceptance in issues of
# processing efficiency in practice. Thus, for example, in the
substitution of
# the individual character (see process()) we employ the normal alphabet
instead
# of a shuffled alphabet and we apply also a mono-alphabetical substitution
# instead of a poly-alphabetical substitution at the end of each round.
(User
# having good experiences in programming may like to perform such
modifications
# to NEOCLASSIC, albeit on his own responsibility.)

# If both sender and recipient are in genuinely democratic countries and
hence
# there is no problem at all arising from any third person's knowing of
their
# encrypted message transfer (the protection being solely against the
# surveillance of the Big Brother of the Internet), then the ciphertext
could be
# simply sent via email as usual. Otherwise, as I recently argued in
some Usenet
# groups, use of facilities of the genre of remailer or Tor is risky
anyway for
# the sender. For his email carries his own IP address (which cannot be
faked)
# and hence the agencies could efficiently obtain his identity by
tapping at the
# entrance of the remailer or Tor network, to which his email is being
sent. In
# such situations the best solution is to post the ciphertext (encrypted at
# home) to a Usenet group like alt.anonymous.messages from a call shop or
# internet cafe (utilising the facility available there to access the Usenet
# group, thus not involving one's own IP address, nor email address). The
# recipient can collect the ciphertext similarly and decrypt it (at home) to
# obtain the plaintext. In order that the recipient could uniquely
identify the
# post of the sender, a chosen pseudonym and/or some special plaintext
string in
# the subject line of the post may not always be sufficient, because other
# posters may by chance or for other reasons employ the same distinguishing
# feature. So the recipient may under circumstances have to pick up also a
# number of posts that are not from his partner and later discard these
(after
# failing to decrypt them properly with the agreed-upon session key).
One method
# of avoiding this is to encrypt something agreed upon (which may be
dynamically
# varying) and use the corresponding ciphertext as the subject line for
posting
# to the Usenet group. (The cipher for obtaining the subject line need not
# necessarily be one of very high quality, since the purpose is solely to
# perform an arbitrary transformation for purpose of distinguishing, though
# users of NEOCLASSIC could certainly conveniently do the subject line
# encryption processing with NEOCLASSIC as well. Its key should however
# preferably be one different from the session key that is used to
encrypt the
# message proper.) It goes without saying that in non-democratic
countries there
# may be agents doing observations in call shops or internet cafes and the
# communication partners have to take the potential risks duly into account.
# (A Usenet post claimed that in one West-European country customers of call
# shops or internet cafes must show their identities on entrance. The
present
# author unfortunately could currently neither verify that claim nor
have good
# ideas of eliminating risks arising from such control.)