Cant Get ECDH to Work with HKDF crypto

Hi guys, I am creatiny encryption with nrf9160. 

I managed to do CHACHA20-poly1305 and ECDH 

But now I am trying to pass ECDH Master key to HKDF-SHA256 to generate a temp session key which fails. I have look through examples and tried most of the stuff but HKDF function always fails at the end of generating a session key with Error of -137 or -135

Here is my lib

#pragma once

#include <psa/crypto.h>
#include <psa/crypto_extra.h>
#include <array>
#include <span>
#include <cstring>
#include <concepts>
#include <cstdint>
#include "rtos/mutex.hpp"

namespace CryptoApi
{
    using key32_ECDH = std::array<std::uint8_t, 32>;

    namespace ECDHHelper
    {
        struct result
        {
            key32_ECDH dev_pub;              /* device public key (send upstream)   */
            key32_ECDH mk;                   /* raw master key (shared secret)      */
            std::array<std::uint8_t, 32> sk; /* session key (HKDF-SHA-256 32 bytes) */
        };

        /**
         * @brief
         *
         * @param server_pub, takes in public key from server
         * @param out output master key and also session key
         * @return psa_status_t
         */
        psa_status_t helper(const key32_ECDH &server_pub, result &out);

    }

    class ECDHCurveX25519
    {
    public:
        ECDHCurveX25519()
        {
            lock_.init();
             psa_crypto_init();
        }
        psa_status_t init();
        /*Key pair */
        psa_status_t generate();
        psa_status_t import_private(const key32_ECDH &priv);
        const key32_ECDH &public_key() const noexcept { return pub_; }
        const key32_ECDH &private_key() const noexcept { return priv_; }
        /*ECDH: our priv + peer pub -> shared secret*/
        psa_status_t agree(const key32_ECDH &peer_pub, key32_ECDH &shared_out) const;

        /*HKDF-SHA256*/
        psa_status_t hkdf_sha256(const key32_ECDH &mk,
                                 const void *info, size_t info_len,
                                 void *out, size_t out_len);

        /* -------- housekeeping ------------------------------------------- */
        void zeroise();
        ~ECDHCurveX25519() { zeroise(); }

    private:
        psa_key_attributes_t make_attr();

        key32_ECDH priv_{}; /* may be all zero if you used generate() only */
        key32_ECDH pub_{};
        psa_key_id_t key_id_{0};

        mutable rtos::Mutex lock_;
    };

    inline psa_status_t ECDHCurveX25519::init()
    {
        rtos::Mutex::Guard Glock(lock_);
        psa_status_t stat = psa_crypto_init();
        return stat;
    }

    inline psa_key_attributes_t ECDHCurveX25519::make_attr()
    {

        rtos::Mutex::Guard Glock(lock_);
        psa_key_attributes_t a = PSA_KEY_ATTRIBUTES_INIT;
        psa_set_key_usage_flags(&a, PSA_KEY_USAGE_DERIVE);
        psa_set_key_lifetime(&a, PSA_KEY_LIFETIME_VOLATILE);
        psa_set_key_algorithm(&a, PSA_ALG_ECDH);
        psa_set_key_type(&a, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_MONTGOMERY));
        psa_set_key_bits(&a, 255);
        return a;
    }

    inline psa_status_t ECDHCurveX25519::generate()
    {
        rtos::Mutex::Guard Glock(lock_);
        psa_key_attributes_t attr = make_attr();
        psa_status_t st = psa_generate_key(&attr, &key_id_);
        if (st)
        {
            return st;
        }
        size_t olen = 0;

        return psa_export_public_key(key_id_, pub_.data(), pub_.size(), &olen);
    }

    inline psa_status_t ECDHCurveX25519::import_private(const key32_ECDH &priv)
    {
        rtos::Mutex::Guard Glock(lock_);
        priv_ = priv;
        psa_destroy_key(key_id_);
        psa_key_attributes_t attr = make_attr();
        size_t olen = 0;
        psa_status_t st = psa_import_key(&attr, priv_.data(), priv_.size(), &key_id_);
        if (st)
        {
            return st;
        }

        return psa_export_public_key(key_id_, pub_.data(), pub_.size(), &olen);
    }

    inline psa_status_t ECDHCurveX25519::agree(const key32_ECDH &peer_pub, key32_ECDH &shared_out) const
    {
        rtos::Mutex::Guard Glock(lock_);

        size_t olen = 0;
        return psa_raw_key_agreement(PSA_ALG_ECDH, key_id_, peer_pub.data(), peer_pub.size(), shared_out.data(), shared_out.size(), &olen);
    }

    inline psa_status_t ECDHCurveX25519::hkdf_sha256(const key32_ECDH &mk, const void *info, size_t info_len, void *out, size_t out_len)
    {
        rtos::Mutex::Guard Glock(lock_);

        psa_status_t st;
        psa_key_id_t mk_id = 0;
        psa_key_derivation_operation_t op = PSA_KEY_DERIVATION_OPERATION_INIT;

        /* 1. import MK as DERIVE key */
        {
            psa_key_attributes_t a = PSA_KEY_ATTRIBUTES_INIT;
            psa_set_key_lifetime(&a, PSA_KEY_LIFETIME_VOLATILE);
            psa_set_key_type(&a, PSA_KEY_TYPE_DERIVE); // ★
            psa_set_key_bits(&a, 256);
            psa_set_key_usage_flags(&a, PSA_KEY_USAGE_DERIVE); // ★
            psa_set_key_algorithm(&a,
                                  PSA_ALG_HKDF(PSA_ALG_SHA_256)); // ★

            st = psa_import_key(&a, mk.data(), mk.size(), &mk_id);
            if (st)
                goto exit;
        }

        /* 2. HKDF-SHA-256 */
        st = psa_key_derivation_setup(&op,
                                      PSA_ALG_HKDF(PSA_ALG_SHA_256));
        if (st)
            goto exit;

        st = psa_key_derivation_input_bytes(&op,
                                            PSA_KEY_DERIVATION_INPUT_SALT, nullptr, 0); // empty salt
        if (st)
            goto exit;

        st = psa_key_derivation_input_key(&op,
                                          PSA_KEY_DERIVATION_INPUT_SECRET, mk_id); // secret handle
        if (st)
            goto exit;

        /* 2-a. tell HKDF how many bytes you will take -------- */
        st = psa_key_derivation_set_capacity(&op, out_len); // typically 32
        if (st)
            goto exit;

        if (info_len)
        {
            st = psa_key_derivation_input_bytes(&op,
                                                PSA_KEY_DERIVATION_INPUT_INFO,
                                                static_cast<const uint8_t *>(info), info_len);
            if (st)
                goto exit;
        }

        /* 3. pull 32-byte session key */
        st = psa_key_derivation_output_bytes(&op,
                                             static_cast<uint8_t *>(out), out_len); // raw bytes

    exit:
        psa_key_derivation_abort(&op);
        psa_destroy_key(mk_id);
        return st;
    }

    inline void ECDHCurveX25519::zeroise()
    {
        rtos::Mutex::Guard Glock(lock_);
        psa_destroy_key(key_id_);
        key_id_ = 0;
        std::memset(priv_.data(), 0, priv_.size());
        std::memset(pub_.data(), 0, pub_.size());
    }

}

And here is the helper function I created to test everything

#include "ECDHCurveX25519.hpp"
#include <zephyr/logging/log.h>

LOG_MODULE_REGISTER(ECDH_HELPER, LOG_LEVEL_INF);

namespace CryptoApi
{
   namespace ECDHHelper
   {

      /**
       * @brief
       *
       * @param server_pub, takes in public key from server
       * @param out output master key and also session key
       * @return psa_status_t
       */
      psa_status_t helper(const key32_ECDH &server_pub, result &out)
      {
         ECDHCurveX25519 ECDHCurveX25519_;

         psa_status_t st;

         // /*Fresh key pair*/
         // st = ECDHCurveX25519_.init();
         // if (st)
         // {
         //    LOG_ERR("Initialization failed: %d", st);
         //    return st;
         // }

         st = ECDHCurveX25519_.generate();
         if (st)
         {
            LOG_ERR("generate failed: %d", st);
            return st;
         }

         /*ECDH -> MK*/
         st = ECDHCurveX25519_.agree(server_pub, out.mk);
         if (st)
         {
            LOG_ERR("agree: %d", st);
            return st;
         }

         LOG_HEXDUMP_INF(out.mk.data(), out.mk.size(), "MK (shared)");

         /* 3) HKDF-SHA-256 → SK ------------------------------------------- */
         st = ECDHCurveX25519_.hkdf_sha256(out.mk, nullptr, 0,
                                           out.sk.data(), out.sk.size() );
         if (st)
         {
            LOG_ERR("hkdf: %d", st);
            return st;
         }

         LOG_HEXDUMP_INF(out.sk.data(), out.sk.size(), "SK (session)");

         /* 4) wipe MK in RAM if you won’t need it beyond this struct --------
          * (comment this out if you want to store MK)                        */
         // std::memset(out.mk.data(), 0, out.mk.size());

         return PSA_SUCCESS;
      }

   }

}

my master key is generated succesfully the issue comes at hkdf function here is my output as well


Parents Reply Children
No Data
Related