From c40326a96396a031572cc297d0224cf6a3df6ca8 Mon Sep 17 00:00:00 2001 From: Alex S Date: Sat, 28 Sep 2019 19:10:52 +0100 Subject: [PATCH] bits indicator, shuffle options --- pom.xml | 19 --- src/pwgen/BitsPasswordJPanel.java | 18 +-- src/pwgen/CharPasswordJPanel.java | 54 ++++--- src/pwgen/DictPasswordJPanel.java | 230 +++++++++++++++++------------- src/pwgen/PasswordJPanel.java | 145 +++++++------------ src/pwgen/PwUtil.java | 150 +++++++++++++++++++ src/pwgen/WordPasswordJPanel.java | 26 ++-- 7 files changed, 393 insertions(+), 249 deletions(-) create mode 100644 src/pwgen/PwUtil.java diff --git a/pom.xml b/pom.xml index 587dfec..c9abe07 100644 --- a/pom.xml +++ b/pom.xml @@ -40,25 +40,6 @@ - - maven-antrun-plugin - 1.8 - - - install - - - - - - - - - run - - - - diff --git a/src/pwgen/BitsPasswordJPanel.java b/src/pwgen/BitsPasswordJPanel.java index 570ccf6..88b6adb 100644 --- a/src/pwgen/BitsPasswordJPanel.java +++ b/src/pwgen/BitsPasswordJPanel.java @@ -5,6 +5,8 @@ import javax.swing.*; +import static pwgen.PwUtil.*; + public class BitsPasswordJPanel extends PasswordJPanel { private final JSpinner bitsSpinner = new JSpinner(new SpinnerNumberModel(8, 8, 4096, 8)); @@ -20,25 +22,25 @@ public BitsPasswordJPanel () { protected void loadPrefs () throws Exception { System.out.println("load prefs"); Preferences prefs = Preferences.userNodeForPackage(getClass()); - bitsSpinner.setValue(Integer.valueOf(prefs.getInt("bits", 64))); - b64CheckBox.setSelected(prefs.getBoolean("b64", true)); + bitsSpinner.setValue(Integer.valueOf(prefs.getInt("btbits", 64))); + b64CheckBox.setSelected(prefs.getBoolean("btb64", true)); } @Override protected void savePrefs () throws Exception { System.out.println("save prefs"); Preferences prefs = Preferences.userNodeForPackage(getClass()); - prefs.putInt("bits", ((Number) bitsSpinner.getValue()).intValue()); - prefs.putBoolean("any", b64CheckBox.isSelected()); + prefs.putInt("btbits", ((Number) bitsSpinner.getValue()).intValue()); + prefs.putBoolean("btb64", b64CheckBox.isSelected()); prefs.flush(); } @Override - public String generate () { + public void generate () { StringBuilder sb = new StringBuilder(); - int bits = ((Number) bitsSpinner.getValue()).intValue(); - sb.append(bits(bits, b64CheckBox.isSelected())); + int b = intValue(bitsSpinner); + sb.append(bits(b, b64CheckBox.isSelected())); Collections.shuffle(new StringBuilderList(sb), RANDOM); - return sb.toString(); + setValue(sb.toString(), Math.pow(2, b)); } } diff --git a/src/pwgen/CharPasswordJPanel.java b/src/pwgen/CharPasswordJPanel.java index 896cbf8..e0b2651 100644 --- a/src/pwgen/CharPasswordJPanel.java +++ b/src/pwgen/CharPasswordJPanel.java @@ -5,6 +5,8 @@ import javax.swing.*; +import static pwgen.PwUtil.*; + public class CharPasswordJPanel extends PasswordJPanel { private final JSpinner lowerSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 99, 1)); @@ -12,6 +14,8 @@ public class CharPasswordJPanel extends PasswordJPanel { private final JSpinner digitSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 99, 1)); private final JSpinner punctSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 99, 1)); private final JSpinner anySpinner = new JSpinner(new SpinnerNumberModel(0, 0, 99, 1)); + private final JCheckBox shuffleBox = new JCheckBox("Shuffle"); + public CharPasswordJPanel () { optionPanel.add(new JLabel("Upper")); @@ -24,40 +28,54 @@ public CharPasswordJPanel () { optionPanel.add(punctSpinner); optionPanel.add(new JLabel("Any")); optionPanel.add(anySpinner); + optionPanel.add(shuffleBox); } @Override protected void loadPrefs () throws Exception { System.out.println("load prefs"); Preferences prefs = Preferences.userNodeForPackage(getClass()); - lowerSpinner.setValue(prefs.getInt("lower", 6)); - upperSpinner.setValue(prefs.getInt("upper", 1)); - digitSpinner.setValue(prefs.getInt("digitx", 1)); - punctSpinner.setValue(prefs.getInt("punct", 0)); - anySpinner.setValue(prefs.getInt("any", 0)); + lowerSpinner.setValue(prefs.getInt("chlower", 5)); + upperSpinner.setValue(prefs.getInt("chupper", 1)); + digitSpinner.setValue(prefs.getInt("chdigit", 1)); + punctSpinner.setValue(prefs.getInt("chpunct", 1)); + anySpinner.setValue(prefs.getInt("chany", 0)); + shuffleBox.setSelected(prefs.getBoolean("chshuf", true)); } @Override protected void savePrefs () throws Exception { System.out.println("save prefs"); Preferences prefs = Preferences.userNodeForPackage(getClass()); - prefs.putInt("upper", (Integer) upperSpinner.getValue()); - prefs.putInt("lower", (Integer) lowerSpinner.getValue()); - prefs.putInt("digitx", (Integer) digitSpinner.getValue()); - prefs.putInt("punct", (Integer) punctSpinner.getValue()); - prefs.putInt("any", (Integer) anySpinner.getValue()); + prefs.putInt("chupper", (Integer) upperSpinner.getValue()); + prefs.putInt("chlower", (Integer) lowerSpinner.getValue()); + prefs.putInt("chdigit", (Integer) digitSpinner.getValue()); + prefs.putInt("chpunct", (Integer) punctSpinner.getValue()); + prefs.putInt("chany", (Integer) anySpinner.getValue()); + prefs.putBoolean("chshuf", shuffleBox.isSelected()); prefs.flush(); } @Override - public String generate () { + public void generate () { StringBuilder sb = new StringBuilder(); - sb.append(upper((Integer) upperSpinner.getValue())); - sb.append(lower((Integer) lowerSpinner.getValue())); - sb.append(digit((Integer) digitSpinner.getValue())); - sb.append(punct((Integer) punctSpinner.getValue())); - sb.append(any((Integer) anySpinner.getValue())); - Collections.shuffle(new StringBuilderList(sb), RANDOM); - return sb.toString(); + int u = intValue(upperSpinner); + int l = intValue(lowerSpinner); + int d = intValue(digitSpinner); + int p = intValue(punctSpinner); + int a = intValue(anySpinner); + sb.append(upper(u)); + sb.append(lower(l)); + sb.append(digit(d)); + sb.append(punct(p)); + sb.append(any(a)); + double v = pow(26, u + l) + pow(10, d) + pow(PUNCT, p) + pow(ANY, a); + System.out.println("v=" + v); + if (shuffleBox.isSelected()) { + Collections.shuffle(new StringBuilderList(sb), RANDOM); + double f = fac(sb.length()) / (fac(u)*fac(l)*fac(d)*fac(p)*fac(a)); + v = v * f; + } + setValue(sb.toString(), v); } } diff --git a/src/pwgen/DictPasswordJPanel.java b/src/pwgen/DictPasswordJPanel.java index 86a62cf..9d7ecd5 100644 --- a/src/pwgen/DictPasswordJPanel.java +++ b/src/pwgen/DictPasswordJPanel.java @@ -8,18 +8,32 @@ import javax.swing.*; +import static pwgen.PwUtil.*; + public class DictPasswordJPanel extends PasswordJPanel { - - private static final int MAX = 8; - private static final int MIN = 4; + + private static final int MIN = 3; + private static final int MAX = 13; + private static Comparator> LI_CMP = new Comparator>() { + @Override + public int compare(List l1, List l2) { + int c = l1.size() - l2.size(); + for (int n = 0; c == 0 && n < l1.size(); n++) { + c = l1.get(Integer.valueOf(n)) - l2.get(Integer.valueOf(n)); + } + return c; + } + }; private final JLabel fileLabel = new JLabel(); private final JButton fileButton = new JButton("File..."); private final JSpinner wordSpinner = new JSpinner(new SpinnerNumberModel(8, 0, 99, 1)); private final JSpinner digitSpinner = new JSpinner(new SpinnerNumberModel(1, 0, 99, 1)); private final JSpinner punctSpinner = new JSpinner(new SpinnerNumberModel(1, 0, 99, 1)); - private final SortedMap> dict = new TreeMap<>(); + /** map of word length to list of words */ + private final SortedMap> dict = new TreeMap<>(); private final JCheckBox titleBox = new JCheckBox("Title"); + private final JCheckBox shuffleBox = new JCheckBox("Shuffle"); private File file; @@ -36,6 +50,7 @@ public DictPasswordJPanel () { optionPanel.add(new JLabel("Punct")); optionPanel.add(punctSpinner); optionPanel.add(titleBox); + optionPanel.add(shuffleBox); } private void file () { @@ -50,7 +65,7 @@ private void file () { } private void loadfile () { - dict.clear(); + Map> dictset = new TreeMap<>(); Pattern p = Pattern.compile("[a-z]+"); try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) { String l; @@ -58,23 +73,28 @@ private void loadfile () { // try to avoid poor quality words l = l.trim().toLowerCase(); if (p.matcher(l).matches() && l.length() >= MIN && l.length() <= MAX) { - dict.compute(l.length(), (k,v) -> v != null ? v : new TreeSet<>()).add(l); + dictset.computeIfAbsent(l.length(), k -> new TreeSet<>()).add(l); } } } catch (Exception e) { e.printStackTrace(); JOptionPane.showMessageDialog(this, e.toString()); } + dict.clear(); + for (Map.Entry> e : dictset.entrySet()) { + dict.put(e.getKey(), new ArrayList<>(e.getValue())); + } } @Override protected void loadPrefs () { Preferences prefs = Preferences.userNodeForPackage(getClass()); - wordSpinner.setValue(prefs.getInt("len", 7)); - digitSpinner.setValue(prefs.getInt("digit", 1)); - punctSpinner.setValue(prefs.getInt("punct", 1)); - titleBox.setSelected(prefs.getBoolean("title", false)); - String f = prefs.get("file", ""); + wordSpinner.setValue(prefs.getInt("dclen", 6)); + digitSpinner.setValue(prefs.getInt("dcdigit", 1)); + punctSpinner.setValue(prefs.getInt("dcpunct", 1)); + titleBox.setSelected(prefs.getBoolean("dctitle", true)); + shuffleBox.setSelected(prefs.getBoolean("dcshuf", true)); + String f = prefs.get("dcfile", ""); System.out.println("loaded file pref " + f); if (f.length() > 0) { file = new File(f); @@ -85,113 +105,127 @@ protected void loadPrefs () { @Override protected void savePrefs () throws Exception { Preferences prefs = Preferences.userNodeForPackage(getClass()); - prefs.putInt("len", (Integer) wordSpinner.getValue()); - prefs.putInt("digit", (Integer) digitSpinner.getValue()); - prefs.putInt("punct", (Integer) punctSpinner.getValue()); - prefs.putBoolean("title", titleBox.isSelected()); + prefs.putInt("dclen", (Integer) wordSpinner.getValue()); + prefs.putInt("dcdigit", (Integer) digitSpinner.getValue()); + prefs.putInt("dcpunct", (Integer) punctSpinner.getValue()); + prefs.putBoolean("dctitle", titleBox.isSelected()); + prefs.putBoolean("dcshuf", shuffleBox.isSelected()); if (file != null) { System.out.println("save file pref " + file.getAbsolutePath()); - prefs.put("file", file.getAbsolutePath()); + prefs.put("dcfile", file.getAbsolutePath()); } prefs.sync(); } + + private Set> partitions(int n, int s) { + Set> out = new TreeSet<>(LI_CMP); + if (n == 1 && s >= MIN && s <= MAX) { + out.add(Arrays.asList(Integer.valueOf(s))); + } else if (n > 1 && s >= MIN) { + for (int i = MIN; i < MAX; i++) { + for (List j : partitions(n - 1, s - i)) { + out.add(join(j, i)); + } + } + } + return out.size() > 0 ? out : Collections.emptySet(); + } + + private List join (List l, int i) { + List l2 = new ArrayList<>(); + l2.addAll(l); + l2.add(i); + Collections.sort(l2); + return l2; + } + + private Long dictproduct(List x) { + long v = 1; + for (Integer i : x) { + List s = dict.get(i); + v = v * (s != null ? s.size() : 0); + } + return Long.valueOf(v); + } + + private long sum (List l) { + long v = 0; + for (Long x : l) { + v = v + x; + } + return v; + } @Override - public String generate () { + public void generate () { if (dict.size() == 0) { loadfile(); } - + if (dict.size() == 0) { JOptionPane.showMessageDialog(this, "No words in dictionary"); - return ""; - } - - List parts = new ArrayList<>(); - - { - int len = ((Integer)wordSpinner.getValue()).intValue(); - - List seq = seq(len); - if (seq == null) { - JOptionPane.showMessageDialog(this, "Could not sequence"); - return ""; - } - - for (int x : seq) { - Set s = dict.get(x); - if (s != null && s.size() > 0) { - String[] a = s.toArray(new String[s.size()]); - String word = a[RANDOM.nextInt(a.length)]; - if (titleBox.isSelected()) { - word = word.substring(0, 1).toUpperCase() + word.substring(1); - } - parts.add(word); - } - } + return; } - - { - int dig = ((Integer)digitSpinner.getValue()).intValue(); - if (dig > 0) { - parts.add(digit(dig)); + + List list = new ArrayList<>(); + + int wordlen = intValue(wordSpinner); + List> plist = new ArrayList<>(); + List pcounts = new ArrayList<>(); + for (int n = 1; n < (wordlen / MIN); n++) { + Set> pset = partitions(n, wordlen); + //System.out.println("parts(" + n + ")=" + pset); + for (List p : pset) { + plist.add(p); + pcounts.add(dictproduct(p)); + //System.out.println("part=" + p + " prod(p)=" + dictproduct(p)); } } - + + long ptotal = sum(pcounts); + int words = 0; + System.out.println("ptotal=" + ptotal); + { - int pun = ((Integer)punctSpinner.getValue()).intValue(); - if (pun > 0) { - parts.add(punct(pun)); + long index = (RANDOM.nextLong() >>> 1) % ptotal; + long sum = 0; + for (int n = 0; n < pcounts.size(); n++) { + sum = sum + pcounts.get(n); + if (sum >= index) { + List p = plist.get(n); + words = p.size(); + Collections.shuffle(p, RANDOM); + for (Integer i : p) { + String word = randomItem(dict.get(i)); + if (titleBox.isSelected()) { + word = word.substring(0, 1).toUpperCase() + word.substring(1); + } + list.add(word); + } + break; + } } } - - Collections.shuffle(parts, RANDOM); - return String.join("", parts); - } - - private List seq (int len) { - - List> seqs = new ArrayList<>(); - - if (dict.keySet().contains(len)) { - seqs.add(Arrays.asList(len)); - } - - if (seqs.size() == 0 || len >= MAX) { - List seq = sumseq(len); - if (seq != null) { - seqs.add(seq); - } + + int dig = intValue(digitSpinner); + if (dig > 0) { + list.add(digit(dig)); } - - return seqs.size() > 0 ? seqs.get(RANDOM.nextInt(seqs.size())) : null; - } - - private List sumseq (int len) { - // add sizes to list - // shuffle list - // pick a subset... - Integer[] keys = dict.keySet().toArray(new Integer[dict.size()]); - List seq = new ArrayList<>(); - for (int n = 0; n < keys.length; n++) { - if (keys[n] <= len - MIN) { - for (int m = 0; m < len - MIN; m++) { - seq.add(keys[n]); - } - } + + int pun = intValue(punctSpinner); + if (pun > 0) { + list.add(punct(pun)); } -// System.out.println("seq=" + seq); - for (int n = 0; n < 100; n++) { - Collections.shuffle(seq, RANDOM); - int sum = 0; - for (int m = 0; m < seq.size() && sum < len; m++) { - sum += seq.get(m); - if (sum == len) { -// System.out.println("n=" + n); - return new ArrayList<>(seq.subList(0, m + 1)); - } - } + + double space = ptotal + pow(10, dig) + pow(PUNCT, pun); + + if (shuffleBox.isSelected()) { + Collections.shuffle(list, RANDOM); + double f = fac(list.size()) / (fac(words)*fac(dig)*fac(pun)); + space = space * f; } - return null; + + setValue(String.join("", list), space); } + } diff --git a/src/pwgen/PasswordJPanel.java b/src/pwgen/PasswordJPanel.java index e8aff03..caf5125 100644 --- a/src/pwgen/PasswordJPanel.java +++ b/src/pwgen/PasswordJPanel.java @@ -1,22 +1,20 @@ package pwgen; -import java.awt.Font; -import java.awt.GridLayout; -import java.awt.Toolkit; +import java.awt.*; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; -import java.security.SecureRandom; -import java.util.Base64; import javax.swing.*; public abstract class PasswordJPanel extends JPanel { - - protected static final SecureRandom RANDOM = new SecureRandom(); - + + private static final Font FONT = new Font("monospaced", Font.PLAIN, 16); + protected final JPanel optionPanel = new JPanel(); private final JTextField textField = new JTextField(); + private final JLabel bitsLabel = new JLabel("Bits"); + private final JTextField bitsField = new JTextField(); private final JButton generateButton = new JButton("Generate"); private final JButton copyButton = new JButton("Copy"); @@ -24,18 +22,44 @@ public PasswordJPanel () { super(new GridLayout(2, 1)); setBorder(BorderFactory.createEmptyBorder()); - textField.setColumns(32); - textField.setFont(new Font("monospaced", Font.PLAIN, 16)); - textField.setBorder(BorderFactory.createEtchedBorder()); - - generateButton.addActionListener(e -> textField.setText(generate())); + textField.setFont(FONT); + + bitsField.setFont(FONT); + bitsField.setEditable(false); + bitsField.setColumns(4); + + generateButton.addActionListener(e -> generate()); copyButton.addActionListener(e -> copy()); - JPanel buttonPanel = new JPanel(); - buttonPanel.add(textField); - buttonPanel.add(generateButton); - buttonPanel.add(copyButton); + JPanel buttonPanel = new JPanel(new GridBagLayout()); + + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 0; + c.insets = new Insets(5,5,5,5); + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 4; + c.weighty = 1; + buttonPanel.add(textField, c); + + c.gridx++; + c.fill = GridBagConstraints.NONE; + c.weightx = 0; + buttonPanel.add(bitsLabel, c); + + c.gridx++; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 0; + buttonPanel.add(bitsField, c); + + c.gridx++; + c.fill = GridBagConstraints.NONE; + c.weightx = 0; + buttonPanel.add(generateButton, c); + + c.gridx++; + buttonPanel.add(copyButton, c); add(optionPanel); add(buttonPanel); @@ -46,88 +70,15 @@ private void copy () { clip.setContents(new StringSelection(textField.getText()), null); } - protected abstract String generate(); + protected abstract void generate(); + + protected void setValue (String pass, double size) { + textField.setText(pass); + bitsField.setText(size > 0 ? Integer.toString(PwUtil.log2(size)) : ""); + } protected abstract void loadPrefs () throws Exception; protected abstract void savePrefs () throws Exception; - - /** - * return a printable ascii character - */ - protected String any (int max) { - StringBuilder sb = new StringBuilder(); - for (int n = 0; n < max; n++) { - // don't include 0x7f (delete) - // but do include space - char c = (char) (RANDOM.nextInt(95) + 32); - sb.append(c); - } - return sb.toString(); - } - - protected String lower (int max) { - StringBuilder sb = new StringBuilder(); - for (int n = 0; n < max; n++) { - char c = (char) ('a' + RANDOM.nextInt(26)); - sb.append(c); - } - return sb.toString(); - } - - protected String upper (int max) { - StringBuilder sb = new StringBuilder(); - for (int n = 0; n < max; n++) { - char c = (char) ('A' + RANDOM.nextInt(26)); - sb.append(c); - } - return sb.toString(); - } - - protected String digit (int max) { - StringBuilder sb = new StringBuilder(); - for (int n = 0; n < max; n++) { - char c = (char) ('0' + RANDOM.nextInt(10)); - sb.append(c); - } - return sb.toString(); - } - - protected String punct (int max) { - StringBuilder sb = new StringBuilder(); - for (int n = 0; n < max; n++) { - while (true) { - char c = (char) (RANDOM.nextInt(95) + 32); - if (!Character.isLetterOrDigit(c) && !Character.isWhitespace(c)) { - sb.append(c); - break; - } - } - } - return sb.toString(); - } - - protected String hex (int max) { - StringBuilder sb = new StringBuilder(); - for (int n = 0; n < max; n++) { - sb.append(Integer.toHexString(RANDOM.nextInt(16))); - } - return sb.toString(); - } - - protected String bits (int bits, boolean b64) { - byte[] a = new byte[bits/8]; - RANDOM.nextBytes(a); - if (b64) { - return Base64.getEncoder().encodeToString(a); - } else { - StringBuilder sb = new StringBuilder(); - for (int n = 0; n < a.length; n++) { - byte b = a[n]; - sb.append(Integer.toHexString(b&0xf)).append(Integer.toHexString((b>>4)&0xf)); - } - return sb.toString(); - } - } - + } diff --git a/src/pwgen/PwUtil.java b/src/pwgen/PwUtil.java new file mode 100644 index 0000000..3892c77 --- /dev/null +++ b/src/pwgen/PwUtil.java @@ -0,0 +1,150 @@ +package pwgen; + +import javax.swing.*; +import java.security.SecureRandom; +import java.util.Base64; + +public class PwUtil { + + protected static final SecureRandom RANDOM = new SecureRandom(); + protected static final int PUNCT = punctCount(); + protected static final int ANY = 126 - 33; + + public static int intValue(JSpinner s) { + return ((Number) s.getValue()).intValue(); + } + + /** generate random integer in range */ + public static int randomInt (int greaterThanOrEqual, int lessThan) { + return RANDOM.nextInt(lessThan - greaterThanOrEqual) + greaterThanOrEqual; + } + + /** return x to the power of y */ + public static double pow (int x, int y) { + return Math.pow(x, y); + } + + /** return log2 of v */ + public static int log2 (double v) { + return (int) (Math.log(v) / Math.log(2)); + } + + /** return factorial of i */ + public static double fac (int i) { + double v = 1; + for (int n = 1; n <= i; n++) { + v = v * n; + } + return v; + } + + private static int punctCount() { + int c = 0; + for (int n = 32; n < 127; n++) { + char ch = (char) n; + if (!Character.isWhitespace(ch) && !Character.isLetterOrDigit(ch)) { + c++; + } + } + return c; + } + + /** pick random item from list */ + public static T randomItem(java.util.List l) { + return l.get(RANDOM.nextInt(l.size())); + } + + /** + * return a sequence of any ascii characters + */ + public static String any (int count) { + StringBuilder sb = new StringBuilder(); + for (int n = 0; n < count; n++) { + // don't include 0x7f (delete) or space + char c = (char) randomInt(33, 127); + sb.append(c); + } + return sb.toString(); + } + + /** + * return string of lowercase ascii characters + */ + public static String lower (int count) { + StringBuilder sb = new StringBuilder(); + for (int n = 0; n < count; n++) { + char c = (char) ('a' + RANDOM.nextInt(26)); + sb.append(c); + } + return sb.toString(); + } + + /** + * return string of uppercase ascii characters + */ + public static String upper (int count) { + StringBuilder sb = new StringBuilder(); + for (int n = 0; n < count; n++) { + char c = (char) ('A' + RANDOM.nextInt(26)); + sb.append(c); + } + return sb.toString(); + } + + /** + * return string of decimal ascii characters + */ + public static String digit (int count) { + StringBuilder sb = new StringBuilder(); + for (int n = 0; n < count; n++) { + char c = (char) ('0' + RANDOM.nextInt(10)); + sb.append(c); + } + return sb.toString(); + } + + /** + * return string of punctuation ascii characters + */ + public static String punct (int count) { + StringBuilder sb = new StringBuilder(); + for (int n = 0; n < count; n++) { + while (true) { + char c = (char) randomInt(33, 127); + if (!Character.isLetterOrDigit(c) && !Character.isWhitespace(c)) { + sb.append(c); + break; + } + } + } + return sb.toString(); + } + + public static String hex (int max) { + StringBuilder sb = new StringBuilder(); + for (int n = 0; n < max; n++) { + sb.append(Integer.toHexString(RANDOM.nextInt(16))); + } + return sb.toString(); + } + + /** generate random bits as hex or base64 string */ + public static String bits (int bits, boolean b64) { + byte[] a = new byte[bits/8]; + RANDOM.nextBytes(a); + if (b64) { + return Base64.getEncoder().encodeToString(a); + } else { + StringBuilder sb = new StringBuilder(); + for (int n = 0; n < a.length; n++) { + byte b = a[n]; + sb.append(Integer.toHexString(b&0xf)).append(Integer.toHexString((b>>4)&0xf)); + } + return sb.toString(); + } + } + + private PwUtil() { + + } +} diff --git a/src/pwgen/WordPasswordJPanel.java b/src/pwgen/WordPasswordJPanel.java index f70499c..532524b 100644 --- a/src/pwgen/WordPasswordJPanel.java +++ b/src/pwgen/WordPasswordJPanel.java @@ -7,11 +7,14 @@ import javax.swing.*; +import static pwgen.PwUtil.*; + public class WordPasswordJPanel extends PasswordJPanel { private final JSpinner wordSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 99, 1)); private final JSpinner digitSpinner = new JSpinner(new SpinnerNumberModel(0, 0, 99, 1)); private final JCheckBox titleBox = new JCheckBox("Title"); + private final JCheckBox shuffleBox = new JCheckBox("Shuffle"); public WordPasswordJPanel () { optionPanel.add(new JLabel("Word")); @@ -19,39 +22,44 @@ public WordPasswordJPanel () { optionPanel.add(new JLabel("Digit")); optionPanel.add(digitSpinner); optionPanel.add(titleBox); + optionPanel.add(shuffleBox); } @Override protected void loadPrefs () { Preferences prefs = Preferences.userNodeForPackage(getClass()); - wordSpinner.setValue(prefs.getInt("words", 7)); - digitSpinner.setValue(prefs.getInt("digit", 1)); - titleBox.setSelected(prefs.getBoolean("title", false)); + wordSpinner.setValue(prefs.getInt("wdwords", 7)); + digitSpinner.setValue(prefs.getInt("wddigit", 1)); + titleBox.setSelected(prefs.getBoolean("wdtitle", true)); + shuffleBox.setSelected(prefs.getBoolean("wdshuf", false)); } @Override protected void savePrefs () throws Exception { Preferences prefs = Preferences.userNodeForPackage(getClass()); - prefs.putInt("words", (Integer) wordSpinner.getValue()); - prefs.putInt("digit", (Integer) digitSpinner.getValue()); - prefs.putBoolean("title", titleBox.isSelected()); + prefs.putInt("wdwords", (Integer) wordSpinner.getValue()); + prefs.putInt("wddigit", (Integer) digitSpinner.getValue()); + prefs.putBoolean("wdtitle", titleBox.isSelected()); + prefs.putBoolean("wdshuf", shuffleBox.isSelected()); prefs.sync(); } @Override - public String generate () { + public void generate () { List list = new ArrayList<>(); int word = ((Integer) wordSpinner.getValue()).intValue(); int digit = ((Integer) digitSpinner.getValue()).intValue(); list.add(word(word, titleBox.isSelected())); list.add(digit(digit)); - Collections.shuffle(list, RANDOM); + if (shuffleBox.isSelected()) { + Collections.shuffle(list, RANDOM); + } StringBuilder sb = new StringBuilder(); for (String s : list) { sb.append(s); } - return sb.toString(); + setValue(sb.toString(), 0); } protected String word (int len, boolean title) {