-
Notifications
You must be signed in to change notification settings - Fork 1
add round-trip for share derived via interpolation #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
06c26dc
dd4767a
725552e
5c32130
65875bc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| from itertools import product | ||
| from src.codex32.codex32 import Codex32String, IDX_ORDER | ||
|
|
||
|
|
||
| def recover_with_bytes(hrp, header_str, share_idx_list, share_bytes_list, target="s"): | ||
| """Recover codex32 secret from share bytes assuming default padding.""" | ||
| pad_len = (5 - (len(share_bytes_list[0]) * 8) % 5) % 5 | ||
| default_padded_shares = IDX_ORDER[1 : 1 + len(share_idx_list)] | ||
| pad_candidates = product(range(1 << pad_len), repeat=len(share_idx_list)) | ||
| for pad_vals in pad_candidates: | ||
| given_shares = [] | ||
| for idx, data, pad_val in zip(share_idx_list, share_bytes_list, pad_vals): | ||
| given_shares.append(Codex32String.from_seed(data, hrp + '1' + header_str + idx, pad_val)) | ||
| for share_idx in default_padded_shares: | ||
| initial_share = Codex32String.interpolate_at(given_shares, share_idx) | ||
| round_tripped_initial_share = Codex32String.from_seed(initial_share.data, hrp + "1" + header_str + share_idx) | ||
| if str(round_tripped_initial_share) != str(initial_share): | ||
| break | ||
| return Codex32String.interpolate_at(given_shares, target) | ||
|
|
||
| def test_round_trip_recovery(): | ||
| # secret share from seed | ||
| s = Codex32String.from_seed(bytes.fromhex("68f14219957131d21b615271058437e8"), "ms13k00ls") | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
However for bip85 we don't directly encode secrets unless threshold is "0". |
||
| assert s.s == "ms13k00lsdrc5yxv4wycayxmp2fcstppharks8z0r84pf3uj" | ||
|
|
||
| # derive 'a' via proposed BIP-85 | ||
| a = Codex32String.from_seed(bytes.fromhex("641be1cb12c97ede1c6bad8edf067760"), "ms13k00la") | ||
| assert a.s == "ms13k00lavsd7rjcje9ldu8rt4k8d7pnhvppyrt5gpff9wwl" | ||
|
|
||
| # derive 'c' via proposed BIP-85 | ||
| c = Codex32String.from_seed(bytes.fromhex("61b3c4052f7a31dc2b425c843a13c9b4"), "ms13k00lc") | ||
| assert c.s == "ms13k00lcvxeugpf00gcac26ztjzr5y7fkjl7fx7nx7ykhkr" | ||
|
|
||
| # derive next share via interpolation | ||
| d = Codex32String.interpolate_at([s, a, c], "d") | ||
| assert d.s == "ms13k00ldp4v5nw8lph96x47mjxzgwjexe44p32swkq99e0w" | ||
|
|
||
| # now round-trip d share ('d' is derived via interpolation, NOT via 'from_seed') | ||
| dd = Codex32String.from_seed(d.data, "ms13k00ld") | ||
| # they are NOT equal after round-trip - seem we miss padding at interpolation level | ||
| # assert dd.s == d.s # FAIL (should equal) | ||
|
|
||
| # irrelevant | ||
| # e = Codex32String.interpolate_at([s, a, c], "e") | ||
| # assert e.s == "ms13k00lezuknydaaygk5u20zs4fm736vj909mdj6xqp8pc2" | ||
| # | ||
| # f = Codex32String.interpolate_at([s, a, c], "f") | ||
| # assert f.s == "ms13k00lf0ehe53zsu6vrxcjjh9v7wzsa83mqfvku3fd8kem" | ||
|
|
||
| # recover from shares, use 'd' without round-trip | ||
| rec_s = Codex32String.interpolate_at([a, d, c], "s") | ||
| # recover from shares, use 'd' after round-trip | ||
| rec_ss = Codex32String.interpolate_at([a, dd, c], "s") | ||
|
|
||
|
|
||
| xx = recover_with_bytes("ms", "3k00l", ["a", "d", "c"], [a.data, d.data, c.data]) | ||
| # recover with round-trip D share | ||
| yy = recover_with_bytes("ms", "3k00l", ["a", "d", "c"], [a.data, dd.data, c.data]) | ||
|
|
||
| print(" s:", s.data.hex()) | ||
| print(" rec_s:", rec_s.data.hex()) | ||
| print("rec_ss:", rec_ss.data.hex()) | ||
| print("rec_xx:", xx.data.hex()) | ||
| print("rec_yy:", yy.data.hex()) | ||
| assert s.data == rec_s.data | ||
| assert s.data == yy.data # FAIL | ||
| assert s.data == xx.data # FAIL | ||
| assert s.data == rec_ss.data # FAIL | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change this to:
default_padded_shares = IDX_ORDER[ : len(share_idx_list)]and it should be able to solve the padding for your vector where 's' is encoded from bytes.
As a standard I think A, C, D should get default padding not S, A, C for threshold 3 share sets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does not help 725552e
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for testing. I'll have to try a different approach when I'm home.
If it's impossible to always recover the correct secret discarding padding, I'll include the fingerprint based recovery method, which should be more reliable.
I'm thinking of altering the
Codex32String.datamethod to return a padding (and maybe header) byte on shares, keeping the current behavior for secrets. What do you think?BIP-93 only defines how to decode secrets:
So we are free invent a share decoding process that works for both our applications.
This would mean
len(Codex32String("MS12NAMES6XQGUZTTXKEQNJSJZV4JV3NZ5K3KWGSPHUH6EVW").data) == 16butlen(Codex32String("MS12NAMEDLL4F8JLH4E5VDVULDLFXU2JHDNLSM97XVENRXEG").data) == 17 or 18That way there's no need to try and guess the padding bits because
.dataouputs them.This won't disrupt your application unless it's unacceptable to store an unusual byte length secret.
This comment was marked as outdated.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not an issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a way to close the issue of shares lacking a proper byte decoding:
apoelstra/rust-codex32#2 (comment)
The idea adds bytes to store threshold, share index and padding so the data can recover the secret.
Could be as small as 2 extra bytes with 4-8 bits of identifier or 4 extra bytes and 20-bits of id in the decoding.