@@ -448,13 +448,13 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
448
448
var initialHandshake = InitialHandshakePayload . Create ( payload . Span ) ;
449
449
450
450
// if PluginAuth is supported, then use the specified auth plugin; else, fall back to protocol capabilities to determine the auth type to use
451
- m_currentAuthenticationMethod = ( initialHandshake . ProtocolCapabilities & ProtocolCapabilities . PluginAuth ) != 0 ? initialHandshake . AuthPluginName ! :
451
+ var currentAuthenticationMethod = ( initialHandshake . ProtocolCapabilities & ProtocolCapabilities . PluginAuth ) != 0 ? initialHandshake . AuthPluginName ! :
452
452
( initialHandshake . ProtocolCapabilities & ProtocolCapabilities . SecureConnection ) == 0 ? "mysql_old_password" :
453
453
"mysql_native_password" ;
454
- Log . ServerSentAuthPluginName ( m_logger , Id , m_currentAuthenticationMethod ) ;
455
- if ( m_currentAuthenticationMethod is not "mysql_native_password" and not "sha256_password" and not "caching_sha2_password" )
454
+ Log . ServerSentAuthPluginName ( m_logger , Id , currentAuthenticationMethod ) ;
455
+ if ( currentAuthenticationMethod is not "mysql_native_password" and not "sha256_password" and not "caching_sha2_password" )
456
456
{
457
- Log . UnsupportedAuthenticationMethod ( m_logger , Id , m_currentAuthenticationMethod ) ;
457
+ Log . UnsupportedAuthenticationMethod ( m_logger , Id , currentAuthenticationMethod ) ;
458
458
throw new NotSupportedException ( $ "Authentication method '{ initialHandshake . AuthPluginName } ' is not supported.") ;
459
459
}
460
460
@@ -529,7 +529,8 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
529
529
cs . ConnectionAttributes = CreateConnectionAttributes ( cs . ApplicationName ) ;
530
530
531
531
var password = GetPassword ( cs , connection ) ;
532
- using ( var handshakeResponsePayload = HandshakeResponse41Payload . Create ( initialHandshake , cs , password , m_compressionMethod , connection . ZstandardPlugin ? . CompressionLevel , m_characterSet , m_supportsConnectionAttributes ? cs . ConnectionAttributes : null ) )
532
+ AuthenticationUtility . CreateResponseAndPasswordHash ( password , initialHandshake . AuthPluginData , out var authenticationResponse , out m_passwordHash ) ;
533
+ using ( var handshakeResponsePayload = HandshakeResponse41Payload . Create ( initialHandshake , cs , authenticationResponse , m_compressionMethod , connection . ZstandardPlugin ? . CompressionLevel , m_characterSet , m_supportsConnectionAttributes ? cs . ConnectionAttributes : null ) )
533
534
await SendReplyAsync ( handshakeResponsePayload , ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
534
535
payload = await ReceiveReplyAsync ( ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
535
536
@@ -560,7 +561,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
560
561
// there is no shared secret that can be used to validate the certificate
561
562
Log . CertificateErrorNoPassword ( m_logger , Id , m_sslPolicyErrors ) ;
562
563
}
563
- else if ( ValidateFingerprint ( ok . StatusInfo , initialHandshake . AuthPluginData . AsSpan ( 0 , 20 ) , password ) )
564
+ else if ( ValidateFingerprint ( ok . StatusInfo , initialHandshake . AuthPluginData . AsSpan ( 0 , 20 ) ) )
564
565
{
565
566
Log . CertificateErrorValidThumbprint ( m_logger , Id , m_sslPolicyErrors ) ;
566
567
ignoreCertificateError = true ;
@@ -626,36 +627,20 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
626
627
/// </summary>
627
628
/// <param name="validationHash">The validation hash received from the server.</param>
628
629
/// <param name="challenge">The auth plugin data from the initial handshake.</param>
629
- /// <param name="password">The user's password.</param>
630
630
/// <returns><c>true</c> if the validation hash matches the locally-computed value; otherwise, <c>false</c>.</returns>
631
- private bool ValidateFingerprint ( byte [ ] ? validationHash , ReadOnlySpan < byte > challenge , string password )
631
+ private bool ValidateFingerprint ( byte [ ] ? validationHash , ReadOnlySpan < byte > challenge )
632
632
{
633
633
// expect 0x01 followed by 64 hex characters giving a SHA2 hash
634
634
if ( validationHash ? . Length != 65 || validationHash [ 0 ] != 1 )
635
635
return false ;
636
636
637
- byte [ ] ? passwordHashResult = null ;
638
- switch ( m_currentAuthenticationMethod )
639
- {
640
- case "mysql_native_password" :
641
- passwordHashResult = AuthenticationUtility . HashPassword ( [ ] , password , onlyHashPassword : true ) ;
642
- break ;
643
-
644
- case "client_ed25519" :
645
- AuthenticationPlugins . TryGetPlugin ( m_currentAuthenticationMethod , out var ed25519Plugin ) ;
646
- if ( ed25519Plugin is IAuthenticationPlugin2 plugin2 )
647
- passwordHashResult = plugin2 . CreatePasswordHash ( password , challenge ) ;
648
- break ;
649
- }
650
- if ( passwordHashResult is null )
637
+ // the authentication plugin must have provided a password hash (via IAuthenticationPlugin3) that we saved for future use
638
+ if ( m_passwordHash is null )
651
639
return false ;
652
640
653
- Span < byte > combined = stackalloc byte [ 32 + challenge . Length + passwordHashResult . Length ] ;
654
- passwordHashResult . CopyTo ( combined ) ;
655
- challenge . CopyTo ( combined [ passwordHashResult . Length ..] ) ;
656
- m_remoteCertificateSha2Thumbprint ! . CopyTo ( combined [ ( passwordHashResult . Length + challenge . Length ) ..] ) ;
657
-
641
+ // hash password hash || scramble || certificate thumbprint
658
642
Span < byte > hashBytes = stackalloc byte [ 32 ] ;
643
+ Span < byte > combined = [ .. m_passwordHash , .. challenge , .. m_remoteCertificateSha2Thumbprint ! ] ;
659
644
#if NET5_0_OR_GREATER
660
645
SHA256 . TryHashData ( combined , hashBytes , out _ ) ;
661
646
#else
@@ -804,8 +789,8 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
804
789
DatabaseOverride = null ;
805
790
}
806
791
var password = GetPassword ( cs , connection ) ;
807
- var hashedPassword = AuthenticationUtility . CreateAuthenticationResponse ( AuthPluginData ! , password ) ;
808
- using ( var changeUserPayload = ChangeUserPayload . Create ( cs . UserID , hashedPassword , cs . Database , m_characterSet , m_supportsConnectionAttributes ? cs . ConnectionAttributes : null ) )
792
+ AuthenticationUtility . CreateResponseAndPasswordHash ( password , AuthPluginData , out var nativeResponse , out m_passwordHash ) ;
793
+ using ( var changeUserPayload = ChangeUserPayload . Create ( cs . UserID , nativeResponse , cs . Database , m_characterSet , m_supportsConnectionAttributes ? cs . ConnectionAttributes : null ) )
809
794
await SendAsync ( changeUserPayload , ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
810
795
payload = await ReceiveReplyAsync ( ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
811
796
if ( payload . HeaderByte == AuthenticationMethodSwitchRequestPayload . Signature )
@@ -849,13 +834,12 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
849
834
// if the server didn't support the hashed password; rehash with the new challenge
850
835
var switchRequest = AuthenticationMethodSwitchRequestPayload . Create ( payload . Span ) ;
851
836
Log . SwitchingToAuthenticationMethod ( m_logger , Id , switchRequest . Name ) ;
852
- m_currentAuthenticationMethod = switchRequest . Name ;
853
837
switch ( switchRequest . Name )
854
838
{
855
839
case "mysql_native_password" :
856
840
AuthPluginData = switchRequest . Data ;
857
- var hashedPassword = AuthenticationUtility . CreateAuthenticationResponse ( AuthPluginData , password ) ;
858
- payload = new ( hashedPassword ) ;
841
+ AuthenticationUtility . CreateResponseAndPasswordHash ( password , AuthPluginData , out var nativeResponse , out m_passwordHash ) ;
842
+ payload = new ( nativeResponse ) ;
859
843
await SendReplyAsync ( payload , ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
860
844
return await ReceiveReplyAsync ( ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
861
845
@@ -908,14 +892,15 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
908
892
throw new NotSupportedException ( "'MySQL Server is requesting the insecure pre-4.1 auth mechanism (mysql_old_password). The user password must be upgraded; see https://dev.mysql.com/doc/refman/5.7/en/account-upgrades.html." ) ;
909
893
910
894
case "client_ed25519" :
911
- if ( ! AuthenticationPlugins . TryGetPlugin ( switchRequest . Name , out var ed25519Plugin ) )
895
+ if ( ! AuthenticationPlugins . TryGetPlugin ( switchRequest . Name , out var ed25519Plugin ) || ed25519Plugin is not IAuthenticationPlugin3 ed25519Plugin3 )
912
896
throw new NotSupportedException ( "You must install the MySqlConnector.Authentication.Ed25519 package and call Ed25519AuthenticationPlugin.Install to use client_ed25519 authentication." ) ;
913
- payload = new ( ed25519Plugin . CreateResponse ( password , switchRequest . Data ) ) ;
897
+ ed25519Plugin3 . CreateResponseAndPasswordHash ( password , switchRequest . Data , out var ed25519Response , out m_passwordHash ) ;
898
+ payload = new ( ed25519Response ) ;
914
899
await SendReplyAsync ( payload , ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
915
900
return await ReceiveReplyAsync ( ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
916
901
917
902
case "parsec" :
918
- if ( ! AuthenticationPlugins . TryGetPlugin ( switchRequest . Name , out var parsecPlugin ) )
903
+ if ( ! AuthenticationPlugins . TryGetPlugin ( switchRequest . Name , out var parsecPlugin ) || parsecPlugin is not IAuthenticationPlugin3 parsecPlugin3 )
919
904
throw new NotSupportedException ( "You must install the MySqlConnector.Authentication.Ed25519 package and call ParsecAuthenticationPlugin.Install to use parsec authentication." ) ;
920
905
payload = new ( [ ] ) ;
921
906
await SendReplyAsync ( payload , ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
@@ -925,7 +910,8 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
925
910
switchRequest . Data . CopyTo ( combinedData ) ;
926
911
payload . Span . CopyTo ( combinedData . Slice ( switchRequest . Data . Length ) ) ;
927
912
928
- payload = new ( parsecPlugin . CreateResponse ( password , combinedData ) ) ;
913
+ parsecPlugin3 . CreateResponseAndPasswordHash ( password , combinedData , out var parsecResponse , out m_passwordHash ) ;
914
+ payload = new ( parsecResponse ) ;
929
915
await SendReplyAsync ( payload , ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
930
916
return await ReceiveReplyAsync ( ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
931
917
@@ -2192,7 +2178,7 @@ protected override void OnStatementBegin(int index)
2192
2178
private PayloadData m_setNamesPayload ;
2193
2179
private byte [ ] ? m_pipelinedResetConnectionBytes ;
2194
2180
private Dictionary < string , PreparedStatements > ? m_preparedStatements ;
2195
- private string ? m_currentAuthenticationMethod ;
2181
+ private byte [ ] ? m_passwordHash ;
2196
2182
private byte [ ] ? m_remoteCertificateSha2Thumbprint ;
2197
2183
private SslPolicyErrors m_sslPolicyErrors ;
2198
2184
}
0 commit comments