*and by "ghetto", I mean "extremely applied". Basically, it's just interpretive labor on RFC 5652: Cryptographic Message Syntax and RFC 5280: X.509.

P.S. CMS is way more technically elegant than PGP. I dare you to read the RFC and compare.


Firstly: the "root object" that you'll be shitting nakedly into a binary (or ASCII-armored) file that sits in the filesystem or gets otherwise transferred is a ContentInfo data stream. This is an ASN.1 SEQUENCE with 2 elements:

  1. OBJECT IDENTIFIER giving "context" and defining what the following element should contain
  2. [0] EXPLICIT OCTET STRING OPTIONAL, containing whatever the OID in the first element says to

Before we go on, just take note that AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER, parameters ANY DEFINED BY algorithm OPTIONAL }; I didn't want to define terms that aren't directly in a critical chain of logic, but this one is used all the frickin time here so I figured I'd define it for convenience.

We'll generally want the EnvelopedData content type (1.2.840.113549.1.7.3) at the first level; it is a SEQUENCE with 3 to 5 elements:

  1. version (an integer)
  2. [0] IMPLICIT OriginatorInfo OPTIONAL
  3. SET OF CHOICE { KeyTransRecipentInfo, [1] KeyAgreeRecipientInfo, [2] KEKRecipientInfo, [3] PasswordRecipientInfo, [4] OtherRecipientInfo }
  4. EncryptedContentInfo
  5. [1] IMPLICIT UnprotectedAttributes SET OF AlgorithmIdentifier OPTIONAL
    • A kinda-comprehensive list of choices can be found here
      • Abandon all hope, ye who enter here
    • You probably should include at least id-aa-contentHint (1.2.840.113549.1.9.16.2.4) to specify the contentType of your plaintext data
    • if this field is present, then the version field must be 2

Currently, we'll focus on PasswordRecipientInfo, which was defined in RFC 3211 as a SEQUENCE with 3 to 4 elements:

  1. version (always equal to 0)
  2. keyDerivationAlgorithm [0] AlgorithmIdentifier OPTIONAL
    • PBKDF2 (1.2.840.113549.1.5.12) is a good choice here
    • PBKDF2-params ::= SEQUENCE { salt CHOICE { specified OCTET STRING, otherSource AlgorithmIdentifier }, iterationCount INTEGER, keyLength INTEGER OPTIONAL, prf AlgorithmIdentifier DEFAULT hMAC-SHA1 }
  3. keyEncryptionAlgorithm AlgorithmIdentifier
  4. encryptedKey OCTET STRING

EncryptedContentInfo is a SEQUENCE with 2 to 3 elements:

  1. contentType OBJECT IDENTIFIER
  2. contentEncryptionAlgorithm AlgorithmIdentifier
    • Nonces generally go here, but it's entirely up to whoever defined the selected algorithm
  3. encryptedContent [0] IMPLICIT OPTIONAL OCTET STRING DEFINED BY contentEncryptionAlgorithm
    • encryptedContent is the result of encrypting the content. The field is optional, [but] if [it] is not present, its intended value must be supplied by other means.”

TODO: SignedData, which is a complicated composite doodad that supports most other use-cases as a subset

Leave a Reply

Your email address will not be published. Required fields are marked *

Warning: This site uses Akismet to filter spam. Until or unless I can find a suitable replacement anti-spam solution, this means that (per their indemnification document) all commenters' IP addresses will be sent to Automattic, Inc., who may choose to share such with 3rd parties.
If this is unacceptable to you, I highly recommend using an anonymous proxy or public Wi-Fi connection when commenting.