*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. The OID 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 *