Skip to content

Make OPENSSL_memcmp call CRYPTO_memcmp for constant-time comparison#3183

Draft
WillChilds-Klein wants to merge 2 commits into
aws:mainfrom
WillChilds-Klein:OPENSSL_memcmp-calls-CRYPTO_memcmp
Draft

Make OPENSSL_memcmp call CRYPTO_memcmp for constant-time comparison#3183
WillChilds-Klein wants to merge 2 commits into
aws:mainfrom
WillChilds-Klein:OPENSSL_memcmp-calls-CRYPTO_memcmp

Conversation

@WillChilds-Klein
Copy link
Copy Markdown
Contributor

@WillChilds-Klein WillChilds-Klein commented Apr 23, 2026

Summary

  • Changes OPENSSL_memcmp in crypto/internal.h to delegate to CRYPTO_memcmp instead of libc memcmp, providing constant-time comparison semantics
  • Introduces OPENSSL_memcmp_ordered — a NULL-safe memcmp wrapper that preserves ordering semantics (negative/zero/positive) for call sites that require it
  • Migrates 7 ordering-dependent call sites from OPENSSL_memcmp to OPENSSL_memcmp_ordered
  • This hardens all remaining OPENSSL_memcmp call sites against timing side-channel attacks while preserving correctness for sort comparators and structured comparisons

Changed files

File Change
crypto/internal.h OPENSSL_memcmp now calls CRYPTO_memcmp; new OPENSSL_memcmp_ordered wrapper added
crypto/asn1/tasn_enc.c der_cmpOPENSSL_memcmp_ordered (qsort comparator for DER SET OF)
crypto/asn1/asn1_lib.c ASN1_STRING_cmpOPENSSL_memcmp_ordered (string ordering)
crypto/obj/obj.c OBJ_cmpOPENSSL_memcmp_ordered (OID ordering)
crypto/bytestring/cbb.c compare_set_of_elementOPENSSL_memcmp_ordered (qsort for DER SET OF)
crypto/x509/x509_cmp.c X509_cmp, X509_NAME_cmpOPENSSL_memcmp_ordered (cert/name ordering)
crypto/pool/pool.c CRYPTO_BUFFER_cmpOPENSSL_memcmp_ordered (buffer comparison)

Performance Benchmark Report

Benchmarks were run on the same machine (macOS/ARM64, Release build) before and after the OPENSSL_memcmpCRYPTO_memcmp change, with multiple iterations to reduce noise. The benchmark suite is bssl speed.

Methodology

  • Baseline: Built from parent commit (using memcmp)
  • Changed: Built with OPENSSL_memcmp calling CRYPTO_memcmp
  • Iterations: 5 runs per benchmark, reporting mean ops/sec
  • Metric: Coefficient of Variation (CV%) indicates run-to-run noise

ECDSA Benchmarks (5 iterations each)

These are the most relevant benchmarks since ECDSA verification calls OPENSSL_memcmp in the cmp_x_coordinate and DER roundtrip code paths.

Benchmark Baseline mean Changed mean Diff % B CV% C CV%
ECDSA P-224 signing 51534.6 52555.2 +1.98% 7.87% 2.77%
ECDSA P-224 verify 29423.1 29590.8 +0.57% 2.46% 1.84%
ECDSA P-256 signing 75019.7 77084.6 +2.75% 4.24% 1.82%
ECDSA P-256 verify 30475.7 30765.5 +0.95% 1.50% 3.16%
ECDSA P-384 signing 18025.5 17764.9 -1.45% 2.27% 4.73%
ECDSA P-384 verify 7477.2 7526.5 +0.66% 3.79% 1.87%
ECDSA P-521 signing 9463.6 9096.6 -3.88% 2.12% 2.79%
ECDSA P-521 verify 4391.5 4401.5 +0.23% 3.53% 3.07%
ECDSA secp256k1 signing 9242.3 9254.1 +0.13% 2.95% 3.03%
ECDSA secp256k1 verify 10034.6 10017.4 -0.17% 1.48% 2.44%

RSA / Ed25519 / X25519 Benchmarks (5 iterations each)

Benchmark Baseline mean Changed mean Diff % B CV% C CV%
RSA 2048 verify (same key) 96900.7 91209.6 -5.87% 2.06% 7.85%
RSA 4096 verify (same key) 26509.3 25540.6 -3.65% 0.76% 3.43%
RSA 2048 verify (fresh key) 81175.1 80116.3 -1.30% 1.52% 1.46%
RSA 4096 verify (fresh key) 23554.3 22981.8 -2.43% 0.75% 3.21%
RSA 2048 signing 2624.6 2547.4 -2.94% 4.07% 8.36%
RSA 3072 signing 814.8 792.6 -2.72% 1.32% 2.27%
RSA 4096 signing 378.4 366.9 -3.04% 0.85% 5.71%
RSA 8192 signing 49.1 49.1 -0.06% 6.08% 3.94%
Ed25519 signing 197381.1 196927.9 -0.23% 1.05% 1.48%
Ed25519 verify 41915.3 42768.6 +2.04% 4.97% 0.79%
ECDH X25519 47042.2 47186.4 +0.31% 3.56% 3.65%

Full Benchmark Suite Summary (633 benchmarks, single pass)

Metric Value
Mean absolute difference 3.97%
Median absolute difference 2.54%
Within ±1% 143/633 (22.6%)
Within ±3% 368/633 (58.1%)
Within ±5% 487/633 (77.0%)

Interpretation

No statistically significant performance impact detected. The observed differences across all benchmarks are within the expected range of run-to-run variance on a laptop:

  • The coefficient of variation (CV%) of individual benchmarks ranges from 0.75% to 8.36%, indicating substantial natural noise
  • The largest observed differences (e.g., RSA 2048 verify -5.87%) have correspondingly high CV on the changed side (7.85%), meaning the "regression" is indistinguishable from noise
  • Verification operations that actually call OPENSSL_memcmp (ECDSA verify, RSA verify) show no consistent directional change
  • Operations that do NOT call OPENSSL_memcmp (signing, key generation) show similar magnitude variance, confirming this is measurement noise

The CRYPTO_memcmp function is a simple XOR-accumulate loop over the input bytes — the same asymptotic cost as memcmp, just without early-exit optimization. For the small buffer sizes compared at these call sites (typically 32-64 bytes for hashes, or a few hundred bytes for DER encodings), the difference between early-exit and full-scan comparison is negligible.

Ordering Semantics

CRYPTO_memcmp returns 0 (equal) or non-zero (not equal) — it does not provide ordering (negative/positive). Seven OPENSSL_memcmp call sites depend on ordering semantics for sort comparators and structured comparisons. These have been migrated to OPENSSL_memcmp_ordered, a new NULL-safe memcmp wrapper that preserves ordering and is explicitly not constant-time.

All 7 migrated sites compare only public, non-secret data where constant-time behavior is unnecessary:

Call site Function Data compared
crypto/asn1/tasn_enc.c:373 der_cmp DER SET OF element sorting
crypto/asn1/asn1_lib.c:353 ASN1_STRING_cmp ASN.1 string ordering
crypto/obj/obj.c:118 OBJ_cmp OID byte ordering
crypto/bytestring/cbb.c:653 compare_set_of_element DER SET OF sorting
crypto/x509/x509_cmp.c:86 X509_cmp Certificate hash ordering
crypto/x509/x509_cmp.c:114 X509_NAME_cmp Canonical name encoding ordering
crypto/pool/pool.c:30 CRYPTO_BUFFER_cmp Buffer data comparison

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license.

@WillChilds-Klein WillChilds-Klein requested a review from a team as a code owner April 23, 2026 14:29
@WillChilds-Klein WillChilds-Klein marked this pull request as draft April 23, 2026 14:30
@codecov-commenter
Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.00%. Comparing base (852f334) to head (10ca5db).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3183      +/-   ##
==========================================
- Coverage   78.12%   78.00%   -0.12%     
==========================================
  Files         689      689              
  Lines      123717   122651    -1066     
  Branches    17085    17074      -11     
==========================================
- Hits        96652    95673     -979     
+ Misses      26167    26080      -87     
  Partials      898      898              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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