Skip to content

Commit

Permalink
PKCS11: Add support for RSA encryption (#211)
Browse files Browse the repository at this point in the history
PKCS11: Add support for RSA encryption
  • Loading branch information
aveenismail authored Nov 23, 2021
1 parent c68d542 commit 14aa888
Show file tree
Hide file tree
Showing 7 changed files with 692 additions and 156 deletions.
18 changes: 18 additions & 0 deletions pkcs11/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,11 @@ set (
ecdh_derive_test.c
)

set (
SOURCE_RSA_ENC_TEST
rsa_enc_test.c
)

if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Windows")
add_executable (ecdh_derive_test ${SOURCE_ECDH_DERIVE})

Expand All @@ -258,4 +263,17 @@ add_test (
NAME ecdh_derive_test
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ecdh_derive_test ${CMAKE_CURRENT_BINARY_DIR}/../yubihsm_pkcs11.${LIBEXT}
)

add_executable (rsa_enc_test ${SOURCE_RSA_ENC_TEST})

target_link_libraries (
rsa_enc_test
${LIBCRYPTO_LDFLAGS}
"-ldl")

add_test (
NAME rsa_enc_test
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/rsa_enc_test ${CMAKE_CURRENT_BINARY_DIR}/../yubihsm_pkcs11.${LIBEXT}
)

endif()
2 changes: 1 addition & 1 deletion pkcs11/tests/ecdh_derive_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ static void get_function_list(char *argv[]) {
*(void **) (&fn) = dlsym(handle, "C_GetFunctionList");
assert(fn != NULL);

CK_RV rv = ((CK_C_GetFunctionList) fn)(&p11);
CK_RV rv = fn(&p11);
assert(rv == CKR_OK);
}

Expand Down
316 changes: 316 additions & 0 deletions pkcs11/tests/rsa_enc_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,316 @@
/*
* Copyright 2015-2018 Yubico AB
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifdef NDEBUG
#undef NDEBUG
#endif
#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#include <openssl/ec.h>
#include <openssl/x509.h>
#include <openssl/rand.h>
#include <openssl/err.h>

#include "../pkcs11.h"

#ifndef DEFAULT_CONNECTOR_URL
#define DEFAULT_CONNECTOR_URL "http://127.0.0.1:12345"
#endif

#define BUFSIZE 1024

CK_FUNCTION_LIST_PTR p11;
CK_SESSION_HANDLE session;

static void get_function_list(char *argv[]) {
void *handle = dlopen(argv[1], RTLD_NOW | RTLD_GLOBAL);
assert(handle != NULL);
CK_C_GetFunctionList fn;

*(void **) (&fn) = dlsym(handle, "C_GetFunctionList");
assert(fn != NULL);

CK_RV rv = fn(&p11);
assert(rv == CKR_OK);
}

static void open_session() {
CK_C_INITIALIZE_ARGS initArgs;
memset(&initArgs, 0, sizeof(initArgs));

const char *connector_url;
connector_url = getenv("DEFAULT_CONNECTOR_URL");
if (connector_url == NULL) {
connector_url = DEFAULT_CONNECTOR_URL;
}
char config[256];
assert(strlen(connector_url) + strlen("connector=") < 256);
sprintf(config, "connector=%s", connector_url);
initArgs.pReserved = (void *) config;
CK_RV rv = p11->C_Initialize(&initArgs);
assert(rv == CKR_OK);

rv = p11->C_OpenSession(0, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL, NULL,
&session);
assert(rv == CKR_OK);

const char *password = "0001password";
rv = p11->C_Login(session, CKU_USER, (CK_UTF8CHAR_PTR) password,
(CK_ULONG) strlen(password));
assert(rv == CKR_OK);
printf("Session open and authenticated\n");
}

static void close_session() {
CK_RV rv = p11->C_Logout(session);
assert(rv == CKR_OK);

rv = p11->C_Finalize(NULL);
assert(rv == CKR_OK);
}

static void print_session_state() {
CK_SESSION_INFO pInfo;
CK_RV rv = p11->C_GetSessionInfo(session, &pInfo);
assert(rv == CKR_OK);
CK_STATE state = pInfo.state;

printf("session state: ");
switch (state) {
case 0:
printf("read-only public session\n");
break;
case 1:
printf("read-only user functions\n");
break;
case 2:
printf("read-write public session\n");
break;
case 3:
printf("read-write user functions\n");
break;
case 4:
printf("read-write so functions\n");
break;
default:
printf("unknown state\n");
break;
}
}

static bool destroy_object(CK_OBJECT_HANDLE key) {
if ((p11->C_DestroyObject(session, key)) != CKR_OK) {
printf("WARN. Failed to destroy object 0x%lx on HSM. FAIL\n", key);
return false;
}
return true;
}

static void import_rsa_key(int keylen, EVP_PKEY **evp, RSA **rsak,
CK_OBJECT_HANDLE_PTR keyid) {
CK_BYTE e[] = {0x01, 0x00, 0x01};
CK_BYTE *p, *q, *dp, *dq, *qinv;
p = malloc(keylen / 16);
q = malloc(keylen / 16);
dp = malloc(keylen / 16);
dq = malloc(keylen / 16);
qinv = malloc(keylen / 16);

BIGNUM *e_bn;
CK_ULONG class_k = CKO_PRIVATE_KEY;
CK_ULONG kt = CKK_RSA;
CK_BYTE id = 0;
const BIGNUM *bp, *bq, *biqmp, *bdmp1, *bdmq1;

// unsigned char *px;
int p_len, q_len, dp_len, dq_len, qinv_len;
int len = keylen / 16;
CK_BBOOL dec_capability = CK_TRUE;

CK_ATTRIBUTE privateKeyTemplate[] = {{CKA_CLASS, &class_k, sizeof(class_k)},
{CKA_KEY_TYPE, &kt, sizeof(kt)},
{CKA_ID, &id, sizeof(id)},
{CKA_DECRYPT, &dec_capability,
sizeof(dec_capability)},
{CKA_PUBLIC_EXPONENT, e, sizeof(e)},
{CKA_PRIME_1, p, (keylen / 16)},
{CKA_PRIME_2, q, (keylen / 16)},
{CKA_EXPONENT_1, dp, (keylen / 16)},
{CKA_EXPONENT_2, dq, (keylen / 16)},
{CKA_COEFFICIENT, qinv, (keylen / 16)}};
int len_correct = 0;

e_bn = BN_bin2bn(e, 3, NULL);
if (e_bn == NULL)
exit(EXIT_FAILURE);

do {
assert(RSA_generate_key_ex(*rsak, keylen, e_bn, NULL) == 1);

RSA_get0_factors(*rsak, &bp, &bq);
RSA_get0_crt_params(*rsak, &bdmp1, &bdmq1, &biqmp);
p_len = BN_bn2bin(bp, p);
q_len = BN_bn2bin(bq, q);
dp_len = BN_bn2bin(bdmp1, dp);
dq_len = BN_bn2bin(bdmq1, dq);
qinv_len = BN_bn2bin(biqmp, qinv);
len_correct = p_len == len && q_len == len && dp_len == len &&
dq_len == len && qinv_len == len;
} while (!len_correct);

if (EVP_PKEY_set1_RSA(*evp, *rsak) == 0)
exit(EXIT_FAILURE);

assert(p11->C_CreateObject(session, privateKeyTemplate, 10, keyid) == CKR_OK);

BN_free(e_bn);
free(p);
free(q);
free(dp);
free(dq);
free(qinv);
}

static void test_rsa_encrypt(CK_OBJECT_HANDLE keyid, RSA *rsak,
CK_MECHANISM_TYPE mech_type, int padding,
CK_ULONG expected_enc_len) {
CK_BYTE data[32] = {0};
CK_ULONG data_len = sizeof(data);
CK_BYTE enc[1024] = {0};
CK_ULONG enc_len;
CK_BYTE dec[512] = {0};
CK_BYTE dec_internal[512] = {0};
CK_ULONG dec_internal_len;

CK_RSA_PKCS_OAEP_PARAMS params = {0};
CK_MECHANISM mech = {mech_type, &params, sizeof(params)};
if (mech_type == CKM_RSA_PKCS) {
mech.pParameter = NULL;
mech.ulParameterLen = 0;
}

if (RAND_bytes(data, data_len) <= 0)
exit(EXIT_FAILURE);

// Encrypt
assert(p11->C_EncryptInit(session, &mech, keyid) == CKR_OK);
enc_len = 0;
assert(p11->C_Encrypt(session, data, data_len, NULL, &enc_len) == CKR_OK);
assert(enc_len == expected_enc_len);
assert(p11->C_Encrypt(session, data, data_len, enc, &enc_len) == CKR_OK);
assert(enc_len == expected_enc_len);

CK_ULONG err;
RSA_private_decrypt((int) enc_len, (unsigned char *) enc,
(unsigned char *) dec, rsak, padding);
if ((err = ERR_get_error())) {
ERR_load_crypto_strings();
fprintf(stderr, "RSA_private_decrypt:[%lu](%s)\n", err,
ERR_error_string(err, NULL));
}
assert(memcmp(dec, data, data_len) == 0);

if (mech_type == CKM_RSA_PKCS) {
assert(p11->C_DecryptInit(session, &mech, keyid) == CKR_OK);
dec_internal_len = 0;
assert(p11->C_Decrypt(session, enc, enc_len, NULL, &dec_internal_len) ==
CKR_OK);
assert(p11->C_Decrypt(session, enc, enc_len, dec_internal,
&dec_internal_len) == CKR_OK);
assert(memcmp(dec_internal, data, data_len) == 0);
}

// Encrypt Update
assert(p11->C_EncryptInit(session, &mech, keyid) == CKR_OK);
enc_len = sizeof(enc);
assert(p11->C_EncryptUpdate(session, data, 10, enc, &enc_len) == CKR_OK);
enc_len = sizeof(enc);
assert(p11->C_EncryptUpdate(session, data + 10, 22, enc, &enc_len) == CKR_OK);
enc_len = 0;
assert(p11->C_EncryptFinal(session, NULL, &enc_len) == CKR_OK);
assert(enc_len == expected_enc_len);
assert(p11->C_EncryptFinal(session, enc, &enc_len) == CKR_OK);

RSA_private_decrypt(enc_len, enc, dec, rsak, padding);
if ((err = ERR_get_error())) {
ERR_load_crypto_strings();
fprintf(stderr, "RSA_private_decrypt:[%lu](%s)\n", err,
ERR_error_string(err, NULL));
}
assert(memcmp(dec, data, data_len) == 0);

if (mech_type == CKM_RSA_PKCS) {
// Decrypt Update
assert(p11->C_DecryptInit(session, &mech, keyid) == CKR_OK);
dec_internal_len = sizeof(dec_internal);
assert(p11->C_DecryptUpdate(session, enc, 10, NULL, &dec_internal_len) ==
CKR_OK);
dec_internal_len = sizeof(dec_internal);
assert(p11->C_DecryptUpdate(session, enc + 10, enc_len - 10, NULL,
&dec_internal_len) == CKR_OK);
dec_internal_len = 0;
assert(p11->C_DecryptFinal(session, NULL, &dec_internal_len) == CKR_OK);
assert(p11->C_DecryptFinal(session, dec_internal, &dec_internal_len) ==
CKR_OK);
assert(memcmp(dec_internal, data, data_len) == 0);
}
}

static void test_encrypt_RSA(int keysize, CK_ULONG expected_enc_len) {
EVP_PKEY *evp = EVP_PKEY_new();
RSA *rsak = RSA_new();
CK_OBJECT_HANDLE keyid;

import_rsa_key(keysize, &evp, &rsak, &keyid);
if (evp == NULL || rsak == NULL)
exit(EXIT_FAILURE);

printf("RSA %d : RSA_PKCS1_PADDING\n", keysize);
test_rsa_encrypt(keyid, rsak, CKM_RSA_PKCS, RSA_PKCS1_PADDING,
expected_enc_len);

printf("RSA %d : RSA_PKCS1_OAEP_PADDING\n", keysize);
test_rsa_encrypt(keyid, rsak, CKM_RSA_PKCS_OAEP, RSA_PKCS1_OAEP_PADDING,
expected_enc_len);

RSA_free(rsak);
EVP_PKEY_free(evp);
destroy_object(keyid);
}

int main(int argc, char **argv) {

if (argc != 2) {
fprintf(stderr, "usage: /path/to/yubihsm_pkcs11/module\n");
exit(EXIT_FAILURE);
}

get_function_list(argv);
open_session();
print_session_state();

test_encrypt_RSA(2048, 256);
test_encrypt_RSA(3072, 384);
test_encrypt_RSA(4096, 512);

close_session();
return (EXIT_SUCCESS);
}
Loading

0 comments on commit 14aa888

Please sign in to comment.