Enforce PIN requirement for storage encryption#131
Conversation
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. WalkthroughRemoved boot-time storage_crypto initialization and migration; added PIN rate-limiting/lockout and APIs; made storage_crypto self-test skippable when uninitialized; record failed decrypt attempts; updated mocks and tests to exercise new behaviors. Changes
Sequence Diagram(s)sequenceDiagram
participant Caller as Caller
participant StorageCrypto as storage_crypto
participant RateLimit as RateLimit
participant KeyDerive as KeyDerivation
participant AttemptTrack as AttemptTracker
Caller->>StorageCrypto: storage_crypto_init(pin)
StorageCrypto->>RateLimit: storage_crypto_check_rate_limit()
alt Locked / Must wait
RateLimit-->>StorageCrypto: ERR_PIN_LOCKED / ERR_PIN_MUST_WAIT
StorageCrypto-->>Caller: return error
else Allowed
StorageCrypto->>StorageCrypto: validate PIN length
alt Invalid PIN
StorageCrypto->>AttemptTrack: storage_crypto_record_attempt(false)
AttemptTrack-->>StorageCrypto: record (maybe lockout)
StorageCrypto-->>Caller: ERR_PIN_INVALID
else Valid PIN
StorageCrypto->>KeyDerive: derive key from device id + pin
alt Derive success
StorageCrypto->>AttemptTrack: storage_crypto_record_attempt(true)
StorageCrypto-->>Caller: success
else Derive fail
StorageCrypto->>AttemptTrack: storage_crypto_record_attempt(false)
StorageCrypto-->>Caller: error
end
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@main/storage_crypto.c`:
- Around line 159-183: The code currently only records failed attempts for
invalid PIN format and successful key init; update storage_crypto_init so that
when derive_key(device_id, ..., (const uint8_t *)pin, pin_len, storage_key)
returns non-zero you call storage_crypto_record_attempt(false) before returning
the error, and ensure secure_memzero/device cleanup still occurs; likewise, in
storage_crypto_decrypt (the decryption routine in storage.c) detect the
wrong-PIN decryption failure path and call storage_crypto_record_attempt(false)
when decryption fails due to an incorrect PIN so those failures count toward the
lockout limit.
🧹 Nitpick comments (1)
test/native/mocks/storage_crypto.h (1)
45-52: Prefer bounded length to mirror production.
strlencan read past non‑terminated buffers; usingstrnlen(..., STORAGE_CRYPTO_MAX_PIN_LEN + 1)keeps the mock aligned with real behavior.♻️ Suggested change
- size_t pin_len = pin ? strlen(pin) : 0; + size_t pin_len = pin ? strnlen(pin, STORAGE_CRYPTO_MAX_PIN_LEN + 1) : 0;
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@main/storage_crypto.c`:
- Around line 121-129: The signed cast of the time delta causes stale timestamps
to appear "recent" after ~24.8 days; fix the check in the loop that counts
recent attempts by using unsigned arithmetic: compute the difference between now
and pin_attempt_times[i] as an unsigned type (e.g., uint32_t or uint64_t) and
compare that to PIN_RATE_LIMIT_WINDOW_MS instead of casting to int32_t, ensuring
the condition becomes something like (now - pin_attempt_times[i]) <
PIN_RATE_LIMIT_WINDOW_MS so that stale (older) timestamps are not treated as
recent; update the loop that uses pin_attempt_count, pin_attempt_times,
PIN_RATE_LIMIT_MAX, PIN_RATE_LIMIT_WINDOW_MS and keep the ERR_PIN_MUST_WAIT
return logic unchanged.
- Require non-empty PIN for storage_crypto_init() - Remove auto-init with NULL at boot (share ops need explicit unlock) - Update self_test to be non-critical since crypto not init at boot - Update mock and tests to match new behavior
- Persist PIN rate limit state to NVS storage (survives reboots) - Add "unlock" RPC method to initialize storage crypto with PIN - Run storage migration after successful PIN entry - Add rate limit check before share decryption attempts - Remove storage_crypto_reset_rate_limit from public header - Replace storage_crypto self-test with AES-GCM test vectors - Fail hard on non-ESP platforms if device ID unavailable - Fix timing side channel in rate limit check (constant-time loop)
8f6593d to
913c33b
Compare
Summary
Test plan
Summary by CodeRabbit
New Features
Improvements
Tests
✏️ Tip: You can customize this high-level summary in your review settings.