-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Conversation
…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 ) ) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 ) ) |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 ) ) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 )
There was a problem hiding this comment.
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 ); |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok
/bot run checks |
/bot run checks |
There was a problem hiding this 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, |
There was a problem hiding this comment.
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 =
)?
There was a problem hiding this comment.
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 ) ) |
There was a problem hiding this comment.
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. */ |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 ) ) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 */ |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 ); | ||
|
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 ); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
PKCS #11 Refactor
Description
Checklist:
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.