delay sign assemblies with the ecma key
[mono.git] / mcs / class / Mono.Security / Mono.Security / StrongName.cs
index 2d1775aa8ef0b195b888c3cc34f0572c3b00499c..b95525ba479e2e71935b4e08466639fac84bb61f 100644 (file)
@@ -9,7 +9,10 @@
 //
 
 using System;
+using System.Configuration.Assemblies;
+using System.Globalization;
 using System.IO;
+using System.Reflection;
 using System.Security.Cryptography;
 
 using Mono.Security.Cryptography;
@@ -95,9 +98,22 @@ namespace Mono.Security {
                        if (data == null)
                                throw new ArgumentNullException ("data");
 
-                       RSA = CryptoConvert.FromCapiKeyBlob (data);
-                       if (rsa == null)
-                               throw new ArgumentException ("data isn't a correctly encoded RSA public key");
+                       // check for ECMA key
+                       if (data.Length == 16) {
+                               int i = 0;
+                               int sum = 0;
+                               while (i < data.Length)
+                                       sum += data [i++];
+                               if (sum == 4) {
+                                       // it is the ECMA key
+                                       publicKey = (byte[]) data.Clone ();
+                               }
+                       }
+                       else {
+                               RSA = CryptoConvert.FromCapiKeyBlob (data);
+                               if (rsa == null)
+                                       throw new ArgumentException ("data isn't a correctly encoded RSA public key");
+                       }
                }
 
                public StrongName (RSA rsa) : base ()
@@ -118,7 +134,7 @@ namespace Mono.Security {
                        get {
                                if (rsa == null)
                                        return false;
-#if INSIDE_CORLIB || NET_1_2
+#if INSIDE_CORLIB
                                // the easy way
                                if (RSA is RSACryptoServiceProvider) {
                                        // available as internal for corlib
@@ -135,7 +151,7 @@ namespace Mono.Security {
                                                RSAParameters p = rsa.ExportParameters (true);
                                                return ((p.D != null) && (p.P != null) && (p.Q != null));
                                        }
-                                       catch {
+                                       catch (CryptographicException) {
                                                return false;
                                        }
                                }
@@ -174,7 +190,7 @@ namespace Mono.Security {
                                        publicKey [6] = 0x00;
                                        publicKey [7] = 0x00;
                                        // Length of Public Key (in bytes)
-                                       byte[] lastPart = BitConverter.GetBytes (publicKey.Length - 12);
+                                       byte[] lastPart = BitConverterLE.GetBytes (publicKey.Length - 12);
                                        publicKey [8] = lastPart [0];
                                        publicKey [9] = lastPart [1];
                                        publicKey [10] = lastPart [2];
@@ -182,28 +198,28 @@ namespace Mono.Security {
                                        // Ok from here - Same structure as keypair - expect for public key
                                        publicKey [12] = 0x06;          // PUBLICKEYBLOB
                                        // we can copy this part
-                                       Array.Copy (keyPair, 1, publicKey, 13, publicKey.Length - 13);
+                                       Buffer.BlockCopy (keyPair, 1, publicKey, 13, publicKey.Length - 13);
                                        // and make a small adjustment 
                                        publicKey [23] = 0x31;          // (RSA1 not RSA2)
                                }
-                               return publicKey;
+                               return (byte[]) publicKey.Clone ();
                        }
                }
 
                public byte[] PublicKeyToken {
                        get {
-                               if (keyToken != null)
-                                       return keyToken;
-                               byte[] publicKey = PublicKey;
-                               if (publicKey == null)
-                                       return null;
-                                HashAlgorithm ha = SHA1.Create (TokenAlgorithm);
-                               byte[] hash = ha.ComputeHash (publicKey);
-                               // we need the last 8 bytes in reverse order
-                               keyToken = new byte [8];
-                               Array.Copy (hash, (hash.Length - 8), keyToken, 0, 8);
-                               Array.Reverse (keyToken, 0, 8);
-                               return keyToken;
+                               if (keyToken == null) {
+                                       byte[] publicKey = PublicKey;
+                                       if (publicKey == null)
+                                               return null;
+                                       HashAlgorithm ha = SHA1.Create (TokenAlgorithm);
+                                       byte[] hash = ha.ComputeHash (publicKey);
+                                       // we need the last 8 bytes in reverse order
+                                       keyToken = new byte [8];
+                                       Buffer.BlockCopy (hash, (hash.Length - 8), keyToken, 0, 8);
+                                       Array.Reverse (keyToken, 0, 8);
+                               }
+                               return (byte[]) keyToken.Clone ();
                        }
                }
 
@@ -214,7 +230,7 @@ namespace Mono.Security {
                                return tokenAlgorithm; 
                        }
                        set {
-                               string algo = value.ToUpper ();
+                               string algo = value.ToUpper (CultureInfo.InvariantCulture);
                                if ((algo == "SHA1") || (algo == "MD5")) {
                                        tokenAlgorithm = value;
                                        InvalidateCache ();
@@ -232,9 +248,9 @@ namespace Mono.Security {
                private UInt32 RVAtoPosition (UInt32 r, int sections, byte[] headers) 
                {
                        for (int i=0; i < sections; i++) {
-                               UInt32 p = BitConverter.ToUInt32 (headers, i * 40 + 20);
-                               UInt32 s = BitConverter.ToUInt32 (headers, i * 40 + 12);
-                               int l = (int) BitConverter.ToUInt32 (headers, i * 40 + 8);
+                               UInt32 p = BitConverterLE.ToUInt32 (headers, i * 40 + 20);
+                               UInt32 s = BitConverterLE.ToUInt32 (headers, i * 40 + 12);
+                               int l = (int) BitConverterLE.ToUInt32 (headers, i * 40 + 8);
                                if ((s <= r) && (r < s + l)) {
                                        return p + r - s;
                                }
@@ -253,9 +269,9 @@ namespace Mono.Security {
                        // ref: Section 24.2.1, Partition II Metadata
                        byte[] mz = new byte [128];
                        stream.Read (mz, 0, 128);
-                       if (BitConverter.ToUInt16 (mz, 0) != 0x5a4d)
+                       if (BitConverterLE.ToUInt16 (mz, 0) != 0x5a4d)
                                return null;
-                       UInt32 peHeader = BitConverter.ToUInt32 (mz, 60);
+                       UInt32 peHeader = BitConverterLE.ToUInt32 (mz, 60);
                        cs.Write (mz, 0, 128);
                        if (peHeader != 128) {
                                byte[] mzextra = new byte [peHeader - 128];
@@ -267,9 +283,9 @@ namespace Mono.Security {
                        // ref: Section 24.2.2, Partition II Metadata
                        byte[] pe = new byte [248];
                        stream.Read (pe, 0, 248);
-                       if (BitConverter.ToUInt32 (pe, 0) != 0x4550)
+                       if (BitConverterLE.ToUInt32 (pe, 0) != 0x4550)
                                return null;
-                       if (BitConverter.ToUInt16 (pe, 4) != 0x14c)
+                       if (BitConverterLE.ToUInt16 (pe, 4) != 0x14c)
                                return null;
                        // MUST zeroize both CheckSum and Security Directory
                        byte[] v = new byte [8];
@@ -277,15 +293,15 @@ namespace Mono.Security {
                        Buffer.BlockCopy (v, 0, pe, 152, 8);
                        cs.Write (pe, 0, 248);
 
-                       UInt16 numSection = BitConverter.ToUInt16 (pe, 6);
+                       UInt16 numSection = BitConverterLE.ToUInt16 (pe, 6);
                        int sectionLength = (numSection * 40);
                        byte[] sectionHeaders = new byte [sectionLength];
                        stream.Read (sectionHeaders, 0, sectionLength);
                        cs.Write (sectionHeaders, 0, sectionLength);
 
-                       UInt32 cliHeaderRVA = BitConverter.ToUInt32 (pe, 232);
+                       UInt32 cliHeaderRVA = BitConverterLE.ToUInt32 (pe, 232);
                        UInt32 cliHeaderPos = RVAtoPosition (cliHeaderRVA, numSection, sectionHeaders);
-                       int cliHeaderSiz = (int) BitConverter.ToUInt32 (pe, 236);
+                       int cliHeaderSiz = (int) BitConverterLE.ToUInt32 (pe, 236);
 
                        // CLI Header
                        // ref: Section 24.3.3, Partition II Metadata
@@ -293,13 +309,13 @@ namespace Mono.Security {
                        stream.Position = cliHeaderPos;
                        stream.Read (cli, 0, cliHeaderSiz);
 
-                       UInt32 strongNameSignatureRVA = BitConverter.ToUInt32 (cli, 32);
+                       UInt32 strongNameSignatureRVA = BitConverterLE.ToUInt32 (cli, 32);
                        info.SignaturePosition = RVAtoPosition (strongNameSignatureRVA, numSection, sectionHeaders);
-                       info.SignatureLength = BitConverter.ToUInt32 (cli, 36);
+                       info.SignatureLength = BitConverterLE.ToUInt32 (cli, 36);
 
-                       UInt32 metadataRVA = BitConverter.ToUInt32 (cli, 8);
+                       UInt32 metadataRVA = BitConverterLE.ToUInt32 (cli, 8);
                        info.MetadataPosition = RVAtoPosition (metadataRVA, numSection, sectionHeaders);
-                       info.MetadataLength = BitConverter.ToUInt32 (cli, 12);
+                       info.MetadataLength = BitConverterLE.ToUInt32 (cli, 12);
 
                        if (options == StrongNameOptions.Metadata) {
                                cs.Close ();
@@ -313,8 +329,8 @@ namespace Mono.Security {
 
                        // now we hash every section EXCEPT the signature block
                        for (int i=0; i < numSection; i++) {
-                               UInt32 start = BitConverter.ToUInt32 (sectionHeaders, i * 40 + 20);
-                               int length = (int) BitConverter.ToUInt32 (sectionHeaders, i * 40 + 16);
+                               UInt32 start = BitConverterLE.ToUInt32 (sectionHeaders, i * 40 + 20);
+                               int length = (int) BitConverterLE.ToUInt32 (sectionHeaders, i * 40 + 16);
                                byte[] section = new byte [length];
                                stream.Position = start;
                                stream.Read (section, 0, length);
@@ -358,15 +374,12 @@ namespace Mono.Security {
                {
                        bool result = false;
                        StrongNameSignature sn;
-                       FileStream fs = File.OpenRead (fileName);
-                       try {
+                       using (FileStream fs = File.OpenRead (fileName)) {
                                sn = StrongHash (fs, StrongNameOptions.Signature);
-                               if (sn.Hash == null)
-                                       return false;
-                       }
-                       finally {
                                fs.Close ();
                        }
+                       if (sn.Hash == null)
+                               return false;
 
                        byte[] signature = null;
                        try {
@@ -375,18 +388,13 @@ namespace Mono.Security {
                                signature = sign.CreateSignature (sn.Hash);
                                Array.Reverse (signature);
                        }
-                       catch {
+                       catch (CryptographicException) {
                                return false;
                        }
 
-                       try {
-                               fs = File.OpenWrite (fileName);
+                       using (FileStream fs = File.OpenWrite (fileName)) {
                                fs.Position = sn.SignaturePosition;
                                fs.Write (signature, 0, signature.Length);
-                       }
-                       catch {
-                       }
-                       finally {
                                fs.Close ();
                                result = true;
                        }
@@ -395,26 +403,98 @@ namespace Mono.Security {
 
                public bool Verify (string fileName) 
                {
-                       bool result = false;
                        StrongNameSignature sn;
-                       FileStream fs = File.OpenRead (fileName);
-                       try {
+                       using (FileStream fs = File.OpenRead (fileName)) {
                                sn = StrongHash (fs, StrongNameOptions.Signature);
-                               if (sn.Hash == null)
+                               fs.Close ();
+                       }
+                       if (sn.Hash == null) {
+                                Console.WriteLine ("hash is null");
+                               return false;
+                        }
+
+                       try {
+                               AssemblyHashAlgorithm algorithm = AssemblyHashAlgorithm.SHA1;
+                               if (tokenAlgorithm == "MD5")
+                                       algorithm = AssemblyHashAlgorithm.MD5;
+                               bool v = Verify (rsa, algorithm, sn.Hash, sn.Signature);
+                                Console.WriteLine ("returning v:   " + v);
+                                return v;
+                       }
+                       catch (CryptographicException e) {
+                               // no exception allowed
+                                Console.WriteLine ("exception:  " + e);
+                               return false;
+                       }
+               }
+
+#if INSIDE_CORLIB
+               // We don't want a dependency on StrongNameManager in Mono.Security.dll
+               static public bool IsAssemblyStrongnamed (string assemblyName) 
+               {
+                       try {
+                               // this doesn't load the assembly (well it unloads it ;)
+                               // http://weblogs.asp.net/nunitaddin/posts/9991.aspx
+                               AssemblyName an = AssemblyName.GetAssemblyName (assemblyName);
+                               if (an == null)
                                        return false;
+
+                               byte[] publicKey = StrongNameManager.GetMappedPublicKey (an.GetPublicKeyToken ());
+                               if ((publicKey == null) || (publicKey.Length < 12)) {
+                                       // no mapping
+                                       publicKey = an.GetPublicKey ();
+                                       if ((publicKey == null) || (publicKey.Length < 12))
+                                               return false;
+                               }
+
+                               // Note: MustVerify is based on the original token (by design). Public key
+                               // remapping won't affect if the assembly is verified or not.
+                               if (!StrongNameManager.MustVerify (an)) {
+                                       return true;
+                               }
+
+                               RSA rsa = CryptoConvert.FromCapiPublicKeyBlob (publicKey, 12);
+                               StrongName sn = new StrongName (rsa);
+                               bool result = sn.Verify (assemblyName);
+                               return result;
                        }
-                       finally {
-                               fs.Close ();
+                       catch {
+                               // no exception allowed
+                               return false;
                        }
+               }
 
+               // TODO
+               // we would get better performance if the runtime hashed the
+               // assembly - as we wouldn't have to load it from disk a 
+               // second time. The runtime already have implementations of
+               // SHA1 (and even MD5 if required someday).
+               static public bool VerifySignature (byte[] publicKey, int algorithm, byte[] hash, byte[] signature) 
+               {
                        try {
-                               RSAPKCS1SignatureDeformatter vrfy = new RSAPKCS1SignatureDeformatter (rsa);
-                               vrfy.SetHashAlgorithm (TokenAlgorithm);
-                               result = vrfy.VerifySignature (sn.Hash, sn.Signature);
+                               RSA rsa = CryptoConvert.FromCapiPublicKeyBlob (publicKey);
+                               return Verify (rsa, (AssemblyHashAlgorithm) algorithm, hash, signature);
                        }
                        catch {
+                               // no exception allowed
+                               return false;
                        }
-                       return result;
                }
-       }       
+#endif
+               static private bool Verify (RSA rsa, AssemblyHashAlgorithm algorithm, byte[] hash, byte[] signature) 
+               {
+                       RSAPKCS1SignatureDeformatter vrfy = new RSAPKCS1SignatureDeformatter (rsa);
+                       switch (algorithm) {
+                       case AssemblyHashAlgorithm.MD5:
+                               vrfy.SetHashAlgorithm ("MD5");
+                               break;
+                       case AssemblyHashAlgorithm.SHA1:
+                       case AssemblyHashAlgorithm.None:
+                       default:
+                               vrfy.SetHashAlgorithm ("SHA1");
+                               break;
+                       }
+                       return vrfy.VerifySignature (hash, signature);
+               }
+       }
 }