Move SHA2 routines to a new generic API layer for crypto hashes

Two new routines to allocate a hash context and to free it are created,
as these become necessary for the goal behind this refactoring: switch
the all cryptohash implementations for OpenSSL to use EVP (for FIPS and
also because upstream does not recommend the use of low-level cryptohash
functions for 20 years).  Note that OpenSSL hides the internals of
cryptohash contexts since 1.1.0, so it is necessary to leave the
allocation to OpenSSL itself, explaining the need for those two new
routines.  This part is going to require more work to properly track
hash contexts with resource owners, but this not introduced here.
Still, this refactoring makes the move possible.

This reduces the number of routines for all SHA2 implementations from
twelve (SHA{224,256,386,512} with init, update and final calls) to five
(create, free, init, update and final calls) by incorporating the hash
type directly into the hash context data.

The new cryptohash routines are moved to a new file, called cryptohash.c
for the fallback implementations, with SHA2 specifics becoming a part
internal to src/common/.  OpenSSL specifics are part of
cryptohash_openssl.c.  This infrastructure is usable for more hash
types, like MD5 or HMAC.

Any code paths using the internal SHA2 routines are adapted to report
correctly errors, which are most of the changes of this commit.  The
zones mostly impacted are checksum manifests, libpq and SCRAM.

Note that e21cbb4 was a first attempt to switch SHA2 to EVP, but it
lacked the refactoring needed for libpq, as done here.

This patch has been tested on Linux and Windows, with and without
OpenSSL, and down to 1.0.1, the oldest version supported on HEAD.

Author: Michael Paquier
Reviewed-by: Daniel Gustafsson
Discussion: https://postgr.es/m/20200924025314.GE7405@paquier.xyz
This commit is contained in:
Michael Paquier 2020-12-02 10:37:20 +09:00
parent 888671a8cd
commit 87ae9691d2
23 changed files with 1039 additions and 549 deletions

View File

@ -33,6 +33,7 @@
#include <time.h>
#include "common/cryptohash.h"
#include "common/sha2.h"
#include "px.h"
@ -42,7 +43,6 @@ void init_sha384(PX_MD *h);
void init_sha512(PX_MD *h);
/* SHA224 */
static unsigned
int_sha224_len(PX_MD *h)
{
@ -55,42 +55,7 @@ int_sha224_block_len(PX_MD *h)
return PG_SHA224_BLOCK_LENGTH;
}
static void
int_sha224_update(PX_MD *h, const uint8 *data, unsigned dlen)
{
pg_sha224_ctx *ctx = (pg_sha224_ctx *) h->p.ptr;
pg_sha224_update(ctx, data, dlen);
}
static void
int_sha224_reset(PX_MD *h)
{
pg_sha224_ctx *ctx = (pg_sha224_ctx *) h->p.ptr;
pg_sha224_init(ctx);
}
static void
int_sha224_finish(PX_MD *h, uint8 *dst)
{
pg_sha224_ctx *ctx = (pg_sha224_ctx *) h->p.ptr;
pg_sha224_final(ctx, dst);
}
static void
int_sha224_free(PX_MD *h)
{
pg_sha224_ctx *ctx = (pg_sha224_ctx *) h->p.ptr;
px_memset(ctx, 0, sizeof(*ctx));
pfree(ctx);
pfree(h);
}
/* SHA256 */
static unsigned
int_sha256_len(PX_MD *h)
{
@ -103,42 +68,7 @@ int_sha256_block_len(PX_MD *h)
return PG_SHA256_BLOCK_LENGTH;
}
static void
int_sha256_update(PX_MD *h, const uint8 *data, unsigned dlen)
{
pg_sha256_ctx *ctx = (pg_sha256_ctx *) h->p.ptr;
pg_sha256_update(ctx, data, dlen);
}
static void
int_sha256_reset(PX_MD *h)
{
pg_sha256_ctx *ctx = (pg_sha256_ctx *) h->p.ptr;
pg_sha256_init(ctx);
}
static void
int_sha256_finish(PX_MD *h, uint8 *dst)
{
pg_sha256_ctx *ctx = (pg_sha256_ctx *) h->p.ptr;
pg_sha256_final(ctx, dst);
}
static void
int_sha256_free(PX_MD *h)
{
pg_sha256_ctx *ctx = (pg_sha256_ctx *) h->p.ptr;
px_memset(ctx, 0, sizeof(*ctx));
pfree(ctx);
pfree(h);
}
/* SHA384 */
static unsigned
int_sha384_len(PX_MD *h)
{
@ -151,42 +81,7 @@ int_sha384_block_len(PX_MD *h)
return PG_SHA384_BLOCK_LENGTH;
}
static void
int_sha384_update(PX_MD *h, const uint8 *data, unsigned dlen)
{
pg_sha384_ctx *ctx = (pg_sha384_ctx *) h->p.ptr;
pg_sha384_update(ctx, data, dlen);
}
static void
int_sha384_reset(PX_MD *h)
{
pg_sha384_ctx *ctx = (pg_sha384_ctx *) h->p.ptr;
pg_sha384_init(ctx);
}
static void
int_sha384_finish(PX_MD *h, uint8 *dst)
{
pg_sha384_ctx *ctx = (pg_sha384_ctx *) h->p.ptr;
pg_sha384_final(ctx, dst);
}
static void
int_sha384_free(PX_MD *h)
{
pg_sha384_ctx *ctx = (pg_sha384_ctx *) h->p.ptr;
px_memset(ctx, 0, sizeof(*ctx));
pfree(ctx);
pfree(h);
}
/* SHA512 */
static unsigned
int_sha512_len(PX_MD *h)
{
@ -199,37 +94,40 @@ int_sha512_block_len(PX_MD *h)
return PG_SHA512_BLOCK_LENGTH;
}
/* Generic interface for all SHA2 methods */
static void
int_sha512_update(PX_MD *h, const uint8 *data, unsigned dlen)
int_sha2_update(PX_MD *h, const uint8 *data, unsigned dlen)
{
pg_sha512_ctx *ctx = (pg_sha512_ctx *) h->p.ptr;
pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
pg_sha512_update(ctx, data, dlen);
if (pg_cryptohash_update(ctx, data, dlen) < 0)
elog(ERROR, "could not update %s context", "SHA2");
}
static void
int_sha512_reset(PX_MD *h)
int_sha2_reset(PX_MD *h)
{
pg_sha512_ctx *ctx = (pg_sha512_ctx *) h->p.ptr;
pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
pg_sha512_init(ctx);
if (pg_cryptohash_init(ctx) < 0)
elog(ERROR, "could not initialize %s context", "SHA2");
}
static void
int_sha512_finish(PX_MD *h, uint8 *dst)
int_sha2_finish(PX_MD *h, uint8 *dst)
{
pg_sha512_ctx *ctx = (pg_sha512_ctx *) h->p.ptr;
pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
pg_sha512_final(ctx, dst);
if (pg_cryptohash_final(ctx, dst) < 0)
elog(ERROR, "could not finalize %s context", "SHA2");
}
static void
int_sha512_free(PX_MD *h)
int_sha2_free(PX_MD *h)
{
pg_sha512_ctx *ctx = (pg_sha512_ctx *) h->p.ptr;
pg_cryptohash_ctx *ctx = (pg_cryptohash_ctx *) h->p.ptr;
px_memset(ctx, 0, sizeof(*ctx));
pfree(ctx);
pg_cryptohash_free(ctx);
pfree(h);
}
@ -238,18 +136,17 @@ int_sha512_free(PX_MD *h)
void
init_sha224(PX_MD *md)
{
pg_sha224_ctx *ctx;
ctx = palloc0(sizeof(*ctx));
pg_cryptohash_ctx *ctx;
ctx = pg_cryptohash_create(PG_SHA224);
md->p.ptr = ctx;
md->result_size = int_sha224_len;
md->block_size = int_sha224_block_len;
md->reset = int_sha224_reset;
md->update = int_sha224_update;
md->finish = int_sha224_finish;
md->free = int_sha224_free;
md->reset = int_sha2_reset;
md->update = int_sha2_update;
md->finish = int_sha2_finish;
md->free = int_sha2_free;
md->reset(md);
}
@ -257,18 +154,17 @@ init_sha224(PX_MD *md)
void
init_sha256(PX_MD *md)
{
pg_sha256_ctx *ctx;
ctx = palloc0(sizeof(*ctx));
pg_cryptohash_ctx *ctx;
ctx = pg_cryptohash_create(PG_SHA256);
md->p.ptr = ctx;
md->result_size = int_sha256_len;
md->block_size = int_sha256_block_len;
md->reset = int_sha256_reset;
md->update = int_sha256_update;
md->finish = int_sha256_finish;
md->free = int_sha256_free;
md->reset = int_sha2_reset;
md->update = int_sha2_update;
md->finish = int_sha2_finish;
md->free = int_sha2_free;
md->reset(md);
}
@ -276,18 +172,17 @@ init_sha256(PX_MD *md)
void
init_sha384(PX_MD *md)
{
pg_sha384_ctx *ctx;
ctx = palloc0(sizeof(*ctx));
pg_cryptohash_ctx *ctx;
ctx = pg_cryptohash_create(PG_SHA384);
md->p.ptr = ctx;
md->result_size = int_sha384_len;
md->block_size = int_sha384_block_len;
md->reset = int_sha384_reset;
md->update = int_sha384_update;
md->finish = int_sha384_finish;
md->free = int_sha384_free;
md->reset = int_sha2_reset;
md->update = int_sha2_update;
md->finish = int_sha2_finish;
md->free = int_sha2_free;
md->reset(md);
}
@ -295,18 +190,17 @@ init_sha384(PX_MD *md)
void
init_sha512(PX_MD *md)
{
pg_sha512_ctx *ctx;
ctx = palloc0(sizeof(*ctx));
pg_cryptohash_ctx *ctx;
ctx = pg_cryptohash_create(PG_SHA512);
md->p.ptr = ctx;
md->result_size = int_sha512_len;
md->block_size = int_sha512_block_len;
md->reset = int_sha512_reset;
md->update = int_sha512_update;
md->finish = int_sha512_finish;
md->free = int_sha512_free;
md->reset = int_sha2_reset;
md->update = int_sha2_update;
md->finish = int_sha2_finish;
md->free = int_sha2_free;
md->reset(md);
}

View File

@ -527,8 +527,12 @@ scram_verify_plain_password(const char *username, const char *password,
password = prep_password;
/* Compute Server Key based on the user-supplied plaintext password */
scram_SaltedPassword(password, salt, saltlen, iterations, salted_password);
scram_ServerKey(salted_password, computed_key);
if (scram_SaltedPassword(password, salt, saltlen, iterations,
salted_password) < 0 ||
scram_ServerKey(salted_password, computed_key) < 0)
{
elog(ERROR, "could not compute server key");
}
if (prep_password)
pfree(prep_password);
@ -651,8 +655,17 @@ mock_scram_secret(const char *username, int *iterations, char **salt,
char *encoded_salt;
int encoded_len;
/* Generate deterministic salt */
/*
* Generate deterministic salt.
*
* Note that we cannot reveal any information to an attacker here so the
* error messages need to remain generic. This should never fail anyway
* as the salt generated for mock authentication uses the cluster's nonce
* value.
*/
raw_salt = scram_mock_salt(username);
if (raw_salt == NULL)
elog(ERROR, "could not encode salt");
encoded_len = pg_b64_enc_len(SCRAM_DEFAULT_SALT_LEN);
/* don't forget the zero-terminator */
@ -660,12 +673,6 @@ mock_scram_secret(const char *username, int *iterations, char **salt,
encoded_len = pg_b64_encode(raw_salt, SCRAM_DEFAULT_SALT_LEN, encoded_salt,
encoded_len);
/*
* Note that we cannot reveal any information to an attacker here so the
* error message needs to remain generic. This should never fail anyway
* as the salt generated for mock authentication uses the cluster's nonce
* value.
*/
if (encoded_len < 0)
elog(ERROR, "could not encode salt");
encoded_salt[encoded_len] = '\0';
@ -1084,7 +1091,8 @@ verify_final_nonce(scram_state *state)
/*
* Verify the client proof contained in the last message received from
* client in an exchange.
* client in an exchange. Returns true if the verification is a success,
* or false for a failure.
*/
static bool
verify_client_proof(scram_state *state)
@ -1095,27 +1103,35 @@ verify_client_proof(scram_state *state)
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);
/*
* Calculate ClientSignature. Note that we don't log directly a failure
* here even when processing the calculations as this could involve a mock
* authentication.
*/
if (scram_HMAC_init(&ctx, state->StoredKey, SCRAM_KEY_LEN) < 0 ||
scram_HMAC_update(&ctx,
state->client_first_message_bare,
strlen(state->client_first_message_bare)) < 0 ||
scram_HMAC_update(&ctx, ",", 1) < 0 ||
scram_HMAC_update(&ctx,
state->server_first_message,
strlen(state->server_first_message)) < 0 ||
scram_HMAC_update(&ctx, ",", 1) < 0 ||
scram_HMAC_update(&ctx,
state->client_final_message_without_proof,
strlen(state->client_final_message_without_proof)) < 0 ||
scram_HMAC_final(ClientSignature, &ctx) < 0)
{
elog(ERROR, "could not calculate client signature");
}
/* 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 (scram_H(ClientKey, SCRAM_KEY_LEN, client_StoredKey) < 0)
elog(ERROR, "could not hash stored key");
if (memcmp(client_StoredKey, state->StoredKey, SCRAM_KEY_LEN) != 0)
return false;
@ -1346,19 +1362,22 @@ build_server_final_message(scram_state *state)
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);
if (scram_HMAC_init(&ctx, state->ServerKey, SCRAM_KEY_LEN) < 0 ||
scram_HMAC_update(&ctx,
state->client_first_message_bare,
strlen(state->client_first_message_bare)) < 0 ||
scram_HMAC_update(&ctx, ",", 1) < 0 ||
scram_HMAC_update(&ctx,
state->server_first_message,
strlen(state->server_first_message)) < 0 ||
scram_HMAC_update(&ctx, ",", 1) < 0 ||
scram_HMAC_update(&ctx,
state->client_final_message_without_proof,
strlen(state->client_final_message_without_proof)) < 0 ||
scram_HMAC_final(ServerSignature, &ctx) < 0)
{
elog(ERROR, "could not calculate server signature");
}
siglen = pg_b64_enc_len(SCRAM_KEY_LEN);
/* don't forget the zero-terminator */
@ -1388,12 +1407,12 @@ build_server_final_message(scram_state *state)
/*
* Deterministically generate salt for mock authentication, using a SHA256
* hash based on the username and a cluster-level secret key. Returns a
* pointer to a static buffer of size SCRAM_DEFAULT_SALT_LEN.
* pointer to a static buffer of size SCRAM_DEFAULT_SALT_LEN, or NULL.
*/
static char *
scram_mock_salt(const char *username)
{
pg_sha256_ctx ctx;
pg_cryptohash_ctx *ctx;
static uint8 sha_digest[PG_SHA256_DIGEST_LENGTH];
char *mock_auth_nonce = GetMockAuthenticationNonce();
@ -1406,10 +1425,16 @@ scram_mock_salt(const char *username)
StaticAssertStmt(PG_SHA256_DIGEST_LENGTH >= SCRAM_DEFAULT_SALT_LEN,
"salt length greater than SHA256 digest length");
pg_sha256_init(&ctx);
pg_sha256_update(&ctx, (uint8 *) username, strlen(username));
pg_sha256_update(&ctx, (uint8 *) mock_auth_nonce, MOCK_AUTH_NONCE_LEN);
pg_sha256_final(&ctx, sha_digest);
ctx = pg_cryptohash_create(PG_SHA256);
if (pg_cryptohash_init(ctx) < 0 ||
pg_cryptohash_update(ctx, (uint8 *) username, strlen(username)) < 0 ||
pg_cryptohash_update(ctx, (uint8 *) mock_auth_nonce, MOCK_AUTH_NONCE_LEN) < 0 ||
pg_cryptohash_final(ctx, sha_digest) < 0)
{
pg_cryptohash_free(ctx);
return NULL;
}
pg_cryptohash_free(ctx);
return (char *) sha_digest;
}

View File

@ -65,7 +65,9 @@ InitializeBackupManifest(backup_manifest_info *manifest,
else
{
manifest->buffile = BufFileCreateTemp(false);
pg_sha256_init(&manifest->manifest_ctx);
manifest->manifest_ctx = pg_cryptohash_create(PG_SHA256);
if (pg_cryptohash_init(manifest->manifest_ctx) < 0)
elog(ERROR, "failed to initialize checksum of backup manifest");
}
manifest->manifest_size = UINT64CONST(0);
@ -79,6 +81,16 @@ InitializeBackupManifest(backup_manifest_info *manifest,
"\"Files\": [");
}
/*
* Free resources assigned to a backup manifest constructed.
*/
void
FreeBackupManifest(backup_manifest_info *manifest)
{
pg_cryptohash_free(manifest->manifest_ctx);
manifest->manifest_ctx = NULL;
}
/*
* Add an entry to the backup manifest for a file.
*/
@ -166,6 +178,9 @@ AddFileToBackupManifest(backup_manifest_info *manifest, const char *spcoid,
int checksumlen;
checksumlen = pg_checksum_final(checksum_ctx, checksumbuf);
if (checksumlen < 0)
elog(ERROR, "could not finalize checksum of file \"%s\"",
pathname);
appendStringInfo(&buf,
", \"Checksum-Algorithm\": \"%s\", \"Checksum\": \"",
@ -310,7 +325,8 @@ SendBackupManifest(backup_manifest_info *manifest)
* twice.
*/
manifest->still_checksumming = false;
pg_sha256_final(&manifest->manifest_ctx, checksumbuf);
if (pg_cryptohash_final(manifest->manifest_ctx, checksumbuf) < 0)
elog(ERROR, "failed to finalize checksum of backup manifest");
AppendStringToManifest(manifest, "\"Manifest-Checksum\": \"");
hex_encode((char *) checksumbuf, sizeof checksumbuf, checksumstringbuf);
checksumstringbuf[PG_SHA256_DIGEST_STRING_LENGTH - 1] = '\0';
@ -373,7 +389,10 @@ AppendStringToManifest(backup_manifest_info *manifest, char *s)
Assert(manifest != NULL);
if (manifest->still_checksumming)
pg_sha256_update(&manifest->manifest_ctx, (uint8 *) s, len);
{
if (pg_cryptohash_update(manifest->manifest_ctx, (uint8 *) s, len) < 0)
elog(ERROR, "failed to update checksum of backup manifest");
}
BufFileWrite(manifest->buffile, s, len);
manifest->manifest_size += len;
}

View File

@ -733,6 +733,7 @@ perform_base_backup(basebackup_options *opt)
WalSndResourceCleanup(true);
pgstat_progress_end_command();
FreeBackupManifest(&manifest);
}
/*
@ -1094,7 +1095,9 @@ sendFileWithContent(const char *filename, const char *content,
len;
pg_checksum_context checksum_ctx;
pg_checksum_init(&checksum_ctx, manifest->checksum_type);
if (pg_checksum_init(&checksum_ctx, manifest->checksum_type) < 0)
elog(ERROR, "could not initialize checksum of file \"%s\"",
filename);
len = strlen(content);
@ -1130,7 +1133,10 @@ sendFileWithContent(const char *filename, const char *content,
update_basebackup_progress(pad);
}
pg_checksum_update(&checksum_ctx, (uint8 *) content, len);
if (pg_checksum_update(&checksum_ctx, (uint8 *) content, len) < 0)
elog(ERROR, "could not update checksum of file \"%s\"",
filename);
AddFileToBackupManifest(manifest, NULL, filename, len,
(pg_time_t) statbuf.st_mtime, &checksum_ctx);
}
@ -1584,7 +1590,9 @@ sendFile(const char *readfilename, const char *tarfilename,
bool verify_checksum = false;
pg_checksum_context checksum_ctx;
pg_checksum_init(&checksum_ctx, manifest->checksum_type);
if (pg_checksum_init(&checksum_ctx, manifest->checksum_type) < 0)
elog(ERROR, "could not initialize checksum of file \"%s\"",
readfilename);
fd = OpenTransientFile(readfilename, O_RDONLY | PG_BINARY);
if (fd < 0)
@ -1758,7 +1766,8 @@ sendFile(const char *readfilename, const char *tarfilename,
update_basebackup_progress(cnt);
/* Also feed it to the checksum machinery. */
pg_checksum_update(&checksum_ctx, (uint8 *) buf, cnt);
if (pg_checksum_update(&checksum_ctx, (uint8 *) buf, cnt) < 0)
elog(ERROR, "could not update checksum of base backup");
len += cnt;
throttle(cnt);
@ -1772,7 +1781,8 @@ sendFile(const char *readfilename, const char *tarfilename,
{
cnt = Min(sizeof(buf), statbuf->st_size - len);
pq_putmessage('d', buf, cnt);
pg_checksum_update(&checksum_ctx, (uint8 *) buf, cnt);
if (pg_checksum_update(&checksum_ctx, (uint8 *) buf, cnt) < 0)
elog(ERROR, "could not update checksum of base backup");
update_basebackup_progress(cnt);
len += cnt;
throttle(cnt);
@ -1780,8 +1790,8 @@ sendFile(const char *readfilename, const char *tarfilename,
}
/*
* Pad to a block boundary, per tar format requirements. (This small
* piece of data is probably not worth throttling, and is not checksummed
* Pad to a block boundary, per tar format requirements. (This small piece
* of data is probably not worth throttling, and is not checksummed
* because it's not actually part of the file.)
*/
pad = tarPaddingBytesRequired(len);

View File

@ -13,6 +13,7 @@
*/
#include "postgres.h"
#include "common/cryptohash.h"
#include "common/md5.h"
#include "common/sha2.h"
#include "utils/builtins.h"
@ -78,16 +79,21 @@ sha224_bytea(PG_FUNCTION_ARGS)
bytea *in = PG_GETARG_BYTEA_PP(0);
const uint8 *data;
size_t len;
pg_sha224_ctx ctx;
pg_cryptohash_ctx *ctx;
unsigned char buf[PG_SHA224_DIGEST_LENGTH];
bytea *result;
len = VARSIZE_ANY_EXHDR(in);
data = (unsigned char *) VARDATA_ANY(in);
pg_sha224_init(&ctx);
pg_sha224_update(&ctx, data, len);
pg_sha224_final(&ctx, buf);
ctx = pg_cryptohash_create(PG_SHA224);
if (pg_cryptohash_init(ctx) < 0)
elog(ERROR, "could not initialize %s context", "SHA224");
if (pg_cryptohash_update(ctx, data, len) < 0)
elog(ERROR, "could not update %s context", "SHA224");
if (pg_cryptohash_final(ctx, buf) < 0)
elog(ERROR, "could not finalize %s context", "SHA224");
pg_cryptohash_free(ctx);
result = palloc(sizeof(buf) + VARHDRSZ);
SET_VARSIZE(result, sizeof(buf) + VARHDRSZ);
@ -102,16 +108,21 @@ sha256_bytea(PG_FUNCTION_ARGS)
bytea *in = PG_GETARG_BYTEA_PP(0);
const uint8 *data;
size_t len;
pg_sha256_ctx ctx;
pg_cryptohash_ctx *ctx;
unsigned char buf[PG_SHA256_DIGEST_LENGTH];
bytea *result;
len = VARSIZE_ANY_EXHDR(in);
data = (unsigned char *) VARDATA_ANY(in);
pg_sha256_init(&ctx);
pg_sha256_update(&ctx, data, len);
pg_sha256_final(&ctx, buf);
ctx = pg_cryptohash_create(PG_SHA256);
if (pg_cryptohash_init(ctx) < 0)
elog(ERROR, "could not initialize %s context", "SHA256");
if (pg_cryptohash_update(ctx, data, len) < 0)
elog(ERROR, "could not update %s context", "SHA256");
if (pg_cryptohash_final(ctx, buf) < 0)
elog(ERROR, "could not finalize %s context", "SHA256");
pg_cryptohash_free(ctx);
result = palloc(sizeof(buf) + VARHDRSZ);
SET_VARSIZE(result, sizeof(buf) + VARHDRSZ);
@ -126,16 +137,21 @@ sha384_bytea(PG_FUNCTION_ARGS)
bytea *in = PG_GETARG_BYTEA_PP(0);
const uint8 *data;
size_t len;
pg_sha384_ctx ctx;
pg_cryptohash_ctx *ctx;
unsigned char buf[PG_SHA384_DIGEST_LENGTH];
bytea *result;
len = VARSIZE_ANY_EXHDR(in);
data = (unsigned char *) VARDATA_ANY(in);
pg_sha384_init(&ctx);
pg_sha384_update(&ctx, data, len);
pg_sha384_final(&ctx, buf);
ctx = pg_cryptohash_create(PG_SHA384);
if (pg_cryptohash_init(ctx) < 0)
elog(ERROR, "could not initialize %s context", "SHA384");
if (pg_cryptohash_update(ctx, data, len) < 0)
elog(ERROR, "could not update %s context", "SHA384");
if (pg_cryptohash_final(ctx, buf) < 0)
elog(ERROR, "could not finalize %s context", "SHA384");
pg_cryptohash_free(ctx);
result = palloc(sizeof(buf) + VARHDRSZ);
SET_VARSIZE(result, sizeof(buf) + VARHDRSZ);
@ -150,16 +166,21 @@ sha512_bytea(PG_FUNCTION_ARGS)
bytea *in = PG_GETARG_BYTEA_PP(0);
const uint8 *data;
size_t len;
pg_sha512_ctx ctx;
pg_cryptohash_ctx *ctx;
unsigned char buf[PG_SHA512_DIGEST_LENGTH];
bytea *result;
len = VARSIZE_ANY_EXHDR(in);
data = (unsigned char *) VARDATA_ANY(in);
pg_sha512_init(&ctx);
pg_sha512_update(&ctx, data, len);
pg_sha512_final(&ctx, buf);
ctx = pg_cryptohash_create(PG_SHA512);
if (pg_cryptohash_init(ctx) < 0)
elog(ERROR, "could not initialize %s context", "SHA512");
if (pg_cryptohash_update(ctx, data, len) < 0)
elog(ERROR, "could not update %s context", "SHA512");
if (pg_cryptohash_final(ctx, buf) < 0)
elog(ERROR, "could not finalize %s context", "SHA512");
pg_cryptohash_free(ctx);
result = palloc(sizeof(buf) + VARHDRSZ);
SET_VARSIZE(result, sizeof(buf) + VARHDRSZ);

View File

@ -624,7 +624,7 @@ verify_manifest_checksum(JsonManifestParseState *parse, char *buffer,
size_t number_of_newlines = 0;
size_t ultimate_newline = 0;
size_t penultimate_newline = 0;
pg_sha256_ctx manifest_ctx;
pg_cryptohash_ctx *manifest_ctx;
uint8 manifest_checksum_actual[PG_SHA256_DIGEST_LENGTH];
uint8 manifest_checksum_expected[PG_SHA256_DIGEST_LENGTH];
@ -652,9 +652,15 @@ verify_manifest_checksum(JsonManifestParseState *parse, char *buffer,
"last line not newline-terminated");
/* Checksum the rest. */
pg_sha256_init(&manifest_ctx);
pg_sha256_update(&manifest_ctx, (uint8 *) buffer, penultimate_newline + 1);
pg_sha256_final(&manifest_ctx, manifest_checksum_actual);
manifest_ctx = pg_cryptohash_create(PG_SHA256);
if (manifest_ctx == NULL)
context->error_cb(context, "out of memory");
if (pg_cryptohash_init(manifest_ctx) < 0)
context->error_cb(context, "could not initialize checksum of manifest");
if (pg_cryptohash_update(manifest_ctx, (uint8 *) buffer, penultimate_newline + 1) < 0)
context->error_cb(context, "could not update checksum of manifest");
if (pg_cryptohash_final(manifest_ctx, manifest_checksum_actual) < 0)
context->error_cb(context, "could not finalize checksum of manifest");
/* Now verify it. */
if (parse->manifest_checksum == NULL)
@ -667,6 +673,7 @@ verify_manifest_checksum(JsonManifestParseState *parse, char *buffer,
if (memcmp(manifest_checksum_actual, manifest_checksum_expected,
PG_SHA256_DIGEST_LENGTH) != 0)
context->error_cb(context, "manifest checksum mismatch");
pg_cryptohash_free(manifest_ctx);
}
/*

View File

@ -726,13 +726,26 @@ verify_file_checksum(verifier_context *context, manifest_file *m,
}
/* Initialize checksum context. */
pg_checksum_init(&checksum_ctx, m->checksum_type);
if (pg_checksum_init(&checksum_ctx, m->checksum_type) < 0)
{
report_backup_error(context, "could not initialize checksum of file \"%s\"",
relpath);
return;
}
/* Read the file chunk by chunk, updating the checksum as we go. */
while ((rc = read(fd, buffer, READ_CHUNK_SIZE)) > 0)
{
bytes_read += rc;
pg_checksum_update(&checksum_ctx, buffer, rc);
if (pg_checksum_update(&checksum_ctx, buffer, rc) < 0)
{
report_backup_error(context, "could not update checksum of file \"%s\"",
relpath);
close(fd);
return;
}
}
if (rc < 0)
report_backup_error(context, "could not read file \"%s\": %m",
@ -767,6 +780,13 @@ verify_file_checksum(verifier_context *context, manifest_file *m,
/* Get the final checksum. */
checksumlen = pg_checksum_final(&checksum_ctx, checksumbuf);
if (checksumlen < 0)
{
report_backup_error(context,
"could not finalize checksum of file \"%s\"",
relpath);
return;
}
/* And check it against the manifest. */
if (checksumlen != m->checksum_length)

View File

@ -82,9 +82,11 @@ OBJS_COMMON = \
ifeq ($(with_openssl),yes)
OBJS_COMMON += \
protocol_openssl.o \
sha2_openssl.o
cryptohash_openssl.o
else
OBJS_COMMON += sha2.o
OBJS_COMMON += \
cryptohash.o \
sha2.o
endif
# A few files are currently only built for frontend, not server

View File

@ -77,8 +77,9 @@ pg_checksum_type_name(pg_checksum_type type)
/*
* Initialize a checksum context for checksums of the given type.
* Returns 0 for a success, -1 for a failure.
*/
void
int
pg_checksum_init(pg_checksum_context *context, pg_checksum_type type)
{
context->type = type;
@ -92,24 +93,55 @@ pg_checksum_init(pg_checksum_context *context, pg_checksum_type type)
INIT_CRC32C(context->raw_context.c_crc32c);
break;
case CHECKSUM_TYPE_SHA224:
pg_sha224_init(&context->raw_context.c_sha224);
context->raw_context.c_sha224 = pg_cryptohash_create(PG_SHA224);
if (context->raw_context.c_sha224 == NULL)
return -1;
if (pg_cryptohash_init(context->raw_context.c_sha224) < 0)
{
pg_cryptohash_free(context->raw_context.c_sha224);
return -1;
}
break;
case CHECKSUM_TYPE_SHA256:
pg_sha256_init(&context->raw_context.c_sha256);
context->raw_context.c_sha256 = pg_cryptohash_create(PG_SHA256);
if (context->raw_context.c_sha256 == NULL)
return -1;
if (pg_cryptohash_init(context->raw_context.c_sha256) < 0)
{
pg_cryptohash_free(context->raw_context.c_sha256);
return -1;
}
break;
case CHECKSUM_TYPE_SHA384:
pg_sha384_init(&context->raw_context.c_sha384);
context->raw_context.c_sha384 = pg_cryptohash_create(PG_SHA384);
if (context->raw_context.c_sha384 == NULL)
return -1;
if (pg_cryptohash_init(context->raw_context.c_sha384) < 0)
{
pg_cryptohash_free(context->raw_context.c_sha384);
return -1;
}
break;
case CHECKSUM_TYPE_SHA512:
pg_sha512_init(&context->raw_context.c_sha512);
context->raw_context.c_sha512 = pg_cryptohash_create(PG_SHA512);
if (context->raw_context.c_sha512 == NULL)
return -1;
if (pg_cryptohash_init(context->raw_context.c_sha512) < 0)
{
pg_cryptohash_free(context->raw_context.c_sha512);
return -1;
}
break;
}
return 0;
}
/*
* Update a checksum context with new data.
* Returns 0 for a success, -1 for a failure.
*/
void
int
pg_checksum_update(pg_checksum_context *context, const uint8 *input,
size_t len)
{
@ -122,25 +154,32 @@ pg_checksum_update(pg_checksum_context *context, const uint8 *input,
COMP_CRC32C(context->raw_context.c_crc32c, input, len);
break;
case CHECKSUM_TYPE_SHA224:
pg_sha224_update(&context->raw_context.c_sha224, input, len);
if (pg_cryptohash_update(context->raw_context.c_sha224, input, len) < 0)
return -1;
break;
case CHECKSUM_TYPE_SHA256:
pg_sha256_update(&context->raw_context.c_sha256, input, len);
if (pg_cryptohash_update(context->raw_context.c_sha256, input, len) < 0)
return -1;
break;
case CHECKSUM_TYPE_SHA384:
pg_sha384_update(&context->raw_context.c_sha384, input, len);
if (pg_cryptohash_update(context->raw_context.c_sha384, input, len) < 0)
return -1;
break;
case CHECKSUM_TYPE_SHA512:
pg_sha512_update(&context->raw_context.c_sha512, input, len);
if (pg_cryptohash_update(context->raw_context.c_sha512, input, len) < 0)
return -1;
break;
}
return 0;
}
/*
* Finalize a checksum computation and write the result to an output buffer.
*
* The caller must ensure that the buffer is at least PG_CHECKSUM_MAX_LENGTH
* bytes in length. The return value is the number of bytes actually written.
* bytes in length. The return value is the number of bytes actually written,
* or -1 for a failure.
*/
int
pg_checksum_final(pg_checksum_context *context, uint8 *output)
@ -168,19 +207,27 @@ pg_checksum_final(pg_checksum_context *context, uint8 *output)
memcpy(output, &context->raw_context.c_crc32c, retval);
break;
case CHECKSUM_TYPE_SHA224:
pg_sha224_final(&context->raw_context.c_sha224, output);
if (pg_cryptohash_final(context->raw_context.c_sha224, output) < 0)
return -1;
pg_cryptohash_free(context->raw_context.c_sha224);
retval = PG_SHA224_DIGEST_LENGTH;
break;
case CHECKSUM_TYPE_SHA256:
pg_sha256_final(&context->raw_context.c_sha256, output);
retval = PG_SHA256_DIGEST_LENGTH;
if (pg_cryptohash_final(context->raw_context.c_sha256, output) < 0)
return -1;
pg_cryptohash_free(context->raw_context.c_sha256);
retval = PG_SHA224_DIGEST_LENGTH;
break;
case CHECKSUM_TYPE_SHA384:
pg_sha384_final(&context->raw_context.c_sha384, output);
if (pg_cryptohash_final(context->raw_context.c_sha384, output) < 0)
return -1;
pg_cryptohash_free(context->raw_context.c_sha384);
retval = PG_SHA384_DIGEST_LENGTH;
break;
case CHECKSUM_TYPE_SHA512:
pg_sha512_final(&context->raw_context.c_sha512, output);
if (pg_cryptohash_final(context->raw_context.c_sha512, output) < 0)
return -1;
pg_cryptohash_free(context->raw_context.c_sha512);
retval = PG_SHA512_DIGEST_LENGTH;
break;
}

190
src/common/cryptohash.c Normal file
View File

@ -0,0 +1,190 @@
/*-------------------------------------------------------------------------
*
* cryptohash.c
* Fallback implementations for cryptographic hash functions.
*
* This is the set of in-core functions used when there are no other
* alternative options like OpenSSL.
*
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/common/cryptohash.c
*
*-------------------------------------------------------------------------
*/
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif
#include <sys/param.h>
#include "common/cryptohash.h"
#include "sha2_int.h"
/*
* In backend, use palloc/pfree to ease the error handling. In frontend,
* use malloc to be able to return a failure status back to the caller.
*/
#ifndef FRONTEND
#define ALLOC(size) palloc(size)
#define FREE(ptr) pfree(ptr)
#else
#define ALLOC(size) malloc(size)
#define FREE(ptr) free(ptr)
#endif
/*
* pg_cryptohash_create
*
* Allocate a hash context. Returns NULL on failure for an OOM. The
* backend issues an error, without returning.
*/
pg_cryptohash_ctx *
pg_cryptohash_create(pg_cryptohash_type type)
{
pg_cryptohash_ctx *ctx;
ctx = ALLOC(sizeof(pg_cryptohash_ctx));
if (ctx == NULL)
return NULL;
ctx->type = type;
switch (type)
{
case PG_SHA224:
ctx->data = ALLOC(sizeof(pg_sha224_ctx));
break;
case PG_SHA256:
ctx->data = ALLOC(sizeof(pg_sha256_ctx));
break;
case PG_SHA384:
ctx->data = ALLOC(sizeof(pg_sha384_ctx));
break;
case PG_SHA512:
ctx->data = ALLOC(sizeof(pg_sha512_ctx));
break;
}
if (ctx->data == NULL)
{
explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
FREE(ctx);
return NULL;
}
return ctx;
}
/*
* pg_cryptohash_init
*
* Initialize a hash context. Note that this implementation is designed
* to never fail, so this always returns 0.
*/
int
pg_cryptohash_init(pg_cryptohash_ctx *ctx)
{
if (ctx == NULL)
return 0;
switch (ctx->type)
{
case PG_SHA224:
pg_sha224_init((pg_sha224_ctx *) ctx->data);
break;
case PG_SHA256:
pg_sha256_init((pg_sha256_ctx *) ctx->data);
break;
case PG_SHA384:
pg_sha384_init((pg_sha384_ctx *) ctx->data);
break;
case PG_SHA512:
pg_sha512_init((pg_sha512_ctx *) ctx->data);
break;
}
return 0;
}
/*
* pg_cryptohash_update
*
* Update a hash context. Note that this implementation is designed
* to never fail, so this always returns 0.
*/
int
pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
{
if (ctx == NULL)
return 0;
switch (ctx->type)
{
case PG_SHA224:
pg_sha224_update((pg_sha224_ctx *) ctx->data, data, len);
break;
case PG_SHA256:
pg_sha256_update((pg_sha256_ctx *) ctx->data, data, len);
break;
case PG_SHA384:
pg_sha384_update((pg_sha384_ctx *) ctx->data, data, len);
break;
case PG_SHA512:
pg_sha512_update((pg_sha512_ctx *) ctx->data, data, len);
break;
}
return 0;
}
/*
* pg_cryptohash_final
*
* Finalize a hash context. Note that this implementation is designed
* to never fail, so this always returns 0.
*/
int
pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest)
{
if (ctx == NULL)
return 0;
switch (ctx->type)
{
case PG_SHA224:
pg_sha224_final((pg_sha224_ctx *) ctx->data, dest);
break;
case PG_SHA256:
pg_sha256_final((pg_sha256_ctx *) ctx->data, dest);
break;
case PG_SHA384:
pg_sha384_final((pg_sha384_ctx *) ctx->data, dest);
break;
case PG_SHA512:
pg_sha512_final((pg_sha512_ctx *) ctx->data, dest);
break;
}
return 0;
}
/*
* pg_cryptohash_free
*
* Free a hash context.
*/
void
pg_cryptohash_free(pg_cryptohash_ctx *ctx)
{
if (ctx == NULL)
return;
FREE(ctx->data);
explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
FREE(ctx);
}

View File

@ -0,0 +1,197 @@
/*-------------------------------------------------------------------------
*
* cryptohash_openssl.c
* Set of wrapper routines on top of OpenSSL to support cryptographic
* hash functions.
*
* This should only be used if code is compiled with OpenSSL support.
*
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/common/cryptohash_openssl.c
*
*-------------------------------------------------------------------------
*/
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif
#include <openssl/sha.h>
#include "common/cryptohash.h"
/*
* In backend, use palloc/pfree to ease the error handling. In frontend,
* use malloc to be able to return a failure status back to the caller.
*/
#ifndef FRONTEND
#define ALLOC(size) palloc(size)
#define FREE(ptr) pfree(ptr)
#else
#define ALLOC(size) malloc(size)
#define FREE(ptr) free(ptr)
#endif
/*
* pg_cryptohash_create
*
* Allocate a hash context. Returns NULL on failure for an OOM. The
* backend issues an error, without returning.
*/
pg_cryptohash_ctx *
pg_cryptohash_create(pg_cryptohash_type type)
{
pg_cryptohash_ctx *ctx;
ctx = ALLOC(sizeof(pg_cryptohash_ctx));
if (ctx == NULL)
return NULL;
ctx->type = type;
switch (type)
{
case PG_SHA224:
case PG_SHA256:
ctx->data = ALLOC(sizeof(SHA256_CTX));
break;
case PG_SHA384:
case PG_SHA512:
ctx->data = ALLOC(sizeof(SHA512_CTX));
break;
}
if (ctx->data == NULL)
{
explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
FREE(ctx);
return NULL;
}
return ctx;
}
/*
* pg_cryptohash_init
*
* Initialize a hash context. Returns 0 on success, and -1 on failure.
*/
int
pg_cryptohash_init(pg_cryptohash_ctx *ctx)
{
int status = 0;
if (ctx == NULL)
return 0;
switch (ctx->type)
{
case PG_SHA224:
status = SHA224_Init((SHA256_CTX *) ctx->data);
break;
case PG_SHA256:
status = SHA256_Init((SHA256_CTX *) ctx->data);
break;
case PG_SHA384:
status = SHA384_Init((SHA512_CTX *) ctx->data);
break;
case PG_SHA512:
status = SHA512_Init((SHA512_CTX *) ctx->data);
break;
}
/* OpenSSL internals return 1 on success, 0 on failure */
if (status <= 0)
return -1;
return 0;
}
/*
* pg_cryptohash_update
*
* Update a hash context. Returns 0 on success, and -1 on failure.
*/
int
pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len)
{
int status;
if (ctx == NULL)
return 0;
switch (ctx->type)
{
case PG_SHA224:
status = SHA224_Update((SHA256_CTX *) ctx->data, data, len);
break;
case PG_SHA256:
status = SHA256_Update((SHA256_CTX *) ctx->data, data, len);
break;
case PG_SHA384:
status = SHA384_Update((SHA512_CTX *) ctx->data, data, len);
break;
case PG_SHA512:
status = SHA512_Update((SHA512_CTX *) ctx->data, data, len);
break;
}
/* OpenSSL internals return 1 on success, 0 on failure */
if (status <= 0)
return -1;
return 0;
}
/*
* pg_cryptohash_final
*
* Finalize a hash context. Returns 0 on success, and -1 on failure.
*/
int
pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest)
{
int status;
if (ctx == NULL)
return 0;
switch (ctx->type)
{
case PG_SHA224:
status = SHA224_Final(dest, (SHA256_CTX *) ctx->data);
break;
case PG_SHA256:
status = SHA256_Final(dest, (SHA256_CTX *) ctx->data);
break;
case PG_SHA384:
status = SHA384_Final(dest, (SHA512_CTX *) ctx->data);
break;
case PG_SHA512:
status = SHA512_Final(dest, (SHA512_CTX *) ctx->data);
break;
}
/* OpenSSL internals return 1 on success, 0 on failure */
if (status <= 0)
return -1;
return 0;
}
/*
* pg_cryptohash_free
*
* Free a hash context.
*/
void
pg_cryptohash_free(pg_cryptohash_ctx *ctx)
{
if (ctx == NULL)
return;
FREE(ctx->data);
explicit_bzero(ctx, sizeof(pg_cryptohash_ctx));
FREE(ctx);
}

View File

@ -29,9 +29,9 @@
/*
* Calculate HMAC per RFC2104.
*
* The hash function used is SHA-256.
* The hash function used is SHA-256. Returns 0 on success, -1 on failure.
*/
void
int
scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen)
{
uint8 k_ipad[SHA256_HMAC_B];
@ -44,13 +44,21 @@ scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen)
*/
if (keylen > SHA256_HMAC_B)
{
pg_sha256_ctx sha256_ctx;
pg_cryptohash_ctx *sha256_ctx;
pg_sha256_init(&sha256_ctx);
pg_sha256_update(&sha256_ctx, key, keylen);
pg_sha256_final(&sha256_ctx, keybuf);
sha256_ctx = pg_cryptohash_create(PG_SHA256);
if (sha256_ctx == NULL)
return -1;
if (pg_cryptohash_init(sha256_ctx) < 0 ||
pg_cryptohash_update(sha256_ctx, key, keylen) < 0 ||
pg_cryptohash_final(sha256_ctx, keybuf) < 0)
{
pg_cryptohash_free(sha256_ctx);
return -1;
}
key = keybuf;
keylen = SCRAM_KEY_LEN;
pg_cryptohash_free(sha256_ctx);
}
memset(k_ipad, HMAC_IPAD, SHA256_HMAC_B);
@ -62,45 +70,75 @@ scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen)
ctx->k_opad[i] ^= key[i];
}
ctx->sha256ctx = pg_cryptohash_create(PG_SHA256);
if (ctx->sha256ctx == NULL)
return -1;
/* tmp = H(K XOR ipad, text) */
pg_sha256_init(&ctx->sha256ctx);
pg_sha256_update(&ctx->sha256ctx, k_ipad, SHA256_HMAC_B);
if (pg_cryptohash_init(ctx->sha256ctx) < 0 ||
pg_cryptohash_update(ctx->sha256ctx, k_ipad, SHA256_HMAC_B) < 0)
{
pg_cryptohash_free(ctx->sha256ctx);
return -1;
}
return 0;
}
/*
* Update HMAC calculation
* The hash function used is SHA-256.
* The hash function used is SHA-256. Returns 0 on success, -1 on failure.
*/
void
int
scram_HMAC_update(scram_HMAC_ctx *ctx, const char *str, int slen)
{
pg_sha256_update(&ctx->sha256ctx, (const uint8 *) str, slen);
Assert(ctx->sha256ctx != NULL);
if (pg_cryptohash_update(ctx->sha256ctx, (const uint8 *) str, slen) < 0)
{
pg_cryptohash_free(ctx->sha256ctx);
return -1;
}
return 0;
}
/*
* Finalize HMAC calculation.
* The hash function used is SHA-256.
* The hash function used is SHA-256. Returns 0 on success, -1 on failure.
*/
void
int
scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx)
{
uint8 h[SCRAM_KEY_LEN];
pg_sha256_final(&ctx->sha256ctx, h);
Assert(ctx->sha256ctx != NULL);
if (pg_cryptohash_final(ctx->sha256ctx, h) < 0)
{
pg_cryptohash_free(ctx->sha256ctx);
return -1;
}
/* H(K XOR opad, tmp) */
pg_sha256_init(&ctx->sha256ctx);
pg_sha256_update(&ctx->sha256ctx, ctx->k_opad, SHA256_HMAC_B);
pg_sha256_update(&ctx->sha256ctx, h, SCRAM_KEY_LEN);
pg_sha256_final(&ctx->sha256ctx, result);
if (pg_cryptohash_init(ctx->sha256ctx) < 0 ||
pg_cryptohash_update(ctx->sha256ctx, ctx->k_opad, SHA256_HMAC_B) < 0 ||
pg_cryptohash_update(ctx->sha256ctx, h, SCRAM_KEY_LEN) < 0 ||
pg_cryptohash_final(ctx->sha256ctx, result) < 0)
{
pg_cryptohash_free(ctx->sha256ctx);
return -1;
}
pg_cryptohash_free(ctx->sha256ctx);
return 0;
}
/*
* Calculate SaltedPassword.
*
* The password should already be normalized by SASLprep.
* The password should already be normalized by SASLprep. Returns 0 on
* success, -1 on failure.
*/
void
int
scram_SaltedPassword(const char *password,
const char *salt, int saltlen, int iterations,
uint8 *result)
@ -120,63 +158,94 @@ scram_SaltedPassword(const char *password,
*/
/* First iteration */
scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len);
scram_HMAC_update(&hmac_ctx, salt, saltlen);
scram_HMAC_update(&hmac_ctx, (char *) &one, sizeof(uint32));
scram_HMAC_final(Ui_prev, &hmac_ctx);
if (scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len) < 0 ||
scram_HMAC_update(&hmac_ctx, salt, saltlen) < 0 ||
scram_HMAC_update(&hmac_ctx, (char *) &one, sizeof(uint32)) < 0 ||
scram_HMAC_final(Ui_prev, &hmac_ctx) < 0)
{
return -1;
}
memcpy(result, Ui_prev, SCRAM_KEY_LEN);
/* Subsequent iterations */
for (i = 2; i <= iterations; i++)
{
scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len);
scram_HMAC_update(&hmac_ctx, (const char *) Ui_prev, SCRAM_KEY_LEN);
scram_HMAC_final(Ui, &hmac_ctx);
if (scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len) < 0 ||
scram_HMAC_update(&hmac_ctx, (const char *) Ui_prev, SCRAM_KEY_LEN) < 0 ||
scram_HMAC_final(Ui, &hmac_ctx) < 0)
{
return -1;
}
for (j = 0; j < SCRAM_KEY_LEN; j++)
result[j] ^= Ui[j];
memcpy(Ui_prev, Ui, SCRAM_KEY_LEN);
}
return 0;
}
/*
* Calculate SHA-256 hash for a NULL-terminated string. (The NULL terminator is
* not included in the hash).
* not included in the hash). Returns 0 on success, -1 on failure.
*/
void
int
scram_H(const uint8 *input, int len, uint8 *result)
{
pg_sha256_ctx ctx;
pg_cryptohash_ctx *ctx;
pg_sha256_init(&ctx);
pg_sha256_update(&ctx, input, len);
pg_sha256_final(&ctx, result);
ctx = pg_cryptohash_create(PG_SHA256);
if (ctx == NULL)
return -1;
if (pg_cryptohash_init(ctx) < 0 ||
pg_cryptohash_update(ctx, input, len) < 0 ||
pg_cryptohash_final(ctx, result) < 0)
{
pg_cryptohash_free(ctx);
return -1;
}
pg_cryptohash_free(ctx);
return 0;
}
/*
* Calculate ClientKey.
* Calculate ClientKey. Returns 0 on success, -1 on failure.
*/
void
int
scram_ClientKey(const uint8 *salted_password, uint8 *result)
{
scram_HMAC_ctx ctx;
scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN);
scram_HMAC_update(&ctx, "Client Key", strlen("Client Key"));
scram_HMAC_final(result, &ctx);
if (scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN) < 0 ||
scram_HMAC_update(&ctx, "Client Key", strlen("Client Key")) < 0 ||
scram_HMAC_final(result, &ctx) < 0)
{
return -1;
}
return 0;
}
/*
* Calculate ServerKey.
* Calculate ServerKey. Returns 0 on success, -1 on failure.
*/
void
int
scram_ServerKey(const uint8 *salted_password, uint8 *result)
{
scram_HMAC_ctx ctx;
scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN);
scram_HMAC_update(&ctx, "Server Key", strlen("Server Key"));
scram_HMAC_final(result, &ctx);
if (scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN) < 0 ||
scram_HMAC_update(&ctx, "Server Key", strlen("Server Key")) < 0 ||
scram_HMAC_final(result, &ctx) < 0)
{
return -1;
}
return 0;
}
@ -207,12 +276,18 @@ scram_build_secret(const char *salt, int saltlen, int iterations,
iterations = SCRAM_DEFAULT_ITERATIONS;
/* Calculate StoredKey and ServerKey */
scram_SaltedPassword(password, salt, saltlen, iterations,
salted_password);
scram_ClientKey(salted_password, stored_key);
scram_H(stored_key, SCRAM_KEY_LEN, stored_key);
scram_ServerKey(salted_password, server_key);
if (scram_SaltedPassword(password, salt, saltlen, iterations,
salted_password) < 0 ||
scram_ClientKey(salted_password, stored_key) < 0 ||
scram_H(stored_key, SCRAM_KEY_LEN, stored_key) < 0 ||
scram_ServerKey(salted_password, server_key) < 0)
{
#ifdef FRONTEND
return NULL;
#else
elog(ERROR, "could not calculate stored key and server key");
#endif
}
/*----------
* The format is:

View File

@ -1,12 +1,13 @@
/*-------------------------------------------------------------------------
*
* sha2.c
* Set of SHA functions for SHA-224, SHA-256, SHA-384 and SHA-512.
* SHA functions for SHA-224, SHA-256, SHA-384 and SHA-512.
*
* This is the set of in-core functions used when there are no other
* alternative options like OpenSSL.
* This includes the fallback implementation for SHA2 cryptographic
* hashes.
*
* Portions Copyright (c) 2016-2020, PostgreSQL Global Development Group
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/common/sha2.c
@ -56,9 +57,19 @@
#include "postgres_fe.h"
#endif
#include <sys/param.h>
#include "sha2_int.h"
#include "common/sha2.h"
/*
* In backend, use palloc/pfree to ease the error handling. In frontend,
* use malloc to be able to return a failure status back to the caller.
*/
#ifndef FRONTEND
#define ALLOC(size) palloc(size)
#define FREE(ptr) pfree(ptr)
#else
#define ALLOC(size) malloc(size)
#define FREE(ptr) free(ptr)
#endif
/*
* UNROLLED TRANSFORM LOOP NOTE:

91
src/common/sha2_int.h Normal file
View File

@ -0,0 +1,91 @@
/*-------------------------------------------------------------------------
*
* sha2_int.h
* Internal headers for fallback implementation of SHA{224,256,384,512}
*
* Portions Copyright (c) 2016-2020, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/common/sha2_int.h
*
*-------------------------------------------------------------------------
*/
/* $OpenBSD: sha2.h,v 1.2 2004/04/28 23:11:57 millert Exp $ */
/*
* FILE: sha2.h
* AUTHOR: Aaron D. Gifford <me@aarongifford.com>
*
* Copyright (c) 2000-2001, Aaron D. Gifford
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
*/
#ifndef PG_SHA2_INT_H
#define PG_SHA2_INT_H
#include "common/sha2.h"
typedef struct pg_sha256_ctx
{
uint32 state[8];
uint64 bitcount;
uint8 buffer[PG_SHA256_BLOCK_LENGTH];
} pg_sha256_ctx;
typedef struct pg_sha512_ctx
{
uint64 state[8];
uint64 bitcount[2];
uint8 buffer[PG_SHA512_BLOCK_LENGTH];
} pg_sha512_ctx;
typedef struct pg_sha256_ctx pg_sha224_ctx;
typedef struct pg_sha512_ctx pg_sha384_ctx;
/* Interface routines for SHA224/256/384/512 */
extern void pg_sha224_init(pg_sha224_ctx *ctx);
extern void pg_sha224_update(pg_sha224_ctx *ctx, const uint8 *input0,
size_t len);
extern void pg_sha224_final(pg_sha224_ctx *ctx, uint8 *dest);
extern void pg_sha256_init(pg_sha256_ctx *ctx);
extern void pg_sha256_update(pg_sha256_ctx *ctx, const uint8 *input0,
size_t len);
extern void pg_sha256_final(pg_sha256_ctx *ctx, uint8 *dest);
extern void pg_sha384_init(pg_sha384_ctx *ctx);
extern void pg_sha384_update(pg_sha384_ctx *ctx,
const uint8 *, size_t len);
extern void pg_sha384_final(pg_sha384_ctx *ctx, uint8 *dest);
extern void pg_sha512_init(pg_sha512_ctx *ctx);
extern void pg_sha512_update(pg_sha512_ctx *ctx, const uint8 *input0,
size_t len);
extern void pg_sha512_final(pg_sha512_ctx *ctx, uint8 *dest);
#endif /* PG_SHA2_INT_H */

View File

@ -1,102 +0,0 @@
/*-------------------------------------------------------------------------
*
* sha2_openssl.c
* Set of wrapper routines on top of OpenSSL to support SHA-224
* SHA-256, SHA-384 and SHA-512 functions.
*
* This should only be used if code is compiled with OpenSSL support.
*
* Portions Copyright (c) 2016-2020, PostgreSQL Global Development Group
*
* IDENTIFICATION
* src/common/sha2_openssl.c
*
*-------------------------------------------------------------------------
*/
#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif
#include <openssl/sha.h>
#include "common/sha2.h"
/* Interface routines for SHA-256 */
void
pg_sha256_init(pg_sha256_ctx *ctx)
{
SHA256_Init((SHA256_CTX *) ctx);
}
void
pg_sha256_update(pg_sha256_ctx *ctx, const uint8 *data, size_t len)
{
SHA256_Update((SHA256_CTX *) ctx, data, len);
}
void
pg_sha256_final(pg_sha256_ctx *ctx, uint8 *dest)
{
SHA256_Final(dest, (SHA256_CTX *) ctx);
}
/* Interface routines for SHA-512 */
void
pg_sha512_init(pg_sha512_ctx *ctx)
{
SHA512_Init((SHA512_CTX *) ctx);
}
void
pg_sha512_update(pg_sha512_ctx *ctx, const uint8 *data, size_t len)
{
SHA512_Update((SHA512_CTX *) ctx, data, len);
}
void
pg_sha512_final(pg_sha512_ctx *ctx, uint8 *dest)
{
SHA512_Final(dest, (SHA512_CTX *) ctx);
}
/* Interface routines for SHA-384 */
void
pg_sha384_init(pg_sha384_ctx *ctx)
{
SHA384_Init((SHA512_CTX *) ctx);
}
void
pg_sha384_update(pg_sha384_ctx *ctx, const uint8 *data, size_t len)
{
SHA384_Update((SHA512_CTX *) ctx, data, len);
}
void
pg_sha384_final(pg_sha384_ctx *ctx, uint8 *dest)
{
SHA384_Final(dest, (SHA512_CTX *) ctx);
}
/* Interface routines for SHA-224 */
void
pg_sha224_init(pg_sha224_ctx *ctx)
{
SHA224_Init((SHA256_CTX *) ctx);
}
void
pg_sha224_update(pg_sha224_ctx *ctx, const uint8 *data, size_t len)
{
SHA224_Update((SHA256_CTX *) ctx, data, len);
}
void
pg_sha224_final(pg_sha224_ctx *ctx, uint8 *dest)
{
SHA224_Final(dest, (SHA256_CTX *) ctx);
}

View File

@ -14,6 +14,7 @@
#ifndef CHECKSUM_HELPER_H
#define CHECKSUM_HELPER_H
#include "common/cryptohash.h"
#include "common/sha2.h"
#include "port/pg_crc32c.h"
@ -41,10 +42,10 @@ typedef enum pg_checksum_type
typedef union pg_checksum_raw_context
{
pg_crc32c c_crc32c;
pg_sha224_ctx c_sha224;
pg_sha256_ctx c_sha256;
pg_sha384_ctx c_sha384;
pg_sha512_ctx c_sha512;
pg_cryptohash_ctx *c_sha224;
pg_cryptohash_ctx *c_sha256;
pg_cryptohash_ctx *c_sha384;
pg_cryptohash_ctx *c_sha512;
} pg_checksum_raw_context;
/*
@ -66,8 +67,8 @@ typedef struct pg_checksum_context
extern bool pg_checksum_parse_type(char *name, pg_checksum_type *);
extern char *pg_checksum_type_name(pg_checksum_type);
extern void pg_checksum_init(pg_checksum_context *, pg_checksum_type);
extern void pg_checksum_update(pg_checksum_context *, const uint8 *input,
extern int pg_checksum_init(pg_checksum_context *, pg_checksum_type);
extern int pg_checksum_update(pg_checksum_context *, const uint8 *input,
size_t len);
extern int pg_checksum_final(pg_checksum_context *, uint8 *output);

View File

@ -0,0 +1,40 @@
/*-------------------------------------------------------------------------
*
* cryptohash.h
* Generic headers for cryptographic hash functions.
*
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/include/common/cryptohash.h
*
*-------------------------------------------------------------------------
*/
#ifndef PG_CRYPTOHASH_H
#define PG_CRYPTOHASH_H
/* Context Structures for each hash function */
typedef enum
{
PG_SHA224 = 0,
PG_SHA256,
PG_SHA384,
PG_SHA512
} pg_cryptohash_type;
typedef struct pg_cryptohash_ctx
{
pg_cryptohash_type type;
/* private area used by each hash implementation */
void *data;
} pg_cryptohash_ctx;
extern pg_cryptohash_ctx *pg_cryptohash_create(pg_cryptohash_type type);
extern int pg_cryptohash_init(pg_cryptohash_ctx *ctx);
extern int pg_cryptohash_update(pg_cryptohash_ctx *ctx, const uint8 *data, size_t len);
extern int pg_cryptohash_final(pg_cryptohash_ctx *ctx, uint8 *dest);
extern void pg_cryptohash_free(pg_cryptohash_ctx *ctx);
#endif /* PG_CRYPTOHASH_H */

View File

@ -13,6 +13,7 @@
#ifndef SCRAM_COMMON_H
#define SCRAM_COMMON_H
#include "common/cryptohash.h"
#include "common/sha2.h"
/* Name of SCRAM mechanisms per IANA */
@ -50,19 +51,19 @@
*/
typedef struct
{
pg_sha256_ctx sha256ctx;
pg_cryptohash_ctx *sha256ctx;
uint8 k_opad[SHA256_HMAC_B];
} scram_HMAC_ctx;
extern void scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen);
extern void scram_HMAC_update(scram_HMAC_ctx *ctx, const char *str, int slen);
extern void scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx);
extern int scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen);
extern int scram_HMAC_update(scram_HMAC_ctx *ctx, const char *str, int slen);
extern int scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx);
extern void scram_SaltedPassword(const char *password, const char *salt,
extern int scram_SaltedPassword(const char *password, const char *salt,
int saltlen, int iterations, uint8 *result);
extern void scram_H(const uint8 *str, int len, uint8 *result);
extern void scram_ClientKey(const uint8 *salted_password, uint8 *result);
extern void scram_ServerKey(const uint8 *salted_password, uint8 *result);
extern int scram_H(const uint8 *str, int len, uint8 *result);
extern int scram_ClientKey(const uint8 *salted_password, uint8 *result);
extern int scram_ServerKey(const uint8 *salted_password, uint8 *result);
extern char *scram_build_secret(const char *salt, int saltlen, int iterations,
const char *password);

View File

@ -1,9 +1,10 @@
/*-------------------------------------------------------------------------
*
* sha2.h
* Generic headers for SHA224, 256, 384 AND 512 functions of PostgreSQL.
* Constants related to SHA224, 256, 384 AND 512.
*
* Portions Copyright (c) 2016-2020, PostgreSQL Global Development Group
* Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/include/common/sha2.h
@ -11,49 +12,9 @@
*-------------------------------------------------------------------------
*/
/* $OpenBSD: sha2.h,v 1.2 2004/04/28 23:11:57 millert Exp $ */
/*
* FILE: sha2.h
* AUTHOR: Aaron D. Gifford <me@aarongifford.com>
*
* Copyright (c) 2000-2001, Aaron D. Gifford
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $From: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
*/
#ifndef _PG_SHA2_H_
#define _PG_SHA2_H_
#ifdef USE_OPENSSL
#include <openssl/sha.h>
#endif
/*** SHA224/256/384/512 Various Length Definitions ***********************/
#define PG_SHA224_BLOCK_LENGTH 64
#define PG_SHA224_DIGEST_LENGTH 28
@ -68,48 +29,4 @@
#define PG_SHA512_DIGEST_LENGTH 64
#define PG_SHA512_DIGEST_STRING_LENGTH (PG_SHA512_DIGEST_LENGTH * 2 + 1)
/* Context Structures for SHA224/256/384/512 */
#ifdef USE_OPENSSL
typedef SHA256_CTX pg_sha256_ctx;
typedef SHA512_CTX pg_sha512_ctx;
typedef SHA256_CTX pg_sha224_ctx;
typedef SHA512_CTX pg_sha384_ctx;
#else
typedef struct pg_sha256_ctx
{
uint32 state[8];
uint64 bitcount;
uint8 buffer[PG_SHA256_BLOCK_LENGTH];
} pg_sha256_ctx;
typedef struct pg_sha512_ctx
{
uint64 state[8];
uint64 bitcount[2];
uint8 buffer[PG_SHA512_BLOCK_LENGTH];
} pg_sha512_ctx;
typedef struct pg_sha256_ctx pg_sha224_ctx;
typedef struct pg_sha512_ctx pg_sha384_ctx;
#endif /* USE_OPENSSL */
/* Interface routines for SHA224/256/384/512 */
extern void pg_sha224_init(pg_sha224_ctx *ctx);
extern void pg_sha224_update(pg_sha224_ctx *ctx, const uint8 *input0,
size_t len);
extern void pg_sha224_final(pg_sha224_ctx *ctx, uint8 *dest);
extern void pg_sha256_init(pg_sha256_ctx *ctx);
extern void pg_sha256_update(pg_sha256_ctx *ctx, const uint8 *input0,
size_t len);
extern void pg_sha256_final(pg_sha256_ctx *ctx, uint8 *dest);
extern void pg_sha384_init(pg_sha384_ctx *ctx);
extern void pg_sha384_update(pg_sha384_ctx *ctx,
const uint8 *, size_t len);
extern void pg_sha384_final(pg_sha384_ctx *ctx, uint8 *dest);
extern void pg_sha512_init(pg_sha512_ctx *ctx);
extern void pg_sha512_update(pg_sha512_ctx *ctx, const uint8 *input0,
size_t len);
extern void pg_sha512_final(pg_sha512_ctx *ctx, uint8 *dest);
#endif /* _PG_SHA2_H_ */

View File

@ -28,7 +28,7 @@ typedef struct backup_manifest_info
{
BufFile *buffile;
pg_checksum_type checksum_type;
pg_sha256_ctx manifest_ctx;
pg_cryptohash_ctx *manifest_ctx;
uint64 manifest_size;
bool force_encode;
bool first_file;
@ -48,5 +48,6 @@ extern void AddWALInfoToBackupManifest(backup_manifest_info *manifest,
TimeLineID starttli, XLogRecPtr endptr,
TimeLineID endtli);
extern void SendBackupManifest(backup_manifest_info *manifest);
extern void FreeBackupManifest(backup_manifest_info *manifest);
#endif

View File

@ -63,8 +63,8 @@ static bool read_server_first_message(fe_scram_state *state, char *input);
static bool read_server_final_message(fe_scram_state *state, char *input);
static char *build_client_first_message(fe_scram_state *state);
static char *build_client_final_message(fe_scram_state *state);
static bool verify_server_signature(fe_scram_state *state);
static void calculate_client_proof(fe_scram_state *state,
static bool verify_server_signature(fe_scram_state *state, bool *match);
static bool calculate_client_proof(fe_scram_state *state,
const char *client_final_message_without_proof,
uint8 *result);
@ -256,11 +256,15 @@ pg_fe_scram_exchange(void *opaq, char *input, int inputlen,
* Verify server signature, to make sure we're talking to the
* genuine server.
*/
if (verify_server_signature(state))
*success = true;
else
if (!verify_server_signature(state, success))
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not verify server signature\n"));
goto error;
}
if (!*success)
{
*success = false;
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("incorrect server signature\n"));
}
@ -544,9 +548,15 @@ build_client_final_message(fe_scram_state *state)
goto oom_error;
/* Append proof to it, to form client-final-message. */
calculate_client_proof(state,
state->client_final_message_without_proof,
client_proof);
if (!calculate_client_proof(state,
state->client_final_message_without_proof,
client_proof))
{
termPQExpBuffer(&buf);
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not calculate client proof\n"));
return NULL;
}
appendPQExpBufferStr(&buf, ",p=");
encoded_len = pg_b64_enc_len(SCRAM_KEY_LEN);
@ -745,9 +755,9 @@ read_server_final_message(fe_scram_state *state, char *input)
/*
* Calculate the client proof, part of the final exchange message sent
* by the client.
* by the client. Returns true on success, false on failure.
*/
static void
static bool
calculate_client_proof(fe_scram_state *state,
const char *client_final_message_without_proof,
uint8 *result)
@ -762,60 +772,70 @@ calculate_client_proof(fe_scram_state *state,
* Calculate SaltedPassword, and store it in 'state' so that we can reuse
* it later in verify_server_signature.
*/
scram_SaltedPassword(state->password, state->salt, state->saltlen,
state->iterations, state->SaltedPassword);
scram_ClientKey(state->SaltedPassword, ClientKey);
scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey);
scram_HMAC_init(&ctx, 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,
client_final_message_without_proof,
strlen(client_final_message_without_proof));
scram_HMAC_final(ClientSignature, &ctx);
if (scram_SaltedPassword(state->password, state->salt, state->saltlen,
state->iterations, state->SaltedPassword) < 0 ||
scram_ClientKey(state->SaltedPassword, ClientKey) < 0 ||
scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey) < 0 ||
scram_HMAC_init(&ctx, StoredKey, SCRAM_KEY_LEN) < 0 ||
scram_HMAC_update(&ctx,
state->client_first_message_bare,
strlen(state->client_first_message_bare)) < 0 ||
scram_HMAC_update(&ctx, ",", 1) < 0 ||
scram_HMAC_update(&ctx,
state->server_first_message,
strlen(state->server_first_message)) < 0 ||
scram_HMAC_update(&ctx, ",", 1) < 0 ||
scram_HMAC_update(&ctx,
client_final_message_without_proof,
strlen(client_final_message_without_proof)) < 0 ||
scram_HMAC_final(ClientSignature, &ctx) < 0)
{
return false;
}
for (i = 0; i < SCRAM_KEY_LEN; i++)
result[i] = ClientKey[i] ^ ClientSignature[i];
return true;
}
/*
* Validate the server signature, received as part of the final exchange
* message received from the server.
* message received from the server. *match tracks if the server signature
* matched or not. Returns true if the server signature got verified, and
* false for a processing error.
*/
static bool
verify_server_signature(fe_scram_state *state)
verify_server_signature(fe_scram_state *state, bool *match)
{
uint8 expected_ServerSignature[SCRAM_KEY_LEN];
uint8 ServerKey[SCRAM_KEY_LEN];
scram_HMAC_ctx ctx;
scram_ServerKey(state->SaltedPassword, ServerKey);
if (scram_ServerKey(state->SaltedPassword, ServerKey) < 0 ||
/* calculate ServerSignature */
scram_HMAC_init(&ctx, 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(expected_ServerSignature, &ctx);
if (memcmp(expected_ServerSignature, state->ServerSignature, SCRAM_KEY_LEN) != 0)
scram_HMAC_init(&ctx, ServerKey, SCRAM_KEY_LEN) < 0 ||
scram_HMAC_update(&ctx,
state->client_first_message_bare,
strlen(state->client_first_message_bare)) < 0 ||
scram_HMAC_update(&ctx, ",", 1) < 0 ||
scram_HMAC_update(&ctx,
state->server_first_message,
strlen(state->server_first_message)) < 0 ||
scram_HMAC_update(&ctx, ",", 1) < 0 ||
scram_HMAC_update(&ctx,
state->client_final_message_without_proof,
strlen(state->client_final_message_without_proof)) < 0 ||
scram_HMAC_final(expected_ServerSignature, &ctx) < 0)
{
return false;
}
/* signature processed, so now check after it */
if (memcmp(expected_ServerSignature, state->ServerSignature, SCRAM_KEY_LEN) != 0)
*match = false;
else
*match = true;
return true;
}

View File

@ -129,11 +129,12 @@ sub mkvcbuild
if ($solution->{options}->{openssl})
{
push(@pgcommonallfiles, 'sha2_openssl.c');
push(@pgcommonallfiles, 'cryptohash_openssl.c');
push(@pgcommonallfiles, 'protocol_openssl.c');
}
else
{
push(@pgcommonallfiles, 'cryptohash.c');
push(@pgcommonallfiles, 'sha2.c');
}

View File

@ -3179,6 +3179,8 @@ pg_conn_host_type
pg_conv_map
pg_crc32
pg_crc32c
pg_cryptohash_ctx
pg_cryptohash_type
pg_ctype_cache
pg_enc
pg_enc2gettext