>> µTLS - DEFINING LIGHTWEIGHT SECURITY FOR IoT (PART 8)
The final pieces are falling into place - µTLS (micro TLS) is almost
ready to be used for real!
µTLS (micro TLS) is almost ready - so far, we have sorted out how
to establish a trusted connection using public-key cryptography where we
can then exchange a secret session key for ongoing communication using
a symmetric cipher; now we just need to add a level of integrity to what
is being sent. It is the final piece in the suite of algorithms used in
Transport Layer Security.
Typically; the encryption layer adds data integrity using a
a MAC (Message Authentication Code).
There is one problem; not all encryption layers may have this implemented
- so it makes sense to introduce our own system. Our main goal is to
verify that what was received was actually what we sent - so we devised
the following logic that would be quite easy to integrate on both
sides.
:: SOURCE :: | :: DESTINATION ::
digest = hash(message); | decrypted = decrypt(message);
encrypted = encrypt(message); | if (digest == hash(decrypted))
| accept message;
| else
| reject message;
Initially we considered the likes of
MD5 and
SHA-xxx
variants as candidate algorithms - as there are a number of open source
implementations available on Arduino. In the end; we simply couldn't
afford the program overhead required for the algorithms - so, we went on
the hunt for a more lightweight one - as security should be covered by
the encryption layer.
Bob Jenkins
has written and published a number of hash functions over the years -
the goal is to find an algorithm that produces a unique value for each
string or buffer that is provided to it. The less likely there are
duplicate values; the better rated the algorithm is. Using the
suggestions outlined - I created an algorithm to do the job
nicely and not compromise program space or RAM.
A few sample strings - what would typically be sent from an IoT device.
buf = '{ "ts"=123456, "temp"=28.6, "humidity"=65.4 }' hash = 0x28f102c8
buf = '{ "ts"=123456, "temp"=28.7, "humidity"=65.4 }' hash = 0x9293e049
buf = '{ "ts"=123456, "temp"=28.7, "humidity"=65.3 }' hash = 0xc445b92e
Even with a single character change in the original message - the
hash changes dramatically. 👍
Integration into the µTLS (micro TLS) protocol involves simply
adding a new key where appropriate and making some changes to the
server and client processing engines to extract the value and then
perform a new hash on the decrypted data and check that the values match,
-- 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: 224
{
"session": "e91f5584152639c72d5358a05f9cd887585d13b63a499",
"security": {
"protocol": "aes-128",
"response": "aes-128"
},
"data": {
"integrity": "1004330131",
"buffer":"eqySwTniMYF/XJB8WqdEeEpzRvafX0egrOz4jwfORI2svbM5QoSHDC6
4lqVY8s9n"
}
}
-- response:
HTTP/1.1 200 OK
Date: Fri, 23 Dec 2016 12:08:27 GMT
Server: Apache
Content-Length: 334
Connection: close
Content-Type: application/json
sending a message over microTLS
{
"session": "e91f5584152639c72d5358a05f9cd887585d13b63a499",
"security": {
"response": "aes-128",
"protocol": "aes-128"
},
"data": {
"ts": "1482494909",
** found ts: 1482494909
"integrity": "2729352711",
** found digest: 2729352711
"buffer": "jjbMd11bFXeeBn7XX+NzWWPCSCCcnkbbQKJaaFb766Gyh44sgG
WWqk="
** found buffer: *streamed*
}
}
:: done
app state: 7
current time: 1482494909
session_ping: 1482495209
scratch_use: 32
** decoded:
length: 32
buffer: 8D B3 1D D5 B1 57 78 19 FB 5F E3 73 58 F0 92 09
C9 E4 6D 02 89 68 56 FB E8 6C A1 E2 C8 06 5A A9
ASCII: .....Wx.._.sX.....m..hV..l....Z.
** decrypted:
length: 14
buffer: 68 6F 77 64 79 20 70 61 72 74 6E 65 72 21
ASCII: howdy partner!
digest: pass
An extract of a running µTLS (micro TLS) client is shown above;
we can clearly see the integrity keys in the message exchange. In the
event the hash values match; we can trust what was received. If they
do not match, we can safely assume that something went wrong in the
encryption layer or someone tampered with the message while in transit
through to the client.
Oh; and the obligatory:
Sketch uses 27,198 bytes (84%) of program storage space.
Global variables use 980 bytes (47%) of dynamic memory,
leaving 1,068 bytes for local variables. Maximum is 2,048 bytes.
I also made some improvements to the scratch memory area management -
it added a small amount of program space overhead; but it gives a lot
more flexibility for processing information than the previous version.
With each revision I have refactored sections of code for speed and
clarity.
I'm now putting my sights on migrating my
weather station
to use µTLS - it is a perfect use-case!