Skip to content

Use memcmp_constant_time for SHA-256 hash comparisons#1004

Closed
kodareef5 wants to merge 1 commit intoOpenVPN:masterfrom
kodareef5:fix-timing-and-overflow
Closed

Use memcmp_constant_time for SHA-256 hash comparisons#1004
kodareef5 wants to merge 1 commit intoOpenVPN:masterfrom
kodareef5:fix-timing-and-overflow

Conversation

@kodareef5
Copy link

Three security-critical comparisons use memcmp() on SHA-256 digests where memcmp_constant_time() should be used:

  • ssl_verify.c:244 — certificate fingerprint comparison
  • ssl_mbedtls.c:1038 — TLS certificate hash comparison during renegotiation
  • init.c:2167-2168 — configuration digest comparison

The HMAC verification at crypto.c:652 already uses memcmp_constant_time (CVE-2025-13086 fix). These three sites have the same pattern but were not updated.

Three security-critical comparisons use memcmp() on SHA-256 digests
where memcmp_constant_time() should be used:

- ssl_verify.c:244: certificate fingerprint comparison
- ssl_mbedtls.c:1038: TLS certificate hash comparison during renegotiation
- init.c:2167-2168: configuration digest comparison

The HMAC verification at crypto.c:652 already uses
memcmp_constant_time (CVE-2025-13086 fix). These three sites
have the same pattern but were not updated.
Copy link
Contributor

@schwabe schwabe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Three security-critical comparisons use memcmp() on SHA-256 digests where memcmp_constant_time() should be used:

I seriously disagree with these being the same as the CVE. Just because a hash is used does not mean that comparison of it is time critical. Out of the three changes only one might qualify for being better with constant time and even in that case there is (unlike in the auth token path) no way for an attacker to even abuse timing differences.

return memcmp(a, b, sizeof(struct sha256_digest))
|| !memcmp(a, &zero, sizeof(struct sha256_digest));
return memcmp_constant_time(a, b, sizeof(struct sha256_digest))
|| !memcmp_constant_time(a, &zero, sizeof(struct sha256_digest));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a check if we can keep tun device open or not. The hash is really used to avoid storing all the old options to compare them.

I do not see a reason to make this a constant time check other than blindly applying "hashes must be compared with constant time"

}

if (0 != memcmp(old_sha256_hash, sha256_hash, sizeof(sha256_hash)))
if (0 != memcmp_constant_time(old_sha256_hash, sha256_hash, sizeof(sha256_hash)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also feels like applying "hashes must be compared with constant time" rather than any actual security scenario.
Also the newer mbed TLS versions do not use this anymore.

}
else if (ch1 && ch2
&& !memcmp(ch1->sha256_hash, ch2->sha256_hash, sizeof(ch1->sha256_hash)))
&& !memcmp_constant_time(ch1->sha256_hash, ch2->sha256_hash, sizeof(ch1->sha256_hash)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only one that could maybe be constant time. But then again to have any chance of exploiting this one requires you to be able to generate arbitary certificates that are trusted by CA that is in use by OpenVPN to verify the client certificates.
If that happens you have bigger things to worry about.

@kodareef5
Copy link
Author

Thanks for the thorough review, @schwabe. Your points are well taken:

  • The tun device options check is a functional comparison, not a security gate — constant-time adds no value there.
  • The mbedTLS path is deprecated in newer versions, so hardening it is moot.
  • The certificate fingerprint comparison requires a compromised CA to even begin timing analysis, which is already game over.

I was applying the pattern too broadly without considering the actual threat model at each call site. Closing this — appreciate the detailed feedback.

@kodareef5 kodareef5 closed this Mar 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants