138 | | |
139 | | |
140 | | |
141 | | |
| 138 | '''https://github.com/open-eid/browser-token-signing/blob/master/common/esteid_sign.c''' |
| 139 | {{{ |
| 140 | int EstEID_signHash(char **signature, unsigned int *signatureLength, CK_SLOT_ID slotID, EstEID_Map cert, const char *hash, unsigned int hashLength, EstEID_PINPromptData pinPromptData) { |
| 141 | CK_SESSION_HANDLE session = 0L; |
| 142 | CK_RV loginResult = CKR_FUNCTION_CANCELED; |
| 143 | char *name; |
| 144 | char message[1024]; |
| 145 | int remainingTries = -1; |
| 146 | int attempt = 0, blocked = FALSE; |
| 147 | int isPinPad; |
| 148 | unsigned int privateKeyIndex; |
| 149 | #ifdef _WIN32 |
| 150 | EstEID_PINPromptDataEx pinPromptDataEx; |
| 151 | #endif |
| 152 | |
| 153 | |
| 154 | LOG_LOCATION; |
| 155 | |
| 156 | if (EstEID_CK_failure("C_OpenSession", fl->C_OpenSession(slotID, CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, &session))) return FAILURE; |
| 157 | |
| 158 | name = EstEID_getFullNameWithPersonalCode(cert); |
| 159 | privateKeyIndex = (unsigned)atoi(EstEID_mapGet(cert, "privateKeyIndex")); |
| 160 | remainingTries = EstEID_getRemainingTries(slotID); |
| 161 | for (attempt = 0;; attempt++) { |
| 162 | if (remainingTries == -1) |
| 163 | CLOSE_SESSION_AND_RETURN(FAILURE); |
| 164 | if (!remainingTries || blocked) { |
| 165 | sprintf(EstEID_error, "C_Login error: %s (%li)", pkcs11_error_message(CKR_PIN_LOCKED), CKR_PIN_LOCKED); |
| 166 | pinPromptData.alertFunction(pinPromptData.nativeWindowHandle, l10n("PIN2 blocked, cannot sign!")); |
| 167 | CLOSE_SESSION_AND_RETURN(FAILURE); |
| 168 | } |
| 169 | if (remainingTries < 3 || attempt) { |
| 170 | sprintf(message, "%s%s %i", (attempt ? l10n("Incorrect PIN2! ") : ""), l10n("Tries left:"), remainingTries); |
| 171 | } |
| 172 | else { |
| 173 | message[0] = 0; |
| 174 | } |
| 175 | isPinPad = EstEID_isPinPad(slotID); |
| 176 | if(!isPinPad) { |
| 177 | // Simple card reader |
| 178 | char *pin = pinPromptData.promptFunction(pinPromptData.nativeWindowHandle, name, message, (unsigned)atoi(EstEID_mapGet(cert, "minPinLen")), isPinPad); |
| 179 | if (!pin || strlen(pin) == 0) { |
| 180 | free(pin); |
| 181 | setUserCancelErrorCodeAndMessage(); |
| 182 | CLOSE_SESSION_AND_RETURN(FAILURE); |
| 183 | } |
| 184 | loginResult = fl->C_Login(session, CKU_USER, (unsigned char *)pin, strlen(pin)); |
| 185 | free(pin); |
| 186 | } |
| 187 | else { |
| 188 | // PIN pad |
| 189 | #ifdef _WIN32 |
| 190 | EstEID_log("creating pinpad dialog UI thread"); |
| 191 | pinpad_thread_result = -1; |
| 192 | FAIL_IF_THREAD_ERROR("CreateMutex", (pinpad_thread_mutex = CreateMutex(NULL, FALSE, NULL))); |
| 193 | #else |
| 194 | EstEID_log("creating pinpad worker thread"); |
| 195 | pinpad_thread_result = -1; |
| 196 | FAIL_IF_PTHREAD_ERROR("pthread_mutex_init", pthread_mutex_init(&pinpad_thread_mutex, NULL)); |
| 197 | FAIL_IF_PTHREAD_ERROR("pthread_cond_init", pthread_cond_init(&pinpad_thread_condition, NULL)); |
| 198 | pthread_t pinpad_thread; |
| 199 | EstEID_PINPadThreadData threadData; |
| 200 | threadData.session = session; |
| 201 | threadData.result = CKR_OK; |
| 202 | #endif |
| 203 | EstEID_log("thread launched"); |
| 204 | #ifdef _WIN32 |
| 205 | /* |
| 206 | NB! Due to Firefox for Windows specific behaviour C_Login() is launched from main thread |
| 207 | and UI code is running in separate thread if running on Windows. |
| 208 | */ |
| 209 | pinPromptDataEx.pinPromptData = pinPromptData; |
| 210 | pinPromptDataEx.message = message; |
| 211 | pinPromptDataEx.name = name; |
| 212 | CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&EstEID_pinPadLogin, (LPVOID)&pinPromptDataEx, 0, NULL); |
| 213 | loginResult = fl->C_Login(session, CKU_USER, NULL, 0); |
| 214 | //closePinPadModalSheet(); |
| 215 | #else |
| 216 | FAIL_IF_PTHREAD_ERROR("pthread_create", pthread_create(&pinpad_thread, NULL, EstEID_pinPadLogin, (void*)&threadData)); |
| 217 | pinPromptData.promptFunction(pinPromptData.nativeWindowHandle, name, message, 0, isPinPad); |
| 218 | loginResult = threadData.result; |
| 219 | #endif |
| 220 | EstEID_log("pinpad sheet/dialog closed"); |
| 221 | if (loginResult == CKR_FUNCTION_CANCELED) { |
| 222 | setUserCancelErrorCodeAndMessage(); |
| 223 | CLOSE_SESSION_AND_RETURN(FAILURE); |
| 224 | } |
| 225 | } |
| 226 | EstEID_log("loginResult = %s", pkcs11_error_message(loginResult)); |
| 227 | switch (loginResult) { |
| 228 | case CKR_PIN_LOCKED: |
| 229 | blocked = TRUE; |
| 230 | case CKR_PIN_INCORRECT: |
| 231 | remainingTries--; |
| 232 | case CKR_PIN_INVALID: |
| 233 | case CKR_PIN_LEN_RANGE: |
| 234 | EstEID_log("this was attempt %i, loginResult causes to run next round", attempt); |
| 235 | continue; |
| 236 | default: |
| 237 | if (EstEID_CK_failure("C_Login", loginResult)) CLOSE_SESSION_AND_RETURN(FAILURE); |
| 238 | } |
| 239 | break; // Login successful - correct PIN supplied |
| 240 | } |
| 241 | |
| 242 | return EstEID_RealSign(session, signature, signatureLength, hash, hashLength, name, privateKeyIndex); |
| 243 | } |
| 244 | |
| 245 | |
| 246 | int EstEID_RealSign(CK_SESSION_HANDLE session, char **signature, unsigned int *signatureLength, const char *hash, unsigned int hashLength, char* name, unsigned int privateKeyIndex) { |
| 247 | CK_ULONG objectCount; |
| 248 | unsigned int hashWithPaddingLength = 0; |
| 249 | char *hashWithPadding; |
| 250 | CK_MECHANISM mechanism = {CKM_RSA_PKCS, 0, 0}; |
| 251 | CK_OBJECT_CLASS objectClass = CKO_PRIVATE_KEY; |
| 252 | CK_ATTRIBUTE searchAttribute = {CKA_CLASS, &objectClass, sizeof(objectClass)}; |
| 253 | |
| 254 | unsigned int max = privateKeyIndex + 1; |
| 255 | CK_OBJECT_HANDLE privateKeyHandle[max]; |
| 256 | |
| 257 | if (EstEID_CK_failure("C_FindObjectsInit", fl->C_FindObjectsInit(session, &searchAttribute, 1))) CLOSE_SESSION_AND_RETURN(FAILURE); |
| 258 | |
| 259 | if (EstEID_CK_failure("C_FindObjects", fl->C_FindObjects(session, privateKeyHandle, max, &objectCount))) CLOSE_SESSION_AND_RETURN(FAILURE); |
| 260 | if (EstEID_CK_failure("C_FindObjectsFinal", fl->C_FindObjectsFinal(session))) CLOSE_SESSION_AND_RETURN(FAILURE); |
| 261 | |
| 262 | if (objectCount == 0) CLOSE_SESSION_AND_RETURN(FAILURE); // todo ?? set error message |
| 263 | EstEID_log("found %i private keys in slot, using key in position %i", objectCount, privateKeyIndex); |
| 264 | |
| 265 | if (EstEID_CK_failure("C_SignInit", fl->C_SignInit(session, &mechanism, privateKeyHandle[privateKeyIndex]))) CLOSE_SESSION_AND_RETURN(FAILURE); |
| 266 | |
| 267 | hashWithPadding = EstEID_addPadding(hash, hashLength, &hashWithPaddingLength); |
| 268 | if (hashWithPadding) { // This is additional safeguard, as digest length is checked already before calling EstEID_addPadding() |
| 269 | CK_ULONG len; |
| 270 | if (EstEID_CK_failure("C_Sign", fl->C_Sign(session, (CK_BYTE_PTR)hashWithPadding, hashWithPaddingLength, NULL, &len))) { |
| 271 | free(hashWithPadding); |
| 272 | CLOSE_SESSION_AND_RETURN(FAILURE); |
| 273 | } |
| 274 | *signature = (char *)malloc(len); |
| 275 | if (EstEID_CK_failure("C_Sign", fl->C_Sign(session, (CK_BYTE_PTR)hashWithPadding, hashWithPaddingLength, (CK_BYTE_PTR) * signature, &len))) { |
| 276 | free(hashWithPadding); |
| 277 | CLOSE_SESSION_AND_RETURN(FAILURE); |
| 278 | } |
| 279 | *signatureLength = len; |
| 280 | free(hashWithPadding); |
| 281 | } |
| 282 | |
| 283 | if (session) { |
| 284 | if (EstEID_CK_failure("C_CloseSession", fl->C_CloseSession(session))) { |
| 285 | return FAILURE; |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | if(name) { |
| 290 | free(name); |
| 291 | } |
| 292 | |
| 293 | if (!hashWithPaddingLength) { // This is additional safeguard, as digest length is checked already before calling EstEID_addPadding() |
| 294 | EstEID_log("will not sign due to incorrect incoming message digest length"); |
| 295 | return FAILURE; |
| 296 | } |
| 297 | EstEID_log("successfully signed"); |
| 298 | return SUCCESS; |
| 299 | } |
| 300 | |
| 301 | |
| 302 | |
| 303 | }}} |
| 304 | |
| 305 | |
| 306 | |
| 307 | |
| 308 | |