Szukam sposobu na zaszyfrowanie / zaciemnienie identyfikatora całkowitego na inną liczbę całkowitą. Dokładniej, potrzebuję funkcji int F(int x)
, więc to
- x <-> F (x) to korespondencja jeden do jednego (jeśli x! = y, F (x)! = F (y))
- biorąc pod uwagę F (x), łatwo jest znaleźć x - więc F nie jest funkcją haszującą
- biorąc pod uwagę x i F (x), trudno / niemożliwe jest znaleźć F (y), coś takiego jak
x ^ 0x1234
nie zadziała
Dla jasności nie szukam silnego rozwiązania do szyfrowania, to tylko zaciemnianie. Wyobraźmy sobie aplikację internetową z adresami URL, takich jak example.com/profile/1
, example.com/profile/2
itp profili sami nie są tajne, ale chciałbym, aby zapobiec przypadkowym podglądaczy do widzenia / pobrać wszystkie profile jeden po drugim, więc wolałbym ukryć je za coś podobnego example.com/profile/23423
, example.com/profile/80980234
itp Chociaż tokeny przechowywane w bazie danych mogą wykonać to zadanie dość łatwo, jestem ciekawy, czy jest do tego dostępna jakaś prosta matematyka.
Jednym z ważnych wymagań, których nie byłem pewien x,x+1,...,x+n
, jest to, że wyniki powinny wyglądać „losowo”, to znaczy biorąc pod uwagę sekwencję , F(x),F(x+1)...F(x+n)
nie powinny tworzyć żadnego rodzaju progresji.
źródło
Odpowiedzi:
Ukryj to za pomocą kombinacji 2 lub 3 prostych metod:
x
iy
że są multiplikatywne odwrotności siebie (modulo 2 32 ), a następnie pomnożyć przezx
ukrycie i pomnożyć przezy
przywracanie, wszystkie mnożenia są modulo 2 32 (źródło: „Praktyczne wykorzystanie multyplikatywnych odwrotności” autorstwa Erica Lippert )Metoda systemu liczbowego o zmiennej długości sama w sobie nie spełnia wymagań „progresji”. Zawsze tworzy krótkie progresje arytmetyczne. Ale w połączeniu z inną metodą daje dobre rezultaty.
To samo dotyczy metody reprezentacji modułowej.
Oto przykład kodu w C ++ dla 3 z tych metod. Przykład Shuffle bits może wykorzystywać różne maski i odległości, aby być bardziej nieprzewidywalnym. Pozostałe 2 przykłady są dobre dla małych liczb (tylko po to, aby dać pomysł). Powinny zostać rozszerzone, aby poprawnie zaciemnić wszystkie wartości całkowite.
źródło
Chcesz, aby transformacja była odwracalna, a nie oczywista. To brzmi jak szyfrowanie, które przyjmuje liczbę z danego zakresu i generuje inną liczbę z tego samego zakresu. Jeśli Twój zakres obejmuje liczby 64-bitowe, użyj DES. Jeśli Twój zakres obejmuje liczby 128-bitowe, użyj AES. Jeśli chcesz mieć inny zakres, najlepszym rozwiązaniem jest prawdopodobnie szyfr Hasty Pudding , który jest zaprojektowany do radzenia sobie z różnymi rozmiarami bloków i zakresami liczb, które nie pasują dokładnie do bloku, na przykład od 100 000 do 999 999.
źródło
Pod względem bezpieczeństwa zaciemnianie nie jest wystarczające.
Jeśli jednak próbujesz udaremnić przypadkowego widza, polecam połączenie dwóch metod:
Oto przykład (przy użyciu pseudokodu):
Nie testowałem tego, ale myślę, że jest to odwracalne, powinno być szybkie i niezbyt łatwe do wypróbowania metody.
źródło
return x XOR rotr(31415927, 5)
prawda? Ostatni xor cofa pierwszy, a obroty cofają się nawzajem. Oczywiście każdy łańcuch operacji odwracalnych jest również odwracalny, więc spełnia ten warunek.x = x XOR F(0)
, lubx = x XOR 3087989491
, lubx = x XOR rotr(31415927, 5)
. Twoje pierwsze i ostatnie xory negują się nawzajem, więc wszystko, co robisz, to xorowanie danych wejściowych z przesunięciem bitowym za pomocą klucza - lub równoważnie, xowanie danych wejściowych za pomocą klucza z przesunięciem bitowym. Zauważ, że jest to prawdą, nawet jeśli użyłeś różnych kluczy na każdym etapie - wszystkie klucze można połączyć w jeden klucz, który można xorować za pomocą zwykłego tekstu.Uważam, że ten konkretny fragment kodu Python / PHP jest bardzo przydatny:
https://github.com/marekweb/opaque-id
źródło
Napisałem trochę kodu JS, korzystając z pomysłów z tego wątku:
Daje dobre wyniki, takie jak:
Testowanie z:
XOR1
iXOR2
są po prostu liczbami losowymi od 0 doMAX
.MAX
jest2**32-1
; Powinieneś ustawić to na cokolwiek myślisz, że będzie twój najwyższy identyfikator.COPRIME
jest liczbą względnie pierwszą w /MAX
. Myślę, że same liczby pierwsze są względnie pierwsze z każdą inną liczbą (z wyjątkiem ich wielokrotności).INVERSE
jest trudna do rozgryzienia. Te posty na blogu nie dają prostej odpowiedzi, ale WolframAlpha może znaleźć to dla Ciebie . Po prostu rozwiąż równanie(COPRIME * x) % MAX = 1
dlax
.build
Funkcja jest coś, co stworzone, aby ułatwić tworzenie tych rurociągów kodowania / dekodowania. Możesz podawać mu dowolną liczbę operacji w[encode, decode]
parach. Te funkcje muszą być równe i przeciwne. TeXOR
funkcje są ich własne komplementy, więc nie trzeba się tam parę.Oto kolejna zabawna inwolucja :
(zakłada 24-bitowe liczby całkowite - po prostu zmień liczby na inny rozmiar)
źródło
n
to przyrostek liczby dla BigInts . To nowa funkcja JS, która pozwala przetwarzać naprawdę duże liczby. Musiałem go użyć, ponieważ mnożę przez naprawdę duże liczby, co może spowodować chwilowe przekroczenie wartości pośrednichNumber.MAX_SAFE_INTEGER
i utratę precyzji.Zrób wszystko z fragmentami identyfikatora, które ich nie zniszczą. Na przykład:
Aby odszyfrować, wykonaj to wszystko w odwrotnej kolejności.
Utwórz program, który „zaszyfruje” kilka interesujących wartości i umieści je w tabeli, którą możesz sprawdzić. Ten sam program PRZETESTUJ procedurę szyfrowania / deszyfrowania ZE wszystkimi zestawami wartości, które chcesz mieć w systemie.
Dodawaj rzeczy do powyższej listy do procedur, aż liczby będą wyglądać na odpowiednio zniekształcone.
W każdym innym przypadku zdobądź egzemplarz książki .
źródło
Napisałem artykuł o bezpiecznych permutacjach z szyframi blokowymi , które powinny spełnić twoje wymagania, jak podano.
Sugerowałbym jednak, że jeśli chcesz trudnych do odgadnięcia identyfikatorów, powinieneś po prostu ich używać w pierwszej kolejności: generować UUID i używać ich jako podstawowego klucza do swoich rekordów - nie ma potrzeby, aby móc konwertować na i z „prawdziwego” identyfikatora.
źródło
Nie wiem, jak „trudne” to ma być, jak szybko lub jak mało pamięci użyć. Jeśli nie masz ograniczeń pamięci, możesz stworzyć listę wszystkich liczb całkowitych, przetasować je i użyć tej listy jako mapowania. Jednak nawet dla 4-bajtowej liczby całkowitej potrzebowałbyś dużo pamięci.
Jednak można by to zmniejszyć, więc zamiast mapować wszystkie liczby całkowite, zamapowałbyś tylko 2 (lub w najgorszym przypadku 1) bajt i zastosowałbyś to do każdej grupy w liczbie całkowitej. Tak więc, używając 2 bajtów, liczba całkowita byłaby (grupa1) (grupa2) , odwzorowałbyś każdą grupę na losowej mapie. Ale to oznacza, że jeśli zmienisz tylko grupę 2, mapowanie dla grupy 1 pozostanie takie samo. Można to „naprawić”, mapując różne bity do każdej grupy.
Więc * (grupa2) może być (bit 14,12,10,8,6,4,2,0) tak, dodając 1 zmieniłaby zarówno GRUPA1 i Grupa2 .
Mimo to jest to tylko zabezpieczenie przez zaciemnienie, każdy, kto może wprowadzić liczby do twojej funkcji (nawet jeśli utrzymujesz tę funkcję w tajemnicy), może dość łatwo to rozgryźć.
źródło
Wygeneruj prywatny klucz symetryczny do użytku w aplikacji i zaszyfruj nim swoją liczbę całkowitą. Spełni to wszystkie trzy wymagania, w tym najtrudniejszy # 3: należałoby odgadnąć klucz, aby złamać schemat.
źródło
To, co tu opisujesz, wydaje się być przeciwieństwem funkcji jednokierunkowej: łatwo ją odwrócić, ale bardzo trudno ją zastosować. Jedną z opcji byłoby użycie standardowego, gotowego algorytmu szyfrowania klucza publicznego, w którym ustalasz (tajny, losowo wybrany) klucz publiczny, który utrzymujesz w tajemnicy, oraz klucz prywatny, który udostępniasz światu. W ten sposób twoja funkcja F (x) byłaby szyfrowaniem x przy użyciu klucza publicznego. Następnie możesz łatwo odszyfrować F (x) z powrotem do x, używając prywatnego klucza odszyfrowywania. Zauważ, że role klucza publicznego i prywatnego są tutaj odwrócone - dajesz każdemu klucz prywatny, aby mogli odszyfrować funkcję, ale trzymaj klucz publiczny w tajemnicy na serwerze. W ten sposób:
Ma to wiele zalet. Po pierwsze, możesz mieć pewność, że system kryptograficzny jest bezpieczny, ponieważ jeśli używasz ugruntowanego algorytmu, takiego jak RSA, nie musisz się martwić o przypadkową niepewność. Po drugie, istnieją już biblioteki, które to robią, więc nie musisz zbytnio kodować i możesz być odporny na ataki typu side-channel. Wreszcie, możesz umożliwić każdemu przejście i odwrócenie F (x) bez możliwości obliczenia F (x).
Jeden szczegół - zdecydowanie nie powinieneś tutaj używać tylko standardowego typu int. Nawet w przypadku 64-bitowych liczb całkowitych jest tak mało możliwych kombinacji, że osoba atakująca może po prostu spróbować odwrócić wszystko na siłę, aż znajdzie szyfrowanie F (y) dla niektórych y, nawet jeśli nie ma klucza. Sugerowałbym użycie czegoś w rodzaju wartości 512-bitowej, ponieważ nawet atak science fiction nie byłby w stanie tego brutalnie wymusić.
Mam nadzieję że to pomoże!
źródło
Jeśli
xor
jest do zaakceptowania dla wszystkiego, ale wnioskującF(y)
podanex
iF(x)
wtedy myślę, że można zrobić z solą . Najpierw wybierz tajną funkcję jednokierunkową. Na przykładS(s) = MD5(secret ^ s)
. WtedyF(x) = (s, S(s) ^ x)
gdzies
jest wybierane losowo. Napisałem to jako krotkę, ale można połączyć obie części w liczbę całkowitą, npF(x) = 10000 * s + S(s) ^ x
. Odszyfrowanies
ponownie wyodrębnia sól i używaF'(F(x)) = S(extract s) ^ (extract S(s)^x)
. Podanex
iF(x)
możesz zobaczyćs
(choć jest to nieco zaciemnione) i możesz wywnioskować,S(s)
ale dla innego użytkownikay
z inną losową solą,t
której użytkownikF(x)
nie może znaleźćS(t)
.źródło
S(s)
również będzie wyglądał losowo, więcF(x)
nie będzie miał żadnego postępu.