1 /*-------------------------------------------------------------------------
3 * Shared frontend/backend code for SCRAM authentication
5 * This contains the common low-level functions needed in both frontend and
6 * backend, for implement the Salted Challenge Response Authentication
7 * Mechanism (SCRAM), per IETF's RFC 5802.
9 * Portions Copyright (c) 2017-2023, PostgreSQL Global Development Group
12 * src/common/scram-common.c
14 *-------------------------------------------------------------------------
19 #include "postgres_fe.h"
22 #include "common/base64.h"
23 #include "common/hmac.h"
24 #include "common/scram-common.h"
25 #include "port/pg_bswap.h"
28 * Calculate SaltedPassword.
30 * The password should already be normalized by SASLprep. Returns 0 on
31 * success, -1 on failure with *errstr pointing to a message about the
35 scram_SaltedPassword(const char *password
,
36 pg_cryptohash_type hash_type
, int key_length
,
37 const char *salt
, int saltlen
, int iterations
,
38 uint8
*result
, const char **errstr
)
40 int password_len
= strlen(password
);
41 uint32 one
= pg_hton32(1);
44 uint8 Ui
[SCRAM_MAX_KEY_LEN
];
45 uint8 Ui_prev
[SCRAM_MAX_KEY_LEN
];
46 pg_hmac_ctx
*hmac_ctx
= pg_hmac_create(hash_type
);
50 *errstr
= pg_hmac_error(NULL
); /* returns OOM */
55 * Iterate hash calculation of HMAC entry using given salt. This is
56 * essentially PBKDF2 (see RFC2898) with HMAC() as the pseudorandom
61 if (pg_hmac_init(hmac_ctx
, (uint8
*) password
, password_len
) < 0 ||
62 pg_hmac_update(hmac_ctx
, (uint8
*) salt
, saltlen
) < 0 ||
63 pg_hmac_update(hmac_ctx
, (uint8
*) &one
, sizeof(uint32
)) < 0 ||
64 pg_hmac_final(hmac_ctx
, Ui_prev
, key_length
) < 0)
66 *errstr
= pg_hmac_error(hmac_ctx
);
67 pg_hmac_free(hmac_ctx
);
71 memcpy(result
, Ui_prev
, key_length
);
73 /* Subsequent iterations */
74 for (i
= 2; i
<= iterations
; i
++)
76 if (pg_hmac_init(hmac_ctx
, (uint8
*) password
, password_len
) < 0 ||
77 pg_hmac_update(hmac_ctx
, (uint8
*) Ui_prev
, key_length
) < 0 ||
78 pg_hmac_final(hmac_ctx
, Ui
, key_length
) < 0)
80 *errstr
= pg_hmac_error(hmac_ctx
);
81 pg_hmac_free(hmac_ctx
);
85 for (j
= 0; j
< key_length
; j
++)
87 memcpy(Ui_prev
, Ui
, key_length
);
90 pg_hmac_free(hmac_ctx
);
96 * Calculate hash for a NULL-terminated string. (The NULL terminator is
97 * not included in the hash). Returns 0 on success, -1 on failure with *errstr
98 * pointing to a message about the error details.
101 scram_H(const uint8
*input
, pg_cryptohash_type hash_type
, int key_length
,
102 uint8
*result
, const char **errstr
)
104 pg_cryptohash_ctx
*ctx
;
106 ctx
= pg_cryptohash_create(hash_type
);
109 *errstr
= pg_cryptohash_error(NULL
); /* returns OOM */
113 if (pg_cryptohash_init(ctx
) < 0 ||
114 pg_cryptohash_update(ctx
, input
, key_length
) < 0 ||
115 pg_cryptohash_final(ctx
, result
, key_length
) < 0)
117 *errstr
= pg_cryptohash_error(ctx
);
118 pg_cryptohash_free(ctx
);
122 pg_cryptohash_free(ctx
);
127 * Calculate ClientKey. Returns 0 on success, -1 on failure with *errstr
128 * pointing to a message about the error details.
131 scram_ClientKey(const uint8
*salted_password
,
132 pg_cryptohash_type hash_type
, int key_length
,
133 uint8
*result
, const char **errstr
)
135 pg_hmac_ctx
*ctx
= pg_hmac_create(hash_type
);
139 *errstr
= pg_hmac_error(NULL
); /* returns OOM */
143 if (pg_hmac_init(ctx
, salted_password
, key_length
) < 0 ||
144 pg_hmac_update(ctx
, (uint8
*) "Client Key", strlen("Client Key")) < 0 ||
145 pg_hmac_final(ctx
, result
, key_length
) < 0)
147 *errstr
= pg_hmac_error(ctx
);
157 * Calculate ServerKey. Returns 0 on success, -1 on failure with *errstr
158 * pointing to a message about the error details.
161 scram_ServerKey(const uint8
*salted_password
,
162 pg_cryptohash_type hash_type
, int key_length
,
163 uint8
*result
, const char **errstr
)
165 pg_hmac_ctx
*ctx
= pg_hmac_create(hash_type
);
169 *errstr
= pg_hmac_error(NULL
); /* returns OOM */
173 if (pg_hmac_init(ctx
, salted_password
, key_length
) < 0 ||
174 pg_hmac_update(ctx
, (uint8
*) "Server Key", strlen("Server Key")) < 0 ||
175 pg_hmac_final(ctx
, result
, key_length
) < 0)
177 *errstr
= pg_hmac_error(ctx
);
188 * Construct a SCRAM secret, for storing in pg_authid.rolpassword.
190 * The password should already have been processed with SASLprep, if necessary!
192 * If iterations is 0, default number of iterations is used. The result is
193 * palloc'd or malloc'd, so caller is responsible for freeing it.
195 * On error, returns NULL and sets *errstr to point to a message about the
199 scram_build_secret(pg_cryptohash_type hash_type
, int key_length
,
200 const char *salt
, int saltlen
, int iterations
,
201 const char *password
, const char **errstr
)
203 uint8 salted_password
[SCRAM_MAX_KEY_LEN
];
204 uint8 stored_key
[SCRAM_MAX_KEY_LEN
];
205 uint8 server_key
[SCRAM_MAX_KEY_LEN
];
209 int encoded_salt_len
;
210 int encoded_stored_len
;
211 int encoded_server_len
;
214 /* Only this hash method is supported currently */
215 Assert(hash_type
== PG_SHA256
);
218 iterations
= SCRAM_DEFAULT_ITERATIONS
;
220 /* Calculate StoredKey and ServerKey */
221 if (scram_SaltedPassword(password
, hash_type
, key_length
,
222 salt
, saltlen
, iterations
,
223 salted_password
, errstr
) < 0 ||
224 scram_ClientKey(salted_password
, hash_type
, key_length
,
225 stored_key
, errstr
) < 0 ||
226 scram_H(stored_key
, hash_type
, key_length
,
227 stored_key
, errstr
) < 0 ||
228 scram_ServerKey(salted_password
, hash_type
, key_length
,
229 server_key
, errstr
) < 0)
231 /* errstr is filled already here */
235 elog(ERROR
, "could not calculate stored key and server key: %s",
242 * SCRAM-SHA-256$<iteration count>:<salt>$<StoredKey>:<ServerKey>
245 encoded_salt_len
= pg_b64_enc_len(saltlen
);
246 encoded_stored_len
= pg_b64_enc_len(key_length
);
247 encoded_server_len
= pg_b64_enc_len(key_length
);
249 maxlen
= strlen("SCRAM-SHA-256") + 1
250 + 10 + 1 /* iteration count */
251 + encoded_salt_len
+ 1 /* Base64-encoded salt */
252 + encoded_stored_len
+ 1 /* Base64-encoded StoredKey */
253 + encoded_server_len
+ 1; /* Base64-encoded ServerKey */
256 result
= malloc(maxlen
);
259 *errstr
= _("out of memory");
263 result
= palloc(maxlen
);
266 p
= result
+ sprintf(result
, "SCRAM-SHA-256$%d:", iterations
);
269 encoded_result
= pg_b64_encode(salt
, saltlen
, p
, encoded_salt_len
);
270 if (encoded_result
< 0)
272 *errstr
= _("could not encode salt");
277 elog(ERROR
, "%s", *errstr
);
284 encoded_result
= pg_b64_encode((char *) stored_key
, key_length
, p
,
286 if (encoded_result
< 0)
288 *errstr
= _("could not encode stored key");
293 elog(ERROR
, "%s", *errstr
);
301 encoded_result
= pg_b64_encode((char *) server_key
, key_length
, p
,
303 if (encoded_result
< 0)
305 *errstr
= _("could not encode server key");
310 elog(ERROR
, "%s", *errstr
);
317 Assert(p
- result
<= maxlen
);