Susumu Tomita

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 ステップずつ回すラボ、検問クイズはそのまま残してあります。

カード 27 枚・演習 15 問 対話ラボ 2 つ 検問クイズ Week 1 の知識を使う 外部通信なし

出発点

このノートは、講義スライド 55 枚と同じ順番で進む

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 行ずつ自分の手で

    1 行打って、出た値を貼って、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 行。

    ドリル 1 — share と線形演算

    0 / 0

    ドリル 2 — 掛け算を 1 ラウンドで買う(Beaver)と、GMW の AND

    三つ組 → d, e を開示 → なぜ見せてよいか → 組み立て → d·e の罠 → XOR share → AND の交差項 → OT で運ぶ → コスト表。10 行。

    ドリル 2 — Beaver と GMW

    0 / 0

    スライド 2〜13 / MPC は何をしたい技術か・問題設定

    入力を一か所に集めずに、答えだけを得る

    2/55導入: 3 人の給与平均はいくら?

    知りたいのは平均だけ。でも普通に計算すると、誰かが全員の給与を見てしまう。個別の給与は誰にも見せず、平均だけを知りたい。

    宿題ではここ: Part A はこの「見せずに足す・掛ける」を作る。Part B は「見せずに AND を取る」を作る。

    Q. 「集めて計算する」の何が問題か。(集めた人が全部見える)

    5〜6/55目的/ZK との違い

    MPC は、複数のパーティが秘密の値を持つとき、互いに秘匿したまま共同で計算する技術。ZK は「秘密を明かさずに主張が正しいと証明する」、MPC は「秘密入力を持つ複数の人が、集めずに共同で計算する」。

    Q. 給与平均は ZK と MPC どちらの問題か。(誰も答えを持っていない → 計算が要る → MPC)

    8〜9/55Trusted Third Party の理想世界/MPC の直感的な定義

    信頼できる第三者 T に全員が入力を送り、T が y だけ返せば簡単。でも T には全部の入力が集まる(漏らす・目的外利用・結託・改ざん)。MPC は T を置かず、T がやるはずだった計算をパーティ間のプロトコルで実現する。

    覚える 1 点: MPC = 「T の代わり」。理想世界の T ができることを超えるものではない(次のカードの「保証しないこと」に効く)。

    10〜11/55基本設定と、守りたい性質
    まず押さえる 4 つ:  誰が参加する(party)/各 party が何を持つ(input)/何を計算する(function)/誰が結果を受け取る(output)
    性質:  Correctness(仕様どおりの出力)/Privacy(出力から分かる以上は漏れない)/Robustness(止まる・嘘をつく party がいてもどうなるか)

    Privacy の判定法: ある party がプロトコル中に見たものが「自分の入力と最終出力だけから作れそう」なら漏れていない — Week 3 の simulator と同じ考え方。

    12〜13/55MPC が保証しないこと — 出力からの漏れ、入力の正しさ

    「入力が漏れない」を強く言いすぎない。出力そのものから分かる情報は隠せない(平均 1000 万で、自分と他 1 人を知っていれば残り 1 人が分かる)。入力が本当に正しいかも別問題(嘘の給与、資格の無い投票)→ コミットメント・ZK・監査で別途。

    Q. 3 人の平均を公開してよいか、を決めるのは誰か。(プロトコルではなく設計者。出力を公開すると決めた時点で漏れる分は漏れる)

    やってみる — 紙と鉛筆で。答えは開くまで隠れる

    問題設定

    問 1. 3 人の給与平均が 30 万と公開された。自分は 20 万、もう 1 人が 40 万だと知っている。3 人目の給与は? これは MPC の失敗か。
    合計 = 30 × 3 = 90。90 − 20 − 40 = 30 万。
    失敗ではない。出力(平均)から計算できる情報は、出力を公開すると決めた時点で漏れる(output leakage)。
    防ぐなら「平均を公開しない」「人数を増やす」「差分プライバシーでぼかす」など設計側の話。
    問 2. 「秘密の値が条件を満たすことを、見せずに納得させたい」は ZK か MPC か。「秘密の値を持つ 3 人が、見せずに合計を知りたい」は?
    前者は ZK(証明。答えは prover が持っている)。後者は MPC(計算。答えは誰も持っていない)。
    MPC の入力の正しさを保証したければ ZK を組み合わせる(スライド 13)。

    スライド 15〜23 / 秘密分散

    秘密を、1 個では何も分からない断片(share)に分ける

    15/55秘密分散とは

    秘密 x を、ランダム性を使って作った share に分けて配る。少数の share では x が分からず、決められた数(k 個)以上集めると復元できる(k-of-n)。Sharing と Reconstruct の 2 つのアルゴリズム。

    16/55例: 加法的秘密分散 — 足すと戻る
    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 が分からない)

    17/55加法的秘密分散の特徴 — share のまま足せる
    [x + y] = ([x]₁ + [y]₁, [x]₂ + [y]₂, [x]₃ + [y]₃)      各 party が自分の share を足すだけ。通信なし。復元しない

    宿題ではここ: add_shares(left, right, modulus)。party 数が違えば拒否する。合計・平均・線形変換が MPC と相性がよい理由はこの 1 行

    18〜20/55k-of-n が欲しい理由/Shamir の直感/Shamir の秘密分散法

    加法的は 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 の「表を多項式にする」の入口はここ。

    21〜23/55比較/何を opening してよいか/現実の値をどう扱うか
    加法的: 足すと戻る、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 値だけ(採点器が数える)。

    やってみる — 紙と鉛筆で。答えは開くまで隠れる

    秘密分散(mod 11)

    問 1. p = 11 で秘密 7 を 3 人に加法的に分ける。乱数で share₁ = 3、share₂ = 9 を選んだ。share₃ は?
    share₃ = 7 − 3 − 9 = −5 ≡ 6 (mod 11)。検算 3 + 9 + 6 = 18 = 11 + 7 ✓
    (宿題の share(7, [3, 9], 11) は [3, 9, 6] を返す。−5 のまま返すと落ちる)
    問 2. 上の 3 人が、別の秘密 y の share [2, 5, 4] も持っている。y は? 各人が自分の share を足すと、合計は何の share になるか。
    y = 2 + 5 + 4 = 11 ≡ 0。
    足した share は [5, 14 ≡ 3, 10]。復元すると 5 + 3 + 10 = 18 ≡ 7 = x + y = 7 + 0 ✓。通信なしで足し算ができた。
    問 3. Shamir(p = 11、k = 2)で share が (1, 8) の 1 点だけのとき、secret は 5 だと言えるか。
    言えない。(1, 8) を通る直線は傾きごとに 11 本あり、切片 q(0) は 0〜10 のどれにもなれる。
    2 点目 (2, 0) が来て初めて傾き 3、切片 5 と決まる。「k − 1 個では何も分からない」の意味。

    スライド 25〜32 / MPC での演算

    足し算は無料、掛け算だけ通信が要る

    25/55線形演算は軽い

    加算・減算・公開値による定数倍は、各 party が自分の share にローカルで行える。通信なし。

    注意(宿題の落とし穴): 公開値 c を「足す」とき、全 party が自分の share に c を足すと x + n·c になってしまう。1 つの share にだけ足す。Beaver の公開項 d·e も同じ(次のカード)。

    26/55乗算はローカル計算だけでは閉じない
    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) は並べ替えるだけ。積は展開すると混ざる — 中学の展開公式)

    27〜28/55Beaver triple — 使い捨てマスクで掛け算する
    前処理で、ランダムな 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 を覆うのと同じ)

    29〜30/55Arithmetic circuit と Boolean circuit/制御フロー
    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)。

    31〜32/55重くなりやすい演算/コストはどこで決まるか
    重い: 比較・最大最小・ソート・分岐・秘密での除算・秘密インデックスの配列アクセス(全部「ビット」か「非線形」が要る)
    コストの軸: 通信量/通信ラウンド数/非線形ゲート数(乗算・AND・比較)/party 数/敵対モデル

    覚える 1 点: 普通のコードで同じ 3 行に見えても、MPC では「乗算・AND が何個か」で速さが決まる。

    やってみる — 紙と鉛筆で。答えは開くまで隠れる

    Beaver で掛ける(mod 11、2 人)

    問 1. x の share [1, 2]、y の share [3, 1]、triple a = [4, 1], b = [0, 2], c = [7, 3]。x, y, a, b, c と a·b は?(c = a·b になっているか確かめる)
    x = 3, y = 4, a = 5, b = 2, c = 10。a·b = 10 ✓(triple は正しい)
    問 2. 各人が [d] = [x] − [a]、[e] = [y] − [b] を作り、open する。d, e は?
    [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 と一致)
    問 3. [x·y] = [c] + d·[b] + e·[a] + d·e を作る。d·e は party 1 だけが足す。2 人の share と、復元した積は?
    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 ✓
    問 4. もし 2 人とも d·e を足したら復元値はいくつになるか。
    1 + 18 = 19 ≡ 8 ≠ 1。公開値を全員が足すと n 倍入る(スライド 25 の注意)。旧版ノートの「壊した実測」で採点器が落としていたのがこれ。
    問 5. 同じ triple を 2 回の乗算(x₁·y と x₂·y)に使い回すと、公開値だけから何が分かるか。
    d₁ = x₁ − a、d₂ = x₂ − a → d₁ − d₂ = x₁ − x₂。秘密入力どうしの差が誰にでも分かる。
    Week 3 で nonce r を使い回すと s₁ − s₂ = (e₁ − e₂)·x から x が出るのと同じ形。

    スライド 34〜43 / セキュリティモデルと主要プロトコル

    「誰が、何人、どこまで悪いか」で使う道具が変わる

    35/55party はどう振る舞うか: semi-honest / malicious

    semi-honest は手順は守るが好奇心はある(見たものから推論する)。malicious は嘘の share・無応答・結託など何でもする。malicious に耐えるには検証・MAC・ZK が要り、コストが上がる。

    宿題ではここ: 課題は semi-honest 前提。だから「手順どおりに動く相手に、見えてしまう値は何か」だけを気にする(Beaver の d, e、OT の request)。

    36〜37/55何人まで壊れてよいか/出力保証
    honest majority:   t < n/2(5 人中 2 人まで)→ Shamir 系が使いやすい
    dishonest majority: t ≥ n/2(2 人で片方が悪い)→ 強い道具・前処理が要る(SPDZ)
    出力保証: abort(止まってよい)/fairness(一部だけ得をしない)/guaranteed output delivery(正直な人は必ず出力を得る)
    38〜39/55generic と special-purpose/主要プロトコル
    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 型。

    40/55紛失通信(OT): 選んだ方だけを受け取る
    sender: m₀, m₁    receiver: 選択ビット b    receiver が得るもの: m_b だけ
    隠すもの: receiver は選ばなかった m₁₋b を知らない/sender は b を知らない

    宿題ではここ: Part B の 3 関数が OT の 3 手。OT 単体で何かを計算するのではなく、AND を作る部品。

    41/55OT プロトコルの実現(離散対数ベース)
    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 の分布がずれる — 旧版ノートの分布実測)。

    GMWGMW の AND — XOR share の交差項を OT で運ぶ
    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(提供済み)でローカル。

    42〜43/55Yao の Garbled Circuit/SPDZ

    Yao: 2 者。回路の各 wire の 0/1 にランダムなラベルを付け、一方が回路を「暗号化した表」にし、他方がラベルだけで評価する。自分の入力ラベルは OT で受け取る。SPDZ: offline で Beaver triple を作り、online は速い。share に MAC を付けて malicious を検出。

    やってみる — 紙と鉛筆で。答えは開くまで隠れる

    OT と GMW(mod 23 と 1 bit)

    問 1. 上の OT で receiver が b = 0 を選び r = 4 とすると B は? そのとき sender の k₀, k₁ の元(H の中身)と receiver の A^r は?
    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 を当てられない)
    問 2. GMW の AND で x₁ = 0, x₂ = 1(x = 1)、y₁ = 0, y₂ = 1(y = 1)、r₁ = 1, r₂ = 1 のとき z₁, z₂ と z は?
    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 ✓
    問 3. XOR の share は通信なしで足せるのに、AND だけ OT が要るのはなぜか。
    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 で応用を読む

    45〜49/55応用を見るための型と 4 事例
    型: 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 だけでは解決しないこと」(入力の正しさ、出力からの推測、可用性、運用)が半分を占める。

    51〜52/55関連技術との比較/使い分けの直感
    ZK:  正しいことを証明する(prover が計算、witness を隠す、proof で保証)
    FHE: 暗号化したまま計算する(evaluator 1 者、データと中間値を隠す)
    MPC: 入力を集めず共同計算(複数 party、各入力を隠す、プロトコルと敵対モデル次第)
    TEE: ハードウェア内で計算(enclave を信頼)      DP: 出力から個人を推測しにくくする(統計的)

    Q. 「1 者がデータを預けて計算だけ委託したい」は?(FHE)「複数者が持ち寄る」は?(MPC)「条件を満たすことだけ示したい」は?(ZK)

    53〜54/55MPC を使う前に確認すること/講義のまとめ
    party(誰が、いつオンラインか)/function(非線形が多すぎないか)/security(semi-honest でよいか)/cost(通信・ラウンド・前処理)
    output(出力から漏れすぎないか)/representation(有限体に落とせるか)/operation(運用・監査の責任分界)/そもそも MPC である必要があるか
    55/55ホワイトボードセッション: Private DEX を設計する

    トレーダーは署名済み注文を share にして MPC ノード委員会へ。委員会が share のままマッチング。約定だけを決済コントラクトへ(オンチェーンでは公開)。設計するのは: ノード構成と adversary、価格・数量・優先順位・部分約定、何を output として開示するか、署名・nonce・取消・abort、MPC とオンチェーンの分担。

    型で書く: party = トレーダー + 委員会 / input = 注文 / function = マッチング / output = 約定 / hidden = 未約定の注文 / cost = 比較(価格の大小 — 重い)。

    宿題の 8 関数

    宿題の関数 ↔ スライド ↔ 学校で習ったこと

    関数スライド学校で習ったこと小さい数の例落ちやすい所
    share16余り(中 1)、「合計が決まっているとき最後の 1 個は引き算で決まる」7 = 3 + 9 + 6 (mod 11)最後の share を 0..p−1 に正規化
    reconstruct16足し算3 + 9 + 6 = 18 ≡ 7% modulus 忘れ
    add_shares17, 25足し算の順序を入れ替えてよい(結合・交換法則、中 1)[1,2] + [3,1] = [4,3] → 7party 数が違うのを拒否
    beaver_multiply27〜28展開公式 (x−a+a)(y−b+b)(中 3)d = 9, e = 2 → x·y = 1d·e を全員が足す/open が 2 値を超える
    ot_receiver_request41指数法則 (g^a)^r = (g^r)^a(中 2 の累乗)B = 13(b=1)/ 16(b=0)r の範囲に 0 を含める
    ot_sender_encrypt41同上、⊕ は「桁上がりの無い足し算」k₀ = H(12), k₁ = H(2)k₁ の底は B/A
    ot_receiver_decrypt41同上A^r = 2 → m₁ だけ開く不正な入力を拒否
    gmw_and26, 40〜41展開公式(XOR 版)z₁ ⊕ z₂ = x·yOT をちょうど 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 / thresholdn 個中 k 個で復元、k−1 個では分からないthreshold = しきい値加法的は n-of-n
    opening / openshare を集めて値を復元し、公開すること「開ける」。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 の aa, b、GMW の r₁, r₂
    線形(linear)演算足し算・引き算・公開定数倍。share のままローカルでできる1 次式の形。LSS = Linear Secret Sharingadd_shares
    Arithmetic / Boolean circuit+ と × のゲート/AND・XOR・NOT のゲートarithmetic = 算術、Boolean = ブール(論理)Part A / Part B
    XOR sharebit を x = x₁ ⊕ x₂ に分ける。XOR はローカル、AND は通信⊕ = 排他的論理和(桁上がりの無い足し算 = mod 2 の足し算)gmw_and の入力
    紛失通信(OT)2 つのうち選んだ 1 つだけを、選択を知られずに受け取るOblivious Transfer。oblivious = 気づかない(sender は選択に気づかない)ot_* の 3 関数
    GMWOT で Boolean 回路を評価するプロトコルGoldreich・Micali・Wigderson(人名、1987)gmw_and
    Yao / Garbled Circuit回路を暗号化した表にして 2 者で評価Andrew Yao(人名、1982)。garble = ごちゃ混ぜにする(出ない)
    SPDZdishonest 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)汎用/専用
    PSI2 者の集合の共通部分だけを知るPrivate Set IntersectionMeta の事例
    DP出力にノイズを入れて個人の寄与を推測しにくくするDifferential Privacy(差分プライバシー)Boston の事例

    ラボ

    配って、集めて、確かめる

    秘密と乱数を選ぶとシェアが計算されます。誰のシェアを集めるかを切り替えて、 何人分あれば秘密が決まるのかを自分で確かめてください。

    分ける側の設定

    p = 11
    秘密 s隠したい値
    乱数 a毎回変える
    配ったシェア押すと集める / 外す

    集めたシェアと辻褄が合う秘密

    計算の中身

    ラボ

    GMW の AND を、1 ステップずつ回す

    4 つのシェアビットと 2 つのマスクを選ぶと、OT 2 回込みの実行トレースが出ます。 「P0 から見えた値」「P1 から見えた値」に注目してください—— マスクのおかげで、どちらも相手のシェアを 1 ビットも学べていません。

    入力(シェアとマスク)

    OT: p = 23, g = 2
    x のシェア (x₀, x₁)x = x₀ ⊕ x₁
    y のシェア (y₀, y₁)y = y₀ ⊕ y₁
    マスク (r₀₁, r₁₀)毎回変える乱数のつもりで

    OT の secret は課題のテストと同じ固定値(セッション 01: a=3, b=4 / セッション 10: a=5, b=6)。

    実行トレース(OT の中身込み)

    検問クイズ

    開示検問 — あなたが MPC の監査官になる

    読んだだけでは定着しない。ここではあなたが監査官の席に座り、 シェアの計算・公開してよい値・OT の振る舞いを審査する。問題は毎回その場で生成され、 判定は上のラボと同じ計算器が行う。3 回誤判定したら、その回は終了。

    検問モードを選ぶ

    10 問・ライフ 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)で解きます。 このノートの構成はその対比に合わせてあります。

    この頁の地図

      Shamir の秘密分散 — 1 行ずつ追う

      中核

      Shamir の秘密分散 — 1 行ずつ追う

      アイデアは中学校で習った 1 つの事実だけです。そこから積み上げます。

      1. 出発点は「2 点で直線が 1 本に決まる」

        平面に点が 2 つあれば、その両方を通る直線はただ 1 本です。 逆に点が 1 つだけなら、そこを通る直線は無数にあります

        この非対称がすべてです。 2 つ集めれば決まる。1 つでは何も決まらない。 「決まらない」を「分からない」に使うのが秘密分散の発想です。

      2. 秘密を「直線の切片」に隠す

        秘密の値を 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 番の値だけは誰にも渡さない。だから直線が復元できて初めて秘密にたどり着きます。

      3. 各人に「直線上の 1 点」を配る

        参加者 1 には f(1)、参加者 2 には f(2)、参加者 3 には f(3) を渡す。 この値をシェアと呼びます。

        計算はすべて有限体 F_p の上で行います(Week 1 と同じ世界です)。 以下では見やすさのため p = 11 を使います。

        なぜ有限体なのか: 実数のままだと、シェアの大きさから秘密の大きさが推測できてしまいます (切片が大きければ値も大きい、など)。mod p で巻いてしまえば大小の手がかりが消えます。 Week 1 で見た「順序が存在しない」性質が、ここでは利点として効いています。

      4. 実際に数字で作ってみる

        秘密 s = 3、乱数 a = 5p = 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 とは似ても似つかない数字です。

      5. 2 人集まれば復元できる

        参加者 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 でなければ、逆数を求めてから掛けます。

      6. 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 通り全部を並べて確認できます。

      7. 閾値は次数で決まる

        直線(次数 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 = 67s = 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 つの秘密 ss' を、それぞれ別の直線に隠したとします。

      f(x) = s + a·x  g(x) = s' + b·x

      参加者 if(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 倍)と加算の組み合わせなので無料です。

      線形なことは、全部ローカルで完結する。

      なぜ乗算だけ高いのか

      なぜ乗算だけ高いのか

      同じことを掛け算でやると、途中で破綻します。どこで破綻するのかを式で見ます。

      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' —— 欲しかった積が切片に入っています。

      2. しかし、次数が 1 から 2 に上がっている

        h(x) には の項があります。直線ではなく放物線です。

        何が困るのか: 次数 1 なら 2 人で復元できました。次数 2 は 3 人必要です。 掛け算するたびに必要人数が増えていく。 2 回掛ければ次数 4、3 回で次数 8 —— すぐに参加者の人数を超え、誰も復元できなくなります

      3. もう 1 つの問題 — 乱数が足りない

        h(x) の係数を見ると、x の係数は s·b + s'·a の係数は a·b です。

        元の乱数 a, b から作られていて、独立ではありません。 本来なら 2 つの係数はそれぞれ独立な乱数であってほしいのに、 a·bab に縛られている。 ここから情報が漏れる余地が生まれます。

      4. だから「次数を戻す」作業が要る

        次数 2 になった多項式を、秘密は同じまま次数 1 に作り直す必要があります (degree reduction)。そしてこの作業は 1 人ではできません—— 自分のシェアしか持っていないからです。

        これが通信の正体です。 加算は各自の手元で閉じるのに、乗算は参加者どうしのやりとりが要る。 MPC のコストが乗算の数で決まるのは、この 1 点に尽きます。

      Week 1 と同じ結論、まったく違う理由

      ZK でも MPC でも「コストは乗算の数で決まる」。でも理由は別物です。

      ZK(R1CS 系): 乗算ゲート 1 個が制約 1 本になるから。加算は式の中に畳み込めて行を消費しない。

      MPC: 加算は手元で閉じるが、乗算は次数が倍になって通信が発生するから。

      同じ「回路」を使い、同じ指標でコストを測るのに、痛みの中身が違う。 Week 1 のノートで「3 方式は真ん中の回路を共有する」と書いたことの、具体的な現れ方がこれです。

      Beaver 三つ組 — 重い部分を前もって済ませる

      技法

      Beaver 三つ組 — 重い部分を前もって済ませる

      乗算の通信は消せません。ですが「いつやるか」はずらせます。 Week 6 の co-snark-prove で実装するのがこれです。

      1. 前処理で、掛け算済みの乱数を用意しておく

        まだ何を計算するか決まっていない暇な時間に、次を満たす 3 つの乱数を作ります。

        a, b はランダム  c = a · b

        そして a, b, cそれぞれ秘密分散して配ります値そのものは誰も知りません。知っているのは「c = a·b という関係があること」だけ。

        ここがポイント: 掛け算という重い作業を、入力が来る前に済ませてしまう。 この 3 つ組を Beaver 三つ組 と呼びます。

      2. 本番では、隠したまま 2 つの値を公開する

        秘密 xy の積を計算したくなりました。まず次の 2 つを計算して公開します。

        d = x − a  e = y − b

        引き算はさっき見たとおり無料なので、シェアのまま計算できます。

        公開して大丈夫なのか: 大丈夫です。a は誰も知らない乱数なので、 d = x − aランダムな値にしか見えません。 Week 1 の言葉で言えば、乱数で撹乱されている状態。x は漏れません。

      3. すると積が、足し算だけで書ける

        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 だった)

        右辺を見てください。de公開済みの普通の数a, b, cシェアとして持っている

        つまり右辺は「公開された定数」×「シェア」の足し算だけです。 d·b は b のシェアを d 倍するだけ、a·e は a のシェアを e 倍するだけ。 定数倍と加算は無料でした。 秘密どうしの掛け算が、1 つも残っていません。

      4. 結果として何が起きたか

        乗算 1 回に必要だった「次数を戻す通信」が、d と e を公開する 1 往復に置き換わりました。 重い前処理は、入力が来る前に終わっています。

        これが MPC の実装で最初に出てくる工夫です。 前処理と本番を分ける設計はこの分野の定石で、 Week 6 の co-snark-prove は、まさにこの Beaver 乗算を実装する課題です。

      採点器が守らせる、Beaver の 3 つの掟(壊した実測)

      壊してみる

      採点器が守らせる、Beaver の 3 つの掟

      課題の採点器は「積が合っているか」だけを見ていません。 わざと壊した実装を 3 通り作って通した結果がこれです。 どれも「何を買っている制約なのか」が失敗の形に出ます。

      1. 掟 1 — 公開項 d·e を足すのは、代表 1 人だけ

        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)だけが足す。この区別が曖昧なままだと、 復元して初めて壊れていたと分かります。

      2. 掟 2 — 開いてよいのは d と e の 2 回だけ

        いちばん面白い壊れ方です。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 の正しさは出力だけでは測れない—— 実行中に何を公開したかまで含めてプロトコルです。

      3. 掟 3 — 三つ組は 1 乗算 1 回限り

        同じ 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 が消え、秘密どうしの関係が丸見えになります。 前処理で三つ組を「乗算の回数分」用意する理由がこれです。

      XOR シェアと、AND という壁

      ビットの世界へ

      XOR シェアと、AND という壁

      ここまでは数(有限体の元)の話でした。課題の 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₀ の欠落が答えに出ます。

      紛失通信(OT)— 選んだことを知られずに、1 つだけ受け取る(分布の実測)

      道具

      紛失通信(OT)— 選んだことを知られずに、1 つだけ受け取る

      クロス項 x₀y₁ を計算するには「P1 の y₁ に応じて、P0 の持つ 2 つの値のどちらかを渡す」 必要があります。ただし P0 に y₁ を知られてはいけないし、P1 に両方渡してもいけない。 この綱渡りを実現するのが 1-out-of-2 OT です。

      1. 約束は 2 つ — sender は選択を知らない、receiver は片方しか読めない

        sender は 2 通のメッセージ m₀, m₁ を差し出す。 receiver は選択ビット choice で 1 通だけ受け取る。

        普通の通信では両立しません。どちらが欲しいか言えば sender に選択が漏れ、 両方もらえば receiver が読みすぎる。暗号で「開けられない封筒」を作って初めて両立します。

      2. トイ構成 — Diffie–Hellman の応用(p = 23, g = 2, 位数 q = 11)

        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} が一致する。 選んだ側の封筒だけが開き、もう一方の鍵を得るには離散対数を解く必要があります。

      3. b は 0 を含めて選ぶ — 1 点欠けるだけで選択が漏れる

        課題の仕様に「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 点欠けるだけで壊れます。 仕様の端の値には、たいてい理由があります。

      4. 実測 — choice を無視した request は、選んだ側の半分だけ壊す

        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 の作り方が決めています。

      GMW の AND — OT を 2 回で、クロス項を運ぶ

      組み立て

      GMW の AND — OT を 2 回で、クロス項を運ぶ

      道具がそろいました。クロス項 1 つにつき OT を 1 回。対称なので 2 回で AND が完成します。

      1. セッション 01 — P0 が sender、P1 が y₁ で選ぶ

        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₁ を知らない。誰も何も知らないまま、項だけが移動しています。

      2. セッション 10 — 役割を入れ替えてもう 1 回

        P1 が sender: m₀ = r₁₀, m₁ = r₁₀ ⊕ x₁  P0 が y₀ で選ぶ → t₁₀ = r₁₀ ⊕ x₁·y₀

        採点器は OT の実行回数も数えています(2 回でなければ落ちる)。 クロス項は 2 つ、OT も 2 つ。1 回で済ませる近道はここにはありません。

      3. 出力 — 手持ちの材料を全部 XOR するだけ

        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₀ で消える。打ち消しの場所を間違えると、 復元して初めて壊れていたと分かる——数の世界とまったく同じ教訓です。