16 位元檢查碼(checksum)是一個固定的兩位元組值,會使用已公開的公式(例如 CRC-16-CCITT、CRC-16-MODBUS 或網際網路的 1's 補數和),從一段定義好的訊息位元組範圍計算出來;在同一個公式下,相同的輸入必須永遠產生相同的輸出。16 位元中的「16」這個數字只代表結果的寬度:每一組被接受的位元組集合會剛好對應到兩個位元組,通常以四個十六進位數字表示。演算法本身會因為通訊協定不同而有所差異,這也是為什麼廠商文件必須與任何計算工具搭配閱讀的原因。寬度、初始值、涵蓋的位元組範圍、多項式、反射(reflection)以及最終轉換步驟,都會改變輸出結果。檢查碼計算工具實作了兩種透明的 8 位元公式以及一種模 256 的加總和;它適用於序列埠與 Modbus ASCII 裝置所使用的位元組層級檢查,但當通訊協定指定 CRC-16、Fletcher-16、網際網路檢查碼或任何具名的加密雜湊時,它就不是合適的工具。了解你的目標通訊協定預期哪種公式是第一個決定;第二個決定則是要餵入的正確位元組序列。

how to calculate checksum 16 bit
how to calculate checksum 16 bit

16 位元檢查碼會產生什麼

凡是將其檢查機制稱為 16 位元檢查碼的通訊協定,都承諾了兩項特定的事實:結果剛好是兩個位元組寬,並且計算過程是完全確定性的,讓接收端可以重新計算並進行比對。除此之外的其他細節則是可以自由解釋的空間。不同系列的 16 位元檢查依賴的數學原理完全不同:

  • CRC-16 系列將輸入視為在 GF(2) 上的多項式,並以一個固定的生成多項式去除它,然後公布餘數。多項式、初始值、輸入反射旗標、輸出反射旗標以及最終的 XOR,都會改變答案。
  • 網際網路檢查碼(用於 IPv4、TCP 與 UDP)將資料視為 16 位元的字組,使用 1's 補數運算進行加總,然後把進位折回低 16 位元。
  • Fletcher-16保留兩個累計值(一個簡單的總和,以及該總和的模數總和),並將兩者各以一個位元組輸出。
  • Adler-16屬於同一系列但使用不同的模數,在小眾應用以外並不常見。

在這些公式中,輸入只要有單一位元組的差異,就足以改變輸出中所有兩個位元組的值;而且任何 16 位元檢查碼都可以被能編輯訊息的攻擊者破解:可能的輸出只有 65,536 種,但單一位元組的編輯方式卻有數十億種。寬度能防範的是隨機損壞,而非竄改行為。

你會遇到常見的 16 位元檢查碼公式

三大系列就涵蓋了大多數的嵌入式、工業與網路應用。

CRC-16

CRC-16 是部署最廣泛的 16 位元檢查。常見的參數組合包括 CRC-16-CCITT(多項式 0x1021,初始值 0xFFFF 或 0x0000)、CRC-16-MODBUS(多項式 0x8005,初始值 0xFFFF,輸入與輸出皆反射)、CRC-16-IBM、CRC-16-USB 以及 CRC-16-XMODEM。對於同一筆輸入,每一種變體都會回傳不同的 16 位元值。BALTECH XOR-8 BCC 參考資料說明了即使是更簡單的單位元組公式,廠商文件之間也可能有差異,這也是為什麼實作 CRC-16 時必須引用通訊協定中的每一個參數。

網際網路檢查碼(RFC 1071)

用於 IPv4、ICMPv4、TCP 與 UDP。資料會被分組為 16 位元的字組,使用迴旋進位(end-around carry)進行加總,並傳輸結果的 16 位元 1's 補數。接收端會對資料加上檢查碼欄位計算出相同的總和;正確的訊框其總和會等於全 1(0xFFFF)。如需深入了解相關的循環檢查機制,請參閱逐步解說的 CRC-32 完整性指南

Fletcher-16

兩個累加器 sum1 與 sum2,分別以模 255 與 sum1 之上的模 255 進行更新。輸出為 (sum2 << 8) 或 sum1。常見於 zlib 類型的應用,與 Adler-32 並用。

這些結果都無法由檢查碼計算工具產生。它刻意只做到 8 位元公式,確保輸出不含歧義,如此一來,讀者貼到論壇或工單中的結果能對應到一個明確標示的公式,而不是某種未知的混合結果。

使用檢查碼計算工具計算 8 位元變體

對於使用單位元組檢查的通訊協定,檢查碼計算工具會在相同的位元組集合上顯示三個明確的數值。當規格要求 XOR-8 BCC、Modbus ASCII LRC 或模 256 的加總和時,即可使用它。

  1. 挑選符合通訊協定的輸入模式。當酬載是可供列印的字串時,UTF-8 文字是正確的選擇;當規格直接給你原始位元組(例如 01 03 00 00 00 06)時,十六進位位元組(每個位元組兩個十六進位數字,可選擇加上空白)才是正確的選擇。
  2. 只輸入檢查範圍涵蓋的位元組。Modbus ASCII LRC 明確排除了起始冒號與結尾的 CRLF;請輸入夾在其間的訊息位元組,而非整行。其他通訊協定可能會包含或排除長度與位址位元組;規格才是最終依據。
  3. 執行計算。頁面會顯示三個已標示的數值——XOR-8 BCC、Modbus ASCII LRC,以及模 256 的原始位元組加總——每一個都以剛好兩個小寫的十六進位數字呈現,並附上實際處理的位元組數量。
  4. 比對並複製。將標示的數值與裝置文件進行比對。複製輸出會包含標籤、三個數值以及位元組數量,讓貼上的診斷註記能保留所使用的公式資訊。

格式錯誤的十六進位輸入(奇數個 nibble、殘留的逗號、混用的分隔符)會被拒絕而非悄悄修正;超過 500,000 位元組的輸入也會被拒絕而不會截斷。如果你需要 CRC-16、網際網路檢查碼或具名雜湊,這個工具並不合用——請使用會明確標示其參數的 CRC 或雜湊工具。

實作範例:一個 Modbus 風格的訊框

為了讓公式更具體,這裡取六個類似 Modbus 讀取要求標頭的位元組:01 03 00 00 00 06。以下是計算工具針對這六個位元組所報告的三個數值的精確算術過程。

XOR-8 BCC 從零開始,依序對每個位元組進行 XOR:

  • 0x00 XOR 0x01 = 0x01
  • 0x01 XOR 0x03 = 0x02
  • 0x02 XOR 0x00 = 0x02
  • 0x02 XOR 0x00 = 0x02
  • 0x02 XOR 0x00 = 0x02
  • 0x02 XOR 0x06 = 0x04

結果:BCC = 0x04。在 XOR 中進位不產生影響,順序也無關緊要。

模 256 的位元組加總將這六個位元組相加,並保留低八位元:

  • 0x01 + 0x03 + 0x00 + 0x00 + 0x00 + 0x06 = 0x0A(十進位為 10)

結果:總和 = 0x0A。

Modbus ASCII LRC 會取該總和的二補數,這等同於模 256 的負位元組總和:

  • −0x0A mod 256 = 0xF6(256 減去 10 等於 246)

結果:LRC = 0xF6。作為快速健全性檢查,總和 + LRC 模 256 為 0x0A + 0xF6 = 0x100,捨棄進位後剩下 0x00。接收端會看到相同的零,這就是 Modbus ASCII 用來驗證訊息的方式。

並排比較 16 位元與 8 位元檢查

特性 典型的 16 位元檢查(CRC-16 / 網際網路) 8 位元檢查(XOR-8 BCC / Modbus LRC)
結果寬度 兩個位元組,四個十六進位數字 一個位元組,兩個十六進位數字
偵測所有單位元錯誤 若多項式合適,CRC-16 可以;網際網路檢查碼在正常框架下也可以 XOR-8 與加總式 LRC 都可以
偵測大多數叢發錯誤 CRC-16 可捕捉長達 16 位元的叢發;網際網路檢查碼對較長叢發較弱 僅限短叢發;若出現偶數個相同的翻轉,在 XOR 或加總形式中會相互抵消
數學原理 多項式除法或 1's 補數字組加總 單位元組 XOR 或單位元組相加後取二補數
參數敏感度 高:多項式、初始值、反射與最終 XOR 都會改變輸出 較低:主要取決於初始值(XOR-8 為 0,加總式為 0)以及是否納入框架
身分驗證 無;任何 16 位元值皆可偽造 無;只有 256 種可能輸出
典型通訊協定 Modbus RTU、IPv4、TCP、UDP、USB、Bluetooth HCI Modbus ASCII、特定的序列訊框、簡單的裝置通訊協定

寬度只是其中一個因素。經過妥善選擇的 16 位元 CRC 能在長訊框中捕捉所有單位元錯誤以及大多數多位元錯誤;而 8 位元的 XOR 或加總式檢查可能輸出較少,在相同酬載下發生碰撞的機率也較高。

檢查碼不適用的時機

本文中介紹的每一種公式都是錯誤偵測檢查,而非身分驗證或加密的原始機制。兩則具有相同 8 位元檢查碼的訊息可以輕易地刻意建構,而 CRC-16 碰撞也只是稍微難一點而已。對於憑證傳輸、命令授權、軟體下載驗證,或任何可能面臨主動攻擊者的流量,通訊協定必須指定加密式 MAC(例如 HMAC-SHA-256)或數位簽章。一個僅用來確認訊息完整送達的檢查碼,並不能構成安全防線。

如果目標規格指定的是 CRC-16、Fletcher、Adler、網際網路檢查碼或任何加密雜湊,請選擇會明確標示其所實作參數的工具。檢查碼計算工具的設計刻意保持精簡:它只重現兩種具名的 8 位元公式和一種加總和,讓標籤能對應到裝置文件,而非揣測其原始意圖。

如果你正在權衡各種選項,如何逐步計算 CRC 檢查碼對此有詳細說明。