Skip to content

Make RSA key size configurable #289

@NateSwanson7

Description

@NateSwanson7

Make platform RSA modulusLength configurable on lti.setup()

Summary

Auth.generatePlatformKeyPair hardcodes crypto.generateKeyPairSync('rsa', { modulusLength: 4096, ... }). This call is synchronous (blocking the node event loop), and on small instances (AWS Lambda, small EC2, etc) can take anywhere from a couple of seconds to half a minute on first registration, with significant run-to-run variance even on identical hardware. I'd like to make the modulus length configurable via a new options.keySize on lti.setup(), defaulting to the current 4096 so existing consumers see no behavior change.

Why 4096-bit synchronous keygen is painful

Two things compound:

  1. It's synchronous. crypto.generateKeyPairSync blocks the event loop. Nothing else in the process runs while it executes, so during a first-time dynamic registration, the whole tool freezes for the duration of keygen.

  2. It has high intrinsic variance. RSA keygen retries until the randomly-sampled candidate primes pass primality testing. A "slow-luck" sample can take several times the median on the same hardware. I ran six consecutive fresh 4096-bit registrations on identical local hardware:

    Run keygen + Mongo write Handler total
    1 604 ms 781 ms
    2 335 ms 538 ms
    3 436 ms 604 ms
    4 708 ms 877 ms
    5 969 ms 1138 ms
    6 876 ms 1022 ms

    The keygen step spans 335 ms → 969 ms, a ~2.9× spread. The same multiplicative variance on a smaller, slower CPU (think small EC2 or Lambda cold start) drags the tail into the double-digit-seconds range, which is what users notice.

For comparison, the same setup with modulusLength: 2048 produces handler times in the 150-300 ms range, an order-of-magnitude improvement that also flattens the variance pattern, a smaller prime search space means proportionally less tail.

Why 2048 is a safe option to offer

  • IMS LTI 1.3 spec: 1EdTech Security Framework v1.0 requires platforms and tools to support RS256 on a minimum key size of 2048 bits. 4096 is allowed, not required. (spec)
  • NIST SP 800-57: RSA-2048 is considered secure through at least 2030.
  • Industry practice: Canvas, Moodle, and Blackboard's own LTI tools use 2048-bit RSA. Offering 2048 as an option puts ltijs consumers in the mainstream, not below it.

Proposed solution

A single new options.keySize on lti.setup(), defaulting to 4096:

lti.setup(KEY, db, {
  // ...existing options
  keySize: 2048,
})

Threaded as:

  • Provider.setup() validates that keySize is an integer ≥ 2048 (throws INVALID_KEYSIZE otherwise), stores it on a private field, default 4096.
  • Provider.registerPlatform() accepts an optional keySize arg that falls back to the configured value (mirrors the existing getPlatform / ENCRYPTIONKEY / Database fallback pattern, so the option works through both the direct call path and the DynamicRegistration path where this is the service instance, not the Provider).
  • DynamicRegistration constructor accepts and stores keySize, then passes it through to registerPlatform.
  • Auth.generatePlatformKeyPair(..., keySize = 4096) uses it for modulusLength.

Default value is unchanged at 4096, so this is purely additive, no consumer who isn't passing the new option sees any behavior change.

PR

PR with the change + tests: #288

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions