burrow/src/main/java/io/rudefox/burrow/MnemonicCommand.java

142 lines
4.8 KiB
Java

package io.rudefox.burrow;
import com.bjdweck.bitcoin.mnemonic.Entropy;
import picocli.CommandLine;
import java.security.SecureRandom;
import java.util.Scanner;
@CommandLine.Command(name = "mnemonic", description = "generate mnemonic sentence")
public class MnemonicCommand implements Runnable {
private static final int DEFAULT_BITS_OF_ENTROPY = 256;
private boolean isCustomEntropy() {
return entropyOptions != null &&
(entropyOptions.isDice6Entropy || entropyOptions.isDice8Entropy || entropyOptions.isBinaryEntropy);
}
private boolean isInteractiveMode() {
return entropyOptions != null && entropyOptions.eventMethod.isInteractiveMode;
}
private boolean isBinaryInteractiveMode() {
return isInteractiveMode() && entropyOptions.isBinaryEntropy;
}
private boolean isDice6InteractiveMode() {
return isInteractiveMode() && entropyOptions.isDice6Entropy;
}
private boolean isDice8InteractiveMode() {
return isInteractiveMode() && entropyOptions.isDice8Entropy;
}
@CommandLine.ArgGroup(exclusive = false)
EntropyOptions entropyOptions;
static class EntropyOptions {
@CommandLine.Option(names = {"-2", "--binary-entropy"},
description = "use a coin toss or other binary entropy source")
boolean isBinaryEntropy;
@CommandLine.Option(names = {"-6", "--dice6-entropy"},
description = "use 6-sided dice entropy source")
boolean isDice6Entropy;
@CommandLine.Option(names = {"-8", "--dice8-entropy"},
description = "use 8-sided dice entropy source")
boolean isDice8Entropy;
@CommandLine.ArgGroup(multiplicity = "1")
EventMethod eventMethod;
static class EventMethod {
@CommandLine.Option(names = {"-e", "--events"}, paramLabel = "[0-1]{256} | [1-6]{100} | [1-8]{86}",
description = "string representing events from entropy source",
required = true)
String getEventString;
@CommandLine.Option(names = {"-i", "--interactive"},
description = "use interactive command line mode",
required = true)
boolean isInteractiveMode;
}
}
@CommandLine.Option(names = {"-b", "--bits"}, defaultValue = DEFAULT_BITS_OF_ENTROPY+"",
description = "bits of entropy (default: 256)",
paramLabel = "128|160|192|224|256")
int targetBitsOfEntropy;
public void run() {
Entropy entropyBytes = getEntropy();
System.out.println(entropyBytes.toMnemonic().getSentence());
}
Entropy getEntropy() {
if (!isCustomEntropy())
return generateEntropy();
if (isBinaryInteractiveMode())
return new InteractiveBinaryEntropyGenerator(targetBitsOfEntropy, new Scanner(System.in), CommandLine.Help.Ansi.AUTO.enabled()).generate();
if (isDice6InteractiveMode())
return getDice6EntropyInteractive(new EventBuffer(this.targetBitsOfEntropy, 6));
if (isDice8InteractiveMode())
return new InteractiveDice8EntropyGenerator(targetBitsOfEntropy, new Scanner(System.in), CommandLine.Help.Ansi.AUTO.enabled()).generate();
EventBuffer eventBuffer;
if (entropyOptions.isBinaryEntropy) {
eventBuffer = new EventBuffer(this.targetBitsOfEntropy, 2);
eventBuffer.appendBinaryEvents(entropyOptions.eventMethod.getEventString);
} else if (entropyOptions.isDice6Entropy) {
eventBuffer = new EventBuffer(this.targetBitsOfEntropy, 6);
eventBuffer.appendD6Events(entropyOptions.eventMethod.getEventString);
} else {
eventBuffer = new EventBuffer(this.targetBitsOfEntropy, 8);
eventBuffer.appendD8Events(entropyOptions.eventMethod.getEventString);
}
return eventBuffer.toEntropy();
}
private Entropy getDice6EntropyInteractive(EventBuffer eventBuffer) {
int requiredEvents = eventBuffer.getRequiredEvents();
Scanner scanner = new Scanner(System.in);
boolean firstIteration = true;
while (eventBuffer.events() < requiredEvents) {
int remainingEvents = requiredEvents - eventBuffer.events();
System.out.printf("Input %d %sdice rolls [1-6]: ", remainingEvents, (firstIteration ? "" : "more "));
String inputString = scanner.next();
eventBuffer.appendD6Events(inputString);
firstIteration = false;
}
return eventBuffer.toEntropy();
}
private Entropy generateEntropy() {
SecureRandom random = new SecureRandom();
int byteLength = targetBitsOfEntropy / 8;
byte[] randomBytes = new byte[byteLength];
random.nextBytes(randomBytes);
return Entropy.fromRawEntropy(randomBytes);
}
}