Skip to content

Commit 73641da

Browse files
committed
sskr restore + refactoring
1 parent d7a5ab3 commit 73641da

File tree

5 files changed

+335
-97
lines changed

5 files changed

+335
-97
lines changed

seedtool/seed.h

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,12 @@ class BIP39Seq {
6161
// Returns NULL if restore fails (bad BIP39 checksum).
6262
Seed * restore_seed() const;
6363

64-
private:
65-
void* ctx;
6664
// we need to have full menmonic words saved as string
6765
// which is needed to calculate mnemonic seed
6866
String get_mnemonic_as_string();
67+
68+
private:
69+
void* ctx;
6970
// menmonic seed is needed for bip32 root key
7071
// len of the returned bytes is BIP39_SEED_LEN_512
7172
bool calc_mnemonic_seed();
@@ -74,23 +75,58 @@ class BIP39Seq {
7475
class SSKRShareSeq {
7576
public:
7677
static size_t const MAX_SHARES = 16;
78+
79+
// we currently support only 16 byte seed, therefore WORDS_PER_SHARE and BYTES_PER_SHARE are a constant
80+
static size_t const WORDS_PER_SHARE = 29;
81+
static size_t const BYTES_PER_SHARE = 21;
82+
7783
const size_t METADATA_LENGTH_BYTES = 5;
7884

7985
static SSKRShareSeq * from_seed(Seed const * seed,
8086
uint8_t thresh,
8187
uint8_t nshares,
8288
void(*randgen)(uint8_t *, size_t, void *));
8389

90+
// Read-only, don't free returned value.
91+
uint8_t const * get_share(size_t ndx) const;
92+
93+
// Adds a copy of the argument, returns the share index.
94+
size_t add_share(uint8_t const * share);
95+
96+
// Replace a share's value with a copy of the argument.
97+
void set_share(size_t ndx, uint8_t const * share, size_t len);
98+
99+
// Returns NULL if restore fails, use last_error for diagnostic.
100+
Seed * restore_seed() const;
101+
102+
// Delete the specified share, compact gaps.
103+
void del_share(size_t ndx);
104+
105+
// get share from ur message ur:crypto-sskr
106+
bool get_share_from_ur(String bytewords, size_t sskr_shard_indx);
107+
108+
// Read only, don't free returned value.
84109
String get_share_word(int sharendx, int wndx);
85-
String shares_ur[MAX_SHARES]; // shares in ur format
86-
String shares[MAX_SHARES]; // shares in bytewords format
87-
size_t shares_len;
88-
size_t words_per_share;
110+
111+
String get_share_strings(size_t ndx) const;
112+
113+
size_t numshares() const { return nshares; }
114+
115+
int last_restore_error() { return last_rv; }
116+
117+
uint8_t *shares[MAX_SHARES]; // shares in bytes format
118+
String shares_ur[MAX_SHARES]; // shares in ur format (read only)
119+
String shares_bytewords[MAX_SHARES]; // shares in bytewords format (read-only)
120+
size_t shares_len; // threshold
121+
size_t bytes_in_each_share;
89122

90123
private:
91124
size_t nshares;
125+
mutable int last_rv;
92126
};
93127

128+
String bytewords_get_word(uint8_t index);
129+
94130
/**
95131
* This function is taken from libwally. We cannot import libwally_bip39 because it is clashing with
96132
* bc-bip39.h. bc-bip39.h should be deprecated and replaced with libwally_bip39. To recompile libwally

seedtool/seed.ino

Lines changed: 135 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include "seed.h"
99
#include "wally_crypto.h"
1010
#include "ur.h"
11+
#include "CborEncoder.h"
12+
#include "CborDecoder.h"
1113

1214
namespace seed_internal {
1315

@@ -52,10 +54,11 @@ SSKRShareSeq * SSKRShareSeq::from_seed(Seed const * seed,
5254
sskr_group_descriptor group = { thresh, nshares };
5355
sskr_group_descriptor groups[] = { group };
5456

55-
size_t bytes_in_each_share = 0;
5657
size_t buff_size = 1024;
5758
uint8_t buff[buff_size];
58-
59+
60+
size_t bytes_in_each_share;
61+
5962
size_t share_len = Seed::SIZE + METADATA_LENGTH_BYTES;
6063
int share_count = sskr_count_shards(group_threshold, groups, group_len);
6164

@@ -71,24 +74,25 @@ SSKRShareSeq * SSKRShareSeq::from_seed(Seed const * seed,
7174
buff_size,
7275
NULL,
7376
randgen);
74-
Serial.println(gen_share_count); Serial.println(share_count);
77+
78+
// currently we support only 29 words per share
79+
serial_assert(bytes_in_each_share == BYTES_PER_SHARE);
7580
serial_assert(gen_share_count == (int)share_count);
76-
Serial.println(bytes_in_each_share); Serial.println(share_len);
7781
serial_assert(bytes_in_each_share == share_len);
7882

7983

8084
String strings[MAX_SHARES];
8185
String ur_strings[MAX_SHARES];
82-
uint8_t v[buff_size];
86+
uint8_t shards_byte[MAX_SHARES][buff_size];
8387
for (int i = 0; i < share_count; i++) {
8488
uint8_t* bytes = buff + (i * bytes_in_each_share);
85-
memcpy(v, bytes, bytes_in_each_share);
89+
memcpy(shards_byte[i], bytes, bytes_in_each_share);
8690

8791
CborDynamicOutput output;
8892
CborWriter writer(output);
8993

9094
writer.writeTag(309);
91-
writer.writeBytes(v, bytes_in_each_share);
95+
writer.writeBytes(shards_byte[i], bytes_in_each_share);
9296

9397
// Encode cbor payload as bytewords
9498
char *payload_bytewords = bytewords_encode(bw_standard, output.getData(), output.getSize());
@@ -108,32 +112,138 @@ SSKRShareSeq * SSKRShareSeq::from_seed(Seed const * seed,
108112
}
109113

110114
SSKRShareSeq * sskr = new SSKRShareSeq();
115+
sskr->nshares = nshares;
116+
sskr->shares_len = thresh;
117+
sskr->bytes_in_each_share = bytes_in_each_share;
111118
sskr->shares_len = share_count;
112119
for (int i=0; i<share_count; i++) {
113-
sskr->shares[i] = strings[i];
114-
sskr->shares_ur[i] = ur_strings[i];
120+
sskr->shares_bytewords[i] = strings[i]; // bytewords format
121+
sskr->shares_ur[i] = ur_strings[i]; // ur format
122+
sskr->set_share(i, shards_byte[i], bytes_in_each_share); // byte format
115123
}
116124

117-
// how many words per (bytewords encoded) share are there?
118-
// count spaces. 1 space means 2 words, therefore start with 1 word
119-
sskr->words_per_share = 1;
120-
int pos = 0;
121-
while (pos < strings[0].length()) {
122-
pos = strings[0].indexOf(" ", pos);
123-
if (pos == -1) {
124-
break;
125-
}
126-
else {
127-
sskr->words_per_share++;
128-
pos++;
129-
}
130-
}
131-
132125
return sskr;
133126
}
134127

128+
size_t SSKRShareSeq::add_share(uint8_t const * share) {
129+
serial_assert(nshares < MAX_SHARES);
130+
size_t sharesz = sizeof(uint8_t) * BYTES_PER_SHARE;
131+
shares[nshares] = (uint8_t *) malloc(sharesz);
132+
memcpy(shares[nshares], share, sharesz);
133+
return nshares++;
134+
}
135+
135136
String SSKRShareSeq::get_share_word(int sharendx, int wndx) {
136-
return get_word_from_sentence(shares[sharendx], ' ', wndx);
137+
return get_word_from_sentence(shares_bytewords[sharendx], ' ', wndx);
138+
}
139+
140+
uint8_t const * SSKRShareSeq::get_share(size_t ndx) const {
141+
serial_assert(ndx < nshares);
142+
return shares[ndx];
143+
}
144+
145+
void SSKRShareSeq::set_share(size_t ndx, uint8_t const * share, size_t len) {
146+
Serial.println("** **");
147+
Serial.println(ndx); Serial.println(nshares);
148+
serial_assert(ndx < nshares);
149+
if (shares[ndx])
150+
free(shares[ndx]);
151+
size_t sharesz = sizeof(uint8_t) * len;
152+
shares[ndx] = (uint8_t *) malloc(sharesz);
153+
memcpy(shares[ndx], share, sharesz);
154+
}
155+
156+
String SSKRShareSeq::get_share_strings(size_t ndx) const {
157+
serial_assert(ndx < nshares);
158+
return shares_bytewords[ndx];
159+
}
160+
161+
Seed * SSKRShareSeq::restore_seed() const {
162+
uint8_t seed_data[Seed::SIZE];
163+
164+
Serial.println("restore seed");
165+
Serial.println(bytes_in_each_share);
166+
Serial.println(nshares);
167+
168+
last_rv = sskr_combine(const_cast<const uint8_t **>(shares),
169+
bytes_in_each_share,
170+
nshares,
171+
seed_data,
172+
sizeof(seed_data));
173+
Serial.println(last_rv);
174+
return last_rv < 0 ? NULL : new Seed(seed_data, sizeof(seed_data));
175+
}
176+
177+
class CborListen : public CborListener {
178+
public:
179+
void OnInteger(int32_t value){ };
180+
void OnBytes(unsigned char *data, unsigned int size) {Serial.println("bytes"); memcpy(bytes, data, size); len = size;};
181+
void OnString(String &str) {};
182+
void OnArray(unsigned int size) {};
183+
void OnMap(unsigned int size) {};
184+
void OnTag(uint32_t tag) { tag_ = tag; };
185+
void OnSpecial(uint32_t code) {Serial.println("tag");};
186+
void OnError(const char *error) {Serial.println("error");};
187+
188+
// we are gonna collect the tag and bytes
189+
uint32_t tag_;
190+
uint8_t bytes[200];
191+
size_t len;
192+
};
193+
194+
bool SSKRShareSeq::get_share_from_ur(String bytewords, size_t sskr_shard_indx) {
195+
uint8_t* decoded = NULL;
196+
size_t decoded_len;
197+
198+
bool ret = bytewords_decode(bw_standard, bytewords.c_str(), &decoded, &decoded_len);
199+
if (ret == false) {
200+
if (decoded)
201+
free(decoded);
202+
return ret;
203+
}
204+
205+
CborInput input(decoded, decoded_len);
206+
CborReader reader(input);
207+
CborListen listener;
208+
reader.SetListener(listener);
209+
reader.Run();
210+
211+
// https://github.com/BlockchainCommons/Research/blob/master/papers/bcr-2020-011-sskr.md
212+
if (listener.tag_ != 309)
213+
return false;
214+
215+
free(decoded);
216+
217+
if (sskr_shard_indx >= nshares) {
218+
// init a new share
219+
uint8_t empty_share[SSKRShareSeq::BYTES_PER_SHARE] = {0};
220+
add_share(empty_share);
221+
}
222+
223+
set_share(sskr_shard_indx, listener.bytes, listener.len);
224+
bytes_in_each_share = listener.len;
225+
226+
return true;
227+
}
228+
229+
void SSKRShareSeq::del_share(size_t ndx) {
230+
serial_assert(ndx < nshares);
231+
if (shares[ndx])
232+
free(shares[ndx]);
233+
// Compact any created gap.
234+
for (size_t ii = ndx; ii < nshares-1; ++ii)
235+
shares[ii] = shares[ii+1];
236+
nshares -= 1;
237+
}
238+
239+
240+
String bytewords_get_word(uint8_t index) {
241+
// FIXME bytewords are currently not exposed in the bytewords library. Once exposed, replace this function
242+
char word[5] = {0};
243+
static const char* bytewords = "ableacidalsoapexaquaarchatomauntawayaxisbackbaldbarnbeltbetabiasbluebodybragbrewbulbbuzzcalmcashcatschefcityclawcodecolacookcostcruxcurlcuspcyandarkdatadaysdelidicedietdoordowndrawdropdrumdulldutyeacheasyechoedgeepicevenexamexiteyesfactfairfernfigsfilmfishfizzflapflewfluxfoxyfreefrogfuelfundgalagamegeargemsgiftgirlglowgoodgraygrimgurugushgyrohalfhanghardhawkheathelphighhillholyhopehornhutsicedideaidleinchinkyintoirisironitemjadejazzjoinjoltjowljudojugsjumpjunkjurykeepkenokeptkeyskickkilnkingkitekiwiknoblamblavalazyleaflegsliarlistlimplionlogoloudloveluaulucklungmainmanymathmazememomenumeowmildmintmissmonknailnavyneednewsnextnoonnotenumbobeyoboeomitonyxopenovalowlspaidpartpeckplaypluspoempoolposepuffpumapurrquadquizraceramprealredorichroadrockroofrubyruinrunsrustsafesagascarsetssilkskewslotsoapsolosongstubsurfswantacotasktaxitenttiedtimetinytoiltombtoystriptunatwinuglyundouniturgeuservastveryvetovialvibeviewvisavoidvowswallwandwarmwaspwavewaxywebswhatwhenwhizwolfworkyankyawnyellyogayurtzapszestzinczonezoomzero";
244+
memcpy(word, &bytewords[index * 4], 4);
245+
word[4] = '\0';
246+
return String(word);
137247
}
138248

139249
BIP39Seq * BIP39Seq::from_words(uint16_t * words) {

seedtool/selftest.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ String selftest_testname(size_t ndx);
1111
bool selftest_testrun(size_t ndx);
1212

1313
// Used to populate dummy data in UI testing.
14-
const uint16_t * selftest_dummy_bip39();
15-
const uint16_t * selftest_dummy_sskr(size_t ndx);
16-
const uint16_t * selftest_dummy_sskr_alt(size_t ndx);
14+
const uint8_t * selftest_dummy_sskr(size_t ndx);
1715

1816
#endif // SELFTEST_H

0 commit comments

Comments
 (0)