@@ -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"
8290constexpr 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+
303387std::string Result::GetIdString () const {
304388 assert (IsValid ());
305389
0 commit comments