Um “cofre” para passwords simples e de baixo custo

Introdução

Um dos mais comuns e mais falados problemas de segurança de um sistema de informação são as passwords sem “qualidade” muitas vezes motivadas pela dificuldade de memorização das mesmas.

De forma a enquadrar o leitor, cada password deve ter um comprimento adequado e preferencialmente não ser previsível. Por exemplo, uma password como 1979aMelhorGeracaoDeSempre! (27 caracteres), é previsível se considerarmos que o utilizador nasceu em 1979 e possivelmente falará imenso desse facto gabando a sua geração. Neste caso, apesar de ser fácil de memorizar, é relativamente simples de “adivinhar”, ou melhor deduzir, por parte de alguém que pretenda obter acesso ao sistema no qual o utilizador em causa usa esta password.

Por outro lado, passwords com qualidade como: zb8@g-DMK&7@%pRyhE45DhbbPs$!angSRhHNUenBpu4AZ4+$KLA-gcJFYfdwV=yN$RXw6TmD-YTpBf9?dWRkRAXu35XhwE=d*!vt53-m8dq34fmr?cCAv#k#u*gsSdgg (128 caracteres), apesar de serem praticamente impossíveis de deduzir, são demasiado complexas para serem memorizadas, tornando o seu uso difícil e praticamente inviável.

Quanto mais complexa for uma password, mais complexa será a sua memorização e mais tendenciosa será. Por exemplo contrariamente à anteriormente apresentada que tem 128 caracteres, uma password grande em tamanho, definida por um método que não seja pseudo-aleatório, para memorização tenderá a ser um conjunto de caracteres segundo por exemplo uma cifra César.

Deve sempre existir uma razoabilidade entre o tamanho das passwords e aquilo que elas protegem. Os exemplos dados, são simplesmente para ilustrar a questão da dificuldade de memorizar passwords.

Existem diversos projectos que tentam resolver este problema; entre eles, um dos que me parece mais interessante e que recentemente se tornou comercialmente disponível, o Mooltipass, é baseado em Arduino. Este projecto, tem diversas funcionalidades sendo a sua segurança elevada, na minha opinião, perde um pouco pelo elevado custo.

Com base no conceito de “cofre de passwords” e com a ideia de que o factor custo pode em muitos casos ser um problema, pensei em fazer o meu próprio XXVB“cofre de passwords”, de baixo custo, que se comportasse como um teclado e fosse capaz de digitar qualquer uma das minhas passwords, sem que eu tivesse de as digitar manualmente nem das saber de cabeça! Passemos então, caro leitor, à prática…

Hardware

Existem centenas de microcontroladores disponíveis no mercado, uns mais caros, outros mais baratos, para todos os gostos e propósitos. Neste caso, usei e, recomendo o uso dos microcontroladores baseados em ATMega U32, como o caso do Arduino Leonardo, Yun, etc… Esta escolha prende-se principalmente pelo suporte USB de que dispõem, permitindo que sejam tratados pelo sistema operativo como um teclado USB.

Neste caso usei um Arduino Leonardo R3, baseado no Microcontrolador ATmega32u4, com 20 pinos digitais de input/output, 7 canais PWM, 32KB de memória Flash (dos quais apenas 28 estão disponíveis, pois 4 são usados pelo bootloader), 2.5KB de SRAM, 1KB de EEPROM, e um peso relativamente baixo de 20g (o circuito completo pesará cerca de 70g).

Depois de alguma pesquisa e tendo em conta que era desejável um circuito de pequenas dimensões, baixo custo e uma interface de utilizador simples de usar, optei por adicionar ao Arduino, um LCD Keypad Shield, simples com 2 linhas por 16 colunas, 6 teclas, com 8 caracteres programáveis pelo utilizador, de forma a ser possível “digitar” uma password e interagir com o dispositivo.

Programa

Escolhidos os componentes de hardware, o circuito é relativamente fácil de programar em C++, usando ou o ambiente de desenvolvimento do Arduino, ou outro, como o Visual Studio. Optei por fazer a reprogramação na linha de comandos usando um editor de texto (no meu caso o nano), e usando o platformio, para compilar e fazer upload do programa via interface de linha de comandos (CLI).

Como as passwords serão armazenadas na memória flash do Arduino e considerando que estas devem ser mudadas com regularidade, o que implica compilar novamente o sketch do Arduino e fazer o respectivo upload, o platformio é uma opção bastante boa para estas tarefas, sendo de instalação e utilização simples, cross-platform. Acima de tudo, é eficaz.

Instalação do Platformio

Para instalar o platformio deve-se ter instalado o Python 2.7, (atenção que não é compatível com as versões 3.x).

Em GNU/Linux e Mac OS X, a instalação segue os seguintes passos:

python -c "$(curl -fsSL https://raw.githubusercontent.com/platformio/platformio/master/scripts/get-platformio.py)"
cd /path/get-platformio.py/script
python get-platformio.py
pip install https://github.com/platformio/platformio/archive/develop.zip
pip install platformio && pip install --egg scons
pip install -U platformio
pip install https://github.com/platformio/platformio/archive/develop.zip

Em Windows a instalação segue passos ligeiramente diferentes:

python.exe get-platformio.py
pip search platformio
pip install platformio && pip install --egg scons
pip install -U platformio
pip install https://github.com/platformio/platformio/archive/develop.zip

Uma vez instalado o platfromio, será necessário instalar a plataforma atmelavr. Neste caso os passos são os mesmos quer se esteja a usar Windows, GNU/Linux ou MacOS X.

platformio install atmelavr
cd directorioDoProjecto
platformio init

Dentro desta directoria estará um ficheiro chamado platformio.ini, que devemos editar e colocar com as configurações correctas, de placa de desenvolvimento, plataforma, framework e porta.

#
# Project Configuration File
#
# A detailed documentation with the EXAMPLES is located here:
# http://docs.platformio.org/en/latest/projectconf.html
#
# A sign `#` at the beginning of the line indicates a comment
# Comment lines are ignored.
# Simple and base environment
[env:mybaseenv]
platform = atmelavr
framework = arduino
board = leonardo
upload_port = COM10
#
# Automatic targets - enable auto-uploading
targets = upload

No meu caso a porta correcta foi a COM10. Convém verificar qual a porta que está a ser utilizada pelo Arduino.

Para compilar e fazer o upload do sketch para o Arduino, utilizando o platformio, via CLI, basta digitar o comando:

platformio run --target upload

Voltemos ao desenvolvimento da nossa aplicação, que irá armazenar as passwords e “digitá-las” quando precisarmos.

Tal como escrevi no início do artigo, normalmente temos mais do que uma password diferente e essa será, de facto, a política mais segura. Uma vez que as passwords serão armazenadas num “cofre”, será necessária uma só senha para acedermos a todas as senhas guardadas nele. No entanto precisamos de aceder a cada senha especificamente, para não inserirmos senhas trocadas, bem como para escolhermos que senha digitar. Para tal precisamos de um interface que nos permita escolher a senha. Neste caso será o LCD a mostrar a senha e o keypad vai permitir-nos escolher a senha.

Explorando um pouco o código:

No início do programa, por brincadeira e até nostalgia, criei alguns caracteres de 8×5 bits, dos quais apenas uso dois no início do programa, mas que não deixam de ter a sua graça. Caso o leitor deseje explorar um pouco mais bastará eliminar um dos existentes no programa e criar um novo, sendo o processo simples. Os bits a zero, significam que nada será exibido no LCD e os bits a 1 exactamente o oposto, como se ilustra no exemplo seguinte:

byte smiley[8] = {
  B00000,
  B10001,
  B00000,
  B00000,
  B10001,
  B01110,
  B00000,
};

Como o LCD Keypad tem apenas 6 teclas (Select; Left; Up; Down; Right; Rst), e dessas 6 apenas devem ser usadas 5, pois a tecla Rst está pré-definida para fazer o reset ao circuito, temos de programar a intercepção das restantes teclas, para lhes atribuirmos funcionalidades. Para tal começamos por definir os valores delas quando premidas:

//mapa de teclas e respectivos valores
/*
+----------+---------+
| Texto    | Pino    |
+----------+---------+
|   UP     |   0     |
|  DOWN    |   1     |
|  SELECT  |   2     |
|  RIGHT   |   3     |
|  LEFT    |   4     |
+----------+---------+
*/
#define UP 0
#define DOWN 1
#define SELECT 2
#define RIGHT 3
#define LEFT 4

De seguida criamos as funções destinadas à leitura das teclas se premidas. As teclas estão ligadas a pinos analógicos, logo temos de ler intervalos de valores, usando a função analogread(), para fazer a leitura dos mesmos.

int ReadKey()
{
  int x = 1023;
  do
  {
    x = analogRead (0);
    if (x < 60) return RIGHT;
    else if (x < 200) return UP;
    else if (x < 400) return DOWN;
    else if (x < 600) return LEFT;
    else if (x < 800) return SELECT;
  }
  while (x > 800);
}

Com estas funcionalidades básicas implementadas, optei por criar um array de chars com todos os caracteres “A-Z”; “a-z”; “0-9”, para permitir que a password seja uma string do comprimento que o utilizador decidir. Também foi opção na programação das teclas usar a tecla “left” para acrescentar o caracter actual à password, permitindo assim escrever a password, normalmente, e a tecla Select, para “introduzir” a password, uma vez toda digitada.

Posto isto, basta apenas fazer um simples menu, como é explicado no próprio código, para se navegar entre as senhas armazenadas.

O código do scratch será o seguinte:

/*
*Thx to Mkman for the help and advice!
*In loving memory of
*       Misha II
* 12/12/2004 - 13/07/2015
*    You are missed
*/

#include <LiquidCrystal.h>
#include <String.h>

//cria alguns caracteres engraçados
byte smiley[8] = {
  B00000,
  B10001,
  B00000,
  B00000,
  B10001,
  B01110,
  B00000,
};
byte skull[8] = {
  B00000,
  B01110,
  B10101,
  B11111,
  B11011,
  B01110,
  B01010,
};
byte tulip[8] = {
  B10101,
  B11111,
  B11111,
  B01110,
  B00100,
  B10101,
  B01110,
  B00100
};
byte prompt[8] = {
  B10000,
  B01000,
  B00100,
  B00010,
  B00010,
  B00100,
  B01000,
  B10000
};
byte et[8] = {
  B11111,
  B10101,
  B11111,
  B00100,
  B01110,
  B11111,
  B11111,
  B11111
};
byte sandclock[8] = {
  B11111,
  B01110,
  B01110,
  B00100,
  B01110,
  B01110,
  B11111,
  B00000
};
byte cat[8] = {
  B01010,
  B11111,
  B10101,
  B11111,
  B00100,
  B01110,
  B01110,
  B11111
};

//define a senha para abrir o cofre
static String strkey = "abcddcba";
//Descrições das passwords
//São armazenado na memória flash (não volátil) do arduino.
static char *desc[] = { "P@P", "Gmail", "MyApp", "Blog", "FacelessBook"  };
//Passwords
static char *keys[] = { "Password1", "Password2", "Password3", "Password4", "Password5" };
//array de caracteres ascii com as letras a-z;A-Z;0-9
static char ascii[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
//mapa de teclas e respectivos valores

/*
+----------+---------+
| Texto    | Pino    |
+----------+---------+
|   UP     |   0     |
|  DOWN    |   1     |
|  SELECT  |   2     |
|  RIGHT   |   3     |
|  LEFT    |   4     |
+----------+---------+
*/
#define UP 0
#define DOWN 1
#define SELECT 2
#define RIGHT 3
#define LEFT 4

int index = 0;
#define COUNT 5 //numero de passwords e descrições armazenadas
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); //Inicializa O lcd

/*
*função que tem como missão interceptar a leitura analógica dos pinos das teclas up, down, left, right e select
*/
int ReadKey()
{
  int x = 1023;
  do
  {
    x = analogRead (0);
    if (x < 60) return RIGHT;
    else if (x < 200) return UP;
    else if (x < 400) return DOWN;
    else if (x < 600) return LEFT;
    else if (x < 800) return SELECT;
  }
  while (x > 800);
}
/*
*funcao setup
*/
void setup()
{
  Keyboard.begin();
  lcd.createChar(0, smiley);
  lcd.createChar(1, skull);
  lcd.createChar(2, tulip);
  lcd.createChar(3, prompt);
  lcd.begin(16, 2); //16 colunas por 2 Linhas
  lcd.setCursor(0, 0);
  lcd.print("Lego Pwd Safe");
  lcd.setCursor(0, 1);
  lcd.print("by apocs");
  lcd.write(byte(0));
  lcd.write(byte(1));
  lcd.write(byte(2));
  delay(5000);
  lcd.clear();
  Unlock();
}

/*
*funcao que tem por tarefa interceptar as teclas primidas e permitir que seja composta a string da password
*/
int inkeys()
{
  lcd.write(byte(3));
  int i = 0;
  char c;
  String str;
  //int repeat = 1;
  while (1)
  {
    int key = ReadKey();
    delay(200);
    switch (key)
    {
      case UP:
        if (i >= 62)
        {
          i = 0;
        }
        c = ascii[i];
        lcd.print(c);
        //inc
        i++;
        //lcd.clear();
        break;
      case DOWN:
        if (i <= 0)
        {
          i = 0;
        } else if (i >= 62)
        {
          i = 0;
        }
        else {}
        i--;
        c = ascii[i];
        lcd.print(c);
        break;
      case LEFT:
        lcd.clear();
        break;
      case RIGHT:
        str += c;
        lcd.clear();
        break;
      case SELECT:
        Serial.println(str); //for debug porposes
        Serial.println(strkey); //for debug porposes
        return str.compareTo(strkey);
    }
  }
}
/*
*funcao de bloqueio
*/
void blocked(void) {
  int i = 0;
  for (i = 30; i >= 0; i--) {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Locked");
    lcd.setCursor(0, 1);
    lcd.print("");
    lcd.print("seg");
    delay(1000);
  }
}
/*
* funcao de desbloqueio
*/
void Unlock() {
  int count = 0;
  while (1) {
    if (inkeys() != 0)
      count++;
    else
      return;

    if (count == 2) {
      blocked();
      count = 0;
    }
  }
}

void loop()
{
  // put your main code here, to run repeatedly:
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Pass: ");
  lcd.setCursor(0, 1);
  lcd.print(desc[index]);
  int key = ReadKey();
  delay(200);
  switch (key)
  {
    case UP:
      --index;
      if (index < 0) index = COUNT - 1;
      break;
    case DOWN:
      ++index;
      if (index > (COUNT - 1)) index = 0;
      break;
    case SELECT:
      Keyboard.print(keys[index]);
      lcd.print(" - OK");
      delay(1000);
      break;
  }
}

Uma vez carregado o scratch no Arduino, bastará ligá-lo numa porta USB, que fará a dupla funcionalidade de o alimentar e de comunicar com o computador, permitindo assim que se utilize o circuito para introduzir passwords onde precisamos de as usar.

Cada vez que alteremos ou acrescentemos passwords ao “cofre”, bastará alterar o valor da constante COUNT e/ou acrescentar ou alterar nos vectores de descrição e chaves, as respectivas descrições e chaves.

Conclusão

Como referido no início do artigo, recordar na nossa própria memória dezenas de passwords é extremamente difícil, pelo que o armazenamento externo das mesmas se torna interessante do ponto de vista prático, mas criando um problema de segurança. O armazenamento das mesmas num circuito externo pode ser uma solução, desde que tal se revele prático. Ao longo deste artigo, expliquei como construir um circuito, baseado em Arduino Leonardo e LCD Keypad de 6 teclas, bem como compilar o código e fazer o upload via CLI. Certamente este código pode ser muito melhorado, como por exemplo a colocação das passwords no 1KB de EEPROM do Arduino ou o armazenamento encriptado das senhas, etc. Fica ao critério do leitor, modificar o código e o projecto caso assim o entenda. Neste caso esta solução não é 100% eficaz em termos de segurança pelo que não se recomenda o seu uso em ambientes mais sensíveis.

Em termos de hardware também podem ser feitas diversas melhorias, como por exemplo a ligação de módulos de memória externos, por exemplo um NXP PCF8570P ou uma EEPROM 24LC256, que são relativamente simples em termos de interface com o Arduino, ou até acrescentar funcionalidades de criptografia por hardware, por exemplo com um Atmel ATSHA204, que pode ser facilmente encontrado on-line por um preço bastante baixo, mesmo quando já montado numa breakout board, o que facilitará a ligação ao Arduino.

Outra possibilidade que não foi explorada neste artigo, foi a escrita de um driver não-standard para o dispositivo, que permitisse algumas funcionalidades extra. Neste caso foi uma opção para evitar usar drivers não padrão, uma vez que o dispositivo funciona nas principais plataformas “Microsoft Windows, GNU/Linux e Apple  Mac OS”. Ficou igualmente por explorar o desenvolvimento de uma app com uma interface de utilizador simplificada, para o carregamento de novas passwords e descrições para o circuito! Inicialmente ainda pensei em incluir também esta temática no artigo, mas cedo decidi não o fazer deixando ao critério do leitor desenvolver a sua própria aplicação caso ache interessante. Pessoalmente eu fá-lo-ia usando as ferramentas Xamarin, uma vez que se trata de um sistema cross-platfrom, mas preferi deixar ao critério do leitor e/ou de um segundo artigo sobre este mesmo tema.

Publicado na edição 50 (PDF) da Revista PROGRAMAR.