From 2a486701b3a6384702298a5dbdb69d47a011736a Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Fri, 21 Aug 2026 02:05:36 +0530 Subject: [PATCH 01/15] fix: test for the uppercase prefix intended by `Util::HexToBytes()` --- include/dashbls/util.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dashbls/util.hpp b/include/dashbls/util.hpp index 37fd8547a..fc9ec2cdd 100644 --- a/include/dashbls/util.hpp +++ b/include/dashbls/util.hpp @@ -117,7 +117,7 @@ class Util { } std::vector ret = std::vector(); size_t start_at = 0; - if (hex.rfind("0x", 0) == 0 || hex.rfind("0x", 0) == 0) { + if (hex.rfind("0x", 0) == 0 || hex.rfind("0X", 0) == 0) { start_at = 2; } From 6cce356d0f1a02c0998fd97502d27b80a2ef1367 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Fri, 21 Aug 2026 04:26:43 +0530 Subject: [PATCH 02/15] fix: stop FourBytesToInt shifting into the sign bit --- include/dashbls/util.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dashbls/util.hpp b/include/dashbls/util.hpp index fc9ec2cdd..052a0505e 100644 --- a/include/dashbls/util.hpp +++ b/include/dashbls/util.hpp @@ -143,7 +143,7 @@ class Util { static uint32_t FourBytesToInt(const uint8_t* bytes) { uint32_t sum = 0; for (size_t i = 0; i < 4; i++) { - uint32_t addend = bytes[i] << (8 * (3 - i)); + uint32_t addend = static_cast(bytes[i]) << (8 * (3 - i)); sum += addend; } return sum; From c2050b703ed344e7cc1d39961210b61879bc794a Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Thu, 20 Aug 2026 14:13:24 +0530 Subject: [PATCH 03/15] doc: add MIT license so existing `COPYING.MIT` references land --- COPYING.MIT | 19 +++++++++++++++++++ Makefile.am | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 COPYING.MIT diff --git a/COPYING.MIT b/COPYING.MIT new file mode 100644 index 000000000..72dc60d84 --- /dev/null +++ b/COPYING.MIT @@ -0,0 +1,19 @@ +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Makefile.am b/Makefile.am index 1c1e5d19d..c1b8f3717 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,7 +22,7 @@ noinst_LTLIBRARIES = bin_PROGRAMS = noinst_PROGRAMS = -EXTRA_DIST = +EXTRA_DIST = COPYING.MIT CLEANFILES = From be44876c7810fc6f2134109bd9efae5ea1277699 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Fri, 21 Aug 2026 04:22:30 +0530 Subject: [PATCH 04/15] build: introduce `.gitattributes` and normalize Autotools sources --- .gitattributes | 6 + Makefile.bench.include | 44 +- Makefile.bls.include | 132 +++--- Makefile.mimalloc.include | 92 ++-- Makefile.relic.include | 940 +++++++++++++++++++------------------- Makefile.test.include | 54 +-- 6 files changed, 637 insertions(+), 631 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..686e0aaf6 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +* text=auto eol=lf +*.mk text whitespace=-tab-in-indent,trailing-space,tabwidth=4 +Makefile* text whitespace=-tab-in-indent,trailing-space,tabwidth=4 + +# Vendored dependencies are not subject to normalization. +depends/** -text diff --git a/Makefile.bench.include b/Makefile.bench.include index f584102a3..84b014092 100644 --- a/Makefile.bench.include +++ b/Makefile.bench.include @@ -1,22 +1,22 @@ -# Copyright (c) 2021 The PIVX developers -# Copyright (c) 2022 The Dash Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING.MIT or http://www.opensource.org/licenses/mit-license.php. - -DASHBLS_RUNBENCH = runbench - -runbench_SOURCES = \ - src/test-bench.cpp \ - src/test-utils.hpp - -runbench_SOURCES += \ - $(RELIC_H) \ - $(DASHBLS_H) - -runbench_CPPFLAGS = $(AM_CPPFLAGS) $(DASHBLS_INCLUDES) $(RELIC_INCLUDES) -runbench_CXXFLAGS = $(AM_CXXFLAGS) -runbench_LDFLAGS = -static $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) - -runbench_LDADD = $(LIBDASHBLS) - -noinst_PROGRAMS += $(DASHBLS_RUNBENCH) +# Copyright (c) 2021 The PIVX developers +# Copyright (c) 2022 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING.MIT or http://www.opensource.org/licenses/mit-license.php. + +DASHBLS_RUNBENCH = runbench + +runbench_SOURCES = \ + src/test-bench.cpp \ + src/test-utils.hpp + +runbench_SOURCES += \ + $(RELIC_H) \ + $(DASHBLS_H) + +runbench_CPPFLAGS = $(AM_CPPFLAGS) $(DASHBLS_INCLUDES) $(RELIC_INCLUDES) +runbench_CXXFLAGS = $(AM_CXXFLAGS) +runbench_LDFLAGS = -static $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) + +runbench_LDADD = $(LIBDASHBLS) + +noinst_PROGRAMS += $(DASHBLS_RUNBENCH) diff --git a/Makefile.bls.include b/Makefile.bls.include index 7c35b3ced..517593b2f 100644 --- a/Makefile.bls.include +++ b/Makefile.bls.include @@ -1,66 +1,66 @@ -# Copyright (c) 2021 The PIVX developers -# Copyright (c) 2022 The Dash Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING.MIT or http://www.opensource.org/licenses/mit-license.php. - -LIBDASHBLS = libdashbls.la - -DASHBLS_CPPFLAGS = -DBLSALLOC_MIMALLOC=1 - -DASHBLS_INCLUDES = \ - -I$(builddir) \ - -I$(builddir)/obj \ - -I$(top_srcdir)/include/dashbls - -DASHBLS_H = \ - src/bls.hpp \ - src/chaincode.hpp \ - src/elements.hpp \ - src/extendedprivatekey.hpp \ - src/extendedpublickey.hpp \ - src/hdkeys.hpp \ - src/hkdf.hpp \ - src/legacy.hpp \ - src/privatekey.hpp \ - src/schemes.hpp \ - src/test-utils.hpp \ - src/threshold.hpp \ - src/util.hpp - -libdashbls_la_SOURCES = \ - src/bls.cpp \ - src/chaincode.cpp \ - src/elements.cpp \ - src/extendedprivatekey.cpp \ - src/extendedpublickey.cpp \ - src/legacy.cpp \ - src/privatekey.cpp \ - src/schemes.cpp \ - src/threshold.cpp - -libdashbls_la_SOURCES += \ - $(DASHBLS_H) \ - $(MIMALLOC_H) \ - $(RELIC_H) - -libdashbls_la_LIBADD = \ - $(LIBMIMALLOC) \ - $(LIBRELIC) \ - $(GMP_LIBS) - -libdashbls_la_CPPFLAGS = $(AM_CPPFLAGS) $(RELIC_INCLUDES) $(MIMALLOC_INCLUDES) $(DASHBLS_INCLUDES) $(DASHBLS_CPPFLAGS) -libdashbls_la_CXXFLAGS = $(AM_CXXFLAGS) -libdashbls_la_LDFLAGS = $(AM_LDFLAGS) - -include Makefile.mimalloc.include -include Makefile.relic.include - -if USE_TESTS -include Makefile.test.include -endif - -if USE_BENCH -include Makefile.bench.include -endif - -lib_LTLIBRARIES += $(LIBDASHBLS) +# Copyright (c) 2021 The PIVX developers +# Copyright (c) 2022 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING.MIT or http://www.opensource.org/licenses/mit-license.php. + +LIBDASHBLS = libdashbls.la + +DASHBLS_CPPFLAGS = -DBLSALLOC_MIMALLOC=1 + +DASHBLS_INCLUDES = \ + -I$(builddir) \ + -I$(builddir)/obj \ + -I$(top_srcdir)/include/dashbls + +DASHBLS_H = \ + src/bls.hpp \ + src/chaincode.hpp \ + src/elements.hpp \ + src/extendedprivatekey.hpp \ + src/extendedpublickey.hpp \ + src/hdkeys.hpp \ + src/hkdf.hpp \ + src/legacy.hpp \ + src/privatekey.hpp \ + src/schemes.hpp \ + src/test-utils.hpp \ + src/threshold.hpp \ + src/util.hpp + +libdashbls_la_SOURCES = \ + src/bls.cpp \ + src/chaincode.cpp \ + src/elements.cpp \ + src/extendedprivatekey.cpp \ + src/extendedpublickey.cpp \ + src/legacy.cpp \ + src/privatekey.cpp \ + src/schemes.cpp \ + src/threshold.cpp + +libdashbls_la_SOURCES += \ + $(DASHBLS_H) \ + $(MIMALLOC_H) \ + $(RELIC_H) + +libdashbls_la_LIBADD = \ + $(LIBMIMALLOC) \ + $(LIBRELIC) \ + $(GMP_LIBS) + +libdashbls_la_CPPFLAGS = $(AM_CPPFLAGS) $(RELIC_INCLUDES) $(MIMALLOC_INCLUDES) $(DASHBLS_INCLUDES) $(DASHBLS_CPPFLAGS) +libdashbls_la_CXXFLAGS = $(AM_CXXFLAGS) +libdashbls_la_LDFLAGS = $(AM_LDFLAGS) + +include Makefile.mimalloc.include +include Makefile.relic.include + +if USE_TESTS +include Makefile.test.include +endif + +if USE_BENCH +include Makefile.bench.include +endif + +lib_LTLIBRARIES += $(LIBDASHBLS) diff --git a/Makefile.mimalloc.include b/Makefile.mimalloc.include index fada5fe32..f742e2ae5 100644 --- a/Makefile.mimalloc.include +++ b/Makefile.mimalloc.include @@ -1,46 +1,46 @@ -# Copyright (c) 2022-2025 The Dash Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING.MIT or http://www.opensource.org/licenses/mit-license.php. - -LIBMIMALLOC = libmimalloc-secure.la - -MIMALLOC_CPPFLAGS = \ - -DMI_SECURE=4 \ - -DNDEBUG - -MIMALLOC_INCLUDES = \ - -I$(top_srcdir)/depends/mimalloc/include \ - -I$(top_srcdir)/depends/mimalloc/src - -MIMALLOC_H = \ - depends/mimalloc/include/mimalloc-new-delete.h \ - depends/mimalloc/include/mimalloc-override.h \ - depends/mimalloc/include/mimalloc-stats.h \ - depends/mimalloc/include/mimalloc.h \ - depends/mimalloc/src/bitmap.h - -libmimalloc_secure_la_SOURCES = \ - $(MIMALLOC_H) \ - depends/mimalloc/src/alloc-aligned.c \ - depends/mimalloc/src/alloc-posix.c \ - depends/mimalloc/src/alloc.c \ - depends/mimalloc/src/arena-meta.c \ - depends/mimalloc/src/arena.c \ - depends/mimalloc/src/bitmap.c \ - depends/mimalloc/src/heap.c \ - depends/mimalloc/src/init.c \ - depends/mimalloc/src/libc.c \ - depends/mimalloc/src/options.c \ - depends/mimalloc/src/os.c \ - depends/mimalloc/src/page-map.c \ - depends/mimalloc/src/page.c \ - depends/mimalloc/src/random.c \ - depends/mimalloc/src/stats.c \ - depends/mimalloc/src/prim/prim.c - -libmimalloc_secure_la_CPPFLAGS = $(AM_CPPFLAGS) $(MIMALLOC_CPPFLAGS) $(MIMALLOC_INCLUDES) -libmimalloc_secure_la_CXXFLAGS = $(AM_CXXFLAGS) -libmimalloc_secure_la_CFLAGS = $(AM_CFLAGS) -libmimalloc_secure_la_LDFLAGS = $(AM_LDFLAGS) - -noinst_LTLIBRARIES += $(LIBMIMALLOC) +# Copyright (c) 2022-2025 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING.MIT or http://www.opensource.org/licenses/mit-license.php. + +LIBMIMALLOC = libmimalloc-secure.la + +MIMALLOC_CPPFLAGS = \ + -DMI_SECURE=4 \ + -DNDEBUG + +MIMALLOC_INCLUDES = \ + -I$(top_srcdir)/depends/mimalloc/include \ + -I$(top_srcdir)/depends/mimalloc/src + +MIMALLOC_H = \ + depends/mimalloc/include/mimalloc-new-delete.h \ + depends/mimalloc/include/mimalloc-override.h \ + depends/mimalloc/include/mimalloc-stats.h \ + depends/mimalloc/include/mimalloc.h \ + depends/mimalloc/src/bitmap.h + +libmimalloc_secure_la_SOURCES = \ + $(MIMALLOC_H) \ + depends/mimalloc/src/alloc-aligned.c \ + depends/mimalloc/src/alloc-posix.c \ + depends/mimalloc/src/alloc.c \ + depends/mimalloc/src/arena-meta.c \ + depends/mimalloc/src/arena.c \ + depends/mimalloc/src/bitmap.c \ + depends/mimalloc/src/heap.c \ + depends/mimalloc/src/init.c \ + depends/mimalloc/src/libc.c \ + depends/mimalloc/src/options.c \ + depends/mimalloc/src/os.c \ + depends/mimalloc/src/page-map.c \ + depends/mimalloc/src/page.c \ + depends/mimalloc/src/random.c \ + depends/mimalloc/src/stats.c \ + depends/mimalloc/src/prim/prim.c + +libmimalloc_secure_la_CPPFLAGS = $(AM_CPPFLAGS) $(MIMALLOC_CPPFLAGS) $(MIMALLOC_INCLUDES) +libmimalloc_secure_la_CXXFLAGS = $(AM_CXXFLAGS) +libmimalloc_secure_la_CFLAGS = $(AM_CFLAGS) +libmimalloc_secure_la_LDFLAGS = $(AM_LDFLAGS) + +noinst_LTLIBRARIES += $(LIBMIMALLOC) diff --git a/Makefile.relic.include b/Makefile.relic.include index 92bd7c7e8..52d796932 100644 --- a/Makefile.relic.include +++ b/Makefile.relic.include @@ -1,470 +1,470 @@ -# Copyright (c) 2021 The PIVX developers -# Copyright (c) 2022 The Dash Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING.MIT or http://www.opensource.org/licenses/mit-license.php. - -LIBRELIC = librelic.la - -RELIC_INCLUDES = \ - -I$(top_srcdir)/depends/relic/include \ - -I$(top_srcdir)/depends/relic/include/low \ - -I$(top_srcdir)/depends/relic/src/tmpl - -RELIC_H = \ - depends/relic/include/relic.h \ - depends/relic/include/relic_alloc.h \ - depends/relic/include/relic_arch.h \ - depends/relic/include/relic_bc.h \ - depends/relic/include/relic_bench.h \ - depends/relic/include/relic_bn.h \ - depends/relic/include/relic_conf.h \ - depends/relic/include/relic_core.h \ - depends/relic/include/relic_cp.h \ - depends/relic/include/relic_dv.h \ - depends/relic/include/relic_eb.h \ - depends/relic/include/relic_ec.h \ - depends/relic/include/relic_ed.h \ - depends/relic/include/relic_ep.h \ - depends/relic/include/relic_epx.h \ - depends/relic/include/relic_err.h \ - depends/relic/include/relic_fb.h \ - depends/relic/include/relic_fbx.h \ - depends/relic/include/relic_fp.h \ - depends/relic/include/relic_fpx.h \ - depends/relic/include/relic_label.h \ - depends/relic/include/relic_md.h \ - depends/relic/include/relic_mpc.h \ - depends/relic/include/relic_multi.h \ - depends/relic/include/relic_pc.h \ - depends/relic/include/relic_pp.h \ - depends/relic/include/relic_rand.h \ - depends/relic/include/relic_test.h \ - depends/relic/include/relic_types.h \ - depends/relic/include/relic_util.h \ - depends/relic/include/low/relic_bn_low.h \ - depends/relic/include/low/relic_dv_low.h \ - depends/relic/include/low/relic_fb_low.h \ - depends/relic/include/low/relic_fp_low.h \ - depends/relic/include/low/relic_fpx_low.h \ - depends/relic/src/arch/lzcnt.inc \ - depends/relic/src/tmpl/relic_tmpl_map.h - -RELIC_SOURCES = \ - depends/relic/src/relic_err.c \ - depends/relic/src/relic_core.c \ - depends/relic/src/relic_conf.c \ - depends/relic/src/relic_util.c - -BN_SOURCES = \ - depends/relic/src/bn/relic_bn_add.c \ - depends/relic/src/bn/relic_bn_cmp.c \ - depends/relic/src/bn/relic_bn_div.c \ - depends/relic/src/bn/relic_bn_factor.c \ - depends/relic/src/bn/relic_bn_gcd.c \ - depends/relic/src/bn/relic_bn_inv.c \ - depends/relic/src/bn/relic_bn_lcm.c \ - depends/relic/src/bn/relic_bn_mem.c \ - depends/relic/src/bn/relic_bn_mod.c \ - depends/relic/src/bn/relic_bn_mul.c \ - depends/relic/src/bn/relic_bn_mxp.c \ - depends/relic/src/bn/relic_bn_prime.c \ - depends/relic/src/bn/relic_bn_rec.c \ - depends/relic/src/bn/relic_bn_shift.c \ - depends/relic/src/bn/relic_bn_smb.c \ - depends/relic/src/bn/relic_bn_sqr.c \ - depends/relic/src/bn/relic_bn_srt.c \ - depends/relic/src/bn/relic_bn_util.c - -FP_SOURCES = \ - depends/relic/src/fp/relic_fp_add.c \ - depends/relic/src/fp/relic_fp_cmp.c \ - depends/relic/src/fp/relic_fp_exp.c \ - depends/relic/src/fp/relic_fp_inv.c \ - depends/relic/src/fp/relic_fp_mul.c \ - depends/relic/src/fp/relic_fp_param.c \ - depends/relic/src/fp/relic_fp_prime.c \ - depends/relic/src/fp/relic_fp_rdc.c \ - depends/relic/src/fp/relic_fp_shift.c \ - depends/relic/src/fp/relic_fp_sqr.c \ - depends/relic/src/fp/relic_fp_srt.c \ - depends/relic/src/fp/relic_fp_util.c - -FPX_SOURCES = \ - depends/relic/src/fpx/relic_fp2_mul.c \ - depends/relic/src/fpx/relic_fp2_sqr.c \ - depends/relic/src/fpx/relic_fp3_mul.c \ - depends/relic/src/fpx/relic_fp3_sqr.c \ - depends/relic/src/fpx/relic_fp4_mul.c \ - depends/relic/src/fpx/relic_fp4_sqr.c \ - depends/relic/src/fpx/relic_fp6_mul.c \ - depends/relic/src/fpx/relic_fp6_sqr.c \ - depends/relic/src/fpx/relic_fp8_mul.c \ - depends/relic/src/fpx/relic_fp8_sqr.c \ - depends/relic/src/fpx/relic_fp9_mul.c \ - depends/relic/src/fpx/relic_fp9_sqr.c \ - depends/relic/src/fpx/relic_fp12_mul.c \ - depends/relic/src/fpx/relic_fp12_sqr.c \ - depends/relic/src/fpx/relic_fp18_mul.c \ - depends/relic/src/fpx/relic_fp18_sqr.c \ - depends/relic/src/fpx/relic_fp24_mul.c \ - depends/relic/src/fpx/relic_fp24_sqr.c \ - depends/relic/src/fpx/relic_fp48_mul.c \ - depends/relic/src/fpx/relic_fp48_sqr.c \ - depends/relic/src/fpx/relic_fp54_mul.c \ - depends/relic/src/fpx/relic_fp54_sqr.c \ - depends/relic/src/fpx/relic_fpx_add.c \ - depends/relic/src/fpx/relic_fpx_cmp.c \ - depends/relic/src/fpx/relic_fpx_cyc.c \ - depends/relic/src/fpx/relic_fpx_exp.c \ - depends/relic/src/fpx/relic_fpx_field.c \ - depends/relic/src/fpx/relic_fpx_frb.c \ - depends/relic/src/fpx/relic_fpx_inv.c \ - depends/relic/src/fpx/relic_fpx_pck.c \ - depends/relic/src/fpx/relic_fpx_rdc.c \ - depends/relic/src/fpx/relic_fpx_srt.c \ - depends/relic/src/fpx/relic_fpx_util.c - -FB_SOURCES = \ - depends/relic/src/fb/relic_fb_add.c \ - depends/relic/src/fb/relic_fb_cmp.c \ - depends/relic/src/fb/relic_fb_exp.c \ - depends/relic/src/fb/relic_fb_inv.c \ - depends/relic/src/fb/relic_fb_itr.c \ - depends/relic/src/fb/relic_fb_mul.c \ - depends/relic/src/fb/relic_fb_param.c \ - depends/relic/src/fb/relic_fb_poly.c \ - depends/relic/src/fb/relic_fb_rdc.c \ - depends/relic/src/fb/relic_fb_shift.c \ - depends/relic/src/fb/relic_fb_slv.c \ - depends/relic/src/fb/relic_fb_sqr.c \ - depends/relic/src/fb/relic_fb_srt.c \ - depends/relic/src/fb/relic_fb_trc.c \ - depends/relic/src/fb/relic_fb_util.c - -EP_SOURCES = \ - depends/relic/src/ep/relic_ep_add.c \ - depends/relic/src/ep/relic_ep_cmp.c \ - depends/relic/src/ep/relic_ep_curve.c \ - depends/relic/src/ep/relic_ep_dbl.c \ - depends/relic/src/ep/relic_ep_map.c \ - depends/relic/src/ep/relic_ep_mul.c \ - depends/relic/src/ep/relic_ep_mul_fix.c \ - depends/relic/src/ep/relic_ep_mul_sim.c \ - depends/relic/src/ep/relic_ep_neg.c \ - depends/relic/src/ep/relic_ep_norm.c \ - depends/relic/src/ep/relic_ep_param.c \ - depends/relic/src/ep/relic_ep_pck.c \ - depends/relic/src/ep/relic_ep_psi.c \ - depends/relic/src/ep/relic_ep_util.c - -EPX_SOURCES = \ - depends/relic/src/epx/relic_ep2_add.c \ - depends/relic/src/epx/relic_ep2_cmp.c \ - depends/relic/src/epx/relic_ep2_curve.c \ - depends/relic/src/epx/relic_ep2_dbl.c \ - depends/relic/src/epx/relic_ep2_frb.c \ - depends/relic/src/epx/relic_ep2_map.c \ - depends/relic/src/epx/relic_ep2_mul.c \ - depends/relic/src/epx/relic_ep2_mul_cof.c \ - depends/relic/src/epx/relic_ep2_mul_fix.c \ - depends/relic/src/epx/relic_ep2_mul_sim.c \ - depends/relic/src/epx/relic_ep2_neg.c \ - depends/relic/src/epx/relic_ep2_norm.c \ - depends/relic/src/epx/relic_ep2_pck.c \ - depends/relic/src/epx/relic_ep2_util.c \ - depends/relic/src/epx/relic_ep4_add.c \ - depends/relic/src/epx/relic_ep4_cmp.c \ - depends/relic/src/epx/relic_ep4_curve.c \ - depends/relic/src/epx/relic_ep4_dbl.c \ - depends/relic/src/epx/relic_ep4_frb.c \ - depends/relic/src/epx/relic_ep4_map.c \ - depends/relic/src/epx/relic_ep4_mul.c \ - depends/relic/src/epx/relic_ep4_mul_cof.c \ - depends/relic/src/epx/relic_ep4_mul_fix.c \ - depends/relic/src/epx/relic_ep4_mul_sim.c \ - depends/relic/src/epx/relic_ep4_neg.c \ - depends/relic/src/epx/relic_ep4_norm.c \ - depends/relic/src/epx/relic_ep4_util.c - -EB_SOURCES = \ - depends/relic/src/eb/relic_eb_add.c \ - depends/relic/src/eb/relic_eb_cmp.c \ - depends/relic/src/eb/relic_eb_curve.c \ - depends/relic/src/eb/relic_eb_dbl.c \ - depends/relic/src/eb/relic_eb_frb.c \ - depends/relic/src/eb/relic_eb_hlv.c \ - depends/relic/src/eb/relic_eb_map.c \ - depends/relic/src/eb/relic_eb_mul.c \ - depends/relic/src/eb/relic_eb_mul_fix.c \ - depends/relic/src/eb/relic_eb_mul_sim.c \ - depends/relic/src/eb/relic_eb_neg.c \ - depends/relic/src/eb/relic_eb_norm.c \ - depends/relic/src/eb/relic_eb_param.c \ - depends/relic/src/eb/relic_eb_pck.c \ - depends/relic/src/eb/relic_eb_util.c - -ED_SOURCES = \ - depends/relic/src/ed/relic_ed_add.c \ - depends/relic/src/ed/relic_ed_cmp.c \ - depends/relic/src/ed/relic_ed_curve.c \ - depends/relic/src/ed/relic_ed_dbl.c \ - depends/relic/src/ed/relic_ed_map.c \ - depends/relic/src/ed/relic_ed_mul.c \ - depends/relic/src/ed/relic_ed_mul_fix.c \ - depends/relic/src/ed/relic_ed_mul_sim.c \ - depends/relic/src/ed/relic_ed_neg.c \ - depends/relic/src/ed/relic_ed_norm.c \ - depends/relic/src/ed/relic_ed_param.c \ - depends/relic/src/ed/relic_ed_pck.c \ - depends/relic/src/ed/relic_ed_util.c - -PP_SOURCES = \ - depends/relic/src/pp/relic_pp_add_k2.c \ - depends/relic/src/pp/relic_pp_add_k8.c \ - depends/relic/src/pp/relic_pp_add_k12.c \ - depends/relic/src/pp/relic_pp_add_k24.c \ - depends/relic/src/pp/relic_pp_add_k48.c \ - depends/relic/src/pp/relic_pp_add_k54.c \ - depends/relic/src/pp/relic_pp_dbl_k2.c \ - depends/relic/src/pp/relic_pp_dbl_k8.c \ - depends/relic/src/pp/relic_pp_dbl_k12.c \ - depends/relic/src/pp/relic_pp_dbl_k24.c \ - depends/relic/src/pp/relic_pp_dbl_k48.c \ - depends/relic/src/pp/relic_pp_dbl_k54.c \ - depends/relic/src/pp/relic_pp_exp_k2.c \ - depends/relic/src/pp/relic_pp_exp_k8.c \ - depends/relic/src/pp/relic_pp_exp_k12.c \ - depends/relic/src/pp/relic_pp_exp_k24.c \ - depends/relic/src/pp/relic_pp_exp_k48.c \ - depends/relic/src/pp/relic_pp_exp_k54.c \ - depends/relic/src/pp/relic_pp_map.c \ - depends/relic/src/pp/relic_pp_map_k2.c \ - depends/relic/src/pp/relic_pp_map_k8.c \ - depends/relic/src/pp/relic_pp_map_k12.c \ - depends/relic/src/pp/relic_pp_map_k24.c \ - depends/relic/src/pp/relic_pp_map_k48.c \ - depends/relic/src/pp/relic_pp_map_k54.c \ - depends/relic/src/pp/relic_pp_norm.c - -PC_SOURCES = \ - depends/relic/src/pc/relic_pc_core.c \ - depends/relic/src/pc/relic_pc_exp.c \ - depends/relic/src/pc/relic_pc_util.c - -MD_SOURCES = \ - depends/relic/src/md/blake2.h \ - depends/relic/src/md/blake2-impl.h \ - depends/relic/src/md/blake2s-ref.c \ - depends/relic/src/md/relic_md_blake2s.c \ - depends/relic/src/md/relic_md_hmac.c \ - depends/relic/src/md/relic_md_kdf.c \ - depends/relic/src/md/relic_md_mgf.c \ - depends/relic/src/md/relic_md_sha224.c \ - depends/relic/src/md/relic_md_sha256.c \ - depends/relic/src/md/relic_md_sha384.c \ - depends/relic/src/md/relic_md_sha512.c \ - depends/relic/src/md/relic_md_xmd.c \ - depends/relic/src/md/sha.h \ - depends/relic/src/md/sha224-256.c \ - depends/relic/src/md/sha384-512.c \ - depends/relic/src/md/sha_private.h - -EXTRA_DIST += \ - depends/relic/src/arch/relic_arch_arm.c \ - depends/relic/src/arch/relic_arch_none.c \ - depends/relic/src/arch/relic_arch_x64.c \ - depends/relic/src/arch/relic_arch_x86.c - -RELIC_SOURCES += \ - depends/relic/src/arch/relic_arch_@CPU_ARCH@.c \ - depends/relic/src/rand/relic_rand_call.c \ - depends/relic/src/rand/relic_rand_core.c \ - depends/relic/src/rand/relic_rand_hashd.c \ - depends/relic/src/rand/relic_rand_udev.c - -if WITH_DV -RELIC_SOURCES += \ - depends/relic/src/dv/relic_dv_mem.c \ - depends/relic/src/dv/relic_dv_util.c -endif - -if ARITH_EASY -if WITH_BN -RELIC_SOURCES += $(BN_SOURCES) \ - depends/relic/src/low/easy/relic_bn_add_low.c \ - depends/relic/src/low/easy/relic_bn_div_low.c \ - depends/relic/src/low/easy/relic_bn_mod_low.c \ - depends/relic/src/low/easy/relic_bn_mul_low.c \ - depends/relic/src/low/easy/relic_bn_shift_low.c \ - depends/relic/src/low/easy/relic_bn_sqr_low.c -endif - -if WITH_FP -RELIC_SOURCES += $(FP_SOURCES) \ - depends/relic/src/low/easy/relic_fp_add_low.c \ - depends/relic/src/low/easy/relic_fp_inv_low.c \ - depends/relic/src/low/easy/relic_fp_mul_low.c \ - depends/relic/src/low/easy/relic_fp_rdc_low.c \ - depends/relic/src/low/easy/relic_fp_shift_low.c \ - depends/relic/src/low/easy/relic_fp_sqr_low.c -endif - -if WITH_FPX -RELIC_SOURCES += $(FPX_SOURCES) \ - depends/relic/src/low/easy/relic_fpx_add_low.c \ - depends/relic/src/low/easy/relic_fpx_mul_low.c \ - depends/relic/src/low/easy/relic_fpx_rdc_low.c \ - depends/relic/src/low/easy/relic_fpx_sqr_low.c -endif - -if WITH_FB -RELIC_SOURCES += $(FB_SOURCES) \ - depends/relic/src/low/easy/relic_fb_add_low.c \ - depends/relic/src/low/easy/relic_fb_inv_low.c \ - depends/relic/src/low/easy/relic_fb_itr_low.c \ - depends/relic/src/low/easy/relic_fb_mul_low.c \ - depends/relic/src/low/easy/relic_fb_rdc_low.c \ - depends/relic/src/low/easy/relic_fb_shift_low.c \ - depends/relic/src/low/easy/relic_fb_slv_low.c \ - depends/relic/src/low/easy/relic_fb_sqr_low.c \ - depends/relic/src/low/easy/relic_fb_srt_low.c \ - depends/relic/src/low/easy/relic_fb_trc_low.c -endif -endif - -if ARITH_GMP -if WITH_BN -RELIC_SOURCES += $(BN_SOURCES) \ - depends/relic/src/low/gmp/relic_bn_add_low.c \ - depends/relic/src/low/gmp/relic_bn_div_low.c \ - depends/relic/src/low/gmp/relic_bn_mod_low.c \ - depends/relic/src/low/gmp/relic_bn_mul_low.c \ - depends/relic/src/low/gmp/relic_bn_shift_low.c \ - depends/relic/src/low/gmp/relic_bn_sqr_low.c -endif - -if WITH_FP -RELIC_SOURCES += $(FP_SOURCES) \ - depends/relic/src/low/gmp/relic_fp_add_low.c \ - depends/relic/src/low/gmp/relic_fp_inv_low.c \ - depends/relic/src/low/gmp/relic_fp_mul_low.c \ - depends/relic/src/low/gmp/relic_fp_rdc_low.c \ - depends/relic/src/low/gmp/relic_fp_shift_low.c \ - depends/relic/src/low/gmp/relic_fp_sqr_low.c -endif - -if WITH_FPX -RELIC_SOURCES += $(FPX_SOURCES) \ - depends/relic/src/low/easy/relic_fpx_add_low.c \ - depends/relic/src/low/easy/relic_fpx_mul_low.c \ - depends/relic/src/low/easy/relic_fpx_rdc_low.c \ - depends/relic/src/low/easy/relic_fpx_sqr_low.c -endif - -if WITH_FB -RELIC_SOURCES += $(FB_SOURCES) \ - depends/relic/src/low/gmp/relic_fb_add_low.c \ - depends/relic/src/low/easy/relic_fb_inv_low.c \ - depends/relic/src/low/easy/relic_fb_itr_low.c \ - depends/relic/src/low/easy/relic_fb_mul_low.c \ - depends/relic/src/low/easy/relic_fb_rdc_low.c \ - depends/relic/src/low/gmp/relic_fb_shift_low.c \ - depends/relic/src/low/easy/relic_fb_slv_low.c \ - depends/relic/src/low/easy/relic_fb_sqr_low.c \ - depends/relic/src/low/easy/relic_fb_srt_low.c \ - depends/relic/src/low/easy/relic_fb_trc_low.c -endif -endif - -if WITH_FBX -RELIC_SOURCES += \ - depends/relic/src/fbx/relic_fb2_inv.c \ - depends/relic/src/fbx/relic_fb2_mul.c \ - depends/relic/src/fbx/relic_fb2_slv.c \ - depends/relic/src/fbx/relic_fb2_sqr.c -endif - -if WITH_EP -RELIC_SOURCES += $(EP_SOURCES) -endif - -if WITH_EPX -RELIC_SOURCES += $(EPX_SOURCES) -endif - -if WITH_EB -RELIC_SOURCES += $(EB_SOURCES) -endif - -if WITH_ED -RELIC_SOURCES += $(ED_SOURCES) -endif - -if WITH_PP -RELIC_SOURCES += $(PP_SOURCES) -endif - -if WITH_PC -RELIC_SOURCES += $(PC_SOURCES) -endif - -if WITH_CP -RELIC_SOURCES += \ - depends/relic/src/cp/relic_cp_bbs.c \ - depends/relic/src/cp/relic_cp_bdpe.c \ - depends/relic/src/cp/relic_cp_bgn.c \ - depends/relic/src/cp/relic_cp_bls.c \ - depends/relic/src/cp/relic_cp_cls.c \ - depends/relic/src/cp/relic_cp_cmlhs.c \ - depends/relic/src/cp/relic_cp_ecdh.c \ - depends/relic/src/cp/relic_cp_ecdsa.c \ - depends/relic/src/cp/relic_cp_ecies.c \ - depends/relic/src/cp/relic_cp_ecmqv.c \ - depends/relic/src/cp/relic_cp_ecss.c \ - depends/relic/src/cp/relic_cp_ers.c \ - depends/relic/src/cp/relic_cp_etrs.c \ - depends/relic/src/cp/relic_cp_ghpe.c \ - depends/relic/src/cp/relic_cp_ibe.c \ - depends/relic/src/cp/relic_cp_mklhs.c \ - depends/relic/src/cp/relic_cp_mpss.c \ - depends/relic/src/cp/relic_cp_pcdel.c \ - depends/relic/src/cp/relic_cp_phpe.c \ - depends/relic/src/cp/relic_cp_pok.c \ - depends/relic/src/cp/relic_cp_pss.c \ - depends/relic/src/cp/relic_cp_rabin.c \ - depends/relic/src/cp/relic_cp_rsa.c \ - depends/relic/src/cp/relic_cp_sok.c \ - depends/relic/src/cp/relic_cp_sokaka.c \ - depends/relic/src/cp/relic_cp_vbnn.c \ - depends/relic/src/cp/relic_cp_zss.c -endif - -if WITH_BC -RELIC_SOURCES += \ - depends/relic/src/bc/relic_bc_aes.c \ - depends/relic/src/bc/rijndael-alg-fst.c \ - depends/relic/src/bc/rijndael-alg-fst.h \ - depends/relic/src/bc/rijndael-api-fst.c \ - depends/relic/src/bc/rijndael-api-fst.h -endif - -if WITH_MD -RELIC_SOURCES += $(MD_SOURCES) -endif - -if WITH_MPC -RELIC_SOURCES += \ - depends/relic/src/mpc/relic_mt_mpc.c \ - depends/relic/src/mpc/relic_pc_mpc.c -endif - -librelic_la_SOURCES = \ - $(RELIC_SOURCES) \ - $(RELIC_H) - -librelic_la_CPPFLAGS = $(AM_CPPFLAGS) $(RELIC_CPPFLAGS) $(RELIC_INCLUDES) -librelic_la_CXXFLAGS = $(AM_CXXFLAGS) -librelic_la_CFLAGS = $(AM_CFLAGS) -librelic_la_LDFLAGS = $(AM_LDFLAGS) - -noinst_LTLIBRARIES += $(LIBRELIC) +# Copyright (c) 2021 The PIVX developers +# Copyright (c) 2022 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING.MIT or http://www.opensource.org/licenses/mit-license.php. + +LIBRELIC = librelic.la + +RELIC_INCLUDES = \ + -I$(top_srcdir)/depends/relic/include \ + -I$(top_srcdir)/depends/relic/include/low \ + -I$(top_srcdir)/depends/relic/src/tmpl + +RELIC_H = \ + depends/relic/include/relic.h \ + depends/relic/include/relic_alloc.h \ + depends/relic/include/relic_arch.h \ + depends/relic/include/relic_bc.h \ + depends/relic/include/relic_bench.h \ + depends/relic/include/relic_bn.h \ + depends/relic/include/relic_conf.h \ + depends/relic/include/relic_core.h \ + depends/relic/include/relic_cp.h \ + depends/relic/include/relic_dv.h \ + depends/relic/include/relic_eb.h \ + depends/relic/include/relic_ec.h \ + depends/relic/include/relic_ed.h \ + depends/relic/include/relic_ep.h \ + depends/relic/include/relic_epx.h \ + depends/relic/include/relic_err.h \ + depends/relic/include/relic_fb.h \ + depends/relic/include/relic_fbx.h \ + depends/relic/include/relic_fp.h \ + depends/relic/include/relic_fpx.h \ + depends/relic/include/relic_label.h \ + depends/relic/include/relic_md.h \ + depends/relic/include/relic_mpc.h \ + depends/relic/include/relic_multi.h \ + depends/relic/include/relic_pc.h \ + depends/relic/include/relic_pp.h \ + depends/relic/include/relic_rand.h \ + depends/relic/include/relic_test.h \ + depends/relic/include/relic_types.h \ + depends/relic/include/relic_util.h \ + depends/relic/include/low/relic_bn_low.h \ + depends/relic/include/low/relic_dv_low.h \ + depends/relic/include/low/relic_fb_low.h \ + depends/relic/include/low/relic_fp_low.h \ + depends/relic/include/low/relic_fpx_low.h \ + depends/relic/src/arch/lzcnt.inc \ + depends/relic/src/tmpl/relic_tmpl_map.h + +RELIC_SOURCES = \ + depends/relic/src/relic_err.c \ + depends/relic/src/relic_core.c \ + depends/relic/src/relic_conf.c \ + depends/relic/src/relic_util.c + +BN_SOURCES = \ + depends/relic/src/bn/relic_bn_add.c \ + depends/relic/src/bn/relic_bn_cmp.c \ + depends/relic/src/bn/relic_bn_div.c \ + depends/relic/src/bn/relic_bn_factor.c \ + depends/relic/src/bn/relic_bn_gcd.c \ + depends/relic/src/bn/relic_bn_inv.c \ + depends/relic/src/bn/relic_bn_lcm.c \ + depends/relic/src/bn/relic_bn_mem.c \ + depends/relic/src/bn/relic_bn_mod.c \ + depends/relic/src/bn/relic_bn_mul.c \ + depends/relic/src/bn/relic_bn_mxp.c \ + depends/relic/src/bn/relic_bn_prime.c \ + depends/relic/src/bn/relic_bn_rec.c \ + depends/relic/src/bn/relic_bn_shift.c \ + depends/relic/src/bn/relic_bn_smb.c \ + depends/relic/src/bn/relic_bn_sqr.c \ + depends/relic/src/bn/relic_bn_srt.c \ + depends/relic/src/bn/relic_bn_util.c + +FP_SOURCES = \ + depends/relic/src/fp/relic_fp_add.c \ + depends/relic/src/fp/relic_fp_cmp.c \ + depends/relic/src/fp/relic_fp_exp.c \ + depends/relic/src/fp/relic_fp_inv.c \ + depends/relic/src/fp/relic_fp_mul.c \ + depends/relic/src/fp/relic_fp_param.c \ + depends/relic/src/fp/relic_fp_prime.c \ + depends/relic/src/fp/relic_fp_rdc.c \ + depends/relic/src/fp/relic_fp_shift.c \ + depends/relic/src/fp/relic_fp_sqr.c \ + depends/relic/src/fp/relic_fp_srt.c \ + depends/relic/src/fp/relic_fp_util.c + +FPX_SOURCES = \ + depends/relic/src/fpx/relic_fp2_mul.c \ + depends/relic/src/fpx/relic_fp2_sqr.c \ + depends/relic/src/fpx/relic_fp3_mul.c \ + depends/relic/src/fpx/relic_fp3_sqr.c \ + depends/relic/src/fpx/relic_fp4_mul.c \ + depends/relic/src/fpx/relic_fp4_sqr.c \ + depends/relic/src/fpx/relic_fp6_mul.c \ + depends/relic/src/fpx/relic_fp6_sqr.c \ + depends/relic/src/fpx/relic_fp8_mul.c \ + depends/relic/src/fpx/relic_fp8_sqr.c \ + depends/relic/src/fpx/relic_fp9_mul.c \ + depends/relic/src/fpx/relic_fp9_sqr.c \ + depends/relic/src/fpx/relic_fp12_mul.c \ + depends/relic/src/fpx/relic_fp12_sqr.c \ + depends/relic/src/fpx/relic_fp18_mul.c \ + depends/relic/src/fpx/relic_fp18_sqr.c \ + depends/relic/src/fpx/relic_fp24_mul.c \ + depends/relic/src/fpx/relic_fp24_sqr.c \ + depends/relic/src/fpx/relic_fp48_mul.c \ + depends/relic/src/fpx/relic_fp48_sqr.c \ + depends/relic/src/fpx/relic_fp54_mul.c \ + depends/relic/src/fpx/relic_fp54_sqr.c \ + depends/relic/src/fpx/relic_fpx_add.c \ + depends/relic/src/fpx/relic_fpx_cmp.c \ + depends/relic/src/fpx/relic_fpx_cyc.c \ + depends/relic/src/fpx/relic_fpx_exp.c \ + depends/relic/src/fpx/relic_fpx_field.c \ + depends/relic/src/fpx/relic_fpx_frb.c \ + depends/relic/src/fpx/relic_fpx_inv.c \ + depends/relic/src/fpx/relic_fpx_pck.c \ + depends/relic/src/fpx/relic_fpx_rdc.c \ + depends/relic/src/fpx/relic_fpx_srt.c \ + depends/relic/src/fpx/relic_fpx_util.c + +FB_SOURCES = \ + depends/relic/src/fb/relic_fb_add.c \ + depends/relic/src/fb/relic_fb_cmp.c \ + depends/relic/src/fb/relic_fb_exp.c \ + depends/relic/src/fb/relic_fb_inv.c \ + depends/relic/src/fb/relic_fb_itr.c \ + depends/relic/src/fb/relic_fb_mul.c \ + depends/relic/src/fb/relic_fb_param.c \ + depends/relic/src/fb/relic_fb_poly.c \ + depends/relic/src/fb/relic_fb_rdc.c \ + depends/relic/src/fb/relic_fb_shift.c \ + depends/relic/src/fb/relic_fb_slv.c \ + depends/relic/src/fb/relic_fb_sqr.c \ + depends/relic/src/fb/relic_fb_srt.c \ + depends/relic/src/fb/relic_fb_trc.c \ + depends/relic/src/fb/relic_fb_util.c + +EP_SOURCES = \ + depends/relic/src/ep/relic_ep_add.c \ + depends/relic/src/ep/relic_ep_cmp.c \ + depends/relic/src/ep/relic_ep_curve.c \ + depends/relic/src/ep/relic_ep_dbl.c \ + depends/relic/src/ep/relic_ep_map.c \ + depends/relic/src/ep/relic_ep_mul.c \ + depends/relic/src/ep/relic_ep_mul_fix.c \ + depends/relic/src/ep/relic_ep_mul_sim.c \ + depends/relic/src/ep/relic_ep_neg.c \ + depends/relic/src/ep/relic_ep_norm.c \ + depends/relic/src/ep/relic_ep_param.c \ + depends/relic/src/ep/relic_ep_pck.c \ + depends/relic/src/ep/relic_ep_psi.c \ + depends/relic/src/ep/relic_ep_util.c + +EPX_SOURCES = \ + depends/relic/src/epx/relic_ep2_add.c \ + depends/relic/src/epx/relic_ep2_cmp.c \ + depends/relic/src/epx/relic_ep2_curve.c \ + depends/relic/src/epx/relic_ep2_dbl.c \ + depends/relic/src/epx/relic_ep2_frb.c \ + depends/relic/src/epx/relic_ep2_map.c \ + depends/relic/src/epx/relic_ep2_mul.c \ + depends/relic/src/epx/relic_ep2_mul_cof.c \ + depends/relic/src/epx/relic_ep2_mul_fix.c \ + depends/relic/src/epx/relic_ep2_mul_sim.c \ + depends/relic/src/epx/relic_ep2_neg.c \ + depends/relic/src/epx/relic_ep2_norm.c \ + depends/relic/src/epx/relic_ep2_pck.c \ + depends/relic/src/epx/relic_ep2_util.c \ + depends/relic/src/epx/relic_ep4_add.c \ + depends/relic/src/epx/relic_ep4_cmp.c \ + depends/relic/src/epx/relic_ep4_curve.c \ + depends/relic/src/epx/relic_ep4_dbl.c \ + depends/relic/src/epx/relic_ep4_frb.c \ + depends/relic/src/epx/relic_ep4_map.c \ + depends/relic/src/epx/relic_ep4_mul.c \ + depends/relic/src/epx/relic_ep4_mul_cof.c \ + depends/relic/src/epx/relic_ep4_mul_fix.c \ + depends/relic/src/epx/relic_ep4_mul_sim.c \ + depends/relic/src/epx/relic_ep4_neg.c \ + depends/relic/src/epx/relic_ep4_norm.c \ + depends/relic/src/epx/relic_ep4_util.c + +EB_SOURCES = \ + depends/relic/src/eb/relic_eb_add.c \ + depends/relic/src/eb/relic_eb_cmp.c \ + depends/relic/src/eb/relic_eb_curve.c \ + depends/relic/src/eb/relic_eb_dbl.c \ + depends/relic/src/eb/relic_eb_frb.c \ + depends/relic/src/eb/relic_eb_hlv.c \ + depends/relic/src/eb/relic_eb_map.c \ + depends/relic/src/eb/relic_eb_mul.c \ + depends/relic/src/eb/relic_eb_mul_fix.c \ + depends/relic/src/eb/relic_eb_mul_sim.c \ + depends/relic/src/eb/relic_eb_neg.c \ + depends/relic/src/eb/relic_eb_norm.c \ + depends/relic/src/eb/relic_eb_param.c \ + depends/relic/src/eb/relic_eb_pck.c \ + depends/relic/src/eb/relic_eb_util.c + +ED_SOURCES = \ + depends/relic/src/ed/relic_ed_add.c \ + depends/relic/src/ed/relic_ed_cmp.c \ + depends/relic/src/ed/relic_ed_curve.c \ + depends/relic/src/ed/relic_ed_dbl.c \ + depends/relic/src/ed/relic_ed_map.c \ + depends/relic/src/ed/relic_ed_mul.c \ + depends/relic/src/ed/relic_ed_mul_fix.c \ + depends/relic/src/ed/relic_ed_mul_sim.c \ + depends/relic/src/ed/relic_ed_neg.c \ + depends/relic/src/ed/relic_ed_norm.c \ + depends/relic/src/ed/relic_ed_param.c \ + depends/relic/src/ed/relic_ed_pck.c \ + depends/relic/src/ed/relic_ed_util.c + +PP_SOURCES = \ + depends/relic/src/pp/relic_pp_add_k2.c \ + depends/relic/src/pp/relic_pp_add_k8.c \ + depends/relic/src/pp/relic_pp_add_k12.c \ + depends/relic/src/pp/relic_pp_add_k24.c \ + depends/relic/src/pp/relic_pp_add_k48.c \ + depends/relic/src/pp/relic_pp_add_k54.c \ + depends/relic/src/pp/relic_pp_dbl_k2.c \ + depends/relic/src/pp/relic_pp_dbl_k8.c \ + depends/relic/src/pp/relic_pp_dbl_k12.c \ + depends/relic/src/pp/relic_pp_dbl_k24.c \ + depends/relic/src/pp/relic_pp_dbl_k48.c \ + depends/relic/src/pp/relic_pp_dbl_k54.c \ + depends/relic/src/pp/relic_pp_exp_k2.c \ + depends/relic/src/pp/relic_pp_exp_k8.c \ + depends/relic/src/pp/relic_pp_exp_k12.c \ + depends/relic/src/pp/relic_pp_exp_k24.c \ + depends/relic/src/pp/relic_pp_exp_k48.c \ + depends/relic/src/pp/relic_pp_exp_k54.c \ + depends/relic/src/pp/relic_pp_map.c \ + depends/relic/src/pp/relic_pp_map_k2.c \ + depends/relic/src/pp/relic_pp_map_k8.c \ + depends/relic/src/pp/relic_pp_map_k12.c \ + depends/relic/src/pp/relic_pp_map_k24.c \ + depends/relic/src/pp/relic_pp_map_k48.c \ + depends/relic/src/pp/relic_pp_map_k54.c \ + depends/relic/src/pp/relic_pp_norm.c + +PC_SOURCES = \ + depends/relic/src/pc/relic_pc_core.c \ + depends/relic/src/pc/relic_pc_exp.c \ + depends/relic/src/pc/relic_pc_util.c + +MD_SOURCES = \ + depends/relic/src/md/blake2.h \ + depends/relic/src/md/blake2-impl.h \ + depends/relic/src/md/blake2s-ref.c \ + depends/relic/src/md/relic_md_blake2s.c \ + depends/relic/src/md/relic_md_hmac.c \ + depends/relic/src/md/relic_md_kdf.c \ + depends/relic/src/md/relic_md_mgf.c \ + depends/relic/src/md/relic_md_sha224.c \ + depends/relic/src/md/relic_md_sha256.c \ + depends/relic/src/md/relic_md_sha384.c \ + depends/relic/src/md/relic_md_sha512.c \ + depends/relic/src/md/relic_md_xmd.c \ + depends/relic/src/md/sha.h \ + depends/relic/src/md/sha224-256.c \ + depends/relic/src/md/sha384-512.c \ + depends/relic/src/md/sha_private.h + +EXTRA_DIST += \ + depends/relic/src/arch/relic_arch_arm.c \ + depends/relic/src/arch/relic_arch_none.c \ + depends/relic/src/arch/relic_arch_x64.c \ + depends/relic/src/arch/relic_arch_x86.c + +RELIC_SOURCES += \ + depends/relic/src/arch/relic_arch_@CPU_ARCH@.c \ + depends/relic/src/rand/relic_rand_call.c \ + depends/relic/src/rand/relic_rand_core.c \ + depends/relic/src/rand/relic_rand_hashd.c \ + depends/relic/src/rand/relic_rand_udev.c + +if WITH_DV +RELIC_SOURCES += \ + depends/relic/src/dv/relic_dv_mem.c \ + depends/relic/src/dv/relic_dv_util.c +endif + +if ARITH_EASY +if WITH_BN +RELIC_SOURCES += $(BN_SOURCES) \ + depends/relic/src/low/easy/relic_bn_add_low.c \ + depends/relic/src/low/easy/relic_bn_div_low.c \ + depends/relic/src/low/easy/relic_bn_mod_low.c \ + depends/relic/src/low/easy/relic_bn_mul_low.c \ + depends/relic/src/low/easy/relic_bn_shift_low.c \ + depends/relic/src/low/easy/relic_bn_sqr_low.c +endif + +if WITH_FP +RELIC_SOURCES += $(FP_SOURCES) \ + depends/relic/src/low/easy/relic_fp_add_low.c \ + depends/relic/src/low/easy/relic_fp_inv_low.c \ + depends/relic/src/low/easy/relic_fp_mul_low.c \ + depends/relic/src/low/easy/relic_fp_rdc_low.c \ + depends/relic/src/low/easy/relic_fp_shift_low.c \ + depends/relic/src/low/easy/relic_fp_sqr_low.c +endif + +if WITH_FPX +RELIC_SOURCES += $(FPX_SOURCES) \ + depends/relic/src/low/easy/relic_fpx_add_low.c \ + depends/relic/src/low/easy/relic_fpx_mul_low.c \ + depends/relic/src/low/easy/relic_fpx_rdc_low.c \ + depends/relic/src/low/easy/relic_fpx_sqr_low.c +endif + +if WITH_FB +RELIC_SOURCES += $(FB_SOURCES) \ + depends/relic/src/low/easy/relic_fb_add_low.c \ + depends/relic/src/low/easy/relic_fb_inv_low.c \ + depends/relic/src/low/easy/relic_fb_itr_low.c \ + depends/relic/src/low/easy/relic_fb_mul_low.c \ + depends/relic/src/low/easy/relic_fb_rdc_low.c \ + depends/relic/src/low/easy/relic_fb_shift_low.c \ + depends/relic/src/low/easy/relic_fb_slv_low.c \ + depends/relic/src/low/easy/relic_fb_sqr_low.c \ + depends/relic/src/low/easy/relic_fb_srt_low.c \ + depends/relic/src/low/easy/relic_fb_trc_low.c +endif +endif + +if ARITH_GMP +if WITH_BN +RELIC_SOURCES += $(BN_SOURCES) \ + depends/relic/src/low/gmp/relic_bn_add_low.c \ + depends/relic/src/low/gmp/relic_bn_div_low.c \ + depends/relic/src/low/gmp/relic_bn_mod_low.c \ + depends/relic/src/low/gmp/relic_bn_mul_low.c \ + depends/relic/src/low/gmp/relic_bn_shift_low.c \ + depends/relic/src/low/gmp/relic_bn_sqr_low.c +endif + +if WITH_FP +RELIC_SOURCES += $(FP_SOURCES) \ + depends/relic/src/low/gmp/relic_fp_add_low.c \ + depends/relic/src/low/gmp/relic_fp_inv_low.c \ + depends/relic/src/low/gmp/relic_fp_mul_low.c \ + depends/relic/src/low/gmp/relic_fp_rdc_low.c \ + depends/relic/src/low/gmp/relic_fp_shift_low.c \ + depends/relic/src/low/gmp/relic_fp_sqr_low.c +endif + +if WITH_FPX +RELIC_SOURCES += $(FPX_SOURCES) \ + depends/relic/src/low/easy/relic_fpx_add_low.c \ + depends/relic/src/low/easy/relic_fpx_mul_low.c \ + depends/relic/src/low/easy/relic_fpx_rdc_low.c \ + depends/relic/src/low/easy/relic_fpx_sqr_low.c +endif + +if WITH_FB +RELIC_SOURCES += $(FB_SOURCES) \ + depends/relic/src/low/gmp/relic_fb_add_low.c \ + depends/relic/src/low/easy/relic_fb_inv_low.c \ + depends/relic/src/low/easy/relic_fb_itr_low.c \ + depends/relic/src/low/easy/relic_fb_mul_low.c \ + depends/relic/src/low/easy/relic_fb_rdc_low.c \ + depends/relic/src/low/gmp/relic_fb_shift_low.c \ + depends/relic/src/low/easy/relic_fb_slv_low.c \ + depends/relic/src/low/easy/relic_fb_sqr_low.c \ + depends/relic/src/low/easy/relic_fb_srt_low.c \ + depends/relic/src/low/easy/relic_fb_trc_low.c +endif +endif + +if WITH_FBX +RELIC_SOURCES += \ + depends/relic/src/fbx/relic_fb2_inv.c \ + depends/relic/src/fbx/relic_fb2_mul.c \ + depends/relic/src/fbx/relic_fb2_slv.c \ + depends/relic/src/fbx/relic_fb2_sqr.c +endif + +if WITH_EP +RELIC_SOURCES += $(EP_SOURCES) +endif + +if WITH_EPX +RELIC_SOURCES += $(EPX_SOURCES) +endif + +if WITH_EB +RELIC_SOURCES += $(EB_SOURCES) +endif + +if WITH_ED +RELIC_SOURCES += $(ED_SOURCES) +endif + +if WITH_PP +RELIC_SOURCES += $(PP_SOURCES) +endif + +if WITH_PC +RELIC_SOURCES += $(PC_SOURCES) +endif + +if WITH_CP +RELIC_SOURCES += \ + depends/relic/src/cp/relic_cp_bbs.c \ + depends/relic/src/cp/relic_cp_bdpe.c \ + depends/relic/src/cp/relic_cp_bgn.c \ + depends/relic/src/cp/relic_cp_bls.c \ + depends/relic/src/cp/relic_cp_cls.c \ + depends/relic/src/cp/relic_cp_cmlhs.c \ + depends/relic/src/cp/relic_cp_ecdh.c \ + depends/relic/src/cp/relic_cp_ecdsa.c \ + depends/relic/src/cp/relic_cp_ecies.c \ + depends/relic/src/cp/relic_cp_ecmqv.c \ + depends/relic/src/cp/relic_cp_ecss.c \ + depends/relic/src/cp/relic_cp_ers.c \ + depends/relic/src/cp/relic_cp_etrs.c \ + depends/relic/src/cp/relic_cp_ghpe.c \ + depends/relic/src/cp/relic_cp_ibe.c \ + depends/relic/src/cp/relic_cp_mklhs.c \ + depends/relic/src/cp/relic_cp_mpss.c \ + depends/relic/src/cp/relic_cp_pcdel.c \ + depends/relic/src/cp/relic_cp_phpe.c \ + depends/relic/src/cp/relic_cp_pok.c \ + depends/relic/src/cp/relic_cp_pss.c \ + depends/relic/src/cp/relic_cp_rabin.c \ + depends/relic/src/cp/relic_cp_rsa.c \ + depends/relic/src/cp/relic_cp_sok.c \ + depends/relic/src/cp/relic_cp_sokaka.c \ + depends/relic/src/cp/relic_cp_vbnn.c \ + depends/relic/src/cp/relic_cp_zss.c +endif + +if WITH_BC +RELIC_SOURCES += \ + depends/relic/src/bc/relic_bc_aes.c \ + depends/relic/src/bc/rijndael-alg-fst.c \ + depends/relic/src/bc/rijndael-alg-fst.h \ + depends/relic/src/bc/rijndael-api-fst.c \ + depends/relic/src/bc/rijndael-api-fst.h +endif + +if WITH_MD +RELIC_SOURCES += $(MD_SOURCES) +endif + +if WITH_MPC +RELIC_SOURCES += \ + depends/relic/src/mpc/relic_mt_mpc.c \ + depends/relic/src/mpc/relic_pc_mpc.c +endif + +librelic_la_SOURCES = \ + $(RELIC_SOURCES) \ + $(RELIC_H) + +librelic_la_CPPFLAGS = $(AM_CPPFLAGS) $(RELIC_CPPFLAGS) $(RELIC_INCLUDES) +librelic_la_CXXFLAGS = $(AM_CXXFLAGS) +librelic_la_CFLAGS = $(AM_CFLAGS) +librelic_la_LDFLAGS = $(AM_LDFLAGS) + +noinst_LTLIBRARIES += $(LIBRELIC) diff --git a/Makefile.test.include b/Makefile.test.include index a8ec86cb5..39488b346 100644 --- a/Makefile.test.include +++ b/Makefile.test.include @@ -1,27 +1,27 @@ -# Copyright (c) 2021 The PIVX developers -# Copyright (c) 2022 The Dash Core developers -# Distributed under the MIT software license, see the accompanying -# file COPYING.MIT or http://www.opensource.org/licenses/mit-license.php. - -DASHBLS_RUNTEST = runtest - -RUNTEST_INCLUDES = \ - -I$(top_srcdir)/depends/catch2/include - -RUNTEST_H = \ - depends/catch2/include/catch2/catch.hpp - -runtest_SOURCES = \ - $(DASHBLS_H) \ - $(RELIC_H) \ - $(RUNTEST_H) \ - src/test.cpp \ - src/test-utils.hpp - -runtest_CPPFLAGS = $(AM_CPPFLAGS) $(DASHBLS_INCLUDES) $(RELIC_INCLUDES) $(RUNTEST_INCLUDES) -runtest_CXXFLAGS = $(AM_CXXFLAGS) -runtest_LDFLAGS = -static $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) - -runtest_LDADD = $(LIBDASHBLS) - -noinst_PROGRAMS += $(DASHBLS_RUNTEST) +# Copyright (c) 2021 The PIVX developers +# Copyright (c) 2022 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING.MIT or http://www.opensource.org/licenses/mit-license.php. + +DASHBLS_RUNTEST = runtest + +RUNTEST_INCLUDES = \ + -I$(top_srcdir)/depends/catch2/include + +RUNTEST_H = \ + depends/catch2/include/catch2/catch.hpp + +runtest_SOURCES = \ + $(DASHBLS_H) \ + $(RELIC_H) \ + $(RUNTEST_H) \ + src/test.cpp \ + src/test-utils.hpp + +runtest_CPPFLAGS = $(AM_CPPFLAGS) $(DASHBLS_INCLUDES) $(RELIC_INCLUDES) $(RUNTEST_INCLUDES) +runtest_CXXFLAGS = $(AM_CXXFLAGS) +runtest_LDFLAGS = -static $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) $(PTHREAD_FLAGS) + +runtest_LDADD = $(LIBDASHBLS) + +noinst_PROGRAMS += $(DASHBLS_RUNTEST) From e6293a81b2467f1374bde2b678cd8710885cc520 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Thu, 20 Aug 2026 13:12:41 +0530 Subject: [PATCH 05/15] build: sync definitions to CMake's C++17 requirement, refresh headers --- Makefile.bench.include | 4 ++-- Makefile.bls.include | 26 +++++++++++++------------- Makefile.test.include | 8 +++++--- apple.rust.deps.sh | 2 +- configure.ac | 4 ++-- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/Makefile.bench.include b/Makefile.bench.include index 84b014092..66a89d155 100644 --- a/Makefile.bench.include +++ b/Makefile.bench.include @@ -6,8 +6,8 @@ DASHBLS_RUNBENCH = runbench runbench_SOURCES = \ - src/test-bench.cpp \ - src/test-utils.hpp + include/dashbls/test-utils.hpp \ + src/test-bench.cpp runbench_SOURCES += \ $(RELIC_H) \ diff --git a/Makefile.bls.include b/Makefile.bls.include index 517593b2f..bab80392f 100644 --- a/Makefile.bls.include +++ b/Makefile.bls.include @@ -13,19 +13,19 @@ DASHBLS_INCLUDES = \ -I$(top_srcdir)/include/dashbls DASHBLS_H = \ - src/bls.hpp \ - src/chaincode.hpp \ - src/elements.hpp \ - src/extendedprivatekey.hpp \ - src/extendedpublickey.hpp \ - src/hdkeys.hpp \ - src/hkdf.hpp \ - src/legacy.hpp \ - src/privatekey.hpp \ - src/schemes.hpp \ - src/test-utils.hpp \ - src/threshold.hpp \ - src/util.hpp + include/dashbls/bls.hpp \ + include/dashbls/chaincode.hpp \ + include/dashbls/elements.hpp \ + include/dashbls/extendedprivatekey.hpp \ + include/dashbls/extendedpublickey.hpp \ + include/dashbls/hdkeys.hpp \ + include/dashbls/hkdf.hpp \ + include/dashbls/legacy.hpp \ + include/dashbls/privatekey.hpp \ + include/dashbls/schemes.hpp \ + include/dashbls/test-utils.hpp \ + include/dashbls/threshold.hpp \ + include/dashbls/util.hpp libdashbls_la_SOURCES = \ src/bls.cpp \ diff --git a/Makefile.test.include b/Makefile.test.include index 39488b346..d2de9ccdc 100644 --- a/Makefile.test.include +++ b/Makefile.test.include @@ -12,11 +12,13 @@ RUNTEST_H = \ depends/catch2/include/catch2/catch.hpp runtest_SOURCES = \ + include/dashbls/test-utils.hpp \ + src/test.cpp + +runtest_SOURCES += \ $(DASHBLS_H) \ $(RELIC_H) \ - $(RUNTEST_H) \ - src/test.cpp \ - src/test-utils.hpp + $(RUNTEST_H) runtest_CPPFLAGS = $(AM_CPPFLAGS) $(DASHBLS_INCLUDES) $(RELIC_INCLUDES) $(RUNTEST_INCLUDES) runtest_CXXFLAGS = $(AM_CXXFLAGS) diff --git a/apple.rust.deps.sh b/apple.rust.deps.sh index b2ebc41a4..6fd3177e9 100755 --- a/apple.rust.deps.sh +++ b/apple.rust.deps.sh @@ -315,7 +315,7 @@ build_bls_arch() { -I"../relic-${PFX}/depends/relic/include" \ -I"../../src/" \ -I"../gmplib-${PFX}/include" \ - -x c++ -std=c++14 -stdlib=libc++ -fembed-bitcode -arch "${ARCH}" -isysroot "${SDK}" "${EXTRA_ARGS}" \ + -x c++ -std=c++17 -stdlib=libc++ -fembed-bitcode -arch "${ARCH}" -isysroot "${SDK}" "${EXTRA_ARGS}" \ -c "../../src/${F}.cpp" -o "${F}.o" done diff --git a/configure.ac b/configure.ac index 56b8a7833..b2e26836c 100644 --- a/configure.ac +++ b/configure.ac @@ -47,8 +47,8 @@ case $host in lt_cv_deplibs_check_method="pass_all" ;; esac -dnl Require C++14 compiler (no GNU extensions) -AX_CXX_COMPILE_STDCXX([14], [noext], [mandatory], [nodefault]) +dnl Require C++17 compiler (no GNU extensions) +AX_CXX_COMPILE_STDCXX([17], [noext], [mandatory], [nodefault]) dnl Libtool init checks. LT_INIT([pic-only]) From 09302ea45f21966ce4f422b98539fbd2a35cf4e9 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 17 Aug 2026 02:41:32 +0530 Subject: [PATCH 06/15] fix: make public headers safe to include without additional headers --- include/dashbls/elements.hpp | 9 +++++---- include/dashbls/util.hpp | 10 ++++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/include/dashbls/elements.hpp b/include/dashbls/elements.hpp index 95937f62d..3947e4d29 100644 --- a/include/dashbls/elements.hpp +++ b/include/dashbls/elements.hpp @@ -15,16 +15,17 @@ #ifndef SRC_BLSELEMENTS_HPP_ #define SRC_BLSELEMENTS_HPP_ -extern "C" { -#include "relic.h" -} #include "relic_conf.h" -#include "util.hpp" #if defined GMP && ARITH == GMP #include #endif +extern "C" { +#include "relic.h" +} +#include "util.hpp" + #include namespace bls { diff --git a/include/dashbls/util.hpp b/include/dashbls/util.hpp index 052a0505e..9c3362d80 100644 --- a/include/dashbls/util.hpp +++ b/include/dashbls/util.hpp @@ -15,6 +15,16 @@ #ifndef SRC_BLSUTIL_HPP_ #define SRC_BLSUTIL_HPP_ +#include "relic_conf.h" + +#if defined GMP && ARITH == GMP +#include +#endif + +extern "C" { +#include "relic.h" +} + #include #include #include From f33cf95fc2673fc7825d65cfc3f3875af2097be1 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 18 Aug 2026 19:29:19 +0530 Subject: [PATCH 07/15] refactor(move-only): move the installed headers' definitions into TUs `git diff --color-moved=dimmed-zebra --color-moved-ws=ignore-all-space` --- Makefile.bls.include | 5 +- apple.rust.deps.sh | 2 +- include/dashbls/hdkeys.hpp | 195 +++---------------------------------- include/dashbls/hkdf.hpp | 52 +--------- include/dashbls/util.hpp | 65 ++----------- src/CMakeLists.txt | 8 +- src/hdkeys.cpp | 190 ++++++++++++++++++++++++++++++++++++ src/hkdf.cpp | 62 ++++++++++++ src/util.cpp | 78 +++++++++++++++ 9 files changed, 366 insertions(+), 291 deletions(-) create mode 100644 src/hdkeys.cpp create mode 100644 src/hkdf.cpp create mode 100644 src/util.cpp diff --git a/Makefile.bls.include b/Makefile.bls.include index bab80392f..ebb046eca 100644 --- a/Makefile.bls.include +++ b/Makefile.bls.include @@ -33,10 +33,13 @@ libdashbls_la_SOURCES = \ src/elements.cpp \ src/extendedprivatekey.cpp \ src/extendedpublickey.cpp \ + src/hdkeys.cpp \ + src/hkdf.cpp \ src/legacy.cpp \ src/privatekey.cpp \ src/schemes.cpp \ - src/threshold.cpp + src/threshold.cpp \ + src/util.cpp libdashbls_la_SOURCES += \ $(DASHBLS_H) \ diff --git a/apple.rust.deps.sh b/apple.rust.deps.sh index 6fd3177e9..98684d7cf 100755 --- a/apple.rust.deps.sh +++ b/apple.rust.deps.sh @@ -287,7 +287,7 @@ build_relic_arch() { build_bls_arch() { # shellcheck disable=SC2039 - BLS_FILES=( "bls" "chaincode" "elements" "extendedprivatekey" "extendedpublickey" "legacy" "privatekey" "schemes" "threshold" ) + BLS_FILES=( "bls" "chaincode" "elements" "extendedprivatekey" "extendedpublickey" "hdkeys" "hkdf" "legacy" "privatekey" "schemes" "threshold" "util" ) # shellcheck disable=SC2039 ALL_BLS_OBJ_FILES=$(printf "%s.o " "${BLS_FILES[@]}") diff --git a/include/dashbls/hdkeys.hpp b/include/dashbls/hdkeys.hpp index 629ec5e3d..6e5cd9592 100644 --- a/include/dashbls/hdkeys.hpp +++ b/include/dashbls/hdkeys.hpp @@ -35,188 +35,21 @@ class HDKeys { public: static const uint8_t HASH_LEN = 32; - static PrivateKey KeyGen(const std::vector& seed) - { - return KeyGen(Bytes(seed)); - } + static PrivateKey KeyGen(const std::vector& seed); - static PrivateKey KeyGen(const Bytes& seed) - { - // KeyGen - // 1. PRK = HKDF-Extract("BLS-SIG-KEYGEN-SALT-", IKM || I2OSP(0, 1)) - // 2. OKM = HKDF-Expand(PRK, keyInfo || I2OSP(L, 2), L) - // 3. SK = OS2IP(OKM) mod r - // 4. return SK - - const uint8_t info[1] = {0}; - const size_t infoLen = 0; - - // Required by the ietf spec to be at least 32 bytes - if (seed.size() < 32) { - throw std::invalid_argument("Seed size must be at least 32 bytes"); - } - - // "BLS-SIG-KEYGEN-SALT-" in ascii - const uint8_t saltHkdf[20] = {66, 76, 83, 45, 83, 73, 71, 45, 75, 69, - 89, 71, 69, 78, 45, 83, 65, 76, 84, 45}; - - uint8_t *prk = Util::SecAlloc(32); - uint8_t *ikmHkdf = Util::SecAlloc(seed.size() + 1); - memcpy(ikmHkdf, seed.begin(), seed.size()); - ikmHkdf[seed.size()] = 0; - - const uint8_t L = 48; // `ceil((3 * ceil(log2(r))) / 16)`, where `r` is the - // order of the BLS 12-381 curve - - uint8_t *okmHkdf = Util::SecAlloc(L); - - uint8_t keyInfoHkdf[infoLen + 2]; - memcpy(keyInfoHkdf, info, infoLen); - keyInfoHkdf[infoLen] = 0; // Two bytes for L, 0 and 48 - keyInfoHkdf[infoLen + 1] = L; - - HKDF256::ExtractExpand( - okmHkdf, - L, - ikmHkdf, - seed.size() + 1, - saltHkdf, - 20, - keyInfoHkdf, - infoLen + 2); - - bn_t order; - bn_new(order); - g1_get_ord(order); - - // Make sure private key is less than the curve order - bn_t *skBn = Util::SecAlloc(1); - bn_new(*skBn); - bn_read_bin(*skBn, okmHkdf, L); - bn_mod_basic(*skBn, *skBn, order); - - uint8_t *skBytes = Util::SecAlloc(32); - bn_write_bin(skBytes, 32, *skBn); - PrivateKey k = PrivateKey::FromBytes(Bytes(skBytes, 32)); - - Util::SecFree(prk); - Util::SecFree(ikmHkdf); - Util::SecFree(skBn); - Util::SecFree(okmHkdf); - Util::SecFree(skBytes); - - return k; - } - - static void IKMToLamportSk(uint8_t* outputLamportSk, const uint8_t* ikm, size_t ikmLen, const uint8_t* salt, size_t saltLen) { - // Expands the ikm to 255*HASH_LEN bytes for the lamport sk - const uint8_t info[1] = {0}; - HKDF256::ExtractExpand(outputLamportSk, HASH_LEN * 255, ikm, ikmLen, salt, saltLen, info, 0); - } - - static void ParentSkToLamportPK(uint8_t* outputLamportPk, const PrivateKey& parentSk, uint32_t index) { - uint8_t* salt = Util::SecAlloc(4); - uint8_t* ikm = Util::SecAlloc(HASH_LEN); - uint8_t* notIkm = Util::SecAlloc(HASH_LEN); - uint8_t* lamport0 = Util::SecAlloc(HASH_LEN * 255); - uint8_t* lamport1 = Util::SecAlloc(HASH_LEN * 255); - - Util::IntToFourBytes(salt, index); - parentSk.Serialize(ikm); - - for (size_t i = 0; i < HASH_LEN; i++) { // Flips the bits - notIkm[i] = ikm[i] ^ 0xff; - } - - HDKeys::IKMToLamportSk(lamport0, ikm, HASH_LEN, salt, 4); - HDKeys::IKMToLamportSk(lamport1, notIkm, HASH_LEN, salt, 4); - - uint8_t* lamportPk = Util::SecAlloc(HASH_LEN * 255 * 2); - - for (size_t i = 0; i < 255; i++) { - Util::Hash256(lamportPk + i * HASH_LEN, lamport0 + i * HASH_LEN, HASH_LEN); - } - - for (size_t i=0; i < 255; i++) { - Util::Hash256(lamportPk + 255 * HASH_LEN + i * HASH_LEN, lamport1 + i * HASH_LEN, HASH_LEN); - } - Util::Hash256(outputLamportPk, lamportPk, HASH_LEN * 255 * 2); - - Util::SecFree(salt); - Util::SecFree(ikm); - Util::SecFree(notIkm); - Util::SecFree(lamport0); - Util::SecFree(lamport1); - Util::SecFree(lamportPk); - } - - static PrivateKey DeriveChildSk(const PrivateKey& parentSk, uint32_t index) { - uint8_t* lamportPk = Util::SecAlloc(HASH_LEN); - HDKeys::ParentSkToLamportPK(lamportPk, parentSk, index); - std::vector lamportPkVector(lamportPk, lamportPk + HASH_LEN); - PrivateKey child = HDKeys::KeyGen(lamportPkVector); - Util::SecFree(lamportPk); - return child; - } - - static PrivateKey DeriveChildSkUnhardened(const PrivateKey& parentSk, uint32_t index) { - uint8_t* buf = Util::SecAlloc(G1Element::SIZE + 4); - uint8_t* digest = Util::SecAlloc(HASH_LEN); - memcpy(buf, parentSk.GetG1Element().Serialize().data(), G1Element::SIZE); - Util::IntToFourBytes(buf + G1Element::SIZE, index); - Util::Hash256(digest, buf, G1Element::SIZE + 4); - - PrivateKey ret = PrivateKey::Aggregate({parentSk, PrivateKey::FromBytes(Bytes(digest, HASH_LEN), true)}); - - Util::SecFree(buf); - Util::SecFree(digest); - return ret; - } - - static G1Element DeriveChildG1Unhardened(const G1Element& pk, uint32_t index) { - uint8_t* buf = Util::SecAlloc(G1Element::SIZE + 4); - uint8_t* digest = Util::SecAlloc(HASH_LEN); - memcpy(buf, pk.Serialize().data(), G1Element::SIZE); - - Util::IntToFourBytes(buf + G1Element::SIZE, index); - Util::Hash256(digest, buf, G1Element::SIZE + 4); - - bn_t nonce, ord; - bn_new(nonce); - bn_zero(nonce); - bn_read_bin(nonce, digest, HASH_LEN); - bn_new(ord); - g1_get_ord(ord); - bn_mod_basic(nonce, nonce, ord); - - Util::SecFree(buf); - Util::SecFree(digest); - - G1Element gen = G1Element::Generator(); - return pk + gen * nonce; - } - - static G2Element DeriveChildG2Unhardened(const G2Element& pk, uint32_t index) { - uint8_t* buf = Util::SecAlloc(G2Element::SIZE + 4); - uint8_t* digest = Util::SecAlloc(HASH_LEN); - memcpy(buf, pk.Serialize().data(), G2Element::SIZE); - Util::IntToFourBytes(buf + G2Element::SIZE, index); - Util::Hash256(digest, buf, G2Element::SIZE + 4); - - bn_t nonce, ord; - bn_new(nonce); - bn_zero(nonce); - bn_read_bin(nonce, digest, HASH_LEN); - bn_new(ord); - g1_get_ord(ord); - bn_mod_basic(nonce, nonce, ord); - - Util::SecFree(buf); - Util::SecFree(digest); - - G2Element gen = G2Element::Generator(); - return pk + gen * nonce; - } + static PrivateKey KeyGen(const Bytes& seed); + + static void IKMToLamportSk(uint8_t* outputLamportSk, const uint8_t* ikm, size_t ikmLen, const uint8_t* salt, size_t saltLen); + + static void ParentSkToLamportPK(uint8_t* outputLamportPk, const PrivateKey& parentSk, uint32_t index); + + static PrivateKey DeriveChildSk(const PrivateKey& parentSk, uint32_t index); + + static PrivateKey DeriveChildSkUnhardened(const PrivateKey& parentSk, uint32_t index); + + static G1Element DeriveChildG1Unhardened(const G1Element& pk, uint32_t index); + + static G2Element DeriveChildG2Unhardened(const G2Element& pk, uint32_t index); }; } // end namespace bls #endif // SRC_BLSHDKEYS_HPP_ diff --git a/include/dashbls/hkdf.hpp b/include/dashbls/hkdf.hpp index a7ddc863e..9057c46cb 100644 --- a/include/dashbls/hkdf.hpp +++ b/include/dashbls/hkdf.hpp @@ -35,60 +35,14 @@ class HKDF256 { public: static const uint8_t HASH_LEN = 32; - static void Extract(uint8_t* prk_output, const uint8_t* salt, const size_t saltLen, const uint8_t* ikm, const size_t ikm_len) { - // assert(saltLen == 4); // Used for EIP2333 key derivation - // assert(ikm_len == 32); // Used for EIP2333 key derivation - // Hash256 used as the hash function (sha256) - // PRK Output is 32 bytes (HashLen) - md_hmac(prk_output, ikm, ikm_len, salt, saltLen); - } + static void Extract(uint8_t* prk_output, const uint8_t* salt, const size_t saltLen, const uint8_t* ikm, const size_t ikm_len); - static void Expand(uint8_t* okm, size_t L, const uint8_t* prk, const uint8_t* info, const size_t infoLen) { - assert(L <= 255 * HASH_LEN); // L <= 255 * HashLen - assert(infoLen >= 0); - size_t N = (L + HASH_LEN - 1) / HASH_LEN; // Round up - size_t bytesWritten = 0; - - uint8_t* T = Util::SecAlloc(HASH_LEN); - uint8_t* hmacInput1 = Util::SecAlloc(infoLen + 1); - uint8_t* hmacInput = Util::SecAlloc(HASH_LEN + infoLen + 1); - - assert(N >= 1 && N <= 255); - - for (size_t i = 1; i <= N; i++) { - if (i == 1) { - memcpy(hmacInput1, info, infoLen); - hmacInput1[infoLen] = i; - md_hmac(T, hmacInput1, infoLen + 1, prk, HASH_LEN); - } else { - memcpy(hmacInput, T, HASH_LEN); - memcpy(hmacInput + HASH_LEN, info, infoLen); - hmacInput[HASH_LEN + infoLen] = i; - md_hmac(T, hmacInput, HASH_LEN + infoLen + 1, prk, HASH_LEN); - } - size_t to_write = L - bytesWritten; - if (to_write > HASH_LEN) { - to_write = HASH_LEN; - } - assert (to_write > 0 && to_write <= HASH_LEN); - memcpy(okm + bytesWritten, T, to_write); - bytesWritten += to_write; - } - Util::SecFree(T); - Util::SecFree(hmacInput1); - Util::SecFree(hmacInput); - assert(bytesWritten == L); - } + static void Expand(uint8_t* okm, size_t L, const uint8_t* prk, const uint8_t* info, const size_t infoLen); static void ExtractExpand(uint8_t* output, size_t outputLen, const uint8_t* key, size_t keyLen, const uint8_t* salt, size_t saltLen, - const uint8_t* info, size_t infoLen) { - uint8_t* prk = Util::SecAlloc(HASH_LEN); - HKDF256::Extract(prk, salt, saltLen, key, keyLen); - HKDF256::Expand(output, outputLen, prk, info, infoLen); - Util::SecFree(prk); - } + const uint8_t* info, size_t infoLen); }; } // end namespace bls #endif // SRC_BLSHKDF_HPP_ diff --git a/include/dashbls/util.hpp b/include/dashbls/util.hpp index 9c3362d80..70e1c38ab 100644 --- a/include/dashbls/util.hpp +++ b/include/dashbls/util.hpp @@ -69,25 +69,11 @@ class Util { typedef void (*SecureFreeCallback)(void*); public: static void Hash256(uint8_t* output, const uint8_t* message, - size_t messageLen) { - md_map_sh256(output, message, messageLen); - } + size_t messageLen); - static std::string HexStr(const uint8_t* data, size_t len) { - std::stringstream s; - s << std::hex; - for (size_t i=0; i < len; ++i) - s << std::setw(2) << std::setfill('0') << static_cast(data[i]); - return s.str(); - } + static std::string HexStr(const uint8_t* data, size_t len); - static std::string HexStr(const std::vector &data) { - std::stringstream s; - s << std::hex; - for (size_t i=0; i < data.size(); ++i) - s << std::setw(2) << std::setfill('0') << static_cast(data[i]); - return s.str(); - } + static std::string HexStr(const std::vector &data); /* * Securely allocates a portion of memory, using libsodium. This prevents @@ -108,60 +94,25 @@ class Util { /* * Converts one hex character to an int. */ - static uint8_t char2int(const char input) { - if(input >= '0' && input <= '9') - return input - '0'; - if(input >= 'A' && input <= 'F') - return input - 'A' + 10; - if(input >= 'a' && input <= 'f') - return input - 'a' + 10; - throw std::invalid_argument("Invalid input string"); - } + static uint8_t char2int(const char input); /* * Converts a hex string into a vector of bytes. */ - static std::vector HexToBytes(const std::string hex) { - if (hex.size() % 2 != 0) { - throw std::invalid_argument("Invalid input string, length must be multple of 2"); - } - std::vector ret = std::vector(); - size_t start_at = 0; - if (hex.rfind("0x", 0) == 0 || hex.rfind("0X", 0) == 0) { - start_at = 2; - } - - for (size_t i = start_at; i < hex.size(); i += 2) { - ret.push_back(char2int(hex[i]) * 16 + char2int(hex[i+1])); - } - return ret; - } + static std::vector HexToBytes(const std::string hex); /* * Converts a 32 bit int to bytes. */ static void IntToFourBytes(uint8_t* result, - const uint32_t input) { - for (size_t i = 0; i < 4; i++) { - result[3 - i] = (input >> (i * 8)); - } - } + const uint32_t input); /* * Converts a byte array to a 32 bit int. */ - static uint32_t FourBytesToInt(const uint8_t* bytes) { - uint32_t sum = 0; - for (size_t i = 0; i < 4; i++) { - uint32_t addend = static_cast(bytes[i]) << (8 * (3 - i)); - sum += addend; - } - return sum; - } + static uint32_t FourBytesToInt(const uint8_t* bytes); - static bool HasOnlyZeros(const Bytes& bytes) { - return std::all_of(bytes.begin(), bytes.end(), [](uint8_t byte){ return byte == 0x00; }); - } + static bool HasOnlyZeros(const Bytes& bytes); private: friend class BLS; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5802843a6..18c8f7eef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,15 +3,19 @@ source_group("SrcHeaders" FILES ${HEADERS}) add_library(dashbls ${HEADERS} - ${CMAKE_CURRENT_SOURCE_DIR}/privatekey.cpp ${CMAKE_CURRENT_SOURCE_DIR}/bls.cpp ${CMAKE_CURRENT_SOURCE_DIR}/chaincode.cpp ${CMAKE_CURRENT_SOURCE_DIR}/elements.cpp ${CMAKE_CURRENT_SOURCE_DIR}/extendedprivatekey.cpp ${CMAKE_CURRENT_SOURCE_DIR}/extendedpublickey.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/hdkeys.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/hkdf.cpp ${CMAKE_CURRENT_SOURCE_DIR}/legacy.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/privatekey.cpp ${CMAKE_CURRENT_SOURCE_DIR}/schemes.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/threshold.cpp) + ${CMAKE_CURRENT_SOURCE_DIR}/threshold.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/util.cpp +) target_include_directories(dashbls PUBLIC diff --git a/src/hdkeys.cpp b/src/hdkeys.cpp new file mode 100644 index 000000000..f38cc34ea --- /dev/null +++ b/src/hdkeys.cpp @@ -0,0 +1,190 @@ +// Copyright (c) 2026 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING.MIT or https://opensource.org/license/MIT + +#include "hdkeys.hpp" + +namespace bls { +PrivateKey HDKeys::KeyGen(const std::vector& seed) +{ + return KeyGen(Bytes(seed)); +} + +PrivateKey HDKeys::KeyGen(const Bytes& seed) +{ + // KeyGen + // 1. PRK = HKDF-Extract("BLS-SIG-KEYGEN-SALT-", IKM || I2OSP(0, 1)) + // 2. OKM = HKDF-Expand(PRK, keyInfo || I2OSP(L, 2), L) + // 3. SK = OS2IP(OKM) mod r + // 4. return SK + + const uint8_t info[1] = {0}; + const size_t infoLen = 0; + + // Required by the ietf spec to be at least 32 bytes + if (seed.size() < 32) { + throw std::invalid_argument("Seed size must be at least 32 bytes"); + } + + // "BLS-SIG-KEYGEN-SALT-" in ascii + const uint8_t saltHkdf[20] = {66, 76, 83, 45, 83, 73, 71, 45, 75, 69, + 89, 71, 69, 78, 45, 83, 65, 76, 84, 45}; + + uint8_t *prk = Util::SecAlloc(32); + uint8_t *ikmHkdf = Util::SecAlloc(seed.size() + 1); + memcpy(ikmHkdf, seed.begin(), seed.size()); + ikmHkdf[seed.size()] = 0; + + const uint8_t L = 48; // `ceil((3 * ceil(log2(r))) / 16)`, where `r` is the + // order of the BLS 12-381 curve + + uint8_t *okmHkdf = Util::SecAlloc(L); + + uint8_t keyInfoHkdf[infoLen + 2]; + memcpy(keyInfoHkdf, info, infoLen); + keyInfoHkdf[infoLen] = 0; // Two bytes for L, 0 and 48 + keyInfoHkdf[infoLen + 1] = L; + + HKDF256::ExtractExpand( + okmHkdf, + L, + ikmHkdf, + seed.size() + 1, + saltHkdf, + 20, + keyInfoHkdf, + infoLen + 2); + + bn_t order; + bn_new(order); + g1_get_ord(order); + + // Make sure private key is less than the curve order + bn_t *skBn = Util::SecAlloc(1); + bn_new(*skBn); + bn_read_bin(*skBn, okmHkdf, L); + bn_mod_basic(*skBn, *skBn, order); + + uint8_t *skBytes = Util::SecAlloc(32); + bn_write_bin(skBytes, 32, *skBn); + PrivateKey k = PrivateKey::FromBytes(Bytes(skBytes, 32)); + + Util::SecFree(prk); + Util::SecFree(ikmHkdf); + Util::SecFree(skBn); + Util::SecFree(okmHkdf); + Util::SecFree(skBytes); + + return k; +} + +void HDKeys::IKMToLamportSk(uint8_t* outputLamportSk, const uint8_t* ikm, size_t ikmLen, const uint8_t* salt, size_t saltLen) { + // Expands the ikm to 255*HASH_LEN bytes for the lamport sk + const uint8_t info[1] = {0}; + HKDF256::ExtractExpand(outputLamportSk, HASH_LEN * 255, ikm, ikmLen, salt, saltLen, info, 0); +} + +void HDKeys::ParentSkToLamportPK(uint8_t* outputLamportPk, const PrivateKey& parentSk, uint32_t index) { + uint8_t* salt = Util::SecAlloc(4); + uint8_t* ikm = Util::SecAlloc(HASH_LEN); + uint8_t* notIkm = Util::SecAlloc(HASH_LEN); + uint8_t* lamport0 = Util::SecAlloc(HASH_LEN * 255); + uint8_t* lamport1 = Util::SecAlloc(HASH_LEN * 255); + + Util::IntToFourBytes(salt, index); + parentSk.Serialize(ikm); + + for (size_t i = 0; i < HASH_LEN; i++) { // Flips the bits + notIkm[i] = ikm[i] ^ 0xff; + } + + HDKeys::IKMToLamportSk(lamport0, ikm, HASH_LEN, salt, 4); + HDKeys::IKMToLamportSk(lamport1, notIkm, HASH_LEN, salt, 4); + + uint8_t* lamportPk = Util::SecAlloc(HASH_LEN * 255 * 2); + + for (size_t i = 0; i < 255; i++) { + Util::Hash256(lamportPk + i * HASH_LEN, lamport0 + i * HASH_LEN, HASH_LEN); + } + + for (size_t i=0; i < 255; i++) { + Util::Hash256(lamportPk + 255 * HASH_LEN + i * HASH_LEN, lamport1 + i * HASH_LEN, HASH_LEN); + } + Util::Hash256(outputLamportPk, lamportPk, HASH_LEN * 255 * 2); + + Util::SecFree(salt); + Util::SecFree(ikm); + Util::SecFree(notIkm); + Util::SecFree(lamport0); + Util::SecFree(lamport1); + Util::SecFree(lamportPk); +} + +PrivateKey HDKeys::DeriveChildSk(const PrivateKey& parentSk, uint32_t index) { + uint8_t* lamportPk = Util::SecAlloc(HASH_LEN); + HDKeys::ParentSkToLamportPK(lamportPk, parentSk, index); + std::vector lamportPkVector(lamportPk, lamportPk + HASH_LEN); + PrivateKey child = HDKeys::KeyGen(lamportPkVector); + Util::SecFree(lamportPk); + return child; +} + +PrivateKey HDKeys::DeriveChildSkUnhardened(const PrivateKey& parentSk, uint32_t index) { + uint8_t* buf = Util::SecAlloc(G1Element::SIZE + 4); + uint8_t* digest = Util::SecAlloc(HASH_LEN); + memcpy(buf, parentSk.GetG1Element().Serialize().data(), G1Element::SIZE); + Util::IntToFourBytes(buf + G1Element::SIZE, index); + Util::Hash256(digest, buf, G1Element::SIZE + 4); + + PrivateKey ret = PrivateKey::Aggregate({parentSk, PrivateKey::FromBytes(Bytes(digest, HASH_LEN), true)}); + + Util::SecFree(buf); + Util::SecFree(digest); + return ret; +} + +G1Element HDKeys::DeriveChildG1Unhardened(const G1Element& pk, uint32_t index) { + uint8_t* buf = Util::SecAlloc(G1Element::SIZE + 4); + uint8_t* digest = Util::SecAlloc(HASH_LEN); + memcpy(buf, pk.Serialize().data(), G1Element::SIZE); + + Util::IntToFourBytes(buf + G1Element::SIZE, index); + Util::Hash256(digest, buf, G1Element::SIZE + 4); + + bn_t nonce, ord; + bn_new(nonce); + bn_zero(nonce); + bn_read_bin(nonce, digest, HASH_LEN); + bn_new(ord); + g1_get_ord(ord); + bn_mod_basic(nonce, nonce, ord); + + Util::SecFree(buf); + Util::SecFree(digest); + + G1Element gen = G1Element::Generator(); + return pk + gen * nonce; +} + +G2Element HDKeys::DeriveChildG2Unhardened(const G2Element& pk, uint32_t index) { + uint8_t* buf = Util::SecAlloc(G2Element::SIZE + 4); + uint8_t* digest = Util::SecAlloc(HASH_LEN); + memcpy(buf, pk.Serialize().data(), G2Element::SIZE); + Util::IntToFourBytes(buf + G2Element::SIZE, index); + Util::Hash256(digest, buf, G2Element::SIZE + 4); + + bn_t nonce, ord; + bn_new(nonce); + bn_zero(nonce); + bn_read_bin(nonce, digest, HASH_LEN); + bn_new(ord); + g1_get_ord(ord); + bn_mod_basic(nonce, nonce, ord); + + Util::SecFree(buf); + Util::SecFree(digest); + + G2Element gen = G2Element::Generator(); + return pk + gen * nonce; +} +} // namespace bls diff --git a/src/hkdf.cpp b/src/hkdf.cpp new file mode 100644 index 000000000..d7a53552e --- /dev/null +++ b/src/hkdf.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2026 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING.MIT or https://opensource.org/license/MIT + +#include "hkdf.hpp" + +namespace bls { +void HKDF256::Extract(uint8_t* prk_output, const uint8_t* salt, const size_t saltLen, const uint8_t* ikm, const size_t ikm_len) { + // assert(saltLen == 4); // Used for EIP2333 key derivation + // assert(ikm_len == 32); // Used for EIP2333 key derivation + // Hash256 used as the hash function (sha256) + // PRK Output is 32 bytes (HashLen) + md_hmac(prk_output, ikm, ikm_len, salt, saltLen); +} + +void HKDF256::Expand(uint8_t* okm, size_t L, const uint8_t* prk, const uint8_t* info, const size_t infoLen) { + assert(L <= 255 * HASH_LEN); // L <= 255 * HashLen + assert(infoLen >= 0); + size_t N = (L + HASH_LEN - 1) / HASH_LEN; // Round up + size_t bytesWritten = 0; + + uint8_t* T = Util::SecAlloc(HASH_LEN); + uint8_t* hmacInput1 = Util::SecAlloc(infoLen + 1); + uint8_t* hmacInput = Util::SecAlloc(HASH_LEN + infoLen + 1); + + assert(N >= 1 && N <= 255); + + for (size_t i = 1; i <= N; i++) { + if (i == 1) { + memcpy(hmacInput1, info, infoLen); + hmacInput1[infoLen] = i; + md_hmac(T, hmacInput1, infoLen + 1, prk, HASH_LEN); + } else { + memcpy(hmacInput, T, HASH_LEN); + memcpy(hmacInput + HASH_LEN, info, infoLen); + hmacInput[HASH_LEN + infoLen] = i; + md_hmac(T, hmacInput, HASH_LEN + infoLen + 1, prk, HASH_LEN); + } + size_t to_write = L - bytesWritten; + if (to_write > HASH_LEN) { + to_write = HASH_LEN; + } + assert (to_write > 0 && to_write <= HASH_LEN); + memcpy(okm + bytesWritten, T, to_write); + bytesWritten += to_write; + } + Util::SecFree(T); + Util::SecFree(hmacInput1); + Util::SecFree(hmacInput); + assert(bytesWritten == L); +} + +void HKDF256::ExtractExpand(uint8_t* output, size_t outputLen, + const uint8_t* key, size_t keyLen, + const uint8_t* salt, size_t saltLen, + const uint8_t* info, size_t infoLen) { + uint8_t* prk = Util::SecAlloc(HASH_LEN); + HKDF256::Extract(prk, salt, saltLen, key, keyLen); + HKDF256::Expand(output, outputLen, prk, info, infoLen); + Util::SecFree(prk); +} +} // namespace bls diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 000000000..d47d50270 --- /dev/null +++ b/src/util.cpp @@ -0,0 +1,78 @@ +// Copyright (c) 2026 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING.MIT or https://opensource.org/license/MIT + +#include "util.hpp" + +extern "C" { +#include "relic.h" +} + +namespace bls { +void Util::Hash256(uint8_t* output, const uint8_t* message, + size_t messageLen) { + md_map_sh256(output, message, messageLen); +} + +std::string Util::HexStr(const uint8_t* data, size_t len) { + std::stringstream s; + s << std::hex; + for (size_t i=0; i < len; ++i) + s << std::setw(2) << std::setfill('0') << static_cast(data[i]); + return s.str(); +} + +std::string Util::HexStr(const std::vector &data) { + std::stringstream s; + s << std::hex; + for (size_t i=0; i < data.size(); ++i) + s << std::setw(2) << std::setfill('0') << static_cast(data[i]); + return s.str(); +} + +uint8_t Util::char2int(const char input) { + if(input >= '0' && input <= '9') + return input - '0'; + if(input >= 'A' && input <= 'F') + return input - 'A' + 10; + if(input >= 'a' && input <= 'f') + return input - 'a' + 10; + throw std::invalid_argument("Invalid input string"); +} + +std::vector Util::HexToBytes(const std::string hex) { + if (hex.size() % 2 != 0) { + throw std::invalid_argument("Invalid input string, length must be multple of 2"); + } + std::vector ret = std::vector(); + size_t start_at = 0; + if (hex.rfind("0x", 0) == 0 || hex.rfind("0X", 0) == 0) { + start_at = 2; + } + + for (size_t i = start_at; i < hex.size(); i += 2) { + ret.push_back(char2int(hex[i]) * 16 + char2int(hex[i+1])); + } + return ret; +} + +void Util::IntToFourBytes(uint8_t* result, + const uint32_t input) { + for (size_t i = 0; i < 4; i++) { + result[3 - i] = (input >> (i * 8)); + } +} + +uint32_t Util::FourBytesToInt(const uint8_t* bytes) { + uint32_t sum = 0; + for (size_t i = 0; i < 4; i++) { + uint32_t addend = static_cast(bytes[i]) << (8 * (3 - i)); + sum += addend; + } + return sum; +} + +bool Util::HasOnlyZeros(const Bytes& bytes) { + return std::all_of(bytes.begin(), bytes.end(), [](uint8_t byte){ return byte == 0x00; }); +} +} // namespace bls From 777be772eeb09dd9bef9bd0e8fd31e2f907669ee Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 17 Aug 2026 00:31:23 +0530 Subject: [PATCH 08/15] chore: drop unused libsodium code path --- cmake_modules/Findsodium.cmake | 294 --------------------------------- src/bls.cpp | 9 - 2 files changed, 303 deletions(-) delete mode 100644 cmake_modules/Findsodium.cmake diff --git a/cmake_modules/Findsodium.cmake b/cmake_modules/Findsodium.cmake deleted file mode 100644 index 0350b67fc..000000000 --- a/cmake_modules/Findsodium.cmake +++ /dev/null @@ -1,294 +0,0 @@ -# Written in 2016 by Henrik Steffen Gaßmann -# -# To the extent possible under law, the author(s) have dedicated all -# copyright and related and neighboring rights to this software to the -# public domain worldwide. This software is distributed without any warranty. -# -# You should have received a copy of the CC0 Public Domain Dedication -# along with this software. If not, see -# -# http://creativecommons.org/publicdomain/zero/1.0/ -# -######################################################################## -# Tries to find the local libsodium installation. -# -# On Windows the sodium_DIR environment variable is used as a default -# hint which can be overridden by setting the corresponding cmake variable. -# -# Once done the following variables will be defined: -# -# sodium_FOUND -# sodium_INCLUDE_DIR -# sodium_LIBRARY_DEBUG -# sodium_LIBRARY_RELEASE -# -# -# Furthermore an imported "sodium" target is created. -# - -if (CMAKE_C_COMPILER_ID STREQUAL "GNU" - OR CMAKE_C_COMPILER_ID STREQUAL "Clang") - set(_GCC_COMPATIBLE 1) -endif() - -# static library option -if (NOT DEFINED sodium_USE_STATIC_LIBS) - option(sodium_USE_STATIC_LIBS "enable to statically link against sodium" OFF) -endif() -if(NOT (sodium_USE_STATIC_LIBS EQUAL sodium_USE_STATIC_LIBS_LAST)) - unset(sodium_LIBRARY CACHE) - unset(sodium_LIBRARY_DEBUG CACHE) - unset(sodium_LIBRARY_RELEASE CACHE) - unset(sodium_DLL_DEBUG CACHE) - unset(sodium_DLL_RELEASE CACHE) - set(sodium_USE_STATIC_LIBS_LAST ${sodium_USE_STATIC_LIBS} CACHE INTERNAL "internal change tracking variable") -endif() - - -######################################################################## -# UNIX -if (UNIX) - # import pkg-config - find_package(PkgConfig QUIET) - if (PKG_CONFIG_FOUND) - pkg_check_modules(sodium_PKG QUIET libsodium) - endif() - - if(sodium_USE_STATIC_LIBS) - foreach(_libname ${sodium_PKG_STATIC_LIBRARIES}) - if (NOT _libname MATCHES "^lib.*\\.a$") # ignore strings already ending with .a - list(INSERT sodium_PKG_STATIC_LIBRARIES 0 "lib${_libname}.a") - endif() - endforeach() - list(REMOVE_DUPLICATES sodium_PKG_STATIC_LIBRARIES) - - # if pkgconfig for libsodium doesn't provide - # static lib info, then override PKG_STATIC here.. - if (NOT sodium_PKG_STATIC_FOUND) - set(sodium_PKG_STATIC_LIBRARIES libsodium.a) - endif() - - set(XPREFIX sodium_PKG_STATIC) - else() - if (NOT sodium_PKG_FOUND) - set(sodium_PKG_LIBRARIES sodium) - endif() - - set(XPREFIX sodium_PKG) - endif() - - find_path(sodium_INCLUDE_DIR sodium.h - HINTS ${${XPREFIX}_INCLUDE_DIRS} - ) - find_library(sodium_LIBRARY_DEBUG NAMES ${${XPREFIX}_LIBRARIES} - HINTS ${${XPREFIX}_LIBRARY_DIRS} - ) - find_library(sodium_LIBRARY_RELEASE NAMES ${${XPREFIX}_LIBRARIES} - HINTS ${${XPREFIX}_LIBRARY_DIRS} - ) - - -######################################################################## -# Windows -elseif (WIN32) - set(sodium_DIR "$ENV{sodium_DIR}" CACHE FILEPATH "sodium install directory") - mark_as_advanced(sodium_DIR) - - find_path(sodium_INCLUDE_DIR sodium.h - HINTS ${sodium_DIR} - PATH_SUFFIXES include - ) - - if (MSVC) - # detect target architecture - file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/arch.cpp" [=[ - #if defined _M_IX86 - #error ARCH_VALUE x86_32 - #elif defined _M_X64 - #error ARCH_VALUE x86_64 - #endif - #error ARCH_VALUE unknown - ]=]) - try_compile(_UNUSED_VAR "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/arch.cpp" - OUTPUT_VARIABLE _COMPILATION_LOG - ) - string(REGEX REPLACE ".*ARCH_VALUE ([a-zA-Z0-9_]+).*" "\\1" _TARGET_ARCH "${_COMPILATION_LOG}") - - # construct library path - if (_TARGET_ARCH STREQUAL "x86_32") - string(APPEND _PLATFORM_PATH "Win32") - elseif(_TARGET_ARCH STREQUAL "x86_64") - string(APPEND _PLATFORM_PATH "x64") - else() - message(FATAL_ERROR "the ${_TARGET_ARCH} architecture is not supported by Findsodium.cmake.") - endif() - string(APPEND _PLATFORM_PATH "/$$CONFIG$$") - - if (MSVC_VERSION LESS 1900) - math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 60") - else() - math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 50") - endif() - string(APPEND _PLATFORM_PATH "/v${_VS_VERSION}") - - if (sodium_USE_STATIC_LIBS) - string(APPEND _PLATFORM_PATH "/static") - else() - string(APPEND _PLATFORM_PATH "/dynamic") - endif() - - string(REPLACE "$$CONFIG$$" "Debug" _DEBUG_PATH_SUFFIX "${_PLATFORM_PATH}") - string(REPLACE "$$CONFIG$$" "Release" _RELEASE_PATH_SUFFIX "${_PLATFORM_PATH}") - - find_library(sodium_LIBRARY_DEBUG libsodium.lib - HINTS ${sodium_DIR} - PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX} - ) - find_library(sodium_LIBRARY_RELEASE libsodium.lib - HINTS ${sodium_DIR} - PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX} - ) - if (NOT sodium_USE_STATIC_LIBS) - set(CMAKE_FIND_LIBRARY_SUFFIXES_BCK ${CMAKE_FIND_LIBRARY_SUFFIXES}) - set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll") - find_library(sodium_DLL_DEBUG libsodium - HINTS ${sodium_DIR} - PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX} - ) - find_library(sodium_DLL_RELEASE libsodium - HINTS ${sodium_DIR} - PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX} - ) - set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_BCK}) - endif() - - elseif(_GCC_COMPATIBLE) - if (sodium_USE_STATIC_LIBS) - find_library(sodium_LIBRARY_DEBUG libsodium.a - HINTS ${sodium_DIR} - PATH_SUFFIXES lib - ) - find_library(sodium_LIBRARY_RELEASE libsodium.a - HINTS ${sodium_DIR} - PATH_SUFFIXES lib - ) - else() - find_library(sodium_LIBRARY_DEBUG libsodium.dll.a - HINTS ${sodium_DIR} - PATH_SUFFIXES lib - ) - find_library(sodium_LIBRARY_RELEASE libsodium.dll.a - HINTS ${sodium_DIR} - PATH_SUFFIXES lib - ) - - file(GLOB _DLL - LIST_DIRECTORIES false - RELATIVE "${sodium_DIR}/bin" - "${sodium_DIR}/bin/libsodium*.dll" - ) - find_library(sodium_DLL_DEBUG ${_DLL} libsodium - HINTS ${sodium_DIR} - PATH_SUFFIXES bin - ) - find_library(sodium_DLL_RELEASE ${_DLL} libsodium - HINTS ${sodium_DIR} - PATH_SUFFIXES bin - ) - endif() - else() - message(FATAL_ERROR "this platform is not supported by FindSodium.cmake") - endif() - - -######################################################################## -# unsupported -else() - message(FATAL_ERROR "this platform is not supported by FindSodium.cmake") -endif() - - -######################################################################## -# common stuff - -# extract sodium version -if (sodium_INCLUDE_DIR) - set(_VERSION_HEADER "${_INCLUDE_DIR}/sodium/version.h") - if (EXISTS _VERSION_HEADER) - file(READ "${_VERSION_HEADER}" _VERSION_HEADER_CONTENT) - string(REGEX REPLACE ".*#[ \t]*define[ \t]*SODIUM_VERSION_STRING[ \t]*\"([^\n]*)\".*" "\\1" - sodium_VERSION "${_VERSION_HEADER_CONTENT}") - set(sodium_VERSION "${sodium_VERSION}" PARENT_SCOPE) - endif() -endif() - -# communicate results -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args( - sodium # The name must be either uppercase or match the filename case. - REQUIRED_VARS - sodium_LIBRARY_RELEASE - sodium_LIBRARY_DEBUG - sodium_INCLUDE_DIR - VERSION_VAR - sodium_VERSION -) - -if(Sodium_FOUND) - set(sodium_LIBRARIES - optimized ${sodium_LIBRARY_RELEASE} debug ${sodium_LIBRARY_DEBUG}) -endif() - -# mark file paths as advanced -mark_as_advanced(sodium_INCLUDE_DIR) -mark_as_advanced(sodium_LIBRARY_DEBUG) -mark_as_advanced(sodium_LIBRARY_RELEASE) -if (WIN32) - mark_as_advanced(sodium_DLL_DEBUG) - mark_as_advanced(sodium_DLL_RELEASE) -endif() - -# create imported target -if(sodium_USE_STATIC_LIBS) - set(_LIB_TYPE STATIC) -else() - set(_LIB_TYPE SHARED) -endif() -add_library(sodium ${_LIB_TYPE} IMPORTED) - -set_target_properties(sodium PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${sodium_INCLUDE_DIR}" - IMPORTED_LINK_INTERFACE_LANGUAGES "C" -) - -if (sodium_USE_STATIC_LIBS) - set_target_properties(sodium PROPERTIES - INTERFACE_COMPILE_DEFINITIONS "SODIUM_STATIC" - IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}" - IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}" - ) -else() - if (UNIX) - set_target_properties(sodium PROPERTIES - IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}" - IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}" - ) - elseif (WIN32) - set_target_properties(sodium PROPERTIES - IMPORTED_IMPLIB "${sodium_LIBRARY_RELEASE}" - IMPORTED_IMPLIB_DEBUG "${sodium_LIBRARY_DEBUG}" - ) - if (NOT (sodium_DLL_DEBUG MATCHES ".*-NOTFOUND")) - set_target_properties(sodium PROPERTIES - IMPORTED_LOCATION_DEBUG "${sodium_DLL_DEBUG}" - ) - endif() - if (NOT (sodium_DLL_RELEASE MATCHES ".*-NOTFOUND")) - set_target_properties(sodium PROPERTIES - IMPORTED_LOCATION_RELWITHDEBINFO "${sodium_DLL_RELEASE}" - IMPORTED_LOCATION_MINSIZEREL "${sodium_DLL_RELEASE}" - IMPORTED_LOCATION_RELEASE "${sodium_DLL_RELEASE}" - ) - endif() - endif() -endif() diff --git a/src/bls.cpp b/src/bls.cpp index e0af3a388..d233afe97 100644 --- a/src/bls.cpp +++ b/src/bls.cpp @@ -18,10 +18,6 @@ #include "mimalloc.h" #endif -#if BLSALLOC_SODIUM -#include "sodium.h" -#endif - namespace bls { const size_t BLS::MESSAGE_HASH_LEN; @@ -51,11 +47,6 @@ bool BLS::Init() } #if BLSALLOC_MIMALLOC SetSecureAllocator(mi_malloc, mi_free); -#elif BLSALLOC_SODIUM - if (sodium_init() < 0) { - throw std::runtime_error("libsodium init failed"); - } - SetSecureAllocator(sodium_malloc, sodium_free); #else SetSecureAllocator(malloc, free); #endif From 876202df3ffda3ebd7d8eca62f01a715bc9f8a29 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 18 Aug 2026 13:59:09 +0530 Subject: [PATCH 09/15] refactor: s/bn_helpers/secure/g, move definitions to TU, header internal --- Makefile.bls.include | 2 ++ apple.rust.deps.sh | 2 +- src/CMakeLists.txt | 1 + src/bn_helpers.hpp | 62 -------------------------------------------- src/schemes.cpp | 2 +- src/secure.cpp | 27 +++++++++++++++++++ src/secure.h | 58 +++++++++++++++++++++++++++++++++++++++++ src/threshold.cpp | 2 +- 8 files changed, 91 insertions(+), 65 deletions(-) delete mode 100644 src/bn_helpers.hpp create mode 100644 src/secure.cpp create mode 100644 src/secure.h diff --git a/Makefile.bls.include b/Makefile.bls.include index ebb046eca..9b0e33185 100644 --- a/Makefile.bls.include +++ b/Makefile.bls.include @@ -38,6 +38,8 @@ libdashbls_la_SOURCES = \ src/legacy.cpp \ src/privatekey.cpp \ src/schemes.cpp \ + src/secure.cpp \ + src/secure.h \ src/threshold.cpp \ src/util.cpp diff --git a/apple.rust.deps.sh b/apple.rust.deps.sh index 98684d7cf..d6a19297a 100755 --- a/apple.rust.deps.sh +++ b/apple.rust.deps.sh @@ -287,7 +287,7 @@ build_relic_arch() { build_bls_arch() { # shellcheck disable=SC2039 - BLS_FILES=( "bls" "chaincode" "elements" "extendedprivatekey" "extendedpublickey" "hdkeys" "hkdf" "legacy" "privatekey" "schemes" "threshold" "util" ) + BLS_FILES=( "bls" "chaincode" "elements" "extendedprivatekey" "extendedpublickey" "hdkeys" "hkdf" "legacy" "privatekey" "schemes" "secure" "threshold" "util" ) # shellcheck disable=SC2039 ALL_BLS_OBJ_FILES=$(printf "%s.o " "${BLS_FILES[@]}") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 18c8f7eef..af4516869 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,6 +13,7 @@ add_library(dashbls ${CMAKE_CURRENT_SOURCE_DIR}/legacy.cpp ${CMAKE_CURRENT_SOURCE_DIR}/privatekey.cpp ${CMAKE_CURRENT_SOURCE_DIR}/schemes.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/secure.cpp ${CMAKE_CURRENT_SOURCE_DIR}/threshold.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util.cpp ) diff --git a/src/bn_helpers.hpp b/src/bn_helpers.hpp deleted file mode 100644 index 05175437e..000000000 --- a/src/bn_helpers.hpp +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2026 The Dash Core developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef SRC_BN_HELPERS_HPP_ -#define SRC_BN_HELPERS_HPP_ - -#include "relic_conf.h" - -extern "C" { -#include "relic.h" -} - -#include - -namespace bls { - -// RAII wrapper for dynamically allocated bn_t arrays. -// Ensures bn_free + delete[] runs even when exceptions are thrown. -// Tracks initialization count so partial construction cleans up correctly. -struct BnArrayGuard { - bn_t* data; - size_t count; - size_t initialized{0}; - - explicit BnArrayGuard(size_t n) : data(new bn_t[n]), count(n) { - for (size_t i = 0; i < count; i++) { - bn_new(data[i]); - initialized++; - } - } - - ~BnArrayGuard() { - for (size_t i = 0; i < initialized; i++) { - bn_free(data[i]); - } - delete[] data; - } - - bn_t& operator[](size_t i) { return data[i]; } - const bn_t& operator[](size_t i) const { return data[i]; } - - BnArrayGuard(const BnArrayGuard&) = delete; - BnArrayGuard& operator=(const BnArrayGuard&) = delete; -}; - -// RAII wrapper for a single bn_t value. -struct BnGuard { - bn_t val; - - BnGuard() { bn_new(val); } - ~BnGuard() { bn_free(val); } - - operator bn_t&() { return val; } - - BnGuard(const BnGuard&) = delete; - BnGuard& operator=(const BnGuard&) = delete; -}; - -} // namespace bls - -#endif // SRC_BN_HELPERS_HPP_ diff --git a/src/schemes.cpp b/src/schemes.cpp index af33f1f50..0677e6352 100644 --- a/src/schemes.cpp +++ b/src/schemes.cpp @@ -21,7 +21,7 @@ #include "elements.hpp" #include "schemes.hpp" #include "hdkeys.hpp" -#include "bn_helpers.hpp" +#include "secure.h" using std::string; using std::vector; diff --git a/src/secure.cpp b/src/secure.cpp new file mode 100644 index 000000000..a47ae5658 --- /dev/null +++ b/src/secure.cpp @@ -0,0 +1,27 @@ +// Copyright (c) 2026 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING.MIT or https://opensource.org/license/MIT + +#include "secure.h" + +namespace bls { +BnArrayGuard::BnArrayGuard(size_t n) : data(new bn_t[n]), count(n) +{ + for (size_t i = 0; i < count; i++) { + bn_new(data[i]); + initialized++; + } +} + +BnArrayGuard::~BnArrayGuard() +{ + for (size_t i = 0; i < initialized; i++) { + bn_free(data[i]); + } + delete[] data; +} + +BnGuard::BnGuard() { bn_new(val); } + +BnGuard::~BnGuard() { bn_free(val); } +} // namespace bls diff --git a/src/secure.h b/src/secure.h new file mode 100644 index 000000000..37ca5c8b4 --- /dev/null +++ b/src/secure.h @@ -0,0 +1,58 @@ +// Copyright (c) 2026 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING.MIT or https://opensource.org/license/MIT + +#ifndef DASHBLS_SECURE_H +#define DASHBLS_SECURE_H + +#include "relic_conf.h" + +#if defined GMP && ARITH == GMP +#include +#endif + +extern "C" { +#include "relic.h" +} + +#include + +namespace bls { +/** + * RAII wrapper for a dynamically allocated array of bn_t. + * + * Ensures bn_free and delete[] run even when an exception is thrown, and + * tracks how many were initialised so partial construction still cleans up. + */ +struct BnArrayGuard { + bn_t* data; + size_t count; + size_t initialized{0}; + + explicit BnArrayGuard(size_t n); + ~BnArrayGuard(); + + bn_t& operator[](size_t i) { return data[i]; } + const bn_t& operator[](size_t i) const { return data[i]; } + + BnArrayGuard(const BnArrayGuard&) = delete; + BnArrayGuard& operator=(const BnArrayGuard&) = delete; +}; + +/** + * RAII wrapper for a single bn_t value. + */ +struct BnGuard { + bn_t val; + + BnGuard(); + ~BnGuard(); + + operator bn_t&() { return val; } + + BnGuard(const BnGuard&) = delete; + BnGuard& operator=(const BnGuard&) = delete; +}; +} // namespace bls + +#endif // DASHBLS_SECURE_H diff --git a/src/threshold.cpp b/src/threshold.cpp index 4561dccdc..bd4c93b4f 100644 --- a/src/threshold.cpp +++ b/src/threshold.cpp @@ -7,7 +7,7 @@ #include "threshold.hpp" #include "schemes.hpp" -#include "bn_helpers.hpp" +#include "secure.h" static std::unique_ptr pThresholdScheme(new bls::LegacySchemeMPL); From 5b96a8093d3cf6161ba52b860e87255f2ba60c71 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 25 Aug 2026 21:22:04 +0530 Subject: [PATCH 10/15] refactor: replace `Bn{,Array}Guard` with explicit move-only `util::Bn` --- Makefile.bls.include | 2 + Makefile.test.include | 6 ++- apple.rust.deps.sh | 2 +- src/CMakeLists.txt | 5 ++- src/extendedprivatekey.cpp | 20 +++++----- src/hdkeys.cpp | 26 +++++-------- src/privatekey.cpp | 36 +++++++----------- src/schemes.cpp | 20 +++++----- src/secure.cpp | 36 ++++++++++++------ src/secure.h | 72 +++++++++++++++++++++++------------- src/threshold.cpp | 30 +++++++-------- src/wipe.cpp | 30 +++++++++++++++ src/wipe.h | 29 +++++++++++++++ test/test_secure.cpp | 76 ++++++++++++++++++++++++++++++++++++++ 14 files changed, 272 insertions(+), 118 deletions(-) create mode 100644 src/wipe.cpp create mode 100644 src/wipe.h create mode 100644 test/test_secure.cpp diff --git a/Makefile.bls.include b/Makefile.bls.include index 9b0e33185..20fd35492 100644 --- a/Makefile.bls.include +++ b/Makefile.bls.include @@ -41,6 +41,8 @@ libdashbls_la_SOURCES = \ src/secure.cpp \ src/secure.h \ src/threshold.cpp \ + src/wipe.cpp \ + src/wipe.h \ src/util.cpp libdashbls_la_SOURCES += \ diff --git a/Makefile.test.include b/Makefile.test.include index d2de9ccdc..e134223ca 100644 --- a/Makefile.test.include +++ b/Makefile.test.include @@ -6,14 +6,16 @@ DASHBLS_RUNTEST = runtest RUNTEST_INCLUDES = \ - -I$(top_srcdir)/depends/catch2/include + -I$(top_srcdir)/depends/catch2/include \ + -I$(top_srcdir)/src RUNTEST_H = \ depends/catch2/include/catch2/catch.hpp runtest_SOURCES = \ include/dashbls/test-utils.hpp \ - src/test.cpp + src/test.cpp \ + test/test_secure.cpp runtest_SOURCES += \ $(DASHBLS_H) \ diff --git a/apple.rust.deps.sh b/apple.rust.deps.sh index d6a19297a..189813ac2 100755 --- a/apple.rust.deps.sh +++ b/apple.rust.deps.sh @@ -287,7 +287,7 @@ build_relic_arch() { build_bls_arch() { # shellcheck disable=SC2039 - BLS_FILES=( "bls" "chaincode" "elements" "extendedprivatekey" "extendedpublickey" "hdkeys" "hkdf" "legacy" "privatekey" "schemes" "secure" "threshold" "util" ) + BLS_FILES=( "bls" "chaincode" "elements" "extendedprivatekey" "extendedpublickey" "hdkeys" "hkdf" "legacy" "privatekey" "schemes" "secure" "threshold" "util" "wipe" ) # shellcheck disable=SC2039 ALL_BLS_OBJ_FILES=$(printf "%s.o " "${BLS_FILES[@]}") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index af4516869..89ee46d38 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(dashbls ${CMAKE_CURRENT_SOURCE_DIR}/schemes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/secure.cpp ${CMAKE_CURRENT_SOURCE_DIR}/threshold.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/wipe.cpp ${CMAKE_CURRENT_SOURCE_DIR}/util.cpp ) @@ -47,7 +48,9 @@ install(FILES $ DESTINATION lib) if(BUILD_BLS_TESTS) include(${CMAKE_CURRENT_SOURCE_DIR}/../depends/catch2/CMakeLists.txt) - add_executable(runtest test.cpp) + add_executable(runtest + ${CMAKE_CURRENT_SOURCE_DIR}/test.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../test/test_secure.cpp) target_link_libraries(runtest PRIVATE dashbls PRIVATE catch2) diff --git a/src/extendedprivatekey.cpp b/src/extendedprivatekey.cpp index d5fe746ee..4eb340466 100644 --- a/src/extendedprivatekey.cpp +++ b/src/extendedprivatekey.cpp @@ -12,11 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include #include "bls.hpp" +#include "secure.h" -namespace bls { +#include +namespace bls { ExtendedPrivateKey ExtendedPrivateKey::FromSeed(const Bytes& bytes) { // "BLS HD seed" in ascii const uint8_t prefix[] = {66, 76, 83, 32, 72, 68, 32, 115, 101, 101, 100}; @@ -37,21 +38,18 @@ ExtendedPrivateKey ExtendedPrivateKey::FromSeed(const Bytes& bytes) { md_hmac(IRight, hashInput, bytes.size() + 1, prefix, sizeof(prefix)); // Make sure private key is less than the curve order - bn_t* skBn = Util::SecAlloc(1); - bn_t order; - bn_new(order); + util::Bn skBn; + util::Bn order; g1_get_ord(order); - bn_new(*skBn); - bn_read_bin(*skBn, ILeft, PrivateKey::PRIVATE_KEY_SIZE); - bn_mod_basic(*skBn, *skBn, order); - bn_write_bin(ILeft, PrivateKey::PRIVATE_KEY_SIZE, *skBn); + bn_read_bin(skBn, ILeft, PrivateKey::PRIVATE_KEY_SIZE); + bn_mod_basic(skBn, skBn, order); + bn_write_bin(ILeft, PrivateKey::PRIVATE_KEY_SIZE, skBn); ExtendedPrivateKey esk(ExtendedPublicKey::REVISION, 0, 0, 0, ChainCode::FromBytes(Bytes(IRight, ChainCode::SIZE)), PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE))); - Util::SecFree(skBn); Util::SecFree(ILeft); Util::SecFree(hashInput); return esk; @@ -197,4 +195,4 @@ std::vector ExtendedPrivateKey::Serialize() const { // Destructors in PrivateKey and ChainCode handle cleaning of memory ExtendedPrivateKey::~ExtendedPrivateKey() {} -} // end namespace bls +} // namespace bls diff --git a/src/hdkeys.cpp b/src/hdkeys.cpp index f38cc34ea..c24d8cbfb 100644 --- a/src/hdkeys.cpp +++ b/src/hdkeys.cpp @@ -3,6 +3,7 @@ // file COPYING.MIT or https://opensource.org/license/MIT #include "hdkeys.hpp" +#include "secure.h" namespace bls { PrivateKey HDKeys::KeyGen(const std::vector& seed) @@ -55,23 +56,20 @@ PrivateKey HDKeys::KeyGen(const Bytes& seed) keyInfoHkdf, infoLen + 2); - bn_t order; - bn_new(order); + util::Bn order; g1_get_ord(order); // Make sure private key is less than the curve order - bn_t *skBn = Util::SecAlloc(1); - bn_new(*skBn); - bn_read_bin(*skBn, okmHkdf, L); - bn_mod_basic(*skBn, *skBn, order); + util::Bn skBn; + bn_read_bin(skBn, okmHkdf, L); + bn_mod_basic(skBn, skBn, order); uint8_t *skBytes = Util::SecAlloc(32); - bn_write_bin(skBytes, 32, *skBn); + bn_write_bin(skBytes, 32, skBn); PrivateKey k = PrivateKey::FromBytes(Bytes(skBytes, 32)); Util::SecFree(prk); Util::SecFree(ikmHkdf); - Util::SecFree(skBn); Util::SecFree(okmHkdf); Util::SecFree(skBytes); @@ -151,11 +149,9 @@ G1Element HDKeys::DeriveChildG1Unhardened(const G1Element& pk, uint32_t index) { Util::IntToFourBytes(buf + G1Element::SIZE, index); Util::Hash256(digest, buf, G1Element::SIZE + 4); - bn_t nonce, ord; - bn_new(nonce); - bn_zero(nonce); + util::Bn nonce; + util::Bn ord; bn_read_bin(nonce, digest, HASH_LEN); - bn_new(ord); g1_get_ord(ord); bn_mod_basic(nonce, nonce, ord); @@ -173,11 +169,9 @@ G2Element HDKeys::DeriveChildG2Unhardened(const G2Element& pk, uint32_t index) { Util::IntToFourBytes(buf + G2Element::SIZE, index); Util::Hash256(digest, buf, G2Element::SIZE + 4); - bn_t nonce, ord; - bn_new(nonce); - bn_zero(nonce); + util::Bn nonce; + util::Bn ord; bn_read_bin(nonce, digest, HASH_LEN); - bn_new(ord); g1_get_ord(ord); bn_mod_basic(nonce, nonce, ord); diff --git a/src/privatekey.cpp b/src/privatekey.cpp index d07246d85..2a06fc898 100644 --- a/src/privatekey.cpp +++ b/src/privatekey.cpp @@ -14,9 +14,9 @@ #include "bls.hpp" #include "legacy.hpp" +#include "secure.h" namespace bls { - const size_t PrivateKey::PRIVATE_KEY_SIZE; PrivateKey PrivateKey::FromSeedBIP32(const Bytes& seed) { @@ -30,20 +30,17 @@ PrivateKey PrivateKey::FromSeedBIP32(const Bytes& seed) { // Hash the seed into sk md_hmac(hash, seed.begin(), (int)seed.size(), hmacKey, sizeof(hmacKey)); - bn_t order; - bn_new(order); + util::Bn order; g1_get_ord(order); // Make sure private key is less than the curve order - bn_t* skBn = Util::SecAlloc(1); - bn_new(*skBn); - bn_read_bin(*skBn, hash, PrivateKey::PRIVATE_KEY_SIZE); - bn_mod_basic(*skBn, *skBn, order); + util::Bn skBn; + bn_read_bin(skBn, hash, PrivateKey::PRIVATE_KEY_SIZE); + bn_mod_basic(skBn, skBn, order); PrivateKey k; - bn_copy(k.keydata, *skBn); + bn_copy(k.keydata, skBn); - Util::SecFree(skBn); Util::SecFree(hash); return k; } @@ -57,8 +54,7 @@ PrivateKey PrivateKey::FromBytes(const Bytes& bytes, bool modOrder) PrivateKey k; bn_read_bin(k.keydata, bytes.begin(), PrivateKey::PRIVATE_KEY_SIZE); - bn_t ord; - bn_new(ord); + util::Bn ord; g1_get_ord(ord); if (modOrder) { bn_mod_basic(k.keydata, k.keydata, ord); @@ -80,16 +76,13 @@ PrivateKey PrivateKey::FromByteVector(const std::vector bytes, bool mod // Construct a private key from a bytearray. PrivateKey PrivateKey::RandomPrivateKey() { - bn_t *r = Util::SecAlloc(1); - bn_new(*r); - bn_rand(*r, RLC_POS, 256); + util::Bn r; + bn_rand(r, RLC_POS, 256); PrivateKey k; - bn_copy(k.keydata, *r); - bn_t ord; - bn_new(ord); + bn_copy(k.keydata, r); + util::Bn ord; g1_get_ord(ord); bn_mod_basic(k.keydata, k.keydata, ord); - Util::SecFree(r); return k; } @@ -210,8 +203,7 @@ G2Element operator*(const PrivateKey &k, const G2Element &a) { return a * k; } PrivateKey operator*(const PrivateKey& k, const bn_t& a) { k.CheckKeyData(); - bn_t order; - bn_new(order); + util::Bn order; g2_get_ord(order); PrivateKey ret; @@ -240,8 +232,7 @@ PrivateKey PrivateKey::Aggregate(std::vector const &privateKeys) throw std::length_error("Number of private keys must be at least 1"); } - bn_t order; - bn_new(order); + util::Bn order; g1_get_ord(order); PrivateKey ret; @@ -330,5 +321,4 @@ void PrivateKey::CheckKeyData() const throw std::runtime_error("PrivateKey::CheckKeyData keydata not initialized"); } } - } // end namespace bls diff --git a/src/schemes.cpp b/src/schemes.cpp index 0677e6352..1309a1733 100644 --- a/src/schemes.cpp +++ b/src/schemes.cpp @@ -27,12 +27,11 @@ using std::string; using std::vector; namespace bls { - template -static void HashPubKeys(bn_t* computedTs, size_t nPubKeys, GetBytesFn getBytes) +static void HashPubKeys(std::vector& computedTs, size_t nPubKeys, GetBytesFn getBytes) { - BnGuard order; - g2_get_ord(order.val); + util::Bn order; + g2_get_ord(order); std::vector vecBuffer(nPubKeys * G1Element::SIZE); @@ -54,7 +53,7 @@ static void HashPubKeys(bn_t* computedTs, size_t nPubKeys, GetBytesFn getBytes) Util::Hash256(hash, buffer, 4 + 32); bn_read_bin(computedTs[i], hash, 32); - bn_mod_basic(computedTs[i], computedTs[i], order.val); + bn_mod_basic(computedTs[i], computedTs[i], order); } } @@ -84,7 +83,7 @@ const std::string PopSchemeMPL::CIPHERSUITE_ID = "BLS_SIG_BLS12381G2_XMD:SHA-256 const std::string PopSchemeMPL::POP_CIPHERSUITE_ID = "BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; PrivateKey CoreMPL::KeyGen(const vector& seed) { - return HDKeys::KeyGen(seed); + return CoreMPL::KeyGen(Bytes(seed)); } PrivateKey CoreMPL::KeyGen(const Bytes& seed) { @@ -199,7 +198,7 @@ G2Element CoreMPL::AggregateSecure(std::vector const &vecPublicKeys, throw std::invalid_argument("LegacySchemeMPL::AggregateSigs sigs.size() != pubKeys.size()"); } - BnArrayGuard computedTs(vecPublicKeys.size()); + std::vector computedTs(vecPublicKeys.size()); std::vector, const G2Element*>> vecSorted(vecPublicKeys.size()); for (size_t i = 0; i < vecPublicKeys.size(); i++) { vecSorted[i] = std::make_pair(vecPublicKeys[i].SerializeToArray(fLegacy), &vecSignatures[i]); @@ -208,7 +207,7 @@ G2Element CoreMPL::AggregateSecure(std::vector const &vecPublicKeys, return std::memcmp(a.first.data(), b.first.data(), G1Element::SIZE) < 0; }); - HashPubKeys(computedTs.data, vecSorted.size(), + HashPubKeys(computedTs, vecSorted.size(), [&](size_t i) { return vecSorted[i].first.data(); }); // Raise all signatures to power of the corresponding t's and aggregate the results into aggSig @@ -232,7 +231,7 @@ bool CoreMPL::VerifySecure(const std::vector& vecPublicKeys, const G2Element& signature, const Bytes& message, const bool fLegacy) { - BnArrayGuard computedTs(vecPublicKeys.size()); + std::vector computedTs(vecPublicKeys.size()); std::vector> vecSorted(vecPublicKeys.size()); for (size_t i = 0; i < vecPublicKeys.size(); i++) { vecSorted[i] = vecPublicKeys[i].SerializeToArray(fLegacy); @@ -241,7 +240,7 @@ bool CoreMPL::VerifySecure(const std::vector& vecPublicKeys, return std::memcmp(a.data(), b.data(), G1Element::SIZE) < 0; }); - HashPubKeys(computedTs.data, vecSorted.size(), + HashPubKeys(computedTs, vecSorted.size(), [&](size_t i) { return vecSorted[i].data(); }); G1Element publicKey; @@ -700,5 +699,4 @@ bool LegacySchemeMPL::AggregateVerify(const vector &pubkeys, return CoreMPL::NativeVerify((g1_t*)vecG1.data(), (g2_t*)vecG2.data(), nPubKeys + 1); } - } // end namespace bls diff --git a/src/secure.cpp b/src/secure.cpp index a47ae5658..fcd3e1769 100644 --- a/src/secure.cpp +++ b/src/secure.cpp @@ -3,25 +3,37 @@ // file COPYING.MIT or https://opensource.org/license/MIT #include "secure.h" +#include "util.hpp" + +#include namespace bls { -BnArrayGuard::BnArrayGuard(size_t n) : data(new bn_t[n]), count(n) +namespace util { +Bn::Bn() { - for (size_t i = 0; i < count; i++) { - bn_new(data[i]); - initialized++; - } + bn_null(m_val); + bn_new(m_val); + bn_zero(m_val); } -BnArrayGuard::~BnArrayGuard() +Bn::~Bn() { - for (size_t i = 0; i < initialized; i++) { - bn_free(data[i]); - } - delete[] data; + SecureWipe(m_val, sizeof(bn_st)); + bn_free(m_val); } -BnGuard::BnGuard() { bn_new(val); } +Bn::Bn(Bn&& other) noexcept : Bn() { *this = std::move(other); } -BnGuard::~BnGuard() { bn_free(val); } +Bn& Bn::operator=(Bn&& other) noexcept +{ + if (this != &other) { + SecureWipe(m_val->dp, sizeof(m_val->dp)); + bn_copy(m_val, other.m_val); + SecureWipe(other.m_val, sizeof(bn_st)); + bn_new(other.m_val); + bn_zero(other.m_val); + } + return *this; +} +} // namespace util } // namespace bls diff --git a/src/secure.h b/src/secure.h index 37ca5c8b4..d4b8fa5b4 100644 --- a/src/secure.h +++ b/src/secure.h @@ -5,6 +5,8 @@ #ifndef DASHBLS_SECURE_H #define DASHBLS_SECURE_H +#include "wipe.h" + #include "relic_conf.h" #if defined GMP && ARITH == GMP @@ -17,42 +19,60 @@ extern "C" { #include +// BLS::Init refuses to run unless relic was built ALLOC=AUTO, which puts +// bn_st's digits inline rather than behind a pointer. That inline storage is +// what gets wiped, so catch a mismatch here rather than wipe the wrong bytes. +#if !defined(ALLOC) || !defined(AUTO) || ALLOC != AUTO +#error "secure.h assumes relic is built with ALLOC == AUTO" +#endif + namespace bls { +namespace util { /** - * RAII wrapper for a dynamically allocated array of bn_t. + * An owning relic bn_t that clears itself when destroyed. * - * Ensures bn_free and delete[] run even when an exception is thrown, and - * tracks how many were initialised so partial construction still cleans up. + * Constructing one initialises it, which relic requires before any use. + * Destroying one clears the whole bn_st, so a value that was a secret does not + * outlive its scope, and the used and sign fields do not survive either. */ -struct BnArrayGuard { - bn_t* data; - size_t count; - size_t initialized{0}; - - explicit BnArrayGuard(size_t n); - ~BnArrayGuard(); +class Bn { +public: + Bn(); + ~Bn(); - bn_t& operator[](size_t i) { return data[i]; } - const bn_t& operator[](size_t i) const { return data[i]; } + Bn(const Bn&) = delete; + Bn& operator=(const Bn&) = delete; - BnArrayGuard(const BnArrayGuard&) = delete; - BnArrayGuard& operator=(const BnArrayGuard&) = delete; -}; - -/** - * RAII wrapper for a single bn_t value. - */ -struct BnGuard { - bn_t val; + /** + * Takes over another's value, leaving it cleared and zero. + * + * Move-only, so a secret is never silently duplicated. The digits are + * inline under ALLOC=AUTO, so there is no buffer to steal, and the value is + * copied and the source cleared. + */ + Bn(Bn&& other) noexcept; + Bn& operator=(Bn&& other) noexcept; - BnGuard(); - ~BnGuard(); + /** + * Converts to the underlying bn_t. + * + * @returns A reference that relic entry points taking a bn_t accept. + */ + operator bn_t&() { return m_val; } + operator const bn_t&() const { return m_val; } - operator bn_t&() { return val; } + /** + * Names the conversion, for where a bn_t is not deduced. + * + * @returns A reference to the underlying bn_t. + */ + bn_t& native() { return m_val; } + const bn_t& native() const { return m_val; } - BnGuard(const BnGuard&) = delete; - BnGuard& operator=(const BnGuard&) = delete; +private: + bn_t m_val; }; +} // namespace util } // namespace bls #endif // DASHBLS_SECURE_H diff --git a/src/threshold.cpp b/src/threshold.cpp index bd4c93b4f..00c5558af 100644 --- a/src/threshold.cpp +++ b/src/threshold.cpp @@ -174,13 +174,13 @@ namespace bls { throw std::length_error("At least 2 coefficients required"); } - BnGuard x; - bn_read_bin(x.val, id.begin(), Poly::nIdSize); - ops.ModOrder(x.val); + util::Bn x; + bn_read_bin(x, id.begin(), Poly::nIdSize); + ops.ModOrder(x); BLSType y = vecIn.back(); for (int i = (int) vecIn.size() - 2; i >= 0; i--) { - y = ops.Mul(y, x.val); + y = ops.Mul(y, x); y = ops.Add(y, vecIn[i]); } @@ -205,35 +205,35 @@ namespace bls { */ const size_t k = vec.size(); - BnArrayGuard delta(k); - BnArrayGuard ids2(k); + std::vector delta(k); + std::vector ids2(k); for (size_t i = 0; i < k; i++) { bn_read_bin(ids2[i], ids[i].begin(), Poly::nIdSize); ops.ModOrder(ids2[i]); } - BnGuard a, b, v; + util::Bn a, b, v; - bn_copy(a.val, ids2[0]); + bn_copy(a, ids2[0]); for (size_t i = 1; i < k; i++) { - ops.MulFP(a.val, a.val, ids2[i]); + ops.MulFP(a, a, ids2[i]); } - if (bn_is_zero(a.val)) { + if (bn_is_zero(a)) { throw std::invalid_argument("Zero id"); } for (size_t i = 0; i < k; i++) { - bn_copy(b.val, ids2[i]); + bn_copy(b, ids2[i]); for (size_t j = 0; j < k; j++) { if (j != i) { - ops.SubFP(v.val, ids2[j], ids2[i]); - if (bn_is_zero(v.val)) { + ops.SubFP(v, ids2[j], ids2[i]); + if (bn_is_zero(v)) { throw std::invalid_argument("Duplicate id"); } - ops.MulFP(b.val, b.val, v.val); + ops.MulFP(b, b, v); } } - ops.DivFP(delta[i], a.val, b.val); + ops.DivFP(delta[i], a, b); } /* diff --git a/src/wipe.cpp b/src/wipe.cpp new file mode 100644 index 000000000..701a33434 --- /dev/null +++ b/src/wipe.cpp @@ -0,0 +1,30 @@ +// Copyright (c) 2026 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING.MIT or https://opensource.org/license/MIT + +#include "wipe.h" + +#include + +#if defined(_WIN32) +#include +#endif + +namespace bls { +namespace util { +void SecureWipe(void* ptr, size_t len) noexcept +{ + if (ptr == nullptr || len == 0) { + return; + } +#if defined(_WIN32) + SecureZeroMemory(ptr, len); +#else + std::memset(ptr, 0, len); + // A barrier the compiler may not reorder across, so the memset above cannot + // be dropped as a dead store on a buffer nothing reads again. + __asm__ __volatile__("" : : "r"(ptr) : "memory"); +#endif +} +} // namespace util +} // namespace bls diff --git a/src/wipe.h b/src/wipe.h new file mode 100644 index 000000000..cd955b001 --- /dev/null +++ b/src/wipe.h @@ -0,0 +1,29 @@ +// Copyright (c) 2026 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING.MIT or https://opensource.org/license/MIT + +#ifndef DASHBLS_WIPE_H +#define DASHBLS_WIPE_H + +#include + +namespace bls { +namespace util { +/** + * Overwrites a buffer that held a secret with zeroes. + * + * A memset behind a barrier, as Bitcoin Core's memory_cleanse does it, so the + * store is not dropped on a buffer nothing reads again. A null pointer or a + * zero length is ignored. + * + * See "Dead Store Elimination (Still) Considered Harmful", Yang et al., + * USENIX Security 2017. + * + * @param ptr Start of the buffer to clear. + * @param len How many bytes to clear. + */ +void SecureWipe(void* ptr, size_t len) noexcept; +} // namespace util +} // namespace bls + +#endif // DASHBLS_WIPE_H diff --git a/test/test_secure.cpp b/test/test_secure.cpp new file mode 100644 index 000000000..a68f0ce96 --- /dev/null +++ b/test/test_secure.cpp @@ -0,0 +1,76 @@ +// Copyright (c) 2026 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING.MIT or https://opensource.org/license/MIT + +#include "secure.h" + +#include + +#include +#include +#include +#include +#include + +TEST_CASE("util::SecureWipe clears a buffer") +{ + std::array buf; + buf.fill(0xff); + bls::util::SecureWipe(buf.data(), buf.size()); + REQUIRE(std::all_of(buf.begin(), buf.end(), [](uint8_t b) { + return b == 0; + })); + + // Degenerate arguments are ignored rather than faulting. + bls::util::SecureWipe(nullptr, 16); + bls::util::SecureWipe(buf.data(), 0); +} + +TEST_CASE("A bignum has value semantics and clears itself") +{ + SECTION("A move takes the value and leaves the source cleared") + { + bls::util::Bn expected; + g1_get_ord(expected); + bls::util::Bn order; + g1_get_ord(order); + REQUIRE(bn_is_zero(order) == 0); + + bls::util::Bn moved{std::move(order)}; + REQUIRE(bn_cmp(moved, expected) == RLC_EQ); + REQUIRE(bn_is_zero(order) == 1); + + bls::util::Bn assigned; + assigned = std::move(moved); + REQUIRE(bn_cmp(assigned, expected) == RLC_EQ); + REQUIRE(bn_is_zero(moved) == 1); + + // Self-move is a no-op rather than a clear. + auto& alias = assigned; + assigned = std::move(alias); + REQUIRE(bn_cmp(assigned, expected) == RLC_EQ); + } + + SECTION("Default construction leaves a usable, zeroed value") + { + std::vector vec(4); + REQUIRE(bn_is_zero(vec[3]) == 1); + bn_set_dig(vec[3], 4); + REQUIRE(bn_cmp_dig(vec[3], 4) == RLC_EQ); + } + + SECTION("Destruction leaves nothing behind") + { + alignas(bn_st) std::array storage; + storage.fill(0xa5); + + auto* secret = new (storage.data()) bls::util::Bn(); + bn_set_dig(secret->native(), 42); + REQUIRE(bn_cmp_dig(secret->native(), 42) == RLC_EQ); + secret->~Bn(); + + REQUIRE(std::all_of(storage.begin(), storage.end(), [](uint8_t b) { + return b == 0; + })); + } +} From 46bf3e29107561e74ef6deab3d4dbc702bed008f Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Wed, 26 Aug 2026 19:08:27 +0530 Subject: [PATCH 11/15] refactor: define `util::Sec{Malloc,Free}` over the defined callback --- binds/python/pythonbindings.cpp | 10 +- go-bindings/Makefile | 2 + go-bindings/blschia.cpp | 25 ++-- go-bindings/blschia.h | 4 +- go-bindings/privatekey.cpp | 15 ++- go-bindings/privatekey.go | 9 +- go-bindings/schemes.go | 21 ++-- go-bindings/threshold.go | 21 ++-- go-bindings/util.go | 16 +++ include/dashbls/util.hpp | 25 +--- rust-bindings/bls-dash-sys/bindings.rs | 4 +- .../c-bindings/bip32/extendedprivatekey.cpp | 22 ++-- .../bls-dash-sys/c-bindings/blschia.cpp | 25 ++-- .../bls-dash-sys/c-bindings/blschia.h | 4 +- .../bls-dash-sys/c-bindings/privatekey.cpp | 16 ++- rust-bindings/bls-signatures/src/utils.rs | 7 +- src/bls.cpp | 7 +- src/extendedprivatekey.cpp | 16 +-- src/hdkeys.cpp | 68 +++++------ src/hkdf.cpp | 17 +-- src/privatekey.cpp | 28 ++--- src/secure.cpp | 49 ++++++++ src/secure.h | 61 ++++++++++ src/test.cpp | 16 ++- test/test_secure.cpp | 109 ++++++++++++++++++ 25 files changed, 440 insertions(+), 157 deletions(-) diff --git a/binds/python/pythonbindings.cpp b/binds/python/pythonbindings.cpp index b037a74d6..333ab70ad 100644 --- a/binds/python/pythonbindings.cpp +++ b/binds/python/pythonbindings.cpp @@ -32,6 +32,8 @@ #include #include +#include "secure.h" + namespace py = pybind11; using namespace bls; @@ -161,7 +163,7 @@ PYBIND11_MODULE(dashbls, m) "__bytes__", [](const PrivateKey &k) { uint8_t *output = - Util::SecAlloc(PrivateKey::PRIVATE_KEY_SIZE); + util::SecAlloc(PrivateKey::PRIVATE_KEY_SIZE); { RelicGuard guard; k.Serialize(output); @@ -169,7 +171,7 @@ PYBIND11_MODULE(dashbls, m) py::bytes ret = py::bytes( reinterpret_cast(output), PrivateKey::PRIVATE_KEY_SIZE); - Util::SecFree(output); + util::SecFree(output); return ret; }) .def( @@ -187,12 +189,12 @@ PYBIND11_MODULE(dashbls, m) .def(py::self != py::self, py::call_guard()) .def("__repr__", [](const PrivateKey &k) { RelicGuard guard; - uint8_t *output = Util::SecAlloc(PrivateKey::PRIVATE_KEY_SIZE); + uint8_t *output = util::SecAlloc(PrivateKey::PRIVATE_KEY_SIZE); k.Serialize(output); std::string ret = ""; - Util::SecFree(output); + util::SecFree(output); return ret; }); diff --git a/go-bindings/Makefile b/go-bindings/Makefile index dcea3f24f..fc418f26b 100644 --- a/go-bindings/Makefile +++ b/go-bindings/Makefile @@ -41,6 +41,8 @@ CGO_CXXFLAGS ?= "\ -I$(CURR_DIR)../depends/mimalloc/include \ -I$(CURR_DIR)../depends/relic/include \ -I$(CURR_DIR)../include \ +-I$(CURR_DIR)../include/dashbls \ +-I$(CURR_DIR)../src \ -I$(GMP_PREFIX)/include" CGO_LDFLAGS ?= "\ diff --git a/go-bindings/blschia.cpp b/go-bindings/blschia.cpp index 04f0f5494..f8a23ee1e 100644 --- a/go-bindings/blschia.cpp +++ b/go-bindings/blschia.cpp @@ -12,29 +12,35 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include #include "dashbls/bls.hpp" +#include "secure.h" #include "error.h" #include "blschia.h" std::string gErrMsg; -void SecFree(void *p) { - bls::Util::SecFree(p); +void SecFree(void *p, size_t nBytes) { + bls::util::SecFree(p, nBytes); } void** AllocPtrArray(size_t len) { - // caller to free - return (void**)bls::Util::SecAlloc(sizeof(void*) * len); + try { + return bls::util::SecAlloc(len); + } catch (const std::exception& ex) { + gErrMsg = ex.what(); + return nullptr; + } } void SetPtrArray(void** arrPtr, void* elemPtr, int index) { arrPtr[index] = elemPtr; } -void FreePtrArray(void** inPtr) { - bls::Util::SecFree(inPtr); +void FreePtrArray(void** inPtr, size_t len) { + bls::util::SecFree(inPtr, sizeof(void*) * len); } void* GetPtrAtIndex(void** arrPtr, int index) { @@ -42,7 +48,12 @@ void* GetPtrAtIndex(void** arrPtr, int index) { } uint8_t* SecAllocBytes(size_t len) { - return (uint8_t*)bls::Util::SecAlloc(sizeof(uint8_t) * len); + try { + return bls::util::SecAlloc(len); + } catch (const std::exception& ex) { + gErrMsg = ex.what(); + return nullptr; + } } void* GetAddressAtIndex(uint8_t* ptr, int index) { diff --git a/go-bindings/blschia.h b/go-bindings/blschia.h index 450cda277..9d31cfa49 100644 --- a/go-bindings/blschia.h +++ b/go-bindings/blschia.h @@ -23,14 +23,14 @@ extern "C" { #endif // Export the BLS SecFree method -void SecFree(void *p); +void SecFree(void *p, size_t nBytes); typedef void** carr; // Additional C++ helper funcs for allocations void** AllocPtrArray(size_t len); void SetPtrArray(void **arrPtr, void *elemPtr, int index); -void FreePtrArray(void **inPtr); +void FreePtrArray(void **inPtr, size_t len); void* GetPtrAtIndex(void **arrPtr, int index); // Allocates an array of bytes with size of passed in len argument diff --git a/go-bindings/privatekey.cpp b/go-bindings/privatekey.cpp index 90b7f0aab..f436cec8f 100644 --- a/go-bindings/privatekey.cpp +++ b/go-bindings/privatekey.cpp @@ -14,8 +14,10 @@ #include #include "dashbls/bls.hpp" +#include "secure.h" #include "privatekey.h" #include "blschia.h" +#include #include "error.h" #include "utils.hpp" @@ -47,9 +49,16 @@ CPrivateKey CPrivateKeyAggregate(void** sks, const size_t len) { void* CPrivateKeySerialize(const CPrivateKey sk) { const bls::PrivateKey* skPtr = (bls::PrivateKey*)sk; - uint8_t* buffer = bls::Util::SecAlloc(bls::PrivateKey::PRIVATE_KEY_SIZE); - skPtr->Serialize(buffer); - return (void*)buffer; + uint8_t* buffer = nullptr; + try { + buffer = bls::util::SecAlloc(bls::PrivateKey::PRIVATE_KEY_SIZE); + skPtr->Serialize(buffer); + return (void*)buffer; + } catch (const std::exception& ex) { + bls::util::SecFree(buffer, bls::PrivateKey::PRIVATE_KEY_SIZE); + gErrMsg = ex.what(); + return nullptr; + } } size_t CPrivateKeySizeBytes() { diff --git a/go-bindings/privatekey.go b/go-bindings/privatekey.go index 155338e83..db5dd6aa7 100644 --- a/go-bindings/privatekey.go +++ b/go-bindings/privatekey.go @@ -33,7 +33,7 @@ type PrivateKey struct { // this method allocates the new bls::PrivateKey object and keeps its pointer func PrivateKeyFromBytes(data []byte, modOrder bool) (*PrivateKey, error) { cBytesPtr := cAllocBytes(data) - defer C.SecFree(cBytesPtr) + defer C.SecFree(cBytesPtr, C.size_t(len(data))) var cDidErr C.bool sk := PrivateKey{ val: C.CPrivateKeyFromBytes(cBytesPtr, C.size_t(len(data)), C.bool(modOrder), &cDidErr), @@ -91,7 +91,10 @@ func (sk *PrivateKey) G2Power(el *G2Element) *G2Element { // this method is a binding of the bls::PrivateKey::Serialize func (sk *PrivateKey) Serialize() []byte { ptr := C.CPrivateKeySerialize(sk.val) - defer C.SecFree(ptr) + if ptr == nil { + panic("dashbls: private key serialization failed: " + errFromC().Error()) + } + defer C.SecFree(ptr, C.CPrivateKeySizeBytes()) bytes := C.GoBytes(ptr, C.int(C.CPrivateKeySizeBytes())) runtime.KeepAlive(sk) return bytes @@ -101,7 +104,7 @@ func (sk *PrivateKey) Serialize() []byte { // this method is a binding of the bls::PrivateKey::Aggregate func PrivateKeyAggregate(sks ...*PrivateKey) *PrivateKey { cPrivKeyArrPtr := cAllocPrivKeys(sks...) - defer C.FreePtrArray(cPrivKeyArrPtr) + defer C.FreePtrArray(cPrivKeyArrPtr, C.size_t(len(sks))) sk := PrivateKey{ val: C.CPrivateKeyAggregate(cPrivKeyArrPtr, C.size_t(len(sks))), } diff --git a/go-bindings/schemes.go b/go-bindings/schemes.go index 2289b212b..f6909b135 100644 --- a/go-bindings/schemes.go +++ b/go-bindings/schemes.go @@ -128,7 +128,7 @@ func (s *coreMPL) Verify(pk *G1Element, msg []byte, sig *G2Element) bool { // this method is a binding of bls::CoreMPL::AggregatePubKeys func (s *coreMPL) AggregatePubKeys(pks ...*G1Element) *G1Element { cPkArrPtr := cAllocPubKeys(pks...) - defer C.FreePtrArray(cPkArrPtr) + defer C.FreePtrArray(cPkArrPtr, C.size_t(len(pks))) aggSig := G1Element{ val: C.CCoreMPLAggregatePubKeys(s.val, cPkArrPtr, C.size_t(len(pks))), } @@ -143,7 +143,7 @@ func (s *coreMPL) AggregatePubKeys(pks ...*G1Element) *G1Element { // this method is a binding of bls::CoreMPL::AggregateSigs func (s *coreMPL) AggregateSigs(sigs ...*G2Element) *G2Element { cSigArrPtr := cAllocSigs(sigs...) - defer C.FreePtrArray(cSigArrPtr) + defer C.FreePtrArray(cSigArrPtr, C.size_t(len(sigs))) aggSig := G2Element{ val: C.CCoreMPLAggregateSigs(s.val, cSigArrPtr, C.size_t(len(sigs))), } @@ -194,9 +194,9 @@ func (s *coreMPL) DeriveChildPkUnhardened(el *G1Element, index int) *G1Element { // this method is a binding of bls::CoreMPL::AggregateVerify func (s *coreMPL) AggregateVerify(pks []*G1Element, msgs [][]byte, sig *G2Element) bool { cPkArrPtr := cAllocPubKeys(pks...) - defer C.FreePtrArray(cPkArrPtr) + defer C.FreePtrArray(cPkArrPtr, C.size_t(len(pks))) cMsgArrPtr, msgLens := cAllocMsgs(msgs) - defer C.FreePtrArray(cMsgArrPtr) + defer C.FreePtrArray(cMsgArrPtr, C.size_t(len(msgs))) val := C.CCoreMPLAggregateVerify( s.val, cPkArrPtr, @@ -233,9 +233,9 @@ func NewBasicSchemeMPL() *BasicSchemeMPL { // this method is a binding of bls::BasicSchemeMPL::AggregateVerify func (s *BasicSchemeMPL) AggregateVerify(pks []*G1Element, msgs [][]byte, sig *G2Element) bool { cPkArrPtr := cAllocPubKeys(pks...) - defer C.FreePtrArray(cPkArrPtr) + defer C.FreePtrArray(cPkArrPtr, C.size_t(len(pks))) cMsgArrPtr, msgLens := cAllocMsgs(msgs) - defer C.FreePtrArray(cMsgArrPtr) + defer C.FreePtrArray(cMsgArrPtr, C.size_t(len(msgs))) val := C.CBasicSchemeMPLAggregateVerify( s.val, cPkArrPtr, @@ -317,9 +317,9 @@ func (s *AugSchemeMPL) Verify(pk *G1Element, msg []byte, sig *G2Element) bool { // this method is a binding of bls::AugSchemeMPL::AggregateVerify func (s *AugSchemeMPL) AggregateVerify(pks []*G1Element, msgs [][]byte, sig *G2Element) bool { cPkArrPtr := cAllocPubKeys(pks...) - defer C.FreePtrArray(cPkArrPtr) + defer C.FreePtrArray(cPkArrPtr, C.size_t(len(pks))) cMsgArrPtr, msgLens := cAllocMsgs(msgs) - defer C.FreePtrArray(cMsgArrPtr) + defer C.FreePtrArray(cMsgArrPtr, C.size_t(len(msgs))) val := C.CAugSchemeMPLAggregateVerify( s.val, cPkArrPtr, @@ -385,7 +385,7 @@ func (s *PopSchemeMPL) PopVerify(pk *G1Element, sig *G2Element) bool { func (s *PopSchemeMPL) FastAggregateVerify(pks []*G1Element, msg []byte, sig *G2Element) bool { msgPtr := C.CBytes(msg) cPkArrPtr := cAllocPubKeys(pks...) - defer C.FreePtrArray(cPkArrPtr) + defer C.FreePtrArray(cPkArrPtr, C.size_t(len(pks))) isVerified := C.CPopSchemeMPLFastAggregateVerify( s.val, cPkArrPtr, @@ -407,6 +407,9 @@ func (s *PopSchemeMPL) free() { func cAllocPubKeys(pks ...*G1Element) *unsafe.Pointer { arr := C.AllocPtrArray(C.size_t(len(pks))) + if arr == nil { + panic(secAllocFailed()) + } for i, pk := range pks { C.SetPtrArray(arr, unsafe.Pointer(pk.val), C.int(i)) } diff --git a/go-bindings/threshold.go b/go-bindings/threshold.go index 5f484cee8..0a55e3699 100644 --- a/go-bindings/threshold.go +++ b/go-bindings/threshold.go @@ -67,7 +67,7 @@ func ThresholdPrivateKeyShare(sks []*PrivateKey, hash Hash) (*PrivateKey, error) cHashPtr := C.CBytes(hash[:]) defer C.free(cHashPtr) cArrPtr := cAllocPrivKeys(sks...) - defer C.FreePtrArray(cArrPtr) + defer C.FreePtrArray(cArrPtr, C.size_t(len(sks))) var cDidErr C.bool sk := PrivateKey{ val: C.CThresholdPrivateKeyShare(cArrPtr, C.size_t(len(sks)), cHashPtr, &cDidErr), @@ -87,7 +87,7 @@ func ThresholdPublicKeyShare(pks []*G1Element, hash Hash) (*G1Element, error) { cHashPtr := C.CBytes(hash[:]) defer C.free(cHashPtr) cArrPtr := cAllocPubKeys(pks...) - defer C.FreePtrArray(cArrPtr) + defer C.FreePtrArray(cArrPtr, C.size_t(len(pks))) var cDidErr C.bool pk := G1Element{ val: C.CThresholdPublicKeyShare(cArrPtr, C.size_t(len(pks)), cHashPtr, &cDidErr), @@ -107,7 +107,7 @@ func ThresholdSignatureShare(sigs []*G2Element, hash Hash) (*G2Element, error) { cHashPtr := C.CBytes(hash[:]) defer C.free(cHashPtr) cArrPtr := cAllocSigs(sigs...) - defer C.FreePtrArray(cArrPtr) + defer C.FreePtrArray(cArrPtr, C.size_t(len(sigs))) var cDidErr C.bool sig := G2Element{ val: C.CThresholdSignatureShare(cArrPtr, C.size_t(len(sigs)), cHashPtr, &cDidErr), @@ -125,9 +125,9 @@ func ThresholdSignatureShare(sigs []*G2Element, hash Hash) (*G2Element, error) { // this function is a binding of bls::Threshold::PrivateKeyRecover func ThresholdPrivateKeyRecover(sks []*PrivateKey, hashes []Hash) (*PrivateKey, error) { cArrPtr := cAllocPrivKeys(sks...) - defer C.FreePtrArray(cArrPtr) + defer C.FreePtrArray(cArrPtr, C.size_t(len(sks))) cHashArrPtr := cAllocHashes(hashes) - defer C.FreePtrArray(cHashArrPtr) + defer C.FreePtrArray(cHashArrPtr, C.size_t(len(hashes))) var cDidErr C.bool sk := PrivateKey{ val: C.CThresholdPrivateKeyRecover( @@ -151,9 +151,9 @@ func ThresholdPrivateKeyRecover(sks []*PrivateKey, hashes []Hash) (*PrivateKey, // this function is a binding of bls::Threshold::PublicKeyRecover func ThresholdPublicKeyRecover(pks []*G1Element, hashes []Hash) (*G1Element, error) { cArrPtr := cAllocPubKeys(pks...) - defer C.FreePtrArray(cArrPtr) + defer C.FreePtrArray(cArrPtr, C.size_t(len(pks))) cHashArrPtr := cAllocHashes(hashes) - defer C.FreePtrArray(cHashArrPtr) + defer C.FreePtrArray(cHashArrPtr, C.size_t(len(hashes))) var cDidErr C.bool pk := G1Element{ val: C.CThresholdPublicKeyRecover( @@ -177,9 +177,9 @@ func ThresholdPublicKeyRecover(pks []*G1Element, hashes []Hash) (*G1Element, err // this function is a binding of bls::Threshold::SignatureRecover func ThresholdSignatureRecover(sigs []*G2Element, hashes []Hash) (*G2Element, error) { cArrPtr := cAllocSigs(sigs...) - defer C.FreePtrArray(cArrPtr) + defer C.FreePtrArray(cArrPtr, C.size_t(len(sigs))) cHashArrPtr := cAllocHashes(hashes) - defer C.FreePtrArray(cHashArrPtr) + defer C.FreePtrArray(cHashArrPtr, C.size_t(len(hashes))) var cDidErr C.bool sig := G2Element{ val: C.CThresholdSignatureRecover( @@ -224,6 +224,9 @@ func ThresholdVerify(pk *G1Element, hash Hash, sig *G2Element) bool { func cAllocHashes(hashes []Hash) *unsafe.Pointer { cArrPtr := C.AllocPtrArray(C.size_t(len(hashes))) + if cArrPtr == nil { + panic(secAllocFailed()) + } for i, hash := range hashes { C.SetPtrArray(cArrPtr, unsafe.Pointer(C.CBytes(hash[:])), C.int(i)) } diff --git a/go-bindings/util.go b/go-bindings/util.go index fbea18895..4a6c0e4d0 100644 --- a/go-bindings/util.go +++ b/go-bindings/util.go @@ -21,15 +21,25 @@ import ( "unsafe" ) +func secAllocFailed() string { + return "dashbls: secure allocation failed: " + C.GoString(C.GetLastErrorMsg()) +} + func cAllocBytes(data []byte) unsafe.Pointer { l := C.size_t(len(data)) ptr := unsafe.Pointer(C.SecAllocBytes(l)) + if ptr == nil { + panic(secAllocFailed()) + } C.memcpy(ptr, unsafe.Pointer(&data[0]), l) return ptr } func cAllocSigs(sigs ...*G2Element) *unsafe.Pointer { arr := C.AllocPtrArray(C.size_t(len(sigs))) + if arr == nil { + panic(secAllocFailed()) + } for i, pk := range sigs { C.SetPtrArray(arr, unsafe.Pointer(pk.val), C.int(i)) } @@ -38,6 +48,9 @@ func cAllocSigs(sigs ...*G2Element) *unsafe.Pointer { func cAllocPrivKeys(sks ...*PrivateKey) *unsafe.Pointer { arr := C.AllocPtrArray(C.size_t(len(sks))) + if arr == nil { + panic(secAllocFailed()) + } for i, sk := range sks { C.SetPtrArray(arr, unsafe.Pointer(sk.val), C.int(i)) } @@ -47,6 +60,9 @@ func cAllocPrivKeys(sks ...*PrivateKey) *unsafe.Pointer { func cAllocMsgs(msgs [][]byte) (*unsafe.Pointer, []int) { msgLens := make([]int, len(msgs)) cMsgArrPtr := C.AllocPtrArray(C.size_t(len(msgs))) + if cMsgArrPtr == nil { + panic(secAllocFailed()) + } for i, msg := range msgs { cMsgPtr := C.CBytes(msg) C.SetPtrArray(cMsgArrPtr, unsafe.Pointer(cMsgPtr), C.int(i)) diff --git a/include/dashbls/util.hpp b/include/dashbls/util.hpp index 70e1c38ab..f9a997e92 100644 --- a/include/dashbls/util.hpp +++ b/include/dashbls/util.hpp @@ -26,14 +26,15 @@ extern "C" { } #include +#include +#include #include #include +#include #include #include -#include namespace bls { - class BLS; class Bytes { @@ -75,22 +76,6 @@ class Util { static std::string HexStr(const std::vector &data); - /* - * Securely allocates a portion of memory, using libsodium. This prevents - * paging to disk, and zeroes out the memory when it's freed. - */ - template - static T* SecAlloc(size_t numTs) { - return static_cast(secureAllocCallback(sizeof(T) * numTs)); - } - - /* - * Frees memory allocated using SecAlloc. - */ - static void SecFree(void* ptr) { - secureFreeCallback(ptr); - } - /* * Converts one hex character to an int. */ @@ -114,10 +99,6 @@ class Util { static bool HasOnlyZeros(const Bytes& bytes); - private: - friend class BLS; - static SecureAllocCallback secureAllocCallback; - static SecureFreeCallback secureFreeCallback; }; } // end namespace bls #endif // SRC_BLSUTIL_HPP_ diff --git a/rust-bindings/bls-dash-sys/bindings.rs b/rust-bindings/bls-dash-sys/bindings.rs index 6d570a7f9..6c168d8a7 100644 --- a/rust-bindings/bls-dash-sys/bindings.rs +++ b/rust-bindings/bls-dash-sys/bindings.rs @@ -92,7 +92,7 @@ extern "C" { pub fn PrivateKeySizeBytes() -> usize; - pub fn SecFree(p: *mut ::std::os::raw::c_void); + pub fn SecFree(p: *mut ::std::os::raw::c_void, nBytes: usize); pub fn AllocPtrArray(len: usize) -> *mut *mut ::std::os::raw::c_void; @@ -102,7 +102,7 @@ extern "C" { index: ::std::os::raw::c_int, ); - pub fn FreePtrArray(inPtr: *mut *mut ::std::os::raw::c_void); + pub fn FreePtrArray(inPtr: *mut *mut ::std::os::raw::c_void, len: usize); pub fn GetPtrAtIndex( arrPtr: *mut *mut ::std::os::raw::c_void, diff --git a/rust-bindings/bls-dash-sys/c-bindings/bip32/extendedprivatekey.cpp b/rust-bindings/bls-dash-sys/c-bindings/bip32/extendedprivatekey.cpp index 5e9efb8d8..945138be9 100644 --- a/rust-bindings/bls-dash-sys/c-bindings/bip32/extendedprivatekey.cpp +++ b/rust-bindings/bls-dash-sys/c-bindings/bip32/extendedprivatekey.cpp @@ -1,10 +1,13 @@ #include "extendedprivatekey.h" -#include - #include "../blschia.h" #include "../error.h" + #include "bls.hpp" +#include "secure.h" + +#include +#include BIP32ExtendedPrivateKey BIP32ExtendedPrivateKeyFromBytes(const void* data, size_t len, bool* didErr) { @@ -62,11 +65,16 @@ BIP32ChainCode BIP32ExtendedPrivateKeyGetChainCode(const BIP32ExtendedPrivateKey void* BIP32ExtendedPrivateKeySerialize(const BIP32ExtendedPrivateKey sk) { const bls::ExtendedPrivateKey* skPtr = (bls::ExtendedPrivateKey*)sk; - uint8_t* buffer = - bls::Util::SecAlloc(bls::ExtendedPrivateKey::SIZE); - skPtr->Serialize(buffer); - - return (void*)buffer; + uint8_t* buffer = nullptr; + try { + buffer = bls::util::SecAlloc(bls::ExtendedPrivateKey::SIZE); + skPtr->Serialize(buffer); + return (void*)buffer; + } catch (const std::exception& ex) { + bls::util::SecFree(buffer, bls::ExtendedPrivateKey::SIZE); + gErrMsg = ex.what(); + return nullptr; + } } bool BIP32ExtendedPrivateKeyIsEqual( diff --git a/rust-bindings/bls-dash-sys/c-bindings/blschia.cpp b/rust-bindings/bls-dash-sys/c-bindings/blschia.cpp index 2b3c274dc..c3531d1ac 100644 --- a/rust-bindings/bls-dash-sys/c-bindings/blschia.cpp +++ b/rust-bindings/bls-dash-sys/c-bindings/blschia.cpp @@ -12,9 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include #include #include #include "bls.hpp" +#include "secure.h" #include "error.h" #include "blschia.h" @@ -22,21 +24,25 @@ std::string gErrMsg; -void SecFree(void *p) { - bls::Util::SecFree(p); +void SecFree(void *p, size_t nBytes) { + bls::util::SecFree(p, nBytes); } void** AllocPtrArray(size_t len) { - // caller to free - return (void**)bls::Util::SecAlloc(sizeof(void*) * len); + try { + return bls::util::SecAlloc(len); + } catch (const std::exception& ex) { + gErrMsg = ex.what(); + return nullptr; + } } void SetPtrArray(void** arrPtr, void* elemPtr, int index) { arrPtr[index] = elemPtr; } -void FreePtrArray(void** inPtr) { - bls::Util::SecFree(inPtr); +void FreePtrArray(void** inPtr, size_t len) { + bls::util::SecFree(inPtr, sizeof(void*) * len); } void* GetPtrAtIndex(void** arrPtr, int index) { @@ -44,7 +50,12 @@ void* GetPtrAtIndex(void** arrPtr, int index) { } uint8_t* SecAllocBytes(size_t len) { - return (uint8_t*)bls::Util::SecAlloc(sizeof(uint8_t) * len); + try { + return bls::util::SecAlloc(len); + } catch (const std::exception& ex) { + gErrMsg = ex.what(); + return nullptr; + } } void* GetAddressAtIndex(uint8_t* ptr, int index) { diff --git a/rust-bindings/bls-dash-sys/c-bindings/blschia.h b/rust-bindings/bls-dash-sys/c-bindings/blschia.h index ebc3b518d..6d2654782 100644 --- a/rust-bindings/bls-dash-sys/c-bindings/blschia.h +++ b/rust-bindings/bls-dash-sys/c-bindings/blschia.h @@ -23,14 +23,14 @@ extern "C" { #endif // Export the BLS SecFree method -void SecFree(void *p); +void SecFree(void *p, size_t nBytes); typedef void** carr; // Additional C++ helper funcs for allocations void** AllocPtrArray(size_t len); void SetPtrArray(void **arrPtr, void *elemPtr, int index); -void FreePtrArray(void **inPtr); +void FreePtrArray(void **inPtr, size_t len); void* GetPtrAtIndex(void **arrPtr, int index); // Allocates an array of bytes with size of passed in len argument diff --git a/rust-bindings/bls-dash-sys/c-bindings/privatekey.cpp b/rust-bindings/bls-dash-sys/c-bindings/privatekey.cpp index ca1b2bcde..5bcc6f70b 100644 --- a/rust-bindings/bls-dash-sys/c-bindings/privatekey.cpp +++ b/rust-bindings/bls-dash-sys/c-bindings/privatekey.cpp @@ -14,8 +14,10 @@ #include #include "bls.hpp" +#include "secure.h" #include "privatekey.h" #include "blschia.h" +#include #include "error.h" #include "utils.hpp" @@ -53,10 +55,16 @@ PrivateKey PrivateKeyAggregate(void** sks, const size_t len) { void* PrivateKeySerialize(const PrivateKey sk) { const bls::PrivateKey* skPtr = (bls::PrivateKey*)sk; - uint8_t* buffer = bls::Util::SecAlloc(bls::PrivateKey::PRIVATE_KEY_SIZE); - skPtr->Serialize(buffer); - - return (void*)buffer; + uint8_t* buffer = nullptr; + try { + buffer = bls::util::SecAlloc(bls::PrivateKey::PRIVATE_KEY_SIZE); + skPtr->Serialize(buffer); + return (void*)buffer; + } catch (const std::exception& ex) { + bls::util::SecFree(buffer, bls::PrivateKey::PRIVATE_KEY_SIZE); + gErrMsg = ex.what(); + return nullptr; + } } size_t PrivateKeySizeBytes() { diff --git a/rust-bindings/bls-signatures/src/utils.rs b/rust-bindings/bls-signatures/src/utils.rs index 8461e25e9..07245294b 100644 --- a/rust-bindings/bls-signatures/src/utils.rs +++ b/rust-bindings/bls-signatures/src/utils.rs @@ -40,6 +40,11 @@ impl SecureBox { } pub(crate) unsafe fn from_ptr(ptr: *mut u8, len: usize) -> Self { + assert!( + !ptr.is_null(), + "secure allocation failed in the C binding: {}", + String::from_utf8_lossy(CStr::from_ptr(GetLastErrorMsg()).to_bytes()) + ); SecureBox { c_sec_alloc: ptr, len, @@ -66,6 +71,6 @@ impl Deref for SecureBox { impl Drop for SecureBox { fn drop(&mut self) { - unsafe { SecFree(self.as_mut_ptr()) } + unsafe { SecFree(self.as_mut_ptr(), self.len) } } } diff --git a/src/bls.cpp b/src/bls.cpp index d233afe97..c98cc422d 100644 --- a/src/bls.cpp +++ b/src/bls.cpp @@ -13,6 +13,7 @@ // limitations under the License. #include "bls.hpp" +#include "secure.h" #if BLSALLOC_MIMALLOC #include "mimalloc.h" @@ -24,9 +25,6 @@ const size_t BLS::MESSAGE_HASH_LEN; bool BLSInitResult = BLS::Init(); -Util::SecureAllocCallback Util::secureAllocCallback; -Util::SecureFreeCallback Util::secureFreeCallback; - static void relic_core_initializer(void* ptr) { core_init(); @@ -64,8 +62,7 @@ void BLS::SetSecureAllocator( Util::SecureAllocCallback allocCb, Util::SecureFreeCallback freeCb) { - Util::secureAllocCallback = allocCb; - Util::secureFreeCallback = freeCb; + util::SetSecureAllocator(allocCb, freeCb); } diff --git a/src/extendedprivatekey.cpp b/src/extendedprivatekey.cpp index 4eb340466..078a37a8d 100644 --- a/src/extendedprivatekey.cpp +++ b/src/extendedprivatekey.cpp @@ -22,11 +22,11 @@ ExtendedPrivateKey ExtendedPrivateKey::FromSeed(const Bytes& bytes) { // "BLS HD seed" in ascii const uint8_t prefix[] = {66, 76, 83, 32, 72, 68, 32, 115, 101, 101, 100}; - uint8_t* hashInput = Util::SecAlloc(bytes.size() + 1); + uint8_t* hashInput = util::SecAlloc(bytes.size() + 1); std::memcpy(hashInput, bytes.begin(), bytes.size()); // 32 bytes for secret key, and 32 bytes for chaincode - uint8_t* ILeft = Util::SecAlloc( + uint8_t* ILeft = util::SecAlloc( PrivateKey::PRIVATE_KEY_SIZE); uint8_t IRight[ChainCode::SIZE]; @@ -50,8 +50,8 @@ ExtendedPrivateKey ExtendedPrivateKey::FromSeed(const Bytes& bytes) { ChainCode::FromBytes(Bytes(IRight, ChainCode::SIZE)), PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE))); - Util::SecFree(ILeft); - Util::SecFree(hashInput); + util::SecFree(ILeft, PrivateKey::PRIVATE_KEY_SIZE); + util::SecFree(hashInput, bytes.size() + 1); return esk; } @@ -77,7 +77,7 @@ ExtendedPrivateKey ExtendedPrivateKey::PrivateChild(uint32_t i, const bool fLega uint32_t cmp = (1 << 31); bool hardened = i >= cmp; - uint8_t* ILeft = Util::SecAlloc(PrivateKey::PRIVATE_KEY_SIZE); + uint8_t* ILeft = util::SecAlloc(PrivateKey::PRIVATE_KEY_SIZE); uint8_t IRight[ChainCode::SIZE]; // Chain code is used as hmac key @@ -87,7 +87,7 @@ ExtendedPrivateKey ExtendedPrivateKey::PrivateChild(uint32_t i, const bool fLega size_t inputLen = hardened ? PrivateKey::PRIVATE_KEY_SIZE + 4 + 1 : G1Element::SIZE + 4 + 1; // Hmac input includes sk or pk, int i, and byte with 0 or 1 - uint8_t* hmacInput = Util::SecAlloc(inputLen); + uint8_t* hmacInput = util::SecAlloc(inputLen); // Fill the input with the required data if (hardened) { @@ -116,8 +116,8 @@ ExtendedPrivateKey ExtendedPrivateKey::PrivateChild(uint32_t i, const bool fLega ChainCode::FromBytes(Bytes(IRight, ChainCode::SIZE)), newSk); - Util::SecFree(ILeft); - Util::SecFree(hmacInput); + util::SecFree(ILeft, PrivateKey::PRIVATE_KEY_SIZE); + util::SecFree(hmacInput, inputLen); return esk; } diff --git a/src/hdkeys.cpp b/src/hdkeys.cpp index c24d8cbfb..8a97692b4 100644 --- a/src/hdkeys.cpp +++ b/src/hdkeys.cpp @@ -31,15 +31,15 @@ PrivateKey HDKeys::KeyGen(const Bytes& seed) const uint8_t saltHkdf[20] = {66, 76, 83, 45, 83, 73, 71, 45, 75, 69, 89, 71, 69, 78, 45, 83, 65, 76, 84, 45}; - uint8_t *prk = Util::SecAlloc(32); - uint8_t *ikmHkdf = Util::SecAlloc(seed.size() + 1); + uint8_t *prk = util::SecAlloc(32); + uint8_t *ikmHkdf = util::SecAlloc(seed.size() + 1); memcpy(ikmHkdf, seed.begin(), seed.size()); ikmHkdf[seed.size()] = 0; const uint8_t L = 48; // `ceil((3 * ceil(log2(r))) / 16)`, where `r` is the // order of the BLS 12-381 curve - uint8_t *okmHkdf = Util::SecAlloc(L); + uint8_t *okmHkdf = util::SecAlloc(L); uint8_t keyInfoHkdf[infoLen + 2]; memcpy(keyInfoHkdf, info, infoLen); @@ -64,14 +64,14 @@ PrivateKey HDKeys::KeyGen(const Bytes& seed) bn_read_bin(skBn, okmHkdf, L); bn_mod_basic(skBn, skBn, order); - uint8_t *skBytes = Util::SecAlloc(32); + uint8_t *skBytes = util::SecAlloc(32); bn_write_bin(skBytes, 32, skBn); PrivateKey k = PrivateKey::FromBytes(Bytes(skBytes, 32)); - Util::SecFree(prk); - Util::SecFree(ikmHkdf); - Util::SecFree(okmHkdf); - Util::SecFree(skBytes); + util::SecFree(prk, 32); + util::SecFree(ikmHkdf, seed.size() + 1); + util::SecFree(okmHkdf, L); + util::SecFree(skBytes, 32); return k; } @@ -83,11 +83,11 @@ void HDKeys::IKMToLamportSk(uint8_t* outputLamportSk, const uint8_t* ikm, size_t } void HDKeys::ParentSkToLamportPK(uint8_t* outputLamportPk, const PrivateKey& parentSk, uint32_t index) { - uint8_t* salt = Util::SecAlloc(4); - uint8_t* ikm = Util::SecAlloc(HASH_LEN); - uint8_t* notIkm = Util::SecAlloc(HASH_LEN); - uint8_t* lamport0 = Util::SecAlloc(HASH_LEN * 255); - uint8_t* lamport1 = Util::SecAlloc(HASH_LEN * 255); + uint8_t* salt = util::SecAlloc(4); + uint8_t* ikm = util::SecAlloc(HASH_LEN); + uint8_t* notIkm = util::SecAlloc(HASH_LEN); + uint8_t* lamport0 = util::SecAlloc(HASH_LEN * 255); + uint8_t* lamport1 = util::SecAlloc(HASH_LEN * 255); Util::IntToFourBytes(salt, index); parentSk.Serialize(ikm); @@ -99,7 +99,7 @@ void HDKeys::ParentSkToLamportPK(uint8_t* outputLamportPk, const PrivateKey& par HDKeys::IKMToLamportSk(lamport0, ikm, HASH_LEN, salt, 4); HDKeys::IKMToLamportSk(lamport1, notIkm, HASH_LEN, salt, 4); - uint8_t* lamportPk = Util::SecAlloc(HASH_LEN * 255 * 2); + uint8_t* lamportPk = util::SecAlloc(HASH_LEN * 255 * 2); for (size_t i = 0; i < 255; i++) { Util::Hash256(lamportPk + i * HASH_LEN, lamport0 + i * HASH_LEN, HASH_LEN); @@ -110,40 +110,40 @@ void HDKeys::ParentSkToLamportPK(uint8_t* outputLamportPk, const PrivateKey& par } Util::Hash256(outputLamportPk, lamportPk, HASH_LEN * 255 * 2); - Util::SecFree(salt); - Util::SecFree(ikm); - Util::SecFree(notIkm); - Util::SecFree(lamport0); - Util::SecFree(lamport1); - Util::SecFree(lamportPk); + util::SecFree(salt, 4); + util::SecFree(ikm, HASH_LEN); + util::SecFree(notIkm, HASH_LEN); + util::SecFree(lamport0, HASH_LEN * 255); + util::SecFree(lamport1, HASH_LEN * 255); + util::SecFree(lamportPk, HASH_LEN * 255 * 2); } PrivateKey HDKeys::DeriveChildSk(const PrivateKey& parentSk, uint32_t index) { - uint8_t* lamportPk = Util::SecAlloc(HASH_LEN); + uint8_t* lamportPk = util::SecAlloc(HASH_LEN); HDKeys::ParentSkToLamportPK(lamportPk, parentSk, index); std::vector lamportPkVector(lamportPk, lamportPk + HASH_LEN); PrivateKey child = HDKeys::KeyGen(lamportPkVector); - Util::SecFree(lamportPk); + util::SecFree(lamportPk, HASH_LEN); return child; } PrivateKey HDKeys::DeriveChildSkUnhardened(const PrivateKey& parentSk, uint32_t index) { - uint8_t* buf = Util::SecAlloc(G1Element::SIZE + 4); - uint8_t* digest = Util::SecAlloc(HASH_LEN); + uint8_t* buf = util::SecAlloc(G1Element::SIZE + 4); + uint8_t* digest = util::SecAlloc(HASH_LEN); memcpy(buf, parentSk.GetG1Element().Serialize().data(), G1Element::SIZE); Util::IntToFourBytes(buf + G1Element::SIZE, index); Util::Hash256(digest, buf, G1Element::SIZE + 4); PrivateKey ret = PrivateKey::Aggregate({parentSk, PrivateKey::FromBytes(Bytes(digest, HASH_LEN), true)}); - Util::SecFree(buf); - Util::SecFree(digest); + util::SecFree(buf, G1Element::SIZE + 4); + util::SecFree(digest, HASH_LEN); return ret; } G1Element HDKeys::DeriveChildG1Unhardened(const G1Element& pk, uint32_t index) { - uint8_t* buf = Util::SecAlloc(G1Element::SIZE + 4); - uint8_t* digest = Util::SecAlloc(HASH_LEN); + uint8_t* buf = util::SecAlloc(G1Element::SIZE + 4); + uint8_t* digest = util::SecAlloc(HASH_LEN); memcpy(buf, pk.Serialize().data(), G1Element::SIZE); Util::IntToFourBytes(buf + G1Element::SIZE, index); @@ -155,16 +155,16 @@ G1Element HDKeys::DeriveChildG1Unhardened(const G1Element& pk, uint32_t index) { g1_get_ord(ord); bn_mod_basic(nonce, nonce, ord); - Util::SecFree(buf); - Util::SecFree(digest); + util::SecFree(buf, G1Element::SIZE + 4); + util::SecFree(digest, HASH_LEN); G1Element gen = G1Element::Generator(); return pk + gen * nonce; } G2Element HDKeys::DeriveChildG2Unhardened(const G2Element& pk, uint32_t index) { - uint8_t* buf = Util::SecAlloc(G2Element::SIZE + 4); - uint8_t* digest = Util::SecAlloc(HASH_LEN); + uint8_t* buf = util::SecAlloc(G2Element::SIZE + 4); + uint8_t* digest = util::SecAlloc(HASH_LEN); memcpy(buf, pk.Serialize().data(), G2Element::SIZE); Util::IntToFourBytes(buf + G2Element::SIZE, index); Util::Hash256(digest, buf, G2Element::SIZE + 4); @@ -175,8 +175,8 @@ G2Element HDKeys::DeriveChildG2Unhardened(const G2Element& pk, uint32_t index) { g1_get_ord(ord); bn_mod_basic(nonce, nonce, ord); - Util::SecFree(buf); - Util::SecFree(digest); + util::SecFree(buf, G2Element::SIZE + 4); + util::SecFree(digest, HASH_LEN); G2Element gen = G2Element::Generator(); return pk + gen * nonce; diff --git a/src/hkdf.cpp b/src/hkdf.cpp index d7a53552e..19adde0c4 100644 --- a/src/hkdf.cpp +++ b/src/hkdf.cpp @@ -3,6 +3,7 @@ // file COPYING.MIT or https://opensource.org/license/MIT #include "hkdf.hpp" +#include "secure.h" namespace bls { void HKDF256::Extract(uint8_t* prk_output, const uint8_t* salt, const size_t saltLen, const uint8_t* ikm, const size_t ikm_len) { @@ -19,9 +20,9 @@ void HKDF256::Expand(uint8_t* okm, size_t L, const uint8_t* prk, const uint8_t* size_t N = (L + HASH_LEN - 1) / HASH_LEN; // Round up size_t bytesWritten = 0; - uint8_t* T = Util::SecAlloc(HASH_LEN); - uint8_t* hmacInput1 = Util::SecAlloc(infoLen + 1); - uint8_t* hmacInput = Util::SecAlloc(HASH_LEN + infoLen + 1); + uint8_t* T = util::SecAlloc(HASH_LEN); + uint8_t* hmacInput1 = util::SecAlloc(infoLen + 1); + uint8_t* hmacInput = util::SecAlloc(HASH_LEN + infoLen + 1); assert(N >= 1 && N <= 255); @@ -44,9 +45,9 @@ void HKDF256::Expand(uint8_t* okm, size_t L, const uint8_t* prk, const uint8_t* memcpy(okm + bytesWritten, T, to_write); bytesWritten += to_write; } - Util::SecFree(T); - Util::SecFree(hmacInput1); - Util::SecFree(hmacInput); + util::SecFree(T, HASH_LEN); + util::SecFree(hmacInput1, infoLen + 1); + util::SecFree(hmacInput, HASH_LEN + infoLen + 1); assert(bytesWritten == L); } @@ -54,9 +55,9 @@ void HKDF256::ExtractExpand(uint8_t* output, size_t outputLen, const uint8_t* key, size_t keyLen, const uint8_t* salt, size_t saltLen, const uint8_t* info, size_t infoLen) { - uint8_t* prk = Util::SecAlloc(HASH_LEN); + uint8_t* prk = util::SecAlloc(HASH_LEN); HKDF256::Extract(prk, salt, saltLen, key, keyLen); HKDF256::Expand(output, outputLen, prk, info, infoLen); - Util::SecFree(prk); + util::SecFree(prk, HASH_LEN); } } // namespace bls diff --git a/src/privatekey.cpp b/src/privatekey.cpp index 2a06fc898..c33b686cd 100644 --- a/src/privatekey.cpp +++ b/src/privatekey.cpp @@ -24,7 +24,7 @@ PrivateKey PrivateKey::FromSeedBIP32(const Bytes& seed) { const uint8_t hmacKey[] = {66, 76, 83, 32, 112, 114, 105, 118, 97, 116, 101, 32, 107, 101, 121, 32, 115, 101, 101, 100}; - auto* hash = Util::SecAlloc( + auto* hash = util::SecAlloc( PrivateKey::PRIVATE_KEY_SIZE); // Hash the seed into sk @@ -41,7 +41,7 @@ PrivateKey PrivateKey::FromSeedBIP32(const Bytes& seed) { PrivateKey k; bn_copy(k.keydata, skBn); - Util::SecFree(hash); + util::SecFree(hash, PrivateKey::PRIVATE_KEY_SIZE); return k; } @@ -112,7 +112,7 @@ PrivateKey::~PrivateKey() void PrivateKey::DeallocateKeyData() { if(keydata != nullptr) { - Util::SecFree(keydata); + util::SecFree(keydata, sizeof(bn_st)); keydata = nullptr; } InvalidateCaches(); @@ -145,11 +145,11 @@ const G1Element& PrivateKey::GetG1Element() const { if (!fG1CacheValid) { CheckKeyData(); - g1_st *p = Util::SecAlloc(1); + g1_st *p = util::SecAlloc(1); g1_mul_gen(p, keydata); g1Cache = G1Element::FromNative(p); - Util::SecFree(p); + util::SecFree(p, sizeof(g1_st)); fG1CacheValid = true; } return g1Cache; @@ -159,11 +159,11 @@ const G2Element& PrivateKey::GetG2Element() const { if (!fG2CacheValid) { CheckKeyData(); - g2_st *q = Util::SecAlloc(1); + g2_st *q = util::SecAlloc(1); g2_mul_gen(q, keydata); g2Cache = G2Element::FromNative(q); - Util::SecFree(q); + util::SecFree(q, sizeof(g2_st)); fG2CacheValid = true; } return g2Cache; @@ -177,11 +177,11 @@ bool PrivateKey::HasKeyData() const G1Element operator*(const G1Element &a, const PrivateKey &k) { k.CheckKeyData(); - g1_st* ans = Util::SecAlloc(1); + g1_st* ans = util::SecAlloc(1); a.ToNative(ans); g1_mul(ans, ans, k.keydata); G1Element ret = G1Element::FromNative(ans); - Util::SecFree(ans); + util::SecFree(ans, sizeof(g1_st)); return ret; } @@ -190,11 +190,11 @@ G1Element operator*(const PrivateKey &k, const G1Element &a) { return a * k; } G2Element operator*(const G2Element &a, const PrivateKey &k) { k.CheckKeyData(); - g2_st* ans = Util::SecAlloc(1); + g2_st* ans = util::SecAlloc(1); a.ToNative(ans); g2_mul(ans, ans, k.keydata); G2Element ret = G2Element::FromNative(ans); - Util::SecFree(ans); + util::SecFree(ans, sizeof(g2_st)); return ret; } @@ -217,12 +217,12 @@ PrivateKey operator*(const bn_t& a, const PrivateKey& k) { return a * k; } G2Element PrivateKey::GetG2Power(const G2Element& element) const { CheckKeyData(); - g2_st* q = Util::SecAlloc(1); + g2_st* q = util::SecAlloc(1); element.ToNative(q); g2_mul(q, q, keydata); const G2Element ret = G2Element::FromNative(q); - Util::SecFree(q); + util::SecFree(q, sizeof(g2_st)); return ret; } @@ -310,7 +310,7 @@ G2Element PrivateKey::SignG2( void PrivateKey::AllocateKeyData() { assert(!keydata); - keydata = Util::SecAlloc(1); + keydata = util::SecAlloc(1); keydata->alloc = RLC_BN_SIZE; bn_zero(keydata); } diff --git a/src/secure.cpp b/src/secure.cpp index fcd3e1769..37b1ec09f 100644 --- a/src/secure.cpp +++ b/src/secure.cpp @@ -5,10 +5,59 @@ #include "secure.h" #include "util.hpp" +#include +#include #include namespace bls { namespace util { +namespace { +Util::SecureAllocCallback g_pfnSecureAlloc{nullptr}; +Util::SecureFreeCallback g_pfnSecureFree{nullptr}; +} // anonymous namespace + +void SetSecureAllocator(Util::SecureAllocCallback pfnAlloc, + Util::SecureFreeCallback pfnFree) +{ + if (pfnAlloc == nullptr || pfnFree == nullptr) { + throw std::invalid_argument("secure allocator must not be null"); + } + g_pfnSecureAlloc = pfnAlloc; + g_pfnSecureFree = pfnFree; +} + +void GetSecureAllocator(Util::SecureAllocCallback* ppfnAlloc, + Util::SecureFreeCallback* ppfnFree) +{ + if (ppfnAlloc != nullptr) { + *ppfnAlloc = g_pfnSecureAlloc; + } + if (ppfnFree != nullptr) { + *ppfnFree = g_pfnSecureFree; + } +} + +void* SecMalloc(size_t nBytes) +{ + if (g_pfnSecureAlloc == nullptr) { + throw std::runtime_error("secure allocator used before BLS::Init"); + } + void* pAllocation = g_pfnSecureAlloc(nBytes); + if (pAllocation == nullptr) { + throw std::bad_alloc(); + } + return pAllocation; +} + +void SecFree(void* ptr, size_t nBytes) +{ + SecureWipe(ptr, nBytes); + if (g_pfnSecureFree == nullptr) { + return; + } + g_pfnSecureFree(ptr); +} + Bn::Bn() { bn_null(m_val); diff --git a/src/secure.h b/src/secure.h index d4b8fa5b4..ca4d9b17d 100644 --- a/src/secure.h +++ b/src/secure.h @@ -5,6 +5,7 @@ #ifndef DASHBLS_SECURE_H #define DASHBLS_SECURE_H +#include "util.hpp" #include "wipe.h" #include "relic_conf.h" @@ -18,6 +19,8 @@ extern "C" { } #include +#include +#include // BLS::Init refuses to run unless relic was built ALLOC=AUTO, which puts // bn_st's digits inline rather than behind a pointer. That inline storage is @@ -28,6 +31,64 @@ extern "C" { namespace bls { namespace util { +/** + * Names the allocator that SecMalloc and SecFree go through. + * + * pfnAlloc must return storage aligned for any type with alignment no greater + * than alignof(std::max_align_t). + * + * @param pfnAlloc Called to obtain memory, returning nullptr on failure. + * @param pfnFree Called to release what pfnAlloc returned. + */ +void SetSecureAllocator(Util::SecureAllocCallback pfnAlloc, + Util::SecureFreeCallback pfnFree); + +/** + * Reports the allocator currently installed. + * + * @param ppfnAlloc Receives the installed allocation callback. + * @param ppfnFree Receives the installed release callback. + */ +void GetSecureAllocator(Util::SecureAllocCallback* ppfnAlloc, + Util::SecureFreeCallback* ppfnFree); + +/** + * Allocates nBytes from the installed secure allocator. + * + * @param nBytes How many bytes to allocate. + * @returns The allocation, never nullptr. + * @throws std::bad_alloc if the allocator cannot satisfy the request. + */ +void* SecMalloc(size_t nBytes); + +/** + * Clears nBytes at ptr and returns it to the installed allocator. + * + * @param ptr An allocation from SecMalloc or SecAlloc, or nullptr. + * @param nBytes How much of it to clear, as handed to the allocation. + */ +void SecFree(void* ptr, size_t nBytes); + +/** + * Allocates storage for numTs objects, cleared when freed. + * + * @param numTs How many objects to make room for. + * @returns The allocation, never nullptr. + * @throws std::bad_alloc if numTs objects do not fit in a size_t, or if the + * allocator cannot satisfy the request. + */ +template +T* SecAlloc(size_t numTs) +{ + static_assert(alignof(T) <= alignof(std::max_align_t), + "SecAlloc cannot satisfy an over-aligned type"); + + if (numTs > std::numeric_limits::max() / sizeof(T)) { + throw std::bad_alloc(); + } + return static_cast(SecMalloc(sizeof(T) * numTs)); +} + /** * An owning relic bn_t that clears itself when destroyed. * diff --git a/src/test.cpp b/src/test.cpp index 0bc920edf..dfb28d3df 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -14,15 +14,19 @@ // limitations under the License. #define CATCH_CONFIG_RUNNER -#include #include "bls.hpp" +#include "secure.h" +#include "test-utils.hpp" + #include +#include + extern "C" { #include "relic.h" } -#include "test-utils.hpp" + using std::cout; using std::endl; using std::string; @@ -475,11 +479,11 @@ TEST_CASE("Error handling") { vector seed(32, 0x10); PrivateKey sk1 = BasicSchemeMPL().KeyGen(seed); - uint8_t* skData = Util::SecAlloc(G2Element::SIZE); + uint8_t* skData = util::SecAlloc(G2Element::SIZE); sk1.Serialize(skData); skData[0] = 255; REQUIRE_THROWS(PrivateKey::FromBytes(Bytes(skData, PrivateKey::PRIVATE_KEY_SIZE))); - Util::SecFree(skData); + util::SecFree(skData, G2Element::SIZE); } SECTION("Should throw on a bad public key") @@ -630,7 +634,7 @@ TEST_CASE("Signature tests") PrivateKey sk1 = BasicSchemeMPL().KeyGen(seed); G1Element pk1 = sk1.GetG1Element(); - uint8_t* skData = Util::SecAlloc(G2Element::SIZE); + uint8_t* skData = util::SecAlloc(G2Element::SIZE); sk1.Serialize(skData); PrivateKey sk2 = PrivateKey::FromBytes(Bytes(skData, PrivateKey::PRIVATE_KEY_SIZE)); REQUIRE(sk1 == sk2); @@ -648,7 +652,7 @@ TEST_CASE("Signature tests") REQUIRE(sig1 == sig2); REQUIRE(BasicSchemeMPL().Verify(pk2, message1, sig2)); - Util::SecFree(skData); + util::SecFree(skData, G2Element::SIZE); } SECTION("Should not verify aggregate with same message under BasicScheme") diff --git a/test/test_secure.cpp b/test/test_secure.cpp index a68f0ce96..929f1a9e1 100644 --- a/test/test_secure.cpp +++ b/test/test_secure.cpp @@ -2,16 +2,61 @@ // Distributed under the MIT software license, see the accompanying // file COPYING.MIT or https://opensource.org/license/MIT +#include "bls.hpp" #include "secure.h" #include #include #include +#include +#include +#include +#include #include +#include #include #include +namespace { +alignas(std::max_align_t) uint8_t g_arena[4096]; +size_t g_arenaUsed{0}; + +void* ArenaAlloc(size_t nBytes) +{ + constexpr size_t nAlign = alignof(std::max_align_t); + const size_t nStep = (nBytes + nAlign - 1) & ~(nAlign - 1); + if (nStep > sizeof(g_arena) - g_arenaUsed) { + return nullptr; + } + void* pBlock = g_arena + g_arenaUsed; + g_arenaUsed += nStep; + return pBlock; +} + +void ArenaFree(void*) {} + +struct ArenaGuard { + ArenaGuard() + { + bls::util::GetSecureAllocator(&m_pfnAlloc, &m_pfnFree); + g_arenaUsed = 0; + bls::util::SetSecureAllocator(ArenaAlloc, ArenaFree); + } + + ~ArenaGuard() + { + if (m_pfnAlloc != nullptr && m_pfnFree != nullptr) { + bls::util::SetSecureAllocator(m_pfnAlloc, m_pfnFree); + } + } + +private: + bls::Util::SecureAllocCallback m_pfnAlloc{nullptr}; + bls::Util::SecureFreeCallback m_pfnFree{nullptr}; +}; +} // anonymous namespace + TEST_CASE("util::SecureWipe clears a buffer") { std::array buf; @@ -74,3 +119,67 @@ TEST_CASE("A bignum has value semantics and clears itself") })); } } + +TEST_CASE("Secure allocation is safe to use from several threads") +{ + // The callbacks are reached from every thread that holds a key, so this is + // here to be run under a sanitizer rather than to assert an outcome. + std::vector threads; + + for (int i = 0; i < 4; ++i) { + threads.emplace_back([] { + for (int j = 0; j < 250; ++j) { + auto* p = bls::util::SecAlloc(64); + std::memset(p, 0x7e, 64); + bls::util::SecFree(p, 64); + } + }); + } + for (auto& thread : threads) { + thread.join(); + } + SUCCEED("no allocation raced another"); +} + +TEST_CASE("The secure allocator is never called through a null") +{ + REQUIRE_THROWS_AS(bls::util::SetSecureAllocator(nullptr, std::free), + std::invalid_argument); + REQUIRE_THROWS_AS(bls::util::SetSecureAllocator(std::malloc, nullptr), + std::invalid_argument); + + // Rejecting the bad pair must not have disturbed the installed one. + auto* p = bls::util::SecAlloc(32); + REQUIRE(p != nullptr); + bls::util::SecFree(p, 32); +} + +TEST_CASE("Secure allocation failure throws rather than returning null") +{ + // The raw SecAlloc callers do not check for null, so a count that does not + // fit in a size_t once scaled by the object size has to throw rather than + // wrap into a small allocation the caller then writes past. + REQUIRE_THROWS_AS(bls::util::SecAlloc( + std::numeric_limits::max() / 4), std::bad_alloc); + REQUIRE_THROWS_AS(bls::util::SecAlloc( + std::numeric_limits::max()), std::bad_alloc); +} + +TEST_CASE("A private key has value semantics and clears itself") +{ + SECTION("Destruction leaves nothing behind") + { + // The key's bn_st goes back through SecFree, which the installed + // allocator need not clear, so the extent has to travel with it. + std::memset(g_arena, 0xcd, sizeof(g_arena)); + { + ArenaGuard guard; + bls::PrivateKey sk = bls::PrivateKey::RandomPrivateKey(); + REQUIRE(std::any_of(g_arena, g_arena + sizeof(bn_st), + [](uint8_t b) { return b != 0 && b != 0xcd; })); + } + + REQUIRE(std::all_of(g_arena, g_arena + sizeof(bn_st), + [](uint8_t b) { return b == 0; })); + } +} From 46fc1b4768b7c979bc4ec61938ee2a83780ca52c Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 25 Aug 2026 21:43:50 +0530 Subject: [PATCH 12/15] feat: introduce `util::SecVector` for handling sensitive bytes --- src/extendedprivatekey.cpp | 60 +++++++++---------- src/extendedpublickey.cpp | 29 +++++---- src/hdkeys.cpp | 119 +++++++++++++++---------------------- src/hkdf.cpp | 31 +++++----- src/privatekey.cpp | 9 ++- src/secure.h | 50 ++++++++++++++++ test/test_secure.cpp | 37 ++++++++++++ 7 files changed, 200 insertions(+), 135 deletions(-) diff --git a/src/extendedprivatekey.cpp b/src/extendedprivatekey.cpp index 078a37a8d..8beb64265 100644 --- a/src/extendedprivatekey.cpp +++ b/src/extendedprivatekey.cpp @@ -22,36 +22,35 @@ ExtendedPrivateKey ExtendedPrivateKey::FromSeed(const Bytes& bytes) { // "BLS HD seed" in ascii const uint8_t prefix[] = {66, 76, 83, 32, 72, 68, 32, 115, 101, 101, 100}; - uint8_t* hashInput = util::SecAlloc(bytes.size() + 1); - std::memcpy(hashInput, bytes.begin(), bytes.size()); + util::SecVector hashInput(bytes.size() + 1); + std::memcpy(hashInput.data(), bytes.begin(), bytes.size()); // 32 bytes for secret key, and 32 bytes for chaincode - uint8_t* ILeft = util::SecAlloc( - PrivateKey::PRIVATE_KEY_SIZE); - uint8_t IRight[ChainCode::SIZE]; + util::SecVector ILeft(PrivateKey::PRIVATE_KEY_SIZE); + util::SecVector IRight(ChainCode::SIZE); // Hash the seed into 64 bytes, half will be sk, half will be cc hashInput[bytes.size()] = 0; - md_hmac(ILeft, hashInput, bytes.size() + 1, prefix, sizeof(prefix)); + md_hmac(ILeft.data(), hashInput.data(), bytes.size() + 1, prefix, + sizeof(prefix)); hashInput[bytes.size()] = 1; - md_hmac(IRight, hashInput, bytes.size() + 1, prefix, sizeof(prefix)); + md_hmac(IRight.data(), hashInput.data(), bytes.size() + 1, prefix, + sizeof(prefix)); // Make sure private key is less than the curve order util::Bn skBn; util::Bn order; g1_get_ord(order); - bn_read_bin(skBn, ILeft, PrivateKey::PRIVATE_KEY_SIZE); + bn_read_bin(skBn, ILeft.data(), PrivateKey::PRIVATE_KEY_SIZE); bn_mod_basic(skBn, skBn, order); - bn_write_bin(ILeft, PrivateKey::PRIVATE_KEY_SIZE, skBn); + bn_write_bin(ILeft.data(), PrivateKey::PRIVATE_KEY_SIZE, skBn); ExtendedPrivateKey esk(ExtendedPublicKey::REVISION, 0, 0, 0, - ChainCode::FromBytes(Bytes(IRight, ChainCode::SIZE)), - PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE))); + ChainCode::FromBytes(Bytes(IRight.data(), IRight.size())), + PrivateKey::FromBytes(Bytes(ILeft.data(), ILeft.size()))); - util::SecFree(ILeft, PrivateKey::PRIVATE_KEY_SIZE); - util::SecFree(hashInput, bytes.size() + 1); return esk; } @@ -77,48 +76,47 @@ ExtendedPrivateKey ExtendedPrivateKey::PrivateChild(uint32_t i, const bool fLega uint32_t cmp = (1 << 31); bool hardened = i >= cmp; - uint8_t* ILeft = util::SecAlloc(PrivateKey::PRIVATE_KEY_SIZE); - uint8_t IRight[ChainCode::SIZE]; + util::SecVector ILeft(PrivateKey::PRIVATE_KEY_SIZE); + util::SecVector IRight(ChainCode::SIZE); // Chain code is used as hmac key - uint8_t hmacKey[ChainCode::SIZE]; - chainCode.Serialize(hmacKey); + util::SecVector hmacKey(ChainCode::SIZE); + chainCode.Serialize(hmacKey.data()); size_t inputLen = hardened ? PrivateKey::PRIVATE_KEY_SIZE + 4 + 1 : G1Element::SIZE + 4 + 1; // Hmac input includes sk or pk, int i, and byte with 0 or 1 - uint8_t* hmacInput = util::SecAlloc(inputLen); + util::SecVector hmacInput(inputLen); // Fill the input with the required data if (hardened) { - sk.Serialize(hmacInput); - Util::IntToFourBytes(hmacInput + PrivateKey::PRIVATE_KEY_SIZE, i); + sk.Serialize(hmacInput.data()); + Util::IntToFourBytes(hmacInput.data() + PrivateKey::PRIVATE_KEY_SIZE, i); } else { - memcpy(hmacInput, sk.GetG1Element().Serialize(fLegacy).data(), G1Element::SIZE); - Util::IntToFourBytes(hmacInput + G1Element::SIZE, i); + memcpy(hmacInput.data(), sk.GetG1Element().Serialize(fLegacy).data(), + G1Element::SIZE); + Util::IntToFourBytes(hmacInput.data() + G1Element::SIZE, i); } hmacInput[inputLen - 1] = 0; - md_hmac(ILeft, hmacInput, inputLen, - hmacKey, ChainCode::SIZE); + md_hmac(ILeft.data(), hmacInput.data(), inputLen, + hmacKey.data(), ChainCode::SIZE); // Change 1 byte to generate a different sequence for chaincode hmacInput[inputLen - 1] = 1; - md_hmac(IRight, hmacInput, inputLen, - hmacKey, ChainCode::SIZE); + md_hmac(IRight.data(), hmacInput.data(), inputLen, + hmacKey.data(), ChainCode::SIZE); - PrivateKey newSk = PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE), true); + PrivateKey newSk = + PrivateKey::FromBytes(Bytes(ILeft.data(), ILeft.size()), true); newSk = PrivateKey::Aggregate({sk, newSk}); ExtendedPrivateKey esk(version, depth + 1, sk.GetG1Element().GetFingerprint(), i, - ChainCode::FromBytes(Bytes(IRight, ChainCode::SIZE)), + ChainCode::FromBytes(Bytes(IRight.data(), IRight.size())), newSk); - util::SecFree(ILeft, PrivateKey::PRIVATE_KEY_SIZE); - util::SecFree(hmacInput, inputLen); - return esk; } diff --git a/src/extendedpublickey.cpp b/src/extendedpublickey.cpp index a484dea64..f9dc698fc 100644 --- a/src/extendedpublickey.cpp +++ b/src/extendedpublickey.cpp @@ -13,6 +13,7 @@ // limitations under the License. #include "bls.hpp" +#include "secure.h" namespace bls { @@ -39,41 +40,43 @@ ExtendedPublicKey ExtendedPublicKey::PublicChild(uint32_t i, const bool fLegacy) if (depth >= 255) { throw std::logic_error("Cannot go further than 255 levels"); } - uint8_t ILeft[PrivateKey::PRIVATE_KEY_SIZE]; - uint8_t IRight[ChainCode::SIZE]; + util::SecVector ILeft(PrivateKey::PRIVATE_KEY_SIZE); + util::SecVector IRight(ChainCode::SIZE); // Chain code is used as hmac key - uint8_t hmacKey[ChainCode::SIZE]; - chainCode.Serialize(hmacKey); + util::SecVector hmacKey(ChainCode::SIZE); + chainCode.Serialize(hmacKey.data()); // Public key serialization, i serialization, and one 0 or 1 byte size_t inputLen = G1Element::SIZE + 4 + 1; // Hmac input includes sk or pk, int i, and byte with 0 or 1 - uint8_t hmacInput[G1Element::SIZE + 4 + 1]; + util::SecVector hmacInput(inputLen); // Fill the input with the required data auto vecG1 = pk.Serialize(fLegacy); - memcpy(hmacInput, vecG1.data(), vecG1.size()); + memcpy(hmacInput.data(), vecG1.data(), vecG1.size()); hmacInput[inputLen - 1] = 0; - Util::IntToFourBytes(hmacInput + G1Element::SIZE, i); + Util::IntToFourBytes(hmacInput.data() + G1Element::SIZE, i); - md_hmac(ILeft, hmacInput, inputLen, - hmacKey, ChainCode::SIZE); + md_hmac(ILeft.data(), hmacInput.data(), inputLen, + hmacKey.data(), ChainCode::SIZE); // Change 1 byte to generate a different sequence for chaincode hmacInput[inputLen - 1] = 1; - md_hmac(IRight, hmacInput, inputLen, - hmacKey, ChainCode::SIZE); + md_hmac(IRight.data(), hmacInput.data(), inputLen, + hmacKey.data(), ChainCode::SIZE); - PrivateKey leftSk = PrivateKey::FromBytes(Bytes(ILeft, PrivateKey::PRIVATE_KEY_SIZE), true); + PrivateKey leftSk = + PrivateKey::FromBytes(Bytes(ILeft.data(), ILeft.size()), true); G1Element newPk = pk + leftSk.GetG1Element(); ExtendedPublicKey epk(version, depth + 1, GetPublicKey().GetFingerprint(), i, - ChainCode::FromBytes(Bytes(IRight, ChainCode::SIZE)), + ChainCode::FromBytes( + Bytes(IRight.data(), IRight.size())), newPk); return epk; diff --git a/src/hdkeys.cpp b/src/hdkeys.cpp index 8a97692b4..8f7fd0fbe 100644 --- a/src/hdkeys.cpp +++ b/src/hdkeys.cpp @@ -31,15 +31,14 @@ PrivateKey HDKeys::KeyGen(const Bytes& seed) const uint8_t saltHkdf[20] = {66, 76, 83, 45, 83, 73, 71, 45, 75, 69, 89, 71, 69, 78, 45, 83, 65, 76, 84, 45}; - uint8_t *prk = util::SecAlloc(32); - uint8_t *ikmHkdf = util::SecAlloc(seed.size() + 1); - memcpy(ikmHkdf, seed.begin(), seed.size()); + util::SecVector ikmHkdf(seed.size() + 1); + memcpy(ikmHkdf.data(), seed.begin(), seed.size()); ikmHkdf[seed.size()] = 0; const uint8_t L = 48; // `ceil((3 * ceil(log2(r))) / 16)`, where `r` is the // order of the BLS 12-381 curve - uint8_t *okmHkdf = util::SecAlloc(L); + util::SecVector okmHkdf(L); uint8_t keyInfoHkdf[infoLen + 2]; memcpy(keyInfoHkdf, info, infoLen); @@ -47,9 +46,9 @@ PrivateKey HDKeys::KeyGen(const Bytes& seed) keyInfoHkdf[infoLen + 1] = L; HKDF256::ExtractExpand( - okmHkdf, + okmHkdf.data(), L, - ikmHkdf, + ikmHkdf.data(), seed.size() + 1, saltHkdf, 20, @@ -61,17 +60,12 @@ PrivateKey HDKeys::KeyGen(const Bytes& seed) // Make sure private key is less than the curve order util::Bn skBn; - bn_read_bin(skBn, okmHkdf, L); + bn_read_bin(skBn, okmHkdf.data(), L); bn_mod_basic(skBn, skBn, order); - uint8_t *skBytes = util::SecAlloc(32); - bn_write_bin(skBytes, 32, skBn); - PrivateKey k = PrivateKey::FromBytes(Bytes(skBytes, 32)); - - util::SecFree(prk, 32); - util::SecFree(ikmHkdf, seed.size() + 1); - util::SecFree(okmHkdf, L); - util::SecFree(skBytes, 32); + util::SecVector skBytes(32); + bn_write_bin(skBytes.data(), 32, skBn); + PrivateKey k = PrivateKey::FromBytes(Bytes(skBytes.data(), 32)); return k; } @@ -83,101 +77,86 @@ void HDKeys::IKMToLamportSk(uint8_t* outputLamportSk, const uint8_t* ikm, size_t } void HDKeys::ParentSkToLamportPK(uint8_t* outputLamportPk, const PrivateKey& parentSk, uint32_t index) { - uint8_t* salt = util::SecAlloc(4); - uint8_t* ikm = util::SecAlloc(HASH_LEN); - uint8_t* notIkm = util::SecAlloc(HASH_LEN); - uint8_t* lamport0 = util::SecAlloc(HASH_LEN * 255); - uint8_t* lamport1 = util::SecAlloc(HASH_LEN * 255); + util::SecVector salt(4); + util::SecVector ikm(HASH_LEN); + util::SecVector notIkm(HASH_LEN); + util::SecVector lamport0(HASH_LEN * 255); + util::SecVector lamport1(HASH_LEN * 255); - Util::IntToFourBytes(salt, index); - parentSk.Serialize(ikm); + Util::IntToFourBytes(salt.data(), index); + parentSk.Serialize(ikm.data()); for (size_t i = 0; i < HASH_LEN; i++) { // Flips the bits notIkm[i] = ikm[i] ^ 0xff; } - HDKeys::IKMToLamportSk(lamport0, ikm, HASH_LEN, salt, 4); - HDKeys::IKMToLamportSk(lamport1, notIkm, HASH_LEN, salt, 4); + HDKeys::IKMToLamportSk(lamport0.data(), ikm.data(), HASH_LEN, salt.data(), 4); + HDKeys::IKMToLamportSk( + lamport1.data(), notIkm.data(), HASH_LEN, salt.data(), 4); - uint8_t* lamportPk = util::SecAlloc(HASH_LEN * 255 * 2); + util::SecVector lamportPk(HASH_LEN * 255 * 2); for (size_t i = 0; i < 255; i++) { - Util::Hash256(lamportPk + i * HASH_LEN, lamport0 + i * HASH_LEN, HASH_LEN); + Util::Hash256(lamportPk.data() + i * HASH_LEN, + lamport0.data() + i * HASH_LEN, HASH_LEN); } for (size_t i=0; i < 255; i++) { - Util::Hash256(lamportPk + 255 * HASH_LEN + i * HASH_LEN, lamport1 + i * HASH_LEN, HASH_LEN); + Util::Hash256(lamportPk.data() + 255 * HASH_LEN + i * HASH_LEN, + lamport1.data() + i * HASH_LEN, HASH_LEN); } - Util::Hash256(outputLamportPk, lamportPk, HASH_LEN * 255 * 2); - - util::SecFree(salt, 4); - util::SecFree(ikm, HASH_LEN); - util::SecFree(notIkm, HASH_LEN); - util::SecFree(lamport0, HASH_LEN * 255); - util::SecFree(lamport1, HASH_LEN * 255); - util::SecFree(lamportPk, HASH_LEN * 255 * 2); + Util::Hash256(outputLamportPk, lamportPk.data(), HASH_LEN * 255 * 2); } PrivateKey HDKeys::DeriveChildSk(const PrivateKey& parentSk, uint32_t index) { - uint8_t* lamportPk = util::SecAlloc(HASH_LEN); - HDKeys::ParentSkToLamportPK(lamportPk, parentSk, index); - std::vector lamportPkVector(lamportPk, lamportPk + HASH_LEN); - PrivateKey child = HDKeys::KeyGen(lamportPkVector); - util::SecFree(lamportPk, HASH_LEN); - return child; + util::SecVector lamportPk(HASH_LEN); + HDKeys::ParentSkToLamportPK(lamportPk.data(), parentSk, index); + return HDKeys::KeyGen(Bytes(lamportPk.data(), lamportPk.size())); } PrivateKey HDKeys::DeriveChildSkUnhardened(const PrivateKey& parentSk, uint32_t index) { - uint8_t* buf = util::SecAlloc(G1Element::SIZE + 4); - uint8_t* digest = util::SecAlloc(HASH_LEN); - memcpy(buf, parentSk.GetG1Element().Serialize().data(), G1Element::SIZE); - Util::IntToFourBytes(buf + G1Element::SIZE, index); - Util::Hash256(digest, buf, G1Element::SIZE + 4); - - PrivateKey ret = PrivateKey::Aggregate({parentSk, PrivateKey::FromBytes(Bytes(digest, HASH_LEN), true)}); - - util::SecFree(buf, G1Element::SIZE + 4); - util::SecFree(digest, HASH_LEN); - return ret; + util::SecVector buf(G1Element::SIZE + 4); + util::SecVector digest(HASH_LEN); + memcpy(buf.data(), parentSk.GetG1Element().Serialize().data(), + G1Element::SIZE); + Util::IntToFourBytes(buf.data() + G1Element::SIZE, index); + Util::Hash256(digest.data(), buf.data(), G1Element::SIZE + 4); + + return PrivateKey::Aggregate( + {parentSk, PrivateKey::FromBytes(Bytes(digest.data(), HASH_LEN), true)}); } G1Element HDKeys::DeriveChildG1Unhardened(const G1Element& pk, uint32_t index) { - uint8_t* buf = util::SecAlloc(G1Element::SIZE + 4); - uint8_t* digest = util::SecAlloc(HASH_LEN); - memcpy(buf, pk.Serialize().data(), G1Element::SIZE); + util::SecVector buf(G1Element::SIZE + 4); + util::SecVector digest(HASH_LEN); + memcpy(buf.data(), pk.Serialize().data(), G1Element::SIZE); - Util::IntToFourBytes(buf + G1Element::SIZE, index); - Util::Hash256(digest, buf, G1Element::SIZE + 4); + Util::IntToFourBytes(buf.data() + G1Element::SIZE, index); + Util::Hash256(digest.data(), buf.data(), G1Element::SIZE + 4); util::Bn nonce; util::Bn ord; - bn_read_bin(nonce, digest, HASH_LEN); + bn_read_bin(nonce, digest.data(), HASH_LEN); g1_get_ord(ord); bn_mod_basic(nonce, nonce, ord); - util::SecFree(buf, G1Element::SIZE + 4); - util::SecFree(digest, HASH_LEN); - G1Element gen = G1Element::Generator(); return pk + gen * nonce; } G2Element HDKeys::DeriveChildG2Unhardened(const G2Element& pk, uint32_t index) { - uint8_t* buf = util::SecAlloc(G2Element::SIZE + 4); - uint8_t* digest = util::SecAlloc(HASH_LEN); - memcpy(buf, pk.Serialize().data(), G2Element::SIZE); - Util::IntToFourBytes(buf + G2Element::SIZE, index); - Util::Hash256(digest, buf, G2Element::SIZE + 4); + util::SecVector buf(G2Element::SIZE + 4); + util::SecVector digest(HASH_LEN); + memcpy(buf.data(), pk.Serialize().data(), G2Element::SIZE); + Util::IntToFourBytes(buf.data() + G2Element::SIZE, index); + Util::Hash256(digest.data(), buf.data(), G2Element::SIZE + 4); util::Bn nonce; util::Bn ord; - bn_read_bin(nonce, digest, HASH_LEN); + bn_read_bin(nonce, digest.data(), HASH_LEN); g1_get_ord(ord); bn_mod_basic(nonce, nonce, ord); - util::SecFree(buf, G2Element::SIZE + 4); - util::SecFree(digest, HASH_LEN); - G2Element gen = G2Element::Generator(); return pk + gen * nonce; } diff --git a/src/hkdf.cpp b/src/hkdf.cpp index 19adde0c4..4a0a40f63 100644 --- a/src/hkdf.cpp +++ b/src/hkdf.cpp @@ -5,6 +5,8 @@ #include "hkdf.hpp" #include "secure.h" +#include "secure.h" + namespace bls { void HKDF256::Extract(uint8_t* prk_output, const uint8_t* salt, const size_t saltLen, const uint8_t* ikm, const size_t ikm_len) { // assert(saltLen == 4); // Used for EIP2333 key derivation @@ -20,34 +22,32 @@ void HKDF256::Expand(uint8_t* okm, size_t L, const uint8_t* prk, const uint8_t* size_t N = (L + HASH_LEN - 1) / HASH_LEN; // Round up size_t bytesWritten = 0; - uint8_t* T = util::SecAlloc(HASH_LEN); - uint8_t* hmacInput1 = util::SecAlloc(infoLen + 1); - uint8_t* hmacInput = util::SecAlloc(HASH_LEN + infoLen + 1); + util::SecVector T(HASH_LEN); + util::SecVector hmacInput1(infoLen + 1); + util::SecVector hmacInput(HASH_LEN + infoLen + 1); assert(N >= 1 && N <= 255); for (size_t i = 1; i <= N; i++) { if (i == 1) { - memcpy(hmacInput1, info, infoLen); + memcpy(hmacInput1.data(), info, infoLen); hmacInput1[infoLen] = i; - md_hmac(T, hmacInput1, infoLen + 1, prk, HASH_LEN); + md_hmac(T.data(), hmacInput1.data(), infoLen + 1, prk, HASH_LEN); } else { - memcpy(hmacInput, T, HASH_LEN); - memcpy(hmacInput + HASH_LEN, info, infoLen); + memcpy(hmacInput.data(), T.data(), HASH_LEN); + memcpy(hmacInput.data() + HASH_LEN, info, infoLen); hmacInput[HASH_LEN + infoLen] = i; - md_hmac(T, hmacInput, HASH_LEN + infoLen + 1, prk, HASH_LEN); + md_hmac(T.data(), hmacInput.data(), HASH_LEN + infoLen + 1, prk, + HASH_LEN); } size_t to_write = L - bytesWritten; if (to_write > HASH_LEN) { to_write = HASH_LEN; } assert (to_write > 0 && to_write <= HASH_LEN); - memcpy(okm + bytesWritten, T, to_write); + memcpy(okm + bytesWritten, T.data(), to_write); bytesWritten += to_write; } - util::SecFree(T, HASH_LEN); - util::SecFree(hmacInput1, infoLen + 1); - util::SecFree(hmacInput, HASH_LEN + infoLen + 1); assert(bytesWritten == L); } @@ -55,9 +55,8 @@ void HKDF256::ExtractExpand(uint8_t* output, size_t outputLen, const uint8_t* key, size_t keyLen, const uint8_t* salt, size_t saltLen, const uint8_t* info, size_t infoLen) { - uint8_t* prk = util::SecAlloc(HASH_LEN); - HKDF256::Extract(prk, salt, saltLen, key, keyLen); - HKDF256::Expand(output, outputLen, prk, info, infoLen); - util::SecFree(prk, HASH_LEN); + util::SecVector prk(HASH_LEN); + HKDF256::Extract(prk.data(), salt, saltLen, key, keyLen); + HKDF256::Expand(output, outputLen, prk.data(), info, infoLen); } } // namespace bls diff --git a/src/privatekey.cpp b/src/privatekey.cpp index c33b686cd..08dbf581b 100644 --- a/src/privatekey.cpp +++ b/src/privatekey.cpp @@ -24,24 +24,23 @@ PrivateKey PrivateKey::FromSeedBIP32(const Bytes& seed) { const uint8_t hmacKey[] = {66, 76, 83, 32, 112, 114, 105, 118, 97, 116, 101, 32, 107, 101, 121, 32, 115, 101, 101, 100}; - auto* hash = util::SecAlloc( - PrivateKey::PRIVATE_KEY_SIZE); + util::SecVector hash(PrivateKey::PRIVATE_KEY_SIZE); // Hash the seed into sk - md_hmac(hash, seed.begin(), (int)seed.size(), hmacKey, sizeof(hmacKey)); + md_hmac(hash.data(), seed.begin(), (int)seed.size(), hmacKey, + sizeof(hmacKey)); util::Bn order; g1_get_ord(order); // Make sure private key is less than the curve order util::Bn skBn; - bn_read_bin(skBn, hash, PrivateKey::PRIVATE_KEY_SIZE); + bn_read_bin(skBn, hash.data(), PrivateKey::PRIVATE_KEY_SIZE); bn_mod_basic(skBn, skBn, order); PrivateKey k; bn_copy(k.keydata, skBn); - util::SecFree(hash, PrivateKey::PRIVATE_KEY_SIZE); return k; } diff --git a/src/secure.h b/src/secure.h index ca4d9b17d..745d1321f 100644 --- a/src/secure.h +++ b/src/secure.h @@ -18,9 +18,12 @@ extern "C" { #include "relic.h" } +#include "util.hpp" + #include #include #include +#include // BLS::Init refuses to run unless relic was built ALLOC=AUTO, which puts // bn_st's digits inline rather than behind a pointer. That inline storage is @@ -89,6 +92,53 @@ T* SecAlloc(size_t numTs) return static_cast(SecMalloc(sizeof(T) * numTs)); } +/** + * An allocator over the library's secure allocation. + */ +template +struct SecureAllocator { + using value_type = T; + + SecureAllocator() = default; + template + SecureAllocator(const SecureAllocator&) noexcept {} + + /** + * Allocates storage for n objects. + * + * @param n How many objects to make room for. + * @returns Storage for n objects, cleared when deallocated. + * @throws std::bad_alloc if n objects do not fit in a size_t, or if the + * pool cannot satisfy the request. + */ + T* allocate(size_t n) { return SecAlloc(n); } + + /** + * Returns storage from allocate, clearing it on the way out. + * + * @param p Storage from a previous allocate. + * @param n The count that allocate was given, so the bytes can be + * cleared before release rather than left to the allocator. + */ + void deallocate(T* p, size_t n) { SecFree(p, n * sizeof(T)); } + + template + friend bool operator==( + const SecureAllocator&, const SecureAllocator&) noexcept + { + return true; + } + template + friend bool operator!=( + const SecureAllocator&, const SecureAllocator&) noexcept + { + return false; + } +}; + +template +using SecVector = std::vector>; + /** * An owning relic bn_t that clears itself when destroyed. * diff --git a/test/test_secure.cpp b/test/test_secure.cpp index 929f1a9e1..3aee44dc3 100644 --- a/test/test_secure.cpp +++ b/test/test_secure.cpp @@ -183,3 +183,40 @@ TEST_CASE("A private key has value semantics and clears itself") [](uint8_t b) { return b == 0; })); } } + +TEST_CASE("util::SecVector allocates through the secure allocator") +{ + static_assert(std::is_same::allocator_type, + bls::util::SecureAllocator>::value, + "SecVector has to carry the secure allocator"); + + // Growth reallocates, so this walks allocate and deallocate rather than + // only the one reservation a sized construction would make. + bls::util::SecVector words; + for (uint32_t i = 0; i < 64; ++i) { + words.push_back(i); + } + + REQUIRE(words.size() == 64); + REQUIRE(words.front() == 0); + REQUIRE(words.back() == 63); +} + +TEST_CASE("Secure release clears the bytes before handing them back") +{ + SECTION("util::SecVector, through the allocator") + { + std::memset(g_arena, 0xcd, sizeof(g_arena)); + { + ArenaGuard guard; + bls::util::SecVector secret(64); + std::fill(secret.begin(), secret.end(), 0xab); + } + REQUIRE(std::none_of(g_arena, g_arena + 64, [](uint8_t b) { + return b == 0xab; + })); + REQUIRE(std::all_of(g_arena, g_arena + 64, [](uint8_t b) { + return b == 0; + })); + } +} From 099073d2b1a3e6709dc0d45eae817e9229d44bad Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Wed, 19 Aug 2026 17:45:08 +0530 Subject: [PATCH 13/15] feat: introduce `util::SecPtr` for holding temporaries --- binds/python/pythonbindings.cpp | 25 +++++------ go-bindings/privatekey.cpp | 9 ++-- .../c-bindings/bip32/extendedprivatekey.cpp | 9 ++-- .../bls-dash-sys/c-bindings/privatekey.cpp | 9 ++-- src/privatekey.cpp | 44 ++++++++----------- src/secure.h | 38 ++++++++++++++++ test/test_secure.cpp | 16 +++++++ 7 files changed, 95 insertions(+), 55 deletions(-) diff --git a/binds/python/pythonbindings.cpp b/binds/python/pythonbindings.cpp index 333ab70ad..7c1c5f2f9 100644 --- a/binds/python/pythonbindings.cpp +++ b/binds/python/pythonbindings.cpp @@ -162,17 +162,15 @@ PYBIND11_MODULE(dashbls, m) .def( "__bytes__", [](const PrivateKey &k) { - uint8_t *output = - util::SecAlloc(PrivateKey::PRIVATE_KEY_SIZE); + util::SecPtr output = + util::SecMake(PrivateKey::PRIVATE_KEY_SIZE); { RelicGuard guard; - k.Serialize(output); + k.Serialize(output.get()); } - py::bytes ret = py::bytes( - reinterpret_cast(output), + return py::bytes( + reinterpret_cast(output.get()), PrivateKey::PRIVATE_KEY_SIZE); - util::SecFree(output); - return ret; }) .def( "__deepcopy__", @@ -189,13 +187,12 @@ PYBIND11_MODULE(dashbls, m) .def(py::self != py::self, py::call_guard()) .def("__repr__", [](const PrivateKey &k) { RelicGuard guard; - uint8_t *output = util::SecAlloc(PrivateKey::PRIVATE_KEY_SIZE); - k.Serialize(output); - std::string ret = - ""; - util::SecFree(output); - return ret; + util::SecPtr output = + util::SecMake(PrivateKey::PRIVATE_KEY_SIZE); + k.Serialize(output.get()); + return ""; }); py::class_(m, "Util").def("hash256", [](const py::bytes &message) { diff --git a/go-bindings/privatekey.cpp b/go-bindings/privatekey.cpp index f436cec8f..82e3d3702 100644 --- a/go-bindings/privatekey.cpp +++ b/go-bindings/privatekey.cpp @@ -49,13 +49,12 @@ CPrivateKey CPrivateKeyAggregate(void** sks, const size_t len) { void* CPrivateKeySerialize(const CPrivateKey sk) { const bls::PrivateKey* skPtr = (bls::PrivateKey*)sk; - uint8_t* buffer = nullptr; try { - buffer = bls::util::SecAlloc(bls::PrivateKey::PRIVATE_KEY_SIZE); - skPtr->Serialize(buffer); - return (void*)buffer; + bls::util::SecPtr buffer = + bls::util::SecMake(bls::PrivateKey::PRIVATE_KEY_SIZE); + skPtr->Serialize(buffer.get()); + return (void*)buffer.release(); } catch (const std::exception& ex) { - bls::util::SecFree(buffer, bls::PrivateKey::PRIVATE_KEY_SIZE); gErrMsg = ex.what(); return nullptr; } diff --git a/rust-bindings/bls-dash-sys/c-bindings/bip32/extendedprivatekey.cpp b/rust-bindings/bls-dash-sys/c-bindings/bip32/extendedprivatekey.cpp index 945138be9..d73221354 100644 --- a/rust-bindings/bls-dash-sys/c-bindings/bip32/extendedprivatekey.cpp +++ b/rust-bindings/bls-dash-sys/c-bindings/bip32/extendedprivatekey.cpp @@ -65,13 +65,12 @@ BIP32ChainCode BIP32ExtendedPrivateKeyGetChainCode(const BIP32ExtendedPrivateKey void* BIP32ExtendedPrivateKeySerialize(const BIP32ExtendedPrivateKey sk) { const bls::ExtendedPrivateKey* skPtr = (bls::ExtendedPrivateKey*)sk; - uint8_t* buffer = nullptr; try { - buffer = bls::util::SecAlloc(bls::ExtendedPrivateKey::SIZE); - skPtr->Serialize(buffer); - return (void*)buffer; + bls::util::SecPtr buffer = + bls::util::SecMake(bls::ExtendedPrivateKey::SIZE); + skPtr->Serialize(buffer.get()); + return (void*)buffer.release(); } catch (const std::exception& ex) { - bls::util::SecFree(buffer, bls::ExtendedPrivateKey::SIZE); gErrMsg = ex.what(); return nullptr; } diff --git a/rust-bindings/bls-dash-sys/c-bindings/privatekey.cpp b/rust-bindings/bls-dash-sys/c-bindings/privatekey.cpp index 5bcc6f70b..396e38f26 100644 --- a/rust-bindings/bls-dash-sys/c-bindings/privatekey.cpp +++ b/rust-bindings/bls-dash-sys/c-bindings/privatekey.cpp @@ -55,13 +55,12 @@ PrivateKey PrivateKeyAggregate(void** sks, const size_t len) { void* PrivateKeySerialize(const PrivateKey sk) { const bls::PrivateKey* skPtr = (bls::PrivateKey*)sk; - uint8_t* buffer = nullptr; try { - buffer = bls::util::SecAlloc(bls::PrivateKey::PRIVATE_KEY_SIZE); - skPtr->Serialize(buffer); - return (void*)buffer; + bls::util::SecPtr buffer = + bls::util::SecMake(bls::PrivateKey::PRIVATE_KEY_SIZE); + skPtr->Serialize(buffer.get()); + return (void*)buffer.release(); } catch (const std::exception& ex) { - bls::util::SecFree(buffer, bls::PrivateKey::PRIVATE_KEY_SIZE); gErrMsg = ex.what(); return nullptr; } diff --git a/src/privatekey.cpp b/src/privatekey.cpp index 08dbf581b..9a0b107ac 100644 --- a/src/privatekey.cpp +++ b/src/privatekey.cpp @@ -144,11 +144,10 @@ const G1Element& PrivateKey::GetG1Element() const { if (!fG1CacheValid) { CheckKeyData(); - g1_st *p = util::SecAlloc(1); - g1_mul_gen(p, keydata); + util::SecPtr p = util::SecMake(); + g1_mul_gen(p.get(), keydata); - g1Cache = G1Element::FromNative(p); - util::SecFree(p, sizeof(g1_st)); + g1Cache = G1Element::FromNative(p.get()); fG1CacheValid = true; } return g1Cache; @@ -158,11 +157,10 @@ const G2Element& PrivateKey::GetG2Element() const { if (!fG2CacheValid) { CheckKeyData(); - g2_st *q = util::SecAlloc(1); - g2_mul_gen(q, keydata); + util::SecPtr q = util::SecMake(); + g2_mul_gen(q.get(), keydata); - g2Cache = G2Element::FromNative(q); - util::SecFree(q, sizeof(g2_st)); + g2Cache = G2Element::FromNative(q.get()); fG2CacheValid = true; } return g2Cache; @@ -176,12 +174,10 @@ bool PrivateKey::HasKeyData() const G1Element operator*(const G1Element &a, const PrivateKey &k) { k.CheckKeyData(); - g1_st* ans = util::SecAlloc(1); - a.ToNative(ans); - g1_mul(ans, ans, k.keydata); - G1Element ret = G1Element::FromNative(ans); - util::SecFree(ans, sizeof(g1_st)); - return ret; + util::SecPtr ans = util::SecMake(); + a.ToNative(ans.get()); + g1_mul(ans.get(), ans.get(), k.keydata); + return G1Element::FromNative(ans.get()); } G1Element operator*(const PrivateKey &k, const G1Element &a) { return a * k; } @@ -189,12 +185,10 @@ G1Element operator*(const PrivateKey &k, const G1Element &a) { return a * k; } G2Element operator*(const G2Element &a, const PrivateKey &k) { k.CheckKeyData(); - g2_st* ans = util::SecAlloc(1); - a.ToNative(ans); - g2_mul(ans, ans, k.keydata); - G2Element ret = G2Element::FromNative(ans); - util::SecFree(ans, sizeof(g2_st)); - return ret; + util::SecPtr ans = util::SecMake(); + a.ToNative(ans.get()); + g2_mul(ans.get(), ans.get(), k.keydata); + return G2Element::FromNative(ans.get()); } G2Element operator*(const PrivateKey &k, const G2Element &a) { return a * k; } @@ -216,13 +210,11 @@ PrivateKey operator*(const bn_t& a, const PrivateKey& k) { return a * k; } G2Element PrivateKey::GetG2Power(const G2Element& element) const { CheckKeyData(); - g2_st* q = util::SecAlloc(1); - element.ToNative(q); - g2_mul(q, q, keydata); + util::SecPtr q = util::SecMake(); + element.ToNative(q.get()); + g2_mul(q.get(), q.get(), keydata); - const G2Element ret = G2Element::FromNative(q); - util::SecFree(q, sizeof(g2_st)); - return ret; + return G2Element::FromNative(q.get()); } PrivateKey PrivateKey::Aggregate(std::vector const &privateKeys) diff --git a/src/secure.h b/src/secure.h index 745d1321f..6fec4776a 100644 --- a/src/secure.h +++ b/src/secure.h @@ -22,6 +22,7 @@ extern "C" { #include #include +#include #include #include @@ -139,6 +140,43 @@ struct SecureAllocator { template using SecVector = std::vector>; +/** + * Returns storage from SecAlloc once it is no longer needed. + * + * @param ptr Storage from SecAlloc, or nullptr. + */ +template +struct SecDeleter { + explicit SecDeleter(size_t nCountIn) : nCount(nCountIn) {} + + void operator()(T* ptr) const { SecFree(ptr, nCount * sizeof(T)); } + +private: + size_t nCount; +}; + +/** + * Owns storage from SecAlloc, so a throw does not leak it. + * + * The storage is not constructed or destroyed, only cleared when released, + * which suits the relic structures it is used for. + */ +template +using SecPtr = std::unique_ptr>; + +/** + * Allocates owned storage for numTs objects. + * + * @param numTs How many objects to make room for. + * @returns Storage for numTs objects, cleared when it goes out of scope. + * @throws std::bad_alloc as SecAlloc does. + */ +template +SecPtr SecMake(size_t numTs = 1) +{ + return SecPtr(SecAlloc(numTs), SecDeleter{numTs}); +} + /** * An owning relic bn_t that clears itself when destroyed. * diff --git a/test/test_secure.cpp b/test/test_secure.cpp index 3aee44dc3..e2098157f 100644 --- a/test/test_secure.cpp +++ b/test/test_secure.cpp @@ -219,4 +219,20 @@ TEST_CASE("Secure release clears the bytes before handing them back") return b == 0; })); } + + SECTION("util::SecPtr, through the deleter") + { + std::memset(g_arena, 0xcd, sizeof(g_arena)); + { + ArenaGuard guard; + bls::util::SecPtr secret = + bls::util::SecMake(16); + std::fill(secret.get(), secret.get() + 16, 0xabababab); + } + // The whole array, not just its first object, which a deleter holding + // no count could not have reached. + REQUIRE(std::all_of(g_arena, g_arena + 16 * sizeof(uint32_t), + [](uint8_t b) { return b == 0; })); + REQUIRE(g_arena[16 * sizeof(uint32_t)] == 0xcd); + } } From 1d8bc7f5d98271b5446be384e416fc5e7d57a0f8 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Wed, 26 Aug 2026 19:04:25 +0530 Subject: [PATCH 14/15] fix: clear `ChainCode` and give it value semantics Co-authored-by: pasta --- include/dashbls/chaincode.hpp | 4 +++- src/chaincode.cpp | 28 ++++++++++++++++++----- test/test_secure.cpp | 42 +++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/include/dashbls/chaincode.hpp b/include/dashbls/chaincode.hpp index bd3e57b4d..8b643a451 100644 --- a/include/dashbls/chaincode.hpp +++ b/include/dashbls/chaincode.hpp @@ -38,6 +38,8 @@ class ChainCode { static ChainCode FromBytes(const Bytes& bytes); ChainCode(const ChainCode &cc); + ChainCode& operator=(const ChainCode &cc); + ~ChainCode(); // Comparator implementation. friend bool operator==(ChainCode const &a, ChainCode const &b); @@ -48,7 +50,7 @@ class ChainCode { std::vector Serialize() const; // Prevent direct construction, use static constructor - ChainCode() {} + ChainCode(); private: bn_t chainCode; diff --git a/src/chaincode.cpp b/src/chaincode.cpp index efacdc95a..68bb83d87 100644 --- a/src/chaincode.cpp +++ b/src/chaincode.cpp @@ -13,6 +13,7 @@ // limitations under the License. #include "bls.hpp" +#include "secure.h" namespace bls { @@ -20,17 +21,32 @@ ChainCode ChainCode::FromBytes(const Bytes& bytes) { if (bytes.size() != ChainCode::SIZE) { throw std::invalid_argument("ChainCode::FromBytes: Invalid size"); } - ChainCode c = ChainCode(); - bn_new(c.chainCode); + ChainCode c; bn_read_bin(c.chainCode, bytes.begin(), ChainCode::SIZE); return c; } -ChainCode::ChainCode(const ChainCode &cc) { - uint8_t bytes[ChainCode::SIZE]; - cc.Serialize(bytes); +ChainCode::ChainCode() { + bn_null(chainCode); bn_new(chainCode); - bn_read_bin(chainCode, bytes, ChainCode::SIZE); + bn_zero(chainCode); +} + +ChainCode::ChainCode(const ChainCode &cc) : ChainCode() { + bn_copy(chainCode, cc.chainCode); +} + +ChainCode& ChainCode::operator=(const ChainCode &cc) { + if (this != &cc) { + util::SecureWipe(chainCode->dp, sizeof(chainCode->dp)); + bn_copy(chainCode, cc.chainCode); + } + return *this; +} + +ChainCode::~ChainCode() { + util::SecureWipe(chainCode, sizeof(bn_st)); + bn_free(chainCode); } // Comparator implementation. diff --git a/test/test_secure.cpp b/test/test_secure.cpp index e2098157f..f550690f4 100644 --- a/test/test_secure.cpp +++ b/test/test_secure.cpp @@ -236,3 +236,45 @@ TEST_CASE("Secure release clears the bytes before handing them back") REQUIRE(g_arena[16 * sizeof(uint32_t)] == 0xcd); } } + +TEST_CASE("A chain code has value semantics and clears itself") +{ + const std::vector vecA(bls::ChainCode::SIZE, 0xa7); + const std::vector vecB(bls::ChainCode::SIZE, 0x3c); + + SECTION("Copy and assignment carry the value") + { + const bls::ChainCode a = bls::ChainCode::FromBytes(bls::Bytes(vecA)); + const bls::ChainCode copied = a; + REQUIRE(copied == a); + + bls::ChainCode assigned = bls::ChainCode::FromBytes(bls::Bytes(vecB)); + assigned = a; + REQUIRE(assigned == a); + + // Self-assignment must not wipe the value it is about to copy. + bls::ChainCode& alias = assigned; + assigned = alias; + REQUIRE(assigned == a); + } + + SECTION("Destruction leaves nothing behind") + { + std::memset(g_arena, 0xcd, sizeof(g_arena)); + { + ArenaGuard guard; + // The bn_st lands in the arena, so what the destructor does to it + // stays readable once the scope ends. + auto* pHeld = static_cast( + bls::util::SecMalloc(sizeof(bls::ChainCode))); + new (pHeld) + bls::ChainCode(bls::ChainCode::FromBytes(bls::Bytes(vecA))); + REQUIRE(std::any_of(g_arena, g_arena + sizeof(bls::ChainCode), + [](uint8_t b) { return b != 0 && b != 0xcd; })); + pHeld->~ChainCode(); + + REQUIRE(std::all_of(g_arena, g_arena + sizeof(bls::ChainCode), + [](uint8_t b) { return b == 0; })); + } + } +} From 5c92730d5e3ba8121199a35e5b7185432bb1c3a2 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Wed, 26 Aug 2026 19:00:07 +0530 Subject: [PATCH 15/15] fix: clear `PrivateKey` before another value is copied over it Co-authored-by: pasta --- src/privatekey.cpp | 7 +++++++ test/test_secure.cpp | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/privatekey.cpp b/src/privatekey.cpp index 9a0b107ac..b2b28f51e 100644 --- a/src/privatekey.cpp +++ b/src/privatekey.cpp @@ -127,13 +127,20 @@ PrivateKey& PrivateKey::operator=(const PrivateKey& other) { CheckKeyData(); other.CheckKeyData(); + if (this == &other) { + return *this; + } InvalidateCaches(); + util::SecureWipe(keydata->dp, sizeof(keydata->dp)); bn_copy(keydata, other.keydata); return *this; } PrivateKey& PrivateKey::operator=(PrivateKey&& other) { + if (this == &other) { + return *this; + } DeallocateKeyData(); keydata = std::exchange(other.keydata, nullptr); other.InvalidateCaches(); diff --git a/test/test_secure.cpp b/test/test_secure.cpp index f550690f4..e8ac1d4e5 100644 --- a/test/test_secure.cpp +++ b/test/test_secure.cpp @@ -182,6 +182,45 @@ TEST_CASE("A private key has value semantics and clears itself") REQUIRE(std::all_of(g_arena, g_arena + sizeof(bn_st), [](uint8_t b) { return b == 0; })); } + + SECTION("Copy and assignment carry the value") + { + std::vector vecWide(bls::PrivateKey::PRIVATE_KEY_SIZE, 0x5b); + vecWide[0] = 0x0f; + + bls::PrivateKey sk = bls::PrivateKey::FromBytes(bls::Bytes(vecWide)); + const bls::PrivateKey copied = sk; + REQUIRE(copied == sk); + + // Self-assignment must not clear the digits it is about to read. + bls::PrivateKey& alias = sk; + sk = alias; + REQUIRE(sk.Serialize() == vecWide); + } + + SECTION("Assignment clears the value it replaces") + { + std::vector vecWide(bls::PrivateKey::PRIVATE_KEY_SIZE, 0x5b); + vecWide[0] = 0x0f; + std::vector vecNarrow(bls::PrivateKey::PRIVATE_KEY_SIZE, 0x00); + vecNarrow[bls::PrivateKey::PRIVATE_KEY_SIZE - 1] = 0x01; + + std::memset(g_arena, 0xcd, sizeof(g_arena)); + { + ArenaGuard guard; + bls::PrivateKey wide = + bls::PrivateKey::FromBytes(bls::Bytes(vecWide)); + const bls::PrivateKey narrow = + bls::PrivateKey::FromBytes(bls::Bytes(vecNarrow)); + wide = narrow; + + REQUIRE(wide.Serialize() == vecNarrow); + // 0x5b was only ever a digit of the wide value, so finding it now + // would mean the assignment copied over it rather than clearing. + REQUIRE(std::none_of(g_arena, g_arena + sizeof(bn_st), + [](uint8_t b) { return b == 0x5b; })); + } + } } TEST_CASE("util::SecVector allocates through the secure allocator")