ការធ្វើឱ្យឈីបអេសភីសជាក់ស្តែងជាក់ស្តែងនៅលើ espressif esp8266

បន្ទះឈីបអេសភីអេហ្វអេស 866 បង្កើតបាននូវអ៊ិនធរណេតរបស់អ្វីៗដែលមានរយៈពេល 3 ដុល្លារ។ យោងទៅតាមការបង្កើតការកសាងកម្មវិធីបង្កប់ស្វ័យប្រជាប្រិយស្វ័យប្រជាប្រិយដែលមានប្រជាប្រិយភាពដែលមានប្រជាប្រិយភាពក្នុងរយៈពេល 60 ថ្ងៃចុងក្រោយនេះមានកម្មវិធីបង្កប់ចំនួន 13.341 ការបង្កើតរបស់ប្រវត្ដិតន្រ្តីសម្រាប់វេទិកានោះ។ ក្នុងចំណោមនោះមានតែ 19% ប៉ុណ្ណោះដែលមានការគាំទ្រ SSL ហើយ 10 ភាគរយរួមបញ្ចូលម៉ូឌុលម៉ូឌែលគ្រីប។

ជារឿយៗយើងរិះគន់កង្វះសន្តិសុខក្នុងវិស័យ IOT ហើយជាញឹកញាប់គ្របដណ្តប់លើបណ្តាញ Botnets និងការវាយប្រហារផ្សេងទៀតប៉ុន្តែតើយើងនឹងរៀបចំគម្រោងរបស់យើងចំពោះស្តង់ដារដែលយើងទាមទារដែរឬទេ? តើយើងនឹងឈប់ក្នុងការកំណត់បញ្ហានេះទេរឺក៏យើងអាចក្លាយជាផ្នែកមួយនៃដំណោះស្រាយបានទេ?

អត្ថបទនេះនឹងផ្តោតលើការអនុវត្តការប្រើការអ៊ិនគ្រីប AES និងមុខងារផ្តល់សិទ្ធិដល់ពិធីអនុញ្ញាត MQTT ដោយប្រើបន្ទះឈីប HES 8266 ដែលកំពុងដំណើរការ esp8266 ដែលកំពុងដំណើរការកម្មវិធីបង្កប់ Nodlemcu ។ គោលបំណងរបស់យើងគឺមិនផ្តល់ឱ្យ Panacea ចម្លង / បិទភ្ជាប់មកទេប៉ុន្តែត្រូវឆ្លងកាត់ដំណើរការមួយជំហានម្តង ៗ កំណត់នូវបញ្ហាប្រឈមនិងដំណោះស្រាយនៅតាមផ្លូវ។ លទ្ធផលគឺជាប្រព័ន្ធមួយដែលបញ្ចប់ការបញ្ចប់ដែលបានអ៊ិនគ្រីបនិងផ្ទៀងផ្ទាត់ភាពត្រឹមត្រូវការពារការលួចស្តាប់តាមផ្លូវហើយការធ្វើឱ្យមានសុពលភាពនៃទិន្នន័យដែលមានសុពលភាពដោយមិនពឹងផ្អែកលើ SSL ។

យើងដឹងថាមានវេទិកាដែលមានអនុភាពជាងមុនដែលអាចគាំទ្រ SSL (ឧទាហរណ៍ Raspberry Pi, ទឹកក្រូច pi, mestacment), ប៉ុន្តែសូមចាប់ផ្តើមជាមួយនឹងផ្នែករឹងដែលមានតំលៃថោកបំផុតដែលយើងភាគច្រើនបាននិយាយកុហកហើយពិធីសារមួយដែលសមរម្យសម្រាប់គម្រោងរបស់យើងជាច្រើន។ ។ AES គឺជាអ្វីដែលអ្នកអាចអនុវត្តនៅលើ avr ប្រសិនបើអ្នកត្រូវការ។

រតឹស្ដី

MQTT គឺជាពិធីការផ្ញើសារទម្ងន់ស្រាលដែលដំណើរការលើ TCP / IP ហើយត្រូវបានប្រើជាញឹកញាប់សម្រាប់គម្រោង iOT ។ ឧបករណ៍ម៉ាស៊ីនភ្ញៀវបានចុះឈ្មោះឬផ្សព្វផ្សាយទៅប្រធានបទ (ឧ។ សីតុណ្ហញ / សីតុណ្ហភាព / ផ្ទះបាយ) ហើយសារទាំងនេះត្រូវបានបញ្ជូនបន្តដោយឈ្មួញកណ្តាល MQTT ។ ព័ត៌មានបន្ថែមអំពី MQTT មាននៅលើគេហទំព័ររបស់ពួកគេឬក្នុងស៊េរីថ្មីរបស់យើង។

ពិធីការ MQTT មិនមានមុខងារសុវត្ថិភាពដែលភ្ជាប់មកជាមួយក្រៅពីឈ្មោះអ្នកប្រើ / ការផ្ទៀងផ្ទាត់ពាក្យសំងាត់ដូច្នេះវាជារឿងធម្មតាទេដែលអ៊ិនគ្រីបនិងផ្ទៀងផ្ទាត់ភាពត្រឹមត្រូវនៅលើបណ្តាញ SSL ដែលមាន SSL ជាមួយ SSL ដែលមាន SSL ជាមួយ SSL ដែលមាន SSL ជាមួយ SSL ដែលមាន SSL ជាមួយ SSL ដែលមាន SSL ជាមួយ SSL ដែលមាន SSL ដែលមាន SSL ជាមួយ SSL ដែលមាន SSL ជាមួយ SSL ដែលមាន SSL ជាមួយ SSL ដែលមាន SSL ជាមួយ SSL ដែលមាន SSL ជាមួយ SSL ជាមួយ SSL ។ ទោះយ៉ាងណាក៏ដោយ SSL អាចមានតម្រូវការសម្រាប់ esp8266 ហើយនៅពេលបើកដំណើរការអ្នកនៅសល់ដោយមិនសូវមានការចងចាំតិចសម្រាប់ពាក្យសុំរបស់អ្នក។ ក្នុងនាមជាជំរើសស្រាលអ្នកអាចអ៊ិនគ្រីបបានតែការបង់ប្រាក់ដែលត្រូវបានផ្ញើហើយប្រើលេខសម្គាល់សម័យនិងមុខងារអេសអេសសម្រាប់ការផ្ទៀងផ្ទាត់។

វិធីត្រង់ដើម្បីធ្វើដូចនេះកំពុងប្រើ Lua និងម៉ូឌុលរបស់ Nodemcu ដែលរួមមានការគាំទ្រសម្រាប់ក្បួនដោះស្រាយ AES ក្នុងរបៀប CBC ក៏ដូចជាមុខងារ HMAC Hash ។ ការប្រើការអ៊ិនគ្រីប AES ត្រូវការរបស់បីយ៉ាងត្រឹមត្រូវដើម្បីផលិត Ciphertext: សារជាកូនសោរនិងវ៉ិចទ័រចាប់ផ្តើម (iv) ។ សារនិងគ្រាប់ចុចគឺជាគំនិតត្រង់ប៉ុន្តែវ៉ិចទ័រចាប់ផ្តើមមានតំលៃខ្លះៗ។

នៅពេលដែលអ្នកអ៊ិនកូដសារមួយនៅក្នុង AES ដែលមានគ្រាប់ចុចឋិតិវន្តវាតែងតែផលិតទិន្នផលដូចគ្នា។ ឧទាហរណ៍សារ “usernameampassword” ដែលបានអ៊ិនគ្រីប “អាចបង្កើតបានលទ្ធផលដូចជា” E40d86c04d723 “។ ប្រសិនបើអ្នកដំណើរការការអ៊ិនគ្រីបម្តងទៀតជាមួយលេខកូដនិងសារដូចគ្នាអ្នកនឹងទទួលបានលទ្ធផលដូចគ្នា។ នេះបើកឱ្យអ្នកនូវការវាយប្រហារទូទៅជាច្រើនប្រភេទជាពិសេសការវិភាគលំនាំនិងការវាយប្រហារឡើងវិញ។

នៅក្នុងការវាយប្រហារនៃការវិភាគគំរូអ្នកប្រើចំណេះដឹងដែលទិន្នន័យដែលបានផ្តល់ឱ្យតែងតែផលិតស៊ីភីភីអេសដូចគ្នាដើម្បីទាយថាគោលបំណងឬខ្លឹមសារនៃសារផ្សេងៗគ្នាដោយមិនដឹងពីកូនសោសម្ងាត់។ ឧទាហរណ៍ប្រសិនបើសារ “E40d86c04d723” ត្រូវបានផ្ញើ “មុនការប្រាស្រ័យទាក់ទងផ្សេងទៀត, មួយអាចទាយបានយ៉ាងលឿនវាគឺជាការចូល។ និយាយឱ្យខ្លីប្រសិនបើប្រព័ន្ធចូលមានលក្ខណៈសាមញ្ញការផ្ញើកញ្ចប់នោះ (ការវាយប្រហារតបស្នង) ប្រហែលជាគ្រប់គ្រាន់ដើម្បីកំណត់ខ្លួនអ្នកជាអ្នកប្រើប្រាស់ដែលមានការអនុញ្ញាតហើយភាពវឹកវរ។

IVs ធ្វើឱ្យការវិភាគពីគំរូកាន់តែពិបាក។ IV គឺជាបំណែកនៃទិន្នន័យដែលបានផ្ញើរួមជាមួយនឹងកូនសោដែលកែប្រែលទ្ធផលចុងក្រោយនៃ Ciphertext ។ ដូចដែលឈ្មោះបានបង្ហាញវាចាប់ផ្តើមស្ថានភាពនៃក្បួនដោះស្រាយអ៊ិនគ្រីបមុនពេលទិន្នន័យចូល។ IV ត្រូវការភាពខុសគ្នាសម្រាប់សារនីមួយៗដែលបានផ្ញើដូច្នេះការធ្វើទិន្នន័យដដែលៗទៅក្នុង Ciphertext & Liasy មួយចំនួន (ដូចជា AES-CBC) តម្រូវឱ្យមានការលូតលាស់ដែលមិនអាចទាយទុកជាមុនបានគឺគ្រាន់តែចៃដន្យវាគ្រាន់តែចៃដន្យប៉ុណ្ណោះ។ IVs មិនចាំបាច់រក្សាទុកការសម្ងាត់ទេប៉ុន្តែវាជារឿងធម្មតាដើម្បី obfuscate ពួកគេតាមរបៀបណាមួយ។

ខណៈពេលដែលនេះការពារប្រឆាំងនឹងការវិភាគលំនាំវាមិនជួយក្នុងការធ្វើការវាយធ្វើបាបឡើងវិញទេ។ ឧទាហរណ៍ការបញ្ជូនសំណុំទិន្នន័យដែលបានអ៊ិនគ្រីបដែលបានផ្តល់ឱ្យនឹងនៅតែស្ទួនលទ្ធផល។ ដើម្បីបងា្ករថាយើងត្រូវផ្ទៀងផ្ទាត់អ្នកផ្ញើ។ យើងនឹងប្រើលេខសម្គាល់សម័យប្រជុំសាធារណៈដែលបានបង្កើតជាសាធារណៈសម្រាប់សារនីមួយៗ។ លេខសម្គាល់វេននេះអាចត្រូវបានបង្កើតដោយឧបករណ៍ដែលទទួលបានដោយបញ្ចូលទៅប្រធានបទ MQTT ។

ការបង្ការប្រភេទនៃការវាយប្រហារទាំងនេះមានសារៈសំខាន់ក្នុងករណីប្រើប្រាស់ទូទៅមួយចំនួន។ មានចំនុចដែលគ្រប់គ្រងដោយអ៊ិនធឺរណែតមានហើយនិងឧបករណ៍ប្រើប្រាស់ដែលគួរឱ្យចាប់អារម្មណ៍ក្រៅពីវានឹងល្អប្រសិនបើពួកគេមិនបានប្រើពាក្យបញ្ជាដែលគ្មានសុវត្ថិភាព។ ទីពីរប្រសិនបើខ្ញុំកំពុងធ្វើឱ្យមានអ្នកផ្គត់ផ្គង់រាប់រយខ្ញុំមិនចង់អោយនរណាម្នាក់បំពេញឃ្លាំងផ្ទុកទិន្នន័យរបស់ខ្ញុំទេ។

ការអ៊ិនគ្រីបជាក់ស្តែង

ការអនុវត្តចំណុចខាងលើនៅលើណុនមូកូទាមទារការខិតខំខ្លះ។ អ្នកនឹងត្រូវការកម្មវិធីបង្កប់ដែលបានចងក្រងក្នុងការបញ្ចូលម៉ូឌុល “គ្រីបថូ” បន្ថែមលើអ្នកផ្សេងទៀតដែលអ្នកត្រូវការire for your application. SSL support is not required.

First, let’s assume you’re connected to an MQTT broker with something like the following. You can implement this as a separate function from the cryptography to keep things clean. The client subscribes to a sessionID channel, which publishes suitably long, pseudorandom session IDs. You could encrypt them, but it’s not necessary.

ចេក
2
បី
4
5
6
7
8 ចីម្យង
9
10
កមនើតទៅវិញ
12
មយយ
លមយយ
15 ចីនើវ
m = mqtt.Client("clientid", 120)

m:connect("myserver.com", 1883, 0,
function(client)
print("connected")
client:subscribe("mytopic/sessionID", 0,
function(client) print("subscribe success") end
)
end,
function(client, reason)
print("failed reason: " .. reason)
តីបហ្ចប់
)

m:on("message", function(client, topic, sessionID) end)

Moving on, the node ID is a convenient way to help identify data sources. You can use any string you wish though: nodeid = node.chipid().

Then, we set up a static initialization vector and a key. This is only used to obfuscate the randomized initialization vector sent with each message, NOT used for any data. We also choose a separate key for the data. These keys are 16-bit hex, just replace them with yours.

Finally we’ll need a passphrase for a hash function we’ll be using later. A string of reasonable length is fine.

ចេក
2
បី
4
staticiv = "abcdef2345678901"
ivkey = "2345678901abcdef"
datakey = "0123456789abcdef"
passphrase = "mypassphrase"

We’ll also assume you have some source of data. For this example it will be a value read from the ADC. data = adc.read(0)

Now, we generate a pseudorandom initialization vector. A 16-digit hex number is too large for the pseudorandom number function, so we generate it in two halves (16^8 minus 1) and concatenate them.

ចេក
2
បី
4
5
half1 = node.random(4294967295)
half2 = node.random(4294967295)
I = string.format("%8x", half1)
V = string.format("%8x", half2)
iv = I .. V

We can now run the actual encryption. here we are encrypting the current initialization vector, the node ID, and one piece of sensor data.

ចេក
2
បី
encrypted_iv = crypto.encrypt("AES-CBC", ivkey, iv, staticiv)
encrypted_nodeid = crypto.encrypt("AES-CBC", datakey, nodeid,iv)
encrypted_data = crypto.encrypt("AES-CBC", datakey, data,iv)

Now we apply the hash function for authentication. first we combine the nodeid, iv, data, and session ID into a single message, then compute a HMAC SHA1 hash using the passphrase we defined earlier. We convert it to hex to make it a bit more human-readable for any debugging.

ចេក
2
fullmessage = nodeid .. iv .. data .. sessionID
hmac = crypto.toHex(crypto.hmac("sha1", fullmessage, passphrase))

Now that both encryption and authentication checks are in place, we can place all this information in some structure and send it. Here, we’ll use comma separated values as it’s convenient:

ចេក
2
payload = table.concat({encrypted_iv, eid, data1, hmac}, ",")
m:publish("yourMQTTtopic", payload, 2, 1, function(client) p = "Sent" print(p) end)

When we run the above code on an actual NodeMCU, we would get output something like this:

1d54dd1af0f75a91a00d4dcd8f4ad28d,
d1a0b14d187c5adfc948dfd77c2b2ee5,
564633a4a053153bcbd6ed25370346d5,
c66697df7e7d467112757c841bfb6bce051d6289

All together, the encryption program is as follows (MQTT sections excluded for clarity):

ចេក
2
បី
4
5
6
7
8 ចីម្យង
9
10
កមនើតទៅវិញ
12
មយយ
លមយយ
15 ចីនើវ
អមនត់
កមនើត
18 ចីនើដត
19
nodeid = node.chipid()
staticiv = "abcdef2345678901"
ivkey = "2345678901abcdef"
datakey = "0123456789abcdef"
passphrase = "mypassphrase"

data = adc.read(0)
half1 = node.random(4294967295)
half2 = node.random(4294967295)
I = string.format("%8x", half1)
V = string.format("%8x", half2)
iv = I .. V

encrypted_iv = crypto.encrypt("AES-CBC", ivkey, iv, staticiv)
encrypted_nodeid = crypto.encrypt("AES-CBC", datakey, nodeid,iv)
encrypted_data = crypto.encrypt("AES-CBC", datakey, data,iv)
fullmessage = nodeid .. iv .. data .. sessionID
hmac = crypto.toHex(crypto.hmac("sha1",fullmessage,passphrase))
payload = table.concat({encrypted_iv, encrypted_nodeid, encrypted_data, hmac}, ",")

Decryption

Now, your MQTT broker doesn’t know or care that the data is encrypted, it just passes it on. So, your other MQTT clients subscribed to the topic will need to know how to decrypt the data. On NodeMCU this is rather easy. just split the received data into strings via the commas, and do something like the below. note this end will have generated the session ID so already knows it.

ចេក
2
បី
4
5
6
7
8 ចីម្យង
9
10
staticiv = "abcdef2345678901"
ivkey = "2345678901abcdef"
datakey = "0123456789abcdef"
passphrase = "mypassphrase"

iv = crypto.decrypt("AES-CBC", ivkey, encrypted_iv, staticiv)
nodeid = crypto.decrypt("AES-CBC", datakey, encrypted_nodeid,iv)
data = crypto.decrypt("AES-CBC",datakey, encrypted_data,iv)
fullmessage = nodeid .. iv .. data .. sessionID
hmac = crypto.toHex(crypto.hmac("sha1",fullmessage,passphrase))

Then compare the received and computed HMAC, and regardless of the result, invalidate that session ID by generating a new one.

Once More, In Python

For a little variety, consider how we would handle decryption in Python, if we had an MQTT client on the same virtual machine as the broker that was analysing the data or storing it in a database. lets assume you’ve received the data as a string “payload”, from something like the excellent Paho MQTT client for Python.

In this case it’s convenient to hex encode the encrypted data on the NodeMCU before transmitting. So on the NodeMCU we convert all encrypted data to hex, for example: encrypted_iv = crypto.toHex(crypto.encrypt(“AES-CBC”, ivkey, iv, staticiv))

Publishing a randomized sessionID is not discussed below, but is easy enough using os.urandom() and the Paho MQTT Client. The decryption is handled as follows:

ចេក
2
បី
4
5
6
7
8 ចីម្យង
9
10
កមនើតទៅវិញ
12
មយយ
លមយយ
15 ចីនើវ
អមនត់
កមនើត
18 ចីនើដត
19
20
មយយ
អមយសិច
23
មយយ
25
អមយញចុម
27
28 ផេនតោia
29 ផោន
30
មយយ
32
33
34
35
from Crypto.Cipher import AES
import binascii
from Crypto.Hash import SHA, HMAC

# define all keys
ivkey = ‘2345678901abcdef’
datakey = ‘0123456789abcdef’
staticiv = ‘abcdef2345678901’
passphrase = ‘mypassphrase’

# convert the received string to a list
data = payload.split(",")

# extract list items
encrypted_iv = binascii.unhexlify(data[0])
encrypted_nodeid = binascii.unhexlify(data[1])
encrypted_data = binascii.unhexlify(data[2])
received_hash = binascii.unhexlify(data[3])

# decrypt the initialization vector
iv_decryption_suite = AES.new(ivkey,AES.MODE_CBC, staticiv)
iv = iv_decryption_suite.decrypt(encrypted_iv)

# decrypt the data using the initialization vector
id_decryption_suite = AES.new(datakey,AES.MODE_CBC, iv)
nodeid = id_decryption_suite.decrypt(encrypted_nodeid)
data_decryption_suite = AES.new(datakey,AES.MODE_CBC, iv)
sensordata = data_decryption_suite.decrypt(encrypted_data)

# compute hash function to compare to received_hash
fullmessage = s.join([nodeid,iv,sensordata,sessionID])
hmac = HMAC.new(passphrase,fullmessage,SHA)
computed_hash = hmac.hexdigest()

# see docs.python.org/2/library/hmac.html for how to compare hashes securely

The End, The Beginning

Now we have a system that sends encrypted, authenticated messages through an MQTT server to either another ESP8266 client or a larger system running Python. There are still important loose ends for you to tie up if you implement this yourself. The keys are all stored in the ESP8266s’ flash memory, so you will want to control access to these devices to prevent reverse engineering. The keys are also stored in the code on the computer receiving the data, here running Python. Further, you probably want each client to have a different key and passphrase. That’s a lot of secret material to keep safe and potentially update when necessary. Solving the key distribution problem is left as an exercise for the motivated reader.

And on a closing note, one of the dreadful things about writing an article involving cryptography is the possibility of being wrong on the Internet. This is a fairly straightforward application of the tested-and-true AES-CBC mode with HMAC, so it should be pretty solid. Nonetheless, if you find any interesting shortcomings in the above, please let us know in the comments.

Leave a Reply

Your email address will not be published. Required fields are marked *