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