Click for homepage
← SpellMaster
← Algorithm
  
C-code →
  
1. SpellMaster in Python language
Xavier Bonnetain — 29 July 2022 (9:13)

The first one to solve the mystery is Xavier Bonnetain, a researcher in cryptography in the CARAMBA team at LORIA in Nancy (France). He made small changes to the recovered C-code and managed to reconstruct the alphabet substitution table entirely from the published PT/CT pairs. His solution is written in the Python programming language and can be downloaded below.

Download
Python code
      
     # Spellmaster encrypt/decrypt
     # Written by Xavier Bonnetain
     
     
     def e(c) :
         """[0,27] -> Alphabet"""
     
         if c < 26:
             return chr(c+ord('A'))
         if c == 26:
             return '-'
         return '?'
     
     def d(c) :
         """Alphabet -> [0,27]"""
         a = ord(c)
         if a <= ord('Z') and a >= ord('A'):
             return a - ord('A')
         if c == '-':
             return 26
         return 27
     
     def enc(key, message):
         """Encryption, post-permutation"""
         c = key
         s = ""
         for m in message:
             m = d(m)
             key = 0
             for i in c:
                 key = (key+ (i - 65))
                 if key < -128:
                     key+=256
                 if key >= 128:
                     key-=256
             key = key %28
             r = (key - m) % 28
             s+=e(r)
             c= c[1:]+[(key+m)%28]
         return s
     
     
     def dec(key, message):
         """Decryption, pre-permutation"""
         c = key
         s = ""
         for m in message:
             m = d(m)
             key = 0
             for i in c:
                 key = (key+ (i - 65))
                 if key < -128:
                     key+=256
                 if key >= 128:
                     key-=256
             key = key %28
             r = (key - m) % 28
             s+=e(r)
             c= c[1:]+[(key+r)%28]
         return s
     
     
     def getmatch(key, plaintext, ciphertext):
         """Compute the permutation from a pair"""
         c = key
         for i in range(len(ciphertext)):
             m = d(ciphertext[i])
             key = 0
             for j in c:
                 key = (key+ (j - 65))
                 if key < -128:
                     key+=256
             key = key %28
             r = (key - m) % 28
             c= c[1:]+[(key+r)%28]
             print("'%c' : '%c' ," %(plaintext[i],e(r)))
     
     #The permutation
     dico = {
     'A' : 'H' ,
     'B' : 'O' ,
     'C' : 'W' ,
     'D' : 'A' ,
     'E' : 'S' ,
     'F' : 'M' ,
     'G' : 'J' ,
     'H' : 'R' ,
     'I' : 'N' ,
     'J' : 'E' ,
     'K' : 'D' ,
     'L' : 'G' ,
     'M' : 'B' ,
     'N' : 'X' ,
     'O' : 'K' ,
     'P' : 'T' ,
     'Q' : 'C' ,
     'R' : 'Q' ,
     'S' : 'P' ,
     'T' : 'U' ,
     'U' : 'I' ,
     'V' : 'Y' ,
     'W' : 'F' ,
     'X' : 'Z' ,
     'Y' : 'L' ,
     'Z' : 'V',
     '-' : '-',
     '?' : '?'
     }
     
     def re(x):
         """Inverse permutation"""
         for d in dico:
             if dico[d] == x:
                 return d
         raise AssertionError
     
     
     def expand(key):
         k = [0] * 8
         for i in range(8):
             k[i] = key[i%len(key)]
         return k
     
     #####Toplevel functions
     
     def encrypt(key,message):
         m = [ dico[x] for x in message]
         k = [ d(x)+65 for x in key]
         return enc(expand(k),m)
     
     
     def decrypt(key,cipher):
         k = [ d(x)+65 for x in key]
         m = dec(expand(k),cipher)
         return ''.join(str(re(c)) for c in m)
     
     #####Tests
     
     def check(key,plain,cipher):
         s = encrypt(key,plain)
         print("Expected:\t"+cipher)
         print("Found:\t\t",end="")
         for (a,b) in zip(s,cipher):
             print(a,end="")
         print("\0")
         s = decrypt(key,cipher)
         print("Expected:\t"+plain)
         print("Found:\t\t",end="")
         for (a,b) in zip(s,plain):
             print(a,end="")
         print("\n")
     
     check("CATS","DOGSDOGSDOGSDOGS","WXCQ-DSUCEKQD-WU")
     check("CAT","DOGSDOGSDOGSDOGS","QLGAGVHPOCWEXCID")
     check("SECRECY","CRYPTOMUSEUM-FOREVER","LRCHZHE-GQBBSJROQ--R")
     check("SECRECY","LONG-LIVE-CRYPTOMUSEUM","?HIJVVI-XLXOKRKI?SKIQX")
     check("AAAAAAAA","AAAAAAAAAA","VTPL?DL?HV")
     check("AAAA","BBBB","OTBZ")
     check("AAAAAAAA","BBBBBBBBBBBBBBBB","OTBZNRZNVAJRJFVR")
     check("AAA","BBBBBBBBBBBBBBBBBBBBBBBBB","OTBZNRZNVAJRJFVRVVOTVBBJB")
     check("AAAAAAAA","BBBBBBBBBB","OTBZNRZNVA")
     check("AAAAAAAA","ABCDEFGHIJKLMNOPQRSTUVWXY","VMHTL?QFPCBKSZCJGRZM-EEEZ")
     check("AAAAAAAA","BCDEFGHIJKLMNOPQRSTUVWXYZ","OLXXXIRHMTOWV-JWBPCGQEIFY")
     check("AAAAAAAA","CDEFGHIJKLMNOPQRSTUVWXYZA","GN?HEJTIUVSJOV-ZLVFSUYZEH")
     check("AAAAAAAA","DEFGHIJKLMNOPQRSTUVWXYZAB","ABHIRHMYSZVONCRTFEJSUZAHA")
     check("AAAAAAAA","IS-SPELLMASTER-ALIVE?","PRQGBETNKDV?EHYE-RELK")
     check("BBBBBBBB","IS-SPELLMASTER-ALIVE?","XERHCFUOLZWAFIZF?SM-L")
     
    
Further information
Any links shown in red are currently unavailable. If you like the information on this website, why not make a donation?
© Crypto Museum. Created: Friday 29 July 2022. Last changed: Wednesday, 03 August 2022 - 08:47 CET.
Click for homepage