Repository navigation
x86-64 RSA: vg_rsa_check_crt_key, and PrivateKey::from_crt checks keys with it (17–29× faster loading) - #1355
Merged
Merged
Conversation
…s with it (WIP: src/asm not yet regenerated) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PqW9NMssoiBx9i4PpksckL
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PqW9NMssoiBx9i4PpksckL
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PqW9NMssoiBx9i4PpksckL
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
From the rustls provider analysis in #991. rustls loads server keys with
PrivateKey::from_crt, which ran at 0.10–0.20 of aws-lc-rs's speed because it checked the key with a whole constant-time private-key operation on 0. This PR replaces that check, on x86-64, withvg_rsa_check_crt_key, whose spec is #1343.Stacked on #1343 (the spec,
Spec/Rsa.leanandSpec/Rsa/Contract.lean). This PR changes no file inSpec/orTCB/.Implementation (
Impl/+Proof/+ registration)Impl/Rsa/X86_64/CheckCrtKey.leanisvg_rsa_check_key's code with the checks ofdremoved and the remainders shortened:Its entry stores the arguments in
vg_rsa_check_key's header slots, withpalso ind's slots, which nothing reads. That lets the proof reusecheck_key's context (MainPre,KCtx,KK) and its proven pieces: loads,ltMask,p q = n,mulE,mulXRandeqOne.eandnare checked first. Those are the only branches; every other check is and'ed into a mask, so timing depends only on the lengths,nande.Shorter restoring division (
reduceTop).vg_rsa_check_keydivides a product by doubling once per bit of the whole product, overn's width:64 (w + 2)or64 (2 w + 2)doublings ofw-word numbers. Here each remainder needs a small quotient only when the comparison before it holds:e dX < 2^33 (X − 1)whendX < X − 1q qInv < 2^(64 c) pwhenqInv < p, forc = ⌈q_len / 8⌉So
rstarts as the product's words fromcup, which are below the divisor, and only its lowcwords are divided a bit at a time. All of this is over numbers as long as the divisor,⌈len / 8⌉words. When the comparison fails, the mask is already clear and the remainder isn't used.Proofs:
Proof/Rsa/X86_64/CrtKey*.leanreduceTop_okrests onreduceLoopFrom_ok, which runscheck_key's word loop fromcwords left.ckCode_correct.ckCode_constantTime. ItsreduceToppieces are checked bytaint_decide.ck_impliesgives the shared contract, andck_verifiedtheVerifiedtheorem.Proof/Rsa/CrtKeyParts.lean(crtKeyValid_parts) shows the masks the code computes equalcrtKeyValid.KeyReduce.lean's loop invariants gain a hypothesisQ(check_keypassesTrue), so the loop can run from a remainder known only under the comparison.Before writing the proofs, I ran the code on the x86-64 model interpreter (scratch only, not committed) with a 512-bit key and 10 altered versions. It agreed with
checkCrtKeyon all 11: the valid key, swapped primes, each ofdP,dQ,qInvoff by one,dP + p − 1,qInv + p, leading zeros onpand onq,e = 3, andq + 2.Rust
PrivateKey::from_crtvg_rsa_check_crt_keyand returnsInvalidPrivateKeyunless it returns 1.cfgfor a function only one target has, and the docs say so.private_opis unchanged, including its fault check.What
from_crtguarantees, before and afterBefore, on both targets, it accepted a key exactly when the operation released a result for 0. That held when:
nis valid (odd, 512–8192 bits, no leading zero byte) andeis within BoringSSL's limitsp q = nandqInv < pmfor 0 passesm^e ≡ 0 (mod n), which fails only in degenerate cases such asdP = 0withqInv ≠ 0After, on x86-64, it accepts a key exactly when
checkCrtKeyholds:RSA_check_key's checks withoutd.nande;p q = ndP < p − 1ande dP ≡ 1 (mod p − 1);dQ < q − 1ande dQ ≡ 1 (mod q − 1)qInv < pandq qInv ≡ 1 (mod p)This implies everything the operation checks of the key, so a refusal from
private_opstill means the input is out of range. It also impliesdP, dQ ≥ 1, so 0's result is 0: every key accepted now was accepted before. Keys whose CRT values don't matcheare now refused at load withInvalidPrivateKey, where before they loaded and every operation returnedFault. LikeRSA_check_key, nothing checks thatpandqare prime, which is whyprivate_op's fault check still matters.from_primes(andfrom_components) go throughfrom_crt, so on x86-64 they now also required e ≡ 1modulop − 1andq − 1. Their docs say so.Tests
Faultwould now fail at load. They use a new helper instead,rsa::tests::composite: CRT values that matche = 3for composite factors.p = 2^(8a) − 1,q = 2^(8a) + 1,n = 2^(16a) − 1dP = p/3,dQ = (2^(8a+1) + 1)/3,qInv = 2^(8a−1)e.afrom 32 to 511, either prime first.private_invalidcovers each new check.from_primeswithd = p dPpassescheck_key.rsa_pkcs1_encgets a unit test of itsFaultpath, which onlytests/rsa_guidancereached before.tests/rsa_guidanceandtests/wycheproof/rsa_keys.rsexpect mismatched CRT values to be refused on x86-64.Benchmark
Measured with the
rsa_from_crtgroup from #1352 (its own PR, so not in this one). Sapphire Rapids,VG_CPU_FEATURES=avx,avx2,avx512f,bmi2,adx,OPENSSL_ia32cap=":~0x200000", mean times:Local checks run before pushing:
cargo fmtandclippy -D warningsVG_CPU_FEATURES=noneandavx,avx2,bmi2,adxcheck_vectors,check_arch_gates,check_variants,check_mcdt,check_lean_imports,check_lean_speedandcpu_tests lintlake buildandEmit.leanalgorithms_table.pyleaves the README unchanged.🤖 Generated with Claude Code
https://claude.ai/code/session_01PqW9NMssoiBx9i4PpksckL
Generated by Claude Code