v14-0005-NSS-contrib-modules.patch

application/octet-stream

Filename: v14-0005-NSS-contrib-modules.patch
Type: application/octet-stream
Part: 4
Message: Re: Support for NSS as a libpq TLS backend

Patch

Format: format-patch
Series: patch v14-0005
Subject: NSS contrib modules
File+
contrib/Makefile 1 1
contrib/pgcrypto/Makefile 5 0
contrib/pgcrypto/nss.c 773 0
contrib/pgcrypto/px.c 1 0
contrib/pgcrypto/px.h 1 0
contrib/sslinfo/sslinfo.c 76 88
doc/src/sgml/pgcrypto.sgml 29 9
doc/src/sgml/sslinfo.sgml 11 1
src/tools/msvc/Install.pm 2 1
src/tools/msvc/Mkvcbuild.pm 11 1
From 027cb4c4352a9e2a8309bb1a0c1f7aa567feccbe Mon Sep 17 00:00:00 2001
From: Daniel Gustafsson <daniel@yesql.se>
Date: Wed, 28 Oct 2020 11:34:37 +0100
Subject: [PATCH v14 5/6] NSS contrib modules

---
 contrib/Makefile            |   2 +-
 contrib/pgcrypto/Makefile   |   5 +
 contrib/pgcrypto/nss.c      | 773 ++++++++++++++++++++++++++++++++++++
 contrib/pgcrypto/px.c       |   1 +
 contrib/pgcrypto/px.h       |   1 +
 contrib/sslinfo/sslinfo.c   | 164 ++++----
 doc/src/sgml/pgcrypto.sgml  |  38 +-
 doc/src/sgml/sslinfo.sgml   |  12 +-
 src/tools/msvc/Install.pm   |   3 +-
 src/tools/msvc/Mkvcbuild.pm |  12 +-
 10 files changed, 910 insertions(+), 101 deletions(-)
 create mode 100644 contrib/pgcrypto/nss.c

diff --git a/contrib/Makefile b/contrib/Makefile
index 7a4866e338..d0b01cb2cc 100644
--- a/contrib/Makefile
+++ b/contrib/Makefile
@@ -52,7 +52,7 @@ SUBDIRS = \
 		unaccent	\
 		vacuumlo
 
-ifeq ($(with_openssl),yes)
+ifeq ($(with_ssl),yes)
 SUBDIRS += sslinfo
 else
 ALWAYS_SUBDIRS += sslinfo
diff --git a/contrib/pgcrypto/Makefile b/contrib/pgcrypto/Makefile
index 61eabd2fc0..6c69ee5a6b 100644
--- a/contrib/pgcrypto/Makefile
+++ b/contrib/pgcrypto/Makefile
@@ -7,11 +7,16 @@ INT_TESTS = sha2
 OSSL_SRCS = openssl.c pgp-mpi-openssl.c
 OSSL_TESTS = sha2 des 3des cast5
 
+NSS_SRCS = nss.c pgp-mpi-internal.c imath.c blf.c
+NSS_TESTS = sha2 des 3des
+
 ZLIB_TST = pgp-compression
 ZLIB_OFF_TST = pgp-zlib-DISABLED
 
 CF_SRCS = $(if $(subst no,,$(with_openssl)), $(OSSL_SRCS), $(INT_SRCS))
+CF_SRCS = $(if $(subst no,,$(with_nss)), $(NSS_SRCS), $(INT_SRCS))
 CF_TESTS = $(if $(subst no,,$(with_openssl)), $(OSSL_TESTS), $(INT_TESTS))
+CF_TESTS = $(if $(subst no,,$(with_nss)), $(NSS_TESTS), $(INT_TESTS))
 CF_PGP_TESTS = $(if $(subst no,,$(with_zlib)), $(ZLIB_TST), $(ZLIB_OFF_TST))
 
 SRCS = \
diff --git a/contrib/pgcrypto/nss.c b/contrib/pgcrypto/nss.c
new file mode 100644
index 0000000000..e1291ed403
--- /dev/null
+++ b/contrib/pgcrypto/nss.c
@@ -0,0 +1,773 @@
+/*-------------------------------------------------------------------------
+ *
+ * nss.c
+ *	  Wrapper for using NSS as a backend for pgcrypto PX
+ *
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  contrib/pgcrypto/nss.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "px.h"
+#include "blf.h"
+#include "utils/memutils.h"
+
+/*
+ * BITS_PER_BYTE is also defined in the NSPR header files, so we need to undef
+ * our version to avoid compiler warnings on redefinition.
+ */
+#define pg_BITS_PER_BYTE BITS_PER_BYTE
+#undef BITS_PER_BYTE
+
+#include <nss/nss.h>
+#include <nss/hasht.h>
+#include <nss/pk11func.h>
+#include <nss/pk11pub.h>
+#include <nss/secitem.h>
+#include <nss/sechash.h>
+#include <nss/secoid.h>
+#include <nss/secerr.h>
+
+/*
+ * Ensure that the colliding definitions match, else throw an error. In case
+ * NSPR has removed the definition for some reason, make sure to put ours
+ * back again.
+ */
+#if defined(BITS_PER_BYTE)
+#if BITS_PER_BYTE != pg_BITS_PER_BYTE
+#error "incompatible byte widths between NSPR and postgres"
+#endif
+#else
+#define BITS_PER_BYTE pg_BITS_PER_BYTE
+#endif
+#undef pg_BITS_PER_BYTE
+
+/*
+ * Define our own mechanisms for Blowfish as it's not implemented by NSS.
+ */
+#define BLOWFISH_CBC	(1)
+#define BLOWFISH_ECB	(2)
+
+/*
+ * Data structures for recording cipher implementations as well as ongoing
+ * cipher operations.
+ */
+typedef struct nss_digest
+{
+	NSSInitContext *context;
+	PK11Context *hash_context;
+	HASH_HashType hash_type;
+}			nss_digest;
+
+typedef struct cipher_implementation
+{
+	/* Function pointers to cipher operations */
+	int			(*init) (PX_Cipher *pxc, const uint8 *key, unsigned klen, const uint8 *iv);
+	unsigned	(*get_block_size) (PX_Cipher *pxc);
+	unsigned 	(*get_key_size) (PX_Cipher *pxc);
+	unsigned 	(*get_iv_size) (PX_Cipher *pxc);
+	int			(*encrypt) (PX_Cipher *pxc, const uint8 *data, unsigned dlen, uint8 *res);
+	int			(*decrypt) (PX_Cipher *pxc, const uint8 *data, unsigned dlen, uint8 *res);
+	void		(*free) (PX_Cipher *pxc);
+
+	/* The mechanism describing the cipher used */
+	union
+	{
+		CK_MECHANISM_TYPE nss;
+		CK_ULONG internal;
+	}			mechanism;
+	int			keylen;
+	bool		is_nss;
+}			cipher_implementation;
+
+typedef struct nss_cipher
+{
+	const cipher_implementation *impl;
+	NSSInitContext *context;
+	PK11Context *crypt_context;
+	SECItem *params;
+
+	PK11SymKey *encrypt_key;
+	PK11SymKey *decrypt_key;
+}			nss_cipher;
+
+typedef struct internal_cipher
+{
+	const cipher_implementation *impl;
+	BlowfishContext	context;
+}			internal_cipher;
+
+typedef struct nss_cipher_ref
+{
+	const char *name;
+	const cipher_implementation *impl;
+} nss_cipher_ref;
+
+/*
+ * Prototypes
+ */
+static unsigned nss_get_iv_size(PX_Cipher *pxc);
+static unsigned nss_get_block_size(PX_Cipher *pxc);
+
+/*
+ * nss_GetHashOidTagByHashType
+ *
+ * Returns the corresponding SECOidTag for the passed hash type. NSS 3.43
+ * includes HASH_GetHashOidTagByHashType for this purpose, but at the time of
+ * writing is not commonly available so we need our own version till then.
+ */
+static SECOidTag
+nss_GetHashOidTagByHashType(HASH_HashType type)
+{
+	if (type == HASH_AlgMD2)
+		return SEC_OID_MD2;
+	if (type == HASH_AlgMD5)
+		return SEC_OID_MD5;
+	if (type == HASH_AlgSHA1)
+		return SEC_OID_SHA1;
+	if (type == HASH_AlgSHA224)
+		return SEC_OID_SHA224;
+	if (type == HASH_AlgSHA256)
+		return SEC_OID_SHA256;
+	if (type == HASH_AlgSHA384)
+		return SEC_OID_SHA384;
+	if (type == HASH_AlgSHA512)
+		return SEC_OID_SHA512;
+
+	return SEC_OID_UNKNOWN;
+}
+
+static unsigned
+nss_digest_block_size(PX_MD *pxmd)
+{
+	nss_digest *digest = (nss_digest *) pxmd->p.ptr;
+	const SECHashObject *object;
+
+	object = HASH_GetHashObject(digest->hash_type);
+	return object->blocklength;
+}
+
+static unsigned
+nss_digest_result_size(PX_MD *pxmd)
+{
+	nss_digest *digest = (nss_digest *) pxmd->p.ptr;
+	const SECHashObject *object;
+
+	object = HASH_GetHashObject(digest->hash_type);
+	return object->length;
+}
+
+static void
+nss_digest_free(PX_MD *pxmd)
+{
+	nss_digest *digest = (nss_digest *) pxmd->p.ptr;
+	PRBool	free_ctx = PR_TRUE;
+
+	PK11_DestroyContext(digest->hash_context, free_ctx);
+	NSS_ShutdownContext(digest->context);
+}
+
+static void
+nss_digest_update(PX_MD *pxmd, const uint8 *data, unsigned dlen)
+{
+	nss_digest *digest = (nss_digest *) pxmd->p.ptr;
+
+	PK11_DigestOp(digest->hash_context, data, dlen);
+}
+
+static void
+nss_digest_reset(PX_MD *pxmd)
+{
+	nss_digest *digest = (nss_digest *) pxmd->p.ptr;
+
+	PK11_DigestBegin(digest->hash_context);
+}
+
+static void
+nss_digest_finish(PX_MD *pxmd, uint8 *dst)
+{
+	unsigned int outlen;
+	nss_digest *digest = (nss_digest *) pxmd->p.ptr;
+	const SECHashObject *object;
+
+	object = HASH_GetHashObject(digest->hash_type);
+	PK11_DigestFinal(digest->hash_context, dst, &outlen, object->length);
+}
+
+int
+px_find_digest(const char *name, PX_MD **res)
+{
+	PX_MD	*pxmd;
+	NSSInitParameters params;
+	NSSInitContext *nss_context;
+	bool found = false;
+	nss_digest *digest;
+	SECStatus status;
+	SECOidData *hash;
+	HASH_HashType t;
+
+	/*
+	 * Initialize our own NSS context without a database backing it.
+	 */
+	memset(&params, 0, sizeof(params));
+	params.length = sizeof(params);
+	nss_context = NSS_InitContext("", "", "", "", &params,
+								  NSS_INIT_READONLY | NSS_INIT_NOCERTDB |
+								  NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN |
+								  NSS_INIT_NOROOTINIT | NSS_INIT_PK11RELOAD);
+
+	/*
+	 * There is no API function for looking up a digest algorithm from a name
+	 * string, but there is a publically accessible array which can be scanned
+	 * for the name.
+	 */
+	for (t = HASH_AlgNULL + 1; t < HASH_AlgTOTAL; t++)
+	{
+		SECOidTag 	hash_oid;
+		char *p;
+
+		hash_oid = nss_GetHashOidTagByHashType(t);
+
+		if (hash_oid == SEC_OID_UNKNOWN)
+			return PXE_NO_HASH;
+
+		hash = SECOID_FindOIDByTag(hash_oid);
+		if (pg_strcasecmp(hash->desc, name) == 0)
+		{
+			found = true;
+			break;
+		}
+
+		/*
+		 * NSS saves the algorithm names using SHA-xxx notation whereas
+		 * OpenSSL use SHAxxx. To make sure the user finds the requested
+		 * algorithm let's remove the dash and compare that spelling as well.
+		 */
+		if ((p = strchr(hash->desc, '-')) != NULL)
+		{
+			char tmp[12];
+
+			memcpy(tmp, hash->desc, p - hash->desc);
+			memcpy(tmp + (p - hash->desc), p + 1, strlen(hash->desc) - (p - hash->desc) + 1);
+			if (pg_strcasecmp(tmp, name) == 0)
+			{
+				found = true;
+				break;
+			}
+		}
+	}
+
+	if (!found)
+		return PXE_NO_HASH;
+
+	digest = palloc(sizeof(*digest));
+
+	digest->context = nss_context;
+	digest->hash_context = PK11_CreateDigestContext(hash->offset);
+	digest->hash_type = t;
+	if (digest->hash_context == NULL)
+	{
+		pfree(digest);
+		return -1;
+	}
+
+	status = PK11_DigestBegin(digest->hash_context);
+	if (status != SECSuccess)
+	{
+		PK11_DestroyContext(digest->hash_context, PR_TRUE);
+		pfree(digest);
+		return -1;
+	}
+
+	pxmd = palloc(sizeof(*pxmd));
+	pxmd->result_size = nss_digest_result_size;
+	pxmd->block_size = nss_digest_block_size;
+	pxmd->reset = nss_digest_reset;
+	pxmd->update = nss_digest_update;
+	pxmd->finish = nss_digest_finish;
+	pxmd->free = nss_digest_free;
+	pxmd->p.ptr = (void *) digest;
+
+	*res = pxmd;
+
+	return 0;
+}
+
+static int
+bf_init(PX_Cipher *pxc, const uint8 *key, unsigned klen, const uint8 *iv)
+{
+	internal_cipher *cipher = (internal_cipher *) pxc->ptr;
+
+	blowfish_setkey(&cipher->context, key, klen);
+	if (iv)
+		blowfish_setiv(&cipher->context, iv);
+
+	return 0;
+}
+
+static int
+bf_encrypt(PX_Cipher *pxc, const uint8 *data, unsigned dlen, uint8 *res)
+{
+	internal_cipher *cipher = (internal_cipher *) pxc->ptr;
+	uint8 *buf;
+
+	if (dlen == 0)
+		return 0;
+
+	if (dlen & 7)
+		return PXE_NOTBLOCKSIZE;
+
+	buf = palloc(dlen);
+	memcpy(buf, data, dlen);
+
+	if (cipher->impl->mechanism.internal == BLOWFISH_ECB)
+		blowfish_encrypt_ecb(buf, dlen, &cipher->context);
+	else
+		blowfish_encrypt_cbc(buf, dlen, &cipher->context);
+
+	memcpy(res, buf, dlen);
+	pfree(buf);
+
+	return 0;
+}
+
+static int
+bf_decrypt(PX_Cipher *pxc, const uint8 *data, unsigned dlen, uint8 *res)
+{
+	internal_cipher *cipher = (internal_cipher *) pxc->ptr;
+
+	if (dlen == 0)
+		return 0;
+
+	if (dlen & 7)
+		return PXE_NOTBLOCKSIZE;
+
+	memcpy(res, data, dlen);
+	if (cipher->impl->mechanism.internal == BLOWFISH_ECB)
+		blowfish_decrypt_ecb(res, dlen, &cipher->context);
+	else
+		blowfish_decrypt_cbc(res, dlen, &cipher->context);
+
+	return 0;
+}
+
+static void
+bf_free(PX_Cipher *pxc)
+{
+	internal_cipher *cipher = (internal_cipher *) pxc->ptr;
+
+	pfree(cipher);
+	pfree(pxc);
+}
+
+static int
+nss_symkey_blockcipher_init(PX_Cipher *pxc, const uint8 *key, unsigned klen,
+							const uint8 *iv)
+{
+	nss_cipher	   *cipher = (nss_cipher *) pxc->ptr;
+	SECItem			iv_item;
+	unsigned char  *iv_item_data;
+	SECItem			key_item;
+	int				keylen;
+	PK11SlotInfo   *slot;
+
+	if (cipher->impl->mechanism.nss == CKM_AES_CBC ||
+		cipher->impl->mechanism.nss == CKM_AES_ECB)
+	{
+		if (klen <= 128 / 8)
+			keylen = 128 / 8;
+		else if (klen <= 192 / 8)
+			keylen = 192 / 8;
+		else if (klen <= 256 / 8)
+			keylen = 256 / 8;
+		else
+			return PXE_CIPHER_INIT;
+	}
+	else
+		keylen = cipher->impl->keylen;
+
+	key_item.type = siBuffer;
+	key_item.data = (unsigned char *) key;
+	key_item.len = keylen;
+
+	/*
+	 * If hardware acceleration is configured in NSS one can theoretically get
+	 * a better slot by calling PK11_GetBestSlot() with the mechanism passed
+	 * to it. Unless there are complaints, using the simpler API will make
+	 * error handling easier though.
+	 */
+	slot = PK11_GetInternalSlot();
+	if (!slot)
+		return PXE_CIPHER_INIT;
+
+	/*
+	 * The key must be set up for the operation, and since we don't know at
+	 * this point whether we are asked to encrypt or decrypt we need to store
+	 * both versions.
+	 */
+	cipher->decrypt_key = PK11_ImportSymKey(slot, cipher->impl->mechanism.nss,
+											PK11_OriginUnwrap,
+											CKA_DECRYPT, &key_item,
+											NULL);
+	cipher->encrypt_key = PK11_ImportSymKey(slot, cipher->impl->mechanism.nss,
+											PK11_OriginUnwrap,
+											CKA_ENCRYPT, &key_item,
+											NULL);
+	PK11_FreeSlot(slot);
+
+	if (!cipher->decrypt_key || !cipher->encrypt_key)
+		return PXE_CIPHER_INIT;
+
+	if (iv)
+	{
+		iv_item.type = siBuffer;
+		iv_item.data = (unsigned char *) iv;
+		iv_item.len = nss_get_iv_size(pxc);
+	}
+	else
+	{
+		/*
+		 * The documentation states that either passing .data = 0; .len = 0;
+		 * in the iv_item, or NULL as iv_item, to PK11_ParamFromIV should be
+		 * done when IV is missing. That however leads to segfaults in the
+		 * library, the workaround that works in modern  library versions is
+		 * to pass in a keysized zeroed out IV.
+		 */
+		iv_item_data = palloc0(nss_get_iv_size(pxc));
+		iv_item.type = siBuffer;
+		iv_item.data = iv_item_data;
+		iv_item.len = nss_get_iv_size(pxc);
+	}
+
+	cipher->params = PK11_ParamFromIV(cipher->impl->mechanism.nss, &iv_item);
+
+	/* If we had to make a mock IV, free it once made into a param */
+	if (!iv)
+		pfree(iv_item_data);
+
+	if (cipher->params == NULL)
+		return PXE_CIPHER_INIT;
+
+	return 0;
+}
+
+static int
+nss_decrypt(PX_Cipher *pxc, const uint8 *data, unsigned dlen, uint8 *res)
+{
+	nss_cipher *cipher = (nss_cipher *) pxc->ptr;
+	SECStatus	status;
+	int outlen;
+
+	if (!cipher->crypt_context)
+	{
+		cipher->crypt_context =
+			PK11_CreateContextBySymKey(cipher->impl->mechanism.nss, CKA_DECRYPT,
+									   cipher->decrypt_key, cipher->params);
+	}
+
+	status = PK11_CipherOp(cipher->crypt_context, res, &outlen, dlen, data, dlen);
+	if (status != SECSuccess)
+		return PXE_DECRYPT_FAILED;
+
+	return 0;
+}
+
+static int
+nss_encrypt(PX_Cipher *pxc, const uint8 *data, unsigned dlen, uint8 *res)
+{
+	nss_cipher *cipher = (nss_cipher *) pxc->ptr;
+	SECStatus	status;
+	int outlen;
+
+	if (!cipher->crypt_context)
+	{
+		cipher->crypt_context =
+			PK11_CreateContextBySymKey(cipher->impl->mechanism.nss, CKA_ENCRYPT,
+									   cipher->decrypt_key, cipher->params);
+	}
+
+	status = PK11_CipherOp(cipher->crypt_context, res, &outlen, dlen, data, dlen);
+
+	if (status != SECSuccess)
+		return PXE_DECRYPT_FAILED;
+
+	return 0;
+}
+
+static void
+nss_free(PX_Cipher *pxc)
+{
+	nss_cipher *cipher = pxc->ptr;
+	PRBool		free_ctx = PR_TRUE;
+
+	PK11_FreeSymKey(cipher->encrypt_key);
+	PK11_FreeSymKey(cipher->decrypt_key);
+	PK11_DestroyContext(cipher->crypt_context, free_ctx);
+	NSS_ShutdownContext(cipher->context);
+	pfree(cipher);
+
+	pfree(pxc);
+}
+
+static unsigned
+nss_get_block_size(PX_Cipher *pxc)
+{
+	nss_cipher *cipher = pxc->ptr;
+
+	return PK11_GetBlockSize(cipher->impl->mechanism.nss, NULL);
+}
+
+static unsigned
+nss_get_key_size(PX_Cipher *pxc)
+{
+	nss_cipher *cipher = pxc->ptr;
+
+	return cipher->impl->keylen;
+}
+
+static unsigned
+nss_get_iv_size(PX_Cipher *pxc)
+{
+	nss_cipher *cipher = pxc->ptr;
+
+	return PK11_GetIVLength(cipher->impl->mechanism.nss);
+}
+
+static unsigned
+bf_get_block_size(PX_Cipher *pxc)
+{
+	return 8;
+}
+
+static unsigned
+bf_get_key_size(PX_Cipher *pxc)
+{
+	return 448 / 8;
+}
+
+static unsigned
+bf_get_iv_size(PX_Cipher *pxc)
+{
+	return 8;
+}
+
+/*
+ * Cipher Implementations
+ */
+static const cipher_implementation nss_des_cbc = {
+	.init = nss_symkey_blockcipher_init,
+	.get_block_size = nss_get_block_size,
+	.get_key_size = nss_get_key_size,
+	.get_iv_size = nss_get_iv_size,
+	.encrypt = nss_encrypt,
+	.decrypt = nss_decrypt,
+	.free = nss_free,
+	.mechanism.nss = CKM_DES_CBC,
+	.keylen = 8,
+	.is_nss = true
+};
+
+static const cipher_implementation nss_des_ecb = {
+	.init = nss_symkey_blockcipher_init,
+	.get_block_size = nss_get_block_size,
+	.get_key_size = nss_get_key_size,
+	.get_iv_size = nss_get_iv_size,
+	.encrypt = nss_encrypt,
+	.decrypt = nss_decrypt,
+	.free = nss_free,
+	.mechanism.nss = CKM_DES_ECB,
+	.keylen = 8,
+	.is_nss = true
+};
+
+static const cipher_implementation nss_des3_cbc = {
+	.init = nss_symkey_blockcipher_init,
+	.get_block_size = nss_get_block_size,
+	.get_key_size = nss_get_key_size,
+	.get_iv_size = nss_get_iv_size,
+	.encrypt = nss_encrypt,
+	.decrypt = nss_decrypt,
+	.free = nss_free,
+	.mechanism.nss = CKM_DES3_CBC,
+	.keylen = 24,
+	.is_nss = true
+};
+
+static const cipher_implementation nss_des3_ecb = {
+	.init = nss_symkey_blockcipher_init,
+	.get_block_size = nss_get_block_size,
+	.get_key_size = nss_get_key_size,
+	.get_iv_size = nss_get_iv_size,
+	.encrypt = nss_encrypt,
+	.decrypt = nss_decrypt,
+	.free = nss_free,
+	.mechanism.nss = CKM_DES3_ECB,
+	.keylen = 24,
+	.is_nss = true
+};
+
+static const cipher_implementation nss_aes_cbc = {
+	.init = nss_symkey_blockcipher_init,
+	.get_block_size = nss_get_block_size,
+	.get_key_size = nss_get_key_size,
+	.get_iv_size = nss_get_iv_size,
+	.encrypt = nss_encrypt,
+	.decrypt = nss_decrypt,
+	.free = nss_free,
+	.mechanism.nss = CKM_AES_CBC,
+	.keylen = 32,
+	.is_nss = true
+};
+
+static const cipher_implementation nss_aes_ecb = {
+	.init = nss_symkey_blockcipher_init,
+	.get_block_size = nss_get_block_size,
+	.get_key_size = nss_get_key_size,
+	.get_iv_size = nss_get_iv_size,
+	.encrypt = nss_encrypt,
+	.decrypt = nss_decrypt,
+	.free = nss_free,
+	.mechanism.nss = CKM_AES_ECB,
+	.keylen = 32,
+	.is_nss = true
+};
+
+static const cipher_implementation nss_bf_ecb = {
+	.init = bf_init,
+	.get_block_size = bf_get_block_size,
+	.get_key_size = bf_get_key_size,
+	.get_iv_size = bf_get_iv_size,
+	.encrypt = bf_encrypt,
+	.decrypt = bf_decrypt,
+	.free = bf_free,
+	.mechanism.internal = BLOWFISH_ECB,
+	.keylen = 56,
+	.is_nss = false
+};
+
+static const cipher_implementation nss_bf_cbc = {
+	.init = bf_init,
+	.get_block_size = bf_get_block_size,
+	.get_key_size = bf_get_key_size,
+	.get_iv_size = bf_get_iv_size,
+	.encrypt = bf_encrypt,
+	.decrypt = bf_decrypt,
+	.free = bf_free,
+	.mechanism.internal = BLOWFISH_CBC,
+	.keylen = 56,
+	.is_nss = false
+};
+
+/*
+ * Lookup table for finding the implementation based on a name. CAST5 as well
+ * as BLOWFISH are defined as cipher mechanisms in NSS but error out with
+ * SEC_ERROR_INVALID_ALGORITHM. Blowfish is implemented using the internal
+ * implementation while CAST5 isn't supported at all at this time,
+ */
+static const nss_cipher_ref nss_cipher_lookup[] = {
+	{"des", &nss_des_cbc},
+	{"des-cbc", &nss_des_cbc},
+	{"des-ecb", &nss_des_ecb},
+	{"des3-cbc", &nss_des3_cbc},
+	{"3des", &nss_des3_cbc},
+	{"3des-cbc", &nss_des3_cbc},
+	{"des3-ecb", &nss_des3_ecb},
+	{"3des-ecb", &nss_des3_ecb},
+	{"aes", &nss_aes_cbc},
+	{"aes-cbc", &nss_aes_cbc},
+	{"aes-ecb", &nss_aes_ecb},
+	{"rijndael", &nss_aes_cbc},
+	{"rijndael-cbc", &nss_aes_cbc},
+	{"rijndael-ecb", &nss_aes_ecb},
+	{"blowfish", &nss_bf_cbc},
+	{"blowfish-cbc", &nss_bf_cbc},
+	{"blowfish-ecb", &nss_bf_ecb},
+	{"bf", &nss_bf_cbc},
+	{"bf-cbc", &nss_bf_cbc},
+	{"bf-ecb", &nss_bf_ecb},
+	{NULL}
+};
+
+/*
+ * px_find_cipher
+ *
+ * Search for the requested cipher and see if there is support for it, and if
+ * so return an allocated object containing the playbook for how to encrypt
+ * and decrypt using the cipher.
+ */
+int
+px_find_cipher(const char *alias, PX_Cipher **res)
+{
+	const nss_cipher_ref *cipher_ref;
+	PX_Cipher  *px_cipher;
+	NSSInitParameters params;
+	NSSInitContext *nss_context;
+	nss_cipher	*cipher;
+	internal_cipher *int_cipher;
+
+	for (cipher_ref = nss_cipher_lookup; cipher_ref->name; cipher_ref++)
+	{
+		if (strcmp(cipher_ref->name, alias) == 0)
+			break;
+	}
+
+	if (!cipher_ref->name)
+		return PXE_NO_CIPHER;
+
+	/*
+	 * Fill in the PX_Cipher to pass back to PX describing the operations to
+	 * perform in order to use the cipher.
+	 */
+	px_cipher = palloc(sizeof(*px_cipher));
+	px_cipher->block_size = cipher_ref->impl->get_block_size;
+	px_cipher->key_size = cipher_ref->impl->get_key_size;
+	px_cipher->iv_size = cipher_ref->impl->get_iv_size;
+	px_cipher->free = cipher_ref->impl->free;
+	px_cipher->init = cipher_ref->impl->init;
+	px_cipher->encrypt = cipher_ref->impl->encrypt;
+	px_cipher->decrypt = cipher_ref->impl->decrypt;
+
+	/*
+	 * Set the private data, which is different for NSS and internal ciphers
+	 */
+	if (cipher_ref->impl->is_nss)
+	{
+		/*
+		 * Initialize our own NSS context without a database backing it. At
+		 * some point we might want to allow users to use stored keys in the
+		 * database rather than passing them via SELECT encrypt(), and then
+		 * this need to be changed to open a user specified database.
+		 */
+		memset(&params, 0, sizeof(params));
+		params.length = sizeof(params);
+		nss_context = NSS_InitContext("", "", "", "", &params,
+									  NSS_INIT_READONLY | NSS_INIT_NOCERTDB |
+									  NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN |
+									  NSS_INIT_NOROOTINIT | NSS_INIT_PK11RELOAD);
+
+		/* nss_cipher is the private state struct for the operation */
+		cipher = palloc(sizeof(*cipher));
+		cipher->context = nss_context;
+		cipher->impl = cipher_ref->impl;
+		cipher->crypt_context = NULL;
+
+		px_cipher->ptr = cipher;
+	}
+	else
+	{
+		int_cipher = palloc0(sizeof(*int_cipher));
+
+		int_cipher->impl = cipher_ref->impl;
+		px_cipher->ptr = int_cipher;
+	}
+
+	*res = px_cipher;
+	return 0;
+}
diff --git a/contrib/pgcrypto/px.c b/contrib/pgcrypto/px.c
index 6a4681dae9..a243f575d3 100644
--- a/contrib/pgcrypto/px.c
+++ b/contrib/pgcrypto/px.c
@@ -58,6 +58,7 @@ static const struct error_desc px_err_list[] = {
 	{PXE_MCRYPT_INTERNAL, "mcrypt internal error"},
 	{PXE_NO_RANDOM, "Failed to generate strong random bits"},
 	{PXE_DECRYPT_FAILED, "Decryption failed"},
+	{PXE_ENCRYPT_FAILED, "Encryption failed"},
 	{PXE_PGP_CORRUPT_DATA, "Wrong key or corrupt data"},
 	{PXE_PGP_CORRUPT_ARMOR, "Corrupt ascii-armor"},
 	{PXE_PGP_UNSUPPORTED_COMPR, "Unsupported compression algorithm"},
diff --git a/contrib/pgcrypto/px.h b/contrib/pgcrypto/px.h
index 5487923edb..17d6f22498 100644
--- a/contrib/pgcrypto/px.h
+++ b/contrib/pgcrypto/px.h
@@ -61,6 +61,7 @@
 #define PXE_MCRYPT_INTERNAL			-16
 #define PXE_NO_RANDOM				-17
 #define PXE_DECRYPT_FAILED			-18
+#define PXE_ENCRYPT_FAILED			-19
 
 #define PXE_PGP_CORRUPT_DATA		-100
 #define PXE_PGP_CORRUPT_ARMOR		-101
diff --git a/contrib/sslinfo/sslinfo.c b/contrib/sslinfo/sslinfo.c
index 5ba3988e27..84bb2c65b8 100644
--- a/contrib/sslinfo/sslinfo.c
+++ b/contrib/sslinfo/sslinfo.c
@@ -9,9 +9,11 @@
 
 #include "postgres.h"
 
+#ifdef USE_OPENSSL
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 #include <openssl/asn1.h>
+#endif
 
 #include "access/htup_details.h"
 #include "funcapi.h"
@@ -21,8 +23,8 @@
 
 PG_MODULE_MAGIC;
 
+#ifdef USE_OPENSSL
 static Datum X509_NAME_field_to_text(X509_NAME *name, text *fieldName);
-static Datum X509_NAME_to_text(X509_NAME *name);
 static Datum ASN1_STRING_to_text(ASN1_STRING *str);
 
 /*
@@ -32,6 +34,7 @@ typedef struct
 {
 	TupleDesc	tupdesc;
 } SSLExtensionInfoContext;
+#endif
 
 /*
  * Indicates whether current session uses SSL
@@ -54,9 +57,16 @@ PG_FUNCTION_INFO_V1(ssl_version);
 Datum
 ssl_version(PG_FUNCTION_ARGS)
 {
-	if (MyProcPort->ssl == NULL)
+	const char *version;
+
+	if (!MyProcPort->ssl_in_use)
+		PG_RETURN_NULL();
+
+	version = be_tls_get_version(MyProcPort);
+	if (version == NULL)
 		PG_RETURN_NULL();
-	PG_RETURN_TEXT_P(cstring_to_text(SSL_get_version(MyProcPort->ssl)));
+
+	PG_RETURN_TEXT_P(cstring_to_text(version));
 }
 
 
@@ -67,9 +77,16 @@ PG_FUNCTION_INFO_V1(ssl_cipher);
 Datum
 ssl_cipher(PG_FUNCTION_ARGS)
 {
-	if (MyProcPort->ssl == NULL)
+	const char *cipher;
+
+	if (!MyProcPort->ssl_in_use)
 		PG_RETURN_NULL();
-	PG_RETURN_TEXT_P(cstring_to_text(SSL_get_cipher(MyProcPort->ssl)));
+
+	cipher = be_tls_get_cipher(MyProcPort);
+	if (cipher == NULL)
+		PG_RETURN_NULL();
+
+	PG_RETURN_TEXT_P(cstring_to_text(cipher));
 }
 
 
@@ -83,7 +100,7 @@ PG_FUNCTION_INFO_V1(ssl_client_cert_present);
 Datum
 ssl_client_cert_present(PG_FUNCTION_ARGS)
 {
-	PG_RETURN_BOOL(MyProcPort->peer != NULL);
+	PG_RETURN_BOOL(MyProcPort->peer_cert_valid);
 }
 
 
@@ -99,29 +116,26 @@ PG_FUNCTION_INFO_V1(ssl_client_serial);
 Datum
 ssl_client_serial(PG_FUNCTION_ARGS)
 {
+	char decimal[NAMEDATALEN];
 	Datum		result;
-	Port	   *port = MyProcPort;
-	X509	   *peer = port->peer;
-	ASN1_INTEGER *serial = NULL;
-	BIGNUM	   *b;
-	char	   *decimal;
 
-	if (!peer)
+	if (!MyProcPort->ssl_in_use || !MyProcPort->peer_cert_valid)
+		PG_RETURN_NULL();
+
+	be_tls_get_peer_serial(MyProcPort, decimal, NAMEDATALEN);
+
+	if (!*decimal)
 		PG_RETURN_NULL();
-	serial = X509_get_serialNumber(peer);
-	b = ASN1_INTEGER_to_BN(serial, NULL);
-	decimal = BN_bn2dec(b);
 
-	BN_free(b);
 	result = DirectFunctionCall3(numeric_in,
 								 CStringGetDatum(decimal),
 								 ObjectIdGetDatum(0),
 								 Int32GetDatum(-1));
-	OPENSSL_free(decimal);
 	return result;
 }
 
 
+#ifdef USE_OPENSSL
 /*
  * Converts OpenSSL ASN1_STRING structure into text
  *
@@ -228,7 +242,7 @@ ssl_client_dn_field(PG_FUNCTION_ARGS)
 	text	   *fieldname = PG_GETARG_TEXT_PP(0);
 	Datum		result;
 
-	if (!(MyProcPort->peer))
+	if (!MyProcPort->ssl_in_use || !MyProcPort->peer_cert_valid)
 		PG_RETURN_NULL();
 
 	result = X509_NAME_field_to_text(X509_get_subject_name(MyProcPort->peer), fieldname);
@@ -273,76 +287,23 @@ ssl_issuer_field(PG_FUNCTION_ARGS)
 	else
 		return result;
 }
+#endif							/* USE_OPENSSL */
 
-
-/*
- * Equivalent of X509_NAME_oneline that respects encoding
- *
- * This function converts X509_NAME structure to the text variable
- * converting all textual data into current database encoding.
- *
- * Parameter: X509_NAME *name X509_NAME structure to be converted
- *
- * Returns: text datum which contains string representation of
- * X509_NAME
- */
-static Datum
-X509_NAME_to_text(X509_NAME *name)
+#ifdef USE_NSS
+PG_FUNCTION_INFO_V1(ssl_client_dn_field);
+Datum
+ssl_client_dn_field(PG_FUNCTION_ARGS)
 {
-	BIO		   *membuf = BIO_new(BIO_s_mem());
-	int			i,
-				nid,
-				count = X509_NAME_entry_count(name);
-	X509_NAME_ENTRY *e;
-	ASN1_STRING *v;
-	const char *field_name;
-	size_t		size;
-	char		nullterm;
-	char	   *sp;
-	char	   *dp;
-	text	   *result;
-
-	if (membuf == NULL)
-		ereport(ERROR,
-				(errcode(ERRCODE_OUT_OF_MEMORY),
-				 errmsg("could not create OpenSSL BIO structure")));
-
-	(void) BIO_set_close(membuf, BIO_CLOSE);
-	for (i = 0; i < count; i++)
-	{
-		e = X509_NAME_get_entry(name, i);
-		nid = OBJ_obj2nid(X509_NAME_ENTRY_get_object(e));
-		if (nid == NID_undef)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("could not get NID for ASN1_OBJECT object")));
-		v = X509_NAME_ENTRY_get_data(e);
-		field_name = OBJ_nid2sn(nid);
-		if (field_name == NULL)
-			field_name = OBJ_nid2ln(nid);
-		if (field_name == NULL)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("could not convert NID %d to an ASN1_OBJECT structure", nid)));
-		BIO_printf(membuf, "/%s=", field_name);
-		ASN1_STRING_print_ex(membuf, v,
-							 ((ASN1_STRFLGS_RFC2253 & ~ASN1_STRFLGS_ESC_MSB)
-							  | ASN1_STRFLGS_UTF8_CONVERT));
-	}
-
-	/* ensure null termination of the BIO's content */
-	nullterm = '\0';
-	BIO_write(membuf, &nullterm, 1);
-	size = BIO_get_mem_data(membuf, &sp);
-	dp = pg_any_to_server(sp, size - 1, PG_UTF8);
-	result = cstring_to_text(dp);
-	if (dp != sp)
-		pfree(dp);
-	if (BIO_free(membuf) != 1)
-		elog(ERROR, "could not free OpenSSL BIO structure");
+	PG_RETURN_NULL();
+}
 
-	PG_RETURN_TEXT_P(result);
+PG_FUNCTION_INFO_V1(ssl_issuer_field);
+Datum
+ssl_issuer_field(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_NULL();
 }
+#endif							/* USE_NSS */
 
 
 /*
@@ -358,9 +319,17 @@ PG_FUNCTION_INFO_V1(ssl_client_dn);
 Datum
 ssl_client_dn(PG_FUNCTION_ARGS)
 {
-	if (!(MyProcPort->peer))
+	char		subject[NAMEDATALEN];
+
+	if (!MyProcPort->ssl_in_use || !MyProcPort->peer_cert_valid)
+		PG_RETURN_NULL();
+
+	be_tls_get_peer_subject_name(MyProcPort, subject, NAMEDATALEN);
+
+	if (!*subject)
 		PG_RETURN_NULL();
-	return X509_NAME_to_text(X509_get_subject_name(MyProcPort->peer));
+
+	PG_RETURN_TEXT_P(cstring_to_text(subject));
 }
 
 
@@ -377,12 +346,21 @@ PG_FUNCTION_INFO_V1(ssl_issuer_dn);
 Datum
 ssl_issuer_dn(PG_FUNCTION_ARGS)
 {
-	if (!(MyProcPort->peer))
+	char		issuer[NAMEDATALEN];
+
+	if (!MyProcPort->ssl_in_use || !MyProcPort->peer_cert_valid)
+		PG_RETURN_NULL();
+
+	be_tls_get_peer_issuer_name(MyProcPort, issuer, NAMEDATALEN);
+
+	if (!*issuer)
 		PG_RETURN_NULL();
-	return X509_NAME_to_text(X509_get_issuer_name(MyProcPort->peer));
+
+	PG_RETURN_TEXT_P(cstring_to_text(issuer));
 }
 
 
+#ifdef USE_OPENSSL
 /*
  * Returns information about available SSL extensions.
  *
@@ -516,3 +494,13 @@ ssl_extension_info(PG_FUNCTION_ARGS)
 	/* All done */
 	SRF_RETURN_DONE(funcctx);
 }
+#endif							/* USE_OPENSSL */
+
+#ifdef USE_NSS
+PG_FUNCTION_INFO_V1(ssl_extension_info);
+Datum
+ssl_extension_info(PG_FUNCTION_ARGS)
+{
+	PG_RETURN_NULL();
+}
+#endif							/* USE_NSS */
diff --git a/doc/src/sgml/pgcrypto.sgml b/doc/src/sgml/pgcrypto.sgml
index 8748c64e2d..f03e1f5fcf 100644
--- a/doc/src/sgml/pgcrypto.sgml
+++ b/doc/src/sgml/pgcrypto.sgml
@@ -45,8 +45,9 @@ digest(data bytea, type text) returns bytea
     <literal>sha224</literal>, <literal>sha256</literal>,
     <literal>sha384</literal> and <literal>sha512</literal>.
     If <filename>pgcrypto</filename> was built with
-    <productname>OpenSSL</productname>, more algorithms are available, as
-    detailed in <xref linkend="pgcrypto-with-without-openssl"/>.
+    <productname>OpenSSL</productname> or <productname>NSS</productname>,
+    more algorithms are available, as
+    detailed in <xref linkend="pgcrypto-with-without-openssl-nss"/>.
    </para>
 
    <para>
@@ -763,7 +764,7 @@ pgp_sym_encrypt(data, psw, 'compress-algo=1, cipher-algo=aes256')
     Which cipher algorithm to use.
    </para>
 <literallayout>
-Values: bf, aes128, aes192, aes256 (OpenSSL-only: <literal>3des</literal>, <literal>cast5</literal>)
+Values: bf, aes128, aes192, aes256 (OpenSSL-only: <literal>3des</literal>, <literal>cast5</literal>; NSS-only: <literal>3des</literal>)
 Default: aes128
 Applies to: pgp_sym_encrypt, pgp_pub_encrypt
 </literallayout>
@@ -1152,8 +1153,8 @@ gen_random_uuid() returns uuid
    <para>
     <filename>pgcrypto</filename> configures itself according to the findings of the
     main PostgreSQL <literal>configure</literal> script.  The options that
-    affect it are <literal>--with-zlib</literal> and
-    <literal>--with-openssl</literal>.
+    affect it are <literal>--with-zlib</literal>,
+    <literal>--with-openssl</literal> and <literal>--with-nss</literal>.
    </para>
 
    <para>
@@ -1168,14 +1169,16 @@ gen_random_uuid() returns uuid
     BIGNUM functions.
    </para>
 
-   <table id="pgcrypto-with-without-openssl">
-    <title>Summary of Functionality with and without OpenSSL</title>
-    <tgroup cols="3">
+   <table id="pgcrypto-with-without-openssl-nss">
+    <title>Summary of Functionality with and without external cryptographic
+      library</title>
+    <tgroup cols="4">
      <thead>
       <row>
        <entry>Functionality</entry>
        <entry>Built-in</entry>
        <entry>With OpenSSL</entry>
+       <entry>With NSS</entry>
       </row>
      </thead>
      <tbody>
@@ -1183,51 +1186,67 @@ gen_random_uuid() returns uuid
        <entry>MD5</entry>
        <entry>yes</entry>
        <entry>yes</entry>
+       <entry>yes</entry>
       </row>
       <row>
        <entry>SHA1</entry>
        <entry>yes</entry>
        <entry>yes</entry>
+       <entry>yes</entry>
       </row>
       <row>
        <entry>SHA224/256/384/512</entry>
        <entry>yes</entry>
        <entry>yes</entry>
+       <entry>yes</entry>
       </row>
       <row>
        <entry>Other digest algorithms</entry>
        <entry>no</entry>
        <entry>yes (Note 1)</entry>
+       <entry>yes (Note 1)</entry>
       </row>
       <row>
        <entry>Blowfish</entry>
        <entry>yes</entry>
        <entry>yes</entry>
+       <entry>yes</entry>
       </row>
       <row>
        <entry>AES</entry>
        <entry>yes</entry>
        <entry>yes</entry>
+       <entry>yes</entry>
       </row>
       <row>
        <entry>DES/3DES/CAST5</entry>
        <entry>no</entry>
        <entry>yes</entry>
+       <entry>yes</entry>
+      </row>
+      <row>
+       <entry>CAST5</entry>
+       <entry>no</entry>
+       <entry>yes</entry>
+       <entry>no</entry>
       </row>
       <row>
        <entry>Raw encryption</entry>
        <entry>yes</entry>
        <entry>yes</entry>
+       <entry>yes</entry>
       </row>
       <row>
        <entry>PGP Symmetric encryption</entry>
        <entry>yes</entry>
        <entry>yes</entry>
+       <entry>yes</entry>
       </row>
       <row>
        <entry>PGP Public-Key encryption</entry>
        <entry>yes</entry>
        <entry>yes</entry>
+       <entry>yes</entry>
       </row>
      </tbody>
     </tgroup>
@@ -1240,7 +1259,8 @@ gen_random_uuid() returns uuid
    <orderedlist>
     <listitem>
      <para>
-      Any digest algorithm <productname>OpenSSL</productname> supports
+      Any digest algorithm <productname>OpenSSL</productname> and
+      <productname>NSS</productname> supports
       is automatically picked up.
       This is not possible with ciphers, which need to be supported
       explicitly.
diff --git a/doc/src/sgml/sslinfo.sgml b/doc/src/sgml/sslinfo.sgml
index e16f61b41d..e8a4e7b332 100644
--- a/doc/src/sgml/sslinfo.sgml
+++ b/doc/src/sgml/sslinfo.sgml
@@ -22,7 +22,8 @@
 
  <para>
   This extension won't build at all unless the installation was
-  configured with <literal>--with-openssl</literal>.
+  configured with SSL support, such as <literal>--with-openssl</literal>
+  or <literal>--with-nss</literal>.
  </para>
 
  <sect2>
@@ -208,6 +209,9 @@ emailAddress
      the X.500 and X.509 standards, so you cannot just assign arbitrary
      meaning to them.
     </para>
+    <para>
+     This function is only available when using <productname>OpenSSL</productname>.
+    </para>
     </listitem>
    </varlistentry>
 
@@ -223,6 +227,9 @@ emailAddress
      Same as <function>ssl_client_dn_field</function>, but for the certificate issuer
      rather than the certificate subject.
     </para>
+    <para>
+     This function is only available when using <productname>OpenSSL</productname>.
+    </para>
     </listitem>
    </varlistentry>
 
@@ -238,6 +245,9 @@ emailAddress
      Provide information about extensions of client certificate: extension name,
      extension value, and if it is a critical extension.
     </para>
+    <para>
+     This function is only available when using <productname>OpenSSL</productname>.
+    </para>
     </listitem>
    </varlistentry>
   </variablelist>
diff --git a/src/tools/msvc/Install.pm b/src/tools/msvc/Install.pm
index ea3af48777..afe72a7e97 100644
--- a/src/tools/msvc/Install.pm
+++ b/src/tools/msvc/Install.pm
@@ -438,7 +438,8 @@ sub CopyContribFiles
 		{
 			# These configuration-based exclusions must match vcregress.pl
 			next if ($d eq "uuid-ossp"  && !defined($config->{uuid}));
-			next if ($d eq "sslinfo"    && !defined($config->{openssl}));
+			next if ($d eq "sslinfo"    && !defined($config->{openssl})
+			  && !defined($config->{nss}));
 			next if ($d eq "xml2"       && !defined($config->{xml}));
 			next if ($d =~ /_plperl$/   && !defined($config->{perl}));
 			next if ($d =~ /_plpython$/ && !defined($config->{python}));
diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm
index 29879b51af..379f861a56 100644
--- a/src/tools/msvc/Mkvcbuild.pm
+++ b/src/tools/msvc/Mkvcbuild.pm
@@ -442,9 +442,14 @@ sub mkvcbuild
 		push @contrib_excludes, 'xml2';
 	}
 
+	if (!$solution->{options}->{openssl} && !$solution->{options}->{nss})
+	{
+		push @contrib_excludes, 'sslinfo';
+	}
+
 	if (!$solution->{options}->{openssl})
 	{
-		push @contrib_excludes, 'sslinfo', 'ssl_passphrase_callback';
+		push @contrib_excludes, 'ssl_passphrase_callback';
 	}
 
 	if (!$solution->{options}->{uuid})
@@ -474,6 +479,11 @@ sub mkvcbuild
 		$pgcrypto->AddFiles('contrib/pgcrypto', 'openssl.c',
 			'pgp-mpi-openssl.c');
 	}
+	elsif ($solution->{options}->{nss})
+	{
+		$pgcrypto->AddFiles('contrib/pgcrypto', 'nss.c',
+			'pgp-mpi-internal.c', 'imath.c', 'blf.c');
+	}
 	else
 	{
 		$pgcrypto->AddFiles(
-- 
2.21.1 (Apple Git-122.3)