/Users/deen/code/yugabyte-db/build/debugcov-clang-dynamic-arm64-ninja/postgres_build/src/interfaces/libpq/scram-common.c
Line | Count | Source (jump to first uncovered line) |
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-2018, 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 | 16.3k | { |
37 | 16.3k | uint8 k_ipad[SHA256_HMAC_B]; |
38 | 16.3k | int i; |
39 | 16.3k | 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 | 16.3k | if (keylen > SHA256_HMAC_B) |
46 | 0 | { |
47 | 0 | pg_sha256_ctx sha256_ctx; |
48 | |
|
49 | 0 | pg_sha256_init(&sha256_ctx); |
50 | 0 | pg_sha256_update(&sha256_ctx, key, keylen); |
51 | 0 | pg_sha256_final(&sha256_ctx, keybuf); |
52 | 0 | key = keybuf; |
53 | 0 | keylen = SCRAM_KEY_LEN; |
54 | 0 | } |
55 | | |
56 | 16.3k | memset(k_ipad, HMAC_IPAD, SHA256_HMAC_B); |
57 | 16.3k | memset(ctx->k_opad, HMAC_OPAD, SHA256_HMAC_B); |
58 | | |
59 | 65.7k | for (i = 0; i < keylen; i++49.3k ) |
60 | 49.3k | { |
61 | 49.3k | k_ipad[i] ^= key[i]; |
62 | 49.3k | ctx->k_opad[i] ^= key[i]; |
63 | 49.3k | } |
64 | | |
65 | | /* tmp = H(K XOR ipad, text) */ |
66 | 16.3k | pg_sha256_init(&ctx->sha256ctx); |
67 | 16.3k | pg_sha256_update(&ctx->sha256ctx, k_ipad, SHA256_HMAC_B); |
68 | 16.3k | } |
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 | 16.3k | { |
77 | 16.3k | pg_sha256_update(&ctx->sha256ctx, (const uint8 *) str, slen); |
78 | 16.3k | } |
79 | | |
80 | | /* |
81 | | * Finalize HMAC calculation. |
82 | | * The hash function used is SHA-256. |
83 | | */ |
84 | | void |
85 | | scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx) |
86 | 16.3k | { |
87 | 16.3k | uint8 h[SCRAM_KEY_LEN]; |
88 | | |
89 | 16.3k | pg_sha256_final(&ctx->sha256ctx, h); |
90 | | |
91 | | /* H(K XOR opad, tmp) */ |
92 | 16.3k | pg_sha256_init(&ctx->sha256ctx); |
93 | 16.3k | pg_sha256_update(&ctx->sha256ctx, ctx->k_opad, SHA256_HMAC_B); |
94 | 16.3k | pg_sha256_update(&ctx->sha256ctx, h, SCRAM_KEY_LEN); |
95 | 16.3k | pg_sha256_final(&ctx->sha256ctx, result); |
96 | 16.3k | } |
97 | | |
98 | | /* |
99 | | * Calculate SaltedPassword. |
100 | | * |
101 | | * The password should already be normalized by SASLprep. |
102 | | */ |
103 | | void |
104 | | scram_SaltedPassword(const char *password, |
105 | | const char *salt, int saltlen, int iterations, |
106 | | uint8 *result) |
107 | 4 | { |
108 | 4 | int password_len = strlen(password); |
109 | 4 | uint32 one = pg_hton32(1); |
110 | 4 | int i, |
111 | 4 | j; |
112 | 4 | uint8 Ui[SCRAM_KEY_LEN]; |
113 | 4 | uint8 Ui_prev[SCRAM_KEY_LEN]; |
114 | 4 | 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 | 4 | scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len); |
124 | 4 | scram_HMAC_update(&hmac_ctx, salt, saltlen); |
125 | 4 | scram_HMAC_update(&hmac_ctx, (char *) &one, sizeof(uint32)); |
126 | 4 | scram_HMAC_final(Ui_prev, &hmac_ctx); |
127 | 4 | memcpy(result, Ui_prev, SCRAM_KEY_LEN); |
128 | | |
129 | | /* Subsequent iterations */ |
130 | 16.3k | for (i = 2; i <= iterations; i++16.3k ) |
131 | 16.3k | { |
132 | 16.3k | scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len); |
133 | 16.3k | scram_HMAC_update(&hmac_ctx, (const char *) Ui_prev, SCRAM_KEY_LEN); |
134 | 16.3k | scram_HMAC_final(Ui, &hmac_ctx); |
135 | 540k | for (j = 0; j < SCRAM_KEY_LEN; j++524k ) |
136 | 524k | result[j] ^= Ui[j]; |
137 | 16.3k | memcpy(Ui_prev, Ui, SCRAM_KEY_LEN); |
138 | 16.3k | } |
139 | 4 | } |
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 | 2 | { |
149 | 2 | pg_sha256_ctx ctx; |
150 | | |
151 | 2 | pg_sha256_init(&ctx); |
152 | 2 | pg_sha256_update(&ctx, input, len); |
153 | 2 | pg_sha256_final(&ctx, result); |
154 | 2 | } |
155 | | |
156 | | /* |
157 | | * Calculate ClientKey. |
158 | | */ |
159 | | void |
160 | | scram_ClientKey(const uint8 *salted_password, uint8 *result) |
161 | 2 | { |
162 | 2 | scram_HMAC_ctx ctx; |
163 | | |
164 | 2 | scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN); |
165 | 2 | scram_HMAC_update(&ctx, "Client Key", strlen("Client Key")); |
166 | 2 | scram_HMAC_final(result, &ctx); |
167 | 2 | } |
168 | | |
169 | | /* |
170 | | * Calculate ServerKey. |
171 | | */ |
172 | | void |
173 | | scram_ServerKey(const uint8 *salted_password, uint8 *result) |
174 | 4 | { |
175 | 4 | scram_HMAC_ctx ctx; |
176 | | |
177 | 4 | scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN); |
178 | 4 | scram_HMAC_update(&ctx, "Server Key", strlen("Server Key")); |
179 | 4 | scram_HMAC_final(result, &ctx); |
180 | 4 | } |
181 | | |
182 | | |
183 | | /* |
184 | | * Construct a verifier string for SCRAM, stored 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_verifier(const char *salt, int saltlen, int iterations, |
193 | | const char *password) |
194 | 0 | { |
195 | 0 | uint8 salted_password[SCRAM_KEY_LEN]; |
196 | 0 | uint8 stored_key[SCRAM_KEY_LEN]; |
197 | 0 | uint8 server_key[SCRAM_KEY_LEN]; |
198 | 0 | char *result; |
199 | 0 | char *p; |
200 | 0 | int maxlen; |
201 | |
|
202 | 0 | if (iterations <= 0) |
203 | 0 | iterations = SCRAM_DEFAULT_ITERATIONS; |
204 | | |
205 | | /* Calculate StoredKey and ServerKey */ |
206 | 0 | scram_SaltedPassword(password, salt, saltlen, iterations, |
207 | 0 | salted_password); |
208 | 0 | scram_ClientKey(salted_password, stored_key); |
209 | 0 | scram_H(stored_key, SCRAM_KEY_LEN, stored_key); |
210 | |
|
211 | 0 | scram_ServerKey(salted_password, server_key); |
212 | | |
213 | | /*---------- |
214 | | * The format is: |
215 | | * SCRAM-SHA-256$<iteration count>:<salt>$<StoredKey>:<ServerKey> |
216 | | *---------- |
217 | | */ |
218 | 0 | maxlen = strlen("SCRAM-SHA-256") + 1 |
219 | 0 | + 10 + 1 /* iteration count */ |
220 | 0 | + pg_b64_enc_len(saltlen) + 1 /* Base64-encoded salt */ |
221 | 0 | + pg_b64_enc_len(SCRAM_KEY_LEN) + 1 /* Base64-encoded StoredKey */ |
222 | 0 | + pg_b64_enc_len(SCRAM_KEY_LEN) + 1; /* Base64-encoded ServerKey */ |
223 | |
|
224 | 0 | #ifdef FRONTEND |
225 | 0 | result = malloc(maxlen); |
226 | 0 | if (!result) |
227 | 0 | return NULL; |
228 | | #else |
229 | | result = palloc(maxlen); |
230 | | #endif |
231 | | |
232 | 0 | p = result + sprintf(result, "SCRAM-SHA-256$%d:", iterations); |
233 | |
|
234 | 0 | p += pg_b64_encode(salt, saltlen, p); |
235 | 0 | *(p++) = '$'; |
236 | 0 | p += pg_b64_encode((char *) stored_key, SCRAM_KEY_LEN, p); |
237 | 0 | *(p++) = ':'; |
238 | 0 | p += pg_b64_encode((char *) server_key, SCRAM_KEY_LEN, p); |
239 | 0 | *(p++) = '\0'; |
240 | |
|
241 | 0 | Assert(p - result <= maxlen); |
242 | | |
243 | 0 | return result; |
244 | 0 | } |