File: system\security\cryptography\dsa.cs
Project: ndp\clr\src\bcl\mscorlib.csproj (mscorlib)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
// <OWNER>Microsoft</OWNER>
// 
 
//
// DSA.cs
//
 
namespace System.Security.Cryptography {
    using System.Text;
    using System.Runtime.Serialization;
    using System.Security.Util;
    using System.Globalization;
    using System.IO;
    using System.Diagnostics.Contracts;
 
    // DSAParameters is serializable so that one could pass the public parameters
    // across a remote call, but we explicitly make the private key X non-serializable
    // so you cannot accidently send it along with the public parameters.
    [Serializable]
[System.Runtime.InteropServices.ComVisible(true)]
    public struct DSAParameters {
        public byte[]      P;
        public byte[]      Q;
        public byte[]      G;
        public byte[]      Y;
        public byte[]      J;
        [NonSerialized] public byte[]      X;
        public byte[]      Seed;
        public int         Counter;
    }
 
[System.Runtime.InteropServices.ComVisible(true)]
    public abstract class DSA : AsymmetricAlgorithm
    {
        //
        //  Extending this class allows us to know that you are really implementing
        //  an DSA key.  This is required for anybody providing a new DSA key value
        //  implemention.
        //
        //  The class provides no methods, fields or anything else.  Its only purpose is
        //  as a heirarchy member for identification of the algorithm.
        //
 
        protected DSA() { }
 
        //
        // public methods
        //
 
        new static public DSA Create() {
            return Create("System.Security.Cryptography.DSA");
        }
 
        new static public DSA Create(String algName) {
            return (DSA) CryptoConfig.CreateFromName(algName);
        }
 
        static public DSA Create(int keySizeInBits) {
            DSA dsa = (DSA)CryptoConfig.CreateFromName("DSA-FIPS186-3");
            dsa.KeySize = keySizeInBits;
 
            if (dsa.KeySize != keySizeInBits) {
                throw new CryptographicException();
            }
 
            return dsa;
        }
 
        static public DSA Create(DSAParameters parameters) {
            DSA dsa = (DSA)CryptoConfig.CreateFromName("DSA-FIPS186-3");
            dsa.ImportParameters(parameters);
            return dsa;
        }
 
        // DSA does not encode the algorithm identifier into the signature blob, therefore CreateSignature and
        // VerifySignature do not need the HashAlgorithmName value, only SignData and VerifyData do.
        abstract public byte[] CreateSignature(byte[] rgbHash);
 
        abstract public bool VerifySignature(byte[] rgbHash, byte[] rgbSignature); 
 
        protected virtual byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
        {
            throw DerivedClassMustOverride();
        }
 
        protected virtual byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
        {
            throw DerivedClassMustOverride();
        }
 
        public byte[] SignData(byte[] data, HashAlgorithmName hashAlgorithm)
        {
            if (data == null) { throw new ArgumentNullException("data"); }
 
            return SignData(data, 0, data.Length, hashAlgorithm);
        }
 
        public virtual byte[] SignData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
        {
            if (data == null) { throw new ArgumentNullException("data"); }
            if (offset < 0 || offset > data.Length) { throw new ArgumentOutOfRangeException("offset"); }
            if (count < 0 || count > data.Length - offset) { throw new ArgumentOutOfRangeException("count"); }
            if (String.IsNullOrEmpty(hashAlgorithm.Name)) { throw HashAlgorithmNameNullOrEmpty(); }
 
            byte[] hash = HashData(data, offset, count, hashAlgorithm);
            return CreateSignature(hash);
        }
 
        public virtual byte[] SignData(Stream data, HashAlgorithmName hashAlgorithm)
        {
            if (data == null) { throw new ArgumentNullException("data"); }
            if (String.IsNullOrEmpty(hashAlgorithm.Name)) { throw HashAlgorithmNameNullOrEmpty(); }
 
            byte[] hash = HashData(data, hashAlgorithm);
            return CreateSignature(hash);
        }
 
        public bool VerifyData(byte[] data, byte[] signature, HashAlgorithmName hashAlgorithm)
        {
            if (data == null) { throw new ArgumentNullException("data"); }
 
            return VerifyData(data, 0, data.Length, signature, hashAlgorithm);
        }
 
        public virtual bool VerifyData(byte[] data, int offset, int count, byte[] signature, HashAlgorithmName hashAlgorithm)
        {
            if (data == null) { throw new ArgumentNullException("data"); }
            if (offset < 0 || offset > data.Length) { throw new ArgumentOutOfRangeException("offset"); }
            if (count < 0 || count > data.Length - offset) { throw new ArgumentOutOfRangeException("count"); }
            if (signature == null) { throw new ArgumentNullException("signature"); }
            if (String.IsNullOrEmpty(hashAlgorithm.Name)) { throw HashAlgorithmNameNullOrEmpty(); }
 
            byte[] hash = HashData(data, offset, count, hashAlgorithm);
            return VerifySignature(hash, signature);
        }
 
        public virtual bool VerifyData(Stream data, byte[] signature, HashAlgorithmName hashAlgorithm)
        {
            if (data == null) { throw new ArgumentNullException("data"); }
            if (signature == null) { throw new ArgumentNullException("signature"); }
            if (String.IsNullOrEmpty(hashAlgorithm.Name)) { throw HashAlgorithmNameNullOrEmpty(); }
 
            byte[] hash = HashData(data, hashAlgorithm);
            return VerifySignature(hash, signature);
        }
 
        // We can provide a default implementation of FromXmlString because we require 
        // every DSA implementation to implement ImportParameters
        // All we have to do here is parse the XML.
 
        public override void FromXmlString(String xmlString) {
            if (xmlString == null) throw new ArgumentNullException("xmlString");
            Contract.EndContractBlock();
            DSAParameters dsaParams = new DSAParameters();
            Parser p = new Parser(xmlString);
            SecurityElement topElement = p.GetTopElement();
 
            // P is always present
            String pString = topElement.SearchForTextOfLocalName("P");
            if (pString == null) {
                throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidFromXmlString","DSA","P"));
            }
            dsaParams.P = Convert.FromBase64String(Utils.DiscardWhiteSpaces(pString));
 
            // Q is always present
            String qString = topElement.SearchForTextOfLocalName("Q");
            if (qString == null) {
                throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidFromXmlString","DSA","Q"));
            }
            dsaParams.Q = Convert.FromBase64String(Utils.DiscardWhiteSpaces(qString));
 
            // G is always present
            String gString = topElement.SearchForTextOfLocalName("G");
            if (gString == null) {
                throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidFromXmlString","DSA","G"));
            }
            dsaParams.G = Convert.FromBase64String(Utils.DiscardWhiteSpaces(gString));
 
            // Y is always present
            String yString = topElement.SearchForTextOfLocalName("Y");
            if (yString == null) {
                throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidFromXmlString","DSA","Y"));
            }
            dsaParams.Y = Convert.FromBase64String(Utils.DiscardWhiteSpaces(yString));
 
            // J is optional
            String jString = topElement.SearchForTextOfLocalName("J");
            if (jString != null) dsaParams.J = Convert.FromBase64String(Utils.DiscardWhiteSpaces(jString));
 
            // X is optional -- private key if present
            String xString = topElement.SearchForTextOfLocalName("X");
            if (xString != null) dsaParams.X = Convert.FromBase64String(Utils.DiscardWhiteSpaces(xString));
 
            // Seed and PgenCounter are optional as a unit -- both present or both absent
            String seedString = topElement.SearchForTextOfLocalName("Seed");
            String pgenCounterString = topElement.SearchForTextOfLocalName("PgenCounter");
            if ((seedString != null) && (pgenCounterString != null)) {
                dsaParams.Seed = Convert.FromBase64String(Utils.DiscardWhiteSpaces(seedString));
                dsaParams.Counter = Utils.ConvertByteArrayToInt(Convert.FromBase64String(Utils.DiscardWhiteSpaces(pgenCounterString)));
            } else if ((seedString != null) || (pgenCounterString != null)) {
                if (seedString == null) {
                    throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidFromXmlString","DSA","Seed"));
                } else {
                    throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidFromXmlString","DSA","PgenCounter"));
                }
            }
 
            ImportParameters(dsaParams);
        }
 
        // We can provide a default implementation of ToXmlString because we require 
        // every DSA implementation to implement ImportParameters
        // If includePrivateParameters is false, this is just an XMLDSIG DSAKeyValue
        // clause.  If includePrivateParameters is true, then we extend DSAKeyValue with 
        // the other (private) elements.
 
        public override String ToXmlString(bool includePrivateParameters) {
            // From the XMLDSIG spec, RFC 3075, Section 6.4.1, a DSAKeyValue looks like this:
            /* 
               <element name="DSAKeyValue"> 
                 <complexType> 
                   <sequence>
                     <sequence>
                       <element name="P" type="ds:CryptoBinary"/> 
                       <element name="Q" type="ds:CryptoBinary"/> 
                       <element name="G" type="ds:CryptoBinary"/> 
                       <element name="Y" type="ds:CryptoBinary"/> 
                       <element name="J" type="ds:CryptoBinary" minOccurs="0"/> 
                     </sequence>
                     <sequence minOccurs="0">
                       <element name="Seed" type="ds:CryptoBinary"/> 
                       <element name="PgenCounter" type="ds:CryptoBinary"/> 
                     </sequence>
                   </sequence>
                 </complexType>
               </element>
            */
            // we extend appropriately for private component X
            DSAParameters dsaParams = this.ExportParameters(includePrivateParameters);
            StringBuilder sb = new StringBuilder();
            sb.Append("<DSAKeyValue>");
            // Add P, Q, G and Y
            sb.Append("<P>"+Convert.ToBase64String(dsaParams.P)+"</P>");
            sb.Append("<Q>"+Convert.ToBase64String(dsaParams.Q)+"</Q>");
            sb.Append("<G>"+Convert.ToBase64String(dsaParams.G)+"</G>");
            sb.Append("<Y>"+Convert.ToBase64String(dsaParams.Y)+"</Y>");
            // Add optional components if present
            if (dsaParams.J != null) {
                sb.Append("<J>"+Convert.ToBase64String(dsaParams.J)+"</J>");
            }
            if ((dsaParams.Seed != null)) {  // note we assume counter is correct if Seed is present
                sb.Append("<Seed>"+Convert.ToBase64String(dsaParams.Seed)+"</Seed>");
                sb.Append("<PgenCounter>"+Convert.ToBase64String(Utils.ConvertIntToByteArray(dsaParams.Counter))+"</PgenCounter>");
            }
 
            if (includePrivateParameters) {
                // Add the private component
                sb.Append("<X>"+Convert.ToBase64String(dsaParams.X)+"</X>");
            } 
            sb.Append("</DSAKeyValue>");
            return(sb.ToString());
        }
 
        abstract public DSAParameters ExportParameters(bool includePrivateParameters);
 
        abstract public void ImportParameters(DSAParameters parameters);
 
        private static Exception DerivedClassMustOverride()
        {
            return new NotImplementedException(Environment.GetResourceString("NotSupported_SubclassOverride"));
        }
 
        internal static Exception HashAlgorithmNameNullOrEmpty()
        {
            return new ArgumentException(Environment.GetResourceString("Cryptography_HashAlgorithmNameNullOrEmpty"), "hashAlgorithm");
        }
    }
}