Unobservable pin and password entry

This is a combination of two blog posts from Linuxjunk. The general idea to to create a pin and password entry system where even if you want watch the whole process and you know exactly what the user typed in, you still can’t unambiguously guess their password. I think it’s a pretty neat idea.

A one to many mapping is created between password symbols and the numbers you type in. That means the value you type in could come from any of a number of password characters. The mapping should be random so that the values you enter every time will be different. Given enough observations of course the attacker could identify the password, but it offers some security and could offer some advantage over challenge and response type systems.

I have two pieces of code, one from pin entry and one from alphabetic password entry.

Unobservable pin entry

The following is a command line, number mapping based version of the technique described at http://dapl.me/pinsecurity.html. It’s just a proof of concept, but I think Dans general idea is pretty neat. It could be a viable alternative to challenge and response type systems, and having this as a PAM module would be neat.

The following code is just a proof of concept. But it presents the user with two lists of numbers. The user PIN is hard coded as 1803 in this example. The first line shows the numbers 0 through 9. The second a random list of numbers.

The user enters numbers matching mapping from the first line to the second. Using this method it’s impossible for an observer to unambiguously guess the users pin.

Here’s an example run:

0,1,2,3,4,5,6,7,8,9,
2,4,3,3,0,2,4,3,3,4,
please enter pin: 4323
#include <iostream>
#include <vector>

using namespace std;

int main() {

 int valid_pw[5];
 valid_pw[0] = 1;
 valid_pw[1] = 8;
 valid_pw[2] = 0;
 valid_pw[3] = 3;

 int random_mapping[10];

 for(size_t n=0;n<10;n++) {
   random_mapping[n] = rand()%5;
 }

 for(size_t n=0;n<10;n++) {
   cout << n << ",";
 }
 cout << endl;

 for(size_t n=0;n<10;n++) {
   cout << random_mapping[n] << ",";
 }
 cout << endl;

 cout << "please enter pin: ";

 int get_pw[4];

 // get pw...
 string input_line;
 getline(cin, input_line);

 for(size_t n=0;n<4;n++) {
   string singlechar;
   singlechar  += input_line[n];
   get_pw[n] = atoi(singlechar.c_str());
 }

 // validate pw
 bool pass = true;
 for(size_t n=0;n<4;n++) {

   // find all locations with this mapping
   vector<int> valid_locs;
   for(size_t i=0;i<10;i++) {
     if(random_mapping[i] == get_pw[n]) valid_locs.push_back(i);
   }


   // if any match valid_pw[n] then the password is ok.
   bool thispass=false;
   for(size_t i=0;i<valid_locs.size();i++) {
     // cout << "valid: " << valid_locs[i] << endl;
     if(valid_locs[i] == valid_pw[n]) thispass=true;
   }
   if(thispass == false) pass = false;
 }
 cout << endl;

 if(pass) cout << "password valid"   << endl;
     else cout << "password invalid" << endl;
}

Unobservable alphabetic password entry

This version allows you to use alphabetic passwords. It takes a command line argument which is the true password. When run it displays the following prompt:

$ ./a.out test
| a | b | c | d | e | f | g | h | i | j | k | l | m | 
| 1 | 5 | 0 | 5 | 4 | 6 | 8 | 2 | 0 | 9 | 6 | 7 | 9 | 

| n | o | p | q | r | s | t | u | v | w | x | y | z | 
| 4 | 4 | 8 | 3 | 4 | 0 | 3 | 5 | 9 | 7 | 3 | 2 | 1 | 

please enter pin: 3403
password valid
#include <iostream>
#include <vector>

using namespace std;

void display_mapping(size_t start,size_t end,vector<int> random_mapping) {
  cout << "| ";
  for(size_t n=start;n<end;n++) {
    string singlechar;
    singlechar += 'a' + n;
    cout << singlechar << " | ";
  }
  cout << endl;

  cout << "| ";
  for(size_t n=start;n<end;n++) {
    cout << random_mapping[n] << " | ";
  }
  cout << endl;
}

int main(int argc,char **argv) {

 const int entry_alphabet_size    = 10;
 const int password_alphabet_size = 26;

 // read valid password from commandline
 vector<char> valid_pw;
 string valid_pw_str = argv[1];

 for(size_t n=0;n<valid_pw_str.size();n++) {
   string singlechar;
   singlechar  += valid_pw_str[n];
   valid_pw.push_back(singlechar.c_str()[0]);
 }

 // build random mapping, use each entry symbol the same (or as close as) number of times.
 vector<int> random_mapping(26,-1);

 srand(time(NULL));
 for(size_t n=0;n<entry_alphabet_size;n++) {
   for(size_t i=0;i<(password_alphabet_size/entry_alphabet_size);i++) {
     bool unset = true;
     for(;unset;) {
       size_t position = rand()%password_alphabet_size;
       if(random_mapping[position] == -1) { random_mapping[position] = n; unset = false; }
     }
   }
 }

 for(size_t n=0;n<password_alphabet_size;n++) {
   if(random_mapping[n] == -1) random_mapping[n] = rand()%entry_alphabet_size;
 }

 // display mapping for user
 display_mapping(0,13 ,random_mapping);
 cout << endl;
 display_mapping(13,26,random_mapping);
 cout << endl;



 cout << "please enter pin: ";

 vector<int> get_pw;

 // get pw...
 string input_line;
 getline(cin, input_line);

 for(size_t n=0;n<input_line.size();n++) {
   string singlechar;
   singlechar  += input_line[n];
   get_pw.push_back(atoi(singlechar.c_str()));
 }

 // validate pw
 bool pass = true;
 if(valid_pw.size() != get_pw.size()) {
   pass = false;
 } else {
   for(size_t n=0;n<valid_pw.size();n++) {

     // find all locations with this mapping
     vector<char> valid_locs;
     for(size_t i=0;i<26;i++) {
       if(random_mapping[i] == get_pw[n]) valid_locs.push_back('a' + i);
     }

     // if any match valid_pw[n] then the password is ok.
     bool thispass=false;
     for(size_t i=0;i<valid_locs.size();i++) {
       if(valid_locs[i] == valid_pw[n]) thispass=true;
     }
     if(thispass == false) pass = false;
   }
 }

 if(pass) cout << "password valid"   << endl;
     else cout << "password invalid" << endl;
}