Build Your Own ESPKey

ESPKeys run at $70+ and are built off of cheap open source technology. We can build our own for far less to accomplish the same things - all the while learning the inner workings of what it takes to sniff these RFID transimissions and replay them.

Let's start by listing what the ESPKey provides:
  • WiFi Access Point
  • Read RFID
  • Replay Credentials
  • Database To Store Creds
  • Website To Display DB
What off the shelf tools can we use to accomplish this? I chose the ESP8266 - a little WiFi-enabled microcontroller with breakout pins and a fair bit of memory. It can be used as an AP, WiFi extender, or other IoT device. This covers our AP, RFID reader, DB, and webserver. It's important to note too that the AP of the ESP8266 might not be as strong as the ESPKey, but I think this is a strength since you don't want the AP freely visible in a wide radius. Next, we'll need some self stripping wire connectors to attach to the RFID panels' power, ground, and two data cables it uses to transfer information to the security console. These are simple to get on Amazon for a few dollars. For the testing platform, I used a NodeMCU board with a built-in ESP chip, which allowed for easy programming and testing.

 One of the first things we'll do when setting this up is to set up our AP. This will allow the board to broadcast a WiFi connection which, after connecting to, will allow us to view the results of what we've sniffed and saved so far. A lot of what we're doing here doesn't need to be applied to only sniffing RFID door tags, we could use this to sniff wireless names or devices on the network with certain open ports. We can expand to that later, but for now we'll stick with sniffing RFID connections. Below, you will see what I am using to setup the AP functionality. We need this so that someone can connect and look at scan results.



Web Server/AP:

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
 
//SSID and Password to your ESP Access Point
const char* ssid = "ESPAP";
const char* password = "temppassword";
IPAddress local_IP(192,168,4,22);
IPAddress gateway(192,168,4,9);
IPAddress subnet(255,255,255,0);
 
ESP8266WebServer server(80); //Server on port 80

// This is run when the webpage is opened
void handleRoot() {
  server.send(200, "text/plain", "hello from esp8266!");
}
 
void setup(void){
  Serial.begin(9600);
  Serial.println("");
  WiFi.softAPConfig(local_IP, gateway, subnet); // Statically set IP/Gateway/Subnet
  WiFi.mode(WIFI_AP);           //Only Access point
  WiFi.softAP(ssid, password);
 
  server.on("/", handleRoot);      //Which routine to handle at root location
 
  server.begin();                  //Start server
  Serial.println("HTTP server started");
}

void loop(void){
  server.handleClient();          //Handle client requests
}
 
This is a basic webserver/AP with a hardcoded IP and it prints out hello world! on a connection. We will need to change this to have a SPIFFs database and display the contents on our webpage. Below is a quick overview of the SPIFFs specific operations we will be using to read and write this data.

  if (spiffsActive) {
    if (SPIFFS.exists(TESTFILE)) {
      File f = SPIFFS.open(TESTFILE, "r");
      if (!f) {
        Serial.print("Unable To Open '");
        Serial.print(TESTFILE);
        Serial.println("' for Reading");
        Serial.println();
  
      f = SPIFFS.open(TESTFILE, "a");
      if (!f) {
        Serial.print("Unable To Open '");
        Serial.print(TESTFILE);
        Serial.println("' for Appending");
        Serial.println();
      } else {
        Serial.print("Appending line to file '");
        Serial.print(TESTFILE);
        Serial.println("'");
        Serial.println();
        f.println("xxx"); // This is where we will append the data read from RFID
        f.close();
      }
  
      f = SPIFFS.open(TESTFILE, "r");
      if (!f) {
        Serial.print("Unable To Open '");
        Serial.print(TESTFILE);
        Serial.println("' for Reading");
        Serial.println();
      } else {
        String contents_of_spiffs;
        Serial.print("Contents of file '");
        Serial.print(TESTFILE);
        Serial.println("' after append");
        Serial.println();
        while (f.position()<f.size())
        {
          contents_of_spiffs=f.readStringUntil('\n');
          contents_of_spiffs.trim();
          Serial.println(contents_of_spiffs);
        } 
        f.close();
      }
   
    } else {
      Serial.print("Unable To Find ");
      Serial.println(TESTFILE);
      Serial.println();
    }
  }
 
Our next step is to work on how are we going to read the RFID data on the wire? The way this makes sense in my head is to think of this device as our RFID reader and not an ESPKey, we would connect it up as if it was controlling the sensor, connect all the wires for the right input. But instead of checking credentials and unlocking a door, we're going to write this information to a database to be displayed at later date. To do the actual reading operations for the RFID information, I'm going to refer to code provided by Jon Davis, who worked on an HID Arduino integration project. The reason I chose HID is that it's an industry standard and common to see in enterprises.
Below is the full code that we've created so far. Note that a lot of the SPIFFs operations came from Steve Quinn, so thank him for his work there. He does a fantastic job of reviewing SPIFFs and what it's used for.

// A good section of the SPIFFs operations were taken from Steve Quinn
// We still need to display the SPIFFs info on the web page

bool    spiffsActive = false;
String  contents_of_spiffs;
#include <string.h>
#include "FS.h"
#define TESTFILE "/testfile.txt"
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
 
//SSID and Password to your ESP Access Point
const char* ssid = "ESPAP";
const char* password = "temppassword";
IPAddress local_IP(192,168,4,22);
IPAddress gateway(192,168,4,9);
IPAddress subnet(255,255,255,0);
 
ESP8266WebServer server(80); //Server on port 80

// This is run when the webpage is opened
void handleRoot() {
  server.send(200, "text/plain", contents_of_spiffs);
}

void setup(void){
  Serial.begin(115200);
  // Start filing subsystem
  if (SPIFFS.begin()) {
      Serial.println("SPIFFS Active");
      Serial.println();
      spiffsActive = true;
  } else {
      Serial.println("Unable to activate SPIFFS");
  }
  delay(2000);
  
  Serial.println("");
  
  WiFi.softAPConfig(local_IP, gateway, subnet); // Statically set IP/Gateway/Subnet
  WiFi.mode(WIFI_AP);           //Only Access point
  WiFi.softAP(ssid, password);
 
  server.on("/", handleRoot);      //Which routine to handle at root location
 
  server.begin();                  //Start server
  Serial.println("HTTP server started");
}

void loop(void){

  // Here is where we will read the RFID signals
  
  if (spiffsActive) {
    if (SPIFFS.exists(TESTFILE)) {
      File f = SPIFFS.open(TESTFILE, "r");
      if (!f) {
        Serial.print("Unable To Open '");
        Serial.print(TESTFILE);
        Serial.println("' for Reading");
        Serial.println();
  
      f = SPIFFS.open(TESTFILE, "a");
      if (!f) {
        Serial.print("Unable To Open '");
        Serial.print(TESTFILE);
        Serial.println("' for Appending");
        Serial.println();
      } else {
        Serial.print("Appending line to file '");
        Serial.print(TESTFILE);
        Serial.println("'");
        Serial.println();
        f.println("xxx"); // This is where we will append the data read from RFID
        f.close();
      }
  
      f = SPIFFS.open(TESTFILE, "r");
      if (!f) {
        Serial.print("Unable To Open '");
        Serial.print(TESTFILE);
        Serial.println("' for Reading");
        Serial.println();
      } else {
        String contents_of_spiffs;
        Serial.print("Contents of file '");
        Serial.print(TESTFILE);
        Serial.println("' after append");
        Serial.println();
        while (f.position()<f.size())
        {
          contents_of_spiffs=f.readStringUntil('\n');
          contents_of_spiffs.trim();
          Serial.println(contents_of_spiffs);
        } 
        f.close();
      }
   
    } else {
      Serial.print("Unable To Find ");
      Serial.println(TESTFILE);
      Serial.println();
    }
  }

  server.handleClient();          //Handle client requests
  
  }
}