Merge pull request #3389 from lambdageek/bug-43099
[mono.git] / mcs / class / referencesource / mscorlib / system / security / cryptography / dsa.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // <OWNER>[....]</OWNER>
7 // 
8
9 //
10 // DSA.cs
11 //
12
13 namespace System.Security.Cryptography {
14     using System.Text;
15     using System.Runtime.Serialization;
16     using System.Security.Util;
17     using System.Globalization;
18     using System.IO;
19     using System.Diagnostics.Contracts;
20
21     // DSAParameters is serializable so that one could pass the public parameters
22     // across a remote call, but we explicitly make the private key X non-serializable
23     // so you cannot accidently send it along with the public parameters.
24     [Serializable]
25 [System.Runtime.InteropServices.ComVisible(true)]
26     public struct DSAParameters {
27         public byte[]      P;
28         public byte[]      Q;
29         public byte[]      G;
30         public byte[]      Y;
31         public byte[]      J;
32         [NonSerialized] public byte[]      X;
33         public byte[]      Seed;
34         public int         Counter;
35     }
36
37 [System.Runtime.InteropServices.ComVisible(true)]
38     public abstract class DSA : AsymmetricAlgorithm
39     {
40         //
41         //  Extending this class allows us to know that you are really implementing
42         //  an DSA key.  This is required for anybody providing a new DSA key value
43         //  implemention.
44         //
45         //  The class provides no methods, fields or anything else.  Its only purpose is
46         //  as a heirarchy member for identification of the algorithm.
47         //
48
49         protected DSA() { }
50
51         //
52         // public methods
53         //
54
55         new static public DSA Create() {
56 #if FULL_AOT_RUNTIME
57             return new System.Security.Cryptography.DSACryptoServiceProvider ();
58 #else
59             return Create("System.Security.Cryptography.DSA");
60 #endif
61         }
62
63         new static public DSA Create(String algName) {
64             return (DSA) CryptoConfig.CreateFromName(algName);
65         }
66
67         // DSA does not encode the algorithm identifier into the signature blob, therefore CreateSignature and
68         // VerifySignature do not need the HashAlgorithmName value, only SignData and VerifyData do.
69         abstract public byte[] CreateSignature(byte[] rgbHash);
70
71         abstract public bool VerifySignature(byte[] rgbHash, byte[] rgbSignature); 
72
73         protected virtual byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
74         {
75             throw DerivedClassMustOverride();
76         }
77
78         protected virtual byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
79         {
80             throw DerivedClassMustOverride();
81         }
82
83         public byte[] SignData(byte[] data, HashAlgorithmName hashAlgorithm)
84         {
85             if (data == null) { throw new ArgumentNullException("data"); }
86
87             return SignData(data, 0, data.Length, hashAlgorithm);
88         }
89
90         public virtual byte[] SignData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
91         {
92             if (data == null) { throw new ArgumentNullException("data"); }
93             if (offset < 0 || offset > data.Length) { throw new ArgumentOutOfRangeException("offset"); }
94             if (count < 0 || count > data.Length - offset) { throw new ArgumentOutOfRangeException("count"); }
95             if (String.IsNullOrEmpty(hashAlgorithm.Name)) { throw HashAlgorithmNameNullOrEmpty(); }
96
97             byte[] hash = HashData(data, offset, count, hashAlgorithm);
98             return CreateSignature(hash);
99         }
100
101         public virtual byte[] SignData(Stream data, HashAlgorithmName hashAlgorithm)
102         {
103             if (data == null) { throw new ArgumentNullException("data"); }
104             if (String.IsNullOrEmpty(hashAlgorithm.Name)) { throw HashAlgorithmNameNullOrEmpty(); }
105
106             byte[] hash = HashData(data, hashAlgorithm);
107             return CreateSignature(hash);
108         }
109
110         public bool VerifyData(byte[] data, byte[] signature, HashAlgorithmName hashAlgorithm)
111         {
112             if (data == null) { throw new ArgumentNullException("data"); }
113
114             return VerifyData(data, 0, data.Length, signature, hashAlgorithm);
115         }
116
117         public virtual bool VerifyData(byte[] data, int offset, int count, byte[] signature, HashAlgorithmName hashAlgorithm)
118         {
119             if (data == null) { throw new ArgumentNullException("data"); }
120             if (offset < 0 || offset > data.Length) { throw new ArgumentOutOfRangeException("offset"); }
121             if (count < 0 || count > data.Length - offset) { throw new ArgumentOutOfRangeException("count"); }
122             if (signature == null) { throw new ArgumentNullException("signature"); }
123             if (String.IsNullOrEmpty(hashAlgorithm.Name)) { throw HashAlgorithmNameNullOrEmpty(); }
124
125             byte[] hash = HashData(data, offset, count, hashAlgorithm);
126             return VerifySignature(hash, signature);
127         }
128
129         public virtual bool VerifyData(Stream data, byte[] signature, HashAlgorithmName hashAlgorithm)
130         {
131             if (data == null) { throw new ArgumentNullException("data"); }
132             if (signature == null) { throw new ArgumentNullException("signature"); }
133             if (String.IsNullOrEmpty(hashAlgorithm.Name)) { throw HashAlgorithmNameNullOrEmpty(); }
134
135             byte[] hash = HashData(data, hashAlgorithm);
136             return VerifySignature(hash, signature);
137         }
138
139         // We can provide a default implementation of FromXmlString because we require 
140         // every DSA implementation to implement ImportParameters
141         // All we have to do here is parse the XML.
142
143         public override void FromXmlString(String xmlString) {
144             if (xmlString == null) throw new ArgumentNullException("xmlString");
145             Contract.EndContractBlock();
146             DSAParameters dsaParams = new DSAParameters();
147             Parser p = new Parser(xmlString);
148             SecurityElement topElement = p.GetTopElement();
149
150             // P is always present
151             String pString = topElement.SearchForTextOfLocalName("P");
152             if (pString == null) {
153                 throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidFromXmlString","DSA","P"));
154             }
155             dsaParams.P = Convert.FromBase64String(Utils.DiscardWhiteSpaces(pString));
156
157             // Q is always present
158             String qString = topElement.SearchForTextOfLocalName("Q");
159             if (qString == null) {
160                 throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidFromXmlString","DSA","Q"));
161             }
162             dsaParams.Q = Convert.FromBase64String(Utils.DiscardWhiteSpaces(qString));
163
164             // G is always present
165             String gString = topElement.SearchForTextOfLocalName("G");
166             if (gString == null) {
167                 throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidFromXmlString","DSA","G"));
168             }
169             dsaParams.G = Convert.FromBase64String(Utils.DiscardWhiteSpaces(gString));
170
171             // Y is always present
172             String yString = topElement.SearchForTextOfLocalName("Y");
173             if (yString == null) {
174                 throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidFromXmlString","DSA","Y"));
175             }
176             dsaParams.Y = Convert.FromBase64String(Utils.DiscardWhiteSpaces(yString));
177
178             // J is optional
179             String jString = topElement.SearchForTextOfLocalName("J");
180             if (jString != null) dsaParams.J = Convert.FromBase64String(Utils.DiscardWhiteSpaces(jString));
181
182             // X is optional -- private key if present
183             String xString = topElement.SearchForTextOfLocalName("X");
184             if (xString != null) dsaParams.X = Convert.FromBase64String(Utils.DiscardWhiteSpaces(xString));
185
186             // Seed and PgenCounter are optional as a unit -- both present or both absent
187             String seedString = topElement.SearchForTextOfLocalName("Seed");
188             String pgenCounterString = topElement.SearchForTextOfLocalName("PgenCounter");
189             if ((seedString != null) && (pgenCounterString != null)) {
190                 dsaParams.Seed = Convert.FromBase64String(Utils.DiscardWhiteSpaces(seedString));
191                 dsaParams.Counter = Utils.ConvertByteArrayToInt(Convert.FromBase64String(Utils.DiscardWhiteSpaces(pgenCounterString)));
192             } else if ((seedString != null) || (pgenCounterString != null)) {
193                 if (seedString == null) {
194                     throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidFromXmlString","DSA","Seed"));
195                 } else {
196                     throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidFromXmlString","DSA","PgenCounter"));
197                 }
198             }
199
200             ImportParameters(dsaParams);
201         }
202
203         // We can provide a default implementation of ToXmlString because we require 
204         // every DSA implementation to implement ImportParameters
205         // If includePrivateParameters is false, this is just an XMLDSIG DSAKeyValue
206         // clause.  If includePrivateParameters is true, then we extend DSAKeyValue with 
207         // the other (private) elements.
208
209         public override String ToXmlString(bool includePrivateParameters) {
210             // From the XMLDSIG spec, RFC 3075, Section 6.4.1, a DSAKeyValue looks like this:
211             /* 
212                <element name="DSAKeyValue"> 
213                  <complexType> 
214                    <sequence>
215                      <sequence>
216                        <element name="P" type="ds:CryptoBinary"/> 
217                        <element name="Q" type="ds:CryptoBinary"/> 
218                        <element name="G" type="ds:CryptoBinary"/> 
219                        <element name="Y" type="ds:CryptoBinary"/> 
220                        <element name="J" type="ds:CryptoBinary" minOccurs="0"/> 
221                      </sequence>
222                      <sequence minOccurs="0">
223                        <element name="Seed" type="ds:CryptoBinary"/> 
224                        <element name="PgenCounter" type="ds:CryptoBinary"/> 
225                      </sequence>
226                    </sequence>
227                  </complexType>
228                </element>
229             */
230             // we extend appropriately for private component X
231             DSAParameters dsaParams = this.ExportParameters(includePrivateParameters);
232             StringBuilder sb = new StringBuilder();
233             sb.Append("<DSAKeyValue>");
234             // Add P, Q, G and Y
235             sb.Append("<P>"+Convert.ToBase64String(dsaParams.P)+"</P>");
236             sb.Append("<Q>"+Convert.ToBase64String(dsaParams.Q)+"</Q>");
237             sb.Append("<G>"+Convert.ToBase64String(dsaParams.G)+"</G>");
238             sb.Append("<Y>"+Convert.ToBase64String(dsaParams.Y)+"</Y>");
239             // Add optional components if present
240             if (dsaParams.J != null) {
241                 sb.Append("<J>"+Convert.ToBase64String(dsaParams.J)+"</J>");
242             }
243             if ((dsaParams.Seed != null)) {  // note we assume counter is correct if Seed is present
244                 sb.Append("<Seed>"+Convert.ToBase64String(dsaParams.Seed)+"</Seed>");
245                 sb.Append("<PgenCounter>"+Convert.ToBase64String(Utils.ConvertIntToByteArray(dsaParams.Counter))+"</PgenCounter>");
246             }
247
248             if (includePrivateParameters) {
249                 // Add the private component
250                 sb.Append("<X>"+Convert.ToBase64String(dsaParams.X)+"</X>");
251             } 
252             sb.Append("</DSAKeyValue>");
253             return(sb.ToString());
254         }
255
256         abstract public DSAParameters ExportParameters(bool includePrivateParameters);
257
258         abstract public void ImportParameters(DSAParameters parameters);
259
260         private static Exception DerivedClassMustOverride()
261         {
262             return new NotImplementedException(Environment.GetResourceString("NotSupported_SubclassOverride"));
263         }
264
265         internal static Exception HashAlgorithmNameNullOrEmpty()
266         {
267             return new ArgumentException(Environment.GetResourceString("Cryptography_HashAlgorithmNameNullOrEmpty"), "hashAlgorithm");
268         }
269     }
270 }