Skip to content

Commit b83702d

Browse files
LeoComandinigreenaddress
authored andcommitted
Expose ecdh
1 parent 8899246 commit b83702d

File tree

10 files changed

+108
-1
lines changed

10 files changed

+108
-1
lines changed

include/wally.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ WALLY_FN_BBB3_BS(aes_cbc, wally_aes_cbc)
374374
WALLY_FN_BBB3_BS(scriptsig_multisig_from_bytes, wally_scriptsig_multisig_from_bytes)
375375
WALLY_FN_BB_B(hmac_sha256, wally_hmac_sha256)
376376
WALLY_FN_BB_B(hmac_sha512, wally_hmac_sha512)
377+
WALLY_FN_BB_B(ecdh, wally_ecdh)
377378
WALLY_FN_BP3_A(addr_segwit_from_bytes, wally_addr_segwit_from_bytes)
378379
WALLY_FN_BB_BS(scriptsig_p2pkh_from_der, wally_scriptsig_p2pkh_from_der)
379380
WALLY_FN_B_A(bip32_key_unserialize_alloc, bip32_key_unserialize_alloc)

include/wally_crypto.h

+18
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,24 @@ WALLY_CORE_API int wally_format_bitcoin_message(const unsigned char *bytes,
450450
size_t len,
451451
size_t *written);
452452

453+
/**
454+
*
455+
* Compute an EC Diffie-Hellman secret in constant time
456+
*
457+
* :param pub_key: The public key.
458+
* :param pub_key_len: The length of ``pubkey`` in bytes. Must be ``EC_PUBLIC_KEY_LEN``.
459+
* :param bytes: The private key.
460+
* :param bytes_len: The length of ``privkey`` in bytes. Must be ``EC_PRIVATE_KEY_LEN``.
461+
* :param output: Destination for the shared secret.
462+
* :param output_len: The length of ``output`` in bytes. Must be ``SHA256_LEN``.
463+
*/
464+
WALLY_CORE_API int wally_ecdh(const unsigned char *pub_key,
465+
size_t pub_key_len,
466+
const unsigned char *bytes,
467+
size_t bytes_len,
468+
unsigned char *bytes_out,
469+
size_t len);
470+
453471
#ifdef __cplusplus
454472
}
455473
#endif

src/Makefile.am

+2
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ libwallycore_la_SOURCES = \
172172
bip38.c \
173173
bip39.c \
174174
bech32.c \
175+
ecdh.c \
175176
elements.c \
176177
hex.c \
177178
hmac.c \
@@ -246,6 +247,7 @@ if RUN_PYTHON_TESTS
246247
$(AM_V_at)$(PYTHON_TEST) test/test_bip32.py
247248
$(AM_V_at)$(PYTHON_TEST) test/test_bip38.py
248249
$(AM_V_at)$(PYTHON_TEST) test/test_bip39.py
250+
$(AM_V_at)$(PYTHON_TEST) test/test_ecdh.py
249251
$(AM_V_at)$(PYTHON_TEST) test/test_hash.py
250252
$(AM_V_at)$(PYTHON_TEST) test/test_hex.py
251253
$(AM_V_at)$(PYTHON_TEST) test/test_hmac.py

src/ecdh.c

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#include "internal.h"
2+
#include <include/wally_crypto.h>
3+
#include "secp256k1/include/secp256k1.h"
4+
#include "secp256k1/include/secp256k1_ecdh.h"
5+
6+
int wally_ecdh(const unsigned char *pub_key, size_t pub_key_len,
7+
const unsigned char *bytes, size_t bytes_len,
8+
unsigned char *bytes_out, size_t len)
9+
{
10+
const secp256k1_context *ctx = secp_ctx();
11+
secp256k1_pubkey pub;
12+
13+
if (!ctx)
14+
return WALLY_ENOMEM;
15+
16+
if (!pub_key || pub_key_len != EC_PUBLIC_KEY_LEN ||
17+
!pubkey_parse(ctx, &pub, pub_key, pub_key_len) ||
18+
!bytes || bytes_len != EC_PRIVATE_KEY_LEN ||
19+
!secp256k1_ec_seckey_verify(ctx, bytes) ||
20+
!bytes_out || len != SHA256_LEN) {
21+
wally_clear(&pub, sizeof(pub));
22+
return WALLY_EINVAL;
23+
}
24+
25+
if (!secp256k1_ecdh(ctx, bytes_out, &pub, bytes, NULL, NULL)) {
26+
wally_clear(&pub, sizeof(pub));
27+
wally_clear(bytes_out, len);
28+
return WALLY_ERROR;
29+
}
30+
31+
wally_clear(&pub, sizeof(pub));
32+
return WALLY_OK;
33+
}

src/swig_java/swig.i

+1
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ static jbyteArray create_array(JNIEnv *jenv, const unsigned char* p, size_t len)
418418
%returns_array_(wally_ec_sig_from_der, 3, 4, EC_SIGNATURE_LEN);
419419
%returns_size_t(wally_ec_sig_to_der);
420420
%returns_void__(wally_ec_sig_verify);
421+
%returns_array_(wally_ecdh, 5, 6, SHA256_LEN);
421422
%returns_size_t(wally_format_bitcoin_message);
422423
%returns_array_(wally_hash160, 3, 4, HASH160_LEN);
423424
%returns_string(wally_hex_from_bytes);

src/swig_python/python_extra.py_in

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ ec_sig_from_bytes = _wrap_bin(ec_sig_from_bytes, EC_SIGNATURE_LEN)
6767
ec_sig_from_der = _wrap_bin(ec_sig_from_der, EC_SIGNATURE_LEN)
6868
ec_sig_normalize = _wrap_bin(ec_sig_normalize, EC_SIGNATURE_LEN)
6969
ec_sig_to_der = _wrap_bin(ec_sig_to_der, EC_SIGNATURE_DER_MAX_LEN, resize=True)
70+
ecdh = _wrap_bin(ecdh, SHA256_LEN)
7071

7172
def base58check_from_bytes(buf):
7273
return base58_from_bytes(buf, BASE58_FLAG_CHECKSUM)

src/test/test_ecdh.py

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import unittest
2+
from util import *
3+
4+
5+
class ECDHTests(unittest.TestCase):
6+
7+
def priv_to_pub(self, priv):
8+
pub, _ = make_cbuffer('00'*33)
9+
ret = wally_ec_public_key_from_private_key(priv, len(priv), pub, len(pub))
10+
self.assertEqual(ret, WALLY_OK)
11+
return pub
12+
13+
def test_ecdh(self):
14+
"""Tests for ECDH"""
15+
16+
priv1, _ = make_cbuffer('aa'*32)
17+
priv2, _ = make_cbuffer('bb'*32)
18+
pub1 = self.priv_to_pub(priv1)
19+
pub2 = self.priv_to_pub(priv2)
20+
21+
out12, _ = make_cbuffer('00'*32)
22+
out21, _ = make_cbuffer('00'*32)
23+
ret = wally_ecdh(pub1, len(pub1), priv2, len(priv2), out12, len(out12))
24+
self.assertEqual(ret, WALLY_OK)
25+
ret = wally_ecdh(pub2, len(pub2), priv1, len(priv1), out21, len(out21))
26+
self.assertEqual(ret, WALLY_OK)
27+
28+
self.assertEqual(out12, out21)
29+
30+
out, _ = make_cbuffer('00'*32)
31+
priv_bad, _ = make_cbuffer('00'*32)
32+
pub_bad, _ = make_cbuffer('02' + '00'*32)
33+
34+
for args in [
35+
(None, 32, pub1, 32, out, 32), # Missing private key
36+
(priv_bad, 32, pub1, 33, out, 32), # Invalid private key
37+
(priv1, 31, pub1, 32, out, 32), # Invalid private key length
38+
(priv1, 32, None, 33, out, 32), # Missing public key
39+
(priv1, 32, pub_bad, 33, out, 32), # Invalid public key
40+
(priv1, 32, pub1, 32, out, 32), # Invalid public key length
41+
(priv1, 32, pub1, 32, None, 32), # Missing output
42+
(priv1, 32, pub1, 33, out, 31), # Invalid output length
43+
]:
44+
self.assertEqual(WALLY_EINVAL, wally_ecdh(*args))
45+
self.assertEqual(h(out), utf8('00'*32))
46+
47+
48+
if __name__ == '__main__':
49+
unittest.main()

src/test/util.py

+1
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ class wally_tx(Structure):
172172
('wally_ec_sig_normalize', c_int, [c_void_p, c_ulong, c_void_p, c_ulong]),
173173
('wally_ec_sig_to_der', c_int, [c_void_p, c_ulong, c_void_p, c_ulong, c_ulong_p]),
174174
('wally_ec_sig_verify', c_int, [c_void_p, c_ulong, c_void_p, c_ulong, c_uint, c_void_p, c_ulong]),
175+
('wally_ecdh', c_int, [c_void_p, c_ulong, c_void_p, c_ulong, c_void_p]),
175176
('wally_get_operations', c_int, [POINTER(operations)]),
176177
('wally_set_operations', c_int, [POINTER(operations)]),
177178
('wally_format_bitcoin_message', c_int, [c_void_p, c_ulong, c_uint, c_void_p, c_ulong, c_ulong_p]),

src/wrap_js/src/combined.c

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "bip32.c"
77
#include "bip38.c"
88
#include "bip39.c"
9+
#include "ecdh.c"
910
#include "elements.c"
1011
#include "hex.c"
1112
#include "hmac.c"

tools/msvc/build.bat

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ copy src\ccan\ccan\str\hex\hex.c src\ccan\ccan\str\hex\hex_.c
1414
REM Compile everything (wally, ccan, libsecp256k) in one lump.
1515
REM Define USE_ECMULT_STATIC_PRECOMPUTATION to pick up the
1616
REM ecmult_static_context.h file generated previously
17-
cl /DUSE_ECMULT_STATIC_PRECOMPUTATION /DWALLY_CORE_BUILD /DHAVE_CONFIG_H /DSECP256K1_BUILD /I%LIBWALLY_DIR%\src\wrap_js\windows_config /I%LIBWALLY_DIR% /I%LIBWALLY_DIR%\src /I%LIBWALLY_DIR%\include /I%LIBWALLY_DIR%\src\ccan /I%LIBWALLY_DIR%\src\secp256k1 /Zi /LD src/aes.c src/base58.c src/bip32.c src/bip38.c src/bip39.c src/elements.c src/hex.c src/hmac.c src/internal.c src/mnemonic.c src/pbkdf2.c src/script.c src/scrypt.c src/sign.c src/transaction.c src/wif.c src/wordlist.c src/ccan/ccan/crypto/ripemd160/ripemd160.c src/ccan/ccan/crypto/sha256/sha256.c src/ccan/ccan/crypto/sha512/sha512.c src\ccan\ccan\str\hex\hex_.c src/secp256k1/src/secp256k1.c /Fewally.dll
17+
cl /DUSE_ECMULT_STATIC_PRECOMPUTATION /DWALLY_CORE_BUILD /DHAVE_CONFIG_H /DSECP256K1_BUILD /I%LIBWALLY_DIR%\src\wrap_js\windows_config /I%LIBWALLY_DIR% /I%LIBWALLY_DIR%\src /I%LIBWALLY_DIR%\include /I%LIBWALLY_DIR%\src\ccan /I%LIBWALLY_DIR%\src\secp256k1 /Zi /LD src/aes.c src/base58.c src/bip32.c src/bip38.c src/bip39.c src/ecdh.c src/elements.c src/hex.c src/hmac.c src/internal.c src/mnemonic.c src/pbkdf2.c src/script.c src/scrypt.c src/sign.c src/transaction.c src/wif.c src/wordlist.c src/ccan/ccan/crypto/ripemd160/ripemd160.c src/ccan/ccan/crypto/sha256/sha256.c src/ccan/ccan/crypto/sha512/sha512.c src\ccan\ccan\str\hex\hex_.c src/secp256k1/src/secp256k1.c /Fewally.dll

0 commit comments

Comments
 (0)