nasa / nasa/CryptoLib

🐛 [BUG] - Conformance - SA Rekey Never Checks That the New Encryption/Authentication Keys Are Active

Open
#533 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
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.csa_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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.