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 1teqcj-00G0C6-0l for pgsql-hackers@arkaria.postgresql.org; Mon, 03 Feb 2025 07:12:01 +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 1teqci-00B3Ae-2Q for pgsql-hackers@arkaria.postgresql.org; Mon, 03 Feb 2025 07:12:00 +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 1teqch-00B3AW-Kb for pgsql-hackers@lists.postgresql.org; Mon, 03 Feb 2025 07:11:59 +0000 Received: from mail-pl1-x629.google.com ([2607:f8b0:4864:20::629]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.96) (envelope-from ) id 1teqcf-002uLU-0C for pgsql-hackers@lists.postgresql.org; Mon, 03 Feb 2025 07:11:58 +0000 Received: by mail-pl1-x629.google.com with SMTP id d9443c01a7336-21654fdd5daso68728235ad.1 for ; Sun, 02 Feb 2025 23:11:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1738566716; x=1739171516; darn=lists.postgresql.org; h=content-transfer-encoding:content-disposition:mime-version :message-id:subject:cc:to:from:date:from:to:cc:subject:date :message-id:reply-to; bh=Zps+AO4nizdxa+rDujkKFFJYdvYtq80QWTPJWv4BipY=; b=NyfZYb186AL72HZTP2YvFpAva3KAsUQ2k75Vg25Nfe1kZ/aK8jSMLJ0js+2RNsZY5I V80qqARU+MoQ2cGF8m97MhvKLMV4t2PebT9ziYl4vu0Fq1VaZF166I4XzUJ6yBRYEJ+0 qTF/orz7/7wxnO4reiZseE376+SyM0NPoJJ6IQPNbISeYQhQ1/5DDZC2OQvPcpXkBQxT mPhN/fcD2b/HDjuCdjro1J8BQ6S5ftwOoLab6JDwvlra3mOGc/NrKXhv1akjEbNJWFRq uhUUHViFA7gUn//JNMAlmjHDl2vQrle5ENwMKyGohFiiPU+yZRu+k9OXlYstNvgVwwu/ +NPQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1738566716; x=1739171516; h=content-transfer-encoding:content-disposition:mime-version :message-id:subject:cc:to:from:date:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Zps+AO4nizdxa+rDujkKFFJYdvYtq80QWTPJWv4BipY=; b=tV5+opFXivDEcffI9dhjC1sjxLbxSJwODJNqCNxbgLp705IOnL3ba9uvvmu4d9gVWA lEe35hsgnzXyq5FdAaBHVF1j2kkT6W1PG4KWZQaMSYMFPVnOdx/eiWYORbYVtRMwhKMu 7GXlBi+NUtwaQ6mIlhoxmI0D3PWZdpPG4Ct1xfyCLtVTiK4ixL3mrDCGOC6JjcWT0Em2 lYv96JqZ/fIwIFOZckn+p5jomnuBz6Bl8UQK69ibBoylNwVDJhN7Husp6TKjrqgtnpze TfYPvZiVFtqg8DDpH7yXOTtPvihTytL6SkoEM1CoMfmXNIVwtDnMSWlUbBqSMZ76QxeZ vDzg== X-Gm-Message-State: AOJu0Yy1nSNafENehDZVzC9b2VW+cQnNtiQF5U80/TWSfAtj16GQiwOJ QiifQLLUwMHkjfOMhhIdV2Q9BpeDURB8MqZihIQnOWSurJh93lDeLWpvy7x7ros= X-Gm-Gg: ASbGnctyHswOeuBi9qAKEldBFuzr42pzLZl9AZFmwdtKB1hFYxryn2dzTIMdaGvSLEC Ovjukxl4S2Nj+IjHjQWJ1Di6yg0+Wv46lMnBGuUTkg95gbMsrGeDyDVsmHJHEePi2pk3wsPQS23 2nodQvA87VJnLWlj07MzSuiZUpnukOqXO4uFhWqvWU+wnrXAvFS1He9vY4UYZKhT4mcDSq6Y5IT zFLH6PR6Ga/Jmo1fDQ8ViRpWgpkzelSBm8rDlutY6T3PfDoGloIdegWFl0rOVAghb92NYDyewgO oeZO/gPLOQU= X-Google-Smtp-Source: AGHT+IF1xS3pY8tYUmJmw59xkgMHnqjkm8JH78RQMDvTIanRnEEoo0vCAWuBzrG8bV6Tr26BDC6u1w== X-Received: by 2002:a17:902:ec81:b0:216:682f:175 with SMTP id d9443c01a7336-21dd7dd7c28mr319190025ad.49.1738566716120; Sun, 02 Feb 2025 23:11:56 -0800 (PST) Received: from localhost ([124.127.236.130]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-21de31ede21sm68885205ad.27.2025.02.02.23.11.55 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 02 Feb 2025 23:11:55 -0800 (PST) Date: Mon, 3 Feb 2025 15:11:48 +0800 From: Zixuan Fu To: pgsql-hackers@lists.postgresql.org Cc: Zixuan Fu Subject: Optimize scram_SaltedPassword performance Message-ID: MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="myCK34Dxo2urg0xY" Content-Disposition: inline Content-Transfer-Encoding: 8bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --myCK34Dxo2urg0xY Content-Type: text/plain; charset=us-ascii Content-Disposition: inline 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. 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. With this change, the performance improves by approximately **4x** (reducing execution time from 4ms to 1ms). The built-in PostgreSQL HMAC implementation does not support context reuse, and modifying it would require substantial changes. Therefore, `pg_hmac_reuse()` simply calls `pg_hmac_init()` in that case, maintaining the existing logic. Regards, Zixuan --myCK34Dxo2urg0xY Content-Type: text/x-patch; charset=utf-8 Content-Disposition: attachment; filename*0=v1-0001-Optimize-scram_SaltedPassword-with-HMAC-context-reuse; filename*1=".patch" Content-Transfer-Encoding: 8bit From e13d9e9c674d1cf4f671ca81b950ae010fd958a4 Mon Sep 17 00:00:00 2001 From: Zi-Xuan Fu Date: Mon, 3 Feb 2025 13:33:43 +0800 Subject: [PATCH v1] Optimize "scram_SaltedPassword" with OpenSSL HMAC context reuse `scram_SaltedPassword()` requires a large number of HMAC iterations, with SCRAM-SHA-256 defaulting to 4096 rounds. Previously, each iteration reinitialized the HMAC context, incurring significant overhead. However, OpenSSL optimizes this case by allowing context reuse when the key remains unchanged—requiring only a lightweight state reset by passing NULL as the key. To leverage this, I introduced `pg_hmac_reuse()`, which sets the key to NULL when OpenSSL is used. This optimization improves performance by approximately 4x (reducing execution time from 4ms to 1ms). The native PostgreSQL HMAC implementation does not support context reuse, and modifying it would require substantial changes. Therefore, `pg_hmac_reuse()` simply calls `pg_hmac_init()` in that case, maintaining the existing logic. --- src/common/hmac.c | 11 +++++++++++ src/common/hmac_openssl.c | 12 ++++++++++++ src/common/scram-common.c | 2 +- src/include/common/hmac.h | 1 + 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/common/hmac.c b/src/common/hmac.c index 9b85910672..6de95c44ee 100644 --- a/src/common/hmac.c +++ b/src/common/hmac.c @@ -214,6 +214,17 @@ pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len) return 0; } +/* + * pg_hmac_reuse + * + * Reuse a HMAC context with the same key. Returns 0 on success, -1 on failure. + */ +int +pg_hmac_reuse(pg_hmac_ctx *ctx, const uint8 *key, size_t len) +{ + return pg_hmac_init(ctx, key, len); +} + /* * pg_hmac_update * diff --git a/src/common/hmac_openssl.c b/src/common/hmac_openssl.c index 31cd188904..9e7a92884d 100644 --- a/src/common/hmac_openssl.c +++ b/src/common/hmac_openssl.c @@ -208,6 +208,18 @@ pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len) return 0; } +/* + * pg_hmac_reuse + * + * Reuse a HMAC context with the same key. Returns 0 on success, -1 on failure. + */ +int +pg_hmac_reuse(pg_hmac_ctx *ctx, const uint8 *key, size_t len) +{ + /* OpenSSL skips unnecessary reinitialization if the key is NULL */ + return pg_hmac_init(ctx, NULL, len); +} + /* * pg_hmac_update * diff --git a/src/common/scram-common.c b/src/common/scram-common.c index 400900c51c..5717daff28 100644 --- a/src/common/scram-common.c +++ b/src/common/scram-common.c @@ -84,7 +84,7 @@ scram_SaltedPassword(const char *password, CHECK_FOR_INTERRUPTS(); #endif - if (pg_hmac_init(hmac_ctx, (uint8 *) password, password_len) < 0 || + if (pg_hmac_reuse(hmac_ctx, (uint8 *) password, password_len) < 0 || pg_hmac_update(hmac_ctx, (uint8 *) Ui_prev, key_length) < 0 || pg_hmac_final(hmac_ctx, Ui, key_length) < 0) { diff --git a/src/include/common/hmac.h b/src/include/common/hmac.h index c4d069e49a..e9a0366d6e 100644 --- a/src/include/common/hmac.h +++ b/src/include/common/hmac.h @@ -22,6 +22,7 @@ typedef struct pg_hmac_ctx pg_hmac_ctx; extern pg_hmac_ctx *pg_hmac_create(pg_cryptohash_type type); extern int pg_hmac_init(pg_hmac_ctx *ctx, const uint8 *key, size_t len); +extern int pg_hmac_reuse(pg_hmac_ctx *ctx, const uint8 *key, size_t len); extern int pg_hmac_update(pg_hmac_ctx *ctx, const uint8 *data, size_t len); extern int pg_hmac_final(pg_hmac_ctx *ctx, uint8 *dest, size_t len); extern void pg_hmac_free(pg_hmac_ctx *ctx); -- 2.34.1 --myCK34Dxo2urg0xY--