資料完整性與錯誤檢查基礎概念
本文介紹程式、通訊協定與嵌入式系統中常見的資料完整性檢查方法,包含 Parity、Checksum、LRC、CRC8、CRC16、CRC32、PEC、MD5、SHA-256、HMAC 與數位簽章,並說明它們分別適合用於「錯誤偵測」或「安全驗證」。
1. 為什麼需要資料完整性檢查?
在程式與硬體通訊中,資料可能因為各種原因發生錯誤:
- 通訊線路受到雜訊干擾。
- UART / I2C / SPI / CAN / RS-485 傳輸時發生 bit flip。
- Flash、EEPROM 或檔案儲存時資料損壞。
- 封包長度、位址、資料欄位解析錯誤。
- 韌體更新檔下載不完整。
- 惡意攻擊者刻意竄改資料。
因此,系統通常會在資料後面附加一段檢查值,例如 checksum、CRC 或 hash,用來確認資料是否正確。
2. 錯誤偵測 vs. 安全驗證
很多人會把 CRC、Checksum、MD5、SHA、HMAC 混在一起,但它們的目的不同。
最重要的分界是:
CRC / Checksum 主要用來偵測意外錯誤;HMAC / 數位簽章才適合用來防止惡意竄改。
2.1 錯誤偵測
錯誤偵測的目標是確認資料在傳輸或儲存過程中有沒有「不小心壞掉」。
常見例子:
- Parity
- Checksum
- LRC
- CRC8
- CRC16
- CRC32
- PEC
這類方法通常計算速度快、實作簡單,適合低階通訊協定與嵌入式系統。
但它們沒有祕密金鑰。只要攻擊者知道演算法,就可以修改資料後重新計算檢查值,因此不適合拿來防止惡意竄改。
2.2 安全驗證
安全驗證的目標是確認資料是否遭到惡意竄改,以及資料是否來自可信來源。
常見例子:
- HMAC
- 數位簽章
- 韌體簽章驗證
- 憑證驗證
這類方法通常會使用祕密金鑰或私鑰,因此攻擊者即使知道演算法,也很難偽造合法結果。
3. 常見方法總覽
| 名稱 | 類型 | 主要用途 | 可防意外錯誤 | 可防惡意竄改 |
|---|---|---|---|---|
| Parity | 同位檢查 | 偵測簡單 bit 錯誤 | 是 | 否 |
| Checksum | 檢查和 | 快速檢查資料是否損壞 | 是 | 否 |
| LRC | 縱向冗餘檢查 | 簡單封包檢查 | 是 | 否 |
| CRC8 / CRC16 / CRC32 | 循環冗餘檢查 | 通訊、儲存、封包錯誤偵測 | 是 | 否 |
| PEC | Packet Error Code | SMBus / PMBus 封包錯誤檢查 | 是 | 否 |
| MD5 | 密碼學雜湊,已不安全 | 舊系統 fingerprint、非安全性校驗 | 是 | 不建議 |
| SHA-256 | 密碼學雜湊 | 檔案完整性、簽章前摘要 | 是 | 視使用方式而定 |
| HMAC | 金鑰式訊息驗證碼 | API 驗證、訊息完整性與身分驗證 | 是 | 是 |
| 數位簽章 | 非對稱密碼學驗證 | 韌體更新、憑證、文件簽署 | 是 | 是 |
4. Parity:同位檢查
Parity 是最簡單的錯誤偵測方式之一,常見於 UART 等通訊設定中。
Parity 會在資料位元後面附加一個同位檢查位元,讓整筆資料中 1 的數量符合指定規則。
常見模式:
- Even Parity:讓
1的總數為偶數。 - Odd Parity:讓
1的總數為奇數。
優點
- 實作非常簡單。
- 硬體支援常見。
- 適合偵測單一 bit 錯誤。
限制
- 無法偵測所有錯誤。
- 若有偶數個 bit 同時錯誤,可能無法發現。
- 不適合用於高可靠度資料保護。
5. Checksum:檢查和
Checksum 是一種把資料內容加總後得到檢查值的方法。
最簡單的做法是將所有 byte 相加,只保留低 8-bit 或 16-bit 作為 checksum。
例如:
data = [0x12, 0x34, 0x56]
sum = 0x12 + 0x34 + 0x56 = 0x9C
checksum = 0x9C
有些協定會使用補數形式,例如:
checksum = 0x100 - (sum & 0xFF)
這樣接收端把所有資料加上 checksum 後,低 8-bit 應該會等於 0。
優點
- 實作簡單。
- 計算速度快。
- 適合非常低成本的資料檢查。
限制
- 偵錯能力較弱。
- 不同錯誤可能抵消,導致 checksum 一樣。
- 不適合複雜通訊或高可靠度資料檢查。
- 不能防止惡意竄改。
6. LRC:縱向冗餘檢查
LRC (Longitudinal Redundancy Check) 是一種常見的簡單錯誤檢查方式,常見於部分序列通訊與文字型協定。
LRC 通常會對一段資料做 XOR 或加總補數計算。
例如 XOR 型 LRC:
data = [0x12, 0x34, 0x56]
LRC = 0x12 XOR 0x34 XOR 0x56
優點
- 比單純加總稍微好一些。
- 實作簡單。
- 適合簡單通訊協定。
限制
- 偵錯能力仍有限。
- 不如 CRC 適合偵測 burst error。
- 不具備安全性。
7. CRC:循環冗餘檢查
CRC (Cyclic Redundancy Check) 是通訊協定與儲存系統中非常常見的錯誤偵測方法。
CRC 不是單純加總,而是把資料視為二進位多項式,透過指定的生成多項式進行除法運算,最後得到餘數作為檢查值。
常見 CRC 類型:
- CRC-8
- CRC-16
- CRC-32
7.1 CRC 的特點
CRC 很適合偵測:
- 單一 bit 錯誤
- 多 bit 錯誤
- 連續 burst error
- 常見通訊雜訊造成的資料損壞
因此 CRC 常用於:
- 通訊封包
- Flash / EEPROM 資料區塊
- 檔案格式
- 壓縮格式
- 網路封包
- Firmware image 檢查
7.2 CRC 常見參數
討論 CRC 時,不能只說「CRC16」或「CRC8」,因為不同協定可能使用不同參數。
常見參數包含:
| 參數 | 說明 |
|---|---|
| Width | CRC 長度,例如 8、16、32 bit |
| Polynomial | 生成多項式 |
| Initial Value | 初始值 |
| RefIn | 輸入資料是否 bit reflection |
| RefOut | 輸出結果是否 bit reflection |
| XorOut | 最後輸出前是否再 XOR 指定值 |
| Input Range | CRC 計算涵蓋哪些欄位 |
| Byte Order | 結果放入封包時使用 little-endian 或 big-endian |
所以工程實作時,應該明確寫出完整規格,例如:
CRC-16/CCITT-FALSE
Width = 16
Poly = 0x1021
Init = 0xFFFF
RefIn = false
RefOut = false
XorOut = 0x0000
只寫「用 CRC16」通常是不夠的。
7.3 CRC8
CRC8 產生 8-bit 檢查值,常見於短封包或低成本通訊協定。
常見應用:
- SMBus PEC
- 一些感測器封包
- 短距離低速通訊
優點是檢查值短、成本低;缺點是保護能力有限,不適合很長的資料區塊。
7.4 CRC16
CRC16 產生 16-bit 檢查值,常見於工業通訊、Modbus、儲存資料區塊等場景。
常見變體包含:
- CRC-16/IBM
- CRC-16/MODBUS
- CRC-16/CCITT-FALSE
- CRC-16/XMODEM
這些雖然都叫 CRC16,但 polynomial、initial value、reflection 等參數可能不同,結果也會不同。
7.5 CRC32
CRC32 產生 32-bit 檢查值,常見於檔案、壓縮格式、網路與大量資料檢查。
常見應用:
- Ethernet FCS
- ZIP
- PNG
- gzip
- 檔案完整性校驗
CRC32 對一般意外錯誤的偵測能力比 CRC8 / CRC16 更強,但仍然不是安全驗證機制。
8. PEC:封包錯誤碼
PEC (Packet Error Code) 是 SMBus / PMBus 中常見的封包錯誤檢查碼。
PEC 通常是基於 CRC-8 計算,常見 polynomial 為:
x^8 + x^2 + x + 1
也常表示為:
0x07
PEC 的用途
PEC 用來檢查 SMBus / PMBus 封包在傳輸過程中是否發生錯誤。
常見涵蓋內容包含:
- Slave address
- Read / Write bit
- Command code
- Data bytes
- Repeated start 後的 address
- 其他協定定義的欄位
實際計算範圍必須依照協定規格確認。
PEC 與 CRC8 的關係
可以簡單理解為:
PEC 是特定協定中使用的一種 CRC8 檢查碼。
但寫程式時不能只知道「CRC8」,還要確認:
- polynomial
- initial value
- 是否 reflection
- 計算涵蓋哪些 byte
- PEC byte 是否包含在計算內
- address 與 R/W bit 是否包含在計算內
9. MD5 與 SHA:雜湊函數
雜湊函數 (Hash Function) 會把任意長度的輸入資料轉成固定長度的輸出值。
例如:
- MD5:輸出 128-bit
- SHA-1:輸出 160-bit
- SHA-256:輸出 256-bit
9.1 MD5
MD5 曾經是常見的密碼學雜湊函數,但現在已經不安全,因為已知存在碰撞攻擊。
不建議用 MD5 做:
- 密碼儲存
- 數位簽章
- 安全驗證
- 憑證或韌體可信驗證
MD5 仍可能出現在舊系統或非安全用途,例如快速 fingerprint,但新系統通常應避免使用。
9.2 SHA-256
SHA-256 是目前常見且仍被廣泛使用的密碼學雜湊函數。
常見用途:
- 檔案完整性驗證
- 數位簽章前的訊息摘要
- 區塊鏈
- 安全協定中的資料摘要
但要注意:
只有 SHA-256 本身,不能證明資料來源可信。
如果攻擊者可以同時替換檔案與 SHA-256 值,那使用者仍可能被騙。因此,若要驗證來源,需要搭配 HMAC、數位簽章或可信下載通道。
10. HMAC:金鑰式訊息驗證碼
HMAC (Hash-based Message Authentication Code) 是將雜湊函數與共享祕密金鑰結合,用來驗證訊息完整性與來源。
常見形式:
HMAC-SHA256(secret_key, message)
只有知道 secret_key 的雙方,才能產生正確的 HMAC。
適合用途
- API request 驗證
- webhook 驗證
- 裝置與伺服器之間的訊息驗證
- 防止封包被不知道金鑰的人偽造
與 CRC 的差異
CRC 不需要金鑰,所以任何人都能重新計算。
HMAC 需要祕密金鑰,因此攻擊者即使知道資料內容與演算法,也無法在不知道金鑰的情況下偽造合法 HMAC。
11. 數位簽章:驗證來源與不可否認性
數位簽章 (Digital Signature) 使用非對稱密碼學。
基本概念是:
- 使用私鑰簽署。
- 使用公鑰驗證。
常見用途:
- 韌體更新檔驗證
- 軟體安裝檔驗證
- TLS 憑證驗證
- 文件簽署
- 安全開機 (Secure Boot)
與 HMAC 的差異
| 項目 | HMAC | 數位簽章 |
|---|---|---|
| 金鑰類型 | 雙方共享同一把 secret key | 私鑰簽署,公鑰驗證 |
| 驗證者能不能產生合法結果 | 可以,因為驗證者也知道 secret key | 不行,驗證者只有公鑰 |
| 適合場景 | 雙方都可信的系統內部驗證 | 對外發布、韌體更新、不可否認性 |
| 不可否認性 | 較弱 | 較強 |
12. 韌體與嵌入式系統常見使用場景
12.1 通訊封包錯誤檢查
常見選擇:
- UART 簡單協定:Checksum、LRC、CRC8、CRC16
- I2C / SMBus:PEC
- RS-485 / Modbus:CRC16
- CAN:協定本身有 CRC 欄位
目標是偵測通訊過程中的意外錯誤。
12.2 儲存資料完整性檢查
例如設定值存在 Flash 或 EEPROM:
struct config {
uint32_t magic;
uint16_t version;
uint8_t payload[128];
uint32_t crc32;
}
開機時重新計算 CRC32,確認設定資料是否損壞。
12.3 韌體更新檔檢查
韌體更新通常會有兩層概念:
- CRC / Hash:確認檔案下載或傳輸過程沒有損壞。
- 數位簽章:確認韌體確實來自可信來源,沒有被惡意替換。
只做 CRC 不夠安全,因為攻擊者可以改韌體後重新計算 CRC。
12.4 Bootloader 驗證
常見設計:
Bootloader
↓
讀取 firmware image
↓
檢查長度、版本、CRC
↓
驗證數位簽章
↓
驗證通過後才跳轉執行
CRC 可用於快速檢查資料是否壞掉,但是否允許執行,應以簽章驗證結果為準。
13. 選用建議
| 使用情境 | 建議方法 |
|---|---|
| UART 短封包簡單檢查 | Checksum、LRC、CRC8 |
| Modbus RTU | CRC16 |
| SMBus / PMBus | PEC |
| 大型檔案非安全性完整性檢查 | CRC32、SHA-256 |
| 韌體傳輸過程檢查 | CRC32 或 SHA-256 |
| 韌體是否可信 | 數位簽章 |
| API / Webhook 驗證 | HMAC-SHA256 |
| 密碼儲存 | Argon2、bcrypt、scrypt、PBKDF2 |
| 防止惡意竄改 | HMAC 或數位簽章 |
| 只防通訊雜訊 | CRC 通常比 checksum 更適合 |
14. 常見誤解
誤解 1:CRC 可以防止資料被惡意修改
不行。
CRC 沒有祕密金鑰,攻擊者知道演算法後,可以修改資料並重新計算 CRC。
誤解 2:MD5 可以用來做安全驗證
不建議。
MD5 已有碰撞攻擊,不適合密碼學安全用途。
誤解 3:有 SHA-256 就一定安全
不一定。
SHA-256 可以檢查資料是否變動,但如果攻擊者能同時替換資料與 hash 值,就無法保證來源可信。
若要確認來源,應使用 HMAC 或數位簽章。
誤解 4:CRC16 都是一樣的
不是。
CRC16 有很多變體,必須確認 polynomial、initial value、reflection 與計算範圍。
誤解 5:Checksum、CRC、Hash 都是加密
不是。
它們都不是「加密」。加密的重點是讓資料變成未授權者無法讀取的密文,並且可由合法持有人解密。
Checksum、CRC、Hash 的重點是產生檢查值,不是隱藏資料內容。
15. 總結
可以用一句話區分:
Checksum / CRC / PEC 用來抓資料有沒有壞掉;HMAC / 數位簽章用來防資料被惡意偽造或竄改。
簡單判斷方式:
- 只怕通訊雜訊或儲存損壞:使用 CRC、Checksum、PEC。
- 需要確認資料來源可信:使用 HMAC 或數位簽章。
- 需要隱藏資料內容:使用加密。
- 需要儲存密碼:使用 password hashing / KDF,不要直接用 SHA-256 或 MD5。
相關概念延伸閱讀
- 嵌入式系統通訊基礎概念:了解 UART、I2C、SPI、CAN、RS-485 等通訊協定。
- 網路通訊基礎概念與協定原理:了解 TCP/IP、HTTP/HTTPS 與網路分層。