IT Security 1 month ago

CVE-2026-15890: Critical Nonce Reuse Vulnerability in Zephyr RTOS Trusted Storage – Impact, Detection, and Fix

CVE-2026-15890 is a critical race‑condition flaw in Zephyr RTOS's PSA Internal Trusted Storage that allows nonce reuse in AES‑GCM and ChaCha20‑Poly1305, breaking encryption confidentiality and integrity. The advisory details the vulnerability’s impact, detection methods, and the official CVE-2026-15890 is a critical race‑condition flaw in Zephyr RTOS's PSA Internal Trusted Storage that allows nonce reuse in AES‑GCM and ChaCha20‑Poly1305, breaking encryption confidentiality and integrity. The advisory…

IT Security

Vulnerability Overview

What CVE-2026-15890 Is

CVE-2026-15890 represents a critical race condition vulnerability in the Zephyr RTOS PSA Internal Trusted Storage (ITS) transform module. The flaw exists in the nonce generation function for authenticated encryption with associated data (AEAD) algorithms, specifically AES-GCM and ChaCha20-Poly1305. When multiple threads access the nonce generation routine concurrently, unsynchronized static variables can produce duplicate nonce values, fundamentally breaking the security guarantees of AEAD encryption.

This vulnerability is particularly severe because nonce reuse in AEAD ciphers is not merely a performance issue—it is a cryptographic catastrophe that enables attackers to recover plaintext and derive authentication keys.

Affected Systems and Versions

Product: Zephyr RTOS
Affected Component: PSA Internal Trusted Storage (ITS) transform module
Vulnerable Versions: All versions prior to the application of the security patch
Vulnerable File: subsys/secure_storage/src/its/transform/aead_get.c

Organizations using Zephyr RTOS for embedded IoT devices, industrial controllers, or any application requiring secure cryptographic storage should immediately verify their deployment versions.

Severity Assessment

Aspect Rating Justification
CVSS Severity Critical Affects core cryptographic functions with high exploitability
Confidentiality Impact High Plaintext recovery through nonce reuse attacks
Integrity Impact High Authentication key forgery and entry manipulation
Availability Impact Low No direct denial-of-service mechanism
Attack Complexity Low Requires only concurrent thread execution
Privileges Required Low Local thread execution sufficient

Technical Root Cause

Race Condition in Nonce Generation

The vulnerability originates in the secure_storage_its_transform_aead_get_nonce() function, which manages nonce generation for encrypted storage operations. The function maintains a counter to ensure unique nonces across successive encryption operations. However, this counter is stored in function-local static variables without synchronization primitives.

Vulnerable Code Pattern:

static uint64_t s_nonce = 0;
static bool s_nonce_initialized = false;
void secure_storage_its_transform_aead_get_nonce(uint8_t *nonce_buffer) {
    if (!s_nonce_initialized) {
        s_nonce = 0;
        s_nonce_initialized = true;
    }
    // No mutex protection here
    uint64_t current_nonce = s_nonce++;
    memcpy(nonce_buffer, &current_nonce, sizeof(uint64_t));
}

When Thread A and Thread B call this function simultaneously, both may read the same s_nonce value before either increments it, resulting in identical nonces being issued for different encryption operations.

Unsynchronized Static Variables

Static variables in C maintain state across function calls but are not thread-safe by default. In multi-threaded environments, concurrent access to these variables without mutual exclusion creates a classic race condition:

  1. Thread A reads s_nonce (value: 42)
  2. Thread B reads s_nonce (value: 42) — before Thread A increments
  3. Thread A increments to 43 and encrypts with nonce 42
  4. Thread B increments to 44 and encrypts with nonce 42
  5. Result: Two different plaintexts encrypted with identical nonce and key

Why AEAD Algorithms Are Vulnerable to Nonce Reuse

AEAD ciphers like AES-GCM and ChaCha20-Poly1305 derive their authentication tag from both the plaintext and a unique nonce. The security model assumes:

  • Nonce Uniqueness: Each (key, nonce) pair is used exactly once
  • Tag Verification: Tags cannot be forged without knowing the key

When nonces are reused:

  1. Plaintext Recovery: Attackers can XOR two ciphertexts encrypted with the same nonce to cancel the keystream, revealing plaintext differences
  2. Authentication Key Derivation: The authentication key can be computed from two ciphertexts sharing a nonce, enabling tag forgery
  3. Entry Manipulation: Attackers can create valid authentication tags for arbitrary data, compromising stored secrets

Attack Mechanics

Concurrent Write Exploitation

An attacker with the ability to spawn threads on the target system can deliberately trigger concurrent writes to the same storage UID:

Attack Sequence:

  1. Thread Spawning: Attacker creates two threads that simultaneously write sensitive data to the same ITS UID
  2. Nonce Collision: Both threads receive identical nonces from the vulnerable generation function
  3. Dual Encryption: Two different plaintexts are encrypted with the same key and nonce
  4. Storage Persistence: Both ciphertexts are written to backing storage (or one overwrites the other)

The ITS layer does not serialize concurrent writes to the same UID, allowing both operations to complete with compromised nonces.

Data Decryption Through Nonce Reuse

With two ciphertexts (C₁ and C₂) encrypted with the same key K and nonce N:

Mathematical Exploitation:

C₁ = P₁ ⊕ Keystream(K, N)
C₂ = P₂ ⊕ Keystream(K, N)
C₁ ⊕ C₂ = P₁ ⊕ P₂

If an attacker knows or can guess P₁ (e.g., a known format or header), they can recover P₂. Even without known plaintext, statistical analysis of the XOR result reveals information about both plaintexts.

Example Scenario:

  • P₁: Stored AES-256 key (32 bytes) with known structure
  • P₂: Stored ECDSA private key (32 bytes)
  • Attack: XOR reveals both keys to attacker

Authentication Key Forgery

AEAD authentication tags are computed using a derived authentication key. With nonce reuse, this key can be recovered:

  1. Tag Extraction: Attacker obtains authentication tags from both ciphertexts
  2. Key Derivation: Using the known nonce and ciphertexts, the authentication key is computed
  3. Forgery: Attacker can now create valid tags for arbitrary data, enabling:
  • Injection of malicious configuration
  • Replacement of stored keys with attacker-controlled values
  • Modification of security policies

Real-World Attack Scenarios

Scenario 1: IoT Device Compromise

A Zephyr RTOS-based smart lock stores encryption keys in ITS. An attacker with local code execution (via firmware vulnerability) spawns threads to trigger nonce reuse, recovers the stored keys, and gains permanent access to the device.

Scenario 2: Industrial Controller Sabotage

A manufacturing controller uses Zephyr RTOS to store calibration certificates in ITS. An attacker exploits nonce reuse to forge certificates, causing the controller to accept malicious calibration data and producing defective products.

Scenario 3: Credential Theft

A connected medical device stores patient authentication tokens in ITS. Nonce reuse allows an attacker to recover tokens, impersonate patients, and access sensitive health records.


Security Impact

Confidentiality Breach

The vulnerability enables complete recovery of encrypted data stored in ITS. Any sensitive information—cryptographic keys, authentication tokens, personal data—becomes accessible to attackers with local thread execution capability.

Affected Data Types:

  • Persistent cryptographic keys (AES, ECDSA, RSA)
  • Authentication credentials and tokens
  • Configuration secrets
  • Personally identifiable information (PII)

Integrity Compromise

Beyond decryption, attackers can forge authentication tags and inject malicious data into storage:

  • Key Replacement: Substitute legitimate keys with attacker-controlled alternatives
  • Configuration Tampering: Modify security policies and access controls
  • Backdoor Installation: Inject persistent malicious code references

Risk to Cryptographic Keys

Keys stored in ITS are typically the foundation of device security. Their compromise cascades to all cryptographic operations:

  • TLS/DTLS Sessions: Attacker can decrypt all past and future communications
  • Firmware Signing: Attacker can sign malicious firmware updates
  • Device Authentication: Attacker can impersonate the device to backend systems

Organizational Exposure

For organizations deploying Zephyr RTOS:

Exposure Type Impact
Supply Chain Compromised devices in field; potential recall costs
Regulatory GDPR/HIPAA violations if PII/health data exposed
Operational Loss of device integrity; inability to trust security claims
Reputational Customer trust erosion; brand damage

Detection and Assessment

Identifying Vulnerable Deployments

Version Check:

# In Zephyr project directory
git log --oneline subsys/secure_storage/src/its/transform/aead_get.c | head -20

Compare your Zephyr version against the patch release date. All versions prior to the fix are vulnerable.

Component Verification:

# Check if PSA ITS is enabled in your build
grep -r "CONFIG_PSA_ITS" .config
grep -r "CONFIG_SECURE_STORAGE" .config

If these configurations are enabled, your deployment uses the vulnerable component.

Static Code Analysis Approach

Manual Code Review:

  1. Locate subsys/secure_storage/src/its/transform/aead_get.c
  2. Search for static variable declarations without mutex protection
  3. Identify all functions accessing these variables
  4. Verify mutex guards around critical sections

Automated Analysis:

Use static analysis tools to detect unsynchronized access to shared variables:

# Example with clang-analyzer
scan-build make
# Look for warnings about data races and unsynchronized access

Runtime Monitoring for Concurrent Operations

Thread Monitoring:

Implement logging to detect concurrent writes to the same UID:

// Add instrumentation to ITS write path
void its_write_with_monitoring(uint32_t uid, const uint8_t *data, size_t size) {
    static uint32_t last_uid = 0;
    static uint64_t last_write_time = 0;
    uint64_t current_time = k_uptime_get();
    if (uid == last_uid && (current_time - last_write_time) < 10) {
        // Potential concurrent write detected
        LOG_WRN("Concurrent write to UID 0x%x detected", uid);
    }
    last_uid = uid;
    last_write_time = current_time;
    // Proceed with write
}

Audit Checklist

  • [ ] Zephyr version identified and documented
  • [ ] PSA ITS component status confirmed (enabled/disabled)
  • [ ] Source code reviewed for mutex protection in nonce generation
  • [ ] Thread creation patterns analyzed for concurrent storage access
  • [ ] Backup of current firmware created before patching
  • [ ] Test environment prepared for patch validation
  • [ ] Stakeholders notified of vulnerability and remediation timeline

Remediation Strategy

Mutex-based synchronization fix for nonce generation, ensuring thread-safe counter increments.
Mutex-based synchronization fix for nonce generation, ensuring thread-safe counter increments.

Immediate Patching Requirements

Priority: Critical — Apply immediately to all production systems

Patch Availability:

The official fix introduces mutex-based synchronization to the nonce generation function. Check the Zephyr project repository for the specific patch commit:

git log --grep="CVE-2026-15890" --oneline
git show <commit-hash>

Pre-Patch Checklist:

  • [ ] Backup current firmware and configuration
  • [ ] Document current Zephyr version and build configuration
  • [ ] Identify all devices requiring patching
  • [ ] Prepare rollback procedure
  • [ ] Schedule maintenance window with stakeholders

Mutex-Based Synchronization Fix

The official patch replaces unsynchronized static variables with a protected counter:

Fixed Code Pattern:

K_MUTEX_DEFINE(s_nonce_mutex);
static uint64_t s_nonce = 0;
static bool s_nonce_initialized = false;
void secure_storage_its_transform_aead_get_nonce(uint8_t *nonce_buffer) {
    k_mutex_lock(&s_nonce_mutex, K_FOREVER);
    if (!s_nonce_initialized) {
        s_nonce = 0;
        s_nonce_initialized = true;
    }
    uint64_t current_nonce = s_nonce++;
    k_mutex_unlock(&s_nonce_mutex);
    memcpy(nonce_buffer, &current_nonce, sizeof(uint64_t));
}

Key Changes:

  1. Mutex Declaration: K_MUTEX_DEFINE() creates a kernel-managed mutual exclusion lock
  2. Lock Acquisition: k_mutex_lock() ensures only one thread accesses the counter
  3. Atomic Increment: Counter increment is protected from race conditions
  4. Lock Release: k_mutex_unlock() allows other threads to proceed

Testing and Validation

Unit Testing:

// Test concurrent nonce generation
void test_concurrent_nonce_generation(void) {
    uint8_t nonce1[8], nonce2[8];
    // Spawn two threads that simultaneously request nonces
    k_thread_create(&thread1, stack1, STACK_SIZE,
                    thread_get_nonce, (void *)nonce1, NULL, NULL,
                    K_PRIO_PREEMPT(0), 0, K_NO_WAIT);
    k_thread_create(&thread2, stack2, STACK_SIZE,
                    thread_get_nonce, (void *)nonce2, NULL, NULL,
                    K_PRIO_PREEMPT(0), 0, K_NO_WAIT);
    k_thread_join(&thread1, K_FOREVER);
    k_thread_join(&thread2, K_FOREVER);
    // Verify nonces are different
    zassert_mem_different(nonce1, nonce2, 8, "Nonces must be unique");
}

Integration Testing:

  1. Build patched Zephyr version
  2. Deploy to test devices
  3. Run existing application workloads
  4. Monitor for performance regressions (mutex overhead is minimal)
  5. Verify encrypted storage operations complete successfully

Regression Testing:

  • Confirm all ITS read/write operations function correctly
  • Validate encryption/decryption of stored data
  • Test with high thread concurrency to stress-test mutex implementation

Rollout Timeline

Phase Timeline Actions
Preparation Day 1-2 Patch acquisition, testing environment setup, stakeholder notification
Validation Day 3-5 Unit and integration testing, performance analysis, security review
Staged Rollout Day 6-10 Deploy to non-critical systems first, monitor for issues
Production Rollout Day 11-30 Deploy to production systems in batches, maintain rollback readiness
Verification Day 31+ Confirm all systems patched, audit logs reviewed, incident response plan updated

Prevention and Hardening

Code Review Best Practices

Concurrency-Focused Review:

  1. Identify Shared State: Flag all static and global variables
  2. Verify Synchronization: Confirm each shared variable has appropriate locking
  3. Check Lock Scope: Ensure locks protect all accesses, not just some
  4. Analyze Deadlock Risk: Verify lock ordering prevents circular dependencies

Review Checklist:

[ ] All static variables documented with thread-safety status
[ ] Mutex/semaphore guards present for all shared mutable state
[ ] Lock acquisition and release are paired (no missing unlocks)
[ ] No locks held across blocking operations (potential deadlock)
[ ] No nested locks without documented ordering
[ ] Atomic operations used where appropriate (e.g., k_atomic_*)

Thread Safety Implementation

Zephyr RTOS Synchronization Primitives:

Primitive Use Case Overhead
Mutex Mutual exclusion for shared resources Low
Semaphore Signaling and counting Low
Spinlock Short critical sections (ISR context) Very Low
Atomic Operations Single-variable updates Minimal

Best Practice Pattern:

// Declare synchronization primitive
K_MUTEX_DEFINE(resource_mutex);
// Protect access
void access_shared_resource(void) {
    k_mutex_lock(&resource_mutex, K_FOREVER);
    // Critical section - keep brief
    shared_resource.value++;
    k_mutex_unlock(&resource_mutex);
}

Concurrent Access Controls

ITS-Specific Hardening:

  1. Serialize UID Access: Implement per-UID locks to prevent concurrent writes
  2. Atomic Operations: Use atomic compare-and-swap for nonce counters
  3. Thread Affinity: Pin storage operations to a single thread if feasible
  4. Access Logging: Log all storage operations with thread ID and timestamp

Example Implementation:

#define MAX_ITS_UIDS 256
K_MUTEX_DEFINE(its_uid_mutex[MAX_ITS_UIDS]);
int its_write_protected(uint32_t uid, const uint8_t *data, size_t size) {
    uint32_t uid_index = uid % MAX_ITS_UIDS;
    k_mutex_lock(&its_uid_mutex[uid_index], K_FOREVER);
    int result = its_write_internal(uid, data, size);
    k_mutex_unlock(&its_uid_mutex[uid_index]);
    return result;
}

Long-Term Security Posture

Architectural Improvements:

  1. Threat Modeling: Conduct formal threat analysis of cryptographic components
  2. Fuzzing: Implement continuous fuzzing of ITS interfaces
  3. Security Audits: Schedule annual third-party security reviews
  4. Dependency Tracking: Monitor Zephyr project for security advisories
  5. Secure Development: Adopt secure coding standards and training

Incident Response Considerations

Determining Exposure Window

Timeline Reconstruction:

  1. Identify Patch Date: Determine when CVE-2026-15890 fix was released
  2. Deployment Date: Confirm when vulnerable version was deployed to production
  3. Exposure Period: Calculate time between deployment and patching
  4. Activity Logs: Review system logs for suspicious concurrent access patterns

Evidence Collection:

# Extract relevant logs
journalctl --since "2026-01-01" --until "2026-03-15" | grep -i "storage\|nonce\|concurrent"
# Check firmware version history
cat /proc/version
dmesg | grep -i "zephyr\|version"

Assessing Data Compromise

Compromise Assessment Framework:

Question Investigation Method
Was ITS accessed? Check access logs and thread activity during exposure window
Which UIDs were affected? Identify concurrent writes to same UID in logs
What data was stored? Review application documentation and configuration
Was data exfiltrated? Analyze network traffic logs for suspicious data transfers
Were keys compromised? Assume compromise if keys were stored in affected UIDs

Risk Scoring:

Risk = (Data Sensitivity) × (Exposure Duration) × (Concurrent Access Likelihood)
High Risk:   Cryptographic keys stored in ITS during exposure window
Medium Risk: Non-key secrets (tokens, credentials) with moderate exposure
Low Risk:    Non-sensitive data or very brief exposure window

Key Rotation Requirements

Mandatory Key Rotation:

If any cryptographic keys were stored in ITS during the exposure window, assume compromise and rotate immediately:

  1. Identify Affected Keys:
  • AES encryption keys
  • ECDSA/RSA private keys
  • HMAC keys
  • TLS certificates and keys
  1. Rotation Procedure:
   a) Generate new keys
   b) Update all systems using old keys
   c) Revoke old keys (if applicable)
   d) Verify new keys are functional
   e) Destroy old key material securely
  1. Downstream Impact:
  • Re-encrypt all data encrypted with compromised keys
  • Reissue authentication tokens
  • Update TLS certificates
  • Notify backend systems of key changes

Stakeholder Communication

Communication Template:

SECURITY INCIDENT NOTIFICATION
Incident: CVE-2026-15890 - Nonce Reuse in Zephyr RTOS ITS
Severity: Critical
Affected Systems: [List specific devices/systems]
Exposure Window: [Date range]
IMPACT:
- Encrypted data may have been decrypted
- Stored cryptographic keys may be compromised
- System integrity cannot be guaranteed
ACTIONS TAKEN:
- All systems patched to latest Zephyr version
- Cryptographic keys rotated
- Forensic analysis completed
- Enhanced monitoring implemented
CUSTOMER ACTIONS REQUIRED:
- Update devices to patched firmware version
- Rotate any credentials used with affected systems
- Monitor for unauthorized access
TIMELINE:
- [Date]: Vulnerability discovered
- [Date]: Patch released
- [Date]: All systems patched
- [Date]: Key rotation completed

Frequently Asked Questions

Q: What happens when a nonce is reused in AES-GCM or ChaCha20-Poly1305?

A: Nonce reuse with the same key allows attackers to recover plaintext and derive the authentication key, enabling both data decryption and entry forgery. Specifically, XORing two ciphertexts with identical nonces cancels the keystream, revealing plaintext differences. The authentication key can then be computed, allowing attackers to forge valid tags for arbitrary data.

Q: How do I determine if my Zephyr RTOS deployment is affected?

A: Check your Zephyr version against the patch release date. All versions prior to the fix are vulnerable if PSA Internal Trusted Storage is enabled. Verify by checking CONFIG_PSA_ITS and CONFIG_SECURE_STORAGE in your build configuration. Review the source file subsys/secure_storage/src/its/transform/aead_get.c for mutex protection around nonce generation.

Q: Can this vulnerability be exploited remotely?

A: Exploitation requires either local thread execution on the target system or direct access to raw backing storage. Remote exploitation is unlikely unless the system exposes storage interfaces over the network. However, if a device has other remote code execution vulnerabilities, this flaw could be chained for complete compromise.

Q: What is the recommended patching timeline?

A: Apply the fix immediately for production systems. The vulnerability is critical and affects core security functions. Even brief exposure windows can result in key compromise. Prioritize systems storing cryptographic keys or sensitive authentication data.

Q: Do I need to rotate cryptographic keys after patching?

A: Yes. If the system was exposed during the vulnerability window and stored cryptographic keys in ITS, rotate all keys immediately. Assume any keys stored during the exposure period are compromised. This includes AES keys, ECDSA/RSA private keys, and HMAC keys.

Q: Are there workarounds if I cannot patch immediately?

A: Implement strict access controls to prevent concurrent writes to the same UID. Disable multi-threaded access to ITS if possible. Monitor storage access patterns for suspicious concurrent operations. However, these are temporary measures only—patching is essential. Consider disabling ITS entirely if it cannot be patched and is not critical to operations.

Q: Will patching cause performance degradation?

A: The mutex-based fix introduces minimal overhead. Lock contention is unlikely in typical deployments since ITS operations are infrequent. Performance impact is negligible compared to the security benefit.

Q: How do I verify the patch was applied correctly?

A: Review the source code to confirm mutex protection is present in aead_get.c. Run unit tests for concurrent nonce generation. Perform integration testing with your application workload to verify functionality.


References

  • Zephyr Project Security Advisory: [Link to official advisory]
  • CVE-2026-15890 Details: [Link to CVE database entry]
  • NIST SP 800-38D: Recommendation for GCM Mode of Operation
  • RFC 7539: ChaCha20 and Poly1305 AEAD Cipher
  • Zephyr RTOS Documentation: Secure Storage and PSA Crypto
  • CWE-362: Concurrent Modification of Shared Resource Without Synchronization
  • OWASP: Cryptographic Failures and Race Conditions

Document Version: 1.0
Last Updated: [Current Date] Classification: Security Advisory
Distribution: Zephyr RTOS Users, Security Teams, Device Manufacturers

For questions or additional information, contact your security team or the Zephyr Project security contact.

← Back to IT Security