From 9bf245ea2afb75c4180d7fac707bb0c1bcb6b17d Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Fri, 5 Dec 2008 20:23:01 +0000 Subject: Implement RSA encrypt and decrypt. --- configure.in | 9 ++ doc/pkcs11-coverage.txt | 8 ++ src/Makefile.am | 5 +- src/check.c | 11 +++ src/key.c | 78 +++++++++++++++ src/object.c | 48 +++++++++- src/p11-tests.c | 1 + src/p11-tests.h | 38 ++++++++ src/rsa-pkcs.c | 250 ++++++++++++++++++++++++++++++++++++++++++++++++ src/slot.c | 20 ++++ src/test-data.c | 70 ++++++++++++++ 11 files changed, 535 insertions(+), 3 deletions(-) create mode 100644 src/key.c create mode 100644 src/rsa-pkcs.c create mode 100644 src/test-data.c diff --git a/configure.in b/configure.in index f046226..9a3d42a 100644 --- a/configure.in +++ b/configure.in @@ -9,6 +9,15 @@ AM_CONFIG_HEADER([config.h]) AC_PROG_CC AC_PROG_INSTALL +# Depend on openssl +AC_CHECK_LIB(crypto, BN_new, , + [echo "ERROR: Must have openssl library."; exit 1] +) + +AC_CHECK_HEADERS([openssl/rsa.h], , + [echo "ERROR: Must have openssl headers"; exit 1] +) + CFLAGS="$CFLAGS -g -O0 -Wall" AC_CONFIG_FILES([ diff --git a/doc/pkcs11-coverage.txt b/doc/pkcs11-coverage.txt index 2c1abec..b96ee08 100644 --- a/doc/pkcs11-coverage.txt +++ b/doc/pkcs11-coverage.txt @@ -24,9 +24,17 @@ C_CopyObject C_CreateObject - Not Implemented +C_Decrypt +- CKM_RSA_PKCS +- CKM_RSA_X_509 + C_DestroyObject - Not Implemented +C_Encrypt +- CKM_RSA_PKCS +- CKM_RSA_X_509 + C_Finalize - With invalid argument - Normal call diff --git a/src/Makefile.am b/src/Makefile.am index 0a725c1..6e51a6f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,10 +6,13 @@ p11_tests_LDADD = -ldl -lpthread p11_tests_SOURCES = \ check.c \ config.c \ + key.c \ module.c \ msg.c \ object.c \ p11-tests.c \ + rsa-pkcs.c \ session.c \ - slot.c + slot.c \ + test-data.c diff --git a/src/check.c b/src/check.c index 01b19fb..334a9e7 100644 --- a/src/check.c +++ b/src/check.c @@ -4,6 +4,17 @@ #include "p11-tests.h" #include +#include + +int +p11t_check_fail(const char *message, ...) +{ + va_list va; + va_start(va, message); + p11t_msg_va(message, va); + va_end(va); + return 0; +} int p11t_check_returns(const char *message, CK_RV have, CK_RV want) diff --git a/src/key.c b/src/key.c new file mode 100644 index 0000000..f8b79ae --- /dev/null +++ b/src/key.c @@ -0,0 +1,78 @@ + +#include "config.h" + +#include "p11-tests.h" + +CK_OBJECT_HANDLE +find_related_object(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key, CK_OBJECT_CLASS klass) +{ + CK_BYTE id[4096]; + CK_ATTRIBUTE attrs[2]; + + attrs[0].type = CKA_ID; + attrs[0].pValue = id; + attrs[0].ulValueLen = sizeof(id); + + if(!p11t_object_get(session, key, attrs, 1)) + return CK_INVALID; + + attrs[1].type = CKA_CLASS; + attrs[1].ulValueLen = sizeof(klass); + attrs[1].pValue = &klass; + + return p11t_object_find_one(session, attrs, 2); +} + +CK_OBJECT_HANDLE +p11t_key_get_public(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) +{ + return find_related_object(session, key, CKO_PUBLIC_KEY); +} + +CK_OBJECT_HANDLE +p11t_key_get_private(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) +{ + return find_related_object(session, key, CKO_PRIVATE_KEY); +} + +RSA* +p11t_key_export_public_rsa(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) +{ + CK_ATTRIBUTE attrs[3]; + CK_BYTE modulus[4096]; + CK_BYTE public_exponent[4096]; + CK_ULONG modulus_bits; + RSA *rsa; + + attrs[0].type = CKA_MODULUS; + attrs[0].ulValueLen = sizeof(modulus); + attrs[0].pValue = modulus; + + attrs[1].type = CKA_MODULUS_BITS; + attrs[1].ulValueLen = sizeof(modulus_bits); + attrs[1].pValue = &modulus_bits; + + attrs[2].type = CKA_PUBLIC_EXPONENT; + attrs[2].ulValueLen = sizeof(public_exponent); + attrs[2].pValue = public_exponent; + + if(!p11t_object_get(session, key, attrs, 3)) + return NULL; + + if(attrs[0].ulValueLen == CK_INVALID || + attrs[2].ulValueLen == CK_INVALID) + return NULL; + + rsa = RSA_new(); + rsa->n = BN_bin2bn(modulus, attrs[0].ulValueLen, NULL); + rsa->e = BN_bin2bn(public_exponent, attrs[2].ulValueLen, NULL); + assert(rsa && rsa->n && rsa->e); + + if(attrs[1].ulValueLen != CK_INVALID) + { + assert(RSA_size(rsa) == modulus_bits / 8); + } + + return rsa; +} + diff --git a/src/object.c b/src/object.c index 87f89e2..fe4e801 100644 --- a/src/object.c +++ b/src/object.c @@ -364,7 +364,6 @@ p11t_object_find(CK_SESSION_HANDLE session, CK_ATTRIBUTE_PTR attrs, { CK_OBJECT_HANDLE_PTR objects; CK_ATTRIBUTE dummy_attr; - CK_ULONG count; assert(p11t_module_funcs); @@ -387,7 +386,7 @@ p11t_object_find(CK_SESSION_HANDLE session, CK_ATTRIBUTE_PTR attrs, return NULL; } - if(!count) + if(!*n_objects) { free(objects); return NULL; @@ -396,3 +395,48 @@ p11t_object_find(CK_SESSION_HANDLE session, CK_ATTRIBUTE_PTR attrs, return objects; } +CK_OBJECT_HANDLE +p11t_object_find_one(CK_SESSION_HANDLE session, CK_ATTRIBUTE_PTR attrs, + CK_ULONG n_attrs) +{ + CK_OBJECT_HANDLE object; + CK_ULONG count; + + assert(p11t_module_funcs); + + if((p11t_module_funcs->C_FindObjectsInit)(session, attrs, n_attrs) != CKR_OK) + return CK_INVALID; + + if((p11t_module_funcs->C_FindObjects)(session, &object, 1, &count) != CKR_OK) + return CK_INVALID; + + if(count != 1) + return CK_INVALID; + + return object; +} + + +int +p11t_object_get(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, + CK_ATTRIBUTE_PTR attrs, CK_ULONG count) +{ + CK_RV rv; + + assert(p11t_module_funcs); + + if(!count) + return 1; + + rv = (p11t_module_funcs->C_GetAttributeValue)(session, object, attrs, count); + switch(rv) + { + case CKR_OK: + case CKR_BUFFER_TOO_SMALL: + case CKR_ATTRIBUTE_TYPE_INVALID: + case CKR_ATTRIBUTE_SENSITIVE: + return 1; + } + + return 0; +} diff --git a/src/p11-tests.c b/src/p11-tests.c index 689dba4..58d547a 100644 --- a/src/p11-tests.c +++ b/src/p11-tests.c @@ -54,6 +54,7 @@ main(int argc, char* argv[]) p11t_slot_tests(); p11t_session_tests(); p11t_object_tests(); + p11t_rsa_tests(); /* Remaining module tests */ p11t_module_finalize(); diff --git a/src/p11-tests.h b/src/p11-tests.h index b3ddc1e..c4a2e25 100644 --- a/src/p11-tests.h +++ b/src/p11-tests.h @@ -6,6 +6,8 @@ #include #include +#include + #define CK_INVALID ((CK_ULONG)-1) extern int p11t_test_unexpected; @@ -29,6 +31,8 @@ void p11t_msg_prefix(const char *prefix); * check.c */ +int p11t_check_fail(const char *message, ...); + int p11t_check_returns(const char *message, CK_RV have, CK_RV want); #define p11t_check_padded(msg, padded) \ @@ -55,6 +59,16 @@ int p11t_check_string(const char *message, CK_UTF8CHAR_PTR value, CK_ULONG lengt void p11t_config_parse(const char* filename); void p11t_config_cleanup(void); +/* ------------------------------------------------------------------- + * key.c + */ + +CK_OBJECT_HANDLE p11t_key_get_public(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key); + +CK_OBJECT_HANDLE p11t_key_get_private(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key); + +RSA* p11t_key_export_public_rsa(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key); + /* ------------------------------------------------------------------- * module.c */ @@ -76,8 +90,20 @@ void p11t_module_finalize(void); CK_OBJECT_HANDLE_PTR p11t_object_find(CK_SESSION_HANDLE session, CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, CK_ULONG_PTR n_objects); +int p11t_object_get(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, + CK_ATTRIBUTE_PTR attrs, CK_ULONG count); + +CK_OBJECT_HANDLE p11t_object_find_one(CK_SESSION_HANDLE session, CK_ATTRIBUTE_PTR attrs, + CK_ULONG n_attrs); + void p11t_object_tests(void); +/* ------------------------------------------------------------------- + * rsa.c + */ + +void p11t_rsa_tests(void); + /* ------------------------------------------------------------------- * session.c */ @@ -104,5 +130,17 @@ CK_SLOT_ID p11t_slot_get_id(int index); CK_SLOT_INFO_PTR p11t_slot_get_info(CK_SLOT_ID slot); CK_TOKEN_INFO_PTR p11t_slot_get_token_info(CK_SLOT_ID slot); +typedef void (*P11tSlotMechCallback)(CK_SLOT_ID slot, CK_MECHANISM_TYPE mech_type, + CK_MECHANISM_INFO_PTR mech_info); + +void p11t_slot_for_each_mech(CK_MECHANISM_TYPE mech_type, P11tSlotMechCallback callback); + +/* ------------------------------------------------------------------ + * test-data.c + */ + +extern const CK_BYTE p11t_test_data[]; +extern const CK_ULONG p11t_test_data_size; +extern const CK_ULONG p11t_test_data_bits; #endif /* P11TESTST_H_ */ diff --git a/src/rsa-pkcs.c b/src/rsa-pkcs.c new file mode 100644 index 0000000..e97a3b0 --- /dev/null +++ b/src/rsa-pkcs.c @@ -0,0 +1,250 @@ + +#include "config.h" + +#include "p11-tests.h" + +#include +#include +#include + +void +test_rsa_decrypt(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key, + CK_MECHANISM_TYPE mech_type, RSA* rsa) +{ + CK_BYTE encrypted[p11t_test_data_size]; + CK_BYTE decrypted[p11t_test_data_size]; + CK_MECHANISM mech; + const CK_BYTE* data; + CK_ULONG n_data, n_decrypted; + int size, n_encrypted; + CK_RV rv; + + /** C_Decrypt */ + + data = p11t_test_data; + n_data = p11t_test_data_size; + + size = RSA_size(rsa); + assert(size); + assert(size < sizeof(encrypted)); + assert(size < sizeof(decrypted)); + assert(size < n_data); + + switch(mech_type) + { + case CKM_RSA_PKCS: + /** - CKM_RSA_PKCS */ + n_data = size - 11; + n_encrypted = RSA_public_encrypt(n_data, data, encrypted, rsa, RSA_PKCS1_PADDING); + assert(n_encrypted == size); + break; + case CKM_RSA_X_509: + /** - CKM_RSA_X_509 */ + n_data = size; + n_encrypted = RSA_public_encrypt(n_data, data, encrypted, rsa, RSA_NO_PADDING); + assert(n_encrypted == size); + break; + default: + return; + }; + + mech.mechanism = mech_type; + mech.pParameter = NULL; + mech.ulParameterLen = 0; + + rv = (p11t_module_funcs->C_DecryptInit)(session, &mech, key); + p11t_check_returns("C_DecryptInit: rsa", rv, CKR_OK); + + n_decrypted = sizeof(decrypted); + rv = (p11t_module_funcs->C_Decrypt)(session, encrypted, n_encrypted, decrypted, &n_decrypted); + p11t_check_returns("C_Decrypt: rsa", rv, CKR_OK); + + if(n_decrypted != n_data) + p11t_check_fail("C_Decrypt: rsa decrypt failed, wrong length"); + if(memcmp(data, decrypted, n_data) != 0) + p11t_check_fail("C_Decrypt: rsa decrypt failed, bad data"); +} + +void +test_rsa_encrypt(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key, + CK_MECHANISM_TYPE mech_type, RSA* rsa) +{ + CK_BYTE encrypted[p11t_test_data_size]; + CK_BYTE check[p11t_test_data_size]; + CK_OBJECT_HANDLE privkey; + CK_MECHANISM mech; + const CK_BYTE* data; + CK_ULONG n_data, n_check, n_encrypted; + int size; + CK_RV rv; + + data = p11t_test_data; + n_data = p11t_test_data_size; + + /** C_Encrypt */ + + size = RSA_size(rsa); + assert(size); + assert(size < sizeof(encrypted)); + assert(size < sizeof(check)); + assert(size < n_data); + + switch(mech_type) + { + case CKM_RSA_PKCS: + /** - CKM_RSA_PKCS */ + n_data = size - 11; + break; + case CKM_RSA_X_509: + /** - CKM_RSA_X_509 */ + n_data = size; + break; + default: + return; + }; + + mech.mechanism = mech_type; + mech.pParameter = NULL; + mech.ulParameterLen = 0; + + /* Now ask PKCS#11 to decrypt it */ + rv = (p11t_module_funcs->C_EncryptInit)(session, &mech, key); + p11t_check_returns("C_EncryptInit: rsa", rv, CKR_OK); + + n_encrypted = sizeof(encrypted); + rv = (p11t_module_funcs->C_Encrypt)(session, (CK_BYTE*)data, n_data, encrypted, &n_encrypted); + p11t_check_returns("C_Encrypt: rsa", rv, CKR_OK); + + if(size != n_encrypted) + p11t_check_fail("C_Encrypt: rsa encrypt failed, wrong length"); + + /* We need to find a private key in order to validate */ + privkey = p11t_key_get_private(session, key); + if(privkey == CK_INVALID) + return; + + rv = (p11t_module_funcs->C_DecryptInit)(session, &mech, privkey); + p11t_check_returns("C_DecryptInit: rsa validate", rv, CKR_OK); + + n_check = sizeof(check); + rv = (p11t_module_funcs->C_Decrypt)(session, encrypted, n_encrypted, check, &n_check); + p11t_check_returns("C_Decrypt: rsa validate", rv, CKR_OK); + + if(n_check != n_data) + p11t_check_fail("C_Decrypt: rsa validate failed, wrong length"); + if(memcmp(data, check, n_data) != 0) + p11t_check_fail("C_Decrypt: rsa validate failed, bad data"); +} + +void +test_rsa_private_key(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key, + CK_MECHANISM_TYPE mech_type) +{ + CK_ATTRIBUTE attrs[2]; + CK_BBOOL can_decrypt = 0; + CK_BBOOL can_sign = 0; + RSA *rsa; + + attrs[0].type = CKA_DECRYPT; + attrs[0].ulValueLen = sizeof(CK_BBOOL); + attrs[0].pValue = &can_decrypt; + attrs[1].type = CKA_SIGN; + attrs[1].ulValueLen = sizeof(CK_BBOOL); + attrs[1].pValue = &can_sign; + + if(!p11t_object_get(session, key, attrs, 2)) + return; + + rsa = p11t_key_export_public_rsa(session, key); + if(!rsa) + return; + + if(can_decrypt) + test_rsa_decrypt(session, key, mech_type, rsa); + + RSA_free(rsa); +} + +void +test_rsa_public_key(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key, + CK_MECHANISM_TYPE mech_type) +{ + CK_ATTRIBUTE attrs[2]; + CK_BBOOL can_encrypt = 0; + CK_BBOOL can_verify = 0; + RSA *rsa; + + attrs[0].type = CKA_ENCRYPT; + attrs[0].ulValueLen = sizeof(CK_BBOOL); + attrs[0].pValue = &can_encrypt; + attrs[1].type = CKA_VERIFY; + attrs[1].ulValueLen = sizeof(CK_BBOOL); + attrs[1].pValue = &can_verify; + + if(!p11t_object_get(session, key, attrs, 2)) + return; + + rsa = p11t_key_export_public_rsa(session, key); + if(!rsa) + return; + + if(can_encrypt) + test_rsa_encrypt(session, key, mech_type, rsa); + + RSA_free(rsa); +} + +void +test_rsa_pkcs(CK_SLOT_ID slot, CK_MECHANISM_TYPE mech, CK_MECHANISM_INFO_PTR info) +{ + CK_SESSION_HANDLE session; + CK_ATTRIBUTE attrs[2]; + CK_ATTRIBUTE attr; + CK_OBJECT_CLASS klass; + CK_KEY_TYPE key_type = CKK_RSA; + CK_BBOOL token = CK_TRUE; + CK_OBJECT_HANDLE_PTR keys; + CK_ULONG i, n_keys; + + /* Find all keys that match this mechanism */ + attrs[0].type = CKA_TOKEN; + attrs[0].ulValueLen = sizeof(token); + attrs[0].pValue = &token; + attrs[1].type = CKA_KEY_TYPE; + attrs[1].ulValueLen = sizeof(key_type); + attrs[1].pValue = &key_type; + + session = p11t_session_open(slot, 0); + if(session == CK_INVALID) + return; + + if(!p11t_session_login(session)) + return; + + keys = p11t_object_find(session, attrs, 2, &n_keys); + if(!keys) + return; + + for(i = 0; i < n_keys; ++i) + { + attr.type = CKA_CLASS; + attr.ulValueLen = sizeof(klass); + attr.pValue = &klass; + + if(p11t_object_get(session, keys[i], &attr, 1)) + { + if(klass == CKO_PRIVATE_KEY) + test_rsa_private_key(session, keys[i], mech); + else if(klass == CKO_PUBLIC_KEY) + test_rsa_public_key(session, keys[i], mech); + } + } + + free(keys); +} + +void +p11t_rsa_tests(void) +{ + p11t_slot_for_each_mech(CKM_RSA_PKCS, test_rsa_pkcs); +} diff --git a/src/slot.c b/src/slot.c index e10bb4d..c9139d6 100644 --- a/src/slot.c +++ b/src/slot.c @@ -15,6 +15,7 @@ static CK_SLOT_INFO_PTR slot_info = NULL; static CK_TOKEN_INFO_PTR slot_token_info = NULL; static CK_ULONG *slot_mech_count; +static CK_MECHANISM_TYPE_PTR *slot_mech_type; static CK_MECHANISM_INFO_PTR *slot_mech_info; /* ---------------------------------------------------------------------------------- @@ -288,6 +289,8 @@ test_slot_mechanisms(void) slot_mech_info = calloc(p11t_slot_count, sizeof(CK_MECHANISM_INFO_PTR)); assert(slot_mech_info); + slot_mech_type = calloc(p11t_slot_count, sizeof(CK_MECHANISM_TYPE_PTR)); + assert(slot_mech_type); for(i = 0; i < p11t_slot_count; ++i) { @@ -377,6 +380,7 @@ test_slot_mechanisms(void) slot_mech_info[i] = mech_info; } + slot_mech_type[i] = mech_list; slot_mech_count[i] = mech_count; } } @@ -446,3 +450,19 @@ p11t_slot_get_token_info(CK_SLOT_ID slot) return NULL; } + +void +p11t_slot_for_each_mech(CK_MECHANISM_TYPE mech_type, + P11tSlotMechCallback callback) +{ + CK_ULONG i, j; + + for(i = 0; i < p11t_slot_count; ++i) + { + for(j = 0; j < slot_mech_count[i]; ++j) + { + if(mech_type == CK_INVALID || slot_mech_type[i][j] == mech_type) + (callback)(slot_ids[i], slot_mech_type[i][j], &slot_mech_info[i][j]); + } + } +} diff --git a/src/test-data.c b/src/test-data.c new file mode 100644 index 0000000..6d94e74 --- /dev/null +++ b/src/test-data.c @@ -0,0 +1,70 @@ + +#include "config.h" + +#include "p11-tests.h" + +const CK_BYTE p11t_test_data[] = + "0123456789ABCD 1" \ + "0123456789ABCD 2" \ + "0123456789ABCD 3" \ + "0123456789ABCD 4" \ + "0123456789ABCD 5" \ + "0123456789ABCD 6" \ + "0123456789ABCD 7" \ + "0123456789ABCD 8" \ + "0123456789ABCD 9" \ + "0123456789ABCD A" \ + "0123456789ABCD B" \ + "0123456789ABCD C" \ + "0123456789ABCD D" \ + "0123456789ABCD E" \ + "0123456789ABCD F" \ + "0123456789ABCD 1" \ + "0123456789ABCD 2" \ + "0123456789ABCD 3" \ + "0123456789ABCD 4" \ + "0123456789ABCD 5" \ + "0123456789ABCD 6" \ + "0123456789ABCD 7" \ + "0123456789ABCD 8" \ + "0123456789ABCD 9" \ + "0123456789ABCD A" \ + "0123456789ABCD B" \ + "0123456789ABCD C" \ + "0123456789ABCD D" \ + "0123456789ABCD E" \ + "0123456789ABCD F" \ + "0123456789ABCD 1" \ + "0123456789ABCD 2" \ + "0123456789ABCD 3" \ + "0123456789ABCD 4" \ + "0123456789ABCD 5" \ + "0123456789ABCD 6" \ + "0123456789ABCD 7" \ + "0123456789ABCD 8" \ + "0123456789ABCD 9" \ + "0123456789ABCD A" \ + "0123456789ABCD B" \ + "0123456789ABCD C" \ + "0123456789ABCD D" \ + "0123456789ABCD E" \ + "0123456789ABCD F" \ + "0123456789ABCD 1" \ + "0123456789ABCD 2" \ + "0123456789ABCD 3" \ + "0123456789ABCD 4" \ + "0123456789ABCD 5" \ + "0123456789ABCD 6" \ + "0123456789ABCD 7" \ + "0123456789ABCD 8" \ + "0123456789ABCD 9" \ + "0123456789ABCD A" \ + "0123456789ABCD B" \ + "0123456789ABCD C" \ + "0123456789ABCD D" \ + "0123456789ABCD E" \ + "0123456789ABCD F"; + +const CK_ULONG p11t_test_data_size = 1024; +const CK_ULONG p11t_test_data_bits = 1024 * 8; + -- cgit v1.2.3