Skip to content

Commit 139e8c1

Browse files
apoelstraroconnor-blockstream
authored andcommitted
codex32: introduce Lagrange interpolation and derived shares
1 parent 5d504cb commit 139e8c1

File tree

3 files changed

+387
-0
lines changed

3 files changed

+387
-0
lines changed

src/codex32.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ uint8_t gf32_mul(uint8_t x, uint8_t y) {
7878
return GF32_EXP[(GF32_LOG[x] + GF32_LOG[y]) % 31];
7979
}
8080

81+
uint8_t gf32_div(uint8_t x, uint8_t y) {
82+
assert(y != 0);
83+
if (x == 0) {
84+
return 0;
85+
}
86+
return GF32_EXP[(GF32_LOG[x] + 31 - GF32_LOG[y]) % 31];
87+
}
88+
8189
// The bech32 string "secretshare32"
8290
constexpr const std::array<uint8_t, 13> CODEX32_M = {
8391
16, 25, 24, 3, 25, 11, 16, 23, 29, 3, 25, 17, 10
@@ -192,6 +200,24 @@ data CreateChecksum(const std::string& hrp, const data& values, const Residue& g
192200
return ret;
193201
}
194202

203+
// Given a set of share indices and a target index `idx`, which must be in the set,
204+
// compute the Lagrange basis polynomial for `idx` evaluated at the point `eval`.
205+
//
206+
// All inputs are GF32 elements, rather than array indices or anything else.
207+
uint8_t lagrange_coefficient(std::vector<uint8_t>& indices, uint8_t idx, uint8_t eval) {
208+
uint8_t num = 1;
209+
uint8_t den = 1;
210+
for (const auto idx_i : indices) {
211+
if (idx_i != idx) {
212+
num = gf32_mul(num, idx_i ^ eval);
213+
den = gf32_mul(den, idx_i ^ idx);
214+
}
215+
}
216+
217+
// return num / den
218+
return gf32_div(num, den);
219+
}
220+
195221
} // namespace
196222

197223
/** Encode a codex32 string. */
@@ -300,6 +326,64 @@ Result::Result(std::string&& hrp, size_t k, const std::string& id, char share_id
300326
ConvertBits<8, 5, true>([&](unsigned char c) { m_data.push_back(c); }, data.begin(), data.end());
301327
}
302328

329+
Result::Result(const std::vector<Result>& shares, char output_idx) {
330+
m_valid = OK;
331+
332+
int8_t oidx = bech32::internal::CHARSET_REV[(unsigned char) output_idx];
333+
if (oidx == -1) {
334+
m_valid = INVALID_SHARE_IDX;
335+
}
336+
if (shares.empty()) {
337+
m_valid = TOO_FEW_SHARES;
338+
return;
339+
}
340+
size_t k = shares[0].GetK();
341+
if (k > shares.size()) {
342+
m_valid = TOO_FEW_SHARES;
343+
}
344+
if (m_valid != OK) {
345+
return;
346+
}
347+
348+
std::vector<uint8_t> indices;
349+
indices.reserve(shares.size());
350+
for (size_t i = 0; i < shares.size(); ++i) {
351+
// Currently the only supported hrp is "ms" so it is impossible to violate this
352+
assert (shares[0].m_hrp == shares[i].m_hrp);
353+
if (shares[0].m_data[0] != shares[i].m_data[0]) {
354+
m_valid = MISMATCH_K;
355+
}
356+
for (size_t j = 1; j < 5; ++j) {
357+
if (shares[0].m_data[j] != shares[i].m_data[j]) {
358+
m_valid = MISMATCH_ID;
359+
}
360+
}
361+
if (shares[i].m_data.size() != shares[0].m_data.size()) {
362+
m_valid = MISMATCH_LENGTH;
363+
}
364+
365+
indices.push_back(shares[i].m_data[5]);
366+
for (size_t j = i + 1; j < shares.size(); ++j) {
367+
if (shares[i].m_data[5] == shares[j].m_data[5]) {
368+
m_valid = DUPLICATE_SHARE;
369+
}
370+
}
371+
}
372+
373+
m_hrp = shares[0].m_hrp;
374+
m_data.reserve(shares[0].m_data.size());
375+
for (size_t j = 0; j < shares[0].m_data.size(); ++j) {
376+
m_data.push_back(0);
377+
}
378+
379+
for (size_t i = 0; i < shares.size(); ++i) {
380+
uint8_t lagrange_coeff = lagrange_coefficient(indices, shares[i].m_data[5], oidx);
381+
for (size_t j = 0; j < m_data.size(); ++j) {
382+
m_data[j] ^= gf32_mul(lagrange_coeff, shares[i].m_data[j]);
383+
}
384+
}
385+
}
386+
303387
std::string Result::GetIdString() const {
304388
assert(IsValid());
305389

src/codex32.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ enum Error {
3434
INVALID_LENGTH,
3535
INVALID_K,
3636
INVALID_SHARE_IDX,
37+
TOO_FEW_SHARES,
38+
DUPLICATE_SHARE,
39+
MISMATCH_K,
40+
MISMATCH_ID,
41+
MISMATCH_LENGTH,
3742
};
3843

3944
class Result
@@ -48,6 +53,11 @@ class Result
4853
* ignore the case of `id` and `share_idx`. */
4954
Result(std::string&& hrp, size_t k, const std::string& id, char share_idx, const std::vector<unsigned char>& data);
5055

56+
/** Construct a codex32 result by interpolating a set of input shares to obtain an output share
57+
*
58+
* Requires that all input shares have the same k and seed ID */
59+
Result(const std::vector<Result>& shares, char output_idx);
60+
5161
/** Boolean indicating whether the data was successfully parsed.
5262
*
5363
* If this returns false, most of the other methods on this class will assert. */

0 commit comments

Comments
 (0)