Susumu Tomita

Advanced Cryptography Program 2026 / Week 2 自習ノート

秘密を分けて集めずに計算する

Week 2 のテーマは MPC(秘密計算)です。課題 toy-mpc を実際に解き、 わざと壊した版を採点器に通した実測も含めて、このノートにまとめ直しました。

前半は数の世界 —— 秘密分散、なぜ加算は無料で乗算に通信が要るのか、 Beaver 三つ組。 後半はビットの世界 —— XOR シェア、紛失通信(OT)、 そして OT を 2 回使って AND を作る GMW

読了 30 分 対話ラボ 2 つ 検問クイズ Week 1 の知識を使う 外部通信なし

出発点

解きたいのは「持ち寄りたいが、見せたくない」

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 行ずつ追う

    アイデアは中学校で習った 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 人以下では、さっきと同じ理屈で情報がゼロのままです。

    ラボ

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

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

    分ける側の設定

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

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

    計算の中身

    課題で使った形

    加法的秘密分散 — 足して戻る、いちばん単純な分け方

    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 三つ組 — 重い部分を前もって済ませる

    乗算の通信は消せません。ですが「いつやるか」はずらせます。 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 つの掟

    課題の採点器は「積が合っているか」だけを見ていません。 わざと壊した実装を 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 という壁

    ここまでは数(有限体の元)の話でした。課題の 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 つだけ受け取る

    クロス項 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 回で、クロス項を運ぶ

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

    ラボ

    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 つ欠けた瞬間に、どちらも壊れます。