drivers/at86rf2xx; spi security module (AES)#14113
Conversation
benpicco
left a comment
There was a problem hiding this comment.
Looks good!
Neat to have this feature.
Just a few questions:
- do we need to take the radio state into account?
- if we want to encrypt payload, do we really have to write the plaintext onto the device, read it back, and then send it? I would have imagined it would have a mode where it would handle encryption / decryption transparently.
| static inline | ||
| void at86rf2xx_spi_get_bus(const at86rf2xx_t *dev) | ||
| { | ||
| spi_acquire(dev->params.spi, dev->params.cs_pin, SPI_MODE_0, dev->params.spi_clk); | ||
| } | ||
|
|
||
| static inline | ||
| void at86rf2xx_spi_release_bus(const at86rf2xx_t *dev) | ||
| { | ||
| spi_release(dev->params.spi); | ||
| } |
There was a problem hiding this comment.
You could move those to at86rf2xx_internal.h if you want.
But might also be done in a later patch. I was wondering why you didn't just use at86rf2xx_sram_read() and friends, but I guess the problem there is that they will all acquire and release the bus.
Only recently I have learned that this is a bad idea (and even more so after the recent power optimizations after release() on sam0), so the right courser of action would be to convert the driver to not acquire() and release() the bus, just like you did here.
But that would be another PR.
There was a problem hiding this comment.
but I guess the problem there is that they will all acquire and release the bus.
Yes.
And further at86rf2xx_sram_read() writes dummy bytes to the transceiver, while
at86rf2xx_sram_write() does not care what it reads from the transceiver.
For fast SPI, you read the result from the last block operation, while you write the next block.
No. The AES component is independent of the transceiver. It's indeed possible to encrypt/decrypt while sending/receiving (assuming the device is not sleeping).
Yes :(. It's required to write the plaintext packet and then read it from SRAM since it's a "generic" AES crypto accelerator (intended to be used with IEEE802.15.4, but other use cases are also possible) |
| } | ||
|
|
||
| static inline | ||
| uint8_t _aes_status(at86rf2xx_t *dev) |
There was a problem hiding this comment.
can't this be replaced with the at86rf2xx_sram_xxx functions (from 'at86rf2xx_internal.c`)?
There was a problem hiding this comment.
| } | ||
|
|
||
| static inline | ||
| void _aes_open_read(at86rf2xx_t *dev, uint8_t addr) |
| } | ||
|
|
||
| static inline | ||
| void _aes_open_write(at86rf2xx_t *dev, uint8_t addr) |
There was a problem hiding this comment.
I cannot reuse them because, they acquire / release the bus in each call and at86rf2xx_sram_write() ignores what is read from the transceiver, which is important for fast SPI.
There was a problem hiding this comment.
hmmm I would say that these are "presentation layer" functions (not specific to AES). Maybe they can be added as generic functions? What do you think?
There was a problem hiding this comment.
Generally I agree, they could be moved to at86rf2xx_internal because they are not related to the security module. But I am not sure if I should do this because nothing else uses it, so far.
On the one side it touches the actual driver, and I think this should be done, if some day someone decides to modify the at86rf2xx SPI functions in a way that they do not acquire / release the SPI bus for every call. Right now I see no benefit of moving them.
On the other side it doesn´t harm because it is a small change.
I would keep them in place for now.
|
Git is showing a weird diff for 39c23ac. if (!nblocks) {
return 0;
}thing. |
| else { | ||
| puts("CBC test 3 failed"); | ||
| } | ||
| } |
There was a problem hiding this comment.
Would be good if the test would print SUCCESS at the end.
Then you can also add a tests/01-run.py with
#!/usr/bin/env python3
import sys
from testrunner import run
def testfunc(child):
child.expect('START')
child.expect('ECB test 1 done')
child.expect('ECB test 2 done')
child.expect('CBC test 1 done')
child.expect('CBC test 2 done')
child.expect('CBC test 3 done')
child.expect('SUCCESS')
if __name__ == "__main__":
sys.exit(run(testfunc))so we can have make test work for automatic tests.
There was a problem hiding this comment.
Hm looks like you'll have to add a puts("START"); at the beginning of the test too.
This and chmod a+x tests/01-run.py.
Then make test will work.
There was a problem hiding this comment.
Or just remove matching start, test_utilis_interactive_sync will already match the start for you.
There was a problem hiding this comment.
ups forgot that ...
| } | ||
| uint8_t cfg = AT86RF2XX_AES_CTRL_AES_MODE__ECB | | ||
| AT86RF2XX_AES_CTRL_AES_DIR__ENC; | ||
| uint8_t mirror = cfg | AT86RF2XX_AES_CTRL_MIRROR_MASK__AES_REQUEST; |
There was a problem hiding this comment.
What's that mirror about?
There was a problem hiding this comment.
The mirror must be the same value as the cfg and is transfered to the slave to start an AES operation, if AT86RF2XX_AES_CTRL_MIRROR_MASK__AES_REQUEST is set.
| while (!(status & AT86RF2XX_AES_STATUS_MASK__AES_DONE)) { | ||
| AES_DEBUG("status: %02x\n", status); | ||
| status = _aes_status(dev); | ||
| } |
There was a problem hiding this comment.
Could we still get the AT86RF2XX_AES_STATUS_MASK__AES_ER bit set here?
There was a problem hiding this comment.
No the error bit is set before the transceiver has processed a data block. There are two cases:
- The delay between initiating an AES operation and sending the next
cfgtoAT86RF2XX_REG__AES_CTRLwas too short. Meaning the transceiver did not have enough time to process the current block. - Less then 16 bytes of data have been sent to the transceiver. This never happens in the code, so this case cannot arrive.
There was a problem hiding this comment.
Better add that as a comment so in the future this question doesn't come up again :)
There was a problem hiding this comment.
Actually the first case should not arrive either because I am polling if the operation is complete.
I could make an assert() that the error bit is never set and change the return types of all functions to be void because that was the only thing that I expected that could go wrong. Every time I witnessed the error bit was set, there was a mistake in the code.
Or there may be something that I am not aware of and the current implementation would be a little safer.
But I don´t think so. Even when I reduce AT86RF2XX_AES_DELAY_US, the error is not set because the polling is safe.
So I would like to turn it into an assert()
|
This looks pretty good. I'd like to have this, though it's not a requirement, it would sure encourage the work on MAC layer security. :) |
|
Now I think it´s time to squash. |
benpicco
left a comment
There was a problem hiding this comment.
Looks good to me and the automatic test is also working.
Please squash.
50ae1b1 to
6bb3d81
Compare
| DISABLE_MODULE += auto_init_at86rf2xx | ||
|
|
||
| # define the driver to be used for selected boards | ||
| ifneq (,$(filter samr21-xpro,$(BOARD))) | ||
| DRIVER := at86rf233 | ||
| endif | ||
| ifneq (,$(filter iotlab-m3 fox,$(BOARD))) | ||
| DRIVER := at86rf231 | ||
| endif | ||
| ifneq (,$(filter mulle,$(BOARD))) | ||
| DRIVER := at86rf212b | ||
| endif | ||
|
|
||
| # use the at86rf231 as fallback device | ||
| DRIVER ?= at86rf231 | ||
|
|
||
| # include the selected driver | ||
| USEMODULE += $(DRIVER) |
There was a problem hiding this comment.
Do we actually need the base driver here?
There was a problem hiding this comment.
for this in main.c:
at86rf2xx_setup(&dev, &at86rf2xx_params[0]);
dev.netdev.netdev.event_callback = _event_cb;
if (dev.netdev.netdev.driver->init(&dev.netdev.netdev) != 0) {
return EXIT_FAILURE;
}There was a problem hiding this comment.
Yea but do we need it?
I mean if we have to init the radio to use crypto that's fine.
But if we just init it and then never use it, we might as well drop it.
There was a problem hiding this comment.
I don´t think we need the driver capability but it initializes the params, SPI and checks if SPI is working.
You mean I can copy the params and init SPI manually? Then the driver is not needed. Is this better?
There was a problem hiding this comment.
Then I would have to make an own driver rather then a pseudomodule, right?
There was a problem hiding this comment.
A never mind then.
A separate init function would be possible, but that's not worth the hassle now.
If someone really wants to use the crypto without the radio, that could be added later.
|
I am going to insert two empty lines in the python script to make travis happy and squash right away. |
6bb3d81 to
7fe64c5
Compare
|
Then I am going to add a Makefile.ci, to make murdock happy. |
7fe64c5 to
74eec82
Compare
|
Hooray 🎉 , thanks for merging. |
Contribution description
The at86rf2xx IEEE 802.15.4 transceivers are capable to perform
AESin hardware and support block cipher modesECBandCBC.This PR adds a
pseudomodulein theat86rf2xxdriver. You will be able to perform hardware encryption and decryption of blocks of data usingECBandCBCblock ciphers. Nothing is changed from the actual driver.Only
SPItransceivers are supported, for now. Although for MCU integrated transceiversatmega128rfa1andatmega256rfr2it is even simpler and could be added later.The SPI security module supports
fast SPI, which means that you will read output blocki, if you write input blocki + 1to the SPI bus.Testing procedure
I provided a test in
tests/driver_at86rf2xx_aes/.Test vectors are taken from: RFC3602
Issues/PRs references
None