Merge pull request #645 from knocte/nitpicks
[mono-project.git] / mcs / class / corlib / Mono.Security.Authenticode / AuthenticodeBase.cs
blobbee47e7b045ec6e23f4de258b4fde3be29c16ba7
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.
30 using System;
31 using System.IO;
32 using System.Security.Cryptography;
34 namespace Mono.Security.Authenticode {
36 // References:
37 // a. http://www.cs.auckland.ac.nz/~pgut001/pubs/authenticode.txt
39 #if INSIDE_CORLIB
40 internal
41 #else
42 public
43 #endif
44 enum Authority {
45 Individual,
46 Commercial,
47 Maximum
50 #if INSIDE_CORLIB
51 internal
52 #else
53 public
54 #endif
55 class AuthenticodeBase {
57 public const string spcIndirectDataContext = "1.3.6.1.4.1.311.2.1.4";
59 private byte[] fileblock;
60 private FileStream fs;
61 private int blockNo;
62 private int blockLength;
63 private int peOffset;
64 private int dirSecurityOffset;
65 private int dirSecuritySize;
66 private int coffSymbolTableOffset;
68 public AuthenticodeBase ()
70 fileblock = new byte [4096];
73 internal int PEOffset {
74 get {
75 if (blockNo < 1)
76 ReadFirstBlock ();
77 return peOffset;
81 internal int CoffSymbolTableOffset {
82 get {
83 if (blockNo < 1)
84 ReadFirstBlock ();
85 return coffSymbolTableOffset;
89 internal int SecurityOffset {
90 get {
91 if (blockNo < 1)
92 ReadFirstBlock ();
93 return dirSecurityOffset;
97 internal void Open (string filename)
99 if (fs != null)
100 Close ();
101 fs = new FileStream (filename, FileMode.Open, FileAccess.Read, FileShare.Read);
104 internal void Close ()
106 if (fs != null) {
107 fs.Close ();
108 fs = null;
109 blockNo = 0;
113 internal bool ReadFirstBlock ()
115 if (fs == null)
116 return false;
118 fs.Position = 0;
119 // read first block - it will include (100% sure)
120 // the MZ header and (99.9% sure) the PE header
121 blockLength = fs.Read (fileblock, 0, fileblock.Length);
122 blockNo = 1;
123 if (blockLength < 64)
124 return false; // invalid PE file
126 // 1. Validate the MZ header informations
127 // 1.1. Check for magic MZ at start of header
128 if (BitConverterLE.ToUInt16 (fileblock, 0) != 0x5A4D)
129 return false;
131 // 1.2. Find the offset of the PE header
132 peOffset = BitConverterLE.ToInt32 (fileblock, 60);
133 if (peOffset > fileblock.Length) {
134 // just in case (0.1%) this can actually happen
135 string msg = String.Format (Locale.GetText (
136 "Header size too big (> {0} bytes)."),
137 fileblock.Length);
138 throw new NotSupportedException (msg);
140 if (peOffset > fs.Length)
141 return false;
143 // 2. Read between DOS header and first part of PE header
144 // 2.1. Check for magic PE at start of header
145 // PE - NT header ('P' 'E' 0x00 0x00)
146 if (BitConverterLE.ToUInt32 (fileblock, peOffset) != 0x4550)
147 return false;
149 // 2.2. Locate IMAGE_DIRECTORY_ENTRY_SECURITY (offset and size)
150 dirSecurityOffset = BitConverterLE.ToInt32 (fileblock, peOffset + 152);
151 dirSecuritySize = BitConverterLE.ToInt32 (fileblock, peOffset + 156);
153 // COFF symbol tables are deprecated - we'll strip them if we see them!
154 // (otherwise the signature won't work on MS and we don't want to support COFF for that)
155 coffSymbolTableOffset = BitConverterLE.ToInt32 (fileblock, peOffset + 12);
157 return true;
160 internal byte[] GetSecurityEntry ()
162 if (blockNo < 1)
163 ReadFirstBlock ();
165 if (dirSecuritySize > 8) {
166 // remove header from size (not ASN.1 based)
167 byte[] secEntry = new byte [dirSecuritySize - 8];
168 // position after header and read entry
169 fs.Position = dirSecurityOffset + 8;
170 fs.Read (secEntry, 0, secEntry.Length);
171 return secEntry;
173 return null;
176 internal byte[] GetHash (HashAlgorithm hash)
178 if (blockNo < 1)
179 ReadFirstBlock ();
180 fs.Position = blockLength;
182 // hash the rest of the file
183 long n;
184 int addsize = 0;
185 // minus any authenticode signature (with 8 bytes header)
186 if (dirSecurityOffset > 0) {
187 // it is also possible that the signature block
188 // starts within the block in memory (small EXE)
189 if (dirSecurityOffset < blockLength) {
190 blockLength = dirSecurityOffset;
191 n = 0;
192 } else {
193 n = dirSecurityOffset - blockLength;
195 } else if (coffSymbolTableOffset > 0) {
196 fileblock[PEOffset + 12] = 0;
197 fileblock[PEOffset + 13] = 0;
198 fileblock[PEOffset + 14] = 0;
199 fileblock[PEOffset + 15] = 0;
200 fileblock[PEOffset + 16] = 0;
201 fileblock[PEOffset + 17] = 0;
202 fileblock[PEOffset + 18] = 0;
203 fileblock[PEOffset + 19] = 0;
204 // it is also possible that the signature block
205 // starts within the block in memory (small EXE)
206 if (coffSymbolTableOffset < blockLength) {
207 blockLength = coffSymbolTableOffset;
208 n = 0;
209 } else {
210 n = coffSymbolTableOffset - blockLength;
212 } else {
213 addsize = (int) (fs.Length & 7);
214 if (addsize > 0)
215 addsize = 8 - addsize;
217 n = fs.Length - blockLength;
220 // Authenticode(r) gymnastics
221 // Hash from (generally) 0 to 215 (216 bytes)
222 int pe = peOffset + 88;
223 hash.TransformBlock (fileblock, 0, pe, fileblock, 0);
224 // then skip 4 for checksum
225 pe += 4;
226 // Continue hashing from (generally) 220 to 279 (60 bytes)
227 hash.TransformBlock (fileblock, pe, 60, fileblock, pe);
228 // then skip 8 bytes for IMAGE_DIRECTORY_ENTRY_SECURITY
229 pe += 68;
231 // everything is present so start the hashing
232 if (n == 0) {
233 // hash the (only) block
234 hash.TransformFinalBlock (fileblock, pe, blockLength - pe);
236 else {
237 // hash the last part of the first (already in memory) block
238 hash.TransformBlock (fileblock, pe, blockLength - pe, fileblock, pe);
240 // hash by blocks of 4096 bytes
241 long blocks = (n >> 12);
242 int remainder = (int)(n - (blocks << 12));
243 if (remainder == 0) {
244 blocks--;
245 remainder = 4096;
247 // blocks
248 while (blocks-- > 0) {
249 fs.Read (fileblock, 0, fileblock.Length);
250 hash.TransformBlock (fileblock, 0, fileblock.Length, fileblock, 0);
252 // remainder
253 if (fs.Read (fileblock, 0, remainder) != remainder)
254 return null;
256 if (addsize > 0) {
257 hash.TransformBlock (fileblock, 0, remainder, fileblock, 0);
258 hash.TransformFinalBlock (new byte [addsize], 0, addsize);
259 } else {
260 hash.TransformFinalBlock (fileblock, 0, remainder);
263 return hash.Hash;
266 // for compatibility only
267 protected byte[] HashFile (string fileName, string hashName)
269 try {
270 Open (fileName);
271 HashAlgorithm hash = HashAlgorithm.Create (hashName);
272 byte[] result = GetHash (hash);
273 Close ();
274 return result;
276 catch {
277 return null;