Skip to content

Commit db560d0

Browse files
committed
Clean up BIP38_KEY_SWAP_ORDER handling to avoid compiler/analyzer bugs
1 parent 7b66cf8 commit db560d0

File tree

1 file changed

+43
-22
lines changed

1 file changed

+43
-22
lines changed

src/bip38.c

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,27 @@ struct derived_t {
4141
unsigned char half2[BIP38_DERIVED_KEY_LEN / 2];
4242
};
4343

44+
struct bip38_sublayout_t {
45+
uint32_t hash;
46+
unsigned char half1[AES_BLOCK_LEN];
47+
unsigned char half2[AES_BLOCK_LEN];
48+
};
49+
50+
struct bip38_sublayout_swapped_t {
51+
unsigned char half1[AES_BLOCK_LEN];
52+
unsigned char half2[AES_BLOCK_LEN];
53+
uint32_t hash;
54+
};
55+
4456
struct bip38_layout_t {
4557
unsigned char pad1;
4658
unsigned char prefix;
4759
unsigned char ec_type;
4860
unsigned char flags;
49-
uint32_t hash;
50-
unsigned char half1[AES_BLOCK_LEN];
51-
unsigned char half2[AES_BLOCK_LEN];
61+
union {
62+
struct bip38_sublayout_t normal;
63+
struct bip38_sublayout_swapped_t swapped;
64+
} u;
5265
unsigned char decode_hash[BASE58_CHECKSUM_LEN];
5366
};
5467

@@ -58,6 +71,8 @@ static void assert_bip38_assumptions(void)
5871
{
5972
/* derived_t/bip38_layout_t must be contiguous */
6073
BUILD_ASSERT(sizeof(struct derived_t) == BIP38_DERIVED_KEY_LEN);
74+
/* swapped and normal sublayouts must be the same size */
75+
BUILD_ASSERT(sizeof(struct bip38_sublayout_t) == sizeof(struct bip38_sublayout_swapped_t));
6176
/* 44 -> pad1 + 39 + BASE58_CHECKSUM_LEN */
6277
BUILD_ASSERT(sizeof(struct bip38_layout_t) == 44u);
6378
BUILD_ASSERT((sizeof(struct bip38_layout_t) - BASE58_CHECKSUM_LEN - 1) ==
@@ -140,35 +155,39 @@ int bip38_raw_from_private_key(const unsigned char *bytes, size_t bytes_len,
140155
goto finish;
141156

142157
if (flags & BIP38_KEY_RAW_MODE)
143-
buf.hash = base58_get_checksum(bytes, bytes_len);
158+
buf.u.normal.hash = base58_get_checksum(bytes, bytes_len);
144159
else {
145160
const unsigned char network = flags & 0xff;
146161
char *addr58 = NULL;
147162
if ((ret = address_from_private_key(bytes, bytes_len,
148163
network, compressed, &addr58)))
149164
goto finish;
150165

151-
buf.hash = base58_get_checksum((unsigned char *)addr58, strlen(addr58));
166+
buf.u.normal.hash = base58_get_checksum((unsigned char *)addr58, strlen(addr58));
152167
wally_free_string(addr58);
153168
}
154169

155170
ret = wally_scrypt(pass, pass_len,
156-
(unsigned char *)&buf.hash, sizeof(buf.hash), 16384, 8, 8,
171+
(unsigned char *)&buf.u.normal.hash, sizeof(buf.u.normal.hash),
172+
16384, 8, 8,
157173
(unsigned char *)&derived, sizeof(derived));
158174
if (ret)
159175
goto finish;
160176

161177
buf.prefix = BIP38_PREFIX;
162178
buf.ec_type = BIP38_NO_ECMUL; /* FIXME: EC-Multiply support */
163179
buf.flags = BIP38_FLAG_DEFAULT | (compressed ? BIP38_FLAG_COMPRESSED : 0);
164-
aes_enc_impl(bytes + 0, derived.half1_lo, derived.half2, buf.half1);
165-
aes_enc_impl(bytes + 16, derived.half1_hi, derived.half2, buf.half2);
180+
aes_enc_impl(bytes + 0, derived.half1_lo, derived.half2, buf.u.normal.half1);
181+
aes_enc_impl(bytes + 16, derived.half1_hi, derived.half2, buf.u.normal.half2);
166182

167183
if (flags & BIP38_KEY_SWAP_ORDER) {
168-
/* Shuffle hash from the beginning to the end */
169-
uint32_t tmp = buf.hash;
170-
memmove(&buf.hash, buf.half1, AES_BLOCK_LEN * 2);
171-
memcpy(buf.decode_hash - sizeof(uint32_t), &tmp, sizeof(uint32_t));
184+
/* Shuffle hash from the beginning to the end (normal->swapped) */
185+
struct bip38_sublayout_swapped_t swapped;
186+
swapped.hash = buf.u.normal.hash;
187+
memcpy(swapped.half1, buf.u.normal.half1, sizeof(buf.u.normal.half1));
188+
memcpy(swapped.half2, buf.u.normal.half2, sizeof(buf.u.normal.half2));
189+
memcpy(&buf.u.swapped, &swapped, sizeof(swapped));
190+
wally_clear(&swapped, sizeof(swapped));
172191
}
173192

174193
memcpy(bytes_out, &buf.prefix, BIP38_SERIALIZED_LEN);
@@ -250,11 +269,13 @@ static int to_private_key(const char *bip38,
250269
}
251270

252271
if (flags & BIP38_KEY_SWAP_ORDER) {
253-
/* Shuffle hash from the end to the beginning */
254-
uint32_t tmp;
255-
memcpy(&tmp, buf.decode_hash - sizeof(uint32_t), sizeof(uint32_t));
256-
memmove(buf.half1, &buf.hash, AES_BLOCK_LEN * 2);
257-
buf.hash = tmp;
272+
/* Shuffle hash from the end to the beginning (swapped->normal) */
273+
struct bip38_sublayout_t normal;
274+
normal.hash = buf.u.swapped.hash;
275+
memcpy(normal.half1, buf.u.swapped.half1, sizeof(buf.u.swapped.half1));
276+
memcpy(normal.half2, buf.u.swapped.half2, sizeof(buf.u.swapped.half2));
277+
memcpy(&buf.u.normal, &normal, sizeof(normal));
278+
wally_clear(&normal, sizeof(normal));
258279
}
259280

260281
if (buf.prefix != BIP38_PREFIX ||
@@ -272,23 +293,23 @@ static int to_private_key(const char *bip38,
272293
}
273294

274295
if((ret = wally_scrypt(pass, pass_len,
275-
(unsigned char *)&buf.hash, sizeof(buf.hash), 16384, 8, 8,
296+
(unsigned char *)&buf.u.normal.hash, sizeof(buf.u.normal.hash), 16384, 8, 8,
276297
(unsigned char *)&derived, sizeof(derived))))
277298
goto finish;
278299

279-
aes_dec_impl(buf.half1, derived.half1_lo, derived.half2, bytes_out + 0);
280-
aes_dec_impl(buf.half2, derived.half1_hi, derived.half2, bytes_out + 16);
300+
aes_dec_impl(buf.u.normal.half1, derived.half1_lo, derived.half2, bytes_out + 0);
301+
aes_dec_impl(buf.u.normal.half2, derived.half1_hi, derived.half2, bytes_out + 16);
281302

282303
if (flags & BIP38_KEY_RAW_MODE) {
283-
if (buf.hash != base58_get_checksum(bytes_out, len))
304+
if (buf.u.normal.hash != base58_get_checksum(bytes_out, len))
284305
ret = WALLY_EINVAL;
285306
} else {
286307
const unsigned char network = flags & 0xff;
287308
char *addr58 = NULL;
288309
ret = address_from_private_key(bytes_out, len, network,
289310
buf.flags & BIP38_FLAG_COMPRESSED, &addr58);
290311
if (!ret &&
291-
buf.hash != base58_get_checksum((unsigned char *)addr58, strlen(addr58)))
312+
buf.u.normal.hash != base58_get_checksum((unsigned char *)addr58, strlen(addr58)))
292313
ret = WALLY_EINVAL;
293314
wally_free_string(addr58);
294315
}

0 commit comments

Comments
 (0)