百萬富翁問題的介紹與實現


參考自

  1. https://zhuanlan.zhihu.com/p/65564614
  2. https://www.jianshu.com/p/5a220e95cee2
  3. YAO A C.Protocols for secure computation[C].In Proc. of the 23rd Annual Symposium on Foundations of Computer Science,1982.
    4.《聯邦學習》楊強 劉洋等

安全多方計算

安全多方計算最初是針對一個安全兩方計算問題,即所謂的“百萬富翁問題”而被提出的,並與1982年被姚期智提出和推廣。在安全多方計算中,目的是協同地從每一方的隱私輸入中計算函數的結果,而不用將這些輸入展示給其他方。

通常情況下,安全多方計算能夠通過三種不同的框架來實現:不經意間傳輸(Oblivious Transfer,OT)、秘密共享(Secret Share,SS)和閾值同態加密(Threshold Homomorphic Encryption,THE).從某種程度上來講,不經意傳輸協議和閾值同態加密方法都是用了秘密共享的思想,這可能就是為什么秘密共享被廣泛認為是安全多方計算的核心。

百萬富翁問題

兩個富翁,分別為Alice和Bob。他們自己都清楚自己有幾百萬財產,也即,他們心里清楚 1~10中的一個數(代表自己百萬級的財富);他們想知道到底誰的數更大一些。

這里假定:
· 兩人都值得信任,不會作假
· 兩人都希望誠實地比較出誰更服務(即誰的數更大)
· 兩人又都希望知道對方財產到底是多少,如果可能的話,拿到具體數字最好了
· 其實這里假定的是一個安全多方計算的模型 - 半誠實對手模型,即計算方存在獲取其他計算方原始數據的需求,但仍然按照計算協議執行。另外有惡意敵手模型,在這種模型中,參與方可以造假,即不按照計算協議執行計算過程。這就要復雜很多。為簡化期間,本文僅討論半誠實對手模型。

不經意傳輸的解決方案

一個簡單的解決方案就是一下步驟:

  1. Alice找10個一模一樣的箱子,按照1~10的順序擺好,並按照自己的財富值分別往里面放入蘋果梨和香蕉,具體放法為:如果序號小於自己的財富之,放入蘋果,相等,則放入梨,大於自己的財富值,放入香蕉;把10個盒子都叫上鎖;
  2. 並叫Bob過來(或者寄給Bob)Bob根據自己的財富值對相應的箱子再加一把鎖。然后把其他所有箱子銷毀。並把這個選擇的箱子送給Alice。
  3. Alice看到送回來的箱子,但他不知道Bob選擇的是第幾個箱子,因為每個箱子都是一樣的。
  4. Alice、Bob分別開鎖,看里面是什么水果:
    · 如果是蘋果,Alice比Bob富有;
    · 如果是梨,兩人一樣有錢
    · 如果是香蕉,Bob比Alice富有
    簡單吧,可行嗎?當然可行!前提是雙方都是可信的,雙方會遵守協議,所以這是一個半誠實對手模型。如果有一方造假,那么結果就不可信了。那是惡意敵手模型要討論的問題。

密碼學的解決方案

millionare_problem

編程實現一下!

參考的知乎上君莫惘用戶的代碼(程序使用的是RSA公鑰加密算法),進行了部分修改,主要是隨機數x和p的選取要符合規范,並且滿足p<x,x<N (N是RSA中的大質數,N=p*q)。

#coding=utf-8

import math
import random

# 百萬富翁問題實現
# 自己生成公鑰私鑰並解密加密
# 算法無安全性

# 獲取小於等於指定數的素數數組
def get_prime_arr(max):
    prime_array = []
    for i in range(2, max):
        if is_prime(i):
            prime_array.append(i)
    return prime_array


# 判斷是否為素數
def is_prime(num):
    if num == 1:
        raise Exception('1既不是素數也不是合數')
    for i in range(2, math.floor(math.sqrt(num)) + 1):
        if num % i == 0:
            # print("當前數%s為非素數,其有因子%s" % (str(num), str(i)))
            return False
    return True


# 找出一個指定范圍內與n互質的整數e
def find_pub_key(n, max_num):
    while True:
        # 這里是隨機獲取保證隨機性
        e = random.randint(1, max_num)
        if gcd(e, n) == 1:
            break
    return e


# 求兩個數的最大公約數
def gcd(a, b):
    if b == 0:
        return a
    else:
        return gcd(b, a % b)


# 根據e*d mod s = 1,找出d
def find_pri_key(e, s):
    for d in range(100000000):  # 隨機太難找,就按順序找到d,range里的數字隨意
        x = (e * d) % s
        if x == 1:
            return d


# 生成公鑰和私鑰
def build_key():
    prime_arr = get_prime_arr(100)
    p = random.choice(prime_arr)
    # 保證p和q不為同一個數
    while True:
        q = random.choice(prime_arr)
        if p != q:
            break
    print("隨機生成兩個素數p和q. p=", p, " q=", q)
    n = p * q
    s = (p - 1) * (q - 1)
    e = find_pub_key(s, 100)
    print("根據e和(p-1)*(q-1)互質得到: e=", e)
    d = find_pri_key(e, s)
    print("根據 e*d 模 (p-1)*(q-1) 等於 1 得到 d=", d)
    print("公鑰:   n=", n, "  e=", e)
    print("私鑰:   n=", n, "  d=", d)
    return n, e, d


# 加密
def rsa_encrypt(content, ned):
    # 密文B = 明文A的e次方 模 n, ned為公鑰
    # content就是明文A,ned【1】是e, ned【0】是n
    B = pow(content, ned[1]) % ned[0]
    return B


# 解密
def rsa_decrypt(encrypt_result, ned):
    # 明文C = 密文B的d次方 模 n, ned為私鑰匙
    # encrypt_result就是密文, ned【1】是d, ned【0】是n
    C = pow(encrypt_result, ned[1]) % ned[0]
    return C


if __name__ == '__main__':
    pbvk = build_key()
    pbk = (pbvk[0], pbvk[1])  # 公鑰 (n,e)
    pvk = (pbvk[0], pbvk[2])  # 私鑰 (n,d)

    # 生成兩個億萬富翁
    i = random.randint(1, 10)
    j = random.randint(1, 10)
    print("==============================================")
    print("Alice有i = %s億,Bob有j = %s億" % (i, j))

    x = random.randint(50, pbk[0]-1)  # assert(x < N) | N=p*q
    print("隨機選取的大整數x: %s" % x)
    K = rsa_encrypt(x, pbk)
    print("大整數加密后得密文K: %s" % K)
    c = K - j
    print("Alice收到數字c: %s" % c)

    c_list = []
    for k in range(1, 11):
        t = rsa_decrypt(c + k, pvk)
        c_list.append(t)
    print("對c+1到c+10進行解密: %s" % c_list)


    # 選取合適大小的p,這里根據感覺寫了100以內的隨機數,生成的序列的值也要求小於100
    # 這個p是該算法的精華,在實際中選取p的策略要考慮到安全性和性能的因素
    d_list = []
    p = random.randint(30, x)  # assert(p<x)
    for k in range(0, 10):
        d_list.append(c_list[k] % p)
    print("p的值為: %s" % p)
    print("除以p后的余數為: %s" % d_list)

    d_list[i-1] += 1
    for k in range(i, 10):
        d_list[k] += 2
    print("前i-1位數字不動,第i位數字+1,后面數字+2后: %s" % d_list)
    print("第j個數字為: %s" % d_list[j - 1])
    print("x mod p為: %s" % (x % p))
    if d_list[j - 1] == x % p:
        print("i>j,即Alice比Bob有錢。")
        if i - j >= 0:
            print("驗證成功")
        else:
            print("代碼存在錯誤")
    elif d_list[j - 1] == (x%p)+1:
        print("i=j,即Alice和Bob一樣有錢。")
    else:
        print("i<j,即Bob比Alice有錢")
        if i - j < 0:
            print("驗證成功")
        else:
            print("代碼存在錯誤")

執行效果:



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM