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 <time.h>
#include "common/cryptohash.h"
#include "common/sha2.h" #include "common/sha2.h"
#include "px.h" #include "px.h"
@ -42,7 +43,6 @@ void init_sha384(PX_MD *h);
void init_sha512(PX_MD *h); void init_sha512(PX_MD *h);
/* SHA224 */ /* SHA224 */
static unsigned static unsigned
int_sha224_len(PX_MD *h) int_sha224_len(PX_MD *h)
{ {
@ -55,42 +55,7 @@ int_sha224_block_len(PX_MD *h)
return PG_SHA224_BLOCK_LENGTH; 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 */ /* SHA256 */
static unsigned static unsigned
int_sha256_len(PX_MD *h) int_sha256_len(PX_MD *h)
{ {
@ -103,42 +68,7 @@ int_sha256_block_len(PX_MD *h)
return PG_SHA256_BLOCK_LENGTH; 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 */ /* SHA384 */
static unsigned static unsigned
int_sha384_len(PX_MD *h) int_sha384_len(PX_MD *h)
{ {
@ -151,42 +81,7 @@ int_sha384_block_len(PX_MD *h)
return PG_SHA384_BLOCK_LENGTH; 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 */ /* SHA512 */
static unsigned static unsigned
int_sha512_len(PX_MD *h) int_sha512_len(PX_MD *h)
{ {
@ -199,37 +94,40 @@ int_sha512_block_len(PX_MD *h)
return PG_SHA512_BLOCK_LENGTH; return PG_SHA512_BLOCK_LENGTH;
} }
/* Generic interface for all SHA2 methods */
static void 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 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 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 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)); pg_cryptohash_free(ctx);
pfree(ctx);
pfree(h); pfree(h);
} }
@ -238,18 +136,17 @@ int_sha512_free(PX_MD *h)
void void
init_sha224(PX_MD *md) init_sha224(PX_MD *md)
{ {
pg_sha224_ctx *ctx; pg_cryptohash_ctx *ctx;
ctx = palloc0(sizeof(*ctx));
ctx = pg_cryptohash_create(PG_SHA224);
md->p.ptr = ctx; md->p.ptr = ctx;
md->result_size = int_sha224_len; md->result_size = int_sha224_len;
md->block_size = int_sha224_block_len; md->block_size = int_sha224_block_len;
md->reset = int_sha224_reset; md->reset = int_sha2_reset;
md->update = int_sha224_update; md->update = int_sha2_update;
md->finish = int_sha224_finish; md->finish = int_sha2_finish;
md->free = int_sha224_free; md->free = int_sha2_free;
md->reset(md); md->reset(md);
} }
@ -257,18 +154,17 @@ init_sha224(PX_MD *md)
void void
init_sha256(PX_MD *md) init_sha256(PX_MD *md)
{ {
pg_sha256_ctx *ctx; pg_cryptohash_ctx *ctx;
ctx = palloc0(sizeof(*ctx));
ctx = pg_cryptohash_create(PG_SHA256);
md->p.ptr = ctx; md->p.ptr = ctx;
md->result_size = int_sha256_len; md->result_size = int_sha256_len;
md->block_size = int_sha256_block_len; md->block_size = int_sha256_block_len;
md->reset = int_sha256_reset; md->reset = int_sha2_reset;
md->update = int_sha256_update; md->update = int_sha2_update;
md->finish = int_sha256_finish; md->finish = int_sha2_finish;
md->free = int_sha256_free; md->free = int_sha2_free;
md->reset(md); md->reset(md);
} }
@ -276,18 +172,17 @@ init_sha256(PX_MD *md)
void void
init_sha384(PX_MD *md) init_sha384(PX_MD *md)
{ {
pg_sha384_ctx *ctx; pg_cryptohash_ctx *ctx;
ctx = palloc0(sizeof(*ctx));
ctx = pg_cryptohash_create(PG_SHA384);
md->p.ptr = ctx; md->p.ptr = ctx;
md->result_size = int_sha384_len; md->result_size = int_sha384_len;
md->block_size = int_sha384_block_len; md->block_size = int_sha384_block_len;
md->reset = int_sha384_reset; md->reset = int_sha2_reset;
md->update = int_sha384_update; md->update = int_sha2_update;
md->finish = int_sha384_finish; md->finish = int_sha2_finish;
md->free = int_sha384_free; md->free = int_sha2_free;
md->reset(md); md->reset(md);
} }
@ -295,18 +190,17 @@ init_sha384(PX_MD *md)
void void
init_sha512(PX_MD *md) init_sha512(PX_MD *md)
{ {
pg_sha512_ctx *ctx; pg_cryptohash_ctx *ctx;
ctx = palloc0(sizeof(*ctx));
ctx = pg_cryptohash_create(PG_SHA512);
md->p.ptr = ctx; md->p.ptr = ctx;
md->result_size = int_sha512_len; md->result_size = int_sha512_len;
md->block_size = int_sha512_block_len; md->block_size = int_sha512_block_len;
md->reset = int_sha512_reset; md->reset = int_sha2_reset;
md->update = int_sha512_update; md->update = int_sha2_update;
md->finish = int_sha512_finish; md->finish = int_sha2_finish;
md->free = int_sha512_free; md->free = int_sha2_free;
md->reset(md); md->reset(md);
} }

View File

@ -527,8 +527,12 @@ scram_verify_plain_password(const char *username, const char *password,
password = prep_password; password = prep_password;
/* Compute Server Key based on the user-supplied plaintext password */ /* Compute Server Key based on the user-supplied plaintext password */
scram_SaltedPassword(password, salt, saltlen, iterations, salted_password); if (scram_SaltedPassword(password, salt, saltlen, iterations,
scram_ServerKey(salted_password, computed_key); salted_password) < 0 ||
scram_ServerKey(salted_password, computed_key) < 0)
{
elog(ERROR, "could not compute server key");
}
if (prep_password) if (prep_password)
pfree(prep_password); pfree(prep_password);
@ -651,8 +655,17 @@ mock_scram_secret(const char *username, int *iterations, char **salt,
char *encoded_salt; char *encoded_salt;
int encoded_len; 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); 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); encoded_len = pg_b64_enc_len(SCRAM_DEFAULT_SALT_LEN);
/* don't forget the zero-terminator */ /* 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 = pg_b64_encode(raw_salt, SCRAM_DEFAULT_SALT_LEN, encoded_salt,
encoded_len); 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) if (encoded_len < 0)
elog(ERROR, "could not encode salt"); elog(ERROR, "could not encode salt");
encoded_salt[encoded_len] = '\0'; 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 * 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 static bool
verify_client_proof(scram_state *state) verify_client_proof(scram_state *state)
@ -1095,27 +1103,35 @@ verify_client_proof(scram_state *state)
scram_HMAC_ctx ctx; scram_HMAC_ctx ctx;
int i; int i;
/* calculate ClientSignature */ /*
scram_HMAC_init(&ctx, state->StoredKey, SCRAM_KEY_LEN); * Calculate ClientSignature. Note that we don't log directly a failure
scram_HMAC_update(&ctx, * here even when processing the calculations as this could involve a mock
state->client_first_message_bare, * authentication.
strlen(state->client_first_message_bare)); */
scram_HMAC_update(&ctx, ",", 1); if (scram_HMAC_init(&ctx, state->StoredKey, SCRAM_KEY_LEN) < 0 ||
scram_HMAC_update(&ctx, scram_HMAC_update(&ctx,
state->server_first_message, state->client_first_message_bare,
strlen(state->server_first_message)); strlen(state->client_first_message_bare)) < 0 ||
scram_HMAC_update(&ctx, ",", 1); scram_HMAC_update(&ctx, ",", 1) < 0 ||
scram_HMAC_update(&ctx, scram_HMAC_update(&ctx,
state->client_final_message_without_proof, state->server_first_message,
strlen(state->client_final_message_without_proof)); strlen(state->server_first_message)) < 0 ||
scram_HMAC_final(ClientSignature, &ctx); 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 */ /* Extract the ClientKey that the client calculated from the proof */
for (i = 0; i < SCRAM_KEY_LEN; i++) for (i = 0; i < SCRAM_KEY_LEN; i++)
ClientKey[i] = state->ClientProof[i] ^ ClientSignature[i]; ClientKey[i] = state->ClientProof[i] ^ ClientSignature[i];
/* Hash it one more time, and compare with StoredKey */ /* 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) if (memcmp(client_StoredKey, state->StoredKey, SCRAM_KEY_LEN) != 0)
return false; return false;
@ -1346,19 +1362,22 @@ build_server_final_message(scram_state *state)
scram_HMAC_ctx ctx; scram_HMAC_ctx ctx;
/* calculate ServerSignature */ /* calculate ServerSignature */
scram_HMAC_init(&ctx, state->ServerKey, SCRAM_KEY_LEN); if (scram_HMAC_init(&ctx, state->ServerKey, SCRAM_KEY_LEN) < 0 ||
scram_HMAC_update(&ctx, scram_HMAC_update(&ctx,
state->client_first_message_bare, state->client_first_message_bare,
strlen(state->client_first_message_bare)); strlen(state->client_first_message_bare)) < 0 ||
scram_HMAC_update(&ctx, ",", 1); scram_HMAC_update(&ctx, ",", 1) < 0 ||
scram_HMAC_update(&ctx, scram_HMAC_update(&ctx,
state->server_first_message, state->server_first_message,
strlen(state->server_first_message)); strlen(state->server_first_message)) < 0 ||
scram_HMAC_update(&ctx, ",", 1); scram_HMAC_update(&ctx, ",", 1) < 0 ||
scram_HMAC_update(&ctx, scram_HMAC_update(&ctx,
state->client_final_message_without_proof, state->client_final_message_without_proof,
strlen(state->client_final_message_without_proof)); strlen(state->client_final_message_without_proof)) < 0 ||
scram_HMAC_final(ServerSignature, &ctx); scram_HMAC_final(ServerSignature, &ctx) < 0)
{
elog(ERROR, "could not calculate server signature");
}
siglen = pg_b64_enc_len(SCRAM_KEY_LEN); siglen = pg_b64_enc_len(SCRAM_KEY_LEN);
/* don't forget the zero-terminator */ /* 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 * Deterministically generate salt for mock authentication, using a SHA256
* hash based on the username and a cluster-level secret key. Returns a * 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 * static char *
scram_mock_salt(const char *username) scram_mock_salt(const char *username)
{ {
pg_sha256_ctx ctx; pg_cryptohash_ctx *ctx;
static uint8 sha_digest[PG_SHA256_DIGEST_LENGTH]; static uint8 sha_digest[PG_SHA256_DIGEST_LENGTH];
char *mock_auth_nonce = GetMockAuthenticationNonce(); char *mock_auth_nonce = GetMockAuthenticationNonce();
@ -1406,10 +1425,16 @@ scram_mock_salt(const char *username)
StaticAssertStmt(PG_SHA256_DIGEST_LENGTH >= SCRAM_DEFAULT_SALT_LEN, StaticAssertStmt(PG_SHA256_DIGEST_LENGTH >= SCRAM_DEFAULT_SALT_LEN,
"salt length greater than SHA256 digest length"); "salt length greater than SHA256 digest length");
pg_sha256_init(&ctx); ctx = pg_cryptohash_create(PG_SHA256);
pg_sha256_update(&ctx, (uint8 *) username, strlen(username)); if (pg_cryptohash_init(ctx) < 0 ||
pg_sha256_update(&ctx, (uint8 *) mock_auth_nonce, MOCK_AUTH_NONCE_LEN); pg_cryptohash_update(ctx, (uint8 *) username, strlen(username)) < 0 ||
pg_sha256_final(&ctx, sha_digest); 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; return (char *) sha_digest;
} }

View File

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

View File

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

View File

@ -13,6 +13,7 @@
*/ */
#include "postgres.h" #include "postgres.h"
#include "common/cryptohash.h"
#include "common/md5.h" #include "common/md5.h"
#include "common/sha2.h" #include "common/sha2.h"
#include "utils/builtins.h" #include "utils/builtins.h"
@ -78,16 +79,21 @@ sha224_bytea(PG_FUNCTION_ARGS)
bytea *in = PG_GETARG_BYTEA_PP(0); bytea *in = PG_GETARG_BYTEA_PP(0);
const uint8 *data; const uint8 *data;
size_t len; size_t len;
pg_sha224_ctx ctx; pg_cryptohash_ctx *ctx;
unsigned char buf[PG_SHA224_DIGEST_LENGTH]; unsigned char buf[PG_SHA224_DIGEST_LENGTH];
bytea *result; bytea *result;
len = VARSIZE_ANY_EXHDR(in); len = VARSIZE_ANY_EXHDR(in);
data = (unsigned char *) VARDATA_ANY(in); data = (unsigned char *) VARDATA_ANY(in);
pg_sha224_init(&ctx); ctx = pg_cryptohash_create(PG_SHA224);
pg_sha224_update(&ctx, data, len); if (pg_cryptohash_init(ctx) < 0)
pg_sha224_final(&ctx, buf); 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); result = palloc(sizeof(buf) + VARHDRSZ);
SET_VARSIZE(result, 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); bytea *in = PG_GETARG_BYTEA_PP(0);
const uint8 *data; const uint8 *data;
size_t len; size_t len;
pg_sha256_ctx ctx; pg_cryptohash_ctx *ctx;
unsigned char buf[PG_SHA256_DIGEST_LENGTH]; unsigned char buf[PG_SHA256_DIGEST_LENGTH];
bytea *result; bytea *result;
len = VARSIZE_ANY_EXHDR(in); len = VARSIZE_ANY_EXHDR(in);
data = (unsigned char *) VARDATA_ANY(in); data = (unsigned char *) VARDATA_ANY(in);
pg_sha256_init(&ctx); ctx = pg_cryptohash_create(PG_SHA256);
pg_sha256_update(&ctx, data, len); if (pg_cryptohash_init(ctx) < 0)
pg_sha256_final(&ctx, buf); 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); result = palloc(sizeof(buf) + VARHDRSZ);
SET_VARSIZE(result, 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); bytea *in = PG_GETARG_BYTEA_PP(0);
const uint8 *data; const uint8 *data;
size_t len; size_t len;
pg_sha384_ctx ctx; pg_cryptohash_ctx *ctx;
unsigned char buf[PG_SHA384_DIGEST_LENGTH]; unsigned char buf[PG_SHA384_DIGEST_LENGTH];
bytea *result; bytea *result;
len = VARSIZE_ANY_EXHDR(in); len = VARSIZE_ANY_EXHDR(in);
data = (unsigned char *) VARDATA_ANY(in); data = (unsigned char *) VARDATA_ANY(in);
pg_sha384_init(&ctx); ctx = pg_cryptohash_create(PG_SHA384);
pg_sha384_update(&ctx, data, len); if (pg_cryptohash_init(ctx) < 0)
pg_sha384_final(&ctx, buf); 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); result = palloc(sizeof(buf) + VARHDRSZ);
SET_VARSIZE(result, 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); bytea *in = PG_GETARG_BYTEA_PP(0);
const uint8 *data; const uint8 *data;
size_t len; size_t len;
pg_sha512_ctx ctx; pg_cryptohash_ctx *ctx;
unsigned char buf[PG_SHA512_DIGEST_LENGTH]; unsigned char buf[PG_SHA512_DIGEST_LENGTH];
bytea *result; bytea *result;
len = VARSIZE_ANY_EXHDR(in); len = VARSIZE_ANY_EXHDR(in);
data = (unsigned char *) VARDATA_ANY(in); data = (unsigned char *) VARDATA_ANY(in);
pg_sha512_init(&ctx); ctx = pg_cryptohash_create(PG_SHA512);
pg_sha512_update(&ctx, data, len); if (pg_cryptohash_init(ctx) < 0)
pg_sha512_final(&ctx, buf); 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); result = palloc(sizeof(buf) + VARHDRSZ);
SET_VARSIZE(result, 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 number_of_newlines = 0;
size_t ultimate_newline = 0; size_t ultimate_newline = 0;
size_t penultimate_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_actual[PG_SHA256_DIGEST_LENGTH];
uint8 manifest_checksum_expected[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"); "last line not newline-terminated");
/* Checksum the rest. */ /* Checksum the rest. */
pg_sha256_init(&manifest_ctx); manifest_ctx = pg_cryptohash_create(PG_SHA256);
pg_sha256_update(&manifest_ctx, (uint8 *) buffer, penultimate_newline + 1); if (manifest_ctx == NULL)
pg_sha256_final(&manifest_ctx, manifest_checksum_actual); 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. */ /* Now verify it. */
if (parse->manifest_checksum == NULL) if (parse->manifest_checksum == NULL)
@ -667,6 +673,7 @@ verify_manifest_checksum(JsonManifestParseState *parse, char *buffer,
if (memcmp(manifest_checksum_actual, manifest_checksum_expected, if (memcmp(manifest_checksum_actual, manifest_checksum_expected,
PG_SHA256_DIGEST_LENGTH) != 0) PG_SHA256_DIGEST_LENGTH) != 0)
context->error_cb(context, "manifest checksum mismatch"); 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. */ /* 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. */ /* Read the file chunk by chunk, updating the checksum as we go. */
while ((rc = read(fd, buffer, READ_CHUNK_SIZE)) > 0) while ((rc = read(fd, buffer, READ_CHUNK_SIZE)) > 0)
{ {
bytes_read += rc; 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) if (rc < 0)
report_backup_error(context, "could not read file \"%s\": %m", 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. */ /* Get the final checksum. */
checksumlen = pg_checksum_final(&checksum_ctx, checksumbuf); 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. */ /* And check it against the manifest. */
if (checksumlen != m->checksum_length) if (checksumlen != m->checksum_length)

View File

@ -82,9 +82,11 @@ OBJS_COMMON = \
ifeq ($(with_openssl),yes) ifeq ($(with_openssl),yes)
OBJS_COMMON += \ OBJS_COMMON += \
protocol_openssl.o \ protocol_openssl.o \
sha2_openssl.o cryptohash_openssl.o
else else
OBJS_COMMON += sha2.o OBJS_COMMON += \
cryptohash.o \
sha2.o
endif endif
# A few files are currently only built for frontend, not server # 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. * 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) pg_checksum_init(pg_checksum_context *context, pg_checksum_type type)
{ {
context->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); INIT_CRC32C(context->raw_context.c_crc32c);
break; break;
case CHECKSUM_TYPE_SHA224: 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; break;
case CHECKSUM_TYPE_SHA256: 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; break;
case CHECKSUM_TYPE_SHA384: 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; break;
case CHECKSUM_TYPE_SHA512: 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; break;
} }
return 0;
} }
/* /*
* Update a checksum context with new data. * 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, pg_checksum_update(pg_checksum_context *context, const uint8 *input,
size_t len) 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); COMP_CRC32C(context->raw_context.c_crc32c, input, len);
break; break;
case CHECKSUM_TYPE_SHA224: 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; break;
case CHECKSUM_TYPE_SHA256: 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; break;
case CHECKSUM_TYPE_SHA384: 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; break;
case CHECKSUM_TYPE_SHA512: 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; break;
} }
return 0;
} }
/* /*
* Finalize a checksum computation and write the result to an output buffer. * 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 * 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 int
pg_checksum_final(pg_checksum_context *context, uint8 *output) 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); memcpy(output, &context->raw_context.c_crc32c, retval);
break; break;
case CHECKSUM_TYPE_SHA224: 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; retval = PG_SHA224_DIGEST_LENGTH;
break; break;
case CHECKSUM_TYPE_SHA256: case CHECKSUM_TYPE_SHA256:
pg_sha256_final(&context->raw_context.c_sha256, output); if (pg_cryptohash_final(context->raw_context.c_sha256, output) < 0)
retval = PG_SHA256_DIGEST_LENGTH; return -1;
pg_cryptohash_free(context->raw_context.c_sha256);
retval = PG_SHA224_DIGEST_LENGTH;
break; break;
case CHECKSUM_TYPE_SHA384: 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; retval = PG_SHA384_DIGEST_LENGTH;
break; break;
case CHECKSUM_TYPE_SHA512: 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; retval = PG_SHA512_DIGEST_LENGTH;
break; 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. * 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) scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen)
{ {
uint8 k_ipad[SHA256_HMAC_B]; 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) if (keylen > SHA256_HMAC_B)
{ {
pg_sha256_ctx sha256_ctx; pg_cryptohash_ctx *sha256_ctx;
pg_sha256_init(&sha256_ctx); sha256_ctx = pg_cryptohash_create(PG_SHA256);
pg_sha256_update(&sha256_ctx, key, keylen); if (sha256_ctx == NULL)
pg_sha256_final(&sha256_ctx, keybuf); 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; key = keybuf;
keylen = SCRAM_KEY_LEN; keylen = SCRAM_KEY_LEN;
pg_cryptohash_free(sha256_ctx);
} }
memset(k_ipad, HMAC_IPAD, SHA256_HMAC_B); 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->k_opad[i] ^= key[i];
} }
ctx->sha256ctx = pg_cryptohash_create(PG_SHA256);
if (ctx->sha256ctx == NULL)
return -1;
/* tmp = H(K XOR ipad, text) */ /* tmp = H(K XOR ipad, text) */
pg_sha256_init(&ctx->sha256ctx); if (pg_cryptohash_init(ctx->sha256ctx) < 0 ||
pg_sha256_update(&ctx->sha256ctx, k_ipad, SHA256_HMAC_B); pg_cryptohash_update(ctx->sha256ctx, k_ipad, SHA256_HMAC_B) < 0)
{
pg_cryptohash_free(ctx->sha256ctx);
return -1;
}
return 0;
} }
/* /*
* Update HMAC calculation * 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) 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. * 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) scram_HMAC_final(uint8 *result, scram_HMAC_ctx *ctx)
{ {
uint8 h[SCRAM_KEY_LEN]; 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) */ /* H(K XOR opad, tmp) */
pg_sha256_init(&ctx->sha256ctx); if (pg_cryptohash_init(ctx->sha256ctx) < 0 ||
pg_sha256_update(&ctx->sha256ctx, ctx->k_opad, SHA256_HMAC_B); pg_cryptohash_update(ctx->sha256ctx, ctx->k_opad, SHA256_HMAC_B) < 0 ||
pg_sha256_update(&ctx->sha256ctx, h, SCRAM_KEY_LEN); pg_cryptohash_update(ctx->sha256ctx, h, SCRAM_KEY_LEN) < 0 ||
pg_sha256_final(&ctx->sha256ctx, result); pg_cryptohash_final(ctx->sha256ctx, result) < 0)
{
pg_cryptohash_free(ctx->sha256ctx);
return -1;
}
pg_cryptohash_free(ctx->sha256ctx);
return 0;
} }
/* /*
* Calculate SaltedPassword. * 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, scram_SaltedPassword(const char *password,
const char *salt, int saltlen, int iterations, const char *salt, int saltlen, int iterations,
uint8 *result) uint8 *result)
@ -120,63 +158,94 @@ scram_SaltedPassword(const char *password,
*/ */
/* First iteration */ /* First iteration */
scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len); if (scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len) < 0 ||
scram_HMAC_update(&hmac_ctx, salt, saltlen); scram_HMAC_update(&hmac_ctx, salt, saltlen) < 0 ||
scram_HMAC_update(&hmac_ctx, (char *) &one, sizeof(uint32)); scram_HMAC_update(&hmac_ctx, (char *) &one, sizeof(uint32)) < 0 ||
scram_HMAC_final(Ui_prev, &hmac_ctx); scram_HMAC_final(Ui_prev, &hmac_ctx) < 0)
{
return -1;
}
memcpy(result, Ui_prev, SCRAM_KEY_LEN); memcpy(result, Ui_prev, SCRAM_KEY_LEN);
/* Subsequent iterations */ /* Subsequent iterations */
for (i = 2; i <= iterations; i++) for (i = 2; i <= iterations; i++)
{ {
scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len); if (scram_HMAC_init(&hmac_ctx, (uint8 *) password, password_len) < 0 ||
scram_HMAC_update(&hmac_ctx, (const char *) Ui_prev, SCRAM_KEY_LEN); scram_HMAC_update(&hmac_ctx, (const char *) Ui_prev, SCRAM_KEY_LEN) < 0 ||
scram_HMAC_final(Ui, &hmac_ctx); scram_HMAC_final(Ui, &hmac_ctx) < 0)
{
return -1;
}
for (j = 0; j < SCRAM_KEY_LEN; j++) for (j = 0; j < SCRAM_KEY_LEN; j++)
result[j] ^= Ui[j]; result[j] ^= Ui[j];
memcpy(Ui_prev, Ui, SCRAM_KEY_LEN); memcpy(Ui_prev, Ui, SCRAM_KEY_LEN);
} }
return 0;
} }
/* /*
* Calculate SHA-256 hash for a NULL-terminated string. (The NULL terminator is * 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) scram_H(const uint8 *input, int len, uint8 *result)
{ {
pg_sha256_ctx ctx; pg_cryptohash_ctx *ctx;
pg_sha256_init(&ctx); ctx = pg_cryptohash_create(PG_SHA256);
pg_sha256_update(&ctx, input, len); if (ctx == NULL)
pg_sha256_final(&ctx, result); 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_ClientKey(const uint8 *salted_password, uint8 *result)
{ {
scram_HMAC_ctx ctx; scram_HMAC_ctx ctx;
scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN); if (scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN) < 0 ||
scram_HMAC_update(&ctx, "Client Key", strlen("Client Key")); scram_HMAC_update(&ctx, "Client Key", strlen("Client Key")) < 0 ||
scram_HMAC_final(result, &ctx); 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_ServerKey(const uint8 *salted_password, uint8 *result)
{ {
scram_HMAC_ctx ctx; scram_HMAC_ctx ctx;
scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN); if (scram_HMAC_init(&ctx, salted_password, SCRAM_KEY_LEN) < 0 ||
scram_HMAC_update(&ctx, "Server Key", strlen("Server Key")); scram_HMAC_update(&ctx, "Server Key", strlen("Server Key")) < 0 ||
scram_HMAC_final(result, &ctx); 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; iterations = SCRAM_DEFAULT_ITERATIONS;
/* Calculate StoredKey and ServerKey */ /* Calculate StoredKey and ServerKey */
scram_SaltedPassword(password, salt, saltlen, iterations, if (scram_SaltedPassword(password, salt, saltlen, iterations,
salted_password); salted_password) < 0 ||
scram_ClientKey(salted_password, stored_key); scram_ClientKey(salted_password, stored_key) < 0 ||
scram_H(stored_key, SCRAM_KEY_LEN, stored_key); scram_H(stored_key, SCRAM_KEY_LEN, stored_key) < 0 ||
scram_ServerKey(salted_password, server_key) < 0)
scram_ServerKey(salted_password, server_key); {
#ifdef FRONTEND
return NULL;
#else
elog(ERROR, "could not calculate stored key and server key");
#endif
}
/*---------- /*----------
* The format is: * The format is:

View File

@ -1,12 +1,13 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* sha2.c * 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 * This includes the fallback implementation for SHA2 cryptographic
* alternative options like OpenSSL. * 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 * IDENTIFICATION
* src/common/sha2.c * src/common/sha2.c
@ -56,9 +57,19 @@
#include "postgres_fe.h" #include "postgres_fe.h"
#endif #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: * 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 #ifndef CHECKSUM_HELPER_H
#define CHECKSUM_HELPER_H #define CHECKSUM_HELPER_H
#include "common/cryptohash.h"
#include "common/sha2.h" #include "common/sha2.h"
#include "port/pg_crc32c.h" #include "port/pg_crc32c.h"
@ -41,10 +42,10 @@ typedef enum pg_checksum_type
typedef union pg_checksum_raw_context typedef union pg_checksum_raw_context
{ {
pg_crc32c c_crc32c; pg_crc32c c_crc32c;
pg_sha224_ctx c_sha224; pg_cryptohash_ctx *c_sha224;
pg_sha256_ctx c_sha256; pg_cryptohash_ctx *c_sha256;
pg_sha384_ctx c_sha384; pg_cryptohash_ctx *c_sha384;
pg_sha512_ctx c_sha512; pg_cryptohash_ctx *c_sha512;
} pg_checksum_raw_context; } 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 bool pg_checksum_parse_type(char *name, pg_checksum_type *);
extern char *pg_checksum_type_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 int pg_checksum_init(pg_checksum_context *, pg_checksum_type);
extern void pg_checksum_update(pg_checksum_context *, const uint8 *input, extern int pg_checksum_update(pg_checksum_context *, const uint8 *input,
size_t len); size_t len);
extern int pg_checksum_final(pg_checksum_context *, uint8 *output); 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 #ifndef SCRAM_COMMON_H
#define SCRAM_COMMON_H #define SCRAM_COMMON_H
#include "common/cryptohash.h"
#include "common/sha2.h" #include "common/sha2.h"
/* Name of SCRAM mechanisms per IANA */ /* Name of SCRAM mechanisms per IANA */
@ -50,19 +51,19 @@
*/ */
typedef struct typedef struct
{ {
pg_sha256_ctx sha256ctx; pg_cryptohash_ctx *sha256ctx;
uint8 k_opad[SHA256_HMAC_B]; uint8 k_opad[SHA256_HMAC_B];
} scram_HMAC_ctx; } scram_HMAC_ctx;
extern void scram_HMAC_init(scram_HMAC_ctx *ctx, const uint8 *key, int keylen); extern int 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 int 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_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); int saltlen, int iterations, uint8 *result);
extern void scram_H(const uint8 *str, int len, uint8 *result); extern int scram_H(const uint8 *str, int len, uint8 *result);
extern void scram_ClientKey(const uint8 *salted_password, uint8 *result); extern int scram_ClientKey(const uint8 *salted_password, uint8 *result);
extern void scram_ServerKey(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, extern char *scram_build_secret(const char *salt, int saltlen, int iterations,
const char *password); const char *password);

View File

@ -1,9 +1,10 @@
/*------------------------------------------------------------------------- /*-------------------------------------------------------------------------
* *
* sha2.h * 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 * IDENTIFICATION
* src/include/common/sha2.h * 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_ #ifndef _PG_SHA2_H_
#define _PG_SHA2_H_ #define _PG_SHA2_H_
#ifdef USE_OPENSSL
#include <openssl/sha.h>
#endif
/*** SHA224/256/384/512 Various Length Definitions ***********************/ /*** SHA224/256/384/512 Various Length Definitions ***********************/
#define PG_SHA224_BLOCK_LENGTH 64 #define PG_SHA224_BLOCK_LENGTH 64
#define PG_SHA224_DIGEST_LENGTH 28 #define PG_SHA224_DIGEST_LENGTH 28
@ -68,48 +29,4 @@
#define PG_SHA512_DIGEST_LENGTH 64 #define PG_SHA512_DIGEST_LENGTH 64
#define PG_SHA512_DIGEST_STRING_LENGTH (PG_SHA512_DIGEST_LENGTH * 2 + 1) #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_ */ #endif /* _PG_SHA2_H_ */

View File

@ -28,7 +28,7 @@ typedef struct backup_manifest_info
{ {
BufFile *buffile; BufFile *buffile;
pg_checksum_type checksum_type; pg_checksum_type checksum_type;
pg_sha256_ctx manifest_ctx; pg_cryptohash_ctx *manifest_ctx;
uint64 manifest_size; uint64 manifest_size;
bool force_encode; bool force_encode;
bool first_file; bool first_file;
@ -48,5 +48,6 @@ extern void AddWALInfoToBackupManifest(backup_manifest_info *manifest,
TimeLineID starttli, XLogRecPtr endptr, TimeLineID starttli, XLogRecPtr endptr,
TimeLineID endtli); TimeLineID endtli);
extern void SendBackupManifest(backup_manifest_info *manifest); extern void SendBackupManifest(backup_manifest_info *manifest);
extern void FreeBackupManifest(backup_manifest_info *manifest);
#endif #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 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_first_message(fe_scram_state *state);
static char *build_client_final_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 bool verify_server_signature(fe_scram_state *state, bool *match);
static void calculate_client_proof(fe_scram_state *state, static bool calculate_client_proof(fe_scram_state *state,
const char *client_final_message_without_proof, const char *client_final_message_without_proof,
uint8 *result); 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 * Verify server signature, to make sure we're talking to the
* genuine server. * genuine server.
*/ */
if (verify_server_signature(state)) if (!verify_server_signature(state, success))
*success = true; {
else printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not verify server signature\n"));
goto error;
}
if (!*success)
{ {
*success = false;
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("incorrect server signature\n")); libpq_gettext("incorrect server signature\n"));
} }
@ -544,9 +548,15 @@ build_client_final_message(fe_scram_state *state)
goto oom_error; goto oom_error;
/* Append proof to it, to form client-final-message. */ /* Append proof to it, to form client-final-message. */
calculate_client_proof(state, if (!calculate_client_proof(state,
state->client_final_message_without_proof, state->client_final_message_without_proof,
client_proof); client_proof))
{
termPQExpBuffer(&buf);
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not calculate client proof\n"));
return NULL;
}
appendPQExpBufferStr(&buf, ",p="); appendPQExpBufferStr(&buf, ",p=");
encoded_len = pg_b64_enc_len(SCRAM_KEY_LEN); 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 * 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, calculate_client_proof(fe_scram_state *state,
const char *client_final_message_without_proof, const char *client_final_message_without_proof,
uint8 *result) 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 * Calculate SaltedPassword, and store it in 'state' so that we can reuse
* it later in verify_server_signature. * it later in verify_server_signature.
*/ */
scram_SaltedPassword(state->password, state->salt, state->saltlen, if (scram_SaltedPassword(state->password, state->salt, state->saltlen,
state->iterations, state->SaltedPassword); state->iterations, state->SaltedPassword) < 0 ||
scram_ClientKey(state->SaltedPassword, ClientKey) < 0 ||
scram_ClientKey(state->SaltedPassword, ClientKey); scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey) < 0 ||
scram_H(ClientKey, SCRAM_KEY_LEN, StoredKey); scram_HMAC_init(&ctx, StoredKey, SCRAM_KEY_LEN) < 0 ||
scram_HMAC_update(&ctx,
scram_HMAC_init(&ctx, StoredKey, SCRAM_KEY_LEN); state->client_first_message_bare,
scram_HMAC_update(&ctx, strlen(state->client_first_message_bare)) < 0 ||
state->client_first_message_bare, scram_HMAC_update(&ctx, ",", 1) < 0 ||
strlen(state->client_first_message_bare)); scram_HMAC_update(&ctx,
scram_HMAC_update(&ctx, ",", 1); state->server_first_message,
scram_HMAC_update(&ctx, strlen(state->server_first_message)) < 0 ||
state->server_first_message, scram_HMAC_update(&ctx, ",", 1) < 0 ||
strlen(state->server_first_message)); scram_HMAC_update(&ctx,
scram_HMAC_update(&ctx, ",", 1); client_final_message_without_proof,
scram_HMAC_update(&ctx, strlen(client_final_message_without_proof)) < 0 ||
client_final_message_without_proof, scram_HMAC_final(ClientSignature, &ctx) < 0)
strlen(client_final_message_without_proof)); {
scram_HMAC_final(ClientSignature, &ctx); return false;
}
for (i = 0; i < SCRAM_KEY_LEN; i++) for (i = 0; i < SCRAM_KEY_LEN; i++)
result[i] = ClientKey[i] ^ ClientSignature[i]; result[i] = ClientKey[i] ^ ClientSignature[i];
return true;
} }
/* /*
* Validate the server signature, received as part of the final exchange * 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 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 expected_ServerSignature[SCRAM_KEY_LEN];
uint8 ServerKey[SCRAM_KEY_LEN]; uint8 ServerKey[SCRAM_KEY_LEN];
scram_HMAC_ctx ctx; scram_HMAC_ctx ctx;
scram_ServerKey(state->SaltedPassword, ServerKey); if (scram_ServerKey(state->SaltedPassword, ServerKey) < 0 ||
/* calculate ServerSignature */ /* calculate ServerSignature */
scram_HMAC_init(&ctx, ServerKey, SCRAM_KEY_LEN); scram_HMAC_init(&ctx, ServerKey, SCRAM_KEY_LEN) < 0 ||
scram_HMAC_update(&ctx, scram_HMAC_update(&ctx,
state->client_first_message_bare, state->client_first_message_bare,
strlen(state->client_first_message_bare)); strlen(state->client_first_message_bare)) < 0 ||
scram_HMAC_update(&ctx, ",", 1); scram_HMAC_update(&ctx, ",", 1) < 0 ||
scram_HMAC_update(&ctx, scram_HMAC_update(&ctx,
state->server_first_message, state->server_first_message,
strlen(state->server_first_message)); strlen(state->server_first_message)) < 0 ||
scram_HMAC_update(&ctx, ",", 1); scram_HMAC_update(&ctx, ",", 1) < 0 ||
scram_HMAC_update(&ctx, scram_HMAC_update(&ctx,
state->client_final_message_without_proof, state->client_final_message_without_proof,
strlen(state->client_final_message_without_proof)); strlen(state->client_final_message_without_proof)) < 0 ||
scram_HMAC_final(expected_ServerSignature, &ctx); scram_HMAC_final(expected_ServerSignature, &ctx) < 0)
{
if (memcmp(expected_ServerSignature, state->ServerSignature, SCRAM_KEY_LEN) != 0)
return false; 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; return true;
} }

View File

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

View File

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