Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1teutC-00GMIR-8a for pgsql-hackers@arkaria.postgresql.org; Mon, 03 Feb 2025 11:45:19 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1teutB-00Cl64-5J for pgsql-hackers@arkaria.postgresql.org; Mon, 03 Feb 2025 11:45:17 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1teutA-00Cl5w-Mr for pgsql-hackers@lists.postgresql.org; Mon, 03 Feb 2025 11:45:16 +0000 Received: from mail.postgrespro.ru ([93.174.131.139]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1teut7-002wGd-1d for pgsql-hackers@lists.postgresql.org; Mon, 03 Feb 2025 11:45:15 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=postgrespro.ru; s=mx2023; t=1738583109; bh=GUpd1q79JXjExRVnxvkXR2jg4/Nz7W8ChZBpxyxVS1M=; h=Message-ID:Date:User-Agent:Subject:To:Cc:References:From: In-Reply-To:From; b=bbCMPV0y64/M8ODN/2TIVDsuvcB4niOcTx+zUimVPY6nnrIabbbyyIMxU7Wvn7Zly 7r8VpgAZhraIHUPUstw5LVdMaRW7Vn3OgFBZHzsJxMN+ke45izwjXnGQp4FNgvO92F fhIGmTa69Qx/Cg9I0Ut8zPi6wPAsUPFBvDoRtD9nKzEL9s1Zs/8wSLJuwXz8j9Dxwr 1tmQ5aMsmkLakoyrfrJHokWp9k71XjDVtbyENryjWBz3XDREnForQKC3A1MQk2B384 Mgi6906XKqdecnj7hcDU8OiG1VZ3maM/4Br1DE6DoXgJq7JhWGhjn0zbK8DfFrudbc Kk1u8Bdaj4ZqQ== Received: from [172.30.50.118] (unknown [172.30.50.118]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (Client did not present a certificate) (Authenticated sender: y.sokolov@postgrespro.ru) by mail.postgrespro.ru (Postfix/465) with ESMTPSA id CB08460273; Mon, 3 Feb 2025 14:45:08 +0300 (MSK) Message-ID: <886edb58-891a-4bf9-ad16-431c3994e1f0@postgrespro.ru> Date: Mon, 3 Feb 2025 14:45:07 +0300 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: Optimize scram_SaltedPassword performance To: Daniel Gustafsson , Zixuan Fu Cc: PostgreSQL Hackers References: <62fbf768-d2d0-4bb2-840d-a8d2880af82b@postgrespro.ru> <4ABFA50D-9558-4368-8A62-E2B78DE39E0B@yesql.se> Content-Language: en-US From: Yura Sokolov In-Reply-To: <4ABFA50D-9558-4368-8A62-E2B78DE39E0B@yesql.se> Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-KSMG-AntiPhishing: NotDetected X-KSMG-AntiSpam-Interceptor-Info: not scanned X-KSMG-AntiSpam-Status: not scanned, disabled by settings X-KSMG-AntiVirus: Kaspersky Secure Mail Gateway, version 2.1.0.7854, bases: 2025/02/03 07:33:00 #27201296 X-KSMG-AntiVirus-Status: NotDetected, skipped X-KSMG-LinksScanning: not scanned, disabled by settings X-KSMG-Message-Action: skipped X-KSMG-Rule-ID: 1 List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk 03.02.2025 14:17, Daniel Gustafsson пишет: >> On 3 Feb 2025, at 08:45, Yura Sokolov wrote: >> >> 03.02.2025 10:11, Zixuan Fu пишет: >>> Hi Hackers, >>> >>> While profiling a program with `perf`, I noticed that `scram_SaltedPassword` >>> consumed more CPU time than expected. After some investigation, I found >>> that the function performs many HMAC iterations (4096 rounds for >>> SCRAM-SHA-256), and each iteration reinitializes the HMAC context, causing >>> excessive overhead. > > While I don't disagree with speeding up this in general, the whole point of the > SCRAM iterations is to take a lot of time as a way to combat brute forcing. > Any attacker is likely to use a patched libpq (or not use libpq at all) so > clientside it doesn't matter as much but if we make it 4x faster serverside we > don't really achieve much other than making attacks more feasible. > > The relevant portion from RFC 7677 §4 is: > > The strength of this mechanism is dependent in part on the hash > iteration-count, as denoted by "i" in [RFC5802]. As a rule of thumb, > the hash iteration-count should be such that a modern machine will take > 0.1 seconds to perform the complete algorithm; however, this is > unlikely to be practical on mobile devices and other relatively low- > performance systems. At the time this was written, the rule of thumb > gives around 15,000 iterations required; however, a hash iteration- > count of 4096 takes around 0.5 seconds on current mobile handsets. > This computational cost can be avoided by caching the ClientKey > (assuming the Salt and hash iteration-count is stable). Therefore, the > recommendation of this specification is that the hash iteration- count > SHOULD be at least 4096, but careful consideration ought to be given to > using a significantly higher value, particularly where mobile use is > less important. > > The numbers are quite outdated but the gist of it holds. If we speed it up > serverside we need to counter that with a higher iteration count, and for a > rogue client it's unlikely to matter since it wont use our code anyways. > Speeding up HMAC for other usecases is a different story (but also likely to > have less measurable performance impact). There is no sense to not speedup server if client can be made faster. HMAC should protect from an attacker who have very optimized software on very powerful hardware, since it should protect against BRUTE force against known cipher text either. If 4096 iterations could be performed fast in attacker's environment, there's no point to slow down our environment. It is meaningless. >>> OpenSSL has an optimization for this case: when the key remains the >>> same, the HMAC context can be reused with a lightweight state reset by >>> passing NULL as the key. To take advantage of this, I introduced >>> `pg_hmac_reuse()`, which replaces the key with NULL when OpenSSL is used. > >> Where `prev_key`, `prev_len` and `prev_hash` are static variables, filled >> in `pg_hmac_init`. > > Storing any part of a cryptograhic calculation, let alone a key, in a static > variable doesn't seem entirely like a best practice, and it also wont be > threadsafe. `prev_key` stores not the key, but pointer to key. Just to ensure `pg_hmac_reuse` were called with exactly same key. It doesn't leak the key itself. Storing `prev_hash` is more considered as "storing part of key". I may agree it is not exactly good idea if `key` is "human-brain-generated". Thread-safety is more-or-less trivial with `thread_local` and synonyms for. -- Yura