pgcrypto support for bcrypt $2b$ hashes

Started by Daniel Foneover 4 years ago11 messages
#1Daniel Fone
daniel@fone.net.nz
1 attachment(s)

Hello,

I've recently been working with a database containing bcrypt hashes generated by a 3rd-party which use the $2b$ prefix. This prefix was introduced in 2014 and has since been recognised by a number of bcrypt implementations. [1]https://marc.info/?l=openbsd-misc&m=139320023202696[2]https://www.openwall.com/lists/announce/2014/08/31/1[3]https://github.com/kelektiv/node.bcrypt.js/pull/549/files#diff-c55280c5e4da52b0f86244d3b95c5ae0abf2fcd121a071dba1363540875b32bc[4]https://github.com/bcrypt-ruby/bcrypt-ruby/commit/d19ea481618420922b533a8b0ed049109404cb13

At the moment, pgcrypto’s `crypt` doesn’t recognise this prefix. However, simply `replace`ing the prefix with $2a$ allows crypt to validate the hashes. This patch simply adds recognition for the prefix and treats the hash identically to the $2a$ hashes.

Is this a reasonable change to pgcrypto? I note that the last upstream change brought into crypt-blowfish.c was in 2011, predating this prefix. [5]https://github.com/postgres/postgres/commit/ca59dfa6f727fe3bf3a01904ec30e87f7fa5a67e Are there deeper concerns or other upstream changes that need to be addressed alongside this? Is there a better approach to this?

At the moment, the $2x$ variant is supported but not mentioned in the docs, so I haven’t included any documentation updates.

Thanks,

Daniel

[1]: https://marc.info/?l=openbsd-misc&m=139320023202696
[2]: https://www.openwall.com/lists/announce/2014/08/31/1
[3]: https://github.com/kelektiv/node.bcrypt.js/pull/549/files#diff-c55280c5e4da52b0f86244d3b95c5ae0abf2fcd121a071dba1363540875b32bc
[4]: https://github.com/bcrypt-ruby/bcrypt-ruby/commit/d19ea481618420922b533a8b0ed049109404cb13
[5]: https://github.com/postgres/postgres/commit/ca59dfa6f727fe3bf3a01904ec30e87f7fa5a67e

Attachments:

recognise-bcrypt-2b.patch.txttext/plain; name=recognise-bcrypt-2b.patch.txt; x-unix-mode=0644Download
diff --git a/contrib/pgcrypto/crypt-blowfish.c b/contrib/pgcrypto/crypt-blowfish.c
index a663852c..9012ca43 100644
--- a/contrib/pgcrypto/crypt-blowfish.c
+++ b/contrib/pgcrypto/crypt-blowfish.c
@@ -618,7 +618,7 @@ _crypt_blowfish_rn(const char *key, const char *setting,
 
 	if (setting[0] != '$' ||
 		setting[1] != '2' ||
-		(setting[2] != 'a' && setting[2] != 'x') ||
+		(setting[2] != 'a' && setting[2] != 'b' && setting[2] != 'x') ||
 		setting[3] != '$' ||
 		setting[4] < '0' || setting[4] > '3' ||
 		setting[5] < '0' || setting[5] > '9' ||
diff --git a/contrib/pgcrypto/expected/crypt-blowfish.out b/contrib/pgcrypto/expected/crypt-blowfish.out
index d79b0c04..3d6b215c 100644
--- a/contrib/pgcrypto/expected/crypt-blowfish.out
+++ b/contrib/pgcrypto/expected/crypt-blowfish.out
@@ -13,6 +13,13 @@ SELECT crypt('foox', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O');
  $2a$06$RQiOJ.3ELirrXwxIZY8q0OR3CVJrAfda1z26CCHPnB6mmVZD8p0/C
 (1 row)
 
+-- support $2b$ hashes
+SELECT crypt('password', '$2b$06$cqhNY4qXkVeKliLWOxCFieDY6/J4SmCEojsXfHs7iF4X1r3tuNepW');
+                            crypt                             
+--------------------------------------------------------------
+ $2b$06$cqhNY4qXkVeKliLWOxCFieDY6/J4SmCEojsXfHs7iF4X1r3tuNepW
+(1 row)
+
 -- error, salt too short:
 SELECT crypt('foox', '$2a$');
 ERROR:  invalid salt
diff --git a/contrib/pgcrypto/px-crypt.c b/contrib/pgcrypto/px-crypt.c
index 0913ff2c..9272d409 100644
--- a/contrib/pgcrypto/px-crypt.c
+++ b/contrib/pgcrypto/px-crypt.c
@@ -78,6 +78,7 @@ struct px_crypt_algo
 static const struct px_crypt_algo
 			px_crypt_list[] = {
 	{"$2a$", 4, run_crypt_bf},
+	{"$2b$", 4, run_crypt_bf},
 	{"$2x$", 4, run_crypt_bf},
 	{"$2$", 3, NULL},			/* N/A */
 	{"$1$", 3, run_crypt_md5},
diff --git a/contrib/pgcrypto/sql/crypt-blowfish.sql b/contrib/pgcrypto/sql/crypt-blowfish.sql
index 3b5a681c..0e96ee0c 100644
--- a/contrib/pgcrypto/sql/crypt-blowfish.sql
+++ b/contrib/pgcrypto/sql/crypt-blowfish.sql
@@ -6,6 +6,9 @@ SELECT crypt('', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O');
 
 SELECT crypt('foox', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O');
 
+-- support $2b$ hashes
+SELECT crypt('password', '$2b$06$cqhNY4qXkVeKliLWOxCFieDY6/J4SmCEojsXfHs7iF4X1r3tuNepW');
+
 -- error, salt too short:
 SELECT crypt('foox', '$2a$');
 
#2Daniel Gustafsson
daniel@yesql.se
In reply to: Daniel Fone (#1)
Re: pgcrypto support for bcrypt $2b$ hashes

On 24 Sep 2021, at 04:12, Daniel Fone <daniel@fone.net.nz> wrote:

At the moment, pgcrypto’s `crypt` doesn’t recognise this prefix. However, simply `replace`ing the prefix with $2a$ allows crypt to validate the hashes. This patch simply adds recognition for the prefix and treats the hash identically to the $2a$ hashes.

But 2b and 2a hashes aren't equal, although very similar. 2a should have the
many-buggy to one-correct collision safety and 2b hashes shouldn't. The fact
that your hashes work isn't conclusive evidence.

Is this a reasonable change to pgcrypto?

I think it's reasonable to support 2b hashes, but not like how this patch does
it.

I note that the last upstream change brought into crypt-blowfish.c was in 2011, predating this prefix. [5] Are there deeper concerns or other upstream changes that need to be addressed alongside this?

Upgrading our crypt_blowfish.c to the upstream 1.3 version would be the correct
fix IMO, but since we have a few local modifications it's not a drop-in. I
don't think it would be too hairy, but one needs to be very careful when
dealing with crypto.

Is there a better approach to this?

Compile with OpenSSL support, then pgcrypto will use the libcrypto implementation.

At the moment, the $2x$ variant is supported but not mentioned in the docs, so I haven’t included any documentation updates.

Actually it is, in table F.16 in the below documentation page we refer to our
supported level as "Blowfish-based, variant 2a".

https://www.postgresql.org/docs/devel/pgcrypto.html

--
Daniel Gustafsson https://vmware.com/

#3Daniel Fone
daniel@fone.net.nz
In reply to: Daniel Gustafsson (#2)
Re: pgcrypto support for bcrypt $2b$ hashes

Hi Daniel,

Thanks for the feedback.

On 26/09/2021, at 12:09 AM, Daniel Gustafsson <daniel@yesql.se> wrote:

But 2b and 2a hashes aren't equal, although very similar. 2a should have the
many-buggy to one-correct collision safety and 2b hashes shouldn't. The fact
that your hashes work isn't conclusive evidence.

I was afraid this might be a bit naive. Re-reading the crypt_blowfish release notes, it’s principally the changes introducing $2y$ into 1.2 that we need, with support for OpenBSD $2b$ introduced in 1.3. Do I understand this correctly?

Upgrading our crypt_blowfish.c to the upstream 1.3 version would be the correct
fix IMO, but since we have a few local modifications it's not a drop-in. I
don't think it would be too hairy, but one needs to be very careful when
dealing with crypto.

My C experience is limited, but I can make an initial attempt if the effort would be worthwhile. Is this realistically a patch that a newcomer to the codebase should attempt?

Actually it is, in table F.16 in the below documentation page we refer to our
supported level as "Blowfish-based, variant 2a”.

Sorry I wasn’t clear. My point was that the docs only mention $2a$, and $2x$ isn’t mentioned even though pgcrypto supports it. As part of the upgrade to 1.3, perhaps the docs can be updated to mention variants x, y, and b as well.

Thanks,

Daniel

#4Daniel Gustafsson
daniel@yesql.se
In reply to: Daniel Fone (#3)
1 attachment(s)
Re: pgcrypto support for bcrypt $2b$ hashes

On 28 Sep 2021, at 05:15, Daniel Fone <daniel@fone.net.nz> wrote:

Hi Daniel,

Thanks for the feedback.

On 26/09/2021, at 12:09 AM, Daniel Gustafsson <daniel@yesql.se> wrote:

But 2b and 2a hashes aren't equal, although very similar. 2a should have the
many-buggy to one-correct collision safety and 2b hashes shouldn't. The fact
that your hashes work isn't conclusive evidence.

I was afraid this might be a bit naive. Re-reading the crypt_blowfish release notes, it’s principally the changes introducing $2y$ into 1.2 that we need, with support for OpenBSD $2b$ introduced in 1.3. Do I understand this correctly?

Yeah, we'd want a port of 1.3 into pgcrypto essentially.

Upgrading our crypt_blowfish.c to the upstream 1.3 version would be the correct
fix IMO, but since we have a few local modifications it's not a drop-in. I
don't think it would be too hairy, but one needs to be very careful when
dealing with crypto.

My C experience is limited, but I can make an initial attempt if the effort would be worthwhile. Is this realistically a patch that a newcomer to the codebase should attempt?

I don't see why not, the best first patches are those scratching an itch. If
you feel up for it then give it a go, I - and the rest of pgsql-hackers - can
help if you need to bounce ideas. Many of the changes in the pgcrypto BF code
is whitespace and formatting, which are performed via pgindent. I would
suggest to familiarize yourself with pgindent in order to tease them out
easier. Another set of changes are around error handling and reporting, which
is postgres specific.

Actually it is, in table F.16 in the below documentation page we refer to our
supported level as "Blowfish-based, variant 2a”.

Sorry I wasn’t clear. My point was that the docs only mention $2a$, and $2x$ isn’t mentioned even though pgcrypto supports it. As part of the upgrade to 1.3, perhaps the docs can be updated to mention variants x, y, and b as well.

Aha, now I see what you mean, yes you are right. I think the docs should be
updated regardless of the above as a first step to properly match what's in the
tree. Unless there are objections I propose to apply the attached.

--
Daniel Gustafsson https://vmware.com/

Attachments:

pgcrypto_blowfish.diffapplication/octet-stream; name=pgcrypto_blowfish.diff; x-unix-mode=0644Download
diff --git a/doc/src/sgml/pgcrypto.sgml b/doc/src/sgml/pgcrypto.sgml
index bbaa2691fe..70ed9fc38e 100644
--- a/doc/src/sgml/pgcrypto.sgml
+++ b/doc/src/sgml/pgcrypto.sgml
@@ -159,7 +159,7 @@ hmac(data bytea, key bytea, type text) returns bytea
       <entry>yes</entry>
       <entry>128</entry>
       <entry>60</entry>
-      <entry>Blowfish-based, variant 2a</entry>
+      <entry>Blowfish-based, variant 2a and 2x</entry>
      </row>
      <row>
       <entry><literal>md5</literal></entry>
#5Daniel Fone
daniel@fone.net.nz
In reply to: Daniel Gustafsson (#4)
1 attachment(s)
Re: pgcrypto support for bcrypt $2b$ hashes

On 29/09/2021, at 2:33 AM, Daniel Gustafsson <daniel@yesql.se> wrote:

On 28 Sep 2021, at 05:15, Daniel Fone <daniel@fone.net.nz> wrote:

On 26/09/2021, at 12:09 AM, Daniel Gustafsson <daniel@yesql.se> wrote:

Upgrading our crypt_blowfish.c to the upstream 1.3 version would be the correct
fix IMO, but since we have a few local modifications it's not a drop-in. I
don't think it would be too hairy, but one needs to be very careful when
dealing with crypto.

My C experience is limited, but I can make an initial attempt if the effort would be worthwhile. Is this realistically a patch that a newcomer to the codebase should attempt?

I don't see why not, the best first patches are those scratching an itch. If
you feel up for it then give it a go, I - and the rest of pgsql-hackers - can
help if you need to bounce ideas.

I’m glad you said that. I couldn’t resist trying and have attached a patch. By referencing the respective git logs, I didn’t have too much difficulty identifying the material changes in each codebase. I’ve documented all the postgres-specific changes to upstream in the header comment for each file.

Daniel

Attachments:

0001-Merge-upstream-crypt_blowfish-1.3.patchapplication/octet-stream; name=0001-Merge-upstream-crypt_blowfish-1.3.patch; x-unix-mode=0644Download
From d931c95d3ae998b0adbe0b6ce4f5dc536c1b6308 Mon Sep 17 00:00:00 2001
From: Daniel Fone <daniel@fone.net.nz>
Date: Wed, 29 Sep 2021 10:31:26 +1300
Subject: [PATCH] Merge upstream crypt_blowfish 1.3

This adds support for 2y and 2b hash variants.
---
 contrib/pgcrypto/crypt-blowfish.c            | 365 ++++++++++++++-----
 contrib/pgcrypto/crypt-gensalt.c             |  46 ++-
 contrib/pgcrypto/expected/crypt-blowfish.out |   7 +
 contrib/pgcrypto/px-crypt.c                  |  17 +-
 contrib/pgcrypto/px-crypt.h                  |  12 +-
 contrib/pgcrypto/sql/crypt-blowfish.sql      |   3 +
 doc/src/sgml/pgcrypto.sgml                   |   2 +-
 7 files changed, 346 insertions(+), 106 deletions(-)

diff --git a/contrib/pgcrypto/crypt-blowfish.c b/contrib/pgcrypto/crypt-blowfish.c
index a663852c..d59486a6 100644
--- a/contrib/pgcrypto/crypt-blowfish.c
+++ b/contrib/pgcrypto/crypt-blowfish.c
@@ -1,27 +1,40 @@
 /*
  * contrib/pgcrypto/crypt-blowfish.c
  *
+ * The crypt_blowfish homepage is:
+ *
+ *	http://www.openwall.com/crypt/
+ *
  * This code comes from John the Ripper password cracker, with reentrant
  * and crypt(3) interfaces added, but optimizations specific to password
  * cracking removed.
  *
- * Written by Solar Designer <solar at openwall.com> in 1998-2002 and
- * placed in the public domain.
+ * Written by Solar Designer <solar at openwall.com> in 1998-2014.
+ * No copyright is claimed, and the software is hereby placed in the public
+ * domain.  In case this attempt to disclaim copyright and place the software
+ * in the public domain is deemed null and void, then the software is
+ * Copyright (c) 1998-2014 Solar Designer and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
  *
- * There's absolutely no warranty.
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
  *
  * It is my intent that you should be able to use this on your system,
- * as a part of a software package, or anywhere else to improve security,
- * ensure compatibility, or for any other purpose. I would appreciate
+ * as part of a software package, or anywhere else to improve security,
+ * ensure compatibility, or for any other purpose.  I would appreciate
  * it if you give credit where it is due and keep your modifications in
  * the public domain as well, but I don't require that in order to let
  * you place this code and any modifications you make under a license
  * of your choice.
  *
- * This implementation is compatible with OpenBSD bcrypt.c (version 2a)
- * by Niels Provos <provos at citi.umich.edu>, and uses some of his
- * ideas.  The password hashing algorithm was designed by David Mazieres
- * <dm at lcs.mit.edu>.
+ * This implementation is fully compatible with OpenBSD's bcrypt.c for prefix
+ * "$2b$", originally by Niels Provos <provos at citi.umich.edu>, and it uses
+ * some of his ideas.  The password hashing algorithm was designed by David
+ * Mazieres <dm at lcs.mit.edu>.  For information on the level of
+ * compatibility for bcrypt hash prefixes other than "$2b$", please refer to
+ * the comments in BF_set_key() below and to the openwall crypt(3) man page.
  *
  * There's a paper on the algorithm that explains its design decisions:
  *
@@ -30,13 +43,25 @@
  * Some of the tricks in BF_ROUND might be inspired by Eric Young's
  * Blowfish library (I can't be sure if I would think of something if I
  * hadn't seen his code).
+ *
+ * Changes from upstream crypt_blowfish 1.3
+ *
+ *  - Indent with pgindent
+ *  - Set BF_ASM 0 instead of 1
+ *  - Don't set errno, report errors instead
+ *  - Add do/while to macros
+ *  - Remove semicolons from macros
+ *  - Use autoconf's WORDS_BIGENDIAN
+ *  - Add parentheses to macros when args are used in computations.
+ *  - Validate salt length
+ *  - Check for interrupts when generating hash
+ *  - Move blowfish gensalt to crypt-gensalt.c
  */
 
 #include "postgres.h"
 #include "miscadmin.h"
 
 #include "px-crypt.h"
-#include "px.h"
 
 #ifdef __i386__
 #define BF_ASM				0	/* 1 */
@@ -519,13 +544,9 @@ BF_swap(BF_word *x, int count)
 	L = tmp4 ^ data.ctx.P[BF_N + 1]
 
 #if BF_ASM
-
-extern void _BF_body_r(BF_ctx *ctx);
-
 #define BF_body() \
 	_BF_body_r(&data.ctx)
 #else
-
 #define BF_body() \
 do { \
 	L = R = 0; \
@@ -549,39 +570,128 @@ do { \
 
 static void
 BF_set_key(const char *key, BF_key expanded, BF_key initial,
-		   int sign_extension_bug)
+		   unsigned char flags)
 {
 	const char *ptr = key;
-	int			i,
+	unsigned int bug,
+				i,
 				j;
-	BF_word		tmp;
+	BF_word		safety,
+				sign,
+				diff,
+				tmp[2];
+
+/*
+ * There was a sign extension bug in older revisions of this function.  While
+ * we would have liked to simply fix the bug and move on, we have to provide
+ * a backwards compatibility feature (essentially the bug) for some systems and
+ * a safety measure for some others.  The latter is needed because for certain
+ * multiple inputs to the buggy algorithm there exist easily found inputs to
+ * the correct algorithm that produce the same hash.  Thus, we optionally
+ * deviate from the correct algorithm just enough to avoid such collisions.
+ * While the bug itself affected the majority of passwords containing
+ * characters with the 8th bit set (although only a percentage of those in a
+ * collision-producing way), the anti-collision safety measure affects
+ * only a subset of passwords containing the '\xff' character (not even all of
+ * those passwords, just some of them).  This character is not found in valid
+ * UTF-8 sequences and is rarely used in popular 8-bit character encodings.
+ * Thus, the safety measure is unlikely to cause much annoyance, and is a
+ * reasonable tradeoff to use when authenticating against existing hashes that
+ * are not reliably known to have been computed with the correct algorithm.
+ *
+ * We use an approach that tries to minimize side-channel leaks of password
+ * information - that is, we mostly use fixed-cost bitwise operations instead
+ * of branches or table lookups.  (One conditional branch based on password
+ * length remains.  It is not part of the bug aftermath, though, and is
+ * difficult and possibly unreasonable to avoid given the use of C strings by
+ * the caller, which results in similar timing leaks anyway.)
+ *
+ * For actual implementation, we set an array index in the variable "bug"
+ * (0 means no bug, 1 means sign extension bug emulation) and a flag in the
+ * variable "safety" (bit 16 is set when the safety measure is requested).
+ * Valid combinations of settings are:
+ *
+ * Prefix "$2a$": bug = 0, safety = 0x10000
+ * Prefix "$2b$": bug = 0, safety = 0
+ * Prefix "$2x$": bug = 1, safety = 0
+ * Prefix "$2y$": bug = 0, safety = 0
+ */
+	bug = (unsigned int) flags & 1;
+	safety = ((BF_word) flags & 2) << 15;
+
+	sign = diff = 0;
 
 	for (i = 0; i < BF_N + 2; i++)
 	{
-		tmp = 0;
+		tmp[0] = tmp[1] = 0;
 		for (j = 0; j < 4; j++)
 		{
-			tmp <<= 8;
-			if (sign_extension_bug)
-				tmp |= (BF_word_signed) (signed char) *ptr;
-			else
-				tmp |= (unsigned char) *ptr;
-
+			tmp[0] <<= 8;
+			tmp[0] |= (unsigned char) *ptr; /* correct */
+			tmp[1] <<= 8;
+			tmp[1] |= (BF_word_signed) (signed char) *ptr;	/* bug */
+/*
+ * Sign extension in the first char has no effect - nothing to overwrite yet,
+ * and those extra 24 bits will be fully shifted out of the 32-bit word.  For
+ * chars 2, 3, 4 in each four-char block, we set bit 7 of "sign" if sign
+ * extension in tmp[1] occurs.  Once this flag is set, it remains set.
+ */
+			if (j)
+				sign |= tmp[1] & 0x80;
 			if (!*ptr)
 				ptr = key;
 			else
 				ptr++;
 		}
+		diff |= tmp[0] ^ tmp[1];	/* Non-zero on any differences */
 
-		expanded[i] = tmp;
-		initial[i] = BF_init_state.P[i] ^ tmp;
+		expanded[i] = tmp[bug];
+		initial[i] = BF_init_state.P[i] ^ tmp[bug];
 	}
+
+/*
+ * At this point, "diff" is zero iff the correct and buggy algorithms produced
+ * exactly the same result.  If so and if "sign" is non-zero, which indicates
+ * that there was a non-benign sign extension, this means that we have a
+ * collision between the correctly computed hash for this password and a set of
+ * passwords that could be supplied to the buggy algorithm.  Our safety measure
+ * is meant to protect from such many-buggy to one-correct collisions, by
+ * deviating from the correct algorithm in such cases.  Let's check for this.
+ */
+	diff |= diff >> 16;			/* still zero iff exact match */
+	diff &= 0xffff;				/* ditto */
+	diff += 0xffff;				/* bit 16 set iff "diff" was non-zero (on
+								 * non-match) */
+	sign <<= 9;					/* move the non-benign sign extension flag to
+								 * bit 16 */
+	sign &= ~diff & safety;		/* action needed? */
+
+/*
+ * If we have determined that we need to deviate from the correct algorithm,
+ * flip bit 16 in initial expanded key.  (The choice of 16 is arbitrary, but
+ * let's stick to it now.  It came out of the approach we used above, and it's
+ * not any worse than any other choice we could make.)
+ *
+ * It is crucial that we don't do the same to the expanded key used in the main
+ * Eksblowfish loop.  By doing it to only one of these two, we deviate from a
+ * state that could be directly specified by a password to the buggy algorithm
+ * (and to the fully correct one as well, but that's a side-effect).
+ */
+	initial[0] ^= sign;
 }
 
-char *
-_crypt_blowfish_rn(const char *key, const char *setting,
-				   char *output, int size)
+static const unsigned char flags_by_subtype[26] =
+{2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0};
+
+static char *
+BF_crypt(const char *key, const char *setting,
+		 char *output, int size,
+		 BF_word min)
 {
+#if BF_ASM
+	extern void _BF_body_r(BF_ctx *ctx);
+#endif
 	struct
 	{
 		BF_ctx		ctx;
@@ -618,7 +728,8 @@ _crypt_blowfish_rn(const char *key, const char *setting,
 
 	if (setting[0] != '$' ||
 		setting[1] != '2' ||
-		(setting[2] != 'a' && setting[2] != 'x') ||
+		setting[2] < 'a' || setting[2] > 'z' ||
+		!flags_by_subtype[(unsigned int) (unsigned char) setting[2] - 'a'] ||
 		setting[3] != '$' ||
 		setting[4] < '0' || setting[4] > '3' ||
 		setting[5] < '0' || setting[5] > '9' ||
@@ -631,16 +742,16 @@ _crypt_blowfish_rn(const char *key, const char *setting,
 	}
 
 	count = (BF_word) 1 << ((setting[4] - '0') * 10 + (setting[5] - '0'));
-	if (count < 16 || BF_decode(data.binary.salt, &setting[7], 16))
+	if (count < min || BF_decode(data.binary.salt, &setting[7], 16))
 	{
-		px_memset(data.binary.salt, 0, sizeof(data.binary.salt));
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("invalid salt")));
 	}
 	BF_swap(data.binary.salt, 4);
 
-	BF_set_key(key, data.expanded_key, data.ctx.P, setting[2] == 'x');
+	BF_set_key(key, data.expanded_key, data.ctx.P,
+			   flags_by_subtype[(unsigned int) (unsigned char) setting[2] - 'a']);
 
 	memcpy(data.ctx.S, BF_init_state.S, sizeof(data.ctx.S));
 
@@ -675,51 +786,36 @@ _crypt_blowfish_rn(const char *key, const char *setting,
 	{
 		CHECK_FOR_INTERRUPTS();
 
-		data.ctx.P[0] ^= data.expanded_key[0];
-		data.ctx.P[1] ^= data.expanded_key[1];
-		data.ctx.P[2] ^= data.expanded_key[2];
-		data.ctx.P[3] ^= data.expanded_key[3];
-		data.ctx.P[4] ^= data.expanded_key[4];
-		data.ctx.P[5] ^= data.expanded_key[5];
-		data.ctx.P[6] ^= data.expanded_key[6];
-		data.ctx.P[7] ^= data.expanded_key[7];
-		data.ctx.P[8] ^= data.expanded_key[8];
-		data.ctx.P[9] ^= data.expanded_key[9];
-		data.ctx.P[10] ^= data.expanded_key[10];
-		data.ctx.P[11] ^= data.expanded_key[11];
-		data.ctx.P[12] ^= data.expanded_key[12];
-		data.ctx.P[13] ^= data.expanded_key[13];
-		data.ctx.P[14] ^= data.expanded_key[14];
-		data.ctx.P[15] ^= data.expanded_key[15];
-		data.ctx.P[16] ^= data.expanded_key[16];
-		data.ctx.P[17] ^= data.expanded_key[17];
-
-		BF_body();
-
-		tmp1 = data.binary.salt[0];
-		tmp2 = data.binary.salt[1];
-		tmp3 = data.binary.salt[2];
-		tmp4 = data.binary.salt[3];
-		data.ctx.P[0] ^= tmp1;
-		data.ctx.P[1] ^= tmp2;
-		data.ctx.P[2] ^= tmp3;
-		data.ctx.P[3] ^= tmp4;
-		data.ctx.P[4] ^= tmp1;
-		data.ctx.P[5] ^= tmp2;
-		data.ctx.P[6] ^= tmp3;
-		data.ctx.P[7] ^= tmp4;
-		data.ctx.P[8] ^= tmp1;
-		data.ctx.P[9] ^= tmp2;
-		data.ctx.P[10] ^= tmp3;
-		data.ctx.P[11] ^= tmp4;
-		data.ctx.P[12] ^= tmp1;
-		data.ctx.P[13] ^= tmp2;
-		data.ctx.P[14] ^= tmp3;
-		data.ctx.P[15] ^= tmp4;
-		data.ctx.P[16] ^= tmp1;
-		data.ctx.P[17] ^= tmp2;
-
-		BF_body();
+		int			done;
+
+		for (i = 0; i < BF_N + 2; i += 2)
+		{
+			data.ctx.P[i] ^= data.expanded_key[i];
+			data.ctx.P[i + 1] ^= data.expanded_key[i + 1];
+		}
+
+		done = 0;
+		do
+		{
+			BF_body();
+			if (done)
+				break;
+			done = 1;
+
+			tmp1 = data.binary.salt[0];
+			tmp2 = data.binary.salt[1];
+			tmp3 = data.binary.salt[2];
+			tmp4 = data.binary.salt[3];
+			for (i = 0; i < BF_N; i += 4)
+			{
+				data.ctx.P[i] ^= tmp1;
+				data.ctx.P[i + 1] ^= tmp2;
+				data.ctx.P[i + 2] ^= tmp3;
+				data.ctx.P[i + 3] ^= tmp4;
+			}
+			data.ctx.P[16] ^= tmp1;
+			data.ctx.P[17] ^= tmp2;
+		} while (1);
 	} while (--count);
 
 	for (i = 0; i < 6; i += 2)
@@ -747,10 +843,113 @@ _crypt_blowfish_rn(const char *key, const char *setting,
 	BF_encode(&output[7 + 22], data.binary.output, 23);
 	output[7 + 22 + 31] = '\0';
 
-/* Overwrite the most obvious sensitive data we have on the stack. Note
- * that this does not guarantee there's no sensitive data left on the
- * stack and/or in registers; I'm not aware of portable code that does. */
-	px_memset(&data, 0, sizeof(data));
-
 	return output;
 }
+
+int
+_crypt_output_magic(const char *setting, char *output, int size)
+{
+	if (size < 3)
+		return -1;
+
+	output[0] = '*';
+	output[1] = '0';
+	output[2] = '\0';
+
+	if (setting[0] == '*' && setting[1] == '0')
+		output[1] = '1';
+
+	return 0;
+}
+
+/*
+ * Please preserve the runtime self-test.  It serves two purposes at once:
+ *
+ * 1. We really can't afford the risk of producing incompatible hashes e.g.
+ * when there's something like gcc bug 26587 again, whereas an application or
+ * library integrating this code might not also integrate our external tests or
+ * it might not run them after every build.  Even if it does, the miscompile
+ * might only occur on the production build, but not on a testing build (such
+ * as because of different optimization settings).  It is painful to recover
+ * from incorrectly-computed hashes - merely fixing whatever broke is not
+ * enough.  Thus, a proactive measure like this self-test is needed.
+ *
+ * 2. We don't want to leave sensitive data from our actual password hash
+ * computation on the stack or in registers.  Previous revisions of the code
+ * would do explicit cleanups, but simply running the self-test after hash
+ * computation is more reliable.
+ *
+ * The performance cost of this quick self-test is around 0.6% at the "$2a$08"
+ * setting.
+ */
+char *
+_crypt_blowfish_rn(const char *key, const char *setting,
+				   char *output, int size)
+{
+	const char *test_key = "8b \xd0\xc1\xd2\xcf\xcc\xd8";
+	const char *test_setting = "$2a$00$abcdefghijklmnopqrstuu";
+	static const char *const test_hashes[2] =
+	{"i1D709vfamulimlGcq0qq3UvuUasvEa\0\x55",	/* 'a', 'b', 'y' */
+	"VUrPmXD6q/nVSSp7pNDhCR9071IfIRe\0\x55"};	/* 'x' */
+	const char *test_hash = test_hashes[0];
+	char	   *retval;
+	const char *p;
+	int			save_errno,
+				ok;
+	struct
+	{
+		char		s[7 + 22 + 1];
+		char		o[7 + 22 + 31 + 1 + 1 + 1];
+	}			buf;
+
+/* Hash the supplied password */
+	_crypt_output_magic(setting, output, size);
+	retval = BF_crypt(key, setting, output, size, 16);
+	save_errno = errno;
+
+/*
+ * Do a quick self-test.  It is important that we make both calls to BF_crypt()
+ * from the same scope such that they likely use the same stack locations,
+ * which makes the second call overwrite the first call's sensitive data on the
+ * stack and makes it more likely that any alignment related issues would be
+ * detected by the self-test.
+ */
+	memcpy(buf.s, test_setting, sizeof(buf.s));
+	if (retval)
+	{
+		unsigned int flags = flags_by_subtype[
+											  (unsigned int) (unsigned char) setting[2] - 'a'];
+
+		test_hash = test_hashes[flags & 1];
+		buf.s[2] = setting[2];
+	}
+	memset(buf.o, 0x55, sizeof(buf.o));
+	buf.o[sizeof(buf.o) - 1] = 0;
+	p = BF_crypt(test_key, buf.s, buf.o, sizeof(buf.o) - (1 + 1), 1);
+
+	ok = (p == buf.o &&
+		  !memcmp(p, buf.s, 7 + 22) &&
+		  !memcmp(p + (7 + 22), test_hash, 31 + 1 + 1 + 1));
+
+	{
+		const char *k = "\xff\xa3" "34" "\xff\xff\xff\xa3" "345";
+		BF_key		ae,
+					ai,
+					ye,
+					yi;
+
+		BF_set_key(k, ae, ai, 2);	/* $2a$ */
+		BF_set_key(k, ye, yi, 4);	/* $2y$ */
+		ai[0] ^= 0x10000;		/* undo the safety (for comparison) */
+		ok = ok && ai[0] == 0xdb9c59bc && ye[17] == 0x33343500 &&
+			!memcmp(ae, ye, sizeof(ae)) &&
+			!memcmp(ai, yi, sizeof(ai));
+	}
+
+	if (ok)
+		return retval;
+
+/* Should not happen */
+	_crypt_output_magic(setting, output, size);
+	return NULL;
+}
diff --git a/contrib/pgcrypto/crypt-gensalt.c b/contrib/pgcrypto/crypt-gensalt.c
index 740f3612..6f2149d2 100644
--- a/contrib/pgcrypto/crypt-gensalt.c
+++ b/contrib/pgcrypto/crypt-gensalt.c
@@ -1,15 +1,31 @@
 /*
- * Written by Solar Designer and placed in the public domain.
- * See crypt_blowfish.c for more information.
- *
  * contrib/pgcrypto/crypt-gensalt.c
  *
+ * Written by Solar Designer <solar at openwall.com> in 2000-2011.
+ * No copyright is claimed, and the software is hereby placed in the public
+ * domain.  In case this attempt to disclaim copyright and place the software
+ * in the public domain is deemed null and void, then the software is
+ * Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * See crypt_blowfish.c for more information.
+ *
  * This file contains salt generation functions for the traditional and
  * other common crypt(3) algorithms, except for bcrypt which is defined
  * entirely in crypt_blowfish.c.
  *
- * Put bcrypt generator also here as crypt-blowfish.c
- * may not be compiled always.        -- marko
+ * Changes from upstream crypt_blowfish 1.3
+ *
+ *  - Format with pgindent
+ *  - Don't set errno
+ *  - Make _crypt_itoa64 static
+ *  - Include bcrypt generator also here as crypt-blowfish.c may not always be
+ *    compiled
  */
 
 #include "postgres.h"
@@ -22,9 +38,11 @@ static unsigned char _crypt_itoa64[64 + 1] =
 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 
 char *
-_crypt_gensalt_traditional_rn(unsigned long count,
+_crypt_gensalt_traditional_rn(const char *prefix, unsigned long count,
 							  const char *input, int size, char *output, int output_size)
 {
+	(void) prefix;
+
 	if (size < 2 || output_size < 2 + 1 || (count && count != 25))
 	{
 		if (output_size > 0)
@@ -40,11 +58,13 @@ _crypt_gensalt_traditional_rn(unsigned long count,
 }
 
 char *
-_crypt_gensalt_extended_rn(unsigned long count,
+_crypt_gensalt_extended_rn(const char *prefix, unsigned long count,
 						   const char *input, int size, char *output, int output_size)
 {
 	unsigned long value;
 
+	(void) prefix;
+
 /* Even iteration counts make it easier to detect weak DES keys from a look
  * at the hash, so they should be avoided */
 	if (size < 3 || output_size < 1 + 4 + 4 + 1 ||
@@ -76,11 +96,13 @@ _crypt_gensalt_extended_rn(unsigned long count,
 }
 
 char *
-_crypt_gensalt_md5_rn(unsigned long count,
+_crypt_gensalt_md5_rn(const char *prefix, unsigned long count,
 					  const char *input, int size, char *output, int output_size)
 {
 	unsigned long value;
 
+	(void) prefix;
+
 	if (size < 3 || output_size < 3 + 4 + 1 || (count && count != 1000))
 	{
 		if (output_size > 0)
@@ -158,11 +180,13 @@ BF_encode(char *dst, const BF_word *src, int size)
 }
 
 char *
-_crypt_gensalt_blowfish_rn(unsigned long count,
+_crypt_gensalt_blowfish_rn(const char *prefix, unsigned long count,
 						   const char *input, int size, char *output, int output_size)
 {
 	if (size < 16 || output_size < 7 + 22 + 1 ||
-		(count && (count < 4 || count > 31)))
+		(count && (count < 4 || count > 31)) ||
+		prefix[0] != '$' || prefix[1] != '2' ||
+		(prefix[2] != 'a' && prefix[2] != 'b' && prefix[2] != 'y'))
 	{
 		if (output_size > 0)
 			output[0] = '\0';
@@ -174,7 +198,7 @@ _crypt_gensalt_blowfish_rn(unsigned long count,
 
 	output[0] = '$';
 	output[1] = '2';
-	output[2] = 'a';
+	output[2] = prefix[2];
 	output[3] = '$';
 	output[4] = '0' + count / 10;
 	output[5] = '0' + count % 10;
diff --git a/contrib/pgcrypto/expected/crypt-blowfish.out b/contrib/pgcrypto/expected/crypt-blowfish.out
index d79b0c04..3d6b215c 100644
--- a/contrib/pgcrypto/expected/crypt-blowfish.out
+++ b/contrib/pgcrypto/expected/crypt-blowfish.out
@@ -13,6 +13,13 @@ SELECT crypt('foox', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O');
  $2a$06$RQiOJ.3ELirrXwxIZY8q0OR3CVJrAfda1z26CCHPnB6mmVZD8p0/C
 (1 row)
 
+-- support $2b$ hashes
+SELECT crypt('password', '$2b$06$cqhNY4qXkVeKliLWOxCFieDY6/J4SmCEojsXfHs7iF4X1r3tuNepW');
+                            crypt                             
+--------------------------------------------------------------
+ $2b$06$cqhNY4qXkVeKliLWOxCFieDY6/J4SmCEojsXfHs7iF4X1r3tuNepW
+(1 row)
+
 -- error, salt too short:
 SELECT crypt('foox', '$2a$');
 ERROR:  invalid salt
diff --git a/contrib/pgcrypto/px-crypt.c b/contrib/pgcrypto/px-crypt.c
index 0913ff2c..1e7a712a 100644
--- a/contrib/pgcrypto/px-crypt.c
+++ b/contrib/pgcrypto/px-crypt.c
@@ -78,7 +78,9 @@ struct px_crypt_algo
 static const struct px_crypt_algo
 			px_crypt_list[] = {
 	{"$2a$", 4, run_crypt_bf},
+	{"$2b$", 4, run_crypt_bf},
 	{"$2x$", 4, run_crypt_bf},
+	{"$2y$", 4, run_crypt_bf},
 	{"$2$", 3, NULL},			/* N/A */
 	{"$1$", 3, run_crypt_md5},
 	{"_", 1, run_crypt_des},
@@ -112,8 +114,9 @@ px_crypt(const char *psw, const char *salt, char *buf, unsigned len)
 struct generator
 {
 	char	   *name;
-	char	   *(*gen) (unsigned long count, const char *input, int size,
-						char *output, int output_size);
+	char	   *(*gen) (const char *prefix, unsigned long count, const char *input,
+						int size, char *output, int output_size);
+	char		*prefix;
 	int			input_len;
 	int			def_rounds;
 	int			min_rounds;
@@ -121,10 +124,10 @@ struct generator
 };
 
 static struct generator gen_list[] = {
-	{"des", _crypt_gensalt_traditional_rn, 2, 0, 0, 0},
-	{"md5", _crypt_gensalt_md5_rn, 6, 0, 0, 0},
-	{"xdes", _crypt_gensalt_extended_rn, 3, PX_XDES_ROUNDS, 1, 0xFFFFFF},
-	{"bf", _crypt_gensalt_blowfish_rn, 16, PX_BF_ROUNDS, 4, 31},
+	{"des", _crypt_gensalt_traditional_rn, "", 2, 0, 0, 0},
+	{"md5", _crypt_gensalt_md5_rn, "$1$", 6, 0, 0, 0},
+	{"xdes", _crypt_gensalt_extended_rn, "_", 3, PX_XDES_ROUNDS, 1, 0xFFFFFF},
+	{"bf", _crypt_gensalt_blowfish_rn, "$2a$", 16, PX_BF_ROUNDS, 4, 31},
 	{NULL, NULL, 0, 0, 0, 0}
 };
 
@@ -154,7 +157,7 @@ px_gen_salt(const char *salt_type, char *buf, int rounds)
 	if (!pg_strong_random(rbuf, g->input_len))
 		return PXE_NO_RANDOM;
 
-	p = g->gen(rounds, rbuf, g->input_len, buf, PX_MAX_SALT_LEN);
+	p = g->gen(g->prefix, rounds, rbuf, g->input_len, buf, PX_MAX_SALT_LEN);
 	px_memset(rbuf, 0, sizeof(rbuf));
 
 	if (p == NULL)
diff --git a/contrib/pgcrypto/px-crypt.h b/contrib/pgcrypto/px-crypt.h
index 08001a81..1a2dce77 100644
--- a/contrib/pgcrypto/px-crypt.h
+++ b/contrib/pgcrypto/px-crypt.h
@@ -56,19 +56,23 @@ int			px_gen_salt(const char *salt_type, char *dst, int rounds);
  */
 
 /* crypt-gensalt.c */
-char	   *_crypt_gensalt_traditional_rn(unsigned long count,
+char	   *_crypt_gensalt_traditional_rn(const char *prefix,
+										  unsigned long count,
 										  const char *input, int size, char *output, int output_size);
-char	   *_crypt_gensalt_extended_rn(unsigned long count,
+char	   *_crypt_gensalt_extended_rn(const char *prefix,
+									   unsigned long count,
 									   const char *input, int size, char *output, int output_size);
-char	   *_crypt_gensalt_md5_rn(unsigned long count,
+char	   *_crypt_gensalt_md5_rn(const char *prefix, unsigned long count,
 								  const char *input, int size, char *output, int output_size);
-char	   *_crypt_gensalt_blowfish_rn(unsigned long count,
+char	   *_crypt_gensalt_blowfish_rn(const char *prefix,
+									   unsigned long count,
 									   const char *input, int size, char *output, int output_size);
 
 /* disable 'extended DES crypt' */
 /* #define DISABLE_XDES */
 
 /* crypt-blowfish.c */
+int			_crypt_output_magic(const char *setting, char *output, int size);
 char	   *_crypt_blowfish_rn(const char *key, const char *setting,
 							   char *output, int size);
 
diff --git a/contrib/pgcrypto/sql/crypt-blowfish.sql b/contrib/pgcrypto/sql/crypt-blowfish.sql
index 3b5a681c..0e96ee0c 100644
--- a/contrib/pgcrypto/sql/crypt-blowfish.sql
+++ b/contrib/pgcrypto/sql/crypt-blowfish.sql
@@ -6,6 +6,9 @@ SELECT crypt('', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O');
 
 SELECT crypt('foox', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O');
 
+-- support $2b$ hashes
+SELECT crypt('password', '$2b$06$cqhNY4qXkVeKliLWOxCFieDY6/J4SmCEojsXfHs7iF4X1r3tuNepW');
+
 -- error, salt too short:
 SELECT crypt('foox', '$2a$');
 
diff --git a/doc/src/sgml/pgcrypto.sgml b/doc/src/sgml/pgcrypto.sgml
index bbaa2691..55842132 100644
--- a/doc/src/sgml/pgcrypto.sgml
+++ b/doc/src/sgml/pgcrypto.sgml
@@ -159,7 +159,7 @@ hmac(data bytea, key bytea, type text) returns bytea
       <entry>yes</entry>
       <entry>128</entry>
       <entry>60</entry>
-      <entry>Blowfish-based, variant 2a</entry>
+      <entry>Blowfish-based, variants 2a, 2b, 2x, 2y</entry>
      </row>
      <row>
       <entry><literal>md5</literal></entry>
-- 
2.32.0
#6Andreas Karlsson
andreas@proxel.se
In reply to: Daniel Fone (#5)
Re: pgcrypto support for bcrypt $2b$ hashes

On 9/28/21 11:58 PM, Daniel Fone wrote:

On 29/09/2021, at 2:33 AM, Daniel Gustafsson <daniel@yesql.se> wrote:
I don't see why not, the best first patches are those scratching an itch. If
you feel up for it then give it a go, I - and the rest of pgsql-hackers - can
help if you need to bounce ideas.

I’m glad you said that. I couldn’t resist trying and have attached a patch. By referencing the respective git logs, I didn’t have too much difficulty identifying the material changes in each codebase. I’ve documented all the postgres-specific changes to upstream in the header comment for each file.

I took a quick look and on a cursory glance it looks good but I got
these compilation warnings.

crypt-blowfish.c: In function ‘BF_crypt’:
crypt-blowfish.c:789:3: warning: ISO C90 forbids mixed declarations and
code [-Wdeclaration-after-statement]
789 | int done;
| ^~~
crypt-blowfish.c: In function ‘_crypt_blowfish_rn’:
crypt-blowfish.c:897:8: warning: variable ‘save_errno’ set but not used
[-Wunused-but-set-variable]
897 | int save_errno,
| ^~~~~~~~~~

Andreas

#7Daniel Fone
daniel@fone.net.nz
In reply to: Andreas Karlsson (#6)
1 attachment(s)
Re: pgcrypto support for bcrypt $2b$ hashes

Hi Andreas,

On 1/10/2021, at 12:17 AM, Andreas Karlsson <andreas@proxel.se> wrote:

On 9/28/21 11:58 PM, Daniel Fone wrote:

On 29/09/2021, at 2:33 AM, Daniel Gustafsson <daniel@yesql.se> wrote:
I don't see why not, the best first patches are those scratching an itch. If
you feel up for it then give it a go, I - and the rest of pgsql-hackers - can
help if you need to bounce ideas.

I’m glad you said that. I couldn’t resist trying and have attached a patch. By referencing the respective git logs, I didn’t have too much difficulty identifying the material changes in each codebase. I’ve documented all the postgres-specific changes to upstream in the header comment for each file.

I took a quick look and on a cursory glance it looks good but I got these compilation warnings.

crypt-blowfish.c: In function ‘BF_crypt’:
crypt-blowfish.c:789:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
789 | int done;
| ^~~
crypt-blowfish.c: In function ‘_crypt_blowfish_rn’:
crypt-blowfish.c:897:8: warning: variable ‘save_errno’ set but not used [-Wunused-but-set-variable]
897 | int save_errno,
| ^~~~~~~~~~

I don’t get these compiler warnings and I can’t find any settings to use that might generate them. I’m compiling on macOS 11.6 configured with `--enable-cassert --enable-depend --enable-debug CFLAGS=-O0`

I’ve optimistically updated the patch to hopefully address them, but I’d like to know what I need to do to get those warnings.

Thanks,

Daniel

Attachments:

0001-Merge-upstream-crypt_blowfish-1.3.patchapplication/octet-stream; name=0001-Merge-upstream-crypt_blowfish-1.3.patch; x-unix-mode=0644Download
From 8105b1409307f7b254daab8d91f7f00e96090633 Mon Sep 17 00:00:00 2001
From: Daniel Fone <daniel@fone.net.nz>
Date: Wed, 29 Sep 2021 10:31:26 +1300
Subject: [PATCH] Merge upstream crypt_blowfish 1.3

This adds support for 2y and 2b hash variants.
---
 contrib/pgcrypto/crypt-blowfish.c            | 363 ++++++++++++++-----
 contrib/pgcrypto/crypt-gensalt.c             |  46 ++-
 contrib/pgcrypto/expected/crypt-blowfish.out |   7 +
 contrib/pgcrypto/px-crypt.c                  |  17 +-
 contrib/pgcrypto/px-crypt.h                  |  12 +-
 contrib/pgcrypto/sql/crypt-blowfish.sql      |   3 +
 doc/src/sgml/pgcrypto.sgml                   |   2 +-
 7 files changed, 344 insertions(+), 106 deletions(-)

diff --git a/contrib/pgcrypto/crypt-blowfish.c b/contrib/pgcrypto/crypt-blowfish.c
index a663852c..2f89b79a 100644
--- a/contrib/pgcrypto/crypt-blowfish.c
+++ b/contrib/pgcrypto/crypt-blowfish.c
@@ -1,27 +1,40 @@
 /*
  * contrib/pgcrypto/crypt-blowfish.c
  *
+ * The crypt_blowfish homepage is:
+ *
+ *	http://www.openwall.com/crypt/
+ *
  * This code comes from John the Ripper password cracker, with reentrant
  * and crypt(3) interfaces added, but optimizations specific to password
  * cracking removed.
  *
- * Written by Solar Designer <solar at openwall.com> in 1998-2002 and
- * placed in the public domain.
+ * Written by Solar Designer <solar at openwall.com> in 1998-2014.
+ * No copyright is claimed, and the software is hereby placed in the public
+ * domain.  In case this attempt to disclaim copyright and place the software
+ * in the public domain is deemed null and void, then the software is
+ * Copyright (c) 1998-2014 Solar Designer and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
  *
- * There's absolutely no warranty.
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
  *
  * It is my intent that you should be able to use this on your system,
- * as a part of a software package, or anywhere else to improve security,
- * ensure compatibility, or for any other purpose. I would appreciate
+ * as part of a software package, or anywhere else to improve security,
+ * ensure compatibility, or for any other purpose.  I would appreciate
  * it if you give credit where it is due and keep your modifications in
  * the public domain as well, but I don't require that in order to let
  * you place this code and any modifications you make under a license
  * of your choice.
  *
- * This implementation is compatible with OpenBSD bcrypt.c (version 2a)
- * by Niels Provos <provos at citi.umich.edu>, and uses some of his
- * ideas.  The password hashing algorithm was designed by David Mazieres
- * <dm at lcs.mit.edu>.
+ * This implementation is fully compatible with OpenBSD's bcrypt.c for prefix
+ * "$2b$", originally by Niels Provos <provos at citi.umich.edu>, and it uses
+ * some of his ideas.  The password hashing algorithm was designed by David
+ * Mazieres <dm at lcs.mit.edu>.  For information on the level of
+ * compatibility for bcrypt hash prefixes other than "$2b$", please refer to
+ * the comments in BF_set_key() below and to the openwall crypt(3) man page.
  *
  * There's a paper on the algorithm that explains its design decisions:
  *
@@ -30,13 +43,25 @@
  * Some of the tricks in BF_ROUND might be inspired by Eric Young's
  * Blowfish library (I can't be sure if I would think of something if I
  * hadn't seen his code).
+ *
+ * Changes from upstream crypt_blowfish 1.3
+ *
+ *  - Indent with pgindent
+ *  - Set BF_ASM 0 instead of 1
+ *  - Don't set errno, report errors instead
+ *  - Add do/while to macros
+ *  - Remove semicolons from macros
+ *  - Use autoconf's WORDS_BIGENDIAN
+ *  - Add parentheses to macros when args are used in computations.
+ *  - Validate salt length
+ *  - Check for interrupts when generating hash
+ *  - Move blowfish gensalt to crypt-gensalt.c
  */
 
 #include "postgres.h"
 #include "miscadmin.h"
 
 #include "px-crypt.h"
-#include "px.h"
 
 #ifdef __i386__
 #define BF_ASM				0	/* 1 */
@@ -519,13 +544,9 @@ BF_swap(BF_word *x, int count)
 	L = tmp4 ^ data.ctx.P[BF_N + 1]
 
 #if BF_ASM
-
-extern void _BF_body_r(BF_ctx *ctx);
-
 #define BF_body() \
 	_BF_body_r(&data.ctx)
 #else
-
 #define BF_body() \
 do { \
 	L = R = 0; \
@@ -549,39 +570,128 @@ do { \
 
 static void
 BF_set_key(const char *key, BF_key expanded, BF_key initial,
-		   int sign_extension_bug)
+		   unsigned char flags)
 {
 	const char *ptr = key;
-	int			i,
+	unsigned int bug,
+				i,
 				j;
-	BF_word		tmp;
+	BF_word		safety,
+				sign,
+				diff,
+				tmp[2];
+
+/*
+ * There was a sign extension bug in older revisions of this function.  While
+ * we would have liked to simply fix the bug and move on, we have to provide
+ * a backwards compatibility feature (essentially the bug) for some systems and
+ * a safety measure for some others.  The latter is needed because for certain
+ * multiple inputs to the buggy algorithm there exist easily found inputs to
+ * the correct algorithm that produce the same hash.  Thus, we optionally
+ * deviate from the correct algorithm just enough to avoid such collisions.
+ * While the bug itself affected the majority of passwords containing
+ * characters with the 8th bit set (although only a percentage of those in a
+ * collision-producing way), the anti-collision safety measure affects
+ * only a subset of passwords containing the '\xff' character (not even all of
+ * those passwords, just some of them).  This character is not found in valid
+ * UTF-8 sequences and is rarely used in popular 8-bit character encodings.
+ * Thus, the safety measure is unlikely to cause much annoyance, and is a
+ * reasonable tradeoff to use when authenticating against existing hashes that
+ * are not reliably known to have been computed with the correct algorithm.
+ *
+ * We use an approach that tries to minimize side-channel leaks of password
+ * information - that is, we mostly use fixed-cost bitwise operations instead
+ * of branches or table lookups.  (One conditional branch based on password
+ * length remains.  It is not part of the bug aftermath, though, and is
+ * difficult and possibly unreasonable to avoid given the use of C strings by
+ * the caller, which results in similar timing leaks anyway.)
+ *
+ * For actual implementation, we set an array index in the variable "bug"
+ * (0 means no bug, 1 means sign extension bug emulation) and a flag in the
+ * variable "safety" (bit 16 is set when the safety measure is requested).
+ * Valid combinations of settings are:
+ *
+ * Prefix "$2a$": bug = 0, safety = 0x10000
+ * Prefix "$2b$": bug = 0, safety = 0
+ * Prefix "$2x$": bug = 1, safety = 0
+ * Prefix "$2y$": bug = 0, safety = 0
+ */
+	bug = (unsigned int) flags & 1;
+	safety = ((BF_word) flags & 2) << 15;
+
+	sign = diff = 0;
 
 	for (i = 0; i < BF_N + 2; i++)
 	{
-		tmp = 0;
+		tmp[0] = tmp[1] = 0;
 		for (j = 0; j < 4; j++)
 		{
-			tmp <<= 8;
-			if (sign_extension_bug)
-				tmp |= (BF_word_signed) (signed char) *ptr;
-			else
-				tmp |= (unsigned char) *ptr;
-
+			tmp[0] <<= 8;
+			tmp[0] |= (unsigned char) *ptr; /* correct */
+			tmp[1] <<= 8;
+			tmp[1] |= (BF_word_signed) (signed char) *ptr;	/* bug */
+/*
+ * Sign extension in the first char has no effect - nothing to overwrite yet,
+ * and those extra 24 bits will be fully shifted out of the 32-bit word.  For
+ * chars 2, 3, 4 in each four-char block, we set bit 7 of "sign" if sign
+ * extension in tmp[1] occurs.  Once this flag is set, it remains set.
+ */
+			if (j)
+				sign |= tmp[1] & 0x80;
 			if (!*ptr)
 				ptr = key;
 			else
 				ptr++;
 		}
+		diff |= tmp[0] ^ tmp[1];	/* Non-zero on any differences */
 
-		expanded[i] = tmp;
-		initial[i] = BF_init_state.P[i] ^ tmp;
+		expanded[i] = tmp[bug];
+		initial[i] = BF_init_state.P[i] ^ tmp[bug];
 	}
+
+/*
+ * At this point, "diff" is zero iff the correct and buggy algorithms produced
+ * exactly the same result.  If so and if "sign" is non-zero, which indicates
+ * that there was a non-benign sign extension, this means that we have a
+ * collision between the correctly computed hash for this password and a set of
+ * passwords that could be supplied to the buggy algorithm.  Our safety measure
+ * is meant to protect from such many-buggy to one-correct collisions, by
+ * deviating from the correct algorithm in such cases.  Let's check for this.
+ */
+	diff |= diff >> 16;			/* still zero iff exact match */
+	diff &= 0xffff;				/* ditto */
+	diff += 0xffff;				/* bit 16 set iff "diff" was non-zero (on
+								 * non-match) */
+	sign <<= 9;					/* move the non-benign sign extension flag to
+								 * bit 16 */
+	sign &= ~diff & safety;		/* action needed? */
+
+/*
+ * If we have determined that we need to deviate from the correct algorithm,
+ * flip bit 16 in initial expanded key.  (The choice of 16 is arbitrary, but
+ * let's stick to it now.  It came out of the approach we used above, and it's
+ * not any worse than any other choice we could make.)
+ *
+ * It is crucial that we don't do the same to the expanded key used in the main
+ * Eksblowfish loop.  By doing it to only one of these two, we deviate from a
+ * state that could be directly specified by a password to the buggy algorithm
+ * (and to the fully correct one as well, but that's a side-effect).
+ */
+	initial[0] ^= sign;
 }
 
-char *
-_crypt_blowfish_rn(const char *key, const char *setting,
-				   char *output, int size)
+static const unsigned char flags_by_subtype[26] =
+{2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0};
+
+static char *
+BF_crypt(const char *key, const char *setting,
+		 char *output, int size,
+		 BF_word min)
 {
+#if BF_ASM
+	extern void _BF_body_r(BF_ctx *ctx);
+#endif
 	struct
 	{
 		BF_ctx		ctx;
@@ -618,7 +728,8 @@ _crypt_blowfish_rn(const char *key, const char *setting,
 
 	if (setting[0] != '$' ||
 		setting[1] != '2' ||
-		(setting[2] != 'a' && setting[2] != 'x') ||
+		setting[2] < 'a' || setting[2] > 'z' ||
+		!flags_by_subtype[(unsigned int) (unsigned char) setting[2] - 'a'] ||
 		setting[3] != '$' ||
 		setting[4] < '0' || setting[4] > '3' ||
 		setting[5] < '0' || setting[5] > '9' ||
@@ -631,16 +742,16 @@ _crypt_blowfish_rn(const char *key, const char *setting,
 	}
 
 	count = (BF_word) 1 << ((setting[4] - '0') * 10 + (setting[5] - '0'));
-	if (count < 16 || BF_decode(data.binary.salt, &setting[7], 16))
+	if (count < min || BF_decode(data.binary.salt, &setting[7], 16))
 	{
-		px_memset(data.binary.salt, 0, sizeof(data.binary.salt));
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 				 errmsg("invalid salt")));
 	}
 	BF_swap(data.binary.salt, 4);
 
-	BF_set_key(key, data.expanded_key, data.ctx.P, setting[2] == 'x');
+	BF_set_key(key, data.expanded_key, data.ctx.P,
+			   flags_by_subtype[(unsigned int) (unsigned char) setting[2] - 'a']);
 
 	memcpy(data.ctx.S, BF_init_state.S, sizeof(data.ctx.S));
 
@@ -673,53 +784,38 @@ _crypt_blowfish_rn(const char *key, const char *setting,
 
 	do
 	{
+		int			done;
+
 		CHECK_FOR_INTERRUPTS();
 
-		data.ctx.P[0] ^= data.expanded_key[0];
-		data.ctx.P[1] ^= data.expanded_key[1];
-		data.ctx.P[2] ^= data.expanded_key[2];
-		data.ctx.P[3] ^= data.expanded_key[3];
-		data.ctx.P[4] ^= data.expanded_key[4];
-		data.ctx.P[5] ^= data.expanded_key[5];
-		data.ctx.P[6] ^= data.expanded_key[6];
-		data.ctx.P[7] ^= data.expanded_key[7];
-		data.ctx.P[8] ^= data.expanded_key[8];
-		data.ctx.P[9] ^= data.expanded_key[9];
-		data.ctx.P[10] ^= data.expanded_key[10];
-		data.ctx.P[11] ^= data.expanded_key[11];
-		data.ctx.P[12] ^= data.expanded_key[12];
-		data.ctx.P[13] ^= data.expanded_key[13];
-		data.ctx.P[14] ^= data.expanded_key[14];
-		data.ctx.P[15] ^= data.expanded_key[15];
-		data.ctx.P[16] ^= data.expanded_key[16];
-		data.ctx.P[17] ^= data.expanded_key[17];
-
-		BF_body();
-
-		tmp1 = data.binary.salt[0];
-		tmp2 = data.binary.salt[1];
-		tmp3 = data.binary.salt[2];
-		tmp4 = data.binary.salt[3];
-		data.ctx.P[0] ^= tmp1;
-		data.ctx.P[1] ^= tmp2;
-		data.ctx.P[2] ^= tmp3;
-		data.ctx.P[3] ^= tmp4;
-		data.ctx.P[4] ^= tmp1;
-		data.ctx.P[5] ^= tmp2;
-		data.ctx.P[6] ^= tmp3;
-		data.ctx.P[7] ^= tmp4;
-		data.ctx.P[8] ^= tmp1;
-		data.ctx.P[9] ^= tmp2;
-		data.ctx.P[10] ^= tmp3;
-		data.ctx.P[11] ^= tmp4;
-		data.ctx.P[12] ^= tmp1;
-		data.ctx.P[13] ^= tmp2;
-		data.ctx.P[14] ^= tmp3;
-		data.ctx.P[15] ^= tmp4;
-		data.ctx.P[16] ^= tmp1;
-		data.ctx.P[17] ^= tmp2;
-
-		BF_body();
+		for (i = 0; i < BF_N + 2; i += 2)
+		{
+			data.ctx.P[i] ^= data.expanded_key[i];
+			data.ctx.P[i + 1] ^= data.expanded_key[i + 1];
+		}
+
+		done = 0;
+		do
+		{
+			BF_body();
+			if (done)
+				break;
+			done = 1;
+
+			tmp1 = data.binary.salt[0];
+			tmp2 = data.binary.salt[1];
+			tmp3 = data.binary.salt[2];
+			tmp4 = data.binary.salt[3];
+			for (i = 0; i < BF_N; i += 4)
+			{
+				data.ctx.P[i] ^= tmp1;
+				data.ctx.P[i + 1] ^= tmp2;
+				data.ctx.P[i + 2] ^= tmp3;
+				data.ctx.P[i + 3] ^= tmp4;
+			}
+			data.ctx.P[16] ^= tmp1;
+			data.ctx.P[17] ^= tmp2;
+		} while (1);
 	} while (--count);
 
 	for (i = 0; i < 6; i += 2)
@@ -747,10 +843,111 @@ _crypt_blowfish_rn(const char *key, const char *setting,
 	BF_encode(&output[7 + 22], data.binary.output, 23);
 	output[7 + 22 + 31] = '\0';
 
-/* Overwrite the most obvious sensitive data we have on the stack. Note
- * that this does not guarantee there's no sensitive data left on the
- * stack and/or in registers; I'm not aware of portable code that does. */
-	px_memset(&data, 0, sizeof(data));
-
 	return output;
 }
+
+int
+_crypt_output_magic(const char *setting, char *output, int size)
+{
+	if (size < 3)
+		return -1;
+
+	output[0] = '*';
+	output[1] = '0';
+	output[2] = '\0';
+
+	if (setting[0] == '*' && setting[1] == '0')
+		output[1] = '1';
+
+	return 0;
+}
+
+/*
+ * Please preserve the runtime self-test.  It serves two purposes at once:
+ *
+ * 1. We really can't afford the risk of producing incompatible hashes e.g.
+ * when there's something like gcc bug 26587 again, whereas an application or
+ * library integrating this code might not also integrate our external tests or
+ * it might not run them after every build.  Even if it does, the miscompile
+ * might only occur on the production build, but not on a testing build (such
+ * as because of different optimization settings).  It is painful to recover
+ * from incorrectly-computed hashes - merely fixing whatever broke is not
+ * enough.  Thus, a proactive measure like this self-test is needed.
+ *
+ * 2. We don't want to leave sensitive data from our actual password hash
+ * computation on the stack or in registers.  Previous revisions of the code
+ * would do explicit cleanups, but simply running the self-test after hash
+ * computation is more reliable.
+ *
+ * The performance cost of this quick self-test is around 0.6% at the "$2a$08"
+ * setting.
+ */
+char *
+_crypt_blowfish_rn(const char *key, const char *setting,
+				   char *output, int size)
+{
+	const char *test_key = "8b \xd0\xc1\xd2\xcf\xcc\xd8";
+	const char *test_setting = "$2a$00$abcdefghijklmnopqrstuu";
+	static const char *const test_hashes[2] =
+	{"i1D709vfamulimlGcq0qq3UvuUasvEa\0\x55",	/* 'a', 'b', 'y' */
+	"VUrPmXD6q/nVSSp7pNDhCR9071IfIRe\0\x55"};	/* 'x' */
+	const char *test_hash = test_hashes[0];
+	char	   *retval;
+	const char *p;
+	int			ok;
+	struct
+	{
+		char		s[7 + 22 + 1];
+		char		o[7 + 22 + 31 + 1 + 1 + 1];
+	}			buf;
+
+/* Hash the supplied password */
+	_crypt_output_magic(setting, output, size);
+	retval = BF_crypt(key, setting, output, size, 16);
+
+/*
+ * Do a quick self-test.  It is important that we make both calls to BF_crypt()
+ * from the same scope such that they likely use the same stack locations,
+ * which makes the second call overwrite the first call's sensitive data on the
+ * stack and makes it more likely that any alignment related issues would be
+ * detected by the self-test.
+ */
+	memcpy(buf.s, test_setting, sizeof(buf.s));
+	if (retval)
+	{
+		unsigned int flags = flags_by_subtype[
+											  (unsigned int) (unsigned char) setting[2] - 'a'];
+
+		test_hash = test_hashes[flags & 1];
+		buf.s[2] = setting[2];
+	}
+	memset(buf.o, 0x55, sizeof(buf.o));
+	buf.o[sizeof(buf.o) - 1] = 0;
+	p = BF_crypt(test_key, buf.s, buf.o, sizeof(buf.o) - (1 + 1), 1);
+
+	ok = (p == buf.o &&
+		  !memcmp(p, buf.s, 7 + 22) &&
+		  !memcmp(p + (7 + 22), test_hash, 31 + 1 + 1 + 1));
+
+	{
+		const char *k = "\xff\xa3" "34" "\xff\xff\xff\xa3" "345";
+		BF_key		ae,
+					ai,
+					ye,
+					yi;
+
+		BF_set_key(k, ae, ai, 2);	/* $2a$ */
+		BF_set_key(k, ye, yi, 4);	/* $2y$ */
+		ai[0] ^= 0x10000;		/* undo the safety (for comparison) */
+		ok = ok && ai[0] == 0xdb9c59bc && ye[17] == 0x33343500 &&
+			!memcmp(ae, ye, sizeof(ae)) &&
+			!memcmp(ai, yi, sizeof(ai));
+	}
+
+	if (ok)
+		return retval;
+
+/* Should not happen */
+	_crypt_output_magic(setting, output, size);
+	return NULL;
+}
diff --git a/contrib/pgcrypto/crypt-gensalt.c b/contrib/pgcrypto/crypt-gensalt.c
index 740f3612..6f2149d2 100644
--- a/contrib/pgcrypto/crypt-gensalt.c
+++ b/contrib/pgcrypto/crypt-gensalt.c
@@ -1,15 +1,31 @@
 /*
- * Written by Solar Designer and placed in the public domain.
- * See crypt_blowfish.c for more information.
- *
  * contrib/pgcrypto/crypt-gensalt.c
  *
+ * Written by Solar Designer <solar at openwall.com> in 2000-2011.
+ * No copyright is claimed, and the software is hereby placed in the public
+ * domain.  In case this attempt to disclaim copyright and place the software
+ * in the public domain is deemed null and void, then the software is
+ * Copyright (c) 2000-2011 Solar Designer and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * See crypt_blowfish.c for more information.
+ *
  * This file contains salt generation functions for the traditional and
  * other common crypt(3) algorithms, except for bcrypt which is defined
  * entirely in crypt_blowfish.c.
  *
- * Put bcrypt generator also here as crypt-blowfish.c
- * may not be compiled always.        -- marko
+ * Changes from upstream crypt_blowfish 1.3
+ *
+ *  - Format with pgindent
+ *  - Don't set errno
+ *  - Make _crypt_itoa64 static
+ *  - Include bcrypt generator also here as crypt-blowfish.c may not always be
+ *    compiled
  */
 
 #include "postgres.h"
@@ -22,9 +38,11 @@ static unsigned char _crypt_itoa64[64 + 1] =
 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
 
 char *
-_crypt_gensalt_traditional_rn(unsigned long count,
+_crypt_gensalt_traditional_rn(const char *prefix, unsigned long count,
 							  const char *input, int size, char *output, int output_size)
 {
+	(void) prefix;
+
 	if (size < 2 || output_size < 2 + 1 || (count && count != 25))
 	{
 		if (output_size > 0)
@@ -40,11 +58,13 @@ _crypt_gensalt_traditional_rn(unsigned long count,
 }
 
 char *
-_crypt_gensalt_extended_rn(unsigned long count,
+_crypt_gensalt_extended_rn(const char *prefix, unsigned long count,
 						   const char *input, int size, char *output, int output_size)
 {
 	unsigned long value;
 
+	(void) prefix;
+
 /* Even iteration counts make it easier to detect weak DES keys from a look
  * at the hash, so they should be avoided */
 	if (size < 3 || output_size < 1 + 4 + 4 + 1 ||
@@ -76,11 +96,13 @@ _crypt_gensalt_extended_rn(unsigned long count,
 }
 
 char *
-_crypt_gensalt_md5_rn(unsigned long count,
+_crypt_gensalt_md5_rn(const char *prefix, unsigned long count,
 					  const char *input, int size, char *output, int output_size)
 {
 	unsigned long value;
 
+	(void) prefix;
+
 	if (size < 3 || output_size < 3 + 4 + 1 || (count && count != 1000))
 	{
 		if (output_size > 0)
@@ -158,11 +180,13 @@ BF_encode(char *dst, const BF_word *src, int size)
 }
 
 char *
-_crypt_gensalt_blowfish_rn(unsigned long count,
+_crypt_gensalt_blowfish_rn(const char *prefix, unsigned long count,
 						   const char *input, int size, char *output, int output_size)
 {
 	if (size < 16 || output_size < 7 + 22 + 1 ||
-		(count && (count < 4 || count > 31)))
+		(count && (count < 4 || count > 31)) ||
+		prefix[0] != '$' || prefix[1] != '2' ||
+		(prefix[2] != 'a' && prefix[2] != 'b' && prefix[2] != 'y'))
 	{
 		if (output_size > 0)
 			output[0] = '\0';
@@ -174,7 +198,7 @@ _crypt_gensalt_blowfish_rn(unsigned long count,
 
 	output[0] = '$';
 	output[1] = '2';
-	output[2] = 'a';
+	output[2] = prefix[2];
 	output[3] = '$';
 	output[4] = '0' + count / 10;
 	output[5] = '0' + count % 10;
diff --git a/contrib/pgcrypto/expected/crypt-blowfish.out b/contrib/pgcrypto/expected/crypt-blowfish.out
index d79b0c04..3d6b215c 100644
--- a/contrib/pgcrypto/expected/crypt-blowfish.out
+++ b/contrib/pgcrypto/expected/crypt-blowfish.out
@@ -13,6 +13,13 @@ SELECT crypt('foox', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O');
  $2a$06$RQiOJ.3ELirrXwxIZY8q0OR3CVJrAfda1z26CCHPnB6mmVZD8p0/C
 (1 row)
 
+-- support $2b$ hashes
+SELECT crypt('password', '$2b$06$cqhNY4qXkVeKliLWOxCFieDY6/J4SmCEojsXfHs7iF4X1r3tuNepW');
+                            crypt                             
+--------------------------------------------------------------
+ $2b$06$cqhNY4qXkVeKliLWOxCFieDY6/J4SmCEojsXfHs7iF4X1r3tuNepW
+(1 row)
+
 -- error, salt too short:
 SELECT crypt('foox', '$2a$');
 ERROR:  invalid salt
diff --git a/contrib/pgcrypto/px-crypt.c b/contrib/pgcrypto/px-crypt.c
index 0913ff2c..1e7a712a 100644
--- a/contrib/pgcrypto/px-crypt.c
+++ b/contrib/pgcrypto/px-crypt.c
@@ -78,7 +78,9 @@ struct px_crypt_algo
 static const struct px_crypt_algo
 			px_crypt_list[] = {
 	{"$2a$", 4, run_crypt_bf},
+	{"$2b$", 4, run_crypt_bf},
 	{"$2x$", 4, run_crypt_bf},
+	{"$2y$", 4, run_crypt_bf},
 	{"$2$", 3, NULL},			/* N/A */
 	{"$1$", 3, run_crypt_md5},
 	{"_", 1, run_crypt_des},
@@ -112,8 +114,9 @@ px_crypt(const char *psw, const char *salt, char *buf, unsigned len)
 struct generator
 {
 	char	   *name;
-	char	   *(*gen) (unsigned long count, const char *input, int size,
-						char *output, int output_size);
+	char	   *(*gen) (const char *prefix, unsigned long count, const char *input,
+						int size, char *output, int output_size);
+	char		*prefix;
 	int			input_len;
 	int			def_rounds;
 	int			min_rounds;
@@ -121,10 +124,10 @@ struct generator
 };
 
 static struct generator gen_list[] = {
-	{"des", _crypt_gensalt_traditional_rn, 2, 0, 0, 0},
-	{"md5", _crypt_gensalt_md5_rn, 6, 0, 0, 0},
-	{"xdes", _crypt_gensalt_extended_rn, 3, PX_XDES_ROUNDS, 1, 0xFFFFFF},
-	{"bf", _crypt_gensalt_blowfish_rn, 16, PX_BF_ROUNDS, 4, 31},
+	{"des", _crypt_gensalt_traditional_rn, "", 2, 0, 0, 0},
+	{"md5", _crypt_gensalt_md5_rn, "$1$", 6, 0, 0, 0},
+	{"xdes", _crypt_gensalt_extended_rn, "_", 3, PX_XDES_ROUNDS, 1, 0xFFFFFF},
+	{"bf", _crypt_gensalt_blowfish_rn, "$2a$", 16, PX_BF_ROUNDS, 4, 31},
 	{NULL, NULL, 0, 0, 0, 0}
 };
 
@@ -154,7 +157,7 @@ px_gen_salt(const char *salt_type, char *buf, int rounds)
 	if (!pg_strong_random(rbuf, g->input_len))
 		return PXE_NO_RANDOM;
 
-	p = g->gen(rounds, rbuf, g->input_len, buf, PX_MAX_SALT_LEN);
+	p = g->gen(g->prefix, rounds, rbuf, g->input_len, buf, PX_MAX_SALT_LEN);
 	px_memset(rbuf, 0, sizeof(rbuf));
 
 	if (p == NULL)
diff --git a/contrib/pgcrypto/px-crypt.h b/contrib/pgcrypto/px-crypt.h
index 08001a81..1a2dce77 100644
--- a/contrib/pgcrypto/px-crypt.h
+++ b/contrib/pgcrypto/px-crypt.h
@@ -56,19 +56,23 @@ int			px_gen_salt(const char *salt_type, char *dst, int rounds);
  */
 
 /* crypt-gensalt.c */
-char	   *_crypt_gensalt_traditional_rn(unsigned long count,
+char	   *_crypt_gensalt_traditional_rn(const char *prefix,
+										  unsigned long count,
 										  const char *input, int size, char *output, int output_size);
-char	   *_crypt_gensalt_extended_rn(unsigned long count,
+char	   *_crypt_gensalt_extended_rn(const char *prefix,
+									   unsigned long count,
 									   const char *input, int size, char *output, int output_size);
-char	   *_crypt_gensalt_md5_rn(unsigned long count,
+char	   *_crypt_gensalt_md5_rn(const char *prefix, unsigned long count,
 								  const char *input, int size, char *output, int output_size);
-char	   *_crypt_gensalt_blowfish_rn(unsigned long count,
+char	   *_crypt_gensalt_blowfish_rn(const char *prefix,
+									   unsigned long count,
 									   const char *input, int size, char *output, int output_size);
 
 /* disable 'extended DES crypt' */
 /* #define DISABLE_XDES */
 
 /* crypt-blowfish.c */
+int			_crypt_output_magic(const char *setting, char *output, int size);
 char	   *_crypt_blowfish_rn(const char *key, const char *setting,
 							   char *output, int size);
 
diff --git a/contrib/pgcrypto/sql/crypt-blowfish.sql b/contrib/pgcrypto/sql/crypt-blowfish.sql
index 3b5a681c..0e96ee0c 100644
--- a/contrib/pgcrypto/sql/crypt-blowfish.sql
+++ b/contrib/pgcrypto/sql/crypt-blowfish.sql
@@ -6,6 +6,9 @@ SELECT crypt('', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O');
 
 SELECT crypt('foox', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O');
 
+-- support $2b$ hashes
+SELECT crypt('password', '$2b$06$cqhNY4qXkVeKliLWOxCFieDY6/J4SmCEojsXfHs7iF4X1r3tuNepW');
+
 -- error, salt too short:
 SELECT crypt('foox', '$2a$');
 
diff --git a/doc/src/sgml/pgcrypto.sgml b/doc/src/sgml/pgcrypto.sgml
index bbaa2691..55842132 100644
--- a/doc/src/sgml/pgcrypto.sgml
+++ b/doc/src/sgml/pgcrypto.sgml
@@ -159,7 +159,7 @@ hmac(data bytea, key bytea, type text) returns bytea
       <entry>yes</entry>
       <entry>128</entry>
       <entry>60</entry>
-      <entry>Blowfish-based, variant 2a</entry>
+      <entry>Blowfish-based, variants 2a, 2b, 2x, 2y</entry>
      </row>
      <row>
       <entry><literal>md5</literal></entry>
-- 
2.32.0

#8Sergey Shinderuk
s.shinderuk@postgrespro.ru
In reply to: Daniel Fone (#7)
Re: pgcrypto support for bcrypt $2b$ hashes

On 02.10.2021 06:48, Daniel Fone wrote:

I don’t get these compiler warnings and I can’t find any settings to use that might generate them. I’m compiling on macOS 11.6 configured with `--enable-cassert --enable-depend --enable-debug CFLAGS=-O0`

Hi, Daniel!
I don't get them from clang on macOS either.

I’ve optimistically updated the patch to hopefully address them, but I’d like to know what I need to do to get those warnings.

But gcc-11 on Ubuntu 20.04 emits them.

Regards,

--
Sergey Shinderuk https://postgrespro.com/

#9Andreas Karlsson
andreas@proxel.se
In reply to: Daniel Fone (#7)
Re: pgcrypto support for bcrypt $2b$ hashes

On 10/2/21 5:48 AM, Daniel Fone wrote:

I don’t get these compiler warnings and I can’t find any settings to use that might generate them. I’m compiling on macOS 11.6 configured with `--enable-cassert --enable-depend --enable-debug CFLAGS=-O0`

I’ve optimistically updated the patch to hopefully address them, but I’d like to know what I need to do to get those warnings.

I run "gcc (Debian 10.3.0-11) 10.3.0" and your new patch silenced the
warnings.

Please add your patch to the current open commitfest.

https://commitfest.postgresql.org/35/

Andreas

#10Daniel Fone
daniel@fone.net.nz
In reply to: Andreas Karlsson (#9)
Re: pgcrypto support for bcrypt $2b$ hashes

On Sat, 2 Oct 2021 at 23:22, Andreas Karlsson <andreas@proxel.se> wrote:

Please add your patch to the current open commitfest.

Done. https://commitfest.postgresql.org/35/3338/

Thanks for the guidance.

Daniel

#11Jacob Champion
jchampion@timescale.com
In reply to: Daniel Fone (#10)
Re: pgcrypto support for bcrypt $2b$ hashes

As discussed in [1]/messages/by-id/86140760-8ba5-6f3a-3e6e-5ca6c060bd24@timescale.com, we're taking this opportunity to return some
patchsets that don't appear to be getting enough reviewer interest.

This is not a rejection, since we don't necessarily think there's
anything unacceptable about the entry, but it differs from a standard
"Returned with Feedback" in that there's probably not much actionable
feedback at all. Rather than code changes, what this patch needs is more
community interest. You might

- ask people for help with your approach,
- see if there are similar patches that your code could supplement,
- get interested parties to agree to review your patch in a CF, or
- possibly present the functionality in a way that's easier to review
overall.

(Doing these things is no guarantee that there will be interest, but
it's hopefully better than endlessly rebasing a patchset that is not
receiving any feedback from the community.)

Once you think you've built up some community support and the patchset
is ready for review, you (or any interested party) can resurrect the
patch entry by visiting

https://commitfest.postgresql.org/38/3338/

and changing the status to "Needs Review", and then changing the
status again to "Move to next CF". (Don't forget the second step;
hopefully we will have streamlined this in the near future!)

Thanks,
--Jacob

[1]: /messages/by-id/86140760-8ba5-6f3a-3e6e-5ca6c060bd24@timescale.com