diff --git a/doc/dox_comments/header_files/pkcs11.h b/doc/dox_comments/header_files/pkcs11.h index 7871ab43317..2eeb29ebdba 100644 --- a/doc/dox_comments/header_files/pkcs11.h +++ b/doc/dox_comments/header_files/pkcs11.h @@ -11,11 +11,24 @@ void wc_Pkcs11_Finalize(Pkcs11Dev* dev); /*! \ingroup PKCS11 + + \brief Sets up a token by slot id or name. A user PIN is copied into + memory the token owns, which wc_Pkcs11Token_Final zeroizes and frees. */ int wc_Pkcs11Token_Init(Pkcs11Token* token, Pkcs11Dev* dev, int slotId, const char* tokenName, const unsigned char *userPin, int userPinSz); +/*! + \ingroup PKCS11 + + \brief Sets up a token by name. A user PIN is copied as by + wc_Pkcs11Token_Init. +*/ +int wc_Pkcs11Token_InitName(Pkcs11Token* token, Pkcs11Dev* dev, + const char* tokenName, int tokenNameSz, + const unsigned char* userPin, int userPinSz); + /*! \ingroup PKCS11 */ @@ -29,19 +42,63 @@ void wc_Pkcs11Token_Final(Pkcs11Token* token); with an id or label on the token return BAD_STATE_E, and a key with neither is generated in software. + Calling this again while the session is open does nothing and keeps the + session's read/write mode. + \return 0 on success. \return BAD_FUNC_ARG when token is NULL. - \return WC_HW_E when opening the session fails. + \return BAD_STATE_E when wc_Pkcs11Token_Open_ex discarded the PIN and its + session was closed. + \return WC_HW_E when opening the session or logging in fails. A session + whose login failed is closed again. \param token Token to open the session on. \param readWrite 1 for a read-write session, 0 for read-only. + \sa wc_Pkcs11Token_Open_ex \sa wc_Pkcs11Token_Close */ int wc_Pkcs11Token_Open(Pkcs11Token* token, int readWrite); /*! \ingroup PKCS11 + + \brief Opens a session as wc_Pkcs11Token_Open does. With discardPin set, + the token's copy of the user PIN is zeroized and freed once this call has + opened the session and logged it in, by its own login or because another + token already logged the application in. A session that was already open + keeps the PIN. The login then lasts while a session of the + application is open. After wc_Pkcs11Token_Close, opening again returns + BAD_STATE_E until wc_Pkcs11Token_Final and a new init with the PIN, and + operations open sessions without logging in. Discarding the PIN is not + safe while other threads use the token. + + \return 0 on success. + \return BAD_FUNC_ARG when token is NULL. + \return BAD_STATE_E when an earlier open discarded the PIN and its session + was closed. + \return WC_HW_E when opening the session or logging in fails. A session + whose login failed is closed again. + + \param token Token to open the session on. + \param readWrite 1 for a read-write session, 0 for read-only. + \param discardPin 1 to discard the PIN once the session is logged in. + + \sa wc_Pkcs11Token_Open + \sa wc_Pkcs11Token_Final + */ +int wc_Pkcs11Token_Open_ex(Pkcs11Token* token, int readWrite, int discardPin); + +/*! + \ingroup PKCS11 + + \brief Closes the session opened with wc_Pkcs11Token_Open. It does not log + out, so the application stays logged in while another of its sessions on + the token is open. + + \param token Token whose session to close. + + \sa wc_Pkcs11Token_Open */ void wc_Pkcs11Token_Close(Pkcs11Token* token); diff --git a/tests/api/test_pkcs11.c b/tests/api/test_pkcs11.c index 09e54e79385..79f89f3d51d 100644 --- a/tests/api/test_pkcs11.c +++ b/tests/api/test_pkcs11.c @@ -733,7 +733,6 @@ int test_wc_Pkcs11_FreeKeepsTokenKey(void) return EXPECT_RESULT(); } -#ifdef TEST_PKCS11_EC_KEYGEN static int test_pkcs11_make_ecc(ecc_key* key, WC_RNG* rng, const byte* id, int idLen) { @@ -747,7 +746,6 @@ static int test_pkcs11_make_ecc(ecc_key* key, WC_RNG* rng, const byte* id, #endif return ret; } -#endif #if defined(TEST_PKCS11_EC_KEYGEN) && defined(HAVE_ECC_KEY_IMPORT) static CK_OBJECT_HANDLE test_pkcs11_find_obj(CK_OBJECT_CLASS cls, @@ -1151,6 +1149,12 @@ int test_wc_Pkcs11_EcKeyUsage(void) return EXPECT_RESULT(); } +static CK_RV test_pkcs11_login_rv = CKR_OK; +static int test_pkcs11_logins = 0; +static byte test_pkcs11_login_pin[8]; +static CK_ULONG test_pkcs11_login_pinLen = 0; +static int test_pkcs11_logouts = 0; +static int test_pkcs11_closes = 0; static int test_pkcs11_opens = 0; static CK_RV test_pkcs11_open_session(CK_SLOT_ID slotId, CK_FLAGS flags, @@ -1166,13 +1170,172 @@ static CK_RV test_pkcs11_open_session(CK_SLOT_ID slotId, CK_FLAGS flags, return CKR_OK; } +static CK_RV test_pkcs11_open_session_fail(CK_SLOT_ID slotId, CK_FLAGS flags, + CK_VOID_PTR app, CK_NOTIFY notify, CK_SESSION_HANDLE_PTR session) +{ + (void)slotId; + (void)flags; + (void)app; + (void)notify; + + *session = 7; + return TEST_CKR_GENERAL_ERROR; +} + +static CK_RV test_pkcs11_login(CK_SESSION_HANDLE session, CK_USER_TYPE type, + CK_UTF8CHAR_PTR pin, CK_ULONG pinLen) +{ + (void)session; + (void)type; + + test_pkcs11_login_pinLen = pinLen; + if (pin != NULL && pinLen <= sizeof(test_pkcs11_login_pin)) + XMEMCPY(test_pkcs11_login_pin, pin, pinLen); + test_pkcs11_logins++; + return test_pkcs11_login_rv; +} + +static CK_RV test_pkcs11_logout(CK_SESSION_HANDLE session) +{ + (void)session; + + test_pkcs11_logouts++; + return CKR_OK; +} + static CK_RV test_pkcs11_close_session(CK_SESSION_HANDLE session) { (void)session; + test_pkcs11_closes++; + return CKR_OK; +} + +static CK_RV test_pkcs11_slot_info(CK_SLOT_ID slotId, CK_SLOT_INFO_PTR info) +{ + (void)slotId; + + XMEMSET(info, 0, sizeof(*info)); return CKR_OK; } +static CK_RV test_pkcs11_slot_list(CK_BBOOL present, CK_SLOT_ID_PTR list, + CK_ULONG_PTR cnt) +{ + (void)present; + + if (list != NULL) + list[0] = 1; + *cnt = 1; + return CKR_OK; +} + +static CK_RV test_pkcs11_close_all(CK_SLOT_ID slotId) +{ + (void)slotId; + + return CKR_OK; +} + +#ifdef HAVE_ECC_SIGN +static int test_pkcs11_sign_login(Pkcs11Token* token, ecc_key* key, + WC_RNG* rng, CK_RV loginRv) +{ + wc_CryptoInfo info; + byte hash[32]; + byte sig[ECC_MAX_SIG_SIZE]; + word32 sigLen = sizeof(sig); + + test_pkcs11_reset(); + test_pkcs11_login_rv = loginRv; + test_pkcs11_logouts = 0; + test_pkcs11_closes = 0; + XMEMSET(hash, 0x11, sizeof(hash)); + XMEMSET(&info, 0, sizeof(info)); + info.algo_type = WC_ALGO_TYPE_PK; + info.pk.type = WC_PK_TYPE_ECDSA_SIGN; + info.pk.eccsign.in = hash; + info.pk.eccsign.inlen = sizeof(hash); + info.pk.eccsign.out = sig; + info.pk.eccsign.outlen = &sigLen; + info.pk.eccsign.rng = rng; + info.pk.eccsign.key = key; + return wc_Pkcs11_CryptoDevCb(1, &info, token); +} +#endif + +int test_wc_Pkcs11_LoginShared(void) +{ + static const byte id[] = { 0x12 }; + Pkcs11Token token; + CK_FUNCTION_LIST func; + ecc_key key; + WC_RNG rng; + EXPECT_DECLS; + + test_pkcs11_store_setup(&token, &func); + func.C_OpenSession = test_pkcs11_open_session; + func.C_Login = test_pkcs11_login; + func.C_Logout = test_pkcs11_logout; + func.C_CloseSession = test_pkcs11_close_session; + func.C_CloseAllSessions = test_pkcs11_close_all; + token.handle = 0; + ExpectNotNull(token.userPin = (CK_UTF8CHAR_PTR)XMALLOC(4, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + if (token.userPin != NULL) + XMEMCPY(token.userPin, "1234", 4); + token.userPinSz = 4; + token.userPinLogin = 1; + token.pinOwned = 1; + XMEMSET(&key, 0, sizeof(key)); + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(test_pkcs11_make_ecc(&key, &rng, id, (int)sizeof(id)), 0); + +#ifdef HAVE_ECC_SIGN + ExpectIntEQ(test_pkcs11_sign_login(&token, &key, &rng, + CKR_USER_ALREADY_LOGGED_IN), 0); + ExpectIntEQ(test_pkcs11_logouts, 0); + ExpectIntEQ(test_pkcs11_closes, 1); + + ExpectIntEQ(test_pkcs11_sign_login(&token, &key, &rng, CKR_OK), 0); + ExpectIntEQ(test_pkcs11_logouts, 0); + ExpectIntEQ(test_pkcs11_closes, 1); + + ExpectIntEQ(test_pkcs11_sign_login(&token, &key, &rng, + TEST_CKR_GENERAL_ERROR), WC_NO_ERR_TRACE(WC_HW_E)); + ExpectIntEQ(test_pkcs11_closes, 1); +#endif + + test_pkcs11_login_rv = CKR_USER_ALREADY_LOGGED_IN; + test_pkcs11_logouts = 0; + ExpectIntEQ(wc_Pkcs11Token_Open(&token, 1), 0); + wc_Pkcs11Token_Close(&token); + ExpectIntEQ(test_pkcs11_logouts, 0); + +#ifdef HAVE_ECC_SIGN + test_pkcs11_logins = 0; + ExpectIntEQ(test_pkcs11_sign_login(&token, &key, &rng, CKR_OK), 0); + ExpectIntEQ(test_pkcs11_logins, 1); +#endif + + ExpectIntEQ(wc_Pkcs11Token_Open_ex(&token, 1, 1), 0); + wc_Pkcs11Token_Close(&token); + +#ifdef HAVE_ECC_SIGN + /* Without the PIN an operation's session does not log in. */ + test_pkcs11_logins = 0; + ExpectIntEQ(test_pkcs11_sign_login(&token, &key, &rng, CKR_OK), 0); + ExpectIntEQ(test_pkcs11_logins, 0); + ExpectIntEQ(test_pkcs11_closes, 1); +#endif + + wc_Pkcs11Token_Final(&token); + wc_ecc_free(&key); + DoExpectIntEQ(wc_FreeRng(&rng), 0); + + return EXPECT_RESULT(); +} + int test_wc_Pkcs11_FreeNoObject(void) { Pkcs11Token token; @@ -1340,6 +1503,79 @@ int test_wc_Pkcs11_RsaKeyGenExponent(void) return EXPECT_RESULT(); } +int test_wc_Pkcs11_TokenPinCopy(void) +{ + static const unsigned char pin[] = "1234"; + unsigned char pin2[4] = { '5', '6', '7', '8' }; + Pkcs11Dev dev; + Pkcs11Token token; + CK_FUNCTION_LIST func; + EXPECT_DECLS; + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&func, 0, sizeof(func)); + func.C_GetSlotInfo = test_pkcs11_slot_info; + func.C_CloseAllSessions = test_pkcs11_close_all; + func.C_OpenSession = test_pkcs11_open_session; + func.C_Login = test_pkcs11_login; + func.C_CloseSession = test_pkcs11_close_session; + dev.func = &func; + + /* A read-only PIN must not be written to on finalize. */ + ExpectIntEQ(wc_Pkcs11Token_Init(&token, &dev, 1, NULL, pin, 4), 0); + ExpectPtrNE(token.userPin, pin); + if (EXPECT_SUCCESS()) + ExpectBufEQ(token.userPin, pin, 4); + wc_Pkcs11Token_Final(&token); + ExpectNull(token.userPin); + ExpectBufEQ(pin, "1234", 4); + + ExpectIntEQ(wc_Pkcs11Token_Init(&token, &dev, 1, NULL, pin2, + (int)sizeof(pin2)), 0); + XMEMSET(pin2, 0, sizeof(pin2)); + if (EXPECT_SUCCESS()) + ExpectBufEQ(token.userPin, "5678", 4); + test_pkcs11_login_rv = CKR_OK; + ExpectIntEQ(wc_Pkcs11Token_Open(&token, 1), 0); + ExpectIntEQ(test_pkcs11_login_pinLen, 4); + ExpectBufEQ(test_pkcs11_login_pin, "5678", 4); + wc_Pkcs11Token_Close(&token); + wc_Pkcs11Token_Final(&token); + + ExpectIntEQ(wc_Pkcs11Token_Init(&token, &dev, 1, NULL, pin, 0), 0); + ExpectNotNull(token.userPin); + ExpectIntEQ(token.userPinSz, 0); + wc_Pkcs11Token_Final(&token); + + func.C_GetSlotList = test_pkcs11_slot_list; + ExpectIntEQ(wc_Pkcs11Token_InitName(&token, &dev, NULL, 0, pin, 4), 0); + ExpectPtrNE(token.userPin, pin); + if (EXPECT_SUCCESS()) + ExpectBufEQ(token.userPin, pin, 4); + wc_Pkcs11Token_Final(&token); + ExpectNull(token.userPin); + + /* A negative length is rejected before the token is set up. */ + XMEMSET(&token, 0, sizeof(token)); + ExpectIntEQ(wc_Pkcs11Token_Init(&token, &dev, 1, NULL, pin, -1), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectNull(token.func); + ExpectIntEQ(wc_Pkcs11Token_InitName(&token, &dev, "t", 1, pin, -1), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectNull(token.func); + + /* A PIN the application set itself is not freed. */ + ExpectIntEQ(wc_Pkcs11Token_Init_NoLogin(&token, &dev, 1, NULL), 0); + token.userPin = (CK_UTF8CHAR_PTR)pin; + token.userPinSz = 4; + token.userPinLogin = 1; + wc_Pkcs11Token_Final(&token); + ExpectNull(token.userPin); + ExpectBufEQ(pin, "1234", 4); + + return EXPECT_RESULT(); +} + int test_wc_Pkcs11_RsaKeyGenError(void) { EXPECT_DECLS; @@ -1422,4 +1658,113 @@ int test_wc_Pkcs11_RsaKeyGenError(void) return EXPECT_RESULT(); } +int test_wc_Pkcs11_TokenOpenDropsPin(void) +{ + static const unsigned char pin[] = "1234"; + Pkcs11Dev dev; + Pkcs11Token token; + CK_FUNCTION_LIST func; + EXPECT_DECLS; + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&func, 0, sizeof(func)); + func.C_GetSlotInfo = test_pkcs11_slot_info; + func.C_CloseAllSessions = test_pkcs11_close_all; + func.C_OpenSession = test_pkcs11_open_session; + func.C_Login = test_pkcs11_login; + func.C_Logout = test_pkcs11_logout; + func.C_CloseSession = test_pkcs11_close_session; + dev.func = &func; + ExpectIntEQ(wc_Pkcs11Token_Init(&token, &dev, 1, NULL, pin, 4), 0); + + /* wc_Pkcs11Token_Open keeps the PIN, so the token can be opened again. */ + test_pkcs11_login_rv = CKR_OK; + test_pkcs11_logins = 0; + ExpectIntEQ(wc_Pkcs11Token_Open(&token, 1), 0); + ExpectNotNull(token.userPin); + ExpectIntEQ(token.pinDropped, 0); + wc_Pkcs11Token_Close(&token); + ExpectIntEQ(wc_Pkcs11Token_Open(&token, 1), 0); + ExpectIntEQ(test_pkcs11_logins, 2); + /* A session that is already open keeps the PIN. */ + ExpectIntEQ(wc_Pkcs11Token_Open_ex(&token, 1, 1), 0); + ExpectNotNull(token.userPin); + ExpectIntEQ(token.pinDropped, 0); + wc_Pkcs11Token_Close(&token); + + /* A login shared with another token also lasts while the session is + * open. */ + test_pkcs11_login_rv = CKR_USER_ALREADY_LOGGED_IN; + ExpectIntEQ(wc_Pkcs11Token_Open_ex(&token, 1, 1), 0); + ExpectNull(token.userPin); + ExpectIntEQ(token.pinDropped, 1); + wc_Pkcs11Token_Close(&token); + wc_Pkcs11Token_Final(&token); + ExpectIntEQ(wc_Pkcs11Token_Init(&token, &dev, 1, NULL, pin, 4), 0); + + test_pkcs11_login_rv = CKR_OK; + test_pkcs11_logins = 0; + test_pkcs11_logouts = 0; + ExpectIntEQ(wc_Pkcs11Token_Open_ex(&token, 1, 1), 0); + ExpectIntEQ(test_pkcs11_logins, 1); + ExpectNull(token.userPin); + ExpectIntEQ(token.userPinSz, 0); + ExpectIntEQ(token.userPinLogin, 0); + wc_Pkcs11Token_Close(&token); + ExpectIntEQ(test_pkcs11_logouts, 0); + + /* Without the PIN no session can be opened until the token is set up + * again. */ + ExpectIntEQ(wc_Pkcs11Token_Open_ex(&token, 1, 1), + WC_NO_ERR_TRACE(BAD_STATE_E)); + ExpectIntEQ(test_pkcs11_logins, 1); + ExpectIntEQ(token.handle, 0); + wc_Pkcs11Token_Final(&token); + ExpectIntEQ(token.pinDropped, 0); + + /* A failed login closes the session and keeps no handle. */ + ExpectIntEQ(wc_Pkcs11Token_Init(&token, &dev, 1, NULL, pin, 4), 0); + test_pkcs11_login_rv = TEST_CKR_GENERAL_ERROR; + test_pkcs11_closes = 0; + ExpectIntEQ(wc_Pkcs11Token_Open_ex(&token, 1, 1), + WC_NO_ERR_TRACE(WC_HW_E)); + ExpectIntEQ(token.handle, 0); + ExpectIntEQ(test_pkcs11_closes, 1); + ExpectNotNull(token.userPin); + + /* A failed C_OpenSession keeps no handle and no login. */ + func.C_OpenSession = test_pkcs11_open_session_fail; + test_pkcs11_logins = 0; + ExpectIntEQ(wc_Pkcs11Token_Open_ex(&token, 1, 1), + WC_NO_ERR_TRACE(WC_HW_E)); + ExpectIntEQ(token.handle, 0); + ExpectIntEQ(test_pkcs11_logins, 0); + ExpectNotNull(token.userPin); + ExpectIntEQ(token.pinDropped, 0); + func.C_OpenSession = test_pkcs11_open_session; + + /* A second open keeps the first session and its login. */ + test_pkcs11_login_rv = CKR_OK; + test_pkcs11_logins = 0; + ExpectIntEQ(wc_Pkcs11Token_Open_ex(&token, 1, 1), 0); + ExpectIntEQ(token.pinDropped, 1); + ExpectIntEQ(wc_Pkcs11Token_Open_ex(&token, 1, 1), 0); + ExpectIntEQ(test_pkcs11_logins, 1); + wc_Pkcs11Token_Close(&token); + wc_Pkcs11Token_Final(&token); + + /* A token without a PIN has nothing to discard and can be reopened. */ + ExpectIntEQ(wc_Pkcs11Token_Init_NoLogin(&token, &dev, 1, NULL), 0); + test_pkcs11_logins = 0; + ExpectIntEQ(wc_Pkcs11Token_Open_ex(&token, 1, 1), 0); + ExpectIntEQ(token.pinDropped, 0); + wc_Pkcs11Token_Close(&token); + ExpectIntEQ(wc_Pkcs11Token_Open_ex(&token, 1, 1), 0); + ExpectIntEQ(test_pkcs11_logins, 0); + wc_Pkcs11Token_Close(&token); + wc_Pkcs11Token_Final(&token); + + return EXPECT_RESULT(); +} + #endif diff --git a/tests/api/test_pkcs11.h b/tests/api/test_pkcs11.h index ff7089a6729..2725429d9ce 100644 --- a/tests/api/test_pkcs11.h +++ b/tests/api/test_pkcs11.h @@ -32,8 +32,11 @@ int test_wc_Pkcs11_EcdsaSigDecode(void); int test_wc_Pkcs11_FreeKeepsTokenKey(void); int test_wc_Pkcs11_RsaIdNoLeak(void); int test_wc_Pkcs11_EcKeyUsage(void); +int test_wc_Pkcs11_LoginShared(void); +int test_wc_Pkcs11_TokenPinCopy(void); int test_wc_Pkcs11_RsaKeyGenError(void); int test_wc_Pkcs11_RsaKeyGenExponent(void); +int test_wc_Pkcs11_TokenOpenDropsPin(void); int test_wc_Pkcs11_FreeNoObject(void); int test_wc_Pkcs11_KeyGenSessionClosed(void); @@ -42,8 +45,11 @@ int test_wc_Pkcs11_KeyGenSessionClosed(void); TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_FreeKeepsTokenKey), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_RsaIdNoLeak), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_EcKeyUsage), \ + TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_LoginShared), \ + TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_TokenPinCopy), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_RsaKeyGenError), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_RsaKeyGenExponent), \ + TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_TokenOpenDropsPin), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_FreeNoObject), \ TEST_DECL_GROUP("pkcs11", test_wc_Pkcs11_KeyGenSessionClosed) diff --git a/wolfcrypt/src/wc_pkcs11.c b/wolfcrypt/src/wc_pkcs11.c index 09bdb96613d..1406dddcd8c 100644 --- a/wolfcrypt/src/wc_pkcs11.c +++ b/wolfcrypt/src/wc_pkcs11.c @@ -957,7 +957,10 @@ static int Pkcs11Token_Init(Pkcs11Token* token, Pkcs11Dev* dev, int slotId, token->userPin = NULL_PTR; token->userPinSz = 0; token->userPinLogin = 0; + token->pinOwned = 0; + token->pinDropped = 0; token->version = dev->version; + token->heap = dev->heap; } XFREE(slot, dev->heap, DYNAMIC_TYPE_TMP_BUFFER); @@ -965,6 +968,34 @@ static int Pkcs11Token_Init(Pkcs11Token* token, Pkcs11Dev* dev, int slotId, return ret; } +/** + * Keep a copy of the user's PIN in the token for logging in. + * + * @param [in] token Token object. + * @param [in] userPin PIN to use to login as user. + * @param [in] userPinSz Number of bytes in PIN. Not negative. + * @return MEMORY_E when dynamic memory allocation fails. + * @return 0 on success. + */ +static int Pkcs11Token_SetPin(Pkcs11Token* token, const unsigned char* userPin, + int userPinSz) +{ + int ret = 0; + + token->userPin = (CK_UTF8CHAR_PTR)XMALLOC(userPinSz > 0 ? userPinSz : 1, + token->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (token->userPin == NULL) + ret = MEMORY_E; + if (ret == 0) { + XMEMCPY(token->userPin, userPin, (size_t)userPinSz); + token->userPinSz = (CK_ULONG)userPinSz; + token->userPinLogin = 1; + token->pinOwned = 1; + } + + return ret; +} + /** * Set up a token for use. Lookup by slotId or tokenName. Set User PIN. * @@ -976,6 +1007,8 @@ static int Pkcs11Token_Init(Pkcs11Token* token, Pkcs11Dev* dev, int slotId, * @param [in] userPin PIN to use to login as user. * @param [in] userPinSz Number of bytes in PIN. * @return BAD_FUNC_ARG when token, dev and/or tokenName is NULL. + * @return BAD_FUNC_ARG when userPinSz is negative. + * @return MEMORY_E when copying the PIN fails. * @return WC_INIT_E when initializing token fails. * @return WC_HW_E when another PKCS#11 library call fails. * @return 0 on success. @@ -989,12 +1022,12 @@ int wc_Pkcs11Token_Init(Pkcs11Token* token, Pkcs11Dev* dev, int slotId, if (tokenName != NULL) { tokenNameSz = XSTRLEN(tokenName); } - ret = Pkcs11Token_Init(token, dev, slotId, tokenName, tokenNameSz); - if (ret == 0 && userPin != NULL) { - token->userPin = (CK_UTF8CHAR_PTR)userPin; - token->userPinSz = (CK_ULONG)userPinSz; - token->userPinLogin = 1; - } + if (userPin != NULL && userPinSz < 0) + ret = BAD_FUNC_ARG; + else + ret = Pkcs11Token_Init(token, dev, slotId, tokenName, tokenNameSz); + if (ret == 0 && userPin != NULL) + ret = Pkcs11Token_SetPin(token, userPin, userPinSz); return ret; } @@ -1032,6 +1065,8 @@ int wc_Pkcs11Token_Init_NoLogin(Pkcs11Token* token, Pkcs11Dev* dev, int slotId, * @param [in] userPin PIN to use to login as user. * @param [in] userPinSz Number of bytes in PIN. * @return BAD_FUNC_ARG when token, dev and/or tokenName is NULL. + * @return BAD_FUNC_ARG when userPinSz is negative. + * @return MEMORY_E when copying the PIN fails. * @return WC_INIT_E when initializing token fails. * @return WC_HW_E when another PKCS#11 library call fails. * @return 0 on success. @@ -1040,12 +1075,14 @@ int wc_Pkcs11Token_InitName(Pkcs11Token* token, Pkcs11Dev* dev, const char* tokenName, int tokenNameSz, const unsigned char* userPin, int userPinSz) { - int ret = Pkcs11Token_Init(token, dev, -1, tokenName, (size_t)tokenNameSz); - if (ret == 0 && userPin != NULL) { - token->userPin = (CK_UTF8CHAR_PTR)userPin; - token->userPinSz = (CK_ULONG)userPinSz; - token->userPinLogin = 1; - } + int ret; + + if (userPin != NULL && userPinSz < 0) + ret = BAD_FUNC_ARG; + else + ret = Pkcs11Token_Init(token, dev, -1, tokenName, (size_t)tokenNameSz); + if (ret == 0 && userPin != NULL) + ret = Pkcs11Token_SetPin(token, userPin, userPinSz); return ret; } @@ -1070,6 +1107,24 @@ int wc_Pkcs11Token_InitName_NoLogin(Pkcs11Token* token, Pkcs11Dev* dev, return Pkcs11Token_Init(token, dev, -1, tokenName, (size_t)tokenNameSz); } +/** + * Zeroize and free the token's copy of the user's PIN. + * A PIN the application set in the token itself is only forgotten. + * + * @param [in] token Token object. + */ +static void Pkcs11Token_ClearPin(Pkcs11Token* token) +{ + if (token->userPin != NULL && token->pinOwned) { + ForceZero(token->userPin, (word32)token->userPinSz); + XFREE(token->userPin, token->heap, DYNAMIC_TYPE_TMP_BUFFER); + } + token->userPin = NULL_PTR; + token->userPinSz = 0; + token->userPinLogin = 0; + token->pinOwned = 0; +} + /** * Finalize token. * Closes all sessions on token. @@ -1081,7 +1136,8 @@ void wc_Pkcs11Token_Final(Pkcs11Token* token) if (token != NULL && token->func != NULL) { token->func->C_CloseAllSessions(token->slotId); token->handle = NULL_PTR; - ForceZero(token->userPin, (word32)token->userPinSz); + Pkcs11Token_ClearPin(token); + token->pinDropped = 0; } } @@ -1125,7 +1181,9 @@ static int Pkcs11OpenSession(Pkcs11Token* token, Pkcs11Session* session, rv = token->func->C_Login(session->handle, CKU_USER, token->userPin, token->userPinSz); PKCS11_RV("C_Login", rv); - if (rv != CKR_OK) { + /* Another token in this process may have logged in already. */ + if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { + token->func->C_CloseSession(session->handle); ret = WC_HW_E; } } @@ -1151,8 +1209,6 @@ static int Pkcs11OpenSession(Pkcs11Token* token, Pkcs11Session* session, static void Pkcs11CloseSession(Pkcs11Token* token, Pkcs11Session* session) { if (token != NULL && session != NULL && token->handle != session->handle) { - if (token->userPin != NULL) - session->func->C_Logout(session->handle); session->func->C_CloseSession(session->handle); } } @@ -1189,10 +1245,29 @@ static int Pkcs11KeyGenSessionCheck(Pkcs11Session* session, int idLen, * @param [in] token Token object. * @param [in] readWrite Boolean indicating to open session for Read/Write. * @return BAD_FUNC_ARG when token is NULL. - * @return WC_HW_E when opening the session fails. - * @return 0 on success. + * @return BAD_STATE_E when the PIN was discarded by an earlier open. + * @return WC_HW_E when opening the session or logging in fails. + * @return 0 on success, or when a session is already open. */ int wc_Pkcs11Token_Open(Pkcs11Token* token, int readWrite) +{ + return wc_Pkcs11Token_Open_ex(token, readWrite, 0); +} + +/** + * Open a session on the token to be used for all operations. + * Optionally discard the copy of the user's PIN once this call has opened and + * logged in the session. + * + * @param [in] token Token object. + * @param [in] readWrite Boolean indicating to open session for Read/Write. + * @param [in] discardPin Boolean indicating to discard the user's PIN. + * @return BAD_FUNC_ARG when token is NULL. + * @return BAD_STATE_E when the PIN was discarded by an earlier open. + * @return WC_HW_E when opening the session or logging in fails. + * @return 0 on success, or when a session is already open. + */ +int wc_Pkcs11Token_Open_ex(Pkcs11Token* token, int readWrite, int discardPin) { int ret = 0; Pkcs11Session session; @@ -1200,9 +1275,17 @@ int wc_Pkcs11Token_Open(Pkcs11Token* token, int readWrite) if (token == NULL) ret = BAD_FUNC_ARG; - if (ret == 0) { + if (ret == 0 && token->handle == NULL_PTR && token->pinDropped) + ret = BAD_STATE_E; + else if (ret == 0 && token->handle == NULL_PTR) { ret = Pkcs11OpenSession(token, &session, readWrite); - token->handle = session.handle; + if (ret == 0) + token->handle = session.handle; + /* The login lasts while this session is open. */ + if (ret == 0 && discardPin && token->userPinLogin) { + Pkcs11Token_ClearPin(token); + token->pinDropped = 1; + } } return ret; diff --git a/wolfssl/wolfcrypt/pkcs11.h b/wolfssl/wolfcrypt/pkcs11.h index d77a5ccfc76..910b9baf63c 100644 --- a/wolfssl/wolfcrypt/pkcs11.h +++ b/wolfssl/wolfcrypt/pkcs11.h @@ -202,6 +202,7 @@ extern "C" { #define CKR_OK 0x00000000UL #define CKR_MECHANISM_INVALID 0x00000070UL #define CKR_SIGNATURE_INVALID 0x000000C0UL +#define CKR_USER_ALREADY_LOGGED_IN 0x00000100UL #define CKR_CRYPTOKI_ALREADY_INITIALIZED 0x00000191UL #define CKD_NULL 0x00000001UL diff --git a/wolfssl/wolfcrypt/wc_pkcs11.h b/wolfssl/wolfcrypt/wc_pkcs11.h index 0477666edbd..406458dc338 100644 --- a/wolfssl/wolfcrypt/wc_pkcs11.h +++ b/wolfssl/wolfcrypt/wc_pkcs11.h @@ -60,8 +60,11 @@ typedef struct Pkcs11Token { CK_SESSION_HANDLE handle; /* Handle to active session */ CK_UTF8CHAR_PTR userPin; /* User's PIN to login with */ CK_ULONG userPinSz; /* Size of user's PIN in bytes */ - byte userPinLogin:1; /* Login with User's PIN */ + WC_BITFIELD userPinLogin:1; /* Login with User's PIN */ + WC_BITFIELD pinOwned:1; /* userPin is the token's copy */ + WC_BITFIELD pinDropped:1; /* PIN discarded after logging in */ int version; /* Pkcs11InterfaceVersionType */ + void* heap; /* Heap for the copy of the PIN */ } Pkcs11Token; typedef struct Pkcs11Session { @@ -104,6 +107,8 @@ WOLFSSL_API int wc_Pkcs11Token_InitName_NoLogin(Pkcs11Token* token, WOLFSSL_API void wc_Pkcs11Token_Final(Pkcs11Token* token); WOLFSSL_API int wc_Pkcs11Token_Open(Pkcs11Token* token, int readWrite); +WOLFSSL_API int wc_Pkcs11Token_Open_ex(Pkcs11Token* token, int readWrite, + int discardPin); WOLFSSL_API void wc_Pkcs11Token_Close(Pkcs11Token* token); WOLFSSL_API int wc_Pkcs11StoreKey(Pkcs11Token* token, int type, int clear,