4"""Gate: every insecure placeholder-crypto body shall be guarded fail-closed.
6Several secure-side translation units ship an INSECURE placeholder body that is
7only safe under an off-target build or an explicitly-declared insecure dev/eval
10 - libs/ra8_secure_app/src/secure_trng.c deterministic xorshift PRNG as a "TRNG"
11 - libs/ra8_secure_app/src/key_vault.c plain secure-SRAM key store (no HW vault)
12 - libs/ra8_hal/src/ra8_rsip_key_injection.c non-cryptographic xorshift key-wrap
13 - libs/ra8_hal/src/ra8_rsip_ecc.c fiction-opcode ECDSA / ECDH / Ed25519 asym
14 - libs/ra8_hal/src/ra8_rsip_cipher.c fiction-opcode AES / ChaCha / key-install
15 - libs/ra8_hal/src/ra8_rsip_rsa.c fiction-opcode RSA sign / verify / enc / dec
16 - libs/ra8_hal/src/ra8_rsip_asym.c fiction-opcode hash / HMAC / key vault / wrap / KDF
17 - libs/ra8_hal/src/ra8_rsip_devsec.c fiction-register lifecycle / debug / tamper / DPA
19Each such body MUST be wrapped in the guard::
21 #if defined(RA8_INSECURE_STUB_CRYPTO) || defined(RA8_OFF_TARGET)
22 <insecure placeholder body>
24 <fail-closed: every entry point returns a hard error, never k_ra8_ok>
27so a real production/HIL image that sets NEITHER flag compiles the fail-closed
28#else and cannot silently ship the stub. This gate is the compile-time-of-CI
29guarantee the audit asked for: it FAILS if, for any listed TU,
31 1. the guard `#if defined(RA8_INSECURE_STUB_CRYPTO) || defined(RA8_OFF_TARGET)`
32 is absent, or has no matching #else / #endif;
33 2. the #else branch is not fail-closed (no `#error` and no `k_ra8_err_` return);
34 3. the TU's insecure signature token escapes the guarded region (appears before
35 the guard, in the #else, or after the #endif) -- i.e. an insecure body that
36 is not actually behind the guard.
38There is no allowlist: either guard the insecure body fail-closed, or delete it
39in favour of a real backend.
43 check_stub_crypto_guarded.py
45Exit status: 0 if every stub TU is guarded fail-closed, 1 otherwise.
48from __future__
import annotations
53from pathlib
import Path
56REPO_ROOT = Path(__file__).resolve().parents[2]
62 "libs/ra8_secure_app/src/secure_trng.c":
"internal_xorshift64",
63 "libs/ra8_secure_app/src/key_vault.c":
"s_vault",
64 "libs/ra8_hal/src/ra8_rsip_key_injection.c":
"ki_compute_mac",
65 "libs/ra8_hal/src/ra8_rsip_ecc.c":
"k_ra8_rsip_asym_op_eddsa_sign",
66 "libs/ra8_hal/src/ra8_rsip_cipher.c":
"internal_sym_run",
67 "libs/ra8_hal/src/ra8_rsip_rsa.c":
"internal_rsa_dispatch",
68 "libs/ra8_hal/src/ra8_rsip_asym.c":
"internal_hash_pull_digest",
69 "libs/ra8_hal/src/ra8_rsip_devsec.c":
"k_ra8_rsip_off_life_state",
72_RE_IF = re.compile(
r"^\s*#\s*if(n?def)?\b")
73_RE_ELSE = re.compile(
r"^\s*#\s*else\b")
74_RE_ENDIF = re.compile(
r"^\s*#\s*endif\b")
75_RE_ERROR = re.compile(
r"^\s*#\s*error\b")
78def is_guard_open(line: str) -> bool:
79 """Whether ``line`` is the stub-crypto guard opener (either flag order)."""
81 re.match(
r"^\s*#\s*if\b", line)
82 and "RA8_INSECURE_STUB_CRYPTO" in line
83 and "RA8_OFF_TARGET" in line
87def find_guard_region(lines: list[str]) -> tuple[int, int, int] |
None:
88 """Locate the guard's (#if, #else, #endif) 0-based line indices.
90 Returns ``(if_idx, else_idx, endif_idx)`` or ``None`` when the guard opener
91 is absent or its matching #else / #endif cannot be resolved. Nesting of
92 inner #if/#endif is tracked so a nested conditional does not confuse the
95 if_idx = next((i
for i, ln
in enumerate(lines)
if is_guard_open(ln)),
None)
100 for i
in range(if_idx + 1, len(lines)):
104 elif _RE_ENDIF.match(ln):
107 return (if_idx, else_idx, i)
if else_idx
is not None else None
108 elif _RE_ELSE.match(ln)
and depth == 1:
113def check_file(rel: str, token: str, repo_root: Path = REPO_ROOT) -> list[str]:
114 """Return a list of violation strings for one stub TU (empty when clean)."""
115 path = repo_root / rel
116 if not path.is_file():
117 return [f
"{rel}: file not found (expected an insecure stub TU here)"]
118 lines = path.read_text(encoding=
"utf-8").splitlines()
120 region = find_guard_region(lines)
123 f
"{rel}: missing the stub-crypto guard "
124 f
"'#if defined(RA8_INSECURE_STUB_CRYPTO) || defined(RA8_OFF_TARGET)' "
125 f
"with a matching #else / #endif"
127 if_idx, else_idx, endif_idx = region
129 problems: list[str] = []
133 else_body = lines[else_idx + 1 : endif_idx]
134 fail_closed = any(_RE_ERROR.match(ln)
or "k_ra8_err_" in ln
for ln
in else_body)
137 f
"{rel}: the #else branch is not fail-closed "
138 f
"(needs a #error or a k_ra8_err_* hard return, not k_ra8_ok)"
143 hits = [i
for i, ln
in enumerate(lines)
if token
in ln]
144 inside = [i
for i
in hits
if if_idx < i < else_idx]
145 escaped = [i
for i
in hits
if not (if_idx < i < else_idx)]
148 f
"{rel}: insecure signature '{token}' not found inside the guarded "
149 f
"#if region (is the insecure body still present and guarded?)"
152 where =
", ".join(f
"line {i + 1}" for i
in escaped)
154 f
"{rel}: insecure signature '{token}' appears OUTSIDE the guard "
155 f
"({where}) -- the insecure body must be fully inside the "
156 f
"#if defined(RA8_INSECURE_STUB_CRYPTO) || defined(RA8_OFF_TARGET) block"
162def selftest() -> int:
163 """Prove a complete fail-closed guard passes and an escaped stub fires."""
164 rel =
"libs/fixture/stub.c"
165 signature =
"insecure_fixture_signature"
166 with tempfile.TemporaryDirectory(prefix=
"stub-crypto-selftest-")
as raw:
169 path.parent.mkdir(parents=
True)
171 "#if defined(RA8_INSECURE_STUB_CRYPTO) || defined(RA8_OFF_TARGET)\n"
172 f
"static int {signature};\n"
173 "#else\nreturn k_ra8_err_unsupported;\n#endif\n",
176 good_findings = check_file(rel, signature, root)
178 "#if defined(RA8_INSECURE_STUB_CRYPTO) || defined(RA8_OFF_TARGET)\n"
179 "static int placeholder;\n#else\nreturn k_ra8_ok;\n#endif\n"
180 f
"static int {signature};\n",
183 bad_findings = check_file(rel, signature, root)
184 minimum_bad_findings = 2
186 (
not good_findings,
"guarded token plus hard-error branch stays quiet"),
188 len(bad_findings) >= minimum_bad_findings
189 and any(
"not fail-closed" in item
for item
in bad_findings)
190 and any(
"OUTSIDE" in item
for item
in bad_findings),
191 "non-failing else and escaped insecure token both fire",
194 failed = [label
for passed, label
in cases
if not passed]
195 for passed, label
in cases:
196 print(f
" [{'ok' if passed else 'FAIL'}] {label}")
198 print(f
"check_stub_crypto_guarded.py --selftest: {len(failed)} failure(s)", file=sys.stderr)
200 print(
"check_stub_crypto_guarded.py --selftest: all cases pass (both directions).")
205 """Verify every placeholder-crypto TU is guarded fail-closed.
207 These TUs hold deliberately insecure stand-ins for real crypto. The guard
208 must make the ``#else`` half -- the branch taken when the real
209 implementation is absent -- refuse to build or fail closed, so a
210 misconfiguration cannot silently ship the placeholder as if it were the
213 Returns 1 listing each unguarded TU, 0 when all are fail-closed.
216 if args == [
"--selftest"]:
219 print(
"usage: check_stub_crypto_guarded.py [--selftest]", file=sys.stderr)
221 all_problems: list[str] = []
222 for rel, token
in STUB_TUS.items():
223 all_problems.extend(check_file(rel, token))
226 print(
"check_stub_crypto_guarded.py: insecure placeholder crypto not guarded fail-closed:")
227 for p
in all_problems:
229 print(
"Fix each at the root -- wrap the insecure body in")
230 print(
" #if defined(RA8_INSECURE_STUB_CRYPTO) || defined(RA8_OFF_TARGET)")
231 print(
"and make the #else fail closed (return k_ra8_err_* / #error), or replace")
232 print(
"the placeholder with a real crypto backend.")
235 count = len(STUB_TUS)
236 print(f
"check_stub_crypto_guarded.py: PASS -- {count} stub crypto TU(s) guarded fail-closed.")
240if __name__ ==
"__main__":
void main(void)
The application entry point Reset_Handler hands control to.