pk_wrap: rework signature extraction to work with small r and s values
There is a probability that r will be encoded as 31 or less bytes in DER,
so additional padding is added in such case.
Added a signature-part extraction function to tidy up the code further.
diff --git a/library/pk_wrap.c b/library/pk_wrap.c
index 0d1d91b..e33ea3f 100644
--- a/library/pk_wrap.c
+++ b/library/pk_wrap.c
@@ -480,69 +480,92 @@
#if defined(MBEDTLS_USE_PSA_CRYPTO)
/*
+ * Extract one signature part of an ASN.1 integer type to a given buffer
+ * and adjust padding according to part_size.
+ */
+static int extract_ecdsa_sig_part( unsigned char **from, const unsigned char *end,
+ unsigned char *to, size_t part_size )
+{
+ int ret;
+ size_t len_total, len_partial, zero_padding;
+
+ if( ( ret = mbedtls_asn1_get_tag( from, end, &len_partial,
+ MBEDTLS_ASN1_INTEGER ) ) != 0 )
+ {
+ return( ret );
+ }
+
+ while( **from == '\0' && len_partial > 0 )
+ {
+ ( *from )++;
+ len_partial--;
+ }
+
+ if( len_partial > part_size || len_partial == 0 )
+ return( MBEDTLS_ERR_PK_SIG_LEN_MISMATCH );
+
+ zero_padding = part_size - len_partial;
+ memcpy( to + zero_padding, *from, len_partial );
+ len_total = len_partial + zero_padding;
+ while( zero_padding > 0 )
+ {
+ zero_padding--;
+ to[zero_padding] = 0;
+ }
+
+ ( *from ) += len_partial;
+ return len_total;
+}
+
+/*
* Convert a signature from an ASN.1 sequence of two integers
* to a raw {r,s} buffer. Note: upon a successful call, the caller
* takes ownership of the sig->p buffer.
*/
static int extract_ecdsa_sig( unsigned char **p, const unsigned char *end,
- mbedtls_asn1_buf *sig )
+ mbedtls_asn1_buf *sig, size_t int_size )
{
- int ret, tag_type;
- size_t len_signature, len_partial;
+ int ret;
if( ( end - *p ) < 1 )
{
return( MBEDTLS_ERR_ASN1_OUT_OF_DATA );
}
- tag_type = **p;
- if( ( ret = mbedtls_asn1_get_tag( p, end, &len_partial,
- MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
- {
- return( ret );
- }
-
- if( ( ret = mbedtls_asn1_get_tag( p, end, &len_partial, MBEDTLS_ASN1_INTEGER ) )
- != 0 )
- return( ret );
-
- if( **p == '\0' )
- {
- ( *p )++;
- len_partial--;
- }
-
- sig->p = mbedtls_calloc( 2, len_partial );
+ sig->p = mbedtls_calloc( 2, int_size );
if( sig->p == NULL )
return( MBEDTLS_ERR_ASN1_ALLOC_FAILED );
- memcpy( sig->p, *p, len_partial );
- len_signature = len_partial;
- ( *p ) += len_partial;
- if( ( ret = mbedtls_asn1_get_tag( p, end, &len_partial,
- MBEDTLS_ASN1_INTEGER ) ) != 0 )
+ sig->tag = **p;
+
+ if( ( ret = mbedtls_asn1_get_tag( p, end, &sig->len,
+ MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
{
- mbedtls_free( sig->p );
- sig->p = NULL;
- return( ret );
+ goto cleanup;
}
- if( **p == '\0' )
+ /* Extract r */
+ if( ( ret = extract_ecdsa_sig_part( p, end, sig->p, int_size ) ) < 0)
{
- ( *p )++;
- len_partial--;
+ goto cleanup;
}
+ sig->len = ret;
- // Check if both parts are of the same size
- if( len_partial != len_signature )
- return( MBEDTLS_ERR_PK_SIG_LEN_MISMATCH );
+ /* Extract s */
+ if( ( ret = extract_ecdsa_sig_part( p, end, sig->p + sig->len, int_size ) ) < 0)
+ {
+ goto cleanup;
+ }
+ sig->len += ret;
- memcpy( sig->p + len_partial, *p, len_partial );
- len_signature += len_partial;
- sig->tag = tag_type;
- sig->len = len_signature;
- ( *p ) += len_partial;
return( 0 );
+
+cleanup:
+ mbedtls_free( sig->p );
+ sig->p = NULL;
+ sig->len = 0;
+ sig->tag = 0;
+ return( ret );
}
static psa_ecc_curve_t mbedtls_ecc_group_to_psa( mbedtls_ecp_group_id grpid )
@@ -637,7 +660,8 @@
psa_type = PSA_KEY_TYPE_ECC_PUBLIC_KEY( curve );
- if( ( ret = extract_ecdsa_sig( &p, p + sig_len, &signature ) ) != 0 )
+ if( ( ret = extract_ecdsa_sig( &p, p + sig_len, &signature,
+ ( ( (mbedtls_ecdsa_context *) ctx )->grp.nbits + 7) / 8 ) ) != 0 )
return( ret );
key_len = mbedtls_pk_write_pubkey_der( &key, buf, buf_len );
@@ -678,7 +702,7 @@
}
ret = 0;
- cleanup:
+cleanup:
psa_destroy_key( key_slot );
mbedtls_free( signature.p );
return( ret );