1414*/
1515
1616using System ;
17- using System . Collections . Generic ;
18- using System . Linq ;
1917using System . Runtime . InteropServices ;
2018using System . Security ;
2119using System . Security . Cryptography ;
2220using System . Text ;
21+ using MongoDB . Bson ;
2322
2423namespace MongoDB . Driver
2524{
@@ -30,7 +29,7 @@ public sealed class PasswordEvidence : MongoIdentityEvidence
3029 {
3130 // private fields
3231 private readonly SecureString _securePassword ;
33- private readonly string _digest ;
32+ private readonly string _digest ; // used to implement Equals without referring to the SecureString
3433
3534 // constructors
3635 /// <summary>
@@ -56,14 +55,6 @@ public PasswordEvidence(string password)
5655 /// <summary>
5756 /// Gets the password.
5857 /// </summary>
59- public string Password
60- {
61- get { return CreateString ( _securePassword ) ; }
62- }
63-
64- /// <summary>
65- /// Gets the secure password.
66- /// </summary>
6758 public SecureString SecurePassword
6859 {
6960 get { return _securePassword ; }
@@ -95,6 +86,24 @@ public override int GetHashCode()
9586 return _digest . GetHashCode ( ) ;
9687 }
9788
89+ // internal methods
90+ /// <summary>
91+ /// Computes the MONGODB-CR password digest.
92+ /// </summary>
93+ /// <param name="username">The username.</param>
94+ /// <returns></returns>
95+ internal string ComputeMongoCRPasswordDigest ( string username )
96+ {
97+ using ( var md5 = MD5 . Create ( ) )
98+ {
99+ var encoding = new UTF8Encoding ( false , true ) ;
100+ var prefixBytes = encoding . GetBytes ( username + ":mongo:" ) ;
101+ md5 . TransformBlock ( prefixBytes , 0 , prefixBytes . Length , null , 0 ) ;
102+ TransformFinalBlock ( md5 , _securePassword ) ;
103+ return BsonUtils . ToHexString ( md5 . Hash ) ;
104+ }
105+ }
106+
98107 // private static methods
99108 private static SecureString CreateSecureString ( string str )
100109 {
@@ -112,56 +121,54 @@ private static SecureString CreateSecureString(string str)
112121 return null ;
113122 }
114123
115- [ SecuritySafeCritical ]
116- private static string CreateString ( SecureString secureString )
124+ /// <summary>
125+ /// Computes the hash value of the secured string
126+ /// </summary>
127+ private static string GenerateDigest ( SecureString secureString )
117128 {
118- IntPtr strPtr = IntPtr . Zero ;
119- if ( secureString == null || secureString . Length == 0 )
120- {
121- return string . Empty ;
122- }
123-
124- try
129+ using ( var sha256 = new SHA256Managed ( ) )
125130 {
126- strPtr = Marshal . SecureStringToBSTR ( secureString ) ;
127- return Marshal . PtrToStringBSTR ( strPtr ) ;
128- }
129- finally
130- {
131- if ( strPtr != IntPtr . Zero )
132- {
133- Marshal . ZeroFreeBSTR ( strPtr ) ;
134- }
131+ TransformFinalBlock ( sha256 , secureString ) ;
132+ return BsonUtils . ToHexString ( sha256 . Hash ) ;
135133 }
136134 }
137135
138- /// <summary>
139- /// Computes the hash value of the secured string
140- /// </summary>
141136 [ SecuritySafeCritical ]
142- private static string GenerateDigest ( SecureString secureString )
137+ private static void TransformFinalBlock ( HashAlgorithm hash , SecureString secureString )
143138 {
144- IntPtr unmanagedRef = Marshal . SecureStringToBSTR ( secureString ) ;
145- // stored with 0's in between each character...
146- byte [ ] bytes = new byte [ secureString . Length * 2 ] ;
147- var byteArrayHandle = GCHandle . Alloc ( bytes , GCHandleType . Pinned ) ;
148- Marshal . Copy ( unmanagedRef , bytes , 0 , secureString . Length * 2 ) ;
149- using ( var SHA256 = new SHA256Managed ( ) )
139+ var bstr = Marshal . SecureStringToBSTR ( secureString ) ;
140+ try
150141 {
142+ var passwordChars = new char [ secureString . Length ] ;
143+ var passwordCharsHandle = GCHandle . Alloc ( passwordChars , GCHandleType . Pinned ) ;
151144 try
152145 {
153- return Convert . ToBase64String ( SHA256 . ComputeHash ( bytes ) ) ;
146+ Marshal . Copy ( bstr , passwordChars , 0 , passwordChars . Length ) ;
147+
148+ var passwordBytes = new byte [ secureString . Length * 3 ] ; // worst case for UTF16 to UTF8 encoding
149+ var passwordBytesHandle = GCHandle . Alloc ( passwordBytes , GCHandleType . Pinned ) ;
150+ try
151+ {
152+ var encoding = new UTF8Encoding ( false , true ) ;
153+ var length = encoding . GetBytes ( passwordChars , 0 , passwordChars . Length , passwordBytes , 0 ) ;
154+ hash . TransformFinalBlock ( passwordBytes , 0 , length ) ;
155+ }
156+ finally
157+ {
158+ Array . Clear ( passwordBytes , 0 , passwordBytes . Length ) ;
159+ passwordBytesHandle . Free ( ) ;
160+ }
154161 }
155162 finally
156163 {
157- for ( int i = 0 ; i < bytes . Length ; i ++ )
158- {
159- bytes [ i ] = ( byte ) '\0 ' ;
160- }
161- byteArrayHandle . Free ( ) ;
162- Marshal . ZeroFreeBSTR ( unmanagedRef ) ;
164+ Array . Clear ( passwordChars , 0 , passwordChars . Length ) ;
165+ passwordCharsHandle . Free ( ) ;
163166 }
164167 }
168+ finally
169+ {
170+ Marshal . ZeroFreeBSTR ( bstr ) ;
171+ }
165172 }
166173 }
167174}
0 commit comments