/* * Iterate hash calculation of HMAC entry using given salt. * scram_Hi() is essentially PBKDF2 (see RFC2898) with HMAC() as the * pseudorandom function. */ static void scram_Hi(const char *str, const char *salt, int saltlen, int iterations, uint8 *result) { int str_len = strlen(str); uint32 one = htonl(1); int i, j; uint8 Ui[SCRAM_KEY_LEN]; uint8 Ui_prev[SCRAM_KEY_LEN]; scram_HMAC_ctx hmac_ctx; /* First iteration */ scram_HMAC_init(&hmac_ctx, (uint8 *) str, str_len); scram_HMAC_update(&hmac_ctx, salt, saltlen); scram_HMAC_update(&hmac_ctx, (char *) &one, sizeof(uint32)); scram_HMAC_final(Ui_prev, &hmac_ctx); memcpy(result, Ui_prev, SCRAM_KEY_LEN); /* Subsequent iterations */ for (i = 2; i <= iterations; i++) { scram_HMAC_init(&hmac_ctx, (uint8 *) str, str_len); scram_HMAC_update(&hmac_ctx, (const char *) Ui_prev, SCRAM_KEY_LEN); scram_HMAC_final(Ui, &hmac_ctx); for (j = 0; j < SCRAM_KEY_LEN; j++) result[j] ^= Ui[j]; memcpy(Ui_prev, Ui, SCRAM_KEY_LEN); } }
/* * Calculate ClientKey or ServerKey. * * The password should already be normalized by SASLprep. */ void scram_ClientOrServerKey(const char *password, const char *salt, int saltlen, int iterations, const char *keystr, uint8 *result) { uint8 keybuf[SCRAM_KEY_LEN]; scram_HMAC_ctx ctx; scram_Hi(password, salt, saltlen, iterations, keybuf); scram_HMAC_init(&ctx, keybuf, SCRAM_KEY_LEN); scram_HMAC_update(&ctx, keystr, strlen(keystr)); scram_HMAC_final(result, &ctx); }
/* * Build the final server-side message of an exchange. */ static char * build_server_final_message(scram_state *state) { uint8 ServerSignature[SCRAM_KEY_LEN]; char *server_signature_base64; int siglen; scram_HMAC_ctx ctx; /* calculate ServerSignature */ scram_HMAC_init(&ctx, state->ServerKey, SCRAM_KEY_LEN); scram_HMAC_update(&ctx, state->client_first_message_bare, strlen(state->client_first_message_bare)); scram_HMAC_update(&ctx, ",", 1); scram_HMAC_update(&ctx, state->server_first_message, strlen(state->server_first_message)); scram_HMAC_update(&ctx, ",", 1); scram_HMAC_update(&ctx, state->client_final_message_without_proof, strlen(state->client_final_message_without_proof)); scram_HMAC_final(ServerSignature, &ctx); server_signature_base64 = palloc(pg_b64_enc_len(SCRAM_KEY_LEN) + 1); siglen = pg_b64_encode((const char *) ServerSignature, SCRAM_KEY_LEN, server_signature_base64); server_signature_base64[siglen] = '\0'; /*------ * The syntax for the server-final-message is: (RFC 5802) * * verifier = "v=" base64 * ;; base-64 encoded ServerSignature. * * server-final-message = (server-error / verifier) * ["," extensions] * *------ */ return psprintf("v=%s", server_signature_base64); }
/* * Verify the client proof contained in the last message received from * client in an exchange. */ static bool verify_client_proof(scram_state *state) { uint8 ClientSignature[SCRAM_KEY_LEN]; uint8 ClientKey[SCRAM_KEY_LEN]; uint8 client_StoredKey[SCRAM_KEY_LEN]; scram_HMAC_ctx ctx; int i; /* calculate ClientSignature */ scram_HMAC_init(&ctx, state->StoredKey, SCRAM_KEY_LEN); scram_HMAC_update(&ctx, state->client_first_message_bare, strlen(state->client_first_message_bare)); scram_HMAC_update(&ctx, ",", 1); scram_HMAC_update(&ctx, state->server_first_message, strlen(state->server_first_message)); scram_HMAC_update(&ctx, ",", 1); scram_HMAC_update(&ctx, state->client_final_message_without_proof, strlen(state->client_final_message_without_proof)); scram_HMAC_final(ClientSignature, &ctx); /* Extract the ClientKey that the client calculated from the proof */ for (i = 0; i < SCRAM_KEY_LEN; i++) ClientKey[i] = state->ClientProof[i] ^ ClientSignature[i]; /* Hash it one more time, and compare with StoredKey */ scram_H(ClientKey, SCRAM_KEY_LEN, client_StoredKey); if (memcmp(client_StoredKey, state->StoredKey, SCRAM_KEY_LEN) != 0) return false; return true; }