Sw backend ECDH implementation 55/288055/12
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Thu, 9 Feb 2023 08:11:02 +0000 (09:11 +0100)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Fri, 17 Mar 2023 09:20:01 +0000 (10:20 +0100)
Unit tests included

Change-Id: I8e91ebbba587451e3b96212d7e74a3585e480702

src/manager/crypto/sw-backend/internals.cpp
src/manager/crypto/sw-backend/internals.h
src/manager/crypto/sw-backend/obj.cpp
unit-tests/test_sw-backend.cpp

index 2b08293..e1e40a5 100644 (file)
@@ -36,6 +36,7 @@
 #include <openssl/obj_mac.h>
 
 #include <ckm/ckm-error.h>
+#include <key-impl.h>
 #include <dpl/log/log.h>
 #include <utils.h>
 
@@ -185,6 +186,17 @@ typedef ParamCheck<ParamName::GEN_EC,
                ElipticCurve::prime256v1,
                ElipticCurve::secp384r1>> EcdsaEcCheck;
 
+// key derivation
+typedef ParamCheck<ParamName::ALGO_TYPE,
+               AlgoType,
+               true,
+               Type<AlgoType>::Equals<AlgoType::ECDH>> IsEcdh;
+
+typedef ParamCheck<ParamName::ECDH_PUBKEY,
+               RawBuffer,
+               true,
+               DefaultValidator<RawBuffer>> EcdhPubKeyCheck;
+
 typedef std::map<AlgoType, ValidatorVector> ValidatorMap;
 ValidatorMap initValidators()
 {
@@ -201,6 +213,7 @@ ValidatorMap initValidators()
        validators.emplace(AlgoType::AES_CFB, VBuilder<IvSizeCheck>::Build());
        validators.emplace(AlgoType::AES_GCM, VBuilder<GcmIvCheck, GcmTagCheck>::Build());
        validators.emplace(AlgoType::RSA_OAEP, VBuilder<RsaLabelCheck>::Build());
+       validators.emplace(AlgoType::ECDH, VBuilder<EcdhPubKeyCheck>::Build());
        return validators;
 };
 
@@ -651,6 +664,24 @@ RawBuffer decryptDataAesGcmPacked(
                           aad);
 }
 
+EC_KEY* getEcKey(EVP_PKEY* evpKey)
+{
+       int subType = EVP_PKEY_type(EVP_PKEY_id(evpKey));
+       if (subType != EVP_PKEY_EC)
+               ThrowErr(Exc::Crypto::InputParam, "Invalid key type: ", subType);
+
+       EC_KEY *ecKey = EVP_PKEY_get0_EC_KEY(evpKey);
+       if (!ecKey)
+               ThrowErr(Exc::Crypto::InternalError, "Can't get EC key");
+
+       return ecKey;
+}
+
+int getCurve(const EC_KEY* ecKey)
+{
+       return EC_GROUP_get_curve_name(EC_KEY_get0_group(ecKey));
+}
+
 } // namespace
 
 
@@ -898,6 +929,63 @@ int verify(EVP_PKEY *pkey,
        return digestVerifyMessage(pkey, message, signature, md_algo, rsa_padding);
 }
 
+Data deriveECDH(const EvpShPtr &pkey, const CryptoAlgorithm &alg)
+{
+       validateParams<IsEcdh>(alg);
+
+       EC_KEY *ecKey = getEcKey(pkey.get());
+
+       // get private key curve name
+       int prvCurve = getCurve(ecKey);
+
+       auto prv = EC_KEY_get0_private_key(ecKey);
+       if (!prv)
+               ThrowErr(Exc::Crypto::InputParam, "ECDH requires own private EC key");
+
+       // Create the context for the shared secret derivation
+       auto ctx = newCtx(pkey.get());
+       if (ctx == nullptr)
+               ThrowErr(Exc::Crypto::InternalError, "Key context creation failed");
+
+       // import peer's public key from buffer
+       RawBuffer pubKeyBuffer;
+       [[maybe_unused]] bool hasPubKey = alg.getParam(ParamName::ECDH_PUBKEY, pubKeyBuffer);
+       assert(hasPubKey);
+       auto peerKey = std::make_shared<KeyImpl>(pubKeyBuffer);
+       if (peerKey->getType() != KeyType::KEY_ECDSA_PUBLIC)
+               ThrowErr(Exc::Crypto::InputParam, "ECDH requires peer's public EC key");
+       auto peerEvp = peerKey->getEvpShPtr().get();
+       assert(peerEvp);
+
+       int pubCurve = getCurve(getEcKey(peerEvp));
+
+       if (pubCurve != prvCurve)
+               ThrowErr(Exc::Crypto::InputParam, "Private and public key use different ECs");
+
+       // initialise
+       if (1 != EVP_PKEY_derive_init(ctx.get()))
+               ThrowErr(Exc::Crypto::InternalError, "EVP_PKEY_derive_init failed");
+
+       // provide the peer public key
+       if (1 != EVP_PKEY_derive_set_peer(ctx.get(), peerEvp))
+               ThrowErr(Exc::Crypto::InternalError, "EVP_PKEY_derive_set_peer failed");
+
+       // determine buffer length for shared secret
+       size_t secretLen;
+       if(1 != EVP_PKEY_derive(ctx.get(), nullptr, &secretLen))
+               ThrowErr(Exc::Crypto::InternalError, "Failed to determine ECDH secret length");
+
+       RawBuffer secret(secretLen);
+
+       // derive the shared secret
+       if (1 != (EVP_PKEY_derive(ctx.get(), secret.data(), &secretLen)))
+               ThrowErr(Exc::Crypto::InternalError, "ECDH failed");
+
+       // Never use a derived secret directly. Typically it is passed
+       // through some hash function to produce a key
+       return { DataType::BINARY_DATA, std::move(secret)};
+}
+
 } // namespace Internals
 } // namespace SW
 } // namespace Crypto
index fa8bd93..52f8a2e 100644 (file)
@@ -90,6 +90,8 @@ int verify(EVP_PKEY *pkey,
                   const RawBuffer &message,
                   const RawBuffer &signature);
 
+Data deriveECDH(const EvpShPtr &pkey, const CryptoAlgorithm &alg);
+
 } // namespace Internals
 } // namespace SW
 } // namespace Crypto
index 7961458..f256eea 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <generic-backend/exception.h>
 #include <sw-backend/obj.h>
+#include <sw-backend/store.h>
 #include <sw-backend/internals.h>
 
 namespace CKM {
@@ -124,9 +125,11 @@ RawBuffer AKey::decrypt(const CryptoAlgorithm &alg, const RawBuffer &data)
        return Internals::asymmetricDecrypt(getEvpShPtr(), alg, data);
 }
 
-Token AKey::derive(const CryptoAlgorithm &, const Password &, const RawBuffer &)
+Token AKey::derive(const CryptoAlgorithm &alg, const Password &pass, const RawBuffer & /* digest */)
 {
-       return Token();
+       auto data = Internals::deriveECDH(getEvpShPtr(), alg);
+
+       return Token(backendId(), data.type, Store::pack(data.buffer, pass));
 }
 
 EvpShPtr AKey::getEvpShPtr()
index fe7da08..f21e679 100644 (file)
@@ -1015,4 +1015,101 @@ NEGATIVE_TEST_CASE(cert)
        BOOST_REQUIRE_THROW(cert.getEvpShPtr(), Exc::Crypto::InternalError);
 }
 
+POSITIVE_TEST_CASE(deriveECDH)
+{
+       CryptoAlgorithm gen;
+       gen.setParam(ParamName::ALGO_TYPE, AlgoType::ECDSA_GEN);
+       gen.setParam(ParamName::GEN_EC, ElipticCurve::prime256v1);
+
+       auto ours = STORE.generateAKey(gen, "", "", RawBuffer(), RawBuffer());
+       auto peers = STORE.generateAKey(gen, "", "", RawBuffer(), RawBuffer());
+
+       CryptoAlgorithm derive;
+       derive.setParam(ParamName::ALGO_TYPE, AlgoType::ECDH);
+
+       // our part
+       GObjUPtr peersPublic;
+       BOOST_REQUIRE_NO_THROW(peersPublic = STORE.getObject(peers.second, ""));
+       derive.setParam(ParamName::ECDH_PUBKEY, peersPublic->getBinary());
+
+       GObjUPtr oursPrivate;
+       BOOST_REQUIRE_NO_THROW(oursPrivate = STORE.getObject(ours.first, ""));
+
+       Token oursDerived;
+       BOOST_REQUIRE_NO_THROW(oursDerived = oursPrivate->derive(derive, "", RawBuffer()));
+
+       BOOST_REQUIRE(oursDerived.backendId == CryptoBackend::OpenSSL);
+       BOOST_REQUIRE(oursDerived.dataType == DataType::BINARY_DATA);
+
+       GObjUPtr oursDerivedObj;
+       BOOST_REQUIRE_NO_THROW(oursDerivedObj = STORE.getObject(oursDerived, ""));
+       BOOST_REQUIRE(!oursDerivedObj->getBinary().empty());
+
+       // peer's part
+       GObjUPtr oursPublic;
+       BOOST_REQUIRE_NO_THROW(oursPublic = STORE.getObject(ours.second, ""));
+       derive.setParam(ParamName::ECDH_PUBKEY, oursPublic->getBinary());
+
+       GObjUPtr peersPrivate;
+       BOOST_REQUIRE_NO_THROW(peersPrivate = STORE.getObject(peers.first, ""));
+
+       Token peersDerived;
+       BOOST_REQUIRE_NO_THROW(peersDerived = peersPrivate->derive(derive, "", RawBuffer()));
+
+       BOOST_REQUIRE(peersDerived.backendId == CryptoBackend::OpenSSL);
+       BOOST_REQUIRE(peersDerived.dataType == DataType::BINARY_DATA);
+
+       GObjUPtr peersDerivedObj;
+       BOOST_REQUIRE_NO_THROW(peersDerivedObj = STORE.getObject(peersDerived, ""));
+       BOOST_REQUIRE(oursDerivedObj->getBinary() == peersDerivedObj->getBinary());
+}
+
+NEGATIVE_TEST_CASE(deriveECDH)
+{
+       CryptoAlgorithm gen;
+       gen.setParam(ParamName::ALGO_TYPE, AlgoType::ECDSA_GEN);
+       gen.setParam(ParamName::GEN_EC, ElipticCurve::prime256v1);
+
+       auto ours = STORE.generateAKey(gen, "", "", RawBuffer(), RawBuffer());
+       auto peers = STORE.generateAKey(gen, "", "", RawBuffer(), RawBuffer());
+
+       GObjUPtr oursPrivate;
+       BOOST_REQUIRE_NO_THROW(oursPrivate = STORE.getObject(ours.first, ""));
+
+       CryptoAlgorithm derive;
+
+       // no algorithm
+       BOOST_REQUIRE_THROW(oursPrivate->derive(derive, "", RawBuffer()), Exc::Crypto::InputParam);
+
+       // wrong algorithm
+       derive.setParam(ParamName::ALGO_TYPE, AlgoType::ECDSA_GEN);
+       BOOST_REQUIRE_THROW(oursPrivate->derive(derive, "", RawBuffer()), Exc::Crypto::InputParam);
+
+       // no pubkey
+       derive.setParam(ParamName::ALGO_TYPE, AlgoType::ECDH);
+       BOOST_REQUIRE_THROW(oursPrivate->derive(derive, "", RawBuffer()), Exc::Crypto::InputParam);
+
+       // empty pubkey
+       derive.setParam(ParamName::ECDH_PUBKEY, RawBuffer());
+       BOOST_REQUIRE_THROW(oursPrivate->derive(derive, "", RawBuffer()), Exc::Crypto::InputParam);
+
+       // private key instead of public
+       derive.setParam(ParamName::ECDH_PUBKEY, oursPrivate->getBinary());
+       BOOST_REQUIRE_THROW(oursPrivate->derive(derive, "", RawBuffer()), Exc::Crypto::InputParam);
+
+       // public key instead of private key
+       GObjUPtr oursPublic;
+       BOOST_REQUIRE_NO_THROW(oursPublic = STORE.getObject(ours.second, ""));
+       derive.setParam(ParamName::ECDH_PUBKEY, oursPublic->getBinary());
+       BOOST_REQUIRE_THROW(oursPublic->derive(derive, "", RawBuffer()), Exc::Crypto::InputParam);
+
+       // RSA key instead of EC
+       gen.setParam(ParamName::ALGO_TYPE, AlgoType::RSA_GEN);
+       gen.setParam(ParamName::GEN_KEY_LEN, 1024);
+       auto rsa = STORE.generateAKey(gen, "", "", RawBuffer(), RawBuffer());
+       GObjUPtr rsaPrivate;
+       BOOST_REQUIRE_NO_THROW(rsaPrivate = STORE.getObject(rsa.first, ""));
+       BOOST_REQUIRE_THROW(rsaPrivate->derive(derive, "", RawBuffer()), Exc::Crypto::InputParam);
+}
+
 BOOST_AUTO_TEST_SUITE_END()