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