CRC-32

  • 10,856 views
Uploaded on

CRC-32の説明です。

CRC-32の説明です。

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
10,856
On Slideshare
0
From Embeds
0
Number of Embeds
4

Actions

Shares
Downloads
0
Comments
0
Likes
4

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. CRC-32 七誌
  • 2. 経緯 1. ZIPの実装でCRC-32が必要になった 2. そもそもCRCって何だろう? 3. Wikipediaで何やら説明されている 4. 読んだけど意味がよくわからない • CRC-32の規格が色々あるけどどれ? • 0x04c11db7とかの定数は何? • ZIP仕様書の0xdebb20e3との関係は? 5. というわけで調べてみた
  • 3. 結論から言うと • ZIPで使われているCRC-32はIEEE 802.3 • CRCの計算は特殊な割り算の余り • マジックナンバーは検算用の値
  • 4. 目次 1. 計算編 ― Wikipediaの解説を追試 2. 理論編 ― 計算方法の根拠 3. 手順編 ― CRC-32の計算手順 4. 実装編 ― 実装に必要なテクニックなど 5. 番外編 ― マジックナンバー
  • 5. 1. 計算編 Wikipediaの解説を追試
  • 6. 手探り • どこから手を付ければ良いんだろう? • とりあえずWikipediaに書いてある計算をやっ てみよう
  • 7. CRCの計算 1. 先頭が一致ならXOR 2. 右シフト 左端まで1~2を繰り返す 11010011101100 1011
  • 8. CRCの計算 1. 先頭が一致ならXOR 2. 右シフト 左端まで1~2を繰り返す 11010011101100 1011 0110
  • 9. CRCの計算 1. 先頭が一致ならXOR 2. 右シフト 左端まで1~2を繰り返す 01100011101100 1011
  • 10. CRCの計算 1. 先頭が一致ならXOR 2. 右シフト 左端まで1~2を繰り返す 01100011101100 1011 0111
  • 11. CRCの計算 1. 先頭が一致ならXOR 2. 右シフト 左端まで1~2を繰り返す 00111011101100 1011
  • 12. CRCの計算 1. 先頭が一致ならXOR 2. 右シフト 左端まで1~2を繰り返す 00111011101100 1011 0101
  • 13. CRCの計算 1. 先頭が一致ならXOR 2. 右シフト 左端まで1~2を繰り返す 00010111101100 1011
  • 14. CRCの計算 1. 先頭が一致ならXOR 2. 右シフト 左端まで1~2を繰り返す 00010111101100 1011 0000
  • 15. CRCの計算 1. 先頭が一致ならXOR 2. 右シフト 左端まで1~2を繰り返す 00000001101100 1011
  • 16. CRCの計算 1. 先頭が一致ならXOR 2. 右シフト 左端まで1~2を繰り返す 00000001101100 1011 0110
  • 17. CRCの計算 1. 先頭が一致ならXOR 2. 右シフト 左端まで1~2を繰り返す 00000000110100 1011
  • 18. CRCの計算 1. 先頭が一致ならXOR 2. 右シフト 左端まで1~2を繰り返す 00000000110100 1011 0110
  • 19. CRCの計算 1. 先頭が一致ならXOR 2. 右シフト 左端まで1~2を繰り返す 00000000011000 1011
  • 20. CRCの計算 1. 先頭が一致ならXOR 2. 右シフト 左端まで1~2を繰り返す 00000000011000 1011 0111
  • 21. CRCの計算 1. 先頭が一致ならXOR 2. 右シフト 左端まで1~2を繰り返す 00000000001110 1011
  • 22. CRCの計算 1. 先頭が一致ならXOR 2. 右シフト 左端まで1~2を繰り返す 00000000001110 1011 0101
  • 23. CRCの計算 1. 先頭が一致ならXOR 2. 右シフト 左端まで1~2を繰り返す 求める値 00000000000101 1011
  • 24. まとめ 0x34ec CRC-3 上位1bitを付加 • 0x34ecに対するCRC-3の値 • Wikipediaの一覧表にCRC-3はない • 計算例として仮に出しただけ?
  • 25. 2. 理論編 CRCの計算方法は どのような根拠に基づくのか
  • 26. modulo 2 • 計算例で求めた値はmodulo 2の剰余 – 0x34ec mod2 0xb = 5 • modulo 2とは、繰り上がり・繰り下がりを無視 して各桁を独立に計算する方法 • 足し算・引き算・XORが同一の値 – 0+0=0-0=0^0=0 0+1=0-1=0^1=1 – 1+0=1-0=1^0=1 1+1=1-1=1^1=0 – 実装では加算・減算の代用にXORを使う • つまりCRCとは特殊な割り算の余り
  • 27. 割り算(普通) • 10進数の割り算は掛け算が必要 • 2進数は0と1しかないため掛け算が不要 28 11100 35 987 100011 1111011011 除数まま→ 100011 掛け算→ 70 引き算→ 110101 引き算→ 287 除数まま→ 100011 掛け算→ 280 引き算→ 100100 除数まま→ 100011 引き算→ 7 引き算→ 111
  • 28. 割り算(modulo 2) • 引き算の代わりに排他的論理和(XOR) 11100 11111 100011 1111011011 100011 1111011011 100011 100011 引き算→ 110101 XOR→ 111101 100011 100011 引き算→ 100100 XOR→ 111100 100011 100011 引き算→ 111 XOR→ 111111 100011 XOR→ 111001 100011 XOR→ 11010
  • 29. なぜmodulo 2? • CRCはデータチェックが目的 – 割り算をすること自体に意味はない • データの破損が検出できれば何でも良い – 通常の算数と違っても問題はない • 計算が簡単なmodulo 2を採用 – 引き算は繰り下がりで他の桁に影響 – XORは他の桁に影響を及ぼさない
  • 30. CRC-32 • 除数0x104c11db7によるmodulo 2剰余 • バイト配列を巨大数に見立てて割る • 前処理・後処理で検出力向上 – 前処理(被除数)→計算→後処理(剰余) – modulo 2除算自体は素直に計算 • サンプル実装は最適化されている – 原理と実装のつながりが分かりにくい – 最適化を考慮して仕様が決められた?
  • 31. 3. 手順編 CRC-32の計算手順
  • 32. 被除数の作成 (1) • バイトごとにビット順序を反転 – “a” → 01100001 → 10000110 → 0x86 – “b” → 01100010 → 01000110 → 0x46 – “ab” → 0x86 0x46 – “abcd” → 0x86 0x46 0xc6 0x26 • 計算のハードウェア処理を考慮? – シリアルポートのプロトコルとも関係? – Wikipediaにそのようなことが書いてある
  • 33. 被除数の作成 (2) • 4バイトの0を後置 – “a” → 0x86 0x00 0x00 0x00 0x00 • ビッグエンディアンとして数値化 – “a” → 0x8600000000 – “abcd” → 0x8646c62600000000 • 被除数を除数より大きくするための処置 – 被除数<除数 のとき 被除数=剰余 – 被除数がそのまま剰余になるのを回避
  • 34. 前処理(被除数) • そのままでは直前の0が無視される – “a” → 0x8600000000 – “¥0¥0a” → 0x00008600000000 – 0の個数の誤りが検出不可能 • 対策として上位4バイトのビット値を反転 – “a” → 0x8600000000 → 0x79ffffff00 – “¥0¥0a” → 0x00008600000000 → 0xffff79ff000000 • ff×4に続く00の個数は検知不能 – レアケースのため無視?
  • 35. 除数(1) • Wikipediaにある多項式は除数 • x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+ x4+x2+x+1 • x = 2 → 232+226+223+222+・・・+22+21+20 – 2進数でビットが立つ位置を表す式 • 100000100110000010001110110110111 • 0x104c11db7 (=CRC-32の除数) • CRCの種類ごとに除数は異なる
  • 36. 除数(2) • Wikipediaで多項式の横にある3つの値は、 計算用に除数から派生した値 1. 除数から最上位ビットを落とした値 – 0x104c11db7 → 0x04c11db7 2. 1のビット順序を反転した値 – 0x04c11db7 → 0xedb88320 3. 除数を1ビット右シフトした値 – 0x104c11db7 >> 1 → 0x82608edb • 1と2は実装編で説明、3は用途不明
  • 37. modulo 2剰余 • 除数0x104c11db7でmodulo 2剰余 – “a” → 0x79ffffff00 mod2 0x104c11db7 → 0x3d8212e8 – “abcd” → 0x79b939d900000000 mod2 0x104c11db7 → 0x774cbe48 • 計算方法のカスタマイズはない – 実装上の都合により計算方法は変形して実装さ れるが(後述)、計算結果は同一
  • 38. 後処理(剰余) • 剰余のビット順序を反転 – “a” → 0x3d8212e8 → 0x174841bc – “abcd” → 0x774cbe48 → 0x127d32ee – 入力をバイトごとに反転した反動? • ビット値を反転してCRC-32の値とする – “a” → 0x174841bc → 0xe8b7be43 – “abcd” → 0x127d32ee → 0xed82cd11 – 前処理で上位4バイトを反転した反動? • 反転によりマジックナンバー出現(後述)
  • 39. 実装例(F#) • ビット順序を反転する関数を定義 let rev (v:uint32) = let mutable ret = 0u for i = 0 to 31 do if v &&& (1u <<< i) <> 0u then ret <- ret ||| (1u <<< (31 - i)) ret printfn "%x" (rev 1u) // 80000000 • 破壊的代入に突っ込まないでください・・・
  • 40. 実装例(F#) • データが1バイトの場合 let crc32_1 (b:byte) = let b' = uint32 b let mutable 被除数 = uint64(~~~(rev b')) <<< 8 let 除数 = 0x104c11db7UL let 最上位 = 0x100000000UL for i = 7 downto 0 do if 被除数 &&& (最上位 <<< i) <> 0UL then 被除数 <- 被除数 ^^^ (除数 <<< i) rev(~~~(uint32 被除数)) printf "%x" (crc32_1(byte 'a')) // e8b7be43
  • 41. 実装例(F#) • データが4バイトの場合 let crc32_4 (buf:byte[]) = let buf' = BitConverter.ToUInt32(buf, 0) let mutable 被除数 = uint64(~~~(rev buf')) <<< 32 let 除数 = 0x104c11db7UL let 最上位 = 0x100000000UL for i = 31 downto 0 do if 被除数 &&& (最上位 <<< i) <> 0UL then 被除数 <- 被除数 ^^^ (除数 <<< i) rev(~~~(uint32 被除数)) let bytes = Encoding.ASCII.GetBytes "abcd" printf "%x" (crc32_4 bytes) // ed82cd11
  • 42. 検算(Python) • 既存実装としてPythonと比較 >>> from struct import pack >>> from binascii import crc32, hexlify >>> hexlify(pack(">i", crc32("a"))) 'e8b7be43' >>> hexlify(pack(">i", crc32("abcd"))) 'ed82cd11' • F#の独自実装で求めた値と一致! – “a” → 0xe8b7be43 – “abcd” → 0xed82cd11
  • 43. まとめ 1. 入力 • “abcd” → 0x61 0x62 0x63 0x64 2. バイトごとにビット順序を反転 • 0x86 0x46 0xc6 0x26 3. 4バイトの0を後置してビッグエンディアンとして数値化 • 0x8646c62600000000 4. 上位4バイトのビット値を反転 (ここまで前処理) • 0x79b939d900000000 5. 除数0x104c11db7でmodulo 2の剰余を計算 • 0x774cbe48 6. 剰余のビット順序を反転 (ここから後処理) • 0x127d32ee 7. ビット値を反転してCRC-32の値とする • 0xed82cd11
  • 44. 4. 実装編 実装に必要なテクニックなど
  • 45. 巨大数 • 巨大なデータをそのまま数値化して被除数と して扱うのは困難 – 手順編の例で、“abcd”のように被除数が64bitに 収まるようにしたのはそのため • 除数は33bitだが、32bitで扱えないか? – たった1bitはみ出しただけなのに・・・ • 実装に際して何らかの工夫が必要 – 既存の実装は色々あるのに、工夫の意味を理解 しないとまともに読めない!
  • 46. シフトの相対性 • 計算編では除数をシフト – 被除数 ←11010011101100 – 除数 ←1011→ • 除数を固定して被除数をシフトしても、計算結 果は変わらない – 被除数 ←11010011101100 – 除数 ←1011 • 計算に関係するのはあくまで相対位置
  • 47. 実装例(F#) let crc32_4 (buf:byte[]) = 除数をシフト let buf' = BitConverter.ToUInt32(buf, 0) let mutable 被除数 = uint64(~~~(rev buf')) <<< 32 let 除数 = 0x104c11db7UL let 最上位 = 0x100000000UL for i = 31 downto 0 do if 被除数 &&& (最上位 <<< i) <> 0UL then 被除数 <- 被除数 ^^^ (除数 <<< i) rev(~~~(uint32 被除数)) let crc32_4 (buf:byte[]) = 被除数をシフト let buf' = BitConverter.ToUInt32(buf, 0) let mutable 被除数 = uint64(~~~(rev buf')) let 除数 = 0x104c11db7UL let 最上位 = 0x100000000UL for i = 0 to 31 do 被除数 <- (if 被除数 &&& 最上位 = 0UL then 被除数 else (被除数 ^^^ 除数)) <<< 1 rev(~~~(uint32 被除数))
  • 48. 最上位ビット • 被除数の最上位ビットはXORで0になる – 被除数 ←11010011101100 – 除数 ←1011 • 被除数の最上位ビットは値だけ見て、シフトで 押し出して捨ててしまっても構わない – どうせ捨てるので、除数とXORする必要はない – XORしないなら、除数から最上位ビットを取り除 いても計算結果に影響しない • CRC-32の除数が1bit減って32bitに収まる
  • 49. 実装例(F#) let crc32_4 (buf:byte[]) = 除数が33bit let buf' = BitConverter.ToUInt32(buf, 0) let mutable 被除数 = uint64(~~~(rev buf')) let 除数 = 0x104c11db7UL let 最上位 = 0x100000000UL for i = 0 to 31 do 被除数 <- (if 被除数 &&& 最上位 = 0UL then 被除数 else (被除数 ^^^ 除数)) <<< 1 rev(~~~(uint32 被除数)) let crc32_4 (buf:byte[]) = 計算を32bit化 let buf' = BitConverter.ToUInt32(buf, 0) let mutable 被除数 = ~~~(rev buf') let 除数 = 0x04c11db7u // Wikipediaに出てきた値(標準) let 最上位 = 0x80000000u for i = 0 to 31 do let 被除数' = 被除数 <<< 1 被除数 <- if 被除数 &&& 最上位 = 0u then 被除数' else 被除数' ^^^ 除数 rev(~~~被除数)
  • 50. ビット順序の反転(1) • 計算を反転させるとどうなるか? • 標準 – 被除数 ←11010011101100 – 除数 ←1011 • 反転 – 被除数 ←00110111001011→ – 除数 ← 1101 • 最終的に得られた結果を反転すれば、同じ値 が得られる
  • 51. ビット順序の反転(2) • 元はコードのあちこちで反転させていた • 計算自体を反転させると、反転が相殺して消 え、コードが単純化になる – データをバイトごとに反転させる必要がなくなる • 除数は反転した値を用意して使う – Wikipediaに出てきた0xedb88320 • これを想定して規格が決められた? – いずれにしても計算は単純な方が良い
  • 52. 実装例(F#) let crc32_4 (buf:byte[]) = 標準 let buf' = BitConverter.ToUInt32(buf, 0) let mutable 被除数 = ~~~(rev buf') let 除数 = 0x04c11db7u // Wikipediaに出てきた値(標準) let 最上位 = 0x80000000u for i = 0 to 31 do let 被除数' = 被除数 <<< 1 被除数 <- if 被除数 &&& 最上位 = 0u then 被除数' else 被除数' ^^^ 除数 rev(~~~被除数) let crc32_4 (buf:byte[]) = 反転 let buf' = BitConverter.ToUInt32(buf, 0) let mutable 被除数 = ~~~buf' let 除数 = 0xedb88320u // Wikipediaに出てきた値(反転) for i = 0 to 31 do let 被除数' = 被除数 >>> 1 被除数 <- if 被除数 &&& 1u = 0u then 被除数' else 被除数' ^^^ 除数 ~~~被除数
  • 53. データ逐次投入(1) 被除数を一気に作成(標準) 被除数を一気に作成(反転) 1. データ 1. データ – “abcde” – “abcde” 2. 16進数 2. 16進数 – 0x61 0x62 0x63 0x64 0x65 – 0x61 0x62 0x63 0x64 0x65 3. ビット順序反転 3. バイト順序反転 – 0x86 0x46 0xc6 0x26 0xa6 – 0x65 0x64 0x63 0x62 0x61 4. 数値化 4. 数値化 – 0x8646c626a600000000 – 0x6564636261 5. 4バイトビット値反転 5. 4バイトビット値反転 – 0x79b939d9a600000000 – 0x659b9c9d9e
  • 54. データ逐次投入(2) 被除数を一気に作成(反転) 被除数を逐次投入で作成 1. データ 1. 初期値(4バイト反転込み) – “abcde” – ffffffff 2. 16進数 2. 下位から1文字ずつXOR – 0x61 0x62 0x63 0x64 0x65 – ffffffff 3. バイト順序反転 ^ 61 – 0x65 0x64 0x63 0x62 0x61 ^ 62 4. 数値化 ^ 63 ^ 64 – 0x6564636261 ^ 65 5. 4バイトビット値反転 3. 結果 – 0x659b9c9d9e – 0x659b9c9d9e
  • 55. データ逐次投入(3) • CRC計算との関係 – 初期値 ffffffff→ – データ 61→ 62→ 63→ 64→ 66→ – 除数 edb88320 • これらをXORで重ねて計算を進める
  • 56. データ逐次投入(4) • データを逐次投入しながら計 初期値 ffffffff→ データ 算を進めても、同一の結果が 61→ 除数 edb88320 得られる ffffff→ • 交換法則による計算順序の 62→ 入れ替え edb88320 ffff→ • XORは交換法則が成り立つ 63→ • 逐次投入により、任意の長さ edb88320 のデータ処理が可能 ff→ 64→ edb88320 65→ edb88320
  • 57. 実装例(F#) let crc32_4 (buf:byte[]) = 一気に用意 let buf' = BitConverter.ToUInt32(buf, 0) let mutable 被除数 = ~~~buf' let 除数 = 0xedb88320u for i = 0 to 31 do let 被除数' = 被除数 >>> 1 被除数 <- if 被除数 &&& 1u = 0u then 被除数' else 被除数' ^^^ 除数 ~~~被除数 let crc32 (buf:byte[]) = 逐次投入により任意長対応 let mutable 被除数 = ~~~0u let 除数 = 0xedb88320u for b in buf do 被除数 <- 被除数 ^^^ uint32(b) for j = 0 to 7 do let 被除数' = 被除数 >>> 1 被除数 <- if 被除数 &&& 1u = 0u then 被除数' else 被除数' ^^^ 除数 ~~~被除数
  • 58. テーブル • ビット単位で計算すると無駄が多い • テーブルを用いてバイト単位で計算 – 1バイト(0~255)のすべての計算パターンを事 前にキャッシュしておく • XORの交換法則により、あらかじめ計算した 値を後で重ねても、同一の結果となる • CRCのサンプル実装として出回っているコー ドの大半はテーブルを用いている
  • 59. 実装例(F#) let crc32_table = [| for i in 0..255 -> let mutable reg = uint32 i for j = 0 to 7 do let reg' = reg >>> 1 reg <- if reg &&& 1u = 0u then reg' else reg' ^^^ 0xedb88320u reg |] let crc32 (buf:byte[]) = let mutable reg = ~~~0u for b in buf do reg <- reg ^^^ (uint32 b) let t = crc32_table.[int(reg) &&& 0xff] reg <- (reg >>> 8) ^^^ t ~~~reg • 大抵のサンプル実装は似たようなコード
  • 60. 5. 番外編 CRCの特性によるマジックナンバー
  • 61. マジックナンバー(1) 1. データのCRCを計算 2. データの後に1で計算したCRCを追加 3. 2のCRCを計算 – データの内容に関係なく常に同じ値になる – この性質を検算に利用することがある データ CRC CRC 常に同じ値=マジックナンバー
  • 62. マジックナンバー(2) • CRC-32で確認、見事に一致! – “a” → 0xe8b7be43 → 0x2144df1c – “abcd” → 0xed82cd11 → 0x2144df1c – XORによる相殺の結果 • CRCは最後にビット値を反転するが、反転す る前の0xdebb20e3がZIP仕様書に記載され ているマジックナンバー – ~0xdebb20e3 → 0x2144df1c • 後処理で反転する目的がこれ?
  • 63. 検算(Python) >>> from struct import pack >>> from binascii import crc32 >>> def magic(data1): ... crc1 = crc32(data1) ... data2 = data1 + pack("<i", crc1) ... return crc32(data2) ... >>> hex(magic("a")) '0x2144df1c' >>> hex(magic("abcd")) '0x2144df1c' >>> hex(magic("abcdefghijklmn")) '0x2144df1c'
  • 64. ご清聴ありがとうございました