2 // AuthenticodeBase.cs: Authenticode signature base class
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
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.
32 using System.Security.Cryptography;
34 namespace Mono.Security.Authenticode {
37 // a. http://www.cs.auckland.ac.nz/~pgut001/pubs/authenticode.txt
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;
62 private int blockLength;
64 private int dirSecurityOffset;
65 private int dirSecuritySize;
67 public AuthenticodeBase ()
69 fileblock = new byte [4096];
72 internal void Open (string filename)
76 fs = new FileStream (filename, FileMode.Open, FileAccess.Read, FileShare.Read);
79 internal void Close ()
88 internal bool ReadFirstBlock ()
94 // read first block - it will include (100% sure)
95 // the MZ header and (99.9% sure) the PE header
96 blockLength = fs.Read (fileblock, 0, fileblock.Length);
99 return false; // invalid PE file
101 // 1. Validate the MZ header informations
102 // 1.1. Check for magic MZ at start of header
103 if (BitConverterLE.ToUInt16 (fileblock, 0) != 0x5A4D)
106 // 1.2. Find the offset of the PE header
107 peOffset = BitConverterLE.ToInt32 (fileblock, 60);
108 if (peOffset > fileblock.Length) {
109 // just in case (0.1%) this can actually happen
110 string msg = String.Format (Locale.GetText (
111 "Header size too big (> {0} bytes)."),
113 throw new NotSupportedException (msg);
115 if (peOffset > fs.Length)
118 // 2. Read between DOS header and first part of PE header
119 // 2.1. Check for magic PE at start of header
120 if (BitConverterLE.ToUInt16 (fileblock, peOffset) != 0x4550)
123 // 2.2. Locate IMAGE_DIRECTORY_ENTRY_SECURITY (offset and size)
124 dirSecurityOffset = BitConverterLE.ToInt32 (fileblock, peOffset + 152);
125 dirSecuritySize = BitConverterLE.ToInt32 (fileblock, peOffset + 156);
130 internal byte[] GetSecurityEntry ()
135 if (dirSecuritySize > 8) {
136 // remove header from size (not ASN.1 based)
137 byte[] secEntry = new byte [dirSecuritySize - 8];
138 // position after header and read entry
139 fs.Position = dirSecurityOffset + 8;
140 fs.Read (secEntry, 0, secEntry.Length);
146 // returns null if the file isn't signed
147 internal byte[] GetHash (HashAlgorithm hash)
151 fs.Position = blockLength;
153 // hash the rest of the file
154 long n = fs.Length - blockLength;
155 // minus any authenticode signature (with 8 bytes header)
156 if (dirSecurityOffset > 0) {
157 // it is also possible that the signature block
158 // starts within the block in memory (small EXE)
159 if (dirSecurityOffset < blockLength) {
160 blockLength = dirSecurityOffset;
164 n -= (dirSecuritySize);
167 // Authenticode(r) gymnastics
168 // Hash from (generally) 0 to 215 (216 bytes)
169 int pe = peOffset + 88;
170 hash.TransformBlock (fileblock, 0, pe, fileblock, 0);
171 // then skip 4 for checksum
173 // Continue hashing from (generally) 220 to 279 (60 bytes)
174 hash.TransformBlock (fileblock, pe, 60, fileblock, pe);
175 // then skip 8 bytes for IMAGE_DIRECTORY_ENTRY_SECURITY
178 // everything is present so start the hashing
180 // hash the (only) block
181 hash.TransformFinalBlock (fileblock, pe, blockLength - pe);
184 // hash the last part of the first (already in memory) block
185 hash.TransformBlock (fileblock, pe, blockLength - pe, fileblock, pe);
187 // hash by blocks of 4096 bytes
188 long blocks = (n >> 12);
189 int remainder = (int)(n - (blocks << 12));
190 if (remainder == 0) {
195 while (blocks-- > 0) {
196 fs.Read (fileblock, 0, fileblock.Length);
197 hash.TransformBlock (fileblock, 0, fileblock.Length, fileblock, 0);
200 if (fs.Read (fileblock, 0, remainder) != remainder)
202 hash.TransformFinalBlock (fileblock, 0, remainder);
207 // for compatibility only
208 protected byte[] HashFile (string fileName, string hashName)
212 HashAlgorithm hash = HashAlgorithm.Create (hashName);
213 byte[] result = GetHash (hash);