View source code
Display the source code in std/digest/package.d from which this page was generated on github.
Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using local clone.

Function std.digest.secureEqual

Securely compares two digest representations while protecting against timing attacks. Do not use == to compare digest representations.

bool secureEqual(R1, R2) (
  R1 r1,
  R2 r2
)
if (isInputRange!R1 && isInputRange!R2 && !isInfinite!R1 && !isInfinite!R2 && (isIntegral!(ElementEncodingType!R1) || isSomeChar!(ElementEncodingType!R1)) && !is(CommonType!(ElementEncodingType!R1, ElementEncodingType!R2) == void));

The attack happens as follows:

  1. An attacker wants to send harmful data to your server, which requires a integrity HMAC SHA1 token signed with a secret.
  2. The length of the token is known to be 40 characters long due to its format, so the attacker first sends "0000000000000000000000000000000000000000", then "1000000000000000000000000000000000000000", and so on.
  3. The given HMAC token is compared with the expected token using the == string comparison, which returns false as soon as the first wrong element is found. If a wrong element is found, then a rejection is sent back to the sender.
  4. Eventually, the attacker is able to determine the first character in the correct token because the sever takes slightly longer to return a rejection. This is due to the comparison moving on to second item in the two arrays, seeing they are different, and then sending the rejection.
  5. It may seem like too small of a difference in time for the attacker to notice, but security researchers have shown that differences as small as 20µs can be reliably distinguished even with network inconsistencies.
  6. Repeat the process for each character until the attacker has the whole correct token and the server accepts the harmful data. This can be done in a week with the attacker pacing the attack to 10 requests per second with only one client.

This function defends against this attack by always comparing every single item in the array if the two arrays are the same length. Therefore, this function is always Ο(n) for ranges of the same length.

This attack can also be mitigated via rate limiting and banning IPs which have too many rejected requests. However, this does not completely solve the problem, as the attacker could be in control of a bot net. To fully defend against the timing attack, rate limiting, banning IPs, and using this function should be used together.

Parameters

NameDescription
r1 A digest representation
r2 A digest representation

Returns

true if both representations are equal, false otherwise

See Also

The Wikipedia article on timing attacks.

Example

import std.digest.hmac : hmac;
import std.digest.sha : SHA1;
import std.string : representation;

// a typical HMAC data integrity verification
auto secret = "A7GZIP6TAQA6OHM7KZ42KB9303CEY0MOV5DD6NTV".representation;
auto data = "data".representation;

auto hex1 = data.hmac!SHA1(secret).toHexString;
auto hex2 = data.hmac!SHA1(secret).toHexString;
auto hex3 = "data1".representation.hmac!SHA1(secret).toHexString;

assert( secureEqual(hex1[], hex2[]));
assert(!secureEqual(hex1[], hex3[]));

Authors

Johannes Pfau

License

Boost License 1.0.