循環冗餘校驗(CRC, Cyclic Redundancy Check)是一種簡單快速的雜湊函數,可以藉由比對資料傳輸或是儲存前後的循環冗餘校驗碼,檢測其是否有錯誤發生。常被用來作為伺服器應用為目的來使用的Node.js,會有很大的機會需要使用CRC進行一些計算。可惜的是,Node.js雖然可以藉由內建的「Crypto」模組來使用許多常見的雜湊函數計算資料的校驗和(checksum),但就是不支援CRC。那麼如果要在Node.js上計算CRC,該怎麼做比較好呢?



首先我們需要知道CRC演算法,實際上分了許多不同版本的函數,如8位元的CRC8、16位元的CRC16、32位元的CRC32、64位元的CRC64,CRC的位元數愈高,雜湊出來的數值碰撞機率自然會比較低,但會需要更多的時間去運算。但是CRC也不完全是使用位元數量去做變化,很有可能會遇到同樣的資料,A函式庫算出來的CRC32和B函式庫算出來的CRC32結果不同的情形。舉例來說,請看以下的PHP程式:

<?php
	header('Content-Type: text/plain');
	$data = 'test';
	$crc1 = dechex(crc32($data));
	$crc2 = bin2hex(mhash(MHASH_CRC32, $data));
	$crc3 = bin2hex(mhash(MHASH_CRC32B, $data));
	echo $crc1, ', ', $crc2, ', ', $crc3;
?>

執行結果為:

d87f7e0c, accf8b33, d87f7e0c

不曉得大家有沒有發現?同樣都是CRC32,字串「test」在上面程式中似乎可以算出「d87f7e0c16」和「accf8b3316」這兩種結果。這是由於除了CRC的位元數量之外,還有其他變數在影響著CRC演算法的計算結果。

大體來說,若不包含準備進行CRC計算的資料的話,會影響CRC演算法結果的變數共有五個,分別是「位元數量」、「多項式」、「起始值」、「終止XOR值」和「是否反射」。

藉由調控這五個變數,可以衍生出各種版本的CRC雜湊函數,於是會細分成CRC8-ATM、CRC8-CDMA、CRC16-CCITT、CRC32-IEEE、CRC32-C、CRC64-ECMA、CRC64-ISO等等十分雜亂的CRC函數名稱。

順帶一提,「mhash」函式庫的「CRC32」函數算是比較特別的,並沒有其他常見的CRC函式庫可以算出跟「mhash」函式庫一樣的結果。於是在乎這點的程式設計師多半就會使用比較標準的「CRC32B」去計算CRC校驗和,以求在不同的平台上也能一致。但也是有沒發現到或是不在乎這個問題的程式設計師依舊使用mhash函式庫的「CRC32」函數,導致與其他系統串接時雙方可能都會有一些障礙。

回到Node.js的主題上,若要在Node.js上實作CRC演算法,最好是能將先前提到的五個變數考慮進去,確保演算法去串接其他人實作的CRC函數之相容性。至於實作的方式本篇文章將不會介紹,本篇文章要介紹的是「node-crc」這個模組。

node-crc

「node-crc」是一個使用Node.js 8之後才支援的N-API所開發的模組,使用C語言實作出各種不同版本的CRC函數,並且可用「位元數量」、「多項式」、「起始值」、「終止XOR值」和「是否反射」這五個變數來控制輸出結果。

GitHub:

https://github.com/magiclen/node-crc

npm:

https://www.npmjs.com/package/node-crc

安裝

直接使用npm指令進行安裝:

npm install --save node-crc

用法

初始化

使用「require」函數來引入「node-crc」模組。

const crc = require('node-crc');
計算CRC校驗和

使用模組的「crc」函數可以傳入「位元數量」、「多項式」、「起始值」、「終止XOR值」和「是否反射」這五個變數以及要計算的資料Buffer來計算出該資料的CRC校驗和,結果會以Buffer的型態回傳。

var result = crc.crc(24, false, 0x00864cfb, 0x00000000, 0x00b704ce, 0x00000000, 0x00000000, 0x00000000, Buffer.from('hello', 'utf8')).toString('hex');
// Arguments: the length of bits, reflection, low bits of expression, high bits of expression, low bits of the initial value, high bits of the initial value, low bits of the final xor value, high bits of the final xor value, the source data buffer

模組同時也提供數個常見的CRC函數,可直接被呼叫使用,列表如下:

  • crc8(crc8atm)
  • crc8cdma
  • crc16(crc16ibm)
  • crc16ccitt(crcccitt)
  • crc32(crc32ieee,在「mhash」中的名稱為「CRC32B」)
  • crc32mhash(在「mhash」中的名稱為「CRC32」)
  • crc32c
  • crc64(crc64ecma)
  • crc64iso

用法如下:

var result = crc.crc32(Buffer.from('hello', 'utf8')).toString('hex');
var result2 = crc.crc64(Buffer.from('world', 'utf8')).toString('hex');