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