# NEOCLASSIC, A simple versatile encryption scheme (with authentication) based # largely on the straightforward use of well-known classical crypto techniques.
# 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  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.
#  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: firstname.lastname@example.org
# Loading a system module of Python that provides functionalities of its # built-in PRNG.
# 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()
# 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.)