web-dev-qa-db-fra.com

Fonction de hachage qui produit des hachages courts?

Existe-t-il un moyen de chiffrement qui peut prendre une chaîne de n'importe quelle longueur et produire un hachage de moins de 10 caractères? Je veux produire des identifiants raisonnablement uniques, mais basés sur le contenu du message, plutôt que de manière aléatoire.

Je peux vivre avec contraindre les messages à des valeurs entières, cependant, si des chaînes de longueur arbitraire sont impossibles. Cependant, le hachage ne doit pas être similaire pour deux entiers consécutifs, dans ce cas.

77
user234932

Vous pouvez utiliser n'importe quel algorithme de hachage communément disponible (par exemple, SHA-1), ce qui vous donnera un résultat légèrement plus long que ce dont vous avez besoin. Il suffit de tronquer le résultat à la longueur souhaitée, ce qui peut être suffisant.

Par exemple, en Python:

>>> import hashlib
>>> hash = hashlib.sha1("my message".encode("UTF-8")).hexdigest()
>>> hash
'104ab42f1193c336aa2cf08a2c946d5c6fd0fcdb'
>>> hash[:10]
'104ab42f11'
63
Greg Hewgill

Si vous n'avez pas besoin d'un algorithme fort contre la modification intentionnelle, j'ai trouvé un algorithme appelé adler32 qui produit des résultats assez courts (~ 8 caractères). Choisissez-le dans le menu déroulant ici pour l'essayer:

http://www.sha1-online.com/

36
B T

Vous devez hacher le contenu pour obtenir un résumé. Il y a beaucoup de hachages disponibles mais 10 caractères est assez petit pour le jeu de résultats. Il y a bien longtemps, les gens utilisaient CRC-32, qui produit un hachage de 33 bits (4 caractères plus un bit). Il existe également le CRC-64 qui produit un hachage de 65 bits. MD5, qui produit un hachage de 128 bits (16 octets/caractères), est considéré comme interrompu à des fins de cryptographie car il est possible de trouver deux messages ayant le même hachage. Il va sans dire que chaque fois que vous créez un résumé de 16 octets à partir d'un message de longueur arbitraire, vous allez vous retrouver avec des doublons. Plus le résumé est court, plus le risque de collision est élevé.

Cependant, vous craignez que le hachage ne soit pas similaire pour deux messages consécutifs (entiers ou non) devrait être vrai pour tous les hachages. Même un seul changement dans le message d'origine devrait produire un résultat de synthèse extrêmement différent.

Donc, utiliser quelque chose comme CRC-64 (et le résultat en base 64) devrait vous rendre dans le quartier que vous recherchez.

10
John

Vous pouvez utiliser un algorithme de hachage existant qui produit quelque chose de court, comme MD5 (128 bits) ou SHA1 (160). Ensuite, vous pouvez raccourcir cette étape en ajoutant XORing des sections du résumé avec d’autres sections. Cela augmentera les risques de collision, mais pas autant que de simplement tronquer le résumé.

Vous pouvez également inclure la longueur des données d'origine dans le résultat pour le rendre plus unique. Par exemple, XORing la première moitié d'un résumé MD5 avec la seconde moitié donnerait 64 bits. Ajoutez 32 bits pour la longueur des données (ou moins si vous savez que cette longueur tiendra toujours dans moins de bits). Cela donnerait un résultat de 96 bits (12 octets) que vous pourriez ensuite transformer en chaîne hexadécimale de 24 caractères. Alternativement, vous pouvez utiliser le codage en base 64 pour le rendre encore plus court.

6
dynamichael

Je résume simplement une réponse qui m'a été utile (en notant le commentaire de @ erasmospunk sur l'utilisation de l'encodage en base 64). Mon objectif était d'avoir une courte chaîne qui était majoritairement unique ...

Je ne suis pas un expert, donc corrigez ceci s'il contient des erreurs flagrantes (dans Python encore une fois, comme la réponse acceptée):

import base64
import hashlib
import uuid

unique_id = uuid.uuid4()
# unique_id = UUID('8da617a7-0bd6-4cce-ae49-5d31f2a5a35f')

hash = hashlib.sha1(str(unique_id).encode("UTF-8"))
# hash.hexdigest() = '882efb0f24a03938e5898aa6b69df2038a2c3f0e'

result = base64.b64encode(hash.digest())
# result = b'iC77DySgOTjliYqmtp3yA4osPw4='

Le result utilise ici plus que des caractères hexadécimaux (ce que vous obtiendriez si vous utilisiez hash.hexdigest()), de sorte qu’il est moins susceptible d’avoir une collision (c’est-à-dire, devrait être plus sûr de tronquer que une digestion hexagonale).

Remarque: Utilisation de UUID4 (aléatoire). Voir http: //en.wikipedia.org/wiki/Universally_unique_identifier pour les autres types.

6
JJ Geewax

Si tu as besoin "sub-10-character hash" vous pouvez utiliser l’algorithme Fletcher-32 qui produit un hachage à 8 caractères (32 bits), CRC-32 ou Adler-32 .

Le facteur CRC-32 est plus lent que l’Adler32 d’un facteur 20 à 100%.

Fletcher-32 est légèrement plus fiable qu'Adler-32. Son coût de calcul est inférieur à celui de la somme de contrôle Adler: comparaison Fletcher vs Adler .

Un exemple de programme avec quelques implémentations de Fletcher est donné ci-dessous:

    #include <stdio.h>
    #include <string.h>
    #include <stdint.h> // for uint32_t

    uint32_t fletcher32_1(const uint16_t *data, size_t len)
    {
            uint32_t c0, c1;
            unsigned int i;

            for (c0 = c1 = 0; len >= 360; len -= 360) {
                    for (i = 0; i < 360; ++i) {
                            c0 = c0 + *data++;
                            c1 = c1 + c0;
                    }
                    c0 = c0 % 65535;
                    c1 = c1 % 65535;
            }
            for (i = 0; i < len; ++i) {
                    c0 = c0 + *data++;
                    c1 = c1 + c0;
            }
            c0 = c0 % 65535;
            c1 = c1 % 65535;
            return (c1 << 16 | c0);
    }

    uint32_t fletcher32_2(const uint16_t *data, size_t l)
    {
        uint32_t sum1 = 0xffff, sum2 = 0xffff;

        while (l) {
            unsigned tlen = l > 359 ? 359 : l;
            l -= tlen;
            do {
                sum2 += sum1 += *data++;
            } while (--tlen);
            sum1 = (sum1 & 0xffff) + (sum1 >> 16);
            sum2 = (sum2 & 0xffff) + (sum2 >> 16);
        }
        /* Second reduction step to reduce sums to 16 bits */
        sum1 = (sum1 & 0xffff) + (sum1 >> 16);
        sum2 = (sum2 & 0xffff) + (sum2 >> 16);
        return (sum2 << 16) | sum1;
    }

    int main()
    {
        char *str1 = "abcde";  
        char *str2 = "abcdef";

        size_t len1 = (strlen(str1)+1) / 2; //  '\0' will be used for padding 
        size_t len2 = (strlen(str2)+1) / 2; // 

        uint32_t f1 = fletcher32_1(str1,  len1);
        uint32_t f2 = fletcher32_2(str1,  len1);

        printf("%u %X \n",    f1,f1);
        printf("%u %X \n\n",  f2,f2);

        f1 = fletcher32_1(str2,  len2);
        f2 = fletcher32_2(str2,  len2);

        printf("%u %X \n",f1,f1);
        printf("%u %X \n",f2,f2);

        return 0;
    }

Sortie:

4031760169 F04FC729                                                                                                                                                                                                                              
4031760169 F04FC729                                                                                                                                                                                                                              

1448095018 56502D2A                                                                                                                                                                                                                              
1448095018 56502D2A                                                                                                                                                                                                                              

En accord avec vecteurs de test :

"abcde"  -> 4031760169 (0xF04FC729)
"abcdef" -> 1448095018 (0x56502D2A)

Adler-32 présente une faiblesse pour les messages courts comportant quelques centaines d'octets, car les sommes de contrôle de ces messages couvrent mal les 32 bits disponibles. Vérifiez ça:

L'algorithme Adler32 n'est pas assez complexe pour rivaliser avec des sommes de contrôle comparables .

3
sg7

Vous pouvez utiliser la bibliothèque hashids qui a des implémentations pour PHP, Javascript, Python, etc. Pour plus de détails, consultez ce lien

2
SaidbakR

J'ai eu besoin récemment de quelque chose du genre d'une simple fonction de réduction de chaîne. En gros, le code ressemblait à ceci (code C/C++ à venir):

size_t ReduceString(char *Dest, size_t DestSize, const char *Src, size_t SrcSize, bool Normalize)
{
    size_t x, x2 = 0, z = 0;

    memset(Dest, 0, DestSize);

    for (x = 0; x < SrcSize; x++)
    {
        Dest[x2] = (char)(((unsigned int)(unsigned char)Dest[x2]) * 37 + ((unsigned int)(unsigned char)Src[x]));
        x2++;

        if (x2 == DestSize - 1)
        {
            x2 = 0;
            z++;
        }
    }

    // Normalize the alphabet if it looped.
    if (z && Normalize)
    {
        unsigned char TempChr;
        y = (z > 1 ? DestSize - 1 : x2);
        for (x = 1; x < y; x++)
        {
            TempChr = ((unsigned char)Dest[x]) & 0x3F;

            if (TempChr < 10)  TempChr += '0';
            else if (TempChr < 36)  TempChr = TempChr - 10 + 'A';
            else if (TempChr < 62)  TempChr = TempChr - 36 + 'a';
            else if (TempChr == 62)  TempChr = '_';
            else  TempChr = '-';

            Dest[x] = (char)TempChr;
        }
    }

    return (SrcSize < DestSize ? SrcSize : DestSize);
}

Il y a probablement plus de collisions que souhaité, mais il n'est pas conçu pour être utilisé comme une fonction de hachage cryptographique. Vous pouvez essayer différents multiplicateurs (c.-à-d. Changer le nombre 37 en un autre nombre premier) si vous obtenez trop de collisions. Une des caractéristiques intéressantes de cet extrait est que lorsque Src est plus court que Dest, Dest se termine avec la chaîne d'entrée telle quelle (0 * 37 + valeur = valeur). Si vous voulez quelque chose "lisible" à la fin du processus, Normaliser ajustera les octets transformés au prix de collisions croissantes.

La source:

https://github.com/cubiclesoft/cross-platform-cpp/blob/master/sync/sync_util.cpp

0
CubicleSoft

Exécutez-le simplement dans un terminal (sous MacOS ou Linux):

crc32 <(echo "some string")

8 caractères de long.

0
sgon00