dotnet / dotnet/runtime

[API Proposal]: KEM support for EnvelopedCms

Open
#130,883 4 comments 0 reactions 1 assignee Claimed by @vcsjones View on GitHub
api-approved area-System.Security
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

### Background and motivation

Enveloped CMS has support for KEMs. RFC 9629 generalized an KEMReceipientInfo, and RFC 9936 specifies how to use ML-KEM with KEMRecipientInfo.

This is a proposal to add a KEMRecipientInfo and ML-KEM to `EnvelopedCms`.

### API Proposal

```csharp
namespace System.Security.Cryptography.Pkcs;

public sealed class KemRecipientInfo : RecipientInfo
{
internal KemRecipientInfo();

public override int Version { get; }
public override SubjectIdentifier RecipientIdentifier { get; }
public override AlgorithmIdentifier KeyEncryptionAlgorithm { get; }
public override byte[] EncryptedKey { get; }

public AlgorithmIdentifier KeyEncapsulationAlgorithm { get; }
public ReadOnlyMemory KeyEncapsulationCiphertext { get; }
public AlgorithmIdentifier KeyDerivationAlgorithm { get; }
public int KeyEncryptionKeyLengthInBytes { get; }

// Important, "null" means the OPTIONAL OCTET STRING is missing entirely.
// Present, but empty, means the OCTET STRING was present with a length of zero.
public ReadOnlyMemory? UserKeyingMaterial { get; }
}

public partial enum RecipientInfoType
{
// Existing values omitted.
KeyEncapsulation = 3,
}

public sealed partial class EnvelopedCms
{
public void Decrypt(KemRecipientInfo recipientInfo, MLKem privateKey);
}

public sealed partial class CmsRecipient
{
// Factories for specifying a userKeyingMaterial. It's important to recognize that an _empty_
// userKeyingMaterial is not the same as an _absent_ UKM. If someone wants to create a recipient
// with an no UKM, whatsoever, they use a constructor. If they want to specify a UKM, including
// an empty one, they use the factory. That is also why this parameter is not optional.
// We use a factory method instead of constructor overloading because `null` inputs will result in overload ambiguity.
// This will reject non-KEM certificates. That is another reason for not using a constructor.
public static CmsRecipient CreateForKeyEncapsulation(
X509Certificate2 certificate,
ReadOnlySpan userKeyingMaterial);

public static CmsRecipient CreateForKeyEncapsulation(
SubjectIdentifierType recipientIdentifierType,
X509Certificate2 certificate,
ReadOnlySpan userKeyingMaterial);
}

```

### API Usage

```csharp
using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
using System.Text;

byte[] encodedCms = File.ReadAllBytes("message.p7m");

using MLKem privateKey = MLKem.ImportFromPem(File.ReadAllText("mlkem-private-key.pem"));

EnvelopedCms cms = new();
cms.Decode(encodedCms);

KemRecipientInfo recipient = (KemRecipientInfo)cms.RecipientInfos[0];

cms.Decrypt(recipient, privateKey);
```

### Alternative Designs

_No response_

### Risks

_No response_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.