<mosaic.cnfolio.com>

Authentication system using monome keypad


During Priliminary Report Phase






Week prior to 15/06/08









Sunday 15th June





Not too sure how to install Java Processing so that applications for the monome can be written and run in Java. Time for research.

Monday 16th June





Researching how to install the Monomic libraries. Looks like I have to use some more external libs in order to even use monomic. These libraries come from http://www.sojamo.de/libraries/oscP5/ and allow processing to use the OpenSoundProtocol (used to control the monome)





The monomic libraries did NOT work correctly the frist time. This was not expected. The OSCP5 libraries worked ok, but the monomic libraries did not. The error given was:
java.lang.NoClassDefFoundError: oscP5/MessageOUTat Temporary_415_2609.setup(Temporary_415_2609.java:5)

After digging around the monome.org forums, it became apparent that other people have encountered the same error. It looks as though the OSCP5 libraries were updated, causing the monomic libraries to fail. The thread which first reaises the issue is here: http://post.monome.org/comments.php?DiscussionID=715, and a cross-posted thread here: http://post.monome.org/comments.php?DiscussionID=177 offers the solution, in the form of an older version of the OSCP5.jar. Using the older version of this file, the sample applications were able to run without error.


.

The code for this application is pretty simple, as is shown below:

  1. import processing.serial.*;
  2. import jklabs.monomic.*;
  3.  
  4. Monome m;
  5.  
  6. void setup() {
  7.   m = new MonomeOSC(this);
  8. }
  9.  
  10. void monomePressed(int x, int y) {
  11.   m.setValue(x, y, !m.isLit(x,y));
  12.   m.setValue(7-x, y, !m.isLit(7-x,y));
  13.   m.setValue(x, 7-y, !m.isLit(x,7-y));
  14.   m.setValue(7-x, 7-y, !m.isLit(7-x,7-y));
  15. }
  16.  
  17. void draw() {
  18.  
  19. }



Next task is to toy around with processing to gain some familliarity with the platform. The sample programs and command refernce sheets will be used as I learn how to manipulate the monome, everything from reading button presses to displaying patterns using the LEDs.




Wednesday 18th June



Took a short while to get the laptop back in working order since knocking the plug out. Rebooted the machine to find that processing no longer worked with any programs. Found out it was due to the fact that when changing the library versions to the correct working versions (older ones, as described above) I had neglected to REMOVE the incorrect ones. Whilst I did rename them, they still had the .jar extension which meant they were still being loaded, causing errors. Removed the incorrect libraries completely to get back to where I was previously, with a working software platform.


Building upon my previous progress, I have built a VERY basic GUI, consisting of two buttons. Actually this GUI is a modified piece of example code, with the button actions altered to my purpose. I envisage the GUI having two buttons, one to RECORD your authentication pattern, and another to AUTHENTICATE using it. This will provide a simple 2-button platform for a user to test the idea of pattern based authentication.



Image above shows the very BASIC (and preliminary) GUI, with the circle on the right being the button to start to "record" your pattern.


In oder to record a pattern, the coordinates of pressed buttons will need to be stored in an array. I am using an array of the following type:
  1. int storedCoords[] = new int[10][2]

The first dimension is 10 long, and the second dimention is 2 long. This means I can store X and Y coordinates for ten button presses (arbitrary number). For example:
storedCoords[0][0] = 3 and storedCoords[0][1] = 5

This means that the 1st X coordinate is 3, and the first Y coordinate is 5. Similarly, if the second key press was 7,8 - the array will be populated as follows:
storedCoords[1][0] = 7 and storedCoords[1][1] =8


By storing the coordinates as integers in an array, it will be easier to perform manipulations and or calculations using them.


The code for this is as follows, and includes the code for drawing the GUI.
  1. import processing.serial.*;
  2. import jklabs.monomic.*;
  3.  
  4. Monome m;
  5.  
  6.  
  7. int rectX, rectY;      // Position of square button
  8. int circleX, circleY;  // Position of circle button
  9. int rectSize = 50;     // Diameter of rect
  10. int circleSize = 53;   // Diameter of circle
  11. color rectColor, circleColor, baseColor;
  12. color rectHighlight, circleHighlight;
  13. color currentColor;
  14. boolean rectOver = false;
  15. boolean circleOver = false;
  16. boolean Record=false;
  17. int storedCoords[][] = new int[10][2];
  18. int arrayPlace = 0;
  19.  
  20. void setup() {
  21.  
  22.  
  23.   size(200, 200);
  24.   smooth();
  25.   rectColor = color(0);
  26.   rectHighlight = color(51);
  27.   circleColor = color(255);
  28.   circleHighlight = color(204);
  29.  
  30.   circleX = width/2+circleSize/2+10;
  31.   circleY = height/2;
  32.   rectX = width/2-rectSize-10;
  33.   rectY = height/2-rectSize/2;
  34.   ellipseMode(CENTER);
  35.  
  36.   m = new MonomeOSC(this);
  37.  
  38.   for (int i=0; i < 8; i++)
  39.    for (int j=0; j < 2; j++)
  40.      storedCoords[i][j] = -1;
  41. }
  42.  
  43. void draw()
  44. {
  45.   update(mouseX, mouseY);
  46.   background(currentColor);
  47.  
  48.   if(rectOver) {
  49.    fill(rectHighlight);
  50.   } else {
  51.    fill(rectColor);
  52.   }
  53.   stroke(255);
  54.   rect(rectX, rectY, rectSize, rectSize);
  55.  
  56.   if(circleOver) {
  57.    fill(circleHighlight);
  58.   } else {
  59.    fill(circleColor);
  60.   }
  61.   stroke(0);
  62.   ellipse(circleX, circleY, circleSize, circleSize);
  63. }
  64.  
  65. void update(int x, int y)
  66. {
  67.   if( overCircle(circleX, circleY, circleSize) ) {
  68.    circleOver = true;
  69.    rectOver = false;
  70.   } else if ( overRect(rectX, rectY, rectSize, rectSize) ) {
  71.    rectOver = true;
  72.    circleOver = false;
  73.   } else {
  74.    circleOver = rectOver = false;
  75.   }
  76. }
  77.  
  78. void mousePressed()
  79. {
  80.   if(circleOver) {
  81.    circleCommand();
  82.   }
  83.   if(rectOver) {
  84.    rectCommand();
  85.   }
  86. }
  87.  
  88. boolean overRect(int x, int y, int width, int height)
  89. {
  90.   if (mouseX >= x && mouseX <= x+width &&
  91.      mouseY >= y && mouseY <= y+height) {
  92.    return true;
  93.   } else {
  94.    return false;
  95.   }
  96. }
  97.  
  98. boolean overCircle(int x, int y, int diameter)
  99. {
  100.   float disX = x - mouseX;
  101.   float disY = y - mouseY;
  102.   if(sqrt(sq(disX) + sq(disY)) < diameter/2 ) {
  103.    return true;
  104.   } else {
  105.    return false;
  106.   }
  107. }
  108.  
  109. void circleCommand() {
  110.   Record = true;
  111.   m.lightsOn();
  112. };
  113.  
  114. void rectCommand() {
  115. }
  116.  
  117. void monomePressed(int x, int y) {
  118.  
  119.   m.setValue(x,y,!m.isLit(x,y));
  120.   if (Record == true) {
  121.  
  122.      println("pressed: "+x+","+y+" Recorded");
  123.      storedCoords[arrayPlace][0] = x;
  124.      storedCoords[arrayPlace][1] = y;
  125.      arrayPlace++;
  126.      for (int i=0; i < storedCoords.length; i++) {
  127.        println("Stored X: "+storedCoords[i][0]+" , Stored Y: "+storedCoords[i][1]);
  128.      }
  129.   } else {
  130.      println("pressed: "+x+","+y);
  131.   }
  132. }


This code will record coordinates when placed in record mode by pressing the circle with the mouse. The entire monome is lit up when record mode is enabled. With each key press, the history of recorded presses is shown, as pictured below.



In the above example I pressed a pattern of keys in a diagonal line from bottom left to top right. This will hopefully be the basis for the recording of a pattern. The comparisson logic is yet to be considered.

Thursday 19th June



The following code is the newly altered GUI, and is fully commented.

  1. import processing.serial.*;
  2. import jklabs.monomic.*;
  3.  
  4. Monome m; // New instance of monome called m
  5.  
  6. int goButtonX, goButtonY;      // Position of go button
  7. int recordButtonX, recordButtonY;  // Position of record button
  8. int recordButtonSize, goButtonSize = 50;     // Diamater of both buttons
  9. color recordButtonColour, goButtonColour;  // Colour of both buttons
  10. color recordButtonHighlight, goButtonHighlight; // Colour of both buttons when highlighted
  11.  
  12. boolean goButtonOver = false; // Boolean var to denote if the go button is rolled over
  13. boolean recordButtonOver = false; // As above for record button
  14. boolean Record = false; // Boolean var to denote whether the monome is recording keystrokes
  15. int storedCoords[][] = new int[10][2]// 2d array to store coordinates
  16. int arrayPlace = 0; // Counter var for handling the array
  17.  
  18. void setup() { // Method run once at startup
  19.  
  20.   background(102); // Set the background colour
  21.   size(200, 200); // Size of the convas
  22.   smooth(); // Smooth edges                     
  23.   recordButtonColour = color(255,100,100)// Colour of the record button                                                               
  24.   recordButtonHighlight = color(255,140,140); // Colour of the record button when highlighted
  25.   goButtonColour = color(132,255,132); // Go button colour
  26.   goButtonHighlight = color(179,255,179); // Colour of the go button when highlighted
  27.  
  28.   goButtonX = 130; // X and Y coordinates for the go button
  29.   goButtonY = 70;
  30.   recordButtonX = 20; // X and Y coordinates for the record button
  31.   recordButtonY = 70;
  32.   goButtonSize = 50; // Dimensions of the two buttons
  33.   recordButtonSize = 50;
  34.  
  35.   m = new MonomeOSC(this); // Tell the monome m to use the OSC protocol to communicate
  36.  
  37.   for (int i=0; i < 8; i++)  // Fill the array with -1s
  38.    for (int j=0; j < 2; j++)
  39.      storedCoords[i][j] = -1;
  40.  
  41.   PFont fontA = loadFont("Ziggurat-HTF-Black-32.vlw"); // Select the font for words
  42.   textFont(fontA, 20); // Select the size for wods
  43.   fill(0); // Select the colour
  44.   text("Pattern-Based", 16,25 ); // Words, with coordinates
  45.   fill(51); // Change colour
  46.   text("Authentication", 13, 48);
  47.   textFont(fontA, 15); // Change font size
  48.   fill(204);
  49.   text("Record", 17, 138);
  50.   text("Pattern", 14, 158);
  51.   fill(204);
  52.   text("Attempt", 120, 138);
  53.   text("Pattern", 123, 158);
  54.  
  55. }
  56.  
  57. void draw() // Looping method running constantly
  58. {
  59.   update(mouseX, mouseY); // Call method update with current x and y coordinates of the mouse
  60.  
  61.   if(recordButtonOver) {  // If mouse is over the record button
  62.    fill(recordButtonHighlight); // Then set the corect colour
  63.   }
  64.   else { // If the mouse is NOT over the record button
  65.    fill(recordButtonColour); // Set the correct colour
  66.   }
  67.   stroke(0); // Line border colour
  68.   rect(recordButtonX, recordButtonY, recordButtonSize, recordButtonSize); // Draw the rectangle
  69.  
  70.    if(goButtonOver) { // As above but for the go button
  71.    fill(goButtonHighlight);
  72.   }
  73.   else {
  74.    fill(goButtonColour);
  75.   }
  76.   stroke(0);
  77.   rect(goButtonX, goButtonY, goButtonSize, goButtonSize);
  78.  
  79. }
  80.  
  81. void update(int x, int y) // Method to update the colours of the buttons
  82. {
  83.   if( overRect(recordButtonX, recordButtonY, recordButtonSize, recordButtonSize) ) { // Check to see if the mouse is over the record button
  84.    recordButtonOver = true; // Set the boolean var to true
  85.    goButtonOver = false;
  86.   }
  87.   else if ( overRect(goButtonX, goButtonY, goButtonSize, goButtonSize) ) {  // Check to see if the mouse is over the go button
  88.    recordButtonOver = false;
  89.    goButtonOver = true; // Set the boolean var to true
  90.   }
  91.   else { // Is the mouse is not over any button
  92.    recordButtonOver = goButtonOver = false; // Set both vars to false
  93.   }
  94. }
  95.  
  96. void mousePressed() // When the mouse has been pressed
  97. {
  98.   if(recordButtonOver) { // If the boolean mouse over var is true
  99.    recordButtonCommand(); // Run the method asociated with the record button
  100.   }
  101.   if(goButtonOver) { // As above but for go button
  102.    goButtonCommand();
  103.   }
  104. }
  105.  
  106. boolean overRect(int x, int y, int width, int height) // Method to check to see if the mouse is over a rectangle
  107. {
  108.   if (mouseX >= x && mouseX <= x+width && // If the mouse is within the boundaries of a rectangle
  109.   mouseY >= y && mouseY <= y+height) {
  110.    return true; // return true
  111.   }
  112.   else {
  113.    return false; // If not, then return false
  114.   }
  115. }
  116.  
  117.  
  118. void recordButtonCommand() { // Actions to perform when the record button is pressed
  119.   Record = true; // Set the record var to true
  120.   m.lightsOn(); // Turn all of the monome lights on
  121. }
  122.  
  123. void goButtonCommand() { // Actions to perform when the go button is pressed
  124. }
  125.  
  126. void monomePressed(int x, int y) { // Actions to perform when a monome key press is detected
  127.   m.setValue(x,y,!m.isLit(x,y)); // Invert that key's LED state (ie on->off, off->on)
  128.  
  129.   if (Record == true) { // If we are currently recording
  130.    println("pressed: "+x+","+y+" Recorded"); // Echo the X Y coords of the keypress for debug reasons
  131.    storedCoords[arrayPlace][0] = x; // Store the X coordinate
  132.    storedCoords[arrayPlace][1] = y; // Store the Y coordinate
  133.    arrayPlace++; // Increment our array counter
  134.    for (int i=0; i < storedCoords.length; i++) { // Each time a keystroke is recorded, output the entire array to show each recorded coordinate so far. Debug reasons.
  135.      println("Stored X: "+storedCoords[i][0]+" , Stored Y: "+storedCoords[i][1]);
  136.    }
  137.   }
  138.   else { // If NOT recording, just show the echo the keypress to screen.
  139.    println("pressed: "+x+","+y);
  140.   }
  141. }


This code gives the following look to the GUI:



The iamges show the buttons in their normal state (left), and the subtle rollover effects for each button (record button middle, go button right).


  1. int relativePosition(int x1, int y1, int x2, int y2)
  2. {
  3.   if (x1 == x2) {
  4.    if (y1 < y2) return 90;
  5.    else if (y1 > y2) return 270;
  6.   }
  7.   else if (y1 == y2) {
  8.    if (x1 < x2) return 0;
  9.    else if (x1 > x2) return 180;
  10.   }
  11.   return -1;
  12. }


Above shows a few absoulte cases where no computation needs to be done to see the relative angle between two points. The four cases coded above are for when one key press is immediately above the other, below the other, to the left of the other, and to the right of the other. The angles of these are 0 degrees, 180 degrees, 270 degrees and 90 degrees respectively. When none of these cases are true, an implementation of pythagorean theorem will be implemented along with the sine rules to calculate the relative angles...


  1.   if (x2 > x1 && y2 > y1) { // Ie if the movement is in the top right quadrant
  2.    int deltax = x2-x1; // a
  3.    int deltay = y2-y1; // b
  4.     // c is the DISTANCE between the two points, or the hyponenuse of the triangle.
  5.    double c = sqrt((deltax*deltax)+(deltay*deltay)); // a^2 + b^2 = c^2
  6.    // println("c: "+c);
  7.    double angle = deltax/c;
  8.    // println("angle: "+angle);
  9.    angle = 90-Math.toDegrees(Math.asin(angle)); // toDegrees converst from radians to degrees
  10.    angle = Math.round(angle); // round the angle
  11.    return angle;
  12.   }


During testing I have noticed that the relative position method does not need to be divided into QUADRANTS, but only HALVES - left and right. Line 9 on the above code segment has an offset of 90 degrees. This is the same for both the top right and bottom right quadrants. The offset is 270 for top left and bottom left quadrants. Therefore the proposed four code segments can be reduced to two. The if statements for absolute cases (shown above) are now redundant, but were useful for proof of concept.


  1.   if (x1 == x2 && y1 == y2) return -1; // If the coordinates are identical, return -1



  1.   if (y2 >= y1) { // Ie if the movement is in the top right quadrant.  Less than or equal to, to cover the 0 degrees event (directly above)


The entire method now looks like this:

  1. double relativePosition(int x1, int y1, int x2, int y2)
  2. {
  3.  
  4.   if (x1 == x2 && y1 == y2) return -1; // If the coordinates are identical, return -1
  5.  
  6.   if (y2 >= y1) { // Ie if the movement is in the top right quadrant.  Less than or equal to, to cover the 0 degrees event (directly above)
  7.    int deltax = x2-x1; // a
  8.    int deltay = y2-y1; // b
  9.     // c is the DISTANCE between the two points, or the hyponenuse of the triangle.
  10.    double c = sqrt((deltax*deltax)+(deltay*deltay)); // a^2 + b^2 = c^2
  11.    // println("c: "+c);
  12.    double angle = deltax/c;
  13.    // println("angle: "+angle);
  14.    angle = 90-Math.toDegrees(Math.asin(angle)); // toDegrees converts from radians to degrees
  15.    angle = Math.round(angle); // round the angle
  16.    return angle;
  17.   }
  18.  
  19.   if (y2 < y1) { // Ie if the movement is in the bottom left quadrant
  20.    int deltax = x2-x1; // a
  21.    int deltay = y2-y1; // b
  22.     // c is the DISTANCE between the two points, or the hyponenuse of the triangle.
  23.    double c = sqrt((deltax*deltax)+(deltay*deltay)); // a^2 + b^2 = c^2
  24.    // println("c: "+c);
  25.    double angle = deltax/c;
  26.    // println("angle: "+angle);
  27.    angle = 270+Math.toDegrees(Math.asin(angle)); // toDegrees converts from radians to degrees
  28.    angle = Math.round(angle); // round the angle
  29.    return angle;
  30.   }
  31.  
  32.   return -1; // Return -1 in any other case (should never get called)
  33.  
  34. }


Code testing shows good results!



Next task is to devise a method of storing these relative positions, then comparing them to another pattern.

Thursday 19th June



The following video shows a complete test of the software written so far. The software will record ten keystrokes, and calculate the relative positioning of each. The software does not yet COMPARE patterns, nor is it possible to end the keystorke recording after less than ten keystrokes. This is next on the to-do list.



Tuesday 24th June



With the system for RECORDING a pattern pretty much in place, it is now time to consider implementing a method by which a user can attempt to authenticate. This will basically mean a method to compare two patterns, and their relative positioning. Depending on how complicated this task becomes, I may use ONLY the relative positioning of keys, or a combination of relative positioning and absolute positioning to make the comparison. The latter is a possibility because I am not only calculating the relative angles, but also storing the absolute coordinates.

The first task is to modify the system to store the relative angles of each key press in an array. Since I already have a 2d array set up, I will add another element to the second dimension to store the relative angles. This will mean that the array structure will now become:


storedCoords[1][0] - X coordinate of the 2nd key press
storedCoords[1][1] - Y coordinate of the 2nd key press
storedCoords[1][2] - Relative angle between the 1st and the 2nd key presses.


If data types become a problem here (I have angles as doubles and coordinate arrays using ints) and there is no easy way to convert a double to an int, I will consider using a separate array for the angles.


  1. int Angle = (int)(relativePosition(storedCoords[arrayPlace-1][0],storedCoords[arrayPlace-1][1],x,y)); // Convert from double to int and store as Angle
  2. storedCoords[arrayPlace][2] = Angle; // Store angle in the array
  3. println("angle: "+storedCoords[arrayPlace][2]); // Print the angle





The code which accomplishes this is below. I have created a new boolean variable called recordedOK which is set after recording some key presses. I have also moved the numberOfCoords variable declaration out of the method so that it can become a global var. This allows the number of key strokes recorded to be shown on the GUI, as shown above.

  1. if(Record) {
  2.    fill(255);
  3.    text("Recording", statusBoxX+52, statusBoxY+16);
  4.   }
  5.   if(RecordedOK) {
  6.    fill(100);
  7.    text ("Recorded "+numberOfCoords+" keystrokes", statusBoxX+10, statusBoxY+16);
  8.   }


I will now use similar techniques to allow the GUI to show when the monome software is attempting to authenticate a user.


The GUI is now much more intuitive, and reflecs what the program is actually doing at the time. It should now be a lot clearer for the user. Using the framework so far, I shall start to create the method to collect the user's authentication attempt.


  1. else if (Auth == true) { // If we are in authentication mode
  2.    //   int counter = numberOfCoords; // Assign value to counter
  3.    counter--; // Decrement the value of counter with each key stroke.
  4.    if (counter == 0) Auth = false; // When  we have pressed the same number of keys as was recorded, stop authenticating
  5. }


The above code works well. When the authentication attempt reaches the right number of key presses, authentication ends, and the GUI is updated to reflect this. Here is the code for the entire project so far:

  1. import processing.serial.*;
  2. import jklabs.monomic.*;
  3.  
  4. Monome m; // New instance of monome called m
  5.  
  6. int goButtonX, goButtonY;      // Position of go button
  7. int recordButtonX, recordButtonY;  // Position of record button
  8. int recordButtonSize, goButtonSize = 50;     // Diamater of both buttons
  9. int statusBoxX, statusBoxY; // Position of status box
  10. int statusBoxWidth, statusBoxHeight; // Dimensions of the status box
  11. int numberOfCoords = 0; // Number of recorded coordinates
  12. int counter = 0; // Used during authentication to count the remaining keystrokes
  13. color statusBoxColour, statusBoxRecordColour, statusBoxAuthColour; // Colour of the satus box;
  14. color recordButtonColour, goButtonColour;  // Colour of both buttons
  15. color recordButtonHighlight, goButtonHighlight; // Colour of both buttons when highlighted
  16.  
  17. boolean goButtonOver = false; // Boolean var to denote if the go button is rolled over
  18. boolean recordButtonOver = false; // As above for record button
  19. boolean Record = false; // Boolean var to denote whether the monome is recording keystrokes
  20. boolean RecordedOK = false;
  21. boolean Auth = false; // Boolean var to denote whether the monome is authenticating a user
  22. int storedCoords[][] = new int[10][3]// 2d array to store coordinates
  23. int arrayPlace = 0; // Counter var for handling the array
  24.  
  25. void setup() { // Method run once at startup
  26.  
  27.    background(102); // Set the background colour
  28.   size(200, 200); // Size of the convas
  29.   smooth(); // Smooth edges                     
  30.   recordButtonColour = color(255,100,100)// Colour of the record button                                                               
  31.   recordButtonHighlight = color(255,140,140); // Colour of the record button when highlighted
  32.   goButtonColour = color(132,255,132); // Go button colour
  33.   goButtonHighlight = color(179,255,179); // Colour of the go button when highlighted
  34.   statusBoxColour = color(204); // Status box colour
  35.   statusBoxRecordColour = color(255,100,100); // Status box colour when recording
  36.   statusBoxAuthColour = color(132,255,132); // Status box colour when authenticating
  37.  
  38.   goButtonX = 130; // X and Y coordinates for the go button
  39.   goButtonY = 70;
  40.   recordButtonX = 20; // X and Y coordinates for the record button
  41.   recordButtonY = 70;
  42.   goButtonSize = 50; // Dimensions of the two buttons
  43.   recordButtonSize = 50;
  44.   statusBoxX = 14; // X and Y coords for the status box
  45.   statusBoxY = 168;
  46.   statusBoxWidth = 170; // dimensions of the status box
  47.   statusBoxHeight = 20;
  48.  
  49.   m = new MonomeOSC(this); // Tell the monome m to use the OSC protocol to communicate
  50.  
  51.  
  52.   PFont fontA = loadFont("Ziggurat-HTF-Black-32.vlw"); // Select the font for words
  53.   PFont fontB = loadFont("ArialMT-15.vlw"); // Second font for words
  54.   textFont(fontA, 20); // Select the size for wods
  55.   fill(0); // Select the colour
  56.   text("Pattern-Based", 16,25 ); // Words, with coordinates
  57.   fill(51); // Change colour
  58.   text("Authentication", 13, 48);
  59.   textFont(fontA, 15); // Change font size
  60.   fill(204);
  61.   text("Record", 17, 138);
  62.   text("Pattern", 14, 158);
  63.   fill(204);
  64.   text("Attempt", 120, 138);
  65.   text("Pattern", 123, 158);
  66.   textFont(fontB, 15); // Change font size
  67.  
  68. }
  69.  
  70. void draw() // Looping method running constantly
  71. {
  72.   update(mouseX, mouseY); // Call method update with current x and y coordinates of the mouse
  73.  
  74.   if(recordButtonOver) {  // If mouse is over the record button
  75.    fill(recordButtonHighlight); // Then set the corect colour
  76.   }
  77.   else { // If the mouse is NOT over the record button
  78.    fill(recordButtonColour); // Set the correct colour
  79.   }
  80.   stroke(0); // Line border colour
  81.   rect(recordButtonX, recordButtonY, recordButtonSize, recordButtonSize); // Draw the rectangle
  82.  
  83.    if(goButtonOver) { // As above but for the go button
  84.    fill(goButtonHighlight);
  85.   }
  86.   else {
  87.    fill(goButtonColour);
  88.   }
  89.   stroke(0);
  90.   rect(goButtonX, goButtonY, goButtonSize, goButtonSize);
  91.  
  92.   fill(statusBoxColour);
  93.   if(Record) fill(statusBoxRecordColour);
  94.   if(Auth) fill(statusBoxAuthColour); // Colour when authenticating
  95.   rect(statusBoxX, statusBoxY, statusBoxWidth, statusBoxHeight);
  96.  
  97.   if(Record) {
  98.    fill(100);
  99.    text("Recording", statusBoxX+52, statusBoxY+16);
  100.   }
  101.   if(RecordedOK && !Auth) {
  102.    fill(100);
  103.    text ("Recorded "+numberOfCoords+" keystrokes", statusBoxX+10, statusBoxY+16);
  104.   }
  105.   if(Auth) {
  106.    fill(100);
  107.    text("Authenticate Now", statusBoxX+30, statusBoxY+16);
  108.   }
  109.  
  110.   if (!Record && !RecordedOK && !Auth) {
  111.    fill(100);
  112.    text("Ready", statusBoxX+64, statusBoxY+16);
  113.   }
  114.  
  115.  
  116.  
  117. }
  118.  
  119. void update(int x, int y) // Method to update the colours of the buttons
  120. {
  121.   if( overRect(recordButtonX, recordButtonY, recordButtonSize, recordButtonSize) ) { // Check to see if the mouse is over the record button
  122.    recordButtonOver = true; // Set the boolean var to true
  123.    goButtonOver = false;
  124.   }
  125.   else if ( overRect(goButtonX, goButtonY, goButtonSize, goButtonSize) ) {  // Check to see if the mouse is over the go button
  126.    recordButtonOver = false;
  127.    goButtonOver = true; // Set the boolean var to true
  128.   }
  129.   else { // Is the mouse is not over any button
  130.    recordButtonOver = goButtonOver = false; // Set both vars to false
  131.   }
  132. }
  133.  
  134. void mousePressed() // When the mouse has been pressed
  135. {
  136.   if(recordButtonOver) { // If the boolean mouse over var is true
  137.    recordButtonCommand(); // Run the method asociated with the record button
  138.   }
  139.   if(goButtonOver) { // As above but for go button
  140.    goButtonCommand();
  141.   }
  142. }
  143.  
  144. boolean overRect(int x, int y, int width, int height) // Method to check to see if the mouse is over a rectangle
  145. {
  146.   if (mouseX >= x && mouseX <= x+width && // If the mouse is within the boundaries of a rectangle
  147.   mouseY >= y && mouseY <= y+height) {
  148.    return true; // return true
  149.   }
  150.   else {
  151.    return false; // If not, then return false
  152.   }
  153. }
  154.  
  155.  
  156. void recordButtonCommand() { // Actions to perform when the record button is pressed
  157.   numberOfCoords = 0; // Reset the number of coords
  158.   if (Record == true) { // Set the record var to true
  159.    if (numberOfCoords == 0)
  160.      m.lightsOff(); // Turn all of the monome lights off
  161.    Record = false;
  162.  
  163.    for (int i=0; i < 10; i++)  // Count the number of recorded coordinates
  164.    {
  165.      if (storedCoords[i][0] != -2)  numberOfCoords++;
  166.    }
  167.    if (numberOfCoords == 0) println("Error: Must record atleast one key press");
  168.    else {
  169.      println("Stored "+numberOfCoords+" Presses");
  170.      RecordedOK = true; // Recorded OK
  171.    }
  172.   }
  173.   else if (!Auth) {  // Check to see that we are not already trying to authenticate
  174.    arrayPlace = 0;
  175.    for (int i=0; i < 10; i++)  // Fill the array with -2s (no longer -1 because -1 is used to represent consecutive keystrokes on the same key
  176.      for (int j=0; j < 3; j++)
  177.        storedCoords[i][j] = -2;
  178.    Record = true;
  179.    RecordedOK = false;
  180.    m.lightsOn(); // Turn all the monome lights on
  181.   }
  182.   else {
  183.    println("Error: Can not record whilst authenticating");
  184.   }
  185. }
  186.  
  187. void goButtonCommand() { // Actions to perform when the go button is pressed
  188.   if (Record) println("Error: Can not authenticate whilst recording");
  189.   else if (!RecordedOK) println("Error: Must record a pattern before attempting to authenticate");
  190.   else {
  191.    prettyEffect();
  192.    Auth=!Auth;
  193.    counter = numberOfCoords;
  194.   }
  195. }
  196. void monomeReleased(int x, int y) {
  197.  
  198.   if (!Record) m.setValue(x,y,!m.isLit(x,y)); // Invert that key's LED state (ie on->off, off->on)
  199.   if (arrayPlace == 10) {
  200.    recordButtonCommand();// Increment our array counter
  201.    arrayPlace++;
  202.   }
  203. }
  204.  
  205. void monomePressed(int x, int y) { // Actions to perform when a monome key press is detected
  206.  
  207.   m.setValue(x,y,!m.isLit(x,y)); // Invert that key's LED state (ie on->off, off->on)
  208.  
  209.   if (Record == true) { // If we are currently recording
  210.    println("pressed: "+x+","+y+" Recorded"); // Echo the X Y coords of the keypress for debug reasons
  211.    storedCoords[arrayPlace][0] = x; // Store the X coordinate
  212.    storedCoords[arrayPlace][1] = y; // Store the Y coordinate
  213.    if (arrayPlace != 0) {  // Check for 0 because can't have a relatie angle for the first key press
  214.      int Angle = (int)(relativePosition(storedCoords[arrayPlace-1][0],storedCoords[arrayPlace-1][1],x,y)); // Convert from double to int and store as Angle
  215.      storedCoords[arrayPlace][2] = Angle; // Store angle in the array
  216.      println("angle: "+storedCoords[arrayPlace][2]); // Print the angle
  217.    }
  218.    arrayPlace++;
  219.  
  220.    //for (int i=0; i < storedCoords.length; i++) { // Each time a keystroke is recorded, output the entire array to show each recorded
  221.    // cordinate so far. Debug reasons.
  222.    //  println("Stored X: "+storedCoords[i][0]+" , Stored Y: "+storedCoords[i][1]);
  223.    //}
  224.   }
  225.  
  226.   else if (Auth == true) { // If we are in authentication mode
  227.    //   int counter = numberOfCoords; // Assign value to counter
  228.    counter--; // Decrement the value of counter with each key stroke.
  229.    if (counter == 0) Auth = false; // When  we have pressed the same number of keys as was recorded, stop authenticating
  230.   }
  231.  
  232.  
  233.   else { // If NOT recording, just show the echo the keypress to screen.
  234.    println("pressed: "+x+","+y);
  235.   }
  236. }
  237.  
  238. double relativePosition(int x1, int y1, int x2, int y2)
  239. {
  240.  
  241.   if (x1 == x2 && y1 == y2) return -1; // If the coordinates are identical, return -1
  242.  
  243.    if (y2 >= y1) { // Ie if the movement is in the top right quadrant.  Less than or equal to, to cover the 0 degrees event (directly above)
  244.    int deltax = x2-x1; // a
  245.    int deltay = y2-y1; // b
  246.    // c is the DISTANCE between the two points, or the hyponenuse of the triangle.
  247.    double c = sqrt((deltax*deltax)+(deltay*deltay)); // a^2 + b^2 = c^2
  248.    // println("c: "+c);
  249.    double angle = deltax/c;
  250.    // println("angle: "+angle);
  251.    angle = 90-Math.toDegrees(Math.asin(angle)); // toDegrees converts from radians to degrees
  252.    angle = Math.round(angle); // round the angle
  253.    return angle;
  254.   }
  255.  
  256.   if (y2 < y1) { // Ie if the movement is in the bottom left quadrant
  257.    int deltax = x2-x1; // a
  258.    int deltay = y2-y1; // b
  259.    // c is the DISTANCE between the two points, or the hyponenuse of the triangle.
  260.    double c = sqrt((deltax*deltax)+(deltay*deltay)); // a^2 + b^2 = c^2
  261.    // println("c: "+c);
  262.    double angle = deltax/c;
  263.    // println("angle: "+angle);
  264.    angle = 270+Math.toDegrees(Math.asin(angle)); // toDegrees converts from radians to degrees
  265.    angle = Math.round(angle); // round the angle
  266.    return angle;
  267.   }
  268.  
  269.   return -1; // Return -1 in any other case (should never get called)
  270.  
  271. }
  272.  
  273. void prettyEffect() { // A method simply to vertically scroll a pattern.  Acts as a visual cue to the use.
  274.  
  275.   try {
  276.    for (int i = 0; i<8; i++) {
  277.      m.setCol(i, new int[]{
  278.        1,1,1,1,1,1,1,1                                          }
  279.      );
  280.      Thread.currentThread().sleep(40);
  281.    }
  282.    for (int i = 0; i<8; i++) {
  283.      m.setCol(i, new int[]{
  284.        0,0,0,0,0,0,0,0                                          }
  285.      );
  286.      Thread.currentThread().sleep(40);
  287.    }
  288.   }
  289.   catch (InterruptedException e) {
  290.    println("Fail");
  291.   }
  292. }


Wednesday 25th June





The next task is to start work on the comparison logic. I will probably need to start by building a mechanism to store the user's second pattern.


  1. void monomeReleased(int x, int y) {
  2.  
  3.   if (!Record) m.setValue(x,y,!m.isLit(x,y)); // Invert that key's LED state (ie on->off, off->on)
  4.   if (arrayPlace == 10) { // Check to see if our authen
  5.    recordButtonCommand();// Increment our array counter
  6.    arrayPlace++;
  7.    }
  8.    if ((arrayPlace == authenticationArrayPlace && Auth) || (authenticationArrayPlace == 10 && Auth)) {  // Is the authentication attempt complete?
  9.      Auth = false;
  10.     prettyEffect(1);
  11.    }
  12. }


With this completed, it is now time to consider the actual COMPARISON of patterns. Currently I am able to store both the reference pattern, and the second authentication attempt in their respective arrays. I am also storing the relative angles between each key press. I will need to pass both of these arrays into some form of algorithm which will asses whether or not the patterns are a near enough match to constitute a correct authentication. I am thinking about using a points method. Whereby an exact match with either the relative or absolute positioning will score (say) 5 points, and then presses which are near but not quite will score 0-4 points depending on how close they are. Then, taking into account the number of keys in the given pattern, if the attempt accumulates enough points, the attempt will be allowed.

  1. boolean doCompare (int[][] reference, int[][] authentication) {
  2.   int numberOfKeyPresses = authenticationArrayPlace; // Set the number of key presses that we're dealing with (between 1 and 10)
  3.   int angularScore = 0; // Score for the angle tests
  4.   int positioningScore = 0; // Score for the positioning
  5.  
  6.   for (int i = numberOfKeyPresses; i>0; i--) { // Do this for each of the key presses
  7.    if (reference[i][2] == authentication[i][2]) angularScore += 5; // Add 5 to score
  8.   }
  9.   println("angularScore: "+angularScore);
  10.   if (angularScore/numberOfKeyPresses > 3) println("PASS");
  11.   else println("FAIL");
  12.  
  13.   return true;
  14.  
  15. }


The above code is a crude version of what I am hoping to achieve. The angles in each array are directly compared. If they are identical, five points are assigned to the variable anglularScore. Then, if the division of angularScore by numberOfKeyPresses is more than 3, the result is a pass. Else fail. However I am not sure how suitable this method is, as there doesn't appear to be too mch margin for error. I have anothe idea.

My second idea is to divide the angle of the first attempt by the angle of the second attempt. If they are identical, the result will be 1. If either bigger or smaller, the result will be something less than 1, or greater than 1. I will then take the amount by which the division result deviates from 1, and add it to a running total. Then I will check to see if the resulting total is within bounds for a succesful attempt. The bounds will have to be set using trial and error...



  1. boolean doCompare (int[][] reference, int[][] authentication) {
  2.   int numberOfKeyPresses = authenticationArrayPlace; // Set the number of key presses that we're dealing with (between 1 and 10)
  3.   double angularScore = 0; // Score for the angle tests
  4.   double angularScoreTemp = 0;
  5.   int positioningScore = 0; // Score for the positioning
  6.  
  7.   for (int i = (numberOfKeyPresses-1); i>=0; i--) { // Do this for each of the key presses, -1 because 0 is included in the array index
  8.    int refAngle = reference[i][2]; // Reference pattern angle
  9.    int authAngle = authentication[i][2]; // Authentication pattern angle
  10.    angularScoreTemp = ((360+(double)refAngle) / (360+(double)authAngle)); // Divide one by the other.  Adding 360 to avoid division by 0....
  11.    if (angularScoreTemp == 1) {} // Great, do nothing
  12.      if (angularScoreTemp < 1) {       // Eg if authentication angle is greater than reference angle
  13.        angularScore += ((1/angularScoreTemp)-1);
  14.      }
  15.  
  16.    if (angularScoreTemp > 1) { // Eg if authentication angle is less than reference angle
  17.      angularScore += (1-(1/angularScoreTemp));
  18.    }
  19.   }
  20.   println("angularScore: "+angularScore);
  21.   println("adjusted angular score: "+(angularScore/numberOfKeyPresses));
  22.   if (angularScore/numberOfKeyPresses > 0.06) println("FAIL");
  23.   else println("PASS");
  24.  
  25.   return true;
  26.  
  27. }


Note the workaround on line 10 to avoid divide by zero errors. The angular score is used to determin how "close" a pattern is to the correct pattern. The authentication pattern is scored against the reference pattern, and a score given to it. A lower score is better. This score is then divided by the number of keystrokes in the pattern, so that longer patterns arn't at a disadvantage. This score is then checked to see if it is greater than 0.06. If so, then fain the attempt. Otherwise pass. 0.06 is curently an arbitrary number that I chose because it seems to be "about right". Better analysis of this threshold will be performed later.

I eventually hope to couple this angular analysis with absolute positioning checks, to ensure that false positives (ie being accepted for a really dodgy pattern) are reduced. I am hoping to emply a method to check that the authentication keypress is no more than three keys (for example) away from where the keypress SHOULD have been. With the combination of two checks, the pattern checking could (should?) be fairly accurate, but still a tad lenient.


Thursday 3rd July


Spent the past week viewing and arranging a new house to live in for the job in September, hence not being able to get as much project work done as I would have liked.




I now consider myself to be in the final stages of implementing this authentication system in code. I need to update the buisness end so that it considers absolute button positioning. However once this is complete, I can probably begin the research on designing an effective user survey. That, and mount the monome on a bit of wood.


The modification described above means that patterns must be reproduced in a manner that is roughly, but not exactly equal to the first. This is the balance which is trying to be struck. The code which asseses the patterns is shown below:

  1. int doCompare (int[][] reference, int[][] authentication) {
  2.   int numberOfKeyPresses = authenticationArrayPlace; // Set the number of key presses that we're dealing with (between 1 and 10)
  3.   double angularScore = 0; // Score for the angle tests
  4.   double angularScoreTemp = 0;
  5.   double positioningScore = 0; // Score for the positioning
  6.   boolean angularOK = false; // to denote if the angular test was passed
  7.   boolean positioningOK = false; // to denote if the absolute positioning test was passed
  8.  
  9.  
  10.   for (int i = (numberOfKeyPresses-1); i>=0; i--) { // Do this for each of the key presses, -1 because 0 is included in the array index
  11.    int refAngle = reference[i][2]; // Reference pattern angle
  12.    int authAngle = authentication[i][2]; // Authentication pattern angle
  13.    angularScoreTemp = ((360+(double)refAngle) / (360+(double)authAngle)); // Divide one by the other.  Adding 360 to avoid division by 0....
  14.    if (angularScoreTemp == 1) {
  15.    } // Great, do nothing
  16.    if (angularScoreTemp < 1) {       // Eg if authentication angle is greater than reference angle
  17.      angularScore += ((1/angularScoreTemp)-1);
  18.    }
  19.  
  20.    if (angularScoreTemp > 1) { // Eg if authentication angle is less than reference angle
  21.      angularScore += (1-(1/angularScoreTemp));
  22.    }
  23.   }
  24.   //  println("angularScore: "+angularScore);
  25.  
  26.  
  27.  
  28.   for (int i = (numberOfKeyPresses-1); i>=0; i--) { // Do this for each of the key presses, -1 because 0 is included in the array index
  29.    int refX = reference[i][0]; // store the reference X position
  30.    int refY = reference[i][1]; // Store the reference Y position
  31.    int authX = authentication[i][0]// Store the authentication X position
  32.    int authY = authentication[i][1]// Store the authentication Y position
  33.  
  34.    if ((Math.abs(authX-refX) <=1) && (Math.abs(authY-refY) <=1)) {  // If the position is within one X or Y coordinate then increment the positioning score by 1
  35.      positioningScore++;
  36.    }
  37.  
  38.   }
  39.  
  40.   if (angularScore/numberOfKeyPresses < 0.06) {
  41.    angularOK = true;
  42.    println("Angular positioning is OK");
  43.   }
  44.   else println("Angular positioning FAIL");
  45.  
  46.   positioningScore = positioningScore/numberOfKeyPresses;
  47.  
  48.   if (positioningScore > 0.5) {
  49.    positioningOK = true;
  50.    println("Absolute positioning is OK");
  51.   }
  52.   else println("Absolute positioning FAIL");
  53.  
  54.   println("Absolute positioning score: " + positioningScore);
  55.   println("Angular positioning score: "+(angularScore/numberOfKeyPresses)+"\n\n");
  56.   if (angularOK && positioningOK) return 1;
  57.   else return 0;
  58.  
  59. }


Tuesday 8th July


I consider the software implementation to be complete. This means that I am able to turn my attention to the actual design of the survey, and the method by which it will be carried out. I will need to identify the goals of the system, and devise relevant questions which would allow me to gague the effectiveness as the project as a whole. I have been reading "Power of survey design : a user's guide for managing surveys, interpreting results, and influencing respondent" which looks to be a useful resource in devising this survey. However, it does not appear to be possible to rent a hard copy of the book from either the University or Portsmouth libraries. I will be taking notes tomorrow on the first outlines and aims of the user survey, which I will then put here. This research will form the basis for the survey to be taken by prospective end-users.

Wednesday 9th July


Initial Survery Research:




This bias can be mitigated by using lead-in statements on both desirable and undesirable events, that is, "Many believe that . . . while others think that. . . . What is your opinion?"
 


The book also talks about the language that should be used when writing a survey. It would be easy to use technical language in order to get a potentially complicated point over to the user. However, the user may not have experiance with such language, and may simply provide an answer so as to not appear ignorant. Specifically, the book states:


Thursday 10th July


I now have some basic rules for creating a user survey. I am contemplating creating two surveys. One which will be used alongside the equipment which is being physically trialled at the time, and another which could be sent to participants who do not have the equipment in front of them, but who may still be able to provide useful information on pattern-based recognition. I could present the person with hypothetical situations (perhaps a picture of the monome) and gague their response.

Being that I am currently awaiting some guidanc on the initial survey design from my project supervisor, I have written a set of brief instructions which the user will need to follow prior to completing the user survey. The instructions are shown below.




Monday 14th July


Decided that during the user-survey, I will also record the coordinates that the user chooses for their authentication. This will then lend itself to analysis of common button combinations or positions which a user is likely to press. There may or may not be a corrillation or grouping that's noteworthy. The analysis of this information might reveal whether pattern-based authentication is subject to the same observations as passwords. For example, passwords (in English speaking countries) are more likely to contain a number of 'e's due to the structure of the english language. They will contain a number of vowels. They will probably not contain a number (weak pasword policy?). I will be looking for similar observations. For example, do the users use the outer-most buttons or simply concentrate on the inner buttons? What is the average chosen length of a button sequence?

Wednesday 16th July

I have started the user-survey. I am testing the user's memory skills to see if there is a correllation between the number of words/colours a user can remember, and their chosen pattern length. I suggest that those people who are proven to have a good memory capacity will naturally pick a longer chain of buttons in their pattern.

Question X:
Do your day-to-day responsibilities require you to remember more than one password?

Yes / No

Question X:
Do you use passwords which contain non-alphanumeric characters?

Yes / No

Question X:
Passwords are simple and easy to use.

Strongly Disagree 1 2 3 4 5 Strongly Agree

Question X:
Passwords are a secure method of authentication.

Strongly Disagree 1 2 3 4 5 Strongly Agree

Question X:
Please look at the following words for fifteen seconds, and then recite them in order:

Red
Potato
Tree
Cotton
Scone
Tape
Sheep
Slice
Pencil
Coat

Words correctly recited:

Question X:
Please look at the following colours for fifteen seconds, and then recite them in order:









Colours correctly recited:

Question X:
Could you see this method of authentication used as a sole authentication method - for example, entering a PIN number at an ATM?

Yes / No

Question X:
Could you see this method used as a secondary authentication method – for example, used in combination with a password?

Yes / No

Question X:
I successfully authenticated with the pattern-based authentication system.

Yes / No

Question X:
The system was easy to use.

Strongly Disagree 1 2 3 4 5 Strongly Agree

Question X:
I consider a six-button pattern to be more secure than a six-letter password.

Strongly Disagree 1 2 3 4 5 Strongly Agree

 


Friday 18th July


Expanded the user survey further. Also combined the inttructional and survey documents. The points I am aiming to answer with this survey are listed below:

- Basic information about the user's password habbits
- Perceived security of the system as a standalone unit (i.e as the primary authentication method for a given scenario)
- Perceived security of the system as a complimentary unit (i.e as a secondary method of authentication for a given scenario)
- Whether or not there is a correlation between the user's memory recall abilities (using both words and colours (visual memory)) and the pattern length a user is likely to choose
- Overall satisfaction when using the system
- Whether the user would consider using more or less buttons for the same purpose, and their perceived effect on the security of the system.

Monday 21st July


Expanded survey further. Asks about future developments using additional metrics such as time and colour. Also created the formatting for the general look of the survey. Questions are currently numbered as "Question X" so that I can re-arrange the question numbers. I have uploaded the survey so far as images, shown below.






Thursday 31st July


The last week has seen many modifications to the survey, as per advice from my supervisor. I have added questions, as well as changed the order of the questions such that all questions requiring the surveyor's assistance come first. I have also added room for expansion on a few yes or no questions, so I have a reason for some answers. These free-answer sections are optional however. I have also added room for notes at the end of each section, incase the user or surveyor wishes to note any pertinent points.

As well as this, I have written a detailed analysis of each question, which shows the aim of each question, followed by a hypothesis. This is useful so that I am aware of the information Iam hoping to gain by asking a question.


Monday 4th August


The survey is now complete. I have also slightly modified the program so that it outputs to a file when someone attempts authentication with the device. The output will allow me to record the positioning of people#s key presses, as well as the positions of their authentication attempt. This information will prove useful later when looking for correlations between pattern length and memory, as well as looking to see if users use all of the buttons, or just select groups.

A sample output is pasted here:


========================= Mon Aug 04 19:36:11 BST 2008 =========================

2) Ref: (3,0)
   Act: (4,2)

1) Ref: (2,0)
   Act: (4,1)

Angular positioning is FAIL
Absolute positioning FAIL

Angular positioning score: 0.125
Absolute positioning score: 0.0

Authentication attempt FAIL


Tuesday 26th August

One final update to the logbook, the past few weeks have been spent surveying users, and writing the final project report. The software has not changed since the final update. I wish to take this final opportunity to thank my project supervisor Chi for a very interesting and intriguing project, and the monome community for the excellent hardware!!