mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 11:37:35 +00:00
957c61cbbf
This change upgrades to GCC 12.3 and GNU binutils 2.42. The GNU linker appears to have changed things so that only a single de-duplicated str table is present in the binary, and it gets placed wherever the linker wants, regardless of what the linker script says. To cope with that we need to stop using .ident to embed licenses. As such, this change does significant work to revamp how third party licenses are defined in the codebase, using `.section .notice,"aR",@progbits`. This new GCC 12.3 toolchain has support for GNU indirect functions. It lets us support __target_clones__ for the first time. This is used for optimizing the performance of libc string functions such as strlen and friends so far on x86, by ensuring AVX systems favor a second codepath that uses VEX encoding. It shaves some latency off certain operations. It's a useful feature to have for scientific computing for the reasons explained by the test/libcxx/openmp_test.cc example which compiles for fifteen different microarchitectures. Thanks to the upgrades, it's now also possible to use newer instruction sets, such as AVX512FP16, VNNI. Cosmo now uses the %gs register on x86 by default for TLS. Doing it is helpful for any program that links `cosmo_dlopen()`. Such programs had to recompile their binaries at startup to change the TLS instructions. That's not great, since it means every page in the executable needs to be faulted. The work of rewriting TLS-related x86 opcodes, is moved to fixupobj.com instead. This is great news for MacOS x86 users, since we previously needed to morph the binary every time for that platform but now that's no longer necessary. The only platforms where we need fixup of TLS x86 opcodes at runtime are now Windows, OpenBSD, and NetBSD. On Windows we morph TLS to point deeper into the TIB, based on a TlsAlloc assignment, and on OpenBSD/NetBSD we morph %gs back into %fs since the kernels do not allow us to specify a value for the %gs register. OpenBSD users are now required to use APE Loader to run Cosmo binaries and assimilation is no longer possible. OpenBSD kernel needs to change to allow programs to specify a value for the %gs register, or it needs to stop marking executable pages loaded by the kernel as mimmutable(). This release fixes __constructor__, .ctor, .init_array, and lastly the .preinit_array so they behave the exact same way as glibc. We no longer use hex constants to define math.h symbols like M_PI.
965 lines
32 KiB
C
965 lines
32 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│
|
|
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi │
|
|
╞══════════════════════════════════════════════════════════════════════════════╡
|
|
│ Copyright The Mbed TLS Contributors │
|
|
│ │
|
|
│ 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. │
|
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
|
#include "libc/str/str.h"
|
|
#include "third_party/mbedtls/asn1write.h"
|
|
#include "third_party/mbedtls/common.h"
|
|
#include "third_party/mbedtls/ecdsa.h"
|
|
#include "third_party/mbedtls/error.h"
|
|
#include "third_party/mbedtls/hmac_drbg.h"
|
|
#include "third_party/mbedtls/platform.h"
|
|
#include "third_party/mbedtls/profile.h"
|
|
__static_yoink("mbedtls_notice");
|
|
|
|
/**
|
|
* @fileoverview Elliptic curve Digital Signature Algorithm
|
|
*
|
|
* @see SEC1 http://www.secg.org/index.php?action=secg,docs_secg
|
|
*/
|
|
|
|
#if defined(MBEDTLS_ECDSA_C)
|
|
|
|
/* Parameter validation macros based on platform_util.h */
|
|
#define ECDSA_VALIDATE_RET( cond ) \
|
|
MBEDTLS_INTERNAL_VALIDATE_RET( cond, MBEDTLS_ERR_ECP_BAD_INPUT_DATA )
|
|
#define ECDSA_VALIDATE( cond ) \
|
|
MBEDTLS_INTERNAL_VALIDATE( cond )
|
|
|
|
#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 );
|
|
}
|
|
|
|
#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 == NULL ? NULL : &rs_ctx->ecp )
|
|
|
|
/* Utility macro for checking and updating ops budget */
|
|
#define ECDSA_BUDGET( ops ) \
|
|
MBEDTLS_MPI_CHK( mbedtls_ecp_check_budget( grp, ECDSA_RS_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_is_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 */
|
|
|
|
#if defined(MBEDTLS_ECDSA_DETERMINISTIC) || \
|
|
!defined(MBEDTLS_ECDSA_SIGN_ALT) || \
|
|
!defined(MBEDTLS_ECDSA_VERIFY_ALT)
|
|
/*
|
|
* 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 = MBEDTLS_ERR_THIS_CORRUPTION;
|
|
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 );
|
|
}
|
|
#endif /* ECDSA_DETERMINISTIC || !ECDSA_SIGN_ALT || !ECDSA_VERIFY_ALT */
|
|
|
|
#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,
|
|
int (*f_rng_blind)(void *, unsigned char *, size_t),
|
|
void *p_rng_blind,
|
|
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( ! mbedtls_ecdsa_can_do( grp->id ) || 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_blind,
|
|
p_rng_blind,
|
|
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_blind,
|
|
p_rng_blind ) );
|
|
|
|
/*
|
|
* 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_mod_mpi( pk, pk, &grp->N ) );
|
|
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 );
|
|
}
|
|
|
|
int mbedtls_ecdsa_can_do( mbedtls_ecp_group_id gid )
|
|
{
|
|
switch( gid )
|
|
{
|
|
#ifdef MBEDTLS_ECP_DP_CURVE25519_ENABLED
|
|
case MBEDTLS_ECP_DP_CURVE25519: return 0;
|
|
#endif
|
|
#ifdef MBEDTLS_ECP_DP_CURVE448_ENABLED
|
|
case MBEDTLS_ECP_DP_CURVE448: return 0;
|
|
#endif
|
|
default: return 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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 )
|
|
{
|
|
ECDSA_VALIDATE_RET( grp != NULL );
|
|
ECDSA_VALIDATE_RET( r != NULL );
|
|
ECDSA_VALIDATE_RET( s != NULL );
|
|
ECDSA_VALIDATE_RET( d != NULL );
|
|
ECDSA_VALIDATE_RET( f_rng != NULL );
|
|
ECDSA_VALIDATE_RET( buf != NULL || blen == 0 );
|
|
|
|
/* Use the same RNG for both blinding and ephemeral key generation */
|
|
return( ecdsa_sign_restartable( grp, r, s, d, buf, blen,
|
|
f_rng, p_rng, f_rng, p_rng, NULL ) );
|
|
}
|
|
#endif /* !MBEDTLS_ECDSA_SIGN_ALT */
|
|
|
|
#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,
|
|
int (*f_rng_blind)(void *, unsigned char *, size_t),
|
|
void *p_rng_blind,
|
|
mbedtls_ecdsa_restart_ctx *rs_ctx )
|
|
{
|
|
int ret = MBEDTLS_ERR_THIS_CORRUPTION;
|
|
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
|
|
#if defined(MBEDTLS_ECDSA_SIGN_ALT)
|
|
(void) f_rng_blind;
|
|
(void) p_rng_blind;
|
|
ret = mbedtls_ecdsa_sign( grp, r, s, d, buf, blen,
|
|
mbedtls_hmac_drbg_random, p_rng );
|
|
#else
|
|
if( f_rng_blind != NULL )
|
|
ret = ecdsa_sign_restartable( grp, r, s, d, buf, blen,
|
|
mbedtls_hmac_drbg_random, p_rng,
|
|
f_rng_blind, p_rng_blind, rs_ctx );
|
|
else
|
|
{
|
|
mbedtls_hmac_drbg_context *p_rng_blind_det;
|
|
|
|
#if !defined(MBEDTLS_ECP_RESTARTABLE)
|
|
/*
|
|
* To avoid reusing rng_ctx and risking incorrect behavior we seed a
|
|
* second HMAC-DRBG with the same seed. We also apply a label to avoid
|
|
* reusing the bits of the ephemeral key for blinding and eliminate the
|
|
* risk that they leak this way.
|
|
*/
|
|
const char* blind_label = "BLINDING CONTEXT";
|
|
mbedtls_hmac_drbg_context rng_ctx_blind;
|
|
|
|
mbedtls_hmac_drbg_init( &rng_ctx_blind );
|
|
p_rng_blind_det = &rng_ctx_blind;
|
|
mbedtls_hmac_drbg_seed_buf( p_rng_blind_det, md_info,
|
|
data, 2 * grp_len );
|
|
ret = mbedtls_hmac_drbg_update_ret( p_rng_blind_det,
|
|
(const unsigned char*) blind_label,
|
|
strlen( blind_label ) );
|
|
if( ret != 0 )
|
|
{
|
|
mbedtls_hmac_drbg_free( &rng_ctx_blind );
|
|
goto cleanup;
|
|
}
|
|
#else
|
|
/*
|
|
* In the case of restartable computations we would either need to store
|
|
* the second RNG in the restart context too or set it up at every
|
|
* restart. The first option would penalize the correct application of
|
|
* the function and the second would defeat the purpose of the
|
|
* restartable feature.
|
|
*
|
|
* Therefore in this case we reuse the original RNG. This comes with the
|
|
* price that the resulting signature might not be a valid deterministic
|
|
* ECDSA signature with a very low probability (same magnitude as
|
|
* successfully guessing the private key). However even then it is still
|
|
* a valid ECDSA signature.
|
|
*/
|
|
p_rng_blind_det = p_rng;
|
|
#endif /* MBEDTLS_ECP_RESTARTABLE */
|
|
|
|
/*
|
|
* Since the output of the RNGs is always the same for the same key and
|
|
* message, this limits the efficiency of blinding and leaks information
|
|
* through side channels. After mbedtls_ecdsa_sign_det() is removed NULL
|
|
* won't be a valid value for f_rng_blind anymore. Therefore it should
|
|
* be checked by the caller and this branch and check can be removed.
|
|
*/
|
|
ret = ecdsa_sign_restartable( grp, r, s, d, buf, blen,
|
|
mbedtls_hmac_drbg_random, p_rng,
|
|
mbedtls_hmac_drbg_random, p_rng_blind_det,
|
|
rs_ctx );
|
|
|
|
#if !defined(MBEDTLS_ECP_RESTARTABLE)
|
|
mbedtls_hmac_drbg_free( &rng_ctx_blind );
|
|
#endif
|
|
}
|
|
#endif /* MBEDTLS_ECDSA_SIGN_ALT */
|
|
|
|
cleanup:
|
|
mbedtls_hmac_drbg_free( &rng_ctx );
|
|
mbedtls_mpi_free( &h );
|
|
|
|
ECDSA_RS_LEAVE( det );
|
|
|
|
return( ret );
|
|
}
|
|
|
|
/*
|
|
* Deterministic signature wrappers
|
|
*/
|
|
|
|
int mbedtls_ecdsa_sign_det_ext( 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,
|
|
int (*f_rng_blind)(void *, unsigned char *,
|
|
size_t),
|
|
void *p_rng_blind )
|
|
{
|
|
ECDSA_VALIDATE_RET( grp != NULL );
|
|
ECDSA_VALIDATE_RET( r != NULL );
|
|
ECDSA_VALIDATE_RET( s != NULL );
|
|
ECDSA_VALIDATE_RET( d != NULL );
|
|
ECDSA_VALIDATE_RET( buf != NULL || blen == 0 );
|
|
ECDSA_VALIDATE_RET( f_rng_blind != NULL );
|
|
|
|
return( ecdsa_sign_det_restartable( grp, r, s, d, buf, blen, md_alg,
|
|
f_rng_blind, p_rng_blind, 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_ERR_THIS_CORRUPTION;
|
|
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( ! mbedtls_ecdsa_can_do( grp->id ) || 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 );
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
ECDSA_VALIDATE_RET( grp != NULL );
|
|
ECDSA_VALIDATE_RET( Q != NULL );
|
|
ECDSA_VALIDATE_RET( r != NULL );
|
|
ECDSA_VALIDATE_RET( s != NULL );
|
|
ECDSA_VALIDATE_RET( buf != NULL || blen == 0 );
|
|
|
|
return( ecdsa_verify_restartable( grp, buf, blen, Q, r, s, NULL ) );
|
|
}
|
|
#endif /* !MBEDTLS_ECDSA_VERIFY_ALT */
|
|
|
|
/*
|
|
* 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 = MBEDTLS_ERR_THIS_CORRUPTION;
|
|
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_ERR_THIS_CORRUPTION;
|
|
mbedtls_mpi r, s;
|
|
ECDSA_VALIDATE_RET( ctx != NULL );
|
|
ECDSA_VALIDATE_RET( hash != NULL );
|
|
ECDSA_VALIDATE_RET( sig != NULL );
|
|
ECDSA_VALIDATE_RET( slen != NULL );
|
|
|
|
mbedtls_mpi_init( &r );
|
|
mbedtls_mpi_init( &s );
|
|
|
|
#if defined(MBEDTLS_ECDSA_DETERMINISTIC)
|
|
MBEDTLS_MPI_CHK( ecdsa_sign_det_restartable( &ctx->grp, &r, &s, &ctx->d,
|
|
hash, hlen, md_alg, f_rng,
|
|
p_rng, rs_ctx ) );
|
|
#else
|
|
(void) md_alg;
|
|
|
|
#if defined(MBEDTLS_ECDSA_SIGN_ALT)
|
|
(void) rs_ctx;
|
|
|
|
MBEDTLS_MPI_CHK( mbedtls_ecdsa_sign( &ctx->grp, &r, &s, &ctx->d,
|
|
hash, hlen, f_rng, p_rng ) );
|
|
#else
|
|
/* Use the same RNG for both blinding and ephemeral key generation */
|
|
MBEDTLS_MPI_CHK( ecdsa_sign_restartable( &ctx->grp, &r, &s, &ctx->d,
|
|
hash, hlen, f_rng, p_rng, f_rng,
|
|
p_rng, rs_ctx ) );
|
|
#endif /* MBEDTLS_ECDSA_SIGN_ALT */
|
|
#endif /* MBEDTLS_ECDSA_DETERMINISTIC */
|
|
|
|
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 )
|
|
{
|
|
ECDSA_VALIDATE_RET( ctx != NULL );
|
|
ECDSA_VALIDATE_RET( hash != NULL );
|
|
ECDSA_VALIDATE_RET( sig != NULL );
|
|
ECDSA_VALIDATE_RET( slen != NULL );
|
|
return( mbedtls_ecdsa_write_signature_restartable(
|
|
ctx, md_alg, hash, hlen, sig, slen, f_rng, p_rng, NULL ) );
|
|
}
|
|
|
|
/*
|
|
* 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 )
|
|
{
|
|
ECDSA_VALIDATE_RET( ctx != NULL );
|
|
ECDSA_VALIDATE_RET( hash != NULL );
|
|
ECDSA_VALIDATE_RET( sig != NULL );
|
|
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 = MBEDTLS_ERR_THIS_CORRUPTION;
|
|
unsigned char *p = (unsigned char *) sig;
|
|
const unsigned char *end = sig + slen;
|
|
size_t len;
|
|
mbedtls_mpi r, s;
|
|
ECDSA_VALIDATE_RET( ctx != NULL );
|
|
ECDSA_VALIDATE_RET( hash != NULL );
|
|
ECDSA_VALIDATE_RET( sig != NULL );
|
|
|
|
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 defined(MBEDTLS_ECDSA_VERIFY_ALT)
|
|
(void) rs_ctx;
|
|
|
|
if( ( ret = mbedtls_ecdsa_verify( &ctx->grp, hash, hlen,
|
|
&ctx->Q, &r, &s ) ) != 0 )
|
|
goto cleanup;
|
|
#else
|
|
if( ( ret = ecdsa_verify_restartable( &ctx->grp, hash, hlen,
|
|
&ctx->Q, &r, &s, rs_ctx ) ) != 0 )
|
|
goto cleanup;
|
|
#endif /* MBEDTLS_ECDSA_VERIFY_ALT */
|
|
|
|
/* 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 )
|
|
{
|
|
int ret = 0;
|
|
ECDSA_VALIDATE_RET( ctx != NULL );
|
|
ECDSA_VALIDATE_RET( f_rng != NULL );
|
|
|
|
ret = mbedtls_ecp_group_load( &ctx->grp, gid );
|
|
if( ret != 0 )
|
|
return( ret );
|
|
|
|
return( 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 = MBEDTLS_ERR_THIS_CORRUPTION;
|
|
ECDSA_VALIDATE_RET( ctx != NULL );
|
|
ECDSA_VALIDATE_RET( key != NULL );
|
|
|
|
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 )
|
|
{
|
|
ECDSA_VALIDATE( ctx != NULL );
|
|
|
|
mbedtls_ecp_keypair_init( ctx );
|
|
}
|
|
|
|
/*
|
|
* Free context
|
|
*/
|
|
void mbedtls_ecdsa_free( mbedtls_ecdsa_context *ctx )
|
|
{
|
|
if( ctx == NULL )
|
|
return;
|
|
|
|
mbedtls_ecp_keypair_free( ctx );
|
|
}
|
|
|
|
#if defined(MBEDTLS_ECP_RESTARTABLE)
|
|
/*
|
|
* Initialize a restart context
|
|
*/
|
|
void mbedtls_ecdsa_restart_init( mbedtls_ecdsa_restart_ctx *ctx )
|
|
{
|
|
ECDSA_VALIDATE( ctx != NULL );
|
|
|
|
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 )
|
|
{
|
|
if( ctx == NULL )
|
|
return;
|
|
|
|
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 */
|