LibreSSL文档状态更新

2020-08-17 23:54:55

六年多前,LibreSSL是从OpenSSL派生出来的,大约两年前,我在布加勒斯特举行的2018年EuroBSDCon上解释了LibreSSL文档的状态。因此,提供更新似乎是合适的。

请注意,这不是关于LibreSSL状态的一般更新,因为我不是谈论LibreSSL代码大局的合适人选,我的工作主要集中在文档上。尽管如此,可以公平地说,尽管从事LibreSSL的开发人员数量有限,但LibreSSL项目相当活跃,通常每隔几个月就会发布一次。在移植和添加新功能(例如,在过去两年中关于TLSv1.3、CMS、RSA-PSS、RSA-OAEP、GOST、SM3、SM4、XChaCha20)、OpenSSL兼容性改进(包括提供其他OpenSSL-1.1API)以及大量错误修复和代码清理方面,都在继续取得进展。

维护LibreSSL是一项挑战,这不仅是因为从OpenSSL继承的许多有问题的设计选择和可疑的代码质量,而且还因为它的巨大规模。它大约有50万行代码和文档,占/usr/src/lib/下面行的三分之一以上,大约是完整的/usr/src/usr.bin/tree的一半大小-有关大小的概述,请参见附录1。它的近500个手册页记录了刚刚超过3000个功能,几乎占/usr/share/man/man3/中手册页的一半,并且占那里记录的功能的一半以上。即使可能大约有三分之一的LibreSSL API仍然完全没有文档记录。

尤其是在2018年左右,OpenSSL的改进合并在一起。在此之前,我仍然忙于更基本的任务,最近,OpenSSL开发转移到了OpenSSL-3分支,因此OpenSSL-1.1分支中的改进数量有所减少。

虽然几乎所有现有页面都至少接受了一些不是来自OpenSSL的本地改进,但这些本地改进在字符和大小上差别很大,从最简单的打字错误和措辞修复到完全重写。大部分改进都是由我承担的,但其他几个开发人员也做出了贡献,最突出的是Jason McIntyre(jmc@),自2015年以来提交了近100次。

上述概述意味着,即使在这六年之后,在所需的LibreSSL文档中,

因此,值得记住的是,这仍然是一大堆继承的文本,存在着一些系统性的缺陷。

这个转换是使用pod2mdoc(1)程序部分自动化的,但是不可能自动检查正确性,因此每个页面都需要手动检查。到2015年2月,该工具已经完好无损,但直到一年多后在图卢兹举行的12k16黑客马拉松比赛中,才在专注的努力下完成了转换。在转换过程中已经完成了第一轮审查:添加一些完全缺失的标记,解决自动表示标记到语义标记转换出错的情况,解决原始标记已经错误的情况,等等。另外,已经应用了一些内容改进:删除不适用或无用的文本,改进各种措辞,改进另见各节。这一阶段的结束以衬线和半自动检查为标志:例如,记录在多个页面中的函数的情况得到了解决。

安东尼·本特利(Anthony Bentley@)在2014年10月12日提交了libssl部分,从2015年2月14日到2016年11月5日,我用大约20次提交来介绍libcrypto。

MLINK-过去是使用/usr/share/man/下面的ln(1)创建的2000多个附加文件名-在2016年3月30日被Jason McIntyre(jmc@)删除,因为我们基于mandoc的man(1)实现不再需要它们。

从2016年11月10日到12月10日,版权声明和许可证头始终缺失,并系统地添加了libcrypto中的约115个提交和libssl中的约45个提交。

在那个阶段,从OpenSSL合并了许多错误修复,从OpenSSL导入了大约40个缺失的页面。在此阶段接近尾声时,重新组织了BIO、BN、DH、DSA、EC、RSA和SSL的概述页面,以满足原则";每个页面都以一个函数命名。";

从2016年12月12日到12月24日,OpenSSL在X509_dup.pod中聚集在一起的众多对象和函数都有适当的文档,其中包括大约12个提交和大约25个全新的页面。

从2016年12月24日到2017年1月7日,对d2i_*(3)页进行了系统修复,提交了大约15-20个页面,大约有20个全新的页面。OpenSSL仍然没有比d2i_X509.pod更好的东西。

第一次尝试是从最基本的子库BN开始,系统地开始记录未记录的函数。然而,在2017年1月25日至31日的几次提交之后,这一尝试被放弃了,因为决定哪些功能应该记录下来,哪些更好地不记录下来,这是一项非常乏味的工作。

这一小插曲之所以值得注意,是因为它说明了实现完整性是一项严峻的挑战,这不仅是因为API的大小,还因为人们严重怀疑哪些部分最好从一开始就不公开。

从2018年2月11日到3月20日,许多新公开的1.1 API函数的文档从OpenSSL合并,libcrypto中提交了大约40个,libssl中提交了十几个。

从2018年2月22日到4月2日,为许多新发布的1.1 API函数编写了新的文本,提交了大约20次,此后几年又提交了大约15次。

从2018年3月20日到27日,在libcrypto中添加了大约55个提交,在libssl中添加了大约20个正确的历史部分。

对OpenSSL(1)手册页面的系统检查从2018年3月30日开始,2019年6月7日至7月12日期间,由猪口木一郎(Inoguchi@)完成了10次提交。后来,Inoguchi@将cms(2019年11月28日)和certhash(2020年7月14日)条目添加到此手册页面。

将Const限定符添加到许多函数原型是从OpenSSL合并而来的,并记录了从2018年4月25日到8月24日的大约40次提交,其中很大一部分文档提交是由Theo Buehler(TB@)完成的。

Libcryptro在2019年6月6日和libssl分别于2019年6月6日和6月12日建立了手册页的系统树结构,确保每一页都可以从crypto(3)和SSL(3)起点到达,最多遵循两个.Xr链接。

重新审视如何接近完整性的问题,第一个工作阶段由Terra GmbH资助,导致从2019年8月16日至9月1日,大约12个承诺记录了现有页面中的额外功能,外加6个全新的页面。此阶段包括AES、ASN1、EC、EVP、OCSP、RSA和X509子库的工作。

第二个工作阶段由Terra GmbH在今年夏天提供资金,导致从2020年5月24日到2020年7月23日,又有大约12个承诺在现有页面中记录额外的功能,另外还有12个全新的页面。此阶段包括在ChaCha、CMAC、PEM、PKCS7和X509子库中的工作。

在Bucuresti中,我已经非常简短地提到,在许多情况下,文档工作揭示了代码中的错误。为了表明此类事件不是孤立的事件,而是定期发生的,以下是在编写LibreSSL文档时发现的修复错误的提交的完整列表:

将缺少的常量限定符添加到SSL_SESSION_GET_PROTOCOL_VERSION(3);Theo Buehler(TB@)2018年3月17日的参数中。

在替换SK_SET(3);Ingo Schwarze(schwarze@)2018年4月1日中的元素时,请清除";SORTED&34;标志。

修复X509_NAME_ADD_ENTRY(3)中的两个逻辑错误,这两个错误都已超过20年;Ingo Schwarze(schwarze@),2018年4月4日。

将缺少的常量限定符添加到TS_TST_INFO_GET_EXT_BY_OBJ();Theo Buehler(TB@)2018年5月13日的参数中。

接受OpenSSL中记录的BN_CTX_END(3)的NULL参数;Ingo Schwarze(schwarze@)2019年8月20日。

合并来自OpenSSL的错误修复以更正BN_PRIME_CHECKS_FOR_SIZE()的返回值,该返回值由BN_GENERATE_PRIME_EX(3);Ingo Schwarze(schwarze@)2019年8月25日使用。

仅当ssl_create_cipher_list()中有活动密码套件时才包含TLSv1.3密码套件,可从ssl_set_cipher_list(3)等位置使用;joel Sing(jsing@)2020年4月17日。

请勿从X509_ATTRIBUTE_CREATE()中的无效NID输入构造中断输出,PKCS7_ADD_ATTRIBUTE(3)和类似函数使用该函数;Ingo Schwarze(schwarze@),2020年6月4日。

修复由PEM_X509_INFO_READ_BIO(3)导致的释放后使用/双重释放问题-这些错误已超过20年;Ingo Schwarze(schwarze@)2020年7月23日。

在Bucuresti,我列出了以下未完成的任务。尽管自那时以来取得了重大进展,但大纲仍然适用。

许多公共函数没有手册页。它们需要从头开始编写,或者应该添加注释,说明为什么故意没有文档。在2018年EuroBSDCon之前,新写了大约70页,完全重写了大约20页,我估计剩下的工作至少需要很多个月的全职工作。

对于一个重要的子库<;openssl/x509.h>;,已经系统地识别了所有未记录的公共函数,结果表明,仅在X509中就有近200个。将来可能会尝试将它们记录在案,但这还没有最终决定,到目前为止也没有估计这项工作何时可能完成。鉴于我目前的经验水平,我往往平均需要两个小时来记录一个额外的功能,这不是一项微不足道的任务,仅X509一项就可能需要两个多月的全职工作。我';不过,我不打算全职做那项工作。

准确统计LibreSSL中未记录的公共函数的总数,即使没有记录其中的任何一个,都已经需要几天的工作,我认为起草这样一个在不久的将来无法完成的待办事项列表将是浪费时间。然而,我非常粗略地估计,无文档记录的公共函数的数量可能在1500左右,具有很大的不确定性,按子库列出的大致细目请参见附录6。

相比之下,当我最近(2020年6月)与OpenSSL dot org&>的马特·卡斯韦尔(Matt Caswell<;Matt)交谈时,他告诉我他相信OpenSSL,libcrypto中有1530个函数,libssl中有40个函数,还有160个宏仍然没有文档。我无法判断这一说法的准确性,但数量级似乎与我在LibreSSL中的估计一致;考虑到我们编写了大量新页面,OpenSSL中未记录的公共函数的数量可能会稍微高一些。

无论如何,毫无疑问,与LibreSSL文档相比,为未记录的公共函数编写新文档是最重要的开放任务。这不仅仅是我个人的印象。Terra GmbH的文档专业人员和认证专家都同意这一评估,OpenSSL开发团队似乎也同意这一点。

无论是哪种情况,2018年至少多个月的估值显然并不悲观。现在似乎需要大约一年半的全职工作,估计有很大的不确定性。如果您不想最终陷入类似的不令人羡慕的境地,那么在向您自己维护的库中添加额外的功能之前,请三思而后行。通常情况下,数量并不是优势。

几乎所有现有的页面都需要基本的文案编辑,它们通常是冗长的、不精确的和不完整的。修复它们通常需要将文本与代码进行比较,但是代码经常被扭曲,因此阅读它非常耗时。

2018年,我估计这至少需要多个月的全职工作,这个估计显然不悲观。还没有开始或计划这样的工作,当大量的功能仍然完全没有文档记录时,这样的工作很可能不会开始或计划。

在2018年EuroBSDCon上,我希望完成OpenSSL(1)手册页面的第一遍,我估计这需要大约一周的全部时间工作。猪口木一郎(Inoguchi Kinichiro)于2019年6月7日至7月12日完成了这一传球。据我所知,现在所有的子命令和选项都已记录在案。

在2018年,我担心这些可能会在不久的将来造成严重的问题,因为OpenSSL许可证更改为不完全免费的Apache2许可证。幸运的是,预期的问题还没有在实践中出现,因为LibreSSL的目标仍然是OpenSSL-1.1兼容性(仍然是免费的),而不是OpenSSL-3.0的兼容性(不再是完全免费的)。

在这方面,在2018年EuroBSDCon之前已经完成了约70次对现有页面的提交和约60次额外页面的合并,此后又增加了约25次对现有页面的提交和约30次额外页面的合并。

添加、删除或更改代码时,需要持续维护文档。这方面与其他软件相同。上面的阶段列表中提到了一些例子。

我想在这里讨论我在2018年还没有讨论的另一个方面。理想情况下,函数的文档不仅应该描述函数的输入、处理和输出,还应该列出可能发生的错误,并描述调用代码如何准确区分不同类型的错误。

例如,在我们的libc文档中,此类信息大多以错误部分的形式提供,并且许多第4节手册页包含诊断部分。编译这些部分是很棘手的,因为错误往往在奇怪的情况下发生,并且列举所有可能的奇怪之处通常不是一项简单的任务。因此,这些部分中的许多部分并不严格完整。另一方面,在这方面过于热衷很容易导致文档过多,从而向用户公开用户在使用函数时实际上不应该考虑的实现细节,因为这些细节可能会改变。

在LibreSSL中,情况要糟糕得多。一方面,就像在libc中一样,函数返回值通常暗示关于成功或失败的语句,有时,就像在libc中一样,errno(2)可能会提供发生哪种类型的失败的线索。但是,OpenSSL引入了自己的错误代码系统,它有很多问题:

它被过度设计,这是现实世界中定制错误报告框架经常出现的问题。我不确定为什么这种情况经常发生,但是一旦人们开始编写自己的错误报告框架,通常很快就会失控。过去在我的不止一个项目中都发生过同样的事情。在Mandoc,花了很多年和许多步骤才将错误报告框架简化到我现在认为足够的状态。在OpenSSL中,这种广为流传的疾病变得特别糟糕,有大量的函数,其中许多函数相当神秘。仅错误处理模块就有上百个功能。

尽管框架被过度设计到了不寻常的程度,但它的使用方式特别草率。许多消息虽然往往冗长而隐晦,但几乎没有为用户提供任何有用的信息,许多消息甚至由文本组成,这些文本不仅没有具体说明,而且完全具有误导性。

失败后,OpenSSL不仅提供一条错误消息,而且提供错误堆栈,在许多情况下包含多条消息。人们可以争辩说,仅凭这一点已经是一个错误的设计。代码在第一个错误之后可以合理地继续并尝试识别其他不相关的错误的情况是例外,而不是规范。如果由于第一个错误而出现更多错误,那么只有根本原因对用户来说才是真正重要的。因此,一条清晰的错误消息几乎总是比一堆不同的消息要好。在OpenSSL中,这种错误设计的后果比通常更加严重。有时,最有用的消息在堆栈的底部,有时在顶部,有时在中间。堆栈上经常会有很多消息,但它们都是非特定的,没有一个是有帮助的。此外,如何使用这些堆栈是IS';甚至没有适当的文档,既不适合更改库的开发人员,也不适合使用库的应用程序程序员。

更糟糕的是,在许多情况下,OpenSSL函数在不向官方框架报告原因的情况下返回失败。这甚至经常发生在报告其他位置故障原因的函数中。因此,大多数功能可能会失败,也可能会提供原因,也可能不会提供原因。

在许多情况下,未记录使用错误处理框架的函数以及实际上不使用该框架的函数本身都会在内部调用使用该框架的函数,这可能会导致错误堆栈上出现不稳定的消息,无论用户调用的函数是失败的还是成功的。当这些消息持续存在,直到稍后发生另一个错误时,这可能会导致混淆。

某些函数有条件或无条件地清除错误堆栈。这相当于libc中鲁莽的";errno=0;";语句,并且可以隐藏以前的错误。我没有具体搜索,但我不记得曾经见过合适的习惯用法,在可能失败的内部操作之前立即保存堆栈的状态,执行操作,检查成功,然后立即相应地恢复堆栈,类似于libc中流行的习惯用法";int save_errno=errno;if(do_thing()==-1)return-1;errno=save_errno;";类似于libc中广为流传的习惯用法";int save_errno=errno;";类似于libc中流行的习惯用法";int save_errno=errno;";。

给定的函数是否使用错误报告框架只是在手册页中偶尔提到的;大多数人对这个问题完全没有提及。即使提到了,通常也不清楚是报告了所有错误的原因还是只报告了某些错误的原因,并且几乎不存在记录由特定错误条件导致的特定错误代码的手册页。

最重要的是,不仅OpenSSL中的错误报告非常不稳定,甚至仅仅是错误检测也经常是有缺陷的。许多函数可能会静默返回成功,即使它们尝试的基本操作失败了,这仅仅是因为它们忽略了检查某些失败条件。

因此,记录错误报告是如何工作的这项显而易见的任务实际上不可能从审计代码和改进整个错误处理中解脱出来,可能至少在数百个地方是这样。这显然是一个大项目,但我相信它可能是一个相关的项目。它不能从文档工作中解脱出来,但是可以在为未记录的公共函数编写新文档的同时开始。到目前为止,它还没有启动,也没有启动的计划。

虽然LibreSSL项目仍然存在并不断改进,尽管LibreSSL文档在过去两年多的时间里明显优于OpenSSL文档,但LibreSSL文档的质量在完整性和精确度方面仍明显低于OpenBSD质量标准。文档中的一些缺陷与代码中的缺陷有关,此外,为了记录代码而检查代码往往会发现错误,即使没有专门搜索它们,因此处理文档也会对代码有所帮助。

在过去的小时内。

.