>> µTLS - DEFINING LIGHTWEIGHT SECURITY FOR IoT (PART 5)
I figured it was time to play with the big boys - nothing like implementing
an industry standard.
The
National Institute of Standards and Technology,
a branch in the U. S. Government sought public submissions to establish
the
Advanced Encryption Standard
(AES), and since May 26, 2002 it became effective as a federal government
standard - superseding the Data Encryption Standard (DES).
The winning submission was Rijndael, and the approved key sizes were 128,
192 and 256. It was initially announced in the FIPS publication
FIPS 197.
It is only natural that µTLS add support for AES, as it is frequently
as a
Cipher
in TLS protocols and considered secure if the exchange of keys can be done
securely. The FIPS 197 standard documents the algorithm in detail; so
writing an implementation was quite simple - however there are a number of
public domain sources online which have been verified against test vectors.
On the server side; the plan is to hook into the openssl library
within php to encrypt and decrypt AES streams using CBC mode
(Cipher Block Chaining). The openssl library uses the PKCS7 padding
scheme, where there must be at least one padding byte within the input stream.
Something we must take into consideration when writing our Cipher on the
micro-controllers.
Suprisingly; the algorithm was simple enough to integrate into the
µTLS (micro TLS) server and client sketches - within a few hours
I had working examples that could utilize aes-128, aes-192
and aes-256. Our server design is holding up well; only a few
minor tweaks were required.
Enough of the technical blah blah blah - let's see this stuff in action!
:: attempting connection to server
** connected
-- request:
POST /xxx/index.php HTTP/1.1
Host: xxx.xxxxxxxx.xxx
User-Agent: Arduino/1.0
Connection: close
Content-type: application/x-www-form-urlencoded
Content-Length: 283
{
"guid": "ffa90e64-f3e2-4697-b98c-e3c13d1b2362",
"security": {
"protocol": "none",
"response": "none",
"protocol_sub": "aes-128"
}
}
-- response:
HTTP/1.1 200 OK
Date: Sat, 17 Dec 2016 19:21:40 GMT
Server: Apache
Content-Length: 287
Connection: close
Content-Type: application/json
{
"session": "e91f5584152639c72d5358a05f9cd887585590478d0d2",
** found session: e91f5584152639c72d5358a05f9cd887585590478d0d2
"security": {
"response": "aes-128",
"protocol": "aes-128"
},
"data": {
"ts": "1482002503",
** found ts: 1482002503
"buffer": "88wx6UUMKPVVLFkyyuMdJJDdovvQ=="
** found buffer: *streamed*
** decoded form: F3 0C 7A 50 C2 8F 54 B1 64 CA E3 1D 24 37 68 BD
** ASCII form: ..zP..T.d...$7h.
}
}
The client makes the request to the server within the
security.protocol_sub key to use aes-128 for the
communication once the session has been created. We can see within
the response that the 128 bit key (16 bytes) is encapsulated within
the response:
F3 0C 7A 50 C2 8F 54 B1 64 CA E3 1D 24 37 68 BD
During the process of implementing AES, I spent a significant amount of
time understanding how the algorithm works - it is definitely an interesting
read if cryptography is a subject matter that interests you. On the
Arduino side; encryption is done as simple as follows:
char msg[] = "sending a message over microTLS";
int msg_len = strlen(msg);
byte enc[128];
int enc_len = (msg_len + AES_BLOCKSIZE) & ~(AES_BLOCKSIZE-1);
// we must create a random IV - put it in first bytes
for (i=0;i < AES_BLOCKSIZE; i++)
enc[i] = xrand();
// encode our message
AESInitState(&aes, app_aeskey, enc);
AESencrypt(&aes, (byte *)msg, msg_len, enc+AES_BLOCKSIZE);
enc_len += AES_BLOCKSIZE;
The CBC mode of AES uses an initialization vector (IV); that gets updates
after each block of data is encrypted using the algorithm. We generate some
random bytes and then initialize the AESEngine. Due to PKCS7 padding;
we know that the output buffer will be larger than our input buffer -
including the IV in addition to the encoded buffer; it will be of length
modulus the block size.
The subsequent PUT request looks as follows:
:: attempting connection to server
** connected
-- request:
PUT /xxx/index.php HTTP/1.1
Host: xxx.xxxxxxxx.xxx
User-Agent: Arduino/1.0
Connection: close
Content-type: application/x-www-form-urlencoded
Content-Length: 199
{
"session": "e91f5584152639c72d5358a05f9cd887585590478d0d2",
"security": {
"protocol": "aes-128",
"response": "aes-128"
},
"data": {
"buffer": "9PDFlwmTZl57Dz6mqfZGATMNx4OTJb5zsSTTEzt/2sKzlSp
Qr1fL2zCIc818Ujzw"
}
}
-- response:
HTTP/1.1 200 OK
Date: Sat, 17 Dec 2016 19:22:08 GMT
Server: Apache
Content-Length: 299
Connection: close
Content-Type: application/json
sending a message over microTLS
{
"session": "e91f5584152639c72d5358a05f9cd887585590478d0d2",
"security": {
"response": "aes-128",
"protocol": "aes-128"
},
"data": {
"ts": "1482002531",
** found ts: 1482002531
"buffer": "uuF3CRRYZe55yz7+++kY++UeUCCSnFzzmQ8aa/gBiiy
xMCCcnX55gU="
** found buffer: *streamed*
** decoded form: B8 5D C2 45 86 5E E7 2C FB FB E9 18 F9 47 94 09
29 C5 CE 64 3C 6B F8 01 8B 2C 4C 09 C9 D7 E6 05
** ASCII form: .].E.^.,.....G..)..d<k...,L.....
** decrypted: 68 6F 77 64 79 20 70 61 72 74 6E 65 72 21
** ASCII form: howdy partner!
}
}
We can clearly see that the message was received and the server response
was also decrypted.
Sketch uses 17,880 bytes (55%) of program storage space.
Global variables use 475 bytes (23%) of dynamic memory,
leaving 1,573 bytes for local variables. Maximum is 2,048 bytes.
In comparison to the none client, our program increased in size
by around 4kb and only a small amount of dynamic memory was sacrificed
for the purpose of storing the AES key. Since the algorithm remains
the same regardless of the key sizes; we we easily boost up to a 256
byte posing a risk to our free memory.
So; while this is great - how can we make the key exchange secure?
There are a number of ways to do this; the most commonly accepted way is
to use public-key cryptography (RSA, ECC et al) - however, the standard
also included the use of PSK (pre-shared key) systems also can be marked
as secure. For the sake of completeness; I decided to integrate the
concept into the flow of the µTLS (micro TLS) protocol.
To implement this; for each client in the system there needs to be a
pre-shared key generated. The key will exist securely on the server
and compiled into the client. From the out-side world; there is no way
to intercept these keys over network traffic.
In order for the server to verify that the client is who they say they
are; a buffer is created and encrypted using the shared key. On receival;
the server decrypts the buffer and compares it to what it expects it to be.
On success; the sub-protocol initialization is encrypted using the same
key and send back to the client, effectively doing the key exchange
behind a layer of security.
The resulting resource analysis of a sketch using aes-128/aes-128:
Sketch uses 18,314 bytes (56%) of program storage space.
Global variables use 491 bytes (23%) of dynamic memory,
leaving 1,557 bytes for local variables. Maximum is 2,048 bytes.
There is still plenty of resources available to integrate the RSA
algorithm that I had previously written for the Arduino UNO - that will
be my next major milestone in the project. By that time; hopefully the
IoT world would have picked up wind about µTLS (micro TLS) and we
can look at how to get the solution out there to solve the security
issues within the IoT ecosystem.