//   Course Number: COSC 645 Data Applied Cryptography
//    Program Name: ShamirDecoder.java
//         Authors: Brian Hoffman
//                  Derek Waby
//                  Kypros Ioannou
//                  Harsha V. Reddy
//     Description: This prrogram reads in a set of shares generated using 
//                  Shamir's algorithm and uses them to compute the secret 
//                  key. The program is written in a general fashon and may 
//                  be used to recover the key as long as the prime number and
//                  a sufficient number of shares are known. The program makes
//                  no effort to gather the shares. They must be supplied as 
//                  files or entered by the user or users. 
//
//                  The program begins by displaying a GUI with fields to enter 
//                  the prime number and the number of shares. The prime number 
//                  may be read from a file by pressing the button located next
//                  to the field. The initial GUI also displayes a checkbox for 
//                  indicating wether or not the shares should be validated using
//                  Feldman's technique. If checked, on can supply the location 
//                  of the validation file by pressing a button. Finally, one 
//                  can clear the information using the reset button or click 
//                  the submit button to enter the shares. 
//
//                  Pressing the submit button will cause the initial GUI to be 
//                  replaced by one where the shares can be entered into a set
//                  of text fields. The number of fields is dynamic and is set 
//                  according to the number of shares required to rebuild the 
//                  key. For convenience, each of the shares can also be read 
//                  from a file by selecting the button next to each one. Once
//                  entered, one can press the clear, restart, or find key buttons.
//                  The clear button empties all the fields and the restart button
//                  restores the initial screen so that the program can be used 
//                  for a different set of shares. Lastly, the find key button 
//                  will compute the secret key based on the shares provided. It 
//                  will always provide a key, but if any of the shares is not
//                  correct then the key will be wrong. If one chose to validate 
//                  the shares, the program will check each one before computing
//                  they key and warn users of any mistakes. It will clearly indicate.
//                  what share was erroneous so that the user(s) can take action.

// Java Core Packages
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.ArrayList;
import java.math.BigInteger;

// Jave Extension Packages 
import javax.swing.*;
import javax.swing.border.*;

public class ShamirDecoder extends JFrame
{ 
    // private class variables
    Container container;
    int sharesReq;
    BigInteger primeNum;
    File valFile = new File(".");
    File inputDir = new File(".");

    // variables having to do with validation
    boolean priorValidate = false;
    boolean checkShares = false;
    BigInteger largerPrime;
    BigInteger g;
    BigInteger [] valArray;
            
    // GUI Components for initPanel
    GriddedPanel initPanel;
    JLabel sharesReqLabel;
    JTextField sharesReqField;
    JLabel primeLabel;
    JButton readPrimeButton;
    JTextArea primeArea;
    JButton submitButton;
    JButton resetButton;
    JButton restartButton;
    
    // GUI Components for validatePanel
    GriddedPanel validatePanel;
    JTextField valFileField;
    JButton valFileButton;
    JCheckBox checkSharesBox;
    
    // GUI Components for the getSharesPanel
    JPanel getSharesPanel;
    GriddedPanel sharesEntryPanel;
    GriddedPanel sharesButtonPanel;
    JTextField [] shareXFields;
    JTextArea [] shareYAreas;
    JButton findKeyButton;
    JButton clearButton;
    
    // ------------------------------------------------------------------------
    // constructor - sets up the initial GUI that contains the initPanel
    //-------------------------------------------------------------------------
    public ShamirDecoder()
    {
        // title the window 
        super("Shamir Share Decoding Program");
        
        // get content pane and set layout
        container = getContentPane();
        container.setLayout(new BorderLayout());
        
        // create components for the initial panel
        initPanel = new GriddedPanel();
        initPanel.setBorder(BorderFactory.createTitledBorder(
                            BorderFactory.createEtchedBorder(), "Enter Parameters",
                            TitledBorder.CENTER, TitledBorder.TOP ));
        sharesReqLabel = new JLabel("Enter nuber of shares required: ");
        sharesReqField = new JTextField(20);
        primeLabel = new JLabel("Enter the prime number: ");
        readPrimeButton = new JButton("Read Prime From File");
        readPrimeButton.addActionListener(new ReadPrimeHandler(this));
        primeArea = new JTextArea(5,20);
        primeArea.setLineWrap(true);
        submitButton = new JButton("Submit");
        submitButton.addActionListener(new SubmitButtonHandler(this));
        resetButton = new JButton("Reset");
        resetButton.addActionListener(new ResetButtonHandler());

        // create components for the feldman's validate panel
        validatePanel = new GriddedPanel();
        validatePanel.setBorder(BorderFactory.createTitledBorder(
                            BorderFactory.createEtchedBorder(), "Validation",
                            TitledBorder.CENTER, TitledBorder.TOP ));
        checkSharesBox = new JCheckBox("Perform Feldman's Validation of Shares.");
        checkSharesBox.addItemListener( new checkSharesBoxHandler());
        valFileField = new JTextField(10);
        valFileField.setEditable(false);
        valFileButton = new JButton("Set File");
        valFileButton.setEnabled(checkShares);
        valFileButton.addActionListener(new ValFileButtonHandler(this));
        validatePanel.addFilledComponent(checkSharesBox, 1, 1, 3, 1, 
                                         GridBagConstraints.HORIZONTAL);
        validatePanel.addFilledComponent(valFileField, 2, 1, 2, 1,
                                         GridBagConstraints.HORIZONTAL);
        validatePanel.addComponent(valFileButton, 2, 3);
         
        // add components and display panel
        initPanel.addComponent(sharesReqLabel, 1, 1, 3, 1);
        initPanel.addFilledComponent(sharesReqField, 2, 1, 3, 1,
                                     GridBagConstraints.HORIZONTAL);
        initPanel.addComponent(primeLabel, 3, 1, 1, 1);
        initPanel.addFilledComponent(new JScrollPane(primeArea), 4, 1, 3, 2,
                                     GridBagConstraints.BOTH );
        initPanel.addComponent(readPrimeButton, 6, 1, 1, 1);
        initPanel.addComponent(submitButton, 6, 2, 1, 1);
        initPanel.addComponent(resetButton, 6, 3, 1, 1);
        initPanel.addFilledComponent(validatePanel, 7, 1, 3, 1,
                                     GridBagConstraints.HORIZONTAL);
        container.add(initPanel, BorderLayout.CENTER);
       
        // display the initial GUI
        setSize(350,400);
        setVisible(true);   
    }
    
    // ------------------------------------------------------------------------
    // submitButtonHandler - takes the information from the initPanel and uses 
    //                       it to construct the getSharesPanel for reading 
    //                       shares from the user(s). Once constructed, the
    //                       initPanel is replaced in the JFrame by the 
    //                       getSharesPanel.
    //-------------------------------------------------------------------------
    private class SubmitButtonHandler implements ActionListener
    {
        private JFrame parent;
        
        public SubmitButtonHandler(JFrame inFrame)
        {
            parent = inFrame;
        }
        
        public void actionPerformed(ActionEvent event)
        {
           sharesReq = Integer.parseInt(sharesReqField.getText());
           primeNum = new BigInteger(primeArea.getText());
           getSharesPanel = new JPanel(new BorderLayout());
           getSharesPanel.setBorder(BorderFactory.createTitledBorder(
                            BorderFactory.createEtchedBorder(), "Enter Shares",
                            TitledBorder.CENTER, TitledBorder.TOP ));
           
           sharesEntryPanel = new GriddedPanel();
           shareXFields = new JTextField[sharesReq];
           shareYAreas = new JTextArea[sharesReq];
           for(int i=1; i<=sharesReq; i++)
           {
               shareXFields[i-1] = new JTextField(10);
               shareYAreas[i-1] = new JTextArea(3,20);
               
               sharesEntryPanel.addComponent(new JLabel("X: " + i), 2*i-1, 1, 1, 1);
               sharesEntryPanel.addComponent(shareXFields[i-1], 2*i-1, 2, 1, 1);
               JButton tempButton = new JButton("Read from File");
               tempButton.addActionListener(new ReadButtonHandler(parent,i-1));
               sharesEntryPanel.addComponent(tempButton, 2*i-1, 3, 1, 1);
               sharesEntryPanel.addComponent(new JLabel("Y: " + i), 2*i, 1, 1, 1);
               sharesEntryPanel.addComponent(new JScrollPane(shareYAreas[i-1]),
                                             2*i, 2, 2, 1);
           }
           
           getSharesPanel.add(new JScrollPane(sharesEntryPanel), BorderLayout.CENTER);
           sharesButtonPanel = new GriddedPanel();
           findKeyButton = new JButton("Find Key");
           findKeyButton.addActionListener(new FindKeyButtonHandler());
           clearButton = new JButton("Clear Shares");
           clearButton.addActionListener(new ClearButtonHandler());
           restartButton = new JButton("Restart");
           restartButton.addActionListener(new RestartButtonHandler());
           sharesButtonPanel.addFilledComponent(findKeyButton, 1,1);
           sharesButtonPanel.addFilledComponent(clearButton, 1,2);
           sharesButtonPanel.addFilledComponent(restartButton, 1,3);
           
           container.remove(initPanel);
           container.add(getSharesPanel, BorderLayout.CENTER);
           container.add(sharesButtonPanel, BorderLayout.SOUTH);
           container.validate();               
        }
    }
    
    // ------------------------------------------------------------------------
    // resetButtonHandler - resets the fields on the initPanel
    //-------------------------------------------------------------------------
    private class ResetButtonHandler implements ActionListener
    {
        public void actionPerformed(ActionEvent event)
        {   
            sharesReqField.setText("");
            primeArea.setText("");
        }
    } 
    
    // ------------------------------------------------------------------------
    // checkSharesBoxHandler - changes the checkShares variable to reflect 
    //                         the current state of the checkbox on the 
    //                         initPanel
    //-------------------------------------------------------------------------
    private class checkSharesBoxHandler implements ItemListener
    {
        public void itemStateChanged(ItemEvent event)
        {   
            checkShares = !checkShares;
            if (checkShares)
            {
                valFileField.setEditable(true);
                valFileButton.setEnabled(true);
            }
            else
            {
                valFileField.setEditable(false);
                valFileButton.setEnabled(false);
            }   
        }
    } 
    
    // ------------------------------------------------------------------------
    // valFileButtonHandler - displays a JFileChooser that can be used to
    //                        indicate the file containing validation data 
    //                        for the current set of shares
    //-------------------------------------------------------------------------
    private class ValFileButtonHandler implements ActionListener
    {
        private JFrame parent;

        public ValFileButtonHandler(JFrame inFrame)
        {
            parent = inFrame;
        }
        
        public void actionPerformed(ActionEvent event)
        {
            // open a file chooser dialog and get the input file
            JFileChooser chooser = new JFileChooser();
            chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            chooser.setCurrentDirectory(inputDir);
            if(chooser.showOpenDialog(parent) != JFileChooser.APPROVE_OPTION)
                return;
            valFile = chooser.getSelectedFile();
            inputDir = new File(valFile.getParent());
            valFileField.setText(valFile.toString());
        }      
    }
    
    // ------------------------------------------------------------------------
    // findKeyButtonHandler - computes the value of the key based on the shares
    //                        provided.
    //-------------------------------------------------------------------------
    private class FindKeyButtonHandler implements ActionListener
    {
        public void actionPerformed(ActionEvent event)
        {
            BigInteger key;
            BigInteger [] x = new BigInteger[sharesReq];
            BigInteger [] y = new BigInteger[sharesReq];
            
            try
            {
                // get the shares from the GUI 
                for(int i=0; i<sharesReq; i++)
                {
                    x[i] = new BigInteger(shareXFields[i].getText()).mod(primeNum);
                    y[i] = new BigInteger(shareYAreas[i].getText()).mod(primeNum);
                    if (checkShares)
                    {
                        if (!validateShare(x[i], y[i]))
                        {
                            JOptionPane.showMessageDialog(null, 
                            "Could not validate share " + Integer.toString(i+1) +
                            "! It is either invalid or I could not read the validate file",
                            "Invalid Share", JOptionPane.INFORMATION_MESSAGE);
                            return;
                        }
                    }
                }
                
                // compute the key
                key = new BigInteger("0");
                for(int k=0; k<sharesReq; k++)
                {
                    BigInteger temp1 = new BigInteger("1");
                    for(int j=0; j<sharesReq; j++)
                    {
                        if (j==k) continue;
                        BigInteger temp2 = x[k].subtract(x[j]).mod(primeNum);
                        temp2 = temp2.modInverse(primeNum);
                        temp2 = x[j].negate().multiply(temp2);
                        temp1 = temp1.multiply(temp2); 
                    }
                    BigInteger temp3 = y[k].multiply(temp1).mod(primeNum);
                    key = key.add(temp3).mod(primeNum);
                }
             
                // display the key in a JOptionPane
                JOptionPane.showMessageDialog(null, key, "The Secret Key",
                                              JOptionPane.INFORMATION_MESSAGE); 
            }
            catch(NumberFormatException e)
            {
                JOptionPane.showMessageDialog(null,"Check your number formats!",
                        "Number Format Error", JOptionPane.ERROR_MESSAGE);
                return;
            }  
        }
    } 
    
    // ------------------------------------------------------------------------
    // ClearButtonHandler - clears all the shares from the getShares panel by 
    //                      calling the clearShares method
    //-------------------------------------------------------------------------
    private class ClearButtonHandler implements ActionListener
    {
        public void actionPerformed(ActionEvent event)
        {
            clearShares();
        }
    }

    // ------------------------------------------------------------------------
    // RestartButtonHandler - removes the getSharesPanels and replaces it with 
    //                        a cleared initPanel. This allows the user to 
    //                        start over and specify a new prime number, number
    //                        of required shares, and/or validation file.
    //-------------------------------------------------------------------------
    private class RestartButtonHandler implements ActionListener
    {
        public void actionPerformed(ActionEvent event)
        {
            // clear information
            clearShares();
            primeArea.setText("");
            sharesReqField.setText("");
            priorValidate = false;
            largerPrime = null;
            g = null;
            valArray = null;
            
            // replace initial panel
            container.removeAll();
            container.add(initPanel, BorderLayout.CENTER);
            container.validate();
            container.repaint();
        }
    }
    
    // ------------------------------------------------------------------------
    // ReadPrimeHandler - reads the prime number from the file specified using 
    //                    a JFileChooser
    //-------------------------------------------------------------------------
    private class ReadPrimeHandler implements ActionListener
    {
        private JFrame parent;
        private int field; 
        
        public ReadPrimeHandler(JFrame inFrame)
        {
            parent = inFrame;
        }
        
        public void actionPerformed(ActionEvent event)
        {
            // open a file chooser dialog and get the input file
            JFileChooser chooser = new JFileChooser();
            chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            chooser.setCurrentDirectory(inputDir);
            if(chooser.showOpenDialog(parent) != JFileChooser.APPROVE_OPTION)
                return;
            File fileName = chooser.getSelectedFile();
            inputDir = new File(fileName.getParent());
            
            // setup a buffered reader for the file
            FileReader theFile;
            BufferedReader inFile = null;
            try
            {
                 theFile = new FileReader(fileName);
                 inFile = new BufferedReader(theFile);
            
                // read in key from the first line 
                String temp = inFile.readLine();
                if (temp == null)
                {
                    JOptionPane.showMessageDialog(null, "File is Empty", 
                                       "Empty File", JOptionPane.ERROR_MESSAGE);
                    return;
                }
                primeArea.setText(temp);
            }
            catch(IOException ioException)
            {
                JOptionPane.showMessageDialog(null, "Error reading from file!", 
                                  "File Read Error", JOptionPane.ERROR_MESSAGE);
            }
            finally  // close the file stream
            {
                try
                { if (inFile != null) inFile.close(); }
                catch(IOException e)
                {} // do nothing                     
            }   
        } // end ActionPerformed
    } // end ReadButtonHandler
    
    // ------------------------------------------------------------------------
    // ReadButtonHandler - used to read an individual share from the file 
    //                     specified by a JFileChooser
    //-------------------------------------------------------------------------
    private class ReadButtonHandler implements ActionListener
    {
        private JFrame parent;
        private int field; 
        
        public ReadButtonHandler(JFrame inFrame, int inField)
        {
            parent = inFrame;
            field = inField;
        }
        
        public void actionPerformed(ActionEvent event)
        {
            // open a file chooser dialog and get the input file
            JFileChooser chooser = new JFileChooser();
            chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            chooser.setCurrentDirectory(inputDir);
            if(chooser.showOpenDialog(parent) != JFileChooser.APPROVE_OPTION)
                return;
            File fileName = chooser.getSelectedFile();
            inputDir = new File(fileName.getParent());
            
            // setup a buffered reader for the file
            FileReader theFile;
            BufferedReader inFile = null;
            try
            {
                 theFile = new FileReader(fileName);
                 inFile = new BufferedReader(theFile);
            
                // read in key from the first line 
                String temp = inFile.readLine();
                if (temp == null)
                {
                    JOptionPane.showMessageDialog(null, "File is Empty", 
                                       "Empty File", JOptionPane.ERROR_MESSAGE);
                }
                else // read the prime from the second line if it exists
                {
                    shareXFields[field].setText(temp);
                    temp = inFile.readLine();
                    if (temp != null)
                    {
                        shareYAreas[field].setText(temp);
                    }
                    else
                    {
                        JOptionPane.showMessageDialog(null, "File Read Error",
                                       "Read Error", JOptionPane.ERROR_MESSAGE);
                    }
                }
            }
            catch(IOException ioException)
            {
                JOptionPane.showMessageDialog(null, "Error reading from file!", 
                                  "File Read Error", JOptionPane.ERROR_MESSAGE);
            }
            finally  // close the file stream
            {
                try
                { if (inFile != null) inFile.close(); }
                catch(IOException e)
                {} // do nothing                     
            }   
        } // end ActionPerformed
    } // end ReadButtonHandler
    
    // ------------------------------------------------------------------------
    // clearShares - clears all shares from the getSharesPanel
    //-------------------------------------------------------------------------
    private void clearShares()
    {
        for(int i=1; i<=sharesReq; i++)
        {
            shareXFields[i-1].setText("");
            shareYAreas[i-1].setText("");
        }        
    }

    // ------------------------------------------------------------------------
    // validate - performs a Feldman's validation test on the share passed in 
    //-------------------------------------------------------------------------
    private boolean validateShare(BigInteger shareX, BigInteger shareY)
    {
        // if validate has not been called previously for this set, read in 
        // the required data from the file
        if(!priorValidate)
        {            
            // setup a buffered reader for the file
            FileReader theFile;
            BufferedReader inFile = null;
            try
            {
                 theFile = new FileReader(valFile);
                 inFile = new BufferedReader(theFile);
                 priorValidate = true;
                
                 // read in the value of the larger prime number
                 String lineIn = inFile.readLine();
                 if (lineIn == null)
                 {
                     JOptionPane.showMessageDialog(null, "Validate file is empty!", 
                                  "File Read Error", JOptionPane.ERROR_MESSAGE);
                     return false;
                 }
                 largerPrime = new BigInteger(lineIn);
                 
                 // read in the value of g
                 lineIn = inFile.readLine();
                 if (lineIn == null)
                 {
                     JOptionPane.showMessageDialog(null, "Validate file is incorrect!", 
                                  "File Read Error", JOptionPane.ERROR_MESSAGE);
                     return false;
                 }
                 g = new BigInteger(lineIn);
                 
                 // read in g^s from the validate file
                 valArray = new BigInteger[sharesReq];
                 int count = 0;
                 lineIn =  inFile.readLine(); 
                 while((count <  sharesReq) &&(lineIn != null))
                 {
                     valArray[count] = new BigInteger(lineIn);
                     lineIn = inFile.readLine();
                     count++;
                 }              
            }
            catch(IOException ioException)
            {
                JOptionPane.showMessageDialog(null, "Error reading from validate file!", 
                                  "File Read Error", JOptionPane.ERROR_MESSAGE);
                return false;
            }
            catch(NumberFormatException e)
            {
                JOptionPane.showMessageDialog(null, "Validate file does not contain numbers!", 
                                  "File Read Error", JOptionPane.ERROR_MESSAGE);
                return false;
            }
            finally  // close the file stream
            {
                try
                { if (inFile != null) inFile.close(); }
                catch(IOException e)
                {} // do nothing                     
            }
        }
                
        // perform the validation for the current share
        BigInteger lhs = g.modPow(shareY, largerPrime); // compute lhs
 
        //compute rhs
        BigInteger rhs = BigInteger.ONE;
        BigInteger powX = BigInteger.ONE;
        for (int i=0; i<sharesReq; i++)
        { 
            rhs = rhs.multiply(valArray[i].modPow(powX, largerPrime));
            rhs = rhs.mod(largerPrime);
            powX = powX.multiply(shareX);
        }
                    
        // check if the two sides are equal 
        if (rhs.equals(lhs))
            return true;
        else
            return false;
    }
    
    // ------------------------------------------------------------------------
    // main - executes application
    //------------------------------------------------------------------------- 
    public static void main (String args[])
    {
        ShamirDecoder application = new ShamirDecoder();
        application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }       
}
