| 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 | |