File: System\Security\Cryptography\Pkcs\CmsSignature.ECDsa.cs
Web Access
Project: src\src\libraries\System.Security.Cryptography.Pkcs\src\System.Security.Cryptography.Pkcs.csproj (System.Security.Cryptography.Pkcs)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography.X509Certificates;
using Internal.Cryptography;
 
namespace System.Security.Cryptography.Pkcs
{
    internal partial class CmsSignature
    {
        static partial void PrepareRegistrationECDsa(Dictionary<string, CmsSignature> lookup)
        {
            lookup.Add(Oids.ECDsaWithSha1, new ECDsaCmsSignature(Oids.ECDsaWithSha1, HashAlgorithmName.SHA1));
            lookup.Add(Oids.ECDsaWithSha256, new ECDsaCmsSignature(Oids.ECDsaWithSha256, HashAlgorithmName.SHA256));
            lookup.Add(Oids.ECDsaWithSha384, new ECDsaCmsSignature(Oids.ECDsaWithSha384, HashAlgorithmName.SHA384));
            lookup.Add(Oids.ECDsaWithSha512, new ECDsaCmsSignature(Oids.ECDsaWithSha512, HashAlgorithmName.SHA512));
#if NET8_0_OR_GREATER
            lookup.Add(Oids.ECDsaWithSha3_256, new ECDsaCmsSignature(Oids.ECDsaWithSha3_256, HashAlgorithmName.SHA3_256));
            lookup.Add(Oids.ECDsaWithSha3_384, new ECDsaCmsSignature(Oids.ECDsaWithSha3_384, HashAlgorithmName.SHA3_384));
            lookup.Add(Oids.ECDsaWithSha3_512, new ECDsaCmsSignature(Oids.ECDsaWithSha3_512, HashAlgorithmName.SHA3_512));
#endif
            lookup.Add(Oids.EcPublicKey, new ECDsaCmsSignature(null, null));
        }
 
        private sealed partial class ECDsaCmsSignature : CmsSignature
        {
            private readonly HashAlgorithmName? _expectedDigest;
            private readonly string? _signatureAlgorithm;
 
            internal override RSASignaturePadding? SignaturePadding => null;
 
            internal ECDsaCmsSignature(string? signatureAlgorithm, HashAlgorithmName? expectedDigest)
            {
                _signatureAlgorithm = signatureAlgorithm;
                _expectedDigest = expectedDigest;
            }
 
            protected override bool VerifyKeyType(AsymmetricAlgorithm key)
            {
                return (key as ECDsa) != null;
            }
 
            internal override bool VerifySignature(
#if NET || NETSTANDARD2_1
                ReadOnlySpan<byte> valueHash,
                ReadOnlyMemory<byte> signature,
#else
                byte[] valueHash,
                byte[] signature,
#endif
                string? digestAlgorithmOid,
                HashAlgorithmName digestAlgorithmName,
                ReadOnlyMemory<byte>? signatureParameters,
                X509Certificate2 certificate)
            {
                if (_expectedDigest != null && _expectedDigest != digestAlgorithmName)
                {
                    throw new CryptographicException(
                        SR.Format(
                            SR.Cryptography_Cms_InvalidSignerHashForSignatureAlg,
                            digestAlgorithmOid,
                            _signatureAlgorithm));
                }
 
                ECDsa? key = certificate.GetECDsaPublicKey();
 
                if (key == null)
                {
                    return false;
                }
 
                int bufSize;
                checked
                {
                    // fieldSize = ceil(KeySizeBits / 8);
                    int fieldSize = (key.KeySize + 7) / 8;
                    bufSize = 2 * fieldSize;
                }
 
#if NET || NETSTANDARD2_1
                byte[] rented = CryptoPool.Rent(bufSize);
                Span<byte> ieee = new Span<byte>(rented, 0, bufSize);
 
                try
                {
#else
                byte[] ieee = new byte[bufSize];
#endif
                    if (!DsaDerToIeee(signature, ieee))
                    {
                        return false;
                    }
 
                    return key.VerifyHash(valueHash, ieee);
#if NET || NETSTANDARD2_1
                }
                finally
                {
                    CryptoPool.Return(rented, bufSize);
                }
#endif
            }
 
            protected override bool Sign(
#if NET || NETSTANDARD2_1
                ReadOnlySpan<byte> dataHash,
#else
                byte[] dataHash,
#endif
                HashAlgorithmName hashAlgorithmName,
                X509Certificate2 certificate,
                AsymmetricAlgorithm? certKey,
                bool silent,
                [NotNullWhen(true)] out string? signatureAlgorithm,
                [NotNullWhen(true)] out byte[]? signatureValue,
                out byte[]? signatureParameters)
            {
                signatureParameters = null;
                // If there's no private key, fall back to the public key for a "no private key" exception.
                ECDsa? key = certKey as ECDsa ??
                    PkcsPal.Instance.GetPrivateKeyForSigning<ECDsa>(certificate, silent) ??
                    certificate.GetECDsaPublicKey();
 
                if (key == null)
                {
                    signatureAlgorithm = null;
                    signatureValue = null;
                    return false;
                }
 
                string? oidValue =
                    hashAlgorithmName == HashAlgorithmName.SHA1 ? Oids.ECDsaWithSha1 :
                    hashAlgorithmName == HashAlgorithmName.SHA256 ? Oids.ECDsaWithSha256 :
                    hashAlgorithmName == HashAlgorithmName.SHA384 ? Oids.ECDsaWithSha384 :
                    hashAlgorithmName == HashAlgorithmName.SHA512 ? Oids.ECDsaWithSha512 :
#if NET8_0_OR_GREATER
                    hashAlgorithmName == HashAlgorithmName.SHA3_256 ? Oids.ECDsaWithSha3_256 :
                    hashAlgorithmName == HashAlgorithmName.SHA3_384 ? Oids.ECDsaWithSha3_384 :
                    hashAlgorithmName == HashAlgorithmName.SHA3_512 ? Oids.ECDsaWithSha3_512 :
#endif
                    null;
 
                if (oidValue == null)
                {
                    signatureAlgorithm = null;
                    signatureValue = null;
                    return false;
                }
 
                signatureAlgorithm = oidValue;
 
#if NET || NETSTANDARD2_1
                int bufSize;
                checked
                {
                    // fieldSize = ceil(KeySizeBits / 8);
                    int fieldSize = (key.KeySize + 7) / 8;
                    bufSize = 2 * fieldSize;
                }
 
                byte[] rented = CryptoPool.Rent(bufSize);
                int bytesWritten = 0;
 
                try
                {
                    if (key.TrySignHash(dataHash, rented, out bytesWritten))
                    {
                        var signedHash = new ReadOnlySpan<byte>(rented, 0, bytesWritten);
 
                        if (key != null && !certificate.GetECDsaPublicKey()!.VerifyHash(dataHash, signedHash))
                        {
                            // key did not match certificate
                            signatureValue = null;
                            return false;
                        }
 
                        signatureValue = DsaIeeeToDer(signedHash);
                        return true;
                    }
                }
                finally
                {
                    CryptoPool.Return(rented, bytesWritten);
                }
#endif
 
                signatureValue = DsaIeeeToDer(key.SignHash(
#if NET || NETSTANDARD2_1
                    dataHash.ToArray()
#else
                    dataHash
#endif
                    ));
                return true;
            }
        }
    }
}