Kısa karmalar üreten karma işlevi?


102

Herhangi bir uzunlukta bir dizge alıp 10 karakterlik bir karma oluşturabilen bir şifreleme yöntemi var mı? Rastgele yerine mesaj içeriğine dayalı olarak makul ölçüde benzersiz kimlikler üretmek istiyorum.

İsteğe bağlı uzunlukta dizeler imkansızsa, mesajları tam sayı değerleriyle sınırlayarak yaşayabilirim. Ancak, bu durumda hash iki ardışık tam sayı için benzer olmamalıdır.


Buna hash denir. Benzersiz olmayacak.
SLaks

1
Bu aynı zamanda bir hash kesme problemidir, bu yüzden ayrıca stackoverflow.com/q/4784335'e
Peter Krauss

2
Bilginize, Wikipedia'daki hash işlevlerinin listesine bakın .
Basil Bourque

Yanıtlar:


80

Yaygın olarak bulunan herhangi bir hash algoritmasını (örneğin, SHA-1) kullanabilirsiniz, bu size ihtiyacınız olandan biraz daha uzun bir sonuç verecektir. Sonucu istediğiniz uzunlukta kısaltın, bu yeterince iyi olabilir.

Örneğin, Python'da:

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

3
Herhangi bir makul hash işlevi kesilebilir.
Başkan James K. Polk

92
bu, çarpışma riskini çok daha fazla artırmaz mı?
Gabriel Sanmartin

144
@erasmospunk: base64 ile kodlama, çarpışma direnci için hiçbir şey yapmaz, çünkü hash(a)ile çarpışırsa hash(b)o zaman base64(hash(a))da çarpışır base64(hash(b)).
Greg Hewgill

57
@GregHewgill haklısınız, ancak orijinal hash algoritmasının çarpışmasından bahsetmiyoruz (evet, sha1çarpışıyor ama bu başka bir hikaye). 10 karakterlik bir özetiniz varsa, base64vs base16(veya onaltılık) ile kodlanmışsa daha yüksek entropi elde edersiniz . Ne kadar yükseğe? İle base16birlikte size, karakter başına 4 bit bilgi almak base64bu rakam 6bits / karakter olduğunu. Toplamda 10 karakterlik bir "hex" hash, 40 bit entropiye sahipken, bir base64 60bit olacaktır. Bu yüzden biraz daha dirençli, süper net olmasaydım üzgünüm.
John L.Jegutanis

21
@erasmospunk: Ne demek istediğini anlıyorum, evet eğer sonucunuz için sınırlı bir sabit boyutunuz varsa, o zaman base64 kodlamaya karşı onaltılık kodlamaya göre daha önemli bitleri paketleyebilirsiniz.
Greg Hewgill

46

Kasıtlı değişikliklere karşı güçlü bir algoritmaya ihtiyacınız yoksa , oldukça kısa (~ 8 karakter) sonuçlar üreten adler32 adlı bir algoritma buldum . Denemek için buradaki açılır menüden seçin:

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


2
çok eski, çok güvenilir değil.
Mascarpone

1
@Mascarpone "çok güvenilir değil" - kaynak? Sınırları vardır, eğer onları biliyorsanız, kaç yaşında olduğu önemli değildir.
BT

8
@Mascarpone "daha az zayıflık" - yine, hangi zayıf yönler? Neden bu algoritmanın OP'nin kullanımı için% 100 mükemmel olmadığını düşünüyorsunuz?
BT

3
@Mascarpone OP, kripto sınıfı bir hash istediklerini söylemiyor. OTOH, Adler32 bir sağlama toplamıdır, hash değildir, bu nedenle OP'nin gerçekte onunla ne yaptığına bağlı olarak uygun olmayabilir.
PM 2Ring

2
Adler32 için Wikipedia'dan alıntı yapan bir uyarı var : Adler-32'nin birkaç yüz baytlık kısa mesajlar için bir zayıflığı vardır, çünkü bu mesajların sağlama toplamları mevcut 32 biti kapsamaz.
Basil Bourque

13

Özet oluşturmak için içeriği karıştırmanız gerekir. Kullanılabilir birçok karma vardır, ancak 10 karakter sonuç kümesi için oldukça küçüktür. Geçmişte, insanlar 33 bitlik bir karma (temelde 4 karakter artı bir bit) üreten CRC-32'yi kullandılar. Ayrıca 65 bitlik bir hash üreten CRC-64 de vardır. 128 bitlik bir karma (16 bayt / karakter) üreten MD5, kriptografik amaçlar için kırılmış olarak kabul edilir çünkü aynı karmaya sahip iki mesaj bulunabilir. Rasgele uzunluktaki bir mesajdan 16 baytlık bir özet oluşturduğunuzda, yinelemelerle sonuçlanacağınızı söylemeye gerek yok. Sindirim ne kadar kısa olursa, çarpışma riski o kadar artar.

Bununla birlikte, iki ardışık mesaj için (tam sayı olsun veya olmasın) karmanın benzer olmayacağına dair endişeniz tüm karmalarla doğru olmalıdır. Orijinal mesajdaki tek bir bitlik değişiklik bile, çok farklı sonuçlara sahip bir özet üretmelidir.

Bu nedenle, CRC-64 gibi bir şey kullanmak (ve sonucu 64'e göre yapmak) sizi aradığınız mahalleye götürmelidir.


1
CRC'nin bir SHA-1 hash'i oluşturması ve ardından sonucu temel 64'leme, sonuçta ortaya çıkan kimliği çarpışmaya karşı daha dirençli hale getirir mi?

5
"Ancak, iki ardışık mesaj için [...] hash'in benzer olmayacağına dair endişeniz tüm karmalarla doğru olmalıdır." - Bu mutlaka doğru değil. Örneğin, kümeleme veya klon algılama için kullanılan karma işlevler için tam tersi doğrudur: benzer belgelerin benzer (veya hatta aynı) karma değerleri vermesini istiyorsunuz . Benzer girdiler için aynı değerleri vermek üzere özel olarak tasarlanmış bir karma algoritmanın iyi bilinen bir örneği Soundex'tir.
Jörg W Mittag

Mesajın imzasını doğrulamak için karmaları kullanıyorum. Yani temel olarak, bilinen bir mesaj ve belirli bir imza için hash doğru olmalıdır. Yine de yanlış pozitiflerin küçük bir yüzdesinin olması umrumda değil. Tamamen kabul edilebilir. Şu anda kolaylık sağlamak için base62 ile sıkıştırılmış kesilmiş SHA-512 karmasını kullanıyorum (hızlıca kırbaçladığım bir şey).

@ JörgWMittag SoundEx üzerinde mükemmel bir puan. Düzeltilmiş durumdayım. Değil bütün karmaları aynı özelliklere sahiptir.
John

12

Sadece bana yardımcı olan bir cevabı özetledim (@ erasmospunk'ın temel-64 kodlamasını kullanma hakkındaki yorumuna dikkat ederek). Amacım, çoğunlukla benzersiz olan kısa bir diziye sahip olmaktı ...

Uzman değilim, bu yüzden göze batan herhangi bir hata varsa lütfen bunu düzeltin (Python'da yine kabul edilen cevap gibi):

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='

resultBurada (eğer kullanılırsa ne olacak söyleyeyim sadece altıgen karakterden daha fazla kullanıyor hash.hexdigest()o (olduğunu, bir onaltılık sindirmek daha kesecek şekilde daha güvenli olmalıdır) bir çarpışma olması olasılığı düşüktür bu yüzden).

Not: UUID4 (rastgele) kullanma. Diğer türler için http://en.wikipedia.org/wiki/Universally_unique_identifier adresine bakın .


8

İhtiyacınız "sub-10-character hash" olursa , 8 karakter karma (32 bit), CRC-32 veya Adler-32 üreten Fletcher-32 algoritmasını kullanabilirsiniz .

CRC-32, Adler32'den% 20 -% 100 faktör ile daha yavaştır.

Fletcher-32, Adler-32'den biraz daha güvenilirdir. Adler sağlama toplamından daha düşük bir hesaplama maliyetine sahiptir: Fletcher ve Adler karşılaştırması .

Birkaç Fletcher uygulaması içeren örnek bir program aşağıda verilmiştir:

    #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;
    }

Çıktı:

4031760169 F04FC729                                                                                                                                                                                                                              
4031760169 F04FC729                                                                                                                                                                                                                              

1448095018 56502D2A                                                                                                                                                                                                                              
1448095018 56502D2A                                                                                                                                                                                                                              

Test vektörleriyle aynı fikirde :

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

Adler-32, birkaç yüz baytlık kısa mesajlar için bir zayıflığa sahiptir, çünkü bu mesajlar için sağlama toplamları, mevcut 32 biti kapsamaz. Şunu bir kontrol et:

Adler32 algoritması, karşılaştırılabilir sağlama toplamları ile rekabet edecek kadar karmaşık değildir .


7

MD5 (128 bit) veya SHA1 (160) gibi kısa bir şey üreten mevcut bir hash algoritması kullanabilirsiniz. Daha sonra, özetin bölümlerini diğer bölümlerle birlikte XORing yaparak daha da kısaltabilirsiniz. Bu, çarpışma olasılığını artıracak, ancak sindirimi kısaltmak kadar kötü değil.

Ayrıca, daha benzersiz hale getirmek için sonucun bir parçası olarak orijinal verilerin uzunluğunu da ekleyebilirsiniz. Örneğin, bir MD5 özetinin ilk yarısını ikinci yarı ile XORlamak 64 bit ile sonuçlanacaktır. Veri uzunluğu için 32 bit ekleyin (veya bu uzunluğun her zaman daha az bite sığacağını biliyorsanız daha düşük). Bu, 96 bitlik (12 baytlık) bir sonuçla sonuçlanır ve daha sonra 24 karakterli bir onaltılık dizeye dönüşebilirsiniz. Alternatif olarak, daha da kısaltmak için temel 64 kodlamayı kullanabilirsiniz.


2
FWIW, bu XOR katlama olarak bilinir.
PM 2Ring

6

Bunu bir terminalde çalıştırın (MacOS veya Linux'ta):

crc32 <(echo "some string")

8 karakter uzunluğunda.


6

Python için hashlib kitaplığını kullanabilirsiniz . Shake_128 ve shake_256 algoritmaları değişken uzunluklu karma sağlar. İşte bazı çalışma kodları (Python3):

import hashlib
>>> my_string = 'hello shake'
>>> hashlib.shake_256(my_string.encode()).hexdigest(5)
'34177f6a0a'

Uzunluk parametresi x (örnekte 5) ile fonksiyonun 2x uzunluğunda bir karma değer döndürdüğüne dikkat edin .


2

Şimdi 2019 ve daha iyi seçenekler var. Yani xxhash .

~ echo test | xxhsum                                                           
2d7f1808da1fa63c  stdin

Bu bağlantı koptu. daha eksiksiz bir cevap vermek daha iyidir.
eri0o

0

Son zamanlarda basit bir dizi azaltma işlevi doğrultusunda bir şeye ihtiyacım vardı. Temel olarak, kod şöyle bir şeye benziyordu (ileride C / C ++ kodu):

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);
}

Muhtemelen istenenden daha fazla çarpışmaya sahiptir, ancak kriptografik bir hash işlevi olarak kullanılması amaçlanmamıştır. Çok fazla çarpışma yaşarsanız, çeşitli çarpanları deneyebilirsiniz (yani 37'yi başka bir asal sayıya çevirebilirsiniz). Bu kod parçacığının ilginç özelliklerinden biri, Src, Dest'ten daha kısa olduğunda, Dest'in girdi dizgesini olduğu gibi (0 * 37 + değer = değer) ile bitmesidir. İşlemin sonunda "okunabilir" bir şey istiyorsanız, Normalize, çarpışmaların artması pahasına dönüştürülen baytları ayarlayacaktır.

Kaynak:

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


std :: hash belirli kullanım durumlarını çözmez (örneğin, fazladan birkaç satır kod yeterliyken şişkin std :: şablonlarında sürüklemekten kaçınmak). Burada aptalca bir şey yok. Mac OSX'teki büyük sınırlamaların üstesinden gelmek için dikkatlice düşünüldü. Tam sayı istemedim. Bunun için djb2'yi kullanabilirdim ve yine de std :: templates kullanmaktan kaçınabilirdim.
CubicleSoft

Bu hala aptalca geliyor. Neden olur hiç bir kullanmak DestSizekarma kendisi kadar boktan olduğunda 4 (32 bit) daha büyük? Bir int'den daha büyük bir çıktı tarafından sağlanan çarpışma direncini istiyorsanız, SHA'yı kullanırsınız.
Navin

Bak, bu gerçekten geleneksel bir esrar değil. Kullanıcının belirli işletim sistemlerinde (örn. Mac OSX) son derece sınırlı arabellek alanının olduğu ve sonucun gerçek dosya adlarının sınırlı etki alanına sığması gereken yerlerde dize boyutu bildirebileceği ve yalnızca kısaltmak istemediği kullanışlı özelliklere sahiptir. adı çünkü çarpışmalara neden OLACAKTIR (ancak daha kısa dizeler yalnız bırakılır). Kriptografik bir hash her zaman doğru cevap değildir ve std :: hash da her zaman doğru cevap değildir.
CubicleSoft
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.