14
14
*/
15
15
16
16
using System ;
17
- using System . Collections . Generic ;
18
- using System . Linq ;
19
17
using System . Runtime . InteropServices ;
20
18
using System . Security ;
21
19
using System . Security . Cryptography ;
22
20
using System . Text ;
21
+ using MongoDB . Bson ;
23
22
24
23
namespace MongoDB . Driver
25
24
{
@@ -30,7 +29,7 @@ public sealed class PasswordEvidence : MongoIdentityEvidence
30
29
{
31
30
// private fields
32
31
private readonly SecureString _securePassword ;
33
- private readonly string _digest ;
32
+ private readonly string _digest ; // used to implement Equals without referring to the SecureString
34
33
35
34
// constructors
36
35
/// <summary>
@@ -56,14 +55,6 @@ public PasswordEvidence(string password)
56
55
/// <summary>
57
56
/// Gets the password.
58
57
/// </summary>
59
- public string Password
60
- {
61
- get { return CreateString ( _securePassword ) ; }
62
- }
63
-
64
- /// <summary>
65
- /// Gets the secure password.
66
- /// </summary>
67
58
public SecureString SecurePassword
68
59
{
69
60
get { return _securePassword ; }
@@ -95,6 +86,24 @@ public override int GetHashCode()
95
86
return _digest . GetHashCode ( ) ;
96
87
}
97
88
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
+
98
107
// private static methods
99
108
private static SecureString CreateSecureString ( string str )
100
109
{
@@ -112,56 +121,54 @@ private static SecureString CreateSecureString(string str)
112
121
return null ;
113
122
}
114
123
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 )
117
128
{
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 ( ) )
125
130
{
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 ) ;
135
133
}
136
134
}
137
135
138
- /// <summary>
139
- /// Computes the hash value of the secured string
140
- /// </summary>
141
136
[ SecuritySafeCritical ]
142
- private static string GenerateDigest ( SecureString secureString )
137
+ private static void TransformFinalBlock ( HashAlgorithm hash , SecureString secureString )
143
138
{
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
150
141
{
142
+ var passwordChars = new char [ secureString . Length ] ;
143
+ var passwordCharsHandle = GCHandle . Alloc ( passwordChars , GCHandleType . Pinned ) ;
151
144
try
152
145
{
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
+ }
154
161
}
155
162
finally
156
163
{
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 ( ) ;
163
166
}
164
167
}
168
+ finally
169
+ {
170
+ Marshal . ZeroFreeBSTR ( bstr ) ;
171
+ }
165
172
}
166
173
}
167
174
}
0 commit comments