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, 2006 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
;
66 private int coffSymbolTableOffset
;
68 public AuthenticodeBase ()
70 fileblock
= new byte [4096];
73 internal int PEOffset
{
81 internal int CoffSymbolTableOffset
{
85 return coffSymbolTableOffset
;
89 internal int SecurityOffset
{
93 return dirSecurityOffset
;
97 internal void Open (string filename
)
101 fs
= new FileStream (filename
, FileMode
.Open
, FileAccess
.Read
, FileShare
.Read
);
104 internal void Close ()
113 internal bool ReadFirstBlock ()
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
);
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)
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)."),
138 throw new NotSupportedException (msg
);
140 if (peOffset
> fs
.Length
)
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)
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);
160 internal byte[] GetSecurityEntry ()
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
);
176 internal byte[] GetHash (HashAlgorithm hash
)
180 fs
.Position
= blockLength
;
182 // hash the rest of the file
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
;
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
;
210 n
= coffSymbolTableOffset
- blockLength
;
213 addsize
= (int) (fs
.Length
& 7);
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
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
231 // everything is present so start the hashing
233 // hash the (only) block
234 hash
.TransformFinalBlock (fileblock
, pe
, blockLength
- pe
);
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) {
248 while (blocks
-- > 0) {
249 fs
.Read (fileblock
, 0, fileblock
.Length
);
250 hash
.TransformBlock (fileblock
, 0, fileblock
.Length
, fileblock
, 0);
253 if (fs
.Read (fileblock
, 0, remainder
) != remainder
)
257 hash
.TransformBlock (fileblock
, 0, remainder
, fileblock
, 0);
258 hash
.TransformFinalBlock (new byte [addsize
], 0, addsize
);
260 hash
.TransformFinalBlock (fileblock
, 0, remainder
);
266 // for compatibility only
267 protected byte[] HashFile (string fileName
, string hashName
)
271 HashAlgorithm hash
= HashAlgorithm
.Create (hashName
);
272 byte[] result
= GetHash (hash
);