Tổng kiểm tra CRC32 được tính như thế nào?


102

Có lẽ tôi không nhìn thấy nó, nhưng CRC32 có vẻ phức tạp không cần thiết hoặc không được giải thích đầy đủ ở bất cứ đâu tôi có thể tìm thấy trên web.

Tôi hiểu rằng đó là phần còn lại từ phép chia số học không dựa trên giá trị mang theo của giá trị thông báo, chia cho đa thức (bộ tạo), nhưng việc triển khai thực tế của nó khiến tôi không hiểu.

Tôi đã đọc Hướng dẫn không đau đớn cho các thuật toán phát hiện lỗi CRC và tôi phải nói rằng nó không hề dễ dàng. Nó đi qua lý thuyết khá tốt, nhưng tác giả không bao giờ hiểu đơn giản "đây là nó." Anh ta nói các tham số là gì cho thuật toán CRC32 tiêu chuẩn, nhưng anh ta bỏ qua việc trình bày rõ ràng cách bạn truy cập nó.

Phần khiến tôi thích thú là khi anh ấy nói "đây là nó" và sau đó nói thêm, "nhân tiện, nó có thể được đảo ngược hoặc bắt đầu với các điều kiện ban đầu khác nhau" và không đưa ra câu trả lời rõ ràng về cách cuối cùng tính toán tổng kiểm tra CRC32 dựa trên tất cả các thay đổi mà anh ấy vừa thêm vào.

  • Có lời giải thích đơn giản hơn về cách CRC32 được tính không?

Tôi đã cố gắng viết mã bằng C cách bảng được hình thành:

for (i = 0; i < 256; i++)
{
    temp = i;

    for (j = 0; j < 8; j++)
    {
        if (temp & 1)
        {
            temp >>= 1;
            temp ^= 0xEDB88320;
        }
        else {temp >>= 1;}
    }
    testcrc[i] = temp;
}

nhưng điều này dường như tạo ra các giá trị không phù hợp với các giá trị mà tôi đã tìm thấy ở những nơi khác trên Internet. Tôi có thể sử dụng các giá trị tôi tìm thấy trực tuyến, nhưng tôi muốn hiểu cách chúng được tạo ra.

Bất kỳ trợ giúp nào trong việc xóa những con số vô cùng khó hiểu này sẽ rất được đánh giá cao.


9
Mã của bạn để tạo bảng CRC32 dường như là chính xác. Đa thức CRC32 lsbit-first ( đảo ngược ) của 0xEDB88320bạn cũng có thể được viết msbit-first ( normal ) như 0x04C11DB7. Các giá trị bảng bạn tìm thấy ở nơi khác có được tạo bằng cách sử dụng cùng một đa thức CRC không?
jschmier

1
@jschmier xin chào, tôi cảm thấy như tôi đã đi sau một bước anh chàng này hỏi những câu hỏi? stackoverflow.com/questions/62168128/…
bluejayke

Nếu bất cứ ai khác là tò mò muốn đọc "Một không đau Hướng dẫn Để CRC phát hiện lỗi thuật toán" liên quan đến ở trên, rằng URL gốc là hosed nhưng Google dễ dàng tìm thấy nhiều bản, trong đó có một này: zlib.net/crc_v3.txt
Stéphane

Câu trả lời:


114

Đa thức cho CRC32 là:

x 32 + x 26 + x 23 + x 22 + x 16 + x 12 + x 11 + x 10 + x 8 + x 7 + x 5 + x 4 + x 2 + x + 1

Hoặc ở dạng hex và binary:

0x 01 04 C1 1D B7
1 0000 0100 1100 0001 0001 1101 1011 0111

Số hạng cao nhất (x 32 ) thường không được viết rõ ràng, vì vậy thay vào đó, nó có thể được biểu diễn bằng hex giống như

0x 04 C1 1D B7

Hãy thoải mái đếm số 1 và số 0, nhưng bạn sẽ thấy chúng khớp với đa thức, ở đó 1là bit 0 (hoặc bit đầu tiên) và xlà bit 1 (hoặc bit thứ hai).

Tại sao lại là đa thức? Bởi vì cần phải có một đa thức đã cho tiêu chuẩn và tiêu chuẩn được thiết lập bởi IEEE 802.3. Ngoài ra, rất khó để tìm một đa thức phát hiện các lỗi bit khác nhau một cách hiệu quả.

Bạn có thể nghĩ về CRC-32 như một loạt "Số học nhị phân không có đường", hoặc về cơ bản là "XOR và các phép toán thay đổi". Về mặt kỹ thuật, đây được gọi là Số học đa thức.

Để hiểu rõ hơn, hãy nghĩ đến phép nhân này:

(x^3 + x^2 + x^0)(x^3 + x^1 + x^0)
= (x^6 + x^4 + x^3
 + x^5 + x^3 + x^2
 + x^3 + x^1 + x^0)
= x^6 + x^5 + x^4 + 3*x^3 + x^2 + x^1 + x^0

Nếu giả sử x là cơ số 2 thì chúng ta nhận được:

x^7 + x^3 + x^2 + x^1 + x^0

Tại sao? Vì 3x ^ 3 là 11x ^ 11 (nhưng chúng ta chỉ cần 1 hoặc 0 chữ số trước) nên chúng ta chuyển sang:

=1x^110 + 1x^101 + 1x^100          + 11x^11 + 1x^10 + 1x^1 + x^0
=1x^110 + 1x^101 + 1x^100 + 1x^100 + 1x^11 + 1x^10 + 1x^1 + x^0
=1x^110 + 1x^101 + 1x^101          + 1x^11 + 1x^10 + 1x^1 + x^0
=1x^110 + 1x^110                   + 1x^11 + 1x^10 + 1x^1 + x^0
=1x^111                            + 1x^11 + 1x^10 + 1x^1 + x^0

Nhưng các nhà toán học đã thay đổi các quy tắc để nó là mod 2. Vì vậy, về cơ bản bất kỳ đa thức nhị phân nào mod 2 chỉ là phép cộng không có carry hoặc XOR. Vì vậy, phương trình ban đầu của chúng tôi trông giống như:

=( 1x^110 + 1x^101 + 1x^100 + 11x^11 + 1x^10 + 1x^1 + x^0 ) MOD 2
=( 1x^110 + 1x^101 + 1x^100 +  1x^11 + 1x^10 + 1x^1 + x^0 )
= x^6 + x^5 + x^4 + 3*x^3 + x^2 + x^1 + x^0 (or that original number we had)

Tôi biết đây là một bước nhảy vọt của niềm tin nhưng điều này nằm ngoài khả năng của tôi với tư cách là một lập trình viên dòng. Nếu bạn là một kỹ sư hoặc sinh viên CS cứng rắn, tôi thách thức bạn phải phá vỡ điều này. Mọi người sẽ được hưởng lợi từ phân tích này.

Vì vậy, để tìm ra một ví dụ đầy đủ:

   Original message                : 1101011011
   Polynomial of (W)idth 4         :      10011
   Message after appending W zeros : 11010110110000

Bây giờ chúng ta chia Thông điệp tăng cường cho Poly bằng số học CRC. Đây là sự phân chia giống như trước đây:

            1100001010 = Quotient (nobody cares about the quotient)
       _______________
10011 ) 11010110110000 = Augmented message (1101011011 + 0000)
=Poly   10011,,.,,....
        -----,,.,,....
         10011,.,,....
         10011,.,,....
         -----,.,,....
          00001.,,....
          00000.,,....
          -----.,,....
           00010,,....
           00000,,....
           -----,,....
            00101,....
            00000,....
            -----,....
             01011....
             00000....
             -----....
              10110...
              10011...
              -----...
               01010..
               00000..
               -----..
                10100.
                10011.
                -----.
                 01110
                 00000
                 -----
                  1110 = Remainder = THE CHECKSUM!!!!

Phép chia mang lại một thương số mà chúng ta loại bỏ và phần còn lại, là tổng kiểm tra được tính toán. Điều này kết thúc phép tính. Thông thường, tổng kiểm tra sau đó được thêm vào thông báo và kết quả được truyền đi. Trong trường hợp này, đường truyền sẽ là: 11010110111110.

Chỉ sử dụng số 32 bit làm ước số của bạn và sử dụng toàn bộ luồng làm cổ tức của bạn. Bỏ thương và giữ phần còn lại. Đánh dấu phần còn lại vào cuối tin nhắn của bạn và bạn có CRC32.

Nhận xét chàng trai trung bình:

         QUOTIENT
        ----------
DIVISOR ) DIVIDEND
                 = REMAINDER
  1. Lấy 32 bit đầu tiên.
  2. Shift bit
  3. Nếu 32 bit nhỏ hơn DIVISOR, hãy chuyển sang bước 2.
  4. XOR 32 bit bởi DIVISOR. Chuyển sang bước 2.

(Lưu ý rằng luồng phải được chia cho 32 bit hoặc nó phải được đệm. Ví dụ: luồng ANSI 8 bit sẽ phải được đệm. Cũng ở cuối luồng, việc phân chia bị tạm dừng.)


13
+1 cho "Đánh giá về chàng trai trung bình" ở cuối - có thể cân nhắc chuyển quyền này lên đầu - một loại TL; DR: P
aaronsnoswell,

4
@abstractnature Hãy nhớ rằng chúng ta đang chia các đa thức, không chỉ các số nhị phân. Chúng ta không thể thực hiện phép trừ "bình thường" vì chúng ta không thể "mượn" $ x ^ n $ từ $ x ^ {n + 1} $; chúng là những loại khác nhau. Ngoài ra, vì các bit chỉ là 0 hoặc 1, -1 thậm chí sẽ là bao nhiêu? Thực sự, chúng tôi đang làm việc trong vòng các đa thức với hệ số trong trường $ Z / 2Z $, trường này chỉ có hai phần tử, 0 và 1, và trong đó $ 1 + 1 = 0 $. Bằng cách đặt các hệ số vào một trường, sau đó các đa thức tạo thành cái được gọi là Miền Euclide, về cơ bản chỉ cho phép những gì chúng ta đang cố gắng làm được xác định rõ ràng ngay từ đầu.
calavicci

6
Chỉ cần làm rõ đa thức thực tế là 100000100110000010001110110110111 = 0x104C11DB7. MSB là ngầm định, nhưng vẫn cần được tính đến khi triển khai. Vì nó sẽ luôn được đặt vì đa thức cần phải dài 33 bit (vì vậy phần còn lại có thể dài 32 bit) một số người bỏ qua MSB.
Felipe T.

2
x^6 + x^5 + x^4 + 3*x^3 + x^2 + x^1 + x^0 ... If we assume x is base 2 then we get: x^7 + x^3 + x^2 + x^1 + x^0. Đây không phải là cách toán học hoạt động. Các hệ số của đa thức là mod (2) hoặc GF (2), còn lại của x, dẫn đến x ^ 6 + x ^ 5 + x ^ 4 + x ^ 3 + x ^ 2 + x ^ 1 + x ^ 0 (vì 3 mod (2) = 1). Tack the remainder on the end of your message- về mặt kỹ thuật, phần còn lại được trừ từ các bit 0 được thêm vào thông báo, nhưng vì đây là phép toán mod (2) nên cả phép cộng và phép trừ đều giống như XOR và các bit 0 được XOR với phần còn lại giống nhau như phần còn lại.
rcgldr

2
@MarcusJ - Why did you append four 0s though?- các thuật toán phần mềm để tính toán crc nối các số 0 một cách hiệu quả, mặc dù nó không rõ ràng. Nếu hiển thị phép tính CRC bằng cách sử dụng phép chia tay dài, thì các số 0 cần được thêm vào để ví dụ phép chia xuất hiện chính xác.
rcgldr

11

Đối với IEEE802.3, CRC-32. Hãy coi toàn bộ thư như một dòng bit nối tiếp, nối 32 số không vào cuối thư. Tiếp theo, bạn PHẢI đảo ngược các bit của MỌI byte thông báo và bổ sung số 1 cho 32 bit đầu tiên. Bây giờ chia cho đa thức CRC-32, 0x104C11DB7. Cuối cùng, bạn phải bổ sung cho 1 phần còn lại của 32 bit của phép chia này, đảo ngược từng bit trong số 4 byte của phần còn lại. Điều này trở thành CRC 32-bit được nối vào cuối thông báo.

Lý do cho thủ tục kỳ lạ này là các triển khai Ethernet đầu tiên sẽ tuần tự hóa thông điệp từng byte một và truyền bit quan trọng nhất trong mỗi byte đầu tiên. Dòng bit nối tiếp sau đó đi qua một tính toán thanh ghi dịch chuyển CRC-32 nối tiếp, được bổ sung một cách đơn giản và được gửi đi trên dây sau khi thông báo được hoàn thành. Lý do bổ sung 32 bit đầu tiên của thông báo là để bạn không nhận được CRC hoàn toàn bằng 0 ngay cả khi thông báo chỉ toàn là số 0.


2
Đây là câu trả lời tốt nhất ở đây cho đến nay, mặc dù tôi sẽ thay thế 'đảo ngược bit từng 4 byte' bằng 'đảo ngược bit 4 byte, coi chúng như một thực thể' ví dụ: 'abcdefgh ijklmnop qrstuvwx yzABCDEF' thành 'FEDCBAzy xwvutsrq ponmlkji hgfedcba '. Xem thêm: Hướng dẫn băm CRC-32 - Cộng đồng AutoHotkey .
vafylec

1
xin chào, "thông điệp" chính xác là gì, bạn đảo ngược lại bằng cách nào? stackoverflow.com/questions/62168128/…
bluejayke

10

CRC khá đơn giản; bạn lấy một đa thức được biểu diễn dưới dạng các bit và dữ liệu, và chia đa thức thành dữ liệu (hoặc bạn biểu diễn dữ liệu dưới dạng đa thức và làm điều tương tự). Phần còn lại, nằm giữa 0 và đa thức là CRC. Mã của bạn hơi khó hiểu, một phần vì nó chưa hoàn thiện: temp và testcrc không được khai báo, vì vậy không rõ những gì đang được lập chỉ mục và bao nhiêu dữ liệu đang chạy qua thuật toán.

Cách để hiểu CRC là cố gắng tính toán một số ít bằng cách sử dụng một đoạn dữ liệu ngắn (16 bit hoặc lâu hơn) với một đa thức ngắn - có lẽ là 4 bit. Nếu bạn thực hành theo cách này, bạn sẽ thực sự hiểu cách bạn có thể viết mã.

Nếu bạn làm việc này thường xuyên, CRC tính toán trong phần mềm khá chậm. Tính toán phần cứng hiệu quả hơn nhiều và chỉ cần một vài cổng.


1
Đối với CRC32 hoặc CRC32b, chúng ta có nhận được ý nghĩa xung đột băm cho hai chuỗi khác nhau không, chúng ta có nhận được CRC giống nhau không
indianwebdevil

1
xin chào, Tôi hơi bối rối ý bạn là "divifde the polynomials into the data"? stackoverflow.com/questions/62168128/… X là gì trong đa thức được in lại bởi? Tôi có sử dụng byte oter từ đoạn này không?
bluejayke

7

Ngoài Wikipedia kiểm tra dự phòng theo chu kỳ Tính toán các bài báo CRC , tôi thấy một bài báo có tựa đề Đảo ngược CRC - Lý thuyết và Thực hành * là một tài liệu tham khảo tốt.

Về cơ bản, có ba cách tiếp cận để tính toán CRC: cách tiếp cận đại số, cách tiếp cận hướng bit và cách tiếp cận hướng bảng. Trong Reversing CRC - Theory and Practice * , mỗi thuật toán / cách tiếp cận trong số ba thuật toán / cách tiếp cận này được giải thích về lý thuyết kèm theo trong PHỤ LỤC bằng cách triển khai CRC32 trong ngôn ngữ lập trình C.

* Liên kết PDF
Đảo ngược CRC - Lý thuyết và Thực hành.
HU Berlin Public Report
SAR-PR-2006-05
/ 05/2006
Các tác giả:
Martin Stigge, Henryk Plötz, Wolf Müller, Jens-Peter Redlich


xin chào, bạn có thể nói rõ hơn một chút không?
bluejayke

7

Tôi đã dành một thời gian cố gắng tìm ra câu trả lời cho câu hỏi này và cuối cùng tôi đã xuất bản một hướng dẫn về CRC-32 hôm nay: Hướng dẫn băm CRC-32 - Cộng đồng AutoHotkey

Trong ví dụ này từ nó, tôi trình bày cách tính hàm băm CRC-32 cho chuỗi ASCII 'abc':

calculate the CRC-32 hash for the ASCII string 'abc':

inputs:
dividend: binary for 'abc': 0b011000010110001001100011 = 0x616263
polynomial: 0b100000100110000010001110110110111 = 0x104C11DB7

011000010110001001100011
reverse bits in each byte:
100001100100011011000110
append 32 0 bits:
10000110010001101100011000000000000000000000000000000000
XOR the first 4 bytes with 0xFFFFFFFF:
01111001101110010011100111111111000000000000000000000000

'CRC division':
01111001101110010011100111111111000000000000000000000000
 100000100110000010001110110110111
 ---------------------------------
  111000100010010111111010010010110
  100000100110000010001110110110111
  ---------------------------------
   110000001000101011101001001000010
   100000100110000010001110110110111
   ---------------------------------
    100001011101010011001111111101010
    100000100110000010001110110110111
    ---------------------------------
         111101101000100000100101110100000
         100000100110000010001110110110111
         ---------------------------------
          111010011101000101010110000101110
          100000100110000010001110110110111
          ---------------------------------
           110101110110001110110001100110010
           100000100110000010001110110110111
           ---------------------------------
            101010100000011001111110100001010
            100000100110000010001110110110111
            ---------------------------------
              101000011001101111000001011110100
              100000100110000010001110110110111
              ---------------------------------
                100011111110110100111110100001100
                100000100110000010001110110110111
                ---------------------------------
                    110110001101101100000101110110000
                    100000100110000010001110110110111
                    ---------------------------------
                     101101010111011100010110000001110
                     100000100110000010001110110110111
                     ---------------------------------
                       110111000101111001100011011100100
                       100000100110000010001110110110111
                       ---------------------------------
                        10111100011111011101101101010011

remainder: 0b10111100011111011101101101010011 = 0xBC7DDB53
XOR the remainder with 0xFFFFFFFF:
0b01000011100000100010010010101100 = 0x438224AC
reverse bits:
0b00110101001001000100000111000010 = 0x352441C2

thus the CRC-32 hash for the ASCII string 'abc' is 0x352441C2

1
Nếu bạn muốn có tốc độ cao hơn, đã có một phương pháp được một số kỹ sư tại Intel đưa ra vào khoảng năm 2006 bằng cách sử dụng đồng thời 4 hoặc 8 byte chiều rộng bus dữ liệu của máy. Bài báo học thuật: static.aminer.org/pdf/PDF/000/432/446/… Dự án trên Sourceforge: sourceforge.net/projects/slicing-by-8 Trang crc chung: create.stephan-brumme.com/crc32
Alan Corey

1
Xin chào, cảm ơn trông rất tuyệt, nhưng chính xác thì làm cách nào để bạn nhận được giá trị đa thức? chính xác thì X đại diện cho cái gì? Và khi nó nói x ^ 32, thì x đó là lũy thừa của 32 hay toán tử bitwise ^? stackoverflow.com/questions/62168128/…
bluejayke


1

Để giảm crc32 đến việc thực hiện lời nhắc, bạn cần:

  1. Đảo các bit trên mỗi byte
  2. x hoặc bốn byte đầu tiên với 0xFF (điều này để tránh lỗi ở các số 0 ở đầu)
  3. Thêm phần đệm vào cuối (điều này là để làm cho 4 byte cuối cùng tham gia vào hàm băm)
  4. Tính toán lời nhắc
  5. Đảo ngược các bit một lần nữa
  6. xor lại kết quả.

Trong mã này là:


func CRC32 (file []byte) uint32 {
    for i , v := range(file) {
        file[i] = bits.Reverse8(v)
    }
    for i := 0; i < 4; i++ {
        file[i] ^= 0xFF
    }

    // Add padding
    file = append(file, []byte{0, 0, 0, 0}...)
    newReminder := bits.Reverse32(reminderIEEE(file))

    return newReminder ^ 0xFFFFFFFF
}

nơi nhắc nhởIEEE là lời nhắc thuần túy trên GF (2) [x]


1
Tôi có một chút (dự định chơi chữ) khó hiểu điều này? stackoverflow.com/questions/62168128/…
bluejayke

1
hey @bluejayke, hãy kiểm tra thư viện này github.com/furstenheim/sparse_crc32/blob/master/main.go nó thực hiện crc32 cho các tệp thưa thớt, bạn có thể thấy ở đó các chi tiết về tính toán thực sự. Nó không được tối ưu hóa nên dễ làm theo hơn các triển khai bình thường. Có thể những gì bạn không hiểu là phần GF (2) [x]. Về cơ bản x ^ 3 + x có nghĩa là 1010, x ^ 4 + x + 1 có nghĩa là 10011. Sau đó, bạn cần thực hiện phép chia, ví dụ x ^ 3 + x là x * (x ^ 2 + 1). vì vậy lời nhắc của x ^ 3 + x trên x là 0, nhưng trên x ^ 2, lời nhắc sẽ là x ^ 2 * x + x, tức là lời nhắc sẽ là x.
Gabriel Furstenheim

1
@bluejayke và nhắc nhởIEEE có nghĩa là lời nhắc nhở chống lại một đa thức nổi tiếng, đa thức IEEE
Gabriel Furstenheim

chào một lần nữa, cảm ơn vì phản hồi của bạn. Tôi chỉ đang cố gắng hiểu (cho mục đích javascript) ký tự "x" trong đa thức là gì. Có phải "x" là một số loại từ mã cho thứ mà tôi đang thiếu ở đây không? Có rất nhiều thuật ngữ khiến tôi bối rối ở đây, tôi chưa bao giờ nghe nói về CRC32 trước đây và thậm chí sau khi tìm kiếm, tôi không thể tìm thấy nó thực sự được giải thích. Ví dụ: đối với PNG, nó nói rằng tôi cần lấy "CRC cho từng đoạn", điều đó có nghĩa là "cho tất cả dữ liệu trong đoạn"? Nhưng làm thế nào để tôi "cắm nó vào" đa thức? "X" đại diện cho điều gì? Ngoài ra khi nó nói x ^ 32, giống như Math.pow (x, 32) hoặc bitwise ^
bluejayke

1
Xin chào @bluejayke, x là một hàm trừu tượng để giúp tính toán dễ dàng. Nó không được mong đợi để thay thế bởi bất cứ điều gì. x ^ 2 Ý tôi là x * x, như một phép nhân chính thức. Tại đây chrisballance.com/wp-content/uploads/2015/10/CRC-Primer.html bạn có thể tìm thấy lời giải thích hay về sự phân chia đó. Những gì tôi đã cố gắng với câu trả lời của mình là lấp đầy khoảng cách giữa phép chia (trong liên kết đó) và phép tính thực tế
Gabriel Furstenheim
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.