mirror of https://github.com/postgres/postgres
This adapts the publicly available reference implementation on https://www.akkadia.org/drepper/SHA-crypt.txt and adds the new hash algorithms sha256crypt and sha512crypt to crypt() and gen_salt() respectively. Author: Bernd Helmle <mailings@oopsware.de> Reviewed-by: Japin Li <japinli@hotmail.com> Discussion: https://postgr.es/m/c763235a2757e2f5f9e3e27268b9028349cef659.camel@oopsware.depull/210/head
parent
e33f2335a9
commit
749a9e20c9
@ -0,0 +1,640 @@ |
|||||||
|
/*
|
||||||
|
* contrib/pgcrypto/crypt-sha.c |
||||||
|
* |
||||||
|
* This implements shacrypt password hash functions and follows the |
||||||
|
* public available reference implementation from |
||||||
|
* |
||||||
|
* https://www.akkadia.org/drepper/SHA-crypt.txt
|
||||||
|
* |
||||||
|
* This code is public domain. |
||||||
|
* |
||||||
|
* Please see the inline comments for details about the algorithm. |
||||||
|
* |
||||||
|
* Basically the following code implements password hashing with sha256 and |
||||||
|
* sha512 digest via OpenSSL. Additionally, an extended salt generation (see |
||||||
|
* crypt-gensalt.c for details) is provided, which generates a salt suitable |
||||||
|
* for either sha256crypt and sha512crypt password hash generation. |
||||||
|
* |
||||||
|
* Official identifiers for suitable password hashes used in salts are |
||||||
|
* 5 : sha256crypt and |
||||||
|
* 6 : sha512crypt |
||||||
|
* |
||||||
|
* The hashing code below supports and uses salt length up to 16 bytes. Longer |
||||||
|
* input is possible, but any additional byte of the input is disregarded. |
||||||
|
* gen_salt(), when called with a sha256crypt or sha512crypt identifier will |
||||||
|
* always generate a 16 byte long salt string. |
||||||
|
* |
||||||
|
* Output is compatible with any sha256crypt and sha512crypt output |
||||||
|
* generated by e.g. OpenSSL or libc crypt(). |
||||||
|
* |
||||||
|
* The described algorithm uses default computing rounds of 5000. Currently, |
||||||
|
* even when no specific rounds specification is used, we always explicitly |
||||||
|
* print out the rounds option flag with the final hash password string. |
||||||
|
* |
||||||
|
* The length of the specific password hash (without magic bytes and salt |
||||||
|
* string) is: |
||||||
|
* |
||||||
|
* sha256crypt: 43 bytes and |
||||||
|
* sha512crypt: 86 bytes. |
||||||
|
* |
||||||
|
* Overall hashed password length is: |
||||||
|
* |
||||||
|
* sha256crypt: 80 bytes and |
||||||
|
* sha512crypt: 123 bytes |
||||||
|
* |
||||||
|
*/ |
||||||
|
#include "postgres.h" |
||||||
|
|
||||||
|
#include "common/string.h" |
||||||
|
#include "miscadmin.h" |
||||||
|
|
||||||
|
#include "px-crypt.h" |
||||||
|
#include "px.h" |
||||||
|
|
||||||
|
typedef enum |
||||||
|
{ |
||||||
|
PGCRYPTO_SHA256CRYPT = 0, |
||||||
|
PGCRYPTO_SHA512CRYPT = 1, |
||||||
|
PGCRYPTO_SHA_UNKOWN |
||||||
|
} PGCRYPTO_SHA_t; |
||||||
|
|
||||||
|
static unsigned char _crypt_itoa64[64 + 1] = |
||||||
|
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Modern UNIX password, based on SHA crypt hashes |
||||||
|
*/ |
||||||
|
char * |
||||||
|
px_crypt_shacrypt(const char *pw, const char *salt, char *passwd, unsigned dstlen) |
||||||
|
{ |
||||||
|
static const char rounds_prefix[] = "rounds="; |
||||||
|
static char *magic_bytes[2] = {"$5$", "$6$"}; |
||||||
|
|
||||||
|
/* Used to create the password hash string */ |
||||||
|
StringInfo out_buf = NULL; |
||||||
|
|
||||||
|
PGCRYPTO_SHA_t type = PGCRYPTO_SHA_UNKOWN; |
||||||
|
PX_MD *digestA = NULL; |
||||||
|
PX_MD *digestB = NULL; |
||||||
|
int err; |
||||||
|
|
||||||
|
const char *dec_salt_binary; /* pointer into the real salt string */ |
||||||
|
StringInfo decoded_salt = NULL; /* decoded salt string */ |
||||||
|
unsigned char sha_buf[PX_SHACRYPT_DIGEST_MAX_LEN]; |
||||||
|
|
||||||
|
/* temporary buffer for digests */ |
||||||
|
unsigned char sha_buf_tmp[PX_SHACRYPT_DIGEST_MAX_LEN]; |
||||||
|
char rounds_custom = 0; |
||||||
|
char *p_bytes = NULL; |
||||||
|
char *s_bytes = NULL; |
||||||
|
char *cp = NULL; |
||||||
|
const char *fp = NULL; /* intermediate pointer within salt string */ |
||||||
|
const char *ep = NULL; /* holds pointer to the end of the salt string */ |
||||||
|
size_t buf_size = 0; /* buffer size for sha256crypt/sha512crypt */ |
||||||
|
unsigned int block; /* number of bytes processed */ |
||||||
|
uint32 rounds = PX_SHACRYPT_ROUNDS_DEFAULT; |
||||||
|
unsigned int len, |
||||||
|
salt_len = 0; |
||||||
|
|
||||||
|
/* Init result buffer */ |
||||||
|
out_buf = makeStringInfoExt(PX_SHACRYPT_BUF_LEN); |
||||||
|
decoded_salt = makeStringInfoExt(PX_SHACRYPT_SALT_MAX_LEN); |
||||||
|
|
||||||
|
/* Sanity checks */ |
||||||
|
if (!passwd) |
||||||
|
return NULL; |
||||||
|
|
||||||
|
if (pw == NULL) |
||||||
|
elog(ERROR, "null value for password rejected"); |
||||||
|
|
||||||
|
if (salt == NULL) |
||||||
|
elog(ERROR, "null value for salt rejected"); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure result buffers are large enough. |
||||||
|
*/ |
||||||
|
if (dstlen < PX_SHACRYPT_BUF_LEN) |
||||||
|
elog(ERROR, "insufficient result buffer size to encrypt password"); |
||||||
|
|
||||||
|
/* Init contents of buffers properly */ |
||||||
|
memset(&sha_buf, '\0', sizeof(sha_buf)); |
||||||
|
memset(&sha_buf_tmp, '\0', sizeof(sha_buf_tmp)); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Decode the salt string. We need to know how many rounds and which |
||||||
|
* digest we have to use to hash the password. |
||||||
|
*/ |
||||||
|
len = strlen(pw); |
||||||
|
dec_salt_binary = salt; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Analyze and prepare the salt string |
||||||
|
* |
||||||
|
* The magic string should be specified in the first three bytes of the |
||||||
|
* salt string. Do some sanity checks first. |
||||||
|
*/ |
||||||
|
if (strlen(dec_salt_binary) < 3) |
||||||
|
ereport(ERROR, |
||||||
|
errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
||||||
|
errmsg("invalid salt")); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Check format of magic bytes. These should define either 5=sha256crypt |
||||||
|
* or 6=sha512crypt in the second byte, enclosed by ascii dollar signs. |
||||||
|
*/ |
||||||
|
if ((dec_salt_binary[0] != '$') || (dec_salt_binary[2] != '$')) |
||||||
|
ereport(ERROR, |
||||||
|
errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
||||||
|
errmsg("invalid format of salt"), |
||||||
|
errhint("magic byte format for shacrypt is either \"$5$\" or \"$6$\"")); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Check magic byte for supported shacrypt digest. |
||||||
|
* |
||||||
|
* We're just interested in the very first 3 bytes of the salt string, |
||||||
|
* since this defines the digest length to use. |
||||||
|
*/ |
||||||
|
if (strncmp(dec_salt_binary, magic_bytes[0], strlen(magic_bytes[0])) == 0) |
||||||
|
{ |
||||||
|
type = PGCRYPTO_SHA256CRYPT; |
||||||
|
dec_salt_binary += strlen(magic_bytes[0]); |
||||||
|
} |
||||||
|
else if (strncmp(dec_salt_binary, magic_bytes[1], strlen(magic_bytes[1])) == 0) |
||||||
|
{ |
||||||
|
type = PGCRYPTO_SHA512CRYPT; |
||||||
|
dec_salt_binary += strlen(magic_bytes[1]); |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* dec_salt_binary pointer is positioned after the magic bytes now |
||||||
|
* |
||||||
|
* We extract any options in the following code branch. The only optional |
||||||
|
* setting we need to take care of is the "rounds" option. Note that the |
||||||
|
* salt generator already checked for invalid settings before, but we need |
||||||
|
* to do it here again to protect against injection of wrong values when |
||||||
|
* called without the generator. |
||||||
|
* |
||||||
|
* If there is any garbage added after the magic byte and the options/salt |
||||||
|
* string, we don't treat this special: This is just absorbed as part of |
||||||
|
* the salt with up to PX_SHACRYPT_SALT_LEN_MAX. |
||||||
|
* |
||||||
|
* Unknown magic byte is handled further below. |
||||||
|
*/ |
||||||
|
if (strncmp(dec_salt_binary, |
||||||
|
rounds_prefix, sizeof(rounds_prefix) - 1) == 0) |
||||||
|
{ |
||||||
|
const char *num = dec_salt_binary + sizeof(rounds_prefix) - 1; |
||||||
|
char *endp; |
||||||
|
int srounds = strtoint(num, &endp, 10); |
||||||
|
|
||||||
|
if (*endp != '$') |
||||||
|
ereport(ERROR, |
||||||
|
errcode(ERRCODE_SYNTAX_ERROR), |
||||||
|
errmsg("could not parse salt options")); |
||||||
|
|
||||||
|
dec_salt_binary = endp + 1; |
||||||
|
|
||||||
|
/*
|
||||||
|
* We violate supported lower or upper bound of rounds, but in this |
||||||
|
* case we change this value to the supported lower or upper value. We |
||||||
|
* don't do this silently and print a NOTICE in such a case. |
||||||
|
* |
||||||
|
* Note that a salt string generated with gen_salt() would never |
||||||
|
* generated such a salt string, since it would error out. |
||||||
|
* |
||||||
|
* But Drepper's upstream reference implementation supports this when |
||||||
|
* passing the salt string directly, so we maintain compatibility. |
||||||
|
*/ |
||||||
|
if (srounds > PX_SHACRYPT_ROUNDS_MAX) |
||||||
|
{ |
||||||
|
ereport(NOTICE, |
||||||
|
errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), |
||||||
|
errmsg("rounds=%d exceeds maximum supported value (%d), using %d instead", |
||||||
|
srounds, PX_SHACRYPT_ROUNDS_MAX, |
||||||
|
PX_SHACRYPT_ROUNDS_MAX)); |
||||||
|
srounds = PX_SHACRYPT_ROUNDS_MAX; |
||||||
|
} |
||||||
|
else if (srounds < PX_SHACRYPT_ROUNDS_MIN) |
||||||
|
{ |
||||||
|
ereport(NOTICE, |
||||||
|
errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), |
||||||
|
errmsg("rounds=%d is below supported value (%d), using %d instead", |
||||||
|
srounds, PX_SHACRYPT_ROUNDS_MIN, |
||||||
|
PX_SHACRYPT_ROUNDS_MIN)); |
||||||
|
srounds = PX_SHACRYPT_ROUNDS_MIN; |
||||||
|
} |
||||||
|
|
||||||
|
rounds = (uint32) srounds; |
||||||
|
rounds_custom = 1; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* Choose the correct digest length and add the magic bytes to the result |
||||||
|
* buffer. Also handle possible invalid magic byte we've extracted above. |
||||||
|
*/ |
||||||
|
switch (type) |
||||||
|
{ |
||||||
|
case PGCRYPTO_SHA256CRYPT: |
||||||
|
{ |
||||||
|
/* Two PX_MD objects required */ |
||||||
|
err = px_find_digest("sha256", &digestA); |
||||||
|
if (err) |
||||||
|
goto error; |
||||||
|
|
||||||
|
err = px_find_digest("sha256", &digestB); |
||||||
|
if (err) |
||||||
|
goto error; |
||||||
|
|
||||||
|
/* digest buffer length is 32 for sha256 */ |
||||||
|
buf_size = 32; |
||||||
|
|
||||||
|
appendStringInfoString(out_buf, magic_bytes[0]); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
case PGCRYPTO_SHA512CRYPT: |
||||||
|
{ |
||||||
|
/* Two PX_MD objects required */ |
||||||
|
err = px_find_digest("sha512", &digestA); |
||||||
|
if (err) |
||||||
|
goto error; |
||||||
|
|
||||||
|
err = px_find_digest("sha512", &digestB); |
||||||
|
if (err) |
||||||
|
goto error; |
||||||
|
|
||||||
|
buf_size = PX_SHACRYPT_DIGEST_MAX_LEN; |
||||||
|
|
||||||
|
appendStringInfoString(out_buf, magic_bytes[1]); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
case PGCRYPTO_SHA_UNKOWN: |
||||||
|
elog(ERROR, "unknown crypt identifier \"%c\"", salt[1]); |
||||||
|
} |
||||||
|
|
||||||
|
if (rounds_custom > 0) |
||||||
|
appendStringInfo(out_buf, "rounds=%u$", rounds); |
||||||
|
|
||||||
|
/*
|
||||||
|
* We need the real decoded salt string from salt input, this is every |
||||||
|
* character before the last '$' in the preamble. Append every compatible |
||||||
|
* character up to PX_SHACRYPT_SALT_MAX_LEN to the result buffer. Note |
||||||
|
* that depending on the input, there might be no '$' marker after the |
||||||
|
* salt, when there is no password hash attached at the end. |
||||||
|
* |
||||||
|
* We try hard to recognize mistakes, but since we might get an input |
||||||
|
* string which might also have the password hash after the salt string |
||||||
|
* section we give up as soon we reach the end of the input or if there |
||||||
|
* are any bytes consumed for the salt string until we reach the first '$' |
||||||
|
* marker thereafter. |
||||||
|
*/ |
||||||
|
for (ep = dec_salt_binary; |
||||||
|
*ep && ep < (dec_salt_binary + PX_SHACRYPT_SALT_MAX_LEN); |
||||||
|
ep++) |
||||||
|
{ |
||||||
|
/*
|
||||||
|
* Filter out any string which shouldn't be here. |
||||||
|
* |
||||||
|
* First check for accidentally embedded magic strings here. We don't |
||||||
|
* support '$' in salt strings anyways and seeing a magic byte trying |
||||||
|
* to identify shacrypt hashes might indicate that something went |
||||||
|
* wrong when generating this salt string. Note that we later check |
||||||
|
* for non-supported literals anyways, but any '$' here confuses us at |
||||||
|
* this point. |
||||||
|
*/ |
||||||
|
fp = strstr(dec_salt_binary, magic_bytes[0]); |
||||||
|
if (fp != NULL) |
||||||
|
elog(ERROR, "bogus magic byte found in salt string"); |
||||||
|
|
||||||
|
fp = strstr(dec_salt_binary, magic_bytes[1]); |
||||||
|
if (fp != NULL) |
||||||
|
elog(ERROR, "bogus magic byte found in salt string"); |
||||||
|
|
||||||
|
/*
|
||||||
|
* This looks very strict, but we assume the caller did something |
||||||
|
* wrong when we see a "rounds=" option here. |
||||||
|
*/ |
||||||
|
fp = strstr(dec_salt_binary, rounds_prefix); |
||||||
|
if (fp != NULL) |
||||||
|
elog(ERROR, "invalid rounds option specified in salt string"); |
||||||
|
|
||||||
|
if (*ep != '$') |
||||||
|
{ |
||||||
|
if (isalpha(*ep) || isdigit(*ep) || (*ep == '.') || (*ep == '/')) |
||||||
|
appendStringInfoCharMacro(decoded_salt, *ep); |
||||||
|
else |
||||||
|
elog(ERROR, "invalid character in salt string: \"%c\"", *ep); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
/*
|
||||||
|
* We encountered a '$' marker. Check if we already absorbed some |
||||||
|
* bytes from input. If true, we are optimistic and terminate at |
||||||
|
* this stage. If not, we try further. |
||||||
|
* |
||||||
|
* If we already consumed enough bytes for the salt string, |
||||||
|
* everything that is after this marker is considered to be part |
||||||
|
* of an optionally specified password hash and ignored. |
||||||
|
*/ |
||||||
|
if (decoded_salt->len > 0) |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
salt_len = decoded_salt->len; |
||||||
|
appendStringInfoString(out_buf, decoded_salt->data); |
||||||
|
elog(DEBUG1, "using salt \"%s\", salt len = %d, rounds = %u", |
||||||
|
decoded_salt->data, decoded_salt->len, rounds); |
||||||
|
|
||||||
|
/*
|
||||||
|
* Sanity check: at this point the salt string buffer must not exceed |
||||||
|
* expected size. |
||||||
|
*/ |
||||||
|
if (out_buf->len > (3 + 17 * rounds_custom + salt_len)) |
||||||
|
elog(ERROR, "unexpected length of salt string"); |
||||||
|
|
||||||
|
/*-
|
||||||
|
* 1. Start digest A |
||||||
|
* 2. Add the password string to digest A |
||||||
|
* 3. Add the salt to digest A |
||||||
|
*/ |
||||||
|
px_md_update(digestA, (const unsigned char *) pw, len); |
||||||
|
px_md_update(digestA, (const unsigned char *) decoded_salt->data, salt_len); |
||||||
|
|
||||||
|
/*-
|
||||||
|
* 4. Create digest B |
||||||
|
* 5. Add password to digest B |
||||||
|
* 6. Add the salt string to digest B |
||||||
|
* 7. Add the password again to digest B |
||||||
|
* 8. Finalize digest B |
||||||
|
*/ |
||||||
|
px_md_update(digestB, (const unsigned char *) pw, len); |
||||||
|
px_md_update(digestB, (const unsigned char *) dec_salt_binary, salt_len); |
||||||
|
px_md_update(digestB, (const unsigned char *) pw, len); |
||||||
|
px_md_finish(digestB, sha_buf); |
||||||
|
|
||||||
|
/*
|
||||||
|
* 9. For each block (excluding the NULL byte), add digest B to digest A. |
||||||
|
*/ |
||||||
|
for (block = len; block > buf_size; block -= buf_size) |
||||||
|
px_md_update(digestA, sha_buf, buf_size); |
||||||
|
|
||||||
|
/*-
|
||||||
|
* 10. For the remaining N bytes of the password string, add the first N |
||||||
|
* bytes of digest B to A. |
||||||
|
*/ |
||||||
|
px_md_update(digestA, sha_buf, block); |
||||||
|
|
||||||
|
/*-
|
||||||
|
* 11. For each bit of the binary representation of the length of the |
||||||
|
* password string up to and including the highest 1-digit, starting from |
||||||
|
* to lowest bit position (numeric value 1) |
||||||
|
* |
||||||
|
* a) for a 1-digit add digest B (sha_buf) to digest A |
||||||
|
* b) for a 0-digit add the password string |
||||||
|
*/ |
||||||
|
block = len; |
||||||
|
while (block) |
||||||
|
{ |
||||||
|
px_md_update(digestA, |
||||||
|
(block & 1) ? sha_buf : (const unsigned char *) pw, |
||||||
|
(block & 1) ? buf_size : len); |
||||||
|
|
||||||
|
/* right shift to next byte */ |
||||||
|
block >>= 1; |
||||||
|
} |
||||||
|
|
||||||
|
/* 12. Finalize digest A */ |
||||||
|
px_md_finish(digestA, sha_buf); |
||||||
|
|
||||||
|
/* 13. Start digest DP */ |
||||||
|
px_md_reset(digestB); |
||||||
|
|
||||||
|
/*-
|
||||||
|
* 14 Add every byte of the password string (excluding trailing NULL) |
||||||
|
* to the digest DP |
||||||
|
*/ |
||||||
|
for (block = len; block > 0; block--) |
||||||
|
px_md_update(digestB, (const unsigned char *) pw, len); |
||||||
|
|
||||||
|
/* 15. Finalize digest DP */ |
||||||
|
px_md_finish(digestB, sha_buf_tmp); |
||||||
|
|
||||||
|
/*-
|
||||||
|
* 16. produce byte sequence P with same length as password. |
||||||
|
* a) for each block of 32 or 64 bytes of length of the password |
||||||
|
* string the entire digest DP is used |
||||||
|
* b) for the remaining N (up to 31 or 63) bytes use the |
||||||
|
* first N bytes of digest DP |
||||||
|
*/ |
||||||
|
if ((p_bytes = palloc0(len)) == NULL) |
||||||
|
{ |
||||||
|
goto error; |
||||||
|
} |
||||||
|
|
||||||
|
/* N step of 16, copy over the bytes from password */ |
||||||
|
for (cp = p_bytes, block = len; block > buf_size; block -= buf_size, cp += buf_size) |
||||||
|
memcpy(cp, sha_buf_tmp, buf_size); |
||||||
|
memcpy(cp, sha_buf_tmp, block); |
||||||
|
|
||||||
|
/*
|
||||||
|
* 17. Start digest DS |
||||||
|
*/ |
||||||
|
px_md_reset(digestB); |
||||||
|
|
||||||
|
/*-
|
||||||
|
* 18. Repeat the following 16+A[0] times, where A[0] represents the first |
||||||
|
* byte in digest A interpreted as an 8-bit unsigned value |
||||||
|
* add the salt to digest DS |
||||||
|
*/ |
||||||
|
for (block = 16 + sha_buf[0]; block > 0; block--) |
||||||
|
px_md_update(digestB, (const unsigned char *) dec_salt_binary, salt_len); |
||||||
|
|
||||||
|
/*
|
||||||
|
* 19. Finalize digest DS |
||||||
|
*/ |
||||||
|
px_md_finish(digestB, sha_buf_tmp); |
||||||
|
|
||||||
|
/*-
|
||||||
|
* 20. Produce byte sequence S of the same length as the salt string where |
||||||
|
* |
||||||
|
* a) for each block of 32 or 64 bytes of length of the salt string the |
||||||
|
* entire digest DS is used |
||||||
|
* |
||||||
|
* b) for the remaining N (up to 31 or 63) bytes use the first N |
||||||
|
* bytes of digest DS |
||||||
|
*/ |
||||||
|
if ((s_bytes = palloc0(salt_len)) == NULL) |
||||||
|
goto error; |
||||||
|
|
||||||
|
for (cp = s_bytes, block = salt_len; block > buf_size; block -= buf_size, cp += buf_size) |
||||||
|
memcpy(cp, sha_buf_tmp, buf_size); |
||||||
|
memcpy(cp, sha_buf_tmp, block); |
||||||
|
|
||||||
|
/* Make sure we don't leave something important behind */ |
||||||
|
px_memset(&sha_buf_tmp, 0, sizeof sha_buf); |
||||||
|
|
||||||
|
/*-
|
||||||
|
* 21. Repeat a loop according to the number specified in the rounds=<N> |
||||||
|
* specification in the salt (or the default value if none is |
||||||
|
* present). Each round is numbered, starting with 0 and up to N-1. |
||||||
|
* |
||||||
|
* The loop uses a digest as input. In the first round it is the |
||||||
|
* digest produced in step 12. In the latter steps it is the digest |
||||||
|
* produced in step 21.h of the previous round. The following text |
||||||
|
* uses the notation "digest A/B" to describe this behavior. |
||||||
|
*/ |
||||||
|
for (block = 0; block < rounds; block++) |
||||||
|
{ |
||||||
|
/*
|
||||||
|
* Make it possible to abort in case large values for "rounds" are |
||||||
|
* specified. |
||||||
|
*/ |
||||||
|
CHECK_FOR_INTERRUPTS(); |
||||||
|
|
||||||
|
/* a) start digest B */ |
||||||
|
px_md_reset(digestB); |
||||||
|
|
||||||
|
/*-
|
||||||
|
* b) for odd round numbers add the byte sequence P to digest B |
||||||
|
* c) for even round numbers add digest A/B |
||||||
|
*/ |
||||||
|
px_md_update(digestB, |
||||||
|
(block & 1) ? (const unsigned char *) p_bytes : sha_buf, |
||||||
|
(block & 1) ? len : buf_size); |
||||||
|
|
||||||
|
/* d) for all round numbers not divisible by 3 add the byte sequence S */ |
||||||
|
if ((block % 3) != 0) |
||||||
|
px_md_update(digestB, (const unsigned char *) s_bytes, salt_len); |
||||||
|
|
||||||
|
/* e) for all round numbers not divisible by 7 add the byte sequence P */ |
||||||
|
if ((block % 7) != 0) |
||||||
|
px_md_update(digestB, (const unsigned char *) p_bytes, len); |
||||||
|
|
||||||
|
/*-
|
||||||
|
* f) for odd round numbers add digest A/C |
||||||
|
* g) for even round numbers add the byte sequence P |
||||||
|
*/ |
||||||
|
px_md_update(digestB, |
||||||
|
(block & 1) ? sha_buf : (const unsigned char *) p_bytes, |
||||||
|
(block & 1) ? buf_size : len); |
||||||
|
|
||||||
|
/* h) finish digest C. */ |
||||||
|
px_md_finish(digestB, sha_buf); |
||||||
|
} |
||||||
|
|
||||||
|
px_md_free(digestA); |
||||||
|
px_md_free(digestB); |
||||||
|
|
||||||
|
digestA = NULL; |
||||||
|
digestB = NULL; |
||||||
|
|
||||||
|
pfree(s_bytes); |
||||||
|
pfree(p_bytes); |
||||||
|
|
||||||
|
s_bytes = NULL; |
||||||
|
p_bytes = NULL; |
||||||
|
|
||||||
|
/* prepare final result buffer */ |
||||||
|
appendStringInfoCharMacro(out_buf, '$'); |
||||||
|
|
||||||
|
#define b64_from_24bit(B2, B1, B0, N) \ |
||||||
|
do { \
|
||||||
|
unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \
|
||||||
|
int i = (N); \
|
||||||
|
while (i-- > 0) \
|
||||||
|
{ \
|
||||||
|
appendStringInfoCharMacro(out_buf, _crypt_itoa64[w & 0x3f]); \
|
||||||
|
w >>= 6; \
|
||||||
|
} \
|
||||||
|
} while (0) |
||||||
|
|
||||||
|
switch (type) |
||||||
|
{ |
||||||
|
case PGCRYPTO_SHA256CRYPT: |
||||||
|
{ |
||||||
|
b64_from_24bit(sha_buf[0], sha_buf[10], sha_buf[20], 4); |
||||||
|
b64_from_24bit(sha_buf[21], sha_buf[1], sha_buf[11], 4); |
||||||
|
b64_from_24bit(sha_buf[12], sha_buf[22], sha_buf[2], 4); |
||||||
|
b64_from_24bit(sha_buf[3], sha_buf[13], sha_buf[23], 4); |
||||||
|
b64_from_24bit(sha_buf[24], sha_buf[4], sha_buf[14], 4); |
||||||
|
b64_from_24bit(sha_buf[15], sha_buf[25], sha_buf[5], 4); |
||||||
|
b64_from_24bit(sha_buf[6], sha_buf[16], sha_buf[26], 4); |
||||||
|
b64_from_24bit(sha_buf[27], sha_buf[7], sha_buf[17], 4); |
||||||
|
b64_from_24bit(sha_buf[18], sha_buf[28], sha_buf[8], 4); |
||||||
|
b64_from_24bit(sha_buf[9], sha_buf[19], sha_buf[29], 4); |
||||||
|
b64_from_24bit(0, sha_buf[31], sha_buf[30], 3); |
||||||
|
|
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
case PGCRYPTO_SHA512CRYPT: |
||||||
|
{ |
||||||
|
b64_from_24bit(sha_buf[0], sha_buf[21], sha_buf[42], 4); |
||||||
|
b64_from_24bit(sha_buf[22], sha_buf[43], sha_buf[1], 4); |
||||||
|
b64_from_24bit(sha_buf[44], sha_buf[2], sha_buf[23], 4); |
||||||
|
b64_from_24bit(sha_buf[3], sha_buf[24], sha_buf[45], 4); |
||||||
|
b64_from_24bit(sha_buf[25], sha_buf[46], sha_buf[4], 4); |
||||||
|
b64_from_24bit(sha_buf[47], sha_buf[5], sha_buf[26], 4); |
||||||
|
b64_from_24bit(sha_buf[6], sha_buf[27], sha_buf[48], 4); |
||||||
|
b64_from_24bit(sha_buf[28], sha_buf[49], sha_buf[7], 4); |
||||||
|
b64_from_24bit(sha_buf[50], sha_buf[8], sha_buf[29], 4); |
||||||
|
b64_from_24bit(sha_buf[9], sha_buf[30], sha_buf[51], 4); |
||||||
|
b64_from_24bit(sha_buf[31], sha_buf[52], sha_buf[10], 4); |
||||||
|
b64_from_24bit(sha_buf[53], sha_buf[11], sha_buf[32], 4); |
||||||
|
b64_from_24bit(sha_buf[12], sha_buf[33], sha_buf[54], 4); |
||||||
|
b64_from_24bit(sha_buf[34], sha_buf[55], sha_buf[13], 4); |
||||||
|
b64_from_24bit(sha_buf[56], sha_buf[14], sha_buf[35], 4); |
||||||
|
b64_from_24bit(sha_buf[15], sha_buf[36], sha_buf[57], 4); |
||||||
|
b64_from_24bit(sha_buf[37], sha_buf[58], sha_buf[16], 4); |
||||||
|
b64_from_24bit(sha_buf[59], sha_buf[17], sha_buf[38], 4); |
||||||
|
b64_from_24bit(sha_buf[18], sha_buf[39], sha_buf[60], 4); |
||||||
|
b64_from_24bit(sha_buf[40], sha_buf[61], sha_buf[19], 4); |
||||||
|
b64_from_24bit(sha_buf[62], sha_buf[20], sha_buf[41], 4); |
||||||
|
b64_from_24bit(0, 0, sha_buf[63], 2); |
||||||
|
|
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
case PGCRYPTO_SHA_UNKOWN: |
||||||
|
/* we shouldn't land here ... */ |
||||||
|
elog(ERROR, "unsupported digest length"); |
||||||
|
} |
||||||
|
|
||||||
|
*cp = '\0'; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy over result to specified buffer. |
||||||
|
* |
||||||
|
* The passwd character buffer should have at least PX_SHACRYPT_BUF_LEN |
||||||
|
* allocated, since we checked above if dstlen is smaller than |
||||||
|
* PX_SHACRYPT_BUF_LEN (which also includes the NULL byte). |
||||||
|
* |
||||||
|
* In that case we would have failed above already. |
||||||
|
*/ |
||||||
|
memcpy(passwd, out_buf->data, out_buf->len); |
||||||
|
|
||||||
|
/* make sure nothing important is left behind */ |
||||||
|
px_memset(&sha_buf, 0, sizeof sha_buf); |
||||||
|
destroyStringInfo(out_buf); |
||||||
|
destroyStringInfo(decoded_salt); |
||||||
|
|
||||||
|
/* ...and we're done */ |
||||||
|
return passwd; |
||||||
|
|
||||||
|
error: |
||||||
|
if (digestA != NULL) |
||||||
|
px_md_free(digestA); |
||||||
|
|
||||||
|
if (digestB != NULL) |
||||||
|
px_md_free(digestB); |
||||||
|
|
||||||
|
if (out_buf != NULL) |
||||||
|
destroyStringInfo(out_buf); |
||||||
|
|
||||||
|
ereport(ERROR, |
||||||
|
errcode(ERRCODE_INTERNAL_ERROR), |
||||||
|
errmsg("cannot create encrypted password")); |
||||||
|
return NULL; /* keep compiler quiet */ |
||||||
|
} |
@ -0,0 +1,196 @@ |
|||||||
|
-- |
||||||
|
-- crypt() and gensalt: sha256crypt, sha512crypt |
||||||
|
-- |
||||||
|
-- $5$ is sha256crypt |
||||||
|
SELECT crypt('', '$5$Szzz0yzz'); |
||||||
|
crypt |
||||||
|
--------------------------------------------------------- |
||||||
|
$5$Szzz0yzz$cA.ZFZKqblRYjdsbrWtVTYa/qSwPQnt2uh0LBtyYAAD |
||||||
|
(1 row) |
||||||
|
|
||||||
|
SELECT crypt('foox', '$5$Szzz0yzz'); |
||||||
|
crypt |
||||||
|
--------------------------------------------------------- |
||||||
|
$5$Szzz0yzz$7hI0rUWkO2QdBkzamh.vP.MIPlbZiwSvu2smhSi6064 |
||||||
|
(1 row) |
||||||
|
|
||||||
|
CREATE TABLE ctest (data text, res text, salt text); |
||||||
|
INSERT INTO ctest VALUES ('password', '', ''); |
||||||
|
-- generate a salt for sha256crypt, default rounds |
||||||
|
UPDATE ctest SET salt = gen_salt('sha256crypt'); |
||||||
|
UPDATE ctest SET res = crypt(data, salt); |
||||||
|
SELECT res = crypt(data, res) AS "worked" |
||||||
|
FROM ctest; |
||||||
|
worked |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
-- generate a salt for sha256crypt, rounds 9999 |
||||||
|
UPDATE ctest SET salt = gen_salt('sha256crypt', 9999); |
||||||
|
UPDATE ctest SET res = crypt(data, salt); |
||||||
|
SELECT res = crypt(data, res) AS "worked" |
||||||
|
FROM ctest; |
||||||
|
worked |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
-- should fail, below supported minimum rounds value |
||||||
|
UPDATE ctest SET salt = gen_salt('sha256crypt', 10); |
||||||
|
ERROR: gen_salt: Incorrect number of rounds |
||||||
|
-- should fail, exceeds supported maximum rounds value |
||||||
|
UPDATE ctest SET salt = gen_salt('sha256crypt', 1000000000); |
||||||
|
ERROR: gen_salt: Incorrect number of rounds |
||||||
|
TRUNCATE ctest; |
||||||
|
-- $6$ is sha512crypt |
||||||
|
SELECT crypt('', '$6$Szzz0yzz'); |
||||||
|
crypt |
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
$6$Szzz0yzz$EGj.JLAovFyAtCJx3YD1DXD1yTXoO9gv4qgLyHBsJJ1lkpnLB8ZPHekm1qXjJCOBc/8thCuHpxNN8Y5xzRYU5. |
||||||
|
(1 row) |
||||||
|
|
||||||
|
SELECT crypt('foox', '$6$Szzz0yzz'); |
||||||
|
crypt |
||||||
|
---------------------------------------------------------------------------------------------------- |
||||||
|
$6$Szzz0yzz$KqDw1Y8kze.VFapkvTc9Y5fbqzltjeRz1aPGC/pkHRhFQZ2aM6PmZpXQjcD7AOH88Bq0CSD.VlmymQzcBMEUl0 |
||||||
|
(1 row) |
||||||
|
|
||||||
|
INSERT INTO ctest VALUES ('password', '', ''); |
||||||
|
-- generate a salt for sha512crypt, default rounds |
||||||
|
UPDATE ctest SET salt = gen_salt('sha512crypt'); |
||||||
|
UPDATE ctest SET res = crypt(data, salt); |
||||||
|
SELECT res = crypt(data, res) AS "worked" |
||||||
|
FROM ctest; |
||||||
|
worked |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
-- generate a salt for sha512crypt, rounds 9999 |
||||||
|
UPDATE ctest SET salt = gen_salt('sha512crypt', 9999); |
||||||
|
UPDATE ctest SET res = crypt(data, salt); |
||||||
|
SELECT res = crypt(data, res) AS "worked" |
||||||
|
FROM ctest; |
||||||
|
worked |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
-- should fail, below supported minimum rounds value |
||||||
|
UPDATE ctest SET salt = gen_salt('sha512crypt', 10); |
||||||
|
ERROR: gen_salt: Incorrect number of rounds |
||||||
|
-- should fail, exceeds supported maximum rounds value |
||||||
|
UPDATE ctest SET salt = gen_salt('sha512crypt', 1000000000); |
||||||
|
ERROR: gen_salt: Incorrect number of rounds |
||||||
|
-- Extended tests taken from public domain code at |
||||||
|
-- https://www.akkadia.org/drepper/SHA-crypt.txt |
||||||
|
-- |
||||||
|
-- We adapt the tests defined there to make sure we are compatible with the reference |
||||||
|
-- implementation. |
||||||
|
-- This tests sha256crypt (magic byte $5$ with salt and rounds) |
||||||
|
SELECT crypt('Hello world!', '$5$saltstring') |
||||||
|
= '$5$saltstring$5B8vYYiY.CVt1RlTTf8KbXBH3hsxY/GNooZaBBGWEc5' AS result; |
||||||
|
result |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
SELECT crypt('Hello world!', '$5$rounds=10000$saltstringsaltstring') |
||||||
|
= '$5$rounds=10000$saltstringsaltst$3xv.VbSHBb41AL9AvLeujZkZRBAwqFMz2.opqey6IcA' AS result; |
||||||
|
result |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
SELECT crypt('This is just a test', '$5$rounds=5000$toolongsaltstring') |
||||||
|
= '$5$rounds=5000$toolongsaltstrin$Un/5jzAHMgOGZ5.mWJpuVolil07guHPvOW8mGRcvxa5' AS result; |
||||||
|
result |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
SELECT crypt('a very much longer text to encrypt. This one even stretches over more' |
||||||
|
'than one line.', '$5$rounds=1400$anotherlongsaltstring') |
||||||
|
= '$5$rounds=1400$anotherlongsalts$Rx.j8H.h8HjEDGomFU8bDkXm3XIUnzyxf12oP84Bnq1' AS result; |
||||||
|
result |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
SELECT crypt('we have a short salt string but not a short password', '$5$rounds=77777$short') |
||||||
|
= '$5$rounds=77777$short$JiO1O3ZpDAxGJeaDIuqCoEFysAe1mZNJRs3pw0KQRd/' AS result; |
||||||
|
result |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
SELECT crypt('a short string', '$5$rounds=123456$asaltof16chars..') |
||||||
|
= '$5$rounds=123456$asaltof16chars..$gP3VQ/6X7UUEW3HkBn2w1/Ptq2jxPyzV/cZKmF/wJvD' AS result; |
||||||
|
result |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
SELECT crypt('the minimum number is still observed', '$5$rounds=10$roundstoolow') |
||||||
|
= '$5$rounds=1000$roundstoolow$yfvwcWrQ8l/K0DAWyuPMDNHpIVlTQebY9l/gL972bIC' AS result; |
||||||
|
NOTICE: rounds=10 is below supported value (1000), using 1000 instead |
||||||
|
result |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
-- The following tests sha512crypt (magic byte $6$ with salt and rounds) |
||||||
|
SELECT crypt('Hello world!', '$6$saltstring') |
||||||
|
= '$6$saltstring$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJuesI68u4OTLiBFdcbYEdFCoEOfaS35inz1' AS result; |
||||||
|
result |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
SELECT crypt('Hello world!', '$6$rounds=10000$saltstringsaltstring') |
||||||
|
= '$6$rounds=10000$saltstringsaltst$OW1/O6BYHV6BcXZu8QVeXbDWra3Oeqh0sbHbbMCVNSnCM/UrjmM0Dp8vOuZeHBy/YTBmSK6H9qs/y3RnOaw5v.' AS result; |
||||||
|
result |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
SELECT crypt('This is just a test', '$6$rounds=5000$toolongsaltstring') |
||||||
|
= '$6$rounds=5000$toolongsaltstrin$lQ8jolhgVRVhY4b5pZKaysCLi0QBxGoNeKQzQ3glMhwllF7oGDZxUhx1yxdYcz/e1JSbq3y6JMxxl8audkUEm0' AS result; |
||||||
|
result |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
SELECT crypt('a very much longer text to encrypt. This one even stretches over more' |
||||||
|
'than one line.', '$6$rounds=1400$anotherlongsaltstring') |
||||||
|
= '$6$rounds=1400$anotherlongsalts$POfYwTEok97VWcjxIiSOjiykti.o/pQs.wPvMxQ6Fm7I6IoYN3CmLs66x9t0oSwbtEW7o7UmJEiDwGqd8p4ur1' AS result; |
||||||
|
result |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
SELECT crypt('we have a short salt string but not a short password', '$6$rounds=77777$short') |
||||||
|
= '$6$rounds=77777$short$WuQyW2YR.hBNpjjRhpYD/ifIw05xdfeEyQoMxIXbkvr0gge1a1x3yRULJ5CCaUeOxFmtlcGZelFl5CxtgfiAc0' AS result; |
||||||
|
result |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
SELECT crypt('a short string', '$6$rounds=123456$asaltof16chars..') |
||||||
|
= '$6$rounds=123456$asaltof16chars..$BtCwjqMJGx5hrJhZywWvt0RLE8uZ4oPwcelCjmw2kSYu.Ec6ycULevoBK25fs2xXgMNrCzIMVcgEJAstJeonj1' AS result; |
||||||
|
result |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
SELECT crypt('the minimum number is still observed', '$6$rounds=10$roundstoolow') |
||||||
|
= '$6$rounds=1000$roundstoolow$kUMsbe306n21p9R.FRkW3IGn.S9NPN0x50YhH1xhLsPuWGsUSklZt58jaTfF4ZEQpyUNGc0dqbpBYYBaHHrsX.' AS result; |
||||||
|
NOTICE: rounds=10 is below supported value (1000), using 1000 instead |
||||||
|
result |
||||||
|
-------- |
||||||
|
t |
||||||
|
(1 row) |
||||||
|
|
||||||
|
-- cleanup |
||||||
|
DROP TABLE ctest; |
@ -0,0 +1,99 @@ |
|||||||
|
-- |
||||||
|
-- crypt() and gensalt: sha256crypt, sha512crypt |
||||||
|
-- |
||||||
|
|
||||||
|
-- $5$ is sha256crypt |
||||||
|
SELECT crypt('', '$5$Szzz0yzz'); |
||||||
|
|
||||||
|
SELECT crypt('foox', '$5$Szzz0yzz'); |
||||||
|
|
||||||
|
CREATE TABLE ctest (data text, res text, salt text); |
||||||
|
INSERT INTO ctest VALUES ('password', '', ''); |
||||||
|
|
||||||
|
-- generate a salt for sha256crypt, default rounds |
||||||
|
UPDATE ctest SET salt = gen_salt('sha256crypt'); |
||||||
|
UPDATE ctest SET res = crypt(data, salt); |
||||||
|
SELECT res = crypt(data, res) AS "worked" |
||||||
|
FROM ctest; |
||||||
|
|
||||||
|
-- generate a salt for sha256crypt, rounds 9999 |
||||||
|
UPDATE ctest SET salt = gen_salt('sha256crypt', 9999); |
||||||
|
UPDATE ctest SET res = crypt(data, salt); |
||||||
|
SELECT res = crypt(data, res) AS "worked" |
||||||
|
FROM ctest; |
||||||
|
|
||||||
|
-- should fail, below supported minimum rounds value |
||||||
|
UPDATE ctest SET salt = gen_salt('sha256crypt', 10); |
||||||
|
|
||||||
|
-- should fail, exceeds supported maximum rounds value |
||||||
|
UPDATE ctest SET salt = gen_salt('sha256crypt', 1000000000); |
||||||
|
|
||||||
|
TRUNCATE ctest; |
||||||
|
|
||||||
|
-- $6$ is sha512crypt |
||||||
|
SELECT crypt('', '$6$Szzz0yzz'); |
||||||
|
|
||||||
|
SELECT crypt('foox', '$6$Szzz0yzz'); |
||||||
|
|
||||||
|
INSERT INTO ctest VALUES ('password', '', ''); |
||||||
|
|
||||||
|
-- generate a salt for sha512crypt, default rounds |
||||||
|
UPDATE ctest SET salt = gen_salt('sha512crypt'); |
||||||
|
UPDATE ctest SET res = crypt(data, salt); |
||||||
|
SELECT res = crypt(data, res) AS "worked" |
||||||
|
FROM ctest; |
||||||
|
|
||||||
|
-- generate a salt for sha512crypt, rounds 9999 |
||||||
|
UPDATE ctest SET salt = gen_salt('sha512crypt', 9999); |
||||||
|
UPDATE ctest SET res = crypt(data, salt); |
||||||
|
SELECT res = crypt(data, res) AS "worked" |
||||||
|
FROM ctest; |
||||||
|
|
||||||
|
-- should fail, below supported minimum rounds value |
||||||
|
UPDATE ctest SET salt = gen_salt('sha512crypt', 10); |
||||||
|
|
||||||
|
-- should fail, exceeds supported maximum rounds value |
||||||
|
UPDATE ctest SET salt = gen_salt('sha512crypt', 1000000000); |
||||||
|
|
||||||
|
-- Extended tests taken from public domain code at |
||||||
|
-- https://www.akkadia.org/drepper/SHA-crypt.txt |
||||||
|
-- |
||||||
|
-- We adapt the tests defined there to make sure we are compatible with the reference |
||||||
|
-- implementation. |
||||||
|
|
||||||
|
-- This tests sha256crypt (magic byte $5$ with salt and rounds) |
||||||
|
SELECT crypt('Hello world!', '$5$saltstring') |
||||||
|
= '$5$saltstring$5B8vYYiY.CVt1RlTTf8KbXBH3hsxY/GNooZaBBGWEc5' AS result; |
||||||
|
SELECT crypt('Hello world!', '$5$rounds=10000$saltstringsaltstring') |
||||||
|
= '$5$rounds=10000$saltstringsaltst$3xv.VbSHBb41AL9AvLeujZkZRBAwqFMz2.opqey6IcA' AS result; |
||||||
|
SELECT crypt('This is just a test', '$5$rounds=5000$toolongsaltstring') |
||||||
|
= '$5$rounds=5000$toolongsaltstrin$Un/5jzAHMgOGZ5.mWJpuVolil07guHPvOW8mGRcvxa5' AS result; |
||||||
|
SELECT crypt('a very much longer text to encrypt. This one even stretches over more' |
||||||
|
'than one line.', '$5$rounds=1400$anotherlongsaltstring') |
||||||
|
= '$5$rounds=1400$anotherlongsalts$Rx.j8H.h8HjEDGomFU8bDkXm3XIUnzyxf12oP84Bnq1' AS result; |
||||||
|
SELECT crypt('we have a short salt string but not a short password', '$5$rounds=77777$short') |
||||||
|
= '$5$rounds=77777$short$JiO1O3ZpDAxGJeaDIuqCoEFysAe1mZNJRs3pw0KQRd/' AS result; |
||||||
|
SELECT crypt('a short string', '$5$rounds=123456$asaltof16chars..') |
||||||
|
= '$5$rounds=123456$asaltof16chars..$gP3VQ/6X7UUEW3HkBn2w1/Ptq2jxPyzV/cZKmF/wJvD' AS result; |
||||||
|
SELECT crypt('the minimum number is still observed', '$5$rounds=10$roundstoolow') |
||||||
|
= '$5$rounds=1000$roundstoolow$yfvwcWrQ8l/K0DAWyuPMDNHpIVlTQebY9l/gL972bIC' AS result; |
||||||
|
|
||||||
|
-- The following tests sha512crypt (magic byte $6$ with salt and rounds) |
||||||
|
SELECT crypt('Hello world!', '$6$saltstring') |
||||||
|
= '$6$saltstring$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJuesI68u4OTLiBFdcbYEdFCoEOfaS35inz1' AS result; |
||||||
|
SELECT crypt('Hello world!', '$6$rounds=10000$saltstringsaltstring') |
||||||
|
= '$6$rounds=10000$saltstringsaltst$OW1/O6BYHV6BcXZu8QVeXbDWra3Oeqh0sbHbbMCVNSnCM/UrjmM0Dp8vOuZeHBy/YTBmSK6H9qs/y3RnOaw5v.' AS result; |
||||||
|
SELECT crypt('This is just a test', '$6$rounds=5000$toolongsaltstring') |
||||||
|
= '$6$rounds=5000$toolongsaltstrin$lQ8jolhgVRVhY4b5pZKaysCLi0QBxGoNeKQzQ3glMhwllF7oGDZxUhx1yxdYcz/e1JSbq3y6JMxxl8audkUEm0' AS result; |
||||||
|
SELECT crypt('a very much longer text to encrypt. This one even stretches over more' |
||||||
|
'than one line.', '$6$rounds=1400$anotherlongsaltstring') |
||||||
|
= '$6$rounds=1400$anotherlongsalts$POfYwTEok97VWcjxIiSOjiykti.o/pQs.wPvMxQ6Fm7I6IoYN3CmLs66x9t0oSwbtEW7o7UmJEiDwGqd8p4ur1' AS result; |
||||||
|
SELECT crypt('we have a short salt string but not a short password', '$6$rounds=77777$short') |
||||||
|
= '$6$rounds=77777$short$WuQyW2YR.hBNpjjRhpYD/ifIw05xdfeEyQoMxIXbkvr0gge1a1x3yRULJ5CCaUeOxFmtlcGZelFl5CxtgfiAc0' AS result; |
||||||
|
SELECT crypt('a short string', '$6$rounds=123456$asaltof16chars..') |
||||||
|
= '$6$rounds=123456$asaltof16chars..$BtCwjqMJGx5hrJhZywWvt0RLE8uZ4oPwcelCjmw2kSYu.Ec6ycULevoBK25fs2xXgMNrCzIMVcgEJAstJeonj1' AS result; |
||||||
|
SELECT crypt('the minimum number is still observed', '$6$rounds=10$roundstoolow') |
||||||
|
= '$6$rounds=1000$roundstoolow$kUMsbe306n21p9R.FRkW3IGn.S9NPN0x50YhH1xhLsPuWGsUSklZt58jaTfF4ZEQpyUNGc0dqbpBYYBaHHrsX.' AS result; |
||||||
|
|
||||||
|
-- cleanup |
||||||
|
DROP TABLE ctest; |
Loading…
Reference in new issue