比较两个字节数组是否相等不仅是最原始的可重用操作之一,也是密码学中最常用的操作之一。字节数组比较(BAC) 的基本要求是如果两个字节数组相等则返回true
,否则返回false
。当两个字节数组的长度相等并且对应数组位置中的所有字节对也相等时,这两个字节数组相等。
当在安全结构中使用时,BAC 获得了一个重要的附加安全要求:BAC 实现不应该泄漏任何关于正在进行相等性比较的两个数组中的数据的信息。
代码清单 6
static bool Direct(byte[] a, byte[] b)
{
if (a.Length != b.Length) return false;
for (int i = 0; i < a.Length; ++i)
{
if (a[i] != b[i]) return false;
}
return true;
}
直接比较实现了基本的 BAC 要求,但它也根据第一个不等字节对的位置进行了可变次数的比较,这泄漏了定时信息,可以帮助发起有效的猜测攻击来发现数据。这些类型的攻击被称为侧信道攻击(这里的侧信道是定时)。你永远不应该使用与敏感或秘密数据的直接比较。
代码清单 7
static bool
And(byte[] a, byte[] b)
{
bool f
= a.Length == b.Length;
for
(int i = 0; i < a.Length && i < b.Length; ++i)
{
f
&= a[i] == b[i];
}
return
f;
}
和比较类似于直接比较,即使在结果被确定为false
之后,也继续比较字节对。它在每次比较中执行固定数量的工作,这防止了定时攻击。比较是微软在其内部CryptoUtil
类(System.Web.Security.Cryptography
命名空间— System.Web
程序集和System.Web.Helpers
命名空间— System.Web.WebPages
程序集)中使用的 BAC 方法。
代码清单 8
static bool
Xor(byte[] a, byte[] b)
{
int x
= a.Length ^ b.Length;
for
(int i = 0; i < a.Length && i < b.Length; ++i)
{
x
|= a[i] ^ b[i];
}
return
x == 0;
}
异或比较利用了这样一个事实,即相等的字节对异或为零,如果所有字节对相等,则将所有异或结果加在一起只会产生零。“异或”方法可能比“与”方法稍有优势,因为“异或”组合是纯粹的位移位,通常编译成直接汇编的等价物,而布尔“与”组合可以编译成条件分支指令,这些指令可能具有不同的时序。
“与”和“异或”方法迫使您在特定平台上实现基本操作时,对潜在的时序变化做出某些假设。微软对。NET 运行在 Windows 上,在 Windows 上,微软对编译是如何发生的有很好的理解和控制。其他。NET 实现,比如 Mono,也可能针对特定的平台,并且能够很好地处理生成什么样的程序集级代码。
然而,可能会有这样的场景:您不愿意对低级原语(如布尔逻辑或移位操作)的实现做出特定的假设。在这些场景中,您可以先对两个阵列进行随机密钥 HMAC,然后对 HMAC 结果而不是原始阵列进行 BAC。一旦您将原始阵列替换为其 HMAC 等效阵列,BAC 的选择就不再重要:您可以使用任何一种。
代码清单 9
static bool
DoubleHMAC(byte[] a, byte[] b)
{
byte[]
aHMAC, bHMAC;
using
(var hmac = HMACFactories.HMACSHA1())
{
//
key unpredictability not required
hmac.Key
= Guid.NewGuid().ToByteArray();
aHMAC
= hmac.ComputeHash(a);
bHMAC
= hmac.ComputeHash(b);
}
return
Xor(aHMAC, bHMAC) && Xor(a, b);
}
注意当Xor(aHMAC, bHMAC)
返回true
时,我们实际上返回了一个附加的Xor(a, b)
的结果。原因是固定长度的摘要(如散列函数和 HMAC)保证会有域冲突。必须存在两个任意长度不等的数组a
和b
,它们产生相同的固定长度散列/HMAC 值。你想不出这样的a
和b
,但它们确实存在。我们直接在(a, b)
上执行额外的 BAC,以确保此类冲突发生时不会导致假阳性结果。
双 HMAC 方法显然比迄今为止讨论的所有其他方法更慢、更复杂。我们更喜欢对所有微软公司采用异或方法。NET 到 MSIL 语言(C#、VB.NET、F#)和双 HMAC 方法,用于将源代码用未确定的编译器(JavaScript)编译的语言。