1818using System . Runtime . InteropServices ;
1919using System . Security ;
2020using System . Security . Cryptography ;
21- using System . Text ;
2221using MongoDB . Bson ;
2322using MongoDB . Bson . IO ;
2423using MongoDB . Driver . Core . Misc ;
24+ using MongoDB . Shared ;
2525
2626namespace MongoDB . Driver
2727{
@@ -32,7 +32,6 @@ public sealed class PasswordEvidence : MongoIdentityEvidence
3232 {
3333 // private fields
3434 private readonly SecureString _securePassword ;
35- private readonly string _digest ; // used to implement Equals without referring to the SecureString
3635
3736 // constructors
3837 /// <summary>
@@ -41,18 +40,20 @@ public sealed class PasswordEvidence : MongoIdentityEvidence
4140 /// <param name="password">The password.</param>
4241 public PasswordEvidence ( SecureString password )
4342 {
43+ Ensure . IsNotNull ( password , nameof ( password ) ) ;
4444 _securePassword = password . Copy ( ) ;
4545 _securePassword . MakeReadOnly ( ) ;
46- _digest = GenerateDigest ( password ) ;
4746 }
4847
4948 /// <summary>
5049 /// Initializes a new instance of the <see cref="PasswordEvidence" /> class.
5150 /// </summary>
5251 /// <param name="password">The password.</param>
5352 public PasswordEvidence ( string password )
54- : this ( CreateSecureString ( password ) )
55- { }
53+ {
54+ Ensure . IsNotNull ( password , nameof ( password ) ) ;
55+ _securePassword = CreateSecureString ( password ) ;
56+ }
5657
5758 // public properties
5859 /// <summary>
@@ -74,18 +75,26 @@ public SecureString SecurePassword
7475 public override bool Equals ( object rhs )
7576 {
7677 if ( object . ReferenceEquals ( rhs , null ) || GetType ( ) != rhs . GetType ( ) ) { return false ; }
77- return _digest == ( ( PasswordEvidence ) rhs ) . _digest ;
78+
79+ using ( var lhsDecryptedPassword = new DecryptedSecureString ( _securePassword ) )
80+ using ( var rhsDecryptedPassword = new DecryptedSecureString ( ( ( PasswordEvidence ) rhs ) . _securePassword ) )
81+ {
82+ return lhsDecryptedPassword . GetChars ( ) . SequenceEqual ( rhsDecryptedPassword . GetChars ( ) ) ;
83+ }
7884 }
7985
8086 /// <summary>
8187 /// Returns a hash code for this instance.
8288 /// </summary>
8389 /// <returns>
84- /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
90+ /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
8591 /// </returns>
8692 public override int GetHashCode ( )
8793 {
88- return _digest . GetHashCode ( ) ;
94+ using ( var decryptedPassword = new DecryptedSecureString ( _securePassword ) )
95+ {
96+ return new Hasher ( ) . HashStructElements ( decryptedPassword . GetChars ( ) ) . GetHashCode ( ) ;
97+ }
8998 }
9099
91100 // internal methods
@@ -97,94 +106,25 @@ public override int GetHashCode()
97106 internal string ComputeMongoCRPasswordDigest ( string username )
98107 {
99108 using ( var md5 = MD5 . Create ( ) )
109+ using ( var decryptedPassword = new DecryptedSecureString ( _securePassword ) )
100110 {
101111 var encoding = Utf8Encodings . Strict ;
102112 var prefixBytes = encoding . GetBytes ( username + ":mongo:" ) ;
103- var hash = ComputeHash ( md5 , prefixBytes , _securePassword ) ;
113+ var hash = ComputeHash ( md5 , prefixBytes , decryptedPassword . GetUtf8Bytes ( ) ) ;
104114 return BsonUtils . ToHexString ( hash ) ;
105115 }
106116 }
107117
108118 // private static methods
109- private static SecureString CreateSecureString ( string str )
110- {
111- if ( str != null )
112- {
113- var secureStr = new SecureString ( ) ;
114- foreach ( var c in str )
115- {
116- secureStr . AppendChar ( c ) ;
117- }
118- secureStr . MakeReadOnly ( ) ;
119- return secureStr ;
120- }
121-
122- return null ;
123- }
124-
125- /// <summary>
126- /// Computes the hash value of the secured string
127- /// </summary>
128- private static string GenerateDigest ( SecureString password )
129- {
130- using ( var sha256 = SHA256 . Create ( ) )
131- {
132- var hash = ComputeHash ( sha256 , new byte [ 0 ] , password ) ;
133- return BsonUtils . ToHexString ( hash ) ;
134- }
135- }
136-
137- private static byte [ ] ComputeHash ( HashAlgorithm algorithm , byte [ ] prefixBytes , SecureString password )
119+ private static SecureString CreateSecureString ( string value )
138120 {
139- if ( password . Length == 0 )
140- {
141- return ComputeHash ( algorithm , prefixBytes , new byte [ 0 ] ) ;
142- }
143- else
144- {
145- #if NET45
146- var passwordIntPtr = Marshal . SecureStringToGlobalAllocUnicode ( password ) ;
147- #else
148- var passwordIntPtr = SecureStringMarshal . SecureStringToGlobalAllocUnicode ( password ) ;
149- #endif
150- try
151- {
152- var passwordChars = new char [ password . Length ] ;
153- var passwordCharsHandle = GCHandle . Alloc ( passwordChars , GCHandleType . Pinned ) ;
154- try
155- {
156- Marshal . Copy ( passwordIntPtr , passwordChars , 0 , password . Length ) ;
157-
158- return ComputeHash ( algorithm , prefixBytes , passwordChars ) ;
159- }
160- finally
161- {
162- Array . Clear ( passwordChars , 0 , passwordChars . Length ) ;
163- passwordCharsHandle . Free ( ) ;
164- }
165- }
166- finally
167- {
168- Marshal . ZeroFreeGlobalAllocUnicode ( passwordIntPtr ) ;
169- }
170- }
171- }
172-
173- private static byte [ ] ComputeHash ( HashAlgorithm algorithm , byte [ ] prefixBytes , char [ ] passwordChars )
174- {
175- var passwordBytes = new byte [ Utf8Encodings . Strict . GetByteCount ( passwordChars ) ] ;
176- var passwordBytesHandle = GCHandle . Alloc ( passwordBytes , GCHandleType . Pinned ) ;
177- try
178- {
179- Utf8Encodings . Strict . GetBytes ( passwordChars , 0 , passwordChars . Length , passwordBytes , 0 ) ;
180-
181- return ComputeHash ( algorithm , prefixBytes , passwordBytes ) ;
182- }
183- finally
121+ var secureString = new SecureString ( ) ;
122+ foreach ( var c in value )
184123 {
185- Array . Clear ( passwordBytes , 0 , passwordBytes . Length ) ;
186- passwordBytesHandle . Free ( ) ;
124+ secureString . AppendChar ( c ) ;
187125 }
126+ secureString . MakeReadOnly ( ) ;
127+ return secureString ;
188128 }
189129
190130 private static byte [ ] ComputeHash ( HashAlgorithm algorithm , byte [ ] prefixBytes , byte [ ] passwordBytes )
0 commit comments