最先进的加密技术迈向后量子时代

TinySSH 服务器旨在消除后量子密码学的弱点。
111 位读者喜欢这篇文章。
Parts, modules, containers for software

Opensource.com

保密性是计算机科学最重要的功能之一。如果电子保密突然崩溃为完全透明,我们将无法进行电子商务,我们将无法进行私密通信,我们过去的通信将全球可见,并且我们将在无数方面受到严重影响,这将从根本上改变我们的工作和生活能力。想想我们每天花多少时间使用密码、锁屏图案、无线钥匙和生物识别技术来维护我们的保密性,这些技术限制访问以保护我们以及它们失败的后果。

公钥密码系统构成了我们保密性的关键方面。每天都会进行数十亿次通过公共媒介建立私密通信的能力。如果出现能够揭示这种私密对话的技术,其后果将是无法估量的。

在量子计算领域,这样一种技术正在兴起。能够执行 Shor 算法 以直接威胁常用公钥方案(RSA、传统 Diffie-Hellman 和椭圆曲线)的潜在硬件可能比我们预期的更接近实现。D-Wave 公司 承诺交付 一台拥有 5,000 量子比特的绝热量子计算机,但这台机器无法直接运行 Shor 算法,但如果可以,TLS 和 SSH 将受到严重威胁。纠正我们的密码系统迫在眉睫。

与普遍存在的 CMOS 技术渗透到我们的电子设备中不同,量子逻辑 是一种截然不同的设计,它适应不确定性和不确定性,从“量子比特”的形式开始,量子比特是一种二元值,但违反直觉地可以同时为正和负。这些新型计算设备的威胁是非常真实的,以至于美国国家标准与技术研究院 发起了一项竞赛,以征集抗量子公钥密码系统。

在这种争议中,TinySSH,一个专注于嵌入式的极简 SSH 服务器,实现了一种混合密钥交换,涉及 NTRU Prime(NIST 竞赛中的 第二轮决赛入围者)与传统的 ed25519 椭圆曲线密钥相结合。这种方法允许 ed25519 密钥通常保持使用,但透明地添加了“量子前向保密”的外观。OpenSSH v8.0 采用了这种方法

commit dfd591618cdf2c96727ac0eb65f89cf54af0d97e
Author: djm@openbsd.org 
Date:   Mon Jan 21 10:20:12 2019 +0000

    upstream: Add support for a PQC KEX/KEM:
    
    sntrup4591761x25519-sha512@tinyssh.org using the Streamlined NTRU Prime
    4591^761 implementation from SUPERCOP coupled with X25519 as a stop-loss. Not
    enabled by default.

TinySSH 服务器严格遵守与 Daniel J. Bernstein 相关的密码学技术,主要依靠 chacha20-poly1305 处理 SSH 会话流量;不允许使用其他密码。同样,它也不允许密码登录,但要求用户 ed25519 密钥就位才能成功连接。这些约束可能看起来很严格,但它们也是最佳实践。

TinySSH 服务器可以补充或替换 Linux OpenSSH 守护程序,并且提供了使用 inetd 或 systemd 套接字激活执行此操作的说明。TinySSH 的极简主义立场也非常适合 Linux 容器设计,但要实现这一点需要一些技巧。

无论如何,NTRU 密钥交换已在 OpenSSH 中部署了足够长的时间,以至于普遍使用是一个合理的目标。它也 远远优于 ssh-rsa 密钥,ssh-rsa 密钥多年来一直是默认的 SSH2 用户和主机密钥类型,但现在由于依赖 SHA-1 而被严重削弱。虽然 TinySSH 的作者 Jan Mojžíš 暗示 NTRU 密钥交换尚未达到“准备好用于生产环境(包括后量子密码学)”的程度,但启用它肯定比继续依赖 ssh-rsa 更安全。

设计一个容器

一般来说,容器共享一个公共内核,但用户空间是分开的,这使它们与虚拟机区分开来。最直接的 Linux 容器可能从 BusyBox 开始。对于本练习,我获得了使用 musl libc(而不是 uclibc)编译的 1.31.0 版本。选择您合适的架构;我选择了标准的 x86_64 PC。

作为 root 用户,并将 BusyBox 复制到本地目录中,输入以下命令以组织和启动 /home/pqc 作为您的后量子容器

# mkdir -p /home/pqc/bin
# cp busybox-x86_64 /home/pqc/bin
# mkdir /home/pqc/root
# mkdir /home/pqc/ssh
# mkdir -p /home/pqc/var/log
# cd /home/pqc/bin
# chmod 755 busybox-x86_64
# ./busybox-x86_64 --list | awk '{print "ln -s busybox-x86_64 " $0}' | sh
# cd /home/pqc
# tar cf - /usr/share/zoneinfo | tar xvpf -
# ln -s bin sbin
# mkdir /home/pqc/etc
# echo 'NAME="Post-Quantum Container"' > /home/pqc/etc/os-release
# echo 'root::0:0:root:/root:/bin/sh' > /home/pqc/etc/passwd
# echo root:x:0: > /etc/group
# echo 'console::respawn:/bin/getty 38400 /dev/console' > /home/pqc/etc/inittab
# echo '::sysinit:/bin/syslogd' >> /home/pqc/etc/inittab
# echo '::sysinit:/bin/inetd' >> /home/pqc/etc/inittab
# echo 'tssh 2222/tcp' > /home/pqc/etc/services
# echo "tssh stream tcp nowait root /ssh/tinysshd tinysshd -lpsv \
   -x sftp=/ssh/sftp-server /etc/tinyssh/sshkeydir" > /home/pqc/etc/inetd.conf
# systemd-nspawn -bD /home/pqc

在执行上面的最终 nspawn 命令后,将启动一个新的用户空间,允许您以 root 身份登录到模拟基本 Unix System V 的环境中

Spawning container pqc on /home/pqc.
Press ^] three times within 1s to kill container.
Jun 23 14:44:04 pqc syslog.info syslogd started: BusyBox v1.31.0
Jun 23 19:44:04 pqc daemon.info : starting pid 4, tty '': '/bin/inetd'
Jun 23 19:44:04 pqc daemon.err inetd[5]: tssh/tcp: unknown service
Jun 23 19:44:04 pqc daemon.info : starting pid 6, tty '/dev/console': '/bin/getty 38400 /dev/console'

pqc login: root
Jun 23 19:44:06 pqc auth.info login[6]: root login on 'console'
# ps
PID   USER     TIME  COMMAND
    1 root      0:00 init
    3 root      0:00 /bin/syslogd
    5 root      0:00 /bin/inetd
    6 root      0:00 -sh
    7 root      0:00 ps
#

上面的 inetd 守护程序配置为启动 TinySSH,TinySSH 尚未出现在您的容器镜像中。您可以构建 TinySSH 并返回到容器进行安装。

如果您从未见过 nspawn 容器,请花一些时间探索它,然后再继续。如果您想关闭它,请停止它

# halt
The system is going down NOW!
Sent SIGTERM to all processes
Jun 27 21:51:42 pqc daemon.info : The system is going down NOW!
Jun 27 16:51:42 pqc syslog.info syslogd exiting
Sent SIGKILL to all processes
Requesting system halt
Container pqc has been shut down.

小心不要意外停止主机。

如果您在上面的配置中为 root 帐户添加了密码,它将使用 /etc/passwd 文件中的旧 DES 哈希进行存储。只有 nspawn 控制台才允许密码登录;TinySSH 忽略密码。如果您更喜欢影子密码,请在容器内运行以下命令

# echo root:::::::: > /etc/shadow
# chmod 600 /etc/shadow
# passwd -a sha512 root

最终,您将希望禁用控制台 getty 并仅允许通过 TinySSH 登录。这可以通过注释掉 /home/pqc/etc/inittab 的第一行并为 systemd 放置以下服务文件来完成

# echo '[Unit]
Description=pqc container

[Service]
ExecStart=/usr/bin/systemd-nspawn -bD /home/pqc
' > /etc/systemd/system/pqc.service

您尚未准备好执行此操作。

构建 musl

可以通过各种机制在容器内使用 glibc 和现有主机代码。但是,这可能很困难,因为大多数程序中都有许多依赖项,复制起来可能很繁琐。以 Red Hat/CentOS 7 上的 sshd 服务器为例;将所有四十九个共享对象正确放置在容器中并非易事

$ ldd /usr/sbin/sshd
linux-vdso.so.1 =>  (0x00007ffeec564000)
libfipscheck.so.1 => /lib64/libfipscheck.so.1 (0x00007fafbfa3b000)
libwrap.so.0 => /lib64/libwrap.so.0 (0x00007fafbf830000)
libaudit.so.1 => /lib64/libaudit.so.1 (0x00007fafbf607000)
[...]
libkeyutils.so.1 => /lib64/libkeyutils.so.1 (0x00007fafb978e000)
libattr.so.1 => /lib64/libattr.so.1 (0x00007fafb9589000)
libelf.so.1 => /lib64/libelf.so.1 (0x00007fafb9371000)
libbz2.so.1 => /lib64/libbz2.so.1 (0x00007fafb9161000)

musl C 库 允许在编译对象中大幅减少或完全消除运行时链接器依赖项。您可以至少使用它来构建功能正常的 TinySSH。

为了构建 musl,您必须安装 GNU C 编译器和相关工具。如果尚未加载它们,请以 root 身份运行以下命令来获取它们(在 Red Hat/CentOS 平台上)

# yum group install 'Development Tools'

下载 musl,然后解压缩并编译它,最好以非 root 用户身份

$ tar xvzf musl-1.2.0.tar.gz
$ cd musl-1.2.0/
$ ./configure
$ make

…然后,以 root 身份

# make install

大多数安装将在 /usr/local/musl 中进行,但 /lib/ld-musl-x86_64.so.1 除外(它在 /lib64 中会更一致,但当编译静态二进制文件时,这种奇怪的位置是无关紧要的)。

特别值得关注的是 /usr/local/musl/bin/musl-gcc,这是一个简短的 shell 脚本,它使用带有备用 libc 链接的系统 C 编译器。

构建 TinySSH

TinySSH 在容器或基本操作系统安装中的灵活性需要构建过程中的细微变化,主要是将焦点从 glibc 转移到 musl,这并不困难。

处于各种淘汰阶段的旧系统也可以使用 TinySSH 来实现现代最佳实践密码。Red Hat/CentOS 5 可以很好地运行 TinySSH,但旧的 HP-UX 10.20 系统由于缺少 c99 支持而无法编译它。请注意,Red Hat/CentOS 8 包含支持 NTRU 的 OpenSSH 客户端和服务器;如果您的主机操作系统运行的是 OpenSSH v8 或更高版本,则 TinySSH 实际上仅适用于容器使用(在 Red Hat/CentOS 8 上启用 NTRU 密钥需要更改 /usr/share/crypto-policies/DEFAULT/opensshserver.txt 文件)。

下载 20190101.tar.gz 文件,并解压缩它

$ tar xvzf 20190101.tar.gz
$ cd tinyssh-20190101

没有 configure 步骤来执行构建,只需 make

$ make
sh -e make-tinyssh.sh
=== Sat Jun 27 18:00:33 CDT 2020 === obtaining compiler
=== Sat Jun 27 18:00:34 CDT 2020 ===   cc ok
=== Sat Jun 27 18:00:34 CDT 2020 === finishing
=== Sat Jun 27 18:00:34 CDT 2020 === checking compiler options
=== Sat Jun 27 18:00:34 CDT 2020 ===   -pedantic ok
[...]
=== Sat Jun 27 18:00:53 CDT 2020 ===   _tinyssh 6627
=== Sat Jun 27 18:00:53 CDT 2020 ===   74069 words of code
=== Sat Jun 27 18:00:53 CDT 2020 === finishing
=== Sat Jun 27 18:00:53 CDT 2020 === counting words of code
=== Sat Jun 27 18:00:53 CDT 2020 ===   sysdep 936
=== Sat Jun 27 18:00:53 CDT 2020 ===   tinyssh 38680
=== Sat Jun 27 18:00:53 CDT 2020 ===   crypto 21330
=== Sat Jun 27 18:00:53 CDT 2020 ===   60932 words of code
=== Sat Jun 27 18:00:53 CDT 2020 === finishing

这编译了一个 glibc 链接的 TinySSH,您可以显示依赖对象

$ ldd build/bin/tinysshd
	linux-vdso.so.1 =>  (0x00007ffce33e1000)
	libutil.so.1 => /lib64/libutil.so.1 (0x00007f53ade04000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f53ada36000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f53ae007000)

此二进制文件在主机操作系统的 SSH 服务器上运行良好,但尝试将其放置在具有明显依赖项的 nspawn 环境中将因调用 getpwnam() 而失败。与其调试似乎是 glibc 名称服务交换机问题的原因,不如使用 musl 重建代码

make clean
CC='/usr/local/musl/bin/musl-gcc -static' make
ldd build/bin/tinysshd

上面命令的最终输出应该是“not a dynamic executable”。

musl 库也支持使用动态共享对象,就像 glibc 一样。如果您正在 nspawn 中使用 musl 环境准备大量编译的 C 代码,请考虑在您的容器应用程序中使用共享库以减少内存消耗,甚至可以考虑重建 BusyBox 以使用共享对象。

无论如何,以 root 用户身份将静态 TinySSH 二进制文件移动到容器中

mv build/bin/tinysshd /home/pqc/ssh/
mv build/bin/tinysshd-makekey /home/pqc/ssh/
mv build/bin/tinysshd-printkey /home/pqc/ssh/

现在唯一缺少的是容器主机密钥和用户密钥。

安装密钥并测试登录

如果您之前停止了 pqc 容器,请重新启动它。确保您以 root 身份登录,并运行以下命令

mkdir /etc/tinyssh
/ssh/tinysshd-makekey /etc/tinyssh/sshkeydir

这将生成一个新的 ed25519 主机密钥。您可能更喜欢使用来自 OpenSSH 的现有密钥;这将在下面讨论。

此外,必须将 authorized_keys 文件安装到将托管登录的特定用户帐户中。如果您尚未生成此类密钥,请执行此操作。对于此测试用例,最好密钥没有密码

$ ssh-keygen -t ed25519 -f testkey
Generating public/private ed25519 key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in testkey.
Your public key has been saved in testkey.pub.
The key fingerprint is:
SHA256:tahsHrY5b0PqNMuxxDew35xS7FoLWNJ6VNOslUNCFao cfisher@yourhost.com
The key's randomart image is:
+--[ED25519 256]--+
|         .o.+.   |
|           * .   |
|          = *    |
|       . = = .   |
|      o E.o      |
|     o X. o      |
|      /o=o.      |
|     Bo%+*.o     |
|     .O+++=      |
+----[SHA256]-----+

将公钥安装到目标帐户中。此示例使用 root。如果您更喜欢非 root 目标,请创建用户并从 nspawn 控制台对其进行测试。将密钥安装到相应的容器目录中

# mkdir /home/pqc/root/.ssh
# cat testkey.pub >> /home/pqc/root/.ssh/authorized_keys

测试您的登录

# ssh -p 2222 -i testkey root@localhost
The authenticity of host '[localhost]:2222 ([127.0.0.1]:2222)' can't be established.
ED25519 key fingerprint is SHA256:1nnku5/Q8leUwnbB9mDcfPobvsQapj2JJA6FVVQjTJg.
ED25519 key fingerprint is MD5:bc:dc:b6:52:31:8a:59:1d:dc:c7:61:7c:2b:7c:37:01.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[localhost]:2222' (ED25519) to the list of known hosts.
Enter passphrase for key 'testkey': 
~ #

现在容器应该已准备好进行最佳实践 SSH 登录,可以选择使用 NTRU(可能)量子安全的密钥交换进行保护。

后量子登录和 sftp

NTRU 密钥的一个问题是,目前只有两个服务器和一个客户端实现了它们。如果您足够幸运,您的操作系统包含 OpenSSH v8 或更高版本,您可以直接测试 NTRU 交换

$ ssh -p 2222 -i testkey -o KexAlgorithms=sntrup4591761x25519-sha512@tinyssh.org root@localhost

如果您是许多未运行如此新版本的 OpenSSH 版本的 Linux 用户之一,您可以使用 musl 创建一个。我将在下面演示 OpenSSH 的其他组件(sftp-server、scp 等),因此即使是最新 OpenSSH 版本的用户也将有理由拥有 musl 版本。下载最新版本的 OpenSSH,然后使用以下命令执行 musl 构建

$ tar xvzf openssh-8.3p1.tar.gz
$ cd openssh-8.3p1
$ CC='/usr/local/musl/bin/musl-gcc -static' ./configure --without-openssl --without-zlib
$ make

在没有 OpenSSL 的情况下进行构建已经进行了很长时间的实验

$ ./configure --help | grep without-openssl | head -1
  --without-openssl       Disable use of OpenSSL; use only limited internal crypto **EXPERIMENTAL**

构建完成后立即测试 NTRU 登录

$ ./ssh -p 2222 -i ~/testkey -o KexAlgorithms=sntrup4591761x25519-sha512@tinyssh.org root@localhost

如果您使用的密钥带有密码,您可能会看到来自 musl 构建的 ssh 的以下错误

Load key "/home/cfisher/testkey": key encrypted using unsupported cipher

如果是这种情况,请将本机 ssh-agent 与 musl ssh 客户端一起使用,并删除稍后在 ssh 命令行上对密钥的 -i 引用

$ eval $(ssh-agent)
Agent pid 21067
$ ssh-add ~/testkey
Enter passphrase for /home/cfisher/testkey: 
Identity added: /home/cfisher/testkey (cfisher@yourhost.com)
$ ./ssh -p 2222 \
-o KexAlgorithms=sntrup4591761x25519-sha512@tinyssh.org \
root@localhost

您可能已经注意到 inetd 对 TinySSH 的引用中提到了 sftp-server。如果您想允许 sftp 或 scp,请将 musl 构建复制到容器中,并使用正在运行的代理测试登录(请注意我调用了 glibc sftp

# cp sftp-server /home/pqc/ssh
# cp scp /home/pqc/bin

$ echo 'put ssh-agent' | sftp -S ./ssh -o Port=2222 \
-o KexAlgorithms=sntrup4591761x25519-sha512@tinyssh.org \
root@localhost:/root

sftp 会话的输出应为

Connected to localhost.
Changing to: /root
sftp> put ssh-agent
Uploading ssh-agent to /root/ssh-agent
ssh-agent                                     100% 1054KB  14.6MB/s   00:00

现在不鼓励使用 scp discouraged,但它仍然可以使用。此示例假设代理正在运行

$ ./scp -S ./ssh -P 2222 -o KexAlgorithms=sntrup4591761x25519-sha512@tinyssh.org ssh root@localhost:/root
ssh                                           100% 2492KB  59.3MB/s   00:00

为 NTRU 选项设置 shell 别名可能会有所帮助

$ alias pqssh="/your/path/to/ssh \
-o KexAlgorithms=sntrup4591761x25519-sha512@tinyssh.org"
$ pqssh -i testkey -p 2222 root@localhost

如果应允许非本地主机登录到容器,则必须打开端口上的任何防火墙限制。一种方法是

iptables -w -I INPUT -p tcp --dport 2222 --syn -j ACCEPT

欢迎来到后量子密钥交换。

迁移主机密钥

如果您打算在 OpenSSH 和 TinySSH 服务器之间移动主机密钥,那么 TinySSH 密钥格式可能会成为问题,因为它不是 OpenSSH 期望的形式

# ls -al /home/pqc/etc/tinyssh/sshkeydir
total 8
drwxr-xr-x    1 root     0               42 Jun 27 18:36 .
drwxr-xr-x    1 root     0               18 Jun 27 18:36 ..
-rw-------    1 root     0               64 Jun 27 18:36 .ed25519.sk
-rw-r--r--    1 root     0               32 Jun 27 18:36 ed25519.pk

tinysshd-printkey 实用程序只能打印公钥。将私钥移动到 OpenSSH 格式可能需要自定义代码,而这种代码似乎不存在。因此,tinysshd-makekey 实用程序对于生产主机密钥来说令人担忧。

最好维护主机密钥的 OpenSSH 兼容副本,并使用转换器生成 TinySSH 变体。当前的转换器 需要 Python 3(避免使用 旧的、基于 C 的 密钥转换器)。我在 Red Hat/CentOS 8 上加载了 Python 转换器

# python3 -m pip install tinyssh-keyconvert-0.3.2.zip 
WARNING: Running pip install with root privileges is generally not a good idea. Try `__main__.py install --user` instead.
Processing ./tinyssh-keyconvert-0.3.2.zip
Installing collected packages: tinyssh-keyconvert
  Running setup.py install for tinyssh-keyconvert ... done
Successfully installed tinyssh-keyconvert-0.3.2

OpenSSH ed25519 主机密钥通常存储在 /etc/ssh/ssh_host_ed25519_key 中,应在不使用密码的情况下创建。使用以下命令将此类密钥转换为 TinySSH 格式

$ tinyssh-keyconvert ssh_host_ed25519_key --dir .

$ ll ed25519.pk .ed25519.sk 
-rw-r--r--. 1 fishecj itg 32 Jun 25 11:57 ed25519.pk
-rw-------. 1 fishecj itg 64 Jun 25 11:57 .ed25519.sk

将所有相关密钥复制到将使用它们的主机或容器。ssh_host_ed25519_key.pub 公钥不是严格必要的,因为可以使用 ssh-keygen -y 重新生成它。确保在安装完成后删除任何临时副本,因为丢失这些密钥将危及主机的通信安全

$ rm -i .ed25519.sk ed25519.pk ssh_host_ed25519_key

结论

对于上述方法存在许多反对意见。明显的反对意见包括:NTRU Prime 尚未获得官方认可;准备一个标准的 OpenSSH sshd 并不困难;TinySSH 不使用“权限分离”和/或 chroot();;TinySSH 缺少重要功能(端口转发、sftp/scp、X11、VPN 等);应该执行更大的 musl SSH 构建,该构建不使用实验性功能。

关于官方 NTRU 认可,OpenSSH 打算在近期禁用 ssh-rsa。对于旧系统来说,这将是一次痛苦的变革,许多旧系统将留下孤立的 SSH 实现。随着这种程度的变革即将到来,应该施加压力以选择最佳可用密码系统,并且可以为包含 TinySSH NTRU Prime 提出理由。没有管理员希望这样做两次。

TinySSH 服务器的配置难度远低于 OpenSSH sshd 的完整实现。该服务器的作者声称“TinySSH 不可能配置错误”;sshd 则不然。易于管理是一个因素,尤其是在推广到许多服务器时。

TinySSH 的安全记录似乎非常干净,其设计避免了 C 语言的已知问题。此服务器强制执行用户的最佳实践,这可能既是安全优势,也是培训责任。也许最终,失去权限分离是一个合理的权衡。

用户很容易养成不良习惯,并依赖 OpenSSH 中异乎寻常或已弃用的功能。这些功能应谨慎地扩展到有经验且有证明需求的用户。

最后,如果仅将 scp 和/或 sftp-server 服务器组件与 TinySSH 一起使用,则实验性构建可能不相关。OpenSSL 也不包含任何 NTRU 实现,因此这方面可能不会受到任何降级

为了解决更大的问题,SSH 面临着 RSA 密钥立即弃用和转换的巨大动荡(这使得 TinySSH 决定省略 RSA 是明智之举)。忽视 NIST 在 2016 年提出且至今未解答的后量子问题,将愚蠢地浪费更广泛的 NTRU Prime 操作经验的机会,这将对 SSH 和 TLS 都有价值。NIST 于 2016 年启动了后量子密码学竞赛,最终草案计划于 2022-24 年完成。AES 在四年内就已确定,但当前的竞赛已大大延迟。

这艘船需要扬帆起航。

接下来阅读什么

Linux 防火墙入门

防火墙是您计算机抵御网络入侵的第一道防线。下载我们的速查表以确保您的安全。

Grand Turk
二十多年来,断断续续地撰写关于 Linux 和更广泛的 POSIX 操作系统家族的文章。以下是我最近较受欢迎的标题的简短列表:使用 stunnel TLS 的 NFS 加密

评论已关闭。

知识共享许可协议本作品根据知识共享署名-相同方式共享 4.0 国际许可协议获得许可。
© . All rights reserved.