Cryptografische hashfuncties uitgelegd
LinkEen ontwikkelaar downloadt vanaf een mirrorserver een schijfkopie van 4,7 GB voor een besturingssysteem. Het bestand ziet er normaal uit en heeft de verwachte grootte, maar geen van beide zegt of de inhoud klopt. Een mislukte download kan een paar bytes hebben beschadigd en iemand kan het bestand hebben aangepast voordat het de mirrorserver bereikte. De ontwikkelaar moet de gedownloade inhoud dus vergelijken met het origineel van de uitgever. Iedere byte rechtstreeks vergelijken zou uitsluitsel geven, maar daarvoor is een tweede vertrouwde kopie van het volledige bestand van 4,7 GB nodig. Daarmee verdwijnt juist het voordeel van de mirrorserver.
Voordat de uitgever het bestand verspreidt, voert die een algoritme met de naam SHA-256 uit over de inhoud. Het algoritme leest het bestand en geeft een korte reeks van 64 tekens terug, een digest of hashwaarde. De uitgever plaatst deze hashwaarde op de officiële website. Na het downloaden van het grote bestand voert de ontwikkelaar hetzelfde SHA-256-algoritme lokaal uit en krijgt een tweede hashwaarde van 64 tekens. Het bestand hoeft nergens te worden geüpload en de ontwikkelaar heeft geen volledige kopie van de uitgever nodig. Alleen de twee korte resultaten worden vergeleken.
Deze vergelijking werkt omdat SHA-256 deterministisch is. Dat betekent dat dezelfde invoer altijd dezelfde uitvoer geeft. Verschillen de twee hashwaarden, dan wijkt minstens één byte van het gedownloade bestand af van de versie van de uitgever. Zijn ze gelijk, dan vormt dat sterk bewijs dat de bestandsinhoud overeenkomt. De verwachte hashwaarde moet nog wel via een betrouwbare route komen. Komen zowel het bestand als de weergegeven hashwaarde van dezelfde gehackte mirrorserver, dan kan een aanvaller beide vervangen en de vergelijking toch laten slagen.
SHA-256 is een cryptografische hashfunctie. Een hashfunctie ontvangt iedere reeks bytes en brengt die terug tot een resultaat met een vaste grootte. Het cryptografische deel betekent dat de functie is ontworpen voor situaties waarin een aanvaller de vergelijking doelbewust probeert te misleiden. Een veilige functie maakt het onhaalbaar duur om vanuit een hashwaarde terug te werken of verschillende invoerwaarden met een bruikbare gelijke hashwaarde te maken. Zulke gelijke uitkomsten kunnen niet wiskundig worden uitgesloten. Er zijn onbeperkt veel mogelijke invoerwaarden en slechts een vast aantal hashwaarden, dus sommige verschillende invoerwaarden moeten hetzelfde resultaat delen. De rest van dit artikel volgt de berekening stap voor stap en keert daarna terug naar wat deze veiligheidsgrenzen in de praktijk betekenen.
Hoe werkt een cryptografische hashfunctie?
LinkDe eerste stap zet de invoer om in bytes. Een byte is een getal tussen 0 en 255 dat als acht bits in het geheugen staat. Bestanden, afbeeldingen en netwerkberichten bestaan al uit bytes. Tekst moet eerst worden omgezet met een tekencodering zoals UTF-8. Daarin wordt de zichtbare letter A de enkele byte 65, terwijl andere tekens meerdere bytes kunnen innemen. Vanaf dat moment begrijpt de hashfunctie geen letters, pixels, bestandsnamen of andere menselijke betekenis meer. Ze ontvangt alleen een geordende reeks bytewaarden. Hoofdletters, regeleinden, tekencodering en een onzichtbare spatie aan het eind veranderen die reeks allemaal. Verschilt één byte, dan hasht de functie een ander bericht, ook als een mens de twee versies als gelijk beschouwt.
Daarna moeten we naar de grootte van het resultaat kijken. SHA-256 geeft altijd 256 bits terug, oftewel 32 bytes. Dat geldt voor zowel de drie bytes van CAT als iedere byte uit een schijfkopie van 4,7 GB. Meestal schrijven we die 32 bytes in hexadecimale notatie. Iedere byte wordt dan twee hexadecimale tekens, waardoor de weergegeven hashwaarde 64 tekens telt. Dankzij de vaste grootte kunnen we twee grote bestanden later voorstellen met twee korte waarden, maar de vaste grootte vertelt ook dat er informatie verloren is gegaan. Een digest is geen codering, want een codering bewaart genoeg informatie om haar met een decoder om te keren. Het is ook geen versleutelde inhoud. Versleuteling gebruikt een sleutel en heeft een bijbehorende ontsleutelingsbewerking. Een cryptografische hash heeft geen sleutel voor ontsleuteling en het resultaat van 32 bytes kan geen herstelbare kopie van iedere mogelijke invoer bevatten.
SHA-256 kan een bestand van meerdere gigabytes niet in één enorme bewerking mengen. De derde stap verdeelt de bytereeks daarom in blokken. Eerst voegt het algoritme volgens exacte regels padding toe. Deze padding hoort bij de berekening en is geen witruimte die aan het bestand wordt toegevoegd. SHA-256 plaatst eerst een bit met waarde 1, daarna genoeg nulbits om een vereiste positie te bereiken en tot slot een waarde van 64 bits met de oorspronkelijke berichtlengte. De aangevulde reeks past vervolgens in volledige blokken van 512 bits. De tekst abc neemt in UTF-8 bijvoorbeeld 24 bits in. De vereiste padding maakt daar één volledig blok van 512 bits van. Door de oorspronkelijke lengte vast te leggen, kan het algoritme berichten onderscheiden waarvan de bytes anders in een dubbelzinnig patroon zouden eindigen.
In de vierde stap verandert ieder blok de toestand in het geheugen. SHA-256 begint met acht vastgelegde toestandswaarden van elk 32 bits. Het algoritme neemt het eerste blok van 512 bits, leidt daar werkwaarden uit af en voert 64 rondes met bitbewerkingen uit. Het verwerkt het resultaat van die rondes weer in de acht toestandswaarden. Daarna herhaalt het algoritme dit proces voor het volgende blok. Het gebruikt daarbij de bijgewerkte toestand en keert niet terug naar de beginwaarden. Je kunt de toestand voorstellen als acht rijen met elk 32 schakelaars. Ieder verwerkt blok zet die schakelaars in een andere stand en de stand na het ene blok wordt het beginpunt voor het volgende. Na het laatste blok voegt SHA-256 de acht uiteindelijke toestandswaarden samen tot de hashwaarde van 256 bits.
Dit blok-voor-blokproces verklaart hoe de ontwikkelaar een bestand van 4,7 GB kan hashen zonder het helemaal in het geheugen te bewaren. Een streamende implementatie houdt de toestand van acht woorden en een kleine buffer voor een nog onvolledig blok bij. Ze leest een deel van het bestand, vult de buffer, mengt ieder volledig blok in de toestand en gooit de verwerkte bytes daarna weg. Alleen de veranderde toestand gaat door naar de volgende stap. Zodra alle bytes binnen zijn, voegt de implementatie de padding toe, verwerkt ze het laatste blok of de laatste blokken en geeft ze de eindtoestand terug als hashwaarde. Die toestand is een samenvatting van de berekening, geen gecomprimeerd archief. Ze bevat niet genoeg informatie om de al weggegooide blokken opnieuw te maken.
Een kleine hash met de hand volgen
LinkSHA-256 gebruikt veel zorgvuldig ontworpen bewerkingen. Een volledige handmatige berekening zou de beweging van gegevens verbergen onder pagina’s vol getallen. We kunnen hetzelfde algemene patroon volgen met de expres zwakke oefenfunctie TinyMix-8. Deze functie heeft één toestand van acht bits in plaats van acht waarden van 32 bits, behandelt iedere byte als één blok en geeft één byte terug in plaats van 32. TinyMix-8 begint met toestand 101. Voor iedere invoerbyte draait de functie eerst de acht bits van de toestand één plek naar links, waarbij de bit die links wegvalt rechts terugkomt. Vervolgens combineert ze de gedraaide toestand met de invoerbyte via exclusieve OF. Deze bitbewerking geeft 1 terug wanneer de twee vergeleken bits verschillen en 0 wanneer ze gelijk zijn.
Hash nu de tekst CAT. UTF-8 stelt de letters voor met de bytewaarden 67, 65 en 84. In het geheugen verandert de werktoestand als volgt:
begintoestand = 101 = 01100101
draai één bit naar links = 202 = 11001010
combineer met C, byte 67 = 137 = 10001001
draai toestand 137 één bit naar links = 19 = 00010011
combineer met A, byte 65 = 82 = 01010010
draai toestand 82 één bit naar links = 164 = 10100100
combineer met T, byte 84 = 240 = 11110000
De uiteindelijke hashwaarde van één byte is 240, hexadecimaal weergegeven als f0. Volg wat na iedere regel in het geheugen bleef staan. De eerste invoerbyte veranderde de toestand van 101 naar 137, waarna het algoritme verderging. Het bewaarde de letter C niet in een vrij deel van de hashwaarde. De tweede byte veranderde 137 in 82 en de derde veranderde 82 in 240. Nadat de laatste byte was verwerkt, gaf de functie alleen die uiteindelijke toestand van acht bits terug.
TinyMix-8 is niet veilig voor beveiligingstoepassingen. Er zijn maar 256 mogelijke resultaten en de mengregel is makkelijk te analyseren, waardoor een aanvaller eenvoudig botsingen kan vinden of achteruit kan rekenen. De functie verspreidt veranderingen ook slecht. Als we CAT vervangen door BAT, verandert één bit in de eerste invoerbyte, maar de uitvoer verandert slechts van f0 naar fc. Er verschillen maar twee uitvoerbits. Een echte cryptografische functie streeft naar een lawine-effect, waarbij een kleine verandering in de invoer veel uitvoerbits op een onvoorspelbaar ogende manier verandert. Alleen dat uiterlijk bewijst geen veiligheid. Een onveilige functie kan in een paar voorbeelden een sterk lawine-effect laten zien. Daarom beschrijven cryptografen veiligheid aan de hand van concrete opdrachten die een aanvaller niet moet kunnen voltooien.
Wat betekenen de veiligheidseigenschappen?
LinkWe beginnen met preimagebestendigheid. Iemand geeft een aanvaller een hashwaarde, maar niet het bericht waaruit die waarde kwam. De aanvaller wint als die een willekeurige invoer vindt met dezelfde hashwaarde. De oorspronkelijke invoer terugvinden is niet nodig. Daarom verbergt de losse uitspraak dat een hash “onomkeerbaar” is een deel van het probleem. Bij een ideale hash met een uitvoer van n bits vraagt algemeen zoekwerk ongeveer 2n pogingen. Dat grote getal helpt alleen wanneer de mogelijke invoer ook moeilijk te doorzoeken is. Hasht een toepassing een pincode van vier cijfers met SHA-256, dan kan een aanvaller alle 10.000 mogelijke codes hashen en de resultaten vergelijken. De kracht van SHA-256 maakt een kleine verzameling gokpogingen niet groter.
Tweede-preimagebestendigheid verandert het beginpunt. Nu krijgt de aanvaller één bepaald bericht en de bijbehorende hashwaarde. De opdracht is om een ander bericht met diezelfde hashwaarde te vinden. Stel je een ondertekend contract voor waarvan de hashwaarde al is vastgelegd. Een aanvaller wil precies dat contract vervangen zonder de vastgelegde waarde te veranderen en mag het oorspronkelijke contract niet achteraf zelf kiezen. Bij een ideale hash van n bits vraagt het algemene zoekwerk opnieuw ongeveer 2n pogingen.
Botsingsbestendigheid geeft de aanvaller de vrijheid om beide berichten te kiezen. Die kan paren blijven maken totdat twee verschillende berichten dezelfde hashwaarde hebben. Het verjaardagseffect maakt dit zoekwerk veel goedkoper dan het aanvallen van één vastgelegde hashwaarde. Voor een ideale hash met een uitvoer van n bits vraagt algemeen zoeken naar een botsing ongeveer 2n / 2 pogingen. SHA-256 heeft een uitvoer van 256 bits, maar biedt bij een ideale algemene botsingsaanval dus ongeveer 128 bits aan sterkte. De drie opdrachten lijken op elkaar, maar succes bij de ene lost de andere niet automatisch op. Het SHAttered-onderzoek maakte in 2017 twee zichtbaar verschillende pdf-bestanden met dezelfde SHA-1-hashwaarde. Dat was een praktische botsing. Het maakte niet iedere SHA-1-hash omkeerbaar, maar brak wel de botsingsbestendigheid waarop systemen vertrouwden. Kies SHA-1 daarom niet voor nieuwe ontwerpen die deze eigenschap nodig hebben. Ook MD5 is daarvoor ongeschikt.
Hoe werkt een cryptografische hashfunctie in code?
LinkDe pseudocode voor TinyMix-8 hieronder komt exact overeen met de berekening van CAT. Bewerkingen zijn als woorden geschreven, zodat iedere verandering in het geheugen zichtbaar blijft.
; Alleen een oefenfunctie. TinyMix-8 is niet veilig voor beveiliging.
(define beginToestand 101)
(define tinyMix8 (function berichtBytes)
; Het geheugen begint met één werktoestand van acht bits.
(define werkToestand beginToestand)
(forEach berichtByte berichtBytes
; Voor deze stap vat werkToestand alle eerdere bytes samen.
(define gedraaideToestand (draaiAchtBitsNaarLinks werkToestand 1))
(set werkToestand (exclusieveOf gedraaideToestand berichtByte))
; De toestand heeft nu de huidige byte opgenomen.
; De aanroeper mag die byte weggooien als ze niet meer nodig is.)
; De eindtoestand is de vaste hashwaarde van één byte.
(return werkToestand)))
(define berichtBytes (utf8Codeer "CAT"))
; berichtBytes bevat [67, 65, 84].
(define hashwaarde (tinyMix8 berichtBytes))
; hashwaarde bevat 240, hexadecimaal weergegeven als "f0".
Productiecode moet TinyMix-8 niet vervangen door een zelfgeschreven versie van SHA-256. Cryptografische algoritmen zijn makkelijk verkeerd te implementeren en kleine fouten kunnen nog steeds aannemelijke uitvoer opleveren. Toepassingscode hoort een onderhouden cryptografische bibliotheek te gebruiken. Een functie voor het hashen van een bestand krijgt dan een veel eenvoudigere vorm:
(define hashBestandMetSha256 (function bestandspad)
; De bibliotheek maakt de SHA-256-toestand en blokbuffer met vaste grootte.
(define hashToestand (maakSha256Toestand))
(forEach bestandsdeel (leesBestandInDelen bestandspad)
; De bibliotheek buffert gedeeltelijke blokken.
; Ze mengt volledige blokken in de toestand en kan ze daarna weggooien.
(werkHashToestandBij hashToestand bestandsdeel))
; Afronding voegt de vereiste padding toe en geeft 32 hashbytes terug.
(return (rondHashToestandAf hashToestand))))
De bestandsdelen in dit voorbeeld beperken het geheugengebruik. Ze veranderen de hashwaarde niet. Een correcte streamende aanroep en een eenmalige aanroep over dezelfde bytes geven hetzelfde SHA-256-resultaat, want het algoritme ziet dezelfde geordende bytereeks.
Beveiligingsgevoelige code kan ook een vergelijkingsfunctie met constante uitvoeringstijd uit de bibliotheek nodig hebben. Een gewone vergelijking kan bij de eerste afwijkende byte stoppen. De uitvoeringstijd kan dan onthullen hoeveel van een geheime waarde overeenkwam. Bij een vergelijking met constante uitvoeringstijd hangt de tijd niet af van de plek van het eerste verschil. Of dit nodig is, hangt af van de vergeleken waarden en wat een aanvaller kan meten, maar zelf zo’n vergelijking schrijven is zelden verstandig.
Wanneer worden cryptografische hashes gebruikt?
LinkDe download aan het begin van dit artikel is een controle op bestandsintegriteit. Met een gepubliceerde hashwaarde kunnen mensen onbedoelde beschadiging of doelbewuste aanpassing herkennen, zolang ze de verwachte waarde via een betrouwbare route ontvangen. Pakketbeheerders, downloadpagina’s voor software en back-upsystemen gebruiken dit patroon. De berekening moet nog steeds iedere invoerbyte eenmaal lezen. De winst komt daarna, wanneer systemen korte hashwaarden kunnen bewaren en vergelijken in plaats van twee volledige bestanden beschikbaar te houden. Systemen met inhoudsgebaseerde adressering bouwen voort op hetzelfde idee en leiden de identificator van een object af uit de inhoud. Gelijke bytes geven dezelfde identificator, terwijl veranderde bytes met overweldigende waarschijnlijkheid een andere geven. Git identificeert objecten van oudsher met SHA-1 en heeft een overgangsontwerp voor SHA-256 vastgelegd. De objectnaam identificeert de inhoud, maar bewijst niet wie haar heeft gemaakt of goedgekeurd.
Digitale handtekeningen voegen het bewijs van herkomst toe dat een gewone hashwaarde mist. Een systeem voor handtekeningen met publieke sleutels hasht een bericht meestal als onderdeel van de ondertekening. Zo wordt een bericht van willekeurige lengte een hashwaarde met vaste grootte die het handtekeningalgoritme op een vastgelegde manier kan verwerken. Botsingsbestendigheid is nodig omdat een aanvaller geen onschuldig en kwaadaardig document met dezelfde hashwaarde moet kunnen voorbereiden, een handtekening voor het onschuldige document moet kunnen krijgen en die daarna aan het kwaadaardige document moet kunnen hangen. De hash verbindt een compacte waarde aan de berichtbytes. De private sleutel zorgt voor authenticiteit. Iedereen kan een gewone hashwaarde berekenen, dus een document hashen zonder ondertekeningssleutel bewijst niets over de maker.
HMAC behandelt een verwante situatie waarin twee systemen al een geheim delen. Het combineert die geheime sleutel met een cryptografische hash via vastgelegde berekeningen aan de binnen- en buitenkant. De verzender berekent een HMAC voor het bericht en de ontvanger herhaalt de berekening met hetzelfde geheim. Een gelijk resultaat laat zien dat het bericht niet is veranderd en dat de verzender het geheim kende. Een zelfgemaakte uitdrukking zoals hash(geheim + bericht) is geen veilig alternatief. De volgorde, codering, hashconstructie en berichtgrenzen leveren valkuilen op die makkelijk over het hoofd worden gezien. Toepassingscode hoort daarom een bibliotheekimplementatie van HMAC of een ander gevestigd algoritme voor berichtauthenticatie te gebruiken.
Voor wachtwoordopslag is opnieuw een ander soort functie nodig. Gewone SHA-256 is ontworpen om snel te zijn. Daardoor kan een aanvaller met een gestolen wachtwoorddatabase op eigen hardware snel gokpogingen testen. Een salt toevoegen verhelpt die snelheid niet. Wachtwoordhashfuncties zoals Argon2id ontvangen het wachtwoord, een unieke salt en kosteninstellingen. Ze verbruiken doelbewust tijd en geheugen om iedere gokpoging duurder te maken. De salt is niet geheim en kan naast de wachtwoordhash worden opgeslagen. Ze zorgt ervoor dat gelijke wachtwoorden verschillende opgeslagen waarden opleveren en voorkomt dat één vooraf berekende tabel voor ieder account werkt. Kies de kosteninstellingen volgens actuele richtlijnen voor het platform en dreigingsmodel van de toepassing, want geschikte waarden veranderen wanneer hardware sneller wordt.
Afwegingen en beperkingen
LinkEen cryptografische hash brengt iedere hoeveelheid invoer terug tot een korte waarde die makkelijk kan worden opgeslagen en vergeleken. Een streamende implementatie heeft slechts een begrensde hoeveelheid werkgeheugen nodig en een veilig algoritme maakt bruikbare omkerings- en botsingsaanvallen onhaalbaar. Voor dat compacte resultaat gaat informatie verloren. Een hashwaarde kan het bericht niet herstellen, verbergen of de maker ervan aanwijzen. Voor het hashen van een groot bestand moet het algoritme ook iedere byte lezen, waardoor de berekeningstijd met de invoerlengte groeit. Het mengen van bits kost bovendien meer dan de eenvoudigere hashfuncties in hashtabellen in het geheugen, waar snel zoeken en een gelijkmatige verspreiding over buckets zwaarder wegen dan het onhaalbaar maken van terugrekenen.
Het gekozen algoritme moet de eigenschap bieden die de toepassing nodig heeft. De bewezen botsing in SHA-1 betekent niet dat ieder oud algoritme voor ieder doel faalt, net zoals een nieuwere naam geen veiligheid bewijst. Vraag of het algoritme nog voldoende preimagebestendigheid, tweede-preimagebestendigheid of botsingsbestendigheid biedt. Gebruik voor een nieuw systeem actuele platformrichtlijnen en onderhouden bibliotheken in plaats van een algoritme uit het hoofd te kiezen.
Waarom botsingen moeten bestaan
LinkEen hashwaarde van 256 bits heeft 2256 mogelijke uitkomsten. Dat aantal is enorm, maar eindig. Een hashfunctie accepteert berichten van allerlei lengtes en heeft daardoor meer mogelijke invoerwaarden dan uitkomsten. Als iedere mogelijke hashwaarde al aan één bericht is toegewezen, moet het volgende afwijkende bericht een waarde hergebruiken. Dit is het duiventilprincipe toegepast op hashes. TinyMix-8 maakt de grens zichtbaar, want de uitvoer van één byte heeft slechts 256 mogelijke waarden. Bij het hashen van 257 verschillende berichten ontstaat gegarandeerd minstens één botsing, hoe goed de functie haar resultaten ook verdeelt. Willekeurig ogende uitkomsten botsen door het verjaardagseffect meestal al eerder. Een veilige hash van 256 bits voorkomt botsingen niet, maar maakt het doelgericht zoeken ernaar onhaalbaar.
Een subtiele valkuil: verlengingsaanvallen
LinkSHA-256 hoort bij een familie hashconstructies waarin de uiteindelijke hashwaarde genoeg van de interne toestand blootgeeft om een verlengingsaanval mogelijk te maken. Stel dat een toepassing een bericht probeert te authenticeren met SHA256(geheim || bericht), waarbij || betekent dat de twee bytereeksen achter elkaar worden geplaatst. Een aanvaller kent het bericht en de hashwaarde, maar niet het geheim. Met een geschikte gok voor de lengte van het geheim kan de aanvaller de padding reconstrueren die SHA-256 na de oorspronkelijke invoer plaatste. Daarna gebruikt die de gepubliceerde hashwaarde als toestand om vanaf verder te hashen, voegt zelfgekozen bytes toe en berekent een geldige hashwaarde voor de langere invoer. Het geheim verschijnt nooit in het geheugen van de aanvaller. Het effect ervan zit al in de blootgegeven toestand.
De aanval onthult het geheim niet en vindt geen botsing. Ze slaagt doordat de toepassing het geheim en het bericht op een onveilige manier heeft gecombineerd. HMAC voorkomt dit probleem met een vastgelegde binnen- en buitenconstructie. Andere veilige hashfuncties verwerken hun geheugen ook niet allemaal zoals SHA-256. SHA-3 is gebaseerd op Keccak en gebruikt een sponsconstructie, die invoer via herhaalde permutaties in een deel van een grotere toestand opneemt en daarna uitvoer uit die toestand haalt. SHA-256 en SHA-3 accepteren beide invoer van willekeurige lengte en geven een hashwaarde met een vastgelegde grootte terug, maar hun interne stappen verschillen.
Samenvatting
LinkEen cryptografische hashfunctie leest bytes, verdeelt ze in blokken en mengt ieder blok in een toestand met vaste grootte voordat ze die toestand als hashwaarde teruggeeft. Dezelfde bytes leveren hetzelfde resultaat op, terwijl een kleine verandering in de invoer zich over de uitvoer hoort te verspreiden. De veiligheid hangt af van hoe moeilijk het is om een invoer voor een hashwaarde te vinden, één gekozen bericht door een ander te vervangen of een willekeurig botsend paar te maken. Als er maar één idee blijft hangen, laat het dan dit zijn: een hashwaarde is een vingerafdruk, geen slot of handtekening. Ze kan aantonen dat bytes zijn veranderd wanneer de verwachte waarde betrouwbaar is. Gebruik HMAC of een ander algoritme voor berichtauthenticatie wanneer een gedeeld geheim een bericht moet authenticeren, een algoritme voor digitale handtekeningen voor openbaar bewijs van herkomst en een wachtwoordfunctie zoals Argon2id voor wachtwoordopslag.
Meer lezen
Link- Hashtabellen uitgelegd
- NIST: FIPS 180-4, Secure Hash Standard
- NIST: Hash functions project
- NIST: FIPS 202, SHA-3 Standard
- RFC Editor: RFC 2104, HMAC
- RFC Editor: RFC 9106, Argon2
- Git: Overgang tussen hashfuncties