我知道一般的建议,那就是我们永远不要设计¹密码算法。在本网站以及像Bruce Schneier这样的专业人士的网站上,都进行了广泛的讨论。

但是,一般性建议远不止于此:它说我们甚至不应该实施比我们更明智的设计的算法,而应该坚持由专业人员进行的众所周知的,经过测试的实现。 />
这是我无法详细讨论的部分。我还对Schneier的网站进行了简短搜索,但也找不到该断言。

因此,为什么我们也建议不要实施加密算法?我最想回答的是提及一位对此颇受好评的安全专家的答案。

¹更准确地说,根据我们的内心需求进行设计;这可能是一个很好的学习经历;但是请拜托,请不要使用我们设计的内容。

评论

评论不作进一步讨论;此对话已移至聊天。

#1 楼

您想要避免自己实施加密算法的原因是由于旁通道攻击。

什么是旁通道?

与服务器通信时,消息的内容是沟通的“主要”渠道。但是,还有几种其他方法可让您从通讯伙伴那里获取信息,而这些信息并不直接让他们告诉您一些信息。

这些方法包括但不限于:


答复您的时间
服务器处理您的请求所消耗的能量
服务器访问高速缓存以对您做出响应的频率。

什么是旁道攻击?

简单来说,旁道攻击是对包含这些旁道之一的系统的任何攻击。以下面的代码为例:

public bool IsCorrectPasswordForUser(string currentPassword, string inputPassword)
{
    // If both strings don't have the same length, they're not equal.
    if (currentPassword.length != inputPassword.length)
        return false;

    // If the content of the strings differs at any point, stop and return they're not equal.
    for(int i = 0; i < currentPassword.length; i++)
    {
        if (currentPassword[i] != inputPassword[i])
            return false;
    }

    // If the strings were of equal length and never had any differences, they must be equal.
    return true;
}


此代码在功能上似乎是正确的,如果我没有打错任何文字,那么它可能会做应做的事情。您还能发现旁道攻击向量吗?下面是一个演示它的示例:

假设用户的当前密码为Bdd3hHzj(8个字符),并且攻击者正试图破解它。如果攻击者输入的密码长度相同,则将执行if检查和for循环的至少一次迭代;但是如果输入的密码短于或长于8个字符,则仅执行if。前者需要做更多的工作,因此比后者要花更多的时间。比较一下检查1个字符,2个字符,3个字符等密码所花费的时间很简单,并且请注意,只有8个字符是唯一一个明显不同的字符,因此很可能是正确的长度。密码。

有了这些知识,攻击者就可以完善他们的输入。首先,他们尝试aaaaaaaaaaaaaaaZ,每个for循环仅执行Baaaaaaa循环的一次迭代。但是,当它们到达B时,将发生循环的两次迭代,这比从任何其他字符开始的输入还要花费更多的时间来运行。这告诉攻击者,用户密码的第一个字符是字母MyNeatCryptoClass,他们现在可以重复此步骤以确定其余字符。

这与我的密码有什么关系?

密码代码看起来与“常规”代码非常不同。当看上面的例子时,看起来没有什么大不了。这样,当您自己执行事情时,执行原本应该做的代码可能不会带来严重的缺陷。

我可以想到的另一个问题是程序员不是密码学家。他们倾向于以不同的眼光看待世界,并常常做出危险的假设。例如,看下面的单元测试:

public void TestEncryptDecryptSuccess()
{
    string message = "This is a test";
    KeyPair keys = MyNeatCryptoClass.GenerateKeyPair();    

    byte[] cipher = MyNeatCryptoClass.Encrypt(message, keys.Public);
    string decryptedMessage = MyNeatCryptoClass.Decrypt(cipher, keys.Private);

    Assert.Equals(message, decryptedMessage);
}


你能猜出是什么问题吗?我必须承认,这不是一个公平的问题。 q4312079q实现RSA,并且在内部设置为如果未明确给出指数,则使用默认指数1。

是的,如果您使用1的公共指数,则RSA可以正常工作。真的可以“加密”任何东西,因为“ x1”仍然是“ x”。

您可能会问自己,谁会在他们的正确思维下做到这一点,但是确实有这种情况在发生。 >
实现错误

实现自己的代码可能出错的另一个原因是实现错误。正如用户Bakuridu在评论中指出的那样,与其他错误相比,加密代码中的错误是致命的。以下是一些示例:

心脏出血

关于加密,Heartbleed可能是最著名的实现错误之一。尽管没有直接涉及密码代码的实现,但它说明了相对“小的”错误会带来多么大的错误。

虽然链接的Wikipedia文章对此问题进行了更深入的探讨,我想让Randall Munroe比我能简明得多地解释这个问题:


https://xkcd.com/1354/-根据CC 2.5 BY-NC许可的图像

Debian弱PRNG错误

早在2008年,Debian中就有一个错误影响了所使用的所有其他关键材料的随机性。 Bruce Schneier解释了Debian团队所做的更改以及它为什么有问题。

基本要点是,检查C代码中可能出现问题的工具抱怨使用未初始化的变量。尽管通常这是一个问题,但使用基本随机的数据播种PRNG并不坏。但是,由于没有人喜欢盯着警告并且受过培训以忽略警告可能导致其自身下线的问题,因此在某些时候删除了“令人反感”的代码,从而减少了使用OpenSSL的熵。
摘要

总而言之,除非将其设计为一种学习体验,否则请不要实现自己的Crypto!使用经过审核的加密库,该库旨在简化正确的处理方式和错误的处理方式。因为加密很容易出错。

评论


评论不作进一步讨论;此对话已移至聊天。

–Rory Alsop♦
19年5月10日在7:25

就个人而言,我会把“实现错误”放在第一位,因为IMO更有可能让普通的joe开发人员犯一个错误,使他们的密码很容易被破解,而不是受到旁道攻击的打击。我还建议您强调OpenSSL项目的维护者是经验丰富的加密程序员,并且,如果他们犯了这样一个简单的错误,一般的joe开发人员就不可能做得更好。

–伊恩·坎普(Ian Kemp)
19年5月10日在13:33

我知道我已经回答了“ for”,但一个非常非常好的反对意见是“电子邮件验证”的合理实现,其中包含成千上万的示例,尽管规则在过去30年中都是明确,公开且可用的。

–mckenzm
19年6月6日在23:17

@mckenzm为什么? RFC-822为有效的电子邮件地址定义了一个正则表达式。

–MechMK1
19年6月11日在8:14

#2 楼

提到的边信道攻击是一件大事。我再概括一下。您的密码库是非常高风险/高难度的代码。通常,该库是受信任的库,可以保护其余的软系统。这里的错误很容易造成数百万美元的损失。更糟糕的是,您经常必须与自己的编译器抗争。在许多不同的编译器下,对这些算法的受信任实现进行了大量研究,并进行了细微的调整以确保编译器不会做坏事。多么糟糕?好吧,考虑一下每个人都提到的那些旁道攻击。您仔细地编写代码,以避免所有这些攻击,并且一切正常。然后,在其上运行编译器。编译器不知道您在做什么。它可以轻松地看到您为避免侧通道攻击所做的某些事情,看到了一种更快的方法,以及优化了精心添加的代码!这甚至出现在内核代码中,在那里,无序的分配最终使编译器具有优化错误检查的权限!

检测这样的事情只能用反汇编程序,还有很多耐心。

哦,永远不要忘记编译器错误。上个月,我花了一周的大部分时间来跟踪我的代码中的一个错误,该错误实际上非常好-这是我的编译器中的一个已知错误,实际上是导致该问题的原因。现在我很幸运,因为错误使我的程序崩溃了,所以每个人都知道需要做些事情。一些编译器错误更微妙。

评论


稍微乱序的分配不是用密码进行的,这是所有C和C ++开发人员都需要注意的事情。 (当然,在加密代码中它可能会更昂贵)。

–马丁·邦纳(Martin Bonner)支持莫妮卡(Monica)
19年5月7日在14:43

@MartinBonner同意,而不是加密代码,尽管我确实听说它在几年前就已经成为MySQL的漏洞。他们“适当地”检查了循环缓冲区溢出,但是使用指针溢出(UB)进行了检查,因此编译器编译了它们的检查。不是加密,而是加密中您无法承受的那些小事情之一。

–Cort Ammon
19年5月7日15:42

@CortAmmon,关键的区别是涉及加密时,人们会非常积极地寻找您的所有漏洞,并且有充分的理由永远不会告诉您。他们将逐字地抛出当前存在的所有技巧,然后转到尚未想到的事情。

–尼尔森
19年5月8日在7:36



期间,任何“依赖”未定义行为的代码,不仅是密码,都不能被信任。调用UB的代码本质上是没有意义的,并且无法包含不确定性。您提到的情况是,编译器通过应用优化来“破坏”代码,这是源代码错误的产物。那不是编译器的错误,而是程序员的错误。

– Alex Reinking
19年5月8日在10:19



@AlexReinking您与GCC devs有共同的看法=)的确UB是UB,除非您专门针对编译器及其特定行为(在这种情况下,按规范是UB,但不是该编译器的UB)。该部分的目的是说明这些错误可能有多精妙。它甚至可以通过您的测试,适用于您的编译器的多个版本,只有在以后升级编译器并且编译器对UB的响应不同时才会出现。

–Cort Ammon
19年5月8日在16:24

#3 楼

反对使用自己的加密货币的理由是,即使面对大量测试,漏洞也可以隐藏在加密软件中而没有症状。

一切似乎都可以正常运行。例如,在签名/验证应用程序中,验证程序将正常运行。有效签名,拒绝无效签名。签名本身看起来就像是乱七八糟的东西。
但是错误仍然存​​在,等待实际的攻击。<​​br />
您是否曾经在代码中打错过字符并且没​​有注意,导致编辑器突出显示或快速编译或运行时错误,然后及时修复?如果它没有突出显示,编译并运行时没有明显的症状,那么您会遇到这种错字吗?那就是滚动自己的加密货币的陷阱。

#4 楼

即使在不可能进行旁信道攻击的情况下,加密算法也经常具有对安全性至关重要但并不明显的实现细节。两个示例:


ECDSA签名算法在生成签名时需要使用随机整数。对于使用给定私钥生成的每个签名,此整数必须不同。如果重新使用,获得两个签名的任何人都可以使用基本的模块化算法来恢复私钥。 (Sony在PlayStation 3的复制保护中犯了这个错误,每个签名使用相同的数字。)
为RSA算法生成密钥对需要生成两个随机的大素数。在正常情况下,恢复私钥需要整数分解或解决RSA问题,这两者都是非常慢的数学运算。但是,如果一个密钥对与另一个密钥对共享其素数之一,那么只需计算两个公钥的最大公约数,就可以轻松地恢复两对私钥。 (许多路由器在第一次加电时会产生SSL证书,这时随机性不多。有时,两个路由器会碰巧生成具有重叠密钥对的证书。)


#5 楼

我认为小字样说:


只要您的代码没有错误,并且可以避免代码将在每个平台(操作系统和体系结构)上出现陷阱,就可以实施加密算法运行。例如,某些实现可能具有额外的代码来防止边通道攻击。它不是算法固有的,但是必须确保实现安全。这可能是许多问题中的一个。

#6 楼

如果您自己实施加密技术并且对加密技术没有非常扎实的理解,那么很容易出错。在我职业生涯中看到的自家实现中,我想不出一个没有灾难性弱点的实例,而该弱点很容易被利用,导致在大多数情况下彻底中断或至少严重削弱了软件开发能力。保护。

除此之外,即使您有技能和知识来执行自己的实现,对于定时攻击或实现中的实际错误之类的事情,针对实现本身的其他弱点的可能性仍然很高。即使在理想情况下正常工作,也可能会直接泄漏信息。对于这些情况,并不是更多的人已经使用和测试了实现,而是更多的人希望确保它的安全性,因此实现者不一定必须更好地理解。

如果实施自己的行动,您将看到很少数量的白帽子和可能大量的黑帽子,因此攻击者的人数众多。通过使用大型的,高度使用的实现,它平衡了攻击它的白帽和黑帽黑客的数量,从而使攻击更加均匀。

#7 楼

我想提供一个略有不同的观点...

并不是没有人应该实施加密技术。毕竟,有人必须这样做。这只是一项极其艰巨的任务,您应该问自己是否有必要的经验和资源可供使用。

如果您在数学和计算机科学的相关领域拥有丰富的专业知识,强大的同行评审人员团队,在所有环境中都进行有条不紊的认真测试,紧跟相关文献,理解设计和实现方面的陷阱,特定于加密...然后,请继续并实施加密。

评论


..如果您认为尚无适合您使用的产品,并且将达到您所制造的产品的水平。

–bdsl
19年5月10日在11:49

#8 楼

好吧,这很快就升级了。
我意识到这不会流行,但是,如果您认为自己知道自己在做什么,并且对使用的语言和使用方式有了很好的了解,那么您可能会

例如,如果您在启动某些固件之前检查哈希与期望值是否匹配,则实施散列算法可能很好。如果说不正确的值根本没有任何响应,则对攻击者的反馈很少。

由于在每种语言中已经存在SHA256实现,因此您很少想到这种情况:为什么要用一些不同的变量名将其复制出来。发生的事情是某人决定变得聪明,或者想要一种不同的行为,或者不了解细微差别(例如侧面通道等)。

社区中的想法似乎是:牛仔越少越好,让所有人吓退。这可能是正确的,但我认为建议更容易遵循,而无需做出任何似乎过于热心的言论(例如永远不要自己动手做)。当您不了解大多数编程内容时,因为它们无法按预期工作。在“给出正确答案”的意义上,加密始终如您所愿。但是,知道您不知道别人对加密的了解是一项艰巨的任务。这是人们有困难的心态。因此人们问“我为什么不能”,而不是“我可以证明我的问题是什么”。

总的来说,我谨保持“如果你要问:不要”的立场可能是最好的。但这并不意味着您的思维过程是错误的。只是那些提供建议的人不能真正确定它是否是真正的。

评论


值得庆幸的是,SHA-256自然可以抵抗旁通道。

–森林
19年5月9日在5:40

“如果没有人被允许做X,谁被允许做X?”的想法。在任何以“不要自己动手”结尾的问题中都很常见。我的回答不是要永远不要实施加密代码。这个想法是“如果您实现自己的加密代码,您必须比平均代码要了解更多的事情”,而普通开发人员可能无法交付良好的加密代码。

–MechMK1
19年5月10日在8:01

@forest是讽刺吗?否则,是什么使SHA-256天生具有抗旁通道性?

–MechMK1
19年5月10日在8:01

@ MechMK1这不是讽刺。 SHA-256自然具有抗旁通道性,因为它使用加法,固定距离旋转和XOR(即ARX功能),它们在大多数现代处理器上都是恒定时间操作。与AES不同,它不使用任何依赖于秘密的查找表。这意味着即使是简单的实现也将抵制旁通道,而以恒定的时间实现AES需要了解底层CPU架构。

–森林
19年5月10日在8:05



实际上,对于MD4,MD5,SHA-1,SHA-2系列的其余部分以及RIPEMD160,也是如此,它们都是密切相关的ARX哈希值(它们的设计都源自MD4,并以各种方式对其进行了改进) 。也有一些自然抗旁通道的密码,例如ChaCha20。几乎任何纯ARX功能都将易于实现,而无需担心恒定时间行为。

–森林
19年5月10日在8:12



#9 楼

很简单,更广泛使用的较旧的加密软件已经过了更多的测试(友好和不友好)和更多的分析。

此外,加密的历史上充斥着损坏的软件,其中一些是作者撰写的由杰出的密码专家撰写。

因此,您最信任的代码通常是已经存在一段时间的代码。

#10 楼

并非如此,这似乎是针对非专业人士的警告,以使他们更好。

如果您的语言不存在该实现,那么有人将需要这样做。好像没有足够的测试用例,如果您是一个专业人士,就会有同行评审和进一步的测试。

是的,它需要正确,但是如果发布(以前用其他语言实现),那么它几乎应该是样板。

所以也许您不应该这样做,但是显然有人必须这样做。如果不这样做,您将不得不打电话给其他人以弥补您的空白,这是绝对不希望的。

当然应该这样写,以便安全专业人员可以通过视觉上的“指纹”识别它。

评论


我不同意加密实现“几乎是样板”的概念。

–MechMK1
19年5月8日在12:48

另外,那么,在没有现有加密库的地方,您正在使用哪种非常规语言?我的意思是,除非您使用Malbolge进行编程,并且需要专门使用Camellia密码算法,否则您可能根本不在这条船上。

–凯文
19年5月8日在21:27

您会感到惊讶。我已经看到一些商店实现了自己的z / OS汇编器实现。这取决于现成的利息或费用。

–mckenzm
19年5月8日在23:09

@Kevin虽然Delphi有一个加密库,但是它非常有漏洞,其背后的开发团队表示他们不会修复它,而是继续出售它并拒绝对其进行修改。

–亚伦
19年5月9日在6:13

..在开源的情况下,通常会有一个人的名字,它不仅仅是凭空实现的。

–mckenzm
19年5月14日在1:16

#11 楼

与其他许多答案并存的另一个原因是,要做好加密是很困难的事实:

正确地加密很昂贵。

错误地加密是昂贵。

由于正确实现加密是一件非常困难的事情,因此需要大量工作,从业务角度来看,实现自己的加密不太可能。

然后您就有可能将其弄错,这可能包括各种负面后果,包括罚款,不良宣传以及根据您应加密的内容而定的更糟糕的结果。

#12 楼

使用众所周知的专业实现的算法的问题在于,保护消息所需的一切都是关键。如果Evelyn可以找到(或猜测)密钥,那么就可以对消息进行解码。

在第二次世界大战之前,有两种Enigma加密机-一种用于商业,一种用于军事。军事版本比其他版本更复杂,更强大,当然是无法获得的,但是任何人都可以露面并购买商业版本。密码学家找到了其中一个,对其进行了分解和分析,并最终弄清楚了如何构建自己的军事版本。 (这是德国输掉比赛的重要原因。)

为了获得最佳保护,必须将算法保密。而且还没有众所周知的秘密算法。

从理论上讲,您可以构建完全是算法且没有密钥的机器。只需在不需要密钥的情况下在计算机上运行它,并确保Evelyn永远不会获得该算法的副本。 (我可能不想自己做这件事。)

为了获得最佳安全性,我将通过我自己未发布的算法,然后通过专业实现的算法来运行明文。当然,这仅在少数“仅通过邀请”用户中可行;您不能将其放在GitHub上。

如果输入的内容已经被加扰,则必须避免第二个(专业实现的)过程做不安全的事情。另一方面,许多代码破解尝试都是通过尝试每个可能的键并在出现类似明文的内容时停止而起作用的。由于此过程的输出需要进一步的步骤才能变为人类可读,因此代码中断过程将不知道何时停止。

IANAC,但是我从来没有让这样的事情阻止过我。

评论


“另一方面,许多代码破解尝试都是通过尝试所有可能的键并在出现类似明文的内容时停止操作来实现的。”打破二战中的Enigma的一个关键是,这么多消息都是以“ Heil Hitler”开头的。一旦收到不匹配的字母,请中止,增加编码轮,然后重试。一旦获得了完整密钥,您就可以在当天解密其他所有内容...

–瑞士弗兰克
19年5月8日在13:27

这是错误的主意和错误的建议。任何人都可以想出一种自己无法破解的加密方法。诀窍在于提出了一个没人能打破的诀窍-如果您要保守算法的秘密,就无法确定。如果您不是密码分析团队,那么您将很难确定自己的自制算法是否在增加/减少/损害了您要遵循的真实算法的安全性。

–凯文
19年5月8日在18:51

另外,“使用众所周知的专业实现的算法的问题在于,保护消息所需的一切都是关键。” -这不是问题。这是对安全性的关键理解。老实说,如果您确实想停止这种攻击媒介(特定加密算法的盲目应用),只需添加一个Pepper。它可以防止相同的潜在攻击媒介,不会引入潜在的安全漏洞,也不需要自制的安全算法实现。

–凯文
19年5月8日在19:01

如果将其发布为RFC或Wikipedia,则很难保密。这里的主题是“已知”方法。

–mckenzm
19年5月8日在23:03

-1您通过模糊暗示安全性,并且公然违反了Kerckhoffs的原则。那种立场不会在这个站点上获得太多支持。

–森林
19年5月9日在1:39