/* * Copyright (c) 2010 .SE (The Internet Infrastructure Foundation) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /***************************************************************************** softhsm2-util-botan.cpp Code specific for Botan *****************************************************************************/ #include #define UTIL_BOTAN #include "softhsm2-util.h" #include "softhsm2-util-botan.h" #include #include #include #include #include #include #include #include #include #include #include #if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,11,14) #include bool wasInitialized = false; #endif // Init Botan void crypto_init() { #if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,11,14) // The PKCS#11 library might be using Botan // Check if it has already initialized Botan if (Botan::Global_State_Management::global_state_exists()) { wasInitialized = true; } if (!wasInitialized) { Botan::LibraryInitializer::initialize("thread_safe=true"); } #else Botan::LibraryInitializer::initialize("thread_safe=true"); #endif } // Final Botan void crypto_final() { #if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,11,14) if (!wasInitialized) { Botan::LibraryInitializer::deinitialize(); } #else Botan::LibraryInitializer::deinitialize(); #endif } // Import a aes secret key from given path int crypto_import_aes_key ( CK_SESSION_HANDLE hSession, char* filePath, char* label, char* objID, size_t objIDLen ) { const size_t cMaxAesKeySize = 1024 + 1; // including null-character char aesKeyValue[cMaxAesKeySize]; FILE* fp = fopen(filePath, "rb"); if (fp == NULL) { fprintf(stderr, "ERROR: Could not open the secret key file.\n"); return 1; } if (fgets(aesKeyValue, cMaxAesKeySize, fp) == NULL) { fprintf(stderr, "ERROR: Could not read the secret key file.\n"); fclose(fp); return 1; } fclose(fp); CK_BBOOL ckTrue = CK_TRUE; CK_OBJECT_CLASS keyClass = CKO_SECRET_KEY; CK_KEY_TYPE keyType = CKK_AES; CK_ATTRIBUTE keyTemplate[] = { { CKA_CLASS, &keyClass, sizeof(keyClass) }, { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, { CKA_LABEL, label, strlen(label) }, { CKA_ID, objID, objIDLen }, { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, { CKA_ENCRYPT, &ckTrue, sizeof(ckTrue) }, { CKA_DECRYPT, &ckTrue, sizeof(ckTrue) }, { CKA_SENSITIVE, &ckTrue, sizeof(ckTrue) }, { CKA_VALUE, &aesKeyValue, strlen(aesKeyValue) } }; CK_OBJECT_HANDLE hKey; CK_RV rv = p11->C_CreateObject(hSession, keyTemplate, 9, &hKey); if (rv != CKR_OK) { fprintf(stderr, "ERROR: Could not save the secret key in the token. " "Maybe the algorithm is not supported.\n"); return 1; } return 0; } // Import a key pair from given path int crypto_import_key_pair ( CK_SESSION_HANDLE hSession, char* filePath, char* filePIN, char* label, char* objID, size_t objIDLen, int noPublicKey ) { Botan::Private_Key* pkey = crypto_read_file(filePath, filePIN); if (pkey == NULL) { return 1; } Botan::RSA_PrivateKey* rsa = NULL; Botan::DSA_PrivateKey* dsa = NULL; #ifdef WITH_ECC Botan::ECDSA_PrivateKey* ecdsa = NULL; #endif if (pkey->algo_name().compare("RSA") == 0) { rsa = dynamic_cast(pkey); } else if (pkey->algo_name().compare("DSA") == 0) { dsa = dynamic_cast(pkey); } #ifdef WITH_ECC else if (pkey->algo_name().compare("ECDSA") == 0) { ecdsa = dynamic_cast(pkey); } #endif else { fprintf(stderr, "ERROR: %s is not a supported algorithm.\n", pkey->algo_name().c_str()); delete pkey; return 1; } int result = 0; if (rsa) { result = crypto_save_rsa(hSession, label, objID, objIDLen, noPublicKey, rsa); } else if (dsa) { result = crypto_save_dsa(hSession, label, objID, objIDLen, noPublicKey, dsa); } #ifdef WITH_ECC else if (ecdsa) { result = crypto_save_ecdsa(hSession, label, objID, objIDLen, noPublicKey, ecdsa); } #endif else { fprintf(stderr, "ERROR: Could not get the key material.\n"); result = 1; } delete pkey; return result; } // Read the key from file Botan::Private_Key* crypto_read_file(char* filePath, char* filePIN) { if (filePath == NULL) { return NULL; } Botan::AutoSeeded_RNG* rng = new Botan::AutoSeeded_RNG(); Botan::Private_Key* pkey = NULL; try { #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,0) if (filePIN == NULL) { pkey = Botan::PKCS8::load_key(std::string(filePath), *rng); } else { pkey = Botan::PKCS8::load_key(std::string(filePath), *rng, std::string(filePIN)); } #else if (filePIN == NULL) { pkey = Botan::PKCS8::load_key(filePath, *rng); } else { pkey = Botan::PKCS8::load_key(filePath, *rng, filePIN); } #endif } catch (std::exception& e) { fprintf(stderr, "%s\n", e.what()); fprintf(stderr, "ERROR: Perhaps wrong path to file, wrong file format, " "or wrong PIN to file (--file-pin ).\n"); delete rng; return NULL; } delete rng; return pkey; } // Save the key data in PKCS#11 int crypto_save_rsa ( CK_SESSION_HANDLE hSession, char* label, char* objID, size_t objIDLen, int noPublicKey, Botan::RSA_PrivateKey* rsa ) { rsa_key_material_t* keyMat = crypto_malloc_rsa(rsa); if (!keyMat) { fprintf(stderr, "ERROR: Could not convert the key material to binary information.\n"); return 1; } CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY, privClass = CKO_PRIVATE_KEY; CK_KEY_TYPE keyType = CKK_RSA; CK_BBOOL ckTrue = CK_TRUE, ckFalse = CK_FALSE, ckToken = CK_TRUE; if (noPublicKey) { ckToken = CK_FALSE; } CK_ATTRIBUTE pubTemplate[] = { { CKA_CLASS, &pubClass, sizeof(pubClass) }, { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, { CKA_LABEL, label, strlen(label) }, { CKA_ID, objID, objIDLen }, { CKA_TOKEN, &ckToken, sizeof(ckToken) }, { CKA_VERIFY, &ckTrue, sizeof(ckTrue) }, { CKA_ENCRYPT, &ckTrue, sizeof(ckTrue) }, { CKA_WRAP, &ckTrue, sizeof(ckTrue) }, { CKA_PUBLIC_EXPONENT, keyMat->bigE, keyMat->sizeE }, { CKA_MODULUS, keyMat->bigN, keyMat->sizeN } }; CK_ATTRIBUTE privTemplate[] = { { CKA_CLASS, &privClass, sizeof(privClass) }, { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, { CKA_LABEL, label, strlen(label) }, { CKA_ID, objID, objIDLen }, { CKA_SIGN, &ckTrue, sizeof(ckTrue) }, { CKA_DECRYPT, &ckTrue, sizeof(ckTrue) }, { CKA_UNWRAP, &ckTrue, sizeof(ckTrue) }, { CKA_SENSITIVE, &ckTrue, sizeof(ckTrue) }, { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, { CKA_PRIVATE, &ckTrue, sizeof(ckTrue) }, { CKA_EXTRACTABLE, &ckFalse, sizeof(ckFalse) }, { CKA_PUBLIC_EXPONENT, keyMat->bigE, keyMat->sizeE }, { CKA_MODULUS, keyMat->bigN, keyMat->sizeN }, { CKA_PRIVATE_EXPONENT, keyMat->bigD, keyMat->sizeD }, { CKA_PRIME_1, keyMat->bigP, keyMat->sizeP }, { CKA_PRIME_2, keyMat->bigQ, keyMat->sizeQ }, { CKA_EXPONENT_1, keyMat->bigDMP1, keyMat->sizeDMP1 }, { CKA_EXPONENT_2, keyMat->bigDMQ1, keyMat->sizeDMQ1 }, { CKA_COEFFICIENT, keyMat->bigIQMP, keyMat->sizeIQMP } }; CK_OBJECT_HANDLE hKey1, hKey2; CK_RV rv = p11->C_CreateObject(hSession, privTemplate, 19, &hKey1); if (rv != CKR_OK) { fprintf(stderr, "ERROR: Could not save the private key in the token. " "Maybe the algorithm is not supported.\n"); crypto_free_rsa(keyMat); return 1; } rv = p11->C_CreateObject(hSession, pubTemplate, 10, &hKey2); crypto_free_rsa(keyMat); if (rv != CKR_OK) { p11->C_DestroyObject(hSession, hKey1); fprintf(stderr, "ERROR: Could not save the public key in the token.\n"); return 1; } printf("The key pair has been imported.\n"); return 0; } // Convert the Botan key to binary rsa_key_material_t* crypto_malloc_rsa(Botan::RSA_PrivateKey* rsa) { if (rsa == NULL) { return NULL; } rsa_key_material_t* keyMat = (rsa_key_material_t*)malloc(sizeof(rsa_key_material_t)); if (keyMat == NULL) { return NULL; } keyMat->sizeE = rsa->get_e().bytes(); keyMat->sizeN = rsa->get_n().bytes(); keyMat->sizeD = rsa->get_d().bytes(); keyMat->sizeP = rsa->get_p().bytes(); keyMat->sizeQ = rsa->get_q().bytes(); keyMat->sizeDMP1 = rsa->get_d1().bytes(); keyMat->sizeDMQ1 = rsa->get_d2().bytes(); keyMat->sizeIQMP = rsa->get_c().bytes(); keyMat->bigE = (CK_VOID_PTR)malloc(keyMat->sizeE); keyMat->bigN = (CK_VOID_PTR)malloc(keyMat->sizeN); keyMat->bigD = (CK_VOID_PTR)malloc(keyMat->sizeD); keyMat->bigP = (CK_VOID_PTR)malloc(keyMat->sizeP); keyMat->bigQ = (CK_VOID_PTR)malloc(keyMat->sizeQ); keyMat->bigDMP1 = (CK_VOID_PTR)malloc(keyMat->sizeDMP1); keyMat->bigDMQ1 = (CK_VOID_PTR)malloc(keyMat->sizeDMQ1); keyMat->bigIQMP = (CK_VOID_PTR)malloc(keyMat->sizeIQMP); if ( !keyMat->bigE || !keyMat->bigN || !keyMat->bigD || !keyMat->bigP || !keyMat->bigQ || !keyMat->bigDMP1 || !keyMat->bigDMQ1 || !keyMat->bigIQMP ) { crypto_free_rsa(keyMat); return NULL; } rsa->get_e().binary_encode((Botan::byte*)keyMat->bigE); rsa->get_n().binary_encode((Botan::byte*)keyMat->bigN); rsa->get_d().binary_encode((Botan::byte*)keyMat->bigD); rsa->get_p().binary_encode((Botan::byte*)keyMat->bigP); rsa->get_q().binary_encode((Botan::byte*)keyMat->bigQ); rsa->get_d1().binary_encode((Botan::byte*)keyMat->bigDMP1); rsa->get_d2().binary_encode((Botan::byte*)keyMat->bigDMQ1); rsa->get_c().binary_encode((Botan::byte*)keyMat->bigIQMP); return keyMat; } // Free the memory of the key void crypto_free_rsa(rsa_key_material_t* keyMat) { if (keyMat == NULL) return; if (keyMat->bigE) free(keyMat->bigE); if (keyMat->bigN) free(keyMat->bigN); if (keyMat->bigD) free(keyMat->bigD); if (keyMat->bigP) free(keyMat->bigP); if (keyMat->bigQ) free(keyMat->bigQ); if (keyMat->bigDMP1) free(keyMat->bigDMP1); if (keyMat->bigDMQ1) free(keyMat->bigDMQ1); if (keyMat->bigIQMP) free(keyMat->bigIQMP); free(keyMat); } // Save the key data in PKCS#11 int crypto_save_dsa ( CK_SESSION_HANDLE hSession, char* label, char* objID, size_t objIDLen, int noPublicKey, Botan::DSA_PrivateKey* dsa ) { dsa_key_material_t* keyMat = crypto_malloc_dsa(dsa); if (keyMat == NULL) { fprintf(stderr, "ERROR: Could not convert the key material to binary information.\n"); return 1; } CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY, privClass = CKO_PRIVATE_KEY; CK_KEY_TYPE keyType = CKK_DSA; CK_BBOOL ckTrue = CK_TRUE, ckFalse = CK_FALSE, ckToken = CK_TRUE; if (noPublicKey) { ckToken = CK_FALSE; } CK_ATTRIBUTE pubTemplate[] = { { CKA_CLASS, &pubClass, sizeof(pubClass) }, { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, { CKA_LABEL, label, strlen(label) }, { CKA_ID, objID, objIDLen }, { CKA_TOKEN, &ckToken, sizeof(ckToken) }, { CKA_VERIFY, &ckTrue, sizeof(ckTrue) }, { CKA_ENCRYPT, &ckFalse, sizeof(ckFalse) }, { CKA_WRAP, &ckFalse, sizeof(ckFalse) }, { CKA_PRIME, keyMat->bigP, keyMat->sizeP }, { CKA_SUBPRIME, keyMat->bigQ, keyMat->sizeQ }, { CKA_BASE, keyMat->bigG, keyMat->sizeG }, { CKA_VALUE, keyMat->bigY, keyMat->sizeY } }; CK_ATTRIBUTE privTemplate[] = { { CKA_CLASS, &privClass, sizeof(privClass) }, { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, { CKA_LABEL, label, strlen(label) }, { CKA_ID, objID, objIDLen }, { CKA_SIGN, &ckTrue, sizeof(ckTrue) }, { CKA_DECRYPT, &ckFalse, sizeof(ckFalse) }, { CKA_UNWRAP, &ckFalse, sizeof(ckFalse) }, { CKA_SENSITIVE, &ckTrue, sizeof(ckTrue) }, { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, { CKA_PRIVATE, &ckTrue, sizeof(ckTrue) }, { CKA_EXTRACTABLE, &ckFalse, sizeof(ckFalse) }, { CKA_PRIME, keyMat->bigP, keyMat->sizeP }, { CKA_SUBPRIME, keyMat->bigQ, keyMat->sizeQ }, { CKA_BASE, keyMat->bigG, keyMat->sizeG }, { CKA_VALUE, keyMat->bigX, keyMat->sizeX } }; CK_OBJECT_HANDLE hKey1, hKey2; CK_RV rv = p11->C_CreateObject(hSession, privTemplate, 15, &hKey1); if (rv != CKR_OK) { fprintf(stderr, "ERROR: Could not save the private key in the token. " "Maybe the algorithm is not supported.\n"); crypto_free_dsa(keyMat); return 1; } rv = p11->C_CreateObject(hSession, pubTemplate, 12, &hKey2); crypto_free_dsa(keyMat); if (rv != CKR_OK) { p11->C_DestroyObject(hSession, hKey1); fprintf(stderr, "ERROR: Could not save the public key in the token.\n"); return 1; } printf("The key pair has been imported.\n"); return 0; } // Convert the Botan key to binary dsa_key_material_t* crypto_malloc_dsa(Botan::DSA_PrivateKey* dsa) { if (dsa == NULL) { return NULL; } dsa_key_material_t *keyMat = (dsa_key_material_t *)malloc(sizeof(dsa_key_material_t)); if (keyMat == NULL) { return NULL; } keyMat->sizeP = dsa->group_p().bytes(); keyMat->sizeQ = dsa->group_q().bytes(); keyMat->sizeG = dsa->group_g().bytes(); keyMat->sizeX = dsa->get_x().bytes(); keyMat->sizeY = dsa->get_y().bytes(); keyMat->bigP = (CK_VOID_PTR)malloc(keyMat->sizeP); keyMat->bigQ = (CK_VOID_PTR)malloc(keyMat->sizeQ); keyMat->bigG = (CK_VOID_PTR)malloc(keyMat->sizeG); keyMat->bigX = (CK_VOID_PTR)malloc(keyMat->sizeX); keyMat->bigY = (CK_VOID_PTR)malloc(keyMat->sizeY); if (!keyMat->bigP || !keyMat->bigQ || !keyMat->bigG || !keyMat->bigX || !keyMat->bigY) { crypto_free_dsa(keyMat); return NULL; } dsa->group_p().binary_encode((Botan::byte*)keyMat->bigP); dsa->group_q().binary_encode((Botan::byte*)keyMat->bigQ); dsa->group_g().binary_encode((Botan::byte*)keyMat->bigG); dsa->get_x().binary_encode((Botan::byte*)keyMat->bigX); dsa->get_y().binary_encode((Botan::byte*)keyMat->bigY); return keyMat; } // Free the memory of the key void crypto_free_dsa(dsa_key_material_t* keyMat) { if (keyMat == NULL) return; if (keyMat->bigP) free(keyMat->bigP); if (keyMat->bigQ) free(keyMat->bigQ); if (keyMat->bigG) free(keyMat->bigG); if (keyMat->bigX) free(keyMat->bigX); if (keyMat->bigY) free(keyMat->bigY); free(keyMat); } #ifdef WITH_ECC // Save the key data in PKCS#11 int crypto_save_ecdsa ( CK_SESSION_HANDLE hSession, char* label, char* objID, size_t objIDLen, int noPublicKey, Botan::ECDSA_PrivateKey* ecdsa ) { ecdsa_key_material_t* keyMat = crypto_malloc_ecdsa(ecdsa); if (keyMat == NULL) { fprintf(stderr, "ERROR: Could not convert the key material to binary information.\n"); return 1; } CK_OBJECT_CLASS pubClass = CKO_PUBLIC_KEY, privClass = CKO_PRIVATE_KEY; CK_KEY_TYPE keyType = CKK_ECDSA; CK_BBOOL ckTrue = CK_TRUE, ckFalse = CK_FALSE, ckToken = CK_TRUE; if (noPublicKey) { ckToken = CK_FALSE; } CK_ATTRIBUTE pubTemplate[] = { { CKA_CLASS, &pubClass, sizeof(pubClass) }, { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, { CKA_LABEL, label, strlen(label) }, { CKA_ID, objID, objIDLen }, { CKA_TOKEN, &ckToken, sizeof(ckToken) }, { CKA_VERIFY, &ckTrue, sizeof(ckTrue) }, { CKA_ENCRYPT, &ckFalse, sizeof(ckFalse) }, { CKA_WRAP, &ckFalse, sizeof(ckFalse) }, { CKA_EC_PARAMS, keyMat->derParams, keyMat->sizeParams }, { CKA_EC_POINT, keyMat->derQ, keyMat->sizeQ } }; CK_ATTRIBUTE privTemplate[] = { { CKA_CLASS, &privClass, sizeof(privClass) }, { CKA_KEY_TYPE, &keyType, sizeof(keyType) }, { CKA_LABEL, label, strlen(label) }, { CKA_ID, objID, objIDLen }, { CKA_SIGN, &ckTrue, sizeof(ckTrue) }, { CKA_DECRYPT, &ckFalse, sizeof(ckFalse) }, { CKA_UNWRAP, &ckFalse, sizeof(ckFalse) }, { CKA_SENSITIVE, &ckTrue, sizeof(ckTrue) }, { CKA_TOKEN, &ckTrue, sizeof(ckTrue) }, { CKA_PRIVATE, &ckTrue, sizeof(ckTrue) }, { CKA_EXTRACTABLE, &ckFalse, sizeof(ckFalse) }, { CKA_EC_PARAMS, keyMat->derParams, keyMat->sizeParams }, { CKA_VALUE, keyMat->bigD, keyMat->sizeD } }; CK_OBJECT_HANDLE hKey1, hKey2; CK_RV rv = p11->C_CreateObject(hSession, privTemplate, 13, &hKey1); if (rv != CKR_OK) { fprintf(stderr, "ERROR: Could not save the private key in the token. " "Maybe the algorithm is not supported.\n"); crypto_free_ecdsa(keyMat); return 1; } rv = p11->C_CreateObject(hSession, pubTemplate, 10, &hKey2); crypto_free_ecdsa(keyMat); if (rv != CKR_OK) { p11->C_DestroyObject(hSession, hKey1); fprintf(stderr, "ERROR: Could not save the public key in the token.\n"); return 1; } printf("The key pair has been imported.\n"); return 0; } // Convert the Botan key to binary ecdsa_key_material_t* crypto_malloc_ecdsa(Botan::ECDSA_PrivateKey* ecdsa) { if (ecdsa == NULL) { return NULL; } ecdsa_key_material_t *keyMat = (ecdsa_key_material_t *)malloc(sizeof(ecdsa_key_material_t)); if (keyMat == NULL) { return NULL; } #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,0) std::vector derEC = ecdsa->domain().DER_encode(Botan::EC_DOMPAR_ENC_OID); Botan::secure_vector derPoint; #else Botan::SecureVector derEC = ecdsa->domain().DER_encode(Botan::EC_DOMPAR_ENC_OID); Botan::SecureVector derPoint; #endif try { #if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,0) Botan::secure_vector repr = Botan::EC2OSP(ecdsa->public_point(), Botan::PointGFp::UNCOMPRESSED); #else Botan::SecureVector repr = Botan::EC2OSP(ecdsa->public_point(), Botan::PointGFp::UNCOMPRESSED); #endif derPoint = Botan::DER_Encoder() .encode(repr, Botan::OCTET_STRING) .get_contents(); } catch (...) { return NULL; } keyMat->sizeParams = derEC.size(); keyMat->sizeD = ecdsa->private_value().bytes(); keyMat->sizeQ = derPoint.size(); keyMat->derParams = (CK_VOID_PTR)malloc(keyMat->sizeParams); keyMat->bigD = (CK_VOID_PTR)malloc(keyMat->sizeD); keyMat->derQ = (CK_VOID_PTR)malloc(keyMat->sizeQ); if (!keyMat->derParams || !keyMat->bigD || !keyMat->derQ) { crypto_free_ecdsa(keyMat); return NULL; } memcpy(keyMat->derParams, &derEC[0], derEC.size()); ecdsa->private_value().binary_encode((Botan::byte*)keyMat->bigD); memcpy(keyMat->derQ, &derPoint[0], derPoint.size()); return keyMat; } // Free the memory of the key void crypto_free_ecdsa(ecdsa_key_material_t* keyMat) { if (keyMat == NULL) return; if (keyMat->derParams) free(keyMat->derParams); if (keyMat->bigD) free(keyMat->bigD); if (keyMat->derQ) free(keyMat->derQ); free(keyMat); } #endif