CMS_VERIFY(3)

πŸ“¦ version 4.4.0, in alpine 3.24, arch rolling

πŸ’– CMS_VERIFY(3) β€” verifying CMS SignedData, bestie edition πŸ’–

okay so this is a libcrypto page (that's the OpenSSL/BoringSSL-ish crypto library, iykyk) and it's literally about checking whether a CMS SignedData structure is legit. no cap, it's giving "trust but verify" main character energy.


✨ SYNOPSIS ✨

#include <openssl/cms.h>

int
CMS_verify(CMS_ContentInfo *cms, STACK_OF(X509) *certs,
    X509_STORE *store, BIO *indata, BIO *out, unsigned int flags);

STACK_OF(X509) *
CMS_get0_signers(CMS_ContentInfo *cms);

πŸ¦‹ DESCRIPTION πŸ¦‹

So CMS_verify() does the actual vibe check on the CMS SignedData structure cms. Slay.

  • certs is a set of certificates where we go looking for the signing certificate(s) πŸ”
  • store is the trusted certificate store used for chain verification (the trusted inner circle πŸ‘‘)
  • indata is the detached content, but only if the content isn't already inside cms
  • the content gets written to out, but only if out is not NULL

And CMS_get0_signers()? She retrieves the signing certificate(s) from cms. Important: she may only be called after a successful CMS_verify() operation, no exceptions fr fr. The signers must be freed with sk_X509_free(). periodt.


🌸 so how does the verify actually go down 🌸

Normally the verify process proceeds like this, bestie:

  1. First up, some sanity checks are performed on cms. There must be at least one signature on the data. And if the content is detached, indata cannot be NULL β€” like, at all.
  2. An attempt is made to locate all the signing certificate(s), first looking in the certs parameter (if it's not NULL) and then looking in any certificates contained in the cms structure itself. If any signing certificate cannot be located, the operation fails 😭.
  3. Each signing certificate gets chain verified using the smimesign purpose and the supplied trusted certificate store. Any internal certificates in the message are used as untrusted CAs. If CRL checking is enabled in store, any internal CRLs are used in addition to attempting to look them up in store. If any chain verify fails, an error code is returned πŸ’€.
  4. Finally the signed content is read (and written to out if it's not NULL) and the signature is checked. iconic.

If all signatures verify correctly, then the function is successful. πŸŽ‰


πŸ’… the flags (OR'ed together, obviously) πŸ’…

Any of the following flags can be passed to change the default verify behaviour:

  • CMS_NOINTERN β€” do not use the certificates in the message itself when locating the signing certificate(s). This means all the signing certificates must be in the certs parameter.
  • CMS_NOCRL β€” if CRL checking is enabled in store, then any CRLs in the message itself are ignored.
  • CMS_TEXT β€” MIME headers for type text/plain are deleted from the content. If the content is not of type text/plain, an error is returned.
  • CMS_NO_SIGNER_CERT_VERIFY β€” do not verify signing certificates.
  • CMS_NO_ATTR_VERIFY β€” do not check the signed attributes signature.
  • CMS_NO_CONTENT_VERIFY β€” do not check the content digest.

🧚 one very cute use case for CMS_NOINTERN 🧚

One application of CMS_NOINTERN is to only accept messages signed by a small number of certificates. The acceptable certificates would be passed in the certs parameter. In this case, if the signer is not one of the certificates supplied in certs, then the verify will fail because the signer cannot be found. It's giving strict guest list energy πŸšͺ✨.


πŸŽ€ customised verification, for the extra ones πŸŽ€

In some cases the standard techniques for looking up and validating certificates are not appropriate: for example an application may wish to lookup certificates in a database or perform customised verification. This can be achieved by setting and verifying the signers certificates manually using the signed data utility functions. you got this 🫢

⚠️ Care should be taken when modifying the default verify behaviour! For example, setting CMS_NO_CONTENT_VERIFY will totally disable all content verification and any modified content will be considered valid. That combination is however useful if one merely wishes to write the content to out and its validity is not considered important. but like, be careful bestie 😭
🌈 Chain verification should arguably be performed using the signing time rather than the current time. However since the signing time is supplied by the signer it cannot be trusted without additional evidence (such as a trusted timestamp). big trust issues energy, ngl.

⭐ RETURN VALUES ⭐

  • CMS_verify() returns 1 for a successful verification or 0 if an error occurred.
  • CMS_get0_signers() returns all signers or NULL if an error occurred. The signers must be freed with sk_X509_free().
  • The error can be obtained from ERR_get_error(3).

πŸ‘‘ SEE ALSO πŸ‘‘

CMS_ContentInfo_new(3), CMS_get0_SignerInfos(3), CMS_sign(3), CMS_verify_receipt(3)


🌈 STANDARDS 🌈

  • RFC 5652: Cryptographic Message Syntax (CMS), section 5.1: SignedData Type
  • RFC 8419: Use of Edwards-Curve Digital Signature Algorithm (EdDSA) Signatures in the Cryptographic Message Syntax (CMS)

πŸ’œ HISTORY πŸ’œ

These functions first appeared in OpenSSL 0.9.8h and have been available since OpenBSD 6.7. vintage πŸ’…


πŸ’€ BUGS πŸ’€

  • The trusted certificate store is not searched for the signing certificate. This is primarily due to the inadequacies of the current X509_STORE functionality. awkward πŸ™ˆ
  • The lack of single pass processing means that the signed content must all be held in memory if it is not detached. memory go brrr πŸ“

okay that's the whole page, bestie β€” mwah! πŸŽ€ this one's from the Debian edition, dated June 8, 2025, bless them for keeping the docs cute. go verify something and slay the day βœ¨πŸ¦‹

πŸ“¦ where this page comes from