Airgapped hardware wallet based on STM32F469-Discovery board using SD card to pass transaction data

Dependencies:   mbed QSPI_DISCO_F469NI BSP_DISCO_F469NI

Committer:
stepansnigirev
Date:
Sat Jul 20 16:18:54 2019 +0000
Revision:
1:b88ef7630eb3
Parent:
0:f43431023689
Child:
2:120e0ca2f3a7
recover mnemonic

Who changed what in which revision?

UserRevisionLine numberNew contents of line
stepansnigirev 0:f43431023689 1 #include "mbed.h"
stepansnigirev 0:f43431023689 2 #include "helpers.h"
stepansnigirev 0:f43431023689 3
stepansnigirev 0:f43431023689 4 // bitcoin lib
stepansnigirev 0:f43431023689 5 #include "Bitcoin.h"
stepansnigirev 0:f43431023689 6 #include "PSBT.h"
stepansnigirev 0:f43431023689 7
stepansnigirev 0:f43431023689 8 /*********** forward declarations **************/
stepansnigirev 0:f43431023689 9
stepansnigirev 0:f43431023689 10 // generates a new mnemonic
stepansnigirev 0:f43431023689 11 string generateNewMnemonic();
stepansnigirev 0:f43431023689 12
stepansnigirev 0:f43431023689 13 // generates hd keys from the mnemonic
stepansnigirev 0:f43431023689 14 void initKeys(const string mnemonic, const string password = "");
stepansnigirev 0:f43431023689 15
stepansnigirev 0:f43431023689 16 /****************** GUI classes ****************/
stepansnigirev 0:f43431023689 17
stepansnigirev 0:f43431023689 18 Label titleLbl;
stepansnigirev 0:f43431023689 19 Label dataLbl;
stepansnigirev 0:f43431023689 20 QR qr;
stepansnigirev 0:f43431023689 21
stepansnigirev 0:f43431023689 22 Button btn;
stepansnigirev 0:f43431023689 23 Button printBtn;
stepansnigirev 0:f43431023689 24 Label lbl;
stepansnigirev 0:f43431023689 25
stepansnigirev 0:f43431023689 26 /***************** GUI functions ***************/
stepansnigirev 0:f43431023689 27
stepansnigirev 0:f43431023689 28 // if mnemonic is not present we can generate or recover it
stepansnigirev 0:f43431023689 29 void showInitScreen();
stepansnigirev 0:f43431023689 30 // if mnemonic is there we go directly to the main menu
stepansnigirev 0:f43431023689 31 void showMenu();
stepansnigirev 0:f43431023689 32 // handy function to display information to the user
stepansnigirev 0:f43431023689 33 // with a title, a message and OK button that goes to the menu
stepansnigirev 0:f43431023689 34 void showMessage(const string title, const string message);
stepansnigirev 0:f43431023689 35 static lv_res_t toMenuCallback(lv_obj_t * btn);
stepansnigirev 0:f43431023689 36 static lv_res_t showAddressesCallback(lv_obj_t * btn);
stepansnigirev 0:f43431023689 37 static lv_res_t saveXpubCallback(lv_obj_t * btn);
stepansnigirev 0:f43431023689 38 static lv_res_t signPSBTCallback(lv_obj_t * btn);
stepansnigirev 0:f43431023689 39 static lv_res_t showMnemonicCallback(lv_obj_t * btn);
stepansnigirev 0:f43431023689 40 static lv_res_t wipeCallback(lv_obj_t * btn);
stepansnigirev 0:f43431023689 41 static lv_res_t newMnemonicCallback(lv_obj_t * btn);
stepansnigirev 0:f43431023689 42 static lv_res_t enterMnemonicCallback(lv_obj_t * btn);
stepansnigirev 0:f43431023689 43 void showAddress(unsigned int child_index, bool change);
stepansnigirev 0:f43431023689 44
stepansnigirev 0:f43431023689 45 /***************** bitcoin keys ***************/
stepansnigirev 0:f43431023689 46
stepansnigirev 1:b88ef7630eb3 47 string mnemonic;
stepansnigirev 0:f43431023689 48 HDPrivateKey root; // root private key
stepansnigirev 0:f43431023689 49 HDPrivateKey account; // account master private key
stepansnigirev 0:f43431023689 50 HDPublicKey xpub; // account master public key
stepansnigirev 0:f43431023689 51 bool change = false; // internal or external address
stepansnigirev 0:f43431023689 52 unsigned int child_index = 0; // current child index
stepansnigirev 0:f43431023689 53
stepansnigirev 0:f43431023689 54 PSBT psbt; // psbt transaction we will be signing
stepansnigirev 0:f43431023689 55
stepansnigirev 0:f43431023689 56 /******************* Main part *****************/
stepansnigirev 0:f43431023689 57
stepansnigirev 0:f43431023689 58 int main(){
stepansnigirev 0:f43431023689 59 init();
stepansnigirev 0:f43431023689 60
stepansnigirev 0:f43431023689 61 string mnemonic = loadMnemonic();
stepansnigirev 0:f43431023689 62 if(mnemonic.length() == 0){
stepansnigirev 0:f43431023689 63 showInitScreen();
stepansnigirev 0:f43431023689 64 }else{
stepansnigirev 0:f43431023689 65 initKeys(mnemonic);
stepansnigirev 0:f43431023689 66 showMenu();
stepansnigirev 0:f43431023689 67 }
stepansnigirev 0:f43431023689 68
stepansnigirev 0:f43431023689 69 while(1){
stepansnigirev 0:f43431023689 70 gui.update();
stepansnigirev 0:f43431023689 71 }
stepansnigirev 0:f43431023689 72 }
stepansnigirev 0:f43431023689 73
stepansnigirev 0:f43431023689 74 /****************** GUI stuff *****************/
stepansnigirev 0:f43431023689 75 void showInitScreen(){
stepansnigirev 0:f43431023689 76 gui.clear();
stepansnigirev 0:f43431023689 77 titleLbl = Label("Let's set it up!");
stepansnigirev 0:f43431023689 78 titleLbl.size(gui.width(), 20);
stepansnigirev 0:f43431023689 79 titleLbl.position(0, 40);
stepansnigirev 0:f43431023689 80 titleLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 0:f43431023689 81
stepansnigirev 0:f43431023689 82 Button btn(newMnemonicCallback, "Generate new mnemonic");
stepansnigirev 0:f43431023689 83 btn.size(gui.width()-100, 80);
stepansnigirev 0:f43431023689 84 btn.position(0, 200);
stepansnigirev 0:f43431023689 85 btn.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 86
stepansnigirev 0:f43431023689 87 Button btn2(enterMnemonicCallback, "Enter existing mnemonic");
stepansnigirev 0:f43431023689 88 btn2.size(gui.width()-100, 80);
stepansnigirev 0:f43431023689 89 btn2.position(0, 300);
stepansnigirev 0:f43431023689 90 btn2.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 91 }
stepansnigirev 0:f43431023689 92
stepansnigirev 0:f43431023689 93 void showMenu(){
stepansnigirev 0:f43431023689 94 gui.clear();
stepansnigirev 0:f43431023689 95 titleLbl = Label("What do you want to do?");
stepansnigirev 0:f43431023689 96 titleLbl.size(gui.width(), 20);
stepansnigirev 0:f43431023689 97 titleLbl.position(0, 40);
stepansnigirev 0:f43431023689 98 titleLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 0:f43431023689 99
stepansnigirev 0:f43431023689 100 Button btn(showAddressesCallback, "Show addresses");
stepansnigirev 0:f43431023689 101 btn.size(gui.width()-100, 80);
stepansnigirev 0:f43431023689 102 btn.position(0, 100);
stepansnigirev 0:f43431023689 103 btn.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 104
stepansnigirev 0:f43431023689 105 Button btn2(saveXpubCallback, "Export xpub");
stepansnigirev 0:f43431023689 106 btn2.size(gui.width()-100, 80);
stepansnigirev 0:f43431023689 107 btn2.position(0, 300);
stepansnigirev 0:f43431023689 108 btn2.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 109
stepansnigirev 0:f43431023689 110 Button btn3(signPSBTCallback, "Sign PSBT transaction");
stepansnigirev 0:f43431023689 111 btn3.size(gui.width()-100, 80);
stepansnigirev 0:f43431023689 112 btn3.position(0, 400);
stepansnigirev 0:f43431023689 113 btn3.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 114
stepansnigirev 0:f43431023689 115 Button btn4(wipeCallback, "Wipe device");
stepansnigirev 0:f43431023689 116 btn4.size(gui.width()-100, 80);
stepansnigirev 0:f43431023689 117 btn4.position(0, 600);
stepansnigirev 0:f43431023689 118 btn4.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 119
stepansnigirev 0:f43431023689 120 Button btn5(showMnemonicCallback, "Show mnemonic");
stepansnigirev 0:f43431023689 121 btn5.size(gui.width()-100, 80);
stepansnigirev 0:f43431023689 122 btn5.position(0, 700);
stepansnigirev 0:f43431023689 123 btn5.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 124 }
stepansnigirev 0:f43431023689 125
stepansnigirev 0:f43431023689 126 void showMessage(const string title, const string message){
stepansnigirev 0:f43431023689 127 gui.clear();
stepansnigirev 0:f43431023689 128 titleLbl = Label(title);
stepansnigirev 0:f43431023689 129 titleLbl.size(gui.width(), 20);
stepansnigirev 0:f43431023689 130 titleLbl.position(0, 40);
stepansnigirev 0:f43431023689 131 titleLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 0:f43431023689 132
stepansnigirev 0:f43431023689 133 dataLbl = Label(message);
stepansnigirev 0:f43431023689 134 dataLbl.size(gui.width()-100, 100);
stepansnigirev 0:f43431023689 135 dataLbl.position(50, 300);
stepansnigirev 0:f43431023689 136 dataLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 0:f43431023689 137
stepansnigirev 0:f43431023689 138 Button btn(toMenuCallback, "OK");
stepansnigirev 0:f43431023689 139 btn.size(gui.width()-100, 80);
stepansnigirev 0:f43431023689 140 btn.position(0, gui.height()-100);
stepansnigirev 0:f43431023689 141 btn.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 142 }
stepansnigirev 0:f43431023689 143
stepansnigirev 0:f43431023689 144 static lv_res_t nextCallback(lv_obj_t * btn){
stepansnigirev 0:f43431023689 145 child_index++;
stepansnigirev 0:f43431023689 146 showAddress(child_index, change);
stepansnigirev 0:f43431023689 147 return LV_RES_OK;
stepansnigirev 0:f43431023689 148 }
stepansnigirev 0:f43431023689 149 static lv_res_t prevCallback(lv_obj_t * btn){
stepansnigirev 0:f43431023689 150 if(child_index > 0){
stepansnigirev 0:f43431023689 151 child_index--;
stepansnigirev 0:f43431023689 152 showAddress(child_index, change);
stepansnigirev 0:f43431023689 153 }
stepansnigirev 0:f43431023689 154 return LV_RES_OK;
stepansnigirev 0:f43431023689 155 }
stepansnigirev 0:f43431023689 156 static lv_res_t changeCallback(lv_obj_t * btn){
stepansnigirev 0:f43431023689 157 change = !change;
stepansnigirev 0:f43431023689 158 showAddress(child_index, change);
stepansnigirev 0:f43431023689 159 return LV_RES_OK;
stepansnigirev 0:f43431023689 160 }
stepansnigirev 0:f43431023689 161 static lv_res_t xpubCallback(lv_obj_t * btn){
stepansnigirev 0:f43431023689 162 titleLbl.text("Your master public key");
stepansnigirev 0:f43431023689 163 qr.text(xpub.toString());
stepansnigirev 0:f43431023689 164 dataLbl.text(xpub.toString());
stepansnigirev 0:f43431023689 165 qr.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 166 dataLbl.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 167 return LV_RES_OK;
stepansnigirev 0:f43431023689 168 }
stepansnigirev 0:f43431023689 169
stepansnigirev 0:f43431023689 170 void showAddress(unsigned int child_index, bool change){
stepansnigirev 0:f43431023689 171 stringstream title;
stepansnigirev 0:f43431023689 172 title << "Your ";
stepansnigirev 0:f43431023689 173 if(change){
stepansnigirev 0:f43431023689 174 title << "change ";
stepansnigirev 0:f43431023689 175 }else{
stepansnigirev 0:f43431023689 176 title << "receiving ";
stepansnigirev 0:f43431023689 177 }
stepansnigirev 0:f43431023689 178 title << "address #" << child_index << ":";
stepansnigirev 0:f43431023689 179 titleLbl.text(title.str());
stepansnigirev 0:f43431023689 180 string address = xpub.child(change).child(child_index).address();
stepansnigirev 0:f43431023689 181 dataLbl.text(address);
stepansnigirev 0:f43431023689 182 dataLbl.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 183 qr.text(string("bitcoin:") + address);
stepansnigirev 0:f43431023689 184 qr.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 185 }
stepansnigirev 0:f43431023689 186
stepansnigirev 0:f43431023689 187 void showAddressScreen(){
stepansnigirev 0:f43431023689 188 gui.clear();
stepansnigirev 0:f43431023689 189 titleLbl = Label("Your address");
stepansnigirev 0:f43431023689 190 titleLbl.size(gui.width(), 20);
stepansnigirev 0:f43431023689 191 titleLbl.position(0, 40);
stepansnigirev 0:f43431023689 192 titleLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 0:f43431023689 193
stepansnigirev 0:f43431023689 194 dataLbl = Label(" ");
stepansnigirev 0:f43431023689 195 dataLbl.size(gui.width()-100, 100); // full width
stepansnigirev 0:f43431023689 196 dataLbl.position(50, gui.height()-300);
stepansnigirev 0:f43431023689 197 dataLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 0:f43431023689 198
stepansnigirev 0:f43431023689 199 qr = QR(" ");
stepansnigirev 0:f43431023689 200 qr.size(gui.width()-100);
stepansnigirev 0:f43431023689 201 qr.position(0, 100);
stepansnigirev 0:f43431023689 202 qr.align(ALIGN_CENTER);
stepansnigirev 0:f43431023689 203
stepansnigirev 0:f43431023689 204 Button btn(nextCallback, "Next address");
stepansnigirev 0:f43431023689 205 btn.size(gui.width()/3-20, 80);
stepansnigirev 0:f43431023689 206 btn.position(gui.width()*2/3 + 10, gui.height()-100);
stepansnigirev 0:f43431023689 207
stepansnigirev 0:f43431023689 208 Button btn2(prevCallback, "Previous address");
stepansnigirev 0:f43431023689 209 btn2.size(gui.width()/3-20, 80);
stepansnigirev 0:f43431023689 210 btn2.position(10, gui.height()-100);
stepansnigirev 0:f43431023689 211
stepansnigirev 0:f43431023689 212 Button btn3(changeCallback, "Toggle\nchange");
stepansnigirev 0:f43431023689 213 btn3.size(gui.width()/3-20, 80);
stepansnigirev 0:f43431023689 214 btn3.position(gui.width()/3 + 10, gui.height()-100);
stepansnigirev 0:f43431023689 215
stepansnigirev 0:f43431023689 216 Button btn4(xpubCallback, "Show xpub");
stepansnigirev 0:f43431023689 217 btn4.size(gui.width()/2-20, 80);
stepansnigirev 0:f43431023689 218 btn4.position(gui.width()/2+10, gui.height()-200);
stepansnigirev 0:f43431023689 219
stepansnigirev 0:f43431023689 220 Button btn5(toMenuCallback, "Menu");
stepansnigirev 0:f43431023689 221 btn5.size(gui.width()/2-20, 80);
stepansnigirev 0:f43431023689 222 btn5.position(10, gui.height()-200);
stepansnigirev 0:f43431023689 223 }
stepansnigirev 0:f43431023689 224
stepansnigirev 0:f43431023689 225 static lv_res_t toMenuCallback(lv_obj_t * btn){
stepansnigirev 0:f43431023689 226 showMenu();
stepansnigirev 0:f43431023689 227 return LV_RES_OK;
stepansnigirev 0:f43431023689 228 }
stepansnigirev 0:f43431023689 229
stepansnigirev 0:f43431023689 230 static lv_res_t showAddressesCallback(lv_obj_t * btn){
stepansnigirev 0:f43431023689 231 showAddressScreen();
stepansnigirev 0:f43431023689 232 showAddress(child_index, change);
stepansnigirev 0:f43431023689 233 return LV_RES_OK;
stepansnigirev 0:f43431023689 234 }
stepansnigirev 0:f43431023689 235 static lv_res_t saveXpubCallback(lv_obj_t * btn){
stepansnigirev 0:f43431023689 236 // TODO:
stepansnigirev 0:f43431023689 237 // - check if SD card present
stepansnigirev 0:f43431023689 238 // - save xpub to the "xpub.txt"
stepansnigirev 0:f43431023689 239 // - check if write was sucessful
stepansnigirev 0:f43431023689 240 // - show success / fail message
stepansnigirev 0:f43431023689 241 if(!SD.detected()){
stepansnigirev 0:f43431023689 242 showMessage("Error", "SD card is not present");
stepansnigirev 0:f43431023689 243 return LV_RES_OK;
stepansnigirev 0:f43431023689 244 }
stepansnigirev 0:f43431023689 245 int res = SD.save("xpub.txt", xpub.toString());
stepansnigirev 0:f43431023689 246 if(res != SD_SUCCESS){
stepansnigirev 0:f43431023689 247 showMessage("Error", "Something wrong with SD card");
stepansnigirev 0:f43431023689 248 return LV_RES_OK;
stepansnigirev 0:f43431023689 249 }
stepansnigirev 0:f43431023689 250 showMessage("Success","Master public key is saved to the SD card");
stepansnigirev 0:f43431023689 251 return LV_RES_OK;
stepansnigirev 0:f43431023689 252 }
stepansnigirev 0:f43431023689 253 static lv_res_t signConfirmCallback(lv_obj_t * btn){
stepansnigirev 0:f43431023689 254 if(!SD.detected()){
stepansnigirev 0:f43431023689 255 showMessage("Error", "SD card is not present");
stepansnigirev 0:f43431023689 256 return LV_RES_OK;
stepansnigirev 0:f43431023689 257 }
stepansnigirev 0:f43431023689 258 psbt.sign(account);
stepansnigirev 0:f43431023689 259
stepansnigirev 0:f43431023689 260 uint8_t * raw = (uint8_t *)calloc(psbt.length(), sizeof(uint8_t));
stepansnigirev 0:f43431023689 261 size_t len = psbt.serialize(raw, psbt.length());
stepansnigirev 0:f43431023689 262 psbt = PSBT();
stepansnigirev 0:f43431023689 263 // cout << len << endl;
stepansnigirev 0:f43431023689 264 char * b64 = (char *)calloc(2*len+1, sizeof(char));
stepansnigirev 0:f43431023689 265 len = toBase64(raw, len, b64, 2*len+1);
stepansnigirev 0:f43431023689 266 free(raw);
stepansnigirev 0:f43431023689 267
stepansnigirev 0:f43431023689 268 int res = SD.save("signed.psbt", b64);
stepansnigirev 0:f43431023689 269 free(b64);
stepansnigirev 0:f43431023689 270 if(res != SD_SUCCESS){
stepansnigirev 0:f43431023689 271 showMessage("Error", "Something wrong with SD card");
stepansnigirev 0:f43431023689 272 return LV_RES_OK;
stepansnigirev 0:f43431023689 273 }
stepansnigirev 0:f43431023689 274 showMessage("Success", "Signed transaction saved to the SD card");
stepansnigirev 0:f43431023689 275 return LV_RES_OK;
stepansnigirev 0:f43431023689 276 }
stepansnigirev 0:f43431023689 277 void showSignRequest(){
stepansnigirev 0:f43431023689 278 stringstream ss;
stepansnigirev 0:f43431023689 279 ss << "Sending:\n\n";
stepansnigirev 0:f43431023689 280 for(unsigned int i=0; i<psbt.tx.outputsNumber; i++){
stepansnigirev 0:f43431023689 281 // TODO: better to put it into PSBT method like check_output or something
stepansnigirev 0:f43431023689 282 bool mine = false;
stepansnigirev 0:f43431023689 283 if(psbt.txOutsMeta[i].derivationsLen > 0){
stepansnigirev 0:f43431023689 284 for(unsigned int j=0; j<psbt.txOutsMeta[i].derivationsLen; j++){
stepansnigirev 0:f43431023689 285 HDPrivateKey prv = account.derive(psbt.txOutsMeta[i].derivations[j].derivation, psbt.txOutsMeta[i].derivations[j].derivationLen);
stepansnigirev 0:f43431023689 286 PublicKey pub = prv.publicKey();
stepansnigirev 0:f43431023689 287 // TODO: fix public key comparison
stepansnigirev 0:f43431023689 288 if(pub.toString() == psbt.txOutsMeta[i].derivations[j].pubkey.toString()){
stepansnigirev 0:f43431023689 289 mine = true;
stepansnigirev 0:f43431023689 290 }
stepansnigirev 0:f43431023689 291 }
stepansnigirev 0:f43431023689 292 }
stepansnigirev 0:f43431023689 293 ss << psbt.tx.txOuts[i].address(&Testnet);
stepansnigirev 0:f43431023689 294 if(mine){
stepansnigirev 0:f43431023689 295 ss << " (change)";
stepansnigirev 0:f43431023689 296 }
stepansnigirev 0:f43431023689 297 ss << ": " << (float(psbt.tx.txOuts[i].amount)/1e5) << " mBTC\n\n";
stepansnigirev 0:f43431023689 298 }
stepansnigirev 0:f43431023689 299 ss << "Fee: " << (float(psbt.fee())/1e5) << " mBTC";
stepansnigirev 0:f43431023689 300
stepansnigirev 0:f43431023689 301 gui.clear();
stepansnigirev 0:f43431023689 302 titleLbl = Label("Sign transaction?");
stepansnigirev 0:f43431023689 303 titleLbl.size(gui.width(), 20);
stepansnigirev 0:f43431023689 304 titleLbl.position(0, 40);
stepansnigirev 0:f43431023689 305 titleLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 0:f43431023689 306
stepansnigirev 0:f43431023689 307 dataLbl = Label(ss.str());
stepansnigirev 0:f43431023689 308 dataLbl.size(gui.width()-100, 100);
stepansnigirev 0:f43431023689 309 dataLbl.position(50, 300);
stepansnigirev 0:f43431023689 310 dataLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 0:f43431023689 311
stepansnigirev 0:f43431023689 312 Button btn(toMenuCallback, "Cancel");
stepansnigirev 0:f43431023689 313 btn.size(gui.width()/2-45, 80);
stepansnigirev 0:f43431023689 314 btn.position(30, gui.height()-100);
stepansnigirev 0:f43431023689 315
stepansnigirev 0:f43431023689 316 Button btn2(signConfirmCallback, "Confirm");
stepansnigirev 0:f43431023689 317 btn2.size(gui.width()/2-45, 80);
stepansnigirev 0:f43431023689 318 btn2.position(gui.width()/2+30, gui.height()-100);
stepansnigirev 0:f43431023689 319 }
stepansnigirev 0:f43431023689 320 static lv_res_t signPSBTCallback(lv_obj_t * btn){
stepansnigirev 0:f43431023689 321 if(!SD.detected()){
stepansnigirev 0:f43431023689 322 showMessage("Error", "SD card is not present");
stepansnigirev 0:f43431023689 323 return LV_RES_OK;
stepansnigirev 0:f43431023689 324 }
stepansnigirev 0:f43431023689 325 string s = SD.read("unsigned.psbt");
stepansnigirev 0:f43431023689 326 if(s.length() == 0){
stepansnigirev 0:f43431023689 327 showMessage("Fail", "Can't read unsigned.psbt file");
stepansnigirev 0:f43431023689 328 return LV_RES_OK;
stepansnigirev 0:f43431023689 329 }
stepansnigirev 0:f43431023689 330 uint8_t raw[1000];
stepansnigirev 0:f43431023689 331 unsigned int len = fromBase64(s.c_str(), s.length(), raw, sizeof(raw));
stepansnigirev 0:f43431023689 332 if(len == 0){
stepansnigirev 0:f43431023689 333 showMessage("Fail", "Can't convert from base64");
stepansnigirev 0:f43431023689 334 return LV_RES_OK;
stepansnigirev 0:f43431023689 335 }
stepansnigirev 0:f43431023689 336 psbt.reset();
stepansnigirev 0:f43431023689 337 len = psbt.parse(raw, len);
stepansnigirev 0:f43431023689 338 if(len == 0){
stepansnigirev 0:f43431023689 339 showMessage("Fail", "Can't parse PSBT");
stepansnigirev 0:f43431023689 340 return LV_RES_OK;
stepansnigirev 0:f43431023689 341 }
stepansnigirev 0:f43431023689 342 showSignRequest();
stepansnigirev 0:f43431023689 343 return LV_RES_OK;
stepansnigirev 0:f43431023689 344 }
stepansnigirev 0:f43431023689 345 static lv_res_t wipeCallback(lv_obj_t * btn){
stepansnigirev 0:f43431023689 346 wipe();
stepansnigirev 0:f43431023689 347 return LV_RES_OK;
stepansnigirev 0:f43431023689 348 }
stepansnigirev 0:f43431023689 349
stepansnigirev 0:f43431023689 350 /*************** mnemonic stuff ***************/
stepansnigirev 0:f43431023689 351
stepansnigirev 0:f43431023689 352 static lv_res_t newMnemonicCallback(lv_obj_t * btn){
stepansnigirev 0:f43431023689 353 // TODO:
stepansnigirev 0:f43431023689 354 // - generate random buffer (16 or 32 bytes)
stepansnigirev 0:f43431023689 355 // - create a new mnemonic from it
stepansnigirev 0:f43431023689 356 // - save this mnemonic
stepansnigirev 0:f43431023689 357 // - display it to the user
stepansnigirev 0:f43431023689 358 uint8_t randomBuffer[16];
stepansnigirev 0:f43431023689 359 getRandomBuffer(randomBuffer, sizeof(randomBuffer));
stepansnigirev 0:f43431023689 360 string mnemonic = generateMnemonic(randomBuffer, sizeof(randomBuffer));
stepansnigirev 0:f43431023689 361 saveMnemonic(mnemonic);
stepansnigirev 0:f43431023689 362 showMessage("Write down your recovery phrase:", mnemonic);
stepansnigirev 0:f43431023689 363 initKeys(mnemonic);
stepansnigirev 0:f43431023689 364 return LV_RES_OK;
stepansnigirev 0:f43431023689 365 }
stepansnigirev 0:f43431023689 366
stepansnigirev 0:f43431023689 367 static lv_res_t showMnemonicCallback(lv_obj_t * btn){
stepansnigirev 0:f43431023689 368 string mnemonic = loadMnemonic();
stepansnigirev 0:f43431023689 369 showMessage("Here is your recovery phrase:", mnemonic);
stepansnigirev 0:f43431023689 370 return LV_RES_OK;
stepansnigirev 0:f43431023689 371 }
stepansnigirev 0:f43431023689 372
stepansnigirev 1:b88ef7630eb3 373 static const char * keys[] = {"q","w","e","r","t","y","u","i","o","p","\n",
stepansnigirev 1:b88ef7630eb3 374 "a","s","d","f","g","h","j","k","l","\n",
stepansnigirev 1:b88ef7630eb3 375 " ","z","x","c","v","b","n","m","<",""};
stepansnigirev 1:b88ef7630eb3 376
stepansnigirev 1:b88ef7630eb3 377 static lv_res_t typeCallback(lv_obj_t * btn, const char * key){
stepansnigirev 1:b88ef7630eb3 378 if(key[0] == '<'){
stepansnigirev 1:b88ef7630eb3 379 if(mnemonic.length() > 0){
stepansnigirev 1:b88ef7630eb3 380 mnemonic = mnemonic.substr(0,mnemonic.length()-1);
stepansnigirev 1:b88ef7630eb3 381 }
stepansnigirev 1:b88ef7630eb3 382 }else{
stepansnigirev 1:b88ef7630eb3 383 mnemonic += key;
stepansnigirev 1:b88ef7630eb3 384 }
stepansnigirev 1:b88ef7630eb3 385 dataLbl.text(mnemonic);
stepansnigirev 1:b88ef7630eb3 386 return LV_RES_OK;
stepansnigirev 1:b88ef7630eb3 387 }
stepansnigirev 1:b88ef7630eb3 388
stepansnigirev 1:b88ef7630eb3 389 static lv_res_t checkMnemonicCallback(lv_obj_t * btn){
stepansnigirev 1:b88ef7630eb3 390 if(checkMnemonic(mnemonic.c_str())){
stepansnigirev 1:b88ef7630eb3 391 saveMnemonic(mnemonic);
stepansnigirev 1:b88ef7630eb3 392 initKeys(mnemonic);
stepansnigirev 1:b88ef7630eb3 393 showMessage("Mnemonic is ok", "Recovered sucessfully");
stepansnigirev 1:b88ef7630eb3 394 }else{
stepansnigirev 1:b88ef7630eb3 395 mnemonic = "";
stepansnigirev 1:b88ef7630eb3 396 dataLbl.text(mnemonic);
stepansnigirev 1:b88ef7630eb3 397 }
stepansnigirev 1:b88ef7630eb3 398 return LV_RES_OK;
stepansnigirev 1:b88ef7630eb3 399 }
stepansnigirev 1:b88ef7630eb3 400
stepansnigirev 0:f43431023689 401 static lv_res_t enterMnemonicCallback(lv_obj_t * btn){
stepansnigirev 1:b88ef7630eb3 402 gui.clear();
stepansnigirev 1:b88ef7630eb3 403 mnemonic = "";
stepansnigirev 1:b88ef7630eb3 404 titleLbl = Label("Enter your mnemonic");
stepansnigirev 1:b88ef7630eb3 405 titleLbl.size(gui.width(), 20);
stepansnigirev 1:b88ef7630eb3 406 titleLbl.position(0, 40);
stepansnigirev 1:b88ef7630eb3 407 titleLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 1:b88ef7630eb3 408
stepansnigirev 1:b88ef7630eb3 409 dataLbl = Label(mnemonic);
stepansnigirev 1:b88ef7630eb3 410 dataLbl.size(gui.width()-100, 50);
stepansnigirev 1:b88ef7630eb3 411 dataLbl.position(50, 200);
stepansnigirev 1:b88ef7630eb3 412 dataLbl.alignText(ALIGN_TEXT_CENTER);
stepansnigirev 1:b88ef7630eb3 413
stepansnigirev 1:b88ef7630eb3 414 Keyboard kb(typeCallback, keys);
stepansnigirev 1:b88ef7630eb3 415 kb.size(gui.width(), gui.height()/3);
stepansnigirev 1:b88ef7630eb3 416 kb.position(0, gui.height()*2/3-150);
stepansnigirev 1:b88ef7630eb3 417
stepansnigirev 1:b88ef7630eb3 418 Button btnx(checkMnemonicCallback, "Continue");
stepansnigirev 1:b88ef7630eb3 419 btnx.size(gui.width()-100, 80);
stepansnigirev 1:b88ef7630eb3 420 btnx.position(50, gui.height()-100);
stepansnigirev 0:f43431023689 421 return LV_RES_OK;
stepansnigirev 0:f43431023689 422 }
stepansnigirev 0:f43431023689 423
stepansnigirev 0:f43431023689 424 void initKeys(const string mnemonic, const string password){
stepansnigirev 0:f43431023689 425 // TODO:
stepansnigirev 0:f43431023689 426 // - derive root key from the mnemonic and empty password
stepansnigirev 0:f43431023689 427 // - derive account key using m/84'/1'/0'/ derivation path
stepansnigirev 0:f43431023689 428 // - get account master public key
stepansnigirev 0:f43431023689 429 root.fromMnemonic(mnemonic, password);
stepansnigirev 0:f43431023689 430 account = root.derive("m/84'/1'/0'/");
stepansnigirev 0:f43431023689 431 xpub = account.xpub();
stepansnigirev 0:f43431023689 432 }