arduino-enigma/enigma.ino

420 lines
10 KiB
Arduino
Raw Normal View History

2020-09-23 20:16:06 +00:00
#include "Adafruit_Keypad.h"
const byte KBD_ROWS = 3; // rows
const byte KBD_COLS = 9; // columns
char keys[KBD_ROWS][KBD_COLS] = {
{'Q','W','E','R','T','Z','U','I','O'},
{'A','S','D','F','G','H','J','K','*'},
{'P','Y','X','C','V','B','N','M','L'}
};
byte rowPins[KBD_ROWS] = {10,11,12}; //connect to the row pinouts of the keypad
byte colPins[KBD_COLS] = {13,2,3,4,5,6,7,8,9}; //connect to the column pinouts of the keypad
Adafruit_Keypad kbd = Adafruit_Keypad( makeKeymap(keys), rowPins, colPins, KBD_ROWS, KBD_COLS);
#include <LiquidCrystal.h>
LiquidCrystal lcd(19, 18, 17, 16, 15, 14);
2020-09-24 01:05:03 +00:00
const int LCD_COLS = 20;
const int LCD_ROWS = 4;
2020-09-23 20:16:06 +00:00
bool debug_mode = false;
typedef struct {
char *id;
char *name;
char wiring[26];
char *notch;
} Rotor;
Rotor rotors [] = {
// Enigma I, M3, M4
// id name wiring notch
// == ==== ====== =====
{ "ABC", "ABC", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "" }, // 0 alphabet
2020-09-27 15:43:58 +00:00
//
2020-09-23 20:16:06 +00:00
{ "I", "I", "EKMFLGDQVZNTOWYHXUSPAIBRCJ", "Q" }, // 1
{ "II", "II", "AJDKSIRUXBLHWTMCQGZNPYFVOE", "E" }, // 2
{ "III", "III", "BDFHJLCPRTXVZNYEIWGAKMUSQO", "V" }, // 3
{ "IV", "IV", "ESOVPZJAYQUIRHXLNFTGKDCMWB", "J" }, // 4
{ "V", "V", "VZBRGITYUPSDNHLXAWMJQOFECK", "Z" }, // 5
2020-09-27 15:43:58 +00:00
//
2020-09-23 20:16:06 +00:00
{ "VI", "VI", "JPGVOUMFYQBENHZRDKASXLICTW", "ZM" }, // 6
{ "VII", "VII", "NZJHGRCXMYSWBOUFAIVLPEKQDT", "ZM" }, // 7
{ "VIII", "VIII", "FKQHTLXOCBJSPDZRAMEWNIUYGV", "ZM" }, // 8
2020-09-27 15:43:58 +00:00
{ "beta", "Beta", "LEYJVCNIXWPBQMDRTAKZGFUHOS", "" }, // 9 M4 only
{ "gamma", "Gamma", "FSOKANUERHMBTIYCWLQPZXVGJD", "" }, // 10 M4 only
// reflectors
{ "UKW-A", "UKW A", "EJMZALYXVBWFCRQUONTSPIKHGD", "" }, // 11
2020-09-23 20:16:06 +00:00
{ "UKW-B", "UKW B", "YRUHQSLDPXNGOKMIEBFZCWVJAT", "" }, // 12
{ "UKW-C", "UKW C", "FVPJIAOYEDRZXWGCTKUQSBNMHL", "" }, // 13
2020-09-27 15:43:58 +00:00
{ "UKW-B-thin", "UKW B thin", "ENKQAUYWJICOPBLMDXZVFTHRGS", "" }, // 14 M4 only
{ "UKW-C-thin", "UKW C thin", "RDOBJNTKVEHMLFCWZAXGYIPSUQ", "" } // 15 M4 only
};
typedef struct {
char *name;
char *description;
Rotor rotor_types[];
Rotor reflector_types[];
} EnigmaType;
EnigmaType enigma_types[] = {
{
"M3",
"Army",
{ rotors[1], rotors[2], rotors[3], rotors[4], rotors[5] },
{ rotors[11], rotors[12] }
},
{
"M4",
"Navy",
// work out thin rotors
{ rotors[1], rotors[2], rotors[3], rotors[4], rotors[5], rotors[6], rotors[7], rotors[8] },
{ rotors[11], rotors[12] }
}
2020-09-23 20:16:06 +00:00
};
// entry wheel "EintrittswalzeEintrittswalze"
byte entry_wheel_type = 0; //
// rotor types
byte rotor_type [] = { 1, 2, 3 };
// reflector "UmkehrwalzeUmkehrwalze"
byte reflector_type = 12;
2020-09-24 01:05:03 +00:00
const int PLUGBOARD_SIZE = 10;
char plugboard [PLUGBOARD_SIZE][2] = {
{'A','B'}, {'.','.'}, {'.','.'}, {'.','.'}, {'R','Z'},
{'C','D'}, {'.','.'}, {'.','.'}, {'.','.'}, {'G','H'}
2020-09-23 20:16:06 +00:00
};
// track rotor positions
byte rotor_pos [] = { 0, 0, 0 };
#define ROTOR_COUNT (sizeof(rotor_type)/sizeof(rotor_type[0]))
// compute alphabet size
#define AZ (rotors[entry_wheel_type].wiring)
#define AZ_LEN sizeof(rotors[0].wiring)
#define AZ_MAX_INDEX (AZ_LEN-1)
int wrap_pos (int pos) {
if (pos<0) {
return wrap_pos(pos+AZ_LEN); // TODO: optimization opportunity
} else {
return pos % AZ_LEN;
}
}
void rotate (byte rotor) {
rotor_pos[rotor] = wrap_pos(rotor_pos[rotor]+1);
}
bool at_notch (byte rotor) {
return NULL != strchr(rotors[rotor_type[rotor]].notch, AZ[rotor_pos[rotor]]);
}
void print_rotor (int rotor) {
Serial.print(at_notch(rotor) ? '(' : '[');
Serial.print(AZ[rotor_pos[rotor]]);
Serial.print(at_notch(rotor) ? ')' : ']');
}
void print_rotors () {
for (int i=ROTOR_COUNT-1; i>=0; --i) {
print_rotor(i);
}
}
int stridx(char *str, char c) {
return strchr(str, c) - str;
}
char encode (char c) {
bool
rotated1 = false,
rotated2 = false;
print_rotors();
// rotate left rotor if middle rotor is at notch
if (at_notch(1)) {
rotated2 = true;
rotate(2);
}
// rotate middle rotor only if both right and middle are at notch
if (at_notch(0) || at_notch(1)) {
rotated1 = true;
rotate(1);
}
// always rotate the right rotor
rotate(0);
if (debug_mode) {
// did any of the rotors (except for the rightmost) rotate?
Serial.print(rotated2? '>' : '-');
Serial.print(rotated1? '>' : '-');
Serial.print('>');
}
print_rotors();
if (debug_mode)
Serial.print(' ');
// going from rightmost rotor to the left
int pin = stridx(AZ, c);
int pos;
char *wiring;
for (int rotor = 0; rotor < ROTOR_COUNT; ++rotor) {
pos = rotor_pos[rotor];
wiring = rotors[rotor_type[rotor]].wiring;
if (debug_mode)
Serial.print(AZ[pin]);
pin = wrap_pos(stridx(AZ, wiring[wrap_pos(pin + pos)]) - pos);
if (debug_mode) {
Serial.print("->");
Serial.print(AZ[pin]);
Serial.print(" ");
}
}
// reflector
if (debug_mode) {
Serial.print(" ");
Serial.print(AZ[pin]);
}
pin = stridx(AZ, rotors[reflector_type].wiring[pin]);
if (debug_mode) {
2020-09-27 15:43:58 +00:00
Serial.print("->");
Serial.print(AZ[pin]);
2020-09-23 20:16:06 +00:00
Serial.print(" ");
}
// returning back from leftmost to the right
for (int rotor = ROTOR_COUNT-1; rotor >= 0; --rotor) {
if (debug_mode)
Serial.print(AZ[pin]);
pos = rotor_pos[rotor];
wiring = rotors[rotor_type[rotor]].wiring;
pin = wrap_pos(pin + pos);
char ch = AZ[pin];
ch = AZ[stridx(wiring, ch)];
pin = stridx(AZ, ch);
pin = wrap_pos(pin - pos);
if (debug_mode) {
Serial.print("->");
Serial.print(AZ[pin]);
Serial.print(" ");
}
}
2020-09-24 01:05:03 +00:00
char plug_c = AZ[pin];
2020-09-23 20:16:06 +00:00
// plugboard
2020-09-24 01:05:03 +00:00
for (int i=0; i<PLUGBOARD_SIZE; ++i) {
if (AZ[pin] == plugboard[i][0]) {
return plugboard[i][1];
}
}
2020-09-23 20:16:06 +00:00
return AZ[pin];
}
const char *plain = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ";
const char *cipher = "FUVEPUMWARVQKEFGHGDIJFMFXIMRENATHDMCEVOQHIUWXXGYSJADEGKHYJETLBLWVZNUXFNSPICQFGZCZJKYWLLGPXJKBYTNNEFYKQTCJOLGCWHGXUEYOQXDNIGIDEMBXACVPAVYUQCGPXILERRSJSBOOKJW";
int const PLAIN_LEN = strlen(plain);
void test () {
Serial.begin(115200);
Serial.print("Testing "); Serial.print(PLAIN_LEN); Serial.println(" chars.");
bool failed = false;
debug_mode = true;
for (byte i=0; i<PLAIN_LEN; ++i) {
if (i<10) Serial.print(' ');
if (i<100) Serial.print(' ');
Serial.print(i, DEC);
Serial.print(": ");
char encoded = encode(plain[i]);
if (cipher[i] != encoded) {
failed = true;
Serial.print(" (Expected: ");
Serial.print(cipher[i]);
Serial.print(")");
}
Serial.println("");
}
if (failed) {
Serial.println("\n"
"\n* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *"
"\nOne or more characters did not encrypt correctly. See messages above."
"\n* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *"
);
}
}
2020-09-24 01:05:03 +00:00
const int BUF_SIZE = LCD_COLS - ROTOR_COUNT*3 - 1;
2020-09-23 20:16:06 +00:00
char input_buf [BUF_SIZE];
char encoded_buf [BUF_SIZE];
int buf_pos = 0;
void init_buf (char *buf) {
for (int i=0; i<BUF_SIZE; ++i) {
buf[i] = ' ';
}
}
void add_to_buf (char *buf, char c) {
for (int i=0; i<BUF_SIZE-1; ++i) {
buf[i] = buf[i+1];
}
buf[BUF_SIZE-1] = c;
}
void disp_buf (char *buf, int lcd_row) {
lcd.setCursor(LCD_COLS-BUF_SIZE,lcd_row);
for (int i=0; i<BUF_SIZE; ++i) {
lcd.print(buf[i]);
}
}
void disp_rotor (int rotor) {
lcd.setCursor((ROTOR_COUNT-rotor-1)*3,0);
lcd.print(at_notch(rotor) ? '(' : '[');
lcd.print(AZ[rotor_pos[rotor]]);
lcd.print(at_notch(rotor) ? ')' : ']');
}
void disp_rotors () {
for (int i=ROTOR_COUNT-1; i>=0; --i) {
disp_rotor(i);
}
}
2020-09-24 01:05:03 +00:00
void disp_plugboard () {
if (LCD_ROWS > 2) {
for (int i=0; i<PLUGBOARD_SIZE/2; ++i) {
lcd.setCursor(i*3+(LCD_COLS-PLUGBOARD_SIZE/2*3+1),2);
lcd.print(plugboard[i][0]);
lcd.print(plugboard[i][1]);
if (i<PLUGBOARD_SIZE/2-1) {
lcd.print(' ');
}
lcd.setCursor(i*3+(LCD_COLS-PLUGBOARD_SIZE/2*3+1),3);
lcd.print(plugboard[i+PLUGBOARD_SIZE/2][0]);
lcd.print(plugboard[i+PLUGBOARD_SIZE/2][1]);
if (i<PLUGBOARD_SIZE/2-1) {
lcd.print(' ');
}
}
}
}
2020-09-27 15:43:58 +00:00
// config:
// reset
// enigma type
// for each rotor
// - ringsetting
// - rotor selection
// plugboard
// - clear
// - set pair
2020-09-23 20:16:06 +00:00
void setup () {
2020-09-24 01:05:03 +00:00
// Serial.begin(115200);
2020-09-23 20:16:06 +00:00
kbd.begin();
2020-09-24 01:05:03 +00:00
lcd.begin(LCD_COLS, LCD_ROWS);
lcd.setCursor(0,1);
2020-09-27 15:43:58 +00:00
lcd.print("Enigma");
lcd.setCursor(0,2);
lcd.print("M3");
2020-09-23 20:16:06 +00:00
2020-09-27 15:43:58 +00:00
test();
2020-09-23 20:16:06 +00:00
init_buf(input_buf);
init_buf(encoded_buf);
disp_rotors();
2020-09-24 01:05:03 +00:00
disp_plugboard();
2020-09-23 20:16:06 +00:00
}
2020-09-27 15:43:58 +00:00
bool demo_mode = false;
bool config_mode = true;
void enigma () {
2020-09-23 20:16:06 +00:00
kbd.tick();
while (kbd.available()) {
keypadEvent e = kbd.read();
if (e.bit.EVENT == KEY_JUST_PRESSED) {
char plain_c = (char)e.bit.KEY;
char encoded_c = encode(plain_c);
disp_rotors();
add_to_buf(input_buf, plain_c);
add_to_buf(encoded_buf, encoded_c);
disp_buf(input_buf, 0);
disp_buf(encoded_buf, 1);
}
}
delay(10);
}
2020-09-27 15:43:58 +00:00
void demo () {
}
void config () {
// demo? y/n -> demo_mode
// enigma type? menu -> enigma_type
// rotor[
}
void loop () {
if (config_mode) {
config();
} else if (demo_mode) {
demo();
} else {
enigma();
}
}
/*
Eintrittwalze entry wheel (stator)
Greek wheels Beta and Gamma [for obvious reasons] used in the M4
Grundstellung ground setting
Ringstellung ring setting
Stecker plug
Steckerbrett plugboard [sometimes referred to as "Stecker board"]
Umkehrwalze reflector
Walzen wheels
Walzenlage wheel order
*/
/*
https://cryptocellar.org/Enigma/index.html
http://www.mlb.co.jp/linux/science/genigma/enigma-referat/node4.html#SECTION00043000000000000000
*/