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) */ |
66 | pg_sha256_init(&ctx->sha256ctx); |
67 | pg_sha256_update(&ctx->sha256ctx, k_ipad, SHA256_HMAC_B); |
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 |
85 | scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx) |
86 | { |
87 | uint8 h[SCRAM_KEY_LEN]; |
88 | |
89 | pg_sha256_final(&ctx->sha256ctx, h); |
90 | |
91 | /* H(K XOR opad, tmp) */ |
92 | pg_sha256_init(&ctx->sha256ctx); |
93 | pg_sha256_update(&ctx->sha256ctx, ctx->k_opad, SHA256_HMAC_B); |
94 | pg_sha256_update(&ctx->sha256ctx, h, SCRAM_KEY_LEN); |
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 |
104 | scram_SaltedPassword(const char *password, |
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 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 | { |
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 | |
202 | if (iterations <= 0) |
203 | iterations = SCRAM_DEFAULT_ITERATIONS; |
204 | |
205 | /* Calculate StoredKey and ServerKey */ |
206 | scram_SaltedPassword(password, salt, saltlen, iterations, |
207 | salted_password); |
208 | scram_ClientKey(salted_password, stored_key); |
209 | scram_H(stored_key, SCRAM_KEY_LEN, stored_key); |
210 | |
211 | scram_ServerKey(salted_password, server_key); |
212 | |
213 | /*---------- |
214 | * The format is: |
215 | * SCRAM-SHA-256$<iteration count>:<salt>$<StoredKey>:<ServerKey> |
216 | *---------- |
217 | */ |
218 | maxlen = strlen("SCRAM-SHA-256" ) + 1 |
219 | + 10 + 1 /* iteration count */ |
220 | + pg_b64_enc_len(saltlen) + 1 /* Base64-encoded salt */ |
221 | + pg_b64_enc_len(SCRAM_KEY_LEN) + 1 /* Base64-encoded StoredKey */ |
222 | + pg_b64_enc_len(SCRAM_KEY_LEN) + 1; /* Base64-encoded ServerKey */ |
223 | |
224 | #ifdef FRONTEND |
225 | result = malloc(maxlen); |
226 | if (!result) |
227 | return NULL; |
228 | #else |
229 | result = palloc(maxlen); |
230 | #endif |
231 | |
232 | p = result + sprintf(result, "SCRAM-SHA-256$%d:" , iterations); |
233 | |
234 | p += pg_b64_encode(salt, saltlen, p); |
235 | *(p++) = '$'; |
236 | p += pg_b64_encode((char *) stored_key, SCRAM_KEY_LEN, p); |
237 | *(p++) = ':'; |
238 | p += pg_b64_encode((char *) server_key, SCRAM_KEY_LEN, p); |
239 | *(p++) = '\0'; |
240 | |
241 | Assert(p - result <= maxlen); |
242 | |
243 | return result; |
244 | } |
245 | |