1 1.1 christos /* 2 1.1.1.3 christos * Copyright 2017-2024 The OpenSSL Project Authors. All Rights Reserved. 3 1.1 christos * 4 1.1.1.2 christos * Licensed under the Apache License 2.0 (the "License"); 5 1.1 christos * you may not use this file except in compliance with the License. 6 1.1 christos * You may obtain a copy of the License at 7 1.1 christos * https://www.openssl.org/source/license.html 8 1.1 christos * or in the file LICENSE in the source distribution. 9 1.1 christos */ 10 1.1 christos 11 1.1 christos #include "internal/nelem.h" 12 1.1 christos #include "testutil.h" 13 1.1 christos 14 1.1 christos #include <stdio.h> 15 1.1 christos #include <stdlib.h> 16 1.1 christos #include <string.h> 17 1.1 christos #include <ctype.h> 18 1.1 christos 19 1.1 christos #define NUM_REPEATS "1000000" 20 1.1 christos 21 1.1.1.2 christos static ossl_intmax_t num_repeats; 22 1.1 christos static int print_mode = 0; 23 1.1 christos 24 1.1 christos #ifndef OPENSSL_NO_EC 25 1.1 christos # include <openssl/ec.h> 26 1.1 christos # include <openssl/err.h> 27 1.1 christos # include <openssl/obj_mac.h> 28 1.1 christos # include <openssl/objects.h> 29 1.1 christos # include <openssl/rand.h> 30 1.1 christos # include <openssl/bn.h> 31 1.1 christos # include <openssl/opensslconf.h> 32 1.1 christos 33 1.1 christos static const char *kP256DefaultResult = 34 1.1 christos "A1E24B223B8E81BC1FFF99BAFB909EDB895FACDE7D6DA5EF5E7B3255FB378E0F"; 35 1.1 christos 36 1.1 christos /* 37 1.1 christos * Perform a deterministic walk on the curve, by starting from |point| and 38 1.1 christos * using the X-coordinate of the previous point as the next scalar for 39 1.1 christos * point multiplication. 40 1.1 christos * Returns the X-coordinate of the end result or NULL on error. 41 1.1 christos */ 42 1.1.1.2 christos static BIGNUM *walk_curve(const EC_GROUP *group, EC_POINT *point, 43 1.1.1.2 christos ossl_intmax_t num) 44 1.1 christos { 45 1.1 christos BIGNUM *scalar = NULL; 46 1.1.1.2 christos ossl_intmax_t i; 47 1.1 christos 48 1.1 christos if (!TEST_ptr(scalar = BN_new()) 49 1.1 christos || !TEST_true(EC_POINT_get_affine_coordinates(group, point, scalar, 50 1.1 christos NULL, NULL))) 51 1.1 christos goto err; 52 1.1 christos 53 1.1 christos for (i = 0; i < num; i++) { 54 1.1 christos if (!TEST_true(EC_POINT_mul(group, point, NULL, point, scalar, NULL)) 55 1.1 christos || !TEST_true(EC_POINT_get_affine_coordinates(group, point, 56 1.1 christos scalar, 57 1.1 christos NULL, NULL))) 58 1.1 christos goto err; 59 1.1 christos } 60 1.1 christos return scalar; 61 1.1 christos 62 1.1 christos err: 63 1.1 christos BN_free(scalar); 64 1.1 christos return NULL; 65 1.1 christos } 66 1.1 christos 67 1.1 christos static int test_curve(void) 68 1.1 christos { 69 1.1 christos EC_GROUP *group = NULL; 70 1.1 christos EC_POINT *point = NULL; 71 1.1 christos BIGNUM *result = NULL, *expected_result = NULL; 72 1.1 christos int ret = 0; 73 1.1 christos 74 1.1 christos /* 75 1.1 christos * We currently hard-code P-256, though adaptation to other curves. 76 1.1 christos * would be straightforward. 77 1.1 christos */ 78 1.1 christos if (!TEST_ptr(group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) 79 1.1 christos || !TEST_ptr(point = EC_POINT_dup(EC_GROUP_get0_generator(group), 80 1.1 christos group)) 81 1.1 christos || !TEST_ptr(result = walk_curve(group, point, num_repeats))) 82 1.1.1.3 christos goto err; 83 1.1 christos 84 1.1 christos if (print_mode) { 85 1.1 christos BN_print(bio_out, result); 86 1.1 christos BIO_printf(bio_out, "\n"); 87 1.1 christos ret = 1; 88 1.1 christos } else { 89 1.1 christos if (!TEST_true(BN_hex2bn(&expected_result, kP256DefaultResult)) 90 1.1 christos || !TEST_ptr(expected_result) 91 1.1 christos || !TEST_BN_eq(result, expected_result)) 92 1.1 christos goto err; 93 1.1 christos ret = 1; 94 1.1 christos } 95 1.1 christos 96 1.1 christos err: 97 1.1 christos EC_GROUP_free(group); 98 1.1 christos EC_POINT_free(point); 99 1.1 christos BN_free(result); 100 1.1 christos BN_free(expected_result); 101 1.1 christos return ret; 102 1.1 christos } 103 1.1 christos #endif 104 1.1 christos 105 1.1.1.2 christos typedef enum OPTION_choice { 106 1.1.1.2 christos OPT_ERR = -1, 107 1.1.1.2 christos OPT_EOF = 0, 108 1.1.1.2 christos OPT_NUM_REPEATS, 109 1.1.1.2 christos OPT_TEST_ENUM 110 1.1.1.2 christos } OPTION_CHOICE; 111 1.1 christos 112 1.1.1.2 christos const OPTIONS *test_get_options(void) 113 1.1.1.2 christos { 114 1.1.1.2 christos static const OPTIONS test_options[] = { 115 1.1.1.2 christos OPT_TEST_OPTIONS_DEFAULT_USAGE, 116 1.1.1.2 christos { "num", OPT_NUM_REPEATS, 'M', "Number of repeats" }, 117 1.1.1.2 christos { NULL } 118 1.1.1.2 christos }; 119 1.1.1.2 christos return test_options; 120 1.1 christos } 121 1.1 christos 122 1.1 christos /* 123 1.1 christos * Stress test the curve. If the '-num' argument is given, runs the loop 124 1.1 christos * |num| times and prints the resulting X-coordinate. Otherwise runs the test 125 1.1 christos * the default number of times and compares against the expected result. 126 1.1 christos */ 127 1.1 christos int setup_tests(void) 128 1.1 christos { 129 1.1.1.2 christos OPTION_CHOICE o; 130 1.1 christos 131 1.1.1.2 christos if (!opt_intmax(NUM_REPEATS, &num_repeats)) { 132 1.1 christos TEST_error("Cannot parse " NUM_REPEATS); 133 1.1 christos return 0; 134 1.1 christos } 135 1.1.1.2 christos 136 1.1.1.2 christos while ((o = opt_next()) != OPT_EOF) { 137 1.1.1.2 christos switch (o) { 138 1.1.1.2 christos case OPT_NUM_REPEATS: 139 1.1.1.2 christos if (!opt_intmax(opt_arg(), &num_repeats) 140 1.1.1.2 christos || num_repeats < 0) 141 1.1.1.2 christos return 0; 142 1.1.1.2 christos print_mode = 1; 143 1.1.1.2 christos break; 144 1.1.1.2 christos case OPT_TEST_CASES: 145 1.1.1.2 christos break; 146 1.1.1.2 christos default: 147 1.1.1.2 christos case OPT_ERR: 148 1.1 christos return 0; 149 1.1.1.2 christos } 150 1.1 christos } 151 1.1 christos 152 1.1 christos #ifndef OPENSSL_NO_EC 153 1.1 christos ADD_TEST(test_curve); 154 1.1 christos #endif 155 1.1 christos return 1; 156 1.1 christos } 157