Skip to content
This repository has been archived by the owner on Dec 8, 2022. It is now read-only.

PKCS #11 Refactor #2008

Merged
merged 10 commits into from
May 21, 2020
Merged

PKCS #11 Refactor #2008

merged 10 commits into from
May 21, 2020

Conversation

lundinc2
Copy link
Contributor

@lundinc2 lundinc2 commented May 13, 2020

PKCS #11 Refactor

Description

  • Fixed various MISRA violations.
  • Refactored last two remaining functions with a too high GNU Complexity.

Checklist:

  • I have tested my changes. No regression in existing tests.
  • My code is Linted.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

lundinc2 added 6 commits May 13, 2020 15:53
* Fixed various MISRA violations.
* Refactored last two remaining functions with a too high GNU Complexity.
…mplexity of key generation attribute parsing.
@@ -899,13 +894,13 @@ static CK_RV prvEcKeyAttParse( CK_ATTRIBUTE_PTR pxAttribute,
}

/* private EC key attributes. */
if( ( xResult != CKR_OK ) && ( xIsPrivate == CK_TRUE ) )
if( ( xResult == CKR_ATTRIBUTE_VALUE_INVALID ) && ( xIsPrivate == CK_TRUE ) )
Copy link
Contributor

@qiutongs qiutongs May 15, 2020

Choose a reason for hiding this comment

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

Are you intentionally making this logic change? I mean xResult could be some other error, like CKR_TEMPLATE_INCONSISTENT.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, the issue was it would override error messages from an earlier failure, this default case indicates that nothing was caught by the above switch statement

Copy link
Contributor

Choose a reason for hiding this comment

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

ok

Copy link
Contributor

Choose a reason for hiding this comment

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

This approach seems risky from the start, though, because we're attempting to parse the data as a certain format without explicitly checking the attribute type. Safer would be to move these blocks into the switch/case above. That would also make this function easier to review and maintain, since it makes for a more typical structure.

As part of that, xResult could for example be initialized as CKR_OK. Then if you fall into the default switch/case, set it to ATTR_VALUE_INVALID.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have refactored this switch statement to be more clear

{
xResult = prvEcPrivKeyAttParse( pxAttribute, pxMbedContext );
}

/* public EC key attributes. */
if( ( xResult != CKR_OK ) && ( xIsPrivate == CK_FALSE ) )
else if( ( xResult == CKR_ATTRIBUTE_VALUE_INVALID ) && ( xIsPrivate == CK_FALSE ) )
Copy link
Contributor

Choose a reason for hiding this comment

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

similar here.

{
/* Do not write the last 6 bytes to key storage. */
pxDerKey[ MAX_LENGTH_KEY - lDerKeyLength + 1 ] -= 6;
lActualKeyLength -= 6;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it a bug in the old code? There seems no update to lActualKeyLength after initialization.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

this logic needs to be examined thoroughly, i didn't get a regression anywhere when i removed it, but we will need to dig deep into the mbedTLS APIs and how the EC keys are created / used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also i kept the logic here as it was originally, as i need to deep dive

Copy link
Contributor

Choose a reason for hiding this comment

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

In other words, by adding an empty public key to a "naked" private key, mbed is making it so that the resulting private key format is inconsistent with the PKCS #11 standard. We're therefore omitting the last six bytes in that case (i.e. because we're using mbed to encode the key, but we need our storage and I/O values to be conformant with P#11). However, this situation probably only arises when we're doing round-trip testing between keys generated by different modules.


if( xResult == CKR_OK )
{
if( 0 == memcmp( xLabel.pValue, pkcs11configLABEL_DEVICE_PRIVATE_KEY_FOR_TLS, xLabelLength ) )
if( 0 == strcmp( xLabel.pValue, pkcs11configLABEL_DEVICE_PRIVATE_KEY_FOR_TLS ) )
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it less safe if change to strcmp? Is it better to explicitly specify the length?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it should be OK, the second operator is a constant that is always NULL t terminated. MISRA complains when you use mem API with NULL terminated strings

Copy link
Contributor

@qiutongs qiutongs May 15, 2020

Choose a reason for hiding this comment

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

I concern the case when the length of pkcs11configLABEL_DEVICE_PRIVATE_KEY_FOR_TLS is larger than xLabelLength. You might access invalid memory.

Let's use strncmp:

strncmp( xLabel.pValue, pkcs11configLABEL_DEVICE_PRIVATE_KEY_FOR_TLS, xLabelLength )

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

{
PKCS11_PAL_GetObjectValueCleanup( pxObject, ulObjectLength );
}
PKCS11_PAL_GetObjectValueCleanup( pxObject, ulObjectLength );
Copy link
Contributor

Choose a reason for hiding this comment

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

So it is safe to always call cleanup even PKCS11_PAL_GetObjectValue returns error in line 1219?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It should always be safe, as the handle will be invalid if the other calls were unsuccessful.

Copy link
Contributor

Choose a reason for hiding this comment

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

ok

@lundinc2
Copy link
Contributor Author

/bot run checks

qiutongs
qiutongs previously approved these changes May 15, 2020
@lundinc2
Copy link
Contributor Author

/bot run checks

Copy link
Contributor

@dan4thewin dan4thewin left a comment

Choose a reason for hiding this comment

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

Sorry, I didn't get very far.

NULL, 0, /* Q */
NULL, 0, /* D */
NULL, 0 ); /* E */
lMbedReturn = mbedtls_rsa_import_raw( pxRsaContext,
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you describe the impact of these changes ( |= vs = )?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure! Originally all of these functions were called in a loop without checking their return value. The intention of the code was if one call failed, it wouldn't get overwritten from a call after it.

Now it is refactored to check the return value in each loop iteration. Also it seems a bit misleading to have the return value's get OR'd together, as it makes it seems the return value are related, but they are independent of each other.

Lastly, MISRA does not allow for bitwise operations on signed integers.

@@ -899,13 +894,13 @@ static CK_RV prvEcKeyAttParse( CK_ATTRIBUTE_PTR pxAttribute,
}

/* private EC key attributes. */
if( ( xResult != CKR_OK ) && ( xIsPrivate == CK_TRUE ) )
if( ( xResult == CKR_ATTRIBUTE_VALUE_INVALID ) && ( xIsPrivate == CK_TRUE ) )
Copy link
Contributor

Choose a reason for hiding this comment

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

This approach seems risky from the start, though, because we're attempting to parse the data as a certain format without explicitly checking the attribute type. Safer would be to move these blocks into the switch/case above. That would also make this function easier to review and maintain, since it makes for a more typical structure.

As part of that, xResult could for example be initialized as CKR_OK. Then if you fall into the default switch/case, set it to ATTR_VALUE_INVALID.

@@ -948,7 +943,7 @@ void prvFindObjectInListByLabel( uint8_t * pcLabel,
if( 0 == memcmp( pcLabel, xP11Context.xObjectList.xObjects[ ucIndex ].xLabel, xLabelLength ) )
{
*pxPalHandle = xP11Context.xObjectList.xObjects[ ucIndex ].xHandle;
*pxAppHandle = ucIndex + 1; /* Zero is not a valid handle, so let's offset by 1. */
*pxAppHandle = ucIndex + 1UL; /* Zero is not a valid handle, so let's offset by 1. */
Copy link
Contributor

Choose a reason for hiding this comment

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

Converting our internal storage pointer to and from the app handle is something that I've typically seen handled by static helper functions. That makes it easier to maintain and modify down the road, if necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay will follow up with a PR to move this to static helper function. Cutting a ticket to track this effort

{
/* Do not write the last 6 bytes to key storage. */
pxDerKey[ MAX_LENGTH_KEY - lDerKeyLength + 1 ] -= 6;
lActualKeyLength -= 6;
Copy link
Contributor

Choose a reason for hiding this comment

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

In other words, by adding an empty public key to a "naked" private key, mbed is making it so that the resulting private key format is inconsistent with the PKCS #11 standard. We're therefore omitting the last six bytes in that case (i.e. because we're using mbed to encode the key, but we need our storage and I/O values to be conformant with P#11). However, this situation probably only arises when we're doing round-trip testing between keys generated by different modules.

@@ -1214,7 +1220,6 @@ static CK_RV prvSaveDerKeyToPal( mbedtls_pk_context * pxMbedContext,

if( xResult == CKR_OK )
{
xFreeMemory = CK_TRUE;
Copy link
Contributor

Choose a reason for hiding this comment

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

We're still unconditionally calling vPortFree below, though, even in the case where the malloc failed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alfred pointed out that free handles the case when it is passed a NULL pointer, so there is no reason to check if malloc failed when freeing


if( xResult == CKR_OK )
{
if( 0 == memcmp( xLabel.pValue, pkcs11configLABEL_DEVICE_PRIVATE_KEY_FOR_TLS, xLabelLength ) )
if( 0 == strncmp( xLabel.pValue, pkcs11configLABEL_DEVICE_PRIVATE_KEY_FOR_TLS, xLabel.ulValueLen ) )
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't strncmp less efficient than memcmp in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

MISRA complains about NULL terminated strings being operated on by the mem family of functions

{
*pxPalHandle = PKCS11_PAL_FindObject( ( uint8_t * ) pkcs11configLABEL_DEVICE_PRIVATE_KEY_FOR_TLS, ( uint8_t ) pxLabel->ulValueLen );
xIsPrivate = CK_FALSE;
}
else
{
/* Unknown label passed to function */
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't an error condition be set here then?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The pxPalHandle is set to INVALID when initialized, so we will return an error

{
prvFindObjectInListByLabel( ( uint8_t * ) pkcs11configLABEL_DEVICE_PUBLIC_KEY_FOR_TLS, strlen( ( char * ) pkcs11configLABEL_DEVICE_PUBLIC_KEY_FOR_TLS ), &xPalHandle, &xAppHandle2 );

Copy link
Contributor

Choose a reason for hiding this comment

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

This is hard to review in GitHub with the wide columns. Is it over 80?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I'll make a follow up PR to condense all lines to 80 chars


if( lMbedTLSResult == 0 )
{
lMbedTLSResult = mbedtls_mpi_read_binary( &xS, &pucSignature[ 32 ], 32 );
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be great to stop using the hard-coded key length values for ECDSA as part of this refactoring.

Copy link
Contributor Author

@lundinc2 lundinc2 May 18, 2020

Choose a reason for hiding this comment

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

I have cut an issue to track this and will create a follow up PR.

@lundinc2 lundinc2 merged commit 896f879 into aws:master May 21, 2020
alfred2g pushed a commit to alfred2g/amazon-freertos that referenced this pull request May 21, 2020
* Fixed various MISRA violations.
* Refactored last two remaining functions with a too high GNU Complexity.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants