Sunteți pe pagina 1din 102

December 11th, 2012

Table of Contents
Abstract...................................................................................................................... 1
Introduction................................................................................................................ 1
Discussion of the Project............................................................................................ 2
Design Specification................................................................................................ 2
Design Procedure.................................................................................................... 2
Use Cases................................................................................................................ 3
System Description................................................................................................. 5
System Block Diagram......................................................................................... 5
Android Smartphone............................................................................................ 5
Bluetooth Adapter................................................................................................ 6
Bluetooth Module................................................................................................. 6
Lock/Authentication Management........................................................................6
Lock Motor/Actuator............................................................................................. 7
RFID Scanner........................................................................................................ 8
Command and Protocols.......................................................................................... 8
Generic Command............................................................................................... 8
Begin Command................................................................................................... 8
BEGINACK Command........................................................................................... 8
Unlock Command................................................................................................. 9
Lock Command.................................................................................................... 9
Status Command.................................................................................................. 9
Add Member Command........................................................................................ 9
Remove Member Command...............................................................................10
Security................................................................................................................. 10
Message Authentication Activity Diagram.............................................................11
Specification of External Environment...................................................................12
System Input and Output Specification.................................................................13
System Inputs....................................................................................................... 13
Smartphone/Bluetooth Adapter..........................................................................13
Bluetooth Adapter/Bluetooth Module.................................................................13
Bluetooth Module/Lock Manager........................................................................13
1

RFID/Lock Manager............................................................................................ 13
System Outputs..................................................................................................... 14
Lock Manager/Lock Servo................................................................................... 14
Lock Manager/Bluetooth Module........................................................................14
Bluetooth Module/Bluetooth Adapter.................................................................14
Smartphone/Bluetooth Adapter..........................................................................14
Software Implementation...................................................................................... 14
Android Application............................................................................................ 14
Generate/Construct Keys................................................................................... 15
Construct Key Objects........................................................................................ 15
Read/Write Keys to Memory...............................................................................15
Authorize User.................................................................................................... 15
Read/Write Username to Memory.......................................................................15
Get Username from User....................................................................................15
Compare Usernames.......................................................................................... 15
Lock/Unlock System........................................................................................... 15
Discover/Connect to Lock................................................................................... 15
Exchange Message............................................................................................. 15
Construct and Sign/Encrypt Message.................................................................16
Send Message.................................................................................................... 16
Exchange Public Key and Username..................................................................16
Discover/Connect to User Phone........................................................................16
Exchange Message............................................................................................. 16
Construct Message............................................................................................. 16
Send Message.................................................................................................... 16
Get Modulus and Exponent of Public Key...........................................................16
Add/Remove User from System..........................................................................16
Get Users Username and Public Key.................................................................16
Construct and Sign/Encrypt Message.................................................................16
Send Message.................................................................................................... 16
Activity Diagram................................................................................................. 17
Lock Control Software Implementation..............................................................20
Hardware Implementation..................................................................................... 25
2

Hardware Schematic.......................................................................................... 25
RFID Reader....................................................................................................... 25
Bluetooth Module............................................................................................... 25
Servo.................................................................................................................. 26
TI Stellaris Development Board..........................................................................26
Random Number Generator............................................................................... 26
System Testing......................................................................................................... 26
Test Plan................................................................................................................ 26
Test Specifications................................................................................................. 27
Test Cases............................................................................................................. 28
Results...................................................................................................................... 29
Presentation of Results.......................................................................................... 29
Analysis of Errors................................................................................................... 30
Bill of Materials......................................................................................................... 31
Summary.................................................................................................................. 31
Conclusion................................................................................................................ 32
Appendix.................................................................................................................. 33
Appendix A Android Application Code...................................................................33
Appendix A1 (DeviceListActivity.java)................................................................33
Appendix A2 (BluetoothChatService.java)..........................................................36
Appendix A3 (BluetoothChat.java).....................................................................43
Appendix B Stellaris Board Code...........................................................................55
Appendix B1 (main.c)......................................................................................... 55
Appendix B2 (parser.c)....................................................................................... 63
Appendix B3 (pwm.c)......................................................................................... 77
Appendix B4 (rfid_reader.c)................................................................................79
Appendix B5 (userdb.c)...................................................................................... 80
Appendix B6 (utils.c).......................................................................................... 84
Appendix B7 (sbrk.c).......................................................................................... 89
Appendix B8 (startup_gcc.c)..............................................................................89
Appendix B9 (global.h)....................................................................................... 92
Appendix B10 (parser.h)..................................................................................... 93
Appendix B11 (userdb.h)....................................................................................93
3

Appendix B12 (pwm.h)....................................................................................... 95


Appendix B13 (rfid_reader.h).............................................................................. 95
Appendix B14 (utils.h)........................................................................................ 95

Abstract
In todays technology driven world an opportunity to increase security and
convenience for residents of an office or apartment building has become available
for development. Our new Safe Guard Lock System will allow a resident to use their
smartphone as a key to their apartment or office building. The client will need a
system that can provide secure access to any deadbolt lock from the convenience
of their smartphone. This need will be fulfilled by the use of a Stellaris
microcontroller, servo motor, Android smartphone, and RFID Card reader. The
system will had two inputs; the Android Smartphone and RFID reader. The system
had one output to the servo motor to control the physical locking of the door. The
system design used the Stellaris microcontroller as the brain of the system. The
Android Smartphone talked to a Bluetooth module witch intern talked to the
microcontroller via UART. The RFID talked to the microcontroller via a second UART
channel. The Servo was controlled via a pulse width modulation from the
microcontroller. Testing of the system included several test cases. These test cases
included locking and unlocking of the door by the Android device and the RFID card.
Also several bad RFID cards and Android devices were used to ensure that the door
could only be operated by authorized users. The system produced mostly positive
results, and a mostly fully functional product was created given the allotted time.
Some errors did occur and were not able to be solved in time for the final demo.

Introduction
Everyone needs to a means to secure and control access to their homes,
workplaces, cars, and information. Each of us deals with a wide variety of
mechanisms for accomplishing this, which requires us to memorize many passwords
and carry an awkward collection of mechanical and electronic keys. Although
existing electronic lock systems are more convenient for the user and
administrators than mechanical locks, each organization uses a different system,
which prevents any simplification. The smart phone is becoming more ubiquitous in
our society every day. This presents the opportunity to consolidate multiple access
mechanisms into a single device, which most people already carry.
For our final project we propose to design and prototype a system that can
wirelessly unlock a door with a smartphone. The smartphone will transmit the
users identity and credentials to the lock control system (LCS) and request access.
Modern cryptographic techniques will be used to secure communications between
the LCS and smartphone, and a public-private key infrastructure will be used for
authenticating users. If the LCS can authenticate the user and the he or she is on
the list of authorized persons, then the door will be unlocked.
The project will begin by defining a standard for how the system will operate at the
application level. This standard will include the application-layer communication
1

protocol, the method of identifying and authenticating users, and the method of
administering access-control devices (e.g. a lock). We believe our project may be
the basis for a marketable product in the future, and creating such a standard will
allow the system to be implemented with a wide variety of hardware and
communication technologiesan important prerequisite to wide-spread adoption.
We will then implement the user and administrative aspects of the standard in a
simple Android app. The LCS will be implemented in a custom microcontrollerbased circuit. The LCS will be battery powered and will directly control and monitor
the actuator which turns the lock. Communication between the smartphone and
LCS will be via either the Bluetooth or the Near Field Communications protocol. An
actuator will be designed and built to manipulate a common, mechanically-keyed
deadbolt lock. This will allow the system to be temporarily installed and thoroughly
tested on an apartment door without violating restrictions imposed by the lease and
while still allowing access via the mechanical key.

Discussion of the Project


Design Specification
Any prospective client looking to upgrade their security should consider the Safe
Guard Lock System. The system provides a very convenient way to access any door
that needs to be secure. The system also offers a high level of security for the
amount of convenience offered to the client. By effectively making the clients
smartphone their new key the Safe Guard Lock System will simplify their life by
removing one more physical key that they must carry. This physical key could be in
the form of the traditional metal key lock, or a security badge. The system uses the
smartphones existing Bluetooth to connect to the lock. The security of this lock
goes beyond the simple pair key of most Bluetooth devices. After pairing of the
phone the system communicates with a high end security encryption and
decryption algorithm.

As long as a client has a verified authentication key they will be able to access any
door with the Safe Guard Lock System from the ease of their smartphone. There is
also an Administrator to the system that will be able to add and remove users from
the system. The system will come with a default admin, and can be changed to a
custom admin in order for higher security. The system also has a backup physical
RFID reader. Only a single RFID card will be allowed with each system. The RFID is
meant for back up purposes only.

Design Procedure
The design procedure began from the top down. The Safe Guard Lock System was
imagined to be a convenient and secure way to lock and unlock any door with a
2

deadbolt. The idea behind the system was to have a fully incorporated module that
can be fitted over a deadbolt. From there the users smartphone would be able to
lock and unlock the door through the use of an app on their device. The design of
the system began with several choices. These choices included: Bluetooth as the
communications medium between the smartphone and microcontroller, RSA with
SHA-1 hashing as the security encryption/decryption protocol, and UART
communication between the Bluetooth module/RFID module and the
microcontroller.

In the Safe Guard Lock System the main brain was the Stellaris ARM M3 based
microcontroller. This microcontroller was integrated onto an evaluation board that
provided the UART communication channels and pulse width modulation output that
was required for this project. An Android Smartphone with firmware version 2.3.5
was used to build the locking application. The deadbolt lock was controlled using a
pulse width modulation output to a servo motor. The RFID functionality was
provided by an ID-12 RFID reader. The ID-12 reader was connected to a Sparkfun
RFID USB Reader (SEN-09963). This board allowed the ID-12 reader to dump out its
data via mini USB or UART. Finally the JY-MCU BT_BOARD was used for the Bluetooth
communication module on the microcontroller side.

After the selection of all parts of the system they had to be integrated together. This
was accomplished by connecting JY-MCU Bluetooth module to the microcontroller via
UART, the RFID reader to the microcontroller via UART, and the servo was controlled
with the pulse width modulation output from the microcontroller. The Android
smartphone then communicated with the microcontroller through the JY-MCU
Bluetooth module.

Use Cases

Lock/Unlock DoorAn authorized individual opens the smartphone app and


commands the door to be locked/unlocked. The LCON authenticates the user
and locks/unlocks the door. A status message, indicating success or failure is
shown to the user.
Check StatusAn authorized individual opens the smartphone app and
requests lock status. The LCON authenticates the user and reports the lock
status.

Add AIAn Administrator opens the smartphone app and sends the identity
and public key of a new authorized individual to the LCON. The LCON
authenticates the administrator and adds the new AI to its internal database.
A status message, indicating success or failure is shown to the administrator.
Delete AIAn Administrator opens the smartphone app and sends the
identity of the authorized individual to be deleted to the LCON. The LCON
authenticates the administrator and deletes the AI from its internal database.
A status message, indicating success or failure is shown to the administrator.
List AIAn Administrator opens the smartphone app and requests the list of
AIs from the LCON. The LCON authenticates the administrator and sends the
list of AIs from its internal database back to the smartphone.

System Description
System Block Diagram

Figure 1: Overall System Block Diagram

Android Smartphone
Inputs:
User Interface Inputs (i.e. button press, text input)
File Reads to Internal File System of Phone for keys, username, and password
Outputs:
Lock Commands via Bluetooth (reference Commands and Protocols Section)
User Interface Updates (i.e. button states, text updates to user)
File Writes to Internal File System of Phone for keys, username, and password

The android smartphone will act as the user interface for this system. The user will
be first authorized through the smartphone so that the owners keys will not be put
in jeopardy. Once authentication has been established the smartphone
communicates with the Bluetooth adapter and starts the discovery process looking
for the advertisement of the lock. Once the locks advertisement is discovered the
pairing and connection process is initiated. After connection has been established
the smartphone creates a message requesting the lock be opened, which includes
the lock command itself, username, random session token and signature generated
5

from SHA-1 and RSA encryption. These command are also specified in more detail in
the commands and protocols section. Similar messages are constructed to unlock
the door, add and remove users, as well as transfer public keys. The UI is updated
to the user as the lock toggles between the lock and unlock states.
Security Algorithm:
Before sending a command to the lock management system via Bluetooth, a
signature is included in the message using SHA-1 and RSA encryption, which is
provided by the Android framework. A random number, used as the session token,
which is randomly generated by the lock management system at connection is used
in the signature to stop someone from receiving and replaying the message to the
system.
Bluetooth Adapter
Inputs:
Status Updates from the lock manager received via BT
Outputs:
Commands from Android application to be sent via BT to lock management
system

The Bluetooth adapter is the onboard smartphone adapter, which can be accessed
through the Android framework. It will be used for the discovery, pairing, connection
and transaction process between the smartphone and the lock manger.
Bluetooth Module
Inputs:
Commands from the Android application received via BT
Outputs:
Status updates from the lock management system sent to Android application
via BT

The Bluetooth module is a serial port module that communicates to the lock
manager over a UART/RS-232 communication protocol. This module manages the
pairing and connection process between itself and the Bluetooth adapter on the
Android smartphone.
Lock/Authentication Management
Inputs:
6

32-bit binary signal sent from the RFID scanner module via UART when card is
scanned
Commands from the Android application sent via BT
Outputs:
Status updates sent to the Android application via BT
Pulse width modulator output driving servo

The Lock/Authentication manager manages all of the authentication process of the


user and the control of the locking mechanism. Once a user discovers and connects
to the module, the lock manager sends a randomly generated session token to the
phone for use in the encryptions, and then waits until a message is received
requesting what action to take. Once the message is received it should contain two
parts the textual message and the encrypted message. The users name is obtained
from the textual message and the public key corresponding to that user is obtained
from the lookup table of authorized users. The encrypted signature is decrypted
using the public key and the textual message is hashed, if both messages are equal
after this process then the user is authorized for entrance and the lock is signaled to
open. The message can also ask to add a new user to the lookup table, which
requires that the authorized users signature goes through this authentication
process and the new users public key is then added to the lookup table. Based on a
timer the door will automatically relock after the door has be unlocked for a set
amount of time.
Authentication can also be established through the RFID scanner where the user
can put their card up against the door and upon verifying the 32-bit binary number
will open the door for the authorized card. Once the user has been authorized the
lock is signaled to unlock, and will automatically relock after a wait timer expires.

Security Algorithm:
Upon receiving a command from the Android application the lock
management system parses the message for verification. First the signature is
decrypted using the public key stored in the system for the user. The textual
message signed by the user is then hashed by SHA-1 and compared to the
decrypted signature. If the two results are equal then the authorized user sent the
message and the command is processed and executed.
Lock Motor/Actuator
Inputs:
7

Pulse width modulator output from lock management system


Outputs:
None

The lock motor/actuator will be a servo motor that can be controlled by a specified
pulse width modulator output of the lock manager. Once the lock manager has
authorized a user, the appropriate pulse width is supplied to the motor, which turns
until a feedback signals that its reached to the full locked position. A similar sensor
is used to signal that the lock has reached the full unlock position.
RFID Scanner
Inputs:
None
Outputs:
32-bit binary signal corresponding to the ID of the scanned RFID card

The RFID scanner is the back authentication system for users without a smartphone.
The scanner itself is mounted on the door and when the user scans the card and the
32-bit binary signature is authorized with the lock management system, then the
lock is opened if authentication is successful.

Command and Protocols


All of the primary communication is done over Bluetooth and interfaced through a
UART interface. The commands and protocols are as follows:
Generic Command
<Cmd:Parameters(separated by :s)>

All commands will follow this generic interface where the entire command is
surrounded by < and >, and each element inside separated by :. The first
element will be the type of command and each of the following elements will be the
parameters passed in for that command.
Begin Command
<BEGIN>

Indicates that a smartphone wants to communicate with the LCON. This must
proceed every other command sent to the LCON.
BEGINACK Command
<BEGINACK:Status:RN>

The LCON acknowledges the BEGIN command and is ready to receive a command
other than <BEGIN>.
RN - Random number generated by the LCON, which should be included in the
future commands.
Status - Current state of the lock system which will be either LOCKED or UNLOCKED.
Unlock Command
<UNLOCK:TextUserName:RN:EncryptedHash>

User requests that the system unlocks.


TextUserName The user name of the person trying to gain access in ASCII format.
RN The random number previously received from the Lock Control Device.
EncryptedHash The SHA-1 hash of the previous command text (excluding the <
and the trailing :, but including internal :s) is computed, then encrypted using RSA
and the users private key.
Lock Command
<LOCK:TextUserName:RN:EncryptedHash >

User requests that the system locks.


TextUserName, RN & EncrypetdHash See Unlock Command.
Status Command
<STATUS:TextMessage>

TextMessage Message carrying and ASCII format update of the state of the
system. Either LOCKED or UNLOCKED.
Add Member Command
<ADD:TextAdminName:TextUserName:NewKey:Perms:RN:EncryptedHash >
9

Administrator requests an addition to the table of authorized users.


TextAdminName The user name of the admin in control of managing keys in the
system in ASCII format.
NewUserName The user name of the new authorized individual.
NewKey Public key of the new member to add to the system of authorized people.
NewPerms Permissions assigned to this user as an 8-bit field: bit 0 (LSB) =
lock/unlock permission, bit 1 = admin rights.
RN & EncrypetdHash See Unlock Command.
Remove Member Command
<REMOVE:TextAdminName:OldUserName:OldKey:RN:EncryptedHash>

Administrator requests a removal of a user from the table of authorized users.


TextAdminName The user name of the admin in control of managing keys in the
system in ASCII format.
OldName The user name to remove
OldKey Public key of the old member to remove from the system of authorized
people
RN & EncrypetdHash See Unlock Command.

Security
The security of the system depends on being able to distinguish command
messages sent by authorized users from those sent by an unauthorized person (an
attacker). To provide a high degree of security, a public/private key infrastructure is
established and digital signatures are used to ensure the integrity and authenticity
of all command messages. The basic process of creating and verifying a digital
signature is illustrated in Figure 2 below.

10

Figure 2: Digital signatures (photo by Microsoft)

After the command message is created by the Android app, a message digest is
calculated with the one-way hash function SHA1. The users private key is then
used to encrypt the message digest using the RSA encryption algorithm. This
encrypted message digest is appended to the original message and the whole thing
is transmitted to the lock control system. The lock control system parses the
message to separate the signature and the original message. It then uses the same
SHA1 hash function to calculate the message digest and uses the users public key
to decrypt the digest contained in the signature. If the two digests match, then the
signature is verified and the system knows that it really came from the claimed user
and that it has not been altered en route.
One weakness of this system is it does not distinguish when a message was sent. A
valid message and signature are always valid and could be captured by an attacker
and replayed in the future. To eliminate this risk, a random session token is sent
from the lock control system to the Android app. This session token must be
included in the future command message and may be used only once. The phone
must request a new random token before additional command messages are sent.
With this system, an attacker cannot successfully replay an old message because
the session token would no longer be valid. The attacker could not update the
session token in the old message, because that would invalidate the signature. The
attacker cannot successfully calculate a new signature, because they do not have
access to the private key.
11

Message Authentication Activity Diagram


The following activity diagram describes the process of authenticating a message
from a user.

Figure 3: Message Authentication Activity Diagram

12

Specification of External Environment


The Smartphone Controlled Security Door System will be operating on the internal
and external doors of office buildings, warehouses, and residential apartment
buildings. The temperature range will be as extreme as the environment where the
building is located. For instance if the building is located in Dubai the temperatures
will be hot and dry. If the building is located in Alaska then the environment will be
cold and wet. Smartphone Controlled Security Door Systems external components
must be weather proof. The Smartphone Controlled Security Door System will be
running of the building power, so there is no need to worry about battery life.

System Input and Output Specification


The Smartphone Controlled Security Door System controls the locking and unlocking
of any door it is installed in. The system has two inputs and one output that
depends on the inputs. The inputs and outputs are described below.

System Inputs
The Smartphone Controlled Security Door System shall support the following kinds
of input signals:
Smartphone/Bluetooth Adapter
The smartphone application will support version 2.3 Android phones that have an
onboard Bluetooth adaptor. Any inputs at this point will be the user pushing buttons
on the phone, or buttons on the LCD display of the phone.
Bluetooth Adapter/Bluetooth Module
After the user has configured the smartphone the next set of inputs will be the
Bluetooth adapter on the phone providing an input to the Bluetooth module in the
Smartphone Controlled Security Door System. The Bluetooth adapter must
communicate with the following standards:

Operation in the range 2,4002,483.5 MHz


Max 100 mW power consumption
Min data transfer rate of 1 Mbit/s
In input mode the smartphone will act as master and the system will act as
slave

Bluetooth Module/Lock Manager


The Bluetooth module of the system will act as an input to the Lock Manager. The
following are the specifications for this communication.

Communication will occur via UART


115,200 Baud Rate
0-5 Volt

13

RFID/Lock Manager
The RFID reader will act as an input only to the Lock Manager. The following are the
specifications for this communication:

Communication will occur via UART


9,600 Baud Rate
0-5 Volt

System Outputs
The Smartphone Controlled Security Door System shall support the following kinds
of output signals:
Lock Manager/Lock Servo
The lock manager will control the operation of the physical servo on the lock.

5 volt operation
Pulse Width Modulation controls position of servo
Period between pulses is 20 ms
Pulse width varies from 0.6 ms to 2.3 ms for 180 degree rotation

Lock Manager/Bluetooth Module


The Lock Manager will output to the Bluetooth module. The following are the
specifications for this communication.

Communication will occur via UART


115,200 Baud Rate
0-5 Volt

Bluetooth Module/Bluetooth Adapter


When the Bluetooth module needs to talk to the users smartphone it will output to
the Bluetooth adapter. The Bluetooth Module must communicate with the following
standards:

Operation in the range 2,4002,483.5 MHz


Max 100 mW power consumption
Min data transfer rate of 1 Mbit/s
In input mode the smartphone will act as master and the system will act as
slave

Smartphone/Bluetooth Adapter
After the Bluetooth adapter has received a communication from the Bluetooth
module of the system the smartphone will display the correct output letting the user
know what action has occurred.

14

Software Implementation
Android Application
The android application is the interface into the lock system for the user where they
are able to lock/unlock, add/remove user and exchange keys between phones. In
the basic user interaction the process should be that the user opens the application,
signs in with their username and presses the lock button, which should in turn open
the lock of the lock system. In the case where the administrator is adding or
deleting a user the user who wishes to be added to the system connects to the
phone of the administrator. Once connected the user sends their username and
public key to the admin. Then the phones would disconnect and the admin would
connect to the lock management system. Then by pressing the add or remove user
button the lock management system would add or remove the specified user from
the table of authorized users. To allow this functionality the tasks were broken up as
shown below:
Generate/Construct Keys
If its the first time the application is being used then a random public and private
key must be generated for the user and stored in memory for later uses of the
application. Otherwise there should already be a key stored on memory so read in
the value and construct Key objects.
Construct Key Objects
Use the modulus and exponent of the public and private keys to construct PublicKey
and PrivateKey java objects for use in the Security Framework.
Read/Write Keys to Memory
If not previously stored, write the modulus and exponent of the public and private
keys to application file. Otherwise, read the previously stored public and private key
modulus and exponent values and pass to construct key functions.
Authorize User
Verify that the user of the application is the owner of the phone by querying his
username, which can be compared to a stored username in memory.
Read/Write Username to Memory
If its the first time the application is being used then the specified username is
written to memory for future authentication. Otherwise read the previously stored
username from memory.
Get Username from User
Query the user to provide their username by using a pop up dialog and wait for a
submit action.

15

Compare Usernames
Compare the username stored in memory to the one provided by the user. Specify
authentication success if the two are equal otherwise its a failure.
Lock/Unlock System
This process requires that the user discover and connect to the lock over Bluetooth
using the menu navigation. Once connected a session is started given a randomly
generated session token and status of current state of the lock so the UI can update
the user. User sends the lock and unlock commands as result of pressing the toggle
button, which causes a command to be constructed with user information, session
token and users signature.
Discover/Connect to Lock
Request the Bluetooth adapter to discover new devices and present results to user.
When user specifies a device get the mac address and query the device for a
connection.
Exchange Message
Send the specified command to the lock system including the users information
and signature.
Construct and Sign/Encrypt Message
Construct the textual message specified by the Command and Protocols section
which includes the session token. Use SHA-1 hashing and RSA signature
specifications and users private key to produce a user signature to send with the
message.
Send Message
Write the message to the Bluetooth adapter so that it can be pushed over the
connection.
Exchange Public Key and Username
User discovers the Bluetooth advertisement of the Administrators phone, then
connects and sends the users username and public key by command.
Discover/Connect to User Phone
Request the Bluetooth adapter to discover new devices and present results to user.
When user specifies the admins device get the mac address and query the device
for a connection.
Exchange Message
Send the specified command to the administrators phone including the users
information and public key.
Construct Message
Construct a textual message following the specifications in the Commands and
Protocols Section with the users username and public key.
16

Send Message
Write the message to the Bluetooth adapter so that it can be pushed over the
connection.
Get Modulus and Exponent of Public Key
Obtain the modulus and exponent of the public key through manipulation of the
java objects and their characteristics.
Add/Remove User from System
Send a command to the lock management system with a username and public key,
which the specified action to add or remove from the database of authorized user.
Note: This is only sent from an administrative specified phone.
Get Users Username and Public Key
Check to see if a username and public key is cached in the phone from an exchange
action.
Construct and Sign/Encrypt Message
Construct a command based on the Command and Protocols Section for adding and
removing users with the specified action, username and public key. Include a
signature constructed using SHA-1 and RSA signature specifications and the private
key of the user.
Send Message
Write the message to the Bluetooth adapter so that it can be pushed over the
connection.
Activity Diagram
The basic control flow of the Android application is straightforward as to allow for a
simple interface for the user. The functionality of the application is partitioned so
that each task is separate from one another. A representation of the control flow of
the application can be viewed in the Activity Diagram in Figure 4 below:

17

18

Figure 4: System Activity Diagram

This activity diagram can be thought of separating the application into each of the
main tasks that it executes. The first is above the first fork and join and is the
initialization of the application, which has to authorize the user of the application
and acquire public and private keys for that user. After initialization is finished the
user either uses the phone for administrative or basic user purposes. On the far
right path of the first fork, the user connects to administrative phone to pass them
their username and public key so that its cached on the admins phone which can
be used for add and remove user commands. The path to the left of the first fork is
a process requiring a connection to the lock system. Once connected to the lock
system, the second fork is to the left for opening and closing the lock for either
basic or administrative user. The right path of the second fork allows an
administrator to add or remove an authorized user from the lock management
system. Once either path disconnects, it once again has the ability to start a new
task by following any of the paths at the first fork until the application is close.

The code that corresponds to the implementation for the Android application can be
found in the appendix. The over control flow of the user interface follows the
general model of Android applications specified by the Google framework. The user
interface that allows for each of the tasks shown in the activity diagram is shown in
Figure 5 below:

19

Figure 5: Smartphone User Interface

If its the first time the user has used the application then they specify the
username that will be used to access the application and sent with each command
to the lock system. The submit button finalizes the chose and writes the username
to memory. The next time the application is used, if the username is stored in
memory the text box and submit button will be removed from the user interface.
Once authentication of the user is finished they connect to either the lock system or
the administrators phone by using the phones menu button, which shows a list of all
available devices. If connection is made with another phone then the only enabled
button is the Send Public Key to Connected Device which allows for a key
exchange between a basic and admin user. If the connection is made with the lock
system then the Lock, Ad Key, Remove Key buttons and Admin? checkbox
are enabled allowing the user to lock/unlock the door, or add/remove users from the
authorized users table if its an administrator. So the user interface adapters for the
user depending on the state of the program, which allows the user to better assess
the functionality of the application.
Lock Control Software Implementation
We chose to create a bare-metal (sans operating system) software application for
authenticating users and controlling the servo. We utilized the library of code
20

distributed by Texas Instruments wherever it was practical to do so. We also utilized


the open-source libraries libtomcrypt and libtommath to provide cryptographic and
multiple precision math functionality. The implementation is largely interrupt
driven and executes various tasks in a simple round-robin fashion. A timer produces
interrupts at 1 ms intervals and is used for basic timekeeping. The system
functionality can be decomposed into the blocks shown in Figure 6.

Lock Control

Command Parsing

RFID Monitoring

User Database
Management

User Authentication

Message
Hashing

Signature
Verification

Random
Number
Generator

PWM Generation

User Lookup

Add User

Remove
User

Figure 6: Functional decomposition of the lock control system.

The top level functional block is responsible for initializing the system and required
peripherals and providing the basic framework for the other functional blocks to
operate. It integrates information provided by the command parser and RFID
blocks, and subsequently controls the PWM generator (to actuate the lock servo).
It is mostly implemented in the file main.c. After initialization, it enters an infinite
loop, where each subsidiary functional block is either directly implemented or
handled by a function call. At the conclusion of each pass through the loop, the
processor is put into a low-power sleep mode until it is woken by an interrupt. The
source of the interrupt may be UART activity, or the periodic timer.
The Command Parsing block comprises the largest body of code in the project. It is
responsible for parsing commands received via Bluetooth, authenticating the
sender, and taking the appropriate action in response to the command. Some parts
of the implementation of this block reside in main.c, but most is in parser.c.
Whenever data is received by the Bluetooth transceiver, it is sent to UART1, which
causes an interrupt. The UART1 ISR moves the data to a ring buffer (RxBuff) and
21

sets a flag to indicate new data has been received. Each time through the main
loop, this flag is checked and if set, command parsing is initiated by calling the
Parse() function.
The Parse() function returns different values which fall into 3 broad categories:
values indicating a valid command has been received and some action should be
taken, values indicating an invalid or unauthorized command has been received, or
a value indicating the parser must wait for additional bytes to be received. The last
possibility was necessary because of our chosen command protocol. Parsing the
commands would have been much simpler if we could buffer an entire command
and process it all at once. However, the combination of variable command length,
combined with the use of binary data transmission prevented us from knowing
when the entire command had been received, without actually parsing at least part
of it. (Binary data transmission means we were transmitting randomly distributed
bytes, which could not be interpreted as ASCII characters. This precluded using
particular characters to signal the end of the command, because any of the 256
possibilities could appear in these binary data sections.)
Whenever an invalid command is received, an error message indicating this fact is
transmitted over the Bluetooth connection. However, because we parsed each byte
as it was received, we might identify an invalid command before the sender was
finished sending it. If the parser logic was immediately reset, it would regard the
following bytes as a new command--also invalid--and would cause even more error
messages. This is prevented by the use of a 1 s waiting period following the receipt
of any invalid command, during which all command parsing is disabled and all
received data is discarded. The waiting period begins after the last byte is received,
and if a new byte is received during the waiting period, it is extended to 1 s from
that point.
The User Authentication block is responsible for preventing unauthorized individuals
from controlling the lock and is primarily implemented in parser.c. The
authentication process starts with the receipt of an unauthenticated <BEGIN>
command. The Lock Control system responds by supplying a random session token,
which must be included in future authenticated commands. (This prevents valid,
signed command messages from being replayed by an attacker in the future.) After
commands are received the user must be authenticated by hashing the body of the
command and verifying the signature with the users public key.
The Random Number Generator is a combination of software and special hardware.
One GPIO pin, PC5, is configured as an output, and periodic timer interrupts are
used to generate a square wave output on PC5. A second GPIO pin, PC7, is
configured as an input and the two pins are bridged by the RC network shown in
Figure 7. The PC7 pin is configured to generate an interrupt whenever it detects a
changed state. The exact time required for the input (PC7) to register a changed
state after the output (PC5) has changed varies randomly about a mean or
22

expected value. A second periodic timer runs as fast as possible and its value is
read whenever PC7 generates an interrupt. The lower 8 bits of the timers value,
read at these somewhat random intervals, are random and uniformly distributed.
This system produces 50 bytes of random data per second. 64 bytes of random
data are required in order to respond to the <BEGIN> command, so random data is
buffered in advance into a ring buffer (EntropyRB) so it is immediately available
when needed.
The Message Hashing block is primarily implemented in libtomcrypt. The library
functions sha1_init(), sha1_process(), and sha1_done() are called during the parsing
process to facilitate the hash calculation. Likewise, the Signature Verification block
is primarily implemented by the same library, as well as libtommath. The latter
library is necessary for converting a byte array into the very large integer
representing the public modulus, which is used by the RSA algorithm to verify the
users digital signature. After the body and signature of a command have been
parsed, the function rsa_verify_hash_ex() is called to actually verify the users digital
signature.
The User Database Management block is responsible for adding and removing user
records from the database and retrieving the stored information when needed. The
major components of this block are implemented in userdb.c. The user information
is stored in the non-volatile flash memory available on the Stellaris microcontroller.
The linker script main.ld was modified to prevent executable code from being
programmed into the last 64 kB of flash, so this could be used for database storage.
The flash memory must be erased (which sets all bits to 1) before it can be
programmed (which can only cause a bit to be set to 0). The flash can only be
erased in 1024 byte blocks, so it was convenient to allocate one user record to each
1024 byte erase block. When a user record is stored, the target flash location is
first erased with FlashErase(), then programmed with FlashProgram(), both
functions from the LM3S8962 library.
Besides the actual user data, metadata must be also be stored to indicate which
locations in flash hold valid user records, and where new records should be placed
(to facilitate wear leveling). The application code (which is static) must always
know where to find this metadata in flash after power-up. However, because the
memory must be erased before it is programmed, if we were to simply write the
metadata to the same area of the flash everytime, there would be a short window
when there was no valid metadata anywhere in flash. If a reset, power failure, or
system fault occurred during this window, all user data would be lost. The
LM3S8962 includes the Flash Parameter Block module which is specifically intended
to address this problem, and we chose to use the provided functions FlashPBInit(),
FlashPBSave(), and FlashPBGet() for storing the metadata. This module uses a
range of flash addresses and stores new data in a different location, before erasing
the old data location. A system of sequence numbers and checksums is used so,

23

after power-up, the system can determine where the most recent parameter block is
stored within the aforementioned range of flash addresses.
The Add User block is responsible for storing user records in the database. The
block is encapsulated in the StoreUserRec() function. The function first searches for
an existing and identical user record in the database, and does nothing if it finds
one. If no matching user records are found, the function checks the metadata to
find the next available erase block in flash where it can store the new record. After
programming the new record to flash, the metadata parameter block is updated to
reflect the change. If it finds a record with matching username and public key, but
different permissions, the new record is stored and the old record is marked as
invalid in the metadata. This last case provides the mechanism for changing user
permissions.
The Remove User block takes care of removing old user records, to prevent the user
from continuing to use the lock. This block is encapsulated by the
RemoverUserRec() function. It searches the database for a valid user record with
identical username and public key. If it finds one, it updates the metadata
parameter block to mark the record as invalid.
When a command is received, the users record must be retrieved, and this is the
responsibility of the User Lookup block. The lookup is accomplished by performing
an exhaustive search of all valid user records, to find all records matching the user
name. Because there is no provision to prevent duplicate user names, users can
only be uniquely identified by their public keys. (There are provisions to prevent
user records with both identical usernames and identical public keys.) If two or
more users are found with the same name, the Message Hashing block will try each
matching user record until it finds one whose public key can be used to validate the
commands signature.
When the system is powered on, we call the function CheckForAdmin() which scans
the user database for valid administrator accounts. (Users with the admin
permission bit set may add and remove other user records.) If no valid
administrator accounts are found, then a hard-coded default user account with
administrative priviledges is added to the user database. The credentials (user
name and private key variables) necessary to use this account would be distributed
with new lock control systems, to allow the new owner to initially control the system
and to add users. After adding a new administrative user, the default account could
them be removed to ensure system security. If at a later date, the administrator
accidentally deleted himself and all other administrative accounts, the default user
account would be re-added after the next system reset so the system would still be
usable.
The RFID block is implemented as an interrupt service routine (ISR) (see
UART0IntHandler() in main.c), a code section in the main loop, and the function
24

is_rfid_valid(). The ISR is executed whenever information is available on the receive


FIFO of UART0. It places the received bytes onto a software buffer (RFIDbuffer) and
sets a global flag when this buffer is full. This global flag is checked every pass
through the main loop; and, if it is set, the function is_rfid_valid() is called to
determine if the RFID matches the one associated with this lock control device. If it
is the correct the RFID tag, the state of the lock is toggled. Because the RFID tag is
intended only to be a backup method for controlling the lock, the correct
identification number is hard-coded into the application. Ideally, a dynamic
database of RFID tag numbers would be implemented, but we were not able to
accomplish this within our time constraints.
The pulse width modulated (PWM) output is used to control the position of the servo
which manipulates the deadbolt handle. The PWM block is responsible for creating
this output. It is implemented in pwm.c and consists of two functions: pwm_init()
and PWM_Change(). The first function is called once after power-up and initializes
the PWM peripheral module. The servo requires pulses at 50 Hz with the high time
varying between about .6 and 2.6 ms, depending on the desired position. The latter
function is called whenever it is necessary to lock or unlock the door. It
accomplishes this by switching the high time between two preset values, which
were determined by trial and error to be appropriate for locking and unlocking the
door.

Hardware Implementation
Hardware Schematic

Figure 7: Overall System Hardware Schematic

Much of the hardware used in this project had to be supplemented with software
support so some of the software implementation discussed in the Hardware
Implementation section will supplement this discussion. Modules such as the RFID
25

reader and Bluetooth modules had process chips, which allowed the Stellaris board
to communicate to each device over a UART communication channel. So, each of
the inputs and output of these mentioned about has a serial communication made
by pressing bits over the connection.
RFID Reader
The RFID reader module is the Innovations ID-12 module, which communicates over
UART connection. The UART communication was established using the boards
UART0 connection, configured to a baud rate of 9600, which is specified by the data
sheet of the RFID reader. When the RFID card is placed above the reader the card is
power just enough to send a 32-bit binary signal. That signal is read by the module
on the RFID reader board and sent via UART to the UART0 Rx pin of the Stellaris
board. Once the board receives a signal on this pin it triggers an interrupt, which
processes the 32-bit number. The RFID reader did not require any configuration as
the default settings are relevant to the configuration used in this project.
Bluetooth Module
The Bluetooth module consisted of the JY-MCU BC417, which had a UART module
that allowed all interaction with the module to be a serial communication. The UART
connection was made with the UART1 Tx and Rx pins of the Stellaris board. An
additional pin on the Bluetooth module labeled KEY could be set to Vcc, and allowed
for AT configuration commands to be sent and processed. So before integrating the
module into the system the configuration setting were changed so that the baud
rate was 115,200 and the advertised Bluetooth name was LCON2. At this baud
rate the information coming over Bluetooth could be processed fast enough by the
Stellaris board before there was any overflowing buffer problems on the Bluetooth
module. By using the UART interface to the Bluetooth module interaction was
straight forward in the fact that it was like writing directly to the Android device.
Servo
The servo used for this project was the TowerPro SG-5010, which is a powerful servo
that was calibrated to have high enough torque to turn the deadbolt lock. The servo
works by having 3 connections, a ground, power and control connection. The control
connection is used to send a pulse width modulated signal that tells the servo which
position to be in. The Stellaris board had support for pulse width modulation and
was configured on pin PW2 for this project. The datasheet for the servo specified
that the period between pulses is 20ms and that the widths vary between 1 and 2
ms for the full 180-degree rotation. After experimentation of the servo this range
was found to actually be between 0.6 and 2.3 ms to have the servo turn the 180
degrees. The Stellaris board PWM output was then configured to output these
specified values. Once the servo was attached to the lock mechanism it was
calibrated so that it could turn between the locked and unlocked positions. The lock
and unlocked positions were at the 0.9ms and 2.2ms pulse width, which was used in
the software manipulation of the Stellaris board to change between the two states.

26

TI Stellaris Development Board


The Stellaris board was mainly using in the software implementation of the project
as the hardware is configure to work with the board and software loaded onto it. To
get the pulse width modulator pin to output the correct period and pulse width to
the servo a pulse generator module on the board had to be started and configure
for the correct frequency. The UART connection had to also be configured to use the
correct baud rate of the connected modules. Interrupts were configured so that
signals coming into the pins were intercepted and processed by the board.
Random Number Generator
The implementation of the random number generator uses a 100 Kohm resistor and
0.1 micro Farad capacitor as shown in the schematic about. The resistor is
connected across pins PC5 and PC7 on the Stellaris board and the capacitor is
connected between pins PC7 and ground. Reference the Software Implementation
section for a detailed description for how it works.

System Testing
Test Plan
The testing for the Safe Guard Lock System was fairly straight forward. The
operation of a door lock is the main result that needs to be tested along with two
other features of the system. The first test was the ability to lock and unlock the
door from the Smartphone. This simply meant pushing the lock button on the
smartphone, waiting for the deadbolt to lock, then pushing the unlock button on the
smartphone and waiting for the deadbolt to unlock. Next the same process was
performed on the RFID card. The RFID card was swiped and the state of the lock
would change. The card was swiped again and the state of the lock returned to its
original position. An invalid card must also be swiped to ensure the lock doesnt
change position.

Next the add and remove user feature was tested. This meant connecting two
phones via Bluetooth, having the Admin device receive the user key, then the
Admin device connected to the Lock, finally the Admin adds the new user to the
database maintained on the lock. Next the remove user was tested by using the
same key that the Admin just added, connecting to the lock, and then selecting the
remove user button. These actions should remove the user from the database kept
on the lock. Finally the last test was for a newly added user to open the door. The
new user was added to the database via the method described above. Then the new
user selects the unlock and lock buttons. These button presses should unlock and
lock the deadbolt.

27

Test Specifications
The following tests must be performed:

Deadbolt Unlocked from Admin Smartphone


o When the unlock button is pushed on the smartphone app the deadbolt
moves to the unlock position
Deadbolt Locked from Admin Smartphone
o When the lock button is pushed on the smartphone app the deadbolt
moves to the locked position
Deadbolt Unlocked from the RFID Card
o With the deadbolt starting in the locked state the RFID card is swiped,
and then the deadbolt moves to the unlocked state
Deadbolt Locked from the RFID Card
o With the deadbolt starting in the unlocked state the RFID card is
swiped, and then the deadbolt moves to the locked state
Deadbolt does not Operate with Invalid RFID Card
o With the deadbolt in any state an invalid RFID card is swiped, and the
lock does not change state
Add New User Feature
o Connection from the Admins device to the new users device is made,
and the new users key is passed to the Admins device
o The Admin connects with the lock, pushes the add button, and adds
the new user to the database
New User can Unlock the Deadbolt
o After being added to the database the new user pushes the unlock
button, and the deadbolt moves to the unlocked state
New User can Lock the Deadbolt
o After being added to the database the new user pushes the lock
button, and the deadbolt moves to the locked state
Removing a User Feature
o Connection from the Admins device to the users device that is to be
removed is made, and that users key is passed to the Admins device
o The Admin connects with the lock, pushes the remove button, and
removes the user from the database

Test Cases
Below is a list of test cases and the test procedure explaining how to test each case.
Test Case
Deadbolt Unlocked from Admin
Smartphone

1.
2.

Deadbolt Locked from Admin

3.
4.
5.
1.
28

Test Procedure
Power on system and smartphone
If smartphone is not paired with
lock due so now
Launch Safe Guard Lock App
Connect to lock
Press unlock button
Power on system and smartphone

Smartphone

2. If smartphone is not paired with


lock due so now
3. Launch Safe Guard Lock App
4. Connect to lock
5. Press lock button
1. Power on system
2. Set lock to locked state
3. Hold Valid RFID Card to reader
1. Power on system
2. Set lock to unlocked state
3. Hold Valid RFID Card to reader
1. Power on the system
2. Hold Invalid RFID to reader
1. Connect new user and admin
devices via Safe Guard Lock app
2. New user enters user name
3. New user sends key to Admin
4. Disconnect devices
5. Admin connects to lock
6. Admin pushes add user button
1. Power on system
2. New user connects to lock
3. New user selects the unlock
button
1. Power on system
2. New user connects to lock
3. New user selects the lock button
1. Connect user and admin devices
via Safe Guard Lock app
2. User sends key to Admin
3. Disconnect devices
4. Admin connects to lock
5. Admin pushes remove user button

Deadbolt Unlocked from RFID Card


Deadbolt Locked from RFID Card
Deadbolt doesnt Operate with
Invalid RFID Card
Add New User to Database

New User Unlock Deadbolt

New User Lock Deadbolt


Remove User from Database

Results
Presentation of Results
We built the prototype device shown in Figure 8 below to demonstrate and test our
design. The servo was powered by a bench power supply, and the control signal
was driven by the PWM output from the Stellaris board as described above. We had
two RFID cards, only one of which had its ID number programmed into the Lock
Control application. We tried waving both cards in front of the reader. Both cards
were picked up by the reader (confirmed by a loud beep from the reader), but only
the correct one would cause the door to be locked or unlocked. This worked very

29

reliably, and confirms both that the RFID related code as well as the PWM/servo
control code worked as designed.

Figure 8: Prototype lock actuating mechanism

We installed the android app on several phones and tested their performance. One
of these phones was configured to use the default administrator account (see the
Software Implementation section) and it was able to successfully lock and unlock
the door. This confirms that our design was sound, and that all the authentication
steps were working correctly (at least some of the time.) In order for the phone to
exercise control over the lock in this way, many things must work simultaneously:
the random session token is being stored by the phone and included in the
subsequent commands, the command message is being hashed successfully on
both devices, the phone is successfully encrypting the hash with the private key,
the Lock Control system is successfully parsing the command and retrieving the
user record from the database, and the Lock Control system is successfully
decrypting the signature using the users public key.
We were also able to successfully generate private/public key pairs for new users
using the android app, and to transfer the user name and public key to the phone
with the admin user. In turn, we successfully used the android app to add and
remove the new user record to the Lock Control system database. Using some
debugging code, we were able to print the stored user records to a computer
30

terminal and confirm that the user records were being storing correctly in the
database. However, we could not get the phones with these new user accounts to
actually control the lock. The Lock Control system would fail to verify the signature
on messages received from these new users. This is detailed in the Error Analysis
section, and unfortunately we didnt have sufficient time to troubleshoot the
problem further.

Analysis of Errors
In the result of this project there were two errors that persisted that was not fixed
by the deadline of the project. The first was that about every 1 in 10 transactions of
the randomly generated session token from the lock management system to the
Android device was corrupted by noise, which caused the session token to become
invalid and any command sent after that point was not authenticated by the lock
management system. So the general process that created the error was a phone
connected to the lock management system. Once the phone send the begin
command the lock management system acknowledges by sending a begin
acknowledge command with the status of the lock system and a random session
token to be used with each command. When the phone receives this command it
parses the information and stores the session token so that it can be used for future
commands. In the transaction the session token gets corrupted so when its used for
future commands both the session token and signature are invalid and that is
expressed by the lock management system among parsing and sends a failure
command to the phone. If the user then disconnects from the lock management
system and reconnects, this process starts once again and a new session token is
produced. And the commands sent from the phone then are verified as successful
by the lock management system. This type of error could have been caused by
other devices in the lab, as there are other Bluetooth devices being used by the
other groups in the class. This could have also been caused by a difference in
exchange rate as Bluetooth transfer rates are in the mega baud spectrum and the
UART that pushes the Bluetooth information is at 115,200 baud, but the Bluetooth
modules buffer is very large to where successful transfer of 6 KB was done.

The second error that was still present in the project at the time of the deadline was
that a new users public key and username could be added and removed from the
database of authorized users, but the users added couldnt gain the permissions to
unlock and lock the system. To combat the problem it was verified that upon
receiving the add user command that the lock management system was successful
in writing the new users data into flash memory, as the data read after the write
was confirmed to be correct. A similar procedure was done to make sure that the
user could be removed from the system by making sure the valid bit in the table of
authorized users was set to false. Also for comparison at the beginning of the lock
management program, if there are no authorized users in the authorized table then
31

a default key is added to the database. Commands that came from that user was
processed through the database as any other user would be and was verified as an
authorized user that could open or close the lock. So with this insight it was
believed that the error might be in the signature of the message being produced on
the Android application. Upon further investigation it was found that the lock system
was not verifying the message being it was an invalid signature, but the problem
could not be found on the Android side. The process of signing a message using the
default admin key and an ordinary user was identical so it wasnt clear what was
causing the problem.

Other than these two problems the system had the functionality that was specified
in the system description and produced whats described in the Presentation,
Discussion and Analysis of Results section.

Bill of Materials

Summary
For our final project, we designed and prototyped a system for controlling a
deadbolt door lock with a Bluetooth equipped Android smartphone as well as a
backup system utilizing a RFID card. The system consisted of an Android app
installed on a smartphone, a LM3S8962 Stellaris development board, a RFID tag
reader, a Bluetooth transceiver, and a servo motor. The system was secured with
randomly generated session tokens and digital signatures utilizing the SHA1 oneway hash function coupled with RSA private-public key encryption.
The Android app was able to lock or unlock the door by communicating with the
Lock Control system over a wireless Bluetooth connection. The app was able to
generate private/public key pairs for new users and to transmit these to the
administrators smartphone. The administrator was, it turn, able to add these new
user credentials to the Lock Control systems user database or delete them from the
same. Although the design called for these newly added users to be able to lock or
unlock the door just as the administrator could, the prototype device failed to
32

function correctly in this way, nor could this be corrected during the time allotted for
the project. The RFID card associated with the Lock Control system was able to lock
or unlock the door as designed, while the other RFID card was disallowed from
controlling the lock, also in accordance with our design.

Conclusion
Although some aspects of our project did not come to fruition or work correctly, we
found that our design was basically sound, and we are confident that the remaining
bugs could be corrected if given sufficient time. The system was constructed of
readily available components and utilized the popular and common Android
smartphone platform. We expect that the app could be ported to other smartphone
platforms without much difficulty. Most of the heavy-lifting in the software
implementation was done by freely-available and open source software libraries.
Libraries designed for use with modern operating systems on x86 hardware were
reconfigured and built for a bare-metal ARM system with little difficulty (although
there was a steep learning-curve for the tools involved.)
From this experience we conclude that it is easily within the reach of the average
electronics hobbyist to build a similar system for controlling their locks without
physical keys, and to be confident that such a system is equally secure as existing
common locks (probably more secure). Further, we conclude that a commercial
version could be easily produced and marketed without any new technology
development. The widespread availability of such a system would relieve
individuals of the necessity to carry many physical keys with them, and it would
make keyed access much easier to manage for large organizations. The
authentication mechanism we used is based on the OpenPGP standard, and could
be used in many different situations to authenticate users. Essentially, any place a
username and password is currently used, public/private key system could be used
to provide even greater security, with much more convenient access. This means
our system could be part of a revolution in access and authentication which would
greatly improve the lives of many people.

Appendix
Appendix A Android Application Code
Appendix A1 (DeviceListActivity.java)
/*

33

* Copyright (C) 2009 The Android Open Source Project


*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.BluetoothChat;
import java.util.Set;
//import android.R;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
/**
* This Activity appears as a dialog. It lists any paired devices and
* devices detected in the area after discovery. When a device is chosen
* by the user, the MAC address of the device is sent back to the parent
* Activity in the result Intent.
*/
public class DeviceListActivity extends Activity {
// Debugging
private static final String TAG = "DeviceListActivity";
private static final boolean D = true;
// Return Intent extra
public static String EXTRA_DEVICE_ADDRESS = "device_address";
// Member fields
private BluetoothAdapter mBtAdapter;
private ArrayAdapter<String> mPairedDevicesArrayAdapter;
private ArrayAdapter<String> mNewDevicesArrayAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Setup the window
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.device_list);
// Set result CANCELED incase the user backs out
setResult(Activity.RESULT_CANCELED);

34

// Initialize the button to perform device discovery


Button scanButton = (Button) findViewById(R.id.button_scan);
scanButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
doDiscovery();
v.setVisibility(View.GONE);
}
});
// Initialize array adapters. One for already paired devices and
// one for newly discovered devices
mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
// Find and set up the ListView for paired devices
ListView pairedListView = (ListView) findViewById(R.id.paired_devices);
pairedListView.setAdapter(mPairedDevicesArrayAdapter);
pairedListView.setOnItemClickListener(mDeviceClickListener);
// Find and set up the ListView for newly discovered devices
ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
newDevicesListView.setOnItemClickListener(mDeviceClickListener);
// Register for broadcasts when a device is discovered
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
this.registerReceiver(mReceiver, filter);
// Register for broadcasts when discovery has finished
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
this.registerReceiver(mReceiver, filter);
// Get the local Bluetooth adapter
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
// Get a set of currently paired devices
Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();

// If there are paired devices, add each one to the ArrayAdapter


if (pairedDevices.size() > 0) {
findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
for (BluetoothDevice device : pairedDevices) {
mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
} else {
String noDevices = getResources().getText(R.string.none_paired).toString();
mPairedDevicesArrayAdapter.add(noDevices);
}

@Override
protected void onDestroy() {
super.onDestroy();
// Make sure we're not doing discovery anymore
if (mBtAdapter != null) {
mBtAdapter.cancelDiscovery();
}
// Unregister broadcast listeners
this.unregisterReceiver(mReceiver);
}
/**
* Start device discover with the BluetoothAdapter
*/
private void doDiscovery() {
if (D) Log.d(TAG, "doDiscovery()");

35

// Indicate scanning in the title


setProgressBarIndeterminateVisibility(true);
setTitle(R.string.scanning);
// Turn on sub-title for new devices
findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);
// If we're already discovering, stop it
if (mBtAdapter.isDiscovering()) {
mBtAdapter.cancelDiscovery();
}

// Request discover from BluetoothAdapter


mBtAdapter.startDiscovery();

// The on-click listener for all devices in the ListViews


private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
// Cancel discovery because it's costly and we're about to connect
mBtAdapter.cancelDiscovery();
// Get the device MAC address, which is the last 17 chars in the View
String info = ((TextView) v).getText().toString();
String address = info.substring(info.length() - 17);
// Create the result Intent and include the MAC address
Intent intent = new Intent();
intent.putExtra(EXTRA_DEVICE_ADDRESS, address);
// Set result and finish this Activity
setResult(Activity.RESULT_OK, intent);
finish();
};

// The BroadcastReceiver that listens for discovered devices and


// changes the title when discovery is finished
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();

// When discovery finds a device


if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// If it's already paired, skip it, because it's been listed already
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
// When discovery is finished, change the Activity title
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
setProgressBarIndeterminateVisibility(false);
setTitle(R.string.select_device);
if (mNewDevicesArrayAdapter.getCount() == 0) {
String noDevices = getResources().getText(R.string.none_found).toString();
mNewDevicesArrayAdapter.add(noDevices);
}
}

};
}

Appendix A2 (BluetoothChatService.java)
/*

36

* Copyright (C) 2009 The Android Open Source Project


*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.BluetoothChat;
import
import
import
import

java.io.IOException;
java.io.InputStream;
java.io.OutputStream;
java.util.UUID;

import
import
import
import
import
import
import
import
import

android.bluetooth.BluetoothAdapter;
android.bluetooth.BluetoothDevice;
android.bluetooth.BluetoothServerSocket;
android.bluetooth.BluetoothSocket;
android.content.Context;
android.os.Bundle;
android.os.Handler;
android.os.Message;
android.util.Log;

/**
* This class does all the work for setting up and managing Bluetooth
* connections with other devices. It has a thread that listens for
* incoming connections, a thread for connecting with a device, and a
* thread for performing data transmissions when connected.
*/
public class BluetoothChatService {
// Debugging
private static final String TAG = "BluetoothChatService";
private static final boolean D = true;
// Name for the SDP record when creating server socket
private static final String NAME_SECURE = "BluetoothChatSecure";
private static final String NAME_INSECURE = "BluetoothChatInsecure";
// UUID's for serial port bluetooth connections
private static final UUID MY_UUID_SECURE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static final UUID MY_UUID_INSECURE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
// Member fields
private final BluetoothAdapter mAdapter;
private final Handler mHandler;
private AcceptThread mSecureAcceptThread;
private AcceptThread mInsecureAcceptThread;
private ConnectThread mConnectThread;
private ConnectedThread mConnectedThread;
private int mState;
// Constants that indicate the current connection state
public static final int STATE_NONE = 0;
// we're doing nothing
public static final int STATE_LISTEN = 1;
// now listening for incoming connections
public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
public static final int STATE_CONNECTED = 3; // now connected to a remote device
/**
* Constructor. Prepares a new BluetoothChat session.
* @param context The UI Activity Context

37

* @param handler A Handler to send messages back to the UI Activity


*/
public BluetoothChatService(Context context, Handler handler) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
mState = STATE_NONE;
mHandler = handler;
}
/**
* Set the current state of the chat connection
* @param state An integer defining the current connection state
*/
private synchronized void setState(int state) {
if (D) Log.d(TAG, "setState() " + mState + " -> " + state);
mState = state;
// Give the new state to the Handler so the UI Activity can update
mHandler.obtainMessage(BluetoothChat.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
}
/**
* Return the current connection state. */
public synchronized int getState() {
return mState;
}
/**
* Start the chat service. Specifically start AcceptThread to begin a
* session in listening (server) mode. Called by the Activity onResume() */
public synchronized void start() {
if (D) Log.d(TAG, "start");
// Cancel any thread attempting to make a connection
if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
setState(STATE_LISTEN);

// Start the thread to listen on a BluetoothServerSocket


if (mSecureAcceptThread == null) {
mSecureAcceptThread = new AcceptThread(true);
mSecureAcceptThread.start();
}
if (mInsecureAcceptThread == null) {
mInsecureAcceptThread = new AcceptThread(false);
mInsecureAcceptThread.start();
}

/**
* Start the ConnectThread to initiate a connection to a remote device.
* @param device The BluetoothDevice to connect
* @param secure Socket Security type - Secure (true) , Insecure (false)
*/
public synchronized void connect(BluetoothDevice device, boolean secure) {
if (D) Log.d(TAG, "connect to: " + device);
// Cancel any thread attempting to make a connection
if (mState == STATE_CONNECTING) {
if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
// Start the thread to connect with the given device
mConnectThread = new ConnectThread(device, secure);

38

mConnectThread.start();
setState(STATE_CONNECTING);
}
/**
* Start the ConnectedThread to begin managing a Bluetooth connection
* @param socket The BluetoothSocket on which the connection was made
* @param device The BluetoothDevice that has been connected
*/
public synchronized void connected(BluetoothSocket socket, BluetoothDevice
device, final String socketType) {
if (D) Log.d(TAG, "connected, Socket Type:" + socketType);
// Cancel the thread that completed the connection
if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
// Cancel any thread currently running a connection
if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
// Cancel the accept thread because we only want to connect to one device
if (mSecureAcceptThread != null) {
mSecureAcceptThread.cancel();
mSecureAcceptThread = null;
}
if (mInsecureAcceptThread != null) {
mInsecureAcceptThread.cancel();
mInsecureAcceptThread = null;
}
// Start the thread to manage the connection and perform transmissions
mConnectedThread = new ConnectedThread(socket, socketType);
mConnectedThread.start();
// Send the name of the connected device back to the UI Activity
Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_DEVICE_NAME);
Bundle bundle = new Bundle();
bundle.putString(BluetoothChat.DEVICE_NAME, device.getName());
msg.setData(bundle);
mHandler.sendMessage(msg);
}

setState(STATE_CONNECTED);

/**
* Stop all threads
*/
public synchronized void stop() {
if (D) Log.d(TAG, "stop");
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
if (mSecureAcceptThread != null) {
mSecureAcceptThread.cancel();
mSecureAcceptThread = null;
}
if (mInsecureAcceptThread != null) {
mInsecureAcceptThread.cancel();
mInsecureAcceptThread = null;
}
setState(STATE_NONE);

39

}
/**
* Write to the ConnectedThread in an unsynchronized manner
* @param out The bytes to write
* @see ConnectedThread#write(byte[])
*/
public void write(byte[] out) {
// Create temporary object
ConnectedThread r;
// Synchronize a copy of the ConnectedThread
synchronized (this) {
if (mState != STATE_CONNECTED) return;
r = mConnectedThread;
}
// Perform the write unsynchronized
r.write(out);
}
/**
* Indicate that the connection attempt failed and notify the UI Activity.
*/
private void connectionFailed() {
// Send a failure message back to the Activity
Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(BluetoothChat.TOAST, "Unable to connect device");
msg.setData(bundle);
mHandler.sendMessage(msg);
// Start the service over to restart listening mode
BluetoothChatService.this.start();
}
/**
* Indicate that the connection was lost and notify the UI Activity.
*/
private void connectionLost() {
// Send a failure message back to the Activity
Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(BluetoothChat.TOAST, "Device connection was lost");
msg.setData(bundle);
mHandler.sendMessage(msg);

// Start the service over to restart listening mode


BluetoothChatService.this.start();

/**
* This thread runs while listening for incoming connections. It behaves
* like a server-side client. It runs until a connection is accepted
* (or until cancelled).
*/
private class AcceptThread extends Thread {
// The local server socket
private final BluetoothServerSocket mmServerSocket;
private String mSocketType;
public AcceptThread(boolean secure) {
BluetoothServerSocket tmp = null;
mSocketType = secure ? "Secure":"Insecure";
// Create a new listening server socket
try {
if (secure) {
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,
MY_UUID_SECURE);
} else {

40

tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(
NAME_INSECURE, MY_UUID_INSECURE);

}
} catch (IOException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e);
}
mmServerSocket = tmp;

public void run() {


if (D) Log.d(TAG, "Socket Type: " + mSocketType +
"BEGIN mAcceptThread" + this);
setName("AcceptThread" + mSocketType);
BluetoothSocket socket = null;
// Listen to the server socket if we're not connected
while (mState != STATE_CONNECTED) {
try {
// This is a blocking call and will only return on a
// successful connection or an exception
socket = mmServerSocket.accept();
} catch (IOException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e);
break;
}
// If a connection was accepted
if (socket != null) {
synchronized (BluetoothChatService.this) {
switch (mState) {
case STATE_LISTEN:
case STATE_CONNECTING:
// Situation normal. Start the connected thread.
connected(socket, socket.getRemoteDevice(),
mSocketType);
break;
case STATE_NONE:
case STATE_CONNECTED:
// Either not ready or already connected. Terminate new socket.
try {
socket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close unwanted socket", e);
}
break;
}
}
}

}
if (D) Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType);
}

public void cancel() {


if (D) Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this);
try {
mmServerSocket.close();
} catch (IOException e) {
Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e);
}
}
}
/**
* This thread runs while attempting to make an outgoing connection
* with a device. It runs straight through; the connection either
* succeeds or fails.

41

*/
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
private String mSocketType;
public ConnectThread(BluetoothDevice device, boolean secure) {
mmDevice = device;
BluetoothSocket tmp = null;
mSocketType = secure ? "Secure" : "Insecure";

// Get a BluetoothSocket for a connection with the


// given BluetoothDevice
try {
if (secure) {
tmp = device.createRfcommSocketToServiceRecord(
MY_UUID_SECURE);
} else {
tmp = device.createInsecureRfcommSocketToServiceRecord(
MY_UUID_INSECURE);
}
} catch (IOException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
}
mmSocket = tmp;

public void run() {


Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
setName("ConnectThread" + mSocketType);
// Always cancel discovery because it will slow down a connection
mAdapter.cancelDiscovery();
// Make a connection to the BluetoothSocket
try {
// This is a blocking call and will only return on a
// successful connection or an exception
mmSocket.connect();
} catch (IOException e) {
// Close the socket
try {
mmSocket.close();
} catch (IOException e2) {
Log.e(TAG, "unable to close() " + mSocketType +
" socket during connection failure", e2);
}
connectionFailed();
return;
}
// Reset the ConnectThread because we're done
synchronized (BluetoothChatService.this) {
mConnectThread = null;
}
// Start the connected thread
connected(mmSocket, mmDevice, mSocketType);
}

public void cancel() {


try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);
}
}

42

/**
* This thread runs during a connection with a remote device.
* It handles all incoming and outgoing transmissions.
*/
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket, String socketType) {
Log.d(TAG, "create ConnectedThread: " + socketType);
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "temp sockets not created", e);
}

mmInStream = tmpIn;
mmOutStream = tmpOut;

public void run() {


Log.i(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;
// Right after establishing connection with the lock system send the begin
// command so the system is aware of the connection.
String startMsg = "<BEGIN>";
byte[] send = startMsg.getBytes();
write(send);
// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);

// Send the obtained bytes to the UI Activity


mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
break;
}

}
/**
* Write to the connected OutStream.
* @param buffer The bytes to write
*/
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
// Share the sent message back to the UI Activity
mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);

43

public void cancel() {


try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed", e);
}
}
}

Appendix A3 (BluetoothChat.java)
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.BluetoothChat;
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

java.io.BufferedInputStream;
java.io.File;
java.io.FileInputStream;
java.io.FileNotFoundException;
java.io.FileOutputStream;
java.io.IOException;
java.math.BigInteger;
java.security.KeyFactory;
java.security.KeyPair;
java.security.KeyPairGenerator;
java.security.SecureRandom;
java.security.Signature;
java.security.interfaces.RSAPrivateKey;
java.security.interfaces.RSAPublicKey;
java.security.spec.RSAPrivateKeySpec;
java.security.spec.RSAPublicKeySpec;

import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import
import

android.app.Activity;
android.bluetooth.BluetoothAdapter;
android.bluetooth.BluetoothDevice;
android.content.Context;
android.content.Intent;
android.os.Bundle;
android.os.Handler;
android.os.Message;
android.util.Log;
android.view.KeyEvent;
android.view.Menu;
android.view.MenuInflater;
android.view.MenuItem;
android.view.View;
android.view.View.OnClickListener;
android.view.Window;
android.view.inputmethod.EditorInfo;
android.widget.Button;
android.widget.CheckBox;

44

import
import
import
import

android.widget.EditText;
android.widget.TextView;
android.widget.Toast;
android.widget.ToggleButton;

/**
* This is the main Activity that displays the current chat session.
*/
public class BluetoothChat extends Activity {
private static byte[] mod = { (byte)0x00,
(byte)0x87, (byte)0xE6, (byte)0x72, (byte)0xEA, (byte)0x38, (byte)0x82, (byte)0x3D, (byte)0x8B,
(byte)0xC0, (byte)0x2F, (byte)0x85, (byte)0xBD, (byte)0x1C, (byte)0xF2, (byte)0x31, (byte)0x67,
(byte)0x2E, (byte)0x39, (byte)0x1D, (byte)0x64, (byte)0xC4, (byte)0x53, (byte)0x51, (byte)0x43,
(byte)0x7E, (byte)0x2E, (byte)0xF0, (byte)0x42, (byte)0xA5, (byte)0xE8, (byte)0x3C, (byte)0xAA,
(byte)0x84, (byte)0xA9, (byte)0xC6, (byte)0x97, (byte)0xA6, (byte)0x90, (byte)0xF9, (byte)0x7A,
(byte)0x14, (byte)0x2E, (byte)0xCF, (byte)0xF0, (byte)0x42, (byte)0x49, (byte)0x5A, (byte)0x5F,
(byte)0x60, (byte)0x7E, (byte)0x6E, (byte)0xA2, (byte)0xB3, (byte)0x87, (byte)0xBC, (byte)0x9E,
(byte)0xBD, (byte)0x9D, (byte)0xBF, (byte)0x06, (byte)0x7F, (byte)0x9E, (byte)0x2C, (byte)0x06,
(byte)0x3B, (byte)0x84, (byte)0x4C, (byte)0xDF, (byte)0xEE, (byte)0x33, (byte)0x76, (byte)0x3A,
(byte)0xE2, (byte)0x5E, (byte)0xB3, (byte)0x58, (byte)0x3C, (byte)0x07, (byte)0x4C, (byte)0x88,
(byte)0x7B, (byte)0xA6, (byte)0xBB, (byte)0x2E, (byte)0xDF, (byte)0x25, (byte)0x7B, (byte)0x77,
(byte)0x2B, (byte)0x7D, (byte)0xF1, (byte)0xE0, (byte)0xC2, (byte)0x5D, (byte)0xAD,
(byte)0xDE,
(byte)0x7F, (byte)0xD0, (byte)0x34, (byte)0xFD, (byte)0x4F, (byte)0xB0, (byte)0x0D, (byte)0xCA,
(byte)0x69, (byte)0x08, (byte)0xE9, (byte)0x03, (byte)0x0E, (byte)0x15, (byte)0x5F, (byte)0x11,
(byte)0xD2, (byte)0x66, (byte)0x36, (byte)0xD5, (byte)0xCE, (byte)0x70, (byte)0x17, (byte)0x8E,
(byte)0x01, (byte)0xF4, (byte)0x93, (byte)0x36, (byte)0x25, (byte)0xDB, (byte)0xF3,
(byte)0x43 };
private static byte[] privateExp = {
(byte)0x0C, (byte)0x57, (byte)0x19, (byte)0xB2, (byte)0x39, (byte)0x05, (byte)0x62, (byte)0x8F,
(byte)0x51, (byte)0x19, (byte)0x3F, (byte)0x9C, (byte)0xA7, (byte)0x87, (byte)0x3A, (byte)0x83,
(byte)0x33, (byte)0x08, (byte)0x4E, (byte)0xA9, (byte)0xFA, (byte)0xC5, (byte)0xD2, (byte)0x08,
(byte)0x3D, (byte)0xEA, (byte)0x07, (byte)0x39, (byte)0x16, (byte)0x15, (byte)0x9B, (byte)0x84,
(byte)0xA4, (byte)0x5D, (byte)0x42, (byte)0x42, (byte)0x3D, (byte)0x06, (byte)0xC7, (byte)0x10,
(byte)0x95, (byte)0xCA, (byte)0x96, (byte)0x69, (byte)0x2B, (byte)0xAB, (byte)0xBB, (byte)0x80,
(byte)0x13, (byte)0xA4, (byte)0x07, (byte)0x69, (byte)0xD0, (byte)0xC1, (byte)0x8F, (byte)0x98,
(byte)0x1E, (byte)0x81, (byte)0xB7, (byte)0x79, (byte)0xE0, (byte)0x96, (byte)0xBD, (byte)0x4A,
(byte)0x85, (byte)0x9C, (byte)0xE2, (byte)0x92, (byte)0x89, (byte)0x09, (byte)0xFB, (byte)0x54,
(byte)0x13, (byte)0x5B, (byte)0x5F, (byte)0x3B, (byte)0x3D, (byte)0x03, (byte)0xA7, (byte)0x12,
(byte)0x86, (byte)0xA9, (byte)0x67, (byte)0xDB, (byte)0x2A, (byte)0x0C, (byte)0x36, (byte)0x2C,
(byte)0x8D, (byte)0xBB, (byte)0xA0, (byte)0xCB, (byte)0xE9, (byte)0x18, (byte)0x4C, (byte)0xC3,
(byte)0xC0, (byte)0xAD, (byte)0x45, (byte)0xCC, (byte)0x88, (byte)0xC8, (byte)0x76, (byte)0x78,
(byte)0x1C, (byte)0x58, (byte)0xAD, (byte)0xD7, (byte)0xCA, (byte)0x17, (byte)0xFB, (byte)0x82,
(byte)0x9A, (byte)0xC9, (byte)0xB1, (byte)0x4E, (byte)0xF9, (byte)0xC8, (byte)0x9A, (byte)0x62,
(byte)0x2A, (byte)0x30, (byte)0xBB, (byte)0xAF, (byte)0xCB, (byte)0xB9, (byte)0xB9,
(byte)0x01 };
private static byte[] publicExp = { (byte)0x01, (byte)0x00, (byte)0x01 };
boolean SECURE = true;
boolean GENERATE_KEYS = true;
boolean USE_ADMIN_NAME = false;
// Debugging
private static final String TAG = "BluetoothChat";
private static final boolean D = true;
// Message types sent from the BluetoothChatService Handler
public static final int MESSAGE_STATE_CHANGE = 1;
public static final int MESSAGE_READ = 2;
public static final int MESSAGE_WRITE = 3;
public static final int MESSAGE_DEVICE_NAME = 4;
public static final int MESSAGE_TOAST = 5;
// Key names received from the BluetoothChatService Handler
public static final String DEVICE_NAME = "device_name";

45

public static final String TOAST = "toast";


private boolean UserNameProvided = false;
// Name of the file with stored user data
public static final String FILE_NAME = "user_data";
// User name
public static String userName = "Dick Dangle";
// User password
public static String userPW = "Titties";
private static KeyPair keys = null;
// User public key
public static RSAPublicKey publicKey = null;
// User private key
public static RSAPrivateKey privateKey = null;
// Intent request codes
private static final int REQUEST_CONNECT_DEVICE_SECURE = 1;
//private static final int REQUEST_CONNECT_DEVICE_INSECURE = 2;
private static final int REQUEST_ENABLE_BT = 3;
// Layout Views
private TextView mTitle;
private TextView mStatus;
private ToggleButton mLockToggle;
private Button mAddUser;
private Button mRemoveUser;
private Button mSendButton;
private CheckBox mAdminCheckBox;
private TextView mAddRemoveUserText;
private TextView mAddRemoveKeyText;
private Button mUserButton;
private EditText mUsernameText;
private TextView mUsernameView;
// Name of the connected device
private String mConnectedDeviceName = null;
// Local Bluetooth adapter
private BluetoothAdapter mBluetoothAdapter = null;
// Member object for the chat services
private BluetoothChatService mChatService = null;
public static final int KEY_LENGTH = 1032;
public static final int PUB_EXP_LENGTH = 3;
public static final int PRIV_EXP_LENGTH = 1024;
// Command Buffer for args from lock system
byte[] cmdBuffer = new byte[2*KEY_LENGTH];
int cmdIndex = 0;

// in bits

// in bits
// in bytes

// Store new user add/drop key


byte[] extUserKey = new byte[KEY_LENGTH/8];
String extUserName = "";
boolean extUserObtained = false;
private static final int RAND_NUM_LENGTH = 64; // bytes
// Store the random number given at the start of the session
byte[] randNum = new byte[RAND_NUM_LENGTH];
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(D) Log.e(TAG, "+++ ON CREATE +++");
// Set up the window layout
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.main);
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);

46

// Set up the custom title


mTitle = (TextView) findViewById(R.id.title_left_text);
mTitle.setText(R.string.app_name);
mTitle = (TextView) findViewById(R.id.title_right_text);
// Get local Bluetooth adapter
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

// If the adapter is null, then Bluetooth is not supported


if (mBluetoothAdapter == null) {
Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
finish();
return;
}

@Override
public void onStart() {
super.onStart();
if(D) Log.e(TAG, "++ ON START ++");

// If BT is not on, request that it be enabled.


// setupChat() will then be called during onActivityResult
if (!mBluetoothAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
// Otherwise, setup the chat session
} else {
if (mChatService == null) setupChat();
}

@Override
public synchronized void onResume() {
super.onResume();
if(D) Log.e(TAG, "+ ON RESUME +");
// Performing this check in onResume() covers the case in which BT was
// not enabled during onStart(), so we were paused to enable it...
// onResume() will be called when ACTION_REQUEST_ENABLE activity returns.
if (mChatService != null) {
// Only if the state is STATE_NONE, do we know that we haven't started already
if (mChatService.getState() == BluetoothChatService.STATE_NONE) {
// Start the Bluetooth chat services
mChatService.start();
}
}
}
private void setupChat() {
Log.d(TAG, "setupChat()");
// Initialize the submit user button form
mUserButton = (Button) findViewById(R.id.submit_username);
mUsernameText = (EditText) findViewById(R.id.enter_username);
mUsernameView = (TextView) findViewById(R.id.enter_username);
mUserButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
SetUsername();
}
});

// Initialize the send button with a listener that for click events
mSendButton = (Button) findViewById(R.id.button_send);
mSendButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {

47

String pre = "<KEY_EXCHANGE:" + userName + ":";


byte[] preBytes = pre.getBytes();
byte[] pubMod = publicKey.getModulus().toByteArray();
byte[] send = new byte[preBytes.length + KEY_LENGTH/8 + 1];
System.arraycopy(preBytes, 0, send, 0, preBytes.length);
System.arraycopy(pubMod, 0, send, preBytes.length, pubMod.length);
send[send.length - 1] = (byte)'>';
//byte[] test = new byte[];
sendByteArrayMessage(send);
}
});
// Initialize the status
mStatus = (TextView) findViewById(R.id.status);
//mStatus.setText("Status: Waiting For Connection...");
// Initialize the Lock toggle button and add listener for click events
mLockToggle = (ToggleButton) findViewById(R.id.lock_toggle);
mLockToggle.setEnabled(false);
mLockToggle.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (mLockToggle.isChecked()) {
SendLockCommand("LOCK");
} else {
SendLockCommand("UNLOCK");
}
}
});
mAdminCheckBox = (CheckBox) findViewById(R.id.permission_checkbox);
// Initialize the AddUser and RemoveUser buttons with listeners
mAddUser = (Button) findViewById(R.id.add_button);
mRemoveUser = (Button) findViewById(R.id.remove_button);
mAddUser.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
SendAddRemoveCmd("ADD");
}
});
mRemoveUser.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
SendAddRemoveCmd("REMOVE");
}
});
mAddRemoveUserText = (TextView) findViewById(R.id.username_add_drop);
mAddRemoveKeyText = (TextView) findViewById(R.id.key_add_drop);
if (GENERATE_KEYS) {
// Check to see if we currently have a file to read from
File modFile = new File("/data/data/com.example.android.BluetoothChat/files/modulous");
byte[] modBytes = new byte[KEY_LENGTH/8];
File pubExpFile = new File("/data/data/com.example.android.BluetoothChat/files/publicExp");
byte[] pubExpBytes = new byte[PUB_EXP_LENGTH];
File privExpFile = new File("/data/data/com.example.android.BluetoothChat/files/privateExp");
byte[] privExpBytes = new byte[PRIV_EXP_LENGTH/8];
try {
// Get the parameters from the stored files
BufferedInputStream modBuf = new BufferedInputStream(new FileInputStream(modFile));

48

modBuf.read(modBytes, 0, modBytes.length);
modBuf.close();
BufferedInputStream pubBuf = new BufferedInputStream(new FileInputStream(pubExpFile));
pubBuf.read(pubExpBytes, 0, pubExpBytes.length);
pubBuf.close();
BufferedInputStream privBuf = new BufferedInputStream(new FileInputStream(privExpFile));
privBuf.read(privExpBytes, 0, privExpBytes.length);
privBuf.close();
// Construct keys
KeyFactory keyMaker = KeyFactory.getInstance("RSA");
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(modBytes), new
BigInteger(pubExpBytes));
publicKey = (RSAPublicKey)keyMaker.generatePublic(pubKeySpec);
RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(new
BigInteger(modBytes), new BigInteger(privExpBytes));
privateKey = (RSAPrivateKey)keyMaker.generatePrivate(privKeySpec);
} catch (FileNotFoundException e) {
// this means the keys haven't been generated yet, so generate and store them in files
GenerateKeys();
try {
FileOutputStream modOS = openFileOutput("modulous", Context.MODE_PRIVATE);
modOS.write(privateKey.getModulus().toByteArray());
modOS.close();
FileOutputStream pubOS = openFileOutput("publicExp", Context.MODE_PRIVATE);
pubOS.write(publicKey.getPublicExponent().toByteArray());
pubOS.close();
FileOutputStream privOS = openFileOutput("privateExp", Context.MODE_PRIVATE);
privOS.write(privateKey.getPrivateExponent().toByteArray());
privOS.close();
} catch (Exception e1) {
e1.printStackTrace();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
//mStatus.setText("Status: Bad Things");
} catch (Exception ex) {
//mStatus.setText("Status: Bad Things");
}
} else {
ConstructHardKeys();
}
// Initialize the BluetoothChatService to perform bluetooth connections
mChatService = new BluetoothChatService(this, mHandler);
// Initialize the buffer for outgoing messages
//mOutStringBuffer = new StringBuffer("");
if (USE_ADMIN_NAME) {
userName = "Default Admin";
mUsernameView.setText("Default Admin");
mUsernameView.setEnabled(false);
mUserButton.setEnabled(false);
} else {
//GetUsername();
try {

49

File usernameFile = new


File("/data/data/com.example.android.BluetoothChat/files/userName");
int length = (int)usernameFile.length();
byte[] userBytes = new byte[length];
// Get the parameters from the stored files
BufferedInputStream userBuf = new BufferedInputStream(new
FileInputStream(usernameFile));
userBuf.read(userBytes, 0, userBytes.length);
userBuf.close();
userName = new String(userBytes);
UserNameProvided = true;
mUsernameView.setText(userName);
mUsernameView.setEnabled(false);
mUserButton.setEnabled(false);
} catch (FileNotFoundException e) {
// Username has not been aquired
//mStatus.setText("Status: Provide a Username!!");
} catch (Exception ex) {
}

}
}

private void SetUsername() {


String newUsername = mUsernameView.getText().toString();
if (newUsername.length() > 0) {
try {
FileOutputStream userOS = openFileOutput("userName", Context.MODE_PRIVATE);
userOS.write(newUsername.getBytes());
userOS.close();
userName = newUsername;
UserNameProvided = true;
mUsernameView.setEnabled(false);
mUserButton.setEnabled(false);
} catch (Exception ex) {
}
}

private void SendAddRemoveCmd(String cmd) {


if (extUserObtained) {
int size = cmd.getBytes().length +1+ userName.getBytes().length +1+
extUserName.getBytes().length +1+ extUserKey.length +1+ randNum.length;
if (cmd.equals("ADD")) size += 2;
byte[] encrypt = new byte[size];
byte perms = (mAdminCheckBox.isChecked()) ? ((byte)0x03) : ((byte)0x01);
String pre = cmd + ":" + userName + ":" + extUserName + ":";
System.arraycopy(pre.getBytes(), 0, encrypt, 0, pre.getBytes().length);
System.arraycopy(extUserKey, 0, encrypt, pre.getBytes().length, extUserKey.length);
encrypt[extUserKey.length + pre.getBytes().length] = (byte)':';
int current = extUserKey.length + pre.getBytes().length + 1;
if (cmd.equals("ADD")) {
encrypt[current] = perms;
encrypt[current+1] = (byte)':';
current += 2;
}
System.arraycopy(randNum, 0, encrypt, current, randNum.length);

50

byte[] sig = HashAndEncrypt(encrypt);


byte[] send = new byte[1 + encrypt.length + 1 + sig.length + 1];
send[0] = (byte)'<';
System.arraycopy(encrypt, 0, send, 1, encrypt.length);
send[encrypt.length + 1] = (byte)':';
System.arraycopy(sig, 0, send, encrypt.length + 2, sig.length);
send[send.length - 1] = (byte)'>';
sendByteArrayMessage(send);

//mAddRemoveUserText.setText("User Name: ");


//mAddRemoveKeyText.setText("User's Key: ");
//extUserObtained = false;

}
// Generate a new Public and Private Key for the user
private void GenerateKeys() {
try {
//generate the RSA keys
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
gen.initialize(KEY_LENGTH, new SecureRandom());
keys = gen.generateKeyPair();
publicKey = (RSAPublicKey)keys.getPublic();
privateKey = (RSAPrivateKey)keys.getPrivate();
} catch (Exception ex) {
// No such algorithm exception
}
}
private void ConstructHardKeys() {
try {
// Construct keys
KeyFactory keyMaker = KeyFactory.getInstance("RSA");
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger(mod), new
BigInteger(publicExp));
publicKey = (RSAPublicKey)keyMaker.generatePublic(pubKeySpec);
RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(new BigInteger(mod), new
BigInteger(privateExp));
privateKey = (RSAPrivateKey)keyMaker.generatePrivate(privKeySpec);
} catch (Exception ex) {
}
}
// Send the lock command to the locking system
private void SendLockCommand(String cmd) {
if (mChatService.getState() == BluetoothChatService.STATE_CONNECTED) {
String info = cmd + ":" + userName + ":";
byte[] infoBytes = info.getBytes();
byte[] encryptInfo = new byte[infoBytes.length + RAND_NUM_LENGTH];
System.arraycopy(infoBytes, 0, encryptInfo, 0, infoBytes.length);
System.arraycopy(randNum, 0, encryptInfo, infoBytes.length, RAND_NUM_LENGTH);
byte[] encrypt = HashAndEncrypt(encryptInfo);
byte[] send = new byte[1+encryptInfo.length+1+encrypt.length+1];
send[0] = (byte)'<';
System.arraycopy(encryptInfo, 0, send, 1, encryptInfo.length);
send[1+encryptInfo.length] = (byte)':';
System.arraycopy(encrypt, 0, send, 1+encryptInfo.length+1, encrypt.length);
send[send.length-1] = (byte)'>';

sendByteArrayMessage(send);

51

}
// Hash and encrypt the string, returning the result
private byte[] HashAndEncrypt(byte[] str) {
try {
// sign the message using SHA-1 hash and RSA
Signature instance = Signature.getInstance("SHA1withRSA");
instance.initSign(privateKey);
instance.update(str);
return instance.sign();
} catch (Exception ex) {
// No such algorithm exists
}
return new byte[10];
}
@Override
public synchronized void onPause() {
super.onPause();
if(D) Log.e(TAG, "- ON PAUSE -");
}
@Override
public void onStop() {
super.onStop();
if(D) Log.e(TAG, "-- ON STOP --");
}
@Override
public void onDestroy() {
super.onDestroy();
// Stop the Bluetooth chat services
if (mChatService != null) mChatService.stop();
if(D) Log.e(TAG, "--- ON DESTROY ---");
}
private void ensureDiscoverable() {
if(D) Log.d(TAG, "ensure discoverable");
if (mBluetoothAdapter.getScanMode() !=
BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
}
}
/**
* Sends a message.
* @param message A string of text to send.
*/
private void sendByteArrayMessage(byte[] message) {
// Check that we're actually connected before trying anything
if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) {
Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();
return;
}

// Check that there's actually something to send


if (message.length > 0) {
// Get the message bytes and tell the BluetoothChatService to write
mChatService.write(message);
}

// The action listener for the EditText widget, to listen for the return key
private TextView.OnEditorActionListener mWriteListener =
new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {

52

// If the action is a key-up event on the return key, send the message
if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) {
String message = view.getText().toString();
sendByteArrayMessage(message.getBytes());
}
if(D) Log.i(TAG, "END onEditorAction");
return true;

};
// The Handler that gets information back from the BluetoothChatService
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_STATE_CHANGE:
if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
switch (msg.arg1) {
case BluetoothChatService.STATE_CONNECTED:
mTitle.setText(R.string.title_connected_to);
mTitle.append(mConnectedDeviceName);
GoToConnectedState();
break;
case BluetoothChatService.STATE_CONNECTING:
mTitle.setText(R.string.title_connecting);
mLockToggle.setEnabled(false);
mStatus.setText("Status: Connecting to Lock");
break;
case BluetoothChatService.STATE_LISTEN:
case BluetoothChatService.STATE_NONE:
mTitle.setText(R.string.title_not_connected);
GoToDisconnectState();
break;
}
break;
case MESSAGE_WRITE:
//byte[] writeBuf = (byte[]) msg.obj;
// construct a string from the buffer
//String writeMessage = new String(writeBuf);
//mConversationArrayAdapter.add("Me: " + writeMessage);
break;
case MESSAGE_READ:
byte[] readBuf = (byte[]) msg.obj;
synchronized (cmdBuffer){
System.arraycopy(readBuf, 0, cmdBuffer, cmdIndex, msg.arg1);
cmdIndex += msg.arg1;
if (cmdBuffer[cmdIndex - 1] == (byte)('>')) {
//mStatus.setText("Parsing Command");
ParseCommand();
}
}

break;
case MESSAGE_DEVICE_NAME:
// save the connected device's name
mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
Toast.makeText(getApplicationContext(), "Connected to "
+ mConnectedDeviceName, Toast.LENGTH_SHORT).show();
break;
case MESSAGE_TOAST:
Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST),
Toast.LENGTH_SHORT).show();
break;
}

};

53

public void GoToConnectedState() {


mLockToggle.setEnabled(true);
mAddUser.setEnabled(true);
mRemoveUser.setEnabled(true);
mSendButton.setEnabled(true);
mStatus.setText("Status: Connected to Lock");
}
public void GoToDisconnectState() {
mLockToggle.setEnabled(false);
mStatus.setText("Status: Waiting For Connection...");
mAddUser.setEnabled(false);
mRemoveUser.setEnabled(false);
mSendButton.setEnabled(false);
}
public void ParseCommand() {
String msg = new String(cmdBuffer, 1, cmdIndex-2);
String[] tokens = msg.split(":");
if (tokens[0].equals("BEGINACK")) {
// Update the status to the user
if (tokens[1].equals("LOCKED")) {
mStatus.setText("Status: System is Locked");
mLockToggle.setChecked(true);
} else if (tokens[1].equals("UNLOCKED")) {
mStatus.setText("Status: System is Unlocked");
mLockToggle.setChecked(false);
}
// Save the random number for future use
try {
System.arraycopy(cmdBuffer, cmdIndex - 1 - RAND_NUM_LENGTH, randNum, 0,
RAND_NUM_LENGTH);
} catch (Exception ex) {
}
} else if (tokens[0].equals("BEGIN")) {
mLockToggle.setEnabled(false);
mAddUser.setEnabled(false);
mRemoveUser.setEnabled(false);
mSendButton.setEnabled(true);
} else if (tokens[0].equals("STATUS")) {
// Update the status to the user
if (tokens[1].equals("LOCKED")) {
mStatus.setText("Status: System is Locked");
mLockToggle.setChecked(true);
} else if (tokens[1].equals("LOCKED")) {
mStatus.setText("Status: System is Unlocked");
mLockToggle.setChecked(false);
} else if (tokens[1].equals("ADD_SUCCESS")) {
mStatus.setText("Status: Key Add was Successful");
} else if (tokens[1].equals("ADD_FAILED")) {
mStatus.setText("Status: Key Add was Unsuccessful");
}
} else if (tokens[0].equals("KEY_EXCHANGE")) {
// Add the transferred user name and key to text boxes
mAddRemoveUserText.setText("User Name: " + tokens[1]);
mAddRemoveKeyText.setText("User's Key: Loaded");
extUserName = tokens[1];

54

System.arraycopy(cmdBuffer, cmdIndex-1-(KEY_LENGTH/8), extUserKey, 0, KEY_LENGTH/8);


extUserObtained = true;
} else {

// Unknown cmd

// Reset the index of the command buffer


cmdIndex = 0;

public void onActivityResult(int requestCode, int resultCode, Intent data) {


if(D) Log.d(TAG, "onActivityResult " + resultCode);
switch (requestCode) {
case REQUEST_CONNECT_DEVICE_SECURE:
// When DeviceListActivity returns with a device to connect
if (resultCode == Activity.RESULT_OK) {
connectDevice(data, true);
}
break;
/*
case REQUEST_CONNECT_DEVICE_INSECURE:
// When DeviceListActivity returns with a device to connect
if (resultCode == Activity.RESULT_OK) {
connectDevice(data, false);
}
break;
*/
case REQUEST_ENABLE_BT:
// When the request to enable Bluetooth returns
if (resultCode == Activity.RESULT_OK) {
// Bluetooth is now enabled, so set up a chat session
setupChat();
} else {
// User did not enable Bluetooth or an error occured
Log.d(TAG, "BT not enabled");
Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
finish();
}
}
}
private void connectDevice(Intent data, boolean secure) {
// Get the device MAC address
String address = data.getExtras()
.getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
// Get the BLuetoothDevice object
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
// Attempt to connect to the device
mChatService.connect(device, secure);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.option_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Intent serverIntent = null;
switch (item.getItemId()) {
case R.id.secure_connect_scan:
// Launch the DeviceListActivity to see devices and do scan
serverIntent = new Intent(this, DeviceListActivity.class);
startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_SECURE);
return true;

55

case R.id.disconnect_device:
// Launch the DeviceListActivity to see devices and do scan
//serverIntent = new Intent(this, DeviceListActivity.class);
//startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_INSECURE);
//disconnect from the current device
// mChatService.stop();
mChatService.start();
return true;
case R.id.discoverable:
// Ensure this device is discoverable by others
ensureDiscoverable();
return true;
}
return false;
}
}

Appendix B Stellaris Board Code


Appendix B1 (main.c)
//*****************************************************************************
// main.c - This implements most of the functionality of the lock control
//
system
// Authors: David Chamberlain, Rob Greenewald
// Date: December 7, 2012
//*****************************************************************************
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_gpio.h"
#include "inc/lm3s8962.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/uart.h"
#include "driverlib/timer.h"
#include "drivers/rit128x96x4.h"
#include "utils/ustdlib.h"
#include "utils.h"
#include "parser.h"
#include "global.h"
#include "userdb.h"
#include "driverlib/debug.h"
//#include "../libtomcrypt-1.17/src/headers/tomcrypt.h"
#include <tomcrypt.h>
//#include "custcrypt.h"
#include "rfid_reader.h"
#include "pwm.h"
unsigned char rbuff[512];
RingBuffer RxBuff = {rbuff, sizeof(rbuff), 0, 0};
#ifdef DEBUG
unsigned char tbuff0[512];
RingBuffer TxBuffU0 = {tbuff0, sizeof(tbuff0), 0, 0};
char debug_msg[SCREEN_COLS + 1];
#endif
volatile unsigned long g_ulTickCount = 0;
unsigned long ClearCount = SCREENSAVER_TIMEOUT * TICK_RATE;
unsigned long InvalidCount;

56

unsigned long RNGcount = 0;


unsigned char currentRN[RANDOM_NO_LENGTH] =
"1234567812345678123456781234567812345678123456781234567812345678";
volatile struct {
bool but_press:1;
bool screen_active:1;
bool rx_new:1;
bool reset_parser:1;
bool invalid_wait:1;
bool rfid_buf_full:1;
bool is_door_locked:1;
//true if door is in locked state, false if door is in unlock state
} Flags = {false, false, false, true, false, false, false};
// RFID Global Variables
char RFIDbuffer[16];
size_t rfid_pos = 0;
unsigned char ebuff[RANDOM_NO_LENGTH*4 + 1];
RingBuffer EntropyRB = {ebuff, sizeof ebuff, 0, 0};
extern UserRecord newUR;
// The error routine that is called if ASSERT() fails.
#ifdef DEBUG
void __error__(char *pcFilename, unsigned long ulLine)
{
char msg[SCREEN_COLS + 1];
//WakeScreenSaver();
usnprintf(msg, sizeof msg, "ERR: %s ", pcFilename);
ErrorPrint(msg);
usnprintf(msg, sizeof msg, "line: %d ", ulLine);
ErrorPrint(msg);
}
#endif
// The UART interrupt handlers. (Modified TI example code.)
void UART0IntHandler(void)
{
unsigned long ulStatus;
// Get the interrrupt status.
ulStatus = UARTIntStatus(UART0_BASE, true);
// Clear the asserted interrupts.
UARTIntClear(UART0_BASE, ulStatus);
// Loop while there are characters in the receive FIFO.
while(UARTCharsAvail(UART0_BASE))
{
// Read the next character from the UART0 and write it to the RFID
// array
if(16 >= rfid_pos)
{
RFIDbuffer[rfid_pos] = UARTCharGetNonBlocking(UART0_BASE);
rfid_pos++;
}
}
// Make sure the RFID flag is set after the buffer is full
if(16 == rfid_pos)
{
Flags.rfid_buf_full = 0x1;
}
#ifdef DEBUG
while (UARTSpaceAvail(UART0_BASE) && TxBuffU0.start != TxBuffU0.end)
UARTCharPut(UART0_BASE, pullRB(&TxBuffU0));
#endif
}

57

void UART1IntHandler(void)
{
unsigned long ulStatus;
char newbyte;
// Get the interrrupt status.
ulStatus = UARTIntStatus(UART1_BASE, true);
// Clear the asserted interrupts.
UARTIntClear(UART1_BASE, ulStatus);
if (UARTCharsAvail(UART1_BASE))
{
Flags.rx_new = 1;
do // Loop while there are characters in the receive FIFO.
{
newbyte = UARTCharGetNonBlocking(UART1_BASE);
pushRB(newbyte, &RxBuff);
}
while(UARTCharsAvail(UART1_BASE));
}
}
// Debounce the select button: set a flag when it is pressed.
#define BUT_TICKS_PER_POLL ((TICK_RATE / BUT_POLL_RATE) == 0 ? 1 : (TICK_RATE / BUT_POLL_RATE) )
#define BUT_HIST_LENGTH ((BUT_DEAD_TIME * TICK_RATE) / (BUT_TICKS_PER_POLL * 1000 ))
#define BUT_HIST_MASK (0xffffffff << (BUT_HIST_LENGTH > 32 ? 32 : BUT_HIST_LENGTH + 1) )
void DebounceSelect(void)
{
// 0 == button pressed
// The most recent state of the switch is the LSB
static uint32_t history = 0;
if ((g_ulTickCount % BUT_TICKS_PER_POLL) == 0)
{
history = (history << 1) | HWREGBITB(GPIO_PORTF_BASE + ADDR_MASK, 1);
// In order to trigger a button pressed flag, the current reading
// should be 0 and all the previous readings should be 1.
if ((history | BUT_HIST_MASK) == 0xfffffffe )
{
Flags.but_press = 1;
}
}
}
// Write status information to the screen
void PrintStatus(void)
{
WakeScreenSaver();
#ifdef DEBUG
PrintLine(__FILE__);
PrintLine( __TIME__ " " __DATE__);
PrintLine("UART0: " STR(UART0_BAUD) " 8n1");
PrintLine("UART1: " STR(UART1_BAUD) " 8n1");
#else
PrintLine("----> SafeLock <----");
PrintLine("Firmware version:");
PrintLine( __TIME__ " " __DATE__);
#endif
}
// Inform the screen saver of activity which should prevent the screensaver
// from activating;
void WakeScreenSaver(void)
{
if (!Flags.screen_active)
{
Flags.screen_active = true;
WriteScreen();
}

58

ClearCount = g_ulTickCount + SCREENSAVER_TIMEOUT * TICK_RATE;

// Turn the screen off if nothing has happened for a while.


void ScreenSaverCheck(void)
{
if ( Flags.screen_active && g_ulTickCount >= ClearCount )
{
// clear the screen
RIT128x96x4Clear();
Flags.screen_active = false;
}
}
// The System Tick interrupt handler is called every 1/TICK_RATE seconds. The
// interrupt is cleared automatically by hardware.
void SysTickIntHandler(void)
{
g_ulTickCount++;
DebounceSelect();
// create a 25 Hz square wave output on PC5 for the random number generator
if (g_ulTickCount >= RNGcount)
{
//TimerLoadSet(TIMER0_BASE, TIMER_A, 1500000);
HWREGBITB(GPIO_PORTC_BASE + ADDR_MASK, 5) ^= 1;
RNGcount = g_ulTickCount + TICK_RATE/(25*2);
}
}
// When the cap on PC7 charges up/down (in response to the change on PC5) it
// creates a interrupt. The exact time required for this to happen varies
// randomly (around a mean value), so the lower bits of the timer can be used
// to generate random numbers.
void GPIOCIntHandler(void)
{
long status;
status = GPIOPinIntStatus(GPIO_PORTC_BASE, true);
GPIOPinIntClear(GPIO_PORTC_BASE, status);
//usnprintf(debug_msg, sizeof debug_msg, "%d\r\n", 0xff & TimerValueGet(TIMER0_BASE, TIMER_A));
//UART0StringSend(debug_msg);
if (spaceRB(&EntropyRB) > 0)
pushRB(0xff & TimerValueGet(TIMER0_BASE, TIMER_A), &EntropyRB);
}
void ChangeLockState(bool lock)
{
if (lock)
{
PWM_Change(true);
Flags.is_door_locked = true;
UART1StringSend("<STATUS:LOCKED>");
}
else
{
PWM_Change(false);
Flags.is_door_locked = false;
UART1StringSend("<STATUS:UNLOCKED>");
}
}
void TestUserDB(void);
int main(void)
{
volatile unsigned long i;
unsigned char parse_result;
// Set the clock to run at 50 MHz

59

//SysCtlClockSet(SYSCTL_OSC_MAIN | SYSCTL_XTAL_8MHZ | SYSCTL_USE_PLL | SYSCTL_SYSDIV_4);


// Set the clock to run at 8 MHz
SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN | SYSCTL_XTAL_8MHZ);
// Initialize the OLED display
RIT128x96x4Init(1000000);
PrintStatus();
pwm_init();
ParamBlockInit();
CheckForAdmin();
// Enable the peripherals used
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
// Setup GPIO pin for select button
GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, GPIO_PIN_1);
GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_STRENGTH_2MA,
GPIO_PIN_TYPE_STD_WPU);
// Enable the GPIO pin for the LEDs (PF0,2,3). Set the direction as
// output, and enable the GPIO pin for digital function.
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3);
GPIOPadConfigSet(GPIO_PORTF_BASE, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3, GPIO_STRENGTH_2MA,
GPIO_PIN_TYPE_STD_WPU);
// Setup PC5 as an output for random number generation
GPIOPinTypeGPIOOutput(GPIO_PORTC_BASE, GPIO_PIN_5);
GPIOPadConfigSet(GPIO_PORTC_BASE, GPIO_PIN_5, GPIO_STRENGTH_2MA,
GPIO_PIN_TYPE_STD_WPU);
// Setup PC7 as an input for random number generation
GPIOPinTypeGPIOInput(GPIO_PORTC_BASE, GPIO_PIN_7);
GPIOPadConfigSet(GPIO_PORTC_BASE, GPIO_PIN_7, GPIO_STRENGTH_2MA,
GPIO_PIN_TYPE_STD);
GPIOIntTypeSet(GPIO_PORTC_BASE, GPIO_PIN_7, GPIO_BOTH_EDGES);
GPIOPinIntEnable(GPIO_PORTC_BASE,GPIO_PIN_7);
IntEnable(INT_GPIOC);
// Set GPIO A0 and A1 as UART pins.
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
GPIOPinTypeUART(GPIO_PORTD_BASE, GPIO_PIN_2 | GPIO_PIN_3);
// Configure UART0 for x baud 8-N-1 operation.
UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), UART0_BAUD,
(UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
UART_CONFIG_PAR_NONE));
// Configure UART1 for x baud 8-N-1 operation.
UARTConfigSetExpClk(UART1_BASE, SysCtlClockGet(), UART1_BAUD,
(UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
UART_CONFIG_PAR_NONE));
// Enable the UART interrupt.
IntEnable(INT_UART0);
UARTIntEnable(UART0_BASE, UART_INT_RX | UART_INT_RT | UART_INT_TX);
IntEnable(INT_UART1);
UARTIntEnable(UART1_BASE, UART_INT_RX | UART_INT_RT);
TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC);

60

TimerEnable(TIMER0_BASE, TIMER_A);
// Enable processor interrupts.
IntMasterEnable();
// Configure SysTick to periodically interrupt.
SysTickPeriodSet(SysCtlClockGet() / TICK_RATE);
SysTickIntEnable();
SysTickEnable();
//pb.text = "Testin writing to flash: 1 2 3 ... :)\r\n";
//ustrncpy(pb.text, "Testin writing to flash: 1 2 3 ... :)\r\n", 100);
//FlashPBInit(0x30000, 0x30800, 128);
//FlashPBSave(&pb);
//pbloc = FlashPBGet();
//memcpy(pbloc, &pb, sizeof pb);
//UART0StringSend("\r\nThe initialized value of pb.text:\r\n");
//UART0StringSend(pb.text);
//FlashPBInit(0x30000, 0x30800, 128);
//pbloc = FlashPBGet();
//for (i = 0; i < sizeof pb; i++)
// ((unsigned char*)&pb)[i] = pbloc[i];
//UART0StringSend("\r\nNow from flash:\r\n");
//UART0StringSend(pb.text);
#ifdef DEBUG
// Explain the facts.
UART0StringSend("This UART can be used for output, but the input is from the RFID.\r\n");
// turn on the amber ethernet LED (because we can!)
HWREGBITB(GPIO_PORTF_BASE + ADDR_MASK, 3) = 0;
#endif
// Loop forever echoing data through the UART.
while(true)
{
if (Flags.but_press)
{
Flags.but_press = 0;
WakeScreenSaver();
#ifdef DEBUG
// Toggle the green ethernet LED (PF2).
HWREGBITB(GPIO_PORTF_BASE + ADDR_MASK, 2) ^= 1;
TestUserDB();
#endif
}
else
{
ScreenSaverCheck();
}
if (Flags.rx_new)
{
Flags.rx_new = 0;
if (RxBuff.start != RxBuff.end)
{
if (Flags.invalid_wait && g_ulTickCount < InvalidCount)
{
do
{
pullRB(&RxBuff);
}
while(RxBuff.start != RxBuff.end);
InvalidCount = g_ulTickCount + (INVALID_TIMEOUT * TICK_RATE)/1000;

61

}
else
{
Flags.invalid_wait = false;
parse_result = Parse(Flags.reset_parser);
Flags.reset_parser = false;
switch(parse_result)
{
case PARSER_BUSY:
break;
case PARSER_INVALID:
//UART1StringSend("<INVALID>");
WakeScreenSaver();
PrintLine("Invalid command");
Flags.reset_parser = true;
Flags.invalid_wait = true;
InvalidCount = g_ulTickCount + (INVALID_TIMEOUT * TICK_RATE)/1000;
break;
case PARSER_BAD_RN:
UART1StringSend("<BAD_RN>");
WakeScreenSaver();
PrintLine("Bad session token");
Flags.reset_parser = true;
Flags.invalid_wait = true;
InvalidCount = g_ulTickCount + (INVALID_TIMEOUT * TICK_RATE)/1000;
break;
case PARSER_BAD_AUTH:
UART1StringSend("<BAD_AUTH>");
WakeScreenSaver();
PrintLine("Bad authentication");
Flags.reset_parser = true;
Flags.invalid_wait = true;
InvalidCount = g_ulTickCount + (INVALID_TIMEOUT * TICK_RATE)/1000;
break;
case PARSER_BEGIN:
for (i = 0; i < sizeof currentRN; i++)
{
// wait for it if necessary
while (EntropyRB.start == EntropyRB.end);
currentRN[i] = pullRB(&EntropyRB);
}
UART1StringSend("<BEGINACK:");
if (Flags.is_door_locked)
UART1StringSend("LOCKED:");
else
UART1StringSend("UNLOCKED:");
UART1ArraySend(currentRN, sizeof currentRN);
UART1StringSend(">");
#ifdef DEBUG

#endif

//UART0StringSend("Sent the RN:");


//UART0ArraySend(currentRN, sizeof currentRN);
//UART0StringSend(":ENDRN");
Flags.reset_parser = true;
break;
case PARSER_UNLOCK:
ChangeLockState(false);
WakeScreenSaver();
PrintLine("Your will prevails.");
Flags.reset_parser = true;
break;

62

case PARSER_LOCK:
ChangeLockState(true);
WakeScreenSaver();
PrintLine("Barred, the way is.");
Flags.reset_parser = true;
break;
case PARSER_TOGGLE:
// Toggle the ethernet LED (PF2).
HWREGBITB(GPIO_PORTF_BASE + ADDR_MASK, 2) ^= 1;
Flags.reset_parser = true;
break;
case PARSER_ADD_USER:
if (StoreUserRec(&newUR))
{
UART1StringSend("<STATUS:ADD_SUCCESS>");
}
else
{
UART1StringSend("<STATUS:ADD_FAILED>");
}
Flags.reset_parser = true;
break;
case PARSER_RM_USER:
if (RemoveUserRec(newUR.name, &newUR.pub_key))
{
UART1StringSend("<STATUS:REMOVE_SUCCESS>");
}
else
{
UART1StringSend("<STATUS:REMOVE_FAILED>");
}
Flags.reset_parser = true;
break;
default:
ASSERT(false); // should never get here
}
}

}
// Check to see if a RFID has been read.
if(Flags.rfid_buf_full)
// Clear the flag and i for the buffer
Flags.rfid_buf_full = 0;
rfid_pos = 0x0;
// Validate the Card ID
if(is_rfid_valid(RFIDbuffer)){
// If RFID is valid
if(Flags.is_door_locked)
{
// Door is locked, so unlock the door
ChangeLockState(false);
}else
{
// Door is unlocked, so lock the door
ChangeLockState(true);
}
PrintLine("RFID: Valid");
}else{
// If RFID is invalid
PrintLine("RFID: No Losers Allowed");
}
// Clear the rfid buffer

63

clearRFIDBuff();

#ifdef DEBUG
while (UARTSpaceAvail(UART0_BASE) && TxBuffU0.start != TxBuffU0.end)
UARTCharPutNonBlocking(UART0_BASE, pullRB(&TxBuffU0));
ErrorFlush();
#endif
SysCtlSleep();
}
}

Appendix B2 (parser.c)
#define LTM_DESC
#include <stdbool.h>
#include "utils/ustdlib.h"
#include "parser.h"
#include "utils.h"
#include "global.h"
#include "driverlib/debug.h"
#include <tomcrypt.h>
#include <tommath.h>
#include "userdb.h"
extern RingBuffer RxBuff;
#ifdef DEBUG
extern RingBuffer TxBuffU0;
extern char debug_msg[SCREEN_COLS + 1];
#endif
hash_state md;
unsigned char hash[20];
char username[MAX_USER_LENGTH + 1] = "default";
extern unsigned char currentRN[RANDOM_NO_LENGTH];
unsigned char signature[SIGNATURE_SIZE];
hash_state md;
ltc_math_descriptor ltc_mp;
UserRecord newUR = { {0}, 0, {{0}}};
// Parse a begin command
static bool pBegin(unsigned char* result, bool reset)
{
// we expect to read in "EGIN"
static const unsigned char match[] = {'E', 'G', 'I', 'N'};
static size_t pos = 0;
if (reset)
pos = 0;
do
{
if (match[pos++] != pullRB(&RxBuff))
{
*result = PARSER_INVALID;
return false;
}

}
while (RxBuff.start != RxBuff.end && pos < sizeof match);
if (pos == sizeof match)
{
*result = PARSER_BEGIN;
return true;
}
else
{
*result = PARSER_BUSY;

64

return false;

}
// Parse a toggle command
static bool pToggle(unsigned char* result, bool reset)
{
// we expect to read in "OGGLE"
static const unsigned char match[] = {'O', 'G', 'G', 'L', 'E'};
static size_t pos = 0;
if (reset)
pos = 0;
do
{
if (match[pos++] != pullRB(&RxBuff))
{
*result = PARSER_INVALID;
return false;
}

}
while (RxBuff.start != RxBuff.end && pos < sizeof match);
if (pos == sizeof match)
{
*result = PARSER_TOGGLE;
return true;
}
else
{
*result = PARSER_BUSY;
return false;
}
}
// Parse the username field and store it in name.
static bool pUsername(unsigned char* result, bool reset, char* name)
{
static size_t pos = 0;
static struct {
bool name_done:1;
} pf = { false };
if (reset)
{
pos = 0;
pf.name_done = false;
}
do
{
if (pos < MAX_USER_LENGTH)
{
name[pos] = pullRB(&RxBuff);
// check for the delimiter
if (name[pos] == ':')
{
sha1_process(&md, (unsigned char*)&(name[pos]), 1);
name[pos++] = '\0';
pf.name_done = true;
break;
}
// make sure it is a normal printable ASCII character
else if ( name[pos] >= 32 && name[pos] <= '~' )
{
sha1_process(&md, (unsigned char*)&(name[pos++]), 1);
}
else

65

PrintLine("user bad");
*result = PARSER_INVALID;
return false;

}
}
// only one valid posibility at this point
else if (pullRB(&RxBuff) == ':')
{
sha1_process(&md, (unsigned char*)":", 1);
name[pos++] = '\0';
pf.name_done = true;
break;
}
else
{
*result = PARSER_INVALID;
return false;
}

}
while (RxBuff.start != RxBuff.end);
if (pf.name_done)
{
*result = PARSER_NAME_DONE;
return true;
}
else
{
*result = PARSER_BUSY;
return false;
}
}

// Parse the random number field and verify is the correct number
static bool pRandomNumber(unsigned char* result, bool reset)
{
static size_t pos = 0;
if (reset)
pos = 0;
if (pos < RANDOM_NO_LENGTH)
{
do
{
if (currentRN[pos] != pullRB(&RxBuff))
{
*result = PARSER_BAD_RN;
return false;
}
else
{
sha1_process(&md, &(currentRN[pos++]), 1);
}
}
while (RxBuff.start != RxBuff.end && pos < RANDOM_NO_LENGTH);
}
if (RxBuff.start != RxBuff.end)
{
if (pullRB(&RxBuff) == ':')
{
*result = PARSER_RN_DONE;
return true;
}
else
{

66

PrintLine("RN bad");
*result = PARSER_INVALID;
return false;

}
*result = PARSER_BUSY;
return false;
}
// Parse the signature field and verify the signature
static bool pSignature(unsigned char* result, bool reset)
{
static size_t pos = 0;
int verified, err;
ltc_mp = ltm_desc;
size_t matches, i, flash_idx;
static rsa_key key;
static mp_int e, N;
if (reset)
pos = 0;
do
{

signature[pos++] = pullRB(&RxBuff);
}
while (RxBuff.start != RxBuff.end && pos < SIGNATURE_SIZE);
if ( pos == SIGNATURE_SIZE )
{
// find user records matching the name
matches = FindMatchingUsers(username);
usnprintf(debug_msg, sizeof debug_msg, "Found %d matches. ", matches);
PrintLine(debug_msg);
// Create the key object
key.type = PK_PUBLIC;
key.e = &e;
key.N = &N;
mp_init_multi(key.e, key.N, NULL);
mp_set_int(key.e, EXPONENT);
for (i = matches; i > 0; i--)
{
flash_idx = GetNextMatchIdx();
mp_read_unsigned_bin(key.N, FLASH_IDX_TO_URP(flash_idx)->pub_key.C, SIGNATURE_SIZE);
if (register_hash(&sha1_desc) != CRYPT_OK) {
ASSERT(false);
}
err =
rsa_verify_hash_ex( signature, SIGNATURE_SIZE, hash, 20,
LTC_LTC_PKCS_1_V1_5, find_hash("sha1"),
SALT_SIZE, &verified, &key );
if (err != CRYPT_OK)
PrintLine("verify returned error");

if (verified)
{
*result = PARSER_SIG_DONE;
return true;
}

67

PrintLine("not verified");

*result = PARSER_BAD_AUTH;
return false;

}
else
{
*result = PARSER_BUSY;
return false;
}

// Parse a lock command


static bool pLock(unsigned char* result, bool reset)
{
static const unsigned char match[] = {'O', 'C', 'K', ':'};
static struct {
bool reset_name_parser:1;
bool reset_RN_parser:1;
bool reset_signature_parser:1;
bool name_parsed:1;
bool RN_parsed:1;
bool signature_parsed:1;
bool success:1;
} pf = { false, false, false, false, false, false, false };
static size_t pos = 0;
if (reset)
{
pf.reset_name_parser = true;
pf.reset_RN_parser = true;
pf.reset_signature_parser = true;
pf.name_parsed = false;
pf.RN_parsed = false;
pf.signature_parsed = false;
pos = 0;
sha1_init(&md);
sha1_process(&md, (unsigned char*)"L", 1);
}
do
{

if (pos < sizeof match)


{
if (match[pos] != pullRB(&RxBuff))
{
PrintLine("command bad");
*result = PARSER_INVALID;
return false;
}
else
{
sha1_process(&md, &(match[pos++]), 1);
}
}
else if (!pf.name_parsed)
{
pf.success = pUsername(result, pf.reset_name_parser, username);
pf.reset_name_parser = false;
if (pf.success)
pf.name_parsed = true;
else
return false;
}
else if (!pf.RN_parsed)
{
pf.success = pRandomNumber(result, pf.reset_RN_parser);
pf.reset_RN_parser = false;

68

if (pf.success)
{
pf.RN_parsed = true;
sha1_done(&md, hash);
}
else
return false;

}
else if (!pf.signature_parsed)
{
pf.success = pSignature(result, pf.reset_signature_parser);
pf.reset_signature_parser = false;
if (pf.success)
pf.signature_parsed = true;
else
return false;
}
}
while (RxBuff.start != RxBuff.end && !pf.signature_parsed);

if (pf.signature_parsed)
{
*result = PARSER_LOCK;
return true;
}
else
{
*result = PARSER_BUSY;
return false;
}

// Parse an unlock command


static bool pUnlock(unsigned char* result, bool reset)
{
static const unsigned char match[] = {'N', 'L', 'O', 'C', 'K', ':'};
static struct {
bool reset_name_parser:1;
bool reset_RN_parser:1;
bool reset_signature_parser:1;
bool name_parsed:1;
bool RN_parsed:1;
bool signature_parsed:1;
bool success:1;
} pf = { false, false, false, false, false, false, false };
static size_t pos = 0;
if (reset)
{
pf.reset_name_parser = true;
pf.reset_RN_parser = true;
pf.reset_signature_parser = true;
pf.name_parsed = false;
pf.RN_parsed = false;
pf.signature_parsed = false;
pos = 0;
sha1_init(&md);
sha1_process(&md, (unsigned char*)"U", 1);
}
do
{

if (pos < sizeof match)


{
if (match[pos] != pullRB(&RxBuff))
{
PrintLine("command bad");
*result = PARSER_INVALID;

69

return false;
}
else
{
sha1_process(&md, &(match[pos++]), 1);
}
}
else if (!pf.name_parsed)
{
pf.success = pUsername(result, pf.reset_name_parser, username);
pf.reset_name_parser = false;
if (pf.success)
pf.name_parsed = true;
else
return false;
}
else if (!pf.RN_parsed)
{
pf.success = pRandomNumber(result, pf.reset_RN_parser);
pf.reset_RN_parser = false;
if (pf.success)
{
pf.RN_parsed = true;
sha1_done(&md, hash);
}
else
return false;
}
else if (!pf.signature_parsed)
{
pf.success = pSignature(result, pf.reset_signature_parser);
pf.reset_signature_parser = false;
if (pf.success)
pf.signature_parsed = true;
else
return false;
}
}
while (RxBuff.start != RxBuff.end && !pf.signature_parsed);
if (pf.signature_parsed)
{
//UART0StringSend("\r\nThe sha1 digest is:\r\n");
//for (i = 0; i < 20; i++) {
//usnprintf(debug_msg, sizeof(debug_msg), "%02x", hash[i]);
//UART0ArraySend((unsigned char*)debug_msg, 2);
//}
//UART0StringSend("\r\n");
//UART0ArraySend(hash, 20);
//UART0StringSend("\r\n");
*result = PARSER_UNLOCK;
return true;
}
else
{
*result = PARSER_BUSY;
return false;
}
}
// Parse the new key field
static bool pKey(unsigned char* result, bool reset, PublicKey* newkey)
{
static size_t pos = 0;
static struct {
bool gotkey:1;
bool gotzero:1;

70

} pf;
if (reset)
{
pos = 0;
pf.gotkey = false;
pf.gotzero = false;
}
do
{

if (!pf.gotzero)
{
if (pullRB(&RxBuff) == 0)
{
pf.gotzero = true;
sha1_process(&md, (unsigned char*)&pos, 1);
}
else
{
*result = PARSER_INVALID;
return false;
}
}
else if (pos < SIGNATURE_SIZE)
{
newkey->C[pos] = pullRB(&RxBuff);
sha1_process(&md, &(newkey->C[pos++]), 1);
}
else
{
pf.gotkey = true;
if (pullRB(&RxBuff) == ':')
{
sha1_process(&md, (unsigned char*)":", 1);
*result = PARSER_KEY_DONE;
return true;
}
else
{
*result = PARSER_INVALID;
return false;
}
}

}
while (RxBuff.start != RxBuff.end && !pf.gotkey);

*result = PARSER_BUSY;
return false;

// Parse the permission field


static bool pPerms(unsigned char* result, bool reset, unsigned char* perm)
{
static struct {
bool perm_done:1;
} pf = { false };
if (reset)
{
pf.perm_done = false;
}
do
{

if (!pf.perm_done)
{
*perm = pullRB(&RxBuff);
sha1_process(&md, perm, 1);

71

pf.perm_done = true;
}
else if (pullRB(&RxBuff) == ':')
{
sha1_process(&md, (unsigned char*)":", 1);
*result = PARSER_PERMS_DONE;
return true;
}
else
{
*result = PARSER_INVALID;
return false;
}

}
while (RxBuff.start != RxBuff.end);
*result = PARSER_BUSY;
return false;
}

// Parse an ADD command


static bool pAdd(unsigned char* result, bool reset)
{
static const unsigned char match[] = {'D', 'D', ':'};
static struct {
bool reset_Aname_parser:1;
bool reset_Uname_parser:1;
bool reset_key_parser:1;
bool reset_perms_parser:1;
bool reset_RN_parser:1;
bool reset_signature_parser:1;
bool Aname_parsed:1;
bool Uname_parsed:1;
bool key_parsed:1;
bool perms_parsed:1;
bool RN_parsed:1;
bool signature_parsed:1;
bool success:1;
} pf;
static size_t pos = 0;
//static char* newuser[MAX_USER_LENGTH + 1];
//static unsigned char newkey[SIGNATURE_SIZE];
if (reset)
{
pf.reset_Aname_parser = true;
pf.reset_Uname_parser = true;
pf.reset_key_parser = true;
pf.reset_perms_parser = true;
pf.reset_RN_parser = true;
pf.reset_signature_parser = true;
pf.Aname_parsed = false;
pf.Uname_parsed = false;
pf.key_parsed = false;
pf.perms_parsed = false;
pf.RN_parsed = false;
pf.signature_parsed = false;
pos = 0;
sha1_init(&md);
sha1_process(&md, (unsigned char*)"A", 1);
}
do
{
if (pos < sizeof match)
{
if (match[pos] != pullRB(&RxBuff))
{
PrintLine("command bad");

72

*result = PARSER_INVALID;
return false;
}
else
{
sha1_process(&md, &(match[pos++]), 1);
}

}
else if (!pf.Aname_parsed)
{
pf.success = pUsername(result, pf.reset_Aname_parser, username);
pf.reset_Aname_parser = false;
if (pf.success)
{
// TODO: check that username is a valid admin
pf.Aname_parsed = true;
}
else
return false;
}
else if (!pf.Uname_parsed)
{
pf.success = pUsername(result, pf.reset_Uname_parser, newUR.name);
pf.reset_Uname_parser = false;
if (pf.success)
{
pf.Uname_parsed = true;
}
else
return false;
}
else if (!pf.key_parsed)
{
pf.success = pKey(result, pf.reset_key_parser, &newUR.pub_key.C);
pf.reset_key_parser = false;
if (pf.success)
{
pf.key_parsed = true;
}
else
return false;
}
else if (!pf.perms_parsed)
{
pf.success = pPerms(result, pf.reset_perms_parser, &newUR.perm);
pf.reset_perms_parser = false;
if (pf.success)
{
pf.perms_parsed = true;
}
else
return false;
}
else if (!pf.RN_parsed)
{
pf.success = pRandomNumber(result, pf.reset_RN_parser);
pf.reset_RN_parser = false;
if (pf.success)
{
pf.RN_parsed = true;
sha1_done(&md, hash);
}
else
return false;
}
else if (!pf.signature_parsed)
{
pf.success = pSignature(result, pf.reset_signature_parser);
pf.reset_signature_parser = false;

73

if (pf.success)
pf.signature_parsed = true;
else
return false;
}
}
while (RxBuff.start != RxBuff.end && !pf.signature_parsed);

if (pf.signature_parsed)
{
*result = PARSER_ADD_USER;
return true;
}
else
{
*result = PARSER_BUSY;
return false;
}

// Parse an REMOVE command


static bool pRemove(unsigned char* result, bool reset)
{
static const unsigned char match[] = {'E', 'M', 'O', 'V', 'E', ':'};
static struct {
bool reset_Aname_parser:1;
bool reset_Uname_parser:1;
bool reset_key_parser:1;
bool reset_RN_parser:1;
bool reset_signature_parser:1;
bool Aname_parsed:1;
bool Uname_parsed:1;
bool key_parsed:1;
bool perms_parsed:1;
bool RN_parsed:1;
bool signature_parsed:1;
bool success:1;
} pf;
static size_t pos = 0;
if (reset)
{
pf.reset_Aname_parser = true;
pf.reset_Uname_parser = true;
pf.reset_key_parser = true;
pf.reset_RN_parser = true;
pf.reset_signature_parser = true;
pf.Aname_parsed = false;
pf.Uname_parsed = false;
pf.key_parsed = false;
pf.RN_parsed = false;
pf.signature_parsed = false;
pos = 0;
sha1_init(&md);
sha1_process(&md, (unsigned char*)"R", 1);
}
do
{
if (pos < sizeof match)
{
if (match[pos] != pullRB(&RxBuff))
{
PrintLine("command bad");
*result = PARSER_INVALID;
return false;
}
else

74

sha1_process(&md, &(match[pos++]), 1);

}
else if (!pf.Aname_parsed)
{
pf.success = pUsername(result, pf.reset_Aname_parser, username);
pf.reset_Aname_parser = false;
if (pf.success)
{
// TODO: check that username is a valid admin
pf.Aname_parsed = true;
}
else
return false;
}
else if (!pf.Uname_parsed)
{
pf.success = pUsername(result, pf.reset_Uname_parser, newUR.name);
pf.reset_Uname_parser = false;
if (pf.success)
{
pf.Uname_parsed = true;
}
else
return false;
}
else if (!pf.key_parsed)
{
PrintLine("Made it here");
pf.success = pKey(result, pf.reset_key_parser, &newUR.pub_key.C);
pf.reset_key_parser = false;
if (pf.success)
{
pf.key_parsed = true;
}
else
return false;
}
else if (!pf.RN_parsed)
{
pf.success = pRandomNumber(result, pf.reset_RN_parser);
pf.reset_RN_parser = false;
if (pf.success)
{
pf.RN_parsed = true;
sha1_done(&md, hash);
}
else
return false;
}
else if (!pf.signature_parsed)
{
pf.success = pSignature(result, pf.reset_signature_parser);
pf.reset_signature_parser = false;
if (pf.success)
pf.signature_parsed = true;
else
return false;
}
}
while (RxBuff.start != RxBuff.end && !pf.signature_parsed);
if (pf.signature_parsed)
{
*result = PARSER_RM_USER;
return true;
}

75

else
{
*result = PARSER_BUSY;
return false;
}

// Parse the incoming bytes, possibly set some flags/globals, then return a
// status indicator. Assumes there is at least one byte to be read in RxBuff.
// If reset is true, all parser logic is reset.
unsigned char Parse(bool reset)
{
static struct {
bool command_started:1; // we got a '<'
bool command_parsed:1; // we got everything but the '>'
bool command_completed:1; // we got the final '>'
bool reset_func:1;
// reset subsidiary functions
bool success:1;
} pf = {false, false, false, false};
// A set of subsidiary functions will handle the inner parts of the
// commands. They will store a result in the first parameter, and will
// return true if the parsing has completed successfully. If false is
// returned the parsing has failed or is incomplete and the result should
// be immediately returned by Parse() without further processing. If the
// second command is true, the parser logic will be reset.
static bool (*switched_func)(unsigned char*, bool) = NULL;
static unsigned char result;
if (reset)
{
pf.reset_func = true;
pf.command_started = false;
pf.command_parsed = false;
pf.command_completed = false;
switched_func = NULL;
}
// Keep parsing as long as there's stuff in the buffer, but if the command
// is invalid we will break or return.
do
{
if (!pf.command_started)
{
if (pullRB(&RxBuff) == '<')
{
pf.command_started = true;
}
else
{
return PARSER_INVALID;
}
}
else if (!pf.command_parsed)
{
if (switched_func == NULL)
{
// Determine command type
switch(pullRB(&RxBuff))
{
case 'B': // a BEGIN command
switched_func = pBegin;
break;
case 'T': // a TOGGLE command
switched_func = pToggle;
break;

76

case 'U': // a UNLOCK command


switched_func = pUnlock;
break;
case 'L': // a LOCK command
switched_func = pLock;
break;
case 'A': // a ADD command
switched_func = pAdd;
break;
case 'R': // a REMOVE command
switched_func = pRemove;
break;
default:
return PARSER_INVALID;

}
}
else
{
pf.success = switched_func(&result, pf.reset_func);
pf.reset_func = false;
if (pf.success)
pf.command_parsed = true;
else
return result;
}
}
else if (pullRB(&RxBuff) == '>')
pf.command_completed = true;
else
return PARSER_INVALID;

}
while (RxBuff.start != RxBuff.end && !pf.command_completed);
if (pf.command_completed)
return result;
else
return PARSER_BUSY;
}
void TestUserDB(void)
{
size_t i, j;
UserRecord ur = {"david", 0x11, {{ 0xf9, 0xe8, 0x73, 0x61, 0x13, 0x72, 0xfd,
0x56, 0xfa, 0xdb, 0x1b, 0xb1, 0x58, 0x1c, 0x33, 0x84, 0xca, 0x65, 0x80,
0x67, 0x3b, 0x55, 0x9c, 0x0f, 0x79, 0xd1, 0xbb, 0xaf, 0xc5, 0x18, 0x00,
0xe8, 0x1b, 0xd9, 0x83, 0x02, 0x10, 0x6b, 0xfc, 0x3c, 0x34, 0xc1, 0xd1,
0x02, 0x04, 0x43, 0xc5, 0xe1, 0xc0, 0xf9, 0xdf, 0xee, 0x80, 0xbe, 0x0b,
0x53, 0xbf, 0x58, 0xc9, 0x6e, 0x45, 0xcd, 0x4e, 0x69, 0x11, 0xf0, 0x8b,
0xd6, 0xc9, 0xd5, 0x34, 0x17, 0x6c, 0x86, 0x44, 0x8c, 0xb2, 0x56, 0x53,
0x67, 0xee, 0x86, 0x57, 0xb4, 0x9d, 0x74, 0xe7, 0xbc, 0xee, 0x2b, 0x91,
0xef, 0xc4, 0x4e, 0xc3, 0xfe, 0x18, 0x95, 0xf5, 0x09, 0xdd, 0x84, 0x66,
0xc8, 0x35, 0x97, 0xc4, 0xc2, 0x80, 0x3e, 0x58, 0xf6, 0xf2, 0xc7, 0xfe,
0x3b, 0x5a, 0x52, 0xe6, 0x4c, 0x36, 0x68, 0xcc, 0x0d, 0xc5, 0x9e, 0xb7,
0xf1 }}};
//UserRecord ur = {"sam", 0x11, {{ 0xe2, 0xa5, 0xf6, 0xd5, 0x14, 0xcd, 0x96,
// 0xda, 0xfa, 0xe1, 0x9b, 0x6d, 0xb4, 0x2e, 0xce, 0xa4, 0x4f, 0xa2, 0x0c,
// 0x77, 0xbf, 0xcf, 0x08, 0xe6, 0x3d, 0x1c, 0x22, 0xd5, 0xad, 0xba, 0xae,
// 0x97, 0xab, 0x38, 0xb2, 0xa5, 0x47, 0xed, 0x75, 0x4d, 0xf4, 0x49, 0x00,
// 0x68, 0xdd, 0x78, 0x47, 0x4f, 0x6c, 0xa5, 0x4c, 0xf2, 0x15, 0x66, 0xf3,
// 0xdd, 0xd8, 0x52, 0x91, 0x30, 0x61, 0xf0, 0x8d, 0xd0, 0x41, 0x01, 0x57,
// 0xcc, 0xb1, 0xa3, 0x15, 0x29, 0x25, 0x23, 0x5c, 0x71, 0xfc, 0x65, 0xc9,
// 0xa3, 0xc8, 0xd9, 0x23, 0x0d, 0x29, 0x10, 0xd4, 0xc1, 0xe4, 0x2e, 0x95,
// 0x57, 0xff, 0x30, 0x37, 0x62, 0x8e, 0xeb, 0x94, 0xde, 0x03, 0x61, 0x68,

77

//
//
//

0x5c, 0x77, 0x13, 0x83, 0xa0, 0x31, 0x94, 0x41, 0x09, 0x59, 0x30, 0xa9,
0x8b, 0x55, 0x6c, 0x0d, 0xb6, 0x14, 0xc7, 0xf1, 0x45, 0xd0, 0x2f, 0x1c,
0xfd }}};

//if (!RemoveUserRec("sam", &ur.pub_key))


//UART0StringSend("remove failed! ");
//else
//UART0StringSend("remove succeeded ");
//if (!StoreUserRec(&ur))
//UART0StringSend("store failed! ");
//else
//UART0StringSend("store succeeded ");
const UserRecord* rec_flash_loc;
size_t matches, flash_idx;
long num;
matches = FindMatchingUsers("Syrip");
usnprintf(debug_msg, sizeof debug_msg, "Found %d matches. ", matches);
UART0StringSend(debug_msg);
for (i = matches; i > 0; i--)
{
flash_idx = GetNextMatchIdx();
usnprintf(debug_msg, sizeof debug_msg, "Match at %d: ", flash_idx);
UART0StringSend(debug_msg);
rec_flash_loc = FLASH_IDX_TO_URP(flash_idx);
UART0StringSend(rec_flash_loc->name);
//UART0ArraySend(&(rec_flash_loc->perm), 1);
pushRB(rec_flash_loc->perm, &TxBuffU0);
//UART0ArraySend((unsigned char*)&rec_flash_loc->pub_key.C,
//
sizeof(rec_flash_loc->pub_key.C));
for (j = 0; j < sizeof(rec_flash_loc->pub_key.C); j++)
pushRB(rec_flash_loc->pub_key.C[j], &TxBuffU0);
}

usnprintf(debug_msg, sizeof debug_msg, "PB size: %d. ", sizeof(ParameterBlock));


UART0StringSend(debug_msg);

Appendix B3 (pwm.c)
//
//
//
//

Functions for setting up the PWM module and using it to lock or unlock the
door.
Author: Mark Rathjen
Modified by David Chamberlain December 6, 2012

#include
#include
#include
#include
#include
#include
#include
#include

<stdbool.h>
"global.h"
"inc/hw_types.h"
"driverlib/pwm.h"
"inc/hw_gpio.h"
"driverlib/gpio.h"
"inc/hw_memmap.h"
"driverlib/sysctl.h"

unsigned long upper_period, lower_period, inc_period, current_period, ulPeriod;


// Initialize the pwm
void pwm_init(void) {
// Set up the clock to be used by the pwm
SysCtlPWMClockSet(SYSCTL_PWMDIV_2);
//
// Enable the peripherals used by this example.
//
SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);

78

//
// Set GPIO F0 and G1 as PWM pins. They are used to output the PWM0 and
// PWM1 signals.
//
GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_0);
GPIOPinTypePWM(GPIO_PORTB_BASE, GPIO_PIN_1);
//
// Compute the PWM period based on the system clock.
//
// Unlocked State: SysCtlClockGet() / 2000;
// Locked State: SysCtlClockGet() / 900;
ulPeriod = SysCtlClockGet() / 100;
upper_period = SysCtlClockGet() / 900;
lower_period = SysCtlClockGet() / 2000;
current_period = lower_period;
//
// Set the PWM period to 440 (A) Hz.
//
PWMGenConfigure(PWM0_BASE, PWM_GEN_1,
PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_NO_SYNC);
PWMGenPeriodSet(PWM0_BASE, PWM_GEN_1, ulPeriod);
//
// Set PWM0 to a duty cycle initializing lock to unlocked state
//
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_2, lower_period);
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_3, lower_period);
//
// Enable the PWM0 and PWM1 output signals.
//
PWMOutputState(PWM0_BASE, PWM_OUT_2_BIT | PWM_OUT_3_BIT, true);
//
// Enable the PWM generator.
//
PWMGenEnable(PWM0_BASE, PWM_GEN_1);
}
// Change the PWM to lock or unlock the door depending on the parameter
void PWM_Change(bool lock)
{
if (lock)
{
// Toggle the LED (PF0).
HWREGBITB(GPIO_PORTF_BASE + ADDR_MASK, 0) ^= 1;
// Increase the duty cycle
if (current_period == lower_period) {
current_period = upper_period;
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_2, current_period);
}
}
else
{
// Toggle the LED (PF0).
HWREGBITB(GPIO_PORTF_BASE + ADDR_MASK, 0) ^= 1;
//
// Decrease the duty cycle
if (current_period == upper_period) {

79

current_period = lower_period;
PWMPulseWidthSet(PWM0_BASE, PWM_OUT_2, current_period);
}

Appendix B4 (rfid_reader.c)
//*****************************************************************************
//
// rfid_reader.c - This function communicates with the RFID Reader Module
//
via UART0.
//
//*****************************************************************************
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

"rfid_reader.h"
"inc/hw_ints.h"
"inc/hw_memmap.h"
"inc/hw_types.h"
"driverlib/debug.h"
"driverlib/gpio.h"
"driverlib/interrupt.h"
"driverlib/sysctl.h"
"driverlib/uart.h"
"drivers/rit128x96x4.h"
"inc/lm3s8962.h"

extern char RFIDbuffer[16];


char adminId[] = {0x02,'5','0','0','0','8','F','6','D','4','E','F','C',0x0D,0x0A,0x03};
short is_rfid_valid(char *id){
short good = 0;
// Check the incoming buffer against the Admin ID
for(short t = 0; t < 16; t++){
if(id[t] != adminId[t])
return good;
}
// If the RFID matches return 1
good = 1;
return good;
}
//*****************************************************************************
//
// function hashit: Hashes a RFID for storage in the array
//
//*****************************************************************************
/*
short hashit(char *toHash){
short sum = 0;
// Sum the RFID
for(short x = 0; x < 16; x++){
sum += *toHash;
toHash++;
}
sum = sum * 17;
return (sum % 53);
}
*/
// Clears the buffer that the RFID Data is read into from UART0
void clearRFIDBuff(void){
for(int x = 0; x<16; x++){
RFIDbuffer[x] = 0xFF;
}
}

Appendix B5 (userdb.c)
#include <stdbool.h>
#include "utils/ustdlib.h"

80

#include
#include
#include
#include
#include
#include
#include

"parser.h"
"utils.h"
"global.h"
"driverlib/debug.h"
"driverlib/flash.h"
"utils/flash_pb.h"
"userdb.h"

#ifdef DEBUG
extern char debug_msg[SCREEN_COLS + 1];
#endif
//unsigned char public_modulus[SIGNATURE_SIZE] = { 0xe2, 0xa5, 0xf6, 0xd5,
//0x14, 0xcd, 0x96, 0xda, 0xfa, 0xe1, 0x9b, 0x6d, 0xb4, 0x2e, 0xce, 0xa4, 0x4f,
//0xa2, 0x0c, 0x77, 0xbf, 0xcf, 0x08, 0xe6, 0x3d, 0x1c, 0x22, 0xd5, 0xad, 0xba,
//0xae, 0x97, 0xab, 0x38, 0xb2, 0xa5, 0x47, 0xed, 0x75, 0x4d, 0xf4, 0x49, 0x00,
//0x68, 0xdd, 0x78, 0x47, 0x4f, 0x6c, 0xa5, 0x4c, 0xf2, 0x15, 0x66, 0xf3, 0xdd,
//0xd8, 0x52, 0x91, 0x30, 0x61, 0xf0, 0x8d, 0xd0, 0x41, 0x01, 0x57, 0xcc, 0xb1,
//0xa3, 0x15, 0x29, 0x25, 0x23, 0x5c, 0x71, 0xfc, 0x65, 0xc9, 0xa3, 0xc8, 0xd9,
//0x23, 0x0d, 0x29, 0x10, 0xd4, 0xc1, 0xe4, 0x2e, 0x95, 0x57, 0xff, 0x30, 0x37,
//0x62, 0x8e, 0xeb, 0x94, 0xde, 0x03, 0x61, 0x68, 0x5c, 0x77, 0x13, 0x83, 0xa0,
//0x31, 0x94, 0x41, 0x09, 0x59, 0x30, 0xa9, 0x8b, 0x55, 0x6c, 0x0d, 0xb6, 0x14,
//0xc7, 0xf1, 0x45, 0xd0, 0x2f, 0x1c, 0xfd };
unsigned char default_modulus[SIGNATURE_SIZE] = { 0x87, 0xE6, 0x72, 0xEA, 0x38,
0x82, 0x3D, 0x8B, 0xC0, 0x2F, 0x85, 0xBD, 0x1C, 0xF2, 0x31, 0x67, 0x2E,
0x39, 0x1D, 0x64, 0xC4, 0x53, 0x51, 0x43, 0x7E, 0x2E, 0xF0, 0x42, 0xA5,
0xE8, 0x3C, 0xAA, 0x84, 0xA9, 0xC6, 0x97, 0xA6, 0x90, 0xF9, 0x7A, 0x14,
0x2E, 0xCF, 0xF0, 0x42, 0x49, 0x5A, 0x5F, 0x60, 0x7E, 0x6E, 0xA2, 0xB3,
0x87, 0xBC, 0x9E, 0xBD, 0x9D, 0xBF, 0x06, 0x7F, 0x9E, 0x2C, 0x06, 0x3B,
0x84, 0x4C, 0xDF, 0xEE, 0x33, 0x76, 0x3A, 0xE2, 0x5E, 0xB3, 0x58, 0x3C,
0x07, 0x4C, 0x88, 0x7B, 0xA6, 0xBB, 0x2E, 0xDF, 0x25, 0x7B, 0x77, 0x2B,
0x7D, 0xF1, 0xE0, 0xC2, 0x5D, 0xAD, 0xDE, 0x7F, 0xD0, 0x34, 0xFD, 0x4F,
0xB0, 0x0D, 0xCA, 0x69, 0x08, 0xE9, 0x03, 0x0E, 0x15, 0x5F, 0x11, 0xD2,
0x66, 0x36, 0xD5, 0xCE, 0x70, 0x17, 0x8E, 0x01, 0xF4, 0x93, 0x36, 0x25,
0xDB, 0xF3, 0x43 };
const char default_admin[] = "Default Admin";
ParameterBlock pb;
// Temp copy of the parameter block in flash
const ParameterBlock* pb_flash_loc;
// Pointer to the real parameter block in flash
bool matches[MAX_NO_OF_USERS];
size_t next_match_idx;
extern UserRecord newUR;
// This should be called after power-up. It looks for a valid parameter block
// in flash. If it doesn't find one, it creates one with the default values.
// In any case, it saves a pointer to a valid parameter block in pb_flash_loc.
void ParamBlockInit(void)
{
size_t i;
size_t count=0;
FlashPBInit(PARAM_BLOCK_START, PARAM_BLOCK_END, PARAM_BLOCK_SIZE);
pb_flash_loc = (ParameterBlock*) FlashPBGet();
// If there is no valid parameter block, FlashPBGet returns NULL
if (pb_flash_loc == NULL)
{
for (i = 0; i < sizeof(pb.user_rec_stat); i++)
pb.user_rec_stat[i] = 0;
pb.next_rec_loc = 2;
FlashPBSave((unsigned char*)&pb);
pb_flash_loc = (ParameterBlock*) FlashPBGet();

}
else
{
pb.next_rec_loc = pb_flash_loc->next_rec_loc;
for (i = 0; i < sizeof(pb.user_rec_stat); i++)
pb.user_rec_stat[i] = pb_flash_loc->user_rec_stat[i];

81

}
#ifdef DEBUG
usnprintf(debug_msg, sizeof(debug_msg), "next_rec_loc = %d", pb_flash_loc->next_rec_loc);
WakeScreenSaver();
PrintLine(debug_msg);
for (i=0; i<MAX_NO_OF_USERS;i++)
{
if (is_valid_rec(i))
count++;
}
usnprintf(debug_msg, sizeof(debug_msg), "user count: %d", count);
PrintLine(debug_msg);
#endif
}
// Check if a valid admin is in the user DB. If not, add the default account.
void CheckForAdmin()
{
size_t i;
for (i = 0; i < MAX_NO_OF_USERS; i++)
{
if (is_valid_rec(i) && (FLASH_IDX_TO_URP(i)->perm) & PERM_ADMIN)
return;
}
// There are no admins, add the default
for (i = 0; i < sizeof(default_admin); i++)
{
newUR.name[i] = default_admin[i];
}
for (i = 0; i < sizeof(default_modulus); i++)
{
newUR.pub_key.C[i] = default_modulus[i];
}

newUR.perm = PERM_ADMIN;
StoreUserRec(&newUR);

// Returns true if flash_idx has a valid user record


bool is_valid_rec(size_t flash_idx)
{
return ((pb_flash_loc->user_rec_stat[flash_idx / 8] >> (flash_idx % 8)) & 1);
}
// Finds the next free spot in the user DB and stores its index in free_idx.
// Returns false if there is no free spot.
bool get_free_DB_idx(size_t* free_idx, size_t start_idx)
{
bool success;
size_t flash_idx, stop_idx;
// Find next free spot
stop_idx = flash_idx = start_idx;
success = ! is_valid_rec(flash_idx);
while (!success)
{
flash_idx = (flash_idx + 1) % MAX_NO_OF_USERS;
if (flash_idx == stop_idx)
break;
}

success = ! is_valid_rec(flash_idx);

if (success)

82

*free_idx = flash_idx;
return true;

}
else
return false;
}

// Finds all the valid user records with names matching name and returns the
// number of matches. Retrieve pointers to these records by following this
// function with calls to GetNextMatchIdx(). Name must be null-terminated.
size_t FindMatchingUsers(const char* name)
{
size_t i;
size_t count = 0;
for (i = 0; i < MAX_NO_OF_USERS; i++)
{
matches[i] = false;
if (is_valid_rec(i))
{
if (ustrcmp(FLASH_IDX_TO_URP(i)->name, name) == 0)
{
matches[i] = true;
if (count == 0)
next_match_idx = i;
count++;
}

}
}

return count;

// Returns the index of the next matching user record


size_t GetNextMatchIdx(void)
{
while ( next_match_idx < sizeof(matches) && ! matches[next_match_idx] )
next_match_idx++;
ASSERT(next_match_idx != sizeof(matches))
return next_match_idx++;
}
// Returns true if two arrays are identical
bool ArrayEquals(const unsigned long* ray1, const unsigned long* ray2, size_t length)
{
size_t j;

for (j = 0; j < length; j++)


{
if (ray1[j] != ray2[j])
{
return false;
}
}
return true;

// Stores a new user record into flash memory. Returns true if the operation
// was successful. Might fail if we are out of flash, or the available flash
// is bad. If an identical user rec exists, nothing will be changed and it
// still returns true. If the user rec exists, but the permissions are
// changing, the old record will be deleted and the new written.
bool StoreUserRec(const UserRecord* rec)
{
size_t free_idx, start_idx, old_idx, i;

83

bool write_failed = false;


bool delete_old = false;
const UserRecord* rec_flash_loc;
// Check if this user is already in the DB
for (i = FindMatchingUsers(rec->name); i > 0; i--)
{
old_idx = GetNextMatchIdx();
rec_flash_loc = FLASH_IDX_TO_URP(old_idx);
if (ArrayEquals(rec->pub_key.L, rec_flash_loc->pub_key.L, sizeof(PublicKey)/sizeof(long)))
{
if (rec_flash_loc->perm == rec->perm)
{
// This user is already in the DB and no changes are necessary
return true;
}
else
{
delete_old = true;
break;
}
}
}
// Get the index where we should write to
if (!get_free_DB_idx(&free_idx, pb.next_rec_loc))
return false;
start_idx = free_idx;
// Keep trying until the write succeeds or we have tried all free blocks
while (true)
{
// Write the user record to flash
pb.next_rec_loc = (free_idx + 1) % MAX_NO_OF_USERS;
rec_flash_loc = FLASH_IDX_TO_URP(free_idx);
FlashErase((unsigned long)rec_flash_loc);
FlashProgram((unsigned long*)rec, (unsigned long)rec_flash_loc, sizeof(UserRecord));
write_failed = false;
// Verify the data in flash
for (i = 0; i < sizeof(UserRecord); i++)
{
if (((unsigned char*)rec_flash_loc)[i] != ((unsigned char*)rec)[i])
{
write_failed = true;
PrintLine("write failed");
break;
}
}
if (write_failed)
{
if (!get_free_DB_idx(&free_idx, pb.next_rec_loc))
{
PrintLine("no more free");
return false;
}
if (free_idx == start_idx)
{
PrintLine("free == start");
return false;
}

}
else
{
break;

84

}
}
// Update the parameter block
HWREGBITB(pb.user_rec_stat + free_idx / 8, free_idx % 8) = 1;
if (delete_old)
HWREGBITB(pb.user_rec_stat + old_idx / 8, old_idx % 8) = 0;

FlashPBSave((unsigned char*)&pb);
pb_flash_loc = (ParameterBlock*)FlashPBGet();
return true;

// Tries to find and delete from the user DB a record with the given name and
// pub_key. Returns true if it succeeds.
bool RemoveUserRec(const char* name, const PublicKey* pub_key)
{
size_t i, flash_idx;
const UserRecord* rec_flash_loc;
// Assume there is only one record with matching name and pub_key
for (i = FindMatchingUsers(name); i > 0; i--)
{
flash_idx = GetNextMatchIdx();
rec_flash_loc = FLASH_IDX_TO_URP(flash_idx);
if (ArrayEquals(rec_flash_loc->pub_key.L, pub_key->L, sizeof(PublicKey) / sizeof(long)))
{
// Mark the record invalid in the parameter block
HWREGBITB(pb.user_rec_stat + flash_idx / 8, flash_idx % 8) = 0;
FlashPBSave((unsigned char*)&pb);
pb_flash_loc = (ParameterBlock*)FlashPBGet();
return true;
}
}
return false;
}

Appendix B6 (utils.c)
#define DEBUG
#include "utils.h"
#include "driverlib/debug.h"
#include <stdbool.h>
#define ADDR_MASK 0x3FC
ScreenBuffer sbuff = { {{0}}, 0, 0, 0 };
unsigned char errbuff[256];
RingBuffer ErrorMsgBuff = {errbuff, sizeof errbuff, 0, 0};
#ifdef DEBUG
extern RingBuffer RxBuff;
extern RingBuffer TxBuffU0;
#endif
//struct {
// char buff[64];
// size_t start;
// size_t end;
//} ErrorMsgBuff = { {0}, 0, 0 };
extern void WakeScreenSaver(void);
// Send a string to the UART.
void UART0StringSend(const char *pucBuffer)
{
// Loop while there are more characters to send.

85

while(*pucBuffer != '\0')
{
// Write the next character to the UART.
//UARTCharPut(UART0_BASE, *pucBuffer++);
pushRB(*pucBuffer++, &TxBuffU0);
}
}
// Send a string to the UART.
void UART1StringSend(const char *pucBuffer)
{
// Loop while there are more characters to send.
while(*pucBuffer != '\0')
{
#ifdef DEBUG
pushRB(*pucBuffer, &TxBuffU0);
#endif

// Write the next character to the UART.


UARTCharPut(UART1_BASE, *pucBuffer++);

}
// Send an array of chars to UART1.
void UART0ArraySend(const unsigned char* ray, size_t len)
{
size_t i = 0;
while(i < len)
UARTCharPut(UART0_BASE, ray[i++]);
}
// Send an array of chars to UART1.
void UART1ArraySend(const unsigned char* ray, size_t len)
{
size_t i = 0;
while(i < len)
{
#ifdef DEBUG
pushRB(ray[i], &TxBuffU0);
#endif
UARTCharPut(UART1_BASE, ray[i++]);
}

// Writes whatever is in the ScreenBuffer to the screen.


void WriteScreen()
{
unsigned char out_row = 0,
in_row,
Y = 0;
for (out_row = 0; out_row < sbuff.validrows; out_row++)
{
in_row = (out_row + sbuff.firstrow) % SCREEN_ROWS;
RIT128x96x4StringDraw(sbuff.buff[in_row], 0, Y, BRIGHTNESS);
Y += ROW_HEIGHT;
}
}
// Prints a single char to the next available position on the screen. Will
// automatically wrap at the end of the line and scroll to a new line if
// necessary.
void PrintChar(char c)
{
int buf_row;
int X;
int Y;

86

bool update_whole_screen = false;


if (sbuff.column == 0)
{ // start a new row
if (sbuff.validrows < SCREEN_ROWS)
sbuff.validrows++;
else
{
sbuff.firstrow = (sbuff.firstrow + 1) % SCREEN_ROWS;
update_whole_screen = true;
}
}
buf_row = (sbuff.firstrow + sbuff.validrows - 1) % SCREEN_ROWS;
// Store c in the screen buffer
sbuff.buff[buf_row][sbuff.column] = c;
// Write it to the screen
if (update_whole_screen)
{
sbuff.column++;
X = sbuff.column;
while (X < SCREEN_COLS)
sbuff.buff[buf_row][X++] = ' ';
sbuff.buff[buf_row][X] = '\0';
WriteScreen();
}
else
{
sbuff.buff[buf_row][sbuff.column + 1] = '\0';
X = sbuff.column * COL_WIDTH;
Y = (sbuff.validrows - 1) * ROW_HEIGHT;
RIT128x96x4StringDraw( &(sbuff.buff[ buf_row ][ sbuff.column ]), X, Y, BRIGHTNESS);
// update column
sbuff.column++;
if (sbuff.column == SCREEN_COLS)
sbuff.column = 0;
}

//*****************************************************************************
// Prints a string to the next line of screen. If the screen is full, the
// previous contents will be shifted up a line. msg must be null terminated.
// If msg is longer than the width of the screen, it will be truncated.
//*****************************************************************************
void PrintLine(char* msg)
{
int idx = 0;
int row = (sbuff.firstrow + sbuff.validrows) % SCREEN_ROWS;
int col = 0;
do
{
sbuff.buff[row][col++] = msg[idx++];
}
while (msg[idx] != '\0' && col < SCREEN_COLS);
while (col < SCREEN_COLS)
sbuff.buff[row][col++] = ' ';
// last char must be null in all cases
sbuff.buff[row][col] = '\0';
if (sbuff.validrows < SCREEN_ROWS)
sbuff.validrows++;

87

else
sbuff.firstrow = (sbuff.firstrow + 1) % SCREEN_ROWS;

sbuff.column = 0;
WriteScreen();

// Adds item to the end of rb. If rb is full, oldest data is overwritten.


void pushRB(char item, RingBuffer* rb)
{
ASSERT(rb != NULL);
rb->buff[rb->end++] = item;
if (rb->end == rb->size)
rb->end = 0;
if (rb->start == rb->end)
{
#ifdef DEBUG
if (rb == &RxBuff)
ErrorPrint(" RxBuff overflow ");
else if (rb == &TxBuffU0)
ErrorPrint(" TxBuffU0 overflow ");
else if (rb == &ErrorMsgBuff)
ErrorPrint(" ErrorMsgBuff overflow ");
#endif

rb->start++;
if (rb->start == rb->size)
rb->start = 0;

}
// Removes an item from the start of rb. You make sure the buffer isn't empty.
char pullRB(RingBuffer* rb)
{
char result;
ASSERT(rb != NULL);
if (rb->start != rb->end)
{
result = rb->buff[rb->start++];
if (rb->start == rb->size)
rb->start = 0;
}
else
{
result = 0;
#ifdef DEBUG
if (rb == &RxBuff)
ErrorPrint(" RxBuff underflow ");
else if (rb == &TxBuffU0)
ErrorPrint(" TxBuffU0 underflow ");
else if (rb == &ErrorMsgBuff)
ErrorPrint(" ErrorMsgBuff underflow ");
#endif
}
#ifdef DEBUG
if (rb == &RxBuff)
pushRB(result, &TxBuffU0);
#endif
}

return result;

88

// Returns the space available in the ring buffer


size_t spaceRB(RingBuffer* rb)
{
if (rb->start == rb->end)
return rb->size - 1;
else if (rb->start < rb->end)
return rb->size - rb->end + rb->start - 1;
else // rb->start > rb->end
return rb->start - rb->end - 1;
}
// Add a error message to a buffer, which will later be dumped to the screen by
// calling ErrorFlush.
void ErrorPrint(char* msg)
{
size_t space;
// Disable global interrupts
__asm("cpsid i");
space = spaceRB(&ErrorMsgBuff);
// We need space for at least a character and a null
if (space < 2)
{
// Enable global interrupts
__asm("cpsie i");
return;
}
if (*msg != '\0')
{
pushRB(*(msg++), &ErrorMsgBuff);
space--;
}
while (*msg != '\0' && space > 1)
{
pushRB(*(msg++), &ErrorMsgBuff);
space--;
}
pushRB('\0', &ErrorMsgBuff);

// Enable global interrupts


__asm("cpsie i");

// Flush the error message buffer. Put this in the main loop. Do NOT have
// code in an ISR that could write to the screen or call this funtion.
void ErrorFlush(void)
{
char msg[SCREEN_COLS+1];
size_t i;
while (ErrorMsgBuff.start != ErrorMsgBuff.end)
{
// copy the next message in the buffer to msg
i = 0;
msg[i] = pullRB(&ErrorMsgBuff);
while (msg[i] != '\0' && i < SCREEN_COLS)
{
msg[++i] = pullRB(&ErrorMsgBuff);
}
if (msg[i] != '\0')

89

// We ran out of screen columns before finding the null.


// Add the null to msg, and clear the rest of the message in the
// buffer.
msg[i] = '\0';
while (pullRB(&ErrorMsgBuff) != '\0');

//WakeScreenSaver();
//PrintLine(msg);
UART0StringSend(msg);

Appendix B7 (sbrk.c)
// posted by dereksoftstuff to TI E2E Community forums on July 25, 2008
// http://e2e.ti.com/support/microcontrollers/stellaris_arm_cortex-m3_microcontroller/f/473/t/44452.aspx
//
// modified by David Chamberlain, December 5, 2012
#include <sys/types.h>
//#include "utils.h"
// linker (main.ld) sets heap start and end
extern unsigned int _HEAP_START;
extern unsigned int _HEAP_END;
static caddr_t heap = NULL;
// low level bulk memory allocator - used by malloc
caddr_t _sbrk ( int increment ) {
caddr_t prevHeap;
caddr_t nextHeap;
if (heap == NULL) {
// first allocation
heap = (caddr_t)&_HEAP_START;
}
prevHeap = heap;
// Always return data aligned on a 8 byte boundary
nextHeap = (caddr_t)(((unsigned int)(heap + increment) + 7) & ~7);

if (nextHeap > (caddr_t)&_HEAP_END)


{
return NULL; // error - no more memory
}
else
{
heap = nextHeap;
return (caddr_t) prevHeap;
}

Appendix B8 (startup_gcc.c)
//
//
//
//
//
//
//
//
//

startup_gcc.c - Startup code for use with GNU tools.


Copyright (c) 2005-2012 Texas Instruments Incorporated. All rights reserved.
Software License Agreement
Texas Instruments (TI) is supplying this software for use solely and
exclusively on TI's microcontroller products. The software is owned by
TI and/or its suppliers, and is protected under applicable copyright
laws. You may not combine this software with "viral" open-source
software in order to form a larger program.
THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.

90

// NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT


// NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
// CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
// DAMAGES, FOR ANY REASON WHATSOEVER.
// This is part of revision 9453 of the EK-LM3S8962 Firmware Package.
#include "utils.h"
#define ADDR_MASK 0x3FC
// Forward declaration of the default fault handlers.
void ResetISR(void);
static void NmiSR(void);
static void FaultISR(void);
static void IntDefaultHandler(void);
// External declaration for the interrupt handler used by the application.
extern void SysTickIntHandler(void);
extern void UART0IntHandler(void);
extern void UART1IntHandler(void);
extern void GPIOCIntHandler(void);
// The entry point for the application.
extern int main(void);
// Reserve space for the system stack.
static unsigned long pulStack[4096];
// The vector table. Note that the proper constructs must be placed on this to
// ensure that it ends up at physical address 0x0000.0000.
__attribute__ ((section(".isr_vector")))
void (* const g_pfnVectors[])(void) =
{
(void (*)(void))((unsigned long)pulStack + sizeof(pulStack)),
// The initial stack pointer
ResetISR,
// The reset handler
NmiSR,
// The NMI handler
FaultISR,
// The hard fault handler
IntDefaultHandler,
// The MPU fault handler
IntDefaultHandler,
// The bus fault handler
IntDefaultHandler,
// The usage fault handler
0,
// Reserved
0,
// Reserved
0,
// Reserved
0,
// Reserved
IntDefaultHandler,
// SVCall handler
IntDefaultHandler,
// Debug monitor handler
0,
// Reserved
IntDefaultHandler,
// The PendSV handler
SysTickIntHandler,
// The SysTick handler
IntDefaultHandler,
// GPIO Port A
IntDefaultHandler,
// GPIO Port B
GPIOCIntHandler,
// GPIO Port C
IntDefaultHandler,
// GPIO Port D
IntDefaultHandler,
// GPIO Port E
UART0IntHandler,
// UART0 Rx and Tx
UART1IntHandler,
// UART1 Rx and Tx
IntDefaultHandler,
// SSI0 Rx and Tx
IntDefaultHandler,
// I2C0 Master and Slave
IntDefaultHandler,
// PWM Fault
IntDefaultHandler,
// PWM Generator 0
IntDefaultHandler,
// PWM Generator 1
IntDefaultHandler,
// PWM Generator 2
IntDefaultHandler,
// Quadrature Encoder 0
IntDefaultHandler,
// ADC Sequence 0
IntDefaultHandler,
// ADC Sequence 1
IntDefaultHandler,
// ADC Sequence 2
IntDefaultHandler,
// ADC Sequence 3
IntDefaultHandler,
// Watchdog timer

91

};

IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler,
IntDefaultHandler

//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//

Timer 0 subtimer A
Timer 0 subtimer B
Timer 1 subtimer A
Timer 1 subtimer B
Timer 2 subtimer A
Timer 2 subtimer B
Analog Comparator 0
Analog Comparator 1
Analog Comparator 2
System Control (PLL, OSC, BO)
FLASH Control
GPIO Port F
GPIO Port G
GPIO Port H
UART2 Rx and Tx
SSI1 Rx and Tx
Timer 3 subtimer A
Timer 3 subtimer B
I2C1 Master and Slave
Quadrature Encoder 1
CAN0
CAN1
CAN2
Ethernet
Hibernate

// The following are constructs created by the linker, indicating where the
// the "data" and "bss" segments reside in memory. The initializers for the
// for the "data" segment resides immediately following the "text" segment.
extern unsigned long _etext;
extern unsigned long _data;
extern unsigned long _edata;
extern unsigned long _bss;
extern unsigned long _ebss;
// This is the code that gets called when the processor first starts execution
// following a reset event. Only the absolutely necessary set is performed,
// after which the application supplied entry() routine is called. Any fancy
// actions (such as making decisions based on the reset cause register, and
// resetting the bits in that register) are left solely in the hands of the
// application.
void
ResetISR(void)
{
unsigned long *pulSrc, *pulDest;
// Copy the data segment initializers from flash to SRAM.
pulSrc = &_etext;
for(pulDest = &_data; pulDest < &_edata; )
{
*pulDest++ = *pulSrc++;
}
// Zero fill the bss segment.
__asm(" ldr
r0, =_bss\n"
" ldr
r1, =_ebss\n"
" mov
r2, #0\n"
" .thumb_func\n"
"zero_loop:\n"
"
cmp
r0, r1\n"
"
it
lt\n"
"
strlt r2, [r0], #4\n"
"
blt
zero_loop");

// Call the application's entry point.


main();

92

// This is the code that gets called when the processor receives a NMI. This
// simply enters an infinite loop, preserving the system state for examination
// by a debugger.
static void
NmiSR(void)
{
//HWREGBITB(GPIO_PORTF_BASE + ADDR_MASK, 0) ^= 1;
// Enter an infinite loop.
while(1)
{
}
}
// This is the code that gets called when the processor receives a fault
// interrupt. This simply enters an infinite loop, preserving the system state
// for examination by a debugger.
static void
FaultISR(void)
{
HWREGBITB(GPIO_PORTF_BASE + ADDR_MASK, 0) ^= 1;
// Enter an infinite loop.
while(1)
{
}
}
// This is the code that gets called when the processor receives an unexpected
// interrupt. This simply enters an infinite loop, preserving the system state
// for examination by a debugger.
static void
IntDefaultHandler(void)
{
//HWREGBITB(GPIO_PORTF_BASE + ADDR_MASK, 0) ^= 1;
ErrorPrint("ERR: unhandled interrupt");
// Go into an infinite loop.
while(1)
{
}
}

Appendix B9 (global.h)
#ifndef GLOBAL_H_DEF
#define GLOBAL_H_DEF
#ifndef NULL
#define NULL ((void *)0)
#endif
// The rate of system ticks in Hz
#define TICK_RATE 1000
#define UART0_BAUD 9600
#define UART1_BAUD 115200
#define BUT_POLL_RATE 500 // Hz
#define BUT_DEAD_TIME 20 // ms
#define SCREENSAVER_TIMEOUT 20 // seconds
#define INVALID_TIMEOUT 1000 // ms
#define RANDOM_NO_LENGTH 64 // bytes
// Add this to the base address of GPIO data registers to unmask the whole byte
// see section 8.2.1.2 in the LM3S8962 datasheet.
#define ADDR_MASK 0x3FC
#define STRX(x) #x
#define STR(x) STRX(x) // convert integer macro x to string literal
// Inform the screen saver of activity which should prevent the screensaver
// from activating;
void WakeScreenSaver(void);

93

#endif

Appendix B10 (parser.h)


#ifndef PARSER_H_DEF
#define PARSER_H_DEF
// Theses are the status indicators returned by parser functions.
#define PARSER_BUSY 0
// Waiting for more bytes to finish parsing
#define PARSER_INVALID 1
// The incoming bytes are not a valid command
#define PARSER_BAD_USER 2 // The user is not known
#define PARSER_BAD_AUTH 3 // User authentication failed
#define PARSER_BAD_PERM 4 // User doesn't have the correct permissions
#define PARSER_UNLOCK 5
// The door should be unlocked
#define PARSER_LOCK 6
// The door should be locked
#define PARSER_STATUS 7
// The door's status should be returned
#define PARSER_BEGIN 8
// Send the random number
#define PARSER_ADD_USER 9 // A user is being added
#define PARSER_TOGGLE 10
// Toggle the LED (for testing)
#define PARSER_BAD_RN 11
// The random number was wrong
#define PARSER_RM_USER 12 // A user is being deleted
// Internal status indicators
#define PARSER_NAME_DONE 128 // A valid user name and trailing ':' has been received
#define PARSER_RN_DONE 129 // A valid RN has been received
#define PARSER_SIG_DONE 130 // A valid signature has been received
#define PARSER_KEY_DONE 131 // A key has been received
#define PARSER_PERMS_DONE 132 // A key has been received
#define
#define
#define
#define

MAX_USER_LENGTH 20 // characters
SIGNATURE_SIZE 128 // bytes
EXPONENT 0x10001
// RSA public exponent
SALT_SIZE 0
// bytes (This is irrelevant for PCKS V1.5 padding)

#define PERM_ADMIN 1

// And permission byte with this

// Parse the incoming bytes, possibly set some flags/globals, then return a
// status indicator. Assumes there is at least one byte to be read in RxBuff.
// If reset is true, all parser logic is reset.
unsigned char Parse(bool reset);
#endif

Appendix B11 (userdb.h)


#ifndef USERDB_H_DEF
#define USERDB_H_DEF
// User Database:
// Each record in the data base will be stored in single erase block of flash,
// which is 1024 bytes. These records will be written directly to flash using
// the FlashErase() and FlashProgram() functions (SW-DRL-UG-9453.pdf). The
// user records can be read just like normal memory. The status of the user
// database, that is which locations hold valid user records, will be stored in
// a flash parameter block, provided by the FlashPB functions
// (SW-EK-LM3S8962-UG-9453.pdf).
#define PARAM_BLOCK_START 0x30000 // the start of the parameter block storage area, see main.ld
#define FLASH_ERASE_BLOCK 1024 // bytes
#define PARAM_BLOCK_END (PARAM_BLOCK_START + FLASH_ERASE_BLOCK*4) // use 4 erase blocks
// The size of a single block. This must be: big enough to hold the
// ParameterBlock struct below, a power of 2, and an integral divisor of 1024.
// Also, ('START - 'END) / 'SIZE < 128.
#define PARAM_BLOCK_SIZE 128
// (bytes) this seems like a good number
#define END_OF_FLASH 0x40000
// can't store more than this in the user DB
#define MAX_NO_OF_USERS ((END_OF_FLASH - PARAM_BLOCK_END) / FLASH_ERASE_BLOCK)
// convert an index to a UserRecord pointer
#define FLASH_IDX_TO_URP(idx) ((const UserRecord*)(PARAM_BLOCK_END + idx * FLASH_ERASE_BLOCK))

94

typedef union {
unsigned char C[SIGNATURE_SIZE / sizeof(char)];
unsigned long L[SIGNATURE_SIZE / sizeof(long)];
} PublicKey;
typedef struct {
char name[MAX_USER_LENGTH + 1];
unsigned char perm;
PublicKey pub_key;
} UserRecord;
typedef struct {
unsigned char sequence; // these two bytes are reserved for the FlashPB functions
unsigned char checksum; // don't write to them
// Each bit in this array will be set to 1 to indicate the corresponding
// location in memory holds a valid user record. The least significant bit
// of index 0 corresponds to the user record located at PARAM_BLOCK_END.
#define USER_REC_STAT_SIZE ((MAX_NO_OF_USERS / sizeof(char)) + 1)
unsigned char user_rec_stat[USER_REC_STAT_SIZE];
// For wear leveling, we will try to use the entire user db area of flash
// before we reuse an old location. This number indicates which erase
// block we should try to write the next user record to. (Don't assume it
// is empty.)
unsigned char next_rec_loc;
// Fill the rest of the PARAM_BLOCK_SIZE allocated to us. This should
// prevent the FlashPBSave() function from trying to read an invalid
// address, which would hard fault the system. It also should create a
// compiler error if this struct is bigger than PARAM_BLOCK_SIZE.
unsigned char padding[PARAM_BLOCK_SIZE - 3 - USER_REC_STAT_SIZE];
} ParameterBlock;
// This should be called after power-up. It looks for a valid parameter block
// in flash. If it doesn't find one, it creates one with the default values.
// In any case, it saves a pointer to a valid parameter block in pb_flash_loc.
void ParamBlockInit(void);
// Check if a valid admin is in the user DB. If not, add the default account.
void CheckForAdmin();
// Returns true if flash_idx has a valid user record
bool is_valid_rec(size_t flash_idx);
// Finds the next free spot in the user DB and stores its index in free_idx.
// Returns false if there is no free spot.
bool get_free_DB_idx(size_t* free_idx, size_t start_idx);
// Finds all the valid user records with names matching name and returns the
// number of matches. Retrieve pointers to these records by following this
// function with calls to GetNextMatchIdx(). Name must be null-terminated.
size_t FindMatchingUsers(const char* name);
// Returns the index of the next matching user record
size_t GetNextMatchIdx(void);
// Returns true if two arrays are identical
bool ArrayEquals(const unsigned long* ray1, const unsigned long* ray2, size_t length);
// Stores a new user record into flash memory. Returns true if the operation
// was successful. Might fail if we are out of flash, or the available flash
// is bad. If an identical user rec exists, nothing will be changed and it
// still returns true. If the user rec exists, but the permissions are
// changing, the old record will be deleted and the new written.
bool StoreUserRec(const UserRecord* rec);
// Tries to find and delete from the user DB a record with the given name and
// pub_key. Returns true if it succeeds.

95

bool RemoveUserRec(const char* name, const PublicKey* pub_key);


#endif

Appendix B12 (pwm.h)


// Initialize the pwm
void pwm_init(void);
// Change the PWM to lock or unlock the door depending on the parameter
void PWM_Change(bool lock);

Appendix B13 (rfid_reader.h)


//*****************************************************************************
//
// rfid_reader.h - Macros and defines used when a rfid reader is added to UART0
//
//*****************************************************************************
//*****************************************************************************
//
// Function Prototypes
//
//*****************************************************************************
short is_rfid_valid(char *id);
void clearRFIDBuff(void);

Appendix B14 (utils.h)


#ifndef UTILS_H_DEF
#define UTILS_H_DEF
#include
#include
#include
#include
#include
#define
#define
#define
#define
#define

<stddef.h>
"inc/hw_types.h"
"inc/hw_memmap.h"
"driverlib/uart.h"
"drivers/rit128x96x4.h"

SCREEN_ROWS 12
SCREEN_COLS 21
BRIGHTNESS 15
ROW_HEIGHT 8
COL_WIDTH 6

typedef struct {
char buff[SCREEN_ROWS][SCREEN_COLS + 1]; // +1 for null terminator
unsigned char firstrow;
unsigned char validrows;
unsigned char column; // the column to write to next
} ScreenBuffer;
// This ring buffer is empty when start == end. Read from the start index,
// write to the end index.
typedef struct {
unsigned char* buff;
unsigned short size;
unsigned short start;
unsigned short end;
} RingBuffer;
// Adds item to the end of rb. If rb is full, oldest data is overwritten.
void pushRB(char item, RingBuffer* rb);
// Removes an item from the start of rb. You make sure the buffer isn't empty.
char pullRB(RingBuffer* rb);
// Returns the space available in the ring buffer
size_t spaceRB(RingBuffer* rb);

96

// Send a string to UART0.


void UART0StringSend(const char *pucBuffer);
// Send a string to UART1.
void UART1StringSend(const char *pucBuffer);
// Send an array of chars to UART1.
void UART0ArraySend(const unsigned char* ray, size_t len);
// Send an array of chars to UART1.
void UART1ArraySend(const unsigned char* ray, size_t len);
// Writes whatever is in the ScreenBuff to the screen.
void WriteScreen();
// Prints a string to the next line of screen. If the screen is full, the
// previous contents will be shifted up a line. msg must be null terminated.
// If msg is longer than the width of the screen, it will be truncated.
void PrintLine(char* msg);
// Prints a single char to the next available position on the screen. Will
// automatically wrap at the end of the line and scroll to a new line if
// necessary.
void PrintChar(char c);
// Route error messages
void ErrorPrint(char* msg);
// Flush the error message buffer
void ErrorFlush(void);
#endif

97

S-ar putea să vă placă și