One control, taken apart
“Are we FIPS compliant?” is not one question — it is five, and four of them a scan answers while the fifth is a document you maintain. The worked case that shows why one encryption control rates full and its neighbour rates partial.
Someone will ask your team, in these words: “what do we scan to prove FIPS and encryption?” The useful answer starts by refusing the question’s shape. That is not one claim. It is five, and four of them a scanner settles while the fifth is a document you maintain.
Conversations about this go wrong in a predictable way. A team buys a tool, gets a green dashboard, and writes “FIPS-validated cryptography” into a package on the strength of a check that read a hostname. The scan was real. The claim it was made to support was not the claim it proved.
“Are we FIPS compliant?”
Encrypted at rest
Every store holding federal data has encryption on, backed by a key you can name.
measured · answered by Cloud config scan
Encrypted in transit
Every listener that terminates a session does so under a named TLS policy, with a certificate behind it.
measured · answered by Cloud config scan
Keys are managed
Keys rotate, no key protecting live data is pending deletion, and the material was generated where you think it was.
measured · answered by Cloud config scan
No broken algorithms
First-party source does not reach for a hash or cipher the standard retired.
measured · answered by Code scan
The module is validated
The cryptography is performed by a module holding a current CMVP certificate — a number, a standard, and a status that has not gone historical.
cited · answered by No scan — a register
Four of those ribs end in an output. The fifth ends in a certificate number — issued by a laboratory, recorded by NIST, and true of a module rather than of your account. No API returns it, because no API knows which module your process loaded. It is looked up, written down, given a review date, and re-read.
Now watch what that does to two recipes from this site’s own corpus. Same plane, same service, neighbouring subject matter, the same rating — and a different reason for it:
AWS Config compliance results across the storage services proving customer data is encrypted at rest — S3 buckets with default server-side encryption, EBS volumes encrypted, and RDS storage encrypted, all backed by KMS
fetch
$ aws configservice get-compliance-details-by-config-rule --config-rule-name encrypted-volumes --compliance-types NON_COMPLIANT$ aws configservice get-compliance-details-by-config-rule --config-rule-name rds-storage-encrypted --compliance-types NON_COMPLIANT$ aws configservice describe-configuration-recorder-status$ aws configservice describe-config-rule-evaluation-status --config-rule-names encrypted-volumes$ aws configservice describe-config-rule-evaluation-status --config-rule-names rds-storage-encrypted$ aws ec2 describe-regions --query 'Regions[].RegionName' --output text$ aws organizations list-accounts --query 'Accounts[].Id' --output textmaps to: sc-28 · cadence: continuous · full recipe: /collect/config-encryption-at-rest
Encryption is either on or it is not, and the rule says which — the check is written as an assertion over the rule's result. It rates partial anyway: the rules see attached volumes and RDS instances, and the list of stores in scope is a document. It was full until the audit of 26 August 2026 found exactly that.
AWS Config compliance results proving KMS customer-managed keys are lifecycle-managed — automatic annual rotation enabled and no active key scheduled for deletion — the key-hygiene half of the cryptographic-protection control
fetch
$ aws configservice get-compliance-details-by-config-rule --config-rule-name cmk-backing-key-rotation-enabled --compliance-types NON_COMPLIANT$ aws configservice get-compliance-details-by-config-rule --config-rule-name kms-cmk-not-scheduled-for-deletion --compliance-types NON_COMPLIANTmaps to: sc-13 · cadence: continuous · full recipe: /collect/config-kms-key-management
Rotation and pending-deletion are fields; the control's own demand — that the cryptographic module be validated — is not. The recipe says so in its notes rather than quietly averaging the two halves into a green tile. Same badge, different distance: one recipe has a written check with a named gap, the other has no check to write.
That is the mechanism behind partial, opened up: a control with several limbs is rated by its weakest one, and the rating is a verdict about the output rather than about the effort. A recipe that scored itself on the limbs it happens to reach would read green forever.
The same split, stated as the table to keep beside you — with the column most tool comparisons leave out:
| Question asked | Answered by | Proves | Silent about |
|---|---|---|---|
| Is our data encrypted at rest? | Cloud config scanAWS Config managed rules, the service CLIs | the state of every store the rule evaluated, continuously | whether the module doing the encrypting is validated |
| Is our data encrypted in transit? | Cloud config scanAWS Config managed rules, the service CLIs | the policy each listener is configured with | what the endpoint actually negotiates when a client connects |
| Will the next deploy be encrypted? | Code scanCodeQL, Semgrep, gosec, Bandit | intent, before the merge — the template declares encryption | everything already deployed, and everything since drifted |
| Does our code use broken cryptography? | Code scanCodeQL, Semgrep, gosec, Bandit | algorithm choice in first-party source | whether a strong algorithm ran inside a validated module |
| What does the endpoint really serve? | Network scantestssl.sh, SSLyze, nmap ssl-enum-ciphers | the protocol versions and ciphers a client can actually negotiate | internal service-to-service paths nothing points a scanner at |
| Is the module CMVP-validated? | No scan — a registera maintained document, re-read on a cadence | nothing — no scan of any kind emits this | the point, if you let a tool answer it for you |
Eight questions follow. Sort each into the thing that answers it. Two of them belong in the bucket that is not a scanner, and one of those two is designed to feel like a cloud question.
The register this step keeps pointing at is the deliverable nobody sells you, because nobody else knows your architecture: for every cryptographic module in the boundary, its version, certificate number, standard, status, sunset date, and where it is used. Some runtimes will at least tell you whether they are running in FIPS mode — Go exposes it to the program itself — which converts one half of one limb from a document into a value worth logging.
Every recipe named here is at /collect, and the controls behind them are on /frontier with the same judgement applied one at a time. The last step generalises what you just did to the whole register.
Exit check — a customer asks “what do we scan for FIPS?” What do you say first?
Split the question before answering it. Encryption at rest, in transit, and key management are cloud-configuration facts collected continuously; algorithm choice is a code scan; what an endpoint actually negotiates needs something that speaks TLS; and whether the module is CMVP-validated is a register you maintain, not a scan you buy. If you can also say why that last limb makes an otherwise well-instrumented control rate partial — and refuse to average it away — the step held.