python - 整数算法/方法的对称密码
问题描述
我正在寻找一种双射变换,我不能将其用于加密,而是用于混淆。这个想法是取整数,1, 2, 3, 4
并将它们投影到另一个轴上。主要思想是确保在 domainA
中投影时两个相邻整数与 domain 之间的最大距离B
。
我用 Python 编写了以下代码,但目标语言是 PHP。我不介意实现,只有算法/方法很重要。
示例n = 8
:
A B
0 <-> 4
1 <-> 5
2 <-> 6
3 <-> 0
4 <-> 2
5 <-> 7
6 <-> 3
7 <-> 1
一种简单的实现是构建一个随机的一对一对应表,然后用作O(1)变换函数。
a = func(0) # a = 4
b = func(a) # b = 0
最有效的算法
最有效的算法包括构建一个需要O(n)内存并在O(1)中执行的哈希表。
class Shuffle:
def __init__(self, n, seed=42):
self._table = list(range(n))
random.seed(seed)
random.shuffle(self._table)
def enc(self, n):
return self._table[n]
def dec(self, n):
return self._table.index(n)
不幸的是,我正在寻找一种更轻、或许更简单的算法。
愚蠢的尝试
有趣的部分来了,因为我尝试使用 One-Time-Pad (OTP),一些对称加密算法 (Blowfish),但没有成功。最终,我尝试使用一种愚蠢有趣的幼稚实现:
def enigma(input): # Because why not?
a = deque([1, 3, 0, 4, 2])
b = deque([3, 0, 4, 2, 1])
u = [0 for i in range(len(input))]
for i, c in enumerate(input):
u[i] = a.index(b.index([4, 3, 2, 1, 0][b[a[c]]]))
a.rotate(1)
b.rotate(3)
return u
def shuffle(n, seed=81293): # Because enigma is not enough
random.seed(seed)
serie = list(range(len(n)))
random.shuffle(serie)
for i, j in enumerate(serie):
n = n.copy()
n[i], n[j] = n[j], n[i]
return n
def int2five(decimal): # Base 10 -> Base 5
radix = 5
s = [0 for i in range(9)]
i = 0
while (decimal):
s[i] = decimal % radix
decimal = decimal // radix
i += 1
return list(reversed(s))
def five2int(five): # Base 5 -> Base 10
return int(''.join([str(x) for x in five]), 5)
def enc(n):
return five2int(enigma(shuffle(int2five(n))))
def dec(s):
return five2int(shuffle(enigma(int2five(s))))
结果:
>>> ['%d <-> %d' % (i, enc(i)) for i in range(7)]
['0 <-> 1729928',
'1 <-> 558053',
'2 <-> 1339303',
'3 <-> 948678',
'4 <-> 167428',
'5 <-> 1729943',
'6 <-> 558068']
为什么我选择base 5?我选择我的工作范围为[0..2e6[
,因此通过查看最佳基础来最大化覆盖范围(是的,以下代码很难看):
>>> [(d, l, s, 100 - round(s/n*100,1)) for (d, l, s) in sorted([(k, round(math.log(n, k), 3), n - k**(math.floor(math.log(n, k))) - 1)
for k in range(2, 100)], key=lambda k: k[2])[0:10]]
[(5, 9.015, 46873, 97.7),
(18, 5.02, 110430, 94.5),
(37, 4.018, 125837, 93.7),
(11, 6.051, 228437, 88.6),
(6, 8.097, 320382, 84.0),...]
我注意到基数 5 可以用 9 位数字表示,覆盖了我范围的 97%。第二好的候选者是基数 18,覆盖率为 94%。
解决方案
就像您想在这里使用格式保留加密一样,一个好的是 Mihir Bellare 的 FFX(此处为白皮书)。
正如您所注意到的,您不会找到任何基于旋转的简单算法,可以将输入域映射X
到forY
和in any range 。Y = X
f: X->Y
X
[x0..xn]
使用这种格式保留加密,您需要将您的范围指定为:
range = radix ** exponent - 1
在您的情况下,您选择基数 5 和 9 作为指数。
使用 FFX 加密
对于实现,您可以使用pyffx,它是基于 Feistel 的加密 (FFX) 的实现。
class Five(pyffx.String):
def __init__(self, ffx, length, **kwargs):
super(Five, self).__init__(ffx, '01234', length, **kwargs)
def pack(self, v):
return super(Five, self).pack(str(v).zfill(self.length))
def unpack(self, v, t):
return int(super(Five, self).unpack(v, t))
然后
>>> import pyffx
>>> radix, exponent = 5, 9
>>> e = Five(b'secret', exponent)
>>> e.encrypt(123)
321301321
在任意域上加密
如前所述,FFX 仅对由radix ** exponent - 1
. 如果您希望在任意域上加密,可以使用名为Cycle Walking的方法,描述如下:
def cycle_walking (x, encipher) {
if encipher(x) is an element of M
return encipher(x)
else
return cycle_walking(encipher(x))
}
为此,您需要使用严格大于目标域 M 的域 N。
推荐阅读
- python - 优化函数以将(有向)边缘列表转换为邻接列表
- xcode - 您如何使用 Xcode 11.0 中的“嵌入按钮”操作
- heroku - 如何将 Act 框架应用程序部署到 Heroku?
- php - 如何在 VS Code 中为已删除的 mysql_* 函数添加智能感知
- flutter - 在搜索委托 buildResults() 中使用 setstate() ..?
- java - 使用 Firestore 中的数据更新片段中的 TexView 时出现 NullPointer 异常
- javascript - 在赛普拉斯测试期间,Chrome 错误是否有解决方法:“需要用户手势才能在卸载前对话框中使用”
- javascript - Switch 语句运行不止一次
- oracle - Oracle SQL Loader:数据文件中的 FILLER 字段超过最大长度
- aws-lambda - OPTIONS 预检的“缺少身份验证令牌”