🌸

ZigでCamelliaブロック暗号を実装する

2024/12/24に公開

はじめに

最近、ZigCamelliaブロック暗号を実装しました。

https://github.com/sorairolake/camellia-zig

3年近く前にCamelliaのRust実装のcamelliaクレートを書きましたが、最近はZigも少し書くようになったのでZigでも実装してみたいと思って実装しました。

この記事では、上記の実装を参照しながらCamelliaを実装する方法について述べたいと思います。

Camelliaについて

Camelliaは、2000年にNTTと三菱電機によって開発されたブロック暗号です。
CamelliaのインターフェースはAES互換で、ブロック長は128ビット、鍵長は128ビット、192ビット、256ビットです。
CamelliaはAESと同等の安全性と処理性能を有しており、AESと同じくCRYPTRECの電子政府推奨暗号リストに載っています。

CamelliaのアルゴリズムはRFC 3713で説明されています。
RFCの日本語訳もあります。

実装

RFCで仕様が説明されているので、そのとおりに実装すれば暗号化と復号ができます。

Camelliaでは鍵スケジュール部で与えられた鍵から副鍵を生成して、生成した副鍵を使って暗号化と復号を行います。

定数

Camelliaではマスク用の定数値MASK8MASK32MASK64MASK128が定義されています。
今回の実装ではMASK8MASK128は使用しないので実装内で定義していません。

https://github.com/sorairolake/camellia-zig/blob/v0.1.0/src/camellia.zig#L16-L20

鍵スケジュール

鍵スケジュール部では与えられた鍵Kから暗号化と復号に使用する副鍵を生成します。

鍵スケジュール部では128ビット変数のKLKRKAKBを使用します。

KLKRは鍵Kから生成します。
Camellia-128の場合は128ビット鍵KKLとして使用し、KRには0を設定します。

https://github.com/sorairolake/camellia-zig/blob/v0.1.0/src/camellia.zig#L187-L189

RFCにはバイトオーダーについて書かれていませんが、アルゴリズム仕様書にバイトオーダーがビッグエンディアンあると書かれているのでKをビッグエンディアンで読み取ります。

Camellia-192の場合は192ビット鍵Kの上位128ビットをKLとして使用し、Kの下位64ビットを64桁左シフトした値とKの下位64ビットをビット反転した値の論理和をKRとして使用します。

https://github.com/sorairolake/camellia-zig/blob/v0.1.0/src/camellia.zig#L194-L197

Camellia-256の場合は256ビット鍵Kの上位128ビットをKLとして使用し、Kの下位128ビットをKRとして使用します。

https://github.com/sorairolake/camellia-zig/blob/v0.1.0/src/camellia.zig#L203-L205

KAKBKLKRから生成します。

https://github.com/sorairolake/camellia-zig/blob/v0.1.0/src/camellia.zig#L91-L109

KAKBの生成時に使用する64ビット定数Sigma1からSigma6は以下のように定義されています。

https://github.com/sorairolake/camellia-zig/blob/v0.1.0/src/consts.zig#L7-L23

副鍵はこれらの変数を左循環シフトした結果の上位64ビットか下位64ビットを使用して生成します。

Camellia-128の場合はKLKAを使用して64ビットの副鍵kw(ホワイトニングで使用)4個、k(ラウンド鍵)18個、ke(6ラウンドごとに使用)4個の計26個生成します。

https://github.com/sorairolake/camellia-zig/blob/v0.1.0/src/camellia.zig#L111-L143

Camellia-192、Camellia-256の場合はKLKRKAKBを使用して64ビットの副鍵kw(ホワイトニングで使用)4個、k(ラウンド鍵)24個、ke(6ラウンドごとに使用)6個の計34個生成します。

https://github.com/sorairolake/camellia-zig/blob/v0.1.0/src/camellia.zig#L145-L185

Zigで左循環シフトはstd.math.rotlでできます。

暗号化

暗号化では最初に128ビットの平文Mを上位64ビットのD1と下位64ビットのD2に分割します。

https://github.com/sorairolake/camellia-zig/blob/v0.1.0/src/camellia.zig#L239-L240

18ラウンド(Camellia-128)または24ラウンド(Camellia-192、Camellia-256)のFeistel構造を使って行います。
6ラウンドごとにFL関数とFLINV関数を挿入します。

128ビットの暗号文CD2を64桁左シフトした値とD1の論理和です。

https://github.com/sorairolake/camellia-zig/blob/v0.1.0/src/camellia.zig#L233-L260

復号

復号は副鍵の順序を反転させて暗号化と同じ処理をすることで行えます。

https://github.com/sorairolake/camellia-zig/blob/v0.1.0/src/camellia.zig#L284-L311

コンポーネント

F関数

ラウンド関数Fはhttps://datatracker.ietf.org/doc/html/rfc3713#section-2.4.1で説明されています。
今回の実装ではRFCのものよりも高速に処理できることからBotanのF関数を利用しています。

https://github.com/sorairolake/camellia-zig/blob/v0.1.0/src/camellia.zig#L22-L53

Camelliaでは4つのS-boxを使用します。
SBOX1は以下のように定義されています。

https://github.com/sorairolake/camellia-zig/blob/v0.1.0/src/consts.zig#L25-L43

SBOX2からSBOX4は以下の式で求めることができます。

sbox_2[x] = math.rotl(u8, sbox_1[x], 1);
sbox_3[x] = math.rotl(u8, sbox_1[x], 7);
sbox_4[x] = sbox_1[math.rotl(u8, x, 1)];

今回の実装ではSBOX2からSBOX4は事前に計算したものを使用しています。

FL関数とFLINV関数

FL関数とFLINV関数はhttps://datatracker.ietf.org/doc/html/rfc3713#section-2.4.2で説明されています。

https://github.com/sorairolake/camellia-zig/blob/v0.1.0/src/camellia.zig#L55-L79

テスト

https://datatracker.ietf.org/doc/html/rfc3713#appendix-Aにテストベクターがあるのでこれで正しく実装できたか検証できます。

https://github.com/sorairolake/camellia-zig/blob/v0.1.0/src/camellia.zig#L517-L548

また、https://info.isl.ntt.co.jp/crypt/eng/camellia/dl/cryptrec/t_camellia.txtにより大きなテストベクターがあります。
今回の実装ではこのテストベクターをYAMLにして、それをZigの標準ライブラリで読み取れるJSONに変換してテストしています。

https://github.com/sorairolake/camellia-zig/blob/v0.1.0/src/tests/camellia_128.zig#L5-L46

終わりに

Zig以外のプログラミング言語でも上記のようにRFCに従ったコードを書くことでCamelliaを実装できます。
Zigは128ビット整数型がプリミティブ型なので他のプログラミング言語よりも簡単に実装できるように思いました。

GitHubで編集を提案

Discussion