Ver código fonte

Upgrade crypto provider, retarget better framework

Phallacy 6 anos atrás
pai
commit
4519ce26e2

+ 131 - 40
Emby.Server.Implementations/Cryptography/CryptographyProvider.cs

@@ -1,40 +1,131 @@
-using System;
-using System.IO;
-using System.Security.Cryptography;
-using System.Text;
-using MediaBrowser.Model.Cryptography;
-
-namespace Emby.Server.Implementations.Cryptography
-{
-    public class CryptographyProvider : ICryptoProvider
-    {
-        public Guid GetMD5(string str)
-        {
-            return new Guid(ComputeMD5(Encoding.Unicode.GetBytes(str)));
-        }
-
-        public byte[] ComputeSHA1(byte[] bytes)
-        {
-            using (var provider = SHA1.Create())
-            {
-                return provider.ComputeHash(bytes);
-            }
-        }
-
-        public byte[] ComputeMD5(Stream str)
-        {
-            using (var provider = MD5.Create())
-            {
-                return provider.ComputeHash(str);
-            }
-        }
-
-        public byte[] ComputeMD5(byte[] bytes)
-        {
-            using (var provider = MD5.Create())
-            {
-                return provider.ComputeHash(bytes);
-            }
-        }
-    }
-}
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Security.Cryptography;
+using System.Text;
+using MediaBrowser.Model.Cryptography;
+
+namespace Emby.Server.Implementations.Cryptography
+{
+    public class CryptographyProvider : ICryptoProvider
+    {
+        private List<string> SupportedHashMethods = new List<string>();
+        private string DefaultHashMethod = "SHA256";
+        private RandomNumberGenerator rng;
+        private int defaultiterations = 1000;
+        public CryptographyProvider()
+        {
+            //Currently supported hash methods from https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptoconfig?view=netcore-2.1
+            //there might be a better way to autogenerate this list as dotnet updates, but I couldn't find one
+            SupportedHashMethods = new List<string>
+            {
+               "MD5"
+                ,"System.Security.Cryptography.MD5"
+                ,"SHA"
+                ,"SHA1"
+                ,"System.Security.Cryptography.SHA1"
+                ,"SHA256"
+                ,"SHA-256"
+                ,"System.Security.Cryptography.SHA256"
+                ,"SHA384"
+                ,"SHA-384"
+                ,"System.Security.Cryptography.SHA384"
+                ,"SHA512"
+                ,"SHA-512"
+                ,"System.Security.Cryptography.SHA512"
+            };
+            rng = RandomNumberGenerator.Create();
+        }
+
+        public Guid GetMD5(string str)
+        {
+            return new Guid(ComputeMD5(Encoding.Unicode.GetBytes(str)));
+        }
+
+        public byte[] ComputeSHA1(byte[] bytes)
+        {
+            using (var provider = SHA1.Create())
+            {
+                return provider.ComputeHash(bytes);
+            }
+        }
+
+        public byte[] ComputeMD5(Stream str)
+        {
+            using (var provider = MD5.Create())
+            {
+                return provider.ComputeHash(str);
+            }
+        }
+
+        public byte[] ComputeMD5(byte[] bytes)
+        {
+            using (var provider = MD5.Create())
+            {
+                return provider.ComputeHash(bytes);
+            }
+        }
+
+        public IEnumerable<string> GetSupportedHashMethods()
+        {
+            return SupportedHashMethods;
+        }
+
+        private byte[] PBKDF2(string method, byte[] bytes, byte[] salt)
+        {
+            using (var r = new Rfc2898DeriveBytes(bytes, salt, defaultiterations, new HashAlgorithmName(method)))
+            {
+                return r.GetBytes(32);
+            }
+        }
+
+        public byte[] ComputeHash(string HashMethod, byte[] bytes)
+        {
+            return ComputeHash(HashMethod, bytes, new byte[0]);
+        }
+
+        public byte[] ComputeHashWithDefaultMethod(byte[] bytes)
+        {
+            return ComputeHash(DefaultHashMethod, bytes);
+        }
+
+        public byte[] ComputeHash(string HashMethod, byte[] bytes, byte[] salt)
+        {
+            if (SupportedHashMethods.Contains(HashMethod))
+            {
+                if (salt.Length == 0)
+                {
+                    using (var h = HashAlgorithm.Create(HashMethod))
+                    {
+                        return h.ComputeHash(bytes);
+                    }
+                }
+                else
+                {
+                    return PBKDF2(HashMethod, bytes, salt);
+                }
+            }
+            else
+            {
+                throw new CryptographicException(String.Format("Requested hash method is not supported: {0}", HashMethod));
+            }
+        }
+
+        public byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt)
+        {
+            return PBKDF2(DefaultHashMethod, bytes, salt);
+        }
+        
+        public byte[] ComputeHash(PasswordHash hash)
+        {
+            return ComputeHash(hash.Id, hash.HashBytes, hash.SaltBytes);
+        }
+        
+        public byte[] GenerateSalt()
+        {
+            byte[] salt = new byte[8];
+            rng.GetBytes(salt);
+            return salt;
+        }
+    }
+}

+ 1 - 1
Emby.Server.Implementations/Emby.Server.Implementations.csproj

@@ -35,7 +35,7 @@
   </ItemGroup>
 
   <PropertyGroup>
-    <TargetFramework>netstandard2.0</TargetFramework>
+    <TargetFramework>netcoreapp2.1</TargetFramework>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>
 

+ 15 - 16
Emby.Server.Implementations/Library/UserManager.cs

@@ -4,6 +4,7 @@ using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Text;
+using System.Text.RegularExpressions;
 using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Events;
@@ -220,22 +221,20 @@ namespace Emby.Server.Implementations.Library
             }
         }
 
-        public bool IsValidUsername(string username)
-        {
-            // Usernames can contain letters (a-z), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)
-            foreach (var currentChar in username)
-            {
-                if (!IsValidUsernameCharacter(currentChar))
-                {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        private static bool IsValidUsernameCharacter(char i)
-        {
-            return !char.Equals(i, '<') && !char.Equals(i, '>');
+        public bool IsValidUsername(string username)
+        {
+            //The old way was dumb, we should make it less dumb, lets do so.
+            //This is some regex that matches only on unicode "word" characters, as well as -, _ and @
+            //In theory this will cut out most if not all 'control' characters which should help minimize any weirdness
+            string UserNameRegex = "^[\\w-'._@]*$";
+            // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)
+            return Regex.IsMatch(username, UserNameRegex);
+        }
+
+        private static bool IsValidUsernameCharacter(char i)
+        {
+            string UserNameRegex = "^[\\w-'._@]*$";
+            return Regex.IsMatch(i.ToString(), UserNameRegex);
         }
 
         public string MakeValidUsername(string username)

+ 8 - 0
MediaBrowser.Model/Cryptography/ICryptoProvider.cs

@@ -1,5 +1,6 @@
 using System;
 using System.IO;
+using System.Collections.Generic;
 
 namespace MediaBrowser.Model.Cryptography
 {
@@ -9,5 +10,12 @@ namespace MediaBrowser.Model.Cryptography
         byte[] ComputeMD5(Stream str);
         byte[] ComputeMD5(byte[] bytes);
         byte[] ComputeSHA1(byte[] bytes);
+        IEnumerable<string> GetSupportedHashMethods();
+        byte[] ComputeHash(string HashMethod, byte[] bytes);
+        byte[] ComputeHashWithDefaultMethod(byte[] bytes);
+        byte[] ComputeHash(string HashMethod, byte[] bytes, byte[] salt);
+        byte[] ComputeHashWithDefaultMethod(byte[] bytes, byte[] salt);
+        byte[] ComputeHash(PasswordHash hash);
+        byte[] GenerateSalt();
     }
 }

+ 93 - 0
MediaBrowser.Model/Cryptography/PasswordHash.cs

@@ -0,0 +1,93 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace MediaBrowser.Model.Cryptography
+{
+    public class PasswordHash
+    {
+        //Defined from this hash storage spec
+        //https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md
+        //$<id>[$<param>=<value>(,<param>=<value>)*][$<salt>[$<hash>]]
+
+        public string Id;
+        public Dictionary<string, string> Parameters = new Dictionary<string, string>();
+        public string Salt;
+        public byte[] SaltBytes;
+        public string Hash;
+        public byte[] HashBytes;
+        public PasswordHash(string StorageString)
+        {
+            string[] a = StorageString.Split('$');
+            Id = a[1];
+            if (a[2].Contains("="))
+            {
+                foreach (string paramset in (a[2].Split(',')))
+                {
+                    if (!String.IsNullOrEmpty(paramset))
+                    {
+                        string[] fields = paramset.Split('=');
+                        Parameters.Add(fields[0], fields[1]);
+                    }
+                }
+                if (a.Length == 4)
+                {
+                    Salt = a[2];
+                    SaltBytes = Convert.FromBase64CharArray(Salt.ToCharArray(), 0, Salt.Length);
+                    Hash = a[3];
+                    HashBytes = Convert.FromBase64CharArray(Hash.ToCharArray(), 0, Hash.Length);
+                }
+                else
+                {
+                    Salt = string.Empty;
+                    Hash = a[3];
+                    HashBytes = Convert.FromBase64CharArray(Hash.ToCharArray(), 0, Hash.Length);
+                }
+            }
+            else
+            {
+                if (a.Length == 4)
+                {
+                    Salt = a[2];
+                    SaltBytes = Convert.FromBase64CharArray(Salt.ToCharArray(), 0, Salt.Length);
+                    Hash = a[3];
+                    HashBytes = Convert.FromBase64CharArray(Hash.ToCharArray(), 0, Hash.Length);
+                }
+                else
+                {
+                    Salt = string.Empty;
+                    Hash = a[2];
+                    HashBytes = Convert.FromBase64CharArray(Hash.ToCharArray(), 0, Hash.Length);
+                }
+
+            }
+
+        }
+
+        public PasswordHash(ICryptoProvider cryptoProvider2)
+        {
+            Id = "SHA256";
+            SaltBytes = cryptoProvider2.GenerateSalt();
+            Salt = Convert.ToBase64String(SaltBytes);
+        }
+        private string SerializeParameters()
+        {
+            string ReturnString = String.Empty;
+            foreach (var KVP in Parameters)
+            {
+                ReturnString += String.Format(",{0}={1}", KVP.Key, KVP.Value);
+            }
+            if (ReturnString[0] == ',')
+            {
+                ReturnString = ReturnString.Remove(0, 1);
+            }
+            return ReturnString;
+        }
+
+        public override string ToString()
+        {
+            return String.Format("${0}${1}${2}${3}", Id, SerializeParameters(), Salt, Hash);
+        }
+    }
+
+}