pkcs11: Support RSA-PSS signatures
This commit is contained in:
committed by
Tobias Brunner
parent
f739657aac
commit
49769fff53
@@ -30,3 +30,8 @@ charon.plugins.pkcs11.use_pubkey = no
|
|||||||
|
|
||||||
charon.plugins.pkcs11.use_rng = no
|
charon.plugins.pkcs11.use_rng = no
|
||||||
Whether the PKCS#11 modules should be used as RNG.
|
Whether the PKCS#11 modules should be used as RNG.
|
||||||
|
|
||||||
|
charon.plugins.pkcs11.use_rsa_pss_hashers = no
|
||||||
|
Whether the PKCS#11 modules should try to use internal hashing for RSA-PSS
|
||||||
|
signatures (some PKCS#11 libraries don't implement internal hashing
|
||||||
|
in conjunction with RSA-PSS correctly).
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2016 Tobias Brunner
|
* Copyright (C) 2011-2016 Tobias Brunner
|
||||||
* Copyright (C) 2010 Martin Willi
|
* Copyright (C) 2010 Martin Willi
|
||||||
|
* Copyright (C) 2024 Andreas Steffen
|
||||||
*
|
*
|
||||||
* Copyright (C) secunet Security Networks AG
|
* Copyright (C) secunet Security Networks AG
|
||||||
*
|
*
|
||||||
@@ -144,6 +145,7 @@ static bool is_mechanism_supported(pkcs11_library_t *p11, CK_SLOT_ID slot,
|
|||||||
CK_MECHANISM_PTR pkcs11_signature_scheme_to_mech(pkcs11_library_t *p11,
|
CK_MECHANISM_PTR pkcs11_signature_scheme_to_mech(pkcs11_library_t *p11,
|
||||||
CK_SLOT_ID slot,
|
CK_SLOT_ID slot,
|
||||||
signature_scheme_t scheme,
|
signature_scheme_t scheme,
|
||||||
|
void *params,
|
||||||
key_type_t type, size_t keylen,
|
key_type_t type, size_t keylen,
|
||||||
hash_algorithm_t *hash)
|
hash_algorithm_t *hash)
|
||||||
{
|
{
|
||||||
@@ -174,6 +176,8 @@ CK_MECHANISM_PTR pkcs11_signature_scheme_to_mech(pkcs11_library_t *p11,
|
|||||||
KEY_RSA, 0, HASH_SHA1},
|
KEY_RSA, 0, HASH_SHA1},
|
||||||
{SIGN_RSA_EMSA_PKCS1_MD5, {CKM_MD5_RSA_PKCS, NULL, 0},
|
{SIGN_RSA_EMSA_PKCS1_MD5, {CKM_MD5_RSA_PKCS, NULL, 0},
|
||||||
KEY_RSA, 0, HASH_UNKNOWN},
|
KEY_RSA, 0, HASH_UNKNOWN},
|
||||||
|
{SIGN_RSA_EMSA_PSS, {CKM_RSA_PKCS_PSS, NULL, 0},
|
||||||
|
KEY_RSA, 0, HASH_UNKNOWN},
|
||||||
{SIGN_ECDSA_WITH_NULL, {CKM_ECDSA, NULL, 0},
|
{SIGN_ECDSA_WITH_NULL, {CKM_ECDSA, NULL, 0},
|
||||||
KEY_ECDSA, 0, HASH_UNKNOWN},
|
KEY_ECDSA, 0, HASH_UNKNOWN},
|
||||||
{SIGN_ECDSA_WITH_SHA1_DER, {CKM_ECDSA_SHA1, NULL, 0},
|
{SIGN_ECDSA_WITH_SHA1_DER, {CKM_ECDSA_SHA1, NULL, 0},
|
||||||
@@ -191,6 +195,11 @@ CK_MECHANISM_PTR pkcs11_signature_scheme_to_mech(pkcs11_library_t *p11,
|
|||||||
{SIGN_ECDSA_521, {CKM_ECDSA, NULL, 0},
|
{SIGN_ECDSA_521, {CKM_ECDSA, NULL, 0},
|
||||||
KEY_ECDSA, 521, HASH_SHA512},
|
KEY_ECDSA, 521, HASH_SHA512},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CK_MECHANISM_PTR mechanism;
|
||||||
|
CK_RSA_PKCS_PSS_PARAMS *rsa_pkcs_pss_params;
|
||||||
|
rsa_pss_params_t *rsa_pss_params;
|
||||||
|
hash_algorithm_t hash_alg = HASH_UNKNOWN;;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < countof(mappings); i++)
|
for (i = 0; i < countof(mappings); i++)
|
||||||
@@ -204,11 +213,145 @@ CK_MECHANISM_PTR pkcs11_signature_scheme_to_mech(pkcs11_library_t *p11,
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
mechanism = malloc_thing(CK_MECHANISM);
|
||||||
|
*mechanism = mappings[i].mechanism;
|
||||||
|
|
||||||
|
if (scheme == SIGN_RSA_EMSA_PSS)
|
||||||
|
{
|
||||||
|
rsa_pss_params = (rsa_pss_params_t*)params;
|
||||||
|
INIT(rsa_pkcs_pss_params,
|
||||||
|
.sLen = rsa_pss_params->salt_len
|
||||||
|
);
|
||||||
|
|
||||||
|
if (lib->settings->get_bool(lib->settings,
|
||||||
|
"%s.plugins.pkcs11.use_rsa_pss_hashers", FALSE, lib->ns))
|
||||||
|
{
|
||||||
|
switch (rsa_pss_params->hash)
|
||||||
|
{
|
||||||
|
case HASH_SHA256:
|
||||||
|
mechanism->mechanism = CKM_SHA256_RSA_PKCS_PSS;
|
||||||
|
break;
|
||||||
|
case HASH_SHA384:
|
||||||
|
mechanism->mechanism = CKM_SHA384_RSA_PKCS_PSS;
|
||||||
|
break;
|
||||||
|
case HASH_SHA512:
|
||||||
|
mechanism->mechanism = CKM_SHA512_RSA_PKCS_PSS;
|
||||||
|
break;
|
||||||
|
case HASH_SHA224:
|
||||||
|
mechanism->mechanism = CKM_SHA224_RSA_PKCS_PSS;
|
||||||
|
break;
|
||||||
|
case HASH_SHA1:
|
||||||
|
mechanism->mechanism = CKM_SHA1_RSA_PKCS_PSS;
|
||||||
|
break;
|
||||||
|
case HASH_SHA3_256:
|
||||||
|
mechanism->mechanism = CKM_SHA3_256_RSA_PKCS_PSS;
|
||||||
|
break;
|
||||||
|
case HASH_SHA3_384:
|
||||||
|
mechanism->mechanism = CKM_SHA3_384_RSA_PKCS_PSS;
|
||||||
|
break;
|
||||||
|
case HASH_SHA3_512:
|
||||||
|
mechanism->mechanism = CKM_SHA3_512_RSA_PKCS_PSS;
|
||||||
|
break;
|
||||||
|
case HASH_SHA3_224:
|
||||||
|
mechanism->mechanism = CKM_SHA3_224_RSA_PKCS_PSS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
free(rsa_pkcs_pss_params);
|
||||||
|
free(mechanism);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!is_mechanism_supported(p11, slot, mechanism))
|
||||||
|
{
|
||||||
|
/* revert to external hashing */
|
||||||
|
mechanism->mechanism = CKM_RSA_PKCS_PSS;
|
||||||
|
hash_alg = rsa_pss_params->hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* use external hashing */
|
||||||
|
hash_alg = rsa_pss_params->hash;
|
||||||
|
}
|
||||||
|
switch (rsa_pss_params->hash)
|
||||||
|
{
|
||||||
|
case HASH_SHA256:
|
||||||
|
rsa_pkcs_pss_params->hashAlg = CKM_SHA256;
|
||||||
|
break;
|
||||||
|
case HASH_SHA384:
|
||||||
|
rsa_pkcs_pss_params->hashAlg = CKM_SHA384;
|
||||||
|
break;
|
||||||
|
case HASH_SHA512:
|
||||||
|
rsa_pkcs_pss_params->hashAlg = CKM_SHA512;
|
||||||
|
break;
|
||||||
|
case HASH_SHA224:
|
||||||
|
rsa_pkcs_pss_params->hashAlg = CKM_SHA224;
|
||||||
|
break;
|
||||||
|
case HASH_SHA1:
|
||||||
|
rsa_pkcs_pss_params->hashAlg = CKM_SHA_1;
|
||||||
|
break;
|
||||||
|
case HASH_SHA3_256:
|
||||||
|
rsa_pkcs_pss_params->hashAlg = CKM_SHA3_256;
|
||||||
|
break;
|
||||||
|
case HASH_SHA3_384:
|
||||||
|
rsa_pkcs_pss_params->hashAlg = CKM_SHA3_384;
|
||||||
|
break;
|
||||||
|
case HASH_SHA3_512:
|
||||||
|
rsa_pkcs_pss_params->hashAlg = CKM_SHA3_512;
|
||||||
|
break;
|
||||||
|
case HASH_SHA3_224:
|
||||||
|
rsa_pkcs_pss_params->hashAlg = CKM_SHA3_224;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
free(rsa_pkcs_pss_params);
|
||||||
|
free(mechanism);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
switch (rsa_pss_params->mgf1_hash)
|
||||||
|
{
|
||||||
|
case HASH_SHA256:
|
||||||
|
rsa_pkcs_pss_params->mgf = CKG_MGF1_SHA256;
|
||||||
|
break;
|
||||||
|
case HASH_SHA384:
|
||||||
|
rsa_pkcs_pss_params->mgf = CKG_MGF1_SHA384;
|
||||||
|
break;
|
||||||
|
case HASH_SHA512:
|
||||||
|
rsa_pkcs_pss_params->mgf = CKG_MGF1_SHA512;
|
||||||
|
break;
|
||||||
|
case HASH_SHA224:
|
||||||
|
rsa_pkcs_pss_params->mgf = CKG_MGF1_SHA224;
|
||||||
|
break;
|
||||||
|
case HASH_SHA1:
|
||||||
|
rsa_pkcs_pss_params->mgf = CKG_MGF1_SHA1;
|
||||||
|
break;
|
||||||
|
case HASH_SHA3_256:
|
||||||
|
rsa_pkcs_pss_params->mgf = CKG_MGF1_SHA3_256;
|
||||||
|
break;
|
||||||
|
case HASH_SHA3_384:
|
||||||
|
rsa_pkcs_pss_params->mgf = CKG_MGF1_SHA3_384;
|
||||||
|
break;
|
||||||
|
case HASH_SHA3_512:
|
||||||
|
rsa_pkcs_pss_params->mgf = CKG_MGF1_SHA3_512;
|
||||||
|
break;
|
||||||
|
case HASH_SHA3_224:
|
||||||
|
rsa_pkcs_pss_params->mgf = CKG_MGF1_SHA3_224;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
free(rsa_pkcs_pss_params);
|
||||||
|
free(mechanism);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
mechanism->pParameter = rsa_pkcs_pss_params;
|
||||||
|
mechanism->ulParameterLen = sizeof(CK_RSA_PKCS_PSS_PARAMS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hash_alg = mappings[i].hash;
|
||||||
|
}
|
||||||
if (hash)
|
if (hash)
|
||||||
{
|
{
|
||||||
*hash = mappings[i].hash;
|
*hash = hash_alg;
|
||||||
}
|
}
|
||||||
return &mappings[i].mechanism;
|
return mechanism;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -286,10 +429,11 @@ METHOD(private_key_t, sign, bool,
|
|||||||
CK_RV rv;
|
CK_RV rv;
|
||||||
hash_algorithm_t hash_alg;
|
hash_algorithm_t hash_alg;
|
||||||
chunk_t hash = chunk_empty;
|
chunk_t hash = chunk_empty;
|
||||||
|
bool success = FALSE;
|
||||||
|
|
||||||
mechanism = pkcs11_signature_scheme_to_mech(this->lib, this->slot, scheme,
|
mechanism = pkcs11_signature_scheme_to_mech(this->lib, this->slot, scheme,
|
||||||
this->type, get_keysize(this),
|
params, this->type,
|
||||||
&hash_alg);
|
get_keysize(this), &hash_alg);
|
||||||
if (!mechanism)
|
if (!mechanism)
|
||||||
{
|
{
|
||||||
DBG1(DBG_LIB, "signature scheme %N not supported",
|
DBG1(DBG_LIB, "signature scheme %N not supported",
|
||||||
@@ -301,19 +445,19 @@ METHOD(private_key_t, sign, bool,
|
|||||||
if (rv != CKR_OK)
|
if (rv != CKR_OK)
|
||||||
{
|
{
|
||||||
DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
|
DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
|
||||||
return FALSE;
|
goto end;
|
||||||
}
|
}
|
||||||
rv = this->lib->f->C_SignInit(session, mechanism, this->object);
|
rv = this->lib->f->C_SignInit(session, mechanism, this->object);
|
||||||
if (this->reauth && !reauth(this, session))
|
if (this->reauth && !reauth(this, session))
|
||||||
{
|
{
|
||||||
this->lib->f->C_CloseSession(session);
|
this->lib->f->C_CloseSession(session);
|
||||||
return FALSE;
|
goto end;
|
||||||
}
|
}
|
||||||
if (rv != CKR_OK)
|
if (rv != CKR_OK)
|
||||||
{
|
{
|
||||||
this->lib->f->C_CloseSession(session);
|
this->lib->f->C_CloseSession(session);
|
||||||
DBG1(DBG_LIB, "C_SignInit() failed: %N", ck_rv_names, rv);
|
DBG1(DBG_LIB, "C_SignInit() failed: %N", ck_rv_names, rv);
|
||||||
return FALSE;
|
goto end;
|
||||||
}
|
}
|
||||||
if (hash_alg != HASH_UNKNOWN)
|
if (hash_alg != HASH_UNKNOWN)
|
||||||
{
|
{
|
||||||
@@ -324,7 +468,7 @@ METHOD(private_key_t, sign, bool,
|
|||||||
{
|
{
|
||||||
DESTROY_IF(hasher);
|
DESTROY_IF(hasher);
|
||||||
this->lib->f->C_CloseSession(session);
|
this->lib->f->C_CloseSession(session);
|
||||||
return FALSE;
|
goto end;
|
||||||
}
|
}
|
||||||
hasher->destroy(hasher);
|
hasher->destroy(hasher);
|
||||||
switch (scheme)
|
switch (scheme)
|
||||||
@@ -357,7 +501,7 @@ METHOD(private_key_t, sign, bool,
|
|||||||
{
|
{
|
||||||
DBG1(DBG_LIB, "C_Sign() failed: %N", ck_rv_names, rv);
|
DBG1(DBG_LIB, "C_Sign() failed: %N", ck_rv_names, rv);
|
||||||
free(buf);
|
free(buf);
|
||||||
return FALSE;
|
goto end;
|
||||||
}
|
}
|
||||||
switch (scheme)
|
switch (scheme)
|
||||||
{
|
{
|
||||||
@@ -382,7 +526,12 @@ METHOD(private_key_t, sign, bool,
|
|||||||
*signature = chunk_create(buf, len);
|
*signature = chunk_create(buf, len);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return TRUE;
|
success = TRUE;
|
||||||
|
|
||||||
|
end:
|
||||||
|
free(mechanism->pParameter);
|
||||||
|
free(mechanism);
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
METHOD(private_key_t, decrypt, bool,
|
METHOD(private_key_t, decrypt, bool,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011 Tobias Brunner
|
* Copyright (C) 2011 Tobias Brunner
|
||||||
* Copyright (C) 2010 Martin Willi
|
* Copyright (C) 2010 Martin Willi
|
||||||
|
* Copyright (C) 2024 Andreas Steffen
|
||||||
*
|
*
|
||||||
* Copyright (C) secunet Security Networks AG
|
* Copyright (C) secunet Security Networks AG
|
||||||
*
|
*
|
||||||
@@ -61,6 +62,7 @@ pkcs11_private_key_t *pkcs11_private_key_connect(key_type_t type, va_list args);
|
|||||||
* @param lib PKCS#11 library of the token the key resides on
|
* @param lib PKCS#11 library of the token the key resides on
|
||||||
* @param slot slot of the token
|
* @param slot slot of the token
|
||||||
* @param scheme signature scheme
|
* @param scheme signature scheme
|
||||||
|
* @param params optional signature scheme parameters
|
||||||
* @param type key type
|
* @param type key type
|
||||||
* @param keylen key length in bits
|
* @param keylen key length in bits
|
||||||
* @param hash hash algorithm to apply first (HASH_UNKNOWN if none)
|
* @param hash hash algorithm to apply first (HASH_UNKNOWN if none)
|
||||||
@@ -68,6 +70,7 @@ pkcs11_private_key_t *pkcs11_private_key_connect(key_type_t type, va_list args);
|
|||||||
CK_MECHANISM_PTR pkcs11_signature_scheme_to_mech(pkcs11_library_t *lib,
|
CK_MECHANISM_PTR pkcs11_signature_scheme_to_mech(pkcs11_library_t *lib,
|
||||||
CK_SLOT_ID slot,
|
CK_SLOT_ID slot,
|
||||||
signature_scheme_t scheme,
|
signature_scheme_t scheme,
|
||||||
|
void *params,
|
||||||
key_type_t type, size_t keylen,
|
key_type_t type, size_t keylen,
|
||||||
hash_algorithm_t *hash);
|
hash_algorithm_t *hash);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2011-2015 Tobias Brunner
|
* Copyright (C) 2011-2015 Tobias Brunner
|
||||||
* Copyright (C) 2010 Martin Willi
|
* Copyright (C) 2010 Martin Willi
|
||||||
|
* Copyright (C) 2024 Andreas Steffen
|
||||||
*
|
*
|
||||||
* Copyright (C) secunet Security Networks AG
|
* Copyright (C) secunet Security Networks AG
|
||||||
*
|
*
|
||||||
@@ -209,14 +210,16 @@ METHOD(public_key_t, verify, bool,
|
|||||||
hash_algorithm_t hash_alg;
|
hash_algorithm_t hash_alg;
|
||||||
chunk_t hash = chunk_empty, parse, r, s;
|
chunk_t hash = chunk_empty, parse, r, s;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
bool success = FALSE;
|
||||||
|
|
||||||
mechanism = pkcs11_signature_scheme_to_mech(this->lib, this->slot, scheme,
|
mechanism = pkcs11_signature_scheme_to_mech(this->lib, this->slot, scheme,
|
||||||
this->type, this->k, &hash_alg);
|
params, this->type, this->k,
|
||||||
|
&hash_alg);
|
||||||
if (!mechanism)
|
if (!mechanism)
|
||||||
{
|
{
|
||||||
DBG1(DBG_LIB, "signature scheme %N not supported",
|
DBG1(DBG_LIB, "signature scheme %N not supported",
|
||||||
signature_scheme_names, scheme);
|
signature_scheme_names, scheme);
|
||||||
return FALSE;
|
goto end;
|
||||||
}
|
}
|
||||||
switch (scheme)
|
switch (scheme)
|
||||||
{
|
{
|
||||||
@@ -231,14 +234,14 @@ METHOD(public_key_t, verify, bool,
|
|||||||
asn1_unwrap(&parse, &r) != ASN1_INTEGER ||
|
asn1_unwrap(&parse, &r) != ASN1_INTEGER ||
|
||||||
asn1_unwrap(&parse, &s) != ASN1_INTEGER)
|
asn1_unwrap(&parse, &s) != ASN1_INTEGER)
|
||||||
{
|
{
|
||||||
return FALSE;
|
goto end;
|
||||||
}
|
}
|
||||||
r = chunk_skip_zero(r);
|
r = chunk_skip_zero(r);
|
||||||
s = chunk_skip_zero(s);
|
s = chunk_skip_zero(s);
|
||||||
len = (get_keysize(this) + 7) / 8;
|
len = (get_keysize(this) + 7) / 8;
|
||||||
if (r.len > len || s.len > len)
|
if (r.len > len || s.len > len)
|
||||||
{
|
{
|
||||||
return FALSE;
|
goto end;
|
||||||
}
|
}
|
||||||
/* concatenate r and s (forced to the defined length) */
|
/* concatenate r and s (forced to the defined length) */
|
||||||
sig = chunk_alloca(2*len);
|
sig = chunk_alloca(2*len);
|
||||||
@@ -255,14 +258,14 @@ METHOD(public_key_t, verify, bool,
|
|||||||
if (rv != CKR_OK)
|
if (rv != CKR_OK)
|
||||||
{
|
{
|
||||||
DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
|
DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
|
||||||
return FALSE;
|
goto end;
|
||||||
}
|
}
|
||||||
rv = this->lib->f->C_VerifyInit(session, mechanism, this->object);
|
rv = this->lib->f->C_VerifyInit(session, mechanism, this->object);
|
||||||
if (rv != CKR_OK)
|
if (rv != CKR_OK)
|
||||||
{
|
{
|
||||||
this->lib->f->C_CloseSession(session);
|
this->lib->f->C_CloseSession(session);
|
||||||
DBG1(DBG_LIB, "C_VerifyInit() failed: %N", ck_rv_names, rv);
|
DBG1(DBG_LIB, "C_VerifyInit() failed: %N", ck_rv_names, rv);
|
||||||
return FALSE;
|
goto end;
|
||||||
}
|
}
|
||||||
if (hash_alg != HASH_UNKNOWN)
|
if (hash_alg != HASH_UNKNOWN)
|
||||||
{
|
{
|
||||||
@@ -273,7 +276,7 @@ METHOD(public_key_t, verify, bool,
|
|||||||
{
|
{
|
||||||
DESTROY_IF(hasher);
|
DESTROY_IF(hasher);
|
||||||
this->lib->f->C_CloseSession(session);
|
this->lib->f->C_CloseSession(session);
|
||||||
return FALSE;
|
goto end;
|
||||||
}
|
}
|
||||||
hasher->destroy(hasher);
|
hasher->destroy(hasher);
|
||||||
switch (scheme)
|
switch (scheme)
|
||||||
@@ -299,9 +302,14 @@ METHOD(public_key_t, verify, bool,
|
|||||||
if (rv != CKR_OK)
|
if (rv != CKR_OK)
|
||||||
{
|
{
|
||||||
DBG1(DBG_LIB, "C_Verify() failed: %N", ck_rv_names, rv);
|
DBG1(DBG_LIB, "C_Verify() failed: %N", ck_rv_names, rv);
|
||||||
return FALSE;
|
goto end;
|
||||||
}
|
}
|
||||||
return TRUE;
|
success = TRUE;
|
||||||
|
|
||||||
|
end:
|
||||||
|
free(mechanism->pParameter);
|
||||||
|
free(mechanism);
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
METHOD(public_key_t, encrypt, bool,
|
METHOD(public_key_t, encrypt, bool,
|
||||||
|
|||||||
Reference in New Issue
Block a user