Hàm băm tạo ra các hàm băm ngắn?


97

Có cách mã hóa nào có thể lấy một chuỗi có độ dài bất kỳ và tạo ra một hàm băm gồm 10 ký tự không? Tôi muốn tạo ID duy nhất hợp lý nhưng dựa trên nội dung tin nhắn, thay vì ngẫu nhiên.

Tuy nhiên, tôi có thể sống với việc ràng buộc các thông báo thành các giá trị số nguyên, nếu các chuỗi có độ dài tùy ý là không thể. Tuy nhiên, hàm băm không được giống nhau cho hai số nguyên liên tiếp, trong trường hợp đó.


Đó được gọi là băm. Nó sẽ không phải là duy nhất.
SLaks

1
Đây cũng là một băm cắt ngắn vấn đề, vì vậy thấy cũng stackoverflow.com/q/4784335
Peter Krauss

2
FYI, hãy xem danh sách các hàm băm trong Wikipedia.
Basil Bourque

Câu trả lời:


76

Bạn có thể sử dụng bất kỳ thuật toán băm nào thường có sẵn (ví dụ: SHA-1), sẽ cho bạn kết quả dài hơn một chút so với những gì bạn cần. Đơn giản chỉ cần cắt ngắn kết quả theo độ dài mong muốn, có thể đủ tốt.

Ví dụ, trong Python:

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

2
Bất kỳ hàm băm hợp lý nào cũng có thể bị cắt bớt.
Tổng thống James K. Polk,

88
Điều này sẽ không làm tăng nguy cơ va chạm lên một mức độ cao hơn nhiều?
Gabriel Sanmartin

143
@erasmospunk: mã hóa với base64 không có tác dụng gì đối với khả năng chống va chạm, vì nếu hash(a)va chạm với hash(b)thì base64(hash(a))cũng va chạm với base64(hash(b)).
Greg Hewgill

56
@GregHewgill bạn nói đúng, nhưng chúng tôi không nói về thuật toán băm ban đầu va chạm (vâng, sha1va chạm nhưng đây là một câu chuyện khác). Nếu bạn có một băm 10 ký tự, bạn sẽ nhận được entropy cao hơn nếu nó được mã hóa bằng base64vs base16(hoặc hex). Cao hơn bao nhiêu? Với việc base16bạn nhận được 4 bit thông tin cho mỗi ký tự, với base64con số này là 6bits / char. Totaly một băm "hex" 10 ký tự sẽ có 40 bit entropy trong khi một cơ sở 64 60 bit. Vì vậy, nó có sức đề kháng cao hơn một chút , xin lỗi nếu tôi không rõ ràng lắm.
John L. Jegutanis

19
@erasmospunk: Ồ, tôi hiểu ý bạn, đúng vậy nếu bạn có kích thước cố định hạn chế cho kết quả của mình thì bạn có thể đóng gói nhiều bit quan trọng hơn với mã hóa base64 so với mã hóa hex.
Greg Hewgill

46

Nếu bạn không cần một thuật toán chống lại sự sửa đổi có chủ đích, tôi đã tìm thấy một thuật toán có tên là adler32 tạo ra kết quả khá ngắn (~ 8 ký tự). Chọn nó từ menu thả xuống ở đây để dùng thử:

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


2
nó rất cũ, không đáng tin cậy lắm.
Mascarpone

1
@Mascarpone "không đáng tin cậy lắm" - nguồn? Nó có những hạn chế, nếu bạn biết chúng thì không quan trọng nó bao nhiêu tuổi.
BT

8
@Mascarpone "ít điểm yếu" - một lần nữa, những gì yếu kém? Tại sao bạn nghĩ rằng thuật toán này không hoàn hảo 100% cho việc sử dụng của OP?
BT

3
@Mascarpone OP không nói rằng họ muốn một hàm băm cấp tiền điện tử. OTOH, Adler32 là tổng kiểm tra, không phải băm, vì vậy nó có thể không phù hợp, tùy thuộc vào những gì OP thực sự đang làm với nó.
Chiều 2 giờ chiều

2
Có một lưu ý đối với Adler32, trích dẫn Wikipedia : Adler-32 có một điểm yếu đối với các tin nhắn ngắn với vài trăm byte, bởi vì tổng kiểm tra cho các tin nhắn này có mức độ bao phủ kém trong 32 bit có sẵn.
Basil Bourque

13

Bạn cần băm nội dung để đưa ra thông báo. Có nhiều hàm băm có sẵn nhưng 10 ký tự là khá nhỏ cho tập kết quả. Quay trở lại, mọi người đã sử dụng CRC-32, tạo ra một hàm băm 33 bit (về cơ bản 4 ký tự cộng với một bit). Ngoài ra còn có CRC-64 tạo ra băm 65 bit. MD5, tạo ra một băm 128 bit (16 byte / ký tự) được coi là bị hỏng cho các mục đích mật mã vì có thể tìm thấy hai thông báo có cùng một băm. Không cần phải nói rằng bất cứ khi nào bạn tạo một bản tóm tắt 16 byte từ một thông báo có độ dài tùy ý, bạn sẽ nhận được các bản sao. Thời gian tiêu hóa càng ngắn, nguy cơ va chạm càng lớn.

Tuy nhiên, mối quan tâm của bạn rằng hàm băm không giống nhau cho hai thông báo liên tiếp (cho dù là số nguyên hay không) phải đúng với tất cả các hàm băm. Ngay cả một chút thay đổi trong tin nhắn ban đầu cũng sẽ tạo ra một thông báo kết quả rất khác.

Vì vậy, sử dụng một thứ gì đó như CRC-64 (và kết quả là base-64) sẽ giúp bạn đến được khu vực lân cận mà bạn đang tìm kiếm.


1
CRC tạo một hàm băm SHA-1 và sau đó đặt kết quả là cơ sở 64 có làm cho ID kết quả có khả năng chống va chạm tốt hơn không?

5
"Tuy nhiên, mối quan tâm của bạn rằng hàm băm không giống nhau cho hai thông báo liên tiếp [...] phải đúng với tất cả các hàm băm." - Điều đó chưa chắc đã đúng. Ví dụ: đối với các hàm băm được sử dụng để phân cụm hoặc phát hiện bản sao, thực tế thì hoàn toàn ngược lại: bạn muốn các tài liệu tương tự mang lại giá trị băm tương tự (hoặc thậm chí giống nhau). Một ví dụ nổi tiếng về thuật toán băm được thiết kế đặc biệt để mang lại các giá trị giống hệt nhau cho đầu vào tương tự là Soundex.
Jörg W Mittag,

Tôi đang sử dụng hàm băm để xác thực chữ ký của thư. Vì vậy, về cơ bản, đối với một thông báo đã biết và chữ ký được chỉ định, hàm băm phải chính xác. Tuy nhiên, tôi không quan tâm liệu sẽ có một tỷ lệ nhỏ dương tính giả hay không. Nó hoàn toàn có thể chấp nhận được. Tôi hiện đang sử dụng hàm băm SHA-512 đã cắt ngắn được nén với base62 (thứ mà tôi đã cập nhật nhanh chóng) để thuận tiện.

@ JörgWMittag Điểm xuất sắc trên SoundEx. Tôi đứng sửa lại. Không phải tất cả các hàm băm đều có đặc điểm giống nhau.
John

12

Chỉ tóm tắt một câu trả lời hữu ích đối với tôi (lưu ý nhận xét của @ erasmospunk về việc sử dụng mã hóa base-64). Mục tiêu của tôi là phải có một chuỗi ngắn mà là chủ yếu là độc đáo ...

Tôi không phải là chuyên gia, vì vậy vui lòng sửa lỗi này nếu nó có bất kỳ lỗi rõ ràng nào (bằng Python một lần nữa giống như câu trả lời được chấp nhận):

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

resultđây không chỉ sử dụng các ký tự hex (những gì bạn sẽ nhận được nếu bạn đã sử dụng hash.hexdigest()) nên ít có khả năng xảy ra va chạm hơn (nghĩa là, cắt bớt sẽ an toàn hơn so với thông báo hex).

Lưu ý: Sử dụng UUID4 (ngẫu nhiên). Xem http://en.wikipedia.org/wiki/Universally_unique_identifier để biết các loại khác.


7

Bạn có thể sử dụng một thuật toán băm hiện có tạo ra một cái gì đó ngắn, như MD5 (128 bit) hoặc SHA1 (160). Sau đó, bạn có thể rút ngắn hơn nữa bằng cách XOR các phần của thông báo với các phần khác. Điều này sẽ làm tăng khả năng xảy ra va chạm, nhưng không tệ bằng việc chỉ đơn giản là cắt ngắn phần thông báo.

Ngoài ra, bạn có thể bao gồm độ dài của dữ liệu gốc như một phần của kết quả để làm cho nó độc đáo hơn. Ví dụ: XOR nửa đầu tiên của thông báo MD5 với nửa thứ hai sẽ dẫn đến 64 bit. Thêm 32 bit cho độ dài của dữ liệu (hoặc thấp hơn nếu bạn biết rằng độ dài đó sẽ luôn phù hợp với ít bit hơn). Điều đó sẽ dẫn đến kết quả 96 bit (12 byte) mà sau đó bạn có thể chuyển thành chuỗi hex 24 ký tự. Ngoài ra, bạn có thể sử dụng mã hóa cơ sở 64 để làm cho nó ngắn hơn.


2
FWIW, đây được gọi là XOR-gấp.
Chiều 2 giờ chiều

7

Nếu cần, "sub-10-character hash" bạn có thể sử dụng thuật toán Fletcher-32 tạo ra 8 ký tự băm (32 bit), CRC-32 hoặc Adler-32 .

CRC-32 chậm hơn Adler32 khoảng 20% ​​- 100%.

Fletcher-32 đáng tin cậy hơn Adler-32 một chút. Nó có chi phí tính toán thấp hơn so với tổng kiểm tra Adler: so sánh Fletcher vs Adler .

Dưới đây là một chương trình mẫu với một số triển khai Fletcher:

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

Đầu ra:

4031760169 F04FC729                                                                                                                                                                                                                              
4031760169 F04FC729                                                                                                                                                                                                                              

1448095018 56502D2A                                                                                                                                                                                                                              
1448095018 56502D2A                                                                                                                                                                                                                              

Đồng ý với vectơ Kiểm tra :

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

Adler-32 có một điểm yếu đối với các tin nhắn ngắn với vài trăm byte, bởi vì tổng kiểm tra cho các tin nhắn này có mức độ bao phủ kém hơn 32 bit có sẵn. Kiểm tra điều này:

Thuật toán Adler32 không đủ phức tạp để cạnh tranh với các tổng kiểm tra tương đương .


6

Chỉ cần chạy điều này trong một thiết bị đầu cuối (trên MacOS hoặc Linux):

crc32 <(echo "some string")

Dài 8 ký tự.


4

Bạn có thể sử dụng thư viện hashlib cho Python. Các shake_128shake_256 thuật toán cung cấp băm chiều dài thay đổi. Đây là một số mã làm việc (Python3):

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

Lưu ý rằng với tham số độ dài x (ví dụ là 5), hàm trả về giá trị băm có độ dài 2x .


1

Bây giờ là năm 2019 và có nhiều lựa chọn tốt hơn. Cụ thể là xxhash .

~ echo test | xxhsum                                                           
2d7f1808da1fa63c  stdin

Liên kết này bị hỏng. tốt hơn nên cung cấp một câu trả lời đầy đủ hơn.
eri0o

0

Tôi cần một cái gì đó dọc theo các dòng của một hàm giảm chuỗi đơn giản gần đây. Về cơ bản, mã trông giống như sau (mã C / C ++ phía trước):

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

Nó có thể có nhiều va chạm hơn mức mong muốn nhưng nó không được sử dụng như một hàm băm mật mã. Bạn có thể thử các cấp số nhân khác nhau (tức là thay đổi số 37 thành một số nguyên tố khác) nếu bạn gặp quá nhiều va chạm. Một trong những tính năng thú vị của đoạn mã này là khi Src ngắn hơn Dest, thì Dest kết thúc bằng chuỗi đầu vào như hiện tại (0 * 37 + value = value). Nếu bạn muốn một thứ gì đó "có thể đọc được" vào cuối quá trình, Normalize sẽ điều chỉnh các byte đã được chuyển đổi với chi phí tăng va chạm.

Nguồn:

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


std :: hash không giải quyết các trường hợp sử dụng nhất định (ví dụ: tránh kéo trong std :: template cồng kềnh khi chỉ cần thêm một vài dòng mã là đủ). Không có gì ngớ ngẩn ở đây. Nó đã được suy nghĩ cẩn thận để giải quyết những hạn chế lớn trong Mac OSX. Tôi không muốn một số nguyên. Đối với điều đó, tôi có thể đã sử dụng djb2 và vẫn tránh sử dụng std :: template.
CubicleSoft

Điều này nghe vẫn còn ngớ ngẩn. Tại sao bạn sẽ không bao giờ sử dụng một DestSizelớn hơn 4 (32 bit) khi băm chính nó là rất không hấp dẫn? Nếu bạn muốn khả năng chống va chạm được cung cấp bởi đầu ra lớn hơn int, bạn sẽ sử dụng SHA.
Navin

Nhìn xem, nó không thực sự là một hàm băm truyền thống. Nó có các thuộc tính hữu ích trong đó người dùng có thể khai báo kích thước chuỗi ở những nơi có không gian đệm cực kỳ hạn chế trên một số hệ điều hành nhất định (ví dụ: Mac OSX) VÀ kết quả phải vừa với miền giới hạn của tên tệp thực VÀ họ không muốn cắt bớt tên bởi vì điều đó SẼ gây ra xung đột (nhưng các chuỗi ngắn hơn được để riêng). Một hàm băm mật mã không phải lúc nào cũng là câu trả lời đúng và std :: hash cũng không phải lúc nào cũng là câu trả lời đúng.
CubicleSoft
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.