Summary
sa_get_from_spi() in src/sa/internal/sa_interface_inmemory.template.c enforces the cross-field invariant arsn_len >= shsnf_len (line 777, returning CRYPTO_LIB_ERR_ARSN_LT_SHSNF) but does not enforce the parallel invariant iv_len >= shivf_len. A Security Association loaded with shivf_len > iv_len (e.g. via a hand-edited inmemory config or an mariadb row crafted by an operator with SA-table write access) causes every per-frame IV-walk loop to start at a negative index and read up to shivf_len bytes from before sa_ptr->iv inside the SecurityAssociation_t struct — leaking pre-iv struct fields into every outgoing TC/AOS frame.
Locations
The vulnerable loop shape is:
for (i = sa_ptr->iv_len - sa_ptr->shivf_len; i < sa_ptr->iv_len; i++)
{
*(p_new_enc_frame + index_temp) = *(sa_ptr->iv + i);
index_temp++;
}
Hits in production code:
src/core/crypto_aos.c:318 (IV print path, debug)
src/core/crypto_aos.c:351 (Crypto_AOS_ApplySecurity transmitted-IV write)
src/core/crypto_aos.c:697 (Crypto_AOS_ProcessSecurity IV reconstruction)
src/core/crypto_tc.c:331 (Crypto_TC_ApplySecurity transmitted-IV write)
src/core/crypto_tc.c:631 (Crypto_TC_ProcessSecurity IV reconstruction)
The sibling shsnf_len/arsn_len pattern appears at crypto_aos.c:365, 710 and crypto_tc.c:644, 1233 — same shape, same risk. The line-777 check in sa_get_from_spi already guards those; the matching check for IV is missing.
Threat model
Loading a malformed SA requires write access to the SA backend (inmemory config file, mariadb security_associations table, custom backend). On a deployed system that means an operator-tier or upstream-tooling-tier compromise — not a remote attacker. The leak is small (≤ shivf_len bytes per frame, typically 12) but it is steady (every encrypted/authenticated frame the SA emits leaks a few bytes of adjacent SA struct memory into the channel).
sa_verify_data() (line 1822 same file) checks each field against the compile-time max (shivf_len > IV_SIZE, shsnf_len > ARSN_SIZE, etc.) but not against the per-SA iv_len / arsn_len. The two layers of check are complementary.
Fix
PR adds the parallel guard immediately after the existing line-777 arsn_len/shsnf_len check, plus the new CRYPTO_LIB_ERR_SHIVF_LEN_GREATER_THAN_IV_LEN (-86) return code in include/crypto_error.h and its name string in src/core/crypto_error.c.
Notes
The Mariadb backend (src/sa/mariadb/sa_interface_mariadb.template.c) doesn't currently check either invariant on insert — a follow-up could add the same two-line guard there too. I left it out of this PR to keep scope tight.
Summary
sa_get_from_spi()insrc/sa/internal/sa_interface_inmemory.template.cenforces the cross-field invariantarsn_len >= shsnf_len(line 777, returningCRYPTO_LIB_ERR_ARSN_LT_SHSNF) but does not enforce the parallel invariantiv_len >= shivf_len. A Security Association loaded withshivf_len > iv_len(e.g. via a hand-edited inmemory config or an mariadb row crafted by an operator with SA-table write access) causes every per-frame IV-walk loop to start at a negative index and read up toshivf_lenbytes from beforesa_ptr->ivinside theSecurityAssociation_tstruct — leaking pre-iv struct fields into every outgoing TC/AOS frame.Locations
The vulnerable loop shape is:
Hits in production code:
src/core/crypto_aos.c:318(IV print path, debug)src/core/crypto_aos.c:351(Crypto_AOS_ApplySecuritytransmitted-IV write)src/core/crypto_aos.c:697(Crypto_AOS_ProcessSecurityIV reconstruction)src/core/crypto_tc.c:331(Crypto_TC_ApplySecuritytransmitted-IV write)src/core/crypto_tc.c:631(Crypto_TC_ProcessSecurityIV reconstruction)The sibling
shsnf_len/arsn_lenpattern appears atcrypto_aos.c:365, 710andcrypto_tc.c:644, 1233— same shape, same risk. The line-777 check insa_get_from_spialready guards those; the matching check for IV is missing.Threat model
Loading a malformed SA requires write access to the SA backend (inmemory config file, mariadb
security_associationstable, custom backend). On a deployed system that means an operator-tier or upstream-tooling-tier compromise — not a remote attacker. The leak is small (≤shivf_lenbytes per frame, typically 12) but it is steady (every encrypted/authenticated frame the SA emits leaks a few bytes of adjacent SA struct memory into the channel).sa_verify_data()(line 1822 same file) checks each field against the compile-time max (shivf_len > IV_SIZE,shsnf_len > ARSN_SIZE, etc.) but not against the per-SAiv_len/arsn_len. The two layers of check are complementary.Fix
PR adds the parallel guard immediately after the existing line-777
arsn_len/shsnf_lencheck, plus the newCRYPTO_LIB_ERR_SHIVF_LEN_GREATER_THAN_IV_LEN(-86) return code ininclude/crypto_error.hand its name string insrc/core/crypto_error.c.Notes
The Mariadb backend (
src/sa/mariadb/sa_interface_mariadb.template.c) doesn't currently check either invariant on insert — a follow-up could add the same two-line guard there too. I left it out of this PR to keep scope tight.