summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorSimo Sorce <simo@redhat.com>2018-10-03 13:12:38 -0400
committerSimo Sorce <simo@redhat.com>2018-11-30 13:51:24 -0500
commit4804febddc2ed958e5ae774de2a8f85edeeff538 (patch)
treea7a693c3e8fb686581425217d0545b85612616c5 /lib
parented3bdddab73c792364deec423b2c2c498a939a64 (diff)
downloadgnutls-4804febddc2ed958e5ae774de2a8f85edeeff538.tar.gz
Constant time/cache PKCS#1 RSA decryptiontmp-fix-CVE-2018-16868
This patch tries to make the code have the same time and memory access aptterns across all branches of the decryption function so that timining or cache side channels are minimized or neutralized. To do so it uses a new nettle rsa decryption function that is side-channel silent. Signed-off-by: Simo Sorce <simo@redhat.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/abstract_int.h1
-rw-r--r--lib/auth/rsa.c109
-rw-r--r--lib/crypto-backend.h9
-rw-r--r--lib/crypto-selftests-pk.c13
-rw-r--r--lib/errors.h2
-rw-r--r--lib/gnutls_int.h4
-rw-r--r--lib/includes/gnutls/abstract.h13
-rw-r--r--lib/libgnutls.map1
-rw-r--r--lib/nettle/pk.c52
-rw-r--r--lib/pk.h1
-rw-r--r--lib/pkcs11_int.h7
-rw-r--r--lib/pkcs11_privkey.c115
-rw-r--r--lib/privkey.c76
13 files changed, 345 insertions, 58 deletions
diff --git a/lib/abstract_int.h b/lib/abstract_int.h
index 5eaf6e9460..d920486597 100644
--- a/lib/abstract_int.h
+++ b/lib/abstract_int.h
@@ -39,6 +39,7 @@ struct gnutls_privkey_st {
gnutls_privkey_sign_data_func sign_data_func;
gnutls_privkey_sign_hash_func sign_hash_func;
gnutls_privkey_decrypt_func decrypt_func;
+ gnutls_privkey_decrypt_func2 decrypt_func2;
gnutls_privkey_deinit_func deinit_func;
gnutls_privkey_info_func info_func;
void *userdata;
diff --git a/lib/auth/rsa.c b/lib/auth/rsa.c
index 6afc91ae67..488569d3b7 100644
--- a/lib/auth/rsa.c
+++ b/lib/auth/rsa.c
@@ -155,12 +155,13 @@ static int
proc_rsa_client_kx(gnutls_session_t session, uint8_t * data,
size_t _data_size)
{
- gnutls_datum_t plaintext = {NULL, 0};
+ const char attack_error[] = "auth_rsa: Possible PKCS #1 attack\n";
gnutls_datum_t ciphertext;
int ret, dsize;
- int use_rnd_key = 0;
ssize_t data_size = _data_size;
- gnutls_datum_t rndkey = {NULL, 0};
+ volatile uint8_t ver_maj, ver_min;
+ volatile uint8_t check_ver_min;
+ volatile uint32_t ok;
#ifdef ENABLE_SSL3
if (get_num_version(session) == GNUTLS_SSL3) {
@@ -184,75 +185,73 @@ proc_rsa_client_kx(gnutls_session_t session, uint8_t * data,
ciphertext.size = dsize;
}
- rndkey.size = GNUTLS_MASTER_SIZE;
- rndkey.data = gnutls_malloc(rndkey.size);
- if (rndkey.data == NULL) {
+ ver_maj = _gnutls_get_adv_version_major(session);
+ ver_min = _gnutls_get_adv_version_minor(session);
+ check_ver_min = (session->internals.allow_wrong_pms == 0);
+
+ session->key.key.data = gnutls_malloc(GNUTLS_MASTER_SIZE);
+ if (session->key.key.data == NULL) {
gnutls_assert();
return GNUTLS_E_MEMORY_ERROR;
}
+ session->key.key.size = GNUTLS_MASTER_SIZE;
- /* we do not need strong random numbers here.
- */
- ret = gnutls_rnd(GNUTLS_RND_NONCE, rndkey.data,
- rndkey.size);
+ /* Fallback value when decryption fails. Needs to be unpredictable. */
+ ret = gnutls_rnd(GNUTLS_RND_NONCE, session->key.key.data,
+ GNUTLS_MASTER_SIZE);
if (ret < 0) {
+ gnutls_free(session->key.key.data);
+ session->key.key.data = NULL;
+ session->key.key.size = 0;
gnutls_assert();
- goto cleanup;
+ return ret;
}
ret =
- gnutls_privkey_decrypt_data(session->internals.selected_key, 0,
- &ciphertext, &plaintext);
-
- if (ret < 0 || plaintext.size != GNUTLS_MASTER_SIZE) {
- /* In case decryption fails then don't inform
- * the peer. Just use a random key. (in order to avoid
- * attack against pkcs-1 formating).
- */
- _gnutls_debug_log("auth_rsa: Possible PKCS #1 format attack\n");
- if (ret >= 0) {
- gnutls_free(plaintext.data);
- plaintext.data = NULL;
- }
- use_rnd_key = 1;
- } else {
- /* If the secret was properly formatted, then
- * check the version number.
- */
- if (_gnutls_get_adv_version_major(session) !=
- plaintext.data[0]
- || (session->internals.allow_wrong_pms == 0
- && _gnutls_get_adv_version_minor(session) !=
- plaintext.data[1])) {
- /* No error is returned here, if the version number check
- * fails. We proceed normally.
- * That is to defend against the attack described in the paper
- * "Attacking RSA-based sessions in SSL/TLS" by Vlastimil Klima,
- * Ondej Pokorny and Tomas Rosa.
- */
- _gnutls_debug_log("auth_rsa: Possible PKCS #1 version check format attack\n");
- }
- }
+ gnutls_privkey_decrypt_data2(session->internals.selected_key,
+ 0, &ciphertext, session->key.key.data,
+ session->key.key.size);
+ /* After this point, any conditional on failure that cause differences
+ * in execution may create a timing or cache access pattern side
+ * channel that can be used as an oracle, so tread very carefully */
+
+ /* Error handling logic:
+ * In case decryption fails then don't inform the peer. Just use the
+ * random key previously generated. (in order to avoid attack against
+ * pkcs-1 formating).
+ *
+ * If we get version mismatches no error is returned either. We
+ * proceed normally. This is to defend against the attack described
+ * in the paper "Attacking RSA-based sessions in SSL/TLS" by
+ * Vlastimil Klima, Ondej Pokorny and Tomas Rosa.
+ */
- if (use_rnd_key != 0) {
- session->key.key.data = rndkey.data;
- session->key.key.size = rndkey.size;
- rndkey.data = NULL;
+ /* ok is 0 in case of error and 1 in case of success. */
+
+ /* if ret < 0 */
+ ok = CONSTCHECK_EQUAL(ret, 0);
+ /* session->key.key.data[0] must equal ver_maj */
+ ok &= CONSTCHECK_EQUAL(session->key.key.data[0], ver_maj);
+ /* if check_ver_min then session->key.key.data[1] must equal ver_min */
+ ok &= CONSTCHECK_NOT_EQUAL(check_ver_min, 0) &
+ CONSTCHECK_EQUAL(session->key.key.data[1], ver_min);
+
+ if (ok) {
+ /* call logging function unconditionally so all branches are
+ * indistinguishable for timing and cache access when debug
+ * logging is disabled */
+ _gnutls_no_log("%s", attack_error);
} else {
- session->key.key.data = plaintext.data;
- session->key.key.size = plaintext.size;
+ _gnutls_debug_log("%s", attack_error);
}
/* This is here to avoid the version check attack
* discussed above.
*/
- session->key.key.data[0] = _gnutls_get_adv_version_major(session);
- session->key.key.data[1] = _gnutls_get_adv_version_minor(session);
+ session->key.key.data[0] = ver_maj;
+ session->key.key.data[1] = ver_min;
- ret = 0;
- cleanup:
- gnutls_free(rndkey.data);
- return ret;
+ return 0;
}
diff --git a/lib/crypto-backend.h b/lib/crypto-backend.h
index ff8f39616e..19f705e14d 100644
--- a/lib/crypto-backend.h
+++ b/lib/crypto-backend.h
@@ -344,10 +344,15 @@ typedef struct gnutls_crypto_pk {
int (*encrypt) (gnutls_pk_algorithm_t, gnutls_datum_t * ciphertext,
const gnutls_datum_t * plaintext,
const gnutls_pk_params_st * pub);
- int (*decrypt) (gnutls_pk_algorithm_t, gnutls_datum_t * plaintext,
+ int (*decrypt) (gnutls_pk_algorithm_t,
+ gnutls_datum_t * plaintext,
const gnutls_datum_t * ciphertext,
const gnutls_pk_params_st * priv);
-
+ int (*decrypt2) (gnutls_pk_algorithm_t,
+ const gnutls_datum_t * ciphertext,
+ unsigned char * plaintext,
+ size_t paintext_size,
+ const gnutls_pk_params_st * priv);
int (*sign) (gnutls_pk_algorithm_t, gnutls_datum_t * signature,
const gnutls_datum_t * data,
const gnutls_pk_params_st *priv,
diff --git a/lib/crypto-selftests-pk.c b/lib/crypto-selftests-pk.c
index e42367a93f..65de8916f5 100644
--- a/lib/crypto-selftests-pk.c
+++ b/lib/crypto-selftests-pk.c
@@ -116,6 +116,7 @@ static int test_rsa_enc(gnutls_pk_algorithm_t pk,
gnutls_datum_t raw_rsa_key = { (void*)rsa_key2048, sizeof(rsa_key2048)-1 };
gnutls_privkey_t key;
gnutls_pubkey_t pub = NULL;
+ unsigned char plaintext2[sizeof(DATASTR) - 1];
ret = gnutls_privkey_init(&key);
if (ret < 0)
@@ -165,6 +166,18 @@ static int test_rsa_enc(gnutls_pk_algorithm_t pk,
goto cleanup;
}
+ ret = gnutls_privkey_decrypt_data2(key, 0, &enc, plaintext2,
+ signed_data.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ if (memcmp(plaintext2, signed_data.data, signed_data.size) != 0) {
+ ret = GNUTLS_E_SELF_TEST_ERROR;
+ gnutls_assert();
+ goto cleanup;
+ }
+
ret = 0;
cleanup:
if (pub != NULL)
diff --git a/lib/errors.h b/lib/errors.h
index e0f6b906c2..baadc0e67e 100644
--- a/lib/errors.h
+++ b/lib/errors.h
@@ -108,6 +108,7 @@ void _gnutls_mpi_log(const char *prefix, bigint_t a);
#define _gnutls_write_log(...) LEVEL(11, __VA_ARGS__)
#define _gnutls_io_log(...) LEVEL(12, __VA_ARGS__)
#define _gnutls_buffers_log(...) LEVEL(13, __VA_ARGS__)
+#define _gnutls_no_log(...) LEVEL(INT_MAX, __VA_ARGS__)
#else
#define _gnutls_debug_log _gnutls_null_log
#define _gnutls_assert_log _gnutls_null_log
@@ -119,6 +120,7 @@ void _gnutls_mpi_log(const char *prefix, bigint_t a);
#define _gnutls_dtls_log _gnutls_null_log
#define _gnutls_read_log _gnutls_null_log
#define _gnutls_write_log _gnutls_null_log
+#define _gnutls_no_log _gnutle_null_log
void _gnutls_null_log(void *, ...);
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index 16881d8827..50a9208346 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -1564,4 +1564,8 @@ inline static bool _gnutls_has_negotiate_ctypes(gnutls_session_t session)
return session->internals.flags & GNUTLS_ENABLE_CERT_TYPE_NEG;
}
+/* Macros to aide constant time/mem checks */
+#define CONSTCHECK_NOT_EQUAL(a, b) ((-((uint32_t)(a) ^ (uint32_t)(b))) >> 31)
+#define CONSTCHECK_EQUAL(a, b) (1U - CONSTCHECK_NOT_EQUAL(a, b))
+
#endif /* GNUTLS_INT_H */
diff --git a/lib/includes/gnutls/abstract.h b/lib/includes/gnutls/abstract.h
index 5fa0fb99db..d69e30ca51 100644
--- a/lib/includes/gnutls/abstract.h
+++ b/lib/includes/gnutls/abstract.h
@@ -75,6 +75,12 @@ typedef int (*gnutls_privkey_decrypt_func) (gnutls_privkey_t key,
const gnutls_datum_t *ciphertext,
gnutls_datum_t * plaintext);
+typedef int (*gnutls_privkey_decrypt_func2) (gnutls_privkey_t key,
+ void *userdata,
+ const gnutls_datum_t *ciphertext,
+ unsigned char * plaintext,
+ size_t plaintext_size);
+
/* to be called to sign pre-hashed data. The input will be
* the output of the hash (such as SHA256) corresponding to
* the signature algorithm. The algorithm GNUTLS_SIGN_RSA_RAW
@@ -542,12 +548,17 @@ int gnutls_privkey_sign_hash2(gnutls_privkey_t signer,
const gnutls_datum_t * hash_data,
gnutls_datum_t * signature);
-
int gnutls_privkey_decrypt_data(gnutls_privkey_t key,
unsigned int flags,
const gnutls_datum_t * ciphertext,
gnutls_datum_t * plaintext);
+int gnutls_privkey_decrypt_data2(gnutls_privkey_t key,
+ unsigned int flags,
+ const gnutls_datum_t * ciphertext,
+ unsigned char * plaintext,
+ size_t plaintext_size);
+
int
gnutls_privkey_export_rsa_raw(gnutls_privkey_t key,
gnutls_datum_t * m, gnutls_datum_t * e,
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index 06181f04ee..bfb447ccfd 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1261,6 +1261,7 @@ GNUTLS_3_6_5
gnutls_anti_replay_deinit;
gnutls_anti_replay_set_window;
gnutls_anti_replay_enable;
+ gnutls_privkey_decrypt_data2;
} GNUTLS_3_6_4;
GNUTLS_FIPS140_3_4 {
diff --git a/lib/nettle/pk.c b/lib/nettle/pk.c
index 4d945c89ad..38c098d8d5 100644
--- a/lib/nettle/pk.c
+++ b/lib/nettle/pk.c
@@ -529,6 +529,57 @@ _wrap_nettle_pk_decrypt(gnutls_pk_algorithm_t algo,
return ret;
}
+/* Note: we do not allocate in this function to avoid asymettric
+ * unallocation (which creates a side channel) in case of failure
+ * */
+static int
+_wrap_nettle_pk_decrypt2(gnutls_pk_algorithm_t algo,
+ const gnutls_datum_t * ciphertext,
+ unsigned char * plaintext,
+ size_t plaintext_size,
+ const gnutls_pk_params_st * pk_params)
+{
+ struct rsa_private_key priv;
+ struct rsa_public_key pub;
+ bigint_t c;
+ uint32_t is_err;
+ int ret;
+
+ if (algo != GNUTLS_PK_RSA || plaintext == NULL) {
+ gnutls_assert();
+ return GNUTLS_E_INTERNAL_ERROR;
+ }
+
+ _rsa_params_to_privkey(pk_params, &priv);
+ ret = _rsa_params_to_pubkey(pk_params, &pub);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ if (ciphertext->size != pub.size)
+ return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+
+ if (_gnutls_mpi_init_scan_nz(&c, ciphertext->data,
+ ciphertext->size) != 0) {
+ return gnutls_assert_val (GNUTLS_E_MPI_SCAN_FAILED);
+ }
+
+ ret = rsa_sec_decrypt(&pub, &priv, NULL, rnd_nonce_func,
+ plaintext_size, plaintext, TOMPZ(c));
+ /* after this point, any conditional on failure that cause differences
+ * in execution may create a timing or cache access pattern side
+ * channel that can be used as an oracle, so thread very carefully */
+ _gnutls_mpi_release(&c);
+ /* Here HAVE_LIB_ERROR() should be fine as it doesn't have
+ * branches in it and returns a bool */
+ is_err = HAVE_LIB_ERROR();
+ /* if is_err != 0 */
+ is_err = CONSTCHECK_NOT_EQUAL(is_err, 0);
+ /* or ret == 0 */
+ is_err |= CONSTCHECK_EQUAL(ret, 0);
+ /* then return GNUTLS_E_DECRYPTION_FAILED */
+ return (int)((is_err * UINT_MAX) & GNUTLS_E_DECRYPTION_FAILED);
+}
+
#define CHECK_INVALID_RSA_PSS_PARAMS(dig_size, salt_size, pub_size, err) \
if (unlikely(dig_size + salt_size + 2 > pub_size)) \
return gnutls_assert_val(err)
@@ -2780,6 +2831,7 @@ int crypto_pk_prio = INT_MAX;
gnutls_crypto_pk_st _gnutls_pk_ops = {
.encrypt = _wrap_nettle_pk_encrypt,
.decrypt = _wrap_nettle_pk_decrypt,
+ .decrypt2 = _wrap_nettle_pk_decrypt2,
.sign = _wrap_nettle_pk_sign,
.verify = _wrap_nettle_pk_verify,
.verify_priv_params = wrap_nettle_pk_verify_priv_params,
diff --git a/lib/pk.h b/lib/pk.h
index c365eece20..f6872f823d 100644
--- a/lib/pk.h
+++ b/lib/pk.h
@@ -28,6 +28,7 @@ extern gnutls_crypto_pk_st _gnutls_pk_ops;
#define _gnutls_pk_encrypt( algo, ciphertext, plaintext, params) _gnutls_pk_ops.encrypt( algo, ciphertext, plaintext, params)
#define _gnutls_pk_decrypt( algo, ciphertext, plaintext, params) _gnutls_pk_ops.decrypt( algo, ciphertext, plaintext, params)
+#define _gnutls_pk_decrypt2( algo, ciphertext, plaintext, size, params) _gnutls_pk_ops.decrypt2( algo, ciphertext, plaintext, size, params)
#define _gnutls_pk_sign( algo, sig, data, params, sign_params) _gnutls_pk_ops.sign( algo, sig, data, params, sign_params)
#define _gnutls_pk_verify( algo, data, sig, params, sign_params) _gnutls_pk_ops.verify( algo, data, sig, params, sign_params)
#define _gnutls_pk_verify_priv_params( algo, params) _gnutls_pk_ops.verify_priv_params( algo, params)
diff --git a/lib/pkcs11_int.h b/lib/pkcs11_int.h
index 8facfa8686..a5187636ed 100644
--- a/lib/pkcs11_int.h
+++ b/lib/pkcs11_int.h
@@ -219,6 +219,13 @@ _gnutls_pkcs11_privkey_decrypt_data(gnutls_pkcs11_privkey_t key,
gnutls_datum_t * plaintext);
int
+_gnutls_pkcs11_privkey_decrypt_data2(gnutls_pkcs11_privkey_t key,
+ unsigned int flags,
+ const gnutls_datum_t * ciphertext,
+ unsigned char * plaintext,
+ size_t plaintext_size);
+
+int
_pkcs11_privkey_get_pubkey (gnutls_pkcs11_privkey_t pkey, gnutls_pubkey_t *pub, unsigned flags);
static inline int pk_to_mech(gnutls_pk_algorithm_t pk)
diff --git a/lib/pkcs11_privkey.c b/lib/pkcs11_privkey.c
index f643a69a66..bf69b69ce4 100644
--- a/lib/pkcs11_privkey.c
+++ b/lib/pkcs11_privkey.c
@@ -715,6 +715,121 @@ _gnutls_pkcs11_privkey_decrypt_data(gnutls_pkcs11_privkey_t key,
return ret;
}
+/*-
+ * _gnutls_pkcs11_privkey_decrypt_data2:
+ * @key: Holds the key
+ * @flags: should be 0 for now
+ * @ciphertext: holds the data to be signed
+ * @plaintext: a preallocated buffer that will be filled with the plaintext
+ * @plaintext_size: size of the plaintext
+ *
+ * This function will decrypt the given data using the public key algorithm
+ * supported by the private key.
+ * Unlike with _gnutls_pkcs11_privkey_decrypt_data the plaintext size is known
+ * and provided by the caller, if the plaintext size differs from the requested
+ * one, the operation fails and the provided buffer is left unchanged.
+ * NOTE: plaintext_size must be exactly the size of the payload in the
+ * ciphertext, otherwise an error is returned and the plaintext buffer is left
+ * unchanged.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ -*/
+int
+_gnutls_pkcs11_privkey_decrypt_data2(gnutls_pkcs11_privkey_t key,
+ unsigned int flags,
+ const gnutls_datum_t * ciphertext,
+ unsigned char * plaintext,
+ size_t plaintext_size)
+{
+ ck_rv_t rv;
+ int ret;
+ struct ck_mechanism mech;
+ unsigned long siglen = ciphertext->size;
+ unsigned req_login = 0;
+ unsigned login_flags = SESSION_LOGIN|SESSION_CONTEXT_SPECIFIC;
+ unsigned char *buffer;
+ volatile unsigned char value;
+ unsigned char mask;
+
+ PKCS11_CHECK_INIT_PRIVKEY(key);
+
+ if (key->pk_algorithm != GNUTLS_PK_RSA)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ mech.mechanism = CKM_RSA_PKCS;
+ mech.parameter = NULL;
+ mech.parameter_len = 0;
+
+ ret = gnutls_mutex_lock(&key->mutex);
+ if (ret != 0)
+ return gnutls_assert_val(GNUTLS_E_LOCKING_ERROR);
+
+ buffer = gnutls_malloc(siglen);
+ if (!buffer) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ /* Initialize signing operation; using the private key discovered
+ * earlier. */
+ REPEAT_ON_INVALID_HANDLE(
+ rv = pkcs11_decrypt_init(key->sinfo.module, key->sinfo.pks,
+ &mech, key->ref)
+ );
+ if (rv != CKR_OK) {
+ gnutls_assert();
+ ret = pkcs11_rv_to_err(rv);
+ goto cleanup;
+ }
+
+ retry_login:
+ if (key->reauth || req_login) {
+ if (req_login)
+ login_flags = SESSION_FORCE_LOGIN|SESSION_LOGIN;
+ ret =
+ pkcs11_login(&key->sinfo, &key->pin,
+ key->uinfo, login_flags);
+ if (ret < 0) {
+ gnutls_assert();
+ _gnutls_debug_log("PKCS #11 login failed, trying operation anyway\n");
+ /* let's try the operation anyway */
+ }
+ }
+
+ ret = 0;
+ siglen = ciphertext->size;
+ rv = pkcs11_decrypt(key->sinfo.module, key->sinfo.pks,
+ ciphertext->data, ciphertext->size,
+ buffer, &siglen);
+ if (unlikely(rv == CKR_USER_NOT_LOGGED_IN && req_login == 0)) {
+ req_login = 1;
+ goto retry_login;
+ }
+
+ /* NOTE: These branches are not side-channel silent */
+ if (rv != CKR_OK) {
+ gnutls_assert();
+ ret = pkcs11_rv_to_err(rv);
+ } else if (siglen != plaintext_size) {
+ gnutls_assert();
+ ret = GNUTLS_E_INVALID_REQUEST;
+ }
+
+ /* conditionally copy buffer in a side-channel silent way */
+ /* on success mask is 0xFF, on failure it is 0 */
+ mask = ((uint32_t)ret >> 31) - 1U;
+ for (size_t i = 0; i < plaintext_size; i++) {
+ value = (buffer[i] & mask) + (plaintext[i] & ~mask);
+ plaintext[i] = value;
+ }
+
+ cleanup:
+ gnutls_mutex_unlock(&key->mutex);
+ gnutls_free(buffer);
+ return ret;
+}
+
/**
* gnutls_pkcs11_privkey_export_url:
* @key: Holds the PKCS 11 key
diff --git a/lib/privkey.c b/lib/privkey.c
index 26e3cee893..55bd3181ab 100644
--- a/lib/privkey.c
+++ b/lib/privkey.c
@@ -1555,6 +1555,82 @@ gnutls_privkey_decrypt_data(gnutls_privkey_t key,
}
/**
+ * gnutls_privkey_decrypt_data2:
+ * @key: Holds the key
+ * @flags: zero for now
+ * @ciphertext: holds the data to be decrypted
+ * @plaintext: a preallocated buffer that will be filled with the plaintext
+ * @plaintext_size: in/out size of the plaintext
+ *
+ * This function will decrypt the given data using the algorithm
+ * supported by the private key. Unlike with gnutls_privkey_decrypt_data()
+ * this function operates in constant time and constant memory access.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ * negative error value.
+ *
+ * Since: 3.6.5
+ **/
+
+int
+gnutls_privkey_decrypt_data2(gnutls_privkey_t key,
+ unsigned int flags,
+ const gnutls_datum_t * ciphertext,
+ unsigned char * plaintext,
+ size_t plaintext_size)
+{
+ /* Note: except for the backwards compatibility function, no
+ * conditional code should be called after the decryption
+ * function call, to avoid creating oracle attacks based
+ * on cache/timing side channels */
+
+ /* backwards compatibility */
+ if (key->type == GNUTLS_PRIVKEY_EXT &&
+ key->key.ext.decrypt_func2 == NULL &&
+ key->key.ext.decrypt_func != NULL) {
+ gnutls_datum_t plain;
+ int ret;
+ ret = key->key.ext.decrypt_func(key,
+ key->key.ext.userdata,
+ ciphertext,
+ &plain);
+ if (plain.size != plaintext_size) {
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ } else {
+ memcpy(plaintext, plain.data, plain.size);
+ }
+ gnutls_free(plain.data);
+ return ret;
+ }
+
+ switch (key->type) {
+ case GNUTLS_PRIVKEY_X509:
+ return _gnutls_pk_decrypt2(key->pk_algorithm, ciphertext,
+ plaintext, plaintext_size,
+ &key->key.x509->params);
+#ifdef ENABLE_PKCS11
+ case GNUTLS_PRIVKEY_PKCS11:
+ return _gnutls_pkcs11_privkey_decrypt_data2(key->key.pkcs11,
+ flags,
+ ciphertext,
+ plaintext,
+ plaintext_size);
+#endif
+ case GNUTLS_PRIVKEY_EXT:
+ if (key->key.ext.decrypt_func2 == NULL)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ return key->key.ext.decrypt_func2(key,
+ key->key.ext.userdata,
+ ciphertext, plaintext,
+ plaintext_size);
+ default:
+ gnutls_assert();
+ return GNUTLS_E_INVALID_REQUEST;
+ }
+}
+
+/**
* gnutls_privkey_import_x509_raw:
* @pkey: The private key
* @data: The private key data to be imported