点对点卡牌游戏的密码学

20

我正在考虑编写一款半流行纸牌游戏的电脑版本。我希望它在没有中央服务器的情况下运行,并且我正在尝试想出一种方案,使作弊变得不可能,而无需信任客户端。

据我所见,基本问题是每个玩家都有几堆牌(抽牌堆、手牌和弃牌堆)。除了按照游戏规则(例如抽牌或弃牌)允许之外,不能让任何一方玩家改变这些堆的组成,也不能让玩家知道他们或对手的牌堆中有什么。

我觉得应该有一种方式可以利用类似公钥密码学的东西来解决这个问题,但是我一直发现我的方案有漏洞。有人能建议一种协议或者指导一些相关资源吗?

[编辑]

好的,我又思考了一下,想到了一个想法。如果你能挑出任何漏洞,请让我知道。

在洗牌时,玩家拥有一堆已知值的牌。他们将这些值与随机盐连接起来,然后对其进行哈希。他们记录盐并将哈希传递给对手。

对手连接自己的盐,再次进行哈希,然后洗牌哈希并将牌组传回原始玩家。

我认为此时,牌组已经被随机化,没有任何一方玩家可以知道值。但是当抽取一张牌时,对手可以揭示他们的盐,使第一个玩家确定原始值是什么;而当玩家打出卡牌时,他们会揭示自己的盐,从而使对手验证其值。


你愿意选择困难而不是不可能吗? - jcomeau_ictx
当然,源代码和协议必须保密,否则某人很容易编写一个启用作弊的客户端。您可以使用TLS进行通信以避免嗅探协议。除此之外,RAM或磁盘中的任何内容都可以被检查,包括加密数据的任何密钥。 - jcomeau_ictx
1
让每个客户签署一份合同,法律上要求他们如果作弊就必须自杀。问题解决了。 - Chris Eberle
1
我认为这是不可能的。就像任何类型的DRM、安全性、验证等一样:客户端运行它,客户端可以更改它。如果你真的想要安全性而不依赖于单个服务器,那么创建一个服务器来处理所有关键事项,但鼓励人们托管自己的服务器。 - user395760
3
您可以从http://en.wikipedia.org/wiki/Mental_poker获取一些灵感。 - bmm6o
显示剩余7条评论
6个回答

8
你的问题是密码学中著名的“心理扑克问题”(Mental Poker Problem) (这是我在大学时最喜欢密码学课程的一部分之一)。虽然会有很大的性能影响,但它是可能解决的(就像所有密码学问题一样,部分地由Ron Rivest解决)。详细信息请查看维基百科页面。

谢谢 - 这是Brian之前建议的,但由于他只发表了评论,我将接受这个答案。有一件事我很好奇,你是否知道 - 这会有多大的性能损失?我读到目前为止这对于例如扑克牌并没有实时性能,但很难想象它可能对于像纸牌游戏这样人类输入绑定的东西来说太慢了。 - so12311
1
@zephyr:最重要的是带宽和延迟;以前你只需要从一个服务器向几个客户端传输几十个字节,现在你需要来回传输许多千字节。此外,在RSA中解密速度相当慢 - 单个1024位解密可能需要数毫秒。听起来不像什么,但将其乘以52张牌,再乘以几个客户端,并加上延迟,它就会开始累积。我能给出的最好建议是:在慢速网络和慢速计算机上尝试一下,看看是否符合您的需求。 - BlueRaja - Danny Pflughoeft
我没有将它发布为答案,因为心理扑克并不完全符合您的情况。我现在已经添加了一个答案。 - bmm6o

5
我想描述一个很快就出现在我的脑海中的想法。我不知道它是否符合您的需求,所以请随意评论。
假设玩家A有卡牌A1,A2,A3,...来自由数字0、1、...N-1表示的集合。这些牌也可能分成堆,但这并不改变以下内容。
不要使用单个列表[A1,A2,A3,...]处理这些卡,而是使用两个[A1_a,A2_a,A3_a,...]和[A1_b,A2_b,A3_b,...],其中一个与玩家A一起使用,另一个与玩家B一起使用。它们是如此生成的,以便每个都是随机的(在范围0…N-1内),但两者相关,使得A1_a + A1_b = A1,A2_a + A2_b = A2,..(所有操作模N)。
因此,没有任何一位玩家实际上可以在没有对方堆栈的情况下知道卡片(即他不能合理地更改卡片);
每当您需要知道卡牌时,两个玩家都会显示其相应值,然后您将它们相加并对N取模;
您可以轻松地实现诸如“抽卡”之类的东西,只需以相同的方式处理两个堆栈即可。

1
@Howard - 这很聪明,我认为你有所发现。但是我觉得可能存在一种攻击方式。如果玩家2知道A1和A2的值(比如其中一张牌在他们手中,另一张刚被弃掉),那么他们可以改变这些值,使得A1_b_prime = A2+A1_b-A1,A2_p_prime = A1+A2_b-A2。我相信这将允许他们交换这两张牌? - so12311
@zephyr 尽管我手头没有完整的答案,但是通过哈希值处理应该是可行的。因此,你提出的任何作弊行为都将在全部牌面公开后被揭示。 - Howard
1
@Howard - 听起来可行,还有一个问题 - 我该如何开始创建相关列表,以便两个玩家都不知道对方的列表? - so12311

5
传统的Mental Poker方案过于复杂。实际上,你的情况要简单得多,因为没有共享的牌堆。你可以这样做:A选手拿起他的牌并使用密钥Ka加密它们。他将它们发送给B进行洗牌,然后使用密钥Kb加密。每次A想要打出一张牌时,他向B请求下一张卡牌的(Kb)解密。 A解密后找到他抽到的牌。最后,A和B揭示Ka和Kb,并与游戏日志进行比较,以防止作弊。
更新:经过反思,这里有一个更简单的解决方案:与上述相同,玩家A加密他的牌并将其发送到B。 B进行洗牌。每当A需要一张牌时,他告诉B他从哪个牌堆中抽牌。B从适当的牌堆中返回(加密的)卡牌。

2
也许玩家们可以在游戏开始时交换他们牌堆的哈希值。
然后当游戏结束时,玩家们交换他们牌堆在游戏开始时的实际构成。现在,玩家的客户端可以检查它是否与之前得到的哈希值匹配,然后检查所有已经进行的移动是否适用于这些牌堆。
唯一的问题是如何最初洗牌。您需要确保玩家不能只按任意顺序开始他的牌堆。
也许可以通过从某个特定来源获取随机性来生成玩家的初始牌堆顺序。但是必须保证另一个玩家在游戏结束之前无法知道另一个玩家的牌堆顺序,但仍然可以检查玩家没有篡改他们的牌堆。不确定如何实现这一点。
另一个想法。也许不是在游戏开始时生成随机牌堆,而是在游戏进行中生成。
每当玩家需要从牌堆中取出一张新卡牌时,对手会发送给他们一些随机种子,用于选择下一张卡牌。
这样用户就没有办法知道卡牌将按什么顺序出现在他们的牌堆中。
我不确定如何防止其他玩家能够知道他们将为对手选择哪张卡牌。如果种子用于从对手拥有哈希值以验证的某种随机排序的卡牌列表中选择一张牌,他们可以检查所有可能的卡牌组合,并找出与哈希值匹配的那个,从而能够通过发送一个会导致选择该卡牌的种子来指定他们想让另一个玩家抽取的卡牌。

这将解决修改问题,但我认为它仍然存在一个问题,即玩家可以检查自己的牌组以查看他们将抽到哪些卡牌。 - so12311
啊,忘了问题的那个关键部分了! - Acorn

2

虽然使用中央服务器可能是最简单的方法,但如果您想避免使用它,则可以使用分布式系统,其中每个玩游戏的人都是其他玩家卡组的存储主机(不是自己或对手的卡组,而是其他任何人的)。我相信Limewire和Bittorrent都是这样工作的,因此您可以通过研究这些源代码来获得一些想法。


这是一个有趣的想法,尽管它让我对勾结产生了一些担忧。 - so12311
当然仍然有可能,但由于不容易确定你拥有谁的数据,并且你对除了自己的游戏之外的任何人都没有直接的兴趣,所以这种可能性较小。 - jcomeau_ictx

0

这是Acorns方法的改编:

设置:

每个玩家都有自己的牌组(一组有序的卡牌)...这些牌组是公开的,并按照自然顺序排列。

每个玩家需要一个非对称(签名)密钥对,公钥交换。

现在来解决随机性问题:

由于玩家可能无法提前看到他们将抽到哪些卡牌,对手将成为我们随机数的来源:

当我们需要一些随机值时,我们向对手请求...随机值与后续要检查的移动一起存储。

由于对手可能无法操纵我们卡牌的顺序,我们将接收到的随机值用我们的私钥进行签名...该签名的值将成为我们的RNG种子(对对手来说是不可预测的),稍后对手可以验证签名是否与他/她生成的随机数相匹配(因此我们也存储签名,并在游戏后交换它们)。

由于我们可以在每一轮进行这种随机数交换,所以随机值对于玩家来说事先是未知的->不能窥视我们牌堆的顺序。

现在我们有了一个种子随机数生成器,因此我们可以获得从该“共享”随机值派生的随机值... 这样我们就可以从我们的牌堆中抽取“随机”(完全确定性的,具有可重复验证的结果)的牌...牌堆/堆不需要洗牌,因为我们可以从我们的随机数生成器中获取随机位置

由于我们已经交换了所有的随机值、签名、初始牌堆(按总顺序)和所有移动,因此我们可以在之后重播整个游戏并检查是否存在异常情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接