#cipher #solitaire #cryptonomicon #pontifex

app thieves-cant

布鲁斯·施奈尔设计的Solitaire密码的Rust实现

3 个版本 (稳定版)

使用旧的Rust 2015

1.0.1 2018年12月7日
1.0.0 2018年12月3日
0.1.0 2018年4月23日

#1301 in 密码学

GPL-3.0 许可证

41KB
883 行代码

盗贼密语

布鲁斯·施奈尔设计的Solitaire密码的Rust实现。

摘要

Solitaire密码是由著名的密码学家布鲁斯·施奈尔为尼尔·斯蒂芬森的小说《密码宇宙》发明的。它涉及使用标准的52张扑克牌牌组,以及两张(最好是不同的)小丑牌来生成密钥流,该密钥流与明文消息结合以产生加密的密文。您可以在这里了解更多关于Solitaire密码的信息。

算法

Solitaire密码中有三种不同的操作:初始化牌组、加密明文和解密密文。每个操作都使用了许多相同的步骤,只有一两个不同。以下是详细步骤:

初始化牌组

要开始生成密钥流,用户必须首先正确地初始化牌组。通常使用密码;一个单词或句子。在我们的例子中,我们将使用密码 SOLITAIRE。从第一步开始,然后对密码中的每个字母重复第二步至第六步。在我们的例子中,您将执行这些步骤九次。请注意,您始终以牌面向上工作,因此牌组顶部的牌始终对您可见。

  1. 重新排列牌组。按照面值和花色重新排列牌组:从A到K,每个花色按桥牌顺序(梅花、方块、红心、黑桃)。最后是两张小丑牌,称为A和B(如何区分小丑牌由您决定,但您必须始终如一地确定A和B是哪两张小丑牌)。

  2. 移动小丑A。在牌组中找到小丑A,并将其向下移动一个位置;也就是说,将其与下面的牌交换。如果小丑A是牌组中的最后一张牌,将其放在牌组的顶部下面。

  3. 移动小丑B。在牌组中找到小丑B,并将其向下移动两个位置。如果小丑B是牌组的倒数第二张牌,将其放在牌组的顶部下面。如果小丑B是牌组中的最后一张牌,将其放在从顶部数第二张牌的下面。

  4. 进行三次分割。将牌堆中第一张Joker牌之上的所有牌(无论哪张Joker牌)与第二张Joker牌之下的所有牌进行交换。例如:如果牌堆排列如下

4 of spades, 3 of clubs, Joker B, ...rest of the cards..., Joker A, King of diamonds, Queen of
Hearts

执行三次分割后,牌堆将看起来像这样

King of diamonds, Queen of Hearts, Joker B, ...rest of the cards..., Joker A, 4 of spades, 3 of
clubs
  1. 进行计数分割。查看牌堆中的最后一张牌,并使用下方的卡片值表将其转换为数字。然后,从牌堆顶部开始向下数这么多张牌,并在那里分割牌堆。将分割后的牌放在牌堆的最后一张牌之前。例如,如果您的牌堆排列如下
3 of clubs, 2 of hearts, 9 of spades, ...rest of the cards..., 2 of clubs

执行计数分割后,牌堆将看起来像这样

9 of spades, ...rest of the cards..., 3 of clubs, 2 of hearts, 2 of clubs

卡片值表

A 2 3 4 5 6 7 8 9 10 J Q K
黑桃 1 2 3 4 5 6 7 8 9 10 11 12 13
红心 14 15 16 17 18 19 20 21 22 23 24 25 26
方块 27 28 29 30 31 32 33 34 35 36 37 38 39
梅花 40 41 42 43 44 45 46 47 48 49 50 51 52

注意,无论哪张,Jokers的值都是53。

  1. 进行值分割。值分割与计数分割非常相似,只是它使用密码中的字母值来确定在牌堆中分割的位置。要确定在牌堆中分割的位置,请参考下方的字母值表。分割牌堆后,将卡片放在牌堆的最后一张牌之前,就像进行计数分割时那样。例如,使用上面的密码SOLITAIRE,以字母S为密钥,您向下数十九张牌,分割牌堆,然后将这十九张牌放在牌堆的最后一张牌之前。

字母值表

字母
A 1
B 2
C 3
D 4
E 5
F 6
G 7
H 8
I 9
J 10
K 11
L 12
M 13
N 14
O 15
P 16
Q 17
R 18
S 19
T 20
U 21
V 22
W 23
X 24
Y 25
Z 26

加密明文

要加密明文,您必须首先生成一个与要加密的明文长度相同的密钥流。然后,对于明文消息中的每个字符,将其与相应的密钥流字符相加,以产生加密的密文字符。具体步骤如下

  1. 编码特殊字符。Solitaire密码不支持对非字母(A-Z)字符进行操作。因此,您必须首先将任何特殊字符编码为一系列字母字符的组合。Thieves' Cant只支持有限的非字母字符子集,如下方的非字母编码表所示。例如:消息HELLO WORLD将被编码为HELLOXZAWORLD

非字母编码表

字符 编码
(空格) XZA
. XZB
, XZC
' XZD
? XZE
! XZF
  1. 分组明文字符。按照惯例,明文消息应分成每组五个字符。如果最后一个组中没有五个字符,则使用'Z'字符填充消息。例如:消息HELLOXZAWORLD被分成字符串HELLO XZAWO RLDZZ

  2. 将每个字符转换为数字。使用上面的字母值表,将明文中的每个字母转换为相应的数字值。

  3. 设置牌堆。使用上面的步骤,使用给定的密码设置牌堆。

  4. 找到输出牌。按照“键入牌组”操作中的步骤2-6进行操作。然后,为了找到输出牌,首先查看牌组的顶部牌,并使用上面的牌值表将其转换为数字。然后,向下数这么多张牌,并记录你数过的牌之后的牌。例如,如果你有以下牌组

3 of clubs, 2 of diamonds, ace of spades, 9 of hearts, ...rest of the cards...

输出牌将是红桃9,其值为35

  1. 生成密文字符。将输出牌的值加上明文字符的值。如果得到的总和大于26,则从总和减去26,直到值小于或等于26。使用上面的字母值表将该值转换为字符。

  2. 重复操作。对明文信息中的每个字符重复步骤5和6。

按照上述步骤,使用密钥SOLITAIRE对文本HI THERE进行加密,将得到密文OEPXV NZLBM

解密密文

为了解密密文,用户应遵循上面“加密明文”部分中的所有步骤。然而,在第4步生成明文值时,你应从牌输出值中减去密文字符的值。如果得到的结果小于1,则将26加到该值上,直到它在1到26之间。然后使用字母值表将该值转换为字符。请记住从明文的末尾移除任何'Z'填充字符,并将特殊字符编码解码为正确的字符。

用法

$ tvct deck -n // Generate a new, randomly shuffled deck.

$ tvct deck -n -f key.deck // Generate a new, randomly shuffled deck, in a new location.

$ tvct deck -n -k "SOLITAIRE" -f key.deck // Generate a new deck, keyed using the string "SOLITAIRE", with a new file location.

$ tvct deck // Print out the order of the cards within the deck.

$ tvct encrypt "HELLO WORLD" // Encrypt a message; output: OAHBF TUMYB BELRT

$ tvct encrypt -f key.deck "HELLO WORLD" // Encrypt the message using a different key file

$ tvct decrypt "OAHBF TUMYB BELRT" // Decrypt the ciphertext, output: HELLO WORLD

$ tvct decrypt -f key.deck "OOAHBF TUMYB BELRT" // Decrypt the ciphertext with a different key file

依赖项

~3.5–4.5MB
~70K SLoC