]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
PKCS#11 provider: add tests
authorZoltan Fridrich <zfridric@redhat.com>
Thu, 20 Feb 2025 15:01:02 +0000 (16:01 +0100)
committerZoltan Fridrich <zfridric@redhat.com>
Tue, 10 Jun 2025 11:01:13 +0000 (13:01 +0200)
Signed-off-by: Zoltan Fridrich <zfridric@redhat.com>
.gitignore
tests/Makefile.am
tests/pkcs11-provider/pkcs11-provider-cipher.c [new file with mode: 0644]
tests/pkcs11-provider/pkcs11-provider-hmac.c [new file with mode: 0644]
tests/pkcs11-provider/pkcs11-provider-pk.c [new file with mode: 0644]
tests/pkcs11-provider/pkcs11-provider-sig.c [new file with mode: 0644]
tests/pkcs11-provider/test-pkcs11-provider.sh [new file with mode: 0755]

index 3bb2580494b87dd972f0d5dba4081af8db722a54..132016f3b0691716b59f3202a82ca0e8b21fddb5 100644 (file)
@@ -602,6 +602,10 @@ tests/pkcs11/pkcs11-pubkey-import-rsa
 tests/pkcs11/pkcs11-rsa-pss-privkey-test
 tests/pkcs11/tls-neg-pkcs11-key
 tests/pkcs11/tls-neg-pkcs11-no-key
+tests/pkcs11-provider/pkcs11-provider-cipher
+tests/pkcs11-provider/pkcs11-provider-hmac
+tests/pkcs11-provider/pkcs11-provider-pk
+tests/pkcs11-provider/pkcs11-provider-sig
 tests/pkcs12-decode/Makefile
 tests/pkcs12-decode/Makefile.in
 tests/pkcs12_encode
index 6b1f83b81a8e39eb826d56bdc5567b4476c266bb..cfae34e4ff73ceee057b668e4983d1da9a263969 100644 (file)
@@ -577,6 +577,20 @@ dist_check_SCRIPTS += system-override-allow-rsa-pkcs1-encrypt.sh
 indirect_tests += rsaes-pkcs1-v1_5
 rsaes_pkcs1_v1_5_SOURCES = rsaes-pkcs1-v1_5.c
 rsaes_pkcs1_v1_5_LDADD = $(COMMON_GNUTLS_LDADD)
+
+dist_check_SCRIPTS += pkcs11-provider/test-pkcs11-provider.sh
+indirect_tests += pkcs11-provider/pkcs11-provider-pk \
+                 pkcs11-provider/pkcs11-provider-sig \
+                 pkcs11-provider/pkcs11-provider-cipher \
+                 pkcs11-provider/pkcs11-provider-hmac
+pkcs11_provider_pk_SOURCES = pkcs11-provider/pkcs11-provider-pk.c
+pkcs11_provider_sig_SOURCES = pkcs11-provider/pkcs11-provider-sig.c
+pkcs11_provider_cipher_SOURCES = pkcs11-provider/pkcs11-provider-cipher.c
+pkcs11_provider_hmac_SOURCES = pkcs11-provider/pkcs11-provider-hmac.c
+pkcs11_provider_pk_LDADD = $(LDADD)
+pkcs11_provider_sig_LDADD = $(LDADD)
+pkcs11_provider_cipher_LDADD = $(LDADD)
+pkcs11_provider_hmac_LDADD = $(LDADD)
 endif
 
 dist_check_SCRIPTS += gnutls-cli-self-signed.sh gnutls-cli-invalid-crl.sh gnutls-cli-rawpk.sh
diff --git a/tests/pkcs11-provider/pkcs11-provider-cipher.c b/tests/pkcs11-provider/pkcs11-provider-cipher.c
new file mode 100644 (file)
index 0000000..9d1f842
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2025 Red Hat, Inc.
+ *
+ * Author: Zoltan Fridrich
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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 a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+static int test_cipher(const char *alg_str, int alg)
+{
+       int ret;
+       gnutls_cipher_hd_t ch;
+       gnutls_datum_t key, iv;
+       uint8_t key16[64], iv16[32], ptext[128], data[128];
+
+       key.data = key16;
+       key.size = gnutls_cipher_get_key_size(alg);
+       assert(key.size <= sizeof(key16));
+
+       iv.data = iv16;
+       iv.size = gnutls_cipher_get_iv_size(alg);
+       assert(iv.size <= sizeof(iv16));
+
+       memset(iv.data, 0xff, iv.size);
+       memset(key.data, 0xfe, key.size);
+       memset(ptext, 0xfa, sizeof(ptext));
+       memset(data, 0xfa, sizeof(data));
+
+       printf("Testing %s encrypt-decrypt\n", alg_str);
+
+       ret = gnutls_cipher_init(&ch, alg, &key, &iv);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_cipher_init: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       ret = gnutls_cipher_encrypt(ch, data, sizeof(data));
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_cipher_encrypt: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       ret = gnutls_cipher_decrypt(ch, data, sizeof(data));
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_cipher_decrypt: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       if (memcmp(data, ptext, sizeof(ptext)) != 0) {
+               fprintf(stderr, "Decrypted data don't match original\n");
+               return -1;
+       }
+
+       printf("ok\n");
+
+       gnutls_cipher_deinit(ch);
+       return 0;
+}
+
+static int test_aead(const char *alg_str, int alg)
+{
+       int ret;
+       gnutls_aead_cipher_hd_t ch;
+       gnutls_datum_t key, iv;
+       size_t out_len, ctext_len, tag_len;
+       uint8_t key16[64], iv16[32], auth[32], ptext[128];
+       uint8_t ctext[128 + 32] = { 0 };
+       uint8_t out[128] = { 0 };
+
+       key.data = key16;
+       key.size = gnutls_cipher_get_key_size(alg);
+       assert(key.size <= sizeof(key16));
+
+       iv.data = iv16;
+       iv.size = gnutls_cipher_get_iv_size(alg);
+       assert(iv.size <= sizeof(iv16));
+
+       tag_len = gnutls_cipher_get_tag_size(alg);
+
+       memset(iv.data, 0xff, iv.size);
+       memset(key.data, 0xfe, key.size);
+       memset(ptext, 0xfa, sizeof(ptext));
+       memset(auth, 0xfb, sizeof(auth));
+
+       printf("Testing %s AEAD encrypt-decrypt\n", alg_str);
+
+       ret = gnutls_aead_cipher_init(&ch, alg, &key);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_aead_cipher_init: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       ctext_len = sizeof(ctext);
+       ret = gnutls_aead_cipher_encrypt(ch, iv.data, iv.size, auth,
+                                        sizeof(auth), tag_len, ptext,
+                                        sizeof(ptext), ctext, &ctext_len);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_aead_cipher_encrypt: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       if (ctext_len != sizeof(ptext) + tag_len) {
+               fprintf(stderr, "output ciphertext length mismatch\n");
+               return -1;
+       }
+
+       out_len = sizeof(out);
+       ret = gnutls_aead_cipher_decrypt(ch, iv.data, iv.size, auth,
+                                        sizeof(auth), tag_len, ctext,
+                                        ctext_len, out, &out_len);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_aead_cipher_decrypt\n");
+               return ret;
+       }
+
+       if (out_len != sizeof(ptext) ||
+           memcmp(out, ptext, sizeof(ptext)) != 0) {
+               fprintf(stderr, "mismatch of decrypted data\n");
+               return -1;
+       }
+
+       printf("ok\n");
+
+       gnutls_aead_cipher_deinit(ch);
+       return 0;
+}
+
+int main(void)
+{
+       int ret;
+
+       gnutls_global_init();
+
+       ret = test_cipher("aes128-cbc", GNUTLS_CIPHER_AES_128_CBC);
+       if (ret < 0)
+               goto cleanup;
+       ret = test_cipher("aes192-cbc", GNUTLS_CIPHER_AES_192_CBC);
+       if (ret < 0)
+               goto cleanup;
+       ret = test_cipher("aes256-cbc", GNUTLS_CIPHER_AES_256_CBC);
+       if (ret < 0)
+               goto cleanup;
+       ret = test_aead("aes128-gcm", GNUTLS_CIPHER_AES_128_GCM);
+       if (ret < 0)
+               goto cleanup;
+       ret = test_aead("aes192-gcm", GNUTLS_CIPHER_AES_192_GCM);
+       if (ret < 0)
+               goto cleanup;
+       ret = test_aead("aes256-gcm", GNUTLS_CIPHER_AES_256_GCM);
+       if (ret < 0)
+               goto cleanup;
+
+cleanup:
+       gnutls_global_deinit();
+       return ret;
+}
diff --git a/tests/pkcs11-provider/pkcs11-provider-hmac.c b/tests/pkcs11-provider/pkcs11-provider-hmac.c
new file mode 100644 (file)
index 0000000..1d02b76
--- /dev/null
@@ -0,0 +1,451 @@
+/*
+ * Copyright (C) 2025 Red Hat, Inc.
+ *
+ * Author: Zoltan Fridrich
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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 a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+static int test_mac(gnutls_mac_algorithm_t alg, const char *key,
+                   size_t key_size, const char *ptext, size_t ptext_size,
+                   const char *exp_digest)
+{
+       int ret = 0;
+       size_t digest_size = 0;
+       uint8_t *digest = NULL;
+       gnutls_hmac_hd_t hd = NULL;
+
+       digest_size = gnutls_hmac_get_len(alg);
+       digest = gnutls_malloc(digest_size);
+       assert(digest != NULL);
+
+       printf("Testing mac %s\n", gnutls_mac_get_name(alg));
+
+       ret = gnutls_hmac_init(&hd, alg, key, key_size);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hmac_init: %s\n", gnutls_strerror(ret));
+               goto cleanup;
+       }
+
+       ret = gnutls_hmac(hd, ptext, ptext_size / 2);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hmac: %s\n", gnutls_strerror(ret));
+               goto cleanup;
+       }
+
+       ret = gnutls_hmac(hd, ptext + (ptext_size / 2),
+                         ptext_size - (ptext_size / 2));
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hmac: %s\n", gnutls_strerror(ret));
+               goto cleanup;
+       }
+
+       gnutls_hmac_output(hd, digest);
+
+       if (memcmp(digest, exp_digest, digest_size) != 0) {
+               fprintf(stderr, "hmac: digest data don't match\n");
+               ret = -1;
+               goto cleanup;
+       }
+
+       ret = gnutls_hmac_fast(alg, key, key_size, ptext, ptext_size, digest);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hmac_fast: %s\n", gnutls_strerror(ret));
+               goto cleanup;
+       }
+
+       if (memcmp(digest, exp_digest, digest_size) != 0) {
+               fprintf(stderr, "hmac_fast: digest data don't match\n");
+               ret = -1;
+               goto cleanup;
+       }
+
+       printf("ok\n");
+
+cleanup:
+       gnutls_hmac_deinit(hd, NULL);
+       gnutls_free(digest);
+       return ret;
+}
+
+static int test_hash(gnutls_digest_algorithm_t alg, const char *alg_str,
+                    const char *text, size_t text_size, const char *exp_digest)
+{
+       int ret = 0;
+       size_t digest_size = 0;
+       uint8_t *digest = NULL;
+       gnutls_hash_hd_t hd = NULL;
+
+       digest_size = gnutls_hash_get_len(alg);
+       digest = gnutls_malloc(digest_size);
+       assert(digest != NULL);
+
+       printf("Testing hash %s\n", alg_str);
+
+       ret = gnutls_hash_init(&hd, alg);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hash_init: %s\n", gnutls_strerror(ret));
+               goto cleanup;
+       }
+
+       ret = gnutls_hash(hd, text, text_size / 2);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hash: %s\n", gnutls_strerror(ret));
+               goto cleanup;
+       }
+
+       ret = gnutls_hash(hd, text + (text_size / 2),
+                         text_size - (text_size / 2));
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hash: %s\n", gnutls_strerror(ret));
+               goto cleanup;
+       }
+
+       gnutls_hash_output(hd, digest);
+
+       if (memcmp(digest, exp_digest, digest_size) != 0) {
+               fprintf(stderr, "hash: digest data don't match\n");
+               ret = -1;
+               goto cleanup;
+       }
+
+       ret = gnutls_hash_fast(alg, text, text_size, digest);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hash_fast: %s\n", gnutls_strerror(ret));
+               goto cleanup;
+       }
+
+       if (memcmp(digest, exp_digest, digest_size) != 0) {
+               fprintf(stderr, "hash_fast: digest data don't match\n");
+               ret = -1;
+               goto cleanup;
+       }
+
+       printf("ok\n");
+
+cleanup:
+       gnutls_hash_deinit(hd, NULL);
+       gnutls_free(digest);
+       return ret;
+}
+
+/*
+static int test_hkdf(gnutls_mac_algorithm_t mac, const char *ikm_hex,
+                    const char *salt_hex, const char *info_hex, size_t length,
+                    const char *prk_hex, const char *okm_hex)
+{
+       int ret = 0;
+       uint8_t buf[1024];
+       gnutls_datum_t hex, ikm, salt, info, prk, okm;
+
+       printf("Testing HKDF %s\n", gnutls_mac_get_name(mac));
+
+       hex.data = (void *)ikm_hex;
+       hex.size = strlen(ikm_hex);
+       ret = gnutls_hex_decode2(&hex, &ikm);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hex_decode2: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       hex.data = (void *)salt_hex;
+       hex.size = strlen(salt_hex);
+       ret = gnutls_hex_decode2(&hex, &salt);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hex_decode2: %s\n",
+                       gnutls_strerror(ret));
+               gnutls_free(ikm.data);
+               return ret;
+       }
+
+       ret = gnutls_hkdf_extract(mac, &ikm, &salt, buf);
+       gnutls_free(ikm.data);
+       gnutls_free(salt.data);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hkdf_extract: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       prk.data = buf;
+       prk.size = strlen(prk_hex) / 2;
+       ret = gnutls_hex_encode2(&prk, &hex);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hex_encode2: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       if (strcmp((char *)hex.data, prk_hex) != 0) {
+               fprintf(stderr, "HKDF: prk doesn't match: %s != %s\n",
+                       (char *)hex.data, prk_hex);
+               gnutls_free(hex.data);
+               return -1;
+       }
+
+       gnutls_free(hex.data);
+
+       hex.data = (void *)info_hex;
+       hex.size = strlen(info_hex);
+       ret = gnutls_hex_decode2(&hex, &info);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hex_decode2: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+       ret = gnutls_hkdf_expand(mac, &prk, &info, buf, length);
+       gnutls_free(info.data);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hkdf_expand: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       okm.data = buf;
+       okm.size = strlen(okm_hex) / 2;
+       ret = gnutls_hex_encode2(&okm, &hex);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hex_encode2: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       if (strcmp((char *)hex.data, okm_hex) != 0) {
+               fprintf(stderr, "HKDF: okm doesn't match: %s != %s\n",
+                       (char *)hex.data, okm_hex);
+               gnutls_free(hex.data);
+               return -1;
+       }
+
+       gnutls_free(hex.data);
+       return 0;
+}
+
+static int test_pbkdf2(gnutls_mac_algorithm_t mac, const char *ikm_hex,
+                      const char *salt_hex, unsigned iter_count, size_t length,
+                      const char *okm_hex)
+{
+       int ret = 0;
+       uint8_t buf[1024];
+       gnutls_datum_t hex, ikm, salt, okm;
+
+       printf("Testing PBKDF2 %s\n", gnutls_mac_get_name(mac));
+
+       hex.data = (void *)ikm_hex;
+       hex.size = strlen(ikm_hex);
+       ret = gnutls_hex_decode2(&hex, &ikm);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hex_decode2: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       hex.data = (void *)salt_hex;
+       hex.size = strlen(salt_hex);
+       ret = gnutls_hex_decode2(&hex, &salt);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hex_decode2: %s\n",
+                       gnutls_strerror(ret));
+               gnutls_free(ikm.data);
+               return ret;
+       }
+
+       ret = gnutls_pbkdf2(mac, &ikm, &salt, iter_count, buf, length);
+       gnutls_free(ikm.data);
+       gnutls_free(salt.data);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_pbkdf2: %s\n", gnutls_strerror(ret));
+               return ret;
+       }
+
+       okm.data = buf;
+       okm.size = length;
+       ret = gnutls_hex_encode2(&okm, &hex);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_hex_encode2: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       if (strcmp((char *)hex.data, okm_hex) != 0) {
+               fprintf(stderr, "PBKDF2: okm doesn't match: %s != %s\n",
+                       (char *)hex.data, okm_hex);
+               gnutls_free(hex.data);
+               return -1;
+       }
+
+       gnutls_free(hex.data);
+       return 0;
+}
+*/
+
+static int test_macs(void)
+{
+       return test_mac(GNUTLS_MAC_SHA1, "keykeykeykeykeykeyke", 20, "abcdefgh",
+                       8,
+                       "\x30\x7d\xac\x31\x1e\x38\xfa\x70\x92\x30"
+                       "\xdd\x27\x97\x27\x32\x45\x4b\x39\x74\x39") < 0 ||
+              test_mac(GNUTLS_MAC_SHA224, "keykeykeykeykeykeykeykeykeyk", 28,
+                       "abcdefgh", 8,
+                       "\x91\xa1\x39\x7d\x2b\xf7\xcd\xdd\x45\x57"
+                       "\x64\x04\x6f\x65\x89\x96\xd5\x1c\x43\x2b"
+                       "\xe1\x59\x10\xe8\xf4\x65\x1e\x46") < 0 ||
+              test_mac(GNUTLS_MAC_SHA256, "keykeykeykeykeykeykeykeykeykeyke",
+                       32, "abcdefgh", 8,
+                       "\xde\xa4\xbf\x53\x29\x54\x17\xb2\xdb\xb6"
+                       "\x75\x14\x31\x12\x49\x75\xb1\xea\x5f\x59"
+                       "\x5b\x13\x52\x6a\x31\x21\xf4\x93\x93\x38"
+                       "\xfc\x82") < 0 ||
+              test_mac(GNUTLS_MAC_SHA384,
+                       "keykeykeykeykeykeykeykeykeykeykeykeykeykeykeykey", 48,
+                       "abcdefgh", 8,
+                       "\x3a\x83\xdb\x7e\x5d\xdc\x03\x24\x23\x00"
+                       "\x0a\x9e\x8a\x81\x4f\x5b\x52\xb9\x49\x9e"
+                       "\xb1\xf0\x73\xc2\x5c\x9f\xb9\x38\xc4\x99"
+                       "\x13\x80\x22\x6f\x08\xda\x0b\xc0\x56\x40"
+                       "\xf4\x09\x26\x60\xb8\xd5\xec\x74") < 0 ||
+              test_mac(
+                      GNUTLS_MAC_SHA512,
+                      "keykeykeykeykeykeykeykeykeykeykeykeykeykeykeykeykeykeykeykeykeyk",
+                      64, "abcdefgh", 8,
+                      "\xc9\x59\x6c\xd2\x87\x90\x75\xd2\xff\x6a"
+                      "\x55\x57\x0b\x52\xff\xf9\x0b\x44\xe1\x8d"
+                      "\x3f\xec\x5d\xc4\x10\x77\x25\x3b\x60\x6c"
+                      "\x14\x0d\x8c\x8d\x29\x08\xbd\x8c\xcf\x7e"
+                      "\x5a\x18\x90\xc0\x6f\x86\xf4\xd0\xe6\x33"
+                      "\xc2\x93\x59\xbf\x8d\x11\x5a\x2d\xa1\x73"
+                      "\xee\x7c\x82\x67") < 0 ||
+              test_mac(GNUTLS_MAC_AES_CMAC_128, "keykeykeykeykeyk", 16,
+                       "abcdefgh", 8,
+                       "\xbe\x1a\xf8\xa3\xd3\x6e\x0d\xbb\x33\x34"
+                       "\xf8\xc5\xe5\xe0\x11\x35") < 0 ||
+              test_mac(GNUTLS_MAC_AES_CMAC_256,
+                       "keykeykeykeykeykeykeykeykeykeyke", 32, "abcdefgh", 8,
+                       "\x3d\x9c\xe6\xe1\x51\x6f\x17\x86\xb4\x19"
+                       "\x4a\x3d\x30\xa8\x08\xf9") < 0;
+}
+
+static int test_hashes(void)
+{
+       return test_hash(GNUTLS_DIG_SHA1, "sha1", "abcdefgh", 8,
+                        "\x42\x5a\xf1\x2a\x07\x43\x50\x2b\x32\x2e"
+                        "\x93\xa0\x15\xbc\xf8\x68\xe3\x24\xd5\x6a") < 0 ||
+              test_hash(GNUTLS_DIG_SHA224, "sha224", "abcdefgh", 8,
+                        "\x17\xeb\x7d\x40\xf0\x35\x6f\x85\x98\xe8"
+                        "\x9e\xaf\xad\x5f\x6c\x75\x9b\x1f\x82\x29"
+                        "\x75\xd9\xc9\xb7\x37\xc8\xa5\x17") < 0 ||
+              test_hash(GNUTLS_DIG_SHA256, "sha256", "abcdefgh", 8,
+                        "\x9c\x56\xcc\x51\xb3\x74\xc3\xba\x18\x92"
+                        "\x10\xd5\xb6\xd4\xbf\x57\x79\x0d\x35\x1c"
+                        "\x96\xc4\x7c\x02\x19\x0e\xcf\x1e\x43\x06"
+                        "\x35\xab") < 0 ||
+              test_hash(GNUTLS_DIG_SHA384, "sha384", "abcdefgh", 8,
+                        "\x90\x00\xcd\x7c\xad\xa5\x9d\x1d\x2e\xb8"
+                        "\x29\x12\xf7\xf2\x4e\x5e\x69\xcc\x55\x17"
+                        "\xf6\x82\x83\xb0\x05\xfa\x27\xc2\x85\xb6"
+                        "\x1e\x05\xed\xf1\xad\x1a\x8a\x9b\xde\xd6"
+                        "\xfd\x29\xeb\x87\xd7\x5a\xd8\x06") < 0 ||
+              test_hash(GNUTLS_DIG_SHA512, "sha512", "abcdefgh", 8,
+                        "\xa3\xa8\xc8\x1b\xc9\x7c\x25\x60\x01\x0d"
+                        "\x73\x89\xbc\x88\xaa\xc9\x74\xa1\x04\xe0"
+                        "\xe2\x38\x12\x20\xc6\xe0\x84\xc4\xdc\xcd"
+                        "\x1d\x2d\x17\xd4\xf8\x6d\xb3\x1c\x2a\x85"
+                        "\x1d\xc8\x0e\x66\x81\xd7\x47\x33\xc5\x5d"
+                        "\xcd\x03\xdd\x96\xf6\x06\x2c\xdd\xa1\x2a"
+                        "\x29\x1a\xe6\xce") < 0;
+       /*
+              test_hash(GNUTLS_DIG_SHA3_224, "sha3-224",
+                        "abcdefgh", 8,
+                        "\x48\xbf\x2e\x86\x40\xcf\xfe\x77\xb6\x7c"
+                        "\x61\x82\xa6\xa4\x7f\x8b\x5a\xf7\x3f\x60"
+                        "\xbd\x20\x4e\xf3\x48\x37\x1d\x03") < 0 ||
+              test_hash(GNUTLS_DIG_SHA3_256, "sha3-256",
+                        "abcdefgh", 8,
+                        "\x3e\x20\x20\x72\x5a\x38\xa4\x8e\xb3\xbb"
+                        "\xf7\x57\x67\xf0\x3a\x22\xc6\xb3\xf4\x1f"
+                        "\x45\x9c\x83\x13\x09\xb0\x64\x33\xec\x64"
+                        "\x97\x79") < 0 ||
+              test_hash(GNUTLS_DIG_SHA3_384, "sha3-384",
+                        "abcdefgh", 8,
+                        "\xf4\xd9\xfc\x5e\x9f\x44\xeb\x87\xfe\x96"
+                        "\x8f\xc8\xe4\xe4\x69\x1e\xb1\xda\xb6\xd8"
+                        "\x21\xfb\x77\x55\x0b\x52\x7f\x71\xcc\xfb"
+                        "\x1b\xa0\x43\x85\x1b\xb0\x54\xf2\x81\x36"
+                        "\x4c\x44\xd8\x54\x19\x04\xdb\x5a") < 0 ||
+              test_hash(GNUTLS_DIG_SHA3_512, "sha3-512",
+                        "abcdefgh", 8,
+                        "\xc9\xf2\x5e\xee\x75\xab\x4c\xf9\xa8\xcf"
+                        "\xd4\x4f\x49\x92\xb2\x82\x07\x9b\x64\xd9"
+                        "\x46\x47\xed\xbd\x88\xe8\x18\xe4\x4f\x70"
+                        "\x1e\xde\xb4\x50\x81\x8f\x72\x72\xcb\xa7"
+                        "\xa2\x02\x05\xb3\x67\x1c\xe1\x99\x1c\xe9"
+                        "\xa6\xd2\xdf\x8d\xba\xd6\xe0\xbb\x3e\x50"
+                        "\x49\x3d\x7f\xa7") < 0;
+       */
+}
+
+/*
+static int test_kdfs(void)
+{
+       return test_hkdf(GNUTLS_MAC_SHA256,
+                        "0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b",
+                        "000102030405060708090a0b0c", "f0f1f2f3f4f5f6f7f8f9",
+                        42,
+                        "077709362c2e32df0ddc3f0dc47bba6390b6c73bb"
+                        "50f9c3122ec844ad7c2b3e5",
+                        "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf"
+                        "1a5a4c5db02d56ecc4c5bf34007208d5b887185865") < 0 ||
+              test_pbkdf2(GNUTLS_MAC_SHA1, "70617373776f7264", "73616c74",
+                          4096, 20,
+                          "4b007901b765489abead49d926f721d065a429c1") < 0;
+}
+*/
+
+int main(void)
+{
+       int ret;
+
+       gnutls_global_init();
+
+       ret = test_macs();
+       if (ret < 0)
+               goto cleanup;
+       ret = test_hashes();
+       if (ret < 0)
+               goto cleanup;
+       /*
+       ret = test_kdfs();
+       if (ret < 0)
+               goto cleanup;
+       */
+
+cleanup:
+       gnutls_global_deinit();
+       return ret;
+}
diff --git a/tests/pkcs11-provider/pkcs11-provider-pk.c b/tests/pkcs11-provider/pkcs11-provider-pk.c
new file mode 100644 (file)
index 0000000..16311f9
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2025 Red Hat, Inc.
+ *
+ * Author: Zoltan Fridrich
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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 a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/abstract.h>
+
+/* sha1 hash of "hello" string */
+const gnutls_datum_t hash_data = {
+       (void *)"\xaa\xf4\xc6\x1d\xdc\xc5\xe8\xa2\xda\xbe"
+               "\xde\x0f\x3b\x48\x2c\xd9\xae\xa9\x43\x4d",
+       20
+};
+
+const gnutls_datum_t raw_data = { (void *)"hello there", 11 };
+
+static int test_encrypt_decrypt(gnutls_pubkey_t *pubkey,
+                               gnutls_privkey_t *privkey)
+{
+       int ret;
+       gnutls_datum_t out = { NULL, 0 };
+       gnutls_datum_t out2 = { NULL, 0 };
+
+       ret = gnutls_pubkey_encrypt_data(*pubkey, 0, &hash_data, &out);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_pubkey_encrypt_data: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       ret = gnutls_privkey_decrypt_data(*privkey, 0, &out, &out2);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_privkey_decrypt_data: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       if (out2.size != hash_data.size) {
+               fprintf(stderr, "Decrypted data don't match original (1)\n");
+               return -1;
+       }
+
+       if (memcmp(out2.data, hash_data.data, hash_data.size) != 0) {
+               fprintf(stderr, "Decrypted data don't match original (2)\n");
+               return -1;
+       }
+
+       /* try again with fixed length API */
+       memset(out2.data, 'A', out2.size);
+       ret = gnutls_privkey_decrypt_data2(*privkey, 0, &out, out2.data,
+                                          out2.size);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_privkey_decrypt_data: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       if (memcmp(out2.data, hash_data.data, hash_data.size) != 0) {
+               fprintf(stderr, "Decrypted data don't match original (2b)\n");
+               return -1;
+       }
+
+       gnutls_free(out.data);
+       gnutls_free(out2.data);
+
+       ret = gnutls_pubkey_encrypt_data(*pubkey, 0, &raw_data, &out);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_pubkey_encrypt_data: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       ret = gnutls_privkey_decrypt_data(*privkey, 0, &out, &out2);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_privkey_decrypt_data: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       if (out2.size != raw_data.size) {
+               fprintf(stderr, "Decrypted data don't match original (3)\n");
+               return -1;
+       }
+
+       if (memcmp(out2.data, raw_data.data, raw_data.size) != 0) {
+               fprintf(stderr, "Decrypted data don't match original (4)\n");
+               return -1;
+       }
+
+       /* try again with fixed length API */
+       memset(out2.data, 'A', out2.size);
+       ret = gnutls_privkey_decrypt_data2(*privkey, 0, &out, out2.data,
+                                          out2.size);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_privkey_decrypt_data: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       if (memcmp(out2.data, raw_data.data, raw_data.size) != 0) {
+               fprintf(stderr, "Decrypted data don't match original (4b)\n");
+               return -1;
+       }
+
+       printf("ok\n");
+
+       gnutls_free(out.data);
+       gnutls_free(out2.data);
+
+       return 0;
+}
+
+static int generate_rsa_keys(gnutls_pubkey_t *pubkey, gnutls_privkey_t *privkey)
+{
+       int ret = 0;
+
+       ret = gnutls_pubkey_init(pubkey);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_pubkey_init: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       ret = gnutls_privkey_init(privkey);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_privkey_init: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       ret = gnutls_privkey_generate(*privkey, GNUTLS_PK_RSA, 2048, 0);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_privkey_generate: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       ret = gnutls_pubkey_import_privkey(*pubkey, *privkey,
+                                          GNUTLS_KEY_DATA_ENCIPHERMENT, 0);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_pubkey_import_privkey: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       return ret;
+}
+
+static int test_rsa(void)
+{
+       int ret = 0;
+       gnutls_pubkey_t pubkey = NULL;
+       gnutls_privkey_t privkey = NULL;
+
+       ret = generate_rsa_keys(&pubkey, &privkey);
+       if (ret < 0) {
+               fprintf(stderr, "Failed to generate RSA keys\n");
+               return ret;
+       }
+
+       printf("Testing RSA encrypt-decrypt\n");
+       ret = test_encrypt_decrypt(&pubkey, &privkey);
+
+       gnutls_privkey_deinit(privkey);
+       gnutls_pubkey_deinit(pubkey);
+       return ret;
+}
+
+static int test_rsa_oaep(void)
+{
+       int ret = 0;
+       gnutls_pubkey_t pubkey = NULL;
+       gnutls_privkey_t privkey = NULL;
+       gnutls_x509_spki_t spki;
+       const gnutls_datum_t label = { (void *)"label", 5 };
+
+       ret = gnutls_x509_spki_init(&spki);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_x509_spki_init: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       ret = gnutls_x509_spki_set_rsa_oaep_params(spki, GNUTLS_DIG_SHA512,
+                                                  &label);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_x509_spki_set_rsa_oaep_params: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       ret = generate_rsa_keys(&pubkey, &privkey);
+       if (ret < 0) {
+               fprintf(stderr, "Failed to generate RSA keys\n");
+               return ret;
+       }
+
+       ret = gnutls_privkey_set_spki(privkey, spki, 0);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_privkey_set_spki: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       ret = gnutls_pubkey_set_spki(pubkey, spki, 0);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_pubkey_set_spki: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       printf("Testing RSA OAEP encrypt-decrypt\n");
+       ret = test_encrypt_decrypt(&pubkey, &privkey);
+
+       gnutls_x509_spki_deinit(spki);
+       gnutls_privkey_deinit(privkey);
+       gnutls_pubkey_deinit(pubkey);
+       return ret;
+}
+
+static int test_ec_keygen(gnutls_ecc_curve_t curve)
+{
+       int ret;
+       gnutls_privkey_t privkey;
+
+       printf("Testing ECDSA key generation\n");
+
+       ret = gnutls_privkey_init(&privkey);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_privkey_init: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       ret = gnutls_privkey_generate(privkey, GNUTLS_PK_EC,
+                                     GNUTLS_CURVE_TO_BITS(curve), 0);
+       if (ret < 0) {
+               fprintf(stderr, "gnutls_privkey_generate: %s\n",
+                       gnutls_strerror(ret));
+               return ret;
+       }
+
+       printf("ok\n");
+
+       gnutls_privkey_deinit(privkey);
+       return 0;
+}
+
+int main(void)
+{
+       int ret;
+
+       gnutls_global_init();
+
+       ret = test_rsa();
+       if (ret < 0)
+               goto cleanup;
+
+       ret = test_rsa_oaep();
+       if (ret < 0)
+               goto cleanup;
+
+       ret = test_ec_keygen(GNUTLS_ECC_CURVE_SECP256R1);
+       if (ret < 0)
+               goto cleanup;
+
+cleanup:
+       gnutls_global_deinit();
+       return ret;
+}
diff --git a/tests/pkcs11-provider/pkcs11-provider-sig.c b/tests/pkcs11-provider/pkcs11-provider-sig.c
new file mode 100644 (file)
index 0000000..4155381
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2025 Red Hat, Inc.
+ *
+ * Author: Zoltan Fridrich
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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 a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/abstract.h>
+#include "common-key-tests.h"
+
+/* sha1 hash of "hello" string */
+const gnutls_datum_t sha1_hash_data = {
+       (void *)"\xaa\xf4\xc6\x1d\xdc\xc5\xe8\xa2\xda\xbe"
+               "\xde\x0f\x3b\x48\x2c\xd9\xae\xa9\x43\x4d",
+       20
+};
+
+const gnutls_datum_t sha256_hash_data = {
+       (void *)"\x2c\xf2\x4d\xba\x5f\xb0\xa3\x0e\x26\xe8\x3b\x2a\xc5\xb9\xe2\x9e"
+               "\x1b\x16\x1e\x5c\x1f\xa7\x42\x5e\x73\x04\x33\x62\x93\x8b\x98\x24",
+       32
+};
+
+const gnutls_datum_t sha256_invalid_hash_data = {
+       (void *)"\x2c\xf2\x4d\xba\x5f\xb1\xa3\x0e\x26\xe8\x3b\x2a\xc5\xb9\xe2\x9e"
+               "\x1b\x16\x1e\x5c\x1f\xa3\x42\x5e\x73\x04\x33\x62\x93\x8b\x98\x24",
+       32
+};
+
+const gnutls_datum_t sha1_invalid_hash_data = {
+       (void *)"\xaa\xf4\xc6\x1d\xdc\xca\xe8\xa2\xda\xbe"
+               "\xde\x0f\x3b\x48\x2c\xb9\xae\xa9\x43\x4d",
+       20
+};
+
+const gnutls_datum_t raw_data = { (void *)"hello", 5 };
+
+#define tests common_key_tests
+
+int main(void)
+{
+       gnutls_x509_privkey_t key;
+       gnutls_x509_crt_t crt;
+       gnutls_pubkey_t pubkey;
+       gnutls_privkey_t privkey;
+       gnutls_sign_algorithm_t sign_algo;
+       gnutls_datum_t signature;
+       gnutls_datum_t signature2;
+       int ret;
+       size_t i;
+       const gnutls_datum_t *hash_data;
+       const gnutls_datum_t *invalid_hash_data;
+
+       gnutls_global_init();
+
+       for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+               if (tests[i].pk != GNUTLS_PK_RSA &&
+                   tests[i].pk != GNUTLS_PK_RSA_PSS &&
+                   tests[i].pk != GNUTLS_PK_ECDSA)
+                       continue;
+
+               printf("testing: %s - %s\n", tests[i].name,
+                      gnutls_sign_algorithm_get_name(tests[i].sigalgo));
+
+               if (tests[i].digest == GNUTLS_DIG_SHA1) {
+                       hash_data = &sha1_hash_data;
+                       invalid_hash_data = &sha1_invalid_hash_data;
+               } else {
+                       hash_data = &sha256_hash_data;
+                       invalid_hash_data = &sha256_invalid_hash_data;
+               }
+
+               ret = gnutls_x509_privkey_init(&key);
+               if (ret < 0) {
+                       fprintf(stderr, "gnutls_x509_privkey_init: %s\n",
+                               gnutls_strerror(ret));
+                       return EXIT_FAILURE;
+               }
+
+               ret = gnutls_x509_privkey_import(key, &tests[i].key,
+                                                GNUTLS_X509_FMT_PEM);
+               if (ret < 0) {
+                       fprintf(stderr, "gnutls_x509_privkey_import: %s\n",
+                               gnutls_strerror(ret));
+                       return EXIT_FAILURE;
+               }
+
+               ret = gnutls_pubkey_init(&pubkey);
+               if (ret < 0) {
+                       fprintf(stderr, "gnutls_privkey_init: %s\n",
+                               gnutls_strerror(ret));
+                       return EXIT_FAILURE;
+               }
+
+               ret = gnutls_privkey_init(&privkey);
+               if (ret < 0) {
+                       fprintf(stderr, "gnutls_pubkey_init: %s\n",
+                               gnutls_strerror(ret));
+                       return EXIT_FAILURE;
+               }
+
+               ret = gnutls_privkey_import_x509(privkey, key, 0);
+               if (ret < 0) {
+                       fprintf(stderr, "gnutls_privkey_import_x509: %s\n",
+                               gnutls_strerror(ret));
+                       return EXIT_FAILURE;
+               }
+
+               ret = gnutls_privkey_sign_hash(privkey, tests[i].digest,
+                                              tests[i].sign_flags, hash_data,
+                                              &signature2);
+               if (ret < 0) {
+                       fprintf(stderr, "gnutls_privkey_sign_hash: %s\n",
+                               gnutls_strerror(ret));
+                       return EXIT_FAILURE;
+               }
+
+               ret = gnutls_privkey_sign_data(privkey, tests[i].digest,
+                                              tests[i].sign_flags, &raw_data,
+                                              &signature);
+               if (ret < 0) {
+                       fprintf(stderr, "gnutls_x509_privkey_sign_hash: %s\n",
+                               gnutls_strerror(ret));
+                       return EXIT_FAILURE;
+               }
+
+               ret = gnutls_x509_crt_init(&crt);
+               if (ret < 0) {
+                       fprintf(stderr, "gnutls_x509_crt_init: %s\n",
+                               gnutls_strerror(ret));
+                       return EXIT_FAILURE;
+               }
+
+               ret = gnutls_x509_crt_import(crt, &tests[i].cert,
+                                            GNUTLS_X509_FMT_PEM);
+               if (ret < 0) {
+                       fprintf(stderr, "gnutls_x509_crt_import: %s\n",
+                               gnutls_strerror(ret));
+                       return EXIT_FAILURE;
+               }
+
+               ret = gnutls_pubkey_import_x509(pubkey, crt, 0);
+               if (ret < 0) {
+                       fprintf(stderr, "gnutls_x509_pubkey_import: %s\n",
+                               gnutls_strerror(ret));
+                       return EXIT_FAILURE;
+               }
+
+               ret = gnutls_pubkey_verify_hash2(
+                       pubkey, tests[i].sigalgo,
+                       GNUTLS_VERIFY_ALLOW_SIGN_WITH_SHA1, hash_data,
+                       &signature);
+               if (ret < 0) {
+                       fprintf(stderr, "gnutls_x509_pubkey_verify_hash2: %s\n",
+                               gnutls_strerror(ret));
+                       return EXIT_FAILURE;
+               }
+
+               ret = gnutls_pubkey_verify_hash2(
+                       pubkey, tests[i].sigalgo,
+                       GNUTLS_VERIFY_ALLOW_SIGN_WITH_SHA1, hash_data,
+                       &signature2);
+               if (ret < 0) {
+                       fprintf(stderr,
+                               "gnutls_x509_pubkey_verify_hash-1 (hashed data): %s\n",
+                               gnutls_strerror(ret));
+                       return EXIT_FAILURE;
+               }
+
+               /* should fail */
+               ret = gnutls_pubkey_verify_hash2(
+                       pubkey, tests[i].sigalgo,
+                       GNUTLS_VERIFY_ALLOW_SIGN_WITH_SHA1, invalid_hash_data,
+                       &signature2);
+               if (ret != GNUTLS_E_PK_SIG_VERIFY_FAILED) {
+                       fprintf(stderr,
+                               "gnutls_x509_pubkey_verify_hash-2 (hashed data): %s\n",
+                               gnutls_strerror(ret));
+                       return EXIT_FAILURE;
+               }
+
+               sign_algo = gnutls_pk_to_sign(
+                       gnutls_pubkey_get_pk_algorithm(pubkey, NULL),
+                       tests[i].digest);
+
+               ret = gnutls_pubkey_verify_hash2(
+                       pubkey, sign_algo, GNUTLS_VERIFY_ALLOW_SIGN_WITH_SHA1,
+                       hash_data, &signature2);
+               if (ret < 0) {
+                       fprintf(stderr,
+                               "gnutls_x509_pubkey_verify_hash2-1 (hashed data): %s\n",
+                               gnutls_strerror(ret));
+                       return EXIT_FAILURE;
+               }
+
+               /* should fail */
+               ret = gnutls_pubkey_verify_hash2(
+                       pubkey, sign_algo, GNUTLS_VERIFY_ALLOW_SIGN_WITH_SHA1,
+                       invalid_hash_data, &signature2);
+               if (ret != GNUTLS_E_PK_SIG_VERIFY_FAILED) {
+                       fprintf(stderr,
+                               "gnutls_x509_pubkey_verify_hash2-2 (hashed data): %s\n",
+                               gnutls_strerror(ret));
+                       return EXIT_FAILURE;
+               }
+
+               /* test the raw interface */
+               gnutls_free(signature.data);
+
+               if (gnutls_pubkey_get_pk_algorithm(pubkey, NULL) ==
+                   GNUTLS_PK_RSA) {
+                       ret = gnutls_privkey_sign_hash(
+                               privkey, tests[i].digest,
+                               GNUTLS_PRIVKEY_SIGN_FLAG_TLS1_RSA, hash_data,
+                               &signature);
+                       if (ret < 0) {
+                               fprintf(stderr,
+                                       "gnutls_privkey_sign_hash: %s\n",
+                                       gnutls_strerror(ret));
+                               return EXIT_FAILURE;
+                       }
+
+                       sign_algo = gnutls_pk_to_sign(
+                               gnutls_pubkey_get_pk_algorithm(pubkey, NULL),
+                               tests[i].digest);
+
+                       ret = gnutls_pubkey_verify_hash2(
+                               pubkey, sign_algo,
+                               GNUTLS_PUBKEY_VERIFY_FLAG_TLS1_RSA, hash_data,
+                               &signature);
+                       if (ret < 0) {
+                               fprintf(stderr,
+                                       "gnutls_pubkey_verify_hash-3 (raw hashed data): %s\n",
+                                       gnutls_strerror(ret));
+                               return EXIT_FAILURE;
+                       }
+
+                       gnutls_free(signature.data);
+                       /* test the legacy API */
+                       ret = gnutls_privkey_sign_raw_data(
+                               privkey, 0, hash_data, &signature);
+                       if (ret < 0) {
+                               fprintf(stderr,
+                                       "gnutls_privkey_sign_raw_data: %s\n",
+                                       gnutls_strerror(ret));
+                               return EXIT_FAILURE;
+                       }
+
+                       ret = gnutls_pubkey_verify_hash2(
+                               pubkey, sign_algo,
+                               GNUTLS_PUBKEY_VERIFY_FLAG_TLS1_RSA, hash_data,
+                               &signature);
+                       if (ret < 0) {
+                               fprintf(stderr,
+                                       "gnutls_pubkey_verify_hash-4 (legacy raw hashed data): %s\n",
+                                       gnutls_strerror(ret));
+                               return EXIT_FAILURE;
+                       }
+               }
+               gnutls_free(signature.data);
+               gnutls_free(signature2.data);
+               gnutls_x509_privkey_deinit(key);
+               gnutls_x509_crt_deinit(crt);
+               gnutls_privkey_deinit(privkey);
+               gnutls_pubkey_deinit(pubkey);
+       }
+
+       gnutls_global_deinit();
+}
diff --git a/tests/pkcs11-provider/test-pkcs11-provider.sh b/tests/pkcs11-provider/test-pkcs11-provider.sh
new file mode 100755 (executable)
index 0000000..3c867b6
--- /dev/null
@@ -0,0 +1,127 @@
+#!/bin/sh
+
+# Copyright (C) 2025 Red Hat, Inc.
+#
+# Author: Zoltan Fridrich <zfridric@redhat.com>
+#
+# This file is part of GnuTLS.
+#
+# GnuTLS is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 3 of the License, or (at
+# your option) any later version.
+#
+# GnuTLS 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 a copy of the GNU General Public License
+# along with GnuTLS.  If not, see <https://www.gnu.org/licenses/>.
+
+: ${testdir=$abs_top_builddir/tests/pkcs11-provider}
+
+if test "${GNUTLS_FORCE_FIPS_MODE}" != 1; then
+       exit 77
+fi
+
+if [ -z "$(which pkcs11-tool 2>/dev/null)" ]; then
+       echo "Need pkcs11-tool from opensc package to run this test."
+       exit 77
+fi
+
+MODULE="/lib64/pkcs11/libkryoptic_pkcs11.so"
+if [ ! -f "$MODULE" ]; then
+        echo "Need Kryoptic module to run this test."
+        exit 77
+fi
+
+LABEL="Kryoptic Token"
+PIN="12345"
+PRIORITY_FILE="${testdir}/gnutls.$$.conf"
+KRYOPTIC_DB="${testdir}/kryoptic.$$.sql"
+export KRYOPTIC_CONF="${testdir}/kryoptic.$$.conf"
+export GNUTLS_SYSTEM_PRIORITY_FAIL_ON_INVALID=1
+export GNUTLS_SYSTEM_PRIORITY_FILE="${PRIORITY_FILE}"
+export GNUTLS_DEBUG_LEVEL=6
+
+cat >"${PRIORITY_FILE}" <<_EOF_
+[overrides]
+allow-rsa-pkcs1-encrypt = true
+
+[provider]
+path = ${MODULE}
+pin = ${PIN}
+_EOF_
+
+cat >"${KRYOPTIC_CONF}" <<_EOF_
+[ec_point_encoding]
+encoding = "Bytes"
+
+[[slots]]
+slot = 22
+dbtype = "sqlite"
+dbargs = "${KRYOPTIC_DB}"
+_EOF_
+
+echo "Initializing token"
+
+# init token
+pkcs11-tool --module "${MODULE}" --init-token --label "${LABEL}" --so-pin "${PIN}" >/dev/null
+if test $? != 0; then
+       echo "failed to initialize token"
+       exit 1
+fi
+# set user pin
+pkcs11-tool --module "${MODULE}" --so-pin "${PIN}"  --login --login-type so --init-pin --pin "${PIN}" >/dev/null
+if test $? != 0; then
+       echo "failed to set user pin"
+       exit 1
+fi
+
+echo "Testing public key algorithms"
+"${testdir}/pkcs11-provider-pk"
+rc=$?
+if test "${rc}" = "0"; then
+       echo "test passed"
+else
+       echo "test failed"
+       rm -f ${PRIORITY_FILE} ${KRYOPTIC_CONF} ${KRYOPTIC_DB}
+       exit ${rc}
+fi
+
+echo "Testing signatures"
+"${testdir}/pkcs11-provider-sig"
+rc=$?
+if test "${rc}" = "0"; then
+       echo "test passed"
+else
+       echo "test failed"
+       rm -f ${PRIORITY_FILE} ${KRYOPTIC_CONF} ${KRYOPTIC_DB}
+       exit ${rc}
+fi
+
+echo "Testing ciphers"
+"${testdir}/pkcs11-provider-cipher"
+rc=$?
+if test "${rc}" = "0"; then
+       echo "test passed"
+else
+       echo "test failed"
+       rm -f ${PRIORITY_FILE} ${KRYOPTIC_CONF} ${KRYOPTIC_DB}
+       exit ${rc}
+fi
+
+echo "Testing hmacs"
+"${testdir}/pkcs11-provider-hmac"
+rc=$?
+if test "${rc}" = "0"; then
+       echo "test passed"
+else
+       echo "test failed"
+       rm -f ${PRIORITY_FILE} ${KRYOPTIC_CONF} ${KRYOPTIC_DB}
+       exit ${rc}
+fi
+
+rm -f ${PRIORITY_FILE} ${KRYOPTIC_CONF} ${KRYOPTIC_DB}
+exit ${rc}