]> git.ipfire.org Git - thirdparty/nettle.git/commitdiff
Implement PSS encoding functions
authorDaiki Ueno <dueno@redhat.com>
Thu, 23 Feb 2017 10:30:27 +0000 (11:30 +0100)
committerDaiki Ueno <dueno@redhat.com>
Tue, 21 Mar 2017 08:43:07 +0000 (09:43 +0100)
Signed-off-by: Daiki Ueno <dueno@redhat.com>
Makefile.in
pss-mgf1.c [new file with mode: 0644]
pss-mgf1.h [new file with mode: 0644]
pss.c [new file with mode: 0644]
pss.h [new file with mode: 0644]
testsuite/.test-rules.make
testsuite/Makefile.in
testsuite/pss-mgf1-test.c [new file with mode: 0644]
testsuite/pss-test.c [new file with mode: 0644]

index 135542f588df50170e29c978dc7d94cc89c75484..0ab77591f823dd5f2789712ce86dc1338f182631 100644 (file)
@@ -144,6 +144,7 @@ hogweed_SOURCES = sexp.c sexp-format.c \
                  pkcs1.c pkcs1-encrypt.c pkcs1-decrypt.c \
                  pkcs1-rsa-digest.c pkcs1-rsa-md5.c pkcs1-rsa-sha1.c \
                  pkcs1-rsa-sha256.c pkcs1-rsa-sha512.c \
+                 pss.c pss-mgf1.c \
                  rsa.c rsa-sign.c rsa-sign-tr.c rsa-verify.c \
                  rsa-pkcs1-sign.c rsa-pkcs1-sign-tr.c rsa-pkcs1-verify.c \
                  rsa-md5-sign.c rsa-md5-sign-tr.c rsa-md5-verify.c \
@@ -196,7 +197,7 @@ HEADERS = aes.h arcfour.h arctwo.h asn1.h blowfish.h \
          memops.h memxor.h \
          nettle-meta.h nettle-types.h \
          pbkdf2.h \
-         pgp.h pkcs1.h realloc.h ripemd160.h rsa.h \
+         pgp.h pkcs1.h pss.h pss-mgf1.h realloc.h ripemd160.h rsa.h \
          salsa20.h sexp.h \
          serpent.h sha.h sha1.h sha2.h sha3.h twofish.h \
          umac.h yarrow.h poly1305.h
diff --git a/pss-mgf1.c b/pss-mgf1.c
new file mode 100644 (file)
index 0000000..67df557
--- /dev/null
@@ -0,0 +1,73 @@
+/* pss-mgf1.c
+
+   PKCS#1 mask generation function 1, used in RSA-PSS (RFC-3447).
+
+   Copyright (C) 2017 Daiki Ueno
+
+   This file is part of GNU Nettle.
+
+   GNU Nettle is free software: you can redistribute it and/or
+   modify it under the terms of either:
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at your
+       option) any later version.
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at your
+       option) any later version.
+
+   or both in parallel, as here.
+
+   GNU Nettle is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see http://www.gnu.org/licenses/.
+*/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "pss-mgf1.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "nettle-internal.h"
+#include "macros.h"
+
+void
+pss_mgf1(const void *seed, const struct nettle_hash *hash,
+        size_t length, uint8_t *mask)
+{
+  TMP_DECL(h, uint8_t, NETTLE_MAX_HASH_DIGEST_SIZE);
+  TMP_DECL(state, uint8_t, NETTLE_MAX_HASH_CONTEXT_SIZE);
+  size_t i;
+  uint8_t c[4];
+
+  TMP_ALLOC(h, hash->digest_size);
+  TMP_ALLOC(state, hash->context_size);
+
+  for (i = 0;;
+       i++, mask += hash->digest_size, length -= hash->digest_size)
+    {
+      WRITE_UINT32(c, i);
+
+      memcpy(state, seed, hash->context_size);
+      hash->update(state, 4, c);
+
+      if (length <= hash->digest_size)
+       {
+         hash->digest(state, length, mask);
+         return;
+       }
+      hash->digest(state, hash->digest_size, mask);
+    }
+}
diff --git a/pss-mgf1.h b/pss-mgf1.h
new file mode 100644 (file)
index 0000000..4a29c10
--- /dev/null
@@ -0,0 +1,58 @@
+/* pss-mgf1.h
+
+   PKCS#1 mask generation function 1, used in RSA-PSS (RFC-3447).
+
+   Copyright (C) 2017 Daiki Ueno
+
+   This file is part of GNU Nettle.
+
+   GNU Nettle is free software: you can redistribute it and/or
+   modify it under the terms of either:
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at your
+       option) any later version.
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at your
+       option) any later version.
+
+   or both in parallel, as here.
+
+   GNU Nettle is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see http://www.gnu.org/licenses/.
+*/
+
+#ifndef NETTLE_PSS_MGF1_H_INCLUDED
+#define NETTLE_PSS_MGF1_H_INCLUDED
+
+#include "nettle-meta.h"
+
+#include "sha1.h"
+#include "sha2.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Namespace mangling */
+#define pss_mgf1 nettle_pss_mgf1
+
+void
+pss_mgf1(const void *seed, const struct nettle_hash *hash,
+        size_t length, uint8_t *mask);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NETTLE_PSS_MGF1_H_INCLUDED */
diff --git a/pss.c b/pss.c
new file mode 100644 (file)
index 0000000..c50f8c8
--- /dev/null
+++ b/pss.c
@@ -0,0 +1,196 @@
+/* pss.c
+
+   PKCS#1 RSA-PSS padding (RFC-3447).
+
+   Copyright (C) 2017 Daiki Ueno
+
+   This file is part of GNU Nettle.
+
+   GNU Nettle is free software: you can redistribute it and/or
+   modify it under the terms of either:
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at your
+       option) any later version.
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at your
+       option) any later version.
+
+   or both in parallel, as here.
+
+   GNU Nettle is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see http://www.gnu.org/licenses/.
+*/
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+#include <string.h>
+
+#include "pss.h"
+#include "pss-mgf1.h"
+
+#include "bignum.h"
+#include "gmp-glue.h"
+
+#include "memxor.h"
+#include "nettle-internal.h"
+
+/* Masks to clear the leftmost N bits.  */
+static const uint8_t pss_masks[8] = {
+  0xFF, 0x7F, 0x3F, 0x1F, 0xF, 0x7, 0x3, 0x1
+};
+
+/* Format the PKCS#1 PSS padding for given salt and digest, using
+ * pss_mgf1() as the mask generation function.
+ *
+ * The encoded messsage is stored in M, and the consistency can be
+ * checked with pss_verify_mgf1(), which takes the encoded message,
+ * the length of salt, and the digest.  */
+int
+pss_encode_mgf1(mpz_t m, size_t bits,
+               const struct nettle_hash *hash,
+               size_t salt_length, const uint8_t *salt,
+               const uint8_t *digest)
+{
+  TMP_GMP_DECL(em, uint8_t);
+  TMP_DECL(state, uint8_t, NETTLE_MAX_HASH_CONTEXT_SIZE);
+  uint8_t pad[8];
+  size_t key_size = (bits + 7) / 8;
+  size_t j;
+
+  TMP_GMP_ALLOC(em, key_size);
+  TMP_ALLOC(state, hash->context_size);
+
+  if (key_size < hash->digest_size + salt_length + 2)
+    {
+      TMP_GMP_FREE(em);
+      return 0;
+    }
+
+  /* Compute M'.  */
+  hash->init(state);
+  memset(pad, 0, 8);
+  hash->update(state, 8, pad);
+  hash->update(state, hash->digest_size, digest);
+  hash->update(state, salt_length, salt);
+
+  /* Store H in EM, right after maskedDB.  */
+  hash->digest(state, hash->digest_size, em + key_size - hash->digest_size - 1);
+
+  /* Compute dbMask.  */
+  hash->init(state);
+  hash->update(state, hash->digest_size, em + key_size - hash->digest_size - 1);
+
+  pss_mgf1(state, hash, key_size - hash->digest_size - 1, em);
+
+  /* Compute maskedDB and store it in front of H in EM.  */
+  for (j = 0; j < key_size - salt_length - hash->digest_size - 2; j++)
+    em[j] ^= 0;
+  em[j++] ^= 1;
+  memxor(em + j, salt, salt_length);
+  j += salt_length;
+
+  /* Store the trailer field following H.  */
+  j += hash->digest_size;
+  *(em + j) = 0xbc;
+
+  /* Clear the leftmost 8 * emLen - emBits of the leftmost octet in EM.  */
+  *em &= pss_masks[(8 * key_size - bits)];
+
+  nettle_mpz_set_str_256_u(m, key_size, em);
+  TMP_GMP_FREE(em);
+  return 1;
+}
+
+/* Check the consistency of given PKCS#1 PSS encoded message, created
+ * with pss_encode_mgf1().
+ *
+ * Returns 1 if the encoded message is consistent, 0 if it is
+ * inconsistent.  */
+int
+pss_verify_mgf1(mpz_t m, size_t bits,
+               const struct nettle_hash *hash,
+               size_t salt_length,
+               const uint8_t *digest)
+{
+  TMP_GMP_DECL(em, uint8_t);
+  TMP_DECL(h2, uint8_t, NETTLE_MAX_HASH_DIGEST_SIZE);
+  TMP_DECL(state, uint8_t, NETTLE_MAX_HASH_CONTEXT_SIZE);
+  uint8_t pad[8], *h, *db, *salt;
+  size_t key_size = (bits + 7) / 8;
+  size_t j;
+  int ret = 0;
+
+  /* Allocate twice the key size to store the intermediate data DB
+   * following the EM value.  */
+  TMP_GMP_ALLOC(em, key_size * 2);
+
+  TMP_ALLOC(h2, hash->digest_size);
+  TMP_ALLOC(state, hash->context_size);
+
+  if (key_size < hash->digest_size + salt_length + 2)
+    goto cleanup;
+
+  nettle_mpz_get_str_256(key_size, em, m);
+
+  /* Check the trailer field.  */
+  if (em[key_size - 1] != 0xbc)
+    goto cleanup;
+
+  /* Extract H.  */
+  h = em + (key_size - hash->digest_size - 1);
+
+  /* Check if the leftmost 8 * emLen - emBits bits of the leftmost
+   * octet of EM are all equal to zero. */
+  if ((*em & ~pss_masks[(8 * key_size - bits)]) != 0)
+    goto cleanup;
+
+  /* Compute dbMask.  */
+  hash->init(state);
+  hash->update(state, hash->digest_size, h);
+
+  db = em + key_size;
+  pss_mgf1(state, hash, key_size - hash->digest_size - 1, db);
+
+  /* Compute DB.  */
+  memxor(db, em, key_size - hash->digest_size - 1);
+
+  *db &= pss_masks[(8 * key_size - bits)];
+  for (j = 0; j < key_size - salt_length - hash->digest_size - 2; j++)
+    if (db[j] != 0)
+      goto cleanup;
+
+  /* Check the octet right after PS is 0x1.  */
+  if (db[j] != 0x1)
+    goto cleanup;
+  salt = db + j + 1;
+
+  /* Compute H'.  */
+  memset(pad, 0, 8);
+  hash->init(state);
+  hash->update(state, 8, pad);
+  hash->update(state, hash->digest_size, digest);
+  hash->update(state, salt_length, salt);
+  hash->digest(state, hash->digest_size, h2);
+
+  /* Check if H' = H.  */
+  if (memcmp(h2, h, hash->digest_size) != 0)
+    goto cleanup;
+
+  ret = 1;
+ cleanup:
+  TMP_GMP_FREE(em);
+  return ret;
+}
diff --git a/pss.h b/pss.h
new file mode 100644 (file)
index 0000000..9733acb
--- /dev/null
+++ b/pss.h
@@ -0,0 +1,65 @@
+/* pss.h
+
+   PKCS#1 RSA-PSS (RFC-3447).
+
+   Copyright (C) 2017 Daiki Ueno
+
+   This file is part of GNU Nettle.
+
+   GNU Nettle is free software: you can redistribute it and/or
+   modify it under the terms of either:
+
+     * the GNU Lesser General Public License as published by the Free
+       Software Foundation; either version 3 of the License, or (at your
+       option) any later version.
+
+   or
+
+     * the GNU General Public License as published by the Free
+       Software Foundation; either version 2 of the License, or (at your
+       option) any later version.
+
+   or both in parallel, as here.
+
+   GNU Nettle is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received copies of the GNU General Public License and
+   the GNU Lesser General Public License along with this program.  If
+   not, see http://www.gnu.org/licenses/.
+*/
+
+#ifndef NETTLE_PSS_H_INCLUDED
+#define NETTLE_PSS_H_INCLUDED
+
+#include "nettle-types.h"
+#include "bignum.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Namespace mangling */
+#define pss_encode_mgf1 nettle_pss_encode_mgf1
+#define pss_verify_mgf1 nettle_pss_verify_mgf1
+
+int
+pss_encode_mgf1(mpz_t m, size_t bits,
+               const struct nettle_hash *hash,
+               size_t salt_length, const uint8_t *salt,
+               const uint8_t *digest);
+
+int
+pss_verify_mgf1(mpz_t m, size_t bits,
+               const struct nettle_hash *hash,
+               size_t salt_length,
+               const uint8_t *digest);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NETTLE_PSS_H_INCLUDED */
index b263e1fdd40e999ed81fa1db9dde8130571a114f..2cb7f7aaff02eaf5c6576eb345ee101f6e830649 100644 (file)
@@ -157,6 +157,9 @@ yarrow-test$(EXEEXT): yarrow-test.$(OBJEXT)
 pbkdf2-test$(EXEEXT): pbkdf2-test.$(OBJEXT)
        $(LINK) pbkdf2-test.$(OBJEXT) $(TEST_OBJS) -o pbkdf2-test$(EXEEXT)
 
+pss-mgf1-test$(EXEEXT): pss-mgf1-test.$(OBJEXT)
+       $(LINK) pss-mgf1-test.$(OBJEXT) $(TEST_OBJS) -o pss-mgf1-test$(EXEEXT)
+
 sexp-test$(EXEEXT): sexp-test.$(OBJEXT)
        $(LINK) sexp-test.$(OBJEXT) $(TEST_OBJS) -o sexp-test$(EXEEXT)
 
@@ -178,6 +181,9 @@ random-prime-test$(EXEEXT): random-prime-test.$(OBJEXT)
 pkcs1-test$(EXEEXT): pkcs1-test.$(OBJEXT)
        $(LINK) pkcs1-test.$(OBJEXT) $(TEST_OBJS) -o pkcs1-test$(EXEEXT)
 
+pss-test$(EXEEXT): pss-test.$(OBJEXT)
+       $(LINK) pss-test.$(OBJEXT) $(TEST_OBJS) -o pss-test$(EXEEXT)
+
 rsa-sign-tr-test$(EXEEXT): rsa-sign-tr-test.$(OBJEXT)
        $(LINK) rsa-sign-tr-test.$(OBJEXT) $(TEST_OBJS) -o rsa-sign-tr-test$(EXEEXT)
 
index 689d4325cc2842e2266ec72c37b3125a7e06a0f4..07058fca702dfdd17c59ad67a9ff5b86f07d638b 100644 (file)
@@ -30,12 +30,12 @@ TS_NETTLE_SOURCES = aes-test.c arcfour-test.c arctwo-test.c \
                    hmac-test.c umac-test.c \
                    meta-hash-test.c meta-cipher-test.c\
                    meta-aead-test.c meta-armor-test.c \
-                   buffer-test.c yarrow-test.c pbkdf2-test.c
+                   buffer-test.c yarrow-test.c pbkdf2-test.c pss-mgf1-test.c
 
 TS_HOGWEED_SOURCES = sexp-test.c sexp-format-test.c \
                     rsa2sexp-test.c sexp2rsa-test.c \
                     bignum-test.c random-prime-test.c \
-                    pkcs1-test.c rsa-sign-tr-test.c \
+                    pkcs1-test.c pss-test.c rsa-sign-tr-test.c \
                     rsa-test.c rsa-encrypt-test.c rsa-keygen-test.c \
                     dsa-test.c dsa-keygen-test.c \
                     curve25519-dh-test.c \
diff --git a/testsuite/pss-mgf1-test.c b/testsuite/pss-mgf1-test.c
new file mode 100644 (file)
index 0000000..640500b
--- /dev/null
@@ -0,0 +1,36 @@
+#include "testutils.h"
+#include "pss-mgf1.h"
+
+void
+test_main(void)
+{
+  struct sha1_ctx sha1ctx;
+  struct sha256_ctx sha256ctx;
+  const struct tstring *seed, *expected;
+  uint8_t mask[120];
+
+  /* From ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1d2-vec.zip */
+  seed = SHEX("df1a896f9d8bc816d97cd7a2c43bad54"
+             "6fbe8cfe");
+  expected = SHEX("66e4672e836ad121ba244bed6576b867d9a447c28a6e66a5b87dee"
+                 "7fbc7e65af5057f86fae8984d9ba7f969ad6fe02a4d75f7445fefd"
+                 "d85b6d3a477c28d24ba1e3756f792dd1dce8ca94440ecb5279ecd3"
+                 "183a311fc89739a96643136e8b0f465e87a4535cd4c59b10028d");
+  sha1_init(&sha1ctx);
+  sha1_update(&sha1ctx, seed->length, seed->data);
+  pss_mgf1(&sha1ctx, &nettle_sha1, expected->length, mask);
+  ASSERT(MEMEQ (expected->length, mask, expected->data));
+
+  /* Test with our own data.  */
+  seed = SDATA("abc");
+  expected = SHEX("cf2db1ac9867debdf8ce91f99f141e5544bf26ca36b3fd4f8e4035"
+                 "eec42cab0d46c386ebccef82ba0bb0b095aaa5548b03cdff695187"
+                 "1c6fb505af68af688332f885d324a47d2145a3d8392c37978d7dc9"
+                 "84c95728950c4cf3de6becc59e60ea506951bd40e6de3863095064"
+                 "3ab2edbb47dc66cb54beb2d1");
+
+  sha256_init(&sha256ctx);
+  sha256_update(&sha256ctx, seed->length, seed->data);
+  pss_mgf1(&sha256ctx, &nettle_sha256, expected->length, mask);
+  ASSERT(MEMEQ (expected->length, mask, expected->data));
+}
diff --git a/testsuite/pss-test.c b/testsuite/pss-test.c
new file mode 100644 (file)
index 0000000..8122655
--- /dev/null
@@ -0,0 +1,118 @@
+#include "testutils.h"
+
+#include "pss.h"
+
+#if HAVE_VALGRIND_MEMCHECK_H
+# include <valgrind/memcheck.h>
+
+static void
+test_unmark_mpz(mpz_t m)
+{
+  VALGRIND_MAKE_MEM_DEFINED (m, sizeof(*m));
+  VALGRIND_MAKE_MEM_DEFINED (&m->_mp_d, sizeof(mp_limb_t) * mpz_size(m));
+}
+
+static int
+pss_encode_mgf1_for_test(mpz_t m, size_t bits,
+                        const struct nettle_hash *hash,
+                        size_t salt_length, const uint8_t *salt,
+                        const uint8_t *digest)
+{
+  int res;
+
+  /* Makes valgrind trigger on any branches depending on the input
+     data. */
+  VALGRIND_MAKE_MEM_UNDEFINED (salt, salt_length);
+  VALGRIND_MAKE_MEM_UNDEFINED (digest, hash->digest_size);
+
+  res = pss_encode_mgf1 (m, bits, hash, salt_length, salt, digest);
+  VALGRIND_MAKE_MEM_DEFINED (&res, sizeof(res));
+  test_unmark_mpz (m);
+  return res;
+}
+#else
+#define pss_encode_mgf1_for_test pss_encode_mgf1
+#endif
+
+void
+test_main(void)
+{
+  struct tstring *salt;
+  struct tstring *digest;
+  mpz_t m;
+  mpz_t expected;
+
+  /* From ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1d2-vec.zip */
+  mpz_init(m);
+  mpz_init(expected);
+
+  salt = SHEX("e3b5d5d002c1bce50c2b65ef88a188d83bce7e61");
+  digest = SHEX("37b66ae0445843353d47ecb0b4fd14c110e62d6a");
+  ASSERT(pss_encode_mgf1(m, 1024, &nettle_sha1,
+                        salt->length, salt->data, digest->data));
+
+  mpz_set_str(expected,
+             "66e4672e836ad121ba244bed6576b867d9a447c28a6e66a5b87dee"
+             "7fbc7e65af5057f86fae8984d9ba7f969ad6fe02a4d75f7445fefd"
+             "d85b6d3a477c28d24ba1e3756f792dd1dce8ca94440ecb5279ecd3"
+             "183a311fc896da1cb39311af37ea4a75e24bdbfd5c1da0de7cecdf"
+             "1a896f9d8bc816d97cd7a2c43bad546fbe8cfebc", 16);
+
+  ASSERT(mpz_cmp(m, expected) == 0);
+
+  mpz_add_ui(m, m, 2);
+  ASSERT(!pss_verify_mgf1(m, 1024, &nettle_sha1, salt->length, digest->data));
+
+  mpz_sub_ui(m, m, 2);
+  ASSERT(pss_verify_mgf1(m, 1024, &nettle_sha1, salt->length, digest->data));
+
+  mpz_clear(m);
+  mpz_clear(expected);
+
+  /* Test with our own data.  */
+  mpz_init(m);
+  mpz_init(expected);
+
+  salt = SHEX("11223344556677889900");
+  /* From sha256-test.c */
+  digest = SHEX("ba7816bf8f01cfea 414140de5dae2223"
+               "b00361a396177a9c b410ff61f20015ad");
+
+  mpz_set_str(expected,
+             "76b9a52705c8382c5367732f993184eff340b6305c9f73e7e308c8"
+             "004fcc15cbbaab01e976bae4b774628595379a2d448a36b3ea6fa8"
+             "353b97eeea7bdac93b4b7807ac98cd4b3bebfb31f3718e1dd3625f"
+             "227fbb8696606498e7070e21c3cbbd7386ea20eb81ac7927e0c6d1"
+             "d7788826a63af767f301bcc05dd65b00da862cbc", 16);
+
+  /* Try bad salt */
+  salt->data[6] = 0x00;
+  ASSERT(pss_encode_mgf1(m, 1024, &nettle_sha256,
+                        salt->length, salt->data, digest->data));
+  ASSERT(mpz_cmp(m, expected) != 0);
+
+  /* Try the good salt */
+  salt->data[6] = 0x77;
+  ASSERT(pss_encode_mgf1(m, 1024, &nettle_sha256,
+                        salt->length, salt->data, digest->data));
+  ASSERT(mpz_cmp(m, expected) == 0);
+
+  /* Try bad message */
+  mpz_add_ui(m, m, 2);
+  ASSERT(!pss_verify_mgf1(m, 1024, &nettle_sha256, salt->length, digest->data));
+
+  /* Try the good message */
+  mpz_sub_ui(m, m, 2);
+  ASSERT(pss_verify_mgf1(m, 1024, &nettle_sha256, salt->length, digest->data));
+
+  /* Try bad digest */
+  digest->data[17] = 0x00;
+  ASSERT(!pss_verify_mgf1(m, 1024, &nettle_sha256, salt->length, digest->data));
+
+  /* Try the good digest */
+  digest->data[17] = 0x03;
+  ASSERT(pss_verify_mgf1(m, 1024, &nettle_sha256, salt->length, digest->data));
+
+  mpz_clear(m);
+  mpz_clear(expected);
+}