da19f4c79f
Summary of merge conflicts: include/mbedtls/ecdh.h -> documentation style include/mbedtls/ecdsa.h -> documentation style include/mbedtls/ecp.h -> alt style, new error codes, documentation style include/mbedtls/error.h -> new error codes library/error.c -> new error codes (generated anyway) library/ecp.c: - code of an extracted function was changed library/ssl_cli.c: - code addition on one side near code change on the other side (ciphersuite validation) library/x509_crt.c -> various things - top fo file: helper structure added near old zeroize removed - documentation of find_parent_in()'s signature: improved on one side, added arguments on the other side - documentation of find_parent()'s signature: same as above - verify_chain(): variables initialised later to give compiler an opportunity to warn us if not initialised on a code path - find_parent(): funcion structure completely changed, for some reason git tried to insert a paragraph of the old structure... - merge_flags_with_cb(): data structure changed, one line was fixed with a cast to keep MSVC happy, this cast is already in the new version - in verify_restratable(): adjacent independent changes (function signature on one line, variable type on the next) programs/ssl/ssl_client2.c: - testing for IN_PROGRESS return code near idle() (event-driven): don't wait for data in the the socket if ECP_IN_PROGRESS tests/data_files/Makefile: adjacent independent additions tests/suites/test_suite_ecdsa.data: adjacent independent additions tests/suites/test_suite_x509parse.data: adjacent independent additions * development: (1059 commits) Change symlink to hardlink to avoid permission issues Fix out-of-tree testing symlinks on Windows Updated version number to 2.10.0 for release Add a disabled CMAC define in the no-entropy configuration Adapt the ARIA test cases for new ECB function Fix file permissions for ssl.h Add ChangeLog entry for PR#1651 Fix MicroBlaze register typo. Fix typo in doc and copy missing warning Fix edit mistake in cipher_wrap.c Update CTR doc for the 64-bit block cipher Update CTR doc for other 128-bit block ciphers Slightly tune ARIA CTR documentation Remove double declaration of mbedtls_ssl_list_ciphersuites Update CTR documentation Use zeroize function from new platform_util Move to new header style for ALT implementations Add ifdef for selftest in header file Fix typo in comments Use more appropriate type for local variable ...
818 lines
24 KiB
C
818 lines
24 KiB
C
/*
|
|
* Elliptic curve DSA
|
|
*
|
|
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
* not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* This file is part of mbed TLS (https://tls.mbed.org)
|
|
*/
|
|
|
|
/*
|
|
* References:
|
|
*
|
|
* SEC1 http://www.secg.org/index.php?action=secg,docs_secg
|
|
*/
|
|
|
|
#if !defined(MBEDTLS_CONFIG_FILE)
|
|
#include "mbedtls/config.h"
|
|
#else
|
|
#include MBEDTLS_CONFIG_FILE
|
|
#endif
|
|
|
|
#if defined(MBEDTLS_ECDSA_C)
|
|
|
|
#include "mbedtls/ecdsa.h"
|
|
#include "mbedtls/asn1write.h"
|
|
|
|
#include <string.h>
|
|
|
|
#if defined(MBEDTLS_ECDSA_DETERMINISTIC)
|
|
#include "mbedtls/hmac_drbg.h"
|
|
#endif
|
|
|
|
#if defined(MBEDTLS_PLATFORM_C)
|
|
#include "mbedtls/platform.h"
|
|
#else
|
|
#include <stdlib.h>
|
|
#define mbedtls_calloc calloc
|
|
#define mbedtls_free free
|
|
#endif
|
|
|
|
#if defined(MBEDTLS_ECP_RESTARTABLE)
|
|
|
|
/*
|
|
* Sub-context for ecdsa_verify()
|
|
*/
|
|
struct mbedtls_ecdsa_restart_ver
|
|
{
|
|
mbedtls_mpi u1, u2; /* intermediate values */
|
|
enum { /* what to do next? */
|
|
ecdsa_ver_init = 0, /* getting started */
|
|
ecdsa_ver_muladd, /* muladd step */
|
|
} state;
|
|
};
|
|
|
|
/*
|
|
* Init verify restart sub-context
|
|
*/
|
|
static void ecdsa_restart_ver_init( mbedtls_ecdsa_restart_ver_ctx *ctx )
|
|
{
|
|
mbedtls_mpi_init( &ctx->u1 );
|
|
mbedtls_mpi_init( &ctx->u2 );
|
|
ctx->state = ecdsa_ver_init;
|
|
}
|
|
|
|
/*
|
|
* Free the components of a verify restart sub-context
|
|
*/
|
|
static void ecdsa_restart_ver_free( mbedtls_ecdsa_restart_ver_ctx *ctx )
|
|
{
|
|
if( ctx == NULL )
|
|
return;
|
|
|
|
mbedtls_mpi_free( &ctx->u1 );
|
|
mbedtls_mpi_free( &ctx->u2 );
|
|
|
|
ecdsa_restart_ver_init( ctx );
|
|
}
|
|
|
|
/*
|
|
* Sub-context for ecdsa_sign()
|
|
*/
|
|
struct mbedtls_ecdsa_restart_sig
|
|
{
|
|
int sign_tries;
|
|
int key_tries;
|
|
mbedtls_mpi k; /* per-signature random */
|
|
mbedtls_mpi r; /* r value */
|
|
enum { /* what to do next? */
|
|
ecdsa_sig_init = 0, /* getting started */
|
|
ecdsa_sig_mul, /* doing ecp_mul() */
|
|
ecdsa_sig_modn, /* mod N computations */
|
|
} state;
|
|
};
|
|
|
|
/*
|
|
* Init verify sign sub-context
|
|
*/
|
|
static void ecdsa_restart_sig_init( mbedtls_ecdsa_restart_sig_ctx *ctx )
|
|
{
|
|
ctx->sign_tries = 0;
|
|
ctx->key_tries = 0;
|
|
mbedtls_mpi_init( &ctx->k );
|
|
mbedtls_mpi_init( &ctx->r );
|
|
ctx->state = ecdsa_sig_init;
|
|
}
|
|
|
|
/*
|
|
* Free the components of a sign restart sub-context
|
|
*/
|
|
static void ecdsa_restart_sig_free( mbedtls_ecdsa_restart_sig_ctx *ctx )
|
|
{
|
|
if( ctx == NULL )
|
|
return;
|
|
|
|
mbedtls_mpi_free( &ctx->k );
|
|
mbedtls_mpi_free( &ctx->r );
|
|
|
|
ecdsa_restart_sig_init( ctx );
|
|
}
|
|
|
|
#if defined(MBEDTLS_ECDSA_DETERMINISTIC)
|
|
/*
|
|
* Sub-context for ecdsa_sign_det()
|
|
*/
|
|
struct mbedtls_ecdsa_restart_det
|
|
{
|
|
mbedtls_hmac_drbg_context rng_ctx; /* DRBG state */
|
|
enum { /* what to do next? */
|
|
ecdsa_det_init = 0, /* getting started */
|
|
ecdsa_det_sign, /* make signature */
|
|
} state;
|
|
};
|
|
|
|
/*
|
|
* Init verify sign_det sub-context
|
|
*/
|
|
static void ecdsa_restart_det_init( mbedtls_ecdsa_restart_det_ctx *ctx )
|
|
{
|
|
mbedtls_hmac_drbg_init( &ctx->rng_ctx );
|
|
ctx->state = ecdsa_det_init;
|
|
}
|
|
|
|
/*
|
|
* Free the components of a sign_det restart sub-context
|
|
*/
|
|
static void ecdsa_restart_det_free( mbedtls_ecdsa_restart_det_ctx *ctx )
|
|
{
|
|
if( ctx == NULL )
|
|
return;
|
|
|
|
mbedtls_hmac_drbg_free( &ctx->rng_ctx );
|
|
|
|
ecdsa_restart_det_init( ctx );
|
|
}
|
|
#endif /* MBEDTLS_ECDSA_DETERMINISTIC */
|
|
|
|
#define ECDSA_RS_ECP &rs_ctx->ecp
|
|
|
|
/* Utility macro for checking and updating ops budget */
|
|
#define ECDSA_BUDGET( ops ) \
|
|
MBEDTLS_MPI_CHK( mbedtls_ecp_check_budget( grp, &rs_ctx->ecp, ops ) );
|
|
|
|
/* Call this when entering a function that needs its own sub-context */
|
|
#define ECDSA_RS_ENTER( SUB ) do { \
|
|
/* reset ops count for this call if top-level */ \
|
|
if( rs_ctx != NULL && rs_ctx->ecp.depth++ == 0 ) \
|
|
rs_ctx->ecp.ops_done = 0; \
|
|
\
|
|
/* set up our own sub-context if needed */ \
|
|
if( mbedtls_ecp_restart_enabled() && \
|
|
rs_ctx != NULL && rs_ctx->SUB == NULL ) \
|
|
{ \
|
|
rs_ctx->SUB = mbedtls_calloc( 1, sizeof( *rs_ctx->SUB ) ); \
|
|
if( rs_ctx->SUB == NULL ) \
|
|
return( MBEDTLS_ERR_ECP_ALLOC_FAILED ); \
|
|
\
|
|
ecdsa_restart_## SUB ##_init( rs_ctx->SUB ); \
|
|
} \
|
|
} while( 0 )
|
|
|
|
/* Call this when leaving a function that needs its own sub-context */
|
|
#define ECDSA_RS_LEAVE( SUB ) do { \
|
|
/* clear our sub-context when not in progress (done or error) */ \
|
|
if( rs_ctx != NULL && rs_ctx->SUB != NULL && \
|
|
ret != MBEDTLS_ERR_ECP_IN_PROGRESS ) \
|
|
{ \
|
|
ecdsa_restart_## SUB ##_free( rs_ctx->SUB ); \
|
|
mbedtls_free( rs_ctx->SUB ); \
|
|
rs_ctx->SUB = NULL; \
|
|
} \
|
|
\
|
|
if( rs_ctx != NULL ) \
|
|
rs_ctx->ecp.depth--; \
|
|
} while( 0 )
|
|
|
|
#else /* MBEDTLS_ECP_RESTARTABLE */
|
|
|
|
#define ECDSA_RS_ECP NULL
|
|
|
|
#define ECDSA_BUDGET( ops ) /* no-op; for compatibility */
|
|
|
|
#define ECDSA_RS_ENTER( SUB ) (void) rs_ctx
|
|
#define ECDSA_RS_LEAVE( SUB ) (void) rs_ctx
|
|
|
|
#endif /* MBEDTLS_ECP_RESTARTABLE */
|
|
|
|
/*
|
|
* Derive a suitable integer for group grp from a buffer of length len
|
|
* SEC1 4.1.3 step 5 aka SEC1 4.1.4 step 3
|
|
*/
|
|
static int derive_mpi( const mbedtls_ecp_group *grp, mbedtls_mpi *x,
|
|
const unsigned char *buf, size_t blen )
|
|
{
|
|
int ret;
|
|
size_t n_size = ( grp->nbits + 7 ) / 8;
|
|
size_t use_size = blen > n_size ? n_size : blen;
|
|
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_read_binary( x, buf, use_size ) );
|
|
if( use_size * 8 > grp->nbits )
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_shift_r( x, use_size * 8 - grp->nbits ) );
|
|
|
|
/* While at it, reduce modulo N */
|
|
if( mbedtls_mpi_cmp_mpi( x, &grp->N ) >= 0 )
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_sub_mpi( x, x, &grp->N ) );
|
|
|
|
cleanup:
|
|
return( ret );
|
|
}
|
|
|
|
#if !defined(MBEDTLS_ECDSA_SIGN_ALT)
|
|
/*
|
|
* Compute ECDSA signature of a hashed message (SEC1 4.1.3)
|
|
* Obviously, compared to SEC1 4.1.3, we skip step 4 (hash message)
|
|
*/
|
|
static int ecdsa_sign_restartable( mbedtls_ecp_group *grp,
|
|
mbedtls_mpi *r, mbedtls_mpi *s,
|
|
const mbedtls_mpi *d, const unsigned char *buf, size_t blen,
|
|
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng,
|
|
mbedtls_ecdsa_restart_ctx *rs_ctx )
|
|
{
|
|
int ret, key_tries, sign_tries;
|
|
int *p_sign_tries = &sign_tries, *p_key_tries = &key_tries;
|
|
mbedtls_ecp_point R;
|
|
mbedtls_mpi k, e, t;
|
|
mbedtls_mpi *pk = &k, *pr = r;
|
|
|
|
/* Fail cleanly on curves such as Curve25519 that can't be used for ECDSA */
|
|
if( grp->N.p == NULL )
|
|
return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
|
|
|
|
/* Make sure d is in range 1..n-1 */
|
|
if( mbedtls_mpi_cmp_int( d, 1 ) < 0 || mbedtls_mpi_cmp_mpi( d, &grp->N ) >= 0 )
|
|
return( MBEDTLS_ERR_ECP_INVALID_KEY );
|
|
|
|
mbedtls_ecp_point_init( &R );
|
|
mbedtls_mpi_init( &k ); mbedtls_mpi_init( &e ); mbedtls_mpi_init( &t );
|
|
|
|
ECDSA_RS_ENTER( sig );
|
|
|
|
#if defined(MBEDTLS_ECP_RESTARTABLE)
|
|
if( rs_ctx != NULL && rs_ctx->sig != NULL )
|
|
{
|
|
/* redirect to our context */
|
|
p_sign_tries = &rs_ctx->sig->sign_tries;
|
|
p_key_tries = &rs_ctx->sig->key_tries;
|
|
pk = &rs_ctx->sig->k;
|
|
pr = &rs_ctx->sig->r;
|
|
|
|
/* jump to current step */
|
|
if( rs_ctx->sig->state == ecdsa_sig_mul )
|
|
goto mul;
|
|
if( rs_ctx->sig->state == ecdsa_sig_modn )
|
|
goto modn;
|
|
}
|
|
#endif /* MBEDTLS_ECP_RESTARTABLE */
|
|
|
|
*p_sign_tries = 0;
|
|
do
|
|
{
|
|
if( *p_sign_tries++ > 10 )
|
|
{
|
|
ret = MBEDTLS_ERR_ECP_RANDOM_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
/*
|
|
* Steps 1-3: generate a suitable ephemeral keypair
|
|
* and set r = xR mod n
|
|
*/
|
|
*p_key_tries = 0;
|
|
do
|
|
{
|
|
if( *p_key_tries++ > 10 )
|
|
{
|
|
ret = MBEDTLS_ERR_ECP_RANDOM_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
MBEDTLS_MPI_CHK( mbedtls_ecp_gen_privkey( grp, pk, f_rng, p_rng ) );
|
|
|
|
#if defined(MBEDTLS_ECP_RESTARTABLE)
|
|
if( rs_ctx != NULL && rs_ctx->sig != NULL )
|
|
rs_ctx->sig->state = ecdsa_sig_mul;
|
|
|
|
mul:
|
|
#endif
|
|
MBEDTLS_MPI_CHK( mbedtls_ecp_mul_restartable( grp, &R, pk, &grp->G,
|
|
f_rng, p_rng, ECDSA_RS_ECP ) );
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( pr, &R.X, &grp->N ) );
|
|
}
|
|
while( mbedtls_mpi_cmp_int( pr, 0 ) == 0 );
|
|
|
|
#if defined(MBEDTLS_ECP_RESTARTABLE)
|
|
if( rs_ctx != NULL && rs_ctx->sig != NULL )
|
|
rs_ctx->sig->state = ecdsa_sig_modn;
|
|
|
|
modn:
|
|
#endif
|
|
/*
|
|
* Accounting for everything up to the end of the loop
|
|
* (step 6, but checking now avoids saving e and t)
|
|
*/
|
|
ECDSA_BUDGET( MBEDTLS_ECP_OPS_INV + 4 );
|
|
|
|
/*
|
|
* Step 5: derive MPI from hashed message
|
|
*/
|
|
MBEDTLS_MPI_CHK( derive_mpi( grp, &e, buf, blen ) );
|
|
|
|
/*
|
|
* Generate a random value to blind inv_mod in next step,
|
|
* avoiding a potential timing leak.
|
|
*/
|
|
MBEDTLS_MPI_CHK( mbedtls_ecp_gen_privkey( grp, &t, f_rng, p_rng ) );
|
|
|
|
/*
|
|
* Step 6: compute s = (e + r * d) / k = t (e + rd) / (kt) mod n
|
|
*/
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( s, pr, d ) );
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_add_mpi( &e, &e, s ) );
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( &e, &e, &t ) );
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( pk, pk, &t ) );
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_inv_mod( s, pk, &grp->N ) );
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( s, s, &e ) );
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( s, s, &grp->N ) );
|
|
}
|
|
while( mbedtls_mpi_cmp_int( s, 0 ) == 0 );
|
|
|
|
#if defined(MBEDTLS_ECP_RESTARTABLE)
|
|
if( rs_ctx != NULL && rs_ctx->sig != NULL )
|
|
mbedtls_mpi_copy( r, pr );
|
|
#endif
|
|
|
|
cleanup:
|
|
mbedtls_ecp_point_free( &R );
|
|
mbedtls_mpi_free( &k ); mbedtls_mpi_free( &e ); mbedtls_mpi_free( &t );
|
|
|
|
ECDSA_RS_LEAVE( sig );
|
|
|
|
return( ret );
|
|
}
|
|
#endif /* MBEDTLS_ECDSA_SIGN_ALT */
|
|
|
|
/*
|
|
* Compute ECDSA signature of a hashed message
|
|
*/
|
|
int mbedtls_ecdsa_sign( mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s,
|
|
const mbedtls_mpi *d, const unsigned char *buf, size_t blen,
|
|
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng )
|
|
{
|
|
return( ecdsa_sign_restartable( grp, r, s, d, buf, blen,
|
|
f_rng, p_rng, NULL ) );
|
|
}
|
|
|
|
#if defined(MBEDTLS_ECDSA_DETERMINISTIC)
|
|
/*
|
|
* Deterministic signature wrapper
|
|
*/
|
|
static int ecdsa_sign_det_restartable( mbedtls_ecp_group *grp,
|
|
mbedtls_mpi *r, mbedtls_mpi *s,
|
|
const mbedtls_mpi *d, const unsigned char *buf, size_t blen,
|
|
mbedtls_md_type_t md_alg,
|
|
mbedtls_ecdsa_restart_ctx *rs_ctx )
|
|
{
|
|
int ret;
|
|
mbedtls_hmac_drbg_context rng_ctx;
|
|
mbedtls_hmac_drbg_context *p_rng = &rng_ctx;
|
|
unsigned char data[2 * MBEDTLS_ECP_MAX_BYTES];
|
|
size_t grp_len = ( grp->nbits + 7 ) / 8;
|
|
const mbedtls_md_info_t *md_info;
|
|
mbedtls_mpi h;
|
|
|
|
if( ( md_info = mbedtls_md_info_from_type( md_alg ) ) == NULL )
|
|
return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
|
|
|
|
mbedtls_mpi_init( &h );
|
|
mbedtls_hmac_drbg_init( &rng_ctx );
|
|
|
|
ECDSA_RS_ENTER( det );
|
|
|
|
#if defined(MBEDTLS_ECP_RESTARTABLE)
|
|
if( rs_ctx != NULL && rs_ctx->det != NULL )
|
|
{
|
|
/* redirect to our context */
|
|
p_rng = &rs_ctx->det->rng_ctx;
|
|
|
|
/* jump to current step */
|
|
if( rs_ctx->det->state == ecdsa_det_sign )
|
|
goto sign;
|
|
}
|
|
#endif /* MBEDTLS_ECP_RESTARTABLE */
|
|
|
|
/* Use private key and message hash (reduced) to initialize HMAC_DRBG */
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_write_binary( d, data, grp_len ) );
|
|
MBEDTLS_MPI_CHK( derive_mpi( grp, &h, buf, blen ) );
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_write_binary( &h, data + grp_len, grp_len ) );
|
|
mbedtls_hmac_drbg_seed_buf( p_rng, md_info, data, 2 * grp_len );
|
|
|
|
#if defined(MBEDTLS_ECP_RESTARTABLE)
|
|
if( rs_ctx != NULL && rs_ctx->det != NULL )
|
|
rs_ctx->det->state = ecdsa_det_sign;
|
|
|
|
sign:
|
|
#endif
|
|
ret = ecdsa_sign_restartable( grp, r, s, d, buf, blen,
|
|
mbedtls_hmac_drbg_random, p_rng, rs_ctx );
|
|
|
|
cleanup:
|
|
mbedtls_hmac_drbg_free( &rng_ctx );
|
|
mbedtls_mpi_free( &h );
|
|
|
|
ECDSA_RS_LEAVE( det );
|
|
|
|
return( ret );
|
|
}
|
|
|
|
/*
|
|
* Deterministic signature wrapper
|
|
*/
|
|
int mbedtls_ecdsa_sign_det( mbedtls_ecp_group *grp, mbedtls_mpi *r, mbedtls_mpi *s,
|
|
const mbedtls_mpi *d, const unsigned char *buf, size_t blen,
|
|
mbedtls_md_type_t md_alg )
|
|
{
|
|
return( ecdsa_sign_det_restartable( grp, r, s, d, buf, blen, md_alg, NULL ) );
|
|
}
|
|
#endif /* MBEDTLS_ECDSA_DETERMINISTIC */
|
|
|
|
#if !defined(MBEDTLS_ECDSA_VERIFY_ALT)
|
|
/*
|
|
* Verify ECDSA signature of hashed message (SEC1 4.1.4)
|
|
* Obviously, compared to SEC1 4.1.3, we skip step 2 (hash message)
|
|
*/
|
|
static int ecdsa_verify_restartable( mbedtls_ecp_group *grp,
|
|
const unsigned char *buf, size_t blen,
|
|
const mbedtls_ecp_point *Q,
|
|
const mbedtls_mpi *r, const mbedtls_mpi *s,
|
|
mbedtls_ecdsa_restart_ctx *rs_ctx )
|
|
{
|
|
int ret;
|
|
mbedtls_mpi e, s_inv, u1, u2;
|
|
mbedtls_ecp_point R;
|
|
mbedtls_mpi *pu1 = &u1, *pu2 = &u2;
|
|
|
|
mbedtls_ecp_point_init( &R );
|
|
mbedtls_mpi_init( &e ); mbedtls_mpi_init( &s_inv );
|
|
mbedtls_mpi_init( &u1 ); mbedtls_mpi_init( &u2 );
|
|
|
|
/* Fail cleanly on curves such as Curve25519 that can't be used for ECDSA */
|
|
if( grp->N.p == NULL )
|
|
return( MBEDTLS_ERR_ECP_BAD_INPUT_DATA );
|
|
|
|
ECDSA_RS_ENTER( ver );
|
|
|
|
#if defined(MBEDTLS_ECP_RESTARTABLE)
|
|
if( rs_ctx != NULL && rs_ctx->ver != NULL )
|
|
{
|
|
/* redirect to our context */
|
|
pu1 = &rs_ctx->ver->u1;
|
|
pu2 = &rs_ctx->ver->u2;
|
|
|
|
/* jump to current step */
|
|
if( rs_ctx->ver->state == ecdsa_ver_muladd )
|
|
goto muladd;
|
|
}
|
|
#endif /* MBEDTLS_ECP_RESTARTABLE */
|
|
|
|
/*
|
|
* Step 1: make sure r and s are in range 1..n-1
|
|
*/
|
|
if( mbedtls_mpi_cmp_int( r, 1 ) < 0 || mbedtls_mpi_cmp_mpi( r, &grp->N ) >= 0 ||
|
|
mbedtls_mpi_cmp_int( s, 1 ) < 0 || mbedtls_mpi_cmp_mpi( s, &grp->N ) >= 0 )
|
|
{
|
|
ret = MBEDTLS_ERR_ECP_VERIFY_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
/*
|
|
* Step 3: derive MPI from hashed message
|
|
*/
|
|
MBEDTLS_MPI_CHK( derive_mpi( grp, &e, buf, blen ) );
|
|
|
|
/*
|
|
* Step 4: u1 = e / s mod n, u2 = r / s mod n
|
|
*/
|
|
ECDSA_BUDGET( MBEDTLS_ECP_OPS_CHK + MBEDTLS_ECP_OPS_INV + 2 );
|
|
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_inv_mod( &s_inv, s, &grp->N ) );
|
|
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( pu1, &e, &s_inv ) );
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( pu1, pu1, &grp->N ) );
|
|
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_mul_mpi( pu2, r, &s_inv ) );
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( pu2, pu2, &grp->N ) );
|
|
|
|
#if defined(MBEDTLS_ECP_RESTARTABLE)
|
|
if( rs_ctx != NULL && rs_ctx->ver != NULL )
|
|
rs_ctx->ver->state = ecdsa_ver_muladd;
|
|
|
|
muladd:
|
|
#endif
|
|
/*
|
|
* Step 5: R = u1 G + u2 Q
|
|
*/
|
|
MBEDTLS_MPI_CHK( mbedtls_ecp_muladd_restartable( grp,
|
|
&R, pu1, &grp->G, pu2, Q, ECDSA_RS_ECP ) );
|
|
|
|
if( mbedtls_ecp_is_zero( &R ) )
|
|
{
|
|
ret = MBEDTLS_ERR_ECP_VERIFY_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
/*
|
|
* Step 6: convert xR to an integer (no-op)
|
|
* Step 7: reduce xR mod n (gives v)
|
|
*/
|
|
MBEDTLS_MPI_CHK( mbedtls_mpi_mod_mpi( &R.X, &R.X, &grp->N ) );
|
|
|
|
/*
|
|
* Step 8: check if v (that is, R.X) is equal to r
|
|
*/
|
|
if( mbedtls_mpi_cmp_mpi( &R.X, r ) != 0 )
|
|
{
|
|
ret = MBEDTLS_ERR_ECP_VERIFY_FAILED;
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
mbedtls_ecp_point_free( &R );
|
|
mbedtls_mpi_free( &e ); mbedtls_mpi_free( &s_inv );
|
|
mbedtls_mpi_free( &u1 ); mbedtls_mpi_free( &u2 );
|
|
|
|
ECDSA_RS_LEAVE( ver );
|
|
|
|
return( ret );
|
|
}
|
|
#endif /* MBEDTLS_ECDSA_VERIFY_ALT */
|
|
|
|
/*
|
|
* Verify ECDSA signature of hashed message
|
|
*/
|
|
int mbedtls_ecdsa_verify( mbedtls_ecp_group *grp,
|
|
const unsigned char *buf, size_t blen,
|
|
const mbedtls_ecp_point *Q, const mbedtls_mpi *r, const mbedtls_mpi *s)
|
|
{
|
|
return( ecdsa_verify_restartable( grp, buf, blen, Q, r, s, NULL ) );
|
|
}
|
|
|
|
/*
|
|
* Convert a signature (given by context) to ASN.1
|
|
*/
|
|
static int ecdsa_signature_to_asn1( const mbedtls_mpi *r, const mbedtls_mpi *s,
|
|
unsigned char *sig, size_t *slen )
|
|
{
|
|
int ret;
|
|
unsigned char buf[MBEDTLS_ECDSA_MAX_LEN];
|
|
unsigned char *p = buf + sizeof( buf );
|
|
size_t len = 0;
|
|
|
|
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_mpi( &p, buf, s ) );
|
|
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_mpi( &p, buf, r ) );
|
|
|
|
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_len( &p, buf, len ) );
|
|
MBEDTLS_ASN1_CHK_ADD( len, mbedtls_asn1_write_tag( &p, buf,
|
|
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) );
|
|
|
|
memcpy( sig, p, len );
|
|
*slen = len;
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/*
|
|
* Compute and write signature
|
|
*/
|
|
int mbedtls_ecdsa_write_signature_restartable( mbedtls_ecdsa_context *ctx,
|
|
mbedtls_md_type_t md_alg,
|
|
const unsigned char *hash, size_t hlen,
|
|
unsigned char *sig, size_t *slen,
|
|
int (*f_rng)(void *, unsigned char *, size_t),
|
|
void *p_rng,
|
|
mbedtls_ecdsa_restart_ctx *rs_ctx )
|
|
{
|
|
int ret;
|
|
mbedtls_mpi r, s;
|
|
|
|
mbedtls_mpi_init( &r );
|
|
mbedtls_mpi_init( &s );
|
|
|
|
#if defined(MBEDTLS_ECDSA_DETERMINISTIC)
|
|
(void) f_rng;
|
|
(void) p_rng;
|
|
|
|
MBEDTLS_MPI_CHK( ecdsa_sign_det_restartable( &ctx->grp, &r, &s, &ctx->d,
|
|
hash, hlen, md_alg, rs_ctx ) );
|
|
#else
|
|
(void) md_alg;
|
|
|
|
MBEDTLS_MPI_CHK( ecdsa_sign_restartable( &ctx->grp, &r, &s, &ctx->d,
|
|
hash, hlen, f_rng, p_rng, rs_ctx ) );
|
|
#endif
|
|
|
|
MBEDTLS_MPI_CHK( ecdsa_signature_to_asn1( &r, &s, sig, slen ) );
|
|
|
|
cleanup:
|
|
mbedtls_mpi_free( &r );
|
|
mbedtls_mpi_free( &s );
|
|
|
|
return( ret );
|
|
}
|
|
|
|
/*
|
|
* Compute and write signature
|
|
*/
|
|
int mbedtls_ecdsa_write_signature( mbedtls_ecdsa_context *ctx, mbedtls_md_type_t md_alg,
|
|
const unsigned char *hash, size_t hlen,
|
|
unsigned char *sig, size_t *slen,
|
|
int (*f_rng)(void *, unsigned char *, size_t),
|
|
void *p_rng )
|
|
{
|
|
return( mbedtls_ecdsa_write_signature_restartable(
|
|
ctx, md_alg, hash, hlen, sig, slen, f_rng, p_rng, NULL ) );
|
|
}
|
|
|
|
#if ! defined(MBEDTLS_DEPRECATED_REMOVED) && \
|
|
defined(MBEDTLS_ECDSA_DETERMINISTIC)
|
|
int mbedtls_ecdsa_write_signature_det( mbedtls_ecdsa_context *ctx,
|
|
const unsigned char *hash, size_t hlen,
|
|
unsigned char *sig, size_t *slen,
|
|
mbedtls_md_type_t md_alg )
|
|
{
|
|
return( mbedtls_ecdsa_write_signature( ctx, md_alg, hash, hlen, sig, slen,
|
|
NULL, NULL ) );
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Read and check signature
|
|
*/
|
|
int mbedtls_ecdsa_read_signature( mbedtls_ecdsa_context *ctx,
|
|
const unsigned char *hash, size_t hlen,
|
|
const unsigned char *sig, size_t slen )
|
|
{
|
|
return( mbedtls_ecdsa_read_signature_restartable(
|
|
ctx, hash, hlen, sig, slen, NULL ) );
|
|
}
|
|
|
|
/*
|
|
* Restartable read and check signature
|
|
*/
|
|
int mbedtls_ecdsa_read_signature_restartable( mbedtls_ecdsa_context *ctx,
|
|
const unsigned char *hash, size_t hlen,
|
|
const unsigned char *sig, size_t slen,
|
|
mbedtls_ecdsa_restart_ctx *rs_ctx )
|
|
{
|
|
int ret;
|
|
unsigned char *p = (unsigned char *) sig;
|
|
const unsigned char *end = sig + slen;
|
|
size_t len;
|
|
mbedtls_mpi r, s;
|
|
|
|
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;
|
|
}
|
|
|
|
if( ( ret = ecdsa_verify_restartable( &ctx->grp, hash, hlen,
|
|
&ctx->Q, &r, &s, rs_ctx ) ) != 0 )
|
|
goto cleanup;
|
|
|
|
/* At this point we know that the buffer starts with a valid signature.
|
|
* Return 0 if the buffer just contains the signature, and a specific
|
|
* error code if the valid signature is followed by more data. */
|
|
if( p != end )
|
|
ret = MBEDTLS_ERR_ECP_SIG_LEN_MISMATCH;
|
|
|
|
cleanup:
|
|
mbedtls_mpi_free( &r );
|
|
mbedtls_mpi_free( &s );
|
|
|
|
return( ret );
|
|
}
|
|
|
|
#if !defined(MBEDTLS_ECDSA_GENKEY_ALT)
|
|
/*
|
|
* Generate key pair
|
|
*/
|
|
int mbedtls_ecdsa_genkey( mbedtls_ecdsa_context *ctx, mbedtls_ecp_group_id gid,
|
|
int (*f_rng)(void *, unsigned char *, size_t), void *p_rng )
|
|
{
|
|
return( mbedtls_ecp_group_load( &ctx->grp, gid ) ||
|
|
mbedtls_ecp_gen_keypair( &ctx->grp, &ctx->d, &ctx->Q, f_rng, p_rng ) );
|
|
}
|
|
#endif /* MBEDTLS_ECDSA_GENKEY_ALT */
|
|
|
|
/*
|
|
* Set context from an mbedtls_ecp_keypair
|
|
*/
|
|
int mbedtls_ecdsa_from_keypair( mbedtls_ecdsa_context *ctx, const mbedtls_ecp_keypair *key )
|
|
{
|
|
int ret;
|
|
|
|
if( ( ret = mbedtls_ecp_group_copy( &ctx->grp, &key->grp ) ) != 0 ||
|
|
( ret = mbedtls_mpi_copy( &ctx->d, &key->d ) ) != 0 ||
|
|
( ret = mbedtls_ecp_copy( &ctx->Q, &key->Q ) ) != 0 )
|
|
{
|
|
mbedtls_ecdsa_free( ctx );
|
|
}
|
|
|
|
return( ret );
|
|
}
|
|
|
|
/*
|
|
* Initialize context
|
|
*/
|
|
void mbedtls_ecdsa_init( mbedtls_ecdsa_context *ctx )
|
|
{
|
|
mbedtls_ecp_keypair_init( ctx );
|
|
}
|
|
|
|
/*
|
|
* Free context
|
|
*/
|
|
void mbedtls_ecdsa_free( mbedtls_ecdsa_context *ctx )
|
|
{
|
|
mbedtls_ecp_keypair_free( ctx );
|
|
}
|
|
|
|
#if defined(MBEDTLS_ECP_RESTARTABLE)
|
|
/*
|
|
* Initialize a restart context
|
|
*/
|
|
void mbedtls_ecdsa_restart_init( mbedtls_ecdsa_restart_ctx *ctx )
|
|
{
|
|
mbedtls_ecp_restart_init( &ctx->ecp );
|
|
|
|
ctx->ver = NULL;
|
|
ctx->sig = NULL;
|
|
#if defined(MBEDTLS_ECDSA_DETERMINISTIC)
|
|
ctx->det = NULL;
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Free the components of a restart context
|
|
*/
|
|
void mbedtls_ecdsa_restart_free( mbedtls_ecdsa_restart_ctx *ctx )
|
|
{
|
|
mbedtls_ecp_restart_free( &ctx->ecp );
|
|
|
|
ecdsa_restart_ver_free( ctx->ver );
|
|
mbedtls_free( ctx->ver );
|
|
ctx->ver = NULL;
|
|
|
|
ecdsa_restart_sig_free( ctx->sig );
|
|
mbedtls_free( ctx->sig );
|
|
ctx->sig = NULL;
|
|
|
|
#if defined(MBEDTLS_ECDSA_DETERMINISTIC)
|
|
ecdsa_restart_det_free( ctx->det );
|
|
mbedtls_free( ctx->det );
|
|
ctx->det = NULL;
|
|
#endif
|
|
}
|
|
#endif /* MBEDTLS_ECP_RESTARTABLE */
|
|
|
|
#endif /* MBEDTLS_ECDSA_C */
|