From c289bf1a308bf7d0e4abfdfff860774ba85d7969 Mon Sep 17 00:00:00 2001 From: Andrzej Kurek Date: Tue, 23 Jan 2018 06:10:53 -0500 Subject: [PATCH] Add ECDSA verify Add tests for external verification when signing is done both internally and externally --- include/mbedtls/ecdsa.h | 27 ++- library/ecdsa.c | 65 +++++- library/pkcs11_client.c | 80 +++++++- tests/suites/test_suite_pkcs11_client.data | 8 + .../suites/test_suite_pkcs11_client.function | 191 ++++++++++++++++++ 5 files changed, 364 insertions(+), 7 deletions(-) diff --git a/include/mbedtls/ecdsa.h b/include/mbedtls/ecdsa.h index c0088db5e..ed88c8a3f 100644 --- a/include/mbedtls/ecdsa.h +++ b/include/mbedtls/ecdsa.h @@ -235,6 +235,27 @@ int mbedtls_ecdsa_write_signature_det( mbedtls_ecdsa_context *ctx, #endif /* MBEDTLS_DEPRECATED_REMOVED */ #endif /* MBEDTLS_ECDSA_DETERMINISTIC */ +/** + * \brief Convert a signature from ASN.1 to a raw concatenation + * of {r,s} + * + * \param sig Signature to be converted + * \param ssize Size of the passed buffer + * \param byte_len Length of a single number of the signature + * \param buf Buffer pointer + * \param slen Size of the written signature + * + * \note The size of the buffer \c ssize should be at least + * 2*byte_len bytes long, otherwise this function will + * return an error. + * + * \return 0 if successful, or a MBEDTLS_ERR_ECP_BAD_INPUT_DATA or + * MBEDTLS_ERR_ASN1_LENGTH_MISMATCH error code + * + */ +int mbedtls_ecdsa_signature_to_raw( const unsigned char *sig, + size_t ssize, uint16_t byte_len, + unsigned char *buf, size_t* slen ); /** * \brief Convert a signature from numbers to ASN.1 * @@ -253,9 +274,9 @@ int mbedtls_ecdsa_write_signature_det( mbedtls_ecdsa_context *ctx, * or a MBEDTLS_ERR_MPI_XXX or MBEDTLS_ERR_ASN1_XXX error code * */ -int ecdsa_signature_to_asn1( const mbedtls_mpi *r, const mbedtls_mpi *s, - unsigned char *sig, size_t *slen, - size_t ssize ); +int mbedtls_ecdsa_signature_to_asn1( const mbedtls_mpi *r, + const mbedtls_mpi *s, unsigned char *sig, + size_t *slen, size_t ssize ); /** * \brief Read and verify an ECDSA signature diff --git a/library/ecdsa.c b/library/ecdsa.c index fdd0afb46..645fbb52e 100644 --- a/library/ecdsa.c +++ b/library/ecdsa.c @@ -286,10 +286,71 @@ cleanup: } #endif /* MBEDTLS_ECDSA_VERIFY_ALT */ +/* + * Convert a signature to a raw concatenation of {r, s} + */ +int mbedtls_ecdsa_signature_to_raw( const unsigned char *sig, + size_t ssize, uint16_t byte_len, + unsigned char *buf, size_t* slen ) +{ + int ret; + unsigned char *p = (unsigned char *) sig; + const unsigned char *end = sig + ssize; + size_t len; + mbedtls_mpi r, s; + + if( 2 * byte_len > ssize ) + { + return MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + } + + mbedtls_mpi_init( &r ); + mbedtls_mpi_init( &s ); + + if( ( ret = mbedtls_asn1_get_tag( &p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) + { + ret += MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + goto cleanup; + } + + if( p + len != end ) + { + ret = MBEDTLS_ERR_ECP_BAD_INPUT_DATA + + MBEDTLS_ERR_ASN1_LENGTH_MISMATCH; + goto cleanup; + } + + if( ( ret = mbedtls_asn1_get_mpi( &p, end, &r ) ) != 0 || + ( ret = mbedtls_asn1_get_mpi( &p, end, &s ) ) != 0 ) + { + ret += MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + goto cleanup; + } + p = (unsigned char *) buf; + if( ( ret = mbedtls_mpi_write_binary(&r, p, byte_len) ) ) + { + ret += MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + goto cleanup; + } + p += byte_len; + if( ( ret = mbedtls_mpi_write_binary(&s, p, byte_len) ) ) + { + ret += MBEDTLS_ERR_ECP_BAD_INPUT_DATA; + goto cleanup; + } + *slen = 2*byte_len; + cleanup: + mbedtls_mpi_free( &r ); + mbedtls_mpi_free( &s ); + + return( ret ); +} + /* * Convert a signature (given by context) to ASN.1 */ -int ecdsa_signature_to_asn1( const mbedtls_mpi *r, const mbedtls_mpi *s, +int mbedtls_ecdsa_signature_to_asn1( const mbedtls_mpi *r, const mbedtls_mpi *s, unsigned char *sig, size_t *slen, size_t ssize ) { int ret; @@ -339,7 +400,7 @@ int mbedtls_ecdsa_write_signature( mbedtls_ecdsa_context *ctx, mbedtls_md_type_t hash, hlen, f_rng, p_rng ) ); #endif - MBEDTLS_MPI_CHK( ecdsa_signature_to_asn1( &r, &s, sig, slen, ssize ) ); + MBEDTLS_MPI_CHK( mbedtls_ecdsa_signature_to_asn1( &r, &s, sig, slen, ssize ) ); cleanup: mbedtls_mpi_free( &r ); diff --git a/library/pkcs11_client.c b/library/pkcs11_client.c index 92120c6ce..70cc0de9a 100644 --- a/library/pkcs11_client.c +++ b/library/pkcs11_client.c @@ -209,7 +209,7 @@ static int pkcs11_sign( void *ctx_arg, } /* The signature buffer is guaranteed to have enough room for the encoded signature by the pk_sign interface. */ - if( ecdsa_signature_to_asn1( &r, &s, sig, sig_len, sig_size ) != 0 ) + if( mbedtls_ecdsa_signature_to_asn1( &r, &s, sig, sig_len, sig_size ) != 0 ) { rv = CKR_GENERAL_ERROR; goto ecdsa_exit; @@ -231,12 +231,88 @@ exit: return( pkcs11_err_to_mbedtls_pk_err( rv ) ); } +static int pkcs11_verify( void *ctx_arg, + mbedtls_md_type_t md_alg, + const unsigned char *hash, size_t hash_len, + const unsigned char *sig, size_t sig_len) +{ + mbedtls_pk_pkcs11_context_t *ctx = ctx_arg; + CK_RV rv; + CK_MECHANISM mechanism = {0, NULL_PTR, 0}; + unsigned char *decoded_sig = NULL_PTR; + size_t decoded_sig_len; + + /* This function takes size_t arguments but the underlying layer + takes unsigned long. Either type may be smaller than the other. + Legitimate values won't overflow either type but we still need + to check for overflow for robustness. */ + if( hash_len > (CK_ULONG)( -1 ) ) + return( MBEDTLS_ERR_PK_BAD_INPUT_DATA ); + + switch( ctx->key_type ) + { +#if defined(MBEDTLS_RSA_C) + case MBEDTLS_PK_RSA: + switch( md_alg ) + { + case MBEDTLS_MD_MD5: + mechanism.mechanism = CKM_MD5_RSA_PKCS; + break; + case MBEDTLS_MD_SHA1: + mechanism.mechanism = CKM_SHA1_RSA_PKCS; + break; + case MBEDTLS_MD_SHA256: + mechanism.mechanism = CKM_SHA256_RSA_PKCS; + break; + case MBEDTLS_MD_SHA384: + mechanism.mechanism = CKM_SHA384_RSA_PKCS; + break; + case MBEDTLS_MD_SHA512: + mechanism.mechanism = CKM_SHA512_RSA_PKCS; + break; + default: + return( MBEDTLS_ERR_PK_INVALID_ALG ); + } + break; +#endif /* MBEDTLS_RSA_C */ +#if defined(MBEDTLS_ECDSA_C) + case MBEDTLS_PK_ECKEY: + mechanism.mechanism = CKM_ECDSA; + break; +#endif /* MBEDTLS_ECDSA_C */ + default: + return( MBEDTLS_ERR_PK_UNKNOWN_PK_ALG ); + } + if( mechanism.mechanism == CKM_ECDSA ) + { + uint16_t byte_len = ( ( ctx->bit_length + 7 ) / 8 ); + decoded_sig = malloc( 2 * byte_len ); + if( mbedtls_ecdsa_signature_to_raw( sig, sig_len, byte_len, + decoded_sig, &decoded_sig_len ) != 0 ) + { + rv = CKR_GENERAL_ERROR; + goto exit; + } + } + rv = C_VerifyInit( ctx->hSession, &mechanism, ctx->hPublicKey ); + if( rv != CKR_OK ) + goto exit; + rv = C_Verify( ctx->hSession, (CK_BYTE_PTR) hash, hash_len, + decoded_sig, decoded_sig_len ); + if( rv != CKR_OK ) + goto exit; + +exit: + free(decoded_sig); + return( pkcs11_err_to_mbedtls_pk_err( rv ) ); +} + static const mbedtls_pk_info_t mbedtls_pk_pkcs11_info = MBEDTLS_PK_OPAQUE_INFO_1( "pkcs11" , pkcs11_pk_get_bitlen , pkcs11_pk_can_do //can_do , pkcs11_pk_signature_size - , NULL //pkcs11_verify + , pkcs11_verify , pkcs11_sign , NULL //pkcs11_decrypt , NULL //pkcs11_encrypt diff --git a/tests/suites/test_suite_pkcs11_client.data b/tests/suites/test_suite_pkcs11_client.data index fb47f5174..33a75c36a 100644 --- a/tests/suites/test_suite_pkcs11_client.data +++ b/tests/suites/test_suite_pkcs11_client.data @@ -5,3 +5,11 @@ pk_import_sign:"data_files/server3.key" PKCS#11 ECDSA generate and sign depends_on:MBEDTLS_PK_C:MBEDTLS_ECDSA_C pk_generate_sign:MBEDTLS_PK_ECDSA + +PKCS#11 ECDSA generate, sign and verify with Cryptoki +depends_on:MBEDTLS_PK_C:MBEDTLS_ECDSA_C +pk_signX_verifyX:MBEDTLS_PK_ECDSA + +PKCS#11 ECDSA import, sign with MbedTLS and verify with Cryptoki +depends_on:MBEDTLS_PK_C:MBEDTLS_ECDSA_C +pk_import_signI_verifyX:"data_files/server3.key" diff --git a/tests/suites/test_suite_pkcs11_client.function b/tests/suites/test_suite_pkcs11_client.function index 1dfe70d98..e14996b78 100644 --- a/tests/suites/test_suite_pkcs11_client.function +++ b/tests/suites/test_suite_pkcs11_client.function @@ -323,3 +323,194 @@ exit: mbedtls_pk_free( &transparent_ctx ); } /* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_PK_C:MBEDTLS_SHA256_C */ +void pk_signX_verifyX( int key_type ) +{ + /* Sign with cryptoki, convert to mbedTLS format and save, + verify by cryptoki with a conversion to a raw, concatenated + format by the engine. */ + mbedtls_pk_context pkcs11_ctx; + mbedtls_pk_context transparent_ctx; + CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hPublicKey = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hPrivateKey = CK_INVALID_HANDLE; + unsigned char hash_value[32] = "Fake hash, it doesn't matter...."; + unsigned char sig_buffer[RSA_KEY_SIZE_BYTES]; + size_t sig_length = sizeof( sig_buffer ); + + mbedtls_pk_init( &pkcs11_ctx ); + mbedtls_pk_init( &transparent_ctx ); + + /* Initialize cryptoki and generate a key in the token */ + hSession = pkcs11_init( ); + TEST_ASSERT( hSession != CK_INVALID_HANDLE ); + + CK_ASSERT( pkcs11_generate_key( key_type, + hSession, + &hPublicKey, &hPrivateKey ) ); + TEST_ASSERT( hPublicKey != CK_INVALID_HANDLE ); + TEST_ASSERT( hPrivateKey != CK_INVALID_HANDLE ); + + /* Prepare the mbed TLS contexts */ + TEST_ASSERT( mbedtls_pk_setup( &transparent_ctx, + mbedtls_pk_info_from_type( key_type ) ) == 0 ); + TEST_ASSERT( mbedtls_pk_setup_pkcs11( &pkcs11_ctx, + hSession, + hPublicKey, + hPrivateKey ) == 0 ); + + /* Retrieve the public key from the token */ + switch( key_type ) + { +#if defined(MBEDTLS_RSA_C) + case MBEDTLS_PK_RSA: + { + unsigned char n_buffer[RSA_KEY_SIZE_BYTES]; + unsigned char e_buffer[RSA_KEY_SIZE_BYTES]; + CK_ATTRIBUTE public_attributes[] = { + {CKA_MODULUS, n_buffer, sizeof( n_buffer )}, + {CKA_PUBLIC_EXPONENT, e_buffer, sizeof( e_buffer )}, + }; + CK_ULONG *n_length = &public_attributes[0].ulValueLen; + CK_ULONG *e_length = &public_attributes[1].ulValueLen; + mbedtls_rsa_context *rsa_ctx = mbedtls_pk_rsa( transparent_ctx ); + + CK_ASSERT( C_GetAttributeValue( hSession, hPublicKey, + public_attributes, ARRAY_LENGTH( public_attributes ) ) ); + TEST_ASSERT( mbedtls_mpi_read_binary( &rsa_ctx->N, + n_buffer, *n_length ) == 0 ); + TEST_ASSERT( mbedtls_mpi_read_binary( &rsa_ctx->E, + e_buffer, *e_length ) == 0 ); + rsa_ctx->len = mbedtls_mpi_size( &rsa_ctx->N ); + } + break; +#endif /* MBEDTLS_RSA_C */ + +#if defined(MBEDTLS_ECDSA_C) + case MBEDTLS_PK_ECDSA: + { + unsigned char ecParams[16]; + unsigned char ecPoint[128]; + CK_ATTRIBUTE public_attributes[] = { + {CKA_EC_PARAMS, ecParams, sizeof( ecParams )}, + {CKA_EC_POINT, ecPoint, sizeof( ecPoint )}, + }; + mbedtls_ecp_keypair *ecp_ctx = mbedtls_pk_ec( transparent_ctx ); + + CK_ASSERT( C_GetAttributeValue( hSession, hPublicKey, + public_attributes, ARRAY_LENGTH( public_attributes ) ) ); + // TODO: lift out a function or two from pkparse.c + // * pk_get_ecparams followed by pk_use_ecparams for ecParams? + // * Some code from pk_group_from_specified to read an octet string for ecPoint? + { + mbedtls_asn1_buf params_asn1; + CK_ULONG ecParams_length = public_attributes[0].ulValueLen; + mbedtls_ecp_group_id grp_id; + params_asn1.tag = ecParams[0]; + params_asn1.len = ecParams[1]; + params_asn1.p = ecParams + 2; + TEST_ASSERT( ecParams_length == 2 + params_asn1.len ); + TEST_ASSERT( mbedtls_oid_get_ec_grp( ¶ms_asn1, &grp_id ) == 0 ); + TEST_ASSERT( mbedtls_ecp_group_load( &ecp_ctx->grp, grp_id ) == 0 ); + } + { + unsigned char *p = ecPoint; + size_t len; + CK_ULONG ecPoint_length = public_attributes[1].ulValueLen; + TEST_ASSERT( mbedtls_asn1_get_tag( &p, + ecPoint + ecPoint_length, + &len, + MBEDTLS_ASN1_OCTET_STRING ) == 0 ); + TEST_ASSERT( mbedtls_ecp_point_read_binary( &ecp_ctx->grp, + &ecp_ctx->Q, + p, len ) == 0 ); + } + } + break; +#endif /* MBEDTLS_ECDSA_C */ + + default: + TEST_ASSERT( !"Unsupported key type in test data" ); + break; + } + + /* Sign with the token and verify in software */ + TEST_ASSERT( mbedtls_pk_sign( &pkcs11_ctx, MBEDTLS_MD_SHA256, + hash_value, 32, + sig_buffer, &sig_length, + NULL, NULL ) == 0 ); + TEST_ASSERT( mbedtls_pk_verify( &pkcs11_ctx, MBEDTLS_MD_SHA256, + hash_value, 32, + sig_buffer, sig_length ) == 0 ); + +exit: + if( hPublicKey != CK_INVALID_HANDLE ) + C_DestroyObject( hSession, hPublicKey ); + if( hPrivateKey != CK_INVALID_HANDLE ) + C_DestroyObject( hSession, hPrivateKey ); + C_CloseSession( hSession ); + C_Finalize( NULL_PTR ); + mbedtls_pk_free( &pkcs11_ctx ); + mbedtls_pk_free( &transparent_ctx ); +} +/* END_CASE */ + +/* BEGIN_CASE depends_on:MBEDTLS_PK_C:MBEDTLS_SHA256_C */ +void pk_import_signI_verifyX( char *file ) +{ + /* Sign with mbedTLS, verify by cryptoki with a conversion + to a raw, concatenated format by the engine. */ + mbedtls_pk_context pkcs11_ctx; + mbedtls_pk_context transparent_ctx; + CK_SESSION_HANDLE hSession = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hPublicKey = CK_INVALID_HANDLE; + CK_OBJECT_HANDLE hPrivateKey = CK_INVALID_HANDLE; + unsigned char hash_value[32] = "Fake hash, it doesn't matter...."; + unsigned char sig_buffer[4096]; + size_t sig_length = sizeof( sig_buffer ); + + mbedtls_pk_init( &pkcs11_ctx ); + mbedtls_pk_init( &transparent_ctx ); + + /* Read a transparent key */ + TEST_ASSERT( mbedtls_pk_parse_keyfile( &transparent_ctx, file, NULL ) == 0 ); + + /* Initialize cryptoki and import the key into the token */ + hSession = pkcs11_init( ); + TEST_ASSERT( hSession != CK_INVALID_HANDLE ); + + TEST_ASSERT( mbedtls_pk_import_to_pkcs11( &transparent_ctx, + MBEDTLS_PK_FLAG_SIGN | + MBEDTLS_PK_FLAG_VERIFY, + hSession, + &hPublicKey, + &hPrivateKey ) == 0 ); + TEST_ASSERT( hPublicKey != CK_INVALID_HANDLE ); + TEST_ASSERT( hPrivateKey != CK_INVALID_HANDLE ); + TEST_ASSERT( mbedtls_pk_setup_pkcs11( &pkcs11_ctx, + hSession, + hPublicKey, + hPrivateKey ) == 0 ); + + /* Sign with the token and verify with cryptoki */ + TEST_ASSERT( sizeof( sig_buffer ) >= mbedtls_pk_signature_size( &pkcs11_ctx ) ); + TEST_ASSERT( mbedtls_pk_sign( &transparent_ctx, MBEDTLS_MD_SHA256, + hash_value, 32, + sig_buffer, &sig_length, + NULL, NULL ) == 0 ); + TEST_ASSERT( mbedtls_pk_verify( &pkcs11_ctx, MBEDTLS_MD_SHA256, + hash_value, 32, + sig_buffer, sig_length ) == 0 ); + +exit: + if( hPublicKey != CK_INVALID_HANDLE ) + C_DestroyObject( hSession, hPublicKey ); + if( hPrivateKey != CK_INVALID_HANDLE ) + C_DestroyObject( hSession, hPrivateKey ); + C_CloseSession( hSession ); + C_Finalize( NULL_PTR ); + mbedtls_pk_free( &pkcs11_ctx ); + mbedtls_pk_free( &transparent_ctx ); +} +/* END_CASE */