Sui Move · DES · Groth16

FeliCa Gate

FeliCaのDES相互認証をMoveだけで実装し、SuiのGroth16ネイティブ検証と並べたPOCです。ウォレット拡張は使いません。ボタンは公開済みパッケージの関数を読み取り専用クエリでそのまま呼びます。

接続中… network devnet package gate

DES · 1 block

64ビットブロックを暗号化する

鍵とブロックを渡すと、チェーン上の des::encrypt_block が16段のFeistelを回して返します。

いま呼んだ Move の実装 — des.move(FIPS の表と16段の Feistel)

        
Feistel · 16 rounds

途中経過を全部見る

L/Rの16段ぶんと48ビットのラウンド鍵。どちらも des::encrypt_block_tracedes::round_keys の戻り値です。

roundLRround key (48-bit)

DES-CBC · PKCS#7

文章をまるごと暗号化して戻す

ブロック長に満たない分はPKCS#7で詰め、各ブロックを直前の暗号文とXORします(CBC)。

FeliCa · Authentication1 / 2

カードとリーダの相互認証を、チェーン上でやり直す

鍵階層の畳み込み、IDmによる個別化、2-key 3DESによるチャレンジ交換、封をした Authentication2レスポンスまで。FeliCaカードユーザーズマニュアル(非公式版) の §7.1・§7.3 をそのままMoveに書き下ろしたものです。

手順向き

R→C はリーダからカードへ、C→R はカードからリーダへ送られる値です。

いま呼んだ Move の実装 — felica_auth.move(§7.1・§7.3 の書き下ろし)

        
Groth16 · BN254

鍵を見せずに「鍵を知っている」を証明する

上の認証は鍵をそのままトランザクションに入れてしまいます。こちらはPoseidonコミットメントの原像を知っていることだけを証明します。

証明している主張そのもの zk/circuits/card_key.circom

        
チェーン側の検証コード — zk_verifier.move

        
検証鍵と証明のバイト列

        
On-chain record

実際に送ったトランザクション

読み取り専用クエリはガスも署名も要りませんが、こちらは本物のトランザクションです。ダイジェストからエクスプローラで追えます。

How it was built

このPOCの作り方

すべてClaude Codeのクラウド環境の中で完結しています(ローカル環境なし・ウォレット拡張なし)。

  1. Moveパッケージdes.move(FIPS 46-3のIP/FP・S-box・鍵スケジュールをそのまま実装)、felica_auth.move(非公式マニュアル §7.1・§7.3 の相互認証)、zk_verifier.movegate.move。Moveのユニットテスト34件が通っています(FIPSの既知答、パリティビットが無視される性質、CBCとECBの差、FeliCa各値のPython参照実装との一致、リプレイ拒否、Groth16の受理/拒否)。
  2. ZK回路circom でPoseidonコミットメントの原像知識を回路化し、snarkjs でGroth16のセットアップと証明を生成。
  3. 直列化:snarkjsのJSONを arkworks の圧縮形式に変換(G1は32バイト、G2は64バイト、フラグはyの符号)。sui::groth16 が受け取れるのはこの形式だけです。
  4. 公開:Sui CLIのバイナリ配布が取得できない環境だったので、sui-move をソースからビルドしてコンパイルし、TypeScript SDKでパッケージを公開しました。
  5. 接続:公開フルノードのJSON-RPCは廃止済みのため、このページはgRPC (gRPC-web) で simulateTransaction を呼んでいます。これが以前の devInspect にあたる読み取り専用の経路です。