timing.c 13.3 KB
Newer Older
1 2 3
/*
 *  Portable interface to the CPU cycle counter
 *
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
4
 *  Copyright (C) 2006-2014, ARM Limited, All Rights Reserved
Paul Bakker's avatar
Paul Bakker committed
5
 *
6
 *  This file is part of mbed TLS (https://tls.mbed.org)
Paul Bakker's avatar
Paul Bakker committed
7
 *
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
23
#if !defined(MBEDTLS_CONFIG_FILE)
24
#include "mbedtls/config.h"
25
#else
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
26
#include MBEDTLS_CONFIG_FILE
27
#endif
28

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
29
#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_PLATFORM_C)
30
#include "mbedtls/platform.h"
31 32
#else
#include <stdio.h>
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
33
#define mbedtls_printf     printf
34 35
#endif

36
#if defined(MBEDTLS_TIMING_C)
37

38
#include "mbedtls/timing.h"
39

40 41
#if !defined(MBEDTLS_TIMING_ALT)

42
#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

#include <windows.h>
#include <winbase.h>

struct _hr_time
{
    LARGE_INTEGER start;
};

#else

#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <signal.h>
#include <time.h>

struct _hr_time
{
    struct timeval start;
};

65
#endif /* _WIN32 && !EFIX64 && !EFI32 */
66

67
#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
68
    ( defined(_MSC_VER) && defined(_M_IX86) ) || defined(__WATCOMC__)
69

70
#define HAVE_HARDCLOCK
71

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
72
unsigned long mbedtls_timing_hardclock( void )
73 74 75 76 77 78
{
    unsigned long tsc;
    __asm   rdtsc
    __asm   mov  [tsc], eax
    return( tsc );
}
79
#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
80
          ( _MSC_VER && _M_IX86 ) || __WATCOMC__ */
81

82
/* some versions of mingw-64 have 32-bit longs even on x84_64 */
83
#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
84 85
    defined(__GNUC__) && ( defined(__i386__) || (                       \
    ( defined(__amd64__) || defined( __x86_64__) ) && __SIZEOF_LONG__ == 4 ) )
86

87
#define HAVE_HARDCLOCK
88

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
89
unsigned long mbedtls_timing_hardclock( void )
90
{
91
    unsigned long lo, hi;
92
    asm volatile( "rdtsc" : "=a" (lo), "=d" (hi) );
93
    return( lo );
94
}
95
#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
96
          __GNUC__ && __i386__ */
97

98
#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
99
    defined(__GNUC__) && ( defined(__amd64__) || defined(__x86_64__) )
100

101
#define HAVE_HARDCLOCK
102

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
103
unsigned long mbedtls_timing_hardclock( void )
104 105
{
    unsigned long lo, hi;
106
    asm volatile( "rdtsc" : "=a" (lo), "=d" (hi) );
107
    return( lo | ( hi << 32 ) );
108
}
109
#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
110
          __GNUC__ && ( __amd64__ || __x86_64__ ) */
111

112
#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
113
    defined(__GNUC__) && ( defined(__powerpc__) || defined(__ppc__) )
114

115
#define HAVE_HARDCLOCK
116

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
117
unsigned long mbedtls_timing_hardclock( void )
118 119 120 121 122
{
    unsigned long tbl, tbu0, tbu1;

    do
    {
123 124 125
        asm volatile( "mftbu %0" : "=r" (tbu0) );
        asm volatile( "mftb  %0" : "=r" (tbl ) );
        asm volatile( "mftbu %0" : "=r" (tbu1) );
126 127 128 129 130
    }
    while( tbu0 != tbu1 );

    return( tbl );
}
131
#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
132
          __GNUC__ && ( __powerpc__ || __ppc__ ) */
133

134
#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
135
    defined(__GNUC__) && defined(__sparc64__)
136

137 138
#if defined(__OpenBSD__)
#warning OpenBSD does not allow access to tick register using software version instead
139
#else
140
#define HAVE_HARDCLOCK
141

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
142
unsigned long mbedtls_timing_hardclock( void )
143 144
{
    unsigned long tick;
145
    asm volatile( "rdpr %%tick, %0;" : "=&r" (tick) );
146 147
    return( tick );
}
148
#endif /* __OpenBSD__ */
149
#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
150
          __GNUC__ && __sparc64__ */
151

152
#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
153 154
    defined(__GNUC__) && defined(__sparc__) && !defined(__sparc64__)

155
#define HAVE_HARDCLOCK
156

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
157
unsigned long mbedtls_timing_hardclock( void )
158 159
{
    unsigned long tick;
160 161
    asm volatile( ".byte 0x83, 0x41, 0x00, 0x00" );
    asm volatile( "mov   %%g1, %0" : "=r" (tick) );
162 163
    return( tick );
}
164
#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
165
          __GNUC__ && __sparc__ && !__sparc64__ */
166

167
#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&      \
168 169
    defined(__GNUC__) && defined(__alpha__)

170
#define HAVE_HARDCLOCK
171

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
172
unsigned long mbedtls_timing_hardclock( void )
173 174
{
    unsigned long cc;
175
    asm volatile( "rpcc %0" : "=r" (cc) );
176 177
    return( cc & 0xFFFFFFFF );
}
178
#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
179
          __GNUC__ && __alpha__ */
180

181
#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&      \
182 183
    defined(__GNUC__) && defined(__ia64__)

184
#define HAVE_HARDCLOCK
185

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
186
unsigned long mbedtls_timing_hardclock( void )
187 188
{
    unsigned long itc;
189
    asm volatile( "mov %0 = ar.itc" : "=r" (itc) );
190 191
    return( itc );
}
192
#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
193
          __GNUC__ && __ia64__ */
194

195
#if !defined(HAVE_HARDCLOCK) && defined(_MSC_VER) && \
196
    !defined(EFIX64) && !defined(EFI32)
197

198
#define HAVE_HARDCLOCK
199

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
200
unsigned long mbedtls_timing_hardclock( void )
201 202
{
    LARGE_INTEGER offset;
203

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
204
    QueryPerformanceCounter( &offset );
205

206
    return( (unsigned long)( offset.QuadPart ) );
207
}
208
#endif /* !HAVE_HARDCLOCK && _MSC_VER && !EFIX64 && !EFI32 */
209

210
#if !defined(HAVE_HARDCLOCK)
211

212
#define HAVE_HARDCLOCK
213 214 215 216

static int hardclock_init = 0;
static struct timeval tv_init;

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
217
unsigned long mbedtls_timing_hardclock( void )
218 219 220 221 222 223 224 225 226 227 228 229 230
{
    struct timeval tv_cur;

    if( hardclock_init == 0 )
    {
        gettimeofday( &tv_init, NULL );
        hardclock_init = 1;
    }

    gettimeofday( &tv_cur, NULL );
    return( ( tv_cur.tv_sec  - tv_init.tv_sec  ) * 1000000
          + ( tv_cur.tv_usec - tv_init.tv_usec ) );
}
231
#endif /* !HAVE_HARDCLOCK */
232

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
233
volatile int mbedtls_timing_alarmed = 0;
234

235
#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
236

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
237
unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset )
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
{
    unsigned long delta;
    LARGE_INTEGER offset, hfreq;
    struct _hr_time *t = (struct _hr_time *) val;

    QueryPerformanceCounter(  &offset );
    QueryPerformanceFrequency( &hfreq );

    delta = (unsigned long)( ( 1000 *
        ( offset.QuadPart - t->start.QuadPart ) ) /
           hfreq.QuadPart );

    if( reset )
        QueryPerformanceCounter( &t->start );

    return( delta );
}

256 257 258
/* It's OK to use a global because alarm() is supposed to be global anyway */
static DWORD alarmMs;

259
static DWORD WINAPI TimerProc( LPVOID TimerContext )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
260
{
261 262
    ((void) TimerContext);
    Sleep( alarmMs );
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
263
    mbedtls_timing_alarmed = 1;
264 265 266
    return( TRUE );
}

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
267
void mbedtls_set_alarm( int seconds )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
268
{
269 270
    DWORD ThreadId;

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
271
    mbedtls_timing_alarmed = 0;
272 273
    alarmMs = seconds * 1000;
    CloseHandle( CreateThread( NULL, 0, TimerProc, NULL, 0, &ThreadId ) );
274 275
}

276
#else /* _WIN32 && !EFIX64 && !EFI32 */
277

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
278
unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset )
279 280 281 282 283 284 285 286 287 288 289
{
    unsigned long delta;
    struct timeval offset;
    struct _hr_time *t = (struct _hr_time *) val;

    gettimeofday( &offset, NULL );

    if( reset )
    {
        t->start.tv_sec  = offset.tv_sec;
        t->start.tv_usec = offset.tv_usec;
290
        return( 0 );
291 292
    }

293 294 295
    delta = ( offset.tv_sec  - t->start.tv_sec  ) * 1000
          + ( offset.tv_usec - t->start.tv_usec ) / 1000;

296 297 298 299
    return( delta );
}

static void sighandler( int signum )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
300
{
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
301
    mbedtls_timing_alarmed = 1;
302 303 304
    signal( signum, sighandler );
}

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
305
void mbedtls_set_alarm( int seconds )
306
{
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
307
    mbedtls_timing_alarmed = 0;
308 309 310 311
    signal( SIGALRM, sighandler );
    alarm( seconds );
}

312
#endif /* _WIN32 && !EFIX64 && !EFI32 */
313

314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
/*
 * Set delays to watch
 */
void mbedtls_timing_set_delay( void *data, uint32_t int_ms, uint32_t fin_ms )
{
    mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data;

    ctx->int_ms = int_ms;
    ctx->fin_ms = fin_ms;

    if( fin_ms != 0 )
        (void) mbedtls_timing_get_timer( &ctx->timer, 1 );
}

/*
 * Get number of delays expired
 */
int mbedtls_timing_get_delay( void *data )
{
    mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data;
    unsigned long elapsed_ms;

    if( ctx->fin_ms == 0 )
        return( -1 );

    elapsed_ms = mbedtls_timing_get_timer( &ctx->timer, 0 );

    if( elapsed_ms >= ctx->fin_ms )
        return( 2 );

    if( elapsed_ms >= ctx->int_ms )
        return( 1 );

    return( 0 );
}

350 351
#endif /* !MBEDTLS_TIMING_ALT */

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
352
#if defined(MBEDTLS_SELF_TEST)
353

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
354
/* To test mbedtls_net_usleep against our functions */
355
#if defined(MBEDTLS_NET_C)
356
#include "mbedtls/net.h"
357 358
#endif

359 360
/*
 * Busy-waits for the given number of milliseconds.
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
361
 * Used for testing mbedtls_timing_hardclock.
362 363 364
 */
static void busy_msleep( unsigned long msec )
{
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
365
    struct mbedtls_timing_hr_time hires;
366 367 368
    unsigned long i = 0; /* for busy-waiting */
    volatile unsigned long j; /* to prevent optimisation */

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
369
    (void) mbedtls_timing_get_timer( &hires, 1 );
370

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
371
    while( mbedtls_timing_get_timer( &hires, 0 ) < msec )
372 373 374 375 376 377
        i++;

    j = i;
    (void) j;
}

378 379 380 381 382 383 384 385
#define FAIL    do                      \
{                                       \
    if( verbose != 0 )                  \
        mbedtls_printf( "failed\n" );   \
                                        \
    return( 1 );                        \
} while( 0 )

386 387
/*
 * Checkup routine
388 389 390
 *
 * Warning: this is work in progress, some tests may not be reliable enough
 * yet! False positives may happen.
391
 */
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
392
int mbedtls_timing_self_test( int verbose )
393 394 395 396
{
    unsigned long cycles, ratio;
    unsigned long millisecs, secs;
    int hardfail;
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
397
    struct mbedtls_timing_hr_time hires;
398 399
    uint32_t a, b;
    mbedtls_timing_delay_context ctx;
400

401
    if( verbose != 0 )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
402
        mbedtls_printf( "  TIMING tests note: will take some time!\n" );
403 404 405


    if( verbose != 0 )
406
        mbedtls_printf( "  TIMING test #1 (set_alarm / get_timer): " );
407 408 409

    for( secs = 1; secs <= 3; secs++ )
    {
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
410
        (void) mbedtls_timing_get_timer( &hires, 1 );
411

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
412 413
        mbedtls_set_alarm( (int) secs );
        while( !mbedtls_timing_alarmed )
414 415
            ;

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
416
        millisecs = mbedtls_timing_get_timer( &hires, 0 );
417 418 419 420

        if( millisecs < 900 * secs || millisecs > 1100 * secs )
        {
            if( verbose != 0 )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
421
                mbedtls_printf( "failed\n" );
422 423 424 425 426 427

            return( 1 );
        }
    }

    if( verbose != 0 )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
428
        mbedtls_printf( "passed\n" );
429 430

    if( verbose != 0 )
431
        mbedtls_printf( "  TIMING test #2 (hardclock / get_timer): " );
432 433 434 435 436 437 438 439 440 441 442 443

    /*
     * Allow one failure for possible counter wrapping.
     * On a 4Ghz 32-bit machine the cycle counter wraps about once per second;
     * since the whole test is about 10ms, it shouldn't happen twice in a row.
     */
    hardfail = 0;

hard_test:
    if( hardfail > 1 )
    {
        if( verbose != 0 )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
444
            mbedtls_printf( "failed\n" );
445 446 447 448 449

        return( 1 );
    }

    /* Get a reference ratio cycles/ms */
450
    millisecs = 1;
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
451
    cycles = mbedtls_timing_hardclock();
452
    busy_msleep( millisecs );
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
453
    cycles = mbedtls_timing_hardclock() - cycles;
454
    ratio = cycles / millisecs;
455

456
    /* Check that the ratio is mostly constant */
457 458
    for( millisecs = 2; millisecs <= 4; millisecs++ )
    {
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
459
        cycles = mbedtls_timing_hardclock();
460
        busy_msleep( millisecs );
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
461
        cycles = mbedtls_timing_hardclock() - cycles;
462 463 464 465 466 467 468 469 470 471 472

        /* Allow variation up to 20% */
        if( cycles / millisecs < ratio - ratio / 5 ||
            cycles / millisecs > ratio + ratio / 5 )
        {
            hardfail++;
            goto hard_test;
        }
    }

    if( verbose != 0 )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
473
        mbedtls_printf( "passed\n" );
474

475
    if( verbose != 0 )
476
        mbedtls_printf( "  TIMING test #3 (set/get_delay        ): " );
477 478 479 480 481 482 483

    for( a = 100; a <= 200; a += 100 )
    {
        for( b = 100; b <= 200; b += 100 )
        {
            mbedtls_timing_set_delay( &ctx, a, a + b );

484
            busy_msleep( a - a / 10 );
485 486 487
            if( mbedtls_timing_get_delay( &ctx ) != 0 )
                FAIL;

488
            busy_msleep( a / 5 );
489 490 491
            if( mbedtls_timing_get_delay( &ctx ) != 1 )
                FAIL;

492
            busy_msleep( b - a / 5 );
493 494 495
            if( mbedtls_timing_get_delay( &ctx ) != 1 )
                FAIL;

496
            busy_msleep( b / 5 );
497 498 499 500 501 502
            if( mbedtls_timing_get_delay( &ctx ) != 2 )
                FAIL;
        }
    }

    mbedtls_timing_set_delay( &ctx, 0, 0 );
503
    busy_msleep( 200 );
504 505 506 507 508 509
    if( mbedtls_timing_get_delay( &ctx ) != -1 )
        FAIL;

    if( verbose != 0 )
        mbedtls_printf( "passed\n" );

510
#if defined(MBEDTLS_NET_C)
511
    if( verbose != 0 )
512
        mbedtls_printf( "  TIMING test #4 (net_usleep/ get_timer): " );
513 514 515

    for( secs = 1; secs <= 3; secs++ )
    {
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
516
        (void) mbedtls_timing_get_timer( &hires, 1 );
517

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
518
        mbedtls_net_usleep( 500000 * secs );
519

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
520
        millisecs = mbedtls_timing_get_timer( &hires, 0 );
521 522 523 524

        if( millisecs < 450 * secs || millisecs > 550 * secs )
        {
            if( verbose != 0 )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
525
                mbedtls_printf( "failed\n" );
526 527 528 529 530 531

            return( 1 );
        }
    }

    if( verbose != 0 )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
532 533
        mbedtls_printf( "passed\n" );
#endif /* MBEDTLS_NET_C */
534

535
    if( verbose != 0 )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
536
        mbedtls_printf( "\n" );
537

538 539 540
    return( 0 );
}

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
541
#endif /* MBEDTLS_SELF_TEST */
542

543
#endif /* MBEDTLS_TIMING_C */