# PKCS#7 CMS - Message digest calculation process

i'm studying RFC 5652 in order to know exactly how to encode/decode PKCS#7 ASN.1 data.

I don't understand how one can create a signature when the field "signedAttrs" is present:

The result of the message digest calculation process depends on whether the signedAttrs field is present. When the field is absent, the result is just the message digest of the content as described above. When the field is present, however, the result is the message digest of the complete DER encoding of the SignedAttrs value contained in the signedAttrs field. Since the SignedAttrs value, when present, must contain the content-type and the message-digest attributes, those values are indirectly included in the result.

By reading the above text I get confused: SignedAttrs field contains message-digest and content-type values but message-digest can be present once calculated and the digest has to be calculated on:

eContent OCTET STRING + full DER encoding of SignedAttrs field (Containing message-digest field).

In the example below there is a PKCS#7 Signed data structure where the envelopedData content field value is being signed + the signed attributes. Where does exactly the messageDigest value come from?

The signature schemes envisioned by PKCS7/CMS use two stages, where the data to be signed is first hashed and that hash is used in signature generation and verification (RSA, EG, DSA, and ECDSA). For your example -- RSASSA-PKCS1-v1_5 with SHA1 -- some data is hashed with SHA1, and that hash is encoded, padded and modexp'ed, which was described in older versions of PKCS1, and PKCS7, as 'encrypting with the private key', although that description was found to be harmful and is no longer used. (See my https://security.stackexchange.com/questions/159282/#159289 for some of the Qs/As here and on security.SX on this topic, and there are more you can find by searching.)

PKCS7 originally simply applied this process to the data (what is now eContent). It and its successor CMS were enhanced to allow either that basic process or a two-stage process:

• the hash of (the value of) eContent (your first red bracket) is computed, and that value is placed in the messageDigest attribute in signedAttrs. In addition eContentType is copied to the contentType attribute in signedAttrs. Other attributes may be added, and some standards that build on CMS define additional requirements on the attributes in signedAttrs.

• (an encoding of) the signedAttrs structure is signed. This is almost your second red bracket, except with the tag modified as described below.

The specification for these two options is intermixed in the document. First note that the previous section (5.3) defines signedAttrs:

      signedAttrs is a collection of attributes that are signed.  The
field is optional, but it MUST be present if the content type of
the EncapsulatedContentInfo value being signed is not id-data.
SignedAttributes MUST be DER encoded, even if the rest of the
structure is BER encoded.  Useful attribute types, such as signing
time, are defined in Section 11.  If the field is present, it MUST
contain, at a minimum, the following two attributes:

A content-type attribute having as its value the content type
of the EncapsulatedContentInfo value being signed.  Section
11.1 defines the content-type attribute.  However, the
content-type attribute MUST NOT be used as part of a
countersignature unsigned attribute as defined in Section 11.4.

A message-digest attribute, having as its value the message
digest of the content.  Section 11.2 defines the message-digest
attribute.


The part you quote (5.4) then says, more fully:

   [With or without signedAttrs] the initial input to the message
digest calculation process is the "value" of the encapsulated content
being signed.  Specifically, the initial input is the
encapContentInfo eContent OCTET STRING to which the signing process
is applied.  Only the octets comprising the value of the eContent
OCTET STRING are input to the message digest algorithm, not the tag
or the length octets.

The result of the message digest calculation process depends on
whether the signedAttrs field is present.  When the field is absent,
the result is just the message digest of the content as described
above.  When the field is present, however, the result is the message
digest of the complete DER encoding of the SignedAttrs value
contained in the signedAttrs field.  Since the SignedAttrs value,
when present, must contain the content-type and the message-digest
attributes, those values are indirectly included in the result.  The
content-type attribute MUST NOT be included in a countersignature
unsigned attribute as defined in Section 11.4.  A separate encoding
of the signedAttrs field is performed for message digest calculation.
The IMPLICIT [0] tag in the signedAttrs is not used for the DER
encoding, rather an EXPLICIT SET OF tag is used.  That is, the DER
encoding of the EXPLICIT SET OF tag, rather than of the IMPLICIT [0]
tag, MUST be included in the message digest calculation along with
the length and content octets of the SignedAttributes value.


Breaking that down: without signedAttrs the hash of eContent is used for the signature in section 5.5. With signedAttrs, the hash of signedAttrs -- only signedAttrs, with the tag of the encoding modified as described -- is used for the signature in 5.5. But since the content of signedAttrs includes the hash of eContent, and the value of eContentType, the signature effectively protects those as well: if you change eContent without changing signedAttrs then the verifier will detect that hash(eContent) doesn't match signedAttrs.messageDigest, and if you change eContent and signedAttrs.messageDigest then the verifier will detect that the signature isn't valid for the modified signedAttrs.

   When the signedAttrs field is absent, only the octets comprising the
value of the SignedData encapContentInfo eContent OCTET STRING (e.g.,
the contents of a file) are input to the message digest calculation.
This has the advantage that the length of the content being signed
need not be known in advance of the signature generation process.

Although the encapContentInfo eContent OCTET STRING tag and length
octets are not included in the message digest calculation, they are
protected by other means.  The length octets are protected by the
nature of the message digest algorithm since it is computationally
infeasible to find any two distinct message contents of any length
that have the same message digest.


This repeats, more verbosely, that without signedAttrs the hash of eContent is used for the signature in 5.5.

5.5 then completes the sequence:

   The input to the signature generation process includes the result of
the message digest calculation process and the signer's private key.
The details of the signature generation depend on the signature
algorithm employed. [...]


"The result of the message digest calculation process" used for signature generation (and verification) -- as defined in 5.4 -- is EITHER: (without signedAttrs) the hash of the value of eContent OR: (with signedAttrs) the hash of the encoding of signedAttrs.