机器学习可以解码 SHA256 哈希吗?

机器算法验证 机器学习 物流
2022-02-09 10:13:00

我有一个 64 个字符的 SHA256 哈希。

我希望训练一个可以预测用于生成哈希的明文是否以 1 开头的模型。

不管这是否“可能”,什么算法是最好的方法?

我最初的想法:

  • 生成大量以 1 开头的散列样本和大量不以 1 开头的散列样本
  • 将散列的 64 个字符中的每一个设置为某种无监督逻辑回归模型的参数。
  • 通过告诉模型何时正确/错误来训练模型。
  • 希望能够创建一个模型,该模型可以预测明文是否以 1 开头,并且具有足够高的准确度(并且具有不错的 kappa)
4个回答

这不是一个真正的统计答案,但是:

,您无法从哈希中确定明文的第一个字符,因为对于给定的哈希,没有“明文”之类的东西。

SHA-256 是一种散列算法。无论您的明文是什么,您都会得到一个 32 字节的签名,通常表示为 64 个字符的十六进制字符串。可能的明文远多于可能的 64 个字符的十六进制字符串 - 可以从任意数量的不同明文生成相同的哈希。没有理由相信第一个字符是/不是“1”在所有产生给定哈希的明文中是统一的。

SHA256 被设计为尽可能随机,因此您不太可能能够将来自 1 前缀明文的散列与不带前缀的明文分开;散列字符串的任何特征都不应该泄露该信息。

不管这是否“可能”,什么算法是最好的方法?

对不起,这是一个无意义的问题。如果某事是不可能的,那么您就无法寻找解决问题的最佳方法。

在这种情况下,这绝对是不可能的,因为散列是一种单向函数:多个输入(实际上是无限的)可以产生相同的输出。如果输入的第一位本身会以某种方式影响特定哈希值的概率,这将意味着哈希算法完全有缺陷。

您当然可以训练神经网络、线性分类器、SVM 等来尝试预测。如果您能够可靠地预测某个散列算法的输出输入,这将证明该算法毫无价值。我想说,对于像 SHA256 这样广泛使用的算法,这种可能性非常低。然而,快速排除新的、未经证实和未经测试的散列算法是一种合理的方法。

虽然不能用一个例子来证明否定的。我仍然觉得一个例子会很有启发性。也许有用。它确实显示了人们将如何(尝试)解决类似的问题。

我想进行二进制预测的情况下,使用二进制向量的特征,随机森林是一个不错的选择。我想这种回答你问题的第二部分:什么是好的算法。

我们很想将 SHA256 字符串预处理为二进制(布尔)向量,因为每个位在统计上是独立的,因此每个位都是一个很好的特征。这将使我们的输入成为 256 个元素的布尔向量。

演示

这是一个演示如何使用 Julia DecisionTree.jl来完成整个事情。

您可以将以下内容复制粘贴到 julia 提示符中。

using SHA
using DecisionTree
using Statistics: mean
using Random: randstring

const maxlen=10_000 # longest string (document) to be hashed.

gen_plaintext(x) = gen_plaintext(Val{x}())
gen_plaintext(::Val{true}) = "1" * randstring(rand(0:maxlen-1))
gen_plaintext(::Val{false}) = randstring(rand(1:maxlen))


bitvector(x) = BitVector(digits(x, base=2, pad=8sizeof(x)))
bitvector(x::AbstractVector) = reduce(vcat, bitvector.(x))

function gen_observation(class)
    plaintext = gen_plaintext(class)
    obs = bitvector(sha256(plaintext))
    obs
end

function feature_mat(obs)
    convert(Array, reduce(hcat, obs)')
end

########################################

const train_labels = rand(Bool, 100_000)
const train_obs = gen_observation.(train_labels)
const train_feature_mat = feature_mat(train_obs)

const test_labels = rand(Bool, 100_000)
const test_obs = gen_observation.(test_labels)
const test_feature_mat = feature_mat(test_obs)


# Train the model
const model = build_forest(train_labels, train_feature_mat)
@show model


#Training Set accuracy:
@show mean(apply_forest(model, train_feature_mat) .== train_labels)

#Test Set accuracy:
@show mean(apply_forest(model, test_feature_mat) .== test_labels)

结果

当我这样做时,训练了 100,000 个长度不超过 10,000 的随机 ASCII 字符串。这是我看到的结果:

训练模型

julia> const model = build_forest(train_labels, train_feature_mat)
Ensemble of Decision Trees
Trees:      10
Avg Leaves: 16124.7
Avg Depth:  17.9

训练集准确率:

julia> mean(apply_forest(model, train_feature_mat) .== train_labels)
0.95162

测试集精度:

julia> mean(apply_forest(model, test_feature_mat) .== test_labels)
0.5016

讨论

所以这基本上没什么。我们从训练集的 95% 到测试集的 50% 以上。有人可以应用适当的假设检验,看看我们是否可以拒绝零
假设,但我很确定我们不能。这是对猜测率的微小改进。

这表明它无法学习。如果是随机森林,则可以从良好拟合到仅达到猜测率。随机森林非常有能力学习困难的输入。如果有什么要学的,我希望至少有几个百分点。

您可以通过更改代码来使用不同的哈希函数。这可能很有趣,当在内置hash函数中使用 julia 时,我得到了基本相同的结果(这不是加密安全的 hsah,但仍然是一个很好的哈希,因此确实应该分开发送类似的字符串)。我也得到了基本相同的结果 CRC32c