Skip to content

[WIP][RFC] Implement Montgomery Modular Multiplication in RISC-V Vector Extension ASM - #18479

Closed
ZenithalHourlyRate wants to merge 2 commits into
openssl:masterfrom
ZenithalHourlyRate:rvv-mmm
Closed

ZenithalHourlyRate wants to merge 2 commits into
openssl:masterfrom
ZenithalHourlyRate:rvv-mmm

Conversation

@ZenithalHourlyRate

Copy link
Copy Markdown
Contributor

This PR tries to demo that OpenSSL could use RISC-V Vector Extension to do modular multiplication, potentially accelerating (most notably) RSA4096 and other crypto operations. The author expect that with higher VLEN (e.g. 2048) the speedup would be more significant.

This is a draft and a baseline, and the author hereby request for comments on this implementation to seek for an eventually SOTA implementation.

More info on this implementation could be found at https://github.com/ZenithalHourlyRate/rvv-mmm

Technical details

This is a modified version of Montgomery Multiplication on the Cell. Most notably, instead of representing one big number (BN) consecutively in one reg, this paper uses a column representation. This reduces the cost of carry propagation and division in each round.

However, by using this representation, this algorithm needs to use a different montgomery radix R than the R OpenSSL used. For example, when M = 2 ** 255 - 19, the R for OpenSSL would be 256, but the R for this algorithm would be 272.

Since the algorithm uses a different R, all computation should then be carried out by bn_mont_mul and we should avoid word variant like bn_from_montgomery_word. By forcing all computation being carried out by bn_mont_mul, we need to break an invariant called minimal representation (details at #6640), thus we can not use BN_value_one as we need to expand the width. This also leads to some quirks on some lib functions, like BN_MONT_CTX_set and BN_mod_exp_mont.

Also, the algorithm needs to allocate space as it uses redundent representation and we need to convert BN to/from it; in the meantime the uint32_t AB[260] = {0}; allows vlsseg8.

For demostration purpose, there are only two implementations generated (256 bits and 4096 bits mmm), and the R for them are constants. You can alter vl/bn in these two perlasm (corresponding to mmm_loop.pl and mmm_mem.pl in my repo) for different BN with different minimum VLEN

Compilation

You need to enable v extention when compiling, I used the lastest toolchain from riscv-collab

CC="riscv64-unknown-linux-gnu-gcc -march=rv64gcv" ./config linux64-riscv64
make -j`nproc`
make DESTDIR=/path/to/sysroot install

Correctness

You can try the following commands between your native openssl and the RVV-backed openssl

# regular openssl
openssl genrsa -out private.pem 4096
# RVV-backed openssl
# may take a long time
export LD_LIBRARY_PATH=/path/to/sysroot/usr/local/lib
qemu-riscv64 -cpu rv64,v=on -L /path/to/sysroot apps/openssl pkeyutl -sign -in in.txt -inkey private.pem -out in.txt.rsa
# verify by regular openssl
openssl pkeyutl -verify -in in.txt -sigfile in.txt.rsa -inkey private.pem

Note that thanks to the feature of MMM, you can use RSA2048 even with 4096bit MMM.

Benchmark

AFAIK, there is no existing Vector 1.0 real machine available generally, let me know if there is one that I can play with.

Discussion

R choice

Currently all bn_mul_mont asm use the R provided by OpenSSL. But this algorithm uses a different R. Also, there is some hard-encoded behavior that relies on the choice of R, e.g. Shay Gueron's suggestion in bn_exp.c

Should we support different R, or just implement RVV MMM just like RSAZ and stick to the OpenSSL R.

subtraction less

From the paper above we know that for subtraction-less MMM we can represent the number in Z/2MZ instead of Z/MZ. This is especially beneficial for modular exponentiation. However, some comments showed that in some asm implementation it is assumed the input is less than M.

VLEN

As the function name bn_mul_mont_rv64imv_zvl128b_sew32_bn4096_nelement260 suggests, we would have a Cartesian product of all possible configurations when compiling. This increases the code size if we generate all of them, but if only some of them are provided, the speedup is a concern.

I know this is the packed SIMD way, but it seems not easy to implement in the vector way. Namely register assignment should be determined at codegen (by perlasm) time instead of runtime. I'll try to explore the vector way.

Checklist

@github-actions github-actions Bot added the severity: fips change The pull request changes FIPS provider sources label Jun 6, 2022
@t8m t8m added the triaged: feature The issue/pr requests/adds a feature label Jun 6, 2022
@fwh-dc

fwh-dc commented Mar 28, 2026

Copy link
Copy Markdown
Contributor

This has been idle for a while. Should we convert this to an issue to allow another developer to take on the work?

@ZenithalHourlyRate

Copy link
Copy Markdown
Contributor Author

This attempt turned out to be inefficient as there were some typos in the reference paper. It was opened as PR in hope it could serve other people. Closing now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

severity: fips change The pull request changes FIPS provider sources triaged: feature The issue/pr requests/adds a feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants