Two-Factor Authentication in the real world

The Internet of Things is mostly about bringing Things to The Internet, but it's also about bringing The Internet to Things. Two-Factor Authentication (2FA) has been offered by mission critical services websites like bank accounts or Bitcoin wallets for some time now, providing an additional layer of security beyond just a password. Since resin.io is all about crossing the gap between the internet and the real world we decided to put this concept into action by building a safe-deposit box that requires 2FA to open.

By combining resin.io with Twilio’s Authy, we got to do this with a very simple deployment system —git push resin master— and a very easy way to add the 2FA mechanism.

Safes and lockers in public spaces or hotels are usually opened by a key or by tapping in a code on a numeric keypad. We've come up with an alternative approach that combines a numeric code with an SMS sent via Authy to the user’s phone, which means that opening the safe involves having both the code and the mobile phone.

Adding this extra layer of security may even make us comparable to this masterpiece of engineering:-

Unpickable (Source: xkcd)

Depending on whether you're keeping chocolate cookies or gold bars in your safe you can spend as much on hardware as you feel necessary to mitigate the ultimate security vulnerability—brute physical force. However, for the less Ocean's 11 inclined, 2FA should suffice!

For the demo, we used a Raspberry Pi 2 and a little circuit on a protoboard. The lock itself is a 5V solenoid:-

Solenoid

We connect the solenoid to a simple driver circuit that uses a transistor which we connect to a Raspberry Pi 2 through one of its GPIO pins, so we can use the Pi to turn it on and off at will.

The components are shown below (prior to placing them inside a big, solid box):-

Safe components

Once we've got everything inside our box, it looks like this (please forgive the tape - this was a prototype :) and note how the solenoid is placed such that it blocks the door when closed):-

The box!

The Pi runs a node.js server which performs authentication via a simple web interface. We use Authy to provide the 2nd factor authentication and resin.io to allow ultra-simple code deployment.

This is what the interface looks like for a user:-

Safebox UI

The procedure to lock the safe is as follows:-

  • The user inputs their email.
  • If it’s a new user, they're asked for a phone number.
  • The UI asks for the user’s code to lock the safe.
  • When the code is inputted by the user, the lock is engaged.

Once this is done, to open the safe:-

  • First, the user has to input the correct code.
  • After inputting the code, Authy sends an SMS to the user.
  • The user inputs the SMS code, and the lock opens.
  • The lock only opens for a few seconds, but it can be opened again by pressing the 'Open' button on the UI.

When you use the UI on your phone, you’ll usually be able to input the SMS code when it arrives as it will appear in your phone’s notification area:-

SMS code

And this is what the unique experience of opening our safe looks like:-

The hardware

So you want to build your own hacker-proof box? Here’s what you’ll need:-

  • A 5V solenoid like this one.
  • A rather hard-to-break box - be careful to choose one with a large enough opening for the Pi’s power cable as well as sufficient space so that the solenoid can be attached such that it blocks the door when it's closed.
  • A small protoboard.
  • Resistors - Refer to the schematic below. We used three 10 ohm resistors in parallel to obtain 3.3 ohms resistance (which we recommend to have a greater max current) and two 470 ohm resistors to obtain 235 ohms for R2 (though here a single 220 ohms would work).
  • A diode and a transistor (we used a 1N4148 diode and a 2N2222 transistor).
  • Assorted protoboard wires (male-male and female-male).

The circuit schematic for the solenoid driver looks like this:-

Schematic

(You can also find it on Upverter)

And this is how we assembled the solenoid driver on the protoboard:-

Circuit on protoboard

The software

As discussed previously, the Raspberry Pi runs a node.js server which implements a multi-step authentication mechanism, in order to achieve this we designed a state-machine using machina.js served up over HTTP via express.js. Most of the interaction operates over socket.io, to provide real time feedback to the user.

We use the “authy” npm package to easily interact with the Authy API, borrowing some code from their tutorial.

State machines consist of states and events which generate transitions from one state to another - the model we use in our application, 'Safebox', has 'open' and 'closed' states with transitions going from one to the other in both directions, passing through intermediate states which handle the auth process.

Whenever a user interacts with the UI, input events are fired via socket.io which are passed on to the state machine.
The code below is an example of how these states and transitions are defined:-

var safebox = new machina.Fsm({  
    /* ...  other properties here */
    states: {
        /* ...  other states here and below */
        closed: {
            _onEnter: function(){
                this.lock.enabled(false);
                this.lock.close();
                this.persistedState.currentState = 'closed';
                this.persistedState.save();
                this.emitStatus();
            },
            input: function(data){
                this.currentUser.comparePassword(data.code, function(err, match){
                    if(match){
                        safebox.transition('authenticating');
                    } else {
                        io.emit('notice', 'That\'s not your code!');
                        safebox.transition('closed');
                    }       
                });
            }
        },
        /* ... */
    }
});

When the machine transitions to the 'closed' state its _onEnter function is called disabling the lock and saving this new state. When the user subsequently inputs a code we compare it against their password only transitioning to the next state if there’s a match.

We used MongoDB to persist the box’s current state and user data - storing user data allows us to store user's passcodes and phone numbers so they don't need to reconfigure their device each time the device is restarted.

The code integrating the user model also interacts with the Authy API which provides methods for sending an SMS to the user and verifying the code that they provide.

On the client side, we have a simple single-page jQuery app that shows different HTML content for each of the state-machine’s states, listens to events on the dialpad and inputs, sends socket.io messages and provides the user with the appropiate feedback. We used Bootstrap and toastr to rapidly design an interface that is reasonably pleasing to the eye :)

We use resin.io to tie everything together with a Dockerfile that sets up the environment and runs our start script:-

FROM resin/rpi-raspbian:jessie  
RUN apt-get update  
RUN apt-get install -y curl  
RUN curl -sL https://deb.nodesource.com/setup | bash -  
RUN apt-get install -y build-essential nodejs mongodb  
COPY . .  
RUN mkdir -p /datadb  
RUN npm install  
EXPOSE 8080  
CMD bash start.sh  

Our start.sh script then starts MongoDB (repairing it in case of an unclean shutdown) and our web server:-

rm /datadb/mongod.lock  
/usr/bin/mongod --dbpath /datadb --repair
/usr/bin/mongod --dbpath /datadb --fork --logpath mongod.log && node index.js

Using resin.io makes our deployment as simple as typing 'git push' and the fact that it allows us to use docker ensures that all our dependencies are met in the precisely the same way for every device. It couldn't be simpler :)

You can find all the source code for the project in our GitHub repository!

We hope this demo shows the power of bringing two-factor authentication to devices, and we're sure there are many other applications where adding it might come in handy. And, of course, we hope it helps you keep your cookies very, very safe!

Any questions? or you'd just like to say hi, come find us on our community chat.

comments powered by Disqus
Terms of Service | Privacy Statement | Master agreement | Copyright 2019 Balena | All Rights Reserved