/* * [MakeKey.java] * * Summary: Collect true random numbers for use in cryptography one-time pads. * * Copyright: (c) 2012-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.0 2012-12-25 initial release. */ package com.mindprod.makekey; import com.mindprod.common18.Build; import com.mindprod.common18.CMPAboutJBox; import com.mindprod.common18.EIO; import com.mindprod.common18.FontFactory; import com.mindprod.common18.HybridJ; import com.mindprod.common18.Laf; import com.mindprod.common18.ST; import com.mindprod.common18.VersionCheck; import javax.swing.JApplet; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.JTextArea; import java.awt.Color; import java.awt.Container; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FilePermission; import java.io.IOException; import static java.lang.System.*; /** * Collect true random numbers for use in cryptography one-time pads. * * @author Roedy Green, Canadian Mind Products * @version 1.0 2012-12-25 initial release. * @since 2012-12-25 */ public final class MakeKey extends JApplet { /** * height of app in pixels. */ private static final int APPLET_HEIGHT = 400; /** * Width of Applet in pixels. */ private static final int APPLET_WIDTH = 650; private static final int FIRST_COPYRIGHT_YEAR = 2012; /** * undisplayed copyright notice * * @noinspection UnusedDeclaration */ private static final String EMBEDDED_COPYRIGHT = "Copyright: (c) 2012-2017 Roedy Green, Canadian Mind Products, http://mindprod.com"; /** * date this version released. */ private static final String RELEASE_DATE = "2012-12-25"; /** * Title of the Applet */ private static final String TITLE_STRING = "MakeKey"; /** * Version */ private static final String VERSION_STRING = "1.0"; /** * Applet background */ private static final Color BACKGROUND_FOR_APPLET = new Color( 0xfffbe0 ); /** * instructions colour */ private static final Color FOREGROUND_FOR_INSTRUCTIONS = new Color( 0x008000 )/* dark green */; /** * for titles */ private static final Color FOREGROUND_FOR_TITLE = new Color( 0xdc143c ); /** * font for generated hex */ private static final Font FONT_FOR_HEX; /** * font for instructions */ private static final Font FONT_FOR_INSTRUCTIONS; /** * font for titles and About buttons */ private static final Font FONT_FOR_TITLE; /** * for for title second line */ private static final Font FONT_FOR_TITLE2; static { FONT_FOR_HEX = FontFactory.build( "Monospaced", Font.PLAIN, 14 ); FONT_FOR_INSTRUCTIONS = FontFactory.build( "Dialog", Font.PLAIN, 12 ); FONT_FOR_TITLE = FontFactory.build( "Dialog", Font.BOLD, 16 ); FONT_FOR_TITLE2 = FontFactory.build( "Dialog", Font.PLAIN, 14 ); } /** * true if running as an Applet */ private final boolean asApplet; /** * where we write the random bytes */ File randomPadFile; /** * contentPane of JApplet */ private Container contentPane; /** * where we write the random bytes */ private FileOutputStream fos; /** * instruction on how to use the program */ private JLabel instructions; private JLabel instructions2; private JLabel instructions3; /** * title on screen */ private JLabel title; /** * title on screen */ private JLabel title2; /** * where user types */ private JScrollPane plainTextScrollPane; /** * there random hex shows up */ private JScrollPane randomHexScrollPane; /** * where user types */ private JTextArea plainText; /** * there random hex shows up */ private JTextArea randomHex; /** * randomPad full filename */ private String randomPadFilename; /** * true if granted permission write random bytes file */ private boolean okToWrite = false; /** * count of chars output to randomHex line */ private int outCount = 0; /** * previous snap of nano time */ private long then = System.nanoTime(); /** * Applet Constructor */ public MakeKey() { asApplet = true; } /** * Application Constructor */ public MakeKey( final String randomPadFilename ) { asApplet = false; this.randomPadFilename = randomPadFilename; } /** * create Swing Components */ private void buildComponents() { title = new JLabel( TITLE_STRING + " " + VERSION_STRING ); title.setFont( FONT_FOR_TITLE ); title.setForeground( FOREGROUND_FOR_TITLE ); // title2 = new JLabel( "released:" + RELEASE_DATE + " build:" + Build.BUILD_NUMBER ); title2.setFont( FONT_FOR_TITLE2 ); title2.setForeground( FOREGROUND_FOR_TITLE ); plainText = new JTextArea( "type anything here\n" ); plainText.setFont( FONT_FOR_HEX ); plainText.setCaretPosition( "type anything here\n".length() ); plainText.setEditable( true ); plainText.setMargin( new Insets( 10, 10, 10, 10 ) ); plainTextScrollPane = new JScrollPane( plainText, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED ); randomHex = new JTextArea( "random hex will appear here\n" ); randomHex.setFont( FONT_FOR_HEX ); randomHex.setEnabled( false ); randomHex.setMargin( new Insets( 10, 10, 10, 10 ) ); randomHexScrollPane = new JScrollPane( randomHex, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED ); instructions = new JLabel( "Type anything you want into the upper window and true random hex will appear in " + "the lower one." ); instructions.setFont( FONT_FOR_INSTRUCTIONS ); instructions.setForeground( FOREGROUND_FOR_INSTRUCTIONS ); instructions2 = new JLabel( "You can even hit the same key over and over. Just don\u2019t hold keys down till" + " they repeat." ); instructions2.setFont( FONT_FOR_INSTRUCTIONS ); instructions2.setForeground( FOREGROUND_FOR_INSTRUCTIONS ); if ( okToWrite ) { instructions3 = new JLabel( "The random binary bytes will also appear in file " + EIO.getCanOrAbsPath( randomPadFile ) ); } else { instructions3 = new JLabel( "Because you did not grant permission, no random bytes will be written to " + "file " + EIO.getCanOrAbsPath( randomPadFile ) ); } instructions3.setFont( FONT_FOR_INSTRUCTIONS ); instructions3.setForeground( FOREGROUND_FOR_INSTRUCTIONS ); } /** * build a menu with Look & Feel and About across the top */ private void buildMenu() { // turn on anti-alias System.setProperty( "swing.aatext", "true" ); final JMenuBar menubar = new JMenuBar(); setJMenuBar( menubar ); final JMenu lafMenu = Laf.buildLookAndFeelMenu(); if ( lafMenu != null ) { menubar.add( lafMenu ); } final JMenu menuHelp = new JMenu( "Help" ); menubar.add( menuHelp ); final JMenuItem aboutItem = new JMenuItem( "About" ); menuHelp.add( aboutItem ); aboutItem.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { // open aboutbox frame new CMPAboutJBox( TITLE_STRING, VERSION_STRING, "Generates true random numbers", "sourcing entropy from keystroke timings.", "freeware", RELEASE_DATE, FIRST_COPYRIGHT_YEAR, "Roedy Green", "MAKEKEY", "1.8" ); } } ); } /** * generate a random byte based on inter keystroke timing. */ private void generateRandomByte() { try { long now = System.nanoTime(); int randomByte = ( int ) ( now - then ) & 0xff; if ( okToWrite ) { fos.write( randomByte ); } then = now; String hexPair = ST.toLZHexString( randomByte, 2 ); randomHex.append( hexPair ); outCount++; if ( outCount >= 32 ) { outCount = 0; randomHex.append( "\n" ); if ( okToWrite ) { fos.flush(); fos.getFD().sync(); } // to get bottom visible final JScrollBar v = randomHexScrollPane.getVerticalScrollBar(); v.setValue( v.getMaximum() ); } else if ( outCount % 4 == 0 ) { randomHex.append( " " ); } } catch ( IOException e ) { err.println( "Unable to write random bytes to " + EIO.getCanOrAbsPath( randomPadFile ) ); System.exit( 2 ); } } /** * hook up the listeners */ private void hookListeners() { plainText.addKeyListener( new KeyListener() { @Override public void keyPressed( final KeyEvent e ) { generateRandomByte(); } @Override public void keyReleased( final KeyEvent e ) { generateRandomByte(); } @Override public void keyTyped( final KeyEvent e ) { // ignore } } ); } /** * layout the components */ private void layoutGridBag() { // basic layout: // ------0------------------------1----- // 0 -------title----- ------title2----- // 1 -----------plain-------------------- // 2 ---------- hex---------------------- // 3 -------------instructions----------- // 4 -------------instructions2---------- // ----------0-------------------1 ----- // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( title, new GridBagConstraints( 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets( 10, 10, 0, 5 ), 0, 0 ) ); contentPane.add( title2, new GridBagConstraints( 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets( 10, 5, 0, 10 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( plainTextScrollPane, new GridBagConstraints( 0, 1, 2, 1, 100.0, 50.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets( 5, 10, 0, 10 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( randomHexScrollPane, new GridBagConstraints( 0, 2, 2, 1, 100.0, 50.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets( 5, 10, 0, 10 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( instructions, new GridBagConstraints( 0, 3, 2, 1, 100.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets( 5, 10, 0, 10 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( instructions2, new GridBagConstraints( 0, 4, 2, 1, 100.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets( 5, 10, 0, 10 ), 0, 0 ) ); // x y w h wtx wty anchor fill T L B R padx pady contentPane.add( instructions3, new GridBagConstraints( 0, 5, 2, 1, 100.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets( 5, 10, 10, 10 ), 0, 0 ) ); } /** * determine the name of the randomPadFile and open it */ private void openRandomPadFile() { if ( asApplet && randomPadFilename == null ) { randomPadFilename = this.getParameter( "file" ); } if ( randomPadFilename == null ) { // none specified on command line or Applet parm, use default. randomPadFilename = "C:\\temp\\random.pad"; } randomPadFile = new File( randomPadFilename ); if ( asApplet ) { final FilePermission filePermission = new FilePermission( EIO.getCanOrAbsPath( randomPadFile ), "write" ); SecurityManager security = System.getSecurityManager(); if ( security != null ) { try { security.checkPermission( filePermission ); okToWrite = true; } catch ( SecurityException e ) { err.println( "We don't have permisson to write " + EIO.getCanOrAbsPath( randomPadFile ) ); okToWrite = false; } } } else { // is app okToWrite = true; } // O P E N try { if ( okToWrite ) { fos = new FileOutputStream( randomPadFile, false /* append */ ); } } catch ( FileNotFoundException e ) { err.println( "Unable to create " + EIO.getCanOrAbsPath( randomPadFile ) ); System.exit( 2 ); } } /** * Allow this Applet to run as as application as well. * * @param args command line arguments ignored. */ public static void main( String args[] ) { // pass in randomPadFilename from the command line. HybridJ.fireup( new MakeKey( args.length == 1 ? args[ 0 ] : null ), TITLE_STRING + " " + VERSION_STRING, APPLET_WIDTH, APPLET_HEIGHT ); } // end main /** * Called by the browser or Applet viewer to inform * this Applet that it is being reclaimed and that it should destroy * any resources that it has allocated. */ public void destroy() { title = null; title2 = null; plainText = null; randomHex = null; instructions = null; instructions2 = null; instructions2 = null; try { if ( okToWrite ) { fos.close(); } } catch ( IOException e ) { err.println( "trouble closing file " + EIO.getCanOrAbsPath( randomPadFile ) ); } } /** * Called by the browser or Applet viewer to inform * this Applet that it has been loaded into the system. */ public void init() { if ( !VersionCheck.isJavaVersionOK( 1, 8, 0, this ) ) { return; } // todo: don't do laf if no permission. buildMenu(); // also initial L&F contentPane = this.getContentPane(); contentPane.setBackground( BACKGROUND_FOR_APPLET ); contentPane.setLayout( new GridBagLayout() ); openRandomPadFile(); buildComponents(); layoutGridBag(); hookListeners(); this.validate(); this.setVisible( true ); } // end init }