-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for alternative RSA implementations #1060
Conversation
This commit adds convenience functions to the RSA module for computing a complete RSA private key (with fields N, P, Q, D, E, DP, DQ, QP) from a subset of core parameters, e.g. (N, D, E).
This commit extends the RSA interface by import/export calls that can be used to setup an RSA context from a subset of the core RSA parameters (N,P,Q,D,E). The intended workflow is the following: 1. Call mbedtls_rsa_import one or multiple times to import the core parameters. 2. Call mbedtls_rsa_complete to deduce remaining core parameters as well as any implementation-defined internal helper variables. The RSA context is ready for use after this call. The import function comes in two variants mbedtls_rsa_import and mbedtls_rsa_import_raw, the former taking pointers to MPI's as input, the latter pointers buffers holding to big-endian encoded MPI's. The reason for this splitting is the following: When only providing an import function accepting const MPI's, a user trying to import raw binary data into an RSA context has to convert these to MPI's first which before passing them to the import function, introducing an unnecessary copy of the data in memory. The alternative would be to have another MPI-based import-function with move-semantics, but this would be in contrast to the rest of the library's interfaces. Similarly, there are functions mbedtls_rsa_export and mbedtls_rsa_export_raw for exporting the core RSA parameters, either as MPI's or in big-endian binary format. The main import/export functions deliberately do not include the additional helper values DP, DQ and QP present in ASN.1-encoded RSA private keys. To nonetheless be able to check whether given parameters DP, DQ and QP are in accordance with a given RSA private key, the interface is extended by a function mbedtls_rsa_check_opt (in line with mbedtls_rsa_check_privkey, mbedtls_rsa_check_pubkey and mbedtls_rsa_check_pub_priv). Exporting the optional parameters is taken care of by mbedtls_export_opt (currently MPI format only).
This commit adds tests for the new library function mbedtls_rsa_deduce_private for deducing the private RSA exponent D from the public exponent E and the factorization (P,Q) of the RSA modulus: - Two toy examples with small numbers that can be checked by hand, one working fine and another failing due to bad parameters. - Two real world examples, one fine and one with bad parameters.
This commit adds test for the new library function mbedtls_rsa_deduce_moduli for deducing the prime factors (P,Q) of an RSA modulus N from knowledge of a pair (D,E) of public and private exponent: - Two toy examples that can be checked by hand, one fine and with bad parameters. - Two real world examples, one fine and one with bad parameters.
This commit adds numerous tests for the new library functions mbedtls_rsa_import and mbedtls_rsa_import_raw in conjunction with mbedtls_rsa_complete for importing and completing core sets of core RSA parameters (N,P,Q,D,E) into an RSA context, with the importing accepting either MPI's or raw big endian buffers. Each test is determined by the following parameters: 1) Set of parameters provided We're testing full sets (N,P,Q,D,E), partial sets (N,-,-,D,E) and (N,P,Q,-,E) that are sufficient to generate missing parameters, and the partial and insufficient set (N, -, Q, -, E). 2) Simultaenous or successive importing The functions rsa_import and rsa_import_raw accept importing parameters at once or one after another. We test both. 3) Sanity of parameters
This commit adds tests for the new library function mbedtls_rsa_export. Each test case performs the following steps: - Parse and convert a set of hex-string decoded core RSA parameters into MPI's. - Use these to initialize an RSA context - Export core RSA parameters as MPI's again afterwards - Compare initial MPI's to exported ones. In the private key case, all core parameters are exported and sanity-checked, regardless of whether they were also used during setup. Each test split is performed twice, once with successive and once with simultaneous exporting.
This commit adds tests for the new library function mbedtls_rsa_export_raw. Each test case performs the following steps: - Parse and convert a set of hex-string decoded core RSA parameters into big endian byte arrays. - Use these to initialize an RSA context - Export core RSA parameters as byte arrays again afterwards - Compare byte strings. Each test split is performed twice, once with successive and once with simultaneous exporting.
This commit adds test for the new library function mbedtls_rsa_check_params for checking a set of RSA core parameters. There are some toy example tests with small numbers that can be verified by hand, as well as tests with real world numbers. Complete, partial and corrupted data are tested, as well the check for primality exactly if a PRNG is provided.
This commit replaces direct manipulation of structure fields in the RSA selftest by calls to the extended interface.
This commit replaces direct manipulation of RSA context structure fields by calls to the extended RSA interface in pk_wrap.c.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two more merge issues, in all.sh
and config.h
.
tests/scripts/all.sh
Outdated
cleanup | ||
cp "$CONFIG_H" "$CONFIG_BAK" | ||
scripts/config.pl set MBEDTLS_RSA_NO_CRT | ||
CC=gcc CFLAGS='-Werror -Wall -Werror -O0' make |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because make
is now a function in keep-going mode, make
must come first on the command line:
make CC=gcc CFLAGS='-Werror -Wall -Werror -O0'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in d8b3851.
include/mbedtls/config.h
Outdated
@@ -270,6 +270,7 @@ | |||
//#define MBEDTLS_CMAC_ALT | |||
//#define MBEDTLS_DES_ALT | |||
//#define MBEDTLS_RSA_ALT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This list was almost in alphabetical order, please move RSA_ALT
and XTEA_ALT
to its rightful place and re-run generate_features.pl
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in d8b3851.
d8b3851
to
11f2805
Compare
- Adapt the change in all.sh to the new keep-going mode - Restore alphabetical order of configuration flags for alternative implementations in config.h and rebuild library/version_features.c
11f2805
to
88683b2
Compare
Running
The failure:
The test case parameters:
The function |
@gilles-peskine-arm This is a change of behavior stemming from the relatively recent change of If desired, solutions to restore the original behavior are to either add Looking at the code I noticed that |
The function `pk_get_rsapubkey` originally performed some basic sanity checks (e.g. on the size of public exponent) on the parsed RSA public key by a call to `mbedtls_rsa_check_pubkey`. This check was dropped because it is not possible to thoroughly check full parameter sanity (i.e. that (-)^E is a bijection on Z/NZ). Still, for the sake of not silently changing existing behavior, this commit puts back the call to `mbedtls_rsa_check_pubkey`.
The function `mbedtls_rsa_complete` is supposed to guarantee that RSA operations will complete without failure. In contrast, it does not ensure consistency of parameters, which is the task of the checking functions `rsa_check_pubkey` and `rsa_check_privkey`. Previously, the maximum allowed size of the RSA modulus was checked in `mbedtls_rsa_check_pubkey`. However, exceeding this size would lead to failure of some RSA operations, hence this check belongs to `mbedtls_rsa_complete` rather than `mbedtls_rsa_check_pubkey`. This commit moves it accordingly.
@gilles-peskine-arm Pushed commits to restore the old behavior and move the size check. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make minor fixes to all.sh
(see my comments) and run it (locally or on CI).
tests/scripts/all.sh
Outdated
scripts/config.pl set MBEDTLS_RSA_NO_CRT | ||
make CC=gcc CFLAGS='-Werror -Wall -Werror -O0' | ||
|
||
msg "test: MBEDTLS_RSA_NO_CRT - main suites (inc. selftests) (ASan build)" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not an ASan build.
tests/scripts/all.sh
Outdated
cleanup | ||
cp "$CONFIG_H" "$CONFIG_BAK" | ||
scripts/config.pl set MBEDTLS_RSA_NO_CRT | ||
make CC=gcc CFLAGS='-Werror -Wall -Werror -O0' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why -O0
? And -Werror
is repeated, shouldn't that be -Werror -Wall -Wextra
or -Werror -Wall
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for spotting, that's a copy-paste error; the ! MBEDTLS_SSL_CLI_C
test above does it this way, too. I change it to -Werror -Wall -Wextra -O0
. Regarding the use of optimization, all.sh
seems to be inconsistent - I have no strong feelings in one direction or the other.
Correct gcc flags in !MBEDTLS_SSL_CLI_C test (preexisting) and build and test for RSA_NO_CRT in ASan mode.
@gilles-peskine-arm Ran |
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reviewed the latest commits and am happy with them.
Reviewed and approved. |
Goal
The main purpose of this PR is to pave the way for alternative RSA implementations by having the library interact with the RSA module through the RSA interface functions only, as opposed to direct manipulation of structure fields.
@mpg @gilles-peskine-arm @yanesca Please review.
Main parts
Use the gained flexibility to remove CRT fieldsDeprecate direct usage of RSA context fields, paving the way for a structure cleanup at a later stage.DP,DQ,QP,RP,RQ
from the RSA context ifRSA_NO_CRT
is defined (see below for whyP,Q
are retained nonetheless). After the preparation, this requires changes in the RSA module implementation only.Changes in more detail
Interface changes
mbedtls_rsa_import
) or as big endian byte arrays (mbedtls_rsa_import_raw
), and import can happen at once or successively.mbedtls_rsa_export
) or as big endian byte arrays (mbedtls_rsa_export_raw
).mbedtls_rsa_complete
that can be used to finalize the initialization of an RSA context after its core parameters have been imported (e.g., computing any internal helper variables from the core parameters).mbedtls_rsa_deduce_moduli
), and the derivation of the private exponent from the prime moduli and the public exponent (mbedtls_rsa_deduce_private
) and the computation of CRT parameters (mbedtls_rsa_deduce_crt
). The new completion function uses these helper functions to setup RSA contexts if they have only been partially specified.These changes are meant to encourage the transition from constructing RSA contexts by hand to building them through interface functions, which is hopefully more stable against future changes and/or drop-in alternative RSA implementations.
The PR does not remove or change the signature or semantics of any existing RSA interface function and is therefore meant to be fully backwards compatible.
Library changes
The library code directly interacting with the RSA module needed adaption in the PK interface layer:
pkwrite.c
,pkparse.c
andpk_wrap.c
.Test changes
all.sh
has been extended by a run withMBEDTLS_RSA_NO_CRT
set.Example program changes
Exemplifying the intended use of the new interface, numerous example programs have been modified accordingly:
key_app
,key_app_writer
,gen_key
,dh_server
,rsa_sign
,rsa_genkey
,rsa_encrypt
,rsa_decrypt
.Comments
Necessity of helper functions
The helper functions, in particular the one computing
P,Q
fromN,D,E
, were written with the intent to support implementations usingN,D,E
only - including our own ifMBEDTLS_RSA_NO_CRT
is set. After all, however, it seems that removingP,Q
introduces problems that outweigh the space benefit it brings - see below. KeepingP,Q
(or at least one of both) in the RSA context frees us from the need for the derivation functionmbedtls_rsa_deduce_moduli
, and we might therefore decide to drop it or to make it optional (disabled by default). This is up to discussion - for now I keptmbedtls_rsa_deduce_moduli
.Reducing the RSA context to
N,D,E
aloneIf neither CRT nor verification are used,
mbedtls_rsa_public
andmbedtls_rsa_private
can be implemented usingN,D,E
only, and so it is tempting to reduce the RSA context toN,D,E
alone. However, the following cumbersome questions arise:What does
mbedtls_rsa_check_privkey
check for if onlyN,D,E
are present?With only
N,D,E
given there are no simple sanity checks. Instead, to get confidence that they belong to some complete RSA keypair, one of the following computation heavy checks would need to be done:P,Q
fromN,D,E
and check thatN,P,Q,D,E
all fit together.N,D,E
only) and check that they are indeed inverse to each other.When setting up an RSA context from a DER encoded RSA key, where to do the checking of the unused fields
P,Q,DP,DQ,QP
?To make the problem clear, assume that we're setting up a bare-bones RSA context which uses
N,D,E
only, and which during setup completely ignoresP,Q,DP,DQ,QP
. When we're callingmbedtls_rsa_check_privkey
afterwards, this will somehow (as discussed above) check ifN,D,E
can be extended to a validN,P',Q',D,E
, but it will not take theP,Q
parameters from the original DER encoded key into consideration. The question is if and where the verification of these original parameters should happen. There are three options:P,Q,DP,DQ,QP
, it must accept and verify them at setup.Comments on that:
The first option is highly redundant if a full-fledged RSA key is used, because we're introducing both an unnecessary copy of the key and an unnecessary check (because for a full RSA context,
mbedtls_rsa_check_privkey
will contain all the checks).For the second, the interface must either enforce all parameters to be passed to the context at once at setup time, or the RSA context has to somehow save them internally until enough parameters are present for verification. In the former case, we're again unnecessarily introducing a full copy of the RSA key during setup when a full-fledged RSA context with all fields is used. In the latter, additional fields have to be added to the RSA context for the temporary storage, counteracting the initial intend of keeping the context small.
The third option sounds bad at first, but on the other we're probably trusting our own private key in the first place anyway?
The approach followed in this PR uses a mixture of the first and second approach: The RSA context must accept and validate the core parameters
N,P,Q,D,E
, and the compatibility-check of the parametersDP,DQ,QP
with the core parameters is done in the interface functionmbedtls_rsa_check_crt
after the setup is completed.I think that the little space gained from removing
P,Q
from the RSA context is not worth the complexity it introduces, so I sticked with it even whenMBEDTLS_RSA_NON_CRT
is set.