2019-01-31 08:24:53 +00:00
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
{
2019-02-13 08:33:00 +00:00
private HashSet < string > SupportedHashMethods ;
2019-02-12 10:16:03 +00:00
public string DefaultHashMethod = > "SHA256" ;
2019-01-31 08:24:53 +00:00
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
2019-02-13 08:33:00 +00:00
SupportedHashMethods = new HashSet < string > ( )
2019-01-31 08:24:53 +00:00
{
"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 ;
}
2019-02-13 08:33:00 +00:00
private byte [ ] PBKDF2 ( string method , byte [ ] bytes , byte [ ] salt , int iterations )
{
using ( var r = new Rfc2898DeriveBytes ( bytes , salt , iterations , new HashAlgorithmName ( method ) ) )
2019-01-31 08:24:53 +00:00
{
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
{
2019-02-13 08:33:00 +00:00
return PBKDF2 ( HashMethod , bytes , salt , defaultiterations ) ;
2019-01-31 08:24:53 +00:00
}
}
else
{
throw new CryptographicException ( String . Format ( "Requested hash method is not supported: {0}" , HashMethod ) ) ;
}
2019-02-13 08:33:00 +00:00
}
2019-01-31 08:24:53 +00:00
public byte [ ] ComputeHashWithDefaultMethod ( byte [ ] bytes , byte [ ] salt )
{
2019-02-13 08:33:00 +00:00
return PBKDF2 ( DefaultHashMethod , bytes , salt , defaultiterations ) ;
2019-01-31 08:24:53 +00:00
}
public byte [ ] ComputeHash ( PasswordHash hash )
2019-02-13 08:33:00 +00:00
{
int iterations = defaultiterations ;
if ( ! hash . Parameters . ContainsKey ( "iterations" ) )
{
hash . Parameters . Add ( "iterations" , defaultiterations . ToString ( ) ) ;
}
else
{
try { iterations = int . Parse ( hash . Parameters [ "iterations" ] ) ; }
catch ( Exception e ) { iterations = defaultiterations ; throw new Exception ( $"Couldn't successfully parse iterations value from string:{hash.Parameters[" iterations "]}" , e ) ; }
}
return PBKDF2 ( hash . Id , hash . HashBytes , hash . SaltBytes , iterations ) ;
}
2019-01-31 08:24:53 +00:00
public byte [ ] GenerateSalt ( )
{
2019-02-13 08:33:00 +00:00
byte [ ] salt = new byte [ 64 ] ;
2019-01-31 08:24:53 +00:00
rng . GetBytes ( salt ) ;
return salt ;
2019-02-13 08:33:00 +00:00
}
2019-01-31 08:24:53 +00:00
}
}