Puncte:0

puteți genera rapid un număr de identificare, fără coliziuni și fără ca ID-urile să dezvăluie informații?

drapel ru

Există o modalitate standard de a genera numere de identificare una după alta, astfel încât:

  • Puteți garanta, sau aproape garanta, că evitați coliziunile. (Prin „aproape garanție”, mă refer, de exemplu, dacă ați generat numere complet aleatorii de 24 de cifre și ați generat „doar” 1 milion dintre ele, atunci chiar și cu paradoxul zilei de naștere, șansele de coliziune ar fi mici.)
  • Vrei ca numerele de identificare să fie scurte, nu grele - în special, tu nu doresc să se bazeze pe lungimea numărului de identificare (și pe alegerea valorilor aleatorii) pentru a evita coliziunile, așa cum este descris în punctul anterior. Trebuie să o faci altfel.
  • Nu doriți să evitați coliziunile de fiecare dată când generați o nouă valoare, uitându-vă la toate valorile preexistente pentru a vedea dacă a fost deja folosită. Aceasta ar fi doar o căutare log(n) de fiecare dată pe o listă sortată, dar să presupunem că vreau să evit oricum asta.
  • Nu doriți ca numărul de identificare să dezvăluie informații despre momentul în care a fost generat sau despre câte numere de identificare au fost generate între numărul ID X și numărul ID Y. Fără această condiție, problema este banală; ați putea folosi doar ora ceasului (plus o valoare aleatorie suficient de mare pentru a evita coliziunile între numerele generate în aceeași valoare a timpului de ceas) sau puteți folosi doar numere întregi secvențiale (cu excepția faptului că acum un atacator știe că dacă cineva a generat valoarea ID-ului 5000 pe 1 martie și valoarea ID 6000 pe 1 aprilie, au fost generate alte 1000 de valori între atunci).

Am încercat să găsesc un răspuns banal, dar niciunul dintre cele pe care le-am încercat nu părea să funcționeze. Puteți lua pur și simplu codul hash SHA-256 al numerelor 1, 2, 3 etc. (plus o cheie secretă), dar aceasta are aceeași problemă ca doar alegerea numerelor aleatorii din spațiul disponibil -- dacă vă bazați pe lungimea hash-ului (de exemplu SHA-256) pentru a evita coliziunile, numerele de identificare rezultate sunt lungi și greu de manevrat, iar dacă scurtați hash-ul, creșteți șansa de coliziuni.

Sau ați putea genera ID-uri noi incrementând de fiecare dată cu o valoare aleatorie între 1 și n, în loc să creșteți întotdeauna cu 1. Problema este că, în funcție de ceea ce poate face atacatorul, își pot da seama ce este n -- dacă au capacitatea de a genera două ID-uri în secvență și de a face acest lucru în mod repetat, ar putea da seama de n, sau dacă au capacitatea de a verifica care ID-uri sunt valide, ar putea verifica fiecare număr dintr-un interval mic, pentru a vedea cât de dens este împachetat validul ID-urile sunt și descoperă n din asta.

Singura soluție pe care am putut să o găsesc este următoarea: mai întâi, faceți ceva pregătire în avans. Pentru oricâte valori de ID vă așteptați să generați (să zicem, 1 milion), luați toate numerele întregi de la 1 la 1 milion și, în ordine, începeți să calculați hash-ul fiecărui număr întreg plus o cheie secretă. Trunchiați hash-ul la orice valoare credeți că este suficient de scurtă. Dar, cu o trunchiere suficient de scurtă, vă așteptați să vedeți ciocniri. Deci, de fiecare dată când generați un nou hash trunchiat pentru un anumit întreg, verificați-l cu valorile generate anterior și, dacă există o coliziune, adăugați acel număr întreg la o listă L de numere întregi în care hash-ul acelui întreg se ciocnește cu cel al unui întreg mai mic. (Deci, de fapt, dacă planul tău este să generezi 1 milion de ID-uri, în timpul lucrării sale de pregătire va trebui să mergi puțin la ultimul milion de numere întregi, pentru a compensa cele pe care le-ai omis.)

Apoi, în timpul execuției, când generați ID-urile, începeți doar cu un numărător întreg. De fiecare dată când generați un nou ID, creșteți numărul întreg și verificați dacă este în lista dvs. L, iar dacă este, săriți și pentru a trece la următorul întreg. (Acest lucru implică o „căutare log n”, aparent încălcând una dintre regulile pe care le-am declarat, dar ceea ce voiam cu adevărat să fac era să evit să fiu nevoit să verific fiecare nouă valoare ID cu fiecare valoare generată până acum; verificarea L ar trebui să fie mult mai rapidă.) Și puteți ajusta acest lucru pentru compromisuri (cu cât faceți hashurile trunchiate mai lungi, cu atât L va fi mai scurt și, prin urmare, cu atât verificarea va fi mai scurtă de fiecare dată când generați un nou ID; dar este posibil ca valorile ID mai lungi să nu fie de dorit).

Dar asta se simte ca un hack. Există o modalitate standard? Dacă nu, vă puteți gândi la o modalitate mai bună?

kelalaka avatar
drapel in
Pentru o cheie fixă, criptați cu modul AES-ECB. AES este o familie de permutări și o cheie selectează una dintre ele. În plus, aș opta pentru SHA-512, SHAKE sau BLAKE-512, etc. Nu se așteaptă găsirea unei coliziuni, totuși, odată ce vei găsi, vei fi faimos!
poncho avatar
drapel my
@kelalaka: SHA-512? Deci, sugerezi un id de 512 biți (154 de cifre)???
kelalaka avatar
drapel in
Crezi că poți avea mai mult de $2^{100}$ ID-uri?
poncho avatar
drapel my
@kelalaka: dacă te bazezi pe rezistența la coliziune a lui SHA-512, trebuie să scoți întregul hash - un hash trunchiat nu ar implica o coliziune în funcția hash originală.
Bennett avatar
drapel ru
Problema cu utilizarea oricărui tip de hash pentru a genera ID-ul este aceeași problemă cu utilizarea numerelor aleatoare, așa cum este descris în declarația problemei -- dacă evitați coliziunile făcându-le pur și simplu lungi, este prea greu de manevrat și dacă le trunchiați la faceți-le mai scurte, creșteți șansa unei coliziuni. Caut o modalitate de a evita coliziunile fără a folosi pur și simplu valori foarte lungi.
Puncte:2
drapel my

Cea mai eficientă modalitate ar fi să folosiți a Algoritmul de criptare pentru păstrarea formatului; acesta este un algoritm care este o permutare peste un set de dimensiuni arbitrare (de exemplu, o secvență de 10 cifre zecimale).

Utilizarea acesteia ar fi simplă: ați alege o cheie aleatorie și ați stoca-o; ai păstra, de asemenea, un număr de secvență (în același format ca rezultatul, de exemplu, ai putea începe cu 0000000000). Apoi, când vine timpul să generați următorul ID, ați incrementa numărul de secvență și ați trimite-l prin algoritmul FPE; acesta este următorul tău ID [1].

Deoarece algoritmul FPE este o permutare, nu veți genera niciodată același ID de două ori până la încheierea numărului de secvență; prin urmare, fără ciocniri. Puteți face ID-ul cât de scurt este necesar (algoritmii actuali FPE au probleme cu spațiile cu adevărat mici; dacă păstrați ID-ul să fie, să zicem, cel puțin 6 cifre, ați fi în siguranță). Și, deoarece algoritmii FPE sunt siguri, orice ID nu oferă nicio informație despre niciun alt ID (inclusiv ordinea relativă de generare).

Dezavantajul: nu există (din câte știu eu) biblioteci FPE comune disponibile. Pentru utilizare, aș sugera FF1 din acest document; implementarea acesteia de la zero ar fi puțin de lucru (dar v-ar satisface nevoile).


O metodă mai puțin eficientă, dar mai ușor de implementat ar fi să faceți o variație a ceea ce ați sugerat: păstrați o listă cu ID-urile pe care nu le-ați atribuit încă.

Aici, în timpul configurării, veți inițializa o listă cu toate ID-urile posibile în ordine secvențială, să spunem de la 000000 la 999999. În plus, veți seta o valoare N la cel mai mare ID nealocat (în acest exemplu, N = 999999).

Apoi, când vine timpul să emiti un nou ID, ai alege un număr aleatoriu x între 0 și N (inclusiv). Apoi, ați schimba ID-urile la indicii N și x (și dacă x=N, atunci această operație de schimb nu face nimic); apoi, veți scoate valoarea care nu este în indicele N (și apoi decrementați N).

Asta e; acesta este de fapt Fisher-Yates amestecă, ați putea face acest lucru la cerere (cum am scris-o), sau puteți face toate amestecurile la momentul configurarii (și doar citiți lucruri de pe listă atunci când generați ID-uri).

Acest lucru este mai puțin eficient decât ideea FPE - atât pentru că configurarea este mai implicată, cât și pentru că trebuie să păstrați o listă mare de ID-uri în jur. Pe de altă parte, este mult mai ușor de implementat decât să încerci să implementezi FF1 de la zero...


[1]: Algoritmii de criptare pentru păstrarea formatului au, de asemenea, o „ajustare”; ați dori să păstrați o valoare fixă ​​(de exemplu, șirul gol); o modificare oferă un serviciu de care utilizarea dvs. specifică nu are nevoie.

Postează un răspuns

Majoritatea oamenilor nu înțeleg că a pune multe întrebări deblochează învățarea și îmbunătățește legătura interpersonală. În studiile lui Alison, de exemplu, deși oamenii își puteau aminti cu exactitate câte întrebări au fost puse în conversațiile lor, ei nu au intuit legătura dintre întrebări și apreciere. În patru studii, în care participanții au fost implicați în conversații ei înșiși sau au citit transcrieri ale conversațiilor altora, oamenii au avut tendința să nu realizeze că întrebarea ar influența – sau ar fi influențat – nivelul de prietenie dintre conversatori.