Lobsters Hottest 工具

摘要

一篇技术博文,探讨随机性、Linux熵以及构建一个名为morerandom的工具,该工具使用WASM插件来为系统熵池提供熵。

<p><a href="https://lobste.rs/s/aml5vb/entropy">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/06/08 03:18

# 熵源: https://arch.dog/bark/entropy ## 必须要全部收集 最近我一直在思考随机性。不知道为什么,可能是在进行某种自我大脑的元分析吧。不管怎样。事先声明,我不是密码学家,数学能力也不算强。这类话题我通常听从别人的意见。总之!熵!它就在我们身边!众所周知,Silicon Graphics (https://www.sciencenews.org/article/lava-lamp-randomness) 和 Cloudflare (https://blog.cloudflare.com/randomness-101-lavarand-in-production/) 曾将摄像头对准熔岩灯(以及后来各种其他源)来收集熵,然后将其输入随机性算法。能够生成不可预测的字节流是一项很有价值的能力,具体取决于你的工作负载。但即使你的工作负载**不需要**它,做这件事也很有趣。特别是当你可以在看似无用的实现细节中大做文章时!有一天我坐下来,对 Linux 熵产生了浓厚的兴趣。你知道吗,你只需向 `/dev/{u}random` 写入数据,就能给 Linux 系统的熵池“喂”数据?更进一步,你知道 `urandom` 和 `random` 之间其实差别很小,除了在系统认为拥有足够熵之前它们的行为有所不同吗?还有,它们在启动时会非常频繁地使用熵池自身的数据重新播种,启动完成后则每分钟重新播种一次?我强烈推荐 Wireguard 创建者 Jason Donenfeld 的这场演讲 (https://youtube.com/watch?v=-_yzaSp2xtY),其中讲述了内核 random.c 实现的现代化改造,并透露了一些未来计划。观看那场演讲也能为本文后面我会提到的一些决策提供更多背景信息。 熵池的数据来自多个源,但内核层面只能消耗这么多。从用户空间层面来看,能够“喂”给池子数据开启了无数可能性。真的有必要吗?不,其实没有,但这件事能让我会心一笑,这就够了。反正**也没坏处**。于是我坐下来,开始拼凑一个概念:我能用 WASM 插件向系统熵池喂数据吗?选择 WASM 是因为我只想试验用它作为插件系统。我之前在项目中试验过插件系统,包括编写各种 Minecraft Bukkit 插件,以及在我的 Platypus 服务器监控 (https://git.gmem.ca/arch/platypus/src/branch/staging/pluginhandler/pluginhandler.go) 项目中使用一个完整的 Go 解释器 (https://github.com/containous/yaegi)。我理解相关理论以及**一些**实际实现做法,但通常缺少的是一个能让人们使用自己最熟悉语言的可靠运行时。WASM 为我们提供了一个坚实基础,许多语言都能编译到它。所以虽然这只是一个“借口”来更多地玩 WASM,我的大脑仍然非常专注于给熵池喂数据。 于是,morerandom (https://git.gmem.ca/arch/morerandom) 诞生了。没费多少搜索就找到了 Extism (https://github.com/extism/extism),它是专为其他程序设计的插件系统。在“宿主”端的实现非常简单,不过有一点小瑕疵:在插件端你需要使用他们的库(例如,我有一个 `1.wat` (https://git.gmem.ca/arch/morerandom/src/branch/main/plugins/1.wat) 插件,只是回显 `1`,但复杂程度有点过度了)。最初(也是当前)的实现只是调用 WASM 二进制的 `random() -> FnResult` 函数,然后返回该结果中包含的原始字节。这足够作为概念验证了,但紧接着齿轮开始转动。我可以用麦克风来获取熵!用摄像头!但是……这些设备并非我所有机器都能访问……而且将它们暴露给 WASM 插件既棘手又有风险……所以下一步是在二进制自身中添加麦克风和摄像头支持。由于 Rust 中已经有现成的库,这相当容易,我只需输出原始字节。很简单!我实质上用 Rust 复刻了 lavarand。酷!但是……等等,我家实验环境中的其他机器可能也想把这些熵喂进它们自己的池子……我该怎么做?更进一步,如何避免恶意客户端提取原始的麦克风和摄像头字节,从而可能窥探到我?于是兔子洞越挖越深。 为了实现通过网络获取熵,我添加了 gRPC(Google 的远程过程调用,虽然现在它已不属于 Google 项目)。对于不熟悉的人来说,gRPC 从高层次上看是一种让代码库在**远程机器上**调用函数的方式。我们定义了一个共同的模式 (https://git.gmem.ca/arch/morerandom/src/branch/main/proto/service.proto),二进制文件使用该模式对函数调用的结果进行编码和解码,通常比使用 HTTP 调用更快更小。服务器上的一个小型客户端二进制连接到我的台式机上的 morerandom 服务器,并调用服务器暴露的 `get_random()` (https://git.gmem.ca/arch/morerandom/src/branch/main/src/bin/client.rs#L36) 函数。客户端获取字节,然后将其打印到标准输出。我们很开心,对吧?还没完全。我们仍然有提取原始数据的问题。为此,我从 Linux 的 `random.c` 实现中借鉴了一页,开始研究 ChaCha20 (https://en.wikipedia.org/wiki/Salsa20#ChaCha_variant) 算法。我不会假装完全理解该算法,但我**目前**的理解是:给它一个种子,它就能推断出几乎无尽的加密数据流——在我们的情况下就是随机性。对于种子,我们使用自己收集的熵!为了简化,我们首先使用现代的 BLAKE3 (https://github.com/BLAKE3-team/BLAKE3) 哈希算法对数据进行哈希处理,输入是激活的插件、摄像头和麦克风的字节,然后使用产生的哈希作为我们的熵种子。我实质上重新实现了 `/dev/{u}random`,最终结果是一个 ChaCha20 流。与内核实现的相似之处更进一步:服务器每隔一分钟左右通过从插件、麦克风和摄像头获取数据,并混合现有 ChaCha20 流中的几个字节,来重新为这个熵播种。理论上这意味着即使你猜到了服务器插件、摄像头和麦克风的初始状态,你仍需要持续控制这些输入才能保持对种子的控制——这并非不可能,也因此我不建议将其作为唯一熵源——但实际上**非常**困难。 后来我添加了一个 HTTP 服务器,用于演示随机数和布尔值的生成,以便在其他项目中轻松使用(我想也是为了稍微炫耀一下)。 **MoreRandom 熵流图** 我已经对代码库迭代了几次,清理并使其更健壮,但核心的熵收集流程基本保持不变。除了服务器之外,还有一个独立的二进制文件,它执行一次性收集、哈希和播种,然后将 32 字节数据打印到标准输出。 *如果我查看原始数据,我会通过 xxd 运行它,否则输出会非常乱码!原始字节很有趣。* ``` # 一次运行 00000000: 8d55 7c9f 893f f21a af3b 9436 7928 a8ed .U|..?...;.6y(.. 00000010: 3a55 da08 313d ab65 96a5 9d00 c8ff b40d :U..1=.e........ # 另一次运行 00000000: 2071 208f ba71 4e0e 248f da65 b701 ac8b q ..qN.$..e.... 00000010: 2c43 b572 72b4 34ca ad42 8b2e 24d2 76c1 ,C.rr.4..B..$.v. ``` 一切都已就位,我家庭实验室里的服务器运行一个 systemd 定时器,每天使用 morerandom 客户端二进制从我的台式机获取熵,并将其写入自己的熵池。我觉得这个项目基本完成了!这很有成就感。我认为实现相当健壮,但我确信自己还会找到其他可以摆弄的东西。我想扩展 WASM 的能力和可用函数,但目前确实没有什么好理由。这个项目是学习 ChaCha20、BLAKE3 的一次绝佳经历,也让我对 Linux 上的熵变得有点**过于**痴迷。如果你有任何反馈、问题等,欢迎在联邦宇宙 (https://floofy.tech/@arch) 上联系我!

相似文章

什么是随机生成?

Lobsters Hottest

本文探讨了计算机中的伪随机数生成,重点聚焦于线性同余生成器(LCG)及其质量可视化。文章还提及了 Cloudflare 的熔岩灯等熵源,并作为基于属性的测试的前导内容。

在CSS中尝试Random()函数

Hacker News Top

一篇探索新的CSS random()函数的博客文章,该函数允许将属性设置为随机值以实现创意设计,包含演示和浏览器支持详情。

熵到底是什么?

Wired

一篇关于熵真正含义的深刻解释,将常见的“无序”比喻与使用掷骰子类比的概率解释进行对比。