# 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 encryption algorithms that, # when appropriately used (eventually in combinations, i.e. the so-called # superencipherment), 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 user of my software at the very beginning: # The user is himslef responsible for the appropriate choice of the system # parameters of the software in any actual applications.

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

# 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 streám encryption # processing. 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 preferrably # 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.0, released on 07.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 #

# 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: snipped-for-privacy@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 by the user, if needed. Plaintext may use a subset of # alphabet, but ciphertext will generally involve characters of the whole # 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=list(alphabetstr) 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.

def process(roundkey,textstring,feedbackval,kn): global alphabet,lenalpha lenalpham1=lenalpha-1 lentext=len(textstring) random.seed(roundkey) cipheralphabet=alphabet[:] random.shuffle(cipheralphabet) cipheralphabetstr="".join(cipheralphabet) ciphersequence=[i for i in range(lentext)] random.shuffle(ciphersequence) 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 cipherhext # alphabet). transtab=newstring.maketrans(alphabetstr,cipheralphabetstr) newstring=newstring.translate(transtab) else: # Begin of decryption processing for this round (reversal of encryption # processing): tmpstring=textstring[:] transtab=tmpstring.maketrans(cipheralphabetstr,alphabetstr) tmpstring=tmpstring.translate(transtab) newlist=["" for i in range(lentext)] for i in range(lentext): tt=tmpstring[i] gg=alphabet.index(tt) rr=random.randint(0,lenalpham1) idx=(gg-sigma-rr)%lenalpha newlist[ciphersequence[i]]=alphabet[idx] sigma=(sigma+idx)%lenalpha 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 preferrably 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. # Encryption: kn=0. # Decryption: kn=1.

def neoclassic(sessionkey,numround,roundkeybits,authenblocklen,inputstring,kn): global alphabet,lenalpha 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) random.seed(sessionkey) 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)] if kn==0: # Append authentication block. textstring=inputstring+authenblock # Encryption processing. for i in range(numround): textstring=process(roundkeys[i],textstring,feedbackvals[i],kn) else: # Decryption processing: textstring=inputstring 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 authenblock (lastblock) should be identical to the authenblock # that is obtained from computing with sessionkey above. 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.) For 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)