Implementar a máquina Enigma

18

A máquina Enigma é uma máquina de cifra bastante complexa usada pelos alemães e outros para criptografar suas mensagens. É seu trabalho implementar esta máquina *.

Etapa 1, rotação

Nossa máquina enigma possui 3 slots para rotores e 5 rotores disponíveis para cada um desses slots. Cada rotor possui 26 posições possíveis diferentes (de Aa Z). Cada rotor tem uma posição de entalhe predefinida :

Rotor  Notch
------------
1      Q
2      E
3      V
4      J
5      Z

Ao pressionar a tecla, ocorrem as seguintes etapas:

  1. O rotor no slot 1 gira
  2. Se o rotor no slot 1 ultrapassar o entalhe, ele girará o rotor no slot 2.
  3. Se o rotor no slot 2 estiver no seu entalhe (mas não se moveu apenas para lá), os rotores 2 e 3 giram uma vez.

Se estamos usando rotores 1,3,5 e eles estão em posições P,U,Hentão a sequência de posições é: P,U,H> Q,U,H> R,V,H>S,W,I

Etapa 2, Substituição

Cada um dos rotores realiza uma substituição simples de caracteres. A seguir, é apresentado um gráfico de cada um dos rotores na Aposição:

  ABCDEFGHIJKLMNOPQRSTUVWXYZ
  --------------------------
1 EKMFLGDQVZNTOWYHXUSPAIBRCJ
2 AJDKSIRUXBLHWTMCQGZNPYFVOE
3 BDFHJLCPRTXVZNYEIWGAKMUSQO
4 ESOVPZJAYQUIRHXLNFTGKDCMWB
5 VZBRGITYUPSDNHLXAWMJQOFECK
R YRUHQSLDPXNGOKMIEBFZCWVJAT

Rotor 1 na posição T é PAIBRCJEKMFLGDQVZNTOWYHXUS, que substitua a letra Cpara I.

Depois que os três rotores executam sua substituição, o refletor é atingido (listado como Racima). Ele realiza sua própria substituição e reflete o sinal de volta através dos rotores. Os rotores executam uma substituição reversa na ordem inversa.

Meios de substituição inversa que, em vez de rotor 1, substituindo Acom E, ela substitui EcomA

Os slots são preenchidos com rotores 1,2,3, todos em posição A. A letra Qsegue o caminho Q>X>V>Matravés dos rotores. Mreflete para O, que segue o caminho reverso de O>Z>S>S. Portanto, Aé substituído por S.

Entrada / Saída

Você passou:

  1. Uma lista de 3 rotores (como números inteiros)
  2. Uma lista de 3 posições iniciais do rotor (como letras)
  3. Uma sequência que precisa ser criptografada.

Você pode assumir que sua entrada será bem formada e todos os caracteres serão maiúsculos, sem espaços.

Você deve retornar a sequência criptografada.

Opcionalmente, você pode aceitar os rotores, entalhes e refletores como entrada. Para aqueles que não conseguem tirar 95 bytes de sua pontuação, como95 = ceil(log2(26 letters ^(26*6 rotors +5 notches))/8 bytes)

Casos de teste

Rotor Position Input              Output
4,1,5 H,P,G    AAAAAAAAA          RPWKMBZLN
1,2,3 A,A,A    PROGRAMMINGPUZZLES RTFKHDOVZSXTRMVPFC
1,2,3 A,A,A    RTFKHDOVZSXTRMVPFC PROGRAMMINGPUZZLES
2,5,3 U,L,I    GIBDZNJLGXZ        UNCRACKABLE

Minha implementação pode ser encontrada no Github . Eu testei, mas posso ter erros na minha implementação (o que significa que meus casos de teste provavelmente estão errados).

* Tentei fazer isso o mais preciso possível , mas devido às variações entre as máquinas, posso ter alguns detalhes incorretos. No entanto, sua tarefa é implementar o que descrevi, mesmo que eu seja impreciso. Não incluo o painel por simplicidade

Nathan Merrill
fonte
11
Esta é uma implementação correta para o algoritmo de criptografia usado no Enigma I, M3 e M4. Todas as configurações estão presentes, o painel de controle e o comutador Uhr também funcionam: https://github.com/arduinoenigma/ArduinoEnigmaEngineAndUhr Esse é o mesmo mecanismo de criptografia usado no Arduino Enigma Machine Simulator
Eu acho que entendo, mas não parece funcionar direito. Aqui está uma lista explicando gist.github.com/JJ-Atkinson/ddd3896fe10d85b3b584 .
J Atkin
No primeiro exemplo, você disse "se estivermos usando os rotores 1, 3 e 5", mas acho que seriam os rotores 1, 2 e 5 (ou o que for para o final).
coredump
@coredump Fixed
Nathan Merrill
Meu entendimento de como os rotores funcionam ainda está incorreto?
J Atkin 10/02

Respostas:

4

Python 3, 403 bytes

Eu acho que isso está funcionando corretamente. Os rotores passaram para ele:

def z(p,o,m,f,g,h):
 O=ord;b=lambda a:a[1:]+a[:1];d=lambda a:chr(a+O('A'));e=lambda a:O(a)-O('A');i=[list(g[i-1])for i in p];j=[f[i-1]for i in p];i=[x[e(y):]+x[:e(y)]for x,y in zip(i,o)];k=[]
 for l in m:
  if i[0][0]==j[0]:i[1]=b(i[1])
  elif i[1][0]==j[1]:i[1]=b(i[1]);i[2]=b(i[2])
  i[0]=b(i[0]);c=l
  for n in i:c=n[e(c)]
  c=h[e(c)]
  for n in reversed(i):c=d(n.index(c))
  k+=[c]
 return''.join(k)

fé o entalhe, gsão os rotores e hé o refletor.

Ungolfed:

shift = lambda rotor: rotor[1:] + rotor[:1]
letter = lambda num: chr(num + ord('A'))
number = lambda chr: ord(chr) - ord('A')


def encode(rotors, rotorStart, message, defaultRotors, reflector, rotorNotchPositions):
    usedRotors = [list(defaultRotors[i - 1]) for i in rotors]
    notches = [rotorNotchPositions[i - 1] for i in rotors]
    usedRotors = [rotor[number(offset):] + rotor[:number(offset)] for rotor, offset in zip(usedRotors, rotorStart)]

    sub = []

    for char in message:
        # print([''.join(rotor) for rotor in usedRotors])
        if usedRotors[0][0] == notches[0]:
            usedRotors[1] = shift(usedRotors[1])
        elif usedRotors[1][0] == notches[1]:
            usedRotors[1] = shift(usedRotors[1])
            usedRotors[2] = shift(usedRotors[2])

        usedRotors[0] = shift(usedRotors[0])

        c = char
        for rotor in usedRotors:
            c = rotor[number(c)]
        c = reflector[number(c)]
        for rotor in reversed(usedRotors):
            c = letter(rotor.index(c))
        sub += [c]
        print([''.join(rotor) for rotor in usedRotors], char, c, message)

    return ''.join(sub)

rotorNotchPositions = 'QEVJZ'
*defaultRotors, reflector = [
    #ABCDEFGHIJKLMNOPQRSTUVWXYZ#
    "EKMFLGDQVZNTOWYHXUSPAIBRCJ",  # 1
    "AJDKSIRUXBLHWTMCQGZNPYFVOE",  # 2
    "BDFHJLCPRTXVZNYEIWGAKMUSQO",  # 3
    "ESOVPZJAYQUIRHXLNFTGKDCMWB",  # 4
    "VZBRGITYUPSDNHLXAWMJQOFECK",  # 5
    "YRUHQSLDPXNGOKMIEBFZCWVJAT"   # R
]

#             Rotor       Position        Input                 Output
assert encode((4, 1, 5), ('H', 'R', 'G'), 'AAAAAAAAA',
              defaultRotors, reflector, rotorNotchPositions) == 'PXSHJMMHR'
assert encode((1, 2, 3), ('A', 'A', 'A'), 'PROGRAMMINGPUZZLES',
              defaultRotors, reflector, rotorNotchPositions) == 'RTFKHDOCCDAHRJJDFC'
assert encode((1, 2, 3), ('A', 'A', 'A'), 'RTFKHDOVZSXTRMVPFC',
              defaultRotors, reflector, rotorNotchPositions) == 'PROGRAMRXGVGUVFCES'
assert encode((2, 5, 3), ('U', 'L', 'I'), 'GIBDZNJLGXZ',
              defaultRotors, reflector, rotorNotchPositions) == 'UNCRAUPSCTK'

Eu acho que isso está funcionando, mas produz uma saída diferente, devido ao que (acho) é um bug no impl de referência.

J Atkin
fonte