知りたいのは平均だけ。でも普通に計算すると、誰かが全員の給与を見てしまう。個別の給与は誰にも見せず、平均だけを知りたい。
宿題ではここ: Part A はこの「見せずに足す・掛ける」を作る。Part B は「見せずに AND を取る」を作る。
Q. 「集めて計算する」の何が問題か。(集めた人が全部見える)
Advanced Cryptography Program 2026 / Week 2 自習ノート
このノートは講義スライド「Week 2: MPC」55 枚と同じ順番で進みます。
1 カード = スライド 1〜2 枚 = 主張 1 つ。宿題 toy-mpc の 8 個の関数が
スライドのどこに当たるかを、毎回「宿題ではここ」で示します。
数はすべて 1〜2 桁(mod 11、mod 23、1 bit)。章ごとの「やってみる」を紙で解いてから答えを開いてください。 Shamir を配って集めるラボと、GMW の AND を 1 ステップずつ回すラボ、検問クイズはそのまま残してあります。
出発点
1 枚のカードがスライド 1〜2 枚に対応し、主張は 1 つ、式か例は 1 個です。
宿題 toy-mpc の 8 個の関数(Part A: share / reconstruct / add_shares / beaver_multiply、
Part B: ot_receiver_request / ot_sender_encrypt / ot_receiver_decrypt / gmw_and)が
スライドのどこに当たるかを「宿題ではここ」で毎回示します。数はすべて 1〜2 桁です。
読み方
上から順に、1 カードずつ。カード末尾の Q. に自分の言葉で答えられたら次へ。章の終わりの「やってみる」は紙と鉛筆で解いてから答えを開いてください。
Week 3・4 との関係
MPC は「秘密を分けて、集めずに計算する」。Week 3 の Schnorr は「秘密を見せずに、知っていると納得させる」。どちらも「乱数で覆う」で秘密を守る(share の乱数、Beaver の a・b、nonce の r)。使い回した瞬間に壊れるのも同じです。
この頁の構成
対話ドリル — MPC を、1 行ずつ自分の手で
宿題 toy-mpc の Part A / Part B を、関数を書く前に手で 1 回通します。1 行打つ → 出た値を貼る → その値を指す 1〜2 文が開く。「足し算は無料、掛け算だけ通信が要る」が、説明ではなく数で入ります。
準備(1 行だけ)
python3
ターミナルで python3 と打つだけです。宿題のファイルは要りません(mod 7 と 1 bit の計算だけ)。行頭に空白を入れると IndentationError になるので、貼るときは行頭から。
ドリル 1 — 分けて、戻して、そのまま計算する
share を作る → 足すと戻る → 同じ 2 個から別の秘密 → share のまま足す・定数倍 → 定数足しだけ壊れる → 1 人だけ足す → 掛け算はできない。10 行。
ドリル 2 — 掛け算を 1 ラウンドで買う(Beaver)と、GMW の AND
三つ組 → d, e を開示 → なぜ見せてよいか → 組み立て → d·e の罠 → XOR share → AND の交差項 → OT で運ぶ → コスト表。10 行。
スライド 2〜13 / MPC は何をしたい技術か・問題設定
知りたいのは平均だけ。でも普通に計算すると、誰かが全員の給与を見てしまう。個別の給与は誰にも見せず、平均だけを知りたい。
宿題ではここ: Part A はこの「見せずに足す・掛ける」を作る。Part B は「見せずに AND を取る」を作る。
Q. 「集めて計算する」の何が問題か。(集めた人が全部見える)
MPC は、複数のパーティが秘密の値を持つとき、互いに秘匿したまま共同で計算する技術。ZK は「秘密を明かさずに主張が正しいと証明する」、MPC は「秘密入力を持つ複数の人が、集めずに共同で計算する」。
Q. 給与平均は ZK と MPC どちらの問題か。(誰も答えを持っていない → 計算が要る → MPC)
信頼できる第三者 T に全員が入力を送り、T が y だけ返せば簡単。でも T には全部の入力が集まる(漏らす・目的外利用・結託・改ざん)。MPC は T を置かず、T がやるはずだった計算をパーティ間のプロトコルで実現する。
覚える 1 点: MPC = 「T の代わり」。理想世界の T ができることを超えるものではない(次のカードの「保証しないこと」に効く)。
まず押さえる 4 つ: 誰が参加する(party)/各 party が何を持つ(input)/何を計算する(function)/誰が結果を受け取る(output) 性質: Correctness(仕様どおりの出力)/Privacy(出力から分かる以上は漏れない)/Robustness(止まる・嘘をつく party がいてもどうなるか)
Privacy の判定法: ある party がプロトコル中に見たものが「自分の入力と最終出力だけから作れそう」なら漏れていない — Week 3 の simulator と同じ考え方。
「入力が漏れない」を強く言いすぎない。出力そのものから分かる情報は隠せない(平均 1000 万で、自分と他 1 人を知っていれば残り 1 人が分かる)。入力が本当に正しいかも別問題(嘘の給与、資格の無い投票)→ コミットメント・ZK・監査で別途。
Q. 3 人の平均を公開してよいか、を決めるのは誰か。(プロトコルではなく設計者。出力を公開すると決めた時点で漏れる分は漏れる)
やってみる — 紙と鉛筆で。答えは開くまで隠れる
合計 = 30 × 3 = 90。90 − 20 − 40 = 30 万。 失敗ではない。出力(平均)から計算できる情報は、出力を公開すると決めた時点で漏れる(output leakage)。 防ぐなら「平均を公開しない」「人数を増やす」「差分プライバシーでぼかす」など設計側の話。
前者は ZK(証明。答えは prover が持っている)。後者は MPC(計算。答えは誰も持っていない)。 MPC の入力の正しさを保証したければ ZK を組み合わせる(スライド 13)。
スライド 15〜23 / 秘密分散
秘密 x を、ランダム性を使って作った share に分けて配る。少数の share では x が分からず、決められた数(k 個)以上集めると復元できる(k-of-n)。Sharing と Reconstruct の 2 つのアルゴリズム。
x = share₁ + share₂ + … + shareₙ (mod p) share₁..shareₙ₋₁ はランダム、shareₙ = x − (残り全部) 講義: p = 101, x = 42 → 17, 60, 66。 17 + 60 + 66 = 143 = 101 + 42 ✓ 1 つの share(17)だけ見ても 42 とは分からない
宿題ではここ: share(secret, randomness, modulus) — randomness が share₁..shareₙ₋₁、最後の 1 つを引き算で作る。reconstruct(shares, modulus) — 全部足して % modulus。結果は必ず 0..p−1 に正規化(Week 3 の逆元と同じ罠。負の share のまま返すと落ちる)。
Q. なぜ最後の 1 つだけ計算で決めるのか。(合計が x になるように帳尻を合わせる。他はランダムだから share を見ても x が分からない)
[x + y] = ([x]₁ + [y]₁, [x]₂ + [y]₂, [x]₃ + [y]₃) 各 party が自分の share を足すだけ。通信なし。復元しない
宿題ではここ: add_shares(left, right, modulus)。party 数が違えば拒否する。合計・平均・線形変換が MPC と相性がよい理由はこの 1 行。
加法的は n-of-n(1 個欠けると復元できない)。k-of-n にしたい → 多項式。1 点では直線が決まらず、2 点で決まる(中 2)。k 個の点で次数 k−1 の多項式が決まる。secret を切片 q(0) に置き、party i に q(i) を渡す。
p = 11, secret = 5, k = 2: q(z) = 5 + 3z(3 はランダム) → P1: q(1) = 8, P2: q(2) = 11 ≡ 0, P3: q(3) = 14 ≡ 3 2 点 (1, 8), (2, 0) から: 傾き (0 − 8)/(2 − 1) = −8 ≡ 3、切片 8 − 3·1 = 5 → secret = 5 ✓ 1 点 (1, 8) だけでは切片は何にでもなれる
宿題には出ない(宿題は加法的)。Week 3 の「補間」と Week 4 の「表を多項式にする」の入口はここ。
加法的: 足すと戻る、n-of-n、加減は軽い、乗算は追加処理 Shamir: 点から多項式を戻す、k-of-n、加減は軽い、乗算は追加処理 流れ: secret → share → share のまま計算 → output share → 許可された output だけ opening → output 現実の値(給与・小数・負数)は有限体や固定小数点に encode してから share する
覚える 1 点: opening(share を集めて復元)は「漏らしてよい値」にだけ行う。設計は「何を open するか」を決めること。宿題の Beaver で open するのは d と e の 2 値だけ(採点器が数える)。
やってみる — 紙と鉛筆で。答えは開くまで隠れる
share₃ = 7 − 3 − 9 = −5 ≡ 6 (mod 11)。検算 3 + 9 + 6 = 18 = 11 + 7 ✓ (宿題の share(7, [3, 9], 11) は [3, 9, 6] を返す。−5 のまま返すと落ちる)
y = 2 + 5 + 4 = 11 ≡ 0。 足した share は [5, 14 ≡ 3, 10]。復元すると 5 + 3 + 10 = 18 ≡ 7 = x + y = 7 + 0 ✓。通信なしで足し算ができた。
言えない。(1, 8) を通る直線は傾きごとに 11 本あり、切片 q(0) は 0〜10 のどれにもなれる。 2 点目 (2, 0) が来て初めて傾き 3、切片 5 と決まる。「k − 1 個では何も分からない」の意味。
スライド 25〜32 / MPC での演算
加算・減算・公開値による定数倍は、各 party が自分の share にローカルで行える。通信なし。
注意(宿題の落とし穴): 公開値 c を「足す」とき、全 party が自分の share に c を足すと x + n·c になってしまう。1 つの share にだけ足す。Beaver の公開項 d·e も同じ(次のカード)。
x·y = ([x]₁ + [x]₂)([y]₁ + [y]₂) = [x]₁[y]₁ + [x]₁[y]₂ + [x]₂[y]₁ + [x]₂[y]₂
↑ 交差項: party 1 の share と party 2 の share の両方が要る → 誰も 1 人では計算できない
Q. 足し算では交差項が出なかったのはなぜか。((a+b)+(c+d) は並べ替えるだけ。積は展開すると混ざる — 中学の展開公式)
前処理で、ランダムな a, b と c = a·b の share を配っておく: [a], [b], [c]
本番: [d] = [x] − [a], [e] = [y] − [b] を作り、d と e を open(公開) ← a, b はランダムなので d, e から x, y は分からない
[x·y] = [c] + d·[b] + e·[a] + d·e (d·e は公開値なので 1 つの share にだけ足す)
p = 11, x = 3, y = 4, a = 5, b = 2, c = 10: d = 3 − 5 ≡ 9, e = 4 − 2 = 2
x·y = 10 + 9·2 + 2·5 + 9·2 = 10 + 18 + 10 + 18 = 56 ≡ 1 = 12 mod 11 ✓(3·4 = 12)
宿題ではここ: beaver_multiply(x_shares, y_shares, triple, modulus)。open するのは d と e の 2 値だけ(採点器 test_beaver_multiply が「公開したのは 2 値か」を数える)。triple は 1 回の乗算に 1 個。使い回すと d₁ − d₂ = x₁ − x₂ が漏れる(Week 3 の nonce と同じ)。
Q. なぜ d を公開してよいのか。(a がランダムで一度きりだから。Week 3 の s = r + e·x で r が x を覆うのと同じ)
Arithmetic: ゲートは + と ×。合計・平均・内積が得意。比較は重い Boolean: ゲートは AND / XOR / NOT。比較・分岐・ビット演算が得意。bit 数だけ回路が大きい 秘密に依存する if は書けない → [result] = [cond]·[x] + (1 − [cond])·0 のように「選択」に直す(Week 1 の回路と同じ発想)
宿題ではここ: Part A が Arithmetic(+ と Beaver の ×)、Part B が Boolean(XOR は無料、AND に OT)。
重い: 比較・最大最小・ソート・分岐・秘密での除算・秘密インデックスの配列アクセス(全部「ビット」か「非線形」が要る) コストの軸: 通信量/通信ラウンド数/非線形ゲート数(乗算・AND・比較)/party 数/敵対モデル
覚える 1 点: 普通のコードで同じ 3 行に見えても、MPC では「乗算・AND が何個か」で速さが決まる。
やってみる — 紙と鉛筆で。答えは開くまで隠れる
x = 3, y = 4, a = 5, b = 2, c = 10。a·b = 10 ✓(triple は正しい)
[d] = [1 − 4, 2 − 1] = [−3 ≡ 8, 1] → d = 9。 [e] = [3 − 0, 1 − 2] = [3, −1 ≡ 10] → e = 13 ≡ 2 (d = x − a = 3 − 5 ≡ 9、e = 4 − 2 = 2 と一致)
party 1: 7 + 9·0 + 2·4 + 9·2 = 7 + 0 + 8 + 18 = 33 ≡ 0 party 2: 3 + 9·2 + 2·1 + 0 = 3 + 18 + 2 = 23 ≡ 1 復元 0 + 1 = 1 = x·y = 12 mod 11 ✓
1 + 18 = 19 ≡ 8 ≠ 1。公開値を全員が足すと n 倍入る(スライド 25 の注意)。旧版ノートの「壊した実測」で採点器が落としていたのがこれ。
d₁ = x₁ − a、d₂ = x₂ − a → d₁ − d₂ = x₁ − x₂。秘密入力どうしの差が誰にでも分かる。 Week 3 で nonce r を使い回すと s₁ − s₂ = (e₁ − e₂)·x から x が出るのと同じ形。
スライド 34〜43 / セキュリティモデルと主要プロトコル
semi-honest は手順は守るが好奇心はある(見たものから推論する)。malicious は嘘の share・無応答・結託など何でもする。malicious に耐えるには検証・MAC・ZK が要り、コストが上がる。
宿題ではここ: 課題は semi-honest 前提。だから「手順どおりに動く相手に、見えてしまう値は何か」だけを気にする(Beaver の d, e、OT の request)。
honest majority: t < n/2(5 人中 2 人まで)→ Shamir 系が使いやすい dishonest majority: t ≥ n/2(2 人で片方が悪い)→ 強い道具・前処理が要る(SPDZ) 出力保証: abort(止まってよい)/fairness(一部だけ得をしない)/guaranteed output delivery(正直な人は必ず出力を得る)
generic: 関数を回路にして評価(Yao / GMW / BGW / SPDZ) special-purpose: PSI、threshold signature、MPC wallet、secure aggregation Yao(2 者、Boolean、garbling + OT)/GMW(OT で Boolean 回路)/BGW(n 者、honest majority、Shamir)/SPDZ(dishonest majority、malicious、Beaver + MAC)
覚える 1 点: 名前ではなく「party 数・敵対モデル・回路表現・前処理の有無」のセットで見る。宿題の Part B は GMW 型。
sender: m₀, m₁ receiver: 選択ビット b receiver が得るもの: m_b だけ 隠すもの: receiver は選ばなかった m₁₋b を知らない/sender は b を知らない
宿題ではここ: Part B の 3 関数が OT の 3 手。OT 単体で何かを計算するのではなく、AND を作る部品。
1. sender: 乱数 a、A = g^a を送る
2. receiver: 乱数 r、 B = g^r(b = 0)または B = A·g^r(b = 1)を送る ← どちらもランダムに見えるので sender は b を区別できない
3. sender: k₀ = H(B^a), k₁ = H((B/A)^a)、 C₀ = m₀ ⊕ k₀, C₁ = m₁ ⊕ k₁ を送る
4. receiver: 計算できる鍵は k = H(A^r) = H(g^{ar}) = k_b だけ → m_b = C_b ⊕ k
mod 23, g = 2, a = 3 → A = 8。 receiver b = 1, r = 4 → B = 8·2⁴ = 128 ≡ 13
sender: B^a = 13³ ≡ 12 → k₀ = H(12)、 (B/A)^a = (13·8⁻¹)³ = (13·3)³ = 16³ ≡ 2 → k₁ = H(2)
receiver: A^r = 8⁴ ≡ 2 → k = H(2) = k₁ ✓ b = 1 の方だけ開く。k₀ = H(12) は作れない(12 を出すには a が要る)
宿題ではここ: ot_receiver_request(sender_public, choice, receiver_secret) = 手 2、ot_sender_encrypt(sender_secret, request, m0, m1) = 手 3、ot_receiver_decrypt(...) = 手 4。receiver の r は 0 を含む範囲から一様に(0 を除くと b = 0 と b = 1 で B の分布がずれる — 旧版ノートの分布実測)。
x = x₁ ⊕ x₂, y = y₁ ⊕ y₂ (各自 1 bit ずつ持つ) x·y = x₁y₁ ⊕ x₁y₂ ⊕ x₂y₁ ⊕ x₂y₂ ← x₁y₁ は P1、x₂y₂ は P2 が 1 人で計算できる。交差項 x₁y₂ と x₂y₁ が要 OT OT①: P1 が sender、乱数 r₁、m₀ = r₁、m₁ = r₁ ⊕ x₁。P2 は y₂ で選ぶ → r₁ ⊕ x₁·y₂ を得る(x₁ は分からない) OT②: 役を交代。P2 が乱数 r₂、P1 は y₁ で選び r₂ ⊕ x₂·y₁ を得る z₁ = x₁y₁ ⊕ r₁ ⊕ r₂(の P1 側), z₂ = x₂y₂ ⊕ (r₁ ⊕ x₁y₂) ⊕ (r₂ ⊕ x₂y₁) → z₁ ⊕ z₂ = x·y 例: x₁=1, x₂=0(x=1), y₁=1, y₂=1(y=0), r₁=1, r₂=0 OT①: P2 は r₁ ⊕ x₁·y₂ = 1 ⊕ 1 = 0 OT②: P1 は r₂ ⊕ x₂·y₁ = 0 ⊕ 0 = 0 z₁ = 1·1 ⊕ 1 ⊕ 0 = 0, z₂ = 0·1 ⊕ 0 ⊕ 0 = 0 → z = 0 = 1·0 ✓
宿題ではここ: gmw_and(x_shares, y_shares, masks, ot_secrets)。OT をちょうど 2 回使う(採点器が数える)。XOR は xor_shares(提供済み)でローカル。
Yao: 2 者。回路の各 wire の 0/1 にランダムなラベルを付け、一方が回路を「暗号化した表」にし、他方がラベルだけで評価する。自分の入力ラベルは OT で受け取る。SPDZ: offline で Beaver triple を作り、online は速い。share に MAC を付けて malicious を検出。
やってみる — 紙と鉛筆で。答えは開くまで隠れる
B = g^r = 2⁴ = 16。 sender: B^a = 16³ = 4096 ≡ 2 → k₀ = H(2)、(B/A)^a = (16·3)³ = 48³ ≡ 2³ = 8 → k₁ = H(8)。 receiver: A^r = 8⁴ ≡ 2 → k = H(2) = k₀ ✓。b = 0 なら m₀ だけ開く。 (b = 1 のときも B は 13 で「ランダムに見える」。16 か 13 かで sender は b を当てられない)
OT①: P2 は r₁ ⊕ x₁·y₂ = 1 ⊕ 0 = 1 OT②: P1 は r₂ ⊕ x₂·y₁ = 1 ⊕ 0 = 1 z₁ = x₁y₁ ⊕ r₁ ⊕ r₂ = 0 ⊕ 1 ⊕ 1 = 0, z₂ = x₂y₂ ⊕ 1 ⊕ 1 = 1 ⊕ 0 = 1 → z = 1 = 1·1 ✓
x ⊕ y = (x₁ ⊕ y₁) ⊕ (x₂ ⊕ y₂) と並べ替えられる(各自ローカル)。 x·y は展開すると x₁y₂, x₂y₁ の交差項が出て、2 人の bit が両方要る → 誰か 1 人では作れない。 Arithmetic の「加算は無料、乗算は通信」と同じ理由(Boolean では XOR が加算、AND が乗算)。
スライド 45〜55 / 応用例・比較・まとめ
型: party/input/function/output/hidden/cost/type(generic か special-purpose か) Fireblocks MPC wallet(2-of-2 の署名。鍵を 1 か所に置かない。special-purpose) Meta Private Data Lookup(漏洩パスワード集合との照合。PSI。Meta は結果を得ない) Boston 賃金格差(複数企業の給与を集めずに統計。出力からの推測は DP など別設計) デンマーク砂糖大根入札(2008、秘密入札の double auction。初期の実用 MPC)
覚える 1 点: どの事例も「MPC だけでは解決しないこと」(入力の正しさ、出力からの推測、可用性、運用)が半分を占める。
ZK: 正しいことを証明する(prover が計算、witness を隠す、proof で保証) FHE: 暗号化したまま計算する(evaluator 1 者、データと中間値を隠す) MPC: 入力を集めず共同計算(複数 party、各入力を隠す、プロトコルと敵対モデル次第) TEE: ハードウェア内で計算(enclave を信頼) DP: 出力から個人を推測しにくくする(統計的)
Q. 「1 者がデータを預けて計算だけ委託したい」は?(FHE)「複数者が持ち寄る」は?(MPC)「条件を満たすことだけ示したい」は?(ZK)
party(誰が、いつオンラインか)/function(非線形が多すぎないか)/security(semi-honest でよいか)/cost(通信・ラウンド・前処理) output(出力から漏れすぎないか)/representation(有限体に落とせるか)/operation(運用・監査の責任分界)/そもそも MPC である必要があるか
トレーダーは署名済み注文を share にして MPC ノード委員会へ。委員会が share のままマッチング。約定だけを決済コントラクトへ(オンチェーンでは公開)。設計するのは: ノード構成と adversary、価格・数量・優先順位・部分約定、何を output として開示するか、署名・nonce・取消・abort、MPC とオンチェーンの分担。
型で書く: party = トレーダー + 委員会 / input = 注文 / function = マッチング / output = 約定 / hidden = 未約定の注文 / cost = 比較(価格の大小 — 重い)。
宿題の 8 関数
| 関数 | スライド | 学校で習ったこと | 小さい数の例 | 落ちやすい所 |
|---|---|---|---|---|
| share | 16 | 余り(中 1)、「合計が決まっているとき最後の 1 個は引き算で決まる」 | 7 = 3 + 9 + 6 (mod 11) | 最後の share を 0..p−1 に正規化 |
| reconstruct | 16 | 足し算 | 3 + 9 + 6 = 18 ≡ 7 | % modulus 忘れ |
| add_shares | 17, 25 | 足し算の順序を入れ替えてよい(結合・交換法則、中 1) | [1,2] + [3,1] = [4,3] → 7 | party 数が違うのを拒否 |
| beaver_multiply | 27〜28 | 展開公式 (x−a+a)(y−b+b)(中 3) | d = 9, e = 2 → x·y = 1 | d·e を全員が足す/open が 2 値を超える |
| ot_receiver_request | 41 | 指数法則 (g^a)^r = (g^r)^a(中 2 の累乗) | B = 13(b=1)/ 16(b=0) | r の範囲に 0 を含める |
| ot_sender_encrypt | 41 | 同上、⊕ は「桁上がりの無い足し算」 | k₀ = H(12), k₁ = H(2) | k₁ の底は B/A |
| ot_receiver_decrypt | 41 | 同上 | A^r = 2 → m₁ だけ開く | 不正な入力を拒否 |
| gmw_and | 26, 40〜41 | 展開公式(XOR 版) | z₁ ⊕ z₂ = x·y | OT をちょうど 2 回 |
提出前の自己チェック — 6 問、自分の言葉で
(1) share の最後の 1 個だけ計算で決める理由。 (2) 足し算に通信が要らない理由と、掛け算に要る理由(交差項)。 (3) Beaver で d, e を公開してよい理由と、triple を使い回してはいけない理由。 (4) 公開値 d·e を 1 人だけが足す理由。 (5) OT で sender が b を当てられない理由と、receiver が片方しか開けない理由。 (6) GMW の AND で OT が 2 回要る理由。
用語集
| 用語 | 意味 | なぜその言葉か | 宿題では |
|---|---|---|---|
| MPC / SMPC | 複数の参加者が入力を隠したまま共同で計算する技術 | (Secure) Multi-Party Computation。party = 参加主体 | 課題名 toy-mpc |
| party | 計算に参加する 1 主体(人・企業・サーバ) | 「当事者」 | share のリストの各要素 |
| Trusted Third Party (TTP) | 全員の入力を預かって計算する、信頼できる仮想の第三者 | MPC はこの T を置かずに同じ結果を得る | — |
| 秘密分散(secret sharing) | 秘密を share に分けて配る。少数では分からず、決められた数で復元 | share = 分け前・持ち分 | share / reconstruct |
| 加法的秘密分散 | 足すと戻る分け方(n-of-n) | additive = 足し算の | Part A 全体 |
| Shamir 秘密分散 | 多項式の切片に秘密を置き、点を配る(k-of-n) | Adi Shamir(人名、1979)。RSA の S | (出ない) |
| k-of-n / threshold | n 個中 k 個で復元、k−1 個では分からない | threshold = しきい値 | 加法的は n-of-n |
| opening / open | share を集めて値を復元し、公開すること | 「開ける」。Week 3 の commitment の open と同じ語感 | Beaver の d, e |
| Beaver triple | 前処理で配る (a, b, c = a·b) の share。1 回の乗算に 1 個 | Donald Beaver(人名、1991)。triple = 3 つ組 | beaver_multiply の triple 引数 |
| マスク(mask) | 秘密を覆う使い捨ての乱数 | 「覆面」。d = x − a の a | a, b、GMW の r₁, r₂ |
| 線形(linear)演算 | 足し算・引き算・公開定数倍。share のままローカルでできる | 1 次式の形。LSS = Linear Secret Sharing | add_shares |
| Arithmetic / Boolean circuit | + と × のゲート/AND・XOR・NOT のゲート | arithmetic = 算術、Boolean = ブール(論理) | Part A / Part B |
| XOR share | bit を x = x₁ ⊕ x₂ に分ける。XOR はローカル、AND は通信 | ⊕ = 排他的論理和(桁上がりの無い足し算 = mod 2 の足し算) | gmw_and の入力 |
| 紛失通信(OT) | 2 つのうち選んだ 1 つだけを、選択を知られずに受け取る | Oblivious Transfer。oblivious = 気づかない(sender は選択に気づかない) | ot_* の 3 関数 |
| GMW | OT で Boolean 回路を評価するプロトコル | Goldreich・Micali・Wigderson(人名、1987) | gmw_and |
| Yao / Garbled Circuit | 回路を暗号化した表にして 2 者で評価 | Andrew Yao(人名、1982)。garble = ごちゃ混ぜにする | (出ない) |
| SPDZ | dishonest majority・malicious 向け。offline/online + MAC | 著者 4 人の頭文字(Smart・Pastro・Damgård・Zakarias)。「スピーズ」 | (出ない) |
| semi-honest / malicious | 手順は守るが覗く/何でもする | 半分正直/悪意 | 課題は semi-honest |
| honest / dishonest majority | 過半数が正直(t < n/2)/そうでない | majority = 過半数 | — |
| abort / fairness / GOD | 止まってよい/一部だけ得をしない/正直者は必ず出力を得る | guaranteed output delivery | — |
| output leakage | 出力そのものから分かってしまう情報 | leak = 漏れる。プロトコルの失敗ではない | 平均の例 |
| generic / special-purpose | 任意の関数を回路で/用途特化(PSI、threshold signature) | 汎用/専用 | — |
| PSI | 2 者の集合の共通部分だけを知る | Private Set Intersection | Meta の事例 |
| DP | 出力にノイズを入れて個人の寄与を推測しにくくする | Differential Privacy(差分プライバシー) | Boston の事例 |
ラボ
秘密と乱数を選ぶとシェアが計算されます。誰のシェアを集めるかを切り替えて、 何人分あれば秘密が決まるのかを自分で確かめてください。
—
集めたシェアと辻褄が合う秘密
計算の中身
—
ラボ
4 つのシェアビットと 2 つのマスクを選ぶと、OT 2 回込みの実行トレースが出ます。 「P0 から見えた値」「P1 から見えた値」に注目してください—— マスクのおかげで、どちらも相手のシェアを 1 ビットも学べていません。
OT の secret は課題のテストと同じ固定値(セッション 01: a=3, b=4 / セッション 10: a=5, b=6)。
—
実行トレース(OT の中身込み)
—
検問クイズ
読んだだけでは定着しない。ここではあなたが監査官の席に座り、 シェアの計算・公開してよい値・OT の振る舞いを審査する。問題は毎回その場で生成され、 判定は上のラボと同じ計算器が行う。3 回誤判定したら、その回は終了。
検問モードを選ぶ
誤答するとその場で解説が出る。分野を絞って弱点だけ潰すこともできる。
持ち帰り
① 「t 人以下では情報がゼロ」は、なぜ「難しい」ではなく「不可能」なのか。 ラボで 11 通り全部に辻褄の合う乱数が存在することを見れば、計算量の話ではないと分かります。
② 加算が無料なのは、秘密分散が線形だから。ではなぜ線形だと無料になるのか。
f(i) + g(i) = (f+g)(i) という 1 行がすべてでした。
③ 乗算で壊れるのは「次数」と「乱数の独立性」の 2 つ。 どちらか片方だけ直しても足りない、という理解が要ります。
④ Beaver は乗算を消したのではなく、時間をずらしただけ。 前処理でいくつ三つ組を用意するかが、そのまま実行可能な乗算の回数になります。
⑤ 「答えが正しい」と「開きすぎていない」は別の性質。 x を直接開く Beaver 実装は、積のテストに全部通って、開示回数の監査だけで落ちました。 MPC のレビューで見るべきは出力ではなく、実行中に何が公開されたかです。
⑥ 数の乗算とビットの AND は、同じ場所に同じ壁がある。 線形(加算・XOR)はタダ、非線形(乗算・AND)だけが通信を要求する。 数の世界は Beaver、ビットの世界は OT——道具は違っても、 「クロス項を、マスクして運ぶ」という解き方は同じでした。
そして Week 1 の問いはここでも生きます ——
「この値を縛っているものは何か」。
MPC では「この値を隠しているのは何か」に姿を変えます。
d = x − a が安全なのは a がランダムだから。
OT の request が安全なのは b が 0 込みの一様乱数だから。
隠している乱数の条件が 1 つ欠けた瞬間に、どちらも壊れます。
付録
上のカード列で骨組みが入ったあとで、細部を足したいときに開いてください。Shamir の 1 行ずつの導出、壊した採点結果(公開項 d·e を全員が足す/開示 4 回/triple 再利用)、OT の分布実測はここにあります。
出発点
3 人が自分の年収を持っています。平均だけ知りたい。でも自分の額は誰にも見せたくない。
素朴な手はいくつかありますが、どれも壊れます。
案 1: 信頼できる人に集める。その人は全員の額を知ります。 信頼の一点集中が残るだけで、問題は消えていません。
案 2: 順番に足していく。最初の人が自分の額を言って次に回す—— 最初の人の額が 2 人目に丸見えです。
欲しいのは「誰も他人の値を知らないまま、全員が結果だけを得る」仕組み。 これが MPC(Multi-Party Computation、秘密計算)です。
Week 1 とのつながり
MPC も ZK も、計算を算術回路に翻訳してから暗号をかけます。
違うのは組み方です。ZK は検証の形——prover が答えを主張し、回路は嘘を禁じるだけ。 MPC は計算の形——主張してくれる人がいないので、値そのものを計算します。
そして Week 1 で見た「コストは乗算の数で決まる」が、MPC でもそのまま成り立ちます。 ただし理由はまったく別で、そこが今週の山場です。
公式課題は toy-mpc。前半(Part A)が数の乗算を Beaver 三つ組で、
後半(Part B)がビットの AND を紛失通信(OT)で解きます。
このノートの構成はその対比に合わせてあります。
この頁の地図
中核
アイデアは中学校で習った 1 つの事実だけです。そこから積み上げます。
平面に点が 2 つあれば、その両方を通る直線はただ 1 本です。 逆に点が 1 つだけなら、そこを通る直線は無数にあります。
この非対称がすべてです。 2 つ集めれば決まる。1 つでは何も決まらない。 「決まらない」を「分からない」に使うのが秘密分散の発想です。
秘密の値を s とします。乱数 a を 1 つ用意して、次の直線を作ります。
f(x) = s + a·x
ここで x = 0 を入れると f(0) = s + a·0 = s。
秘密は「x = 0 のときの値」として埋め込まれています。
なぜ x = 0 に置くのか: 配るのは x = 1, 2, 3, … の値だけにするためです。
0 番の値だけは誰にも渡さない。だから直線が復元できて初めて秘密にたどり着きます。
参加者 1 には f(1)、参加者 2 には f(2)、参加者 3 には f(3) を渡す。
この値をシェアと呼びます。
計算はすべて有限体 F_p の上で行います(Week 1 と同じ世界です)。
以下では見やすさのため p = 11 を使います。
なぜ有限体なのか: 実数のままだと、シェアの大きさから秘密の大きさが推測できてしまいます (切片が大きければ値も大きい、など)。mod p で巻いてしまえば大小の手がかりが消えます。 Week 1 で見た「順序が存在しない」性質が、ここでは利点として効いています。
秘密 s = 3、乱数 a = 5、p = 11 とします。
f(x) = 3 + 5x (mod 11)
f(1) = 3 + 5 = 8
f(2) = 3 + 10 = 13 = 11 + 2 → 2
f(3) = 3 + 15 = 18 = 11 + 7 → 7
配るのは 8, 2, 7 の 3 つ。
どれも秘密の 3 とは似ても似つかない数字です。
参加者 1(x=1, y=8)と参加者 2(x=2, y=2)が集まったとします。
傾き a = (y₂ − y₁) ÷ (x₂ − x₁) = (2 − 8) ÷ (2 − 1) = −6 ÷ 1 = −6 = 5 (mod 11)
切片 s = y₁ − a·x₁ = 8 − 5·1 = 3 ✓
−6 がなぜ 5 なのか: mod 11 の世界では −6 + 11 = 5。
Week 1 でやった「引き算は逆元を足すこと」がここでも効いています。
割り算も同じで、÷1 は 1 の逆数 1 を掛けること。
もし分母が 1 でなければ、逆数を求めてから掛けます。
参加者 1 だけが y = 8 を持っているとします。この人にとって、
秘密 s は 0 から 10 のどれでもありえます。
s = 0 なら a = 8(0 + 8·1 = 8 ✓)
s = 1 なら a = 7(1 + 7·1 = 8 ✓)
s = 2 なら a = 6(2 + 6·1 = 8 ✓)
…… 11 通りすべてに、辻褄の合う a が 1 つずつ存在する
これが「情報がゼロ」の正確な意味です。 シェアを見る前の推測(11 通りが等確率)と、見た後の推測がまったく同じ。 1 ビットも減っていない。「解読が難しい」ではなく「原理的に不可能」です。 下のラボで実際に 11 通り全部を並べて確認できます。
直線(次数 1)なら 2 点で決まりました。放物線(次数 2)なら 3 点必要です。 一般に次数 t の多項式は t+1 点で決まる。
f(x) = s + a₁x + a₂x² + … + a_t x^t → 復元に必要な人数は t + 1
設計の自由度がここにあります。 「5 人中 3 人集まれば復元できる」なら次数 2 を使う。 t 人以下では、さっきと同じ理屈で情報がゼロのままです。
課題で使った形
Shamir は「n 人中 t+1 人で復元」という閾値が選べる高機能版です。
課題 toy-mpc が使うのは、もっと単純な加法的秘密分散——
全員そろわないと戻らない代わりに、作り方が足し算だけで済みます。
秘密 s を n 人に分けるには、乱数を n−1 個引いて、
それをそのまま最初の n−1 人のシェアにします。最後の 1 人だけが帳尻を合わせます。
share₁ = r₁, …, shareₙ₋₁ = rₙ₋₁
shareₙ = s − (r₁ + … + rₙ₋₁) (mod p)
復元は全シェアの合計。p = 67 で s = 42 を乱数
[5, 11] で分けると、シェアは [5, 11, 26]
(42 − 16 = 26)。足せば 42 に戻ります。
「n−1 人では情報ゼロ」の理屈は Shamir とまったく同じです。 手元のシェアがどんな値でも、どんな秘密とも辻褄の合う「残り 1 枚」が必ず存在するので、 候補は 1 つも減りません。
実測 — 正規化を忘れると採点器に落とされる
シェアは 0..p−1 に丸めた「体の元」でなければいけません。
share(-3, [70, -2], 67) の正解は [3, 65, 63]。
mod を取らない実装はこう落ちます。
AssertionError: Lists differ: [70, -2, -71] != [3, 65, 63]
値としては同じ直線上にあっても、表現がそろっていないと他の関数と噛み合いません。 Week 1 で「体の元として正規化する」と言っていたことが、ここで初めて実務の制約になります。
どちらを使うか
閾値が要るなら Shamir、全員参加でよいなら加法的。
これから見る Beaver 乗算はどちらの上でも同じ形で動きます。課題が加法的を選んだのは、 MPC の本質(何を公開してよいか)に集中するために、復元の仕組みを最小にしたからです。
性質
ここから MPC の本題です。まず「タダでできること」から。
2 つの秘密 s と s' を、それぞれ別の直線に隠したとします。
f(x) = s + a·x g(x) = s' + b·x
参加者 i は f(i) と g(i) を持っています。
この 2 つを手元で足してみます。
f(i) + g(i)
= (s + a·i) + (s' + b·i)
= (s + s') + (a + b)·i
いま出てきた式を h(x) = (s+s') + (a+b)·x と置くと、
これはちょうど h(i) です。
つまり各自が手元で足しただけで、「s + s' を秘密とする直線」のシェアになっている。 誰とも通信していません。
確かめる
s = 3(f = 3 + 5x)と s' = 4(g = 4 + 2x)、p = 11
参加者 1: f(1)=8, g(1)=6 → 8 + 6 = 14 = 3
参加者 2: f(2)=2, g(2)=8 → 2 + 8 = 10 = 10
この 2 点 (1,3), (2,10) から復元すると……
傾き = (10−3) ÷ (2−1) = 7
切片 = 3 − 7·1 = −4 = 7
そして s + s' = 3 + 4 = 7 ✓
定数倍も同じ理由でタダ
c·f(i) = c·s + c·a·i なので、秘密を c 倍した直線のシェアになります。
引き算も定数倍(−1 倍)と加算の組み合わせなので無料です。
線形なことは、全部ローカルで完結する。
壁
同じことを掛け算でやると、途中で破綻します。どこで破綻するのかを式で見ます。
参加者 i が手元で f(i) · g(i) を計算します。
f(i)·g(i) = (s + a·i)(s' + b·i)
= s·s' + (s·b + s'·a)·i + (a·b)·i²
これを h(x) = s·s' + (sb + s'a)·x + (ab)·x² と置けば、
確かに h(i) です。そして h(0) = s·s' —— 欲しかった積が切片に入っています。
h(x) には x² の項があります。直線ではなく放物線です。
何が困るのか: 次数 1 なら 2 人で復元できました。次数 2 は 3 人必要です。 掛け算するたびに必要人数が増えていく。 2 回掛ければ次数 4、3 回で次数 8 —— すぐに参加者の人数を超え、誰も復元できなくなります。
h(x) の係数を見ると、x の係数は s·b + s'·a、
x² の係数は a·b です。
元の乱数 a, b から作られていて、独立ではありません。
本来なら 2 つの係数はそれぞれ独立な乱数であってほしいのに、
a·b は a と b に縛られている。
ここから情報が漏れる余地が生まれます。
次数 2 になった多項式を、秘密は同じまま次数 1 に作り直す必要があります (degree reduction)。そしてこの作業は 1 人ではできません—— 自分のシェアしか持っていないからです。
これが通信の正体です。 加算は各自の手元で閉じるのに、乗算は参加者どうしのやりとりが要る。 MPC のコストが乗算の数で決まるのは、この 1 点に尽きます。
Week 1 と同じ結論、まったく違う理由
ZK でも MPC でも「コストは乗算の数で決まる」。でも理由は別物です。
ZK(R1CS 系): 乗算ゲート 1 個が制約 1 本になるから。加算は式の中に畳み込めて行を消費しない。
MPC: 加算は手元で閉じるが、乗算は次数が倍になって通信が発生するから。
同じ「回路」を使い、同じ指標でコストを測るのに、痛みの中身が違う。 Week 1 のノートで「3 方式は真ん中の回路を共有する」と書いたことの、具体的な現れ方がこれです。
技法
乗算の通信は消せません。ですが「いつやるか」はずらせます。
Week 6 の co-snark-prove で実装するのがこれです。
まだ何を計算するか決まっていない暇な時間に、次を満たす 3 つの乱数を作ります。
a, b はランダム c = a · b
そして a, b, c をそれぞれ秘密分散して配ります。
値そのものは誰も知りません。知っているのは「c = a·b という関係があること」だけ。
ここがポイント: 掛け算という重い作業を、入力が来る前に済ませてしまう。 この 3 つ組を Beaver 三つ組 と呼びます。
秘密 x と y の積を計算したくなりました。まず次の 2 つを計算して公開します。
d = x − a e = y − b
引き算はさっき見たとおり無料なので、シェアのまま計算できます。
公開して大丈夫なのか: 大丈夫です。a は誰も知らない乱数なので、
d = x − a はランダムな値にしか見えません。
Week 1 の言葉で言えば、乱数で撹乱されている状態。x は漏れません。
d = x − a を移項すると x = d + a、同様に y = e + b。
これを掛けて展開します。
x · y = (d + a)(e + b)
= d·e + d·b + a·e + a·b
= d·e + d·b + a·e + c (a·b = c だった)
右辺を見てください。d と e は公開済みの普通の数。
a, b, c はシェアとして持っている。
つまり右辺は「公開された定数」×「シェア」の足し算だけです。
d·b は b のシェアを d 倍するだけ、a·e は a のシェアを e 倍するだけ。
定数倍と加算は無料でした。
秘密どうしの掛け算が、1 つも残っていません。
乗算 1 回に必要だった「次数を戻す通信」が、d と e を公開する 1 往復に置き換わりました。 重い前処理は、入力が来る前に終わっています。
これが MPC の実装で最初に出てくる工夫です。
前処理と本番を分ける設計はこの分野の定石で、
Week 6 の co-snark-prove は、まさにこの Beaver 乗算を実装する課題です。
壊してみる
課題の採点器は「積が合っているか」だけを見ていません。 わざと壊した実装を 3 通り作って通した結果がこれです。 どれも「何を買っている制約なのか」が失敗の形に出ます。
x·y = c + d·b + e·a + d·e のうち、c, b, a は
シェアとして分散されています。でも d·e は公開値どうしの積、ただの数です。
全員が自分のシェアに足すと、復元時に人数分カウントされます。
# d·e を全 party に足した壊れ版(3 party, p = 67) AssertionError: 57 != 41 # (n−1)·d·e = 2·d·e だけ余る
シェアと公開値は「足し方」が違う。シェアは全員が成分ごとに、 公開値は代表 1 人(party 0)だけが足す。この区別が曖昧なままだと、 復元して初めて壊れていたと分かります。
いちばん面白い壊れ方です。d = x − a を「x を開いて、a を開いて、引く」と実装しても、
積の値のテストは全部通ります。落ちるのはこれだけ。
test_fixed_share_vector ................. ok
test_products_for_two_and_three_parties . ok
test_opens_exactly_the_two_masked_differences ... FAIL
AssertionError: 4 != 2 # 開示回数が 2 回ではない
「答えが正しい」と「開きすぎていない」は別の性質。採点器は reconstruct の呼び出しを数えて、 マスク済みの d, e 以外を開いたら落とします。MPC の正しさは出力だけでは測れない—— 実行中に何を公開したかまで含めてプロトコルです。
同じ a で 2 つの秘密を隠すと、攻撃者はネットワーク上の公開値だけから
差を復元できます。実測(p = 67, x₁ = 12, x₂ = 55, 同じ a = 29):
公開値: d₁ = 50, d₂ = 26 攻撃者: d₁ − d₂ = 24 (mod 67) 真の値: x₁ − x₂ = 12 − 55 = −43 = 24 (mod 67) ← 一致
d = x − a が安全なのは、a がワンタイムパッドだから。2 回使えば
d₁ − d₂ = x₁ − x₂ で a が消え、秘密どうしの関係が丸見えになります。
前処理で三つ組を「乗算の回数分」用意する理由がこれです。
ビットの世界へ
ここまでは数(有限体の元)の話でした。課題の Part B はビットを扱います。 構図は驚くほど同じです。
ビット x の XOR シェアは x = x₀ ⊕ x₁ となる 2 ビットの組。
乱数 m を引いて (m, x ⊕ m) と分ければ、
片方だけ見ても x は 50:50 のままです。
XOR は手元で完結します。(x₀ ⊕ y₀, x₁ ⊕ y₁) を作れば
x ⊕ y のシェアになる——加法的シェアの「加算は無料」と同じ理屈です
(XOR は mod 2 の加算そのものです)。
壁は AND です。展開してみると:
x·y = (x₀ ⊕ x₁)(y₀ ⊕ y₁)
= x₀y₀ ⊕ x₀y₁ ⊕ x₁y₀ ⊕ x₁y₁
x₀y₀ は P0 が、x₁y₁ は P1 が手元で計算できます。
でもクロス項 x₀y₁ と x₁y₀ は、どちらの手元にも材料がそろっていません。
乗算で次数が上がったのと同じ場所に、同じ形の壁が現れます。
実測 — クロス項をサボると、そこだけ落ちる
「各自が自分のシェアだけ AND する」壊れ版は、混合シェアの組合せだけ失敗します。
gmw_and (x_shares=(0, 0), ...) ... ok gmw_and (x_shares=(0, 1), y_shares=(1, 0)) FAIL gmw_and (x_shares=(1, 0), y_shares=(0, 1)) FAIL gmw_and (x_shares=(1, 1), y_shares=(1, 0)) FAIL AssertionError: 0 != 1
失敗の分布が原因を指しています。両方のシェアが同じ側に寄っている組は クロス項が 0 なので偶然通る。1 が割れて入った組だけ、 x₀y₁ / x₁y₀ の欠落が答えに出ます。
道具
クロス項 x₀y₁ を計算するには「P1 の y₁ に応じて、P0 の持つ 2 つの値のどちらかを渡す」
必要があります。ただし P0 に y₁ を知られてはいけないし、P1 に両方渡してもいけない。
この綱渡りを実現するのが 1-out-of-2 OT です。
sender は 2 通のメッセージ m₀, m₁ を差し出す。
receiver は選択ビット choice で 1 通だけ受け取る。
普通の通信では両立しません。どちらが欲しいか言えば sender に選択が漏れ、 両方もらえば receiver が読みすぎる。暗号で「開けられない封筒」を作って初めて両立します。
sender は秘密 a から A = g^a を公開。
receiver は秘密 b を引いて、request B の作り方を choice で変えます。
choice = 0 → B = g^b
choice = 1 → B = A·g^b
sender は 2 つの鍵を K₀ = B^a, K₁ = (B/A)^a と導出して
それぞれのメッセージを暗号化。receiver の鍵は常に A^b = g^{ab} です。
choice = 0 なら K₀ = (g^b)^a = g^{ab} が一致し、choice = 1 なら K₁ = (A·g^b/A)^a = g^{ab} が一致する。 選んだ側の封筒だけが開き、もう一方の鍵を得るには離散対数を解く必要があります。
課題の仕様に「receiver secret b は 0..q−1 から選ぶ」とあります。 0 を除いて 1..q−1 にすると何が起きるか、request の分布を全部数えました。
| b の範囲 | choice = 0 の request 集合 | choice = 1 の request 集合 |
|---|---|---|
| 0..10(正) | 部分群の 11 要素すべて | 部分群の 11 要素すべて(同一) |
| 1..10(誤) | 1 が出ない | 8 (= A) が出ない |
request = 8 を見たら choice = 0 と確定、request = 1 なら choice = 1 と確定。 「一様分布が choice によらず同じ」という性質は、範囲が 1 点欠けるだけで壊れます。 仕様の端の値には、たいてい理由があります。
request を常に B = g^b にした壊れ版では、choice = 0 の復号は全部成功し、
choice = 1 だけがゴミを返します。
choice=0 ... ok(全部成功) choice=1: AssertionError: b'\xc2\x97\xc1W\xb7' != b'right'
B に A を織り込むのは receiver の仕事で、それをして初めて sender 側の K₁ = (B/A)^a と鍵が揃う。「どちらの封筒が開くか」は request の作り方が決めています。
組み立て
道具がそろいました。クロス項 1 つにつき OT を 1 回。対称なので 2 回で AND が完成します。
P0 は乱数マスク r₀₁ を引き、2 通のメッセージを用意します。
m₀ = r₀₁ m₁ = r₀₁ ⊕ x₀
P1 が y₁ を choice にして OT すると、受け取る値は
t₀₁ = r₀₁ ⊕ x₀·y₁(y₁ = 0 なら m₀、1 なら m₁——ちょうどこの式になります)。
クロス項 x₀y₁ が「r₀₁ で焼き付けられた形」で P1 に渡りました。 P1 が見るのはマスク済みの 1 ビットだけなので x₀ は漏れない。 P0 は OT の約束で y₁ を知らない。誰も何も知らないまま、項だけが移動しています。
P1 が sender: m₀ = r₁₀, m₁ = r₁₀ ⊕ x₁ P0 が y₀ で選ぶ → t₁₀ = r₁₀ ⊕ x₁·y₀
採点器は OT の実行回数も数えています(2 回でなければ落ちる)。 クロス項は 2 つ、OT も 2 つ。1 回で済ませる近道はここにはありません。
z₀ = x₀y₀ ⊕ r₀₁ ⊕ t₁₀ (P0 の手元にある材料だけ)
z₁ = x₁y₁ ⊕ r₁₀ ⊕ t₀₁ (P1 の手元にある材料だけ)
XOR すると、マスクがペアで消えます。
z₀ ⊕ z₁ = x₀y₀ ⊕ x₁y₁ ⊕ r₀₁ ⊕ (r₀₁ ⊕ x₀y₁) ⊕ r₁₀ ⊕ (r₁₀ ⊕ x₁y₀)
= x₀y₀ ⊕ x₀y₁ ⊕ x₁y₀ ⊕ x₁y₁ = x·y ✓
Beaver の d·e と同じく、マスクは「入れた人が自分の側で打ち消す」設計です。 r₀₁ は P0 が入れて P0 の z₀ で消える。打ち消しの場所を間違えると、 復元して初めて壊れていたと分かる——数の世界とまったく同じ教訓です。