From: Daiki Ueno dueno@redhat.com
Signed-off-by: Daiki Ueno dueno@redhat.com --- There was an obvious buffer overrun in mgf1-test.c, which should be fixed in this version. --- Makefile.in | 5 +- mgf1-sha256.c | 47 +++++++++++ mgf1-sha384.c | 47 +++++++++++ mgf1-sha512.c | 47 +++++++++++ mgf1.c | 72 +++++++++++++++++ mgf1.h | 70 ++++++++++++++++ nettle-types.h | 3 + pss-sha256.c | 64 +++++++++++++++ pss-sha512.c | 90 +++++++++++++++++++++ pss.c | 195 +++++++++++++++++++++++++++++++++++++++++++++ pss.h | 105 ++++++++++++++++++++++++ testsuite/.test-rules.make | 6 ++ testsuite/Makefile.in | 4 +- testsuite/mgf1-test.c | 23 ++++++ testsuite/pss-test.c | 35 ++++++++ 15 files changed, 810 insertions(+), 3 deletions(-) create mode 100644 mgf1-sha256.c create mode 100644 mgf1-sha384.c create mode 100644 mgf1-sha512.c create mode 100644 mgf1.c create mode 100644 mgf1.h create mode 100644 pss-sha256.c create mode 100644 pss-sha512.c create mode 100644 pss.c create mode 100644 pss.h create mode 100644 testsuite/mgf1-test.c create mode 100644 testsuite/pss-test.c
diff --git a/Makefile.in b/Makefile.in index 135542f..035074c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -110,6 +110,7 @@ nettle_SOURCES = aes-decrypt-internal.c aes-decrypt.c \ md2.c md2-meta.c md4.c md4-meta.c \ md5.c md5-compress.c md5-compat.c md5-meta.c \ memeql-sec.c memxor.c memxor3.c \ + mgf1.c mgf1-sha256.c mgf1-sha384.c mgf1-sha512.c \ nettle-meta-aeads.c nettle-meta-armors.c \ nettle-meta-ciphers.c nettle-meta-hashes.c \ pbkdf2.c pbkdf2-hmac-sha1.c pbkdf2-hmac-sha256.c \ @@ -144,6 +145,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-sha256.c pss-sha512.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 \ @@ -194,9 +196,10 @@ HEADERS = aes.h arcfour.h arctwo.h asn1.h blowfish.h \ md2.h md4.h \ md5.h md5-compat.h \ memops.h memxor.h \ + mgf1.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 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/mgf1-sha256.c b/mgf1-sha256.c new file mode 100644 index 0000000..11e908c --- /dev/null +++ b/mgf1-sha256.c @@ -0,0 +1,47 @@ +/* mgf1-sha256.c + + PKCS#1 mask generation function 1, based on SHA-256. + + 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 "mgf1.h" + +#include "nettle-meta.h" + +int +mgf1_sha256(const struct sha256_ctx *hash, size_t mask_length, uint8_t *mask) +{ + struct sha256_ctx state; + return mgf1(hash, &state, &nettle_sha256, mask_length, mask); +} diff --git a/mgf1-sha384.c b/mgf1-sha384.c new file mode 100644 index 0000000..5dd2b07 --- /dev/null +++ b/mgf1-sha384.c @@ -0,0 +1,47 @@ +/* mgf1-sha384.c + + PKCS#1 mask generation function 1, based on SHA-384. + + 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 "mgf1.h" + +#include "nettle-meta.h" + +int +mgf1_sha384(const struct sha384_ctx *hash, size_t mask_length, uint8_t *mask) +{ + struct sha384_ctx state; + return mgf1(hash, &state, &nettle_sha384, mask_length, mask); +} diff --git a/mgf1-sha512.c b/mgf1-sha512.c new file mode 100644 index 0000000..d1fba5f --- /dev/null +++ b/mgf1-sha512.c @@ -0,0 +1,47 @@ +/* mgf1-sha512.c + + PKCS#1 mask generation function 1, based on SHA-512. + + 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 "mgf1.h" + +#include "nettle-meta.h" + +int +mgf1_sha512(const struct sha512_ctx *hash, size_t mask_length, uint8_t *mask) +{ + struct sha512_ctx state; + return mgf1(hash, &state, &nettle_sha512, mask_length, mask); +} diff --git a/mgf1.c b/mgf1.c new file mode 100644 index 0000000..a96b086 --- /dev/null +++ b/mgf1.c @@ -0,0 +1,72 @@ +/* mgf1.c + + PKCS#1 mask generation function 1 (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 "mgf1.h" + +#include <assert.h> +#include <string.h> + +#include "nettle-internal.h" + +#define MGF1_MIN(a,b) ((a) < (b) ? (a) : (b)) + +int +mgf1(void *seed, void *state, const struct nettle_hash *hash, + size_t mask_length, uint8_t *mask) +{ + TMP_DECL(h, uint8_t, NETTLE_MAX_HASH_DIGEST_SIZE); + size_t i, blocks; + uint8_t c[4], *p; + + TMP_ALLOC(h, hash->digest_size); + + blocks = (mask_length + hash->digest_size - 1) / hash->digest_size; + for (i = 0, p = mask; i < blocks; i++, p += hash->digest_size) + { + c[0] = (i >> 24) & 0xFF; + c[1] = (i >> 16) & 0xFF; + c[2] = (i >> 8) & 0xFF; + c[3] = i & 0xFF; + + memcpy(state, seed, hash->context_size); + hash->update(state, 4, c); + hash->digest(state, hash->digest_size, h); + memcpy(p, h, MGF1_MIN(hash->digest_size, mask_length - (p - mask))); + } + + return 1; +} diff --git a/mgf1.h b/mgf1.h new file mode 100644 index 0000000..bbaffce --- /dev/null +++ b/mgf1.h @@ -0,0 +1,70 @@ +/* mgf1.h + + PKCS#1 mask generation function 1 (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_MGF1_H_INCLUDED +#define NETTLE_MGF1_H_INCLUDED + +#include "nettle-meta.h" + +#include "sha1.h" +#include "sha2.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Namespace mangling */ +#define mgf1 nettle_mgf1 +#define mgf1_sha256 nettle_mgf1_sha256 +#define mgf1_sha384 nettle_mgf1_sha384 +#define mgf1_sha512 nettle_mgf1_sha512 + +int +mgf1(void *seed, void *state, const struct nettle_hash *hash, + size_t mask_length, uint8_t *mask); + +int +mgf1_sha256(const struct sha256_ctx *hash, size_t mask_length, uint8_t *mask); + +int +mgf1_sha384(const struct sha384_ctx *hash, size_t mask_length, uint8_t *mask); + +int +mgf1_sha512(const struct sha512_ctx *hash, size_t mask_length, uint8_t *mask); + +#ifdef __cplusplus +} +#endif + +#endif /* NETTLE_MGF1_H_INCLUDED */ diff --git a/nettle-types.h b/nettle-types.h index 475937d..b687958 100644 --- a/nettle-types.h +++ b/nettle-types.h @@ -103,6 +103,9 @@ typedef int nettle_armor_decode_update_func(void *ctx,
typedef int nettle_armor_decode_final_func(void *ctx);
+typedef int nettle_mgf_func(void *ctx, size_t mask_length, uint8_t *mask); + + #ifdef __cplusplus } #endif diff --git a/pss-sha256.c b/pss-sha256.c new file mode 100644 index 0000000..9c12037 --- /dev/null +++ b/pss-sha256.c @@ -0,0 +1,64 @@ +/* pss.c + + PKCS#1 RSA-PSS padding, using SHA-256 (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.h" + +int +pss_sha256_encode(mpz_t m, size_t bits, + size_t salt_length, const uint8_t *salt, + const uint8_t *digest) +{ + struct sha256_ctx state; + return pss_encode(m, bits, + &state, &nettle_sha256, + (nettle_mgf_func *) mgf1_sha256, + salt_length, salt, + digest); +} + +int +pss_sha256_verify(mpz_t m, size_t bits, + size_t salt_length, + const uint8_t *digest) +{ + struct sha256_ctx state; + return pss_verify(m, bits, + &state, &nettle_sha256, + (nettle_mgf_func *) mgf1_sha256, + salt_length, + digest); +} diff --git a/pss-sha512.c b/pss-sha512.c new file mode 100644 index 0000000..e24b45d --- /dev/null +++ b/pss-sha512.c @@ -0,0 +1,90 @@ +/* pss.c + + PKCS#1 RSA-PSS padding, using SHA-384 and SHA-512 (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.h" + +int +pss_sha384_encode(mpz_t m, size_t bits, + size_t salt_length, const uint8_t *salt, + const uint8_t *digest) +{ + struct sha384_ctx state; + return pss_encode(m, bits, + &state, &nettle_sha384, + (nettle_mgf_func *) mgf1_sha384, + salt_length, salt, + digest); +} + +int +pss_sha384_verify(mpz_t m, size_t bits, + size_t salt_length, + const uint8_t *digest) +{ + struct sha384_ctx state; + return pss_verify(m, bits, + &state, &nettle_sha384, + (nettle_mgf_func *) mgf1_sha384, + salt_length, + digest); +} + +int +pss_sha512_encode(mpz_t m, size_t bits, + size_t salt_length, const uint8_t *salt, + const uint8_t *digest) +{ + struct sha512_ctx state; + return pss_encode(m, bits, + &state, &nettle_sha512, + (nettle_mgf_func *) mgf1_sha512, + salt_length, salt, + digest); +} + +int +pss_sha512_verify(mpz_t m, size_t bits, + size_t salt_length, + const uint8_t *digest) +{ + struct sha512_ctx state; + return pss_verify(m, bits, + &state, &nettle_sha512, + (nettle_mgf_func *) mgf1_sha512, + salt_length, + digest); +} diff --git a/pss.c b/pss.c new file mode 100644 index 0000000..5dd23a7 --- /dev/null +++ b/pss.c @@ -0,0 +1,195 @@ +/* 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 "bignum.h" +#include "gmp-glue.h" + +#include "memxor.h" +#include "nettle-internal.h" + +static const uint8_t pss_masks[8] = { + 0xFF, 0x7F, 0x3F, 0x1F, 0xF, 0x7, 0x3, 0x1 +}; + +int +pss_encode(mpz_t m, size_t bits, + void *state, const struct nettle_hash *hash, + nettle_mgf_func mgf, + size_t salt_length, const uint8_t *salt, + const uint8_t *digest) +{ + TMP_GMP_DECL(em, uint8_t); + uint8_t pad[8]; + size_t key_size = (bits + 7) / 8; + size_t j; + + TMP_GMP_ALLOC(em, key_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); + + mgf(state, 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; +} + +int +pss_verify(mpz_t m, size_t bits, + void *state, const struct nettle_hash *hash, + nettle_mgf_func mgf, + size_t salt_length, + const uint8_t *digest) +{ + TMP_GMP_DECL(em, uint8_t); + TMP_DECL(h2, uint8_t, NETTLE_MAX_HASH_DIGEST_SIZE); + uint8_t pad[8], *h, *db, *salt; + size_t key_size = (bits + 7) / 8; + size_t j; + + /* 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); + + if (key_size < hash->digest_size + salt_length + 2) + { + TMP_GMP_FREE(em); + return 0; + } + + nettle_mpz_get_str_256(key_size, em, m); + + /* Check the trailer field. */ + if (em[key_size - 1] != 0xbc) + { + TMP_GMP_FREE(em); + return 0; + } + + /* 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) + { + TMP_GMP_FREE(em); + return 0; + } + + /* Compute dbMask. */ + hash->init(state); + hash->update(state, hash->digest_size, h); + + db = em + key_size; + mgf(state, 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) { + TMP_GMP_FREE(em); + return 0; + } + + /* Check the octet right after PS is 0x1. */ + if (db[j] != 0x1) + { + TMP_GMP_FREE(em); + return 0; + } + 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) + { + TMP_GMP_FREE(em); + return 0; + } + + TMP_GMP_FREE(em); + return 1; +} diff --git a/pss.h b/pss.h new file mode 100644 index 0000000..1ebe1eb --- /dev/null +++ b/pss.h @@ -0,0 +1,105 @@ +/* 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" + +#include "mgf1.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Namespace mangling */ +#define pss_encode nettle_pss_encode +#define pss_verify nettle_pss_verify +#define pss_sha256_encode nettle_pss_sha256_encode +#define pss_sha256_verify nettle_pss_sha256_verify +#define pss_sha384_encode nettle_pss_sha384_encode +#define pss_sha384_verify nettle_pss_sha384_verify +#define pss_sha512_encode nettle_pss_sha512_encode +#define pss_sha512_verify nettle_pss_sha512_verify + +int +pss_encode(mpz_t m, size_t bits, + void *state, const struct nettle_hash *hash, + nettle_mgf_func mgf, + size_t salt_length, const uint8_t *salt, + const uint8_t *digest); + +int +pss_verify(mpz_t m, size_t bits, + void *state, const struct nettle_hash *hash, + nettle_mgf_func mgf, + size_t salt_length, + const uint8_t *digest); + +int +pss_sha256_encode(mpz_t m, size_t bits, + size_t salt_length, const uint8_t *salt, + const uint8_t *digest); + +int +pss_sha256_verify(mpz_t m, size_t bits, + size_t salt_length, + const uint8_t *digest); + +int +pss_sha384_encode(mpz_t m, size_t bits, + size_t salt_length, const uint8_t *salt, + const uint8_t *digest); + +int +pss_sha384_verify(mpz_t m, size_t bits, + size_t salt_length, + const uint8_t *digest); + +int +pss_sha512_encode(mpz_t m, size_t bits, + size_t salt_length, const uint8_t *salt, + const uint8_t *digest); + +int +pss_sha512_verify(mpz_t m, size_t bits, + size_t salt_length, + const uint8_t *digest); + +#ifdef __cplusplus +} +#endif + +#endif /* NETTLE_PSS_H_INCLUDED */ diff --git a/testsuite/.test-rules.make b/testsuite/.test-rules.make index b263e1f..2b4499f 100644 --- a/testsuite/.test-rules.make +++ b/testsuite/.test-rules.make @@ -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)
+mgf1-test$(EXEEXT): mgf1-test.$(OBJEXT) + $(LINK) mgf1-test.$(OBJEXT) $(TEST_OBJS) -o 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)
diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in index 689d432..4a9604f 100644 --- a/testsuite/Makefile.in +++ b/testsuite/Makefile.in @@ -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 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/mgf1-test.c b/testsuite/mgf1-test.c new file mode 100644 index 0000000..967be07 --- /dev/null +++ b/testsuite/mgf1-test.c @@ -0,0 +1,23 @@ +#include "testutils.h" +#include "mgf1.h" + +void +test_main(void) +{ + struct sha256_ctx hash; + struct tstring *expected; + uint8_t mask[120]; + int ret; + + expected = SHEX("cf2db1ac9867debdf8ce91f99f141e5544bf26ca36b3fd4f8e4035" + "eec42cab0d46c386ebccef82ba0bb0b095aaa5548b03cdff695187" + "1c6fb505af68af688332f885d324a47d2145a3d8392c37978d7dc9" + "84c95728950c4cf3de6becc59e60ea506951bd40e6de3863095064" + "3ab2edbb47dc66cb54beb2d1"); + + sha256_init(&hash); + sha256_update(&hash, 3, "abc"); + ret = mgf1_sha256(&hash, 120, mask); + ASSERT(ret == 1); + ASSERT(MEMEQ (120, mask, expected->data)); +} diff --git a/testsuite/pss-test.c b/testsuite/pss-test.c new file mode 100644 index 0000000..2d53e03 --- /dev/null +++ b/testsuite/pss-test.c @@ -0,0 +1,35 @@ +#include "testutils.h" + +#include "pss.h" + +void +test_main(void) +{ + struct tstring *salt; + struct tstring *digest; + mpz_t m; + mpz_t expected; + int ret; + + mpz_init(m); + mpz_init(expected); + + salt = SHEX("11223344556677889900"); + /* From sha256-test.c */ + digest = SHEX("ba7816bf8f01cfea 414140de5dae2223" + "b00361a396177a9c b410ff61f20015ad"); + ret = pss_sha256_encode(m, 1024, salt->length, salt->data, digest->data); + ASSERT(ret == 1); + + mpz_set_str(expected, + "76b9a52705c8382c5367732f993184eff340b6305c9f73e7e308c8" + "004fcc15cbbaab01e976bae4b774628595379a2d448a36b3ea6fa8" + "353b97eeea7bdac93b4b7807ac98cd4b3bebfb31f3718e1dd3625f" + "227fbb8696606498e7070e21c3cbbd7386ea20eb81ac7927e0c6d1" + "d7788826a63af767f301bcc05dd65b00da862cbc", 16); + + ASSERT(mpz_cmp(m, expected) == 0); + + ret = pss_sha256_verify(m, 1024, salt->length, digest->data); + ASSERT(ret == 1); +}
From: Daiki Ueno dueno@redhat.com
Signed-off-by: Daiki Ueno dueno@redhat.com --- rsa-pss-sign-tr-test.c now checks SHA-384/SHA-512 variants as well as SHA-256. --- Makefile.in | 2 + nettle.texinfo | 30 ++++ rsa-pss-sha256-sign-tr.c | 64 ++++++++ rsa-pss-sha256-verify.c | 60 +++++++ rsa-pss-sha512-sign-tr.c | 87 +++++++++++ rsa-pss-sha512-verify.c | 79 ++++++++++ rsa-verify.c | 14 ++ rsa.h | 55 +++++++ testsuite/.test-rules.make | 3 + testsuite/Makefile.in | 1 + testsuite/rsa-pss-sign-tr-test.c | 327 +++++++++++++++++++++++++++++++++++++++ 11 files changed, 722 insertions(+) create mode 100644 rsa-pss-sha256-sign-tr.c create mode 100644 rsa-pss-sha256-verify.c create mode 100644 rsa-pss-sha512-sign-tr.c create mode 100644 rsa-pss-sha512-verify.c create mode 100644 testsuite/rsa-pss-sign-tr-test.c
diff --git a/Makefile.in b/Makefile.in index 035074c..2075390 100644 --- a/Makefile.in +++ b/Makefile.in @@ -152,6 +152,8 @@ hogweed_SOURCES = sexp.c sexp-format.c \ rsa-sha1-sign.c rsa-sha1-sign-tr.c rsa-sha1-verify.c \ rsa-sha256-sign.c rsa-sha256-sign-tr.c rsa-sha256-verify.c \ rsa-sha512-sign.c rsa-sha512-sign-tr.c rsa-sha512-verify.c \ + rsa-pss-sha256-sign-tr.c rsa-pss-sha256-verify.c \ + rsa-pss-sha512-sign-tr.c rsa-pss-sha512-verify.c \ rsa-encrypt.c rsa-decrypt.c rsa-decrypt-tr.c \ rsa-keygen.c rsa-blind.c \ rsa2sexp.c sexp2rsa.c \ diff --git a/nettle.texinfo b/nettle.texinfo index 9cfaf43..529a8e6 100644 --- a/nettle.texinfo +++ b/nettle.texinfo @@ -3770,6 +3770,36 @@ of the digest together with an object identifier for the used hash algorithm. @end deftypefun
+While the above functions for the RSA signature operations use the +@cite{PKCS#1} padding scheme, Nettle also provides the variants based on +the PSS padding scheme, specified in @cite{RFC 3447}. + +Creating an RSA signature with the PSS padding scheme is done with one +of the following functions: + +@deftypefun int rsa_pss_sha256_sign_digest_tr(const struct rsa_public_key *@var{pub}, const struct rsa_private_key *@var{key}, void *@var{random_ctx}, nettle_random_func *@var{random}, size_t @var{salt_length}, const uint8_t *@var{salt}, const uint8_t *@var{digest}, mpz_t @var{signature}) +@deftypefunx int rsa_pss_sha384_sign_digest_tr(const struct rsa_public_key *@var{pub}, const struct rsa_private_key *@var{key}, void *@var{random_ctx}, nettle_random_func *@var{random}, size_t @var{salt_length}, const uint8_t *@var{salt}, const uint8_t *@var{digest}, mpz_t @var{signature}) +@deftypefunx int rsa_pss_sha512_sign_digest_tr(const struct rsa_public_key *@var{pub}, const struct rsa_private_key *@var{key}, void *@var{random_ctx}, nettle_random_func *@var{random}, size_t @var{salt_length}, const uint8_t *@var{salt}, const uint8_t *@var{digest}, mpz_t @var{signature}) +Creates a signature using the PSS padding scheme. @var{salt} should +point to a salt string of size @var{salt_length}. @var{digest} should +point to a digest of size @code{SHA256_DIGEST_SIZE}, +@code{SHA384_DIGEST_SIZE}, or @code{SHA512_DIGEST_SIZE}respectively. The +signature is stored in @var{signature} (which must have been +@code{mpz_init}:ed earlier). +Returns one on success, or zero on failure. +@end deftypefun + +Verifying an RSA signature with the PSS padding scheme is done with one +of the following functions: + +@deftypefun int rsa_pss_sha256_verify_digest (const struct rsa_public_key *@var{key}, size_t @var{salt_length}, const uint8_t *@var{digest}, const mpz_t @var{signature}) +@deftypefunx int rsa_pss_sha384_verify_digest (const struct rsa_public_key *@var{key}, size_t @var{salt_length}, const uint8_t *@var{digest}, const mpz_t @var{signature}) +@deftypefunx int rsa_pss_sha512_verify_digest (const struct rsa_public_key *@var{key}, size_t @var{salt_length}, const uint8_t *@var{digest}, const mpz_t @var{signature}) +Returns 1 if the signature is valid, or 0 if it isn't. @var{digest} +should point to a digest of size @code{SHA256_DIGEST_SIZE}, +@code{SHA384_DIGEST_SIZE}, or @code{SHA512_DIGEST_SIZE} respectively. +@end deftypefun + The following function is used to encrypt a clear text message using RSA. @deftypefun int rsa_encrypt (const struct rsa_public_key *@var{key}, void *@var{random_ctx}, nettle_random_func *@var{random}, size_t @var{length}, const uint8_t *@var{cleartext}, mpz_t @var{ciphertext}) Returns 1 on success, 0 on failure. If the message is too long then this diff --git a/rsa-pss-sha256-sign-tr.c b/rsa-pss-sha256-sign-tr.c new file mode 100644 index 0000000..4284fe3 --- /dev/null +++ b/rsa-pss-sha256-sign-tr.c @@ -0,0 +1,64 @@ +/* rsa-pss-sha256-sign-tr.c + + Signatures using RSA and SHA-256, with PSS padding. + + 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 "rsa.h" + +#include "bignum.h" +#include "pss.h" + +int +rsa_pss_sha256_sign_digest_tr(const struct rsa_public_key *pub, + const struct rsa_private_key *key, + void *random_ctx, nettle_random_func *random, + size_t salt_length, const uint8_t *salt, + const uint8_t *digest, + mpz_t s) +{ + mpz_t m; + int res; + + mpz_init (m); + + res = (pss_sha256_encode(m, mpz_sizeinbase(pub->n, 2) - 1, + salt_length, salt, digest) + && rsa_compute_root_tr (pub, key, + random_ctx, random, + s, m)); + + mpz_clear (m); + return res; +} diff --git a/rsa-pss-sha256-verify.c b/rsa-pss-sha256-verify.c new file mode 100644 index 0000000..4bdc07f --- /dev/null +++ b/rsa-pss-sha256-verify.c @@ -0,0 +1,60 @@ +/* rsa-pss-sha256-verify.c + + Verifying signatures created with RSA and SHA-256, with PSS padding. + + 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 "rsa.h" + +#include "bignum.h" +#include "pss.h" + +int +rsa_pss_sha256_verify_digest(const struct rsa_public_key *key, + size_t salt_length, + const uint8_t *digest, + const mpz_t signature) +{ + int res; + mpz_t m; + + mpz_init (m); + + res = (_rsa_verify_recover(key, m, signature) && + pss_sha256_verify(m, mpz_sizeinbase(key->n, 2) - 1, + salt_length, digest)); + + mpz_clear (m); + return res; +} diff --git a/rsa-pss-sha512-sign-tr.c b/rsa-pss-sha512-sign-tr.c new file mode 100644 index 0000000..5a0a5c5 --- /dev/null +++ b/rsa-pss-sha512-sign-tr.c @@ -0,0 +1,87 @@ +/* rsa-pss-sha512-sign-tr.c + + Signatures using RSA and SHA-384/SHA-512, with PSS padding. + + 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 "rsa.h" + +#include "bignum.h" +#include "pss.h" + +int +rsa_pss_sha384_sign_digest_tr(const struct rsa_public_key *pub, + const struct rsa_private_key *key, + void *random_ctx, nettle_random_func *random, + size_t salt_length, const uint8_t *salt, + const uint8_t *digest, + mpz_t s) +{ + mpz_t m; + int res; + + mpz_init (m); + + res = (pss_sha384_encode(m, mpz_sizeinbase(pub->n, 2) - 1, + salt_length, salt, digest) + && rsa_compute_root_tr (pub, key, + random_ctx, random, + s, m)); + + mpz_clear (m); + return res; +} + +int +rsa_pss_sha512_sign_digest_tr(const struct rsa_public_key *pub, + const struct rsa_private_key *key, + void *random_ctx, nettle_random_func *random, + size_t salt_length, const uint8_t *salt, + const uint8_t *digest, + mpz_t s) +{ + mpz_t m; + int res; + + mpz_init (m); + + res = (pss_sha512_encode(m, mpz_sizeinbase(pub->n, 2) - 1, + salt_length, salt, digest) + && rsa_compute_root_tr (pub, key, + random_ctx, random, + s, m)); + + mpz_clear (m); + return res; +} diff --git a/rsa-pss-sha512-verify.c b/rsa-pss-sha512-verify.c new file mode 100644 index 0000000..cd1d59c --- /dev/null +++ b/rsa-pss-sha512-verify.c @@ -0,0 +1,79 @@ +/* rsa-pss-sha512-verify.c + + Verifying signatures created with RSA and SHA-384/SHA-512, with PSS padding. + + 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 "rsa.h" + +#include "bignum.h" +#include "pss.h" + +int +rsa_pss_sha384_verify_digest(const struct rsa_public_key *key, + size_t salt_length, + const uint8_t *digest, + const mpz_t signature) +{ + int res; + mpz_t m; + + mpz_init (m); + + res = (_rsa_verify_recover(key, m, signature) && + pss_sha384_verify(m, mpz_sizeinbase(key->n, 2) - 1, + salt_length, digest)); + + mpz_clear (m); + return res; +} + +int +rsa_pss_sha512_verify_digest(const struct rsa_public_key *key, + size_t salt_length, + const uint8_t *digest, + const mpz_t signature) +{ + int res; + mpz_t m; + + mpz_init (m); + + res = (_rsa_verify_recover(key, m, signature) && + pss_sha512_verify(m, mpz_sizeinbase(key->n, 2) - 1, + salt_length, digest)); + + mpz_clear (m); + return res; +} diff --git a/rsa-verify.c b/rsa-verify.c index 07715e2..43a55d2 100644 --- a/rsa-verify.c +++ b/rsa-verify.c @@ -62,3 +62,17 @@ _rsa_verify(const struct rsa_public_key *key,
return res; } + +int +_rsa_verify_recover(const struct rsa_public_key *key, + mpz_t m, + const mpz_t s) +{ + if ( (mpz_sgn(s) <= 0) + || (mpz_cmp(s, key->n) >= 0) ) + return 0; + + mpz_powm(m, s, key->e, key->n); + + return 1; +} diff --git a/rsa.h b/rsa.h index 6d2574b..2143fcd 100644 --- a/rsa.h +++ b/rsa.h @@ -79,6 +79,12 @@ extern "C" { #define rsa_sha512_sign_digest nettle_rsa_sha512_sign_digest #define rsa_sha512_sign_digest_tr nettle_rsa_sha512_sign_digest_tr #define rsa_sha512_verify_digest nettle_rsa_sha512_verify_digest +#define rsa_pss_sha256_sign_digest_tr nettle_rsa_pss_sha256_sign_digest_tr +#define rsa_pss_sha256_verify_digest nettle_rsa_pss_sha256_verify_digest +#define rsa_pss_sha384_sign_digest_tr nettle_rsa_pss_sha384_sign_digest_tr +#define rsa_pss_sha384_verify_digest nettle_rsa_pss_sha384_verify_digest +#define rsa_pss_sha512_sign_digest_tr nettle_rsa_pss_sha512_sign_digest_tr +#define rsa_pss_sha512_verify_digest nettle_rsa_pss_sha512_verify_digest #define rsa_encrypt nettle_rsa_encrypt #define rsa_decrypt nettle_rsa_decrypt #define rsa_decrypt_tr nettle_rsa_decrypt_tr @@ -93,6 +99,7 @@ extern "C" { #define rsa_keypair_from_der nettle_rsa_keypair_from_der #define rsa_keypair_to_openpgp nettle_rsa_keypair_to_openpgp #define _rsa_verify _nettle_rsa_verify +#define _rsa_verify_recover _nettle_rsa_verify_recover #define _rsa_check_size _nettle_rsa_check_size #define _rsa_blind _nettle_rsa_blind #define _rsa_unblind _nettle_rsa_unblind @@ -341,6 +348,49 @@ rsa_sha512_verify_digest(const struct rsa_public_key *key, const uint8_t *digest, const mpz_t signature);
+/* PSS style signatures */ +int +rsa_pss_sha256_sign_digest_tr(const struct rsa_public_key *pub, + const struct rsa_private_key *key, + void *random_ctx, nettle_random_func *random, + size_t salt_length, const uint8_t *salt, + const uint8_t *digest, + mpz_t s); + +int +rsa_pss_sha256_verify_digest(const struct rsa_public_key *key, + size_t salt_length, + const uint8_t *digest, + const mpz_t signature); + +int +rsa_pss_sha384_sign_digest_tr(const struct rsa_public_key *pub, + const struct rsa_private_key *key, + void *random_ctx, nettle_random_func *random, + size_t salt_length, const uint8_t *salt, + const uint8_t *digest, + mpz_t s); + +int +rsa_pss_sha384_verify_digest(const struct rsa_public_key *key, + size_t salt_length, + const uint8_t *digest, + const mpz_t signature); + +int +rsa_pss_sha512_sign_digest_tr(const struct rsa_public_key *pub, + const struct rsa_private_key *key, + void *random_ctx, nettle_random_func *random, + size_t salt_length, const uint8_t *salt, + const uint8_t *digest, + mpz_t s); + +int +rsa_pss_sha512_verify_digest(const struct rsa_public_key *key, + size_t salt_length, + const uint8_t *digest, + const mpz_t signature); +
/* RSA encryption, using PKCS#1 */ /* These functions uses the v1.5 padding. What should the v2 (OAEP) @@ -480,6 +530,11 @@ _rsa_verify(const struct rsa_public_key *key, const mpz_t m, const mpz_t s);
+int +_rsa_verify_recover(const struct rsa_public_key *key, + mpz_t m, + const mpz_t s); + size_t _rsa_check_size(mpz_t n);
diff --git a/testsuite/.test-rules.make b/testsuite/.test-rules.make index 2b4499f..27c9e00 100644 --- a/testsuite/.test-rules.make +++ b/testsuite/.test-rules.make @@ -187,6 +187,9 @@ pss-test$(EXEEXT): pss-test.$(OBJEXT) rsa-sign-tr-test$(EXEEXT): rsa-sign-tr-test.$(OBJEXT) $(LINK) rsa-sign-tr-test.$(OBJEXT) $(TEST_OBJS) -o rsa-sign-tr-test$(EXEEXT)
+rsa-pss-sign-tr-test$(EXEEXT): rsa-pss-sign-tr-test.$(OBJEXT) + $(LINK) rsa-pss-sign-tr-test.$(OBJEXT) $(TEST_OBJS) -o rsa-pss-sign-tr-test$(EXEEXT) + rsa-test$(EXEEXT): rsa-test.$(OBJEXT) $(LINK) rsa-test.$(OBJEXT) $(TEST_OBJS) -o rsa-test$(EXEEXT)
diff --git a/testsuite/Makefile.in b/testsuite/Makefile.in index 4a9604f..09feb09 100644 --- a/testsuite/Makefile.in +++ b/testsuite/Makefile.in @@ -36,6 +36,7 @@ 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 pss-test.c rsa-sign-tr-test.c \ + rsa-pss-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/rsa-pss-sign-tr-test.c b/testsuite/rsa-pss-sign-tr-test.c new file mode 100644 index 0000000..f14c375 --- /dev/null +++ b/testsuite/rsa-pss-sign-tr-test.c @@ -0,0 +1,327 @@ +#include "testutils.h" +#include "knuth-lfib.h" +#include "nettle-internal.h" + +#define SALT "This is a magic salt" +#define MSG1 "None so blind as those who will not see" +#define MSG2 "Fortune knocks once at every man's door" + +typedef int (*test_pss_sign_tr_func) (const struct rsa_public_key *pub, + const struct rsa_private_key *key, + void *random_ctx, nettle_random_func *random, + size_t salt_length, const uint8_t *salt, + const uint8_t *digest, + mpz_t s); + +typedef int (*test_pss_verify_func) (const struct rsa_public_key *key, + size_t salt_length, + const uint8_t *digest, + const mpz_t signature); + +static void +test_rsa_pss_sign_tr(struct rsa_public_key *pub, + struct rsa_private_key *key, + test_pss_sign_tr_func sign_tr_func, + test_pss_verify_func verify_func, + void *ctx, const struct nettle_hash *hash, + size_t salt_length, const uint8_t *salt, + size_t length, const uint8_t *message, + mpz_t expected) +{ + mpz_t signature; + struct knuth_lfib_ctx lfib; + uint8_t digest[NETTLE_MAX_HASH_DIGEST_SIZE]; + + knuth_lfib_init(&lfib, 1111); + + hash->init(ctx); + hash->update(ctx, length, message); + hash->digest(ctx, hash->digest_size, digest); + + mpz_init(signature); + + mpz_set_ui (signature, 17); + /* Try bad private key */ + mpz_add_ui(key->p, key->p, 2); + + ASSERT(!sign_tr_func(pub, key, + &lfib, (nettle_random_func *) knuth_lfib_random, + salt_length, salt, + digest, signature)); + + mpz_sub_ui(key->p, key->p, 2); + + ASSERT(!mpz_cmp_ui(signature, 17)); + + /* Try the good private key */ + ASSERT(sign_tr_func(pub, key, + &lfib, (nettle_random_func *) knuth_lfib_random, + salt_length, salt, + digest, signature)); + + if (verbose) + { + fprintf(stderr, "rsa-pss-tr signature: "); + mpz_out_str(stderr, 16, signature); + fprintf(stderr, "\nrsa-pss-tr expected: "); + mpz_out_str(stderr, 16, expected); + fprintf(stderr, "\n"); + } + + ASSERT (mpz_cmp(signature, expected) == 0); + + /* Try bad data */ + ASSERT (!verify_func(pub, + salt_length, (void*)"The magick words" "The magick words", + signature)); + + /* Try correct data */ + ASSERT (verify_func(pub, salt_length, digest, signature)); + + /* Try bad signature */ + mpz_combit(signature, 17); + ASSERT (!verify_func(pub, salt_length, digest, signature)); + + mpz_clear(signature); +} + + +void +test_main(void) +{ + struct rsa_public_key pub; + struct rsa_private_key key; + struct sha256_ctx sha256ctx; + struct sha384_ctx sha384ctx; + struct sha512_ctx sha512ctx; + mpz_t p1; + mpz_t q1; + struct tstring *salt; + struct tstring *msg; + + mpz_t expected; + + mpz_init(expected); + + mpz_init(p1); + mpz_init(q1); + + rsa_private_key_init(&key); + rsa_public_key_init(&pub); + + test_rsa_set_key_1(&pub, &key); + + /* Test signatures */ + mpz_set_str(expected, + "25e6ce0cc00e917e177a09cb4dfd843d104c179b71aded60e68ebc" + "ca2cabb1e51502adf28e53fa7ede42619f21a1162755b9658edf88" + "a038bb4fea2bb73306fb384d5785c1a8c98a255277c91a4f88ddd3" + "52ebdc78f71f7e62b7a870dac4ab25f1004453457e831a1572f7c9" + "23fcc48e3b69db582127d14471c7195dce", 16); + + test_rsa_pss_sign_tr(&pub, &key, + rsa_pss_sha256_sign_digest_tr, + rsa_pss_sha256_verify_digest, + &sha256ctx, &nettle_sha256, + LDATA(SALT), LDATA(MSG1), expected); + + mpz_set_str(expected, + "52f4393ccc92b5672dd3cfd8624765d3a4cdb50c7a92060c33b4663" + "fa545b32ce56ec8cd44fe9720df301906ae40921e844b6d80331194" + "972f98e309c937c887c53da940778f29d52dd9489e6016a07e9aa16" + "b1ea8fefc0860ad69068ad6f94a4b0c8fc8a0797b08c58cf4a8df90" + "ee1375feedf7bf73f16ebb2d1cc7e4", 16); + + test_rsa_pss_sign_tr(&pub, &key, + rsa_pss_sha256_sign_digest_tr, + rsa_pss_sha256_verify_digest, + &sha256ctx, &nettle_sha256, + LDATA(SALT), LDATA(MSG2), expected); + + /* 777-bit key, generated by + * + * lsh-keygen -a rsa -l 777 -f advanced-hex + * + * Interesting because the size of n doesn't equal the sum of the + * sizes of p and q. + * + * (private-key (rsa-pkcs1 + * (n #013b04440e3eef25 d51c738d508a7fa8 b3445180c342af0f + * 4cb5a789047300e2 cfc5c5450974cfc2 448aeaaa7f43c374 + * c9a3b038b181f2d1 0f1a2327fd2c087b a49bf1086969fd2c + * d1df3fd69f81fa4b 162cc8bbb363fc95 b7b24b9c53d0c67e + * f52b#) + * (e #3f1a012d#) + * (d #f9bae89dacca6cca c21e0412b4df8355 6fe7c5322bbae8ad + * 3f11494fd12bc076 d4a7da3050fe109d 2074db09cc6a93b4 + * 745479522558379e a0ddfa74f86c9e9e a22c3b0e93d51447 + * 0feb38105dd35395 63b91ee32776f40c 67b2a175690f7abb + * 25#) + * (p #0b73c990eeda0a2a 2c26416052c85560 0c5c0f5ce86a8326 + * 166acea91786237a 7ff884e66dbfdd3a ab9d9801414c1506 + * 8b#) + * (q #1b81c19a62802a41 9c99283331b0badb 08eb0c25ffce0fbf + * 50017850036f32f3 2132a845b91a5236 61f7b451d587383f + * e1#) + * (a #0a912fc93a6cca6b 3521725a3065b3be 3c9745e29c93303d + * 7d29316c6cafa4a2 89945f964fcdea59 1f9d248b0b6734be + * c9#) + * (b #1658eca933251813 1eb19c77aba13d73 e0b8f4ce986d7615 + * 764c6b0b03c18146 46b7f332c43e05c5 351e09006979ca5b + * 05#) + * (c #0114720dace7b27f 2bf2850c1804869f 79a0aad0ec02e6b4 + * 05e1831619db2f10 bb9b6a8fd5c95df2 eb78f303ea0c0cc8 + * 06#))) + */ + + mpz_set_str(pub.n, + "013b04440e3eef25" "d51c738d508a7fa8" "b3445180c342af0f" + "4cb5a789047300e2" "cfc5c5450974cfc2" "448aeaaa7f43c374" + "c9a3b038b181f2d1" "0f1a2327fd2c087b" "a49bf1086969fd2c" + "d1df3fd69f81fa4b" "162cc8bbb363fc95" "b7b24b9c53d0c67e" + "f52b", 16); + + mpz_set_str(pub.e, "3f1a012d", 16); + + ASSERT (rsa_public_key_prepare(&pub)); + + mpz_set_str(key.p, + "0b73c990eeda0a2a" "2c26416052c85560" "0c5c0f5ce86a8326" + "166acea91786237a" "7ff884e66dbfdd3a" "ab9d9801414c1506" + "8b", 16); + + mpz_set_str(key.q, + "1b81c19a62802a41" "9c99283331b0badb" "08eb0c25ffce0fbf" + "50017850036f32f3" "2132a845b91a5236" "61f7b451d587383f" + "e1", 16); + + mpz_set_str(key.a, + "0a912fc93a6cca6b" "3521725a3065b3be" "3c9745e29c93303d" + "7d29316c6cafa4a2" "89945f964fcdea59" "1f9d248b0b6734be" + "c9", 16); + + mpz_set_str(key.b, + "1658eca933251813" "1eb19c77aba13d73" "e0b8f4ce986d7615" + "764c6b0b03c18146" "46b7f332c43e05c5" "351e09006979ca5b" + "05", 16); + + mpz_set_str(key.c, + "0114720dace7b27f" "2bf2850c1804869f" "79a0aad0ec02e6b4" + "05e1831619db2f10" "bb9b6a8fd5c95df2" "eb78f303ea0c0cc8" + "06", 16); + + ASSERT (rsa_private_key_prepare(&key)); + ASSERT (pub.size == key.size); + + /* Test signatures */ + mpz_set_str(expected, + "1a4d28331341cabf7ac85bc59a58d439b7ec2c607c6a74e35b5909" + "1dfa3d9de9fde93e4a431f0f768bec07c39995d253209f86e3dc84" + "037ecd5d23d963fab4fa8a001e018d82cb19d743a94ba7dc7a821e" + "87b72e67a0fe058f956208f7060dc104", 16); + + test_rsa_pss_sign_tr(&pub, &key, + rsa_pss_sha256_sign_digest_tr, + rsa_pss_sha256_verify_digest, + &sha256ctx, &nettle_sha256, + LDATA(SALT), LDATA(MSG1), expected); + + /* From FIPS 186-2 */ + mpz_set_str(pub.n, + "be499b5e7f06c83f" "a0293e31465c8eb6" "b58af920bae52a7b" + "5b9bfeb7aa72db12" "64112eb3fd431d31" "a2a7e50941566929" + "494a0e891ed56139" "18b4b51b0d1fb977" "83b26acf7d0f384c" + "fb35f4d2824f5dd3" "80623a26bf180b63" "961c619dcdb20cae" + "406f22f6e276c80a" "37259490cfeb72c1" "a71a84f1846d3308" + "77ba3e3101ec9c7b" , 16); + + mpz_set_str(pub.e, "11", 16); + + ASSERT (rsa_public_key_prepare(&pub)); + + mpz_set_str(key.p, + "e7a80c5d211c06ac" "b900939495f26d36" "5fc2b4825b75e356" + "f89003eaa5931e6b" "e5c3f7e6a633ad59" "db6289d06c354c23" + "5e739a1e3f3d39fb" "40d1ffb9cb44288f", 16); + + mpz_set_str(key.q, + "d248aa248000f720" "258742da67b71194" "0c8f76e1ecd52b67" + "a6ffe1e49354d66f" "f84fa601804743f5" "838da2ed4693a5a2" + "8658d6528cc1803b" "f6c8dc73c5230b55", 16); + + mpz_set_str(key.d, + "0d0f17362bdad181" "db4e1fe03e8de1a3" "208989914e14bf26" + "9558826bfa20faf4" "b68dba6bb989a01f" "03a21c44665dc5f6" + "48cb5b59b954eb10" "77a80263bd22cdfb" "88d39164b7404f4f" + "1106ee01cf60b776" "95748d8fdaf9fd42" "8963fe75144010b1" + "934c8e26a8823967" "2cf49b3422a07c4d" "834ba208d570fe40" + "8e7095c90547e68d", 16); + + /* a = d % (p-1) */ + mpz_sub_ui(p1, key.p, 1); + mpz_fdiv_r(key.a, key.d, p1); + mpz_clear(p1); + + /* b = d % (q-1) */ + mpz_sub_ui(q1, key.q, 1); + mpz_fdiv_r(key.b, key.d, q1); + mpz_clear(q1); + + /* c = q^{-1} (mod p) */ + mpz_invert(key.c, key.q, key.p); + + ASSERT (rsa_private_key_prepare(&key)); + ASSERT (pub.size == key.size); + + mpz_set_str(expected, + "11e169f2fd40b07641b9768a2ab19965fb6c27f10fcf0323fcc6d1" + "2eb4f1c06b330ddaa1ea504407afa29de9ebe0374fe9d1e7d0ffbd" + "5fc1cf3a3446e4145415d2ab24f789b3464c5c43a256bbc1d692cf" + "7f04801dac5bb401a4a03ab7d5728a860c19e1a4dc797ca542c820" + "3cec2e601eb0c51f567f2eda022b0b9ebddeeefa", 16); + + salt = SHEX("11223344555432167890"); + msg = SHEX("c7f5270fca725f9bd19f519a8d7cca3cc5c079024029f3bae510f9" + "b02140fe238908e4f6c18f07a89c687c8684669b1f1db2baf9251a" + "3c829faccb493084e16ec9e28d58868074a5d6221667dd6e528d16" + "fe2c9f3db4cfaf6c4dce8c8439af38ceaaaa9ce2ecae7bc8f4a5a5" + "5e3bf96df9cd575c4f9cb327951b8cdfe4087168"); + + test_rsa_pss_sign_tr(&pub, &key, + rsa_pss_sha256_sign_digest_tr, + rsa_pss_sha256_verify_digest, + &sha256ctx, &nettle_sha256, + salt->length, salt->data, msg->length, msg->data, + expected); + + mpz_set_str(expected, + "b281ad934b2775c0cba5fb10aa574d2ed85c7f99b942b78e497024" + "80069362ed394baded55e56cfcbe7b0b8d2217a05a60e1acd725cb" + "09060dfac585bc2132b99b41cdbd530c69d17cdbc84bc6b9830fc7" + "dc8e1b2412cfe06dcf8c1a0cc3453f93f25ebf10cb0c90334fac57" + "3f449138616e1a194c67f44efac34cc07a526267", 16); + + test_rsa_pss_sign_tr(&pub, &key, + rsa_pss_sha384_sign_digest_tr, + rsa_pss_sha384_verify_digest, + &sha384ctx, &nettle_sha384, + salt->length, salt->data, msg->length, msg->data, + expected); + mpz_set_str(expected, + "8ffc38f9b820ef6b080fd2ec7de5626c658d79056f3edf610a295b" + "7b0546f73e01ffdf4d0070ebf79c33fd86c2d608be9438b3d420d0" + "9535b97cd3d846ecaf8f6551cdf93197e9f8fb048044473ab41a80" + "1e9f7fc983c62b324361dade9f71a65952bd35c59faaa4d6ff462f" + "68a6c4ec0b428aa47336f2178aeb276136563b7d", 16); + + test_rsa_pss_sign_tr(&pub, &key, + rsa_pss_sha512_sign_digest_tr, + rsa_pss_sha512_verify_digest, + &sha512ctx, &nettle_sha512, + salt->length, salt->data, msg->length, msg->data, + expected); + + rsa_private_key_clear(&key); + rsa_public_key_clear(&pub); + mpz_clear(expected); +}
Daiki Ueno ueno@gnu.org writes:
From: Daiki Ueno dueno@redhat.com
Comments and questions on patch 1/2:
index 135542f..035074c 100644 --- a/Makefile.in +++ b/Makefile.in @@ -110,6 +110,7 @@ nettle_SOURCES = aes-decrypt-internal.c aes-decrypt.c \ md2.c md2-meta.c md4.c md4-meta.c \ md5.c md5-compress.c md5-compat.c md5-meta.c \ memeql-sec.c memxor.c memxor3.c \
nettle-meta-aeads.c nettle-meta-armors.c \ nettle-meta-ciphers.c nettle-meta-hashes.c \ pbkdf2.c pbkdf2-hmac-sha1.c pbkdf2-hmac-sha256.c \mgf1.c mgf1-sha256.c mgf1-sha384.c mgf1-sha512.c \
@@ -144,6 +145,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 \
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 \pss.c pss-sha256.c pss-sha512.c \
@@ -194,9 +196,10 @@ HEADERS = aes.h arcfour.h arctwo.h asn1.h blowfish.h \ md2.h md4.h \ md5.h md5-compat.h \ memops.h memxor.h \
mgf1.h \
mgf1.h is intended as a public, rather than internal, header? Maybe rename to pss-mgf1.h, unless you foresee some non-pss use for it.
index 0000000..11e908c --- /dev/null +++ b/mgf1-sha256.c +int +mgf1_sha256(const struct sha256_ctx *hash, size_t mask_length, uint8_t *mask)
Rename first argument "seed", for consistency with the mgf1 function. "hash" is generally used for struct nettle_hash, using it for a context struct is a bit confusing. (And similarly for the other functions mgf1_sha* functions).
--- /dev/null +++ b/mgf1.c @@ -0,0 +1,72 @@ +/* mgf1.c
+#define MGF1_MIN(a,b) ((a) < (b) ? (a) : (b))
Could consider moving the definition to macros.h or nettle-internal.h (I'm a bit surprised a macro like this isn't defined already, from a quick search, there's only GMP_MIN in mini-gmp.c, which isn't visible here).
+int +mgf1(void *seed, void *state, const struct nettle_hash *hash,
I think seed should be declared as const void *.
size_t mask_length, uint8_t *mask)
+{
- TMP_DECL(h, uint8_t, NETTLE_MAX_HASH_DIGEST_SIZE);
- size_t i, blocks;
- uint8_t c[4], *p;
- TMP_ALLOC(h, hash->digest_size);
- blocks = (mask_length + hash->digest_size - 1) / hash->digest_size;
- for (i = 0, p = mask; i < blocks; i++, p += hash->digest_size)
- {
c[0] = (i >> 24) & 0xFF;
c[1] = (i >> 16) & 0xFF;
c[2] = (i >> 8) & 0xFF;
c[3] = i & 0xFF;
Use the WRITE_UINT32 macro.
memcpy(state, seed, hash->context_size);
hash->update(state, 4, c);
hash->digest(state, hash->digest_size, h);
memcpy(p, h, MGF1_MIN(hash->digest_size, mask_length - (p - mask)));
No need for the second memcpy, just pass the desired length to hash->digest.
Also, I'd consider rewriting the loop to decrement mask_length as you go (and rename it to just length). Then you may also be able to eliminate the blocks variable. You might also want to handle the final iteration specially (there are a couple of ways to do that, not sure what's cleanest), then you get rid of the MIN conditional. You might be able to get some ideas from the pbkdf2 function.
And unless you really need the original value of mask around, I think it makes the code simpler to update it throughout the loop too, and eliminate the extra loop variable p.
--- /dev/null +++ b/pss-sha256.c @@ -0,0 +1,64 @@ +/* pss.c
I admit filenames in this place are of questionable utility. But this one is not correct.
+int +pss_sha256_encode(mpz_t m, size_t bits,
size_t salt_length, const uint8_t *salt,
const uint8_t *digest)
+{
- struct sha256_ctx state;
- return pss_encode(m, bits,
&state, &nettle_sha256,
(nettle_mgf_func *) mgf1_sha256,
Since you pass &nettle_sha256 as an argument here, do you really need the specialized function mgf1_sha256 at all? Couldn't pss_encode use the generic mgf1 directly? If this loss of generality seems like a problem, pss_encode could be renamed to pss_encode_mgf1.
--- /dev/null +++ b/pss.c +int +pss_encode(mpz_t m, size_t bits,
void *state, const struct nettle_hash *hash,
Is state needed by the callers? If not, allocate it locally here (using TMP_ALLOC and hash->context_size. If we need a NETTLE_MAX_HASH_CONTEXT_SIZE, we could add that in some way, preferably as a separate patch with some sanity check which could go in testsuite/meta-hash-test).
nettle_mgf_func mgf,
size_t salt_length, const uint8_t *salt,
const uint8_t *digest)
+{
- TMP_GMP_DECL(em, uint8_t);
- uint8_t pad[8];
- size_t key_size = (bits + 7) / 8;
- size_t j;
- TMP_GMP_ALLOC(em, key_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);
- mgf(state, 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;
+}
+int +pss_verify(mpz_t m, size_t bits,
void *state, const struct nettle_hash *hash,
nettle_mgf_func mgf,
size_t salt_length,
const uint8_t *digest)
+{
It would be nice with some comments summarizing how pss_encode and pss_verify relate.
- /* Check if H' = H. */
- if (memcmp(h2, h, hash->digest_size) != 0)
- {
TMP_GMP_FREE(em);
return 0;
- }
You could add a fail: label and use goto, to avoid repeating this block lots of times. Also there are lots of different ways this function can fail. What are the consequences if one of the falure cases is handled incorrectly, do we need tests for them all?
--- /dev/null +++ b/testsuite/pss-test.c @@ -0,0 +1,35 @@ +#include "testutils.h"
+#include "pss.h"
+void +test_main(void) +{
- struct tstring *salt;
- struct tstring *digest;
- mpz_t m;
- mpz_t expected;
- int ret;
- mpz_init(m);
- mpz_init(expected);
- salt = SHEX("11223344556677889900");
- /* From sha256-test.c */
- digest = SHEX("ba7816bf8f01cfea 414140de5dae2223"
"b00361a396177a9c b410ff61f20015ad");
- ret = pss_sha256_encode(m, 1024, salt->length, salt->data, digest->data);
- ASSERT(ret == 1);
- mpz_set_str(expected,
"76b9a52705c8382c5367732f993184eff340b6305c9f73e7e308c8"
"004fcc15cbbaab01e976bae4b774628595379a2d448a36b3ea6fa8"
"353b97eeea7bdac93b4b7807ac98cd4b3bebfb31f3718e1dd3625f"
"227fbb8696606498e7070e21c3cbbd7386ea20eb81ac7927e0c6d1"
"d7788826a63af767f301bcc05dd65b00da862cbc", 16);
Nice with unit tests for this function too. Thanks! Are there any official test vectors?
- ASSERT(mpz_cmp(m, expected) == 0);
- ret = pss_sha256_verify(m, 1024, salt->length, digest->data);
- ASSERT(ret == 1);
Simpler with just ASSERT(pss_sha256_verify(...));
Some test also for the failure case is desirable. Three reasonably simple checks are to try flipping some bit of m, digest or salt and check that it returns failure.
Regards, /Niels
nisse@lysator.liu.se (Niels Möller) writes:
Thank you for the detailed comments. Please find attached the updated patches.
mgf1.h \
mgf1.h is intended as a public, rather than internal, header? Maybe rename to pss-mgf1.h, unless you foresee some non-pss use for it.
RSA-OAEP could also use it, though I am not sure if it is worth being supported.
index 0000000..11e908c --- /dev/null +++ b/mgf1-sha256.c +int +mgf1_sha256(const struct sha256_ctx *hash, size_t mask_length, uint8_t *mask)
Rename first argument "seed", for consistency with the mgf1 function. "hash" is generally used for struct nettle_hash, using it for a context struct is a bit confusing. (And similarly for the other functions mgf1_sha* functions).
I removed mgf1_sha* functions and made the mgf1 function more generic, based on the suggestion below for pss_sha*_encode. The interface now looks like:
void mgf1(const void *seed, const struct nettle_hash *hash, size_t length, uint8_t *mask);
--- /dev/null +++ b/mgf1.c @@ -0,0 +1,72 @@ +/* mgf1.c
+#define MGF1_MIN(a,b) ((a) < (b) ? (a) : (b))
Could consider moving the definition to macros.h or nettle-internal.h (I'm a bit surprised a macro like this isn't defined already, from a quick search, there's only GMP_MIN in mini-gmp.c, which isn't visible here).
After rewriting the loop, the macro is no longer necessary. I just removed it.
+int +mgf1(void *seed, void *state, const struct nettle_hash *hash,
I think seed should be declared as const void *.
Sure, fixed.
c[0] = (i >> 24) & 0xFF;
c[1] = (i >> 16) & 0xFF;
c[2] = (i >> 8) & 0xFF;
c[3] = i & 0xFF;
Use the WRITE_UINT32 macro.
Done.
memcpy(state, seed, hash->context_size);
hash->update(state, 4, c);
hash->digest(state, hash->digest_size, h);
memcpy(p, h, MGF1_MIN(hash->digest_size, mask_length - (p - mask)));
No need for the second memcpy, just pass the desired length to hash->digest.
Done.
Also, I'd consider rewriting the loop to decrement mask_length as you go (and rename it to just length). Then you may also be able to eliminate the blocks variable. You might also want to handle the final iteration specially (there are a couple of ways to do that, not sure what's cleanest), then you get rid of the MIN conditional. You might be able to get some ideas from the pbkdf2 function.
And unless you really need the original value of mask around, I think it makes the code simpler to update it throughout the loop too, and eliminate the extra loop variable p.
Thank you, those changes made the code much simpler.
--- /dev/null +++ b/pss-sha256.c @@ -0,0 +1,64 @@ +/* pss.c
I admit filenames in this place are of questionable utility. But this one is not correct.
I removed pss-sha*.c, in favor of consolidating them into the generic pss_{encode,verify}_mgf1 functions.
+int +pss_sha256_encode(mpz_t m, size_t bits,
size_t salt_length, const uint8_t *salt,
const uint8_t *digest)
+{
- struct sha256_ctx state;
- return pss_encode(m, bits,
&state, &nettle_sha256,
(nettle_mgf_func *) mgf1_sha256,
Since you pass &nettle_sha256 as an argument here, do you really need the specialized function mgf1_sha256 at all? Couldn't pss_encode use the generic mgf1 directly? If this loss of generality seems like a problem, pss_encode could be renamed to pss_encode_mgf1.
Done.
--- /dev/null +++ b/pss.c +int +pss_encode(mpz_t m, size_t bits,
void *state, const struct nettle_hash *hash,
Is state needed by the callers? If not, allocate it locally here (using TMP_ALLOC and hash->context_size. If we need a NETTLE_MAX_HASH_CONTEXT_SIZE, we could add that in some way, preferably as a separate patch with some sanity check which could go in testsuite/meta-hash-test).
I am attaching a separate patch for this.
It would be nice with some comments summarizing how pss_encode and pss_verify relate.
Done.
- /* Check if H' = H. */
- if (memcmp(h2, h, hash->digest_size) != 0)
- {
TMP_GMP_FREE(em);
return 0;
- }
You could add a fail: label and use goto, to avoid repeating this block lots of times. Also there are lots of different ways this function can fail. What are the consequences if one of the falure cases is handled incorrectly, do we need tests for them all?
--- /dev/null +++ b/testsuite/pss-test.c @@ -0,0 +1,35 @@ +#include "testutils.h"
+#include "pss.h"
+void +test_main(void) +{
- struct tstring *salt;
- struct tstring *digest;
- mpz_t m;
- mpz_t expected;
- int ret;
- mpz_init(m);
- mpz_init(expected);
- salt = SHEX("11223344556677889900");
- /* From sha256-test.c */
- digest = SHEX("ba7816bf8f01cfea 414140de5dae2223"
"b00361a396177a9c b410ff61f20015ad");
- ret = pss_sha256_encode(m, 1024, salt->length, salt->data, digest->data);
- ASSERT(ret == 1);
- mpz_set_str(expected,
"76b9a52705c8382c5367732f993184eff340b6305c9f73e7e308c8"
"004fcc15cbbaab01e976bae4b774628595379a2d448a36b3ea6fa8"
"353b97eeea7bdac93b4b7807ac98cd4b3bebfb31f3718e1dd3625f"
"227fbb8696606498e7070e21c3cbbd7386ea20eb81ac7927e0c6d1"
"d7788826a63af767f301bcc05dd65b00da862cbc", 16);
Nice with unit tests for this function too. Thanks! Are there any official test vectors?
There are (historical?) test vectors from RSA for these intermediate primitives: ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1d2-vec.zip
The reason I didn't use them was that those test vectors use SHA-1 as the underlying hash algorithm, while the previous patch only provides SHA-2 variants of the pss_*_encode() functions.
Now that the generic pss_encode_mgf1() is provided, it is possible to use SHA-1 in the tests, so I just added them.
- ASSERT(mpz_cmp(m, expected) == 0);
- ret = pss_sha256_verify(m, 1024, salt->length, digest->data);
- ASSERT(ret == 1);
Simpler with just ASSERT(pss_sha256_verify(...));
Done.
Some test also for the failure case is desirable. Three reasonably simple checks are to try flipping some bit of m, digest or salt and check that it returns failure.
Done.
Regards,
Daiki Ueno ueno@gnu.org writes:
nisse@lysator.liu.se (Niels Möller) writes:
Thank you for the detailed comments. Please find attached the updated patches.
I hope you're ok if we do this piecewise. Here are comments on some on the pieces.
mgf1.h \
mgf1.h is intended as a public, rather than internal, header? Maybe rename to pss-mgf1.h, unless you foresee some non-pss use for it.
RSA-OAEP could also use it, though I am not sure if it is worth being supported.
Maybe rename it pkcs1-mgf1.h, then? I feel "mgf1.h" is a bit too obscure for a short name.
diff --git a/nettle-internal.h b/nettle-internal.h index 4e3098b..47b35c2 100644 --- a/nettle-internal.h +++ b/nettle-internal.h @@ -54,6 +54,7 @@ /* Arbitrary limits which apply to systems that don't have alloca */ #define NETTLE_MAX_HASH_BLOCK_SIZE 128 #define NETTLE_MAX_HASH_DIGEST_SIZE 64 +#define NETTLE_MAX_HASH_CONTEXT_SIZE 512 #define NETTLE_MAX_SEXP_ASSOC 17 #define NETTLE_MAX_CIPHER_BLOCK_SIZE 32
It's not so nice with a literal constant, since sizes are somewhat platform dependent. I'm considering the patch at the end of this message instead. It uses sizeof(sha3_224_ctx), which turns out to be the largest one by a quite large margin (and 352 bytes, on x86_64). The drawback is that code using this constant needs to include sha3.h to get the size, but I think that's ok for implementation files.
--- /dev/null +++ b/testsuite/pss-test.c @@ -0,0 +1,101 @@ +#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->_mp_alloc, sizeof(m->_mp_alloc));
- VALGRIND_MAKE_MEM_DEFINED (&m->_mp_size, sizeof(m->_mp_size));
- VALGRIND_MAKE_MEM_DEFINED (&m->_mp_d, sizeof(mp_limb_t) * m->_mp_alloc);
^ This looks wrong.
+}
I'd suggest
VALGRIND_MAKE_MEM_DEFINED(m, sizeof(*m)); VALGRIND_MAKE_MEM_DEFINED(m->_mp_d, sizeof(mp_limb_t) * mpz_size(m));
The first is a bit tricky since the mpz_t is a typedef:ed array, I hope I got it right.
--- a/nettle.texinfo +++ b/nettle.texinfo @@ -3770,6 +3770,36 @@ of the digest together with an object identifier for the used hash algorithm. @end deftypefun
+While the above functions for the RSA signature operations use the +@cite{PKCS#1} padding scheme, Nettle also provides the variants based on +the PSS padding scheme, specified in @cite{RFC 3447}.
+Creating an RSA signature with the PSS padding scheme is done with one +of the following functions:
It would be nice if the documentation gave some explanation of the purpose of the salt input, and some guidance on how to select the salt length and contents.
Regards, /Niels
diff --git a/nettle-internal.h b/nettle-internal.h index 4e3098b..9c4c699 100644 --- a/nettle-internal.h +++ b/nettle-internal.h @@ -54,6 +54,7 @@ /* Arbitrary limits which apply to systems that don't have alloca */ #define NETTLE_MAX_HASH_BLOCK_SIZE 128 #define NETTLE_MAX_HASH_DIGEST_SIZE 64 +#define NETTLE_MAX_HASH_CONTEXT_SIZE (sizeof(struct sha3_224_ctx)) #define NETTLE_MAX_SEXP_ASSOC 17 #define NETTLE_MAX_CIPHER_BLOCK_SIZE 32
diff --git a/testsuite/meta-hash-test.c b/testsuite/meta-hash-test.c index 0dcd1b9..f7fa536 100644 --- a/testsuite/meta-hash-test.c +++ b/testsuite/meta-hash-test.c @@ -1,6 +1,8 @@ #include "testutils.h" #include "nettle-internal.h" #include "nettle-meta.h" +/* For NETTLE_MAX_HASH_CONTEXT_SIZE */ +#include "sha3.h"
const char* hashes[] = { "md2", @@ -34,7 +36,8 @@ test_main(void) while (NULL != nettle_hashes[j]) j++; ASSERT(j == count); /* we are not missing testing any hashes */ - for (j = 0; NULL != nettle_hashes[j]; j++) + for (j = 0; NULL != nettle_hashes[j]; j++) { ASSERT(nettle_hashes[j]->digest_size <= NETTLE_MAX_HASH_DIGEST_SIZE); + ASSERT(nettle_hashes[j]->context_size <= NETTLE_MAX_HASH_CONTEXT_SIZE); + } } - diff --git a/tools/nettle-hash.c b/tools/nettle-hash.c index fc991ee..488dff3 100644 --- a/tools/nettle-hash.c +++ b/tools/nettle-hash.c @@ -53,11 +53,11 @@ list_algorithms (void) { unsigned i; const struct nettle_hash *alg; - printf ("%10s digestsize (internal block size), in units of octets\n", "name"); + printf ("%10s digestsize (internal block size, context size), in units of octets\n", "name");
for (i = 0; (alg = nettle_hashes[i]); i++) - printf ("%10s %d (%d)\n", - alg->name, alg->digest_size, alg->block_size); + printf ("%10s %d (%d, %d)\n", + alg->name, alg->digest_size, alg->block_size, alg->context_size); };
static const struct nettle_hash *
Hello,
nisse@lysator.liu.se (Niels Möller) writes:
I hope you're ok if we do this piecewise. Here are comments on some on the pieces.
Sure, I really appreciate that :-)
I have incorporated the suggested changes here: https://gitlab.com/dueno/nettle/commits/wip/dueno/rsa-padding
If you prefer, I can post those to the list.
Maybe rename it pkcs1-mgf1.h, then? I feel "mgf1.h" is a bit too obscure for a short name.
Renamed.
+#define NETTLE_MAX_HASH_CONTEXT_SIZE 512
It's not so nice with a literal constant, since sizes are somewhat platform dependent. I'm considering the patch at the end of this message instead. It uses sizeof(sha3_224_ctx), which turns out to be the largest one by a quite large margin (and 352 bytes, on x86_64). The drawback is that code using this constant needs to include sha3.h to get the size, but I think that's ok for implementation files.
Yes, that seems like a good idea.
I'd suggest
VALGRIND_MAKE_MEM_DEFINED(m, sizeof(*m)); VALGRIND_MAKE_MEM_DEFINED(m->_mp_d, sizeof(mp_limb_t) * mpz_size(m));
The first is a bit tricky since the mpz_t is a typedef:ed array, I hope I got it right.
Fixed, thanks for pointing that out.
+While the above functions for the RSA signature operations use the +@cite{PKCS#1} padding scheme, Nettle also provides the variants based on +the PSS padding scheme, specified in @cite{RFC 3447}.
+Creating an RSA signature with the PSS padding scheme is done with one +of the following functions:
It would be nice if the documentation gave some explanation of the purpose of the salt input, and some guidance on how to select the salt length and contents.
I have added the following:
"These variants take advantage of a randomly choosen salt value, which could enhance the security by causing output to be different for equivalent inputs.
However, assuming the same security level as inverting the @acronym{RSA} algorithm, a longer salt value does not always mean a better security @uref{http://www.iacr.org/archive/eurocrypt2002/23320268/coron.pdf%7D. The typical choices of the length are between 0 and the digest size of the underlying hash function."
Hanno Böck hanno@hboeck.de writes:
I'd recommend adding test cases for key sizes like 2049 bits, 2047 bits that are not divisible by 8. Such keys aren't common, but they do sometimes appear in the wild in TLS certificates. So this could easily lead to bugs that rarely show up and are hard to debug.
Thank you for the suggestion. The current test includes a case for a 777-bit key, though it is self-generated. Would this be sufficient, or is there any test vector for such keys?
By the way, I have added (partial) verification support for gnutls: https://gitlab.com/dueno/gnutls/commits/wip/dueno/rsa-padding
For testing RSA-PSS certificate verification, one can do: $ certtool --verify --load-ca-certificate ca.crt --infile ca.crt
Regards,
Daiki Ueno ueno@gnu.org writes:
I have incorporated the suggested changes here: https://gitlab.com/dueno/nettle/commits/wip/dueno/rsa-padding
If you prefer, I can post those to the list.
I think I can get them from there, I'll post any comments to the list.
nisse@lysator.liu.se (Niels Möller) writes:
It's not so nice with a literal constant, since sizes are somewhat platform dependent. I'm considering the patch at the end of this message instead. It uses sizeof(sha3_224_ctx), which turns out to be the largest one by a quite large margin (and 352 bytes, on x86_64). The drawback is that code using this constant needs to include sha3.h to get the size, but I think that's ok for implementation files.
Yes, that seems like a good idea.
That change now committed. You may want to rebase your branch.
Regards, /Niels
Daiki Ueno ueno@gnu.org writes:
I have incorporated the suggested changes here: https://gitlab.com/dueno/nettle/commits/wip/dueno/rsa-padding
Thanks!
I've added these changes on a branch merge-pss in the main repo, together with some smaller post-merge cleanups.
I'm considering renaming some of the pss files and functions to use a "pkcs1" prefix, and perhaps move declarations to pkcs1.h, do you think that's appropriate?
"These variants take advantage of a randomly choosen salt value, which could enhance the security by causing output to be different for equivalent inputs.
However, assuming the same security level as inverting the @acronym{RSA} algorithm, a longer salt value does not always mean a better security @uref{http://www.iacr.org/archive/eurocrypt2002/23320268/coron.pdf%7D. The typical choices of the length are between 0 and the digest size of the underlying hash function."
That's better, but still not crystal clear. In what scenarios does the salt provide additional security? If the attacker gets to see signatures but not the corresponding messages?
Let me think aloud...
In that scenario, for a small message space, the attacker could try all possible messages to find the message for which the signature is valid, which is an information leakage. But if we also have a large salt space, there can be multiple messages for which the signature is valid. Which is good in this particular attack scenario, but otherwise sounds a bit dangerous...
Or is the salt only intended to hide message repetition, still in the atack scanario where the attacker observes the signatures but not the signed messages?
Regards, /Niels
On Tue, 2017-04-04 at 23:39 +0200, Niels Möller wrote:
"These variants take advantage of a randomly choosen salt value, which could enhance the security by causing output to be different for equivalent inputs.
However, assuming the same security level as inverting the @acronym{RSA} algorithm, a longer salt value does not always mean a better security @uref{http://www.iacr.org/archive/eurocrypt2002/23320268/coron.pdf%7D . The typical choices of the length are between 0 and the digest size of the underlying hash function."
That's better, but still not crystal clear. In what scenarios does the salt provide additional security? If the attacker gets to see signatures but not the corresponding messages?
The salt is needed in the "tight" proof for RSA-PSS, that in the end assures that if RSA-PSS is broken RSA is broken. As far as I understand it is not tied to some concrete attack. The paper above ties that salt size with the total number of signatures generated, and PKCS#1 transforms this to a "security level" question, by tying the salt size to length of the selected hash.
regards, Nikos
Nikos Mavrogiannopoulos nmav@redhat.com writes:
The salt is needed in the "tight" proof for RSA-PSS, that in the end assures that if RSA-PSS is broken RSA is broken. As far as I understand it is not tied to some concrete attack. The paper above ties that salt size with the total number of signatures generated, and PKCS#1 transforms this to a "security level" question, by tying the salt size to length of the selected hash.
Thanks. Is it possible to boil this down to some easy one-size-fits-all recommendation?
Looking at RFC 3447 (I still haven't read it carefully), I don't see any solid recommendation, it says "Typical salt lengths in octets are hLen (the length of the output of the hash function Hash) and 0." (Sec 9.1), and the definition of RSASSA-PSS-Params says "saltLength is the octet length of the salt. It shall be an integer. For a given hashAlgorithm, the default value of saltLength is the octet length of the hash value." (A.2.3)
Is TLS also using salt length == digest size? If so, I think we should recommend that and say that it's what's most widely used.
And using an empty salt seems a bit pointless, then there's no theoretical or practical advantages over pkcs1 v1.5 signatures, right? (See also http://crypto.stackexchange.com/questions/1217/rsa-pss-salt-size).
Regards, /Niels
On Sun, Apr 9, 2017 at 12:13 PM, Niels Möller nisse@lysator.liu.se wrote:
Nikos Mavrogiannopoulos nmav@redhat.com writes:
The salt is needed in the "tight" proof for RSA-PSS, that in the end assures that if RSA-PSS is broken RSA is broken. As far as I understand it is not tied to some concrete attack. The paper above ties that salt size with the total number of signatures generated, and PKCS#1 transforms this to a "security level" question, by tying the salt size to length of the selected hash.
Thanks. Is it possible to boil this down to some easy one-size-fits-all recommendation?
Looking at RFC 3447 (I still haven't read it carefully), I don't see any solid recommendation, it says "Typical salt lengths in octets are hLen
I think the updated pkcs1 2.2 document (rfc8017), has a more solid recommendation. "For a given hashAlgorithm, the default value of saltLength is the octet length of the hash value. Unlike the other fields of type RSASSA-PSS-params, saltLength does not need to be fixed for a given RSA key pair."
Is TLS also using salt length == digest size? If so, I think we should recommend that and say that it's what's most widely used.
I do not remember whether the latest draft had any specific recommendations.
regards, Nikos
nisse@lysator.liu.se (Niels Möller) writes:
I've added these changes on a branch merge-pss in the main repo, together with some smaller post-merge cleanups.
Thank you!
I'm considering renaming some of the pss files and functions to use a "pkcs1" prefix, and perhaps move declarations to pkcs1.h, do you think that's appropriate?
Considering that PSS is part of PKCS#1, I agree to move the declaration to pkcs1.h. On the other hand, some people refer to the PKCS#1 v1.5 padding as "pkcs1", which might cause confusion.
"These variants take advantage of a randomly choosen salt value, which could enhance the security by causing output to be different for equivalent inputs.
However, assuming the same security level as inverting the @acronym{RSA} algorithm, a longer salt value does not always mean a better security @uref{http://www.iacr.org/archive/eurocrypt2002/23320268/coron.pdf%7D. The typical choices of the length are between 0 and the digest size of the underlying hash function."
That's better, but still not crystal clear. In what scenarios does the salt provide additional security? If the attacker gets to see signatures but not the corresponding messages?
I am no expert in math, but having a look at the original paper[1] (section 1.4), I got an impression that introducing salt does not directly correspond to any practical attacks, but is to ensure the same level of security with smaller RSA modulus (than the one required by zero-salt version).
nisse@lysator.liu.se (Niels Möller) writes:
I've tried to actually enable use of this function, by replacing pss_encode_mgf1 by pss_encode_mgf1_for_test below in the pss-test.c file.
I am sorry for forgetting to update the test code after introducing the valgrind check.
It's curious that pkcs#1 v1.5 signatures don't have this particular issue, since v1.5 padding always puts the most significant 1 bit at the same position.
So I guess we'll unfortunately have to take out the valgrind magic from this test.
That's unfortunate, but I can't think of any other idea either.
Footnotes: [1] http://web.cs.ucdavis.edu/~rogaway/papers/exact.pdf
Regards,
Daiki Ueno ueno@gnu.org writes:
nisse@lysator.liu.se (Niels Möller) writes:
I've tried to actually enable use of this function, by replacing pss_encode_mgf1 by pss_encode_mgf1_for_test below in the pss-test.c file.
I am sorry for forgetting to update the test code after introducing the valgrind check.
It's curious that pkcs#1 v1.5 signatures don't have this particular issue, since v1.5 padding always puts the most significant 1 bit at the same position.
So I guess we'll unfortunately have to take out the valgrind magic from this test.
How about the following workaround?
The idea is: the first bytes of EM comes from MGF1, that actually comes from the underlying hash function. In this test, we know that the bytes will never be zero. Thus, clearing the "undefined" client request on the first few bytes of digest would remove the warning.
diff --git a/testsuite/pss-test.c b/testsuite/pss-test.c index 8122655..42f7843 100644 --- a/testsuite/pss-test.c +++ b/testsuite/pss-test.c @@ -2,14 +2,49 @@
#include "pss.h"
+struct nettle_hash nettle_test_sha1; +struct nettle_hash nettle_test_sha256; + #if HAVE_VALGRIND_MEMCHECK_H # include <valgrind/memcheck.h>
+/* Workaround, to prevent false-positive "Conditional jump based on + uninitialized value". mpz_import removes the leading zero limbs + from an mpz value. That logic depends on the salt input. In this + test, however, we know that the first limb of the mpz is not zero, + and the limb comes from the output of the hash function. Clear the + "undefined" request on the first few bytes corresponding to the limb. */ +static void +nettle_test_sha1_digest(struct sha1_ctx *ctx, + size_t length, + uint8_t *digest) +{ + sha1_digest(ctx, length, digest); + + /* If LENGTH equals to the digest size, the output may be placed at + the beginning of the mask generated by pss_mgf1. */ + if (length == SHA1_DIGEST_SIZE) + VALGRIND_MAKE_MEM_DEFINED (digest, sizeof (mp_limb_t)); +} + +static void +nettle_test_sha256_digest(struct sha256_ctx *ctx, + size_t length, + uint8_t *digest) +{ + sha256_digest(ctx, length, digest); + + /* If LENGTH equals to the digest size, the output may be placed at + the beginning of the mask generated by pss_mgf1. */ + if (length == SHA256_DIGEST_SIZE) + VALGRIND_MAKE_MEM_DEFINED (digest, sizeof (mp_limb_t)); +} + 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)); + VALGRIND_MAKE_MEM_DEFINED (m->_mp_d, sizeof(mp_limb_t) * mpz_size(m)); }
static int @@ -27,11 +62,16 @@ pss_encode_mgf1_for_test(mpz_t m, size_t bits,
res = pss_encode_mgf1 (m, bits, hash, salt_length, salt, digest); VALGRIND_MAKE_MEM_DEFINED (&res, sizeof(res)); + + /* DIGEST is reused in the calls to pss_verify_mgf1. */ + VALGRIND_MAKE_MEM_DEFINED (digest, hash->digest_size); test_unmark_mpz (m); return res; } #else #define pss_encode_mgf1_for_test pss_encode_mgf1 +#define nettle_test_sha1_digest nettle_sha1_digest +#define nettle_test_sha256_digest nettle_sha256_digest #endif
void @@ -42,14 +82,20 @@ test_main(void) mpz_t m; mpz_t expected;
+ memcpy(&nettle_test_sha1, &nettle_sha1, sizeof(struct nettle_hash)); + nettle_test_sha1.digest = (nettle_hash_digest_func *) nettle_test_sha1_digest; + + memcpy(&nettle_test_sha256, &nettle_sha256, sizeof(struct nettle_hash)); + nettle_test_sha256.digest = (nettle_hash_digest_func *) nettle_test_sha256_digest; + /* 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)); + ASSERT(pss_encode_mgf1_for_test(m, 1024, &nettle_test_sha1, + salt->length, salt->data, digest->data));
mpz_set_str(expected, "66e4672e836ad121ba244bed6576b867d9a447c28a6e66a5b87dee" @@ -87,13 +133,13 @@ test_main(void)
/* Try bad salt */ salt->data[6] = 0x00; - ASSERT(pss_encode_mgf1(m, 1024, &nettle_sha256, + ASSERT(pss_encode_mgf1(m, 1024, &nettle_test_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, + ASSERT(pss_encode_mgf1(m, 1024, &nettle_test_sha256, salt->length, salt->data, digest->data)); ASSERT(mpz_cmp(m, expected) == 0);
Regards,
nisse@lysator.liu.se (Niels Möller) writes:
Daiki Ueno ueno@gnu.org writes:
I have incorporated the suggested changes here: https://gitlab.com/dueno/nettle/commits/wip/dueno/rsa-padding
Thanks!
I've added these changes on a branch merge-pss in the main repo, together with some smaller post-merge cleanups.
Some late comments.
In testsuite/Makefile.in, pss-mgf1-test.c is listed in TS_NETTLE_SOURCES. Should be moved to TS_HOGWEED_SOURCES, to not get link failured in builds without hogweed. Right?
Alternatively, we could move pss-mgf1.c from libhogweed to libnettle, but that doesn't seem very useful to me.
Both pss_mgf1 and pss_encode_mgf1 allocate the hash context using
TMP_DECL(state, uint8_t, NETTLE_MAX_HASH_CONTEXT_SIZE); ... TMP_ALLOC(state, hash->context_size);
That should work fine if we are using alloca, which provides suitable alignment, but if !HAVE_ALLOCA, we're only guaranteed uint8_t alignment.
Nikos recent SIV patch does something similar. I'm thinking that maybe we need some variant of TMP_DECL/TMP_ALLOC which takes a size in bytes and guarantees alignment of at least uint64_t or so.
Alternatively, can we drop support for compilers lacking alloca, or substitute malloc rather than fixed size stack allocation? In the latter case, we'd need to augment TMP_* facilities with TMP_FREE to deallocate properly in that case (like we already do for TMP_GMP_ALLOC).
Regards, /Niels
nisse@lysator.liu.se (Niels Möller) writes:
In testsuite/Makefile.in, pss-mgf1-test.c is listed in TS_NETTLE_SOURCES. Should be moved to TS_HOGWEED_SOURCES, to not get link failured in builds without hogweed. Right?
Moved now.
Both pss_mgf1 and pss_encode_mgf1 allocate the hash context using
TMP_DECL(state, uint8_t, NETTLE_MAX_HASH_CONTEXT_SIZE); ... TMP_ALLOC(state, hash->context_size);
That should work fine if we are using alloca, which provides suitable alignment, but if !HAVE_ALLOCA, we're only guaranteed uint8_t alignment.
It seems this currently doesn't work at all without alloca. If I delete "#define HAVE_ALLOCA 1 " from config.h, compilation fails with
In file included from /home/nisse/hack/nettle/pss.c:48:0: /home/nisse/hack/nettle/pss.c: In function ‘nettle_pss_encode_mgf1’: /home/nisse/hack/nettle/nettle-internal.h:57:46: error: invalid application of ‘sizeof’ to incomplete type ‘struct sha3_224_ctx’ #define NETTLE_MAX_HASH_CONTEXT_SIZE (sizeof(struct sha3_224_ctx)) ^ /home/nisse/hack/nettle/nettle-internal.h:49:46: note: in definition of macro ‘TMP_DECL’ # define TMP_DECL(name, type, max) type name[max] ^~~ /home/nisse/hack/nettle/pss.c:70:28: note: in expansion of macro ‘NETTLE_MAX_HASH_CONTEXT_SIZE’ TMP_DECL(state, uint8_t, NETTLE_MAX_HASH_CONTEXT_SIZE); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
For now, I think I'll fix this, and add a TMP_ALIGN_DECL, TMP_ALIGN_ALLOC. I still wonder if support for compilers without alloca is relevant?
Regards, /Niels
nisse@lysator.liu.se (Niels Möller) writes:
For now, I think I'll fix this, and add a TMP_ALIGN_DECL, TMP_ALIGN_ALLOC.
Below patch seems to work. Other options?
Regards, /Niels
diff --git a/nettle-internal.h b/nettle-internal.h index 38c8d2a8..b109e944 100644 --- a/nettle-internal.h +++ b/nettle-internal.h @@ -35,20 +35,41 @@ #ifndef NETTLE_INTERNAL_H_INCLUDED #define NETTLE_INTERNAL_H_INCLUDED
+#include <assert.h> + #include "nettle-meta.h"
+/* For definition of NETTLE_MAX_HASH_CONTEXT_SIZE. */ +#include "sha3.h" + /* Temporary allocation, for systems that don't support alloca. Note * that the allocation requests should always be reasonably small, so * that they can fit on the stack. For non-alloca systems, we use a - * fix maximum size, and abort if we ever need anything larger. */ + * fix maximum size + an assert. + * + * TMP_DECL and TMP_ALLOC allocate an array of the given type, and + * take the array size (not byte size) as argument. + * + * TMP_DECL_ALIGN and TMP_ALLOC_ALIGN are intended for context + * structs, with void * pointer, size in bytes, and alignment + * requirements. On systems without alloca, implemented as an array of + * uint64_t, to ensure alignment. Since it is used as void * argument, + * no type casts are needed. + */
#if HAVE_ALLOCA # define TMP_DECL(name, type, max) type *name # define TMP_ALLOC(name, size) (name = alloca(sizeof (*name) * (size))) +# define TMP_DECL_ALIGN(name, max) void *name +# define TMP_ALLOC_ALIGN(name, size) (name = alloca(size)) #else /* !HAVE_ALLOCA */ # define TMP_DECL(name, type, max) type name[max] # define TMP_ALLOC(name, size) \ - do { if ((size) > (sizeof(name) / sizeof(name[0]))) abort(); } while (0) + do { assert((size_t)(size) <= (sizeof(name) / sizeof(name[0]))); } while (0) +# define TMP_DECL_ALIGN(name, max) \ + uint64_t name[((max) + (sizeof(uint64_t) - 1))/ sizeof(uint64_t)] +# define TMP_ALLOC_ALIGN(name, size) \ + do { assert((size_t)(size) <= (sizeof(name))); } while (0) #endif
/* Arbitrary limits which apply to systems that don't have alloca */ diff --git a/pss-mgf1.c b/pss-mgf1.c index 67df5570..3f5e204b 100644 --- a/pss-mgf1.c +++ b/pss-mgf1.c @@ -48,12 +48,12 @@ 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); + TMP_DECL_ALIGN(state, NETTLE_MAX_HASH_CONTEXT_SIZE); size_t i; uint8_t c[4];
TMP_ALLOC(h, hash->digest_size); - TMP_ALLOC(state, hash->context_size); + TMP_ALLOC_ALIGN(state, hash->context_size);
for (i = 0;; i++, mask += hash->digest_size, length -= hash->digest_size) diff --git a/pss.c b/pss.c index 88125c06..d28e7b13 100644 --- a/pss.c +++ b/pss.c @@ -67,12 +67,12 @@ pss_encode_mgf1(mpz_t m, size_t bits, const uint8_t *digest) { TMP_GMP_DECL(em, uint8_t); - TMP_DECL(state, uint8_t, NETTLE_MAX_HASH_CONTEXT_SIZE); + TMP_DECL_ALIGN(state, NETTLE_MAX_HASH_CONTEXT_SIZE); size_t key_size = (bits + 7) / 8; size_t j;
TMP_GMP_ALLOC(em, key_size); - TMP_ALLOC(state, hash->context_size); + TMP_ALLOC_ALIGN(state, hash->context_size);
if (key_size < hash->digest_size + salt_length + 2) { @@ -127,7 +127,7 @@ pss_verify_mgf1(const mpz_t m, size_t bits, { 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); + TMP_DECL_ALIGN(state, NETTLE_MAX_HASH_CONTEXT_SIZE); uint8_t *h, *db, *salt; size_t key_size = (bits + 7) / 8; size_t j; @@ -138,7 +138,7 @@ pss_verify_mgf1(const mpz_t m, size_t bits, TMP_GMP_ALLOC(em, key_size * 2);
TMP_ALLOC(h2, hash->digest_size); - TMP_ALLOC(state, hash->context_size); + TMP_ALLOC_ALIGN(state, hash->context_size);
if (key_size < hash->digest_size + salt_length + 2) goto cleanup;
nisse@lysator.liu.se (Niels Möller) writes:
nisse@lysator.liu.se (Niels Möller) writes:
For now, I think I'll fix this, and add a TMP_ALIGN_DECL, TMP_ALIGN_ALLOC.
Below patch seems to work. Other options?
Replying to myself again, I guess one other option is to drop alloca completely in favor of c99 (and gnuc) variable length arrays. alloca isn't exactly equivalent, but I'm fairly confident that all use of alloca within nettle can be easily replaced with variable length arrays. Two slight drawbacks:
1. It's unfortunately demoted to an optional feature in c11 (see https://en.wikipedia.org/wiki/C11_(C_standard_revision)). I would guess this is unlikely to be a real problem.
2. When allocating a context struct where we only know the size, we'd still need some hack to allocate as a uint64_t array or so, to ensure sufficient alignment. Unlike alloca, which will to whatever is needed with the stack pointer to get same minimum alignment as malloc.
Regards, /Niels
On Sun, 2018-02-18 at 22:30 +0100, Niels Möller wrote:
nisse@lysator.liu.se (Niels Möller) writes:
For now, I think I'll fix this, and add a TMP_ALIGN_DECL, TMP_ALIGN_ALLOC.
Below patch seems to work. Other options?
Regards, /Niels
diff --git a/nettle-internal.h b/nettle-internal.h index 38c8d2a8..b109e944 100644 --- a/nettle-internal.h +++ b/nettle-internal.h @@ -35,20 +35,41 @@ #ifndef NETTLE_INTERNAL_H_INCLUDED #define NETTLE_INTERNAL_H_INCLUDED
+#include <assert.h>
#include "nettle-meta.h"
+/* For definition of NETTLE_MAX_HASH_CONTEXT_SIZE. */ +#include "sha3.h"
/* Temporary allocation, for systems that don't support alloca. Note
- that the allocation requests should always be reasonably small,
so
- that they can fit on the stack. For non-alloca systems, we use a
- fix maximum size, and abort if we ever need anything larger. */
- fix maximum size + an assert.
- TMP_DECL and TMP_ALLOC allocate an array of the given type, and
- take the array size (not byte size) as argument.
- TMP_DECL_ALIGN and TMP_ALLOC_ALIGN are intended for context
- structs, with void * pointer, size in bytes, and alignment
- requirements. On systems without alloca, implemented as an array
of
- uint64_t, to ensure alignment. Since it is used as void *
argument,
- no type casts are needed.
- */
#define ALIGN16(x) \ ((void *)(((ptrdiff_t)(x)+(ptrdiff_t)0x0f)&~((ptrdiff_t)0x0f)))
#if HAVE_ALLOCA # define TMP_DECL(name, type, max) type *name # define TMP_ALLOC(name, size) (name = alloca(sizeof (*name) * (size))) +# define TMP_DECL_ALIGN(name, max) void *name +# define TMP_ALLOC_ALIGN(name, size) (name = alloca(size))
What about this macros (untested, just idea demo):
#if defined(HAVE_ALLOCA) # define TMP_DECL_ALLOC(name, type, max, size) type *name = alloca(sizeof (*name) * (size))) #else if __STDC_VERSION__ >= 199901L # define TMP_DECL_ALLOC(name, type, max, size) \ type _tmp##name[size+16]; \ type *name = ALIGN16(_tmp##name) #else /* fallback for pre-C99 and pre-alloca() times # define TMP_DECL_ALLOC(name, type, max, size) \ type _tmp##name[max+16]; \ type *name = ALIGN16(_tmp##name); \ assert(size <= max) #endif
A more simplified version would be by eliminating the need to calculate max, and this removing support for compilers which don't have alloca() or C99 support (not sure if there are any of these).
regards, Nikos
Nikos Mavrogiannopoulos nmav@redhat.com writes:
#define ALIGN16(x) \ ((void *)(((ptrdiff_t)(x)+(ptrdiff_t)0x0f)&~((ptrdiff_t)0x0f)))
I think I'd prefer allocating a uint64_t array (largest type used in nettle context structs), and leave to the compiler to figure out what alignment is needed and how to get it.
What about this macros (untested, just idea demo):
#if defined(HAVE_ALLOCA) # define TMP_DECL_ALLOC(name, type, max, size) type *name = alloca(sizeof (*name) * (size))) #else if __STDC_VERSION__ >= 199901L # define TMP_DECL_ALLOC(name, type, max, size) \ type _tmp##name[size+16]; \ type *name = ALIGN16(_tmp##name) #else /* fallback for pre-C99 and pre-alloca() times # define TMP_DECL_ALLOC(name, type, max, size) \ type _tmp##name[max+16]; \ type *name = ALIGN16(_tmp##name); \ assert(size <= max) #endif
Not so nice with number of cases increased from two to three. But as you suggest, maybe we don't need the third case.
But I wonder if we can get down to just one case. Which relevant compilers don't support variable length arrays? I'd guess this could be expeted mainly for embedded platforms with either older compilers, or c11 compilers taking advantage of variable-length arrays being optional.
Regards, /Niels
On Mon, 2018-02-19 at 15:27 +0100, Niels Möller wrote:
Nikos Mavrogiannopoulos nmav@redhat.com writes:
#define ALIGN16(x) \ ((void *)(((ptrdiff_t)(x)+(ptrdiff_t)0x0f)&~((ptrdiff_t)0x0f)))
I think I'd prefer allocating a uint64_t array (largest type used in nettle context structs), and leave to the compiler to figure out what alignment is needed and how to get it.
That way you get 8-byte alignment which is ok, but if you use it for aesni key state for example, it results to slower operations.
What about this macros (untested, just idea demo):
#if defined(HAVE_ALLOCA) # define TMP_DECL_ALLOC(name, type, max, size) type *name = alloca(sizeof (*name) * (size))) #else if __STDC_VERSION__ >= 199901L # define TMP_DECL_ALLOC(name, type, max, size) \ type _tmp##name[size+16]; \ type *name = ALIGN16(_tmp##name) #else /* fallback for pre-C99 and pre-alloca() times # define TMP_DECL_ALLOC(name, type, max, size) \ type _tmp##name[max+16]; \ type *name = ALIGN16(_tmp##name); \ assert(size <= max) #endif
Not so nice with number of cases increased from two to three. But as you suggest, maybe we don't need the third case.
But I wonder if we can get down to just one case. Which relevant compilers don't support variable length arrays? I'd guess this could be expeted mainly for embedded platforms with either older compilers, or c11 compilers taking advantage of variable-length arrays being optional.
I don't really know. gnutls requires C99 to work, though I couldn't find any usage of variable arrays on the code.
Said that, variable arrays and alloca() are ok when the input doesn't come externally but I'm not sure if we can enforce that in nettle. What about moving to malloc() unconditionally?
regards, Nikos
Nikos Mavrogiannopoulos nmav@redhat.com writes:
On Mon, 2018-02-19 at 15:27 +0100, Niels Möller wrote:
I think I'd prefer allocating a uint64_t array (largest type used in nettle context structs), and leave to the compiler to figure out what alignment is needed and how to get it.
That way you get 8-byte alignment which is ok, but if you use it for aesni key state for example, it results to slower operations.
My concern now is not performance, but correctness on platforms that require proper data alignment.
But to nevertheless comment about performance, for the benefit of x86_64 simd instructions, I've been thinking about specifying 16-byte alignment for things like union nettle_block16 and aes subkeys, but I'm not aware of any nice and portable way to do that. And if we do it only for some of the possible compilers on a platform, we get an inconsistent ABI.
And I doubt it makes much difference for performance, the bottleneck for AES isn't the reading of subkeys (at least not since recent aesni changes which loads the subkeys into registers outside of the block loop). memxor is the only function in Nettle which is likely to be limited by memory accesses, and it uses its own logic to do aligned accesses.
Speaking of memxor, I wonder if one could take advantage of alignment info propagated by recent versions of gcc, and do something like
#define COMPILE_TIME_ZERO(x) (__builtin_constant_p((x) == 0) && (x) == 0))
inline void memxor (void *src, void *dst, size_t n) { if (COMPILE_TIME_ZERO((intptr_t) src & 7) && COMPILE_TIME_ZERO((intptr_t) dst & 7) && COMPILE_TIME_ZERO(n & 7)) memxor_words(...); else ... }
to skip some runtime alignment checks when alignment can be derived at compile time.
Said that, variable arrays and alloca() are ok when the input doesn't come externally but I'm not sure if we can enforce that in nettle. What about moving to malloc() unconditionally?
The intention is to use TMP_ALLOC only for allocations with small bounds, like digest or block sizes, other small buffers (e.g., 512 bytes used in ctr.c and cbc.c), and in the pss code, hash context sizes.
You fixed a few potentially larger allocations to use TMP_GMP_ALLOC some time ago, iirc.
Regards, /Niels
On Sat, 2018-02-17 at 23:55 +0100, Niels Möller wrote:
nisse@lysator.liu.se (Niels Möller) writes:
Daiki Ueno ueno@gnu.org writes:
I have incorporated the suggested changes here: https://gitlab.com/dueno/nettle/commits/wip/dueno/rsa-padding
Thanks!
I've added these changes on a branch merge-pss in the main repo, together with some smaller post-merge cleanups.
Some late comments.
In testsuite/Makefile.in, pss-mgf1-test.c is listed in TS_NETTLE_SOURCES. Should be moved to TS_HOGWEED_SOURCES, to not get link failured in builds without hogweed. Right?
Alternatively, we could move pss-mgf1.c from libhogweed to libnettle, but that doesn't seem very useful to me.
Both pss_mgf1 and pss_encode_mgf1 allocate the hash context using
TMP_DECL(state, uint8_t, NETTLE_MAX_HASH_CONTEXT_SIZE); ... TMP_ALLOC(state, hash->context_size);
That should work fine if we are using alloca, which provides suitable alignment, but if !HAVE_ALLOCA, we're only guaranteed uint8_t alignment.
What if TMP_ALLOC() allocates 16 bytes more and makes sure it returns a 16-byte aligned buffer? That is, return something passed from:
#define ALIGN16(x) \ ((void *)(((ptrdiff_t)(x)+(ptrdiff_t)0x0f)&~((ptrdiff_t)0x0f)))
(the 16-byte alignment is because that's the worse case alignment needed, e.g., for AESNI keys etc).
Alternatively, can we drop support for compilers lacking alloca, or substitute malloc rather than fixed size stack allocation? In the latter case, we'd need to augment TMP_* facilities with TMP_FREE to deallocate properly in that case (like we already do for TMP_GMP_ALLOC).
What about switching to C99 buffers like: uint8_t buffer[buffer_size]; and fallback to alloca otherwise?
That would still require additional allocation for alignment handling, but it would be standard C. The current macros would also be simplified by such a move.
regards, Nikos
Daiki Ueno ueno@gnu.org writes:
I'd suggest
VALGRIND_MAKE_MEM_DEFINED(m, sizeof(*m)); VALGRIND_MAKE_MEM_DEFINED(m->_mp_d, sizeof(mp_limb_t) * mpz_size(m));
The first is a bit tricky since the mpz_t is a typedef:ed array, I hope I got it right.
Fixed, thanks for pointing that out.
I've tried to actually enable use of this function, by replacing pss_encode_mgf1 by pss_encode_mgf1_for_test below in the pss-test.c file. Turns out it's not truly side-channel silent with respect to the salt and digest inputs. The problem is that the initial bytes of the padded value, em, depend on both digest and salt.
When converting em to an mpz_t, gmp wants internal normalization, so it has to check if the high bits of the number are zero or not, resulting in a branch depending on the input values.
The valgrind failure looks like this:
==23591== Memcheck, a memory error detector ==23591== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al. ==23591== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info ==23591== Command: ./pss-test ==23591== ==23591== Conditional jump or move depends on uninitialised value(s) ==23591== at 0x52C1D01: __gmpz_import (in /usr/lib/x86_64-linux-gnu/libgmp.so.10.2.0) ==23591== by 0x4E414C9: nettle_mpz_set_str_256_u (bignum.c:146) ==23591== by 0x4E42973: nettle_pss_encode_mgf1 (pss.c:124) ==23591== by 0x10B16B: pss_encode_mgf1_for_test.constprop.0 (pss-test.c:28) ==23591== by 0x10B32B: test_main (pss-test.c:51) ==23591== by 0x10AF6E: main (testutils.c:134)
It's curious that pkcs#1 v1.5 signatures don't have this particular issue, since v1.5 padding always puts the most significant 1 bit at the same position.
So I guess we'll unfortunately have to take out the valgrind magic from this test. It's still desirable to test if RSA private key operations are side-channel silent with respect to the private key.
If/when nettle's RSA code is reorganized to use the mpn interface for signatures rather than the mpz interface, normalization would be less of an issue, since most mpn functions tolerate leading zeros.
Regards, /Niels
Hi,
I haven't looked into the code, but just some advice from someone who has previously implemented PSS: It's easy to get the support for unusual keysizes wrong. I'd recommend adding test cases for key sizes like 2049 bits, 2047 bits that are not divisible by 8. Such keys aren't common, but they do sometimes appear in the wild in TLS certificates. So this could easily lead to bugs that rarely show up and are hard to debug.
nettle-bugs@lists.lysator.liu.se