@@ -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
@@ -532,7 +532,13 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
532
532
var useCachingSha2 = initialHandshake . AuthPluginName == "caching_sha2_password" ;
533
533
534
534
var password = GetPassword ( cs , connection ) ;
535
- using ( var handshakeResponsePayload = HandshakeResponse41Payload . Create ( initialHandshake , cs , password , useCachingSha2 , m_compressionMethod , connection . ZstandardPlugin ? . CompressionLevel , m_characterSet , m_supportsConnectionAttributes ? cs . ConnectionAttributes : null ) )
535
+ byte [ ] authenticationResponse ;
536
+ if ( useCachingSha2 )
537
+ authenticationResponse = AuthenticationUtility . CreateScrambleResponse ( Utility . TrimZeroByte ( initialHandshake . AuthPluginData . AsSpan ( ) ) , password ) ;
538
+ else
539
+ AuthenticationUtility . CreateResponseAndPasswordHash ( password , initialHandshake . AuthPluginData , out authenticationResponse , out m_passwordHash ) ;
540
+
541
+ using ( var handshakeResponsePayload = HandshakeResponse41Payload . Create ( initialHandshake , cs , authenticationResponse , m_compressionMethod , connection . ZstandardPlugin ? . CompressionLevel , m_characterSet , m_supportsConnectionAttributes ? cs . ConnectionAttributes : null ) )
536
542
await SendReplyAsync ( handshakeResponsePayload , ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
537
543
payload = await ReceiveReplyAsync ( ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
538
544
@@ -553,7 +559,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
553
559
}
554
560
else if ( ! m_isSecureConnection && password . Length != 0 )
555
561
{
556
- var publicKey = await GetRsaPublicKeyAsync ( m_currentAuthenticationMethod , cs , ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
562
+ var publicKey = await GetRsaPublicKeyAsync ( currentAuthenticationMethod , cs , ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
557
563
payload = await SendEncryptedPasswordAsync ( AuthPluginData , publicKey , password , ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
558
564
}
559
565
else
@@ -583,7 +589,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
583
589
// there is no shared secret that can be used to validate the certificate
584
590
Log . CertificateErrorNoPassword ( m_logger , Id , m_sslPolicyErrors ) ;
585
591
}
586
- else if ( ValidateFingerprint ( ok . StatusInfo , initialHandshake . AuthPluginData . AsSpan ( 0 , 20 ) , password ) )
592
+ else if ( ValidateFingerprint ( ok . StatusInfo , initialHandshake . AuthPluginData . AsSpan ( 0 , 20 ) ) )
587
593
{
588
594
Log . CertificateErrorValidThumbprint ( m_logger , Id , m_sslPolicyErrors ) ;
589
595
ignoreCertificateError = true ;
@@ -649,36 +655,20 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
649
655
/// </summary>
650
656
/// <param name="validationHash">The validation hash received from the server.</param>
651
657
/// <param name="challenge">The auth plugin data from the initial handshake.</param>
652
- /// <param name="password">The user's password.</param>
653
658
/// <returns><c>true</c> if the validation hash matches the locally-computed value; otherwise, <c>false</c>.</returns>
654
- private bool ValidateFingerprint ( byte [ ] ? validationHash , ReadOnlySpan < byte > challenge , string password )
659
+ private bool ValidateFingerprint ( byte [ ] ? validationHash , ReadOnlySpan < byte > challenge )
655
660
{
656
661
// expect 0x01 followed by 64 hex characters giving a SHA2 hash
657
662
if ( validationHash ? . Length != 65 || validationHash [ 0 ] != 1 )
658
663
return false ;
659
664
660
- byte [ ] ? passwordHashResult = null ;
661
- switch ( m_currentAuthenticationMethod )
662
- {
663
- case "mysql_native_password" :
664
- passwordHashResult = AuthenticationUtility . HashPassword ( [ ] , password , onlyHashPassword : true ) ;
665
- break ;
666
-
667
- case "client_ed25519" :
668
- AuthenticationPlugins . TryGetPlugin ( m_currentAuthenticationMethod , out var ed25519Plugin ) ;
669
- if ( ed25519Plugin is IAuthenticationPlugin2 plugin2 )
670
- passwordHashResult = plugin2 . CreatePasswordHash ( password , challenge ) ;
671
- break ;
672
- }
673
- if ( passwordHashResult is null )
665
+ // the authentication plugin must have provided a password hash (via IAuthenticationPlugin3) that we saved for future use
666
+ if ( m_passwordHash is null )
674
667
return false ;
675
668
676
- Span < byte > combined = stackalloc byte [ 32 + challenge . Length + passwordHashResult . Length ] ;
677
- passwordHashResult . CopyTo ( combined ) ;
678
- challenge . CopyTo ( combined [ passwordHashResult . Length ..] ) ;
679
- m_remoteCertificateSha2Thumbprint ! . CopyTo ( combined [ ( passwordHashResult . Length + challenge . Length ) ..] ) ;
680
-
669
+ // hash password hash || scramble || certificate thumbprint
681
670
Span < byte > hashBytes = stackalloc byte [ 32 ] ;
671
+ Span < byte > combined = [ .. m_passwordHash , .. challenge , .. m_remoteCertificateSha2Thumbprint ! ] ;
682
672
#if NET5_0_OR_GREATER
683
673
SHA256 . TryHashData ( combined , hashBytes , out _ ) ;
684
674
#else
@@ -827,8 +817,8 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
827
817
DatabaseOverride = null ;
828
818
}
829
819
var password = GetPassword ( cs , connection ) ;
830
- var hashedPassword = AuthenticationUtility . CreateAuthenticationResponse ( AuthPluginData ! , password ) ;
831
- using ( var changeUserPayload = ChangeUserPayload . Create ( cs . UserID , hashedPassword , cs . Database , m_characterSet , m_supportsConnectionAttributes ? cs . ConnectionAttributes : null ) )
820
+ AuthenticationUtility . CreateResponseAndPasswordHash ( password , AuthPluginData , out var nativeResponse , out m_passwordHash ) ;
821
+ using ( var changeUserPayload = ChangeUserPayload . Create ( cs . UserID , nativeResponse , cs . Database , m_characterSet , m_supportsConnectionAttributes ? cs . ConnectionAttributes : null ) )
832
822
await SendAsync ( changeUserPayload , ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
833
823
payload = await ReceiveReplyAsync ( ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
834
824
if ( payload . HeaderByte == AuthenticationMethodSwitchRequestPayload . Signature )
@@ -872,13 +862,12 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
872
862
// if the server didn't support the hashed password; rehash with the new challenge
873
863
var switchRequest = AuthenticationMethodSwitchRequestPayload . Create ( payload . Span ) ;
874
864
Log . SwitchingToAuthenticationMethod ( m_logger , Id , switchRequest . Name ) ;
875
- m_currentAuthenticationMethod = switchRequest . Name ;
876
865
switch ( switchRequest . Name )
877
866
{
878
867
case "mysql_native_password" :
879
868
AuthPluginData = switchRequest . Data ;
880
- var hashedPassword = AuthenticationUtility . CreateAuthenticationResponse ( AuthPluginData , password ) ;
881
- payload = new ( hashedPassword ) ;
869
+ AuthenticationUtility . CreateResponseAndPasswordHash ( password , AuthPluginData , out var nativeResponse , out m_passwordHash ) ;
870
+ payload = new ( nativeResponse ) ;
882
871
await SendReplyAsync ( payload , ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
883
872
return await ReceiveReplyAsync ( ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
884
873
@@ -931,14 +920,15 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
931
920
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." ) ;
932
921
933
922
case "client_ed25519" :
934
- if ( ! AuthenticationPlugins . TryGetPlugin ( switchRequest . Name , out var ed25519Plugin ) )
923
+ if ( ! AuthenticationPlugins . TryGetPlugin ( switchRequest . Name , out var ed25519Plugin ) || ed25519Plugin is not IAuthenticationPlugin3 ed25519Plugin3 )
935
924
throw new NotSupportedException ( "You must install the MySqlConnector.Authentication.Ed25519 package and call Ed25519AuthenticationPlugin.Install to use client_ed25519 authentication." ) ;
936
- payload = new ( ed25519Plugin . CreateResponse ( password , switchRequest . Data ) ) ;
925
+ ed25519Plugin3 . CreateResponseAndPasswordHash ( password , switchRequest . Data , out var ed25519Response , out m_passwordHash ) ;
926
+ payload = new ( ed25519Response ) ;
937
927
await SendReplyAsync ( payload , ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
938
928
return await ReceiveReplyAsync ( ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
939
929
940
930
case "parsec" :
941
- if ( ! AuthenticationPlugins . TryGetPlugin ( switchRequest . Name , out var parsecPlugin ) )
931
+ if ( ! AuthenticationPlugins . TryGetPlugin ( switchRequest . Name , out var parsecPlugin ) || parsecPlugin is not IAuthenticationPlugin3 parsecPlugin3 )
942
932
throw new NotSupportedException ( "You must install the MySqlConnector.Authentication.Ed25519 package and call ParsecAuthenticationPlugin.Install to use parsec authentication." ) ;
943
933
payload = new ( [ ] ) ;
944
934
await SendReplyAsync ( payload , ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
@@ -948,7 +938,8 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
948
938
switchRequest . Data . CopyTo ( combinedData ) ;
949
939
payload . Span . CopyTo ( combinedData . Slice ( switchRequest . Data . Length ) ) ;
950
940
951
- payload = new ( parsecPlugin . CreateResponse ( password , combinedData ) ) ;
941
+ parsecPlugin3 . CreateResponseAndPasswordHash ( password , combinedData , out var parsecResponse , out m_passwordHash ) ;
942
+ payload = new ( parsecResponse ) ;
952
943
await SendReplyAsync ( payload , ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
953
944
return await ReceiveReplyAsync ( ioBehavior , cancellationToken ) . ConfigureAwait ( false ) ;
954
945
@@ -2216,7 +2207,7 @@ protected override void OnStatementBegin(int index)
2216
2207
private PayloadData m_setNamesPayload ;
2217
2208
private byte [ ] ? m_pipelinedResetConnectionBytes ;
2218
2209
private Dictionary < string , PreparedStatements > ? m_preparedStatements ;
2219
- private string ? m_currentAuthenticationMethod ;
2210
+ private byte [ ] ? m_passwordHash ;
2220
2211
private byte [ ] ? m_remoteCertificateSha2Thumbprint ;
2221
2212
private SslPolicyErrors m_sslPolicyErrors ;
2222
2213
}
0 commit comments