Funcția hash

O funcție hash ( de exemplu, funcție hash din  hash -  „turn into minced meat”, „hash” [1] ) sau o funcție de convoluție  este o funcție care convertește o matrice de date de intrare de lungime arbitrară într-un șir de biți de ieșire al unui lungime stabilită, realizată de un anumit algoritm . Transformarea efectuată de funcția hash se numește hashing . Datele de intrare se numesc matrice de intrare, „ cheie ” sau „ mesaj ”. Rezultatul conversiei se numește „ hash ”, „ hash code ”, „ hash sum ”, „ rezumat mesaj ”.

Funcțiile hash sunt utilizate în următoarele cazuri:

În cazul general (conform principiului lui Dirichlet ) nu există o corespondență unu-la-unu între codul hash și datele originale. Valorile returnate de funcția hash sunt mai puțin diverse decât valorile matricei de intrare. Cazul în care o funcție hash transformă mai mult de o matrice de intrare în aceleași rezumate se numește „ coliziune ”. Probabilitatea de coliziune este utilizată pentru a evalua calitatea funcțiilor hash.

Există mulți algoritmi de hashing cu proprietăți diferite. Exemple de proprietate:

Alegerea uneia sau alteia funcții hash este determinată de specificul problemei care se rezolvă. Cel mai simplu exemplu de funcție hash este încadrarea datelor cu un cod de redundanță ciclică ( CRC , cod de redundanță ciclică ) . 

Istorie

Criptarea mesajelor fără posibilitatea decriptării fără ambiguitate, dar numai pentru a confirma prioritatea autorului, a fost folosită de mult timp.

Galileo Galilei a observat inelele lui Saturn , pe care le-a confundat cu „urechi”. Nefiind sigur, dar dorind să-și afirme prioritatea, a publicat un mesaj cu literele rearanjate: smaismrmilmepoetaleumibunenugttauiras . În 1610, el a dezvăluit fraza originală: Altissimum planetam tergeminum obseruaui , care în latină înseamnă „a observat cea mai înaltă planetă în triplet”. Astfel, la momentul publicării primului mesaj, fraza originală nu a fost dezvăluită, dar s-a creat o oportunitate de a o confirma ulterior.

La mijlocul anilor 1650, Christian Huygens a văzut inelele și a publicat un mesaj cu litere aranjate alfabetic: aaaaaaacccccdeeeeghiiiiiiiillllmmnnnnnnnnnooooppqrrsttttuuuuuu . După ceva timp, a fost publicată și sintagma originală: Annulo cingitur, tenui plano, nusquam cohaerente, ad eclipticam inclinato  - „Înconjurat de un inel subțire, plat, nicăieri suspendat, înclinat spre ecliptică ”. Acest caz diferă de utilizarea unei funcții hash, inclusiv scopul de a confirma ulterior un mesaj nerezolvat, doar prin aceea că mesajul de ieșire nu are o lungime fixă, ci este determinat de lungimea intrării. De fapt, alfabetizarea literelor mesajului original este o funcție hash, dar numai cu un rezultat de lungime nefixă.

În ianuarie 1953, Hans Peter Luhn ( în germană:  Hans Peter Luhn ) (angajat la IBM ) a propus „codarea hash”. Donald Knuth îl creditează pe Hans ca fiind primul care a prezentat ideea sistematică de „hashing”.

În 1956, Arnold Dumey , în „  Computers and automation ”, a fost primul care a descris ideea de „hashing” așa cum o cunosc majoritatea programatorilor astăzi. Dumi a considerat „hashing” ca o soluție la „problema dicționarului”, a sugerat să folosească restul împărțirii cu un număr prim ca „adresă hash” [2] .

În 1957, W. Wesley Peterson a publicat un articol în IBM Journal of Research and Development despre găsirea de text în fișiere mari . Această lucrare este considerată prima lucrare „serioasă” despre „hashing”. În articol, Wesley a definit „adresarea deschisă”, subliniind scăderea performanței la ștergere. Șase ani mai târziu, a fost publicată lucrarea lui Werner Buchholz ( germană: Werner Buchholz ), în care a fost efectuat un studiu amplu al „funcțiilor hash”. În următorii câțiva ani, „hashing” a fost utilizat pe scară largă, dar nu a fost publicată nicio lucrare semnificativă.   

În 1967, „hashing” în sensul modern este menționat în cartea Principles of Digital Computing Systems de Herbert Hellermann [3] . În 1968, Robert Morris a publicat un  studiu amplu despre „hashing” în revista Communications of the ACM . Această lucrare este considerată o publicație „ cheie ”, introducând conceptul de „hashing” în circulația științifică și fixând termenul „hash”, folosit anterior doar de specialiști ( jargon ).

Până la începutul anilor 1990, în literatura de limbă rusă, datorită lucrărilor lui Andrei Petrovici Ershov , cuvântul „aranjament” a fost folosit ca echivalent cu termenul „hashing” , iar termenul „conflict” a fost folosit pentru „ coliziuni ”. ( A.P. Ershov a folosit „aranjament” din 1956 ). Ediția din 1989 în limba rusă a Algoritmilor și structurilor de date de Niklaus Wirth folosește și termenul „aranjament”. De asemenea, s-a propus denumirea metodei printr-un alt cuvânt rusesc: „ okroshka ”. Cu toate acestea, niciuna dintre aceste opțiuni nu a prins rădăcini, iar în literatura rusă se folosește predominant termenul „hashing” [4] .

Tipuri de „funcții hash”

O funcție hash „bună” trebuie să îndeplinească două proprietăți :

Să introducem notația:

Acesta este:

.

Ca exemplu de funcție hash „proastă”, putem cita funcția cu , care se potrivește cu un număr natural de zece cifre cu trei cifre selectate din mijlocul pătratului de douăzeci de cifre al numărului . S-ar părea că valorile „codurilor hash” ar trebui distribuite uniform între „ 000 ” și „ 999 ”, dar pentru datele „ reale ” acest lucru este adevărat numai dacă „ cheile ” nu au un număr „mare” de zerouri în stânga sau în dreapta [4 ] .

Să luăm în considerare câteva implementări simple și fiabile ale „funcțiilor hash”.

„Funcții hash” bazate pe diviziune

1. „Cod hash” ca restul împărțirii la numărul tuturor „hash-urilor” posibile

Funcția hash poate calcula „hash” ca restul împărțirii intrării la :

,

unde  este numărul tuturor hashurilor posibile (date de ieșire).

În același timp, este evident că pentru par valoarea funcției va fi par pentru par și impar - pentru impar . De asemenea, nu utilizați sistemul numeric al computerului ca grad de bază , deoarece „codul hash” va depinde doar de câteva cifre ale numărului situat în dreapta, ceea ce va duce la un număr mare de coliziuni . În practică, de obicei se alege unul simplu ; în majoritatea cazurilor această alegere este destul de satisfăcătoare.

2. „Cod hash” ca un set de coeficienți ai polinomului rezultat

O funcție hash poate efectua modul doi împărțirea datelor de intrare printr-un polinom . În această metodă , trebuie să fie o putere de doi, iar cheile binare ( ) sunt reprezentate ca polinoame , ca „cod hash” valorile coeficienților polinomului obținute ca restul împărțirii datelor de intrare la un pre -polinomul de grad selectat este „luat” :

Cu alegerea corectă este garantată absența coliziunilor între chei aproape identice [4] .

„Funcții hash” bazate pe înmulțire

Notăm prin simbol numărul de numere care pot fi reprezentate printr-un cuvânt mașină . De exemplu, pentru computerele pe 32 de biți compatibile cu IBM PC , .

Să alegem o constantă astfel încât să fie coprimă cu . Atunci o funcție hash care utilizează înmulțirea ar putea arăta astfel:

În acest caz, pe un computer cu un sistem de numere binar este o putere de doi și va consta din biții înalți din jumătatea dreaptă a produsului .

Un avantaj al funcțiilor hash bazate pe împărțire și înmulțire este utilizarea avantajoasă a non-aleatoriei cheilor reale. De exemplu, dacă cheile sunt o progresie aritmetică (de exemplu, succesiunea de nume „Nume 1”, „Nume 2”, „Nume 3”), o funcție hash care utilizează înmulțirea va mapa progresia aritmetică la o progresie aproximativ aritmetică de diferite valori hash, ceea ce va reduce numărul de coliziuni în comparație cu o situație aleatorie [4] .

Una dintre funcțiile hash care utilizează multiplicarea este funcția hash care utilizează hashul Fibonacci . Hashingul Fibonacci se bazează pe proprietățile raportului de aur . Ca constantă , se alege aici un număr întreg, cel mai apropiat de și coprim de , unde  este raportul de aur [4] .

Hashing de șir de lungime variabilă

Metodele de mai sus sunt aplicabile și atunci când este necesar să se ia în considerare chei formate din mai multe cuvinte sau chei de lungime variabilă.

De exemplu, puteți combina cuvinte într-unul singur folosind adăugarea modulo sau operația XOR . Unul dintre algoritmii care funcționează pe acest principiu este funcția hash Pearson.

Hashingul Pearson  este un algoritm propus de Peter  Pearson pentru procesoarele cu registre de 8 biți, a căror sarcină este să convertească rapid un șir de lungime arbitrară într-un cod hash. Ca intrare, funcția primește un cuvânt format din caractere, fiecare câte 1 octet și returnează o valoare în intervalul de la 0 la 255. În acest caz, valoarea codului hash depinde de fiecare caracter al cuvântului de intrare.

Algoritmul poate fi descris de următorul pseudocod, care ia un șir ca intrare și folosește un tabel de permutare :

h := 0 pentru fiecare c din indicele buclei W := h xor c h := T[index] sfârșitul buclei întoarcere h

Printre avantajele algoritmului:

  • ușurință de calcul;
  • absența unor astfel de date de intrare pentru care probabilitatea unei coliziuni este cea mai mare;
  • posibilitatea de modificare într-o funcție hash ideală [5] .

Ca metodă alternativă de hashing chei formate din caractere ( ), putem oferi calculul

[patru]

Hashing perfect

O  funcție hash ideală este o funcție care mapează fiecare cheie din set la un set de numere întregi fără ciocniri . În matematică, o astfel de transformare se numește mapare injectivă .

Descriere
  1. O funcție se numește funcție hash ideală pentru dacă este injectivă pe .
  2. O funcție se numește funcție hash ideală minimă pentru dacă este o funcție hash perfectă și .
  3. Pentru un număr întreg , funcția se numește o funcție hash perfectă (k-PHF) pentru dacă pentru fiecare avem .

Hashingul ideal este utilizat atunci când este necesar să se atribuie un identificator unic unei chei fără a stoca nicio informație despre cheie. Un exemplu de utilizare a hashingului ideal (sau mai degrabă , ideal): plasarea hashurilor asociate cu datele stocate în memorie mare și lentă în memorie mică și rapidă. Mărimea blocului poate fi aleasă astfel încât datele necesare să fie citite din memoria lentă într-o singură solicitare. O abordare similară este utilizată, de exemplu, în routerele hardware . De asemenea, hashingul ideal este folosit pentru a accelera munca algoritmilor pe grafice, dacă reprezentarea graficului nu se încadrează în memoria principală [6] .

Hashing universal

Hashingul universal se numește hashing, în care nu este utilizată o funcție hash specifică, ci o funcție hash este selectată dintr-o familie dată conform unui algoritm aleator . Hashingul universal este de obicei caracterizat printr-un număr redus de coliziuni; este utilizat, de exemplu, în implementarea tabelelor hash și în criptografie.

Descriere

Să presupunem că vrem să mapăm cheile din spațiu la numere . La intrare, algoritmul primește date de la un set de dimensiuni . Setul nu este cunoscut dinainte. De regulă, algoritmul ar trebui să furnizeze cel mai mic număr de coliziuni , ceea ce este dificil de realizat folosind o anumită funcție hash. Numărul de coliziuni poate fi redus prin alegerea aleatorie a unei funcții hash de fiecare dată când trebuie să faceți hash. Funcția hash este selectată dintr-un set specific de funcții hash numite familia universală [7] .

Metode de abordare a coliziunilor

O coliziune (uneori un conflict [2] sau o coliziune) este un caz în care o funcție hash pentru diferite blocuri de intrare returnează aceleași coduri hash.

Tehnici pentru tratarea coliziunilor în tabelele hash

Cele mai multe dintre primele lucrări care descriu hashingul s-au ocupat de metode de tratare a coliziunilor în tabelele hash. Apoi, funcțiile hash au fost folosite la căutarea textului în fișiere mari. Există două metode principale pentru a trata coliziunile în tabelele hash:

  1. metoda lanțului (metoda legăturii directe);
  2. metoda adresei deschise.

Când se utilizează metoda de înlănțuire, tabelul hash stochează perechi „ listă de chei conectată” - „cod hash”. Pentru fiecare cheie, se calculează un cod hash de către funcția hash; dacă codul hash a fost obținut mai devreme (pentru o altă cheie), cheia este adăugată la lista existentă de chei asociate cu codul hash; în caz contrar, o nouă pereche „listă de chei” - „cod hash” este creată, iar cheia este adăugată la lista creată. În general, dacă există chei și liste, dimensiunea medie a tabelului hash va fi . În acest caz, la căutarea prin tabel, comparativ cu cazul în care căutarea se efectuează secvenţial, cantitatea medie de muncă va scădea cu aproximativ un factor.

Când utilizați metoda de adresare deschisă, tabelul hash stochează perechi de cod cheie-hash. Pentru fiecare cheie, se calculează un cod hash de către funcția hash; perechea „cheie” - „cod hash” este stocată în tabel. În acest caz, la căutarea prin tabel, în comparație cu cazul în care sunt utilizate liste legate, nu sunt utilizate legături, se efectuează o enumerare secvențială a perechilor „cheie” - „cod hash”, enumerarea se oprește după cheia necesară e gasit. Secvența în care sunt scanate celulele tabelului se numește secvența sondei [4] .

Sarea criptografică

Pentru a proteja parolele și semnăturile digitale de falsificare, au fost create mai multe metode care funcționează chiar dacă criptoanalistul știe cum să construiască coliziuni pentru funcția hash utilizată. Una dintre aceste metode este adăugarea unei așa-numite „sare” criptografică la datele de intrare  - un șir de date aleatorii; uneori se adaugă și „sare” codului hash. Adăugarea de date aleatoare face mult mai dificilă analiza tabelelor hash rezultate. Această metodă, de exemplu, este utilizată la salvarea parolelor în sisteme de operare asemănătoare UNIX .

Aplicații ale funcțiilor hash

Funcțiile hash sunt utilizate pe scară largă în criptografie.

Hash-ul este folosit ca o cheie în multe structuri de date - tabele hash , filtre Bloom și arbori cartezieni .

Funcții hash criptografice

Printre numeroasele funcții hash existente, se obișnuiește să le evidențiem pe cele sigure criptografic utilizate în criptografie , deoarece le sunt impuse cerințe suplimentare. Pentru ca o funcție hash să fie considerată sigură din punct de vedere criptografic, trebuie să îndeplinească trei cerințe de bază pe care se bazează majoritatea aplicațiilor funcțiilor hash în criptografie:

  • ireversibilitate : pentru o valoare hash dată m , ar trebui să fie imposibil din punct de vedere computațional să se găsească un bloc de date pentru care ;
  • rezistență la coliziuni de primul fel : pentru un mesaj dat M , ar trebui să fie imposibil din punct de vedere computațional să se găsească un alt mesaj N pentru care ;
  • rezistență la coliziuni de tip 2 : ar trebui să fie imposibil din punct de vedere computațional să preluați o pereche de mesaje care au același hash.

Aceste cerințe nu sunt independente:

  • funcția reversibilă este instabilă la ciocniri de primul și al doilea fel;
  • o funcție care este instabilă la coliziunile de primul fel este instabilă la coliziunile de al doilea fel; inversul nu este adevărat.

Nu a fost dovedită existența unor funcții hash ireversibile pentru care calculul oricărei preimagine a unei valori hash date este teoretic imposibil. De obicei, găsirea reciprocei este doar o sarcină dificilă din punct de vedere computațional.

Atacul de ziua de naștere vă permite să găsiți coliziuni pentru o funcție hash cu valori de lungime n biți, în medie, peste calculele aproximative ale funcției hash. Prin urmare, o funcție hash de n biți este considerată sigură dacă complexitatea de calcul a găsirii coliziunilor pentru aceasta este aproape de .

Funcțiile hash criptografice ar trebui să aibă un efect de avalanșă - cu cea mai mică modificare a argumentului, valoarea funcției se schimbă foarte mult. În special, valoarea hash nu trebuie să scurgă informații nici măcar despre biți individuali ai argumentului. Această cerință este cheia pentru puterea criptografică a algoritmilor de hashing care hashing parola utilizatorului pentru a obține cheia [8] .

Hashing-ul este adesea folosit în algoritmii de semnătură digitală, unde nu mesajul în sine este criptat, ci codul său hash, care reduce timpul de calcul și, de asemenea, crește puterea criptografică. De asemenea, în cele mai multe cazuri, valorile codurilor lor hash sunt stocate în loc de parole.

Sume de control

Algoritmii de calcul al sumelor de control sunt algoritmi hardware simpli, rapidi și ușor de implementat, utilizați pentru a proteja datele de distorsiuni neintenționate, inclusiv erori hardware. Din punctul de vedere al matematicii, astfel de algoritmi sunt funcții hash care calculează codul de control. Codul de control este utilizat pentru a detecta erorile care pot apărea în timpul transmiterii și stocării informațiilor.

Algoritmii pentru calcularea sumelor de control sunt de zeci și sute de ori mai rapid decât funcțiile hash criptografice și mult mai simpli în execuția hardware.

Prețul pentru o viteză atât de mare este lipsa puterii criptografice - capacitatea de a „potrivi” cu ușurință un mesaj la o sumă de control precunoscută. De asemenea, lățimea de biți a sumelor de control (număr tipic: 32 de biți) este de obicei mai mică decât lățimea de biți a hashurilor criptografice (numerele tipice: 128, 160 și 256 de biți), ceea ce înseamnă că pot apărea coliziuni neintenționate.

Cel mai simplu algoritm pentru calcularea sumei de control este împărțirea mesajului (date de intrare) în cuvinte de 32 sau 16 biți și apoi însumarea cuvintelor. Un astfel de algoritm este utilizat, de exemplu, în protocoalele TCP / IP .

De regulă, algoritmii de sumă de control ar trebui să detecteze erorile hardware tipice, de exemplu, ar trebui să detecteze mai multe erori consecutive de biți până la o anumită lungime. Așa-numita familie de algoritmi „ cod de redundanță ciclică ” îndeplinește aceste cerințe. Acestea includ, de exemplu, algoritmul CRC32 utilizat în dispozitivele Ethernet și în formatul de compresie a datelor ZIP .

Suma de control, de exemplu, poate fi transmisă prin canalul de comunicare împreună cu textul principal (date). La capătul de recepție, suma de control poate fi recalculată și comparată cu valoarea transmisă. Dacă se găsește o discrepanță, transmisia a devenit deranjată și poate fi solicitată o retransmisie.

Un exemplu de utilizare a hashingului în viața de zi cu zi este numărarea numărului de valize transportate în bagaje. Pentru a verifica siguranța valizelor, nu este necesar să verificați siguranța fiecărei valize, este suficient să numărați numărul de valize în timpul încărcării și descărcării. Numerele potrivite vor însemna că nu se pierde nicio valiză. Adică, numărul de valize este un cod hash.

Această metodă poate fi completată pentru a proteja informațiile transmise de falsificare ( metoda MAC ). În acest caz, hashingul este realizat printr-o funcție securizată a mesajului combinată cu o cheie secretă cunoscută doar de expeditorul și destinatarul mesajului. Criptanalistul, după ce a interceptat mesajul și valoarea funcției hash, nu va putea recupera codul, adică nu va putea falsifica mesajul (vezi protecția împotriva imitației ).

Hashing geometric

Hashingul geometric este o  metodă utilizată pe scară largă în grafica computerizată și geometria computațională pentru rezolvarea problemelor pe un plan sau într-un spațiu tridimensional, de exemplu, pentru a găsi cele mai apropiate perechi de puncte dintr-un set de puncte sau pentru a căuta imagini identice. Funcția hash din această metodă ocupă de obicei un spațiu metric ca intrare și o împarte, creând o grilă de celule. Tabelul hash în acest caz este o matrice cu doi sau mai mulți indici și se numește „fișier grilă” ( fișier grilă în engleză ). Hashingul geometric este utilizat în telecomunicații atunci când se lucrează cu semnale multidimensionale [9] .  

Accelerarea recuperării datelor

Un tabel hash este o structură de date care vă permite să stocați perechi de forma „cheie” - „cod hash” și suportă operațiunile de căutare, inserare și ștergere a unui element. Tabelele hash sunt folosite pentru a accelera căutările, de exemplu, atunci când câmpurile de text sunt scrise într-o bază de date, codul hash al acestora poate fi calculat, iar datele pot fi plasate într-o secțiune corespunzătoare acestui cod hash. Apoi, atunci când căutați date, va fi necesar să calculați mai întâi codul hash al textului și se va ști imediat în ce secțiune ar trebui căutate. Adică, va fi necesar să căutați nu în întreaga bază de date, ci doar într-una dintre secțiunile acesteia, iar acest lucru accelerează căutarea.

În acest caz, analogul de zi cu zi al hashingului poate fi plasarea cuvintelor în dicționar în ordine alfabetică. Prima literă a unui cuvânt este codul său hash, iar la căutare, nu se caută întregul dicționar, ci doar cuvintele care încep cu litera dorită.

Note

  1. Virt2, 2010 , p. 257.
  2. 1 2 Wirth, 1989 .
  3. Herbert Hellerman. Principiile sistemelor informatice digitale. NY: McGraw-Hill, 1967, 424 p.
  4. 1 2 3 4 5 6 7 Knuth, 2007 .
  5. Pearson, Peter K. (iunie 1990), Fast Hashing of Variable-Length Text Strings , Communications of the ACM vol . 33 (6): 677, doi : 10.1145/78973.78978 , < http://epaperpress.com/vbhash/ download/p677-pearson.pdf > 
  6. Djamal Belazzougui, Fabiano C. Botelho, Martin Dietzfelbinger. Hash, displace, and compress  (neopr.) . — Springer Berlin / Heidelberg, 2009.
  7. Miltersen, Peter Bro Universal Hashing ( PDF). Arhivat din original pe 24 iunie 2009.  
  8. Schneier, 2002 .
  9. Wolfson, HJ & Rigoutsos, I (1997). Hashing geometric: o prezentare generală. IEEE Computational Science and Engineering, 4(4), 10-21.

Literatură

  • Bruce Schneier . Criptografia aplicată. Protocoale, algoritmi, texte sursă în limbaj C. - M . : Triumf, 2002. - ISBN 5-89392-055-4 .
  • Donald Knuth . Arta de a programa. Volumul 3. Sortarea și căutarea = The Art of Computer Programming, vol.3. Sortare și căutare. — ediția a II-a. - M . : " Williams ", 2007. - S. 824. - ISBN 0-201-89685-0 .
  • Niklaus Wirth . Algoritmi și structuri de date. - M . : " Mir ", 1989. - ISBN 5-03-001045-9 .
  • Niklaus Wirth . Algoritmi și structuri de date. Noua versiune pentru Oberon. - M . : „DMK Press”, 2010. - ISBN 978-5-94074-584-6 .

Link -uri