Szyfrowanie CipherSaber

11

Zaimplementuj program szyfrujący CipherSaber , jak opisano poniżej. Wytyczne:

  • Najmniejszy wpis w bajtach wygrywa.
    • Jednak w odejściu od norm możesz publikować ciekawe wpisy, nawet jeśli nie są to poważne wpisy w golfa.
  • Wpis zwykle oznacza program, który pobiera zwykły tekst ze standardowego wejścia i zapisuje tekst zaszyfrowany na standardowym wyjściu, z kluczem określonym (przez użytkownika) w dowolny sposób.
    • Jeśli jednak chcesz zastosować to jako procedurę, to też jest w porządku.
  • IV musi pochodzić z kryptograficznie bezpiecznego generatora liczb pseudolosowych. Jeśli Twój język tego nie obsługuje, wybierz inny. ;-)
  • Nie używaj żadnych bibliotek, wywołań systemowych ani instrukcji specyficznych dla kryptografii (innych niż PRNG, jak określono powyżej). Oczywiście ogólne operacje bitowe niskiego poziomu są w porządku.

CipherSaber jest odmianą RC4 / Arcfour, więc zacznę od opisania tego drugiego, a następnie zmian wprowadzonych przez CipherSaber.

0. RC4 / Arcfour

Arcfour jest w pełni określony gdzie indziej , ale dla kompletności opiszę go tutaj. (W przypadku jakichkolwiek rozbieżności między szkicem internetowym a tym opisem ten pierwszy ma charakter normatywny).

Konfiguracja klucza

Skonfigurować dwie tablice, Si S2, oba o długości 256, gdzie k_1znajduje się pierwszy bajt klucza i k_njest ostatnim.

S = [0, ..., 255]
S2 = [k_1, ..., k_n, k_1, ...]

( S2jest wypełniony bajtami klucza, raz po raz, aż wszystkie 256 bajtów zostaną wypełnione).

Następnie zainicjuj wartość j0 i potasuj 256 razy:

j = 0
for i in (0 .. 255)
    j = (j + S[i] + S2[i]) mod 256
    swap S[i], S[j]
end

To kończy konfigurację klucza. S2Tablica nie jest już używana tutaj i może być usunięty.

Generowanie strumienia szyfrów

Zainicjuj ii jna 0, a następnie wygeneruj strumień kluczy w następujący sposób:

i = 0
j = 0
while true
    i = (i + 1) mod 256
    j = (j + S[i]) mod 256
    swap S[i], S[j]
    k = (S[i] + S[j]) mod 256
    yield S[k]
end

Szyfrowanie / deszyfrowanie danych

  • Aby zaszyfrować, XOR wyjściowy strumień kluczy za pomocą zwykłego tekstu
  • Aby odszyfrować, XOR wyjście klucza z tekstem zaszyfrowanym

1. CipherSaber

CipherSaber (który implementujemy w tym pytaniu) jest odmianą RC4 / Arcfour na dwa sposoby:

10 bajtów IV / nonce

Podczas szyfrowania wiadomości należy uzyskać 10 losowych bajtów, takich jak via /dev/urandom, i zapisać je w pierwszych 10 bajtach zaszyfrowanego wyjścia. Podczas odszyfrowywania wiadomości pierwsze 10 bajtów wejścia to IV użyte do ich zaszyfrowania.

Etap konfiguracji klucza RC4 / Arcfour jest uruchamiany z passphrase || IVkluczem, gdzie passphrasejest określone przez użytkownika hasło, IVjest jak opisano powyżej i ||jest konkatenacją. Więc hasło „Witaj, świecie!” a IV „superkalifa” (jakkolwiek mało prawdopodobne jest to :-P) skutkowałoby kluczem „Witaj, świecie! superkalif”.

Wiele iteracji konfiguracji klucza

Aby zapobiec usterce, która spowodowała całkowite złamanie szyfrowania WEP, pętla tasowania etapu konfiguracji klucza RC4 jest uruchamiana określoną liczbę razy. Wartość jnależy zachować między iteracjami.

2. Wektory testowe

Oto kilka wektorów testowych, których możesz użyć do przetestowania swoich programów. Ponadto, Squeamish ossifrage stworzył narzędzie do szyfrowania i deszyfrowania CipherSaber , którego można użyć do sprawdzania poprawności wyników.

Wystarczy zaimplementować program szyfrujący. Nie trzeba dostarczać programu deszyfrującego, ale dane wyjściowe programu szyfrującego muszą poprawnie przechodzić do oryginalnego wejścia, gdy są przetwarzane za pomocą poprawnie zaimplementowanego programu deszyfrującego przy użyciu właściwego klucza.

Chris Jester-Young
źródło

Odpowiedzi:

7

Pyth, 100 bajtów

$import os$KsM$os.urandom(10)$JuuXN@LN,T=+Z+@NT@+CMzKT)bGQU=b256=Z0sCM+K.exCb@=XJ=N@LJ,hk=+Z@Jhk)sNw

Ten skrypt korzysta z $polecenia, które umożliwia wykonanie kodu w języku Python. Aby zapobiec wykonywaniu złośliwego kodu na serwerze, to polecenie jest wyłączone w kompilatorze online. Musisz uruchomić go z kompilatorem off-line, który możesz znaleźć tutaj .

Dane wejściowe mają format:

secret key
5 (number of repeats)
secret message

Program wyświetla zaszyfrowany ciąg znaków, który może zawierać znaki niedrukowalne. Jeśli chcesz to zweryfikować za pomocą narzędzia do szyfrowania i deszyfrowania CipherSaber , możesz użyć następującego kodu, który konwertuje ciąg znaków na ciąg cyfr szesnastkowych.

$import os$KsM$os.urandom(10)$JuuXN@LN,T=+Z+@NT@+CMzKT)bGQU=b256=Z0         
jdm.[2.HCd`0
sCM+K.exCb@=XJ=N@LJ,hk=+Z@Jhk)sNw

Pyth nie obsługuje kryptograficznie bezpiecznych liczb pseudolosowych, a importowanie ich z Pythona kosztuje 25 bajtów. Krótszy kod, który używa normalnego generatora liczb pseudolosowych lub Pythona, a także działa w kompilatorze online, to:

KmO=b256TJuuXN@LN,T=+Z+@NT@+CMzKT)bGQUb=Z0sCM+K.exCb@=XJ=N@LJ,hk=+Z@Jhk)sNw

Wypróbuj online: zwracanie ciągu znaków lub szeregu cyfr szesnastkowych

Kod nie jest niczym specjalnym. Tylko wiele nieprzyzwoitych zadań i natychmiastowe ponowne wykorzystanie obliczonych wyników i zastosowanie dwukrotnie sztuczki polegającej na zamianie list .

Wyjaśnienie:

                                  implicit: z = 1st input (= key string)
                                  Q = 2nd input (number of repetitions)
$import os$KsM$os.urandom(10)$
$import os$                       import Python's os module
              $os.urandom(10)$    create 10 cryptographically secure 
                                  pseudo-random bytes
            sM                    convert them to ints
           K                      store them in K

JuuXN@LN,T=+Z+@NT@+CMzKT)bGQU=b256
                             =b256assign b with 256
 u                         QUb    start with G = [0, 1, ..., 255], 
                                  evaluate the following expression Q times and
                                  update G with the result each time:
  u                      bG         start with N = G, 
                                    for each T in [0, 1, ..., 255] evaluate the
                                    following expression and update N each time:
                   CMz                convert key to list of ints
                  +   K               extend it with K
                 @     T              take the Tth element (modulo length)
              @NT                     take the Tth element of N
             +                        add these two values
           +Z                         add Z (with is initially 0)
          =                           and update Z with the result
        ,T  Z                         make the pair of indices [T, Z] 
     @LN                              look-up their values in N
   XN                   )             and switch these two values in N
J                                 assign the result (the key setup) to J

=Z0                               set Z to 0

sCM+K.exCb@=XJ=N@LJ,hk=+Z@Jhk)sNw 
                                w read a string from input (message)
     .e                           map each index k, char b in message to:
                         @Jhk       look-up the (k+1)th element in J
                      =+Z           add it to Z and update Z
                   ,hk  Z           make the pair of indices [k+1,Z]
                @LJ                 look-up their values in J
              =N                    assign the result to N
            XJ N             )      swap these values in J
           =                        and update J with the result
          @  J                sN    take the sum(N)th element of J
        Cb                          convert b to int
       x                            bitwise xor of these two elements
   +K                             insert K at the beginning
 CM                               convert each element to char
s                                 sum them (generate a string)
                                  implicitly print
Jakube
źródło
Najwyraźniej wbudowane funkcje Pyth nie mają kryptograficznie bezpiecznych liczb pseudolosowych . Możesz zachować swój wpis bez zmian i nie będzie on kwalifikować się do zielonego tikowania, lub możesz utworzyć wersję, która będzie używać urandom(która może być osobnym wpisem, jeśli chcesz), jeśli zależy ci na „wygraniu”. :-)
Chris Jester-Young
@ ChrisJester-Young Przepraszam za to. Nie sądziłem, że generator liczb losowych Pythona jest tak niepewny. Poprawiono go na koszt 25 bajtów.
Jakube,
4

Python 2 - 373 350 326 317 bajtów

Pyth może przyjdzie później. Definiuje jedną funkcję, c(p,d,r,m)która pobiera listy bajtów dla hasła i danych oraz int dla powtórzeń i trybu, który szyfruje, gdy 1, i odszyfrowuje, gdy 0. Jest tak, ponieważ jedyną różnicą w nich jest IV. Zwraca listę bajtów.

import os
B=256
def c(p,d,r,m):
    if m:v=map(ord,os.urandom(10))
    else:v,d=d[:10],d[10:]
    p+=v;S=range(B);T=(p*B)[:B];j=0;exec"for i in range(B):j=(j+S[i]+T[i])%B;S[i],S[j]=S[j],S[i]\n"*r;o=[];i=j=0
    for b in d:i=-~i%B;j=(j+S[i])%B;S[i],S[j]=S[j],S[i];k=(S[i]+S[j])%B;o+=[S[k]^b]
    return v+o if m else o

Oto niektóre funkcje kodu testowego / pomocnika:

phrase = "hello"
text = "Mary had a little lamb, little lamb, little lamb"
N = 5

def make_bytes(string):
    return map(ord, string)

def make_string(bytes):
    return "".join(map(chr, bytes))

def make_hex(bytes):
    return " ".join("%02x" % i for i in bytes)

def from_hex(hex_str):
    return [int(i, 16) for i in hex_str.split()]

cipher = c(make_bytes(phrase), make_bytes(text), N, 1)
print make_hex(cipher)
plain = c(make_bytes(phrase), cipher, N, 0)
print make_string(plain)
Maltysen
źródło
Wystarczy napisać program szyfrujący. Możesz więc usunąć else:v,d=d[:10],d[10:]część.
Jakube
3

Rubin - 263 znaków

To jest moja Rubinowa odpowiedź na oryginalne pytanie dotyczące stackoverflow w 2010 roku! Jest to enkoder i dekoder w jednym programie

Parametry są następujące:
E lub D (do kodowania lub dekodowania)
klucza
liczbę razy

$ ruby saber.rb e gnibbler 10 < in.txt | ruby saber.rb d gnibbler 10

o,k,l=ARGV;print o<'e'?(v=STDIN.read(10))*0:v=(0..9).map{rand(256).chr}.join;j=0;E=255
S=Array 0..255;(S*l.to_i).each{|i|j=j+S[i]+((k+v)*E)[i].ord&E;S[i],S[j]=S[j],S[i]};i=j=0
STDIN.each_byte{|c|i=i+1&E;j=j+S[i]&E;S[i],S[j]=S[j],S[i];print (c^S[S[i]+S[j]&E]).chr}
gnibbler
źródło
2

C, 312 bajtów

Akceptuje klucz i liczenie iteracji mieszania kluczy w wierszu poleceń, a następnie szyfruje wszystko na standardowym wejściu na standardowe wyjście. Korzysta z funkcji biblioteki BSD / Darwin arc4random(), która jest PRNG opartym na RC4. Automatycznie się wysiewa, więc wyniki będą za każdym razem inne.

unsigned char n,i,j,q,x,t,s[256],k[256];main(int c,char**v){for(strcpy(k,v[1]),n=strlen(k);x<10;x++)putchar(k[n++]=arc4random());do{s[i]=i;}while(++i);for(x=atoi(v[2]);x--;)do{t=s[i];s[i]=s[j+=s[i]+k[i%n]];s[j]=t;}while(++i);for(;(c=getchar())>0;){q+=s[++i];t=s[i];s[i]=s[q];s[q]=t;t=s[i]+s[q];putchar(c^s[t]);}}

Wersja Tidier:

unsigned char n,i,j,q,x,t,s[256],k[256];
main(int c,char**v) {
  for (strcpy(k,v[1]),n=strlen(k);x<10;x++) putchar(k[n++]=arc4random());
  do {
    s[i]=i;
  }
  while(++i);
  for (x=atoi(v[2]);x--;) do {
    t=s[i];
    s[i]=s[j+=s[i]+k[i%n]];
    s[j]=t;
  }
  while (++i);
  for (;(c=getchar())>0;) {
    q+=s[++i];
    t=s[i];
    s[i]=s[q];
    s[q]=t;
    t=s[i]+s[q];
    putchar(c^s[t]);
  }
}

Przykład:

$ echo -n 'Ciphersaber' | ./csgolf 'hello' 20 | xxd -p
0f6257c330e5e01c3eab07bc9cb4ee4c3eaa514a85
r3mainer
źródło
1

Python - 266 znaków

To jest moja odpowiedź Pythona na pierwotne pytanie dotyczące stackoverflow w 2010 roku! Jest to enkoder i dekoder w jednym programie

Parametry są następujące:
E lub D (do kodowania lub dekodowania)
klucza
liczbę razy

$ python saber.py e gnibbler 10 < in.txt | python saber.py d gnibbler 10

Ta wersja próbuje połączyć 2 pętle rc4 w jedną (zapisuje do tej pory 11 bajtów ...)

import os,sys;X,Y=sys.stdin.read,os.write;_,o,k,l=sys.argv;p='d'<o
V=(X,os.urandom)[p](10);Y(1,V*p);E,S=255,range(256)
for q in S*int(l),X():
 t=q<'';j=0;i=-t
 for c in q:i=i+1&E;j=j+S[i]+t*ord(((k+V)*E)[i])&E;S[i],S[j]=S[j],S[i];t or Y(1,chr(ord(c)^S[S[i]+S[j]&E]))
gnibbler
źródło