Skip to content

Commit 3ac551a

Browse files
authored
Merge pull request #1637 from evoskuil/master
Guard deriving public from invalid extended private key, update tests.
2 parents 5d7b560 + b20e1d5 commit 3ac551a

3 files changed

Lines changed: 145 additions & 0 deletions

File tree

src/wallet/keys/hd_private.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,9 @@ hd_key hd_private::to_hd_key() const NOEXCEPT
231231

232232
hd_public hd_private::to_public() const NOEXCEPT
233233
{
234+
if (!valid_)
235+
return {};
236+
234237
const auto key = static_cast<hd_public>(*this).to_hd_key();
235238
return { key, hd_public::to_prefix(lineage_.prefixes) };
236239
}

test/wallet/keys/hd_private.cpp

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,112 @@ BOOST_AUTO_TEST_CASE(hd_private__derive_public__long_seed__expected)
128128
BOOST_REQUIRE_EQUAL(m0xH1yH2_pub.encoded(), "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt");
129129
}
130130

131+
BOOST_AUTO_TEST_CASE(hd_private__constructor__null_key__decodes_to_invalid)
132+
{
133+
// the 11...14rcJhr is a serialization of a null key;
134+
static const auto null_encoded = "1111111111111111111111111111111111111111111111111111111111111111111111111111114rcJhr";
135+
const hd_private xprv_null(null_encoded);
136+
137+
BOOST_REQUIRE(!xprv_null);
138+
}
139+
140+
BOOST_AUTO_TEST_CASE(hd_private__to_public__from_invalid_private__invalid)
141+
{
142+
// the 11...14rcJhr is a serialization of a null key;
143+
static const auto xprv_invalid_encoded = "1111111111111111111111111111111111111111111111111111111111111111111111111111114rcJhr";
144+
const hd_private xprv_invalid(xprv_invalid_encoded);
145+
146+
BOOST_REQUIRE(!xprv_invalid);
147+
BOOST_REQUIRE(!xprv_invalid.to_public());
148+
}
149+
150+
BOOST_AUTO_TEST_CASE(hd_private__derive_private__depth_overflow__invalid)
151+
{
152+
// xprv_254_depth was created from "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
153+
// by manually setting the depth to 254
154+
static const auto xprv_254_encoded = "xprvJ6xRbBsatSpgzr9c3hYbM2RohnAcHiiN74vQWqdRPx914xeq41t3u4rPXTsNxd5kvLSnqpsMx1cMx8cytMM5RbS7G54nwC5p5P5MQFSjf36";
155+
const hd_private xprv_254(xprv_254_encoded);
156+
157+
158+
const auto xprv_255 = xprv_254.derive_private(14);
159+
const auto xprv_256 = xprv_255.derive_private(70);
160+
161+
BOOST_REQUIRE_EQUAL(xprv_254.lineage().depth, 254);
162+
BOOST_REQUIRE(xprv_254);
163+
164+
// the maximal valid depth is 255
165+
BOOST_REQUIRE_EQUAL(xprv_255.lineage().depth, 255);
166+
BOOST_REQUIRE(xprv_255);
167+
168+
// depth overflows uint from 255 to 0
169+
BOOST_REQUIRE_EQUAL(xprv_256.lineage().depth, 0);
170+
BOOST_REQUIRE(!xprv_256);
171+
}
172+
173+
BOOST_AUTO_TEST_CASE(hd_private__derive_private__hardened_depth_overflow__invalid)
174+
{
175+
// xprv_254_depth was created from "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
176+
// by manually setting the depth to 254
177+
static const auto xprv254_encoded = "xprvJ6xRbBsatSpgzr9c3hYbM2RohnAcHiiN74vQWqdRPx914xeq41t3u4rPXTsNxd5kvLSnqpsMx1cMx8cytMM5RbS7G54nwC5p5P5MQFSjf36";
178+
const hd_private xprv_254(xprv254_encoded);
179+
180+
const auto xprv_255 = xprv_254.derive_private(1337 + hd_first_hardened_key);
181+
const auto xprv_256 = xprv_255.derive_private(8887 + hd_first_hardened_key);
182+
183+
BOOST_REQUIRE_EQUAL(xprv_254.lineage().depth, 254);
184+
BOOST_REQUIRE(xprv_254);
185+
186+
// the maximal valid depth is 255
187+
BOOST_REQUIRE_EQUAL(xprv_255.lineage().depth, 255);
188+
BOOST_REQUIRE(xprv_255);
189+
190+
// depth overflows uint from 255 to 0
191+
BOOST_REQUIRE_EQUAL(xprv_256.lineage().depth, 0);
192+
BOOST_REQUIRE(!xprv_256);
193+
}
194+
195+
BOOST_AUTO_TEST_CASE(hd_private__derive_public__must_not_overflow_depth__expected)
196+
{
197+
// xprv_254_depth was created from "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
198+
// by manually setting the depth to 254
199+
static const auto xprv_254_encoded = "xprvJ6xRbBsatSpgzr9c3hYbM2RohnAcHiiN74vQWqdRPx914xeq41t3u4rPXTsNxd5kvLSnqpsMx1cMx8cytMM5RbS7G54nwC5p5P5MQFSjf36";
200+
const hd_private xprv_254(xprv_254_encoded);
201+
202+
const auto xprv_255 = xprv_254.derive_private(14);
203+
const auto xpub_256 = xprv_255.derive_public(70);
204+
205+
BOOST_REQUIRE_EQUAL(xprv_254.lineage().depth, 254);
206+
BOOST_REQUIRE(xprv_254);
207+
208+
// the maximal valid depth is 255
209+
BOOST_REQUIRE_EQUAL(xprv_255.lineage().depth, 255);
210+
BOOST_REQUIRE(xprv_255);
211+
212+
// depth overflows uint from 255 to 0
213+
BOOST_REQUIRE_EQUAL(xpub_256.lineage().depth, 0);
214+
BOOST_REQUIRE(!xpub_256);
215+
}
216+
217+
BOOST_AUTO_TEST_CASE(hd_private__derive_public__hardened_must_not_overflow_depth__expected)
218+
{
219+
// xprv_254_depth was created from "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
220+
// by manually setting the depth to 254
221+
static const auto xprv_254_encoded = "xprvJ6xRbBsatSpgzr9c3hYbM2RohnAcHiiN74vQWqdRPx914xeq41t3u4rPXTsNxd5kvLSnqpsMx1cMx8cytMM5RbS7G54nwC5p5P5MQFSjf36";
222+
const hd_private xprv_254(xprv_254_encoded);
223+
224+
const auto xprv_255 = xprv_254.derive_private(141);
225+
const auto xpub_256 = xprv_255.derive_public(19287 + hd_first_hardened_key);
226+
227+
BOOST_REQUIRE_EQUAL(xprv_254.lineage().depth, 254);
228+
BOOST_REQUIRE(xprv_254);
229+
230+
// the maximal valid depth is 255
231+
BOOST_REQUIRE_EQUAL(xprv_255.lineage().depth, 255);
232+
BOOST_REQUIRE(xprv_255);
233+
234+
// depth overflows uint from 255 to 0
235+
BOOST_REQUIRE_EQUAL(xpub_256.lineage().depth, 0);
236+
BOOST_REQUIRE(!xpub_256);
237+
}
238+
131239
BOOST_AUTO_TEST_SUITE_END()

test/wallet/keys/hd_public.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ BOOST_AUTO_TEST_CASE(hd_public__encoded__round_trip__expected)
4444
BOOST_REQUIRE_EQUAL(key.encoded(), encoded);
4545
}
4646

47+
BOOST_AUTO_TEST_CASE(hd_public__constructor__null_key__decodes_to_invalid)
48+
{
49+
// the 11...14rcJhr is a serialization of a null key;
50+
static const auto null_encoded = "1111111111111111111111111111111111111111111111111111111111111111111111111111114rcJhr";
51+
const hd_private xpub_null(null_encoded);
52+
53+
BOOST_REQUIRE(!xpub_null);
54+
}
55+
56+
4757
BOOST_AUTO_TEST_CASE(hd_public__derive_public__short_seed__expected)
4858
{
4959
data_chunk seed;
@@ -93,4 +103,28 @@ BOOST_AUTO_TEST_CASE(hd_public__derive_public__long_seed__expected)
93103
BOOST_REQUIRE_EQUAL(m0xH1yH2_pub.encoded(), "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt");
94104
}
95105

106+
BOOST_AUTO_TEST_CASE(hd_public__derive_public__depth_overflow__invalid)
107+
{
108+
// xprv_254_depth was created from "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
109+
// by manually setting the depth to 254
110+
static const auto xprv_254_encoded = "xprvJ6xRbBsatSpgzr9c3hYbM2RohnAcHiiN74vQWqdRPx914xeq41t3u4rPXTsNxd5kvLSnqpsMx1cMx8cytMM5RbS7G54nwC5p5P5MQFSjf36";
111+
const hd_private xprv_254(xprv_254_encoded);
112+
hd_public xpub_254 = xprv_254.to_public();
113+
114+
const auto xpub_255 = xpub_254.derive_public(1);
115+
const auto xpub_256 = xpub_255.derive_public(0);
116+
117+
BOOST_REQUIRE_EQUAL(xpub_254.lineage().depth, 254);
118+
BOOST_REQUIRE(xpub_254);
119+
120+
// the maximal valid depth is 255
121+
BOOST_REQUIRE_EQUAL(xpub_255.lineage().depth, 255);
122+
BOOST_REQUIRE(xpub_255);
123+
124+
// depth overflows uint from 255 to 0
125+
BOOST_REQUIRE_EQUAL(xpub_256.lineage().depth, 0);
126+
BOOST_REQUIRE(!xpub_256);
127+
}
128+
129+
96130
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)