🐛 [BUG] - Conformance - SA Rekey Never Checks That the New Encryption/Authentication Keys Are Active
Nobody has claimed this yet.
- Dominant language
- C
- Stars
- 168
- Forks
- 67
- PR merge metrics
- No merged PRs in 30d
Description
Description
Bug Report: SA Rekey Never Checks That the New Encryption/Authentication Keys Are Active
Summary
| Field | Value |
|---|---|
| Product | NASA CryptoLib (SDLS Protocol Implementation) |
| Version | 1.4.2 & dev branch - details specific to v1.4.2 |
| Component | src/sa/internal/sa_interface_inmemory.template.c — sa_rekey |
| Issue | SA Rekey accepts and binds a new encryption/authentication key ID to an SA without verifying that key is actually ACTIVE |
| Bug Type | Conformance |
| Severity | Medium |
| Impact | An SA can be rekeyed to reference a key that was never provisioned, was deactivated, or was destroyed — the SA transitions to KEYED and appears usable, but the underlying key material is not what the protocol's key-lifecycle state machine says it should be |
Description
An SA Rekey command supplies new encryption/authentication key IDs (ekid/akid) for the
target SA. Per the SDLS-EP key-lifecycle model, a key must be ACTIVE (i.e. have completed the
OTAR → PREACTIVE → ACTIVE transition) before it's eligible to be bound to an SA this way —
rekeying with an inactive key silently associates the SA with key material that hasn't been
through — or has left — the activation process.
sa_rekey reads both key IDs directly off the wire and assigns them straight into the SA
struct, then flips the SA's state to SA_KEYED, with no lookup of — and no check on — either
key's key_state anywhere in the function.
Affected Code
sa_rekey (src/sa/internal/sa_interface_inmemory.template.c:1265-1310)
static int32_t sa_rekey(TC_t *tc_frame)
{
...
// Check SPI exists and in 'Unkeyed' state
if (spi < NUM_SA)
{
sa[spi].lpid = ...;
if (sa[spi].sa_state == SA_UNKEYED)
{ // Encryption Key
sa[spi].ekid =
((uint8_t)sdls_frame.tlv_pdu.data[count] << BYTE_LEN) | (uint8_t)sdls_frame.tlv_pdu.data[count + 1];
count = count + 2;
sa[spi].akid =
((uint8_t)sdls_frame.tlv_pdu.data[count] << BYTE_LEN) | (uint8_t)sdls_frame.tlv_pdu.data[count + 1];
count = count + 2;
// ... IV handling ...
sa[spi].sa_state = SA_KEYED;
}
}
Both ekid and akid are written straight from the wire into the SA struct with no call to
key_if->get_key() and no key_state check of any kind, then the SA is immediately
transitioned to SA_KEYED. The key-state field these key IDs should be checked against already
exists and is tracked correctly elsewhere in the codebase (see the sibling Crypto_Key_verify
lifecycle discussion) — sa_rekey simply never looks it up.
Reproduction Sequence (from the fuzzing logs)
Replaying the full multi-packet mismatch pcap (crypto_triage, -DDEBUG=ON,
NULL_CRYPTO_CONFIG=ON) isolates a clean two-step reproduction: create an SA, then rekey it to
an inactive key with no prior encryption/auth configuration to interfere.
| # | Direction | Hex | Meaning |
|---|---|---|---|
| 1 | ground → flight | 002c10a6000000001104d0003f0c001001010c00000000000000000000000001020080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e12b |
TC frame, sg=0x1, pid=0x1 (SA Create), SPI 63 — leaves SPI 63 in SA_UNKEYED |
| 2 | ground → flight | 002c101e00000000160090003f00820082000000000000000000000000551f |
TC frame, sg=0x1, pid=0x6 (SA Rekey), SPI 63, new ekid=akid=0x0082 (130) |
Debug replay excerpt for packet 2 (immediately following packet 1's SA Create):
SA Create
spi = 63
...
SA status:
spi = 63
sa_state = 0x1 <- SA_UNKEYED
...
Status code: 0
...
SA Rekey
SPI 63 IV updated to: 0x000000000000000000000000
SPI 63 changed to KEYED state with encrypted Key ID 130.
spi = 63
ekid = 130
Status code: 0
Suggested Fix
Look up both key IDs via key_if->get_key() and check key_state == KEY_ACTIVE before
accepting them, mirroring the check the oracle already performs:
crypto_key_t *new_ekp = key_if->get_key(new_ekid);
crypto_key_t *new_akp = key_if->get_key(new_akid);
if (new_ekp == NULL || new_ekp->key_state != KEY_ACTIVE ||
new_akp == NULL || new_akp->key_state != KEY_ACTIVE)
{
return CRYPTO_LIB_ERR_KEY_STATE_INVALID;
}
placed before the keys are written into sa[spi].
Discovered using the StratoFuzz protocol fuzzing framework.
Branch Name
dev
Reproduction steps
## Reproduction Sequence (from the fuzzing logs)
Replaying the full multi-packet mismatch pcap (`crypto_triage`, `-DDEBUG=ON`,
`NULL_CRYPTO_CONFIG=ON`) isolates a clean two-step reproduction: create an SA, then rekey it to
an inactive key with no prior encryption/auth configuration to interfere.
| # | Direction | Hex | Meaning |
|---|-----------|-----|---------|
| 1 | ground → flight | `002c10a6000000001104d0003f0c001001010c00000000000000000000000001020080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e12b` | TC frame, `sg=0x1, pid=0x1` (SA Create), SPI 63 — leaves SPI 63 in `SA_UNKEYED` |
| 2 | ground → flight | `002c101e00000000160090003f00820082000000000000000000000000551f` | TC frame, `sg=0x1, pid=0x6` (SA Rekey), SPI 63, new `ekid=akid=0x0082` (130) — the same key ID shown never-active in `BUG-016` |
Debug replay excerpt for packet 2 (immediately following packet 1's `SA Create`):
SA Create
spi = 63
...
SA status:
spi = 63
sa_state = 0x1 <- SA_UNKEYED
...
Status code: 0
...
SA Rekey
SPI 63 IV updated to: 0x000000000000000000000000
SPI 63 changed to KEYED state with encrypted Key ID 130.
spi = 63
ekid = 130
Status code: 0
Screenshots
Logs
OS
Linux
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/sa/internal/sa_interface_inmemory.template.c at sa_rekey (around lines 1265-1310), then inspect the existing key_if->get_key() and key_state handling referenced in the issue. Replay the two-packet sequence with crypto_triage, -DDEBUG=ON, and NULL_CRYPTO_CONFIG=ON. Done means inactive or missing encryption/authentication keys are rejected, while active keys can still transition the SA to SA_KEYED.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100