Skip to content

Commit 46ce8bd

Browse files
committed
Improvements to key management GUI
1 parent e9630c6 commit 46ce8bd

File tree

4 files changed

+82
-40
lines changed

4 files changed

+82
-40
lines changed

convex-gui/src/main/java/convex/gui/keys/KeyGenPanel.java

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package convex.gui.keys;
22

3-
import java.awt.BorderLayout;
43
import java.awt.Color;
54
import java.awt.Font;
65
import java.util.List;
@@ -35,7 +34,7 @@
3534
@SuppressWarnings("serial")
3635
public class KeyGenPanel extends JPanel {
3736

38-
private static final String NOTE_CONSTRAINT = "align 50%,span,width 100:600:1000";
37+
private static final String NOTE_CONSTRAINT = "align 25%,span,width 100:800:1000";
3938
private static final String TEXTAREA_CONSTRAINT = "grow,width 10:500:800";
4039
JTextArea mnemonicArea;
4140
JPasswordField passArea;
@@ -56,6 +55,9 @@ public class KeyGenPanel extends JPanel {
5655
JPanel formPanel;
5756

5857
static Font HEX_FONT=Toolkit.MONO_FONT;
58+
59+
static final int CC = Constants.CHAIN_CODE;
60+
static final String DEAFULT_BIP32_PATH="m/44/"+CC+"/0/0/0";
5961

6062
/**
6163
* Format a hex string in blocks for digits
@@ -131,7 +133,7 @@ private String checkWarnings(String s, String p) {
131133
if (BIP39.extendWord(badWord)!=null) {
132134
warn += "Should normalise abbreviated word: "+badWord+". ";
133135
} else {
134-
warn +="Not in standard word list: "+badWord+". ";
136+
warn +="Not in standard word list: "+badWord.toUpperCase()+". ";
135137
}
136138
} else if (!s.equals(BIP39.normaliseFormat(s))) {
137139
warn+="Not normalised! ";
@@ -242,17 +244,19 @@ private void generatePublicKey() {
242244
* @param manager GUI manager root component
243245
*/
244246
public KeyGenPanel(PeerGUI manager) {
245-
setLayout(new BorderLayout());
247+
setLayout(new MigLayout());
246248

247249
// Main Key generation form
248250

249251
formPanel = new JPanel();
250252
// formPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
251253
formPanel.setLayout(new MigLayout("wrap 3","[][40]10[grow,fill]",""));
252-
add(formPanel, BorderLayout.CENTER);
254+
add(formPanel, "dock center");
255+
256+
addNote("Seed Phrase","Press 'Generate' to create a Mnemonic seed phrase (normally 12 words) or enter words you have already created elsewhere. You should also use a good passphrase. Keep a safe backup." );;
253257

254258
{ // Mnemonic entry box
255-
addLabel("Mnemonic Phrase","BIP39 Mnemonic phrase. These should be 12, 15, 18, 21 or 24 random words from the BIP39 standard word list.");
259+
addLabel("Mnemonic Phrase","BIP39 Mnemonic phrase. These should be 12, 15, 18, 21 or 24 random words from the BIP39 standard word list.\n\nKeep a secure offline backup of this along with the passphrase so that you can re-generate your key pairs if required. If you are serious about your key security, engraving on metal plates and locking in a safe is a reasonable idea.");
256260
mnemonicArea = makeTextArea();
257261
mnemonicArea.setWrapStyleWord(true);
258262
mnemonicArea.setLineWrap(true);
@@ -269,17 +273,32 @@ public KeyGenPanel(PeerGUI manager) {
269273
}
270274

271275
{ // Passphrase entry box
272-
addLabel("Passphrase","BIP39 secret passphrase. This acts as a secret 'extra word' to generate the BIP39 seed alongside the mnemonic. Strong passphrase recommended.");
276+
addLabel("Passphrase","BIP39 secret passphrase. This acts as a secret 'extra word' to generate the BIP39 seed alongside the mnemonic. Strong passphrase recommended.\n\nYou can leave this blank, but then anyone with access to the mnemonic words can easily re-create and use your keys.");
273277
passArea = new JPasswordField();
274-
passArea.setBackground(Color.BLACK);
275-
formPanel.add(passArea,"grow,width 10:300:400");
278+
passArea.setBackground(Color.BLACK);
279+
formPanel.add(Toolkit.wrapPasswordField(passArea));
276280
passArea.getDocument().addDocumentListener(Toolkit.createDocumentListener(() -> {
277281
if (!passArea.isFocusOwner()) return;
278282
updatePass();
279283
}));
280284
}
281285

282-
{
286+
{
287+
formPanel.add(new JPanel()); // skip first 2 columns
288+
formPanel.add(new JPanel()); // skip first 2 columns
289+
warningArea = makeTextArea();
290+
291+
warningArea.setLineWrap(true);
292+
warningArea.setWrapStyleWord(true);
293+
warningArea.setEditable(false);
294+
warningArea.setToolTipText("This is a quick heuristic check of mnemonic and passphrase.\nHeeding any warnings is advised, but you can ignore them if you know what you are doing (or don't care).");
295+
formPanel.add(warningArea,TEXTAREA_CONSTRAINT);
296+
}
297+
298+
addNote("Key Derivation","Below are the BIP39 / SLIP-10 key derivation steps. You can usually ignore these, but they are available in case you want to verify or modify the key derivation steps.");
299+
300+
301+
{
283302
addLabel("BIP39 Seed","This is the BIP39 seed generated from the mnemonic and passphrase. You can also enter this directly.");
284303
seedArea = makeTextArea();
285304
seedArea.setRows(2);
@@ -294,20 +313,8 @@ public KeyGenPanel(PeerGUI manager) {
294313
}));
295314
}
296315

297-
{
298-
formPanel.add(new JPanel()); // skip first 2 columns
299-
formPanel.add(new JPanel()); // skip first 2 columns
300-
warningArea = makeTextArea();
301-
302-
warningArea.setLineWrap(true);
303-
warningArea.setWrapStyleWord(true);
304-
warningArea.setEditable(false);
305-
warningArea.setToolTipText("This is a quick heuristic check of mnemonic and passphrase.\nHeeding any warnings is advised, but you can ignore them if you know what you are doing (or don't care).");
306-
formPanel.add(warningArea,TEXTAREA_CONSTRAINT);
307-
}
308316

309317

310-
addNote("Once the BIP39 seed is generated, we use SLIP-10 to create a derivation path to an Ed25519 private key. \n\nInstead of a BIP39 seed, you can also use another good secret source of random entropy, e.g. SLIP-0039.");
311318

312319
{
313320
addLabel("SLIP-10 Master Key","SLIP-10 creates a Master Key from the BIP39 seed, which acts as the root of key generation for a heirarchical deterministic wallet.");
@@ -321,7 +328,6 @@ public KeyGenPanel(PeerGUI manager) {
321328
}
322329

323330
{
324-
int CC = Constants.CHAIN_CODE;
325331
addLabel("BIP32 Path","This is the hierarchical path for key generation as defined in BIP32. \n - 'm' specifies the master key. \n - 44 is the purpose defined as BIP-44. \n - "+CC+" is the Convex SLIP-0044 chain code. \n - The other numbers (account, change, index) may be set at the user's discretion, but are zero by default. \n\nIf you want multiple keys for the same mnemomic, is is recommended to increment the account number. \n\nWARNING: if you change these values, you will need to retain them in order to recover the specified key.");
326332
derivationArea = makeTextArea();
327333

@@ -330,7 +336,7 @@ public KeyGenPanel(PeerGUI manager) {
330336
derivationArea.setBackground(Color.BLACK);
331337

332338
formPanel.add(derivationArea,TEXTAREA_CONSTRAINT);
333-
derivationArea.setText("m/44/"+CC+"/0/0/0");
339+
derivationArea.setText(DEAFULT_BIP32_PATH);
334340
derivationArea.getDocument().addDocumentListener(Toolkit.createDocumentListener(() -> {
335341
if (!derivationArea.isFocusOwner()) return;
336342
updatePath();
@@ -346,6 +352,9 @@ public KeyGenPanel(PeerGUI manager) {
346352
formPanel.add(derivedKeyArea,TEXTAREA_CONSTRAINT);
347353
derivedKeyArea.setText("(not ready)");
348354
}
355+
356+
addNote("Ed5519 Key Pair","Below is the Ed25519 Key Pair that is actually used by Convex. You can safely share the public key and Identicon image. Keep the private seed secret!");
357+
349358

350359
{
351360
addLabel("Private Ed25519 seed","This is the Ed25519 private seed you need to sign transactions in Convex. \nAny 32-byte hex value will work: you can enter this directly if you obtained a good secret random seed from another source.",true);
@@ -373,13 +382,13 @@ public KeyGenPanel(PeerGUI manager) {
373382
identicon=new Identicon(null,Toolkit.IDENTICON_SIZE_LARGE);
374383
// identicon.setBorder(null);
375384
addLabel("Identicon","This is a visual representation of the public key. It can be used to visually identify different keys.");
376-
formPanel.add(identicon,"grow 0");
385+
formPanel.add(identicon, "growx 0");
377386

378387
////////////////////////////////////////////////////////////////
379388
// Action panel with buttons
380389

381390
JPanel actionPanel = new ActionPanel();
382-
add(actionPanel, BorderLayout.SOUTH);
391+
add(actionPanel, "dock south");
383392

384393
JButton btnRecreate = new ActionButton("Generate",0xe5d5,e -> {
385394
Integer wc=(Integer) numSpinner.getValue();
@@ -395,10 +404,10 @@ public KeyGenPanel(PeerGUI manager) {
395404
numSpinner.setFocusable(false);
396405
actionPanel.add(numSpinner);
397406

398-
JButton btnNewButton = new ActionButton("Export...",0xebbe,e->{
399-
400-
});
401-
actionPanel.add(btnNewButton);
407+
// JButton btnNewButton = new ActionButton("Export...",0xebbe,e->{
408+
//
409+
// });
410+
// actionPanel.add(btnNewButton);
402411

403412
{ // Button to Normalise Mnemonic string
404413
JButton btnNormalise = new ActionButton("Normalise Mnemonic",0xf0ff,e -> {
@@ -407,7 +416,7 @@ public KeyGenPanel(PeerGUI manager) {
407416
mnemonicArea.setText(s2);
408417
updateMnemonic();
409418
});
410-
btnNormalise.setToolTipText("Press to normalise mnemonic text according to BIP39. Removes irregular whitespace, sets characters to lowercase.");
419+
btnNormalise.setToolTipText("Press to normalise mnemonic text according to BIP39. Removes irregular whitespace, sets characters to lowercase, highlights faulty words.");
411420
actionPanel.add(btnNormalise);
412421
}
413422

@@ -434,8 +443,8 @@ private void addLabel(String labelText,String helpText, boolean bold) {
434443
formPanel.add(Toolkit.makeHelp(helpText));
435444
}
436445

437-
private void addNote(String s) {
438-
JComponent ta = Toolkit.makeNote("NOTE",s);
446+
private void addNote(String title,String s) {
447+
JComponent ta = Toolkit.makeNote(title,s);
439448
formPanel.add(ta,NOTE_CONSTRAINT);
440449
}
441450

convex-gui/src/main/java/convex/gui/keys/KeyRingPanel.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,12 @@ public KeyRingPanel() {
129129

130130
private void loadStore(ActionEvent e) {
131131
try {
132-
File f=chooseKeyStore(this);
132+
File f=chooseKeyStore(this,"Load");
133133
if (f==null) return;
134134
if (f.exists()) {
135135
KeyStore ks=PFXTools.loadStore(f, null);
136136
int num=loadKeys(ks,"Loaded from "+f.getCanonicalPath());
137-
Toolkit.showMessge(this,num+" new keys imported.");
137+
Toolkit.showMessge(this,num+" new keys loaded.");
138138
}
139139
} catch (IOException | GeneralSecurityException e1) {
140140
// TODO Auto-generated catch block
@@ -147,15 +147,17 @@ private void loadStore(ActionEvent e) {
147147
* @param parent
148148
* @return
149149
*/
150-
private static File chooseKeyStore(Component parent) {
150+
private static File chooseKeyStore(Component parent,String action) {
151151
JFileChooser chooser = new JFileChooser();
152152
FileNameExtensionFilter filter = new FileNameExtensionFilter("PKCS #12 Keystore", "p12", "pfx");
153153
chooser.setFileFilter(filter);
154154
File defaultDir=FileUtils.getFile(Constants.DEFAULT_KEYSTORE_FILENAME);
155155
if (defaultDir.isDirectory()) {
156156
chooser.setCurrentDirectory(defaultDir);
157+
} else {
158+
chooser.setCurrentDirectory(defaultDir.getParentFile());
157159
}
158-
int returnVal = chooser.showOpenDialog(parent);
160+
int returnVal = chooser.showDialog(parent,action);
159161
if(returnVal != JFileChooser.APPROVE_OPTION) return null;
160162
File f=chooser.getSelectedFile();
161163
return f;
@@ -247,7 +249,7 @@ public static AWalletEntry getKeyRingEntry(AccountKey publicKey) {
247249
public static boolean saveKey(Component parent, AWalletEntry walletEntry) {
248250
boolean locked=walletEntry.isLocked();
249251
try {
250-
File f=chooseKeyStore(parent);
252+
File f=chooseKeyStore(parent,"Save Key");
251253
if (f==null) return false;
252254
KeyStore ks;
253255
if (f.exists()) {

convex-gui/src/main/java/convex/gui/keys/UnlockWalletDialog.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ public UnlockWalletDialog(AWalletEntry walletEntry) {
6363

6464
passwordField = new JPasswordField();
6565
passwordField.setFont(new Font("Monospaced", Font.BOLD, 13));
66-
passwordField.setColumns(20);
67-
passPanel.add(passwordField);
66+
passwordField.setColumns(40);
67+
passPanel.add(Toolkit.wrapPasswordField(passwordField));
6868
mainPanel.add(passPanel);
6969

7070

convex-gui/src/main/java/convex/gui/utils/Toolkit.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
import javax.swing.JLabel;
3333
import javax.swing.JMenuItem;
3434
import javax.swing.JOptionPane;
35+
import javax.swing.JPanel;
36+
import javax.swing.JPasswordField;
3537
import javax.swing.JScrollBar;
3638
import javax.swing.JScrollPane;
3739
import javax.swing.JTextArea;
@@ -52,6 +54,7 @@
5254
import com.formdev.flatlaf.util.SystemInfo;
5355

5456
import convex.core.util.Utils;
57+
import net.miginfocom.swing.MigLayout;
5558

5659
@SuppressWarnings("serial")
5760
public class Toolkit {
@@ -339,6 +342,11 @@ public static Border createEmptyBorder(int x) {
339342
public static JComponent makeHelp(String helpText) {
340343
JLabel help=new JLabel(SymbolIcon.get(0xe887,Toolkit.SMALL_ICON_SIZE));
341344
help.setToolTipText(helpText);
345+
help.addMouseListener(new MouseAdapter() {
346+
public void mousePressed(MouseEvent e) {
347+
JOptionPane.showMessageDialog(help, helpText,"Help Tips",JOptionPane.INFORMATION_MESSAGE);
348+
}
349+
});
342350
return help;
343351
}
344352

@@ -395,4 +403,27 @@ public static void showErrorMessage(Component parent, String attemptFailure,Exce
395403
JOptionPane.showMessageDialog(parent, attemptFailure+ "\n"+e.getMessage());
396404
}
397405

406+
public static Component wrapPasswordField(JPasswordField passArea) {
407+
char ec=passArea.getEchoChar();
408+
JPanel panel=new JPanel();
409+
JLabel eye=new JLabel(SymbolIcon.get(0xe8f4));
410+
eye.addMouseListener(new MouseAdapter() {
411+
public void mouseClicked(MouseEvent e) {
412+
char c=passArea.getEchoChar();
413+
if (c==0) {
414+
eye.setIcon(SymbolIcon.get(0xe8f4));
415+
passArea.setEchoChar(ec);
416+
} else {
417+
eye.setIcon(SymbolIcon.get(0xe8f5));
418+
passArea.setEchoChar((char)0);
419+
}
420+
}
421+
});
422+
panel.setBorder(null);
423+
panel.setLayout(new MigLayout());
424+
panel.add(passArea,"dock center");
425+
panel.add(eye,"dock east");
426+
return panel;
427+
}
428+
398429
}

0 commit comments

Comments
 (0)