在数字时代,密码安全是每个人都必须面对的问题。尽管努力使用复杂的密码,但仍然可能遭受安全威胁。本文将介绍一种基于Arduino的密码管理器,它不仅能够存储和管理多个密码,还能通过硬件模拟键盘输入,提高密码输入的安全性。
密码管理器的核心是Arduino Leonardo,它基于ATMega32U4微控制器,具有硬件USB功能。也可以选择Arduino Due或Yún,因为这些板子使用的控制器同样支持USB原生功能。其他Arduino型号则不适用,因为它们使用的是USB-串口转换芯片,无法模拟键盘或鼠标等USB设备。
为了实现键盘模拟功能,设备将不同网站的登录密码存储在微控制器的FLASH内存中,通过单次按键操作即可将密码输入到选定的密码框中。由于设备模拟键盘输入,因此该解决方案不受平台限制,可以在Windows、Linux、OS-X甚至手机和平板电脑上使用,无论操作系统如何。
用户界面使用了LCD键盘盾,可以在多个网站购买。例如,可以从eBay上的中国卖家那里购买。每个卖家可能有不同的版本,但它们的工作原理相同。盾牌配备了Hitachi HD44780U兼容的16x2字符LCD和5个按钮。LCD可以使用内置的LiquidCrystal库,无需额外的库。
软件相对简单。启动时,设备会要求输入解锁键。解锁键是盾牌上的一系列按键操作,可以在草图中配置,可以使用任何组合和长度。但是只有四个按键被使用,因此如果真正想将其作为完整的密钥库,建议使用8个或更多的按键组合。
为了防止暴力破解,设备具有锁定计时器。当输入3个无效的解锁代码时,锁定计时器开始工作,需要等待30秒才能再次尝试输入代码。可以通过重置板子退出此状态,但由于Arduino引导程序的原因,至少需要5秒钟。
理论上,如果解锁码使用10个按键操作,那么总的组合数为4^10,即1,048,576。假设解锁码非常强大,需要尝试每一种组合。假设攻击者非常快,可以在1秒内尝试一种组合,但每3种组合就会锁定,那么每3种组合至少需要5秒,这意味着攻击者在理论上需要至少2,446,677秒来测试所有组合,即28天不眠不休。
如果成功解锁设备,可以使用上下按钮选择账户。按下选择键将存储的密码发送到计算机。
#include
// 解锁键。U - 上,D - 下,L - 左,R - 右
char unlock[] = "UUDDLR";
// 密码描述
static char *desc[] = {
"Facebook",
"Gmail",
"CodeProject"
};
// 密码
static char *keys[] = {
"Password1",
"Password2",
"Password3"
};
// 密码和描述的数量
#define COUNT 3
int index = 0;
#define UP 0
#define DOWN 1
#define SELECT 2
#define RIGHT 3
#define LEFT 4
// 初始化LiquidCrystal库。
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
// 等待按键并返回其标识符
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);
}
// 初始化控制器
void setup() {
Keyboard.begin();
lcd.begin(16, 2);
lcd.setCursor(0, 0);
lcd.print("Arduino Keylock");
lcd.setCursor(0, 1);
lcd.print("by Webmaster442");
delay(2000);
Unlock();
}
// 等待解锁码
void Unlock() {
int len = strlen(unlock);
char c;
int good, bad, i, count, repeat;
count = 0;
while (1) {
c = '-';
good = 0;
bad = 0;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Enter unlock key:");
lcd.setCursor(0, 1);
i = 0;
repeat = 1;
while (repeat) {
int key = ReadKey();
delay(200);
switch (key) {
case UP:
c = 'U';
break;
case DOWN:
c = 'D';
break;
case LEFT:
c = 'L';
break;
case RIGHT:
c = 'R';
break;
case SELECT:
repeat = 0;
break;
}
if (unlock[i] == c) ++good;
else if (repeat != 0) ++bad;
lcd.print(c);
++i;
if (i > len) break;
}
if ((good == len) && (bad == 0))
return;
else {
++count;
if (count > 2) {
for (int i = 30; i >= 0; i--) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Stop guessing!");
lcd.setCursor(0, 1);
lcd.print("Wait ");
lcd.print(i);
lcd.print(" Seconds");
delay(1000);
}
count = 0;
}
}
}
}
void loop() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Key: ");
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;
}
}