Nu är det en bra fråga.
Vi måste först ge en precision: många envägsfunktioner, särskilt hashfunktion som vanligt används i kryptografi, accepterar ingångar från ett utrymme som är mycket större än utrymmet för utdata. Till exempel är SHA-256 definierad för ingångar som är strängar på upp till 18446744073709551615 bitar; det finns 2 18446744073709551616 -1 möjliga ingångar, men eftersom utmatningen alltid är en sekvens på 256 bitar finns det bara 2 256 möjliga utgångar för SHA-256. Nödvändigtvis ger vissa distinkta ingångar samma utdata. För en given utgång av SHA-256 är det därför inte möjligt att entydigt återställa ingången som användes, men det kan vara möjligt att beräkna en ingång som ger det angivna utgångsvärdet. Preimage-motstånd handlar om det: svårigheten att hitta en matchande ingång för en utgång (oavsett hur utgången erhölls i första hand).
Så vi pratar om en funktion att alla kan beräkna över vilken input som helst (med hjälp av ett allmänt känt program, inget hemligt värde inblandat - vi pratar inte om kryptering).
Vad akademiker säger
Det är oklart om envägsfunktioner faktiskt kan existera. Just nu har vi många funktioner som ingen vet hur man inverterar; men det betyder inte att de är omöjliga att invertera, i matematisk mening. Observera dock att det inte är bevisat att envägsfunktioner inte kan finnas så hopp kvarstår. Vissa människor misstänker att huruvida envägsfunktioner kan existera eller inte kan vara en av dessa irriterande matematiska påståenden som varken kan bevisas eller motbevisas ( Gödels sats bevisar att sådana saker måste finnas). Men det finns inget bevis på det heller.
Därför finns det inget bevis på att någon given hashfunktion verkligen är motståndskraftig mot förbilder.
Det finns några funktioner som kan kopplas till välkända hårda problem. Till exempel, om n är en produkt av två stora primtal, är funktionen x ⟼ x2 mod n är svår att invertera: att kunna beräkna kvadratrötter modulo ett icke-primärt heltal n (på allmän basis) motsvarar att kunna faktor n , och det problemet är känt för att vara svårt. Inte bevisat att vara svårt, kom ihåg; bara att matematiker har försökt att effektivt faktorera stora heltal under (åtminstone) de senaste 2500 åren, och även om vissa framsteg har gjorts hittade ingen av dessa smarta människor en riktigt mördande algoritm för det. Världsrekord för faktorisering av en "RSA-modul" (en produkt av två slumpmässigt utvalda stora primtal av samma längd) är ett 768-bitars heltal.
Vissa hashfunktioner baserade på sådana "hårda problem" har föreslagits. se till exempel MASH-1 och MASH-2 (om RSA-problemet) och ECOH (med elliptiska kurvor). Det finns bara några få sådana funktioner eftersom:
-
Att göra ett "svårt problem" till en säker hash-funktion är inte lätt. det finns många knepiga problem. Till exempel, medan extrahera kvadratrötter modulo en icke-primär n är vanligtvis hård, finns det värden för vilken extraktion av kvadratrot är lätt.
-
Prestandan för sådana hashfunktioner tenderar att vara, låt oss säga, suboptimal. Som att vara 100 gånger långsammare än en vanligare SHA-1.
Det mer "vanliga" sättet att bygga en hash-funktion är att få ihop kryptografer och få dem att gnaga på några föreslagna mönster; de funktioner som överlever kryptoanalytiska försök i några år anses då "troligen robusta". SHA-3-tävlingen är en sådan ansträngning; vinnaren bör tillkännages senare i år. På de 51 kandidaterna (de som lyckades med det administrativa steget) behölls 14 för "runda 2" och dessa 14 har varit relativt noggrant tittade på av många kryptografer, och ingen av dem fann något som verkligen var värt att säga om funktionerna. Listan har reducerats till 5 och kommer att minskas ytterligare till 1 "snart", men inte av säkerhetsskäl (de flesta faktiska uppgifterna handlade om prestanda, inte motstånd).
Vad gör MD5 svårt att invertera
Eftersom vi inte vet hur man bevisar att en funktion är svår att invertera, är det bästa vi kan göra att ge den ett försök med en specifik funktion för att få en "intuition" av hur funktionen uppnår sitt uppenbara motstånd.
Jag väljer MD5, vilket är välkänt. Ja, MD5 är "trasig", men det är för kollisioner, inte förbilder. Det finns en känd preimage-attack som åtminstone teoretiskt sett är snabbare än det generiska sättet (det "generiska sättet" är "tur", dvs att försöka ingångar tills en matchning är hittade, för en genomsnittlig kostnad av 2128 utvärderingar eftersom MD5 har en 128-bitars utdata; Sasaki-Aoki-attacken har kostat 2 123.4 , vilket är lägre, men ändå alldeles för högt för att verkligen kunna prövas, så resultatet är fortfarande teoretiskt). Men MD5 är relativt enkelt och har motstått attacker under en längre tid, så det är ett intressant exempel.
MD5 består av ett antal utvärderingar av en "komprimeringsfunktion" över datablock. Inmatningsmeddelandet är först vadderat så att dess längd blir en multipel av 512 bitar. Den delas sedan upp i 512-bitarsblock. Ett 128-bitars körläge (innehas i fyra 32-bitars variabler som heter A , B , C och D ) initialiseras till ett konventionellt värde och bearbetas sedan med komprimeringsfunktionen . Kompressionsfunktionen tar körningstillståndet och ett 512-bitars meddelandeblock och blandar dem till ett nytt värde för körningstillståndet. När alla meddelande block har bearbetats så är det slutliga värdet av det körande tillståndet hash-utdata.
Så låt oss koncentrera oss på komprimeringsfunktionen. Det fungerar så här:
- Ingångar: det körande tillståndet ( A B C D ) och ett meddelande block M . Meddelandeblocket är 512 bitar; vi delar upp det i 16 32-bitars ord M0 , M1 , M 2 , ... M15.
- Utgång: det nya körningsstatusvärdet.
-
Bearbetning:
- Spara det aktuella tillståndet i vissa variabler: A → A ', B → B' , C → C ' och D → D'
- Gör 64 rundor som ser ut så här:
- Beräkna T = B + ((A + f i (B, C, D) + M k + X i ) <<< s i under>) . Detta lyder så här: vi beräknar en given funktion fi (en enkel bitvis funktion, som beror på det runda numret i ) över B , C och D . Lägg till värdet A , ett meddelandeord Mk och en konstant X i (tillägg görs modulo 232 ). Rotera resultatet åt vänster med några bitar (skiftbeloppet beror också på rundan). Lägg till slut till B : resultatet är T.
- Rotera tillståndsorden: D → A , C → D , B → C , T → B .
- Lägg till de sparade tillståndsvärdena i de aktuella tillståndsvariablerna: A + A '→ A , B + B' → B , C + C '→ C , D + D '→ D .
Det viktiga är att det finns 64 omgångar, men endast 16 meddelandeord. Detta innebär att varje meddelande ord kommer in i behandlingen fyra gånger . Jag skriver det med fetstil eftersom det är den centrala punkten; motstånd mot förbilder kommer från den egenskapen. Vilket meddelandeord som används i varje omgång beskrivs i MD5-specifikationen (RFC 1321); specifikationen beskriver också funktionerna fi , rotationsräkningarna si och 32-bitars konstanter X i .
Antag nu att du försöker "invertera" MD5; du börjar från utgången och arbetar långsamt upp komprimeringsfunktionen. Först måste du bestämma utgången från runda 64. Faktum är att komprimeringsfunktionens utgång är summan av utgången från runda 64 och det sparade tillståndet ( A 'B' C 'D' värden). Du har ingen av dem, så du måste välja. Ditt hopp är att du kommer att kunna hitta värden för meddelandeorden som gör att du kan få för inmatning av omgång 1 några värden som överensstämmer med ditt godtyckliga beslut om A ' och dess bröder.
Låt oss se hur saker ser ut när du går kompressionsfunktionen bakåt. Du har output av en runda (variablerna A , B , C och D efter omgången) och du vill beräkna ingången för den omgången. Du känner redan till de tidigare värdena för B , C och D men för A och M k du har gott om val: varje 32-bitarsvärde är möjligt för A och alla har motsvarande M k sub > . Först är du glad över det; vem skulle förneka sådan frihet? Välj bara en slumpmässig Mk , och detta ger motsvarande A med bara några operationer (prova det!).
Men efter att du har vänt in på det sättet 16 omgångar (omgångarna 49 till 64, eftersom du arbetar bakåt) försvinner friheten. Du har "valt" värdena för alla meddelandeord. När du försöker invertera omgång 48 vill du beräkna värdet av A strax före den omgången; enligt MD5-specifikationen används meddelandeordet M2 i omgång 48, och du har redan valt värdet M 2 (vid omvändning av omgång 63). Så det finns bara ett val för A . Så vad, skulle du säga. Ett val är tillräckligt för att fortsätta bakåt. Så du fortsätter.
Nu är du i början av komprimeringsfunktionen. Kom ihåg att från början gjorde du ett godtyckligt val av värdena för A 'B' C 'D' : detta gjorde att du kunde beräkna utdata från runda 64 och börja gå bakåt. Nu har du fått inmatningen i omgång 1, som ska vara identisk med A 'B' C 'D' ... och den matchar inte. Det är helt normalt: du valde A 'B' C 'D' godtyckligt och du valde också meddelandeorden Mk godtyckligt, så det kan förväntas att det inte fungerar för det mesta. Så du försöker reparera beräkningen genom att retroaktivt ändra antingen ditt ursprungliga val av A 'B' C 'D', eller ett eller flera av de slumpmässiga valen för M k . Men varje modifiering på alla Mk innebär modifieringar någon annanstans, eftersom varje Mk används fyra gånger. Så du behöver andra modifieringar för att avbryta de andra och så vidare ...
Vid den tiden börjar du förstå problemet med att invertera MD5: varje gång du trycker på en enda bit, utlöser det en hemsk många ändringar genom algoritmen, som du måste avbryta genom att röra vid andra bitar, och det finns bara för många interaktioner. I grund och botten jonglerar du med 2128 bollar samtidigt, och det är alldeles för mycket för att hålla reda på dem alla.
Om varje meddelandeblock var 2048-bitar långt, uppdelat i 64 ord och varje meddelandeord användes bara en gång i MD5, kunde du enkelt invertera det. Du gör som ovan: godtyckligt val av A 'B' C 'D' , godtyckligt val av meddelandeord för omgång 64 till 5; och för de första fyra omgångarna överväger du bara det värde du vill erhålla för den runda ingången (det värde som matchar ditt godtyckliga val av A ', B' , C ' eller D' ) och räkna ut motsvarande meddelandeord. Lätt som en plätt. Men MD5 behandlar inte data med 2048-bitarsblock, utan med 512-bitarsblock, och varje meddelandeord används fyra gånger.
Några ytterligare vändningar
Strukturen för komprimeringsfunktionen för MD5 är faktiskt en generalisering av en Feistel-kodning. I en Feistel-chiffer delas data i två halvor, och för varje runda ändrar vi ena halvan genom att lägga till / xora den till ett mellanliggande värde som beräknas från den andra halvan och från nyckeln; och sedan byter vi de två halvorna. Utöka detta schema till en delning med fyra delar, och du får samma struktur än MD5-rundorna - med 90 ° rotering: MD5 ser ut som krypteringen av nuvarande tillstånd med hjälp av meddelande blocket som tangent (och det finns extra tillägg av utgången från runda 64 med det sparade tillståndet, som avviker MD5 från en roterad chiffer).
Så kanske vi kan bygga hashfunktioner ur block cifrar? Det kan vi faktiskt: det är vad Whirlpool handlar om. En hash-funktion byggd över en roterad blockchiffer (meddelandeblocket är nyckeln); blockkodningen av Whirlpool är "W", ett derivat av Rijndael, bättre känt som AES. Men W har större block (512 bitar istället för 128 bitar) och ett nyckeltabell för reforged.
När du gör en hashfunktion av en roterad blockkodning är preimage-attacker på hashfunktionen något motsvarande viktiga rekonstruktionsattacker på blockkodningen; så det finns något hopp om att blockkodningen är säker, så är hash-funktionen också. Där igen finns det snarkiga detaljer. För en sådan struktur är kollisioner på hashfunktionen som attacker med relaterade nycklar på blockkodningen; relaterade nyckelattacker anses vanligtvis inte vara dödliga och ignoreras ofta (till exempel var de inte en del av utvärderingskriterierna för AES-tävlingen, och Rijndael är känd som lite fläckig i det avseendet, varför W har en helt ny nyckel schema).
Vissa nyare konstruktioner är byggda över en blockkodning som inte roteras, så att hashfunktionens säkerhet kan härledas mer direkt från blockkodningens säkerhet; se till exempel SHA-3-kandidaten Skein, definierad över en blockkodning som heter Threefish.
Omvänt kan man försöka göra en blockkryptering av en hashfunktion. Se till exempel SHACAL, som är SHA-1 "stående upprätt". Och, i kö, SHACAL har några svagheter med relaterade nycklar som liknar de kända svagheterna hos SHA-1 när det gäller kollisioner (ingen faktisk kollision beräknades, men vi har en metod som borde vara nästan en miljon gånger snabbare än generisk kollisionsfyndningsalgoritm).
Därför, i motsats till vad jag sa i inledningen av det här inlägget, har vi pratat om kryptering hela tiden . Det finns fortfarande mycket att upptäcka och studera om länkarna mellan hashfunktioner och symmetrisk kryptering.
TL; DR: det finns ingen TL; DR för detta meddelande . Läs det hela eller börja.