File: System\Security\Cryptography\HpkeX25519DiffieHellmanKemAdapter.cs
Web Access
Project: System.Security.Cryptography.csproj (System.Security.Cryptography)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;

namespace System.Security.Cryptography
{
    internal sealed class HpkeX25519DiffieHellmanKemAdapter : HpkeManagedKemAdapter
    {
        private X25519DiffieHellman? _x25519;

        internal HpkeX25519DiffieHellmanKemAdapter(HpkeSuite suite) : base(suite)
        {
        }

        internal override void ImportDecapsulationKey(ReadOnlySpan<byte> decapsulationKey)
        {
            Debug.Assert(_x25519 is null);
            _x25519 = X25519DiffieHellman.ImportPrivateKey(decapsulationKey);
        }

        internal override void ImportEncapsulationKey(ReadOnlySpan<byte> encapsulationKey)
        {
            Debug.Assert(_x25519 is null);
            _x25519 = X25519DiffieHellman.ImportPublicKey(encapsulationKey);
        }

        internal override void DeriveKeyPair(ReadOnlySpan<byte> ikm)
        {
            Debug.Assert(_x25519 is null);

            Span<byte> privateKey = stackalloc byte[X25519DiffieHellman.PrivateKeySizeInBytes];
            Span<byte> prkBuffer = stackalloc byte[PrkStackBufferSize];

            try
            {
                Span<byte> prk = prkBuffer.Slice(0, KeyDerivationKdf.Nh);
                LabeledExtract(ReadOnlySpan<byte>.Empty, "dkp_prk"u8, ikm, prk);
                LabeledExpand(prk, "sk"u8, ReadOnlySpan<byte>.Empty, privateKey);
                _x25519 = X25519DiffieHellman.ImportPrivateKey(privateKey);
            }
            finally
            {
                CryptographicOperations.ZeroMemory(privateKey);
                CryptographicOperations.ZeroMemory(prkBuffer);
            }
        }

        internal override void Encapsulate(Span<byte> encapsulatedSecret, Span<byte> sharedSecret)
        {
            Debug.Assert(_x25519 is not null);

            using (HpkeX25519DiffieHellmanKemAdapter ephemeral = new HpkeX25519DiffieHellmanKemAdapter(Suite))
            {
                ephemeral.Generate();
                Debug.Assert(ephemeral._x25519 is not null);

                Span<byte> dh = stackalloc byte[X25519DiffieHellman.SecretAgreementSizeInBytes];

                try
                {
                    ephemeral._x25519.DeriveRawSecretAgreement(_x25519, dh);
                    ephemeral.ExportEncapsulationKey(encapsulatedSecret);
                    ExtractAndExpand(dh, encapsulatedSecret, sharedSecret);
                }
                finally
                {
                    CryptographicOperations.ZeroMemory(dh);
                }
            }
        }

        // https://datatracker.ietf.org/doc/html/draft-ietf-hpke-hpke-04#section-4.5
        internal override void Decapsulate(ReadOnlySpan<byte> encapsulatedSecret, Span<byte> sharedSecret)
        {
            Debug.Assert(_x25519 is not null);
            Span<byte> secretAgreement = stackalloc byte[X25519DiffieHellman.SecretAgreementSizeInBytes];

            try
            {
                _x25519.DeriveRawSecretAgreement(encapsulatedSecret, secretAgreement);
                ExtractAndExpand(secretAgreement, encapsulatedSecret, sharedSecret);
            }
            finally
            {
                CryptographicOperations.ZeroMemory(secretAgreement);
            }
        }

        internal override void ExportDecapsulationKey(Span<byte> destination)
        {
            Debug.Assert(_x25519 is not null);
            Debug.Assert(destination.Length == Suite.DecapsulationKeySizeInBytes);

            _x25519.ExportPrivateKey(destination);
        }

        internal override void ExportEncapsulationKey(Span<byte> destination)
        {
            Debug.Assert(_x25519 is not null);
            Debug.Assert(destination.Length == Suite.EncapsulationKeySizeInBytes);

            _x25519.ExportPublicKey(destination);
        }

        public override void Dispose() => _x25519?.Dispose();
    }
}