Merge pull request #6971 from gabor-mezei-arm/6026_Secp192r1_fast_reduction

Extract Secp192r1 fast reduction from the prototype
This commit is contained in:
Gilles Peskine 2023-02-15 16:22:36 +01:00 committed by GitHub
commit e2a9f86755
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 286 additions and 18 deletions

View file

@ -4570,6 +4570,8 @@ static int ecp_group_load(mbedtls_ecp_group *grp,
/* Forward declarations */
#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED)
static int ecp_mod_p192(mbedtls_mpi *);
MBEDTLS_STATIC_TESTABLE
int mbedtls_ecp_mod_p192_raw(mbedtls_mpi_uint *Np, size_t Nn);
#endif
#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED)
static int ecp_mod_p224(mbedtls_mpi *);
@ -4884,10 +4886,12 @@ static inline void carry64(mbedtls_mpi_uint *dst, mbedtls_mpi_uint *carry)
}
#define WIDTH 8 / sizeof(mbedtls_mpi_uint)
#define A(i) N->p + (i) * WIDTH
#define ADD(i) add64(p, A(i), &c)
#define A(i) Np + (i) * WIDTH
#define ADD(i) add64(p, A(i), &c)
#define NEXT p += WIDTH; carry64(p, &c)
#define LAST p += WIDTH; *p = c; while (++p < end) *p = 0
#define RESET last_carry[0] = c; c = 0; p = Np
#define ADD_LAST add64(p, last_carry, &c)
/*
* Fast quasi-reduction modulo p192 (FIPS 186-3 D.2.1)
@ -4895,28 +4899,51 @@ static inline void carry64(mbedtls_mpi_uint *dst, mbedtls_mpi_uint *carry)
static int ecp_mod_p192(mbedtls_mpi *N)
{
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
mbedtls_mpi_uint c = 0;
mbedtls_mpi_uint *p, *end;
/* Make sure we have enough blocks so that A(5) is legal */
MBEDTLS_MPI_CHK(mbedtls_mpi_grow(N, 6 * WIDTH));
p = N->p;
end = p + N->n;
ADD(3); ADD(5); NEXT; // A0 += A3 + A5
ADD(3); ADD(4); ADD(5); NEXT; // A1 += A3 + A4 + A5
ADD(4); ADD(5); LAST; // A2 += A4 + A5
size_t expected_width = 2 * ((192 + biL - 1) / biL);
MBEDTLS_MPI_CHK(mbedtls_mpi_grow(N, expected_width));
ret = mbedtls_ecp_mod_p192_raw(N->p, expected_width);
cleanup:
return ret;
}
MBEDTLS_STATIC_TESTABLE
int mbedtls_ecp_mod_p192_raw(mbedtls_mpi_uint *Np, size_t Nn)
{
mbedtls_mpi_uint c = 0, last_carry[WIDTH] = { 0 };
mbedtls_mpi_uint *p, *end;
if (Nn != 2*((192 + biL - 1)/biL)) {
return MBEDTLS_ERR_ECP_BAD_INPUT_DATA;
}
p = Np;
end = p + Nn;
ADD(3); ADD(5); NEXT; // A0 += A3 + A5
ADD(3); ADD(4); ADD(5); NEXT; // A1 += A3 + A4 + A5
ADD(4); ADD(5); // A2 += A4 + A5
RESET;
/* Use the reduction for the carry as well:
* 2^192 * last_carry = 2^64 * last_carry + last_carry mod P192
*/
ADD_LAST; NEXT; // A0 += last_carry
ADD_LAST; NEXT; // A1 += last_carry
LAST; // A2 += carry
return 0;
}
#undef WIDTH
#undef A
#undef ADD
#undef NEXT
#undef LAST
#undef RESET
#undef ADD_LAST
#endif /* MBEDTLS_ECP_DP_SECP192R1_ENABLED */
#if defined(MBEDTLS_ECP_DP_SECP224R1_ENABLED) || \

View file

@ -76,6 +76,25 @@ int mbedtls_ecp_gen_privkey_mx(size_t n_bits,
#endif /* MBEDTLS_ECP_MONTGOMERY_ENABLED */
#if defined(MBEDTLS_ECP_DP_SECP192R1_ENABLED)
/** Fast quasi-reduction modulo p192 (FIPS 186-3 D.2.1)
*
* This operation expects a 384 bit MPI and the result of the reduction
* is a 192 bit MPI.
*
* \param[in,out] Np The address of the MPI to be converted.
* Must have twice as many limbs as the modulus.
* Upon return this holds the reduced value. The bitlength
* of the reduced value is the same as that of the modulus
* (192 bits).
* \param[in] Nn The length of \p Np in limbs.
*/
MBEDTLS_STATIC_TESTABLE
int mbedtls_ecp_mod_p192_raw(mbedtls_mpi_uint *Np, size_t Nn);
#endif /* MBEDTLS_ECP_DP_SECP192R1_ENABLED */
#endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_ECP_C */
#endif /* MBEDTLS_ECP_INVASIVE_H */

View file

@ -11,4 +11,5 @@ python scripts\generate_ssl_debug_helpers.py || exit /b 1
perl scripts\generate_visualc_files.pl || exit /b 1
python scripts\generate_psa_constants.py || exit /b 1
python tests\scripts\generate_bignum_tests.py || exit /b 1
python tests\scripts\generate_ecp_tests.py || exit /b 1
python tests\scripts\generate_psa_tests.py || exit /b 1

View file

@ -0,0 +1,77 @@
"""Framework classes for generation of ecp test cases."""
# Copyright The Mbed TLS Contributors
# 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.
from typing import List
from . import test_data_generation
from . import bignum_common
class EcpTarget(test_data_generation.BaseTarget):
#pylint: disable=abstract-method, too-few-public-methods
"""Target for ecp test case generation."""
target_basename = 'test_suite_ecp.generated'
class EcpP192R1Raw(bignum_common.ModOperationCommon,
EcpTarget):
"""Test cases for ecp quasi_reduction()."""
symbol = "-"
test_function = "ecp_mod_p192_raw"
test_name = "ecp_mod_p192_raw"
input_style = "fixed"
arity = 1
moduli = ["fffffffffffffffffffffffffffffffeffffffffffffffff"] # type: List[str]
input_values = [
"0", "1",
# Modulus - 1
"fffffffffffffffffffffffffffffffefffffffffffffffe",
# First 8 number generated by random.getrandbits(384) - seed(2,2)
("cf1822ffbc6887782b491044d5e341245c6e433715ba2bdd"
"177219d30e7a269fd95bafc8f2a4d27bdcf4bb99f4bea973"),
("ffed9235288bc781ae66267594c9c9500925e4749b575bd1"
"3653f8dd9b1f282e4067c3584ee207f8da94e3e8ab73738f"),
("ef8acd128b4f2fc15f3f57ebf30b94fa82523e86feac7eb7"
"dc38f519b91751dacdbd47d364be8049a372db8f6e405d93"),
("e8624fab5186ee32ee8d7ee9770348a05d300cb90706a045"
"defc044a09325626e6b58de744ab6cce80877b6f71e1f6d2"),
("2d3d854e061b90303b08c6e33c7295782d6c797f8f7d9b78"
"2a1be9cd8697bbd0e2520e33e44c50556c71c4a66148a86f"),
("fec3f6b32e8d4b8a8f54f8ceacaab39e83844b40ffa9b9f1"
"5c14bc4a829e07b0829a48d422fe99a22c70501e533c9135"),
("97eeab64ca2ce6bc5d3fd983c34c769fe89204e2e8168561"
"867e5e15bc01bfce6a27e0dfcbf8754472154e76e4c11ab2"),
("bd143fa9b714210c665d7435c1066932f4767f26294365b2"
"721dea3bf63f23d0dbe53fcafb2147df5ca495fa5a91c89b"),
# Next 2 number generated by random.getrandbits(192)
"47733e847d718d733ff98ff387c56473a7a83ee0761ebfd2",
"cbd4d3e2d4dec9ef83f0be4e80371eb97f81375eecc1cb63"
]
@property
def arg_a(self) -> str:
return super().format_arg('{:x}'.format(self.int_a)).zfill(2 * self.hex_digits)
def result(self) -> List[str]:
result = self.int_a % self.int_n
return [self.format_result(result)]
@property
def is_valid(self) -> bool:
return True

View file

@ -29,6 +29,18 @@ execute_process(
string(REGEX REPLACE "[^;]*/" ""
base_bignum_generated_data_files "${base_bignum_generated_data_files}")
execute_process(
COMMAND
${MBEDTLS_PYTHON_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/../tests/scripts/generate_ecp_tests.py
--list-for-cmake
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}/..
OUTPUT_VARIABLE
base_ecp_generated_data_files)
string(REGEX REPLACE "[^;]*/" ""
base_ecp_generated_data_files "${base_ecp_generated_data_files}")
execute_process(
COMMAND
${MBEDTLS_PYTHON_EXECUTABLE}
@ -44,14 +56,18 @@ string(REGEX REPLACE "[^;]*/" ""
# Derive generated file paths in the build directory. The generated data
# files go into the suites/ subdirectory.
set(base_generated_data_files
${base_bignum_generated_data_files} ${base_psa_generated_data_files})
${base_bignum_generated_data_files} ${base_ecp_generated_data_files} ${base_psa_generated_data_files})
string(REGEX REPLACE "([^;]+)" "suites/\\1"
all_generated_data_files "${base_generated_data_files}")
set(bignum_generated_data_files "")
set(ecp_generated_data_files "")
set(psa_generated_data_files "")
foreach(file ${base_bignum_generated_data_files})
list(APPEND bignum_generated_data_files ${CMAKE_CURRENT_BINARY_DIR}/suites/${file})
endforeach()
foreach(file ${base_ecp_generated_data_files})
list(APPEND ecp_generated_data_files ${CMAKE_CURRENT_BINARY_DIR}/suites/${file})
endforeach()
foreach(file ${base_psa_generated_data_files})
list(APPEND psa_generated_data_files ${CMAKE_CURRENT_BINARY_DIR}/suites/${file})
endforeach()
@ -75,6 +91,22 @@ if(GEN_FILES)
${CMAKE_CURRENT_SOURCE_DIR}/../scripts/mbedtls_dev/test_case.py
${CMAKE_CURRENT_SOURCE_DIR}/../scripts/mbedtls_dev/test_data_generation.py
)
add_custom_command(
OUTPUT
${ecp_generated_data_files}
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}/..
COMMAND
${MBEDTLS_PYTHON_EXECUTABLE}
${CMAKE_CURRENT_SOURCE_DIR}/../tests/scripts/generate_ecp_tests.py
--directory ${CMAKE_CURRENT_BINARY_DIR}/suites
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/../tests/scripts/generate_ecp_tests.py
${CMAKE_CURRENT_SOURCE_DIR}/../scripts/mbedtls_dev/bignum_common.py
${CMAKE_CURRENT_SOURCE_DIR}/../scripts/mbedtls_dev/ecp.py
${CMAKE_CURRENT_SOURCE_DIR}/../scripts/mbedtls_dev/test_case.py
${CMAKE_CURRENT_SOURCE_DIR}/../scripts/mbedtls_dev/test_data_generation.py
)
add_custom_command(
OUTPUT
${psa_generated_data_files}
@ -107,6 +139,7 @@ endif()
# With this line, only 4 sub-makefiles include the above command, that reduces
# the risk of a race.
add_custom_target(test_suite_bignum_generated_data DEPENDS ${bignum_generated_data_files})
add_custom_target(test_suite_ecp_generated_data DEPENDS ${ecp_generated_data_files})
add_custom_target(test_suite_psa_generated_data DEPENDS ${psa_generated_data_files})
# If SKIP_TEST_SUITES is not defined with -D, get it from the environment.
if((NOT DEFINED SKIP_TEST_SUITES) AND (DEFINED ENV{SKIP_TEST_SUITES}))
@ -129,6 +162,7 @@ function(add_test_suite suite_name)
# Get the test names of the tests with generated .data files
# from the generated_data_files list in parent scope.
set(bignum_generated_data_names "")
set(ecp_generated_data_names "")
set(psa_generated_data_names "")
foreach(generated_data_file ${bignum_generated_data_files})
# Get the plain filename
@ -139,6 +173,15 @@ function(add_test_suite suite_name)
string(SUBSTRING ${generated_data_name} 11 -1 generated_data_name)
list(APPEND bignum_generated_data_names ${generated_data_name})
endforeach()
foreach(generated_data_file ${ecp_generated_data_files})
# Get the plain filename
get_filename_component(generated_data_name ${generated_data_file} NAME)
# Remove the ".data" extension
get_name_without_last_ext(generated_data_name ${generated_data_name})
# Remove leading "test_suite_"
string(SUBSTRING ${generated_data_name} 11 -1 generated_data_name)
list(APPEND ecp_generated_data_names ${generated_data_name})
endforeach()
foreach(generated_data_file ${psa_generated_data_files})
# Get the plain filename
get_filename_component(generated_data_name ${generated_data_file} NAME)
@ -153,6 +196,10 @@ function(add_test_suite suite_name)
set(data_file
${CMAKE_CURRENT_BINARY_DIR}/suites/test_suite_${data_name}.data)
set(dependency test_suite_bignum_generated_data)
elseif(";${ecp_generated_data_names};" MATCHES ";${data_name};")
set(data_file
${CMAKE_CURRENT_BINARY_DIR}/suites/test_suite_${data_name}.data)
set(dependency test_suite_ecp_generated_data)
elseif(";${psa_generated_data_names};" MATCHES ";${data_name};")
set(data_file
${CMAKE_CURRENT_BINARY_DIR}/suites/test_suite_${data_name}.data)
@ -160,7 +207,7 @@ function(add_test_suite suite_name)
else()
set(data_file
${CMAKE_CURRENT_SOURCE_DIR}/suites/test_suite_${data_name}.data)
set(dependency test_suite_bignum_generated_data test_suite_psa_generated_data)
set(dependency test_suite_bignum_generated_data test_suite_ecp_generated_data test_suite_psa_generated_data)
endif()
add_custom_command(

View file

@ -73,6 +73,13 @@ GENERATED_BIGNUM_DATA_FILES := $(patsubst tests/%,%,$(shell \
ifeq ($(GENERATED_BIGNUM_DATA_FILES),FAILED)
$(error "$(PYTHON) scripts/generate_bignum_tests.py --list" failed)
endif
GENERATED_ECP_DATA_FILES := $(patsubst tests/%,%,$(shell \
$(PYTHON) scripts/generate_ecp_tests.py --list || \
echo FAILED \
))
ifeq ($(GENERATED_ECP_DATA_FILES),FAILED)
$(error "$(PYTHON) scripts/generate_ecp_tests.py --list" failed)
endif
GENERATED_PSA_DATA_FILES := $(patsubst tests/%,%,$(shell \
$(PYTHON) scripts/generate_psa_tests.py --list || \
echo FAILED \
@ -80,7 +87,7 @@ GENERATED_PSA_DATA_FILES := $(patsubst tests/%,%,$(shell \
ifeq ($(GENERATED_PSA_DATA_FILES),FAILED)
$(error "$(PYTHON) scripts/generate_psa_tests.py --list" failed)
endif
GENERATED_FILES := $(GENERATED_PSA_DATA_FILES) $(GENERATED_BIGNUM_DATA_FILES)
GENERATED_FILES := $(GENERATED_PSA_DATA_FILES) $(GENERATED_ECP_DATA_FILES) $(GENERATED_BIGNUM_DATA_FILES)
generated_files: $(GENERATED_FILES)
# generate_bignum_tests.py and generate_psa_tests.py spend more time analyzing
@ -89,7 +96,7 @@ generated_files: $(GENERATED_FILES)
# It's rare not to want all the outputs. So always generate all of its outputs.
# Use an intermediate phony dependency so that parallel builds don't run
# a separate instance of the recipe for each output file.
.SECONDARY: generated_bignum_test_data generated_psa_test_data
.SECONDARY: generated_bignum_test_data generated_ecp_test_data generated_psa_test_data
$(GENERATED_BIGNUM_DATA_FILES): generated_bignum_test_data
generated_bignum_test_data: scripts/generate_bignum_tests.py
generated_bignum_test_data: ../scripts/mbedtls_dev/bignum_common.py
@ -102,6 +109,16 @@ generated_bignum_test_data:
echo " Gen $(GENERATED_BIGNUM_DATA_FILES)"
$(PYTHON) scripts/generate_bignum_tests.py
$(GENERATED_ECP_DATA_FILES): generated_ecp_test_data
generated_ecp_test_data: scripts/generate_ecp_tests.py
generated_ecp_test_data: ../scripts/mbedtls_dev/bignum_common.py
generated_ecp_test_data: ../scripts/mbedtls_dev/ecp.py
generated_ecp_test_data: ../scripts/mbedtls_dev/test_case.py
generated_ecp_test_data: ../scripts/mbedtls_dev/test_data_generation.py
generated_ecp_test_data:
echo " Gen $(GENERATED_ECP_DATA_FILES)"
$(PYTHON) scripts/generate_ecp_tests.py
$(GENERATED_PSA_DATA_FILES): generated_psa_test_data
generated_psa_test_data: scripts/generate_psa_tests.py
generated_psa_test_data: ../scripts/mbedtls_dev/crypto_knowledge.py

View file

@ -137,4 +137,5 @@ check scripts/generate_ssl_debug_helpers.py library/ssl_debug_helpers_generated.
check scripts/generate_visualc_files.pl visualc/VS2013
check scripts/generate_psa_constants.py programs/psa/psa_constant_names_generated.c
check tests/scripts/generate_bignum_tests.py $(tests/scripts/generate_bignum_tests.py --list)
check tests/scripts/generate_ecp_tests.py $(tests/scripts/generate_ecp_tests.py --list)
check tests/scripts/generate_psa_tests.py $(tests/scripts/generate_psa_tests.py --list)

View file

@ -0,0 +1,34 @@
#!/usr/bin/env python3
"""Generate test data for ecp functions.
The command line usage, class structure and available methods are the same
as in generate_bignum_tests.py.
"""
# Copyright The Mbed TLS Contributors
# 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.
import sys
import scripts_path # pylint: disable=unused-import
from mbedtls_dev import test_data_generation
# Import modules containing additional test classes
# Test function classes in these modules will be registered by
# the framework
from mbedtls_dev import ecp # pylint: disable=unused-import
if __name__ == '__main__':
# Use the section of the docstring relevant to the CLI as description
test_data_generation.main(sys.argv[1:], "\n".join(__doc__.splitlines()[:4]))

View file

@ -3,6 +3,8 @@
#include "mbedtls/ecdsa.h"
#include "mbedtls/ecdh.h"
#include "bignum_core.h"
#include "bignum_mod_raw_invasive.h"
#include "ecp_invasive.h"
#if defined(MBEDTLS_TEST_HOOKS) && \
@ -1299,3 +1301,46 @@ exit:
mbedtls_mpi_free(&expected_n);
}
/* END_CASE */
/* BEGIN_CASE depends_on:MBEDTLS_TEST_HOOKS */
void ecp_mod_p192_raw(char *input_N,
char *input_X,
char *result)
{
mbedtls_mpi_uint *X = NULL;
mbedtls_mpi_uint *N = NULL;
mbedtls_mpi_uint *res = NULL;
size_t limbs_X;
size_t limbs_N;
size_t limbs_res;
mbedtls_mpi_mod_modulus m;
mbedtls_mpi_mod_modulus_init(&m);
TEST_EQUAL(mbedtls_test_read_mpi_core(&X, &limbs_X, input_X), 0);
TEST_EQUAL(mbedtls_test_read_mpi_core(&N, &limbs_N, input_N), 0);
TEST_EQUAL(mbedtls_test_read_mpi_core(&res, &limbs_res, result), 0);
size_t limbs = limbs_N;
size_t bytes = limbs * sizeof(mbedtls_mpi_uint);
TEST_EQUAL(limbs_X, 2 * limbs);
TEST_EQUAL(limbs_res, limbs);
TEST_EQUAL(mbedtls_mpi_mod_modulus_setup(
&m, N, limbs,
MBEDTLS_MPI_MOD_REP_MONTGOMERY), 0);
TEST_EQUAL(mbedtls_ecp_mod_p192_raw(X, limbs_X), 0);
TEST_LE_U(mbedtls_mpi_core_bitlen(X, limbs_X), 192);
mbedtls_mpi_mod_raw_fix_quasi_reduction(X, &m);
ASSERT_COMPARE(X, bytes, res, bytes);
exit:
mbedtls_free(X);
mbedtls_free(res);
mbedtls_mpi_mod_modulus_free(&m);
mbedtls_free(N);
}
/* END_CASE */