mirror of
https://github.com/bitlair/bitlair_doorduino.git
synced 2025-05-13 12:20:07 +02:00
first checkin
This commit is contained in:
commit
33c1f968f2
15 changed files with 3574 additions and 0 deletions
423
bitlair_doorduino/bitlair_doorduino.ino
Normal file
423
bitlair_doorduino/bitlair_doorduino.ino
Normal file
|
@ -0,0 +1,423 @@
|
|||
#include "OneWire.h"
|
||||
#include "ds1961.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <EEPROM.h>
|
||||
#include "Entropy.h"
|
||||
#include "sha1.h"
|
||||
|
||||
#define PIN_1WIRE 2
|
||||
#define PIN_LEDGREEN 3
|
||||
#define PIN_LEDRED 4
|
||||
|
||||
#define CMD_BUFSIZE 64
|
||||
#define CMD_TIMEOUT 10000 //command timeout in milliseconds
|
||||
|
||||
#define SECRETSIZE 8
|
||||
#define ADDRSIZE 8
|
||||
#define STORAGESIZE (SECRETSIZE + ADDRSIZE)
|
||||
#define EEPROMSIZE 1024
|
||||
#define SHA1SIZE 20
|
||||
|
||||
#define IBUTTON_SEARCH_TIMEOUT 60000 //timeout searching for ibutton
|
||||
|
||||
#define LEDState_Off 0
|
||||
#define LEDState_Reading 1
|
||||
#define LEDState_Authorized 2
|
||||
#define LEDState_Busy 3
|
||||
|
||||
#define htons(x) ( ((x)<<8) | (((x)>>8)&0xFF) )
|
||||
#define ntohs(x) htons(x)
|
||||
|
||||
#define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \
|
||||
((x)<< 8 & 0x00FF0000UL) | \
|
||||
((x)>> 8 & 0x0000FF00UL) | \
|
||||
((x)>>24 & 0x000000FFUL) )
|
||||
#define ntohl(x) htonl(x)
|
||||
|
||||
OneWire ds(PIN_1WIRE);
|
||||
DS1961 ibutton(&ds);
|
||||
|
||||
int Serialprintf (const char* fmt, ...) __attribute__ ((format (printf, 1, 2)));
|
||||
|
||||
int Serialprintf (const char* fmt, ...)
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(buf, sizeof(buf), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
Serial.print(buf);
|
||||
}
|
||||
|
||||
void SetLEDState(uint8_t ledstate)
|
||||
{
|
||||
if (ledstate == LEDState_Off)
|
||||
{
|
||||
digitalWrite(PIN_LEDGREEN, LOW);
|
||||
digitalWrite(PIN_LEDRED, LOW);
|
||||
}
|
||||
else if (ledstate == LEDState_Reading)
|
||||
{
|
||||
digitalWrite(PIN_LEDGREEN, LOW);
|
||||
digitalWrite(PIN_LEDRED, HIGH);
|
||||
}
|
||||
else if (ledstate == LEDState_Authorized)
|
||||
{
|
||||
digitalWrite(PIN_LEDGREEN, HIGH);
|
||||
digitalWrite(PIN_LEDRED, LOW);
|
||||
}
|
||||
else if (ledstate == LEDState_Busy)
|
||||
{
|
||||
digitalWrite(PIN_LEDGREEN, HIGH);
|
||||
digitalWrite(PIN_LEDRED, HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Serial.println("DEBUG: Board started");
|
||||
|
||||
pinMode(PIN_LEDGREEN, OUTPUT);
|
||||
pinMode(PIN_LEDRED, OUTPUT);
|
||||
|
||||
SetLEDState(LEDState_Off);
|
||||
|
||||
Entropy.initialize();
|
||||
}
|
||||
|
||||
void AddButton(uint8_t* addr, uint8_t* secret)
|
||||
{
|
||||
for (uint16_t i = 0; i < EEPROMSIZE / STORAGESIZE; i++)
|
||||
{
|
||||
bool emptyslot = true;
|
||||
uint16_t startaddr = i * STORAGESIZE;
|
||||
for (uint16_t j = 0; j < ADDRSIZE; j++)
|
||||
{
|
||||
uint8_t eeprombyte = EEPROM.read(startaddr + j);
|
||||
if (eeprombyte != 0xFF && eeprombyte != addr[j])
|
||||
{
|
||||
emptyslot = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (emptyslot)
|
||||
{
|
||||
for (uint16_t j = 0; j < ADDRSIZE; j++)
|
||||
EEPROM.write(startaddr + j, addr[j]);
|
||||
|
||||
for (uint16_t j = 0; j < SECRETSIZE; j++)
|
||||
EEPROM.write(startaddr + j + ADDRSIZE, secret[j]);
|
||||
|
||||
Serialprintf("DEBUG: stored button in slot %i\n", i);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Serial.println("ERROR: no room in eeprom to store button");
|
||||
}
|
||||
|
||||
void RemoveButton(uint8_t* addr)
|
||||
{
|
||||
for (uint16_t i = 0; i < EEPROMSIZE / STORAGESIZE; i++)
|
||||
{
|
||||
uint16_t startaddr = i * STORAGESIZE;
|
||||
bool sameaddr = true;
|
||||
for (uint16_t j = 0; j < ADDRSIZE; j++)
|
||||
{
|
||||
uint8_t eeprombyte = EEPROM.read(startaddr + j);
|
||||
if (eeprombyte != addr[j])
|
||||
{
|
||||
sameaddr = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!sameaddr)
|
||||
continue;
|
||||
|
||||
Serialprintf("DEBUG: erasing slot %i\n", i);
|
||||
|
||||
for (uint16_t j = 0; j < STORAGESIZE; j++)
|
||||
EEPROM.write(startaddr + j, 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
bool GetButtonSecret(uint8_t* addr, uint8_t* secret)
|
||||
{
|
||||
for (uint16_t i = 0; i < EEPROMSIZE / STORAGESIZE; i++)
|
||||
{
|
||||
uint16_t startaddr = i * STORAGESIZE;
|
||||
bool sameaddr = true;
|
||||
for (uint16_t j = 0; j < ADDRSIZE; j++)
|
||||
{
|
||||
uint8_t eeprombyte = EEPROM.read(startaddr + j);
|
||||
if (eeprombyte != addr[j])
|
||||
{
|
||||
sameaddr = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sameaddr)
|
||||
{
|
||||
Serialprintf("DEBUG: getting secret from slot %i\n", i);
|
||||
|
||||
for (uint16_t j = 0; j < SECRETSIZE; j++)
|
||||
secret[j] = EEPROM.read(startaddr + j + ADDRSIZE);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Serial.println("DEBUG: can't find secret for button");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AuthenticateButton(uint8_t* addr, uint8_t* secret)
|
||||
{
|
||||
uint8_t mac_from_ibutton[SHA1SIZE];
|
||||
uint8_t data[32];
|
||||
uint8_t nonce[3];
|
||||
|
||||
for (uint8_t i = 0; i < sizeof(nonce); i++)
|
||||
nonce[i] = Entropy.randomByte();
|
||||
|
||||
if (!ibutton.ReadAuthWithChallenge(addr, 0, nonce, data, mac_from_ibutton))
|
||||
{
|
||||
Serial.println("Button authentication failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
sha1::sha1nfo sha1data = {};
|
||||
sha1::sha1_init(&sha1data);
|
||||
sha1::sha1_write(&sha1data, (const char*)secret, 4);
|
||||
sha1::sha1_write(&sha1data, (const char*)data, sizeof(data));
|
||||
sha1::sha1_writebyte(&sha1data, 0xff);
|
||||
sha1::sha1_writebyte(&sha1data, 0xff);
|
||||
sha1::sha1_writebyte(&sha1data, 0xff);
|
||||
sha1::sha1_writebyte(&sha1data, 0xff);
|
||||
sha1::sha1_writebyte(&sha1data, 0x40);
|
||||
sha1::sha1_write(&sha1data, (const char*)addr, ADDRSIZE - 1);
|
||||
sha1::sha1_write(&sha1data, (const char*)secret + 4, 4);
|
||||
sha1::sha1_write(&sha1data, (const char*)nonce, sizeof(nonce));
|
||||
|
||||
uint8_t* sha_computed = sha1::sha1_result(&sha1data);
|
||||
|
||||
uint8_t mac_computed[SHA1SIZE];
|
||||
((uint32_t*)mac_computed)[0] = htonl(ntohl(*(uint32_t *)sha_computed) - 0x67452301);
|
||||
((uint32_t*)mac_computed)[1] = htonl(ntohl(*(uint32_t *)(sha_computed+4)) - 0xefcdab89);
|
||||
((uint32_t*)mac_computed)[2] = htonl(ntohl(*(uint32_t *)(sha_computed+8)) - 0x98badcfe);
|
||||
((uint32_t*)mac_computed)[3] = htonl(ntohl(*(uint32_t *)(sha_computed+12)) - 0x10325476);
|
||||
((uint32_t*)mac_computed)[4] = htonl(ntohl(*(uint32_t *)(sha_computed+16)) - 0xc3d2e1f0);
|
||||
|
||||
for (uint8_t i = 0; i < SHA1SIZE; i++)
|
||||
Serialprintf("%02x %02x\n", ((uint8_t*)mac_from_ibutton)[i], ((uint8_t*)mac_computed)[SHA1SIZE - 1 - i]);
|
||||
|
||||
for (uint8_t i = 0; i < SHA1SIZE; i++)
|
||||
{
|
||||
if (mac_from_ibutton[i] != mac_computed[SHA1SIZE - 1 - i])
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadCMD(char* cmdbuf, uint8_t* cmdbuffill)
|
||||
{
|
||||
uint32_t cmdstarttime = millis();
|
||||
*cmdbuffill = 0;
|
||||
for(;;)
|
||||
{
|
||||
if (Serial.available())
|
||||
{
|
||||
char input = Serial.read();
|
||||
if (input == '\n')
|
||||
{
|
||||
cmdbuf[*cmdbuffill] = 0;
|
||||
return true;
|
||||
}
|
||||
else if (*cmdbuffill < CMD_BUFSIZE - 1)
|
||||
{
|
||||
cmdbuf[*cmdbuffill] = input;
|
||||
(*cmdbuffill)++;
|
||||
}
|
||||
}
|
||||
|
||||
if (millis() - cmdstarttime >= CMD_TIMEOUT)
|
||||
{
|
||||
Serial.println("ERROR: timeout receiving command");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t NextWordPos(char* cmdbuf, uint8_t cmdbuffill, uint8_t index)
|
||||
{
|
||||
bool foundwhitespace = false;
|
||||
for (uint8_t i = index; i < cmdbuffill; i++)
|
||||
{
|
||||
if (!foundwhitespace)
|
||||
{
|
||||
if (cmdbuf[i] == ' ')
|
||||
foundwhitespace = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cmdbuf[i] != ' ')
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool GetHexWordFromCMD(char* cmdbuf, uint8_t cmdbuffill, uint8_t* wordpos, uint8_t* wordbuf, uint8_t wordsize, char* wordname)
|
||||
{
|
||||
*wordpos = NextWordPos(cmdbuf, cmdbuffill, *wordpos);
|
||||
|
||||
if (*wordpos == 0)
|
||||
{
|
||||
Serialprintf("ERROR: no %s found in command\n", wordname);
|
||||
return false;
|
||||
}
|
||||
else if (cmdbuffill - *wordpos < wordsize * 2)
|
||||
{
|
||||
Serialprintf("ERROR: %s is too short\n", wordname);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < wordsize; i++)
|
||||
{
|
||||
if ((cmdbuf[*wordpos + i * 2] == ' ') || (cmdbuf[*wordpos + i * 2 + 1] == ' '))
|
||||
{
|
||||
Serialprintf("ERROR: %s is too short\n", wordname);
|
||||
return false;
|
||||
}
|
||||
|
||||
int numread = sscanf(cmdbuf + *wordpos + i * 2, "%2hhx", wordbuf + i);
|
||||
if (numread != 1)
|
||||
{
|
||||
Serialprintf("ERROR: %s is invalid\n", wordname);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define CMD_ADD_BUTTON "add_button"
|
||||
#define CMD_REMOVE_BUTTON "remove_button"
|
||||
|
||||
void ParseCMD(char* cmdbuf, uint8_t cmdbuffill)
|
||||
{
|
||||
Serial.print("DEBUG: Received cmd: ");
|
||||
Serial.println(cmdbuf);
|
||||
|
||||
bool isadd = strncmp(CMD_ADD_BUTTON, cmdbuf, strlen(CMD_ADD_BUTTON)) == 0;
|
||||
bool isremove = strncmp(CMD_REMOVE_BUTTON, cmdbuf, strlen(CMD_REMOVE_BUTTON)) == 0;
|
||||
|
||||
if (isadd || isremove)
|
||||
{
|
||||
uint8_t wordpos = 0;
|
||||
uint8_t addr[ADDRSIZE];
|
||||
if (!GetHexWordFromCMD(cmdbuf, cmdbuffill, &wordpos, addr, ADDRSIZE, "address"))
|
||||
return;
|
||||
|
||||
Serial.print("DEBUG: Received address ");
|
||||
for (uint8_t i = 0; i < ADDRSIZE; i++)
|
||||
Serialprintf("%02x", addr[i]);
|
||||
Serial.print("\n");
|
||||
|
||||
bool addrvalid = false;
|
||||
for (uint8_t i = 0; i < ADDRSIZE; i++)
|
||||
{
|
||||
if (addr[i] != 0xFF)
|
||||
{
|
||||
addrvalid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!addrvalid)
|
||||
{
|
||||
Serial.println("ERROR: address FFFFFFFFFFFFFFFF is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isadd)
|
||||
{
|
||||
uint8_t secret[SECRETSIZE];
|
||||
if (!GetHexWordFromCMD(cmdbuf, cmdbuffill, &wordpos, secret, SECRETSIZE, "secret"))
|
||||
return;
|
||||
|
||||
Serial.print("DEBUG: Received secret ");
|
||||
for (uint8_t i = 0; i < ADDRSIZE; i++)
|
||||
Serialprintf("%02x", secret[i]);
|
||||
Serial.print("\n");
|
||||
|
||||
AddButton(addr, secret);
|
||||
}
|
||||
else
|
||||
{
|
||||
Serialprintf("DEBUG: removing button\n");
|
||||
RemoveButton(addr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("Unknown command");
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
uint8_t addr[ADDRSIZE];
|
||||
|
||||
for(;;)
|
||||
{
|
||||
if (Serial.available())
|
||||
{
|
||||
uint8_t input = Serial.read();
|
||||
if (input == '\n')
|
||||
{
|
||||
SetLEDState(LEDState_Busy);
|
||||
Serial.println("ready");
|
||||
|
||||
char cmdbuf[CMD_BUFSIZE] = {};
|
||||
uint8_t cmdbuffill;
|
||||
if (ReadCMD(cmdbuf, &cmdbuffill))
|
||||
ParseCMD(cmdbuf, cmdbuffill);
|
||||
}
|
||||
}
|
||||
|
||||
SetLEDState(LEDState_Reading);
|
||||
|
||||
ds.reset_search();
|
||||
if (ds.search(addr) && OneWire::crc8(addr, 7) == addr[7])
|
||||
{
|
||||
SetLEDState(LEDState_Authorized);
|
||||
Serial.print("DEBUG: Found iButton with address: ");
|
||||
for (uint8_t i = 0; i < sizeof(addr); i++)
|
||||
Serialprintf("%02x", addr[i]);
|
||||
Serial.print('\n');
|
||||
|
||||
uint8_t secret[SECRETSIZE];
|
||||
GetButtonSecret(addr, secret);
|
||||
for (uint8_t i = 0; i < SECRETSIZE; i++)
|
||||
|
||||
if (AuthenticateButton(addr, secret))
|
||||
delay(1000);
|
||||
else
|
||||
SetLEDState(LEDState_Busy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue