RFC 9709

Encryption Key Derivation in the Cryptographic Message Syntax (CMS) Using HKDF with SHA-256

๐Ÿ“… published January 2025, from ietf

RFC 9709: Encryption Key Derivation in the Cryptographic Message Syntax (CMS) Using HKDF with SHA-256 ๐ŸŽ€

Okay bestie, let's get into this one. It's from the IETF (the Internet Engineering Task Force, the folks who write the rules for how the internet talks ๐ŸŒ). R. Housley from Vigil Security wrote it, and it was published in January 2025 as ISSN 2070-1721. It's a Standards Track document, so it's an actual official spec and not just an opinion piece ๐Ÿ’…

  • Request for Comments: 9709
  • Author: R. Housley, Vigil Security
  • Category: Standards Track
  • Date: January 2025
  • ISSN: 2070-1721
  • Obsoletes / Updates: the title block doesn't list any RFCs that this one obsoletes or updates

Abstract ๐Ÿ’œ

So the big idea is how to derive the content-encryption key, or the content-authenticated-encryption key, in CMS using HKDF with SHA-256 ๐Ÿ”. The point is protection against an attacker who messes with the content-encryption algorithm identifier, or the content-authenticated-encryption algorithm identifier. Lowkey the whole thing is making sure nobody can swap out the algorithm label without the keys going sideways.

Status of This Memo ๐Ÿ“œ

This is an Internet Standards Track document ๐Ÿฆ‹

This document comes from the IETF, and it represents the consensus of the IETF community. It went through public review and was approved for publication by the Internet Engineering Steering Group (IESG). If you want more on Internet Standards, check out Section 2 of RFC 7841.

For the current status, any errata, and how to give feedback, go to https://www.rfc-editor.org/info/rfc9709.

Copyright (c) 2025 IETF Trust and the persons identified as the document authors. All rights reserved.

This document is covered by BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info), as they stood on the publication date. Please actually read these, because they spell out your rights and restrictions for this document ๐Ÿ™ˆ If you pull Code Components out of this document, they must include the Revised BSD License text described in Section 4.e of the Trust Legal Provisions. They also come without warranty, as the Revised BSD License describes.

Table of Contents ๐Ÿ—‚๏ธ

1.  Introduction
  1.1.  ASN.1
  1.2.  Terminology
  1.3.  Cryptographic Algorithm Agility Considerations
2.  Use of HKDF with SHA-256 to Derive Encryption Keys
3.  The id-alg-cek-hkdf-sha256 Algorithm Identifier
4.  SMIMECapabilities Attribute Conventions
5.  Use of HKDF with SHA-256 with CMS
  5.1.  Enveloped-Data Content Type
  5.2.  Encrypted-Data Content Type
  5.3.  Authenticated-Enveloped-Data Content Type
6.  Security Considerations
7.  Privacy Considerations
8.  Operational Considerations
9.  IANA Considerations
10. References
  10.1.  Normative References
  10.2.  Informative References
Appendix A.  ASN.1 Module
Appendix B.  CMS_CEK_HKDF_SHA256 Function Examples
  B.1.  CMS_CEK_HKDF_SHA256 with AES-128-GCM
  B.2.  CMS_CEK_HKDF_SHA256 with AES-128-CBC
Acknowledgements
Author's Address

1. Introduction โœจ

This doc is all about deriving the content-encryption key for the CMS enveloped-data content type [RFC5652], the content-encryption key for the CMS encrypted-data content type [RFC5652], or the content-authenticated-encryption key for the authenticated-enveloped-data content type [RFC5083] ๐Ÿ’–

The whole point is protection against an attacker who manipulates the content-encryption algorithm identifier or the content-authenticated-encryption algorithm identifier. Johannes Roth and Falko Strenzke actually presented an attack like this at IETF 118 [RS2023], and it goes like this ๐Ÿ˜ญ

  1. The attacker grabs a CMS authenticated-enveloped-data content [RFC5083] that uses either AES-CCM or AES-GCM [RFC5084].
  2. The attacker turns that intercepted content into a "garbage" CMS enveloped-data content (Section 6 of [RFC5652]) made of AES-CBC guess blocks.
  3. The attacker sends the "garbage" message to the victim, and the victim reveals the result of the decryption back to the attacker.
  4. If any of the transformed plaintext blocks match the guess for that block, the attacker learns the plaintext for that block.

Yikes, right? With highly structured messages, one single block can reveal the only sensitive part of the original message ๐Ÿ™ˆ

This attack gets thwarted if the encryption key depends on the delivery of the unmodified algorithm identifier. So the mitigation has three parts ๐Ÿ›ก๏ธ

  1. Potential recipients include the id-alg-cek-hkdf-sha256 algorithm identifier (with no parameters) in S/MIME Capabilities, to show they support this mitigation.
  2. As a flag to the recipient that this mitigation is in use, you carry the id-alg-cek-hkdf-sha256 algorithm identifier as the contentEncryptionAlgorithm in the EncryptedContentInfo structure. That structure shows up in the enveloped-data content type, the encrypted-data content type, and the authenticated-enveloped-data content type. The parameters field of the id-alg-cek-hkdf-sha256 algorithm identifier names the content-encryption algorithm or the content-authenticated-encryption algorithm, plus any associated parameters.
  3. Perform encryption with a derived content-encryption key or content-authenticated-encryption key, using this formula:
CEK' = HKDF(CEK, AlgorithmIdentifier)

1.1. ASN.1 ๐Ÿงฉ

CMS values get generated using ASN.1 [X680]. They use the Basic Encoding Rules (BER) and the Distinguished Encoding Rules (DER) [X690].

1.2. Terminology ๐Ÿ“–

Here's the deal with the key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL". In this document they get read as described in BCP 14 [RFC2119] [RFC8174], but only when they're in all capitals, just like I've shown them here ๐Ÿ’…

1.3. Cryptographic Algorithm Agility Considerations ๐Ÿฆ‹

There's no provision for key derivation functions other than HKDF, and no provision for hash functions other than SHA-256. If the day ever comes when someone needs another key derivation function or another hash function, it'll be super straightforward to assign a new object identifier. For now, keeping the design very simple seems most important ๐ŸŒธ

2. Use of HKDF with SHA-256 to Derive Encryption Keys ๐Ÿ”‘

This mitigation uses the HMAC-based Extract-and-Expand Key Derivation Function (HKDF) [RFC5869] to derive output keying material (OKM) from input keying material (IKM). HKDF runs with the SHA-256 hash function [FIPS180] ๐Ÿง

The derivation includes the DER-encoded AlgorithmIdentifier as the optional info input value. The encoded value has the ASN.1 tag for SEQUENCE (0x30), the length, and the value. This AlgorithmIdentifier gets carried as the parameter to the id-alg-cek-hkdf-sha256 algorithm identifier. And here's the key part โœจ If an attacker changed the originator-provided AlgorithmIdentifier, the recipient would derive a different content-encryption key or content-authenticated-encryption key. Attack foiled ๐Ÿ‘‘

The CMS_CEK_HKDF_SHA256 function uses the HKDF-Extract and HKDF-Expand functions to derive the OKM from the IKM. Its inputs and output look like this:

Inputs:
   IKM   input keying material
   info  DER-encoded AlgorithmIdentifier

Output:
   OKM   output keying material (same size as IKM)

And the OKM gets calculated like this:

   OKM_SIZE = len(IKM)  /* length in octets */
   IF OKM_SIZE > 8160 THEN raise error

   salt = "The Cryptographic Message Syntax"
   PRK = HKDF-Extract(salt, IKM)

   OKM = HKDF-Expand(PRK, info, OKM_SIZE)

3. The id-alg-cek-hkdf-sha256 Algorithm Identifier ๐Ÿท๏ธ

The id-alg-cek-hkdf-sha256 algorithm identifier says that the CMS_CEK_HKDF_SHA256 function from Section 2 is used to derive the content-encryption key or the content-authenticated-encryption key. It's the flag, basically ๐Ÿšฉ

This object identifier names the id-alg-cek-hkdf-sha256 algorithm:

   id-alg-cek-hkdf-sha256 OBJECT IDENTIFIER ::= { iso(1)
      member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9)
      id-smime(16) alg(3) 31 }

The id-alg-cek-hkdf-sha256 parameters field has an ASN.1 type of AlgorithmIdentifier.

Using the conventions from [RFC5911], the id-alg-cek-hkdf-sha256 algorithm identifier gets defined like this ๐Ÿ’œ

     ContentEncryptionAlgorithmIdentifier ::=
       AlgorithmIdentifier{CONTENT-ENCRYPTION, { ... } }

     cea-CEKHKDFSHA256 CONTENT-ENCRYPTION ::= {
       IDENTIFIER id-alg-cek-hkdf-sha256
       PARAMS TYPE ContentEncryptionAlgorithmIdentifier ARE required
       SMIME-CAPS { IDENTIFIED BY id-alg-cek-hkdf-sha256 } }

4. SMIMECapabilities Attribute Conventions ๐Ÿ“ฌ

The SMIMECapabilities attribute is defined in Section 2.5.2 of [RFC8551]. An S/MIME client uses that attribute to announce the set of cryptographic functions it supports ๐Ÿ“ฃ

If an S/MIME client supports the mechanism in this document, the id-alg-cek-hkdf-sha256 object identifier SHOULD be included in the set of cryptographic functions. The parameter with this encoding MUST be absent. Strict, but fair ๐Ÿ’…

The encoding for id-alg-cek-hkdf-sha256, in hexadecimal, is:

   30 0d 06 0b 2a 86 48 86 f7 0d 01 09 10 03 1f

5. Use of HKDF with SHA-256 with CMS ๐ŸŽ€

This section covers the originator and recipient processing you need to implement this mitigation for each of the CMS encrypting content types. It's the practical part, so pay attention ๐Ÿ‘€

5.1. Enveloped-Data Content Type ๐Ÿ’Œ

The fourth step of constructing an enveloped-data content type gets repeated below from Section 6 of [RFC5652]:

   |  4.  The content is encrypted with the content-encryption key.
   |      Content encryption may require that the content be padded to a
   |      multiple of some block size; see Section 6.3.

To implement this mitigation, the originator expands that step like so โœจ

  • Include the id-alg-cek-hkdf-sha256 algorithm identifier in the contentEncryptionAlgorithm.algorithm field of the EncryptedContentInfo structure, and set the contentEncryptionAlgorithm.parameters field to the AlgorithmIdentifier for the content-encryption algorithm that will encrypt the content. That includes both the algorithm and any optional parameters.
  • Derive the new content-encryption key (CEK') from the original content-encryption key (CEK) and the ContentEncryptionAlgorithmIdentifier, which gets carried in the contentEncryptionAlgorithm.parameters field:
   CEK' = CMS_CEK_HKDF_SHA256(CEK, ContentEncryptionAlgorithmIdentifier)
  • The content is encrypted with the new content-encryption key (CEK'). Content encryption may require that the content be padded to a multiple of some block size; see Section 6.3 of [RFC5652].

The presence of the id-alg-cek-hkdf-sha256 algorithm identifier in the contentEncryptionAlgorithm.algorithm field of the EncryptedContentInfo structure tells the recipient to derive the new content-encryption key (CEK') as shown above, and then use it to decrypt the EncryptedContent ๐Ÿ”“ If the id-alg-cek-hkdf-sha256 algorithm identifier is not present in that field, the recipient uses the original content-encryption key (CEK) to decrypt the EncryptedContent instead.


That's part 1 done ๐Ÿซถ Part 2 is coming up next, so stay tuned ๐Ÿ’–

5.2. Encrypted-Data Content Type ๐ŸŽ€

Okay so for encrypted-data, the content-encryption key is managed by other means, just like Section 8 of [RFC5652] spells out ๐Ÿ—๏ธ Here's the mitigation, and it's giving very clean energy ๐Ÿ’…

To implement it, the originator does the following:

  • Include the id-alg-cek-hkdf-sha256 algorithm identifier in the contentEncryptionAlgorithm.algorithm field of the EncryptedContentInfo structure, and set the contentEncryptionAlgorithm.parameters field to the AlgorithmIdentifier for the content-encryption algorithm that will be used to encrypt the content, including both the algorithm and optional parameters.
  • Derive the new content-encryption key (CEK') from the original content-encryption key (CEK) and the ContentEncryptionAlgorithmIdentifier, which is carried in the contentEncryptionAlgorithm.parameters field:
CEK' = CMS_CEK_HKDF_SHA256(CEK, ContentEncryptionAlgorithmIdentifier)
  • The content is encrypted with the new content-encryption key (CEK'). Content encryption may require that the content be padded to a multiple of some block size; see Section 6.3 of [RFC5652] โœจ

The presence of the id-alg-cek-hkdf-sha256 algorithm identifier in the contentEncryptionAlgorithm.algorithm field of the EncryptedContentInfo structure tells the recipient to derive the new content-encryption key (CEK') as shown above, and then use it to decrypt the EncryptedContent. If the id-alg-cek-hkdf-sha256 algorithm identifier is not present in the contentEncryptionAlgorithm.algorithm field of the EncryptedContentInfo structure, then the recipient uses the original content-encryption key (CEK) to decrypt the EncryptedContent ๐Ÿงฉ

5.3. Authenticated-Enveloped-Data Content Type ๐ŸŒธ

The fifth step of building an authenticated-enveloped-data content type is quoted below from Section 2 of [RFC5083], so you can see the original vibe ๐Ÿ“œ

|  5.  The attributes collected in step 4 are authenticated and the
|      CMS content is authenticated and encrypted with the content-
|      authenticated-encryption key.  If the authenticated encryption
|      algorithm requires either the additional authenticated data
|      (AAD) or the content to be padded to a multiple of some block
|      size, then the padding is added as described in Section 6.3 of
|      [CMS].

   |  Note that [CMS] refers to [RFC3852], which has been obsoleted
   |  by [RFC5652], but the text in Section 6.3 was unchanged in RFC
   |  5652.

To implement this mitigation, the originator expands this step as follows:

  • Include the id-alg-cek-hkdf-sha256 algorithm identifier in the contentEncryptionAlgorithm.algorithm field of the EncryptedContentInfo structure, and set the contentEncryptionAlgorithm.parameters field to the AlgorithmIdentifier for the content-authenticated-encryption algorithm that will be used for authenticated encryption of the content, including both the algorithm and optional parameters.
  • Derive the new content-authenticated-encryption key (CEK') from the original content-authenticated-encryption key (CEK) and the ContentEncryptionAlgorithmIdentifier:
CEK' = CMS_CEK_HKDF_SHA256(CEK, ContentEncryptionAlgorithmIdentifier)
  • The attributes collected in step 4 are authenticated and the CMS content is authenticated and encrypted with the new content-authenticated-encryption key (CEK'). If the authenticated encryption algorithm requires either the additional authenticated data (AAD) or the content to be padded to a multiple of some block size, then the padding is added as described in Section 6.3 of [RFC5652] ๐Ÿ’–

The presence of the id-alg-cek-hkdf-sha256 algorithm identifier in the contentEncryptionAlgorithm.algorithm field of the EncryptedContentInfo structure tells the recipient to derive the new content-authenticated-encryption key (CEK') as shown above, and then use it for authenticated decryption of the EncryptedContent and the authentication of the AAD. If the id-alg-cek-hkdf-sha256 algorithm identifier is not present in the contentEncryptionAlgorithm.algorithm field of the EncryptedContentInfo structure, then the recipient uses the original content-authenticated-encryption key (CEK) for decryption and authentication of the EncryptedContent and the authentication of the AAD ๐Ÿ”


6. Security Considerations ๐Ÿšจ

Okay this part is SERIOUS bestie ๐Ÿ˜ญ This mitigation always uses HKDF with SHA-256. One KDF algorithm was selected to avoid the need for negotiation. In the future, if a weakness is found in the KDF algorithm, a new attribute will need to be assigned for use with an alternative KDF algorithm ๐Ÿ”ฎ

If the attacker removes the id-alg-cek-hkdf-sha256 object identifier from the contentEncryptionAlgorithm.algorithm field of the EncryptedContentInfo structure prior to delivery to the recipient, then the recipient will not attempt to derive CEK'. That denies the recipient access to the content, but it does not help the attacker recover the plaintext content ๐Ÿ™…โ€โ™€๏ธ

If the attacker changes the contentEncryptionAlgorithm.parameters field of the EncryptedContentInfo structure prior to delivery to the recipient, then the recipient will derive a different CEK', which will not help the attacker recover the plaintext content. Providing the object identifier as an input to the key derivation function is sufficient to mitigate the attack described in [RS2023], but this mitigation includes both the object identifier and the parameters to protect against some yet-to-be-discovered attack that only manipulates the parameters ๐Ÿ•ต๏ธโ€โ™€๏ธ

Implementations MUST protect the content-encryption keys and content-authenticated-encryption keys, including the CEK and CEK'. Compromise of a content-encryption key may result in disclosure of the associated encrypted content. Compromise of a content-authenticated-encryption key may result in disclosure of the associated encrypted content or allow modification of the authenticated content and the AAD ๐Ÿ˜ฑ๐Ÿ”‘

Implementations MUST randomly generate content-encryption keys and content-authenticated-encryption keys. Using an inadequate pseudorandom number generator (PRNG) to generate cryptographic keys can result in little or no security. An attacker may find it much easier to reproduce the PRNG environment that produced the keys and then search the resulting small set of possibilities, rather than brute-force searching the whole key space. The generation of quality random numbers is difficult. [RFC4086] offers important guidance on this topic ๐ŸŽฒ


7. Privacy Considerations ๐Ÿ™ˆ

If the message-digest attribute is included in the AuthAttributes, then the attribute value will contain the unencrypted one-way hash value of the plaintext of the content. Disclosure of this hash value enables content tracking, and it can be used to determine if the content matches one or more candidates. For these reasons, the AuthAttributes SHOULD NOT contain the message-digest attribute ๐Ÿ‘€


8. Operational Considerations ๐Ÿ“ฌ

CMS is often used to provide encryption in messaging environments, where various forms of unsolicited messages (such as spam and phishing) represent a significant volume of unwanted traffic. Mitigation strategies for unwanted message traffic involve analysis of plaintext message content. When recipients accept unsolicited encrypted messages, they become even more vulnerable to unwanted traffic since many mitigation strategies will be unable to access the plaintext message content.

Therefore, software that receives messages that have been encrypted using CMS ought to provide alternate mechanisms to handle the unwanted message traffic. One approach that does not require disclosure of keying material to a server is to reject or discard encrypted messages unless they purport to come from a member of a previously approved originator list ๐Ÿ—‘๏ธ๐Ÿ’Œ


9. IANA Considerations ๐Ÿณ๏ธโ€๐ŸŒˆ

For the ASN.1 module in Appendix A of this document, IANA has assigned the following object identifier (OID) in the "SMI Security for S/MIME Module Identifier (1.2.840.113549.1.9.16.0)" registry:

     +=========+=================================+============+
     | Decimal | Description                     | References |
     +=========+=================================+============+
     | 80      | id-mod-CMS-CEK-HKDF-SHA256-2023 | RFC 9709   |
     +---------+---------------------------------+------------+

                              Table 1

IANA has allocated the id-alg-cek-hkdf-sha256 algorithm identifier as specified in Section 3 in the "SMI Security for S/MIME Algorithms (1.2.840.113549.1.9.16.3)" registry as follows:

          +=========+========================+============+
          | Decimal | Description            | References |
          +=========+========================+============+
          | 31      | id-alg-cek-hkdf-sha256 | RFC 9709   |
          +---------+------------------------+------------+

                               Table 2

10. References ๐Ÿ“š

10.1. Normative References ๐Ÿง 

10.2. Informative References ๐Ÿ“š

[RFC3852] Housley, R., "Cryptographic Message Syntax (CMS)", RFC 3852, DOI 10.17487/RFC3852, July 2004, <https://www.rfc-editor.org/info/rfc3852>.

[RFC4086] Eastlake 3rd, D., Schiller, J., and S. Crocker, "Randomness Requirements for Security", BCP 106, RFC 4086, DOI 10.17487/RFC4086, June 2005, <https://www.rfc-editor.org/info/rfc4086>.

[RFC5084] Housley, R., "Using AES-CCM and AES-GCM Authenticated Encryption in the Cryptographic Message Syntax (CMS)", RFC 5084, DOI 10.17487/RFC5084, November 2007, <https://www.rfc-editor.org/info/rfc5084>.

[RFC5911] Hoffman, P. and J. Schaad, "New ASN.1 Modules for Cryptographic Message Syntax (CMS) and S/MIME", RFC 5911, DOI 10.17487/RFC5911, June 2010, <https://www.rfc-editor.org/info/rfc5911>.

[RS2023] Roth, J. and F. Strenzke, "AEAD-to-CBC Downgrade Attacks on CMS", IETF 118 Proceedings, 8 November 2023, <https://datatracker.ietf.org/meeting/118/materials/slides-118-lamps-attack-against-aead-in-cms>.


Appendix A. ASN.1 Module ๐Ÿงฉ

Bestie, this ASN.1 module is built on the conventions already set out in [RFC5911], so it's giving "familiar vibes" if you've been there. Here's the whole thing, copied exactly. Don't touch a single character, okay? ๐Ÿซถ

CMS-CEK-HKDF-SHA256-Module-2023
  { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9)
    id-smime(16) id-mod(0) id-mod-CMS-CEK-HKDF-SHA256-2023(80) }

DEFINITIONS IMPLICIT TAGS ::= BEGIN

EXPORTS ALL;

IMPORTS
  AlgorithmIdentifier{}, CONTENT-ENCRYPTION, SMIME-CAPS
  FROM AlgorithmInformation-2009 -- in RFC 5911
    { iso(1) identified-organization(3) dod(6) internet(1)
      security(5) mechanisms(5) pkix(7) id-mod(0)
      id-mod-algorithmInformation-02(58) } ;


--
-- CEK-HKDF-SHA256 Algorithm
--

id-alg-cek-hkdf-sha256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
    us(840) rsadsi(113549) pkcs(1) pkcs-9(9) id-smime(16) alg(3) 31 }

ContentEncryptionAlgorithmIdentifier ::=
    AlgorithmIdentifier{CONTENT-ENCRYPTION, { ... } }

cea-CEKHKDFSHA256 CONTENT-ENCRYPTION ::= {
    IDENTIFIER id-alg-cek-hkdf-sha256
    PARAMS TYPE ContentEncryptionAlgorithmIdentifier ARE required
    SMIME-CAPS { IDENTIFIED BY id-alg-cek-hkdf-sha256 } }

--
-- S/MIME Capability for CEK-HKDF-SHA256 Algorithm
--

SMimeCaps SMIME-CAPS ::= { cap-CMSCEKHKDFSHA256, ... }

cap-CMSCEKHKDFSHA256 SMIME-CAPS ::=
    { -- No value -- IDENTIFIED BY id-alg-cek-hkdf-sha256 }

END

Appendix B. CMS_CEK_HKDF_SHA256 Function Examples ๐Ÿงช

Okay, time for the fun part! This appendix gives you two test vectors for the CMS_CEK_HKDF_SHA256 function. Check your work against these. โœจ

B.1. CMS_CEK_HKDF_SHA256 with AES-128-GCM ๐Ÿ”

This test vector includes an AlgorithmIdentifier for AES-128-GCM.

IKM = c702e7d0a9e064b09ba55245fb733cf3

The AES-128-GCM AlgorithmIdentifier:

algorithm=2.16.840.1.101.3.4.1.6
parameters=GCMParameters:
 aes-nonce=0x5c79058ba2f43447639d29e2
 aes-ICVlen is omitted; it indicates the DEFAULT of 12

DER-encoded AlgorithmIdentifier:

301b0609608648016503040106300e040c5c79058ba2f43447639d29e2
OKM = 2124ffb29fac4e0fbbc7d5d87492bff3

B.2. CMS_CEK_HKDF_SHA256 with AES-128-CBC ๐ŸŽ€

This test vector includes an AlgorithmIdentifier for AES-128-CBC. Same IKM, different cipher, very different vibe. ๐Ÿ’…

IKM = c702e7d0a9e064b09ba55245fb733cf3

The AES-128-CBC AlgorithmIdentifier:

algorithm=2.16.840.1.101.3.4.1.2
parameters=AES-IV=0x651f722ffd512c52fe072e507d72b377

DER-encoded AlgorithmIdentifier:

301d06096086480165030401020410651f722ffd512c52fe072e507d72b377
OKM = 9cd102c52f1e19ece8729b35bfeceb50

Acknowledgements ๐Ÿซถ

Thanks to Mike Ounsworth, Carl Wallace, and Joe Mandel for their careful review and constructive comments. You all are absolutely iconic for this. ๐Ÿ’–


Author's Address ๐Ÿฆ‹

Russ Housley Vigil Security, LLC Herndon, VA United States of America Email: housley@vigilsec.com

๐Ÿ“ฆ where this page comes from