Skip to content

Streamline PSA key type encodings: prepare #333

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

18 changes: 0 additions & 18 deletions include/mbedtls/psa_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -378,24 +378,6 @@ static inline psa_ecc_curve_t mbedtls_psa_translate_ecc_group( mbedtls_ecp_group
}
}


#define MBEDTLS_PSA_ECC_KEY_BITS_OF_CURVE( curve ) \
( curve == PSA_ECC_CURVE_SECP192R1 ? 192 : \
curve == PSA_ECC_CURVE_SECP224R1 ? 224 : \
curve == PSA_ECC_CURVE_SECP256R1 ? 256 : \
curve == PSA_ECC_CURVE_SECP384R1 ? 384 : \
curve == PSA_ECC_CURVE_SECP521R1 ? 521 : \
curve == PSA_ECC_CURVE_SECP192K1 ? 192 : \
curve == PSA_ECC_CURVE_SECP224K1 ? 224 : \
curve == PSA_ECC_CURVE_SECP256K1 ? 256 : \
curve == PSA_ECC_CURVE_BRAINPOOL_P256R1 ? 256 : \
curve == PSA_ECC_CURVE_BRAINPOOL_P384R1 ? 384 : \
curve == PSA_ECC_CURVE_BRAINPOOL_P512R1 ? 512 : \
0 )

#define MBEDTLS_PSA_ECC_KEY_BYTES_OF_CURVE( curve ) \
( ( MBEDTLS_PSA_ECC_KEY_BITS_OF_CURVE( curve ) + 7 ) / 8 )

/* Translations for PK layer */

static inline int mbedtls_psa_err_translate_pk( psa_status_t status )
Expand Down
1 change: 1 addition & 0 deletions include/psa/crypto_values.h
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,7 @@
(type) == PSA_KEY_TYPE_DES ? 8 : \
(type) == PSA_KEY_TYPE_CAMELLIA ? 16 : \
(type) == PSA_KEY_TYPE_ARC4 ? 1 : \
(type) == PSA_KEY_TYPE_CHACHA20 ? 1 : \
0)

/** Vendor-defined algorithm flag.
Expand Down
40 changes: 18 additions & 22 deletions library/psa_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,20 @@ static psa_status_t psa_import_rsa_key( psa_key_type_t type,
#endif /* defined(MBEDTLS_RSA_C) && defined(MBEDTLS_PK_PARSE_C) */

#if defined(MBEDTLS_ECP_C)
static psa_status_t psa_prepare_import_ec_key( psa_ecc_curve_t curve,
mbedtls_ecp_keypair **p_ecp )
{
mbedtls_ecp_group_id grp_id = MBEDTLS_ECP_DP_NONE;
*p_ecp = mbedtls_calloc( 1, sizeof( mbedtls_ecp_keypair ) );
if( *p_ecp == NULL )
return( PSA_ERROR_INSUFFICIENT_MEMORY );
mbedtls_ecp_keypair_init( *p_ecp );

/* Load the group. */
grp_id = mbedtls_ecc_group_of_psa( curve );
return( mbedtls_to_psa_error(
mbedtls_ecp_group_load( &( *p_ecp )->grp, grp_id ) ) );
}

/* Import a public key given as the uncompressed representation defined by SEC1
* 2.3.3 as the content of an ECPoint. */
Expand All @@ -594,19 +608,11 @@ static psa_status_t psa_import_ec_public_key( psa_ecc_curve_t curve,
{
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
mbedtls_ecp_keypair *ecp = NULL;
mbedtls_ecp_group_id grp_id = mbedtls_ecc_group_of_psa( curve );

*p_ecp = NULL;
ecp = mbedtls_calloc( 1, sizeof( *ecp ) );
if( ecp == NULL )
return( PSA_ERROR_INSUFFICIENT_MEMORY );
mbedtls_ecp_keypair_init( ecp );

/* Load the group. */
status = mbedtls_to_psa_error(
mbedtls_ecp_group_load( &ecp->grp, grp_id ) );
status = psa_prepare_import_ec_key( curve, &ecp );
if( status != PSA_SUCCESS )
goto exit;

/* Load the public value. */
status = mbedtls_to_psa_error(
mbedtls_ecp_point_read_binary( &ecp->grp, &ecp->Q,
Expand All @@ -631,9 +637,7 @@ static psa_status_t psa_import_ec_public_key( psa_ecc_curve_t curve,
}
return( status );
}
#endif /* defined(MBEDTLS_ECP_C) */

#if defined(MBEDTLS_ECP_C)
/* Import a private key given as a byte string which is the private value
* in big-endian order. */
static psa_status_t psa_import_ec_private_key( psa_ecc_curve_t curve,
Expand All @@ -643,22 +647,14 @@ static psa_status_t psa_import_ec_private_key( psa_ecc_curve_t curve,
{
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
mbedtls_ecp_keypair *ecp = NULL;
mbedtls_ecp_group_id grp_id = mbedtls_ecc_group_of_psa( curve );

if( PSA_BITS_TO_BYTES( PSA_ECC_CURVE_BITS( curve ) ) != data_length )
return( PSA_ERROR_INVALID_ARGUMENT );

*p_ecp = NULL;
ecp = mbedtls_calloc( 1, sizeof( mbedtls_ecp_keypair ) );
if( ecp == NULL )
return( PSA_ERROR_INSUFFICIENT_MEMORY );
mbedtls_ecp_keypair_init( ecp );

/* Load the group. */
status = mbedtls_to_psa_error(
mbedtls_ecp_group_load( &ecp->grp, grp_id ) );
status = psa_prepare_import_ec_key( curve, &ecp );
if( status != PSA_SUCCESS )
goto exit;

/* Load the secret value. */
status = mbedtls_to_psa_error(
mbedtls_mpi_read_binary( &ecp->d, data, data_length ) );
Expand Down
11 changes: 8 additions & 3 deletions scripts/generate_psa_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,16 @@ def read_line(self, line):
# Other macro without parameter
return

_nonascii_re = re.compile(rb'[^\x00-\x7f]+')
_continued_line_re = re.compile(rb'\\\r?\n\Z')
def read_file(self, header_file):
for line in header_file:
while line.endswith('\\\n'):
m = re.search(self._continued_line_re, line)
while m:
cont = next(header_file)
line = line[:-2] + cont
line = line[:m.start(0)] + cont
m = re.search(self._continued_line_re, line)
line = re.sub(self._nonascii_re, rb'', line).decode('ascii')
self.read_line(line)

@staticmethod
Expand Down Expand Up @@ -380,7 +385,7 @@ def write_file(self, output_file):
def generate_psa_constants(header_file_names, output_file_name):
collector = MacroCollector()
for header_file_name in header_file_names:
with open(header_file_name) as header_file:
with open(header_file_name, 'rb') as header_file:
collector.read_file(header_file)
temp_file_name = output_file_name + '.tmp'
with open(temp_file_name, 'w') as output_file:
Expand Down
22 changes: 18 additions & 4 deletions tests/scripts/test_psa_constant_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,14 @@ class read_file_lines:
except that if process(line) raises an exception, then the read_file_lines
snippet annotates the exception with the file name and line number.
"""
def __init__(self, filename):
def __init__(self, filename, binary=False):
self.filename = filename
self.line_number = 'entry'
self.generator = None
self.binary = binary
def __enter__(self):
self.generator = enumerate(open(self.filename, 'r'))
self.generator = enumerate(open(self.filename,
'rb' if self.binary else 'r'))
return self
def __iter__(self):
for line_number, content in self.generator:
Expand Down Expand Up @@ -100,6 +102,8 @@ def __init__(self):
# Any function ending in _algorithm also gets added to
# self.algorithms.
'key_type': [self.key_types],
'block_cipher_key_type': [self.key_types],
'stream_cipher_key_type': [self.key_types],
'ecc_key_types': [self.ecc_curves],
'dh_key_types': [self.dh_groups],
'hash_algorithm': [self.hash_algorithms],
Expand Down Expand Up @@ -224,13 +228,15 @@ def parse_header_line(self, line):
if m.group(3):
self.argspecs[name] = self._argument_split(m.group(3))

_nonascii_re = re.compile(rb'[^\x00-\x7f]+')
def parse_header(self, filename):
"""Parse a C header file, looking for "#define PSA_xxx"."""
with read_file_lines(filename) as lines:
with read_file_lines(filename, binary=True) as lines:
for line in lines:
line = re.sub(self._nonascii_re, rb'', line).decode('ascii')
self.parse_header_line(line)

_macro_identifier_re = r'[A-Z]\w+'
_macro_identifier_re = re.compile(r'[A-Z]\w+')
def generate_undeclared_names(self, expr):
for name in re.findall(self._macro_identifier_re, expr):
if name not in self.all_declared:
Expand Down Expand Up @@ -385,6 +391,8 @@ def run_one(self, inputs, type_word):
outputs = output.decode('ascii').strip().split('\n')
self.count += len(expressions)
for expr, value, output in zip(expressions, values, outputs):
if self.options.show:
sys.stdout.write('{} {}\t{}\n'.format(type_word, value, output))
if normalize(expr) != normalize(output):
self.errors.append(self.Error(type=type_word,
expression=expr,
Expand Down Expand Up @@ -430,6 +438,12 @@ def main():
parser.add_argument('--program',
default='programs/psa/psa_constant_names',
help='Program to test')
parser.add_argument('--show',
action='store_true',
help='Keep the intermediate C file')
parser.add_argument('--no-show',
action='store_false', dest='show',
help='Don\'t show tested values (default)')
options = parser.parse_args()
headers = [os.path.join(options.include[0], h) for h in HEADERS]
inputs = gather_inputs(headers, TEST_SUITES)
Expand Down
8 changes: 8 additions & 0 deletions tests/suites/test_suite_psa_crypto.data
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,14 @@ PSA import/export EC secp256r1 public key: good
depends_on:MBEDTLS_PK_PARSE_C:MBEDTLS_PK_WRITE_C:MBEDTLS_ECP_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED
import_export:"04dea5e45d0ea37fc566232a508f4ad20ea13d47e4bf5fa4d54a57a0ba012042087097496efc583fed8b24a5b9be9a51de063f5a00a8b698a16fd7f29b5485f320":PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_CURVE_SECP256R1):PSA_KEY_USAGE_EXPORT:PSA_ALG_ECDSA_ANY:256:0:PSA_SUCCESS:1

PSA import/export EC secp521r1 public key: good
depends_on:MBEDTLS_PK_PARSE_C:MBEDTLS_PK_WRITE_C:MBEDTLS_ECP_C:MBEDTLS_ECP_DP_SECP521R1_ENABLED
import_export:"04001de142d54f69eb038ee4b7af9d3ca07736fd9cf719eb354d69879ee7f3c136fb0fbf9f08f86be5fa128ec1a051d3e6c643e85ada8ffacf3663c260bd2c844b6f5600cee8e48a9e65d09cadd89f235dee05f3b8a646be715f1f67d5b434e0ff23a1fc07ef7740193e40eeff6f3bcdfd765aa9155033524fe4f205f5444e292c4c2f6ac1":PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_CURVE_SECP521R1):PSA_KEY_USAGE_EXPORT:PSA_ALG_ECDSA_ANY:521:0:PSA_SUCCESS:1

PSA import/export EC brainpoolP256r1 public key: good
depends_on:MBEDTLS_PK_PARSE_C:MBEDTLS_PK_WRITE_C:MBEDTLS_ECP_C:MBEDTLS_ECP_DP_BP256R1_ENABLED
import_export:"04768c8cae4abca6306db0ed81b0c4a6215c378066ec6d616c146e13f1c7df809b96ab6911c27d8a02339f0926840e55236d3d1efbe2669d090e4c4c660fada91d":PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_CURVE_BRAINPOOL_P256R1):PSA_KEY_USAGE_EXPORT:PSA_ALG_ECDSA_ANY:256:0:PSA_SUCCESS:1

PSA import/export AES key: policy forbids export
depends_on:MBEDTLS_AES_C:MBEDTLS_CIPHER_MODE_CTR
import_export:"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":PSA_KEY_TYPE_AES:PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT:PSA_ALG_CTR:128:0:PSA_ERROR_NOT_PERMITTED:1
Expand Down
83 changes: 83 additions & 0 deletions tests/suites/test_suite_psa_crypto.function
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,22 @@ static const size_t INVALID_EXPORT_LENGTH = ~0U;
#undef KNOWN_SUPPORTED_CIPHER_KEY_TYPE
#endif

#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
int lifetime_is_secure_element( psa_key_lifetime_t lifetime )
{
/* At the moment, anything that isn't a built-in lifetime is either
* a secure element or unassigned. */
return( lifetime != PSA_KEY_LIFETIME_VOLATILE &&
lifetime != PSA_KEY_LIFETIME_PERSISTENT );
}
#else
int lifetime_is_secure_element( psa_key_lifetime_t lifetime )
{
(void) lifetime;
return( 0 );
}
#endif

/** Test if a buffer contains a constant byte value.
*
* `mem_is_char(buffer, c, size)` is true after `memset(buffer, c, size)`.
Expand Down Expand Up @@ -212,6 +228,69 @@ static int construct_fake_rsa_key( unsigned char *buffer,
return( len );
}

int check_key_attributes_sanity( psa_key_handle_t key )
{
int ok = 0;
psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT;
psa_key_lifetime_t lifetime;
psa_key_id_t id;
psa_key_type_t type;
psa_key_type_t bits;

PSA_ASSERT( psa_get_key_attributes( key, &attributes ) );
lifetime = psa_get_key_lifetime( &attributes );
id = psa_get_key_id( &attributes );
type = psa_get_key_type( &attributes );
bits = psa_get_key_bits( &attributes );

/* Persistence */
if( lifetime == PSA_KEY_LIFETIME_VOLATILE )
TEST_ASSERT( id == 0 );
else
{
TEST_ASSERT(
( PSA_KEY_ID_USER_MIN <= id && id <= PSA_KEY_ID_USER_MAX ) ||
( PSA_KEY_ID_USER_MIN <= id && id <= PSA_KEY_ID_USER_MAX ) );
}
#if defined(MBEDTLS_PSA_CRYPTO_SE_C)
/* randomly-generated 64-bit constant, should never appear in test data */
psa_key_slot_number_t slot_number = 0xec94d4a5058a1a21;
psa_status_t status = psa_get_key_slot_number( &attributes, &slot_number );
if( lifetime_is_secure_element( lifetime ) )
{
/* Mbed Crypto currently always exposes the slot number to
* applications. This is not mandated by the PSA specification
* and may change in future versions. */
TEST_EQUAL( status, 0 );
TEST_ASSERT( slot_number != 0xec94d4a5058a1a21 );
}
else
{
TEST_EQUAL( status, PSA_ERROR_INVALID_ARGUMENT );
}
#endif

/* Type and size */
TEST_ASSERT( type != 0 );
TEST_ASSERT( bits != 0 );
TEST_ASSERT( bits <= PSA_MAX_KEY_BITS );
if( PSA_KEY_TYPE_IS_UNSTRUCTURED( type ) )
TEST_ASSERT( bits % 8 == 0 );

/* MAX macros concerning specific key types */
if( PSA_KEY_TYPE_IS_ECC( type ) )
TEST_ASSERT( bits <= PSA_VENDOR_ECC_MAX_CURVE_BITS );
else if( PSA_KEY_TYPE_IS_RSA( type ) )
TEST_ASSERT( bits <= PSA_VENDOR_RSA_MAX_KEY_BITS );
TEST_ASSERT( PSA_BLOCK_CIPHER_BLOCK_SIZE( type ) <= PSA_MAX_BLOCK_CIPHER_BLOCK_SIZE );

ok = 1;

exit:
psa_reset_key_attributes( &attributes );
return( ok );
}

int exercise_mac_setup( psa_key_type_t key_type,
const unsigned char *key_bytes,
size_t key_length,
Expand Down Expand Up @@ -1021,6 +1100,10 @@ static int exercise_key( psa_key_handle_t handle,
psa_algorithm_t alg )
{
int ok;

if( ! check_key_attributes_sanity( handle ) )
return( 0 );

if( alg == 0 )
ok = 1; /* If no algorihm, do nothing (used for raw data "keys"). */
else if( PSA_ALG_IS_MAC( alg ) )
Expand Down
20 changes: 10 additions & 10 deletions tests/suites/test_suite_psa_crypto_metadata.data
Original file line number Diff line number Diff line change
Expand Up @@ -315,25 +315,25 @@ key_type:PSA_KEY_TYPE_HMAC:KEY_TYPE_IS_UNSTRUCTURED
Key type: secret for key derivation
key_type:PSA_KEY_TYPE_DERIVE:KEY_TYPE_IS_UNSTRUCTURED

Key type: AES
Block cipher key type: AES
depends_on:MBEDTLS_AES_C
key_type:PSA_KEY_TYPE_AES:KEY_TYPE_IS_UNSTRUCTURED
block_cipher_key_type:PSA_KEY_TYPE_AES:16

Key type: DES
Block cipher key type: DES
depends_on:MBEDTLS_DES_C
key_type:PSA_KEY_TYPE_DES:KEY_TYPE_IS_UNSTRUCTURED
block_cipher_key_type:PSA_KEY_TYPE_DES:8

Key type: Camellia
Block cipher key type: Camellia
depends_on:MBEDTLS_CAMELLIA_C
key_type:PSA_KEY_TYPE_CAMELLIA:KEY_TYPE_IS_UNSTRUCTURED
block_cipher_key_type:PSA_KEY_TYPE_CAMELLIA:16

Key type: ARC4
Stream cipher key type: ARC4
depends_on:MBEDTLS_ARC4_C
key_type:PSA_KEY_TYPE_ARC4:KEY_TYPE_IS_UNSTRUCTURED
stream_cipher_key_type:PSA_KEY_TYPE_ARC4

Key type: ChaCha20
Stream cipher key type: ChaCha20
depends_on:MBEDTLS_CHACHA20_C
key_type:PSA_KEY_TYPE_CHACHA20:KEY_TYPE_IS_UNSTRUCTURED
stream_cipher_key_type:PSA_KEY_TYPE_CHACHA20

Key type: RSA public key
depends_on:MBEDTLS_RSA_C
Expand Down
27 changes: 27 additions & 0 deletions tests/suites/test_suite_psa_crypto_metadata.function
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,33 @@ void key_type( int type_arg, int classification_flags )
}
/* END_CASE */

/* BEGIN_CASE */
void block_cipher_key_type( int type_arg, int block_size_arg )
{
psa_key_type_t type = type_arg;
size_t block_size = block_size_arg;

test_key_type( type_arg, KEY_TYPE_IS_UNSTRUCTURED );

TEST_EQUAL( type & PSA_KEY_TYPE_CATEGORY_MASK,
PSA_KEY_TYPE_CATEGORY_SYMMETRIC );
TEST_EQUAL( PSA_BLOCK_CIPHER_BLOCK_SIZE( type ), block_size );
}
/* END_CASE */

/* BEGIN_CASE */
void stream_cipher_key_type( int type_arg )
{
psa_key_type_t type = type_arg;

test_key_type( type_arg, KEY_TYPE_IS_UNSTRUCTURED );

TEST_EQUAL( type & PSA_KEY_TYPE_CATEGORY_MASK,
PSA_KEY_TYPE_CATEGORY_SYMMETRIC );
TEST_EQUAL( PSA_BLOCK_CIPHER_BLOCK_SIZE( type ), 1 );
}
/* END_CASE */

/* BEGIN_CASE */
void ecc_key_types( int curve_arg, int curve_bits_arg )
{
Expand Down
Loading