Przegląd
Twoim celem jest wdrożenie szyfrowania RC4. Szyfrowanie RC4, wymyślone przez Rona Rivesta (znanego z RSA), zostało zaprojektowane tak, aby było bezpieczne, ale wystarczająco proste, aby można je było wdrożyć z pamięci przez żołnierzy wojskowych na polu bitwy. Obecnie istnieje kilka ataków na RC4, ale nadal jest on używany w wielu miejscach.
Twój program powinien zaakceptować pojedynczy ciąg zawierający zarówno klucz, jak i niektóre dane. Zostanie zaprezentowany w tym formacie.
\x0Dthis is a keythis is some data to encrypt
Pierwszy bajt reprezentuje długość klucza. Można założyć, że klucz nie będzie dłuższy niż 255 bajtów i nie będzie krótszy niż 1 bajt. Dane mogą być dowolnie długie.
Twój program powinien przetworzyć klucz, a następnie zwrócić zaszyfrowane dane. Szyfrowanie RC4 i deszyfrowanie są identyczne, więc użycie tego samego klucza do „zaszyfrowania” tekstu zaszyfrowanego powinno zwrócić oryginalny tekst jawny.
Jak działa RC4
Inicjalizacja
Inicjalizacja RC4 jest dość prosta. Tablica stanu 256 bajtów jest inicjowana dla wszystkich bajtów od 0 do 255.
S = [0, 1, 2, 3, ..., 253, 254, 255]
Przetwarzanie klucza
Wartości w stanie są zamieniane na podstawie klucza.
j = 0
for i from 0 to 255
j = (j + S[i] + key[i mod keylength]) mod 256
swap S[i] and S[j]
Szyfrowanie
Szyfrowanie odbywa się poprzez użycie stanu do wygenerowania pseudolosowych bajtów, które następnie są przesyłane do danych XOR. Wartości w stanie są ciągle wymieniane.
i = j = 0
for each byte B in data
i = (i + 1) mod 256
j = (j + S[i]) mod 256
swap S[i] and S[j]
K = S[(S[i] + S[j]) mod 256]
output K XOR B
Oczekiwane wejścia i wyjścia
Znaki niedrukowalne będą wyświetlane w \xAB
formacie.
Dane wejściowe: Dane \x01\x00\x00\x00\x00\x00\x00
wyjściowe: Dane \xde\x18\x89A\xa3
wyjściowe (szesnastkowe):de188941a3
Dane wejściowe: Dane \x0Dthis is a keythis is some data to encrypt
wyjściowe: Dane \xb5\xdb?i\x1f\x92\x96\x96e!\xf3\xae(!\xf3\xeaC\xd4\x9fS\xbd?d\x82\x84{\xcdN
wyjściowe (szesnastkowe):b5db3f691f9296966521f3ae2821f3ea43d49f53bd3f6482847bcd4e
Dane wejściowe: Dane \x0dthis is a key\xb5\xdb?i\x1f\x92\x96\x96e!\xf3\xae(!\xf3\xeaC\xd4\x9fS\xbd?d\x82\x84{\xcdN
wejściowe (szesnastkowe): Dane 0d746869732069732061206b6579b5db3f691f9296966521f3ae2821f3ea43d49f53bd3f6482847bcd4e
wyjściowe:this is some data to encrypt
Dane wejściowe: Dane Sthis is a rather long key because the value of S is 83 so the key length must matchand this is the data to be encrypted
wyjściowe: Dane \x96\x1f,\x8f\xa3%\x9b\xa3f[mk\xdf\xbc\xac\x8b\x8e\xfa\xfe\x96B=!\xfc;\x13`c\x16q\x04\x11\xd8\x86\xee\x07
wyjściowe (szesnastkowe):961f2c8fa3259ba3665b6d6bdfbcac8b8efafe96423d21fc3b13606316710411d886ee07
źródło
\xde
, powinien mieć 1 bajt, a konwersja go na liczbę (przez pythonord()
lub javascript.charCodeAt(0)
) powinna zwrócić 222 (0xDE).Odpowiedzi:
JavaScript (ES6),
169168 bajtówPobiera dane wejściowe jako tablicę bajtów. Zwraca inną tablicę bajtów.
W jaki sposób?
Jest to w zasadzie dosłowna implementacja specyfikacji.
My najpierw podzielić tablicy wejściowej do l (długość klucza) i s (dane użytkowe kluczowym przesłaniem +). Następnie w kolejności wykonania:
Inicjalizujemy tablicę stanów S i definiujemy m = 255, która jest później wielokrotnie używana jako maska bitowa.
Przetasowujemy tablicę stanów. Zainicjowane tutaj indeksy I i J są faktycznie używane w następnym kroku.
Stosujemy szyfrowanie.
Przypadki testowe
Pokaż fragment kodu
źródło
138 bajtów, kod maszynowy (16-bit x86)
Uruchamianie: zapisz na codegolf.com, dosbox:
codegolf.com < input.bin
Nie wiem, czy będzie się to liczyło jako wpis, ale postanowiłem to zrobić ręcznie za pomocą edytorów szesnastkowych. Nie użyto do tego żadnych kompilatorów .
edytor ht faktycznie ma asembler, ale szczerze mówiąc, nie wiedziałem o tym, dopóki nie skończyłem ¯ \ _ (ツ) _ / ¯
Dlaczego i jak
Dlaczego: głównie dlatego, że chciałem sprawdzić, czy mogę to zrobić.
Jak: Zacząłem od stworzenia bajtu wypełnionego
NOP
si i następującą prostą częścią: próby napisania pierwszej pętli, która wypełniaS
tate wartościami 0..255. Przełączyłem się na Python i szybko napisałem wersję Pythona, aby mieć coś do przetestowania. Następnie upraszczałem kod Pythona do pseudo kodu / pseudo zestawu. Potem próbowałem pisać małe kawałki. Zdecydowałem, że najłatwiej będzie go odczytać ze standardowego wejścia, więc zacząłem od czegoś małego, który będzie czytał pojedynczy bajt, a następnie dodałem odczyt hasła i inicjalizację klucza. Ustalenie, które rejestry wybrać, zajęło mi trochę czasu.Wydawało mi się, że dodanie pętli de / encryption będzie łatwe, ale najpierw dostałem dekodowanie jednobajtowe, a potem całą pętlę.
Ostatnim krokiem było pozbycie się dodatkowych informacji
nops
, które pozostawiłem między instrukcjami podczas pisania (oczywiście wymagało to również naprawiania skoków).Można zobaczyć małą galerię, że starał się natomiast postępuje tutaj .
Sekcja
Program opiera się na niektórych wartościach początkowych po uruchomieniu (patrz zasoby poniżej).
wypełnij Stan (o 0x200)
odczytać długość, odczytać hasło, przechowywać hasło na ds: 0x300
inicjowanie stanu kluczem (
BP
służy do przechodzenia przez klucz,SI
służy do przechodzenia przez stan)generuj pseudolosową wartość (w
DL
, odDH
0 x x do xor przy 0x140)SI
- ints tego nie dotknie,BX
)DX
)PS Prawdopodobnie może to być jeszcze krótsze, ale zajęło to 4 wieczory, więc nie jestem pewien, czy chcę spędzić kolejne ...
Narzędzia i zasoby
źródło
C (gcc) ,
193 188 182 178 171172 bajtówWypróbuj online!
Edycja: teraz działa z kluczami dłuższymi niż 127 bajtów.
Edycja2: Dodano testcase z kluczem 129-bajtowym do łącza TIO.
Nieco mniej golfowa wersja
źródło
Zestaw instrukcji CPU x86, 133 bajty
A7D-9F8 = 85h = 133 bajty, ale nie wiem, czy obliczenia są prawidłowe, ponieważ dokładna liczba bajtów tej samej funkcji daje 130 bajtów ... Pierwszym argumentem funkcji, którą nazywam „cript”, jest ciąg znaków, drugim argumentem jest długość łańcucha (pierwszy bajt + długość klucza + długość wiadomości). Poniżej znajduje się plik języka asemblera do uzyskania tych procedur cript:
poniżej pliku C, aby sprawdzić wyniki:
wyniki:
źródło
JavaScript (ES6), 262 bajty
Zastanawiałem się nad użyciem tylko funkcji łańcuchowych, ale zdecydowałem się na golfizację algorytmu podanego tutaj: https://gist.github.com/farhadi/2185197 .
Mniej golfa
Pokaż fragment kodu
źródło
Python 2 , 203 bajty
Wypróbuj online!
f
jest generatorem (iterowalnym) ciągów znaków.Nie golfowany:
źródło
Rubinowy, 234 bajty
Niesprawdzone
źródło
C, 181 bajtów
dzięki pułapkowi cat za mniej bajtów:
f (a, n) w „a” byłaby tablica znaków 1Byte len + klucz + komunikat; w n jest rozmiar całej tablicy „a”, nie licząc ostatniego „\ 0”. kod do testu i wyniku byłby taki sam, jak używany do funkcji asemblacji.
źródło
APL (NARS), 329 znaków, 658 bajtów
jak zawsze sprawdzanie błędów byłoby wymagane od kogoś innego ... Wydaje się, że jest poprawne na wejściu i wyjściu, przetestuj:
Tak, wszystko można zmniejszyć ... ale na przykład uczyń mniej, funkcja xor może oznaczać, że będzie mniej ogólna ...
źródło
Rdza 348
Jest to dość strasznie duże, mam nadzieję, że ktoś mógłby podać jakieś sugestie.
Ungolfed: na placu zabaw play.rust-lang.org
źródło
k
zamiastkey
im
zamiastmsg
ifoo&255
zamiast(foo)%256