Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sys/crypto: Enable support for AES-192, AES-256 #16183

Merged
merged 5 commits into from
May 5, 2021

Conversation

Ollrogge
Copy link
Contributor

@Ollrogge Ollrogge commented Mar 11, 2021

Contribution description

This PR adds support for AES-192 and AES-256. The code in /crypto/aes.c did already support AES-192 and AES-256 but the functionality was not made available. This PR adds the additional interfaces to enable AES-192 and AES-256.

Testing procedure

I have added tests for 192 bit and 256 bit keys to the already existing tests for CBC, CTR and ECB mode. The tests have the same origin as the original ones.

I was unable to find official test vectors for 192 bit and 256 bit for CCM and OCB mode. If someone can point me towards test vectors for these modes I can add them as well.

Issues/PRs references

Fixes #16178, waiting for #16253

@leandrolanzieri leandrolanzieri added Area: crypto Area: Cryptographic libraries Type: new feature The issue requests / The PR implemements a new feature for RIOT labels Mar 11, 2021
@PeterKietzmann
Copy link
Member

Fixes #16178

@PeterKietzmann
Copy link
Member

PeterKietzmann commented Mar 11, 2021

@Ollrogge thanks for your contribution, it is very useful!

I have added tests for 192 bit and 256 bit keys to the already existing tests for CBC, CTR and ECB mode. The tests have the same origin as the original ones.

  • Very nice! Please point to the origin. Did you check NIST SP 800-38A ?

  • Why do you replace aes_interface with three interfaces for 128/192/256 bit keys? Their structures only differ in key size, which is configured during aes_init anyway, right?

  • You have extended cipher_context_t, which is fair, to use aes_ functions directly. However, I'm wondering if this obsoletes max_key_size in ciphers.h. Do you have opinions on that? I know, the ciphers module is a beast...

@PeterKietzmann
Copy link
Member

BTW: All tests pass on native and nrf51dk and the PR currently adds about 500 Bytes ROM.

@Ollrogge
Copy link
Contributor Author

@Ollrogge thanks for your contribution, it is very useful!

I have added tests for 192 bit and 256 bit keys to the already existing tests for CBC, CTR and ECB mode. The tests have the same origin as the original ones.

* Very nice! Please point to the origin. Did you check [NIST SP 800-38A ](https://csrc.nist.gov/publications/detail/sp/800-38a/final)?

Yes I did. Sadly it does not contain test vectors for CCM and OCB mode.

* Why do you replace `aes_interface` with three interfaces for 128/192/256 bit keys? Their structures only differ in key size, which is configured during `aes_init` anyway, right?

True. By removing max_key_size they are the same. I did not create a general interface with a name like CIPHER_AES since this would break all code using the CIPHER_AES_128 interface.

* You have extended `cipher_context_t`, which is fair, to use _aes__ functions directly. However, I'm wondering if this obsoletes [`max_key_size`](https://github.com/RIOT-OS/RIOT/blob/master/sys/include/crypto/ciphers.h#L80) in ciphers.h. Do you have opinions on that? I know, the ciphers module is a beast...

Yes I think it does. I removed max_key_size. The check in ciphers.c is not important I think, since the specific modules check the key size anyways.

@PeterKietzmann
Copy link
Member

I did not create a general interface with a name like CIPHER_AES since this would break all code using the CIPHER_AES_128 interface

How? Can you explain in a bit more detail? If aes_interface was allocated with something like AES_KEY_SIZE_UNINITIALIZED and in aes_init (which is always called before computing a cipher) you set the keysize for the context (like you did here), what breaks?

@PeterKietzmann
Copy link
Member

Sadly it does not contain test vectors for CCM and OCB mode.

How about the Cryptographic Algorithm Validation Program CAVP?

The tests have the same origin as the original ones.

What is the origin?

@Ollrogge
Copy link
Contributor Author

How? Can you explain in a bit more detail? If aes_interface was allocated with something like AES_KEY_SIZE_UNINITIALIZED and in aes_init (which is always called before computing a cipher) you set the keysize for the context (like you did here), what breaks?

Sorry, I mean the cipher_id_t which would break other code in my opinion. Currently it is named CIPHER_AES_128 . If I change it to e.g. CIPHER_AES code like this would break.

I could however remove the interfaces and leave just 1 interface called aes_interface and have all ids point to the same interface. I think that is better ?

@Ollrogge
Copy link
Contributor Author

How about the Cryptographic Algorithm Validation Program CAVP?

Nice ! Thank you, these have cases for 192 and 256 bit keys. I will add these.

What is the origin?

this is where I took the additional tests for 192 and 256 bit keys for CBC, CTR and ECB mode from.

@PeterKietzmann
Copy link
Member

I could however remove the interfaces and leave just 1 interface called aes_interface and have all ids point to the same interface. I think that is better ?

Sounds reasonable!

@Ollrogge
Copy link
Contributor Author

Hey @PeterKietzmann,

I have added the additional AES-CCM test cases for 192 and 256 bit keys.
Additionally I changed the AES interfaces the way we talked about.

Copy link
Member

@PeterKietzmann PeterKietzmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ollrogge great improvement on the tests! Still some opens comments, though. Also, there is a ~500 Byte increase in the .text segment that I could not explain at first sight. Can you inspect that? Cosy (see #16083) might be of help.

sys/crypto/aes.c Outdated
@@ -45,12 +45,14 @@
*/
static const cipher_interface_t aes_interface = {
AES_BLOCK_SIZE,
AES_KEY_SIZE,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This must be considered as an API change which I would like to avoid here. My comment was only meant for clarification, not to propose such a change.

sys/crypto/aes.c Outdated
@@ -1037,7 +1038,7 @@ int aes_encrypt(const cipher_context_t *context, const uint8_t *plainBlock,
const AES_KEY *key = &aeskey;

res = aes_set_encrypt_key((unsigned char *)context->context,
AES_KEY_SIZE * 8, &aeskey);
context->key_size * 8, &aeskey);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if context->key_size is not initialized? This happens easily when a user does not utilize the ciphers module (see Fortuna RNG). Wouldn't be necessary with the old cipher_interface_t struct.

cipher->interface = cipher_id;
return cipher->interface->init(&cipher->context, key, key_size);
cipher->context.key_size = key_size;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would rather deal with that in a follow-up PR and focus on aes_* here (see comment about API change above)

@@ -22,30 +22,24 @@
int cipher_init(cipher_t *cipher, cipher_id_t cipher_id, const uint8_t *key,
uint8_t key_size)
{
if (key_size > cipher_id->max_key_size) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why remove that change?

}


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe don't commit any of the changes in ciphers.c

#if defined(MODULE_CRYPTO_3DES)
#define CIPHER_MAX_CONTEXT_SIZE 24
#elif defined(MODULE_CRYPTO_AES)
#if defined(MODULE_CRYPTO_3DES) || defined(MODULE_CRYPTO_AES)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before, CIPHER_MAX_CONTEXT_SIZE was 24 for MODULE_CRYPTO_3DES, now is 32.

-> What was the intention behind this?

sys/crypto/aes.c Outdated
@@ -1305,7 +1306,7 @@ int aes_decrypt(const cipher_context_t *context, const uint8_t *cipherBlock,
const AES_KEY *key = &aeskey;

res = aes_set_decrypt_key((unsigned char *)context->context,
AES_KEY_SIZE * 8, &aeskey);
context->key_size * 8, &aeskey);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

@@ -65,6 +63,7 @@ extern "C" {
* @brief the context for cipher-operations
*/
typedef struct {
uint8_t key_size; /**< key size used */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above.

@@ -76,9 +75,6 @@ typedef struct cipher_interface_st {
/** Blocksize of this cipher */
uint8_t block_size;

/** Maximum key size for this cipher */
uint8_t max_key_size;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it is redundant but probably not a task of this PR (see above)

int err;
uint8_t data[AES_BLOCK_SIZE];

err = aes_init(&ctx, TEST_0_KEY, sizeof(TEST_0_KEY));
err = cipher_init(&cipher, CIPHER_AES_128, TEST_0_KEY, sizeof(TEST_0_KEY));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not replace the plain tests of the AES API with tests of the ciphers API.

@leandrolanzieri
Copy link
Contributor

@Ollrogge great improvement on the tests! Still some opens comments, though. Also, there is a ~500 Byte increase in the .text segment that I could not explain at first sight. Can you inspect that? Cosy (see #16083) might be of help.

I checked the .elf files with elf_diff. Most of the difference comes from aes_set_encrypt_key. The implementation gets bigger. Looking at the code my guess is that, as now it is being called with a non-constant size (here), many of the conditionals can't be optimized on compile-time.

elf_diff_report

An option that might work is to conditionally enable support for the different sizes using pesudomodules. After that, one option could be a macro AES_SIZE(context), which would change between constant numbers or context->key_size depending on the enable modules. Another one would be to add IS_USED() checks on the ifs withing the function and do the optimization manually.

@Ollrogge
Copy link
Contributor Author

Another option @PeterKietzmann and I talked about was to leave the implementation as is and make AES_KEY_SIZE configurable via KConfig. This would be sufficient for my use case.

@PeterKietzmann
Copy link
Member

@leandrolanzieri +1

Could you show a short piece of pseudo code to assist implementing it?

@leandrolanzieri
Copy link
Contributor

I was thinking on defining the pseudomodules on the Makefiles, and then have a macro like:

#if IS_USED(MODULE_AES_128) && !IS_USED(MODULE_AES_192) && !IS_USED(MODULE_AES_256)
#  define KEY_SIZE(ctx) AES_KEY_SIZE_128
#elif !IS_USED(MODULE_AES_128) && IS_USED(MODULE_AES_192) && !IS_USED(MODULE_AES_256)
#  define KEY_SIZE(ctx) AES_KEY_SIZE_192
#elif !IS_USED(MODULE_AES_128) && !IS_USED(MODULE_AES_192) && IS_USED(MODULE_AES_256)
#  define KEY_SIZE(ctx) AES_KEY_SIZE_256
#else
#  define KEY_SIZE(ctx) ctx->keySize
#endif

@Ollrogge
Copy link
Contributor Author

Hey @leandrolanzieri,
I implemented the handling of the AES_KEY_SIZE as you suggested. Using 128 bit keys now only adds 24 Bytes to the .text section.

Additionally I removed all the changes in ciphers.c as you suggested @PeterKietzmann.

I hope its okay that I already squashed the commits. My fixups got really messy in the end.

@PeterKietzmann
Copy link
Member

#16253 was merged. Please update this PR as discussed offline.

@Ollrogge
Copy link
Contributor Author

Ollrogge commented Apr 6, 2021

I updated the PR based on the changes from #16253 . Using 128 bit keys now adds +8 bytes to the .text section compared to master.

While changing the CIPHERS_MAX_KEY_SIZE to be adjusted based on the key sizes used I noticed that it was 20 bytes before. I don't really understand why because 16 bytes should have been sufficient for 128 bit keys ? When using 16 bytes instead of 20 bytes for 128 bit keys the specific tests still pass.

sys/include/crypto/aes.h Outdated Show resolved Hide resolved
sys/include/crypto/ciphers.h Outdated Show resolved Hide resolved
makefiles/pseudomodules.inc.mk Show resolved Hide resolved
@leandrolanzieri
Copy link
Contributor

@Ollrogge thanks for updating the makefiles! It looks like just a couple of boards are overflowing after splitting the crypto tests. For those you can simply add them to the BOARD_INSUFFICIENT_MEMORY list, like so: https://github.com/RIOT-OS/RIOT/blob/master/tests/pkg_libcoap/Makefile.ci Also, you will need to rebase as there is a conflict with sys/Makefile.dep.

I think the test application names are not really descriptive, could you try to use a more precise name for them? Also, it would be nice to add some README.md in the apps to describe shortly what is tested on both applications, and perhaps a reference to where the test vectors come from.

@Ollrogge
Copy link
Contributor Author

@Ollrogge thanks for updating the makefiles! It looks like just a couple of boards are overflowing after splitting the crypto tests. For those you can simply add them to the BOARD_INSUFFICIENT_MEMORY list, like so: https://github.com/RIOT-OS/RIOT/blob/master/tests/pkg_libcoap/Makefile.ci Also, you will need to rebase as there is a conflict with sys/Makefile.dep.

I think the test application names are not really descriptive, could you try to use a more precise name for them? Also, it would be nice to add some README.md in the apps to describe shortly what is tested on both applications, and perhaps a reference to where the test vectors come from.

Rebased my changes onto upstream/master and added the overflowing boards to BOARD_INSUFFICIENT_MEMORY. I also added the README's to the test applications. I renamed sys_crypto test application to sys_crypto_aes_ccm because this test application is only testing AES_CCM. I don't really know how to rename the sys_crypto test application because it literally tests all parts of the crypto module and therefore the general name seems appropriate to me.

Since my latest fixup commit the Murdock test is failing completely. I don't really understand why ? Can you tell me what I did wrong ?

sys/Makefile.dep Outdated Show resolved Hide resolved
Copy link
Contributor

@leandrolanzieri leandrolanzieri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added one more comment regarding the dependency resolution. Otherwise it looks good. Feel free to squash after applying the fix @Ollrogge

sys/Makefile.dep Outdated Show resolved Hide resolved
@PeterKietzmann
Copy link
Member

My ACK holds, but I haven't followed the latest changes that deal with the size of the tests. @leandrolanzieri wanna merge?

@leandrolanzieri
Copy link
Contributor

I'm running tests of the affected modules, just to be on the safe side.

@fjmolinas
Copy link
Contributor

I'm running tests of the affected modules, just to be on the safe side.

Tested OpenWSN, nodes join fine (this is the part where crypto is used):

18:25:00 SUCCESS 5de6 [IEEE802154E] synchronized at slotOffset 0
18:25:00 SUCCESS 67eb [IEEE802154E] synchronized at slotOffset 0
18:25:07 INFO 5de6 [C6T] sending CJOIN request
18:25:08 VERBOSE New node: ae8d:fee1:60c7:cb8a. Creating new OSCORE context in /builds/tmp/RIOT/bin/oscore_context_ae8dfee160c7cb8a.json.
18:25:08 VERBOSE received JRC join request
18:25:08 VERBOSE 5de6 [PACKETFUNCTIONS] copy packet content to big packet (pkt len 1300 > max len 125)
18:25:08 VERBOSE 5de6 [PACKETFUNCTIONS] copy packet content to small packet (pkt len 33 < max len 125)
18:25:08 SUCCESS 5de6 [C6T] node joined
18:25:09 INFO 67eb [C6T] sending CJOIN request
18:25:10 VERBOSE New node: ae8d:fee1:e02c:e22d. Creating new OSCORE context in /builds/tmp/RIOT/bin/oscore_context_ae8dfee1e02ce22d.json.
18:25:10 VERBOSE received JRC join request
18:25:10 VERBOSE 67eb [PACKETFUNCTIONS] copy packet content to big packet (pkt len 1300 > max len 125)
18:25:10 VERBOSE 67eb [PACKETFUNCTIONS] copy packet content to small packet (pkt len 33 < max len 125)
18:25:10 SUCCESS 67eb [C6T] node joined
18:25:22 INFO 67eb [SIXTOP] sending a 6top request
18:25:22 INFO 5de6 [SIXTOP] sending a 6top request
18:25:23 SUCCESS 67eb [SIXTOP] sixtop return code RC_SUCCESS at sixtop state WAIT_ADDRESPONSE
18:25:24 SUCCESS 5de6 [SIXTOP] sixtop return code RC_SUCCESS at sixtop state WAIT_ADDRESPONSE
18:25:26 INFO received RPL DAO from bbbb:0:0:0:ae8d:fee1:60c7:cb8a
        - parents:
           bbbb:0:0:0:ae8d:fee1:208c:870d
        - children:
18:25:35 INFO received RPL DAO from bbbb:0:0:0:ae8d:fee1:60c7:cb8a
        - parents:
           bbbb:0:0:0:ae8d:fee1:208c:870d
        - children:
18:25:46 INFO received RPL DAO from bbbb:0:0:0:ae8d:fee1:60c7:cb8a
        - parents:
           bbbb:0:0:0:ae8d:fee1:208c:870d
        - children:

@leandrolanzieri leandrolanzieri added this to the Release 2021.07 milestone May 5, 2021
@leandrolanzieri
Copy link
Contributor

I have tested the following, which work as expected:

Copy link
Contributor

@leandrolanzieri leandrolanzieri left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes look good, the feature works as expected while keeping the default behaviour and code-size because of the optimizations in place. Tests have been greatly improved.

ACK! Thanks @Ollrogge for the contribution!

@leandrolanzieri leandrolanzieri added CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR and removed CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels May 5, 2021
@leandrolanzieri
Copy link
Contributor

And GO!

@leandrolanzieri leandrolanzieri merged commit d36628d into RIOT-OS:master May 5, 2021
};

typedef struct aes_key_st AES_KEY;
} AES_KEY;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not?

Suggested change
} AES_KEY;
} aes_key_t;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: crypto Area: Cryptographic libraries CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Reviewed: 1-fundamentals The fundamentals of the PR were reviewed according to the maintainer guidelines Reviewed: 2-code-design The code design of the PR was reviewed according to the maintainer guidelines Reviewed: 3-testing The PR was tested according to the maintainer guidelines Reviewed: 4-code-style The adherence to coding conventions by the PR were reviewed according to the maintainer guidelines Reviewed: 5-documentation The documentation details of the PR were reviewed according to the maintainer guidelines Type: new feature The issue requests / The PR implemements a new feature for RIOT
Projects
None yet
Development

Successfully merging this pull request may close these issues.

AES-256 support
5 participants