@@ -351,27 +351,29 @@ void Utils::getHighQualityRandom(RNG* rng, uint8_t* dest, size_t size) {
351351// ========== Secure Counter Generation ==========
352352
353353// Static state for counter generation
354- static uint16_t s_counter_boot_id = 0;
354+ // Counter format: [epoch (2 bytes random)] [sequence (2 bytes incrementing)]
355+ // - epoch: random value, regenerated on boot or sequence wraparound
356+ // - sequence: 0-65535, increments each message
357+ // This guarantees uniqueness across devices (different epochs) and
358+ // within long-running sessions (epoch changes on wraparound)
359+ static uint16_t s_counter_epoch = 0;
355360static uint16_t s_counter_seq = 0;
356361
357362void Utils::generateSecureCounter(uint8_t* counter) {
358- // Counter format: [boot_id (2)] [sequence (2)]
359- // This ensures uniqueness even if:
360- // - Many messages sent quickly
361- // - Device reboots (new boot_id)
362- // Full nonce uniqueness comes from SHA256(key || counter) derivation
363-
364- // Initialize boot_id once per boot with random value
365- if (s_counter_boot_id == 0) {
366- getHardwareRandom((uint8_t*)&s_counter_boot_id, 2);
367- // Ensure non-zero (unlikely, but handle it)
368- if (s_counter_boot_id == 0) s_counter_boot_id = 1;
363+ // Initialize epoch on first call
364+ if (s_counter_epoch == 0) {
365+ getHardwareRandom((uint8_t*)&s_counter_epoch, 2);
366+ if (s_counter_epoch == 0) s_counter_epoch = 1;
369367 }
370368
371- // Copy boot_id (bytes 0-1)
372- memcpy(counter, &s_counter_boot_id, 2);
369+ // On sequence wraparound, generate new epoch
370+ if (s_counter_seq == 0xFFFF) {
371+ getHardwareRandom((uint8_t*)&s_counter_epoch, 2);
372+ if (s_counter_epoch == 0) s_counter_epoch = 1;
373+ s_counter_seq = 0;
374+ }
373375
374- // Copy and increment sequence (bytes 2-3)
376+ memcpy(counter, &s_counter_epoch, 2);
375377 memcpy(counter + 2, &s_counter_seq, 2);
376378 s_counter_seq++;
377379}
0 commit comments