[DRAFT][mysql] Clean-room auth handshake codec (scramble + handshake + packet, Stage 1a of #2093)#3310
Conversation
bc95c03 to
d8752f1
Compare
Companion to the policy/mysql_auth/ codec landed in the previous commit. Tables every algorithm reference, every upstream MySQL/ MariaDB/Percona unit test (24 total) and integration test (5 from libmariadb), maps each to its brpc equivalent or marks it server-side-only, and lists the scope limits of the auth-codec CL.
9a44a6d to
d8752f1
Compare
|
Hi @wwbmmm @chenBright @yanglimingcn — process question on integration testing for this codec, before I write more code. The pure-codec unit tests (59 across scramble / handshake / packet) are in place. For end-to-end auth verification I'd like to add an integration test that drives the full handshake against a real brpc already has a precedent for this in Two questions before I mirror the pattern:
The upstream MariaDB integration tests I'd want to mirror ( Also flagging — CI runs on this branch are currently in |
…packet) Picks up the connection-phase authentication layer of MySQL protocol support per the staged plan announced on dev@brpc.apache.org and PR apache#2093. Three modules under src/brpc/policy/mysql_auth/: - mysql_auth_scramble: mysql_native_password (SHA1 XOR), plus caching_sha2_password fast path (SHA256 XOR) and slow path with both branches: - RSA-OAEP via OpenSSL EVP_PKEY_encrypt + PKCS1_OAEP_PADDING - TLS-cleartext (NUL-terminated password) selected at runtime via CachingSha2PasswordSlowPath(..., bool is_ssl = false). Default is_ssl=false preserves RSA behavior for callers not yet threaded with the connection's TLS state. - mysql_auth_packet: length-encoded int/string, 4-byte packet header, NUL-terminated string. - mysql_auth_handshake: HandshakeV10 parse, HandshakeResponse41 build, AuthSwitchRequest parse, AuthMoreData parse. All written clean-room from MySQL's public protocol documentation; no GPL-licensed source was consulted. SHA-256 and RSA paths use OpenSSL EVP -- works under both OpenSSL and BoringSSL. 75 unit tests across three files under test/mysql_auth/: 36 in brpc_mysql_auth_scramble_unittest.cpp 19 in brpc_mysql_auth_handshake_unittest.cpp 20 in brpc_mysql_auth_packet_unittest.cpp Mirrors every client-relevant case from mysql/mysql-server's GPLv2 unittest/gunit/sha2_password-t.cc + sha256_scramble_t.cc with independently re-derived hex vectors. Server-side cases (cache, storage format, *_verification_*) are out of scope for a client library; full mapping table linked from the PR description. Scope limits in this CL: auth codec only. No PROTOCOL_MYSQL registration, no Socket integration, no compressed packets, no prepared statements, no transactions. All land in the follow-up CL. TLS state plumbing in Stage 1c flows the dispatcher's Socket::is_ssl() into the trailing argument here -- no further API change. Replaces the earlier src/brpc/policy/mysql_auth_hash.{h,cpp} + test/brpc_mysql_auth_hash_unittest.cpp; those files are moved into the new mysql_auth/ subdirectory and renamed.
d8752f1 to
38a01f3
Compare
|
Update — pushed Two changes since the last comment:
Asks:
PR ready for first-pass review whenever you have bandwidth. |
[mysql] Clean-room MySQL client auth handshake (scramble + handshake + packet, Stage 1a of #2093)Picks up MySQL protocol support per the staged plan announced on
dev@brpc.apache.organd on PR #2093.
This is Stage 1a — the connection-phase authentication layer as
pure functions, with no integration into
Socketyet (that lands inthe follow-up CL).
What's in this CL
New subsystem under
src/brpc/policy/mysql_auth/, three modules:mysql_auth_scramble.{h,cpp}NativePasswordScramble,CachingSha2PasswordScramble,CachingSha2PasswordRsaEncrypt,CachingSha2PasswordCleartext,CachingSha2PasswordSlowPath(..., bool is_ssl = false)mysql_auth_packet.{h,cpp}mysql_auth_handshake.{h,cpp}ParseHandshakeV10,BuildHandshakeResponse41,ParseAuthSwitchRequest,ParseAuthMoreDataAll written clean-room from the public MySQL protocol documentation;
SHA-256 and RSA-OAEP use OpenSSL EVP (works under both OpenSSL and
BoringSSL — verified in CI).
Test coverage — by-name mapping to upstream
75 unit tests across 3 files under
test/mysql_auth/:brpc_mysql_auth_scramble_unittest.cpp— 36 testsbrpc_mysql_auth_handshake_unittest.cpp— 19 testsbrpc_mysql_auth_packet_unittest.cpp— 20 testsmysql/mysql-server—unittest/gunit/sha2_password-t.cc(GPLv2)SHA256_digestTest.InitDigestContextSHA256_digestTest.DigestSingleStageSHA256_digestTest.DigestMultiStageSHA256_digestTest.GenerateScrambleMysqlCachingSha2PasswordTest.KnownVector_UpstreamMysqlServerTest+KnownVector_PasswordPassword_AsciiSalt+KnownVector_PasswordSecret_BinarySalt(independently regenerated vectors, including the upstream's ownpw="Ab12#$Cd56&*"/nonce="eF!@34gH%^78"pair)SHA256_digestTest.ValidateScrambleSHA256_digestTest.generate_sha256_scrambleSHA256_digestTest.validate_sha256_scrambleSHA256_digestTest.SHA2_password_cacheSHA256_digestTest.Caching_sha2_password_Serialize_Deserializemysql.usertable storage formatSHA256_digestTest.Caching_sha2_password_generate_fast_digestSHA256_digestTest.Caching_sha2_password_generate_sha2_multi_hashSHA256_digestTest.Caching_sha2_password_authenticate_fast_authenticateSHA256_digestTest.Caching_sha2_password_authenticate_sanityMysqlCachingSha2PasswordTest.EmptyPasswordReturnsEmptyString+LongPassword(empty + overlong-input edge cases — the parts of the upstream sanity test that are client-relevant)mysql/mysql-server—unittest/gunit/xplugin/xcl/sha256_scramble_t.cc(GPLv2)SHA256_digest_test.init_digest_contextSHA256_digest_test.digest_single_stageSHA256_digest_test.digest_multi_stageSHA256_digest_test.generate_scrambleSHA256_digest_test.generate_sha256_scramblemysql/mysql-server—unittest/gunit/xplugin/xpl/user_password_verification_t.cc(GPLv2)User_password_verification.native_plain_verification_get_saltUser_password_verification.native_plain_verification_passUser_password_verification.native_plain_verification_failUser_password_verification.sha256_plain_verification_get_saltUser_password_verification.sha256_plain_verification_passUser_password_verification.sha256_plain_verification_failUser_password_verification.sha256_memory_verification_get_saltUser_password_verification.sha256_memory_verification_passUser_password_verification.sha256_memory_verification_no_entryUser_password_verification.sha256_memory_verification_failSubtotal: 28 upstream unit tests. 5 are client-relevant (#4, #6, #13, #17, #18 — all flavors of the fast-path scramble + the sanity edge cases) and are mirrored in
MysqlCachingSha2PasswordTest.*with independently re-derived hex vectors. 23 are server-side-only (cache, storage, verification) and out of scope for a client library.mysql/mysql-connector-cppInspected
cdk/mysqlx/auth_hash.cc— implementation only, no unit tests. Coverage in this repo is via Boost.Test integration suites only.MariaDB/mariadb-connector-c—unittest/libmariadb/connection.c(integration only)Source file (GPLv2 + FLOSS Exception).
These are live-server tests against
mysql_real_connect(). Cannot run without a realmysqld/mariadbd; deferred to the integration-test CL (see "Deferred work" below).test_auth256caching_sha2_passwordfull handshake — fast-auth-ok and cache-miss + RSA pubkey pathstest_default_authtest_conc312native→caching_sha2or vice-versa)test_parsecparsec(ed25519) auth plugintest_change_userCOM_CHANGE_USERmid-session re-authtest_expired_pwSubtotal: 6 client-relevant integration tests. 0 mirrored in this CL — running them requires
system("which mysqld")-style server bring-up, which I'd like to add as a follow-up commit on this same PR (see reviewer-ping comment asking about preferred pattern).percona/percona-serverNo auth tests beyond what upstream mysql-server already provides.
brpc test inventory
MysqlNativePasswordTestMysqlCachingSha2PasswordTestMysqlCachingSha2RsaTestMysqlCachingSha2CleartextTestMysqlCachingSha2SlowPathTestis_sslflag routing)HandshakeV10TestHandshakeResponse41TestAuthSwitchRequestTestAuthMoreDataTestLenencIntTestLenencStringTestPacketHeaderTestNullTermStringTestLimitations of this CL
mysql_native_password+caching_sha2_passwordsupported.No
sha256_password(deprecated), no MariaDBparsec/ed25519, noauth_pam/LDAP/Kerberos. Callers must refuse unknown plugin namesthey see in an
AuthSwitchRequest.caching_sha2_passwordslow path supports both RSA-OAEP andTLS-cleartext branches. The
CachingSha2PasswordSlowPath()dispatcher takes
bool is_ssl = falseas its trailing argument:when the caller has confirmed the channel is secure
(TLS/unix-socket/shared-mem) it returns the cleartext payload
(1 round trip); otherwise it falls through to RSA-OAEP
(3 round trips). Callers that haven't yet been threaded with
the TLS flag get the safe default (RSA-OAEP, works everywhere).
See "TLS shortcut via
is_sslflag" section below.Socketintegration. NoPROTOCOL_MYSQLregistration, noMysqlChannel. Auth codec is pure functions. Wiring is the next CL.transactions. All planned for later CLs in the Add Mysql Protocol #2093 takeover
sequence.
so the codec assumes the caller has already reassembled.
verified against a real
mysqld. Deferred to a follow-up commit onthis PR — pattern question open with reviewers (see comment thread).
TLS shortcut via
is_sslflagWhat libmysqlclient does
libmysqlclientpicks the slow-path strategy at runtime viais_secure_transport(mysql)insql-common/client_authentication.cc:770-787,which returns true for
VIO_TYPE_SSL(with a non-null cipher),VIO_TYPE_SHARED_MEMORY, orVIO_TYPE_SOCKET(unix socket). Itsresult is stored at line 810 and gated at
line 871:
So: TLS / unix-socket / shared-mem → send cleartext password (one
round trip); plain TCP → request RSA pubkey, encrypt, send ciphertext
(three round trips).
How this CL exposes the same branch
The codec layer keeps the pure-function discipline: it does not
include
brpc/socket.hor accept aSocket*. Instead it accepts adefaulted
bool is_sslparameter and lets the caller (the futureStage-1c protocol dispatcher) make the policy decision.
When the Stage-1c CL wires
PROTOCOL_MYSQLand gains aSocket*atthe dispatcher layer, it threads
Socket::is_ssl()straight into the trailing argument — no API change required.
Test coverage of the new branch
The
is_ssl=truepath adds 11 tests in two suites:MysqlCachingSha2CleartextTest(5 tests) — directly exercises thecleartext payload helper: NUL terminator appending, empty-password
early return, embedded-NUL passthrough, UTF-8 multibyte passthrough,
300-character overlong-password sanity.
MysqlCachingSha2SlowPathTest(6 tests) — exercises thedispatcher: default
is_ssl=falseroutes to RSA (verified bydecrypt round-trip), explicit
is_ssl=falselikewise,is_ssl=truereturns the cleartext payload,
is_ssl=trueignores bad salt + badpubkey arguments,
is_ssl=trueempty password returns empty,is_ssl=falsewith bad pubkey returns empty.Cost comparison (per fresh login, server cache cold)
is_ssl=true(cleartext)is_ssl=false(RSA-OAEP)Steady-state queries take the fast-auth path (server cache hit, no
RSA, no cleartext) regardless of
is_ssl. The dispatcher only fireson cold-cache fresh logins.
Deferred work (in order)
PROTOCOL_MYSQLregistration +MysqlChannel+Socket::WriteOptions::auth_flagsrebase. ThreadsSocket::is_ssl()straight intoCachingSha2PasswordSlowPath'strailing argument.
mysqldintegrationtests mirroring
test_auth256,test_default_auth,test_conc312,test_change_user,test_expired_pwusing the redis-stylesystem()-spawn pattern fromtest/brpc_redis_unittest.cpp.COM_QUERY), basic result-set parsing.MysqlTransaction).COM_STMT_*).Per @yanglimingcn — relationship to #2093
This CL covers the auth-handshake slice of PR #2093
with two key changes:
mysql_auth_hash.cppfrom the original PR (which was flaggedGPL-licensed by @wwbmmm at
#2093 (discussion))
is fully reimplemented from the public spec.
caching_sha2_password(bothfast and slow paths), which Add Mysql Protocol #2093 did not cover — this lets the
follow-up CL connect to MySQL 8.0+ servers under their default
auth plugin without an extra round trip.
I am not pushing to #2093's branch; the original commit history stays
intact.
Per @wwbmmm — review request
This is ready for review whenever you have bandwidth. The protocol
choices to focus on:
do at this layer that I've punted.
External spec gist with the full mapping table (this section, plus
algorithm references and license posture):
https://gist.github.com/rajvarun77/e84f97360c553230966e305438dfbd0e
CI: all 17 jobs passed on the previous draft (
bc95c031). Currentpush (
d8752f14) is inaction_requiredstate pending committerapproval — see the comment thread.