New tests.
[mono.git] / mcs / class / corlib / Mono.Security.Authenticode / AuthenticodeBase.cs
1 //
2 // AuthenticodeBase.cs: Authenticode signature base class
3 //
4 // Author:
5 //      Sebastien Pouliot <sebastien@ximian.com>
6 //
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004, 2006 Novell, Inc (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 #if !MOONLIGHT
31
32 using System;
33 using System.IO;
34 using System.Security.Cryptography;
35
36 namespace Mono.Security.Authenticode {
37
38         // References:
39         // a.   http://www.cs.auckland.ac.nz/~pgut001/pubs/authenticode.txt
40
41 #if INSIDE_CORLIB
42         internal
43 #else
44         public
45 #endif
46         enum Authority {
47                 Individual,
48                 Commercial,
49                 Maximum
50         }
51
52 #if INSIDE_CORLIB
53         internal
54 #else
55         public
56 #endif
57         class AuthenticodeBase {
58
59                 public const string spcIndirectDataContext = "1.3.6.1.4.1.311.2.1.4";
60
61                 private byte[] fileblock;
62                 private FileStream fs;
63                 private int blockNo;
64                 private int blockLength;
65                 private int peOffset;
66                 private int dirSecurityOffset;
67                 private int dirSecuritySize;
68                 private int coffSymbolTableOffset;
69
70                 public AuthenticodeBase ()
71                 {
72                         fileblock = new byte [4096];
73                 }
74
75                 internal int PEOffset {
76                         get {
77                                 if (blockNo < 1)
78                                         ReadFirstBlock ();
79                                 return peOffset;
80                         }
81                 }
82
83                 internal int CoffSymbolTableOffset {
84                         get {
85                                 if (blockNo < 1)
86                                         ReadFirstBlock ();
87                                 return coffSymbolTableOffset;
88                         }
89                 }
90
91                 internal int SecurityOffset {
92                         get {
93                                 if (blockNo < 1)
94                                         ReadFirstBlock ();
95                                 return dirSecurityOffset;
96                         }
97                 }
98
99                 internal void Open (string filename)
100                 {
101                         if (fs != null)
102                                 Close ();
103                         fs = new FileStream (filename, FileMode.Open, FileAccess.Read, FileShare.Read);
104                 }
105
106                 internal void Close ()
107                 {
108                         if (fs != null) {
109                                 fs.Close ();
110                                 fs = null;
111                                 blockNo = 0;
112                         }
113                 }
114
115                 internal bool ReadFirstBlock ()
116                 {
117                         if (fs == null)
118                                 return false;
119
120                         fs.Position = 0;
121                         // read first block - it will include (100% sure) 
122                         // the MZ header and (99.9% sure) the PE header
123                         blockLength = fs.Read (fileblock, 0, fileblock.Length);
124                         blockNo = 1;
125                         if (blockLength < 64)
126                                 return false;   // invalid PE file
127
128                         // 1. Validate the MZ header informations
129                         // 1.1. Check for magic MZ at start of header
130                         if (BitConverterLE.ToUInt16 (fileblock, 0) != 0x5A4D)
131                                 return false;
132
133                         // 1.2. Find the offset of the PE header
134                         peOffset = BitConverterLE.ToInt32 (fileblock, 60);
135                         if (peOffset > fileblock.Length) {
136                                 // just in case (0.1%) this can actually happen
137                                 string msg = String.Format (Locale.GetText (
138                                         "Header size too big (> {0} bytes)."),
139                                         fileblock.Length);
140                                 throw new NotSupportedException (msg);
141                         }
142                         if (peOffset > fs.Length)
143                                 return false;
144
145                         // 2. Read between DOS header and first part of PE header
146                         // 2.1. Check for magic PE at start of header
147                         //      PE - NT header ('P' 'E' 0x00 0x00)
148                         if (BitConverterLE.ToUInt32 (fileblock, peOffset) != 0x4550)
149                                 return false;
150
151                         // 2.2. Locate IMAGE_DIRECTORY_ENTRY_SECURITY (offset and size)
152                         dirSecurityOffset = BitConverterLE.ToInt32 (fileblock, peOffset + 152);
153                         dirSecuritySize = BitConverterLE.ToInt32 (fileblock, peOffset + 156);
154
155                         // COFF symbol tables are deprecated - we'll strip them if we see them!
156                         // (otherwise the signature won't work on MS and we don't want to support COFF for that)
157                         coffSymbolTableOffset = BitConverterLE.ToInt32 (fileblock, peOffset + 12);
158
159                         return true;
160                 }
161
162                 internal byte[] GetSecurityEntry () 
163                 {
164                         if (blockNo < 1)
165                                 ReadFirstBlock ();
166
167                         if (dirSecuritySize > 8) {
168                                 // remove header from size (not ASN.1 based)
169                                 byte[] secEntry = new byte [dirSecuritySize - 8];
170                                 // position after header and read entry
171                                 fs.Position = dirSecurityOffset + 8;
172                                 fs.Read (secEntry, 0, secEntry.Length);
173                                 return secEntry;
174                         }
175                         return null;
176                 }
177
178                 internal byte[] GetHash (HashAlgorithm hash)
179                 {
180                         if (blockNo < 1)
181                                 ReadFirstBlock ();
182                         fs.Position = blockLength;
183
184                         // hash the rest of the file
185                         long n;
186                         int addsize = 0;
187                         // minus any authenticode signature (with 8 bytes header)
188                         if (dirSecurityOffset > 0) {
189                                 // it is also possible that the signature block 
190                                 // starts within the block in memory (small EXE)
191                                 if (dirSecurityOffset < blockLength) {
192                                         blockLength = dirSecurityOffset;
193                                         n = 0;
194                                 } else {
195                                         n = dirSecurityOffset - blockLength;
196                                 }
197                         } else if (coffSymbolTableOffset > 0) {
198                                 fileblock[PEOffset + 12] = 0;
199                                 fileblock[PEOffset + 13] = 0;
200                                 fileblock[PEOffset + 14] = 0;
201                                 fileblock[PEOffset + 15] = 0;
202                                 fileblock[PEOffset + 16] = 0;
203                                 fileblock[PEOffset + 17] = 0;
204                                 fileblock[PEOffset + 18] = 0;
205                                 fileblock[PEOffset + 19] = 0;
206                                 // it is also possible that the signature block 
207                                 // starts within the block in memory (small EXE)
208                                 if (coffSymbolTableOffset < blockLength) {
209                                         blockLength = coffSymbolTableOffset;
210                                         n = 0;
211                                 } else {
212                                         n = coffSymbolTableOffset - blockLength;
213                                 }
214                         } else {
215                                 addsize = (int) (fs.Length & 7);
216                                 if (addsize > 0)
217                                         addsize = 8 - addsize;
218                                 
219                                 n = fs.Length - blockLength;
220                         }
221
222                         // Authenticode(r) gymnastics
223                         // Hash from (generally) 0 to 215 (216 bytes)
224                         int pe = peOffset + 88;
225                         hash.TransformBlock (fileblock, 0, pe, fileblock, 0);
226                         // then skip 4 for checksum
227                         pe += 4;
228                         // Continue hashing from (generally) 220 to 279 (60 bytes)
229                         hash.TransformBlock (fileblock, pe, 60, fileblock, pe);
230                         // then skip 8 bytes for IMAGE_DIRECTORY_ENTRY_SECURITY
231                         pe += 68;
232
233                         // everything is present so start the hashing
234                         if (n == 0) {
235                                 // hash the (only) block
236                                 hash.TransformFinalBlock (fileblock, pe, blockLength - pe);
237                         }
238                         else {
239                                 // hash the last part of the first (already in memory) block
240                                 hash.TransformBlock (fileblock, pe, blockLength - pe, fileblock, pe);
241
242                                 // hash by blocks of 4096 bytes
243                                 long blocks = (n >> 12);
244                                 int remainder = (int)(n - (blocks << 12));
245                                 if (remainder == 0) {
246                                         blocks--;
247                                         remainder = 4096;
248                                 }
249                                 // blocks
250                                 while (blocks-- > 0) {
251                                         fs.Read (fileblock, 0, fileblock.Length);
252                                         hash.TransformBlock (fileblock, 0, fileblock.Length, fileblock, 0);
253                                 }
254                                 // remainder
255                                 if (fs.Read (fileblock, 0, remainder) != remainder)
256                                         return null;
257
258                                 if (addsize > 0) {
259                                         hash.TransformBlock (fileblock, 0, remainder, fileblock, 0);
260                                         hash.TransformFinalBlock (new byte [addsize], 0, addsize);
261                                 } else {
262                                         hash.TransformFinalBlock (fileblock, 0, remainder);
263                                 }
264                         }
265                         return hash.Hash;
266                 }
267
268                 // for compatibility only
269                 protected byte[] HashFile (string fileName, string hashName) 
270                 {
271                         try {
272                                 Open (fileName);
273                                 HashAlgorithm hash = HashAlgorithm.Create (hashName);
274                                 byte[] result = GetHash (hash);
275                                 Close ();
276                                 return result;
277                         }
278                         catch {
279                                 return null;
280                         }
281                 }
282         }
283 }
284
285 #endif
286