講義は FHE の概要 → LWE 暗号の基礎 → TFHE の Programmable Bootstrapping の 3 部構成。
そのあとホワイトボードセッション(テーマ調査・議論・2 グループ相互の発表とフィードバック)。事後課題は、セッションで出たクエスチョンの調査(提出不要)とコードの穴埋め課題。
Advanced Cryptography Program 2026 / Week 5 自習ノート
Week 5(完全準同型暗号 / TFHE)の予習ノートです。事後課題はまだ手元にないので、
対話ドリル 3 本・31 行(mod 64 と mod 32 の整数だけ。python3 と打てば始められる)で
「Δ で置いて s で消して丸める」から「多項式を回して定数項を読む」までを 1 行ずつ自分の手で通し、
そのあと講義スライド 51 枚と同じ順番のカードで言葉を埋めます。
この週の全体像は 1 文で言えます。Bootstrapping は、復号せずに、答えを並べた多項式を暗号文が指す量だけ回して、定数項を読む操作です。 ドリル B でそれを講義スライドの数値そのままで通します。
出発点 — Week 5 の予習
暗号文のまま足し算と掛け算ができると、預けたデータを見せずに計算を任せられます。それが完全準同型暗号(FHE)です。 ところが暗号文にはノイズが入っていて、演算をするたびに増えます。増えすぎると復号が別の平文を返す。 だから「何回演算できるか」は「ノイズがどこまで許されるか」に等しく、その許容量は Δ/2 という 1 つの数で決まります。 ドリル A でこの数を出し、実際に超えさせて壊します。
読み方
ドリル A(LWE とノイズ)→ B(回転で取り出す = Bootstrapping の核)→ C(negacyclic の表と裏)の順に、1 行打って出た値を貼って 1 文読む。準備は python3 と打って >>> が出るだけ(import も不要)。そのあとカードを上から。
前の週とのつながり
必要なのは Week 0 の「余り」と mod だけです。Week 3・4 の楕円曲線や多項式コミットメントは使いません。逆に、ここで出てくる「多項式を xn+1 で割った余りで考える」は Week 3 の NTT と同じ土俵の話で、符号が 1 つ違うだけです。その符号 1 つが、この週の主役になります。
この頁の構成
対話ドリル — 1 行打って、出た値を貼って、1 文読む
python3 を開いて >>> が出たら始められます(import は最後まで不要)。
各行に「この行の意味」、出た値を貼ると、その値を指す解説が開きます。Python を開けないときは「ここで実行」。
ドリル B と C は講義スライドの数値そのままなので、スライドと突き合わせながら進められます。
ドリル A — LWE 暗号とノイズ(p = 4, q = 64, Δ = 16)
Δ で置いて、s で消して、丸めて戻す。そのあと、ノイズを増やして復号を壊す。
13 行。復号が「割り算」ではなく「いちばん近い目盛りへの四捨五入」であることを見てから、暗号文を足し続けてノイズを 9 まで押し上げ、余裕 8 を超えた瞬間に答えが割れるのを実際に見ます。
ドリル B — Programmable Bootstrapping の核(講義スライド 21〜26 の数値そのまま)
ノイズの入った暗号文から、ノイズのない平文を取り出す。やることは「多項式を回して定数項を読む」だけ。
10 行。講義が「5 分とるので各自手を動かして確認」と言った具体例です。s = (1,0,0,1,1,1,0,1)、a = (8,34,4,32,0,31,58,7)、b = 24。リスケーリングして â = (4,17,2,16,0,16,29,4)、b̂ = 12 が出れば、スライド 25 と一致しています。
ドリル C — negacyclic の表と裏(講義スライド 35, 43〜45)
同じ符号反転が、片方では「起きたら困る事故」、もう片方では「NAND を作る仕掛け」になる。
8 行。スライド 35 は「もし Blind Rotation でこれが起きたらなぜ問題か」を問いのまま残しました。スライド 43〜45 は同じ反転をわざと使って NAND を作ります。両方を同じ 1 つの関数で出して、表と裏であることを 12 通り全部で確かめます。
スライド 44 の条件について — 手を動かすと分かること
スライド 44 は成立条件を「3 < n < p−1」と書いています。ただし講義自身の例は p = 8, n = 16 で、3 < 16 < 7 は成り立ちません。実際に効いている条件は Δ と n の関係です。
回転量は r = 3 のとき 3Δ、r = p−1 のとき 2n − Δ。前者が n 未満、後者が n 以上に落ちればよいので、条件は 3Δ < n(Δ = 2n/p なので p > 6 と同じ)と、ノイズの合計について d ≤ Δ です。n, p, d の 45 通りを総当たりして、成立する 26 通りがこの 2 条件と過不足なく一致することを確かめました。講義の例 (p = 8, n = 16, Δ = 4, d ≤ 2) はこれを満たしています。
スライド 2〜11 / FHE の概要
ここからのカード列は講義スライド 51 枚を同じ順番で追いかけます。1 カード = 主張 1 つ。カード末尾の Q. は講義が問いとして置いたもの — 答えを探しながら聞く場所です。
講義は FHE の概要 → LWE 暗号の基礎 → TFHE の Programmable Bootstrapping の 3 部構成。
そのあとホワイトボードセッション(テーマ調査・議論・2 グループ相互の発表とフィードバック)。事後課題は、セッションで出たクエスチョンの調査(提出不要)とコードの穴埋め課題。
暗号方式 = 3 つのアルゴリズムの組 (Gen, Enc, Dec) で、Dec(Enc(m, key_enc), key_dec) = m を満たすもの。
Enc と Dec で同じ鍵なら共通鍵暗号(AES など)。Gen が鍵ペア (pk, sk) を作り、pk で暗号化・sk で復号するなら公開鍵暗号(RSA など)。
準同型暗号 = 暗号文のまま計算できる暗号。Dec(c₁ ⊗ c₂) = m₁ ⊕ m₂。
例: RSA は暗号文同士を掛けて復号すると平文同士の掛け算になる。Paillier は暗号文の乗算が平文の加算になる。サーバーにデータを明かさずに計算を任せられる。
完全準同型暗号 (FHE) = 暗号文のまま、加算と乗算の両方を任意の回数行える準同型暗号。
Q. なぜ暗号文のまま加算と乗算ができたら嬉しいのか?(答えは次のカード)
m₁, m₂ ∈ {0,1} に対して 1 − m₁m₂ を暗号文のまま計算できれば NAND が評価でき、理論上任意の Boolean 回路を暗号文のまま評価できる。
概念の提唱(1978 年、Rivest ら)から初の実現(2009 年)まで 31 年かかった。
「何回まで演算できるか」で 3 段階 — SHE(有限回)→ LHE(パラメータで定めた回数まで、Bootstrapping なし)→ FHE(Bootstrapping により無制限)。
SHE: BGN など LHE: BFV/BGV, CKKS など FHE: GSW, FHEW, TFHE など
方式は「何の計算に向くか」で分かれる — BFV/BGV は整数(SIMD 的な演算可)、CKKS は浮動小数点などの近似値(機械学習と相性が良い)。どちらも Bootstrapping が重く、主に LHE として運用される。
出発点は 2009 年の Gentry's blueprint。演算で「ノイズ」が増えて復号できなくなる問題を Bootstrapping の導入で乗り越えたが、当初は計算量が非常に大きく実用には遠かった。
GSW が近似固有ベクトルの概念で暗号文を行列として扱う道を開き、FHEW が Bootstrapping を 1 秒未満に、TFHE が Torus への拡張で論文報告値約 13 ms まで縮めた。
FHEW は論理ゲートの評価とノイズのリフレッシュを同時に行えるようにした。
講義が置いた問い — 次のカードを見る前に、自分で挙げてみる。
Q. FHE の課題にはどのようなものがあると思いますか?
課題は 3 つ — 実行コスト、検証可能性、安全性モデル。
実行コスト: 暗号化でデータが大きくなる・演算が重い・Bootstrapping は改善されてもなおボトルネック
検証可能性: FHE だけでは計算結果の正しさは保証できない(Week 6 で説明)
安全性: 暗号文を意図的に変形できる機能そのものが non-malleability と両立しない。
多くの方式は CPA 安全性中心。CCA が必要な応用では認証・検証レイヤーを別途設計
CPA / CCA を区別する: CPA(選択平文攻撃)では、攻撃者は選んだ平文の暗号化を要求できるが、復号は要求できない。CCA(選択暗号文攻撃)では、攻撃者が作った暗号文の復号も要求できる(CCA2 はチャレンジ暗号文を見た後も要求できるが、チャレンジそのものは除く)。FHE は Enc(m) → Enc(f(m)) と暗号文を加工する機能が本体なので、加工を禁止する non-malleability と両立しない。CCA が必要な用途では、認証・改ざん検知・復号要求の制限などを別途組み合わせる。
スライド 12〜19 / LWE 暗号とノイズ
先に役を固定する
s が秘密鍵。a は標本ごとに変わる公開ベクトル、e は小さい誤差、b は公開される 1 個の値で、暗号文は組 (a, b) です。文字列は先に UTF-8 のバイト列へ変換し、TFHE では通常さらに bit へ分けて、各 bit を小さい平文 m として暗号化します。Δ は文字を数へ直す処理ではなく、その小さい m を暗号文空間の離れた位置へ置く間隔です。
文字列 → UTF-8 bytes → bits / 小さい整数 → LWE 暗号文 LWE 暗号文 → bits / 小さい整数 → UTF-8 bytes → 文字列
道順 — 近似連立方程式 → LWE 問題 → LWE 暗号とその演算 → 乗算とノイズ → TFHE の PBS(Blind Rotation / Sample Extraction / Key Switching)→ HomNAND。
通常の連立方程式は Gauss の消去法で高速に解ける。しかし各式の左辺に小さな誤差 e を足しただけで、誤差を知らずに解くのは困難になる。
橋: 中学の連立方程式は「片方の式からもう片方を引く」で必ず解けた。誤差入りだと引き算のたびに誤差も混ざり合い、消去が効かなくなる。
誤差入りの式 1 本は、係数ベクトル a と秘密ベクトル s の内積(同じ位置を掛けて全部足す)を使って ⟨a,s⟩ + e = b と 1 行に書ける。
a = (a₀, …, aₖ₋₁), s = (s₀, …, sₖ₋₁) ⟨a,s⟩ = a₀s₀ + ··· + aₖ₋₁sₖ₋₁
何が嬉しいか: s を固定したまま a を変えると、秘密の異なる線形な組み合わせを 1 個の値として何本でも作れる。ノイズがなければ、それらを A s = b と並べて消去法で s を解ける。標本 i ごとに aᵢ・eᵢ・bᵢ は変わるが、s だけは共通です。
射影との関係では、⟨a,s⟩ は射影ベクトルを作る前に現れる 1 個の数です。ただし LWE で使う性質は図形より、⟨a+a′,s⟩ = ⟨a,s⟩ + ⟨a′,s⟩ という線形性です。
判定 LWE = 同じ秘密 s を共有する多数の標本が、bᵢ = ⟨aᵢ,s⟩ + eᵢ (mod q) から来たのか、(aᵢ,bᵢ) が一様ランダムなのかを識別する問題。
単一の組が式を満たすか、ではありません。 e を自由に選べば、ほぼどの (a,b) も式の形にはできます。区別するのは、aᵢ は一様、eᵢ は小さい、s は全標本で共通という分布です。
q = 11, s = (1,0) の観察用の例 a=(2,7), e=+1 → b=3 b−⟨a,s⟩ = +1 a=(8,4), e=−1 → b=7 b−⟨a,s⟩ = −1 a=(5,9), e= 0 → b=5 b−⟨a,s⟩ = 0
この例では s を見せたので残差が小さいと確認できますが、本物の判定ゲームでは s は見えません。大きい次元・mod q・多数の未知誤差の下で LWE 分布と一様分布を効率よく区別できない、という仮定が LWE 仮定です。(aᵢ,bᵢ) 群から共通の s を求める側は探索 LWE。
LWE 暗号 = 秘密鍵 s との内積で平文を隠し、e を足して式を少しずらす共通鍵暗号。b = ⟨a,s⟩ + Δm + e。
Gen: 秘密鍵 s ∈ {0,1}^k を一様ランダムに取る
Enc: c = (a, b = ⟨a,s⟩ + Δm + e) a は一様ランダム、e は分布 χ から。Δ := q/p
Dec: m = ⌊(b − ⟨a,s⟩ mod q) / Δ⌉ mod p (⌊·⌉ は四捨五入)
s を知る人は b から同じ内積を引き、Δm + e を得ます。e 自体を求めるのではありません。⌊(Δm+e)/Δ⌉ = ⌊m + e/Δ⌉ なので、−Δ/2 ≤ e < Δ/2 なら小さいずれが丸め落ちて m が戻ります。以降この暗号文を LWEₛ(Δm) と書く。
例: Δ=16, m=2, e=3 Δm+e = 35, 35/16 = 2.1875, 四捨五入 → 2
中学生の数だけで、暗号化から復号まで
設定: p=4、q=20 なので Δ=q/p=5。秘密鍵 s=(1,1)、公開ベクトル a=(2,3)、平文 m=2、ノイズ e=1 とします。
1. 内積: ⟨a,s⟩ = 2×1 + 3×1 = 5
2. 平文を置く: Δm = 5×2 = 10
3. 暗号化: b = 5 + 10 + 1 = 16
暗号文 c = ((2,3), 16)
4. 鍵で消す: 16 − (2×1 + 3×1) = 11
5. Δで割る: 11÷5 = 2.2
6. 丸める: 2.2 → 2 (平文が戻る)
秘密鍵を誤って (1,0) とすると、16−2=14、14÷5=2.8→3 になり復号に失敗します。正しい鍵でもノイズを e=3 まで増やすと、(5+10+3−5)÷5=2.6→3 となります。s は内積の項を消す鍵、Δ は平文を離して置く間隔、丸めは小さい e だけを結果から落とす操作です。
Q. 暗号文が復号できることを各自確認。
平文との乗算と暗号文同士の加算では、出力のノイズが元より増える — 演算を続ければ、いずれ −Δ/2 ≤ e < Δ/2 に収まらなくなる。
平文 m′ の加算: (a, as + Δ(m+m′) + e) ノイズ e のまま 平文 m′ の乗算: (m′a, m′as + Δmm′ + em′) ノイズ em′ に増加 暗号文同士の加算: (a+a′, (a+a′)s + Δ(m+m′) + e+e′) ノイズ e+e′ に増加
さっきの数値例でノイズだけ追う
Δ=5, s=(1,1) c₁: m=1, e=1 → ⟨a,s⟩ + 5 + 1 c₂: m=1, e=−1 → ⟨a′,s⟩ + 5 − 1 暗号文を加算: 平文 1+1=2、ノイズ 1+(−1)=0 → 復号しやすい 平文を 2 倍: 平文 1×2=2、ノイズ 1×2=2 → まだ |e|<Δ/2 さらに加算: ノイズ 2+2=4 → Δ/2=2.5 を超え失敗
つまり「計算結果」だけでなく、見えない e も一緒に足し算・掛け算されます。暗号文同士の乗算ではノイズだけでなく暗号文の形(次数)も膨らむので、Relinearization / Key Switching で通常形へ戻し、Bootstrapping でノイズをリセットします。
Q. これらの演算結果が正常に復号できるか各自確認してみよう。
暗号文同士の「乗算」の作り方が方式の分かれ目。Gentry's blueprint は暗号文を多項式のベクトルと見て積を取り、ノイズは指数関数的に増える — それを Bootstrapping(暗号文を暗号文の状態で復号する)で削減する。
BGV/BFV/CKKS はテンソル積で掛け、形の崩れた暗号文を Relinearization / Key Switching で通常形へ戻す。ノイズ・スケール管理の中心は BGV の Modulus Switching、CKKS の Rescaling。
「ベクトルを多項式にする」とノイズが出る?
多項式として書き直すだけならノイズは増えません。係数ベクトル (a₀,a₁) を a₀+a₁x と読むと、掛け算は係数の畳み込みになります。
(a₀+a₁x)(b₀+b₁x) = a₀b₀ + (a₀b₁+a₁b₀)x + a₁b₁x² ベクトル: (a₀b₀, a₀b₁+a₁b₀, a₁b₁) ← 2 係数が 3 係数に膨らむ
暗号文の各多項式に … + e が含まれているため、掛け算後には e·e′ や e·(a′s) などの項も現れます。これがノイズの増加です。環の xⁿ=±1 で次数を戻しても、すでに増えたノイズは消えません。そこで Relinearization / Key Switching で形を戻し、Bootstrapping でノイズを抑えます。
テンソル積は、内積のように足して 1 個へ潰さず、組み合わせごとの積を全部残す操作です。(2,3)⊗(5,7)=(10,14,15,21)(内積なら 2×5+3×7=31 の 1 個)。全ての交差項を残すので暗号文同士の積を正確に表せるのがメリットですが、要素数とノイズが増えるため、後で通常サイズへ圧縮します。
交差項とは、(x+y)(u+v)=xu+xv+yu+yv の xv と yu のような「別の項どうしの組み合わせ」です。これを落とすと平文の積 m·m′ も正しく残りません。目的は、秘密鍵を知らずに暗号化された値の積を計算することです。
GSW は暗号文を行列の形にして、積をビット分解と行列積で実現し、Gadget Decomposition でノイズの増大を抑えた。FHEW/TFHE はこれを使って Bootstrapping を高速化し、ノイズを減らしながら関数評価できるようにした。
平文同士の乗算は、平文を bit 表現して暗号文の状態の NAND を組み合わせれば実現できる。
これは「改良の一本道」
基本 LWE ベクトルで隠す。加算は簡単、暗号文同士の乗算が難しい
↓
GSW 行列として持ち、行列積で乗算。Gadget 分解で扱いやすくする
↓
FHEW / TFHE GSW の仕掛けを使って Bootstrapping を高速化
↓
NAND ビットの NAND を暗号文のまま評価。NAND を組み合わせれば任意の回路
つまり GSW は「乗算を表現する工夫」、FHEW/TFHE は「ノイズをリセットする工夫」、NAND は「任意の計算へ組み立てる部品」です。役割が違うだけで、目標は一つ(暗号文のまま計算)です。
ビット分解 ≠ ビット論理。GSW の内部では、13 を 1101₂=1+4+8 のように小さな桁へ分け、各桁を Gadget の重み (1,2,4,8) と組み合わせます。これで大きな係数の積を、決まった小さな部品の行列積へ分解でき、結果の形とノイズを見積もりやすくなります。TFHE の NAND は、暗号化された 0/1 に対して実際に行う別の処理です。
なぜ格子で FHE か:量子耐性に加え、足し算・掛け算を暗号文の中で自然に表せ、ノイズを Key Switching / Bootstrapping で管理でき、多項式・行列の構造でまとめて計算できます。整数向け(BFV/BGV)、近似値向け(CKKS)、ビット向け(TFHE)を選べる代わりに、暗号文が大きく計算も重い、という交換条件です。
スライド 20〜27 / TFHE と Programmable Bootstrapping の基本アイデア
TFHE = FHEW を Torus(トーラス)という代数構造に拡張した方式。論文中では約 13 ms で Bootstrapping を実行するパラメータ例が報告されている。
講義ではトーラスを使わない説明を取り、鍵生成・暗号化・復号は LWE 暗号と同じにする。トーラスの利点が気になる人は Chi20+ / Joy22 へ。
この論文の中心アイデア
TFHE は、LWE の仕組みを捨てた別の暗号ではなく、値を トーラス(1 周する数の世界)に置き、Bootstrapping を高速にした方式です。トーラスは 𝕋=ℝ/ℤ、つまり 0.9+0.3=0.2 のように 1 周すると 0 に戻る世界です。
従来: 大きな整数 q の上で LWE を計算
TFHE : トーラス上の位相(小数の位置)で LWE を計算
↓
Blind Rotation で表を回す
Sample Extraction で1個のLWEに戻す
Key Switching で鍵をそろえる
↓
ノイズを抑えた暗号文
トーラスにしただけでノイズが消えるわけではありません。固定幅整数の折り返しを使いやすくし、NANDなどのビット演算と高速なProgrammable Bootstrappingを組み合わせやすくした、というのがメリットです。13msは論文の特定パラメータでの報告値で、常にその速度という意味ではありません。
整数の 5 mod 3 = 2 と同じ要領で、多項式にも割り算の余りが取れる。mod xⁿ+1 の世界では xⁿ ≡ −1、そして x⁻ᵃ ≡ −xⁿ⁻ᵃ (0 ≤ a ≤ n)。
橋: 「x⁵+x+2 を x²+1 で割った余りは 2x+2」— 数 II の整式の割り算そのもの。係数が有限体 F_p のときは係数側にも mod p を取る。
式を1行ずつ計算する
x²+1 ≡ 0 なので x² ≡ −1 x⁴ ≡ (+1), x⁵ = x·x⁴ ≡ x x⁵+x+2 ≡ x+x+2 = 2x+2
多項式の mod は「割り算の余り」なので、次数が高い項を x²≡−1 で置き換えて次数を下げています。有限体 (𝔽_5) では係数も5で割った余りにします。例えばスライドの式は (3x+2)(x+4)=3x²+14x+8 ≡ 3x²+4x+3、さらに x²≡−1 だから 4x です。
x⁻ᵃ は逆向きの回転です。xᵃ·(−xⁿ⁻ᵃ)=−xⁿ≡1 なので、x⁻ᵃ≡−xⁿ⁻ᵃ。負の指数を実数の小数として計算するのではなく、環の中の「掛けると1になる元」として扱います。この符号反転(negacyclic)が、TFHEの多項式を回すときに現れます。
Q. xⁿ ≡ −1 と x⁻ᵃ ≡ −xⁿ⁻ᵃ が成立することは各自確認。
やりたいこと = 平文を保ったまま、暗号文のノイズをリセットする。作戦: すべての平文を並べたリストの「ノイズの少ない暗号文」から、入力平文 mᵢ の部分だけを切り出す。
図を「暗号化された答え表」として読む
入力: Enc(Δmᵢ + e) ノイズ付き。mᵢ の場所が少しずれている 用意: Enc(表) m₀,m₁,…,mₚ₋₁ を幅を持たせて並べた表 操作: 暗号文が指す量だけ表を回す(秘密鍵で位置を読む必要なし) 読出: 0 番地の係数 → mᵢ の部分が0番地に来る 出力: Enc(Δmᵢ + 新しいノイズ) → 平文は同じ、古いノイズは引き継がない
これは「暗号文を復号してから表を引く」のではありません。表も入力も暗号化されたまま、Blind Rotation が回転を実行します。表の値を mⱼ ではなく f(mⱼ) に置けば、同じ手順で Enc(f(mᵢ)) が出るため Programmable と呼ばれます。
選べる関数の例(p=2 のビット) 恒等 f(0)=0,f(1)=1 → 表 [0,1] NOT f(0)=1,f(1)=0 → 表 [1,0] p=4 なら f(m)=m+1 mod 4 → 表 [1,2,3,0]
つまり「どの位置を読むか」は入力暗号文が決め、「その位置に何を置くか」は計算したい関数が決めます。2入力の NAND などは、複数のビットを組み合わせてからこの1入力の表引きを使います。
図の横長の箱は平文を直接見せている絵ではなく、概念上のテスト多項式です。実際のTFHEではその係数も暗号文の内部にあり、最後にSample ExtractionとKey Switchingで通常のLWE暗号文へ戻します。
多項式 v(x) に x⁻ⁱ を掛けると (mod xⁿ+1)、係数 aᵢ が定数項に来る — 係数の列を「回転」させられる。
暗号文 (a, b) は b − as = Δmᵢ + e を満たす。だからこの量だけ回したとき定数項に mᵢ が来るように、v(x) の係数を組んでおけばよい。
一般式の前に、1回ずつ見る
係数の列: [10, 20, 30, 40]
普通に1つ左へ: [20, 30, 40, 10]
x⁴=−1 の世界: [20, 30, 40, −10] ← 端から来た10だけ符号反転
もう1回 x⁻¹: [30, 40, −10, −20]
↑ 元の30が先頭(定数項)へ
この2回分を多項式でまとめたのが x⁻² です。v(x)=10+20x+30x²+40x³ に掛けると 30+40x−10x²−20x³ になります。つまり x⁻¹ は「1칸左へ回す」、x⁻² はそれを2回、というだけです。
暗号文の b−⟨a,s⟩=Δmᵢ+e は「どれだけ回すか」を表します。表の中で同じ mᵢ を近くの場所にも繰り返しておけば、ノイズ e で少し回転量がずれても、定数項には同じ mᵢ が来ます。
同じ mᵢ を、ノイズ候補ぶんの位置 Δmᵢ+e₀, …, Δmᵢ+e_r のすべてに並べておく。するとノイズ込みの回転量 Δmᵢ+e で回しても、定数項には mᵢ が来る。
まずは「1マス」ではなく「正解の帯」
平文 m=1、Δ=5、ノイズ候補 e∈{−1,0,+1}
正解の位置は Δm+e = 4, 5, 6
幅なし: 位置 5 だけに「1」
幅あり: 位置 4,5,6 に「1,1,1」
実際のノイズが +1 → 回転量は 6
幅なしだと別の値を読むかもしれない
幅ありなら位置6にも「1」があるので、答えは必ず1
暗号文から分かるのは Δm+e ですが、e 自体は分かりません。だから、あり得るノイズの全候補に同じ平文を置きます。回転がどの候補に着地しても、0番地へ来る係数は同じ m です。
スライドの長い式 v_q(x) は、この「同じ数字を何マスも続けて置いた表」を多項式で書いたものです。帯を越えるほどノイズが大きくなると、隣の平文の帯に入り、復号に失敗します。
式の導出は「表 → 式」
f(m)=m。Δ̂=4、ノイズ候補は e=0,1,2,3。4,5,6,7 のどこかに来るので、その全場所に1を置く。j に値 c」を c·xʲ と書いて全部足す。平文0: 位置0〜3 → 0·(1+x+x²+x³) 平文1: 位置4〜7 → 1·(x⁴+x⁵+x⁶+x⁷) 平文2: 位置8〜11 → 2·(x⁸+x⁹+x¹⁰+x¹¹) 平文3: 位置12〜15 → 3·(x¹²+x¹³+x¹⁴+x¹⁵)
実用的なパラメータでは q が大きすぎるので、多項式の次数は q より小さい n にし、a と b を ⌊·(2n/q)⌉ で n の世界へ丸め直してから回す(Rescaling / Modulus Switching と呼ぶ)。
p = 8, q = 64, Δ = 8, n = 16 の手計算例 — b − as ≡ 10 = 8·1 + 2 の暗号文を、リスケーリング後の回転量 4 で回すと、定数項に平文 1 が現れる。
数字を1行ずつ追う
⟨a,s⟩=8·1+32·1+0·1+31·1+7·1=78≡14 (mod 64)。したがって b−⟨a,s⟩=24−14=10=8·1+2。平文は1、ノイズは2。v は位置0〜3に0、4〜7に1、8〜11に2、12〜15に3を置く表。平文1を4マス続けたのは、ノイズ 0≤e<4 で位置がずれても1を読めるようにするため。â=round(a/2)=(4,17,2,16,0,16,29,4)、b̂=round(b/2)=12。â と秘密鍵の内積は 40 なので、回転量は i=12−40≡4 (mod 32)。x⁻⁴v ではそこが位置0〜3へ移るので、定数項(位置0)は 1。これが復号した平文。つまり最後の x⁻⁴ は難しい魔法ではなく、「表を4マス回して、先頭の数字を見る」という操作です。
m = 1, s = (1,0,0,1,1,1,0,1), e = 2, (a, b) = ((8,34,4,32,0,31,58,7), 24) v = 0·(1+x+x²+x³) + 1·(x⁴+…+x⁷) + 2·(x⁸+…+x¹¹) + 3·(x¹²+…+x¹⁵) リスケーリング: Δ̂ = 4, â = (4,17,2,16,0,16,29,4), b̂ = 12 i = b̂ − âs = 12 − (4+16+0+16+4) ≡ 4 (mod 32) → x⁻⁴v の定数項 = 1 = m
Q. 5 分とるので、各自手を動かして具体例を確認(講義中の演習)。
PBS は 3 部品の直列。
さっきの「平文1」の例を3箱に通す
s や平文を知りません。それでも、秘密鍵の各ビットを「暗号化されたスイッチ」として使い、v を必要な量だけ回します。今回なら、結果は x⁻⁴v と同じになります。1 です。その係数だけを、新しいLWE暗号文として取り出します。数字1を平文のまま取り出すのではなく、「1を暗号化したもの」を作ります。1 のまま、最初のLWE暗号文と同じ形式の鍵に変換します。入力: ノイズのあるLWE暗号文 → 出力: 同じ平文(この例では1)を含み、ノイズがリセットされた新しいLWE暗号文。3箱ともサーバーが秘密鍵を直接見ることはありません。
スライド 28〜35 / Blind Rotation
回転量 b − as の計算には秘密鍵 s が要る。そこで as = Σ aⱼsⱼ をほどき、「sⱼ = 0 なら何もしない、1 なら x^aⱼ を掛ける」という 2 択(マルチプレクサ、MUX)を k 回繰り返す形に書き直す。
Q₀ ← x⁻ᵇ·v for j = 0 … k−1: Qⱼ₊₁ ← MUX(sⱼ, Qⱼ, x^aⱼ·Qⱼ) return Q_k (= x^(−b+as)·v)
あとは v, sⱼ, Qⱼ を暗号文にして、この MUX を準同型演算で実行できればよい。
小さな数字で「回す/回さない」を追う
a=(2,5,1), s=(1,0,1), b=3 とする 最初: Q₀ = x⁻³v j=0: s₀=1 なので x²を掛ける → Q₁=x⁻¹v j=1: s₁=0 なので何もしない → Q₂=x⁻¹v j=2: s₂=1 なので x¹を掛ける → Q₃=v 指数をまとめると −3 + 2×1 + 5×0 + 1×1 = 0 だから Q₃ = x⁰v = v
MUX(b,a₀,a₁)=b(a₁−a₀)+a₀ は、b=0 なら a₀、b=1 なら a₁ を選ぶスイッチです。ここでは sⱼ がそのスイッチで、0なら回さず、1なら x^{aⱼ} を掛けます。
実際のBlind Rotationでは sⱼ は秘密なので、スイッチ自体も暗号化したまま(CMUX)動かします。サーバーは「どちらを選んだか」を見ずに、最終的に必要な回転だけを実行できます。sが消えるのではなく、暗号化された sⱼ を評価用のスイッチとして使うため、サーバーが秘密鍵を平文で持つ必要がなくなる、という意味です。
RLWE 暗号 = 平文・鍵・乱数をすべて多項式にした LWE。計算は mod xⁿ+1(係数は mod q)で行う。
Enc: (a(x), b(x) = a(x)s(x) + Δμ(x) + e(x)) Dec: b − as = Δμ + e を計算し、係数ごとに Δ で割って丸める
LWE と同様に、平文との加算・乗算、暗号文同士の加算ができる。
LWEとの違いは「数字」から「多項式」へ
LWE: b = a·s + Δm + e (数字の足し算・掛け算) RLWE: b(x) = a(x)s(x) + Δμ(x) + e(x) (多項式の足し算・掛け算)
たとえば秘密鍵 s(x)=1+x、乱数 a(x)=2+3x なら、マスク部分は
a(x)s(x)=(2+3x)(1+x)=2+5x+3x²
となります。s(x) の係数 (1,1,0,…) が秘密鍵です。復号では b(x)−a(x)s(x) を計算し、各係数を Δ で割って丸めます。つまり、LWEの「内積を1回」が、RLWEでは「多項式の掛け算」に置き換わっただけです。
多項式にすると、1つの暗号文にたくさんの係数を詰めて、回転や掛け算をまとめて扱えます。Blind Rotationで使う x^r の回転も、この多項式の形だから書けます。
Gadget Decomposition = mod q の値の B 進数展開。19 = 10011₂ と同じ要領で、r = Σ rᵢ·q/Bⁱ⁺¹ (0 ≤ rᵢ < B) と桁に分け、g⁻¹(r) = (r₀, …, r_{l−1}) を出力する。
例(B = 4, l = 3): Z₆₄ の 47 = 2·(64/4) + 3·(64/16) + 3·(64/64) → g⁻¹(47) = (2, 3, 3)。等式が成り立つには Bˡ = q が必要で、そうでない場合は各項を丸めて近似する。
まずは「47を4進数の桁に分ける」
普通の4進数なら、47は 2×16 + 3×4 + 3×1 です。実際に計算すると、32+12+3=47。
大きな数 47 ↓ 4進数の桁に分ける (2, 3, 3) 64を一周とする世界でも、使う場所の重みを 64/4=16、64/4²=4、64/4³=1 と決めれば、47 = 2×16 + 3×4 + 3×1。
つまり g⁻¹(47)=(2,3,3) は「47を小さい数字3個で表した」という意味です。rᵢ は必ず 0,1,2,3 のどれかになります。
暗号では、大きな係数をそのまま扱わず小さな桁に分けると、各桁を別々のスイッチや鍵素材に対応させられます。これがGadget Decompositionを使う理由です。
多項式も同じ要領で分解できる — 係数ごとに B 進展開し、q/Bⁱ⁺¹ の位ごとに多項式をまとめ直す。
例: Z₁₆, mod x³+1, B = 2, l = 4 で f = 15x² + 4x + 7 → g⁻¹(f) = (x², x²+x+1, x²+1, x²+1)。
なぜ多項式でも分解するのか
多項式の係数はそれぞれ普通の数字です。まず係数を1個ずつ2進数のように分け、同じ桁の部品を同じ多項式に集めます。
f = 15x² + 4x + 7、q=16、B=2 15 → (1,1,1,1) (8+4+2+1) 4 → (0,1,0,0) (0+4+0+0) 7 → (0,1,1,1) (0+4+2+1) 重み8の桁: x² 重み4の桁: x²+x+1 重み2の桁: x²+1 重み1の桁: x²+1
したがって、
f = x²·8 + (x²+x+1)·4 + (x²+1)·2 + (x²+1)·1
g⁻¹(f)=(x², x²+x+1, x²+1, x²+1) は、この4つの小さい係数の多項式を並べたものです。大きな係数をそのまま扱わず0/1の部品にすると、RGSWのGadget行と掛け合わせて、暗号文のまま制御しやすくなります。値を変えているのではなく、あとで足し戻せる形に分解しています。
RGSW 暗号文 = 「0 の RLWE 暗号文」を 2l 個並べたリスト Z に、平文 m を掛けた Gadget Matrix Gᵀ を足したもの。
各行は RLWEₛ(−s(x)·m·q/Bⁱ) または RLWEₛ(m·q/Bⁱ) の形になっている。Bˡ = q のとき G⁻¹((x₁, x₂))·Gᵀ = (x₁, x₂)。
RGSWの役割だけ先に見る
RGSWは、平文 m(特に秘密鍵ビットの0/1)を暗号化した「掛け算用の暗号文」です。RLWE暗号文 c=Enc(m′) と専用の積を取ると、
RGSW(0) ⊡ Enc(m′) ≈ Enc(0) RGSW(1) ⊡ Enc(m′) ≈ Enc(m′)
となります。つまり、m=0ならOFF、m=1ならONです。これを使うと、秘密鍵ビットを見せずに「回す/回さない」を選べます。
スライドの長いリストやGadget Matrixは、この掛け算を暗号文のまま実現するための部品表です。大きな係数をGadget分解して各行と組み合わせ、最後に足し戻すことで、平文の積 m·m′ を得ます。
External Product ⊡ = RLWE 暗号文を Gadget 分解してから RGSW 暗号文に掛ける積。結果は RLWEₛ(Δmm′) — 平文同士の掛け算が暗号文のまま 1 回でできる。
G⁻¹(RLWE(Δm′))·(Z + mGᵀ) = RLWE(0) + m·RLWE(Δm′) = RLWE(Δmm′)
まずは「暗号化した係数で掛ける」
RGSW (m) は「平文 m を暗号化した掛け算係数」、RLWE (m′) は「掛けられる暗号文」です。専用の積 ⊡ を取ると、
RGSW(0) ⊡ RLWE(m′) → RLWE(0) RGSW(1) ⊡ RLWE(m′) → RLWE(m′) RGSW(2) ⊡ RLWE(m′) → RLWE(2m′)
となります。つまり、RGSWが暗号化されたスイッチ/係数として働き、平文を見ずに掛け算できます。
なぜGadget分解が必要かというと、RLWEの大きな係数を小さな桁に分けてRGSWの各行と対応させるためです。式の
G⁻¹(c)(Z + mGᵀ) = G⁻¹(c)Z + m·G⁻¹(c)Gᵀ
で、c=RLWE(Δm′)。左の項は「0を暗号化したもの」なので平文には影響せず、右の項はGadgetの桁を足し戻して m·c になります。したがって結果の平文は mm′ です。⊡は普通の掛け算ではなく、「分解して、対応する行を掛けて、足し戻す」手順の名前です。
CMUX = MUX(b, a₀, a₁) = b(a₁−a₀) + a₀ の暗号版。選択ビット b を RGSW 暗号文、選択肢を RLWE 暗号文に置き換えて CMUX(c_b, c₀, c₁) = c_b ⊡ (c₁ − c₀) + c₀ = RLWE(Δa_b)。
普通のスイッチを暗号化する
普通のMUX: b=0 → a₀=5 を選ぶ b=1 → a₁=9 を選ぶ 式: b(a₁−a₀)+a₀ b=0: 0×(9−5)+5 = 5 b=1: 1×(9−5)+5 = 9
CMUXでは、a₀=5、a₁=9、bをすべて暗号化します。
c₀ = Enc(5), c₁ = Enc(9), c_b = RGSW(b) CMUX = c_b ⊡ (c₁−c₀) + c₀
External Product ⊡が「暗号化された b × 暗号文」を実行するので、復号すると b=0なら5、b=1なら9です。計算中は、サーバーは選択ビットも選択結果も見ません。
Blind Rotationでは、a₀=Qⱼ(回さない候補)、a₁=x^{aⱼ}Qⱼ(回す候補)、b=sⱼ(秘密鍵ビット)を入れます。だからCMUX一回で、秘密鍵ビットに応じた回転を実行できます。
Q. CMUX の出力が a_b の RLWE 暗号文になることを各自確認してみよう。
Blind Rotation = スライド 28 の MUX 反復を、そのまま CMUX 反復に置き換えたもの。sⱼ を別の鍵 s′ で RGSW 暗号化したリスト (RGSW_{s′}(s₀), …, RGSW_{s′}(s_{k−1})) を Bootstrapping Key と呼ぶ。
â ← ⌊a·(2n/q)⌉, b̂ ← ⌊b·(2n/q)⌉
Q₀ ← x^(−b̂)·RLWE_{s′}(Δv(x))
for j = 0 … k−1: Qⱼ₊₁ ← CMUX(RGSW_{s′}(sⱼ), Qⱼ, x^âⱼ·Qⱼ)
return Q_k (= RLWE_{s′}(Δ·x^(−b̂+âs)·v(x)))
テスト多項式 v(x) は、a(x) = 0 としたノイズのない自明な暗号文 (0, Δv) とみなして初期値にできる。
左と右は「同じ処理」を別の方法で行う
秘密鍵を知っている左側: Q₀=x⁻ᵇ̂v → s₀で回す/回さない → s₁で回す/回さない → … 秘密鍵を知らない右側: Q₀=x⁻ᵇ̂RLWE(Δv) → 暗号化s₀のCMUX → 暗号化s₁のCMUX → …
小さな例 a=(2,5,1), s=(1,0,1), b=3 なら、左側は x⁻³v → x⁻¹v → x⁻¹v → v。右側も同じ結果になりますが、s=(1,0,1)を直接見る代わりに、各ビットをRGSW暗号文にしたBootstrapping KeyをCMUXへ渡します。
つまりBlindの意味: サーバーは回転の答えを知らないまま、暗号化されたスイッチを順番に使って、知っている場合と同じ最終回転を作ります。最後の Q_k はまだ暗号文で、秘密鍵 s′(評価用の別鍵)で暗号化されています。
Bootstrapping Keyと呼ぶ理由: 入力暗号文の秘密鍵 s の各ビットを、別鍵 s′でRGSW暗号化した補助データ (RGSW_{s′}(s₀),…,RGSW_{s′}(s_{k−1}))。秘密鍵そのものは渡さず、このデータを使うとサーバーが復号処理を暗号文のまま評価できるので、Bootstrappingを「起動する鍵」と呼びます。通常のメッセージ暗号化に使う鍵ではありません。
資料の「回転」: 講義では「回転」という言葉を毎回書かず、x^r v(x) と表します。x^rを掛けると各項の指数が r だけずれ、係数の位置が移動するためです。例えば x⁻⁴·x⁴=1 で、4番地の係数を定数項へ移せます。回転しないと、欲しい平文が x⁴ の位置に残り、Sample Extractionが読む定数項には来ません。
Q.(講義が残した問い)x^i mod xⁿ+1 には negacyclic の性質があり、i ≥ n では係数の符号が反転する。Blind Rotation でこれが起こるとなぜ問題なのか。どうすれば回避できるだろうか。
スライド 36〜41 / Sample Extraction と Key Switching
Blind Rotation の出力 RLWE 暗号文 (a′(x), b′(x)) の b′ の定数項 b′₀ には Δm + e₀ が含まれている。あとは積 a′(x)s′(x) の定数項がどうなるかを調べればよい。
何を「取り出す」のか
Blind Rotation後の多項式暗号文は、平文 m を定数項(x⁰の係数)に置いた状態です。Sample Extractionは多項式全体を復号するのではなく、その定数項だけを、普通のLWE暗号文として包み直す処理です。
RLWE( Δ(m + μ₁x + μ₂x²+…) )
↓ 定数項だけ
LWE( Δm )
ただし多項式の掛け算 a′(x)s′(x) にも定数項があるので、次のスライドでそれを「符号つき内積」に書き換えます。暗号文を平文に戻す(復号する)処理ではありません。
xⁿ ≡ −1 に注意すると、積の定数項は a′₀s′₀ − a′₁s′_{n−1} − ⋯ − a′_{n−1}s′₁ という「符号つきの掛けて足す」= 内積の形。だから a″ = (a′₀, −a′_{n−1}, …, −a′₁) と置けば、(a″, b′₀) は秘密鍵 s″ = (s′₀, …, s′_{n−1}) による平文 m の LWE 暗号文になる。
まず n=4 で「なぜマイナスか」
a′(x)=a′₀+a′₁x+a′₂x²+a′₃x³ s′(x)=s′₀+s′₁x+s′₂x²+s′₃x³ 定数項に来るもの: a′₀s′₀ (最初からx⁰) a′₁s′₃x⁴, a′₂s′₂x⁴, a′₃s′₁x⁴ (指数の和が4) x⁴ ≡ −1 なので b′₀のマスク部分 = a′₀s′₀−a′₁s′₃−a′₂s′₂−a′₃s′₁
この符号つきの掛け算を普通の内積に見せるため、a″=(a′₀,−a′₃,−a′₂,−a′₁) と並べ替えます。すると ⟨a″,s″⟩ が上の式と同じになり、(a″,b′₀)を「平文mのLWE暗号文」として扱えます。
Sample Extractionは、定数項を裸で読む処理ではなく、多項式の定数項を内積形式に変換して、暗号文の形式をRLWEからLWEへ変える処理です。
Q. s″ で (a″, b′₀) が復号できることを各自確認。
抽出後の暗号文の鍵は s″ に変わっている。LWEₛ(a″s″) を引き算すれば鍵 s の暗号文に戻せる — が、毎回 a″ を委託元に送って暗号化して送り返してもらうのは避けたい。
平文は同じ、暗号文の「鍵」だけが違う
Sample Extractionの直後:
c = Enc_{s″}(m) (平文m、鍵s″)
欲しい最終形:
Enc_s(m) (同じ平文m、元の鍵s)
これは箱を開けて平文を取り出す処理ではありません。「鍵 s″でロックされた箱」を、平文を見せずに「鍵 sの箱」へ付け替えたい、という問題です。
毎回、秘密鍵の持ち主に復号・再暗号化を頼む方法もあります。しかしそれではサーバーだけで計算が完結せず、通信も必要です。そこで鍵の持ち主が事前に変換用の暗号文(Key Switching Key)を渡し、サーバーが暗号文のまま鍵を乗り換えます。
Key Switchingは新しい平文計算ではなく、暗号文の中身を変えずに、暗号化に使われている鍵の形式だけを変える処理です。
事前に ksk[i,j] = LWEₛ(s″ᵢ·q/Bʲ⁺¹) を渡しておけば(Key switching key。Δ を使わないことに注意)、a″ の Gadget 分解の係数で ksk を組み合わせて引くだけで鍵を乗り換えられる。
LWEₛ(Δm) ← (0, …, 0, b′₀) − Σᵢ Σⱼ ā_{i,j}·ksk[i,j]
まず1個の数字で仕組みを見る
古い鍵: s″=3、古い暗号文の係数: a″=5
b″ = 5×3 + Δm = 15 + Δm
変換用データ: Enc_s(3) (新しい鍵sで3を暗号化)
サーバー: 5×Enc_s(3) = Enc_s(15)
b″ − Enc_s(15) → Enc_s(Δm)
古い暗号文に混ざっていた a″s″=15 を、新しい鍵 sで暗号化した形として再現し、b″から引いて消しています。平文 mを読む必要はありません。
実際は a″がベクトルで値も大きいので、Gadget Decompositionで各係数を小さな桁に分けます。ksk[i,j]は「古い秘密鍵の成分 s″ᵢ×桁の重み」を新しい鍵 sで暗号化した部品です。二重和は、それらを足して a″s″を再現しているだけです。
二重和は Gadget 分解の再構成そのもので、まとめると LWEₛ(a″s″) になる。b′₀ = a″s″ + Δm + e₀ からそれを引くと (−ã, −ãs + Δm + e₀ − ẽ) — 秘密鍵 s による m の暗号文が残る。
Bootstrapping = 暗号文の形式と秘密鍵を変えながら、同じ平文 m の暗号文を作り直す操作。入出力は同じ形・同じ鍵・同じ平文で、ノイズだけが新しくなっている。
何をしているか: 普通の再暗号化ならいったん Dec_s(c) を平文で計算するが、Bootstrapping では秘密鍵 s も暗号化して渡し、復号処理そのものを暗号文のまま評価する。したがって m も s も見えず、古いノイズを引き継がない新しい Enc(m) が得られる(ノイズがゼロになるわけではない)。
「Programmable」は何を変えられるのか
通常のBootstrappingは、定数項に元の m を戻します。Programmable Bootstrappingでは、テスト多項式 v(x)を差し替えて、定数項に好きな関数の値 f(m)を置きます。
入力 m=0,1 の例 恒等関数 f(m)=m: 0→0, 1→1 NOT関数 f(m)=1−m: 0→1, 1→0 回転と定数項抽出は同じ。 変えるのは、あらかじめ置く係数(v(x))だけ。
したがって、動機は2つです。Bootstrappingは計算で増えたノイズをリセットすること、Programmableは同じ仕組みで m だけでなく f(m)(ビット反転、比較、Lookup Tableなど)を出力できることです。
スライド 42〜45 / Programmable と HomNAND
テスト多項式の係数を mⱼ から f(mⱼ) に置き換えると、同じ Blind Rotation でノイズのリセットと関数 f の評価が同時にできる — 出力は LWEₛ(Δf(mᵢ))。
リスケーリングの丸めで回転先が少しずれても同じ値を取り出せるよう、同じ mⱼ に対応する複数の位置に f(mⱼ) を繰り返し置く。異なる平文が同じ位置に重なる場合、1 回の PBS で区別できるのは、それらの f(mⱼ) が同じときだけ。
回転量 i が n を超えると定数項の符号が反転する。v(x) = 1 + x + ⋯ + xⁿ⁻¹ なら定数項は 0 ≤ i ≤ n−1 で +1、n ≤ i ≤ 2n−1 で −1 — 1 回の回転で正と負の 2 値を表現できる。
この性質を bit 演算に利用する。
このスライドで目的が変わるわけではない
ここまでのProgrammable Bootstrappingは、暗号文の入力から f(m) を計算する仕組みでした。ここでは、その f として「値が前半なら0、後半なら1」というビット抽出関数を選びます。negacyclicの符号反転は、その関数を実装するための小技です。
n=4で正負が切り替わる様子
v=1+x+x²+x³
i=1: x⁻¹v = 1+x+x²−x³ → 定数項 +1
i=5: x⁻⁵v = −x⁻¹v
= −1−x−x²+x³ → 定数項 −1
x⁴≡−1なので、4マスを越えて回り込んだときだけ符号が反転します。したがって回転量が 0〜3 なら定数項は +1、4〜7 なら −1です。これは「定数項の符号を見るだけで、回転量が前半か後半か(1ビット)を判定できる」という意味です。
普通の回転なら端から戻っても同じ値ですが、negacyclic回転では戻るときに符号が変わるため、これを意図的にビット演算へ利用できます。
HomNAND = ① bit を 0 ↦ p−1, 1 ↦ 1 とエンコード ② c = (0, …, 0, Δ·1) − c₁ − c₂ を計算(c の平文は r = 1 − m₁ − m₂ mod p)③ 全係数 1 のテスト多項式で PBS ④ パラメータをうまく設定すると出力が NAND の暗号文になる。
3 < n < p−1 であれば、r = 1, 3 のとき定数項は反転せず 1 に、r = p−1 のとき反転して p−1 に — ちょうど NAND の真理値表と一致する。
p=7, n=4で真理値表を計算
ビット0を p−1=6、ビット1を 1 と表します。まず暗号文のまま 1−m₁−m₂ を作ります。
(m₁,m₂)=(0,0): r=1−6−6≡3 (mod7) → 1 (m₁,m₂)=(1,0): r=1−1−6≡1 (mod7) → 1 (m₁,m₂)=(0,1): r=1−6−1≡1 (mod7) → 1 (m₁,m₂)=(1,1): r=1−1−1≡6 (mod7) → 0
最後の矢印はProgrammable Bootstrappingです。r=1,3は回転量の前半なので定数項が+1、r=6=p−1は後半なのでnegacyclicの符号反転で−1≡6になります。これがNANDの表 (0,0),(1,0),(0,1)→1、(1,1)→0です。
「HomNAND」は新しい暗号方式の名前ではなく、既存のPBSとnegacyclicの性質を使って、NANDという論理関数を暗号文のまま実行したものです。
具体例 p = 8, q = 32, n = 16, Δ = 4 — q = 2n なので Blind Rotation 時のリスケーリングは不要。入力暗号文のノイズの和 d ∈ {0, 1, 2} を含めても回転の番号の帯は分かれ、NAND の出力が読める。
(0,0): r = 3 回転の番号 i = 10, 11, 12 定数項 1 → bit 1 (1,0),(0,1): r = 1 回転の番号 i = 2, 3, 4 定数項 1 → bit 1 (1,1): r = 7 回転の番号 i = 26, 27, 28 定数項 −1 = 7 → bit 0
表の数字を1行ずつ確認
ここでは p=8 なので、ビット0は 7(=−1)、ビット1は 1。Δ=4、入力暗号文2つのノイズの合計を d=0,1,2 とします。回転番号は i=Δr−d です。
(0,0): r=3 → i=12−d = 12,11,10(全部0〜15) → 定数項+1 → bit1 (1,0),(0,1): r=1 → i=4−d = 4,3,2(全部0〜15) → 定数項+1 → bit1 (1,1): r=7 → i=28−d = 28,27,26(全部16〜31) → 定数項−1=7 → bit0
negacyclicの性質では、回転番号が 0〜15 なら定数項が+1、16〜31なら−1になります。ノイズで番号が2マスずれても、前半と後半の境界を越えないようにパラメータを選んでいるため、結果が壊れません。
このページは新しい理論を導入するのではなく、前のHomNANDの仕組みが、実際の数値とノイズ込みでも動くことの確認です。
スライド 46〜48 / ホワイトボードセッション
グループに分かれてテーマを調査・議論(イントロ 5 分 + 調査・議論 1 時間 30 分)し、2 グループ 1 組で発表 30 分(15 分 × 2)— お互いの発表にフィードバックする。
議論のテーマ: PBS の流れ(入力 LWE → Blind Rotation → Sample Extraction → Key Switching → 出力 LWE)のどこを、どんな工夫で速くしているかを TFHE-rs のドキュメントとコードから探す。
発表では、速くなる仕組みだけでなく「代わりに増えるコスト」と「新たに生まれた疑問」まで示す。工夫の個数に制限はなく、ソースコードを完全に理解する必要もない。参照先: github.com/zama-ai/tfhe-rs / docs.zama.ai/tfhe-rs。
テーマ1で実際にやること
目的は、TFHE-rsを自作することではありません。 1回のPBSが「どの部品を通り、何を省略・並列化・まとめているから速いのか」を、実行結果と資料から説明することです。
① まず1つのサンプルを動かす(入力bit → 出力bit) ② PBSを4箱に分ける: Blind Rotation / Sample Extraction / Key Switching / 出力 ③ 各箱で「速くする工夫」を1つ見つける ④ 速さの代償(メモリ・前処理・精度・実装の複雑さ)を1つ書く ⑤ 発表: 処理図に、工夫・根拠(docs/コード)・代償を3色で貼る
完成条件: 「この入力を、この処理が受け取り、この出力を返す。ここを○○にしたので速い。ただし△△が増える」と自分の言葉で言えれば十分です。コードを1行ずつ解読したり、速度を自分の環境で再現したりする必要はありません。
議論のテーマ: IND-CPA・IND-CCA1・IND-CCA2 を「攻撃者に許される操作」で比較し、FHE がなぜ non-malleability(頑強性)を満たせないのか、それが IND-CCA2 への具体的な攻撃にどうつながるのかを図または表で示す。
発展課題: IND-CPA のみを満たす FHE に対する鍵回復攻撃とその対策の概要(論文: eprint.iacr.org/2022/1563)。手がかり: indistinguishability game / encryption・decryption oracle / chosen-plaintext・chosen-ciphertext attack。
テーマ2で実際にやること
目的は、暗号方式を破ることではありません。 「攻撃者に何をさせると、どの安全性が破れるか」を、許可証の違いとして整理し、FHEだけはなぜ特別かを示します。
① 3つを表にする IND-CPA : 平文を選んで暗号化してもらえる IND-CCA1 : さらに暗号文の復号も頼める(攻撃前) IND-CCA2 : チャレンジ後も復号を頼める(ただし挑戦文そのものは除く) ② FHEの操作 Enc(m) → Enc(f(m)) を書く ③ これは「暗号文を別の暗号文へ変形」なので non-malleability と衝突すると説明 ④ その変形を使って、CCA2で何を見分けられるかを矢印で示す
発表の最低ライン: 「誰が・何を要求できるか」の比較表1枚、FHEが展性を必要とする理由1枚、攻撃の流れ(入力→暗号文の加工→復号結果→判定)1枚。鍵回復攻撃は発展課題なので、まずこの3枚を完成させればよいです。
スライド 49〜51 / 参考文献
原典(スライド 49) — [Riv78+] 準同型暗号の提唱(1978)/ [RSA78] RSA / [ElG85] ElGamal / [Pai99] Paillier / [Gen09] Gentry, 初の FHE(STOC 2009)/ [Reg05] LWE(Regev, STOC 2005)/ [LPR10] Ring-LWE(EUROCRYPT 2010)
主要 FHE 方式と TFHE(スライド 50) — [Bra12] Brakerski(CRYPTO 2012)/ [Fan12+] BFV / [Bra14+] BGV / [Che17+] CKKS(ASIACRYPT 2017)/ [Gen13+] GSW(CRYPTO 2013)/ [Duc15+] FHEW(EUROCRYPT 2015)/ [CGGI16] TFHE(ASIACRYPT 2016)/ [Chi20+] TFHE(J. Cryptology 2020)/ [Joy22] SoK: FHE over the Torus(2022)
補助資料・書籍・実装(スライド 51) — [Mic21] FHEW 系の Bootstrapping / [Ko25] FHE の入門教科書(arXiv 2025)/ [Kna24] vFHE: 検証可能 FHE(WAHC 2024)/ 岡本『現代暗号の誕生と発展』/ 青野・安田『格子暗号解読のための数学的基礎』/ 縫田『耐量子計算機暗号』/ OpenFHE / 松岡「(完全)準同型暗号の最前線 1(入門編)」(Qiita)
予習チェック
(1) 「暗号文のまま計算できる」を、Enc・Dec と 2 つの演算の式で言えるか。RSA の掛け算はなぜその例になるのか。 (2) 加算と乗算の 2 つだけで「任意の計算」ができると言えるのはなぜか({0,1} 上の 1 − m₁m₂ が鍵)。 (3) 連立方程式は中学の消去法で解けたのに、各式に小さな誤差を足すとなぜ急に解けなくなるのか。 (4) (a, b = as + Δm + e) から、鍵を知る人はどうやって m を取り出すか。Δ = q/p は何のための「桁上げ」か。 (5) 暗号文に演算を重ねると、なぜいつか復号が壊れるのか。壊れる境目はどこか。 (6) 「復号せずにノイズをリセットする」— Bootstrapping がやりたいことを 1 文で言えるか。 (7) xⁿ ≡ −1 (mod xⁿ+1) の世界で x⁻ⁱ を掛けると係数の列に何が起こるか。整数の「余り」とどこが似ていて、どこ(符号)が違うか。
言えなかった番号だけカードに戻る: 1 → 4/51、2 → 6/51、3 → 13/51、4 → 16/51、5 → 17/51、6 → 22/51、7 → 21〜23/51。
用語カード
用語からではなく、知っている数学から入る。「余り」から mod へ、「掛けて足す」から内積へ、「2 進数の筆算」から Gadget Decomposition へ。
| 用語 | 意味 | なぜその言葉か | どこで |
|---|---|---|---|
| ノイズ | 理科の測定に必ず混ざる小さな誤差と同じもの。LWE ではわざと足して方程式を解けなくする。ただし演算のたびに育ち、Δ/2 の枠を超えると四捨五入が隣の値に丸めて復号が壊れる | error(誤差)。LWE の E はこれ | スライド 13, 16〜17 |
| Δ(スケーリングファクター) | 「割り算の前に 10 倍しておくと小数の誤差に強い」の考え方。平文 m を Δ = q/p 倍して数の「上の桁」に置き、ノイズを「下の桁」に隔離する仕切り。復号は Δ で割って四捨五入 | scale(目盛り)を q に合わせて拡大する係数だから | スライド 16 |
| LWE | 中学の連立方程式は消去法で必ず解ける。ところが各式に小さな誤差を足すと、誤差を知らない人には解けなくなる — この難しさを土台にした問題(と、それが困難だという仮定) | Learning With Errors = 誤差(error)込みの式から秘密を学び取る問題 | スライド 13〜15 |
| RLWE | LWE の「数のベクトル」を丸ごと「多項式」に置き換えた版。整数の割り算に余りがあるように多項式にも余りがあり(mod xⁿ+1)、その余りの世界で同じ暗号を作る | R は Ring(環)= 足し算と掛け算が自由にできる集合。多項式の余りの世界がその例 | スライド 29 |
| negacyclic | 時計の針は 12 を超えると 0 に戻る(巡回)。mod xⁿ+1 の世界では 1 周すると xⁿ ≡ −1、つまり符号がマイナスになって戻ってくる | nega(負)+ cyclic(巡回)。「負になって巡回する」 | スライド 21, 35, 43 |
| テスト多項式 | 「関数の値を一覧表にして引く」を多項式でやる。答えの一覧を係数として並べておき、回転させて定数項を読むと表引きになる。v(x) と書く | 入力に応じた値を返す一覧表(テーブル)の役をする多項式だから | スライド 24〜26, 42 |
| Blind Rotation | 回転量 b − as は秘密鍵がないと計算できない。CMUX を鍵のビット数だけ重ねることで、回転量を誰にも見せないままテスト多項式を回す | blind = 目隠し。回す本人も回転量を知らない回転 | スライド 28, 35 |
| CMUX | 2 つの入力から 1 つを選ぶスイッチ(マルチプレクサ、MUX)の暗号版。選択ビットを RGSW 暗号文にして、どちらを選んだか見せずに選ぶ | MUX を暗号文(Ciphertext)の状態で行うから頭に C | スライド 28, 34 |
| External Product | 「ベクトル × 行列」のように種類の違うもの同士の掛け算。RGSW ⊡ RLWE → RLWE と、掛けても結果が元の種類(RLWE)に戻るのがミソ | 同種同士の積ではなく「外部の」相手との積だから。記号は ⊡ | スライド 33 |
| Gadget Decomposition | 19 = 10011₂ のような B 進数の桁分け。大きな係数を掛けるとノイズも大きく増えるので、小さな桁に分けてから掛けて増加を抑える | gadget(小道具)と呼ばれる行列 G(Gadget Matrix)を使った分解だから | スライド 30〜32 |
| RGSW | 「0 の RLWE 暗号文」を 2l 個並べたリストに、平文 × Gadget Matrix を足した形の暗号文。External Product の左側に置く部品 | GSW は提案者 Gentry・Sahai・Waters の頭文字。R は Ring 版の意味 | スライド 32 |
| Sample Extraction | 多項式の積の定数項を書き下すと「符号つきの掛けて足す」= 内積の形をしている。だから RLWE 暗号文から定数項だけを LWE 暗号文として抜き出せる | extract = 抽出。多項式の係数列から標本(sample)を 1 つ抜くから | スライド 36〜37 |
| Key Switching | 平文を変えずに、暗号文の鍵を s″ から元の s へ乗り換える。鍵 s で s″ を暗号化した表(ksk)を事前に渡しておけば、引き算だけで乗り換えられる | switch = 切り替え。鍵の乗り換え | スライド 38〜40 |
| Bootstrapping Key | 秘密鍵の各ビット sⱼ を、別の鍵 s′ で RGSW 暗号化したリスト。Blind Rotation の CMUX の選択ビットとして働く | bootstrap =「靴ひもで自分を持ち上げる」。暗号化された秘密鍵で暗号文自身を復号し直すのに使う鍵 | スライド 35 |
| Programmable Bootstrapping | ノイズのリセット(Bootstrapping)のついでに、テスト多項式の係数を f(m) に書き換えて関数 f も評価する | テスト多項式の係数で挙動を「プログラム」できる Bootstrapping だから | スライド 27, 42 |
| HomNAND | NAND を暗号文のまま評価する操作。1 − m₁ − m₂ の暗号文を作り、PBS の符号反転(negacyclic)で真理値表を再現する | Hom = Homomorphic(準同型)+ NAND | スライド 44〜45 |
| IND-CPA | 「2 つの平文のうちどちらを暗号化したか」を攻撃者が見分けられないという安全性。攻撃者に許されるのは、平文を選んで暗号化させることまで | INDistinguishability(識別不能)+ Chosen-Plaintext Attack(選択平文攻撃) | スライド 11, 48 |
| non-malleability(頑強性) | malleable は金属が「叩けば形を変えられる」こと。non-malleability = 暗号文を叩いて別の平文の暗号文に作り変えられないこと。FHE はその変形こそが機能なので、原理的に満たせない | 「展性(malleability)がない」= 叩いても変形しない = 頑強 | スライド 11, 48 |