Skip to content

Commit

Permalink
add qr-export for secrets
Browse files Browse the repository at this point in the history
  • Loading branch information
kryptonbutterfly committed Jan 27, 2024
1 parent fd37e0a commit d61f01f
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 0 deletions.
37 changes: 37 additions & 0 deletions src/kryptonbutterfly/totp/misc/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.util.Map;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

import kryptonbutterfly.functions.bool_.BoolToIntFunction;
import kryptonbutterfly.math.vector._int.Vec2i;
import kryptonbutterfly.monads.failable.Failable;
import kryptonbutterfly.monads.opt.Opt;

public class Utils
{
Expand Down Expand Up @@ -70,4 +79,32 @@ public static final BufferedImage mirror(BufferedImage src)

return result;
}

public static final Opt<BufferedImage> generateQr(
String data,
Color dark,
Color light,
int size,
ErrorCorrectionLevel level)
{
final BoolToIntFunction colorConverter = bit -> bit ? dark.getRGB() : light.getRGB();
final var sizeRange = range(size);

return Failable.attempt(
() -> new MultiFormatWriter().encode(
data,
BarcodeFormat.QR_CODE,
size,
size,
Map.of(EncodeHintType.ERROR_CORRECTION, level.name())))
.toOpt(e -> e.printStackTrace())
.map(matrix ->
{
final var image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
for (int y : sizeRange)
for (int x : sizeRange)
image.setRGB(x, y, colorConverter.apply(matrix.get(x, y)));
return image;
});
}
}
3 changes: 3 additions & 0 deletions src/kryptonbutterfly/totp/prefs/TotpWindowStates.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@ public final class TotpWindowStates

@Expose
public GuiPrefs qrScan = new GuiPrefs(100, 100, 640, 400, JFrame.NORMAL);

@Expose
public GuiPrefs qrExport = new GuiPrefs(100, 100, 230, 280, JFrame.NORMAL);
}
11 changes: 11 additions & 0 deletions src/kryptonbutterfly/totp/ui/add/manual/AddKey.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package kryptonbutterfly.totp.ui.add.manual;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Window;
import java.util.function.Consumer;
Expand Down Expand Up @@ -31,6 +32,7 @@
@SuppressWarnings("serial")
public final class AddKey extends ObservableDialog<BL, TotpEntry, char[]> implements TotpConstants
{
final JButton btnExport = new JButton("export");
final JTextField txtAccountname = new JTextField();
final JTextField txtSecretkey = new JTextField();
final JTextField txtIssuer = new JTextField();
Expand Down Expand Up @@ -119,6 +121,15 @@ private void init(BL bl)

verticalBox.add(gridPanel);

final var exportPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
getContentPane().add(exportPanel, BorderLayout.NORTH);
{
exportPanel.add(btnExport);
btnExport.setIcon(Assets.getQr16ByBackground(getBackground()));
btnExport.addActionListener(bl::exportQR);
btnExport.addKeyListener(bl.escapeListener);
}

getContentPane().add(verticalBox, BorderLayout.CENTER);
{
gridPanel.add(new JLabel(LABEL_ACCOUNT_NAME));
Expand Down
22 changes: 22 additions & 0 deletions src/kryptonbutterfly/totp/ui/add/manual/BL.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package kryptonbutterfly.totp.ui.add.manual;

import java.awt.Color;
import java.awt.Dialog.ModalityType;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
Expand All @@ -21,13 +24,18 @@

import org.apache.commons.codec.binary.Base32;

import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;

import kryptonbutterfly.functions.void_.Consumer_;
import kryptonbutterfly.monads.opt.Opt;
import kryptonbutterfly.totp.TinyTotp;
import kryptonbutterfly.totp.TotpConstants;
import kryptonbutterfly.totp.misc.UrlQueryParams;
import kryptonbutterfly.totp.misc.Utils;
import kryptonbutterfly.totp.prefs.TotpCategory;
import kryptonbutterfly.totp.prefs.TotpEntry;
import kryptonbutterfly.totp.ui.misc.KeyTypedAdapter;
import kryptonbutterfly.totp.ui.qrexport.QrExportGui;
import kryptonbutterfly.util.swing.Logic;
import kryptonbutterfly.util.swing.events.GuiCloseEvent;
import kryptonbutterfly.util.swing.events.GuiCloseEvent.Result;
Expand Down Expand Up @@ -144,6 +152,20 @@ private void setIcon(AddKey gui, String name, BufferedImage img)
gui.lblIcon.setToolTipText(name);
}

void exportQR(ActionEvent ae)
{
gui.if_(gui -> {
final var account = gui.txtAccountname.getText();
final var secretKey = gui.txtSecretkey.getText();
final var issuer = gui.txtIssuer.getText();
final var url = UrlQueryParams.toUrl(account, secretKey, issuer);
Utils.generateQr(url, Color.BLACK, Color.WHITE, 200, ErrorCorrectionLevel.H)
.if_(
qr -> EventQueue
.invokeLater(() -> new QrExportGui(gui, ModalityType.APPLICATION_MODAL, Consumer_.sink(), qr)));
});
}

void abort(ActionEvent ae)
{
gui.if_(AddKey::dispose);
Expand Down
30 changes: 30 additions & 0 deletions src/kryptonbutterfly/totp/ui/qrexport/BL.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package kryptonbutterfly.totp.ui.qrexport;

import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import kryptonbutterfly.totp.TinyTotp;
import kryptonbutterfly.totp.ui.misc.KeyTypedAdapter;
import kryptonbutterfly.util.swing.Logic;

final class BL extends Logic<QrExportGui, Void>
{
final KeyListener escapeListener = new KeyTypedAdapter(c -> ok(null), KeyEvent.VK_ESCAPE);

BL(QrExportGui gui)
{
super(gui);
}

void ok(ActionEvent ae)
{
gui.if_(QrExportGui::dispose);
}

@Override
protected void disposeAction()
{
gui.if_(TinyTotp.windowStates.qrExport::persistBounds);
}
}
57 changes: 57 additions & 0 deletions src/kryptonbutterfly/totp/ui/qrexport/QrExportGui.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package kryptonbutterfly.totp.ui.qrexport;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Window;
import java.awt.image.BufferedImage;
import java.util.function.Consumer;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;

import kryptonbutterfly.totp.TinyTotp;
import kryptonbutterfly.totp.misc.Assets;
import kryptonbutterfly.util.swing.ObservableDialog;
import kryptonbutterfly.util.swing.events.GuiCloseEvent;

@SuppressWarnings("serial")
public class QrExportGui extends ObservableDialog<BL, Void, Void>
{
private final JButton btnOk = new JButton("ok");

public QrExportGui(
Window owner,
ModalityType modality,
Consumer<GuiCloseEvent<Void>> closeListener,
BufferedImage qr)
{
super(owner, modality, closeListener);
TinyTotp.windowStates.qrExport.setBounds(this);
setTitle("Export Secret");
setIconImage(Assets.getQr16ByBackground(getBackground()).getImage());

add(new JLabel(new ImageIcon(qr)), BorderLayout.CENTER);

final var footerPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
add(footerPanel, BorderLayout.SOUTH);
footerPanel.add(btnOk);

businessLogic.if_(this::init);

setVisible(true);
}

@Override
protected BL createBusinessLogic(Void args)
{
return new BL(this);
}

private void init(BL bl)
{
btnOk.addActionListener(bl::ok);
btnOk.addKeyListener(bl.escapeListener);
}
}

0 comments on commit d61f01f

Please sign in to comment.