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, ¤t_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:
- Thread A reads
s_nonce(value: 42) - Thread B reads
s_nonce(value: 42) — before Thread A increments - Thread A increments to 43 and encrypts with nonce 42
- Thread B increments to 44 and encrypts with nonce 42
- 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:
- Plaintext Recovery: Attackers can XOR two ciphertexts encrypted with the same nonce to cancel the keystream, revealing plaintext differences
- Authentication Key Derivation: The authentication key can be computed from two ciphertexts sharing a nonce, enabling tag forgery
- 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:
- Thread Spawning: Attacker creates two threads that simultaneously write sensitive data to the same ITS UID
- Nonce Collision: Both threads receive identical nonces from the vulnerable generation function
- Dual Encryption: Two different plaintexts are encrypted with the same key and nonce
- 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:
- Tag Extraction: Attacker obtains authentication tags from both ciphertexts
- Key Derivation: Using the known nonce and ciphertexts, the authentication key is computed
- 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:
- Locate
subsys/secure_storage/src/its/transform/aead_get.c - Search for static variable declarations without mutex protection
- Identify all functions accessing these variables
- 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

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, ¤t_nonce, sizeof(uint64_t));
}
Key Changes:
- Mutex Declaration:
K_MUTEX_DEFINE()creates a kernel-managed mutual exclusion lock - Lock Acquisition:
k_mutex_lock()ensures only one thread accesses the counter - Atomic Increment: Counter increment is protected from race conditions
- 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:
- Build patched Zephyr version
- Deploy to test devices
- Run existing application workloads
- Monitor for performance regressions (mutex overhead is minimal)
- 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:
- Identify Shared State: Flag all static and global variables
- Verify Synchronization: Confirm each shared variable has appropriate locking
- Check Lock Scope: Ensure locks protect all accesses, not just some
- 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:
- Serialize UID Access: Implement per-UID locks to prevent concurrent writes
- Atomic Operations: Use atomic compare-and-swap for nonce counters
- Thread Affinity: Pin storage operations to a single thread if feasible
- 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:
- Threat Modeling: Conduct formal threat analysis of cryptographic components
- Fuzzing: Implement continuous fuzzing of ITS interfaces
- Security Audits: Schedule annual third-party security reviews
- Dependency Tracking: Monitor Zephyr project for security advisories
- Secure Development: Adopt secure coding standards and training
Incident Response Considerations
Determining Exposure Window
Timeline Reconstruction:
- Identify Patch Date: Determine when CVE-2026-15890 fix was released
- Deployment Date: Confirm when vulnerable version was deployed to production
- Exposure Period: Calculate time between deployment and patching
- 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:
- Identify Affected Keys:
- AES encryption keys
- ECDSA/RSA private keys
- HMAC keys
- TLS certificates and keys
- 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
- 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.