Skip to content

perf: Enhance signing performance by caching scalar representation in PrivateKey - #242

Merged
jlucaso1 merged 2 commits into
mainfrom
feat-improvements-libsignal
Jan 24, 2026
Merged

perf: Enhance signing performance by caching scalar representation in PrivateKey#242
jlucaso1 merged 2 commits into
mainfrom
feat-improvements-libsignal

Conversation

@jlucaso1

@jlucaso1 jlucaso1 commented Jan 24, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • Performance

    • Further improved signing performance by caching the private scalar to avoid repeated recomputation.
  • Refactor

    • Internal key state now stores ephemeral keys with indirection to reduce stack usage and change ownership semantics.
  • Behavior

    • Some key constructors now require cached values for signing; attempting to sign with keys created without cache may fail.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 24, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds caching of the private-key scalar to Edwards cache structures and PrivateKey, adjusts constructors/accessors to populate and use the cached scalar for signing, and changes a PairCode state to heap-allocate ephemeral keypairs (Box).

Changes

Cohort / File(s) Summary
EdwardsCacheData Scalar Addition
wacore/libsignal/src/core/curve.rs
Adds scalar: Scalar to EdwardsCacheData, updates doc comments, and populates the scalar in get_edwards_cache and from_bytes_with_cache.
PrivateKey Cache Refactoring
wacore/libsignal/src/core/curve/curve25519.rs
Adds scalar: Scalar to PrivateKey; renames compute_ed_public_keycompute_edwards_cache returning (Scalar, CompressedEdwardsY, u8); adds SIGN_BIT_NOT_INITIALIZED, new/adjusted constructors (new_without_cache, from_bytes_with_cache, from_bytes_without_cache), accessors (cached_scalar, cached_ed_public_key, cached_sign_bit), and makes calculate_signature use cached scalar and panic if cache uninitialized.
PairCode State Boxed KeyPair
src/pair_code.rs, wacore/src/pair_code.rs
Changes PairCodeState::WaitingForPhoneConfirmation to store ephemeral_keypair as Box<EphemeralKeyPair> / Box<KeyPair> (heap-allocated) instead of by-value; public enum variant signature/type changed accordingly.

Sequence Diagram(s)

(Skipped — changes are internal caching and a local enum field type change; no new multi-component sequential flow to visualize.)

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐇 I nibble bytes and tuck them tight,
A scalar cached to speed the write,
No more repeats of costly grind,
A sentinel keeps errors confined,
Hooray — my hops make signing light!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main performance optimization: caching the scalar representation in PrivateKey to avoid recomputation during signing operations.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jan 24, 2026

Copy link
Copy Markdown

🐰 Bencher Report

Branchfeat-improvements-libsignal
Testbedubuntu-latest

🚨 1 Alert

BenchmarkMeasure
Units
ViewBenchmark Result
(Result Δ%)
Upper Boundary
(Limit %)
binary_benchmark::unpack_group::bench_unpack_compressedInstructions
instructions x 1e3
📈 plot
🚷 threshold
🚨 alert (🔔)
556.53 x 1e3
(+13.70%)Baseline: 489.46 x 1e3
513.94 x 1e3
(108.29%)

Click to view all benchmark results
BenchmarkInstructionsBenchmark Result
instructions
(Result Δ%)
Upper Boundary
instructions
(Limit %)
binary_benchmark::attr_parser_group::bench_attr_parser attr_lookup:setup_attr_marshaled()📈 view plot
🚷 view threshold
7,031.00
(-4.69%)Baseline: 7,377.06
7,745.91
(90.77%)
binary_benchmark::marshal_group::bench_marshal_allocating📈 view plot
🚷 view threshold
124,647.00
(-12.68%)Baseline: 142,740.04
149,877.04
(83.17%)
binary_benchmark::marshal_group::bench_marshal_reusing_buffer📈 view plot
🚷 view threshold
124,747.00
(-12.67%)Baseline: 142,840.94
149,982.99
(83.17%)
binary_benchmark::roundtrip_group::bench_roundtrip large:setup_large_marshaled()📈 view plot
🚷 view threshold
106,274.00
(0.00%)Baseline: 106,274.00
111,587.70
(95.24%)
binary_benchmark::roundtrip_group::bench_roundtrip small:setup_small_marshaled()📈 view plot
🚷 view threshold
8,487.00
(0.00%)Baseline: 8,487.00
8,911.35
(95.24%)
binary_benchmark::unmarshal_group::bench_unmarshal large:setup_large_marshaled()📈 view plot
🚷 view threshold
49,338.00
(-0.39%)Baseline: 49,533.20
52,009.86
(94.86%)
binary_benchmark::unmarshal_group::bench_unmarshal small:setup_small_marshaled()📈 view plot
🚷 view threshold
3,025.00
(-5.84%)Baseline: 3,212.54
3,373.16
(89.68%)
binary_benchmark::unpack_group::bench_unpack_compressed📈 view plot
🚷 view threshold
🚨 view alert (🔔)
556,531.00
(+13.70%)Baseline: 489,462.46
513,935.59
(108.29%)

binary_benchmark::unpack_group::bench_unpack_uncompressed📈 view plot
🚷 view threshold
779.00
(+0.02%)Baseline: 778.87
817.81
(95.25%)
libsignal_benchmark::conversation_group::bench_full_dm_conversation full:setup_conversation_data()📈 view plot
🚷 view threshold
27,922,065.00
(+0.55%)Baseline: 27,768,815.43
29,157,256.21
(95.76%)
libsignal_benchmark::dm_group::bench_dm_decrypt_first_message decrypt_prekey:setup_dm_with_first_message()📈 view plot
🚷 view threshold
5,571,063.00
(+0.02%)Baseline: 5,569,837.35
5,848,329.22
(95.26%)
libsignal_benchmark::dm_group::bench_dm_encrypt_first_message first_msg:setup_dm_session()📈 view plot
🚷 view threshold
178,206.00
(+0.32%)Baseline: 177,631.26
186,512.82
(95.55%)
libsignal_benchmark::dm_group::bench_dm_encrypt_subsequent_message subsequent:setup_established_dm_session()📈 view plot
🚷 view threshold
179,022.00
(+0.33%)Baseline: 178,439.00
187,360.95
(95.55%)
libsignal_benchmark::dm_group::bench_dm_session_establishment setup:setup_dm_users()📈 view plot
🚷 view threshold
17,224,679.00
(-0.23%)Baseline: 17,264,501.43
18,127,726.51
(95.02%)
libsignal_benchmark::group_messaging_group::bench_group_create_distribution_message create:setup_group_sender()📈 view plot
🚷 view threshold
295,925.00
(+0.10%)Baseline: 295,641.61
310,423.69
(95.33%)
libsignal_benchmark::group_messaging_group::bench_group_decrypt_message decrypt:setup_group_with_encrypted_message()📈 view plot
🚷 view threshold
12,702,357.00
(+0.72%)Baseline: 12,611,739.70
13,242,326.68
(95.92%)
libsignal_benchmark::group_messaging_group::bench_group_encrypt_message encrypt:setup_group_with_distribution()📈 view plot
🚷 view threshold
716,092.00
(+0.02%)Baseline: 715,964.17
751,762.38
(95.26%)
libsignal_benchmark::session_optimization_group::bench_decrypt_with_previous_session previous_session:setup_with_archived_sessions()📈 view plot
🚷 view threshold
41,798.00
(+0.54%)Baseline: 41,572.00
43,650.60
(95.76%)
libsignal_benchmark::session_optimization_group::bench_out_of_order_decryption out_of_order:setup_out_of_order_messages()📈 view plot
🚷 view threshold
5,530,955.00
(-0.25%)Baseline: 5,544,757.25
5,821,995.11
(95.00%)
libsignal_benchmark::session_optimization_group::bench_promote_matching_session promote:setup_promote_matching_session()📈 view plot
🚷 view threshold
965,435.00
(-0.75%)Baseline: 972,685.75
1,021,320.04
(94.53%)
libsignal_benchmark::signature_group::bench_key_generation keygen📈 view plot
🚷 view threshold
2,823,499.00
(+0.02%)Baseline: 2,822,809.80
2,963,950.29
(95.26%)
libsignal_benchmark::signature_group::bench_signature_creation sign:setup_keypair_with_message()📈 view plot
🚷 view threshold
3,445,806.00
(-22.86%)Baseline: 4,466,834.00
4,690,175.70
(73.47%)
libsignal_benchmark::signature_group::bench_signature_verification verify:setup_keypair_with_message()📈 view plot
🚷 view threshold
124,956,624.00
(-0.15%)Baseline: 125,140,173.60
131,397,182.28
(95.10%)
reporting_token_benchmark::content_extraction_group::bench_content_extraction extended:setup_extended_message()📈 view plot
🚷 view threshold
11,819.00
(+0.07%)Baseline: 11,811.28
12,401.84
(95.30%)
reporting_token_benchmark::content_extraction_group::bench_content_extraction simple:setup_simple_message()📈 view plot
🚷 view threshold
3,837.00
(+0.17%)Baseline: 3,830.31
4,021.83
(95.40%)
reporting_token_benchmark::full_generation_group::bench_full_token_generation extended:setup_full_gen_extended()📈 view plot
🚷 view threshold
88,033.00
(+0.03%)Baseline: 88,006.72
92,407.06
(95.27%)
reporting_token_benchmark::full_generation_group::bench_full_token_generation simple:setup_full_gen_simple()📈 view plot
🚷 view threshold
80,074.00
(+0.03%)Baseline: 80,048.76
84,051.20
(95.27%)
reporting_token_benchmark::key_derivation_group::bench_key_derivation📈 view plot
🚷 view threshold
51,035.00
(0.00%)Baseline: 51,035.00
53,586.75
(95.24%)
reporting_token_benchmark::message_encoding_group::bench_message_encoding extended:setup_extended_message()📈 view plot
🚷 view threshold
5,738.00
(+0.13%)Baseline: 5,730.28
6,016.79
(95.37%)
reporting_token_benchmark::message_encoding_group::bench_message_encoding simple:setup_simple_message()📈 view plot
🚷 view threshold
2,121.00
(+0.32%)Baseline: 2,114.31
2,220.03
(95.54%)
reporting_token_benchmark::token_calculation_group::bench_token_calculation📈 view plot
🚷 view threshold
21,921.00
(+0.07%)Baseline: 21,904.89
23,000.13
(95.31%)
🐰 View full continuous benchmarking report in Bencher

@jlucaso1
jlucaso1 merged commit a5aede8 into main Jan 24, 2026
8 checks passed
@jlucaso1
jlucaso1 deleted the feat-improvements-libsignal branch January 25, 2026 13:02
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.

1 participant