File: System\Security\Cryptography\DSA.Xml.cs
Web Access
Project: src\src\libraries\System.Security.Cryptography\src\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;
using System.Text;
 
namespace System.Security.Cryptography
{
    public abstract partial class DSA : AsymmetricAlgorithm
    {
        private const string CounterElementName = "PgenCounter";
 
        private static byte[] ReadRequiredElement(
            ref XmlKeyHelper.ParseState state,
            string name,
            int sizeHint = -1)
        {
            byte[]? ret = XmlKeyHelper.ReadCryptoBinary(ref state, name, sizeHint);
 
            if (ret == null)
            {
                throw new CryptographicException(
                    SR.Format(SR.Cryptography_InvalidFromXmlString, nameof(DSA), name));
            }
 
            return ret;
        }
 
        public override void FromXmlString(string xmlString)
        {
            // ParseDocument does the nullcheck for us.
            XmlKeyHelper.ParseState state = XmlKeyHelper.ParseDocument(xmlString);
 
            byte[] p = ReadRequiredElement(ref state, nameof(DSAParameters.P));
            byte[] q = ReadRequiredElement(ref state, nameof(DSAParameters.Q));
            byte[] g = ReadRequiredElement(ref state, nameof(DSAParameters.G), p.Length);
            byte[] y = ReadRequiredElement(ref state, nameof(DSAParameters.Y), p.Length);
            byte[]? j = XmlKeyHelper.ReadCryptoBinary(ref state, nameof(DSAParameters.J));
            byte[]? seed = XmlKeyHelper.ReadCryptoBinary(ref state, nameof(DSAParameters.Seed));
            int counter = 0;
            byte[]? x = XmlKeyHelper.ReadCryptoBinary(ref state, nameof(DSAParameters.X), q.Length);
 
            if (seed != null)
            {
                byte[] counterBytes = ReadRequiredElement(ref state, CounterElementName);
                counter = XmlKeyHelper.ReadCryptoBinaryInt32(counterBytes);
            }
 
            DSAParameters dsaParameters = new DSAParameters
            {
                P = p,
                Q = q,
                G = g,
                Y = y,
                J = j,
                Seed = seed,
                Counter = counter,
                X = x,
            };
 
            // Check for Counter without Seed after getting X, since that prevents an extra cycle in the
            // canonical element order.
            if (dsaParameters.Seed == null)
            {
                if (XmlKeyHelper.HasElement(ref state, CounterElementName))
                {
                    throw new CryptographicException(
                        SR.Format(
                            SR.Cryptography_InvalidFromXmlString,
                            nameof(DSA),
                            nameof(DSAParameters.Seed)));
                }
            }
 
            ImportParameters(dsaParameters);
        }
 
        public override string ToXmlString(bool includePrivateParameters)
        {
            // The format of this output is based on the xmldsig ds:DSAKeyValue value, except
            // * It writes values as xml:base64Binary instead of ds:CryptoBinary
            //   * It doesn't strip off leading 0x00 byte values before base64
            // * It doesn't emit the output in a namespace
            // * When includePrivateParameters is true it writes an X element.
            //
            // These deviations are inherited from .NET Framework.
 
            // P is KeySizeInBytes long.
            // Q is 160 to 256 bits long, or 20 to 32 bytes.
            // G is the same size as P
            // Y is the same size as P
            // X is the same size as Q
            //
            // Each field gets base64 encoded (after dropping leading 0x00 bytes)
            // so P is (KeySizeInBytes + 2) / 3 * 4, then plus 7 (<P></P>)
            // (For 3072 that's 519 chars, for 1024 it's 179.)
            // Add in maximum-Q: (32 + 2) / 3 * 4 + 7 => 51
            // Then the "<DSAKeyValue></DSAKeyValue>" (27).
            // Grand total, 3 * P + 2 * Q + 27 => 1686 (3072) or 666 (1024).
            // KeySizeInBytes * 2 / 3 comes out to 2048 or 682, so call that good enough.
 
            // Rarely, keys will export the J or Seed values, and they may cause the
            // StringBuilder to need to grow.
 
            DSAParameters keyParameters = ExportParameters(includePrivateParameters);
            Debug.Assert(keyParameters.P != null);
            StringBuilder builder = new StringBuilder((keyParameters.P.Length << 1) / 3);
            builder.Append("<DSAKeyValue>");
            XmlKeyHelper.WriteCryptoBinary(nameof(DSAParameters.P), keyParameters.P, builder);
            XmlKeyHelper.WriteCryptoBinary(nameof(DSAParameters.Q), keyParameters.Q, builder);
            XmlKeyHelper.WriteCryptoBinary(nameof(DSAParameters.G), keyParameters.G, builder);
            XmlKeyHelper.WriteCryptoBinary(nameof(DSAParameters.Y), keyParameters.Y, builder);
 
            if (keyParameters.J != null)
            {
                XmlKeyHelper.WriteCryptoBinary(nameof(DSAParameters.J), keyParameters.J, builder);
            }
 
            if (keyParameters.Seed != null)
            {
                XmlKeyHelper.WriteCryptoBinary(nameof(DSAParameters.Seed), keyParameters.Seed, builder);
                XmlKeyHelper.WriteCryptoBinary(CounterElementName, keyParameters.Counter, builder);
            }
 
            if (includePrivateParameters)
            {
                // .NET Framework compat when a 3rd party type lets X be null when
                // includePrivateParameters is true
                // (the exception would have been from Convert.ToBase64String)
                ArgumentNullException.ThrowIfNull(keyParameters.X, "inArray");
 
                XmlKeyHelper.WriteCryptoBinary(nameof(DSAParameters.X), keyParameters.X, builder);
            }
 
            builder.Append("</DSAKeyValue>");
            return builder.ToString();
        }
    }
}