Another little TADS3/adv3 module. This one provides several simple cipher algorithms: simpleCipher github repo.
IMPORTANT: None of the provided algorithms are safe for any modern cryptographic use. They’re intended to make it easier to implement in-game cryptographic puzzles, which are (by their nature) intended to be solved/broken.
Usage is pretty straightforward. Most of the algorithms are implemented in global singletons which provide an encode()
and decode()
method. A few examples:
Rot13
// Result is 'Gurer vf n fznyy znvyobk urer.'
enc = rot13.encode('There is a small mailbox here.');
// Result is 'There is a small mailbox here.'
dec = rot13.decode(enc);
Caesar Ciphers
For Caesar ciphers, both encode()
and decode()
take a second argument indicating the shift to use:
// Result is 'Qebob fp x pjxii jxfiylu ebob.'
enc = caesar.encode('There is a small mailbox here.', -3);
// Result is 'There is a small mailbox here.'
dec = caesar.decode(enc, -3);
Arbitrary Monoalphabetic Substitution Ciphers
The module also supplies an abstract class for implementing arbitrary monoalphabetic substitution ciphers. In order to use it, you have to create an instance, which will then work like the other examples. The constructor takes two arguments: the first is the cipher alphabet; and the optional second argument is the plaintext alphabet, defaulting to the standard Latin alphabet if none is given.
// Declare the cipher instance
// This uses the default plaintext alphabet, so the cipher
// is:
// plaintext ABCDEFGHIJKLMNOPQRSTUVWXYZ
// ciphertext EKMFLGDQVZNTOWYHXUSPAIBRCJ
// That is, A -> E, B -> K, C -> M, and so on.
cipher = new SimpleCipherMonoalphabetic('EKMFLGDQVZNTOWYHXUSPAIBRCJ');
// Result is 'Pqlul vs e soett oevtkyr qlul.'
enc = cipher.encode('There is a small mailbox here.');
// Result is 'There is a small mailbox here.'
dec = cipher.decode(enc);
Simple M3 Enigma Cipher
Finally, the module provides a toy version of the M3 Enigma machine, specifically one without a plugboard (to make it easier to solve, because the assumption is that all ciphers presented in a game are intended to be solved).
The module provides an enigma
singleton, but you’ll have to create an EnigmaConfig
instance to pass to encode()
and decode()
. The config specifies the rotors to use (the module provides the historical I, II, and III rotors), the reflector to use (the module provides the historical B and C reflectors), and the encryption key. Note that the encryption key must have exactly as many characters as the machine has rotors (the key literally just being the starting settings for the rotors).
// Create the config
cfg = new EnigmaConfig();
// Set the encryption key. Note that the number of letters
// must match the number of rotors used in this config.
cfg.setKey('ABC');
// Set the rotors. Arg is a list of the rotor IDs, in order
// from left to right. Rotors I, II, and III are defined
// in the module, others can be added.
cfg.setRotors([ 'III', 'II', 'I' ]);
// Set the reflector. Arg is the reflector ID. The module
// provides declarations for reflectors B and C.
cfg.setReflector('B');
// Result will be "XOGNZ BBHUW QRBLQ HURWN PUJRM". Note
// that output is divided into 5-character groups, and the
// final group will be padded if the input length isn't
// a multiple of five. In this case the input, after all
// spaces and punctuation are removed, is 14 characters, so
// the last character is padding. This will show up
// in the decode as a trailing 'X'.
// Result will be 'XOGNZ BBHUW QRBLQ HURWN PUJRM'
enc = enigma.encode('There is a small mailbox here.', cfg);
// Result will be 'THERE ISASM ALLMA ILBOX HEREX'. Messages
// are always stripped of non-alphabetic characters and
// converted to upper case. The X is the result of the
// padding described above.
dec = enigma.decode(enc, cfg);
I’ll just reiterate that this is just intended to make it easier to implement codes and crytographic puzzles in games. None of the algorithms provide any sort of meaningful security in a modern environment.