PostgreSQL Source Code  git master
scram-common.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  * scram-common.c
3  * Shared frontend/backend code for SCRAM authentication
4  *
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.
8  *
9  * Portions Copyright (c) 2017-2019, PostgreSQL Global Development Group
10  *
11  * IDENTIFICATION
12  * src/common/scram-common.c
13  *
14  *-------------------------------------------------------------------------
15  */
16 #ifndef FRONTEND
17 #include "postgres.h"
18 #else
19 #include "postgres_fe.h"
20 #endif
21 
22 #include "common/base64.h"
23 #include "common/scram-common.h"
24 #include "port/pg_bswap.h"
25 
26 #define HMAC_IPAD 0x36
27 #define HMAC_OPAD 0x5C
28 
29 /*
30  * Calculate HMAC per RFC2104.
31  *
32  * The hash function used is SHA-256.
33  */
34 void
35 scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen)
36 {
37  uint8 k_ipad[SHA256_HMAC_B];
38  int i;
39  uint8 keybuf[SCRAM_KEY_LEN];
40 
41  /*
42  * If the key is longer than the block size (64 bytes for SHA-256), pass
43  * it through SHA-256 once to shrink it down.
44  */
45  if (keylen > SHA256_HMAC_B)
46  {
47  pg_sha256_ctx sha256_ctx;
48 
49  pg_sha256_init(&sha256_ctx);
50  pg_sha256_update(&sha256_ctx, key, keylen);
51  pg_sha256_final(&sha256_ctx, keybuf);
52  key = keybuf;
53  keylen = SCRAM_KEY_LEN;
54  }
55 
56  memset(k_ipad, HMAC_IPAD, SHA256_HMAC_B);
57  memset(ctx->k_opad, HMAC_OPAD, SHA256_HMAC_B);
58 
59  for (i = 0; i < keylen; i++)
60  {
61  k_ipad[i] ^= key[i];
62  ctx->k_opad[i] ^= key[i];
63  }
64 
65  /* tmp = H(K XOR ipad, text) */
68 }
69 
70 /*
71  * Update HMAC calculation
72  * The hash function used is SHA-256.
73  */
74 void
75 scram_HMAC_update(scram_HMAC_ctx *ctx, const char *str, int slen)
76 {
77  pg_sha256_update(&ctx->sha256ctx, (const uint8 *) str, slen);
78 }
79 
80 /*
81  * Finalize HMAC calculation.
82  * The hash function used is SHA-256.
83  */
84 void
86 {
88 
89  pg_sha256_final(&ctx->sha256ctx, h);
90 
91  /* H(K XOR opad, tmp) */
95  pg_sha256_final(&ctx->sha256ctx, result);
96 }
97 
98 /*
99  * Calculate SaltedPassword.
100  *
101  * The password should already be normalized by SASLprep.
102  */
103 void
105  const char *salt, int saltlen, int iterations,
106  uint8 *result)
107 {
108  int password_len = strlen(password);
109  uint32 one = pg_hton32(1);
110  int i,
111  j;
112  uint8 Ui[SCRAM_KEY_LEN];
113  uint8 Ui_prev[SCRAM_KEY_LEN];
114  scram_HMAC_ctx hmac_ctx;
115 
116  /*
117  * Iterate hash calculation of HMAC entry using given salt. This is
118  * essentially PBKDF2 (see RFC2898) with HMAC() as the pseudorandom
119  * function.
120  */
121 
122  /* First iteration */
123  scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len);
124  scram_HMAC_update(&hmac_ctx, salt, saltlen);
125  scram_HMAC_update(&hmac_ctx, (char *) &one, sizeof(uint32));
126  scram_HMAC_final(Ui_prev, &hmac_ctx);
127  memcpy(result, Ui_prev, SCRAM_KEY_LEN);
128 
129  /* Subsequent iterations */
130  for (i = 2; i <= iterations; i++)
131  {
132  scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len);
133  scram_HMAC_update(&hmac_ctx, (const char *) Ui_prev, SCRAM_KEY_LEN);
134  scram_HMAC_final(Ui, &hmac_ctx);
135  for (j = 0; j < SCRAM_KEY_LEN; j++)
136  result[j] ^= Ui[j];
137  memcpy(Ui_prev, Ui, SCRAM_KEY_LEN);
138  }
139 }
140 
141 
142 /*
143  * Calculate SHA-256 hash for a NULL-terminated string. (The NULL terminator is
144  * not included in the hash).
145  */
146 void
147 scram_H(const uint8 *input, int len, uint8 *result)
148 {
149  pg_sha256_ctx ctx;
150 
151  pg_sha256_init(&ctx);
152  pg_sha256_update(&ctx, input, len);
153  pg_sha256_final(&ctx, result);
154 }
155 
156 /*
157  * Calculate ClientKey.
158  */
159 void
160 scram_ClientKey(const uint8 *salted_password, uint8 *result)
161 {
162  scram_HMAC_ctx ctx;
163 
164  scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN);
165  scram_HMAC_update(&ctx, "Client Key", strlen("Client Key"));
166  scram_HMAC_final(result, &ctx);
167 }
168 
169 /*
170  * Calculate ServerKey.
171  */
172 void
173 scram_ServerKey(const uint8 *salted_password, uint8 *result)
174 {
175  scram_HMAC_ctx ctx;
176 
177  scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN);
178  scram_HMAC_update(&ctx, "Server Key", strlen("Server Key"));
179  scram_HMAC_final(result, &ctx);
180 }
181 
182 
183 /*
184  * Construct a SCRAM secret, for storing in pg_authid.rolpassword.
185  *
186  * The password should already have been processed with SASLprep, if necessary!
187  *
188  * If iterations is 0, default number of iterations is used. The result is
189  * palloc'd or malloc'd, so caller is responsible for freeing it.
190  */
191 char *
192 scram_build_secret(const char *salt, int saltlen, int iterations,
193  const char *password)
194 {
195  uint8 salted_password[SCRAM_KEY_LEN];
196  uint8 stored_key[SCRAM_KEY_LEN];
197  uint8 server_key[SCRAM_KEY_LEN];
198  char *result;
199  char *p;
200  int maxlen;
201  int encoded_salt_len;
202  int encoded_stored_len;
203  int encoded_server_len;
204  int encoded_result;
205 
206  if (iterations <= 0)
207  iterations = SCRAM_DEFAULT_ITERATIONS;
208 
209  /* Calculate StoredKey and ServerKey */
210  scram_SaltedPassword(password, salt, saltlen, iterations,
211  salted_password);
212  scram_ClientKey(salted_password, stored_key);
213  scram_H(stored_key, SCRAM_KEY_LEN, stored_key);
214 
215  scram_ServerKey(salted_password, server_key);
216 
217  /*----------
218  * The format is:
219  * SCRAM-SHA-256$<iteration count>:<salt>$<StoredKey>:<ServerKey>
220  *----------
221  */
222  encoded_salt_len = pg_b64_enc_len(saltlen);
223  encoded_stored_len = pg_b64_enc_len(SCRAM_KEY_LEN);
224  encoded_server_len = pg_b64_enc_len(SCRAM_KEY_LEN);
225 
226  maxlen = strlen("SCRAM-SHA-256") + 1
227  + 10 + 1 /* iteration count */
228  + encoded_salt_len + 1 /* Base64-encoded salt */
229  + encoded_stored_len + 1 /* Base64-encoded StoredKey */
230  + encoded_server_len + 1; /* Base64-encoded ServerKey */
231 
232 #ifdef FRONTEND
233  result = malloc(maxlen);
234  if (!result)
235  return NULL;
236 #else
237  result = palloc(maxlen);
238 #endif
239 
240  p = result + sprintf(result, "SCRAM-SHA-256$%d:", iterations);
241 
242  /* salt */
243  encoded_result = pg_b64_encode(salt, saltlen, p, encoded_salt_len);
244  if (encoded_result < 0)
245  {
246 #ifdef FRONTEND
247  free(result);
248  return NULL;
249 #else
250  elog(ERROR, "could not encode salt");
251 #endif
252  }
253  p += encoded_result;
254  *(p++) = '$';
255 
256  /* stored key */
257  encoded_result = pg_b64_encode((char *) stored_key, SCRAM_KEY_LEN, p,
258  encoded_stored_len);
259  if (encoded_result < 0)
260  {
261 #ifdef FRONTEND
262  free(result);
263  return NULL;
264 #else
265  elog(ERROR, "could not encode stored key");
266 #endif
267  }
268 
269  p += encoded_result;
270  *(p++) = ':';
271 
272  /* server key */
273  encoded_result = pg_b64_encode((char *) server_key, SCRAM_KEY_LEN, p,
274  encoded_server_len);
275  if (encoded_result < 0)
276  {
277 #ifdef FRONTEND
278  free(result);
279  return NULL;
280 #else
281  elog(ERROR, "could not encode server key");
282 #endif
283  }
284 
285  p += encoded_result;
286  *(p++) = '\0';
287 
288  Assert(p - result <= maxlen);
289 
290  return result;
291 }
void scram_H(const uint8 *input, int len, uint8 *result)
Definition: scram-common.c:147
static char password[100]
Definition: streamutil.c:55
int pg_b64_encode(const char *src, int len, char *dst, int dstlen)
Definition: base64.c:49
unsigned char uint8
Definition: c.h:356
char * scram_build_secret(const char *salt, int saltlen, int iterations, const char *password)
Definition: scram-common.c:192
void scram_SaltedPassword(const char *password, const char *salt, int saltlen, int iterations, uint8 *result)
Definition: scram-common.c:104
pg_sha256_ctx sha256ctx
Definition: scram-common.h:53
void pg_sha256_init(pg_sha256_ctx *context)
Definition: sha2.c:268
void scram_ServerKey(const uint8 *salted_password, uint8 *result)
Definition: scram-common.c:173
#define malloc(a)
Definition: header.h:50
#define sprintf
Definition: port.h:194
#define ERROR
Definition: elog.h:43
#define pg_hton32(x)
Definition: pg_bswap.h:121
#define HMAC_OPAD
Definition: scram-common.c:27
unsigned int uint32
Definition: c.h:358
void scram_ClientKey(const uint8 *salted_password, uint8 *result)
Definition: scram-common.c:160
void pg_sha256_update(pg_sha256_ctx *context, const uint8 *data, size_t len)
Definition: sha2.c:465
uint8 k_opad[SHA256_HMAC_B]
Definition: scram-common.h:54
#define HMAC_IPAD
Definition: scram-common.c:26
#define free(a)
Definition: header.h:65
#define Assert(condition)
Definition: c.h:732
#define SCRAM_DEFAULT_ITERATIONS
Definition: scram-common.h:46
void scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx)
Definition: scram-common.c:85
int pg_b64_enc_len(int srclen)
Definition: base64.c:224
void scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen)
Definition: scram-common.c:35
void scram_HMAC_update(scram_HMAC_ctx *ctx, const char *str, int slen)
Definition: scram-common.c:75
void * palloc(Size size)
Definition: mcxt.c:949
void pg_sha256_final(pg_sha256_ctx *context, uint8 *digest)
Definition: sha2.c:566
#define elog(elevel,...)
Definition: elog.h:226
int i
#define SHA256_HMAC_B
Definition: scram-common.h:26
#define SCRAM_KEY_LEN
Definition: scram-common.h:23