Why purpose is hard¶
Everything in the risk model depends on one question — what is this key for? — and real CBOMs frequently do not answer it.
What real generator output looks like¶
From CBOMkit's published Keycloak CBOM: 56 components, 22 of asset type
algorithm.
| observation | count | consequence |
|---|---|---|
cryptoFunctions absent entirely |
6 / 22 | it cannot be the only signal |
cryptoFunctions: [keygen] and nothing else |
12 occurrences | keygen is purpose-neutral — generating a key says nothing about what the key does |
primitive: other |
4 | includes AES and HMACSHA2 |
primitive: pke on EC keys |
5 | EC-secp256r1/384r1/521r1, which in Keycloak are ECDSA and ECDH |
Two consequences invalidate the obvious implementation.
Trusting primitive produces confident wrong answers. Mapping
pke → encryption reports five Keycloak EC keys as harvest-now-decrypt-later
exposures that they are not.
cryptoFunctions is mostly keygen, the most common value in the sample
and the least informative one.
None of this is a defect in CBOMkit. Static analysis of a call site genuinely cannot always tell you what a key is for. The right response is to say so.
The resolution ladder¶
The first signal that decides wins; the others are kept and reported.
cryptoFunctions, purpose-bearing values only.encrypt/decrypt,sign/verify,encapsulate/decapsulate,digest,tag,keyderive.keygen,otherandunknownare neutral — they neither decide nor block a lower signal from deciding.primitive. Clean forkey-agree,kem,signature,hash,mac,kdf,drbgand the cipher families.pkeresolves toambiguous, notencryption.oid, for identity always — but for purpose only when the OID names a use rather than an algorithm.name, last resort, mainly to recover parameters baked into strings likeAES128-CBC-PKCS5orEC-secp521r1.
The OID trap¶
| OID | resolves purpose? | |
|---|---|---|
1.3.132.1.12 |
ECDH | ✅ key agreement |
1.2.840.113549.1.1.10 |
RSASSA-PSS | ✅ signature |
1.2.840.10045.2.1 |
id-ecPublicKey | ❌ identity only — EC keys sign and agree |
1.2.840.113549.1.1.1 |
rsaEncryption | ❌ identity only |
rsaEncryption is the trap. Despite the name, it tags every RSA key in
existence, signing keys included. Reading purpose out of it is exactly the guess
this tool refuses to make.
The plausibility guard¶
A higher-precedence signal can still be wrong in a way the ladder alone cannot
catch. CBOMkit tags AES with cryptoFunctions: ["decapsulate"]. Taken at face
value that resolves a block cipher to key agreement — confidently, with
harvest-now-decrypt-later weighting attached.
A symmetric cipher cannot perform key agreement. So when a signal claims
something an algorithm family cannot do, the result is ambiguous with the
disagreement recorded, not a wrong answer delivered with certainty. Families
that genuinely are dual-use — RSA, bare EC — are unconstrained.
Deliberately excluded¶
CBOMkit records the originating API call in
evidence.occurrences[].additionalContext — Keycloak's ECDH entry carries
javax.crypto.KeyAgreement#getInstance(...), which is a better purpose signal
than anything in cryptoProperties. It is still inference from a call site, and
a normalizer that silently promotes a guess to a verdict is the thing this tool
exists to replace. It is captured as corroboration, shown in reports, and never
allowed to decide.
Three ways to not answer¶
| trigger | exit | |
|---|---|---|
unscored |
purpose is unknown or ambiguous |
3 under --strict |
indeterminate |
a rule needs a fact you did not declare | 3 under --strict |
not-applicable |
the pack demonstrably does not bind you | never |
Defaulting unknown to low hides exposure behind a green build. Defaulting to high trains people to ignore the output. The honest answer sends the user where the answer actually lives:
RSA-2048 ambiguous (primitive: pke)
as key transport → critical · as signature → medium
Declare the purpose in cbomctl.yaml, or regenerate the CBOM with a
generator that records cryptoFunctions.
The gap between those two readings is the entire severity scale. That is why guessing would be indefensible, and why more than a third of a real Keycloak CBOM lands in this section rather than in a verdict.