Skip to content

Device key implementation #6642

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

Merged
merged 18 commits into from
May 24, 2018
Merged

Conversation

yossi2le
Copy link
Contributor

Description

This PR introducing the device key implementation.
The DeviceKey is a mechanism that implements key derivations from a Root Of Trust (ROT) without ever exposing the ROT itself. In DeviceKey there are two API's, one for key derivation of 128 or 256 bits keys and one for injecting the ROT. injection of ROT is needed only if True Random Generator (TRNG) is not supported.

More details and example can be found in the following repository:
https://github.com/ARMmbed/mbed-os-example-devicekey

Pull request type

[ ] Fix
[ ] Refactor
[ ] New target
[X ] Feature
[ ] Breaking change

return instance;
}

virtual ~DeviceKey();
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is dtor virtual? I do not see any virtual methods, not intended to be a base class ?

* @param output buffer for the CMAC result.
* @return 0 on success, negative error code on failure
*/
int calc_cmac(const unsigned char *input, size_t isize, uint32_t *ikey_buff, int ikey_size, unsigned char *output);
Copy link
Contributor

Choose a reason for hiding this comment

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

why chosen calc rather calculate ?

* @param ikey_type type of the required key. Type must be 16 bytes or 32 bytes.
* @return 0 on success, negative error code on failure
*/
int device_key_derived_key(const unsigned char *isalt, size_t isalt_size, unsigned char *output, uint16_t ikey_type);
Copy link
Contributor

Choose a reason for hiding this comment

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

device_key_derived_key - I have to read the description what it does, can this have more meaningful name? From the description - generate_derived_key() or similar? Device key is object's name.

#define DEVICE_KEY_16BYTE 16
#define DEVICE_KEY_32BYTE 32

enum DeviceKeyStatus {
Copy link
Contributor

Choose a reason for hiding this comment

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

These are return types:

  • why then methods have int return type ?
  • not part of the class?

Copy link
Contributor

Choose a reason for hiding this comment

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

Using the enum can cause type problems in C++. Especially when errors need to be propogated upwards. Just using int simplifies a lot of things for users.

Either way @SenRamakri has bigger plans with error codes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@0xc0170 @geky
There is a process now to add Error Codes to mbed-os and when this happen, we will change the implementation to use the new error codes mechanism.
Until then We thought it is better for our code to use an internal error code mechanism and return int in order to excuse the caller from the need of using our enum.

Copy link
Contributor

@0xc0170 0xc0170 Apr 16, 2018

Choose a reason for hiding this comment

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

Sounds good to me. Still does not answer my second question why is this not part of the class itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@0xc0170
Actually only because it mimics the idea of the future solution of _MbedErrorStatus enum. I don't mind to make it part of the class but it’s not the way it would be implemented in the future.

Copy link

@0Grit 0Grit Apr 16, 2018

Choose a reason for hiding this comment

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

Personally in these situations I still use the enum but then use a typdef in any location where data might be used by another processor.
Then again that's what I do for C not C++.

Worst case I'd rather a typedef be used than a bare int; to give better context for anyone using it.

* @ingroup drivers
*/

class DeviceKey : private mbed::NonCopyable<DeviceKey> {
Copy link
Contributor

Choose a reason for hiding this comment

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

What about interrupt safety/thread safety? There should be a note for a driver

@0xc0170
Copy link
Contributor

0xc0170 commented Apr 16, 2018

@yossi2le Who should review this addition? I would assume nvstore and mbedtls teams. Let me know, I'll assign reviewers

@0xc0170 0xc0170 requested a review from SenRamakri April 16, 2018 07:14
@0xc0170
Copy link
Contributor

0xc0170 commented Apr 16, 2018

Please review Ci failures (travis docs and events)

@geky
Copy link
Contributor

geky commented Apr 16, 2018

NVStore is only available if the device has programmable flash (DEVICE_FLASH macro).
https://github.com/ARMmbed/mbed-os/blob/master/features/nvstore/source/nvstore.h#L21-L26

Does Device Key need the same? (Cause of build failures)

@yossi2le
Copy link
Contributor Author

@0xc0170 regarding the review of nvstore and mbedtls. mbedtls indeed should be assigned to this review. nvstore from the other hand is under my team (storage) responsibility.
thanks.

@yossi2le
Copy link
Contributor Author

@geky. Thanks for that, you absolutely right about the DEVICE_FLASH macro.

@ciarmcom
Copy link
Member

ARM Internal Ref: IOTSSL-2222

goto finish;
}

return DEVICEKEY_SUCCESS;

Choose a reason for hiding this comment

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

The cipher context must be freed on case of success, too. Suggestion: just set ret = DEVICEKEY_SUCCESS here.

Copy link
Contributor Author

@yossi2le yossi2le Apr 22, 2018

Choose a reason for hiding this comment

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

@hanno-arm
10x for that. it will be fix.


finish:
mbedtls_cipher_free( &ctx );
return ret;

Choose a reason for hiding this comment

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

You are returning a mixture of own error codes and Mbed TLS specific ones here -- is this intended?

trng_init(&trng_obj);

int ret = trng_get_bytes(&trng_obj, (unsigned char *)output, in_size, &size);
if (DEVICEKEY_SUCCESS != ret || in_size != size) {

Choose a reason for hiding this comment

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

This is not valid use of the TRNG API -- trng_get_bytes is allowed to return less than what was requested, and in this case it's the responsibility of the caller to repeat the call until enough entropy has been gathered.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@hanno-arm
10x for that. Actually the documentation is not clear enough and I think missing an example. I have fixed it. Please review and let me know if this answer the API requirement.


finish:
if (double_size_salt != NULL) {
delete[] double_size_salt;

Choose a reason for hiding this comment

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

Please reliably zeroize double_size_salt to avoid leakage in memory. Consider e.g. mbedtls_zeroize.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@hanno-arm
mbedtls_zeroize is static function duplicate inside every mbedtls module. For now I have copied it as static function to my code. I think we should discuss on where this function should sit in order to have only one copy for everyone.

Choose a reason for hiding this comment

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

@yossi2le Moving the zeroize function to a separate compilation unit is being considered at the moment. For now, I suggest we live with the additional copy here.

}

//Double the salt size cause cmac always return just 16 bytes
double_size_salt = new unsigned char[isalt_size * 2];

Choose a reason for hiding this comment

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

What it is the reason for the use of this doubled salt, as opposed to a different modification of the salt to generate the second half of the key?

Are you following some standard here?

Choose a reason for hiding this comment

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

@hanno-arm We are open to proposals. We do not have any preferences here. If you see any problem with the current method please let us know.

}

//First we read if key exist. If it is exists, we return DEVICEKEY_ALREADY_EXIST error
uint32_t read_key[DEVICE_KEY_32BYTE / sizeof(uint32_t)] = {0};

Choose a reason for hiding this comment

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

Why is read_key not defined as a uint8_t array?

Choose a reason for hiding this comment

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

@hanno-arm This is because NVStore had API of uint32_t. This is internal allocation therefore has no impact externally. Do you have any concerns?

Copy link

@hanno-becker hanno-becker Apr 23, 2018

Choose a reason for hiding this comment

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

@dannybenor No, I don't have concerns, I only found it surprising when I read it. Thanks for your answer!

Copy link

@hanno-becker hanno-becker left a comment

Choose a reason for hiding this comment

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

The TRNG API usage is not used correctly, and the salt needs to be reliably zeroized in the key derivation function.

Copy link

@hanno-becker hanno-becker left a comment

Choose a reason for hiding this comment

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

I would like to request more information on why CMAC was used here for the key generation. A natural alternative would be to use Mbed TLS' implementation of PKCS5-PBKDF2 instead (HKDF is not yet implemented in Mbed TLS, unfortunately). This function takes password and salt of variable lengths and generates a key of variable length from it using HMAC as the underlying primitive. Using this seems straightforward and would also allow to make the DeviceKey interface more flexible by supporting an arbitrary output key length, which might be useful in the future.

@mbed-ci
Copy link

mbed-ci commented May 22, 2018

Build : FAILURE

Build number : 2107
Build artifacts/logs : http://mbed-os.s3-website-eu-west-1.amazonaws.com/?prefix=builds/6642/

@cmonr
Copy link
Contributor

cmonr commented May 22, 2018

@yossi2le @dannybenor Fyi, it looks like we're still facing some CI node issues. Will restart once things are stable again.

@cmonr
Copy link
Contributor

cmonr commented May 22, 2018

/morph build

@mbed-ci
Copy link

mbed-ci commented May 22, 2018

Build : SUCCESS

Build number : 2108
Build artifacts/logs : http://mbed-os.s3-website-eu-west-1.amazonaws.com/?prefix=builds/6642/

Triggering tests

/morph test
/morph uvisor-test
/morph export-build
/morph mbed2-build

@mbed-ci
Copy link

mbed-ci commented May 22, 2018

@mbed-ci
Copy link

mbed-ci commented May 23, 2018

@0xc0170
Copy link
Contributor

0xc0170 commented May 23, 2018

Starting tests now. We identified that some boards require bigger timeout (test takes at least 40 seconds for some nucleo and GCC ARM, tested locally).

/morph build

@mbed-ci
Copy link

mbed-ci commented May 23, 2018

Build : SUCCESS

Build number : 2123
Build artifacts/logs : http://mbed-os.s3-website-eu-west-1.amazonaws.com/?prefix=builds/6642/

Triggering tests

/morph test
/morph uvisor-test
/morph export-build
/morph mbed2-build

@cmonr
Copy link
Contributor

cmonr commented May 23, 2018

Shuffling around some jobs.

/morph export-build

@cmonr
Copy link
Contributor

cmonr commented May 23, 2018

/morph test

@mbed-ci
Copy link

mbed-ci commented May 23, 2018

Test : SUCCESS

Build number : 1921
Test logs :http://mbed-os-logs.s3-website-us-west-1.amazonaws.com/?prefix=logs/6642/1921

@mbed-ci
Copy link

mbed-ci commented May 24, 2018

@cmonr
Copy link
Contributor

cmonr commented May 24, 2018

/morph uvisor-test

2 similar comments
@0xc0170
Copy link
Contributor

0xc0170 commented May 24, 2018

/morph uvisor-test

@0xc0170
Copy link
Contributor

0xc0170 commented May 24, 2018

/morph uvisor-test

@dannybenor
Copy link

Approved from my side


do {

mbedtls_cipher_init(&ctx);

Choose a reason for hiding this comment

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

@yossi2le What is the reason you moved init+free inside this loop?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@hanno-arm
Two devices was failed during CI tests in the second round of the loop when we did CMC update.
We have suspected that maybe free and init will clear up some resources and move it into the loop what actually solved the problem.

Copy link

@hanno-becker hanno-becker May 24, 2018

Choose a reason for hiding this comment

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

@yossi2le That shouldn't happen and should be investigated. In any case, free + init in every iteration is valid usage, too, for the new version is certainly fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.