Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'mcs/class/Mono.Security/Mono.Security.Authenticode')
-rwxr-xr-xmcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeBase.cs184
-rwxr-xr-xmcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeDeformatter.cs123
-rwxr-xr-xmcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeFormatter.cs11
-rw-r--r--mcs/class/Mono.Security/Mono.Security.Authenticode/ChangeLog6
4 files changed, 235 insertions, 89 deletions
diff --git a/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeBase.cs b/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeBase.cs
index 63fbab93a8c..c2d609ecec2 100755
--- a/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeBase.cs
+++ b/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeBase.cs
@@ -5,9 +5,7 @@
// Sebastien Pouliot <sebastien@ximian.com>
//
// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
-// (C) 2004 Novell (http://www.novell.com)
-//
-
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -58,63 +56,167 @@ namespace Mono.Security.Authenticode {
public const string spcIndirectDataContext = "1.3.6.1.4.1.311.2.1.4";
- internal byte[] rawData;
+ private byte[] fileblock;
+ private FileStream fs;
+ private int blockNo;
+ private int blockLength;
+ private int peOffset;
+ private int dirSecurityOffset;
+ private int dirSecuritySize;
public AuthenticodeBase ()
{
+ fileblock = new byte [4096];
}
- protected byte[] HashFile (string fileName, string hashName)
+ internal void Open (string filename)
{
- FileStream fs = new FileStream (fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
- byte[] file = new byte [fs.Length];
- fs.Read (file, 0, file.Length);
- fs.Close ();
+ if (fs != null)
+ Close ();
+ fs = new FileStream (filename, FileMode.Open, FileAccess.Read, FileShare.Read);
+ }
- // MZ - DOS header
- if (BitConverterLE.ToUInt16 (file, 0) != 0x5A4D)
- return null;
+ internal void Close ()
+ {
+ if (fs != null) {
+ fs.Close ();
+ fs = null;
+ blockNo = 0;
+ }
+ }
- // find offset of PE header
- int peOffset = BitConverterLE.ToInt32 (file, 60);
- if (peOffset > file.Length)
- return null;
+ internal bool ReadFirstBlock ()
+ {
+ if (fs == null)
+ return false;
+
+ fs.Position = 0;
+ // read first block - it will include (100% sure)
+ // the MZ header and (99.9% sure) the PE header
+ blockLength = fs.Read (fileblock, 0, fileblock.Length);
+ blockNo = 1;
+ if (blockLength < 64)
+ return false; // invalid PE file
+
+ // 1. Validate the MZ header informations
+ // 1.1. Check for magic MZ at start of header
+ if (BitConverterLE.ToUInt16 (fileblock, 0) != 0x5A4D)
+ return false;
+
+ // 1.2. Find the offset of the PE header
+ peOffset = BitConverterLE.ToInt32 (fileblock, 60);
+ if (peOffset > fileblock.Length) {
+ // just in case (0.1%) this can actually happen
+ string msg = String.Format (Locale.GetText (
+ "Header size too big (> {0} bytes)."),
+ fileblock.Length);
+ throw new NotSupportedException (msg);
+ }
+ if (peOffset > fs.Length)
+ return false;
- // PE - NT header
- if (BitConverterLE.ToUInt16 (file, peOffset) != 0x4550)
- return null;
+ // 2. Read between DOS header and first part of PE header
+ // 2.1. Check for magic PE at start of header
+ if (BitConverterLE.ToUInt16 (fileblock, peOffset) != 0x4550)
+ return false;
- // IMAGE_DIRECTORY_ENTRY_SECURITY
- int dirSecurityOffset = BitConverterLE.ToInt32 (file, peOffset + 152);
- int dirSecuritySize = BitConverterLE.ToInt32 (file, peOffset + 156);
+ // 2.2. Locate IMAGE_DIRECTORY_ENTRY_SECURITY (offset and size)
+ dirSecurityOffset = BitConverterLE.ToInt32 (fileblock, peOffset + 152);
+ dirSecuritySize = BitConverterLE.ToInt32 (fileblock, peOffset + 156);
+
+ return true;
+ }
+
+ internal byte[] GetSecurityEntry ()
+ {
+ if (blockNo < 1)
+ ReadFirstBlock ();
if (dirSecuritySize > 8) {
- rawData = new byte [dirSecuritySize - 8];
- Buffer.BlockCopy (file, dirSecurityOffset + 8, rawData, 0, rawData.Length);
-/* DEBUG
- FileStream debug = new FileStream (fileName + ".sig", FileMode.Create, FileAccess.Write);
- debug.Write (rawData, 0, rawData.Length);
- debug.Close ();*/
+ // remove header from size (not ASN.1 based)
+ byte[] secEntry = new byte [dirSecuritySize - 8];
+ // position after header and read entry
+ fs.Position = dirSecurityOffset + 8;
+ fs.Read (secEntry, 0, secEntry.Length);
+ return secEntry;
+ }
+ return null;
+ }
+
+ // returns null if the file isn't signed
+ internal byte[] GetHash (HashAlgorithm hash)
+ {
+ if (blockNo < 1)
+ ReadFirstBlock ();
+ fs.Position = blockLength;
+
+ // hash the rest of the file
+ long n = fs.Length - blockLength;
+ // minus any authenticode signature (with 8 bytes header)
+ if (dirSecurityOffset > 0) {
+ // it is also possible that the signature block
+ // starts within the block in memory (small EXE)
+ if (dirSecurityOffset < blockLength) {
+ blockLength = dirSecurityOffset;
+ n = 0;
+ }
+ else
+ n -= (dirSecuritySize);
}
- else
- rawData = null;
- HashAlgorithm hash = HashAlgorithm.Create (hashName);
- // 0 to 215 (216) then skip 4 (checksum)
+ // Authenticode(r) gymnastics
+ // Hash from (generally) 0 to 215 (216 bytes)
int pe = peOffset + 88;
- hash.TransformBlock (file, 0, pe, file, 0);
+ hash.TransformBlock (fileblock, 0, pe, fileblock, 0);
+ // then skip 4 for checksum
pe += 4;
- // 220 to 279 (60) then skip 8 (IMAGE_DIRECTORY_ENTRY_SECURITY)
- hash.TransformBlock (file, pe, 60, file, pe);
+ // Continue hashing from (generally) 220 to 279 (60 bytes)
+ hash.TransformBlock (fileblock, pe, 60, fileblock, pe);
+ // then skip 8 bytes for IMAGE_DIRECTORY_ENTRY_SECURITY
pe += 68;
- // 288 to end of file
- int n = file.Length - pe;
- // minus any authenticode signature (with 8 bytes header)
- if (dirSecurityOffset != 0)
- n -= (dirSecuritySize);
- hash.TransformFinalBlock (file, pe, n);
+ // everything is present so start the hashing
+ if (n == 0) {
+ // hash the (only) block
+ hash.TransformFinalBlock (fileblock, pe, blockLength - pe);
+ }
+ else {
+ // hash the last part of the first (already in memory) block
+ hash.TransformBlock (fileblock, pe, blockLength - pe, fileblock, 0);
+
+ // hash by blocks of 4096 bytes
+ long blocks = (n >> 12);
+ int remainder = (int)(n - (blocks << 12));
+ if (remainder == 0) {
+ blocks--;
+ remainder = 4096;
+ }
+ // blocks
+ while (blocks-- > 0) {
+ fs.Read (fileblock, 0, fileblock.Length);
+ hash.TransformBlock (fileblock, 0, fileblock.Length, fileblock, 0);
+ }
+ // remainder
+ if (fs.Read (fileblock, 0, remainder) != remainder)
+ return null;
+ hash.TransformFinalBlock (fileblock, 0, remainder);
+ }
return hash.Hash;
}
+
+ // for compatibility only
+ protected byte[] HashFile (string fileName, string hashName)
+ {
+ try {
+ Open (fileName);
+ HashAlgorithm hash = HashAlgorithm.Create (hashName);
+ byte[] result = GetHash (hash);
+ Close ();
+ return result;
+ }
+ catch {
+ return null;
+ }
+ }
}
}
diff --git a/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeDeformatter.cs b/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeDeformatter.cs
index 4d30ea38aab..ea0bd30e242 100755
--- a/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeDeformatter.cs
+++ b/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeDeformatter.cs
@@ -5,9 +5,7 @@
// Sebastien Pouliot <sebastien@ximian.com>
//
// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
-// (C) 2004 Novell (http://www.novell.com)
-//
-
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -56,6 +54,9 @@ namespace Mono.Security.Authenticode {
private DateTime timestamp;
private X509Certificate signingCertificate;
private int reason;
+ private bool trustedRoot;
+ private bool trustedTimestampRoot;
+ private byte[] entry;
private X509Chain signerChain;
private X509Chain timestampChain;
@@ -69,17 +70,20 @@ namespace Mono.Security.Authenticode {
public AuthenticodeDeformatter (string fileName) : this ()
{
- if (!CheckSignature (fileName)) {
- // invalid or no signature
- if (signedHash != null)
- throw new COMException ("Invalid signature");
- // no exception is thrown when there's no signature in the PE file
- }
+ FileName = fileName;
}
public string FileName {
get { return filename; }
- set { CheckSignature (value); }
+ set {
+ Reset ();
+ try {
+ CheckSignature (value);
+ }
+ catch {
+ reason = 1;
+ }
+ }
}
public byte[] Hash {
@@ -100,7 +104,7 @@ namespace Mono.Security.Authenticode {
public bool IsTrusted ()
{
- if (rawData == null) {
+ if (entry == null) {
reason = 1;
return false;
}
@@ -110,13 +114,13 @@ namespace Mono.Security.Authenticode {
return false;
}
- if (signerChain.Root == null) {
+ if ((signerChain.Root == null) || !trustedRoot) {
reason = 6;
return false;
}
if (timestamp != DateTime.MinValue) {
- if (timestampChain.Root == null) {
+ if ((timestampChain.Root == null) || !trustedTimestampRoot) {
reason = 6;
return false;
}
@@ -138,7 +142,11 @@ namespace Mono.Security.Authenticode {
}
public byte[] Signature {
- get { return (byte[]) rawData.Clone (); }
+ get {
+ if (entry == null)
+ return null;
+ return (byte[]) entry.Clone ();
+ }
}
public DateTime Timestamp {
@@ -156,43 +164,58 @@ namespace Mono.Security.Authenticode {
private bool CheckSignature (string fileName)
{
filename = fileName;
-
- // by default we try with MD5
- string hashName = "MD5";
- // compare the signature's hash with the hash of the file
- hash = HashFile (filename, hashName);
-
- // is a signature present ?
- if (rawData == null)
+ base.Open (filename);
+ entry = base.GetSecurityEntry ();
+ if (entry == null) {
+ // no signature is present
+ reason = 1;
+ base.Close ();
return false;
+ }
- PKCS7.ContentInfo ci = new PKCS7.ContentInfo (rawData);
- if (ci.ContentType != PKCS7.Oid.signedData)
+ PKCS7.ContentInfo ci = new PKCS7.ContentInfo (entry);
+ if (ci.ContentType != PKCS7.Oid.signedData) {
+ base.Close ();
return false;
+ }
PKCS7.SignedData sd = new PKCS7.SignedData (ci.Content);
- if (sd.ContentInfo.ContentType != spcIndirectDataContext)
+ if (sd.ContentInfo.ContentType != spcIndirectDataContext) {
+ base.Close ();
return false;
+ }
coll = sd.Certificates;
ASN1 spc = sd.ContentInfo.Content;
signedHash = spc [0][1][1];
- if (signedHash.Length == 20) {
- // seems to be SHA-1, restart hashing
- hashName = "SHA1";
- hash = HashFile (filename, hashName);
+
+ HashAlgorithm ha = null;
+ switch (signedHash.Length) {
+ case 16:
+ ha = HashAlgorithm.Create ("MD5");
+ hash = GetHash (ha);
+ break;
+ case 20:
+ ha = HashAlgorithm.Create ("SHA1");
+ hash = GetHash (ha);
+ break;
+ default:
+ reason = 5;
+ base.Close ();
+ return false;
}
+ base.Close ();
if (!signedHash.CompareValue (hash))
return false;
// messageDigest is a hash of spcIndirectDataContext (which includes the file hash)
byte[] spcIDC = spc [0].Value;
- HashAlgorithm ha = HashAlgorithm.Create (hashName);
+ ha.Initialize (); // re-using hash instance
byte[] messageDigest = ha.ComputeHash (spcIDC);
- return VerifySignature (sd, messageDigest, hashName);
+ return VerifySignature (sd, messageDigest, ha);
}
private bool CompareIssuerSerial (string issuer, byte[] serial, X509Certificate x509)
@@ -212,7 +235,7 @@ namespace Mono.Security.Authenticode {
}
//private bool VerifySignature (ASN1 cs, byte[] calculatedMessageDigest, string hashName)
- private bool VerifySignature (PKCS7.SignedData sd, byte[] calculatedMessageDigest, string hashName)
+ private bool VerifySignature (PKCS7.SignedData sd, byte[] calculatedMessageDigest, HashAlgorithm ha)
{
string contentType = null;
ASN1 messageDigest = null;
@@ -241,7 +264,7 @@ namespace Mono.Security.Authenticode {
case "1.3.6.1.4.1.311.2.1.12":
// spcSpOpusInfo (Microsoft code signing)
try {
- spcSpOpusInfo = System.Text.Encoding.UTF8.GetString (attr[1][0][1][0].Value);
+ spcSpOpusInfo = System.Text.Encoding.UTF8.GetString (attr[1][0][0][0].Value);
}
catch (NullReferenceException) {
spcSpOpusInfo = null;
@@ -261,13 +284,13 @@ namespace Mono.Security.Authenticode {
return false;
// verify signature
- string hashOID = CryptoConfig.MapNameToOID (hashName);
+ string hashOID = CryptoConfig.MapNameToOID (ha.ToString ());
// change to SET OF (not [0]) as per PKCS #7 1.5
ASN1 aa = new ASN1 (0x31);
foreach (ASN1 a in sd.SignerInfo.AuthenticatedAttributes)
aa.Add (a);
- HashAlgorithm ha = HashAlgorithm.Create (hashName);
+ ha.Initialize ();
byte[] p7hash = ha.ComputeHash (aa.GetBytes ());
byte[] signature = sd.SignerInfo.Signature;
@@ -281,10 +304,9 @@ namespace Mono.Security.Authenticode {
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider) x509.RSA;
if (rsa.VerifyHash (p7hash, hashOID, signature)) {
signerChain.LoadCertificates (coll);
- if (signerChain.Build (x509))
- signingCertificate = x509;
- else
- return false;
+ trustedRoot = signerChain.Build (x509);
+ signingCertificate = x509;
+ break;
}
}
}
@@ -300,17 +322,18 @@ namespace Mono.Security.Authenticode {
// countersignature (1 2 840 113549 1 9 6)
// SET {
PKCS7.SignerInfo cs = new PKCS7.SignerInfo (attr [1]);
- return VerifyCounterSignature (cs, signature, hashName);
+ trustedTimestampRoot = VerifyCounterSignature (cs, signature);
+ break;
default:
// we don't support other unauthenticated attributes
break;
}
}
- return true;
+ return (trustedRoot && trustedTimestampRoot);
}
- private bool VerifyCounterSignature (PKCS7.SignerInfo cs, byte[] signature, string hashName)
+ private bool VerifyCounterSignature (PKCS7.SignerInfo cs, byte[] signature)
{
// SEQUENCE {
// INTEGER 1
@@ -357,6 +380,7 @@ namespace Mono.Security.Authenticode {
if (messageDigest == null)
return false;
// TODO: must be read from the ASN.1 structure
+ string hashName = null;
switch (messageDigest.Length) {
case 16:
hashName = "MD5";
@@ -397,5 +421,20 @@ namespace Mono.Security.Authenticode {
// no certificate can verify this signature!
return false;
}
+
+ private void Reset ()
+ {
+ filename = null;
+ entry = null;
+ hash = null;
+ signedHash = null;
+ signingCertificate = null;
+ reason = -1;
+ trustedRoot = false;
+ trustedTimestampRoot = false;
+ signerChain.Reset ();
+ timestampChain.Reset ();
+ timestamp = DateTime.MinValue;
+ }
}
}
diff --git a/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeFormatter.cs b/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeFormatter.cs
index 9813f809be4..983c55534cc 100755
--- a/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeFormatter.cs
+++ b/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeFormatter.cs
@@ -5,9 +5,7 @@
// Sebastien Pouliot <sebastien@ximian.com>
//
// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
-// (C) 2004 Novell (http://www.novell.com)
-//
-
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -54,6 +52,7 @@ namespace Mono.Security.Authenticode {
private PKCS7.SignedData pkcs7;
private string description;
private Uri url;
+ private byte [] entry;
public AuthenticodeFormatter () : base ()
{
@@ -242,11 +241,11 @@ namespace Mono.Security.Authenticode {
int dirSecuritySize = BitConverter.ToInt32 (file, peOffset + 156);
if (dirSecuritySize > 8) {
- rawData = new byte [dirSecuritySize - 8];
- Buffer.BlockCopy (file, dirSecurityOffset + 8, rawData, 0, rawData.Length);
+ entry = new byte [dirSecuritySize - 8];
+ Buffer.BlockCopy (file, dirSecurityOffset + 8, entry, 0, entry.Length);
}
else
- rawData = null;
+ entry = null;
HashAlgorithm hash = HashAlgorithm.Create (hashAlgorithm);
// 0 to 215 (216) then skip 4 (checksum)
diff --git a/mcs/class/Mono.Security/Mono.Security.Authenticode/ChangeLog b/mcs/class/Mono.Security/Mono.Security.Authenticode/ChangeLog
index 19cc3c81335..0e1b07e15aa 100644
--- a/mcs/class/Mono.Security/Mono.Security.Authenticode/ChangeLog
+++ b/mcs/class/Mono.Security/Mono.Security.Authenticode/ChangeLog
@@ -1,3 +1,9 @@
+2004-09-07 Sebastien Pouliot <sebastien@ximian.com>
+
+ * AuthenticodeBase.cs: Merge optimizations from HEAD.
+ * AuthenticodeDeformatter.cs: Merge optimizations from HEAD.
+ * AuthenticodeFormatter.cs: Merge optimizations from HEAD.
+
2004-05-11 Sebastien Pouliot <sebastien@ximian.com>
* PrivateKey.cs: Better exception reporting. Added globalization to