巡回冗長検査

巡回冗長検査(じゅんかいじょうちょうけんさ、: Cyclic Redundancy Check, CRC)は、誤り検出符号の一種で、主にデータ転送などに伴う偶発的な誤りの検出によく使われている。送信側は定められた生成多項式で除算した余りを検査データとして付加して送信し、受信側で同じ生成多項式を使用してデータを除算し、その余りを比較照合することによって受信データの誤り・破損を検出する。

デジタル回路で簡単に実装でき、数学的にも分析が容易であり、また、ビットのランダム誤りやバースト誤りを検出できるので、HDLC手順やCSMA/CD方式などにおいて誤りチェック・伝送路ノイズチェックによく使われている。パリティや単純な加算によるチェックサムに比べ検出精度が高く、その点では高級なチェックサムと言える。単純なチェックサムと同じく、データの改竄に対する耐性はない。

W・ウェスレイ・ピーターソンが発明し、1961年に論文として発表した[1]。CRC-32と一般に呼ばれているIEEE 802.3のCRCは1975年に定められ、イーサネットなどの各種通信やZIPPNGなど各所に使われている[2]

概要

CRC は、巡回符号の理論に基づいた誤り検出符号の一種である。その計算は筆算による多項式の除算に似ており、送受信するデータを、あらかじめ決めておいた特定の数で割り、その余りをチェック用の値として使う。ただし、通常の算術による計算ではなく、有限体の計算を行う(繰り上がり・繰り下がりのない算術である点が通常の算術と異なる)。除算に使う特定の数を生成多項式という。余りの長さは常に除数の長さ以下であり、除数の長さによって結果の長さを決定できる。

CRCには多数のバリエーションがあり、主に出力結果のビット幅や生成多項式に違いがある。チェック用の値が n ビットになる CRC は CRC-n と表記される。規格によって生成多項式が異なることが多く、CRC-n XXX というように表記される(主な標準CRC)。

CRCは任意の有限体を使って構築できるが、一般に使われているCRCは位数2のGF(2)を使用している。

CRCがよく使われている重要な理由として、効率が保証されている点が挙げられる。nビットCRCは通常、nビット以下[疑問点]の連続する誤り(バースト誤り)を検出できる。言い換えれば、nビットの範囲内に1ビットの誤りが複数存在する場合を検出できる。また、それより長いバースト誤りも 1-2-n の確率で検出する。データ通信での誤りも記憶装置での誤りも、誤りは無作為に出現するわけではなくバースト性がある。そのため、CRCの性質はそれらによく合っており、単にパリティチェックを複数行うよりも便利である。

最も単純な誤り検出であるパリティビットは、最も単純なCRCと見ることもできる(除数は2ビットの 11 である)。

CRCとセキュリティ

CRC値は、メッセージとの1対1の対応が不可能(メッセージより常に情報量が少ないため)という点は、暗号学的ハッシュ関数によるハッシュ値(以下、単に「ハッシュ値」とする)と同じだが、ハッシュ値はそれでも100ビット程度以上の大きさがあるのが普通であり、また、内容が異なるのに同じハッシュ値となるようなメッセージを偽造したりするのは容易でない(という特性が一般に暗号学的ハッシュ関数には必須である)。それに対し、CRC値は一般に小さく、さらには消失訂正という技法によって同じCRC値になるメッセージを容易に作成可能であり、元のメッセージを少しだけ改変したものでもCRC値を同じにできる。なお、元のメッセージに非常によく似た(ごく低い通信エラーと同程度の差異しか無く、攻撃者の意図で決められたものではなくランダムに変えられたような)メッセージであれば、CRCの設計上、大きく異なるものにはなる。

また、通信システム全体の保安として考えた場合、伝送途中で傍受してメッセージを偽物とすりかえるなら、同時にCRCもすり替えることができると考えなければならないだろう(これについてはハッシュ値についても言える)。従ってこれらは直接には、第三者による意図的な改竄などを防ぐ手段にはならない。さらにCRCは分配法則結合法則が成り立つので、メッセージ認証符号HMACのような「秘密の文字列」を前置したり後置したりしても改竄に対する耐性は全く上がらない。

CRCの計算

nビットの2値CRCの計算方法は単純である。入力ビット列を一行に並べ、CRCの除数を表す (n+1) ビットのパターンをその下に左端を合わせるように書く。以下に3ビットCRCの計算の最初の様子を示す。

11010011101100 <--- 入力1011           <--- 除数 (4 Bits)--------------01100011101100 <--- 結果

除数の左端のビットの上にある入力ビットが0なら、何もせず除数を右に1ビットずらす。除数の左端のビットの上にある入力ビットが1なら、除数と入力ビットをXOR演算する(つまり、除数のビット列のうち 1 になっている部分の上にあたる入力ビットを反転させる)。そして、除数を右に1ビットずらす。これを除数が入力ビット列の右端に到達するまで繰り返す。最終状態は以下の通りである。

00000000001110 <--- それまでの計算結果          1011 <--- 除数--------------00000000000101 <--- 余り (3 bits)

除数の左端ビットは毎回入力ビットを0にしていくので、最終的には除数のビット数(nビット)の範囲にだけ1のビットが残ることになる。これが除算の余りであり、同時にCRC関数の値となる(CRC仕様によっては、さらなる後処理を要求する場合がある)。

CRCの数学

このような除法のような計算方法を数学的に分析することで、よりよい誤り検出が可能な除数を選択する方法がわかる。このとき、ビット列の各桁をある変数 x の多項式の係数と見なす。この係数は有限体 GF(2) の元であり、一般的な意味での数ではない。多項式と見なすことで、ビット列はの元と見なすことができるようになる。環は大まかに言えば、数にある意味で似た元の集合であり、それに対して加算に似た操作と乗算に似た操作を作用させることができる。これらの演算は一般的な算術と同様、交換法則、結合法則、分配法則が成り立つ。環では一般的な解析的手法が使えるため、多項式に見立てることで解析が容易になる。

CRC多項式の設計

CRCアルゴリズムの実装で最も重要なのは、生成多項式の選択である。生成多項式は、衝突が起きる確率を最小にしつつ、誤り検出性能を最大にするように選ぶ必要がある。

生成多項式の長さ(多項式に含まれる項の最大の次数に1を加えたもの)は算出される検査値の長さに直接影響するため、最も重要な性質となる。

よく採用される多項式の長さは次の通り。

  • 9 ビット (CRC-8)
  • 17 ビット (CRC-16)
  • 33 ビット (CRC-32)
  • 65 ビット (CRC-64)

検査値の長さがn ビットになるCRCは「n ビット CRC」と呼ばれる。長さn が与えられたとき、生成多項式が互いに異なる複数のCRCを作ることが可能である。n ビットCRCの生成多項式は最大次数がn であり、したがってn + 1個の項を含む(多項式の長さがn + 1)。剰余の長さはn となる。そのような特定のCRCを指す場合、 CRC-n-XXX という形式の呼び名が用いられる。

CRC多項式の設計には、処理速度だけでなく、保護したいブロック(データ+CRC値)の最大長、希望する誤り保護特性、またCRCを実装するのためのリソースの種類も影響する。よくある誤解として、「最良の」CRC多項式は、既約多項式かあるいは既約多項式に因子1 + xを乗じたものだ、というものがある。因子1 + xは任意の奇数個のビット誤りを検出可能にするために用いられる。実際には、その前に述べた全ての事項を考慮して多項式を選択すべきであり、その結果として既約でない多項式が選ばれることもあり得る。しかしながら、既約でない多項式がつくる剰余環零因子を含むため、一定の割合で誤りの検出漏れが発生する。

生成多項式の特性は、アルゴリズムの定義から、以下のように導き出せる。

  • ゼロでない係数が複数あるCRC多項式は、入力メッセージ中のビット誤りが1つしかない場合、必ず検出できる。
  • 多項式の最長の既約部分の長さがkビットであるCRC多項式は、入力メッセージ中のビット誤りが2つしかなく且つ、それらの間隔が両方のビット誤りを含め2k-1ビットより短い場合、必ず検出できる。
  • 長さkビットのCRC多項式は、入力メッセージ中の最初のビット誤りと最後のビット誤りの間隔が両方のビット誤りを含めkビットより長くない(kビットより短いか、ちょうどkビットの) 場合、言い換えるとkビットより長くないバースト誤りが1つしかない場合、必ず検出できる。
  • x + 1 を因数に持つCRC多項式は、入力メッセージ中に奇数個のビット誤りがある場合、必ず検出できる。

特化したCRC

誤り検出符号としてのCRCの概念を実用システムでの実装に移すとき、実装者がそれを複雑化させることがある。以下では、そのような例を解説する。

  • 検査対象のビットストリームに固定ビットパターンを常に前置する実装。これは同期がずれた際にCRCの対象となる部分を明らかにするための実装である。つまり、ある時点でメッセージが受信されるはずだという場合、同期がずれると先頭に0がずっと並んだメッセージを受信したようになる。固定パターンがメッセージの先頭に必ず存在するなら、同期がずれてもメッセージの範囲がすぐにわかる。または、CRCの計算が除算で行われている都合上、先頭に0が並んだメッセージに弱いという欠点があり、これに対処する効果もある。
  • 検査対象のビットストリームに多項式除算を行う前にnビットの0を常に後置する実装(n はCRCのサイズ)。この場合、CRCをビットストリームに加算する形で送出する。するとnビットの0を後置した部分がCRCに置き換わり、それを含めたビットストリームに対して検査を行うと必ず余りが0になる。この方式では、検査データとして付加された部分も一緒に計算してしまって良く、例えば何ビット受信するかあらかじめわからず、突然通信が終了してはじめて終端がわかるような場合でも、検査を行うことができる。
  • ビットストリームの多項式除算の余りに固定ビットパターンをXORする実装。
  • ビット順序: ある種の方式では各バイトの最下位ビットを先頭とする。すると多項式除算での「左端」は通常の意味での最下位ビットになる。これはシリアルポートでの転送でハードウェアによるCRCチェックを行う場合に良く見られる。というのも、シリアルポートでは最下位ビットを先に転送するものが多いためである。
  • バイト順序: 多バイトCRCでは、バイトの転送順序に混乱が見られる。一部の16ビットCRCではCRCを構成する2バイトを入れ替えている。
  • 除数多項式の最上位ビットの省略: nビットCRCは (n+1) ビットの除数で定義されるもので、最上位ビットは常に1である。すると、nビットのレジスタではオーバフローするため、除数の最上位ビットを省略して示すことがある。

主な標準CRC

巡回冗長検査は唯一の標準規格があるわけではなく、例えば CRC-12 では3種類の多項式が使われている[3]。また、CRC-16 にはよく使われているものが8種類、CRC-32 は3種類存在する[4]。よく使われている多項式が最も効果的とは限らない。1993年から2004年にかけて、Koopman と Castagnoli らは16ビットまでと[5]、24ビットおよび32ビット[6][7]の多項式の総当り的調査を行った。そして、それまで利用されていたものよりも(そのメッセージ長でのハミング距離の観点で)性能のよい多項式を発見し、今後の標準化に役立てられるよう発表した[7]iSCSIはその研究成果を取り入れている。

よく使われるCRC-32多項式は、IEEE勧告のものも V.42、イーサネットFDDIZIPPNG などで使われているものも、ハミング符号の生成多項式を使っている。これは、誤り検出性能がよいためである[2]。ただし、iSCSIで使っている Castagnoli CRC-32C の方がさらに優れている[7]

以下の表は、実際に使われている各種アルゴリズムの多項式である。プロトコルによっては、これに事前の逆転や事後の逆転、ビット順序の反転などを施すことがある。独自プロトコルでのCRCは、目くらましとして初期値を複雑化させたり最後にXORしたりすることがあるが、それによって暗号的に強くなるわけではない。

注: ここでは、最上位ビットを省略している。上の特化したCRCの節参照。

名称多項式 (用途)標準 / 反転 (相反多項式の反転)
CRC-1x + 1 (各種ハードウェア。パリティビット0x1 / 0x1 (0x1)
CRC-4-ITUx4 + x + 1 (ITU G.704, p. 12)0x3 / 0xC (0x9)
CRC-5-ITUx5 + x4 + x2 + 1 (ITU G.704, p. 9)0x15 / 0x15 (0x1A)
CRC-5-USBx5 + x2 + 1USBトークンパケット)0x05 / 0x14 (0x12)
CRC-6-ITUx6 + x + 1 (ITU G.704, p. 3)0x03 / 0x30 (0x21)
CRC-7x7 + x3 + 1 (通信系、MMCSD0x09 / 0x48 (0x44)
CRC-8-ATMx8 + x2 + x + 1 (ATM Header Error Correction)0x07 / 0xE0 (0x83)
CRC-8-CCITTx8 + x7 + x3 + x2 + 11-Wire バス0x8D / 0xB1 (0xC6)
CRC-8-Dallas/Maximx8 + x5 + x4 + 11-Wire バス0x31 / 0x8C (0x98)
CRC-8x8 + x7 + x6 + x4 + x2 + 10xD5 / 0xAB (0xEA [5])
CRC-8-SAE J1850x8 + x4 + x3 + x2 + 10x1D / 0xB8 (0x8E)
CRC-10x10 + x9 + x5 + x4 + x + 10x233 / 0x331 (0x319)
CRC-11x11 + x9 + x8 + x7 + x2 + 1 (FlexRay)0x385 / 0x50E (0x5C2)
CRC-12x12 + x11 + x3 + x2 + x + 1 (通信系、[8][9] )0x80F / 0xF01 (0xC07)
CRC-15-CANx15 + x14 + x10 + x8 + x7 + x4 + x3 + 1 0x4599 / 0x4CD1 (0x62CC)
CRC-16-FletcherCRCではない。フレッチャーのチェックサムAdler-32 A & B CRC で使用
CRC-16-CCITTx16 + x12 + x5 + 1X.25、V.41、CDMABluetoothXMODEMHDLCPPPIrDABACnet; CRC-CCITTとも)0x1021 / 0x8408 (0x8810 [5])
CRC-16-IBMx16 + x15 + x2 + 1SDLCUSB、その他; CRC-16とも)0x8005 / 0xA001 (0xC002)
CRC-24-Radix-64 x24 + x23 + x18 + x17 + x14 + x11 + x10 + x7 + x6 + x5 + x4 + x3 + x + 1 (FlexRay)0x864CFB / 0xDF3261 (0xC3267D)
CRC-30x30 + x29 + x21 + x20 + x15 + x13 + x12 + x11 + x8 + x7 + x6 + x2 + x + 1 (CDMA)0x2030B9C7 / 0x38E74301 (0x30185CE3)
CRC-32-AdlerCRCではない; Adler-32Adler-32参照
CRC-32x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x + 1
(V.42, MPEG-2, zlib, PNG [10])
0x04C11DB7 / 0xEDB88320 (0x82608EDB [7])
CRC-32C (Castagnoli)x32 + x28 + x27 + x26 + x25 + x23 + x22 + x20 + x19 + x18 + x14 + x13 + x11 + x10 + x9 + x8 + x6 + 1 (iSCSI, Btrfs, VHDX)0x1EDC6F41 / 0x82F63B78 (0x8F6E37A0 [7])
CRC-32K (Koopman)x32 + x30 + x29 + x28 + x26 + x20 + x19 + x17 + x16 + x15 + x11 + x10 + x7 + x6 + x4 + x2 + x + 10x741B8CD7 / 0xEB31D82E (0xBA0DC66B [7])
CRC-64-ISOx64 + x4 + x3 + x + 1 (HDLC — ISO 3309)0x000000000000001B / 0xD800000000000000 (0x800000000000000D)
CRC-64-ECMA-182x64 + x62 + x57 + x55 + x54 + x53 + x52 + x47 + x46 + x45 + x40 + x39 + x38 + x37 + x35 + x33 + x32 + x31 + x29 + x27 + x24 + x23 + x22 + x21 + x19 + x17 + x13 + x12 + x10 + x9 + x7 + x4 + x + 1 (ECMA-182 p.63)0x42F0E1EBA9EA3693 / 0xC96C5795D7870F42 (0xA17870F5D4F51B49)

以下は、かつて使われていたが、現在はハッシュ関数などに置換されたもの。

  • CRC-128 (IEEE)
  • CRC-256 (IEEE)

実装例

CRC-32

C言語での実装例。RFC 1952 (gzip) や RFC 2083 (PNG) の末尾にも実装例が載っている。zlib などにも含まれている。

uint32_t crc_table[256];void make_crc_table(void) {    for (uint32_t i = 0; i < 256; i++) {        uint32_t c = i;        for (int j = 0; j < 8; j++) {            c = (c & 1) ? (0xEDB88320 ^ (c >> 1)) : (c >> 1);        }        crc_table[i] = c;    }}uint32_t crc32(uint8_t *buf, size_t len) {    uint32_t c = 0xFFFFFFFF;    for (size_t i = 0; i < len; i++) {        c = crc_table[(c ^ buf[i]) & 0xFF] ^ (c >> 8);    }    return c ^ 0xFFFFFFFF;}int main(void){    make_crc_table();    uint32_t crc = crc32();        return 0;}

生成多項式を反転させない場合の実装例。

uint32_t crc_table[256];/* 事前にこの関数を実行しておくこと */void make_crc_table(void){    for(uint32_t i=0; i<256; i++){        uint32_t c = i << 24;        for( int j=0; j<8; j++){            c = ( c << 1) ^ ( ( c & 0x80000000) ? 0x04C11DB7 : 0);        }        crc_table[i] = c;    }}uint32_t crc32(uint8_t *buf, int len){    uint32_t c = 0xffffffff;    for (int i = 0; i < len; i++)    {        c = (c << 8) ^ crc_table[((c >> 24) ^ buf[i]) & 0xff];    }    return c;}

関連項目

脚注

外部リンク

オンラインツール