Skip to content
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

bruig: Seed verification #174

Merged
merged 3 commits into from Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
60 changes: 60 additions & 0 deletions bruig/flutterui/bruig/lib/models/newconfig.dart
@@ -1,6 +1,8 @@
import 'dart:io';
import 'dart:math';

import 'package:bruig/config.dart';
import 'package:bruig/wordlist.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:golib_plugin/definitions.dart';
Expand All @@ -22,6 +24,14 @@ String NetworkTypeStr(NetworkType net) {
}
}

class ConfirmSeedWords {
final int position;
final String correctSeedWord;
final List<String> seedWordChoices;

ConfirmSeedWords(this.position, this.correctSeedWord, this.seedWordChoices);
}

class NewConfigModel extends ChangeNotifier {
final List<String> appArgs;
NewConfigModel(this.appArgs);
Expand All @@ -42,6 +52,7 @@ class NewConfigModel extends ChangeNotifier {
bool advancedSetup = false;
List<String> seedToRestore = [];
Uint8List? multichanBackupRestore;
List<ConfirmSeedWords> confirmSeedWords = [];

Future<LNInfo> tryExternalDcrlnd(
String host, String tlsPath, String macaroonPath) async {
Expand Down Expand Up @@ -69,6 +80,54 @@ class NewConfigModel extends ChangeNotifier {
return cfg;
}

List<ConfirmSeedWords> createConfirmSeedWords(String seed) {
List<ConfirmSeedWords> confirmSeedWords = [];
var seedWords = seed.split(' ');
var numWords = 5;
var numChoices = 3;
for (int i = 0; i < numWords; i++) {
int position;
bool positionUsed;
// Keep generating new positions until we have one that isn't used yet.
do {
positionUsed = false;
position = Random().nextInt(seedWords.length);
for (int k = 0; k < confirmSeedWords.length; k++) {
if (position == confirmSeedWords[k].position) {
positionUsed = true;
}
}
} while (positionUsed);
List<String> seedWordChoices = [seedWords[position]];
// Keep generating new words in the seedWordChoice list until its length
// is equal to the number of choices set above.
do {
var randomSeedWord = Random().nextInt(defaultWordList.length);
var found = false;
for (int j = 0; j < seedWordChoices.length; j++) {
if (defaultWordList[randomSeedWord] == seedWordChoices[j]) {
// Skip word if it's already in the list.
found = true;
}
}
if (!found) {
seedWordChoices.add(defaultWordList[randomSeedWord]);
}
} while (seedWordChoices.length < numChoices);
// Sort the word choices alphabetically each
seedWordChoices.sort((a, b) {
return a.toLowerCase().compareTo(b.toLowerCase());
});
confirmSeedWords.add(
ConfirmSeedWords(position, seedWords[position], seedWordChoices));
}
// Sort the questions by position
confirmSeedWords.sort((a, b) {
return a.position.compareTo(b.position);
});
return confirmSeedWords;
}

Future<void> createNewWallet(
String password, List<String> existingSeed) async {
var rootPath = await lnWalletDir();
Expand All @@ -80,6 +139,7 @@ class NewConfigModel extends ChangeNotifier {
NetworkTypeStr(netType), "admin.macaroon");
rpcHost = res.rpcHost;
newWalletSeed = res.seed;
confirmSeedWords = createConfirmSeedWords(newWalletSeed);
}

Future<bool> hasLNWalletDB() async {
Expand Down
2 changes: 2 additions & 0 deletions bruig/flutterui/bruig/lib/screens/new_config.dart
Expand Up @@ -7,6 +7,7 @@ import 'package:bruig/screens/newconfig/ln_choice.dart';
import 'package:bruig/screens/newconfig/ln_choice_external.dart';
import 'package:bruig/screens/newconfig/ln_choice_internal.dart';
import 'package:bruig/screens/newconfig/ln_wallet_seed.dart';
import 'package:bruig/screens/newconfig/ln_wallet_seed_confirm.dart';
import 'package:bruig/screens/newconfig/network_choice.dart';
import 'package:bruig/screens/newconfig/restore_wallet.dart';
import 'package:bruig/screens/newconfig/server.dart';
Expand Down Expand Up @@ -48,6 +49,7 @@ class _NewConfigScreenState extends State<NewConfigScreen> {
routes: {
"/newconf/initializing": (context) =>
InitializingNewConfPage(newconf),
"/newconf/confirmseed": (context) => ConfirmLNWalletSeedPage(newconf),
"/newconf/deleteOldWallet": (context) => DeleteOldWalletPage(newconf),
"/newconf/networkChoice": (context) => NetworkChoicePage(newconf),
"/newconf/lnChoice": (context) => LNChoicePage(newconf),
Expand Down
Expand Up @@ -18,7 +18,7 @@ class NewLNWalletSeedPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
void done() {
Navigator.of(context).pushNamed("/newconf/server");
Navigator.of(context).pushNamed("/newconf/confirmseed");
}

var backgroundColor = const Color(0xFF19172C);
Expand Down
@@ -0,0 +1,181 @@
import 'dart:async';

import 'package:bruig/models/newconfig.dart';
import 'package:flutter/material.dart';
import 'package:bruig/components/buttons.dart';

class ConfirmLNWalletSeedPage extends StatefulWidget {
final NewConfigModel newconf;
const ConfirmLNWalletSeedPage(this.newconf, {Key? key}) : super(key: key);

@override
State<ConfirmLNWalletSeedPage> createState() =>
_ConfirmLNWalletSeedPageState();
}

class _ConfirmLNWalletSeedPageState extends State<ConfirmLNWalletSeedPage> {
bool _visible = true;
bool answerWrong = false;
int currentQuestion = 0;

void checkAnswer(bool answer) {
setState(() {
// Only update seed if it hasn't been already st
_visible = false;
Timer(const Duration(milliseconds: 500), () {
setState(() {
if (answer) {
currentQuestion++;
} else {
answerWrong = true;
}
_visible = true;
});
});
});
}

@override
Widget build(BuildContext context) {
void done() {
Navigator.of(context).pushNamed("/newconf/server");
}

void goBack() {
// Go back to copy seed page
Navigator.of(context).pop();
// Generate new seed questions
widget.newconf.confirmSeedWords =
widget.newconf.createConfirmSeedWords(widget.newconf.newWalletSeed);
}

var backgroundColor = const Color(0xFF19172C);
var cardColor = const Color(0xFF05031A);
var textColor = const Color(0xFF8E8D98);
var secondaryTextColor = const Color(0xFFE4E3E6);
//var darkTextColor = const Color(0xFF5A5968);
var confirmSeedWords = widget.newconf.confirmSeedWords;
return Container(
color: backgroundColor,
child: Stack(children: [
Container(
decoration: const BoxDecoration(
image: DecorationImage(
fit: BoxFit.fill,
image: AssetImage("assets/images/loading-bg.png")))),
Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomLeft,
end: Alignment.topRight,
colors: [
cardColor,
const Color(0xFF07051C),
backgroundColor.withOpacity(0.34),
],
stops: const [
0,
0.17,
1
])),
padding: const EdgeInsets.all(10),
child: Column(children: [
const SizedBox(height: 89),
Text("Setting up Bison Relay",
style: TextStyle(
color: textColor,
fontSize: 34,
fontWeight: FontWeight.w200)),
const SizedBox(height: 20),
Text("Confirm New Wallet Seed",
style: TextStyle(
color: secondaryTextColor,
fontSize: 21,
fontWeight: FontWeight.w300)),
const SizedBox(height: 34),
AnimatedOpacity(
opacity: _visible ? 1.0 : 0.0,
duration: const Duration(milliseconds: 500),
child: currentQuestion < confirmSeedWords.length
? !answerWrong
? QuestionArea(
confirmSeedWords[currentQuestion], checkAnswer)
: IncorrectArea(goBack)
: Column(children: [
Text("Seed Confirmed",
style: TextStyle(
color: textColor,
fontSize: 20,
fontWeight: FontWeight.w200)),
const SizedBox(height: 20),
Center(
child: LoadingScreenButton(
onPressed: done,
text: "Continue",
))
]),
),
const SizedBox(height: 10),
const SizedBox(height: 34),
]),
)
]));
}
}

class QuestionArea extends StatelessWidget {
final ConfirmSeedWords currentWords;
final Function(bool) checkAnswersCB;
const QuestionArea(this.currentWords, this.checkAnswersCB, {Key? key})
: super(key: key);

@override
Widget build(BuildContext context) {
var textColor = const Color(0xFF8E8D98);
return Column(children: [
Center(
child: Text("Word #${currentWords.position + 1}",
style: TextStyle(
color: textColor,
fontSize: 34,
fontWeight: FontWeight.w200))),
const SizedBox(height: 20),
SizedBox(
width: 600,
child:
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
for (var i in currentWords.seedWordChoices)
Container(
margin: const EdgeInsets.all(5),
child: LoadingScreenButton(
onPressed: () =>
checkAnswersCB(currentWords.correctSeedWord == i),
text: i,
)),
]))
]);
}
}

class IncorrectArea extends StatelessWidget {
final Function() goBackCB;
const IncorrectArea(this.goBackCB, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
var textColor = const Color(0xFF8E8D98);
return Column(children: [
Center(
child: Text("Incorrect, please go back and copy the seed again.",
style: TextStyle(
color: textColor,
fontSize: 20,
fontWeight: FontWeight.w200))),
const SizedBox(height: 20),
Center(
child: LoadingScreenButton(
onPressed: goBackCB,
text: "Go back",
)),
]);
}
}