| /* |
| * Copyright (c) 2010 SURFnet bv |
| * 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. |
| */ |
| |
| /***************************************************************************** |
| OSSLECDSA.cpp |
| |
| OpenSSL ECDSA asymmetric algorithm implementation |
| *****************************************************************************/ |
| |
| #include "config.h" |
| #ifdef WITH_ECC |
| #include "log.h" |
| #include "OSSLECDSA.h" |
| #include "CryptoFactory.h" |
| #include "ECParameters.h" |
| #include "OSSLECKeyPair.h" |
| #include "OSSLComp.h" |
| #include "OSSLUtil.h" |
| #include <algorithm> |
| #include <openssl/ecdsa.h> |
| #include <openssl/pem.h> |
| #include <openssl/err.h> |
| #ifdef WITH_FIPS |
| #include <openssl/fips.h> |
| #endif |
| #include <string.h> |
| |
| // Signing functions |
| bool OSSLECDSA::sign(PrivateKey* privateKey, const ByteString& dataToSign, |
| ByteString& signature, const AsymMech::Type mechanism, |
| const void* /* param = NULL */, const size_t /* paramLen = 0 */) |
| { |
| if (mechanism != AsymMech::ECDSA) |
| { |
| ERROR_MSG("Invalid mechanism supplied (%i)", mechanism); |
| return false; |
| } |
| |
| // Check if the private key is the right type |
| if (!privateKey->isOfType(OSSLECPrivateKey::type)) |
| { |
| ERROR_MSG("Invalid key type supplied"); |
| |
| return false; |
| } |
| |
| OSSLECPrivateKey* pk = (OSSLECPrivateKey*) privateKey; |
| EC_KEY* eckey = pk->getOSSLKey(); |
| |
| if (eckey == NULL) |
| { |
| ERROR_MSG("Could not get the OpenSSL private key"); |
| |
| return false; |
| } |
| |
| // Use the OpenSSL implementation and not any engine |
| #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
| |
| #ifdef WITH_FIPS |
| if (FIPS_mode()) |
| ECDSA_set_method(eckey, FIPS_ecdsa_openssl()); |
| else |
| ECDSA_set_method(eckey, ECDSA_OpenSSL()); |
| #else |
| ECDSA_set_method(eckey, ECDSA_OpenSSL()); |
| #endif |
| |
| #else |
| EC_KEY_set_method(eckey, EC_KEY_OpenSSL()); |
| #endif |
| |
| // Perform the signature operation |
| size_t len = pk->getOrderLength(); |
| if (len == 0) |
| { |
| ERROR_MSG("Could not get the order length"); |
| return false; |
| } |
| signature.resize(2 * len); |
| memset(&signature[0], 0, 2 * len); |
| ECDSA_SIG *sig = ECDSA_do_sign(dataToSign.const_byte_str(), dataToSign.size(), eckey); |
| if (sig == NULL) |
| { |
| ERROR_MSG("ECDSA sign failed (0x%08X)", ERR_get_error()); |
| return false; |
| } |
| // Store the 2 values with padding |
| const BIGNUM* bn_r = NULL; |
| const BIGNUM* bn_s = NULL; |
| ECDSA_SIG_get0(sig, &bn_r, &bn_s); |
| BN_bn2bin(bn_r, &signature[len - BN_num_bytes(bn_r)]); |
| BN_bn2bin(bn_s, &signature[2 * len - BN_num_bytes(bn_s)]); |
| ECDSA_SIG_free(sig); |
| return true; |
| } |
| |
| bool OSSLECDSA::signInit(PrivateKey* /*privateKey*/, const AsymMech::Type /*mechanism*/, |
| const void* /* param = NULL */, const size_t /* paramLen = 0 */) |
| { |
| ERROR_MSG("ECDSA does not support multi part signing"); |
| |
| return false; |
| } |
| |
| bool OSSLECDSA::signUpdate(const ByteString& /*dataToSign*/) |
| { |
| ERROR_MSG("ECDSA does not support multi part signing"); |
| |
| return false; |
| } |
| |
| bool OSSLECDSA::signFinal(ByteString& /*signature*/) |
| { |
| ERROR_MSG("ECDSA does not support multi part signing"); |
| |
| return false; |
| } |
| |
| // Verification functions |
| bool OSSLECDSA::verify(PublicKey* publicKey, const ByteString& originalData, |
| const ByteString& signature, const AsymMech::Type mechanism, |
| const void* /* param = NULL */, const size_t /* paramLen = 0 */) |
| { |
| if (mechanism != AsymMech::ECDSA) |
| { |
| ERROR_MSG("Invalid mechanism supplied (%i)", mechanism); |
| return false; |
| } |
| |
| // Check if the private key is the right type |
| if (!publicKey->isOfType(OSSLECPublicKey::type)) |
| { |
| ERROR_MSG("Invalid key type supplied"); |
| |
| return false; |
| } |
| |
| OSSLECPublicKey* pk = (OSSLECPublicKey*) publicKey; |
| EC_KEY* eckey = pk->getOSSLKey(); |
| |
| if (eckey == NULL) |
| { |
| ERROR_MSG("Could not get the OpenSSL public key"); |
| |
| return false; |
| } |
| |
| // Use the OpenSSL implementation and not any engine |
| #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER) |
| |
| #ifdef WITH_FIPS |
| if (FIPS_mode()) |
| ECDSA_set_method(eckey, FIPS_ecdsa_openssl()); |
| else |
| ECDSA_set_method(eckey, ECDSA_OpenSSL()); |
| #else |
| ECDSA_set_method(eckey, ECDSA_OpenSSL()); |
| #endif |
| |
| #else |
| EC_KEY_set_method(eckey, EC_KEY_OpenSSL()); |
| #endif |
| |
| // Perform the verify operation |
| size_t len = pk->getOrderLength(); |
| if (len == 0) |
| { |
| ERROR_MSG("Could not get the order length"); |
| return false; |
| } |
| if (signature.size() != 2 * len) |
| { |
| ERROR_MSG("Invalid buffer length"); |
| return false; |
| } |
| ECDSA_SIG* sig = ECDSA_SIG_new(); |
| if (sig == NULL) |
| { |
| ERROR_MSG("Could not create an ECDSA_SIG object"); |
| return false; |
| } |
| const unsigned char *s = signature.const_byte_str(); |
| BIGNUM* bn_r = BN_bin2bn(s, len, NULL); |
| BIGNUM* bn_s = BN_bin2bn(s + len, len, NULL); |
| if (bn_r == NULL || bn_s == NULL || |
| !ECDSA_SIG_set0(sig, bn_r, bn_s)) |
| { |
| ERROR_MSG("Could not add data to the ECDSA_SIG object"); |
| ECDSA_SIG_free(sig); |
| return false; |
| } |
| int ret = ECDSA_do_verify(originalData.const_byte_str(), originalData.size(), sig, eckey); |
| if (ret != 1) |
| { |
| if (ret < 0) |
| ERROR_MSG("ECDSA verify failed (0x%08X)", ERR_get_error()); |
| |
| ECDSA_SIG_free(sig); |
| return false; |
| } |
| |
| ECDSA_SIG_free(sig); |
| return true; |
| } |
| |
| bool OSSLECDSA::verifyInit(PublicKey* /*publicKey*/, const AsymMech::Type /*mechanism*/, |
| const void* /* param = NULL */, const size_t /* paramLen = 0 */) |
| { |
| ERROR_MSG("ECDSA does not support multi part verifying"); |
| |
| return false; |
| } |
| |
| bool OSSLECDSA::verifyUpdate(const ByteString& /*originalData*/) |
| { |
| ERROR_MSG("ECDSA does not support multi part verifying"); |
| |
| return false; |
| } |
| |
| bool OSSLECDSA::verifyFinal(const ByteString& /*signature*/) |
| { |
| ERROR_MSG("ECDSA does not support multi part verifying"); |
| |
| return false; |
| } |
| |
| // Encryption functions |
| bool OSSLECDSA::encrypt(PublicKey* /*publicKey*/, const ByteString& /*data*/, |
| ByteString& /*encryptedData*/, const AsymMech::Type /*padding*/) |
| { |
| ERROR_MSG("ECDSA does not support encryption"); |
| |
| return false; |
| } |
| |
| // Decryption functions |
| bool OSSLECDSA::decrypt(PrivateKey* /*privateKey*/, const ByteString& /*encryptedData*/, |
| ByteString& /*data*/, const AsymMech::Type /*padding*/) |
| { |
| ERROR_MSG("ECDSA does not support decryption"); |
| |
| return false; |
| } |
| |
| // Key factory |
| bool OSSLECDSA::generateKeyPair(AsymmetricKeyPair** ppKeyPair, AsymmetricParameters* parameters, RNG* /*rng = NULL */) |
| { |
| // Check parameters |
| if ((ppKeyPair == NULL) || |
| (parameters == NULL)) |
| { |
| return false; |
| } |
| |
| if (!parameters->areOfType(ECParameters::type)) |
| { |
| ERROR_MSG("Invalid parameters supplied for ECDSA key generation"); |
| |
| return false; |
| } |
| |
| ECParameters* params = (ECParameters*) parameters; |
| |
| // Generate the key-pair |
| EC_KEY* eckey = EC_KEY_new(); |
| if (eckey == NULL) |
| { |
| ERROR_MSG("Failed to instantiate OpenSSL ECDSA object"); |
| |
| return false; |
| } |
| |
| EC_GROUP* grp = OSSL::byteString2grp(params->getEC()); |
| EC_KEY_set_group(eckey, grp); |
| EC_GROUP_free(grp); |
| |
| if (!EC_KEY_generate_key(eckey)) |
| { |
| ERROR_MSG("ECDSA key generation failed (0x%08X)", ERR_get_error()); |
| |
| EC_KEY_free(eckey); |
| |
| return false; |
| } |
| |
| // Create an asymmetric key-pair object to return |
| OSSLECKeyPair* kp = new OSSLECKeyPair(); |
| |
| ((OSSLECPublicKey*) kp->getPublicKey())->setFromOSSL(eckey); |
| ((OSSLECPrivateKey*) kp->getPrivateKey())->setFromOSSL(eckey); |
| |
| *ppKeyPair = kp; |
| |
| // Release the key |
| EC_KEY_free(eckey); |
| |
| return true; |
| } |
| |
| unsigned long OSSLECDSA::getMinKeySize() |
| { |
| // Smallest EC group is secp112r1 |
| return 112; |
| } |
| |
| unsigned long OSSLECDSA::getMaxKeySize() |
| { |
| // Biggest EC group is secp521r1 |
| return 521; |
| } |
| |
| bool OSSLECDSA::reconstructKeyPair(AsymmetricKeyPair** ppKeyPair, ByteString& serialisedData) |
| { |
| // Check input |
| if ((ppKeyPair == NULL) || |
| (serialisedData.size() == 0)) |
| { |
| return false; |
| } |
| |
| ByteString dPub = ByteString::chainDeserialise(serialisedData); |
| ByteString dPriv = ByteString::chainDeserialise(serialisedData); |
| |
| OSSLECKeyPair* kp = new OSSLECKeyPair(); |
| |
| bool rv = true; |
| |
| if (!((ECPublicKey*) kp->getPublicKey())->deserialise(dPub)) |
| { |
| rv = false; |
| } |
| |
| if (!((ECPrivateKey*) kp->getPrivateKey())->deserialise(dPriv)) |
| { |
| rv = false; |
| } |
| |
| if (!rv) |
| { |
| delete kp; |
| |
| return false; |
| } |
| |
| *ppKeyPair = kp; |
| |
| return true; |
| } |
| |
| bool OSSLECDSA::reconstructPublicKey(PublicKey** ppPublicKey, ByteString& serialisedData) |
| { |
| // Check input |
| if ((ppPublicKey == NULL) || |
| (serialisedData.size() == 0)) |
| { |
| return false; |
| } |
| |
| OSSLECPublicKey* pub = new OSSLECPublicKey(); |
| |
| if (!pub->deserialise(serialisedData)) |
| { |
| delete pub; |
| |
| return false; |
| } |
| |
| *ppPublicKey = pub; |
| |
| return true; |
| } |
| |
| bool OSSLECDSA::reconstructPrivateKey(PrivateKey** ppPrivateKey, ByteString& serialisedData) |
| { |
| // Check input |
| if ((ppPrivateKey == NULL) || |
| (serialisedData.size() == 0)) |
| { |
| return false; |
| } |
| |
| OSSLECPrivateKey* priv = new OSSLECPrivateKey(); |
| |
| if (!priv->deserialise(serialisedData)) |
| { |
| delete priv; |
| |
| return false; |
| } |
| |
| *ppPrivateKey = priv; |
| |
| return true; |
| } |
| |
| PublicKey* OSSLECDSA::newPublicKey() |
| { |
| return (PublicKey*) new OSSLECPublicKey(); |
| } |
| |
| PrivateKey* OSSLECDSA::newPrivateKey() |
| { |
| return (PrivateKey*) new OSSLECPrivateKey(); |
| } |
| |
| AsymmetricParameters* OSSLECDSA::newParameters() |
| { |
| return (AsymmetricParameters*) new ECParameters(); |
| } |
| |
| bool OSSLECDSA::reconstructParameters(AsymmetricParameters** ppParams, ByteString& serialisedData) |
| { |
| // Check input parameters |
| if ((ppParams == NULL) || (serialisedData.size() == 0)) |
| { |
| return false; |
| } |
| |
| ECParameters* params = new ECParameters(); |
| |
| if (!params->deserialise(serialisedData)) |
| { |
| delete params; |
| |
| return false; |
| } |
| |
| *ppParams = params; |
| |
| return true; |
| } |
| #endif |