# AES/Python, an implementation of AES in Python with examples of encryption # with PCBC (block-chaining, with authentication) and counter mode (without # authentication). # Prologue. # A sketch of the main components of the software follows: # The AES algorithm consists of keyexpansion() for keyscheduling and cipher() # and invcipher() for encryption and decryption respectively. These invoke in # their turn functions listed before them below with names the same as in # FIPS-197 and with minimal deviations of syntax dictated by certain special # properties of Python. After initialization with aesinit(), which invokes the # keyscheduling, the function aes() is the function to be employed by the user # to transform in ECB mode a 16-byte block sequence representing plaintext to # a 16-byte block sequence representing ciphertext, or vice versa. # The function checkencryptionwithfips197() enables the user to do a check in # order to know that the present software works correctly for the examples that # are given in Appendix C of FIPS-197. # The function countermode() generates with a given initial counter value an # arbitrarily long pseudo-random byte sequence needed by the user with AES in # counter mode. Based on it the function countermodeencrypt() and # countermodedecrypt() perform stream encryption processing of a user-given # plaintext string (without authentication (integrity check)). # The functions pcbcencprocessing() and pcbcdecprocessing() encrypts/decrypts # an arbitrarily long 16-byte block sequence according to PCBC (block-chaining). # PCBC is to my knowledge yet rather unknown but was more than a decade ago an # idea of my own which I posted to an Internet group. It works analogously to # CBC with the difference that, instead of employing the ciphertext of the # immediately previous block to xor with the plaintext of the current block, # one employs a block named sigma for doing chaining. sigma is initialized via # encrypting a user-given initialization vector. On processing a plaintext # block, sigma is xor-ed with it to form the input block of aes(). sigma is # then updated by xoring it with the result of computation of a suitably chosen # non-linear function f() of the plaintext and ciphertext of the current block. # Thus at any time point sigma has in a sense summed up all the previously # processed plaintext blocks and ciphertext blocks and consequently has the # property of very strong error propagation such that its last value obtained # (after processing all blocks of the plaintext of the user) can be used as a # superior authentication (integrity check) on decryption processing. In other # words, PCBC is a one-pass encryption processing with integrity check. The # functions aespcbcencrypt() and aespcbcdecrypt() operate on user-given # character strings instead of byte sequences and are based upon # pcbcencprocessing() and pcbcdecprocessing(). # Purposes of functions having names identical to those in FIPS-197 are not # given as comments at the head of the functions. # On a common PC, measurements showed that less then 2 sec. is required to # encrypt/decrypt 10000 bytes. # Version 1.2, released on 16.05.2015. # Update notes: # Version 1.0: released on 01.05.2015. # Version 1.1: 05.05.2015: Add countermodeencrypt() and countermodedecrypt(). # Version 1.2: 16.05.2015: Modification of pcbcencprocessing() and # pcbcdecprocessing(). # Code lines of documents with the same version number are always identical. # There may be interim modifications of comment lines. # 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. # A list of present author's software that are currently directly maintained by # himself is available at http://mok-kong-shen.de. Users are advised to # download such software from that home page only. # The author is indebted to TPS for review and suggestions throughout # AES/Python's development phase. Any remaining deficiencies of the software # are however the sole responsibilty of the author. # Constructive critiques and comments are sincerely solicited. # Email address of the author: mok-kong.shen@t-online.de ################################################################################ import copy sbox=bytearray([ 0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16 ]) invsbox=bytearray([0 for i in range(256)]) for i in range(256): invsbox[sbox[i]]=i def subbytes(state): for i in range(4): for j in range(4): state[i][j]=sbox[state[i][j]] return def invsubbytes(state): for i in range(4): for j in range(4): state[i][j]=sbox.index(state[i][j]) def shiftrows(state): for i in range(1,4): state[i]=state[i][i:]+state[i][:i] return def invshiftrows(state): for i in range(1,4): state[i]=state[i][-i:]+state[i][:-i] # Multication in GF(2**8). (Google with "GF AES" for logic of this function.) def gfm(a,b): if (a < b): t=b b=a a=t r=0 suc=1 while suc: if b&0x01: r^=a t=a&0x80 a=(a<<1)&0xff if t: a^=0x1b b>>=1 suc=b return(r) def mixcolumns(state): s=copy.deepcopy(state) for c in range(4): state[0][c]=gfm(0x02,s[0][c])^gfm(0x03,s[1][c])^s[2][c]^s[3][c] state[1][c]=s[0][c]^gfm(0x02,s[1][c])^gfm(0x03,s[2][c])^s[3][c] state[2][c]=s[0][c]^s[1][c]^gfm(0x02,s[2][c])^gfm(0x03,s[3][c]) state[3][c]=gfm(0x03,s[0][c])^s[1][c]^s[2][c]^gfm(0x02,s[3][c]) return def invmixcolumns(state): s=copy.deepcopy(state) for c in range(4): state[0][c]=\ gfm(0x0e,s[0][c])^gfm(0x0b,s[1][c])^gfm(0x0d,s[2][c])^gfm(0x09,s[3][c]) state[1][c]=\ gfm(0x09,s[0][c])^gfm(0x0e,s[1][c])^gfm(0x0b,s[2][c])^gfm(0x0d,s[3][c]) state[2][c]=\ gfm(0x0d,s[0][c])^gfm(0x09,s[1][c])^gfm(0x0e,s[2][c])^gfm(0x0b,s[3][c]) state[3][c]=\ gfm(0x0b,s[0][c])^gfm(0x0d,s[1][c])^gfm(0x09,s[2][c])^gfm(0x0e,s[3][c]) return def addroundkey(state,ww): for c in range(4): wc=ww[c] for i in range(3,-1,-1): state[i][c]^=wc&0xff wc>>=8 return def subword(u): shfs=[24,16,8,0] v=0 for i in range(4): t=(u>>shfs[i])&0xff v<<=8 v|=sbox[t] return(v) def rotword(u): v=(u>>24)|((u<<8)&0xffffffff) return(v) # We prefer to compute rcon during processing (i.e. not stored as an array of # constants). def rcon(i): assert i > 0 u=0x01 for j in range(i-1): u=gfm(u,0x02) v=u<<24 return(v) # Convert a word to 4 bytes def wordtobytes(word): by=bytearray(4) for i in range(3,-1,-1): by[i]=word&0xff word>>=8 return(by) # The reverse of wordtobytes(). def bytestoword(by): u=0 for i in range(4): u<<=8 u|=by[i] return(u) def keyexpansion(): global nb,nk,nr,keysize,keyhexstring,key,w i=0 w=[0 for i in range(nb*(nr+1))] while i < nk: w[i]=bytestoword([key[4*i],key[4*i+1],key[4*i+2],key[4*i+3]]) i+=1 i=nk while i < nb*(nr+1): temp=w[i-1] if i%nk == 0: s=rotword(temp) ss=subword(s) rr=rcon(i//nk) tt=ss^rr temp=subword(rotword(temp))^rcon(i//nk) elif nk > 6 and i%nk ==4: temp=subword(temp) w[i]=w[i-nk]^temp i+=1 return def cipher(state): global nb,nk,nr,keysize,keyhexstring,key,w addroundkey(state,w[0:nb]) for round in range(1,nr): subbytes(state) shiftrows(state) mixcolumns(state) addroundkey(state,w[round*nb:(round+1)*nb]) subbytes(state) shiftrows(state) addroundkey(state,w[nr*nb:(nr+1)*nb]) return(state) def invcipher(state): global nb,nk,nr,keysize,keyhexstring,key,w addroundkey(state,w[nr*nb:(nr+1)*nb]) for round in range(nr-1,0,-1): invshiftrows(state) invsubbytes(state) addroundkey(state,w[round*nb:(round+1)*nb]) invmixcolumns(state) invshiftrows(state) invsubbytes(state) addroundkey(state,w[0:nb]) return(state) # Performs initialization for this software, in particular keyscheduling. def aesinit(): global nb,nk,nr,keysize,keyhexstring,key,w assert keysize in [128,192,256] and keysize==len(keyhexstring)*4 hexs="0123456789abcdef" for s in keyhexstring: assert s in hexs nb=4 if keysize==128: nk=4 nr=10 elif keysize==192: nk=6 nr=12 else: nk=8 nr=14 # Convert user-given keyhexstring to key. key=hexstrtobyarray(keyhexstring) # Invoke the keyscheduling function. keyexpansion() return # The main function of AES (in ECB mode), working on a block of 4 bytes. # kn=1: for encryption. # kn=2: for decryption. def aes(byarray,kn): assert 1 <= kn <= 2 linby=len(byarray) assert linby == 16 state=[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]] k=0 for j in range(4): for i in range(4): state[i][j]=byarray[k] k+=1 if kn==1: outstate=cipher(state) else: outstate=invcipher(state) outbyarray=bytearray(16) k=0 for j in range(4): for i in range(4): outbyarray[k]=outstate[i][j] k+=1 return(outbyarray) # Transform a byte sequence to a hex string (without the prefix '0x'). def byarraytohexstr(byarray): hexstr="" for i in range(len(byarray)): u=hex(byarray[i])[2:] if len(u) < 2: u='0'+u hexstr+=u return(hexstr) # The reverse of byarraytohexstr(). def hexstrtobyarray(hexstr): byarray=bytearray(0) lhexs=len(hexstr) assert (lhexs%2) == 0 k=0 while k < lhexs: v=int(hexstr[k:k+2],16) byarray+=bytearray([v]) k+=2 return(byarray) # This function serves to check this software with examples in Appendix C of # FIPS-197. def checkencryptionwithfips197(keylength): global nb,nk,nr,keysize,keyhexstring,key,w sizes=[128,192,256] assert keylength in sizes kk=sizes.index(keylength) plaintext="00112233445566778899aabbccddeeff" keystr=[\ "000102030405060708090a0b0c0d0e0f",\ "000102030405060708090a0b0c0d0e0f1011121314151617",\ "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"] ciphertext=[\ "69c4e0d86a7b0430d8cdb78070b4c55a",\ "dda97ca4864cdfe06eaf70a0ec0d7191",\ "8ea2b7ca516745bfeafc49904b496089"] keysize=sizes[kk] keyhexstring=keystr[kk] aesinit() byarray1=hexstrtobyarray(plaintext) byarray2=aes(byarray1,1) hexstring1=byarraytohexstr(byarray2) byarray3=aes(byarray2,2) hexstring2=byarraytohexstr(byarray3) if hexstring1==ciphertext[kk] and hexstring2==plaintext: print("Keysize",keysize, " Check encryption/decryption with FIPS-197 ok") else: print("Keysize",keysize, " Check encryption/decryption with FIPS-197 failed *********") return # Convert an 128-bit integer to 16 bytes. def int128tobytes16(k): assert k < 2**128 byarray=bytearray(16) for i in range(15,-1,-1): byarray[i]=k&0xff k>>=8 return(byarray) # The reverse of int238tobytes(). def bytes16toint128(byarray): lby=len(byarray) assert lby == 16 k=0 for i in range(16): k<<=8 k|=byarray[i] return(k) # Generation of psuedo-random byte sequence with AES in counter mode. def countermode(initialcountervalue,numberofbytes): assert 0 <= initialcountervalue <= 2**128 and numberofbytes%16 == 0 counter=initialcountervalue byarray=bytearray([]) for i in range(numberofbytes//16): bydata=int128tobytes16(counter) byarray+=aes(bydata,1) counter+=1 return(byarray) # Encryption of plaintextstring via xor-ing it with byte sequences from AES in # counter mode (stream encryption processing, without authentication (integrity # check)). def countermodeencrypt(initialcountervalue,plaintextstring): byarray=bytearray(plaintextstring,'latin-1') lby=len(byarray) q,r=divmod(lby,16) if r>0: q+=1 counterbyarray=countermode(initialcountervalue,q*16) cipherbyarray=bytearray(0) for i in range(lby): cipherbyarray+=bytes([byarray[i]^counterbyarray[i]]) return(cipherbyarray) # The reverse of countermodeencrypt(). def countermodedecrypt(initialcountervalue,cipherbyarray): lby=len(cipherbyarray) q,r=divmod(lby,16) if r>0: q+=1 counterbyarray=countermode(initialcountervalue,q*16) plaintextstring="" for i in range(lby): pb=cipherbyarray[i]^counterbyarray[i] plaintextstring+=chr(pb) return(plaintextstring) # To update sigmaint in pcbcencprocessing(), xoring with pint^cint could be # done. We prefer however to use the following non-linear function, since it has # the nice property that for a constant y it is bijective in x and for a # constant x it is bijective in y, indicating that it is optimal for mixing x # and y in respect of entropy. User may replace it with his own favoured # function. The value returned must be in interval [0, 2**128-1]. def nonlinfunc(x,y): r=(2*x*y+x+y)%(2**128) return(r) # Encryption of byte sequences in 16-bytes blocks with PCBC (block-chaining). # Block chaining is effected via sigmaby which is initialized with an encrypted # block of ivstring. After processing the entire inbyarray, the last sigmaby # is encrypted for the purpose of integrity check in pcbcdecprocessing(), as # sigmaby is now in a sense the summation of all processed plaintext and # ciphertext blocks. ivstring is a session-dependent (variable) character string # of maximal length 16. If given too long, it will be curtailed, if too short, # it will be filled with "#". One should attempt to choose it such that, with # the employment of the same key of AES, all ivstring used are different. This # could be done via systematically deriving ivstring from e.g. message number, # date, time, subject, etc. ivstring need not be secret. It can be sent in the # clear, in case there is no systematic scheme of derivation to be used by the # receiver. def pcbcencprocessing(inbyarray,ivstring): byarray=inbyarray[:] lby=len(byarray) assert lby%16 == 0 numblock=lby//16 outbyarray=bytearray(0) liv=len(ivstring) if liv > 16: ivstring=ivstring[:16] else: ivstring+=(16-liv)*"#" # Encrypt ivstring with AES to serve as initial value of sigmaby. sigmaby=bytearray(ivstring,'latin-1') sigmaby=aes(sigmaby,1) sigmaint=bytes16toint128(sigmaby) k=0 for ii in range(numblock): k1=k+16 p=byarray[k:k1] pint=bytes16toint128(p) # xoring of plaintext block with sigmaby. for jj in range(16): p[jj]^=sigmaby[jj] # Encrypt with AES to obtain ciphertext block. c=aes(p,1) outbyarray+=c cint=bytes16toint128(c) # Update sigmaby. sigmaint^=nonlinfunc(pint,cint) sigmaby=int128tobytes16(sigmaint) k=k1 # Encryption of the last sigma to be checked in pcbcdecprocessing(). p=sigmaby c=aes(p,1) outbyarray+=c return(outbyarray) # The reverse of pcbcencprocessing(), authentication (integrity check is done). def pcbcdecprocessing(inbyarray,ivstring): byarray=inbyarray[:] lby=len(byarray) assert lby%16 == 0 numblock=lby//16 outbyarray=bytearray(0) liv=len(ivstring) if liv > 16: ivstring=ivstring[:16] else: ivstring+=(16-liv)*"#" sigmaby=bytearray(ivstring,'latin-1') sigmaby=aes(sigmaby,1) sigmaint=bytes16toint128(sigmaby) k=0 for ii in range(numblock-1): k1=k+16 c=byarray[k:k1] p=aes(c,2) for jj in range(16): p[jj]^=sigmaby[jj] outbyarray+=p pint=bytes16toint128(p) cint=bytes16toint128(c) sigmaint^=nonlinfunc(pint,cint) sigmaby=int128tobytes16(sigmaint) k=k1 k1=k+16 c=byarray[k:k1] checksigmaby=aes(c,2) # Check whether the last sigmaby obtained similarly to that of the sender is # identical to that of the sender (which now has been decrypted checksigmaby). if sigmaby != checksigmaby: print("Authentication (integrity check) failed *********") exit(111) return(outbyarray) # Encrypt with PCBC (block chaining) of plaintextstring, with ivstring as IV # and fillerchar to fill the character string in case its length is not a # multiple of 16. Fillerchar cannot be in the upper or lower case alphabet # and cannot appear in plaintextstring. def aespcbcencrypt(plaintextstring,ivstring,fillerchar): alpha="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" assert fillerchar not in plaintextstring and fillerchar not in alpha lptstr=len(plaintextstring) r=lptstr%16 if r!=0: d=16-r plaintextstring+=d*fillerchar byarray=bytearray(plaintextstring,'latin-1') cipherbyarray=pcbcencprocessing(byarray,ivstring) return(cipherbyarray) # The reverse of aespcbcencrypt(). def aespcbcdecrypt(cipherbyarray,ivstring,fillerchar): byarray=pcbcdecprocessing(cipherbyarray,ivstring) lby=len(byarray) ordfc=ord(fillerchar) plaintextstring="" for i in range(lby): jj=byarray[i] if jj==ordfc: break plaintextstring+=chr(jj) return(plaintextstring) ################################################################################ # Installation of the software. # Both communication partners have to download the same version 3x of Python # from http://www.python.org. (Employing the same version of Python ensures # against any potentially possible incompatibilities among different versions.) # The present code can be stored in a file named e.g. aes.py and the # example given further below run in Python's GUI IDLE. (File --> Open to find # and open the file, then in the window showing the code Run --> Run Module to # run it. One could also type aes.py in a DOS-window.) Modifications of # the code in the code window, e.g. the plaintext string, can be done online and # the code re-run. ################################################################################ # Useful utility functions for I/O. # Read a byte sequence from a binary file. def readbinaryfile(binaryfilename): fp=open(binaryfilename+".bin","rb") byarray=bytearray(fp.read()) fp.close() return(byarray) # Write a byte sequence to a binary file. def writebinaryfile(byarray,binaryfilename): fp=open(binaryfilename+".bin","wb") fp.write(byarray) fp.close() # Read a text file return a textstring. def readtextfile(textfilename): fp=open(textfilename+".txt","r") textstring=fp.read() fp.close() return(textstring) # Write a textstring to a text file. def writetextfile(textstring,textfilename): fp=open(textfilename+".txt","w") fp.write(textstring) fp.close() return ################################################################################ # Check of correctness of our AES implementation with FIPS-197. checkencryptionwithfips197(128) print() checkencryptionwithfips197(192) print() checkencryptionwithfips197(256) print() print() # Illustrative Example 1. Encryption of a plaintext string with PCBC (block # chaining with authentication (integrity check)). print("Illustrative Example 1:") print() # The following parameters are assumed to be used by both the sender and the # receiver. See aespcbcencrypt() and pcbcencprocessing(). # keysize must be one of [128, 196, 256]. keysize=128 # A string of hexadecimals of length keysize/4 specifying the secret key for # AES encryption processing. The hexadecimals should be as random as possible. # One practical way of their generation is throwing a bunch of dice. For a # utility to convert dice throws to a hexadecimal sequence, see DICE. keyhexstring="4a98717c70e997c2715e87c3fa1ef559" # A session-dependent (variable) character string of length 16. ivstring="HGS TR386 010515" # A character employed for eventual filling of the user-given plaintext string # to a length suitable for processing. It must be non-alphabetical and must # not be in user-given plaintext string. fillerchar="#" # Sender side specific coding: plaintext=\ "The significant problems we face cannot be solved at the same level of "\ "thinking we were at when we created them. -- Albert Einstein" aesinit() byarray=aespcbcencrypt(plaintext,ivstring,fillerchar) writebinaryfile(byarray,"cipherbyarray") # Receiver side specific coding: byarray1=readbinaryfile("cipherbyarray") aesinit() plaintext1=aespcbcdecrypt(byarray1,ivstring,fillerchar) print("Message received:") print() print(plaintext1) print() # Check of correctness of processing. print("Message has been corrrectly transmitted:",plaintext1==plaintext) print() print() # Illustrative Example 2. Encryption of a plaintext string via xor-ing it with # bits from AES in counter mode (stream encryption processing, without # authentication (integrity check)). print("Illustrative Example 2:") print() # The following parameters are assumed to be used by both the sender and the # receiver. See countermodeencrypt(). keysize=128 keyhexstring="4a98717c70e997c2715e87c3fa1ef559" counterinitialvalue=3000 # Sender side specific coding. aesinit() plaintext=\ "Was sich ueberhaupt sagen laesst, laesst sich klar sagen; und wovon man "\ "nicht reden kann, darueber muss man schweigen. -- Ludwig Wittgenstein" byarray=countermodeencrypt(counterinitialvalue,plaintext) writebinaryfile(byarray,"cipherbyarray") # Receiver side specific coding. aesinit() byarray1=readbinaryfile("cipherbyarray") plaintext1=countermodedecrypt(counterinitialvalue,byarray1) print("Message received:") print() print(plaintext1) print() # Check of correctness of processing. print("Message has been corrrectly transmitted:",plaintext1==plaintext) ################################################################################ # Epilogue. # We presume that the computer, on which this software is run, is free from # malware infection via software and/or hardware means and that there are no # emission security risks. # The result of encryption of aespcbcencrypt() is binary and can be transported # e.g. as an attached file in a normal email system. Thus in particular the # end-to-end encryption of emails between two persons can be very simply # realized without the use of any claimed(!) secure-email software that commonly # involve S/MIME or PGP which, though being open-source packages, have in # reality not been thoroughly scrutinized by independent experts due to their # huge sizes, leading thus in the recent past to catastrophic consequences like # those of the Heartbleed Bug. Note that, even if one's email system # incorporates such claimed secure features, it evidently can do no harm for # the user anyway to introduce an additional protection layer with the present # software, which costs very little additional work, as our Illustrative # Example clearly show. # Users who have also interest in public key cryptography may note that the # present author has a Python code of RSA key generation and encryption in his # software PROVABLEPRIME, which has also its epilogue some comments of general # interest. # Concerning key management, it could be advantageous to have a master secretkey # that is kept secure for a long duration and is used only to generate secondary # secretkeys for actual use in e.g. a month of a certain year (via encrypting a # string of that time point or something equivalent to it) so as to take care of # eventual needs of key revocation and not to have too much materials processed # with the same secretkey. A hierarchy of secretkeys could also be employed, # if needed. # Use of AES in counter mode as a PRNG to generate arbitrary long bit sequences # is commonly considered to be ok, provided that multiple uses with the same key # employ counter values in intervals that don't overlap on another, which can be # simply done e.g. via using initial counter values of the form g + n*h, where # g is a chosen constant, n the message number and h an estimated upper bound of # the number of bits (8 * message length) that a message has. One communication # partner could, for example, choose g=0 and the other g=2**127. Evidently their # counter intervals would never overlap in practice. If a master key and a # common counter is kept by both partners, then AES in counter mode could also # be used to supply session keys for use to encrpyt messages. # The present author has earlier thought of a paradox concerning AES in counter # mode. The keysize of AES is one of 128, 196 and 256 and the key has a maximum # of keysize bits of entropy. If the output sequence of counter mode being used # is m*keysize bits long, then the average entropy of a bit in that sequence is # 1/m bit, which would be fairly smaller than 1 even for some moderate values # of m. The seemingly paradoxical question is then how bits with such small # entropies could nevertheless be used to do any secure stream encryption # processing. I have in the past raised that issue to a few Internet groups, # without however obtaining any response that resolves that paradox. # Note that the pseudo-random byte sequences generated with AES in counter mode # could, if desired, be post-processed or combined with other pseudo-random # sequences.