Skip to content

docs(1.7): clarify that an omitted nistQuantumSecurityLevel is not 0 - #1177

Open
Lennonhaha wants to merge 2 commits into
CycloneDX:masterfrom
Lennonhaha:docs/nistqsl-omission-vs-zero
Open

Lennonhaha wants to merge 2 commits into
CycloneDX:masterfrom
Lennonhaha:docs/nistqsl-omission-vs-zero

Conversation

@Lennonhaha

Copy link
Copy Markdown

Follow-up to #1175, as @Mehrn0ush suggested: a description-only clarification for 1.7.

The gap

The 1.7 description currently reads:

The NIST security strength category as defined in … . A value of 0 indicates that none of the categories are met.

It defines 0 but says nothing about the omitted case, which leaves two readings open:

  1. omission means 0, or
  2. omission means "not evaluated".

Neither is right, and the two are different mistakes. 0 is a positive assertion that no NIST category is met. An omitted property asserts nothing about categories and additionally says nothing about whether anyone assessed the asset. Consumers that fold absent into 0 therefore upgrade a non-claim into a claim — the failure mode that started this thread.

The change

Description text only, applied to the three 1.7 artifacts so they do not drift apart:

file change
schema/bom-1.7.schema.json property description
schema/bom-1.7.xsd xs:documentation
schema/bom-1.7.proto comment above the field

New wording:

The NIST security strength category as defined in … . This property is optional and has no default: omitting it is not an implicit 0, and does not establish whether an assessment was performed. A value of 0 indicates that none of the categories are met. Consumers should not interpret an omitted value as 0, nor treat it as evidence that the asset is quantum-safe.

No structure, type, bound or required list is touched.

Verification

  • edited schema compiles under ajv; valid-bom-1.7.json still validates
  • two invalid 1.7 fixtures (invalid-bomformat, invalid-component-type) are still rejected — included so the "it validates" claim is not vacuous
  • bom-1.7.xsd still parses as XML
  • no BOM, no CRLF, no U+FFFD in any of the three files

Scope notes

  • 1.6.2 is deliberately untouched. It is released and superseded by 1.7; changing a released artifact should be its own decision, not a side effect of this one.
  • 2.0-dev will need the same wording. I would rather land this wording first and mirror it than propose two competing versions; happy to open that follow-up once this is agreed.
  • Omission is still not a place to smuggle in an assessment state (notEvaluated etc.). If an independent, scale-agnostic way to express "assessed / not assessed" is wanted, that is a new field with its own discussion — not something this description change implies.

The 1.7 description only stated "A value of 0 indicates that none of the
categories are met." It said nothing about the omitted case, which leaves two
readings open: that omission means 0, or that omission means "not evaluated".

Both readings are wrong. 0 is an assertion that none of the categories are met;
an omitted property makes no category assertion at all, and says nothing about
whether an assessment was performed. Encoding "unknown" as 0 turns a non-claim
into a claim.

Description-only change, applied to the three 1.7 artifacts so they stay in
sync: bom-1.7.schema.json (description), bom-1.7.xsd (documentation) and
bom-1.7.proto (comment). No structure, type, bound or required list is touched.

Verified: the edited schema still compiles under ajv; valid-bom-1.7.json still
validates, and two invalid 1.7 fixtures are still rejected, so the check is not
vacuous; the XSD still parses as XML. No BOM, no CRLF, no U+FFFD introduced.

1.6.2 keeps its current wording here: it is released and superseded by 1.7, and
changing a released artifact is a separate decision. 2.0-dev needs the same
wording once this one is settled.

Signed-off-by: 刘天赫 <research@fibemate.net>
@Lennonhaha
Lennonhaha requested a review from a team as a code owner October 26, 2026 02:21

@Mehrn0ush Mehrn0ush left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @Lennonhaha for preparing this clarification. The JSON and XML changes reflect the distinction discussed in #1175: omission makes no category assertion and establishes no assessment status, while an explicit 0 means none of the referenced categories are met. The diff leaves validation constraints unchanged.

Please address the Protobuf wording noted inline so that its accessor default is not confused with an asserted category. The three formats should express the same CycloneDX semantics, with encoding-specific guidance where necessary.

This PR targets master and updates only the 1.7 artifacts. Please link a follow-up for the equivalent clarification in 2.0-dev, or record the agreed plan in #1175, so the 2.0 work remains explicitly tracked.

Maintainers can confirm the appropriate release handling and sequencing.

Comment thread schema/bom-1.7.proto Outdated
// The classical security level that a cryptographic algorithm provides (in bits).
optional int32 classicalSecurityLevel = 10;
// The NIST security strength category as defined in https://csrc.nist.gov/projects/post-quantum-cryptography/post-quantum-cryptography-standardization/evaluation-criteria/security-(evaluation-criteria). A value of 0 indicates that none of the categories are met.
// The NIST security strength category as defined in https://csrc.nist.gov/projects/post-quantum-cryptography/post-quantum-cryptography-standardization/evaluation-criteria/security-(evaluation-criteria). This property is optional and has no default: omitting it is not an implicit 0, and does not establish whether an assessment was performed. A value of 0 indicates that none of the categories are met. Consumers should not interpret an omitted value as 0, nor treat it as evidence that the asset is quantum-safe.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

“has no default” is misleading for this proto3 optional int32. An unset field’s numeric accessor returns 0, while explicit field presence distinguishes absence from a supplied zero. The intended CycloneDX semantics are correct; please describe that distinction here rather than copying the JSON Schema explanation unchanged. See Protobuf field-presence semantics (https://protobuf.dev/programming-guides/field_presence/#semantic-differences).

Suggested replacement:

Suggested change
// The NIST security strength category as defined in https://csrc.nist.gov/projects/post-quantum-cryptography/post-quantum-cryptography-standardization/evaluation-criteria/security-(evaluation-criteria). This property is optional and has no default: omitting it is not an implicit 0, and does not establish whether an assessment was performed. A value of 0 indicates that none of the categories are met. Consumers should not interpret an omitted value as 0, nor treat it as evidence that the asset is quantum-safe.
// The NIST security strength category as defined in https://csrc.nist.gov/projects/post-quantum-cryptography/post-quantum-cryptography-standardization/evaluation-criteria/security-(evaluation-criteria). This field is optional. Absence makes no NIST-category assertion and does not establish whether an assessment was performed. Consumers should use field presence to distinguish absence from an explicitly supplied zero; the numeric accessor value alone is insufficient. A value of 0 indicates that none of the categories are met. Consumers should not treat absence as evidence that the asset is quantum-safe.

@Mehrn0ush

Copy link
Copy Markdown
Contributor

Verified locally on f84e0f7 with protoc:

bom-1.7.proto compiles.
The compiled descriptor set is byte-identical to the pre-PR file, so the change is comment-only.
For AlgorithmProperties.nistQuantumSecurityLevel (Python protobuf): an unset field reads 0 with HasField false, and an explicit 0 reads 0 with HasField true. This is why the Protobuf comment needs the presence wording.

@Lennonhaha

Copy link
Copy Markdown
Author

Thanks for running protoc on it — the descriptor-set comparison is the check I could not do as convincingly from my side.

Two things your result settles:

  1. The change is comment-only, mechanically. A byte-identical descriptor set means no field numbering, type, optionality or option changed. That was the intent, and it is now verified rather than asserted.
  2. The presence wording is load-bearing, not decorative. Unset reading as 0 with HasField false, and explicit 0 reading as 0 with HasField true, is exactly the trap: any consumer that compares the number to 0 cannot distinguish "no assertion recorded" from "none of the categories met". Since the field is optional int32 nistQuantumSecurityLevel = 11, presence is observable in every binding — Python HasField, Java hasNistQuantumSecurityLevel(), Go pointer/*int32 — so the comment now says omitting is not an implicit 0 and does not establish whether an assessment was performed. The same wording is in bom-1.7.schema.json and bom-1.7.xsd, so the three artifacts agree.

One question before this goes further: would it help to add a sentence telling consumers to test presence rather than compare to 0 (naming HasField / has…() / Go pointer as examples)? It is a comment-only addition, so the descriptor set would stay identical. I left it out because the current wording already says what omission means, but if you think the actionable form belongs in the schema comment, I will add it.

@Mehrn0ush

Copy link
Copy Markdown
Contributor

Thanks—yes, please add the presence-check guidance and replace “has no default” in the Protobuf comment. An unset field’s numeric accessor returns 0, so consumers must check field presence to distinguish absence from an explicitly supplied zero. Please keep this language-neutral; examples from individual bindings aren’t necessary. That would address my remaining concern.

…stQuantumSecurityLevel

The Protobuf comment said the property "has no default", which is the wrong model
for `optional int32`: an unset field's numeric accessor returns 0, so the value
alone cannot be told apart from an explicitly supplied zero. That is exactly the
confusion this clarification exists to prevent.

Replace the phrase in all three 1.7 artifacts and state the consequence instead:
an omitted value makes no category assertion and does not establish whether an
assessment was performed; a value of 0 states that none of the categories are
met; consumers must check whether the field is present rather than comparing the
value to 0.

Language-neutral on purpose. No per-binding accessor names, so the wording does
not need revising when bindings change.

Description and documentation text only. No field number, type, optionality,
bound or required list is touched: the JSON structure is unchanged apart from
description strings, and the XSD still parses as XML.

Signed-off-by: 刘天赫 <research@fibemate.net>
@Lennonhaha
Lennonhaha force-pushed the docs/nistqsl-omission-vs-zero branch from 80f0713 to 40d5c0e Compare October 26, 2026 11:51
@Lennonhaha

Copy link
Copy Markdown
Author

Presence wording added, and "has no default" is gone from all three 1.7 artifacts.

This property is optional. Omitting it makes no NIST security category assertion
and does not establish whether an assessment was performed; a value of 0 states
that none of the categories are met. An unset integer field may read back as 0,
so the raw value cannot distinguish absence from an explicit zero: consumers must
check whether the field is present rather than comparing the value to 0. An
omitted value is not 0, and is not evidence that the asset is quantum-safe.

Kept language-neutral - no per-binding accessor names.

Scope, verified rather than asserted: description and documentation text only. The JSON structure is unchanged apart from description strings (compared with description keys stripped), and the XSD still parses as XML. No field number, type, optionality, bound or required list is touched.

One thing I did not do: I have no protoc here, so I am not claiming a descriptor-set comparison for this commit. The previous one is yours; this change is the same class of edit, and the diff contains comment and documentation lines only.

@Mehrn0ush

Copy link
Copy Markdown
Contributor

Thanks, the updated presence wording addresses my concern. I reran protoc: both versions compile and produce byte-identical descriptor sets.

LGTM!

@Lennonhaha

Copy link
Copy Markdown
Author

Thanks for the thorough review and for re-running protoc locally - good to have the descriptor sets confirmed byte-identical.

One detail: the review is still recorded as "changes requested", which keeps the PR blocked. If the current wording works for you, flipping that state would let a maintainer merge. I do not have write access to this repository, so the merge itself is not mine to make.

@Mehrn0ush Mehrn0ush left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants