Security model¶
This page states what the library guarantees, what it leaves to the caller and how it treats hostile input. Report a suspected vulnerability as the security policy describes.
What a successful Verify means¶
When Verify returns nil, every signer of the message passed these checks:
- The signature is valid for the public key in the signer certificate, with a signature algorithm that suits the key type and the digest.
- With signed attributes, the message-digest attribute equals the digest of
p7.Contentand the content-type attribute equals the encapsulated content type. Each appears exactly once. The digests are compared in constant time. - A signing-time attribute, when present, falls within the signer certificate validity.
- When
VerifyOptions.Rootsis set,crypto/x509built a chain from the signer certificate to a root at the check time and the chain allows at least one of the extended key usages inKeyUsages.
What the caller decides¶
- Trust. Without
Rootsno chain is built. The caller must then decide whether the signer certificate is acceptable, for example by comparing it with a certificate it already holds. - Revocation.
Verifydoes not consult CRLs or OCSP. CRLs carried in a message are returned without any check. - Key usage.
Verifychecks extended key usages only throughKeyUsages. It does not check the key usage extension or certificate policies of the signer certificate. - Signing time. The signer chooses the signing-time value.
VerifyAtSigningTimeshould be used only when that time is trusted for another reason. - Recipients.
Encryptdoes not check the validity or the key usage of recipient certificates. - Input size. The library holds the whole message in memory and does not limit its size. Bound
the input before calling
Parse, for example withhttp.MaxBytesReaderfor an HTTP body.
Algorithms¶
The defaults avoid broken algorithms. Each relaxation is an option on the call that needs it.
| Algorithm | Default behavior |
|---|---|
| MD5 digest | Refused by Verify unless AllowMD5 is set. Signing with it requires SetDigestAlgorithm |
| SHA-1 digest | Accepted by Verify. Signing with it requires SetDigestAlgorithm |
| DES-CBC content | Refused by Decrypt and DecryptUsingPSK unless listed in DecryptOptions. Encryption with it requires EncryptOptions |
| DES-EDE3-CBC content | Accepted by Decrypt and DecryptUsingPSK |
| PKCS #1 v1.5 key transport | The default for Encrypt because SCEP peers expect it. Accepted by Decrypt with the countermeasure below |
Decryption failures¶
PKCS #1 v1.5 key transport is open to Bleichenbacher's padding oracle attack when a receiver
reveals whether the padding was valid. Decrypt applies the RFC 3218 countermeasure. It asks the
key for a random content key of the right length when the padding is bad and then continues as
it would with any wrong key. Every failure to decrypt the content key or the
content returns the same ErrDecryptionFailed. The content padding is removed in constant
time.
A crypto.Decrypter other than *rsa.PrivateKey receives *rsa.PKCS1v15DecryptOptions with
SessionKeyLen set and must implement the countermeasure itself to be safe.
Confidentiality without integrity¶
EnvelopedData and EncryptedData use CBC mode, which hides the content but does not authenticate it.
A changed ciphertext or a wrong key is usually reported as ErrDecryptionFailed but can also
decrypt to garbage. Sign the message when the receiver must detect changes. AuthEnvelopedData with
AES-GCM is not supported.
Hostile input¶
Parse accepts input from untrusted peers. The BER converter refuses nesting deeper than 64
levels, lengths longer than four octets or beyond the end of the input, indefinite lengths on
primitive elements, stray end-of-contents octets and trailing data.
Two fuzz targets cover this code. FuzzBer2Der checks that the converter never panics and that its
output converts to itself. FuzzParse checks that parsing, verification and decryption of
arbitrary input return errors instead of panicking. CI runs each for 30 seconds on every push and
pull request to dev and main.
Certificates¶
Embedded certificates are parsed by crypto/x509. One it refuses makes Parse fail.
ParseOptions.LegacyCertificateParser acts only when crypto/x509 refuses a certificate because
its authority key identifier is marked critical. It then parses every certificate of the message
again with a copy of the Go 1.23 parser, not only the refused one. That copy lives under
internal/legacy/x509 and gets no fixes from later Go releases, so enable the option only where
the Windows SCEP clients that need it must be served.
Randomness¶
Content keys, IVs, RSA encryption padding and signatures use crypto/rand.