# 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.

# 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.

# 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.

# 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.

# 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().

# 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.)