-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Replace the 64-bit C field implementation by fiat-crypto output #1319
base: master
Are you sure you want to change the base?
Conversation
Hi, I think I need some help here, too. Would we need to rewrite the code from Fiat Cryptography to use the
|
Ok, yes, that is a problem...
Yeah, I think so.
Well, that is somewhat expected, no?
The good thing is that our int128 implementation is formally proven correct, too. That means in practice we get meaningful guarantees. However, then things seem to get inelegant, and we need to fiddle a bit. We could translate the code "manually". It's probably easy to inspect, but this defeats the idea of using a code generator, at least somewhat. (We'd need to redo this every time we want to update the Fiat-Crypto code... Most likely that doesn't happen very frequently, though.) The most elegant solution will be changes in fiat-crypto, but that means more work on their side. I commented in mit-plv/fiat-crypto#1560 about this. |
Unless I am mistaken, there is no correctness proof for the translation from whatever Fiat Crypto's algorithm representation format is into C code, so mucking about with that translation layer won't lose any formal assurances because there weren't any for that layer to begin with. |
Yes, but then I don't see the point adapting in the first place. I've just read through mit-plv/fiat-crypto#1560, seems like they are aware of this. I also like Andres's three level approach. Either we'd wait for that (which is the most sensible I believe) or instead of replacing the current implementation, we can add the fiat-c and use it if the compiler supports it, and fall back to the existing one if not. |
b275c8f
to
ff0f64a
Compare
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.
One interesting point here is the discussion starting in mit-plv/fiat-crypto#1560 (comment).
The wrappers you add here return and take structs. I think there's, in general, no problem with this, it's just our (historical and possibly performance-guided) coding style to always structs by pointer. But as @roconnor-blockstream points out, passing or returning structs by value is incompatible with VST. As long as we use these wrappers only in this file, this seems perfectly fine to me. (And we may still reconsider this if @roconnor-blockstream wants to prove the entire codebase correct and is close to finishing this. 😉)
edit: @sipa @jonasnick It will be good to hear your opinion on this before @dderjoel and others start to dig deeper.
Code comments:
- I think it will be better to add a real uint128 function for
u128_add_u128_u128
(which can use pointers). The current wrapper you propose here is a bit inelegant and probably hurts performance. Are you willing to give it a try, or should we come up with something? - The wrappers should probably moved above the
/* Autogerated
part.
It was my intention on proving the field, then group operations correct after finishing (one of) the modular inverse implementation. I think performance should probably be the primary concern. However if correctness is the main motivation for this PR, then things get debatable. Each approach to formal correctness comes with a boatload of their own caveats. P.S. If it makes a difference I could expedite my proof of correctness of the field operations. Certainly this code is far easier to reason about than the modular inverse code, and could be completed much quicker. Still the various caveats remain. |
I think we should discuss this in the next IRC meeting on Monday. (I created a "next-meeting" tag for convenience.) |
Ideally, this PR should not affect performance, as the algorithm should be ours (modulo some low-level C details). So the primary concern here is indeed correctness (if you ask me). What would improve performance is cryptopt asm, which requires fiat-crypto. In principle, we could add the asm without updating the C code. (We felt it's better to update the C code first, see #1261, but this could be reconsidered...) |
Sure thing. I was really hoping we'd land at a place where we could do both. mit-plv/fiat-crypto#1560 (comment) mentions some bedrock2 thing which sounds like we might eventually get there, but not today. |
I've incorporated the changes:
I've also ran my benchmark suite (secp_bench) on my 10 machines. With Clang and GCC, with defaults and Clang
|
implementation | default_asm | default_c | fiat_c | fiat_cryptopt |
---|---|---|---|---|
ecmult_gen | 15.4785 | 15.1864 | 14.9061 | 13.9127 |
ecmult_const | 29.3045 | 28.0044 | 27.2884 | 26.4647 |
ecmult_1p | 23.2979 | 21.9812 | 21.5529 | 20.9758 |
ecmult_0p_g | 16.9407 | 15.72 | 15.4955 | 15.3359 |
ecmult_1p_g | 13.6274 | 12.8632 | 12.6361 | 12.2814 |
field_half | 0.00518161 | 0.00510187 | 0.00493484 | 0.00512773 |
field_normalize | 0.0047701 | 0.00483506 | 0.0047308 | 0.00475533 |
field_normalize_weak | 0.00283401 | 0.00282353 | 0.0028121 | 0.00286139 |
field_sqr | 0.0136218 | 0.0141461 | 0.0138296 | 0.0119747 |
field_mul | 0.017029 | 0.0167826 | 0.016331 | 0.014328 |
field_inverse | 1.51311 | 1.5147 | 1.64789 | 1.64148 |
field_inverse_var | 0.921765 | 0.918659 | 0.924158 | 0.924338 |
field_is_square_var | 1.21284 | 1.21378 | 1.22373 | 1.22978 |
field_sqrt | 3.81394 | 3.11671 | 3.79821 | 3.41099 |
GCC -O3 -mtune=native -march=native
implementation | default_asm | default_c | fiat_c | fiat_cryptopt |
---|---|---|---|---|
ecmult_gen | 16.7344 | 16.2269 | 15.7203 | 14.9838 |
ecmult_const | 31.0237 | 29.2068 | 29.1693 | 27.2251 |
ecmult_1p | 24.5181 | 23.3218 | 22.5183 | 21.2852 |
ecmult_0p_g | 17.5664 | 16.801 | 16.2549 | 15.3251 |
ecmult_1p_g | 14.3227 | 13.5419 | 13.1658 | 12.4306 |
field_half | 0.00260505 | 0.0024295 | 0.002416 | 0.00248083 |
field_normalize | 0.00717501 | 0.00694626 | 0.00684726 | 0.0070481 |
field_normalize_weak | 0.00302477 | 0.00293117 | 0.00286769 | 0.00298777 |
field_sqr | 0.0143681 | 0.012288 | 0.0163989 | 0.012114 |
field_mul | 0.0176796 | 0.014202 | 0.0178385 | 0.014767 |
field_inverse | 1.45613 | 1.44189 | 1.60521 | 1.66405 |
field_inverse_var | 0.905133 | 0.87939 | 0.867982 | 0.893649 |
field_is_square_var | 1.26129 | 1.21327 | 1.20329 | 1.23029 |
field_sqrt | 3.94966 | 3.52407 | 4.29576 | 3.41179 |
Clang default
implementation | default_asm | default_c | fiat_c | fiat_cryptopt |
---|---|---|---|---|
ecmult_gen | 14.5922 | 14.8619 | 15.2704 | 13.0178 |
ecmult_const | 28.9864 | 29.0937 | 30.0857 | 25.7223 |
ecmult_1p | 22.7649 | 22.5667 | 23.2965 | 20.3805 |
ecmult_0p_g | 16.5215 | 16.1956 | 16.8933 | 14.6744 |
ecmult_1p_g | 13.3438 | 13.1968 | 13.6445 | 11.9628 |
field_half | 0.00268292 | 0.00246003 | 0.00239556 | 0.00242083 |
field_normalize | 0.00700584 | 0.00699441 | 0.006914 | 0.00695979 |
field_normalize_weak | 0.00282523 | 0.00282798 | 0.00280225 | 0.00281053 |
field_sqr | 0.0137319 | 0.0128169 | 0.0146826 | 0.0119927 |
field_mul | 0.0171099 | 0.0166114 | 0.017404 | 0.0142171 |
field_inverse | 1.44857 | 1.44306 | 1.61719 | 1.61457 |
field_inverse_var | 0.965295 | 0.963569 | 0.957785 | 0.960852 |
field_is_square_var | 1.26972 | 1.2628 | 1.26665 | 1.26727 |
field_sqrt | 3.77267 | 3.51891 | 4.02258 | 3.33573 |
GCC with default
implementation | default_asm | default_c | fiat_c | fiat_cryptopt |
---|---|---|---|---|
ecmult_gen | 15.9837 | 14.9299 | 14.9616 | 14.4035 |
ecmult_const | 30.1929 | 28.1791 | 28.0992 | 26.7815 |
ecmult_1p | 23.8301 | 21.9197 | 21.971 | 20.8491 |
ecmult_0p_g | 16.8746 | 15.8356 | 15.8409 | 14.9011 |
ecmult_1p_g | 13.9126 | 12.8102 | 12.8655 | 12.1725 |
field_half | 0.00263405 | 0.00241618 | 0.00236633 | 0.00249735 |
field_normalize | 0.00737182 | 0.00743337 | 0.00731607 | 0.00734032 |
field_normalize_weak | 0.00293929 | 0.0029294 | 0.00290844 | 0.00289157 |
field_sqr | 0.0140036 | 0.0125453 | 0.0122373 | 0.0120457 |
field_mul | 0.0169176 | 0.0143035 | 0.0148859 | 0.0143003 |
field_inverse | 1.39951 | 1.39713 | 1.61788 | 1.61036 |
field_inverse_var | 0.911792 | 0.912018 | 0.908337 | 0.907321 |
field_is_square_var | 1.19553 | 1.20238 | 1.18607 | 1.19135 |
field_sqrt | 3.86876 | 3.56773 | 3.46217 | 3.36305 |
I just want to let you know that I'd be more than happy to explain the good/bad/ugly of what we fiat-crypto proofs are about in detail, if this would be of interest to the maintainers. Evaluating verification techniques can indeed be subtle, and I would try to be as unbiased of a resource as I can. In short, fiat-crypto proofs about field arithmetic cover limb representation and modular-reduction algorithm, correct determination of bit-sizes of intermediate values (no unexpected overflow / lost carries), and low-level arithmetic optimization. The last verified format of code fiat-crypto-to-C translation is arithmetic operators with truncating casts The main goal of the ongoing fiat-crypto-bedrock2 integration is to enable computer-checked integration proofs of generated field-arithmetic code, underlying polyfills, elliptic-curve algorithms, and application code above. In other words, we are looking to ensure that one proven layer of the cryptographic implementation does not make incompatible assumptions about another layer; we are working to avoid broken integration of individually proven components. Having a slightly lower-level language between fiat-crypto and C is a side benefit from our perspective. Similarly, pretty-printing (in bedrock2) and parsing (in CompCert before VST) do not represent a significant difference in our key considerations for semantic correctness; we chose pretty-printing to alleviate the user-experience concerns around ruling out common but poorly understood features of C that would be hard to model soundly. (The status of this work is that we have an x25519 implementation and some more proven and under submission, but not all performance-hurting shortcuts have been removed yet.) |
If anyone wants to look at the compiler output of e3affa1 vs master: https://godbolt.org/z/fPTafdqd8 |
Here are my notes on the topic of caveats of formal proofs: Regarding using VST
Regarding using Fiat-CryptoI’m less familiar with Fiat-Crypto, so the following caveats are only to the best of my knowledge. @andres-erbsen has written more on this topic above.
MiscellaneousNeither system makes any guarantees about constant-timedness, and arguably it is impossible to make any such statements about C code anyways. Ideally we’d have Fiat-Crypto generate C code that operates within VST’s limitations, however that seem to require an unknown amount of work on the Fiat-Crypto side, with some hints that sometime in the future it might be doable with bedrock2. If we want to go on beyond the field code and prove the group code correct, or the multi-exponentiation with VST, we either need to machine generate the specifications of the field code functions, or generate the specifications by hand. Either way, the correctness of the VST proof of the code would only be correct upto the correctness of the specifications of any non-VST compatible Fiat-Crypto generated code. Whereas if everything were done in VST, then we would have correctness all the way to the bottom (subject to VST’s own caveats). |
@roconnor-blockstream thank you for the overview. FWIW, I think we are trying to communicate very similar perspectives about current state of the tooling, though with different words. As for grand plans for future proofs, I would like to discuss them more, but perhaps outside this thread? I think we are pursuing quite similar directions and one would hope that we would be able to do something to minimize duplication of effort. In particular, ongoing work is also proving elliptic-curve code and its use cases on top of fiat-crypto using bedrock2 (and we are connecting the proofs in Coq using the fiat-crypto field-arithmetic specifications). Instead of proving the same code twice using different tools, I am guessing that our time would be better spent increasing proof coverage throughout the codebase, connecting between different toolchains informally at simple interfaces as needed. I am saying this partly because I believe bedrock2 C is even further semantically diverged from CompCert C (details in §2.4;2.5) than the fiat-crypto output here, but also I think it's just a good idea regardless. Or perhaps there's an even better strategy, but more coordination seems desirable either way. |
Ooph! Sounds like hypothetical bedrock2 generated C would end up containing a lot of But yeah, I guess the hypothetical bedrock2 generated C is likely even worse with respect to also facilitating VST proofs. |
Yeah, I'm bitter about the Do you think #181 would be a good place to continue this discussion, or shall we create a new issue? |
Sure, that works. (I'd love to have the ability to move comments/threads into other issues...) |
* eval out1 mod 115792089237316195423570985008687907853269984665640564039457584007908834671663 = (eval arg1 * eval arg2) mod 115792089237316195423570985008687907853269984665640564039457584007908834671663 | ||
* | ||
* Input Bounds: | ||
* arg1: [[0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1fffffffffffe]] |
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.
How should I be reading this specification?
fe_mul
is supposed to allow up to magnitude 8 input, which means the lower limbs are bounded by 0xFFFFFFFFFFFFF * 8 = 0x7ffffffffffff8
. But the statement here suggests the precondition is 0x1ffffffffffffe
which is only magnitude 2.
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.
Sounds like you're reading it successfully already: it means 0x0 <= arg1[0] && arg1[0] <= 0x1ffffffffffffe
and etc.
I instantiated the template with arguments that I think match the magnitude requirement you describe. The generated code didn't change, but here are the new specifications:
outdated diff
--- fiat-c/src/secp256k1_dettman_64.c 2023-07-31 18:50:07.191232393 +0000
+++ /tmp/det 2023-07-31 19:15:11.379514514 +0000
@@ -1,4 +1,4 @@
-/* Autogenerated: 'src/ExtractionOCaml/dettman_multiplication' --inline --static --use-value-barrier secp256k1_dettman 64 5 48 2 '2^256 - 4294968273' mul square */
+/* Autogenerated: 'src/ExtractionOCaml/dettman_multiplication' --inline --static --use-value-barrier secp256k1_dettman 64 5 48 2 '2^256 - 4294968273' mul square --inbounds-multiplier 4 */
/* curve description: secp256k1_dettman */
/* machine_wordsize = 64 (from "64") */
/* requested operations: mul, square */
@@ -6,7 +6,7 @@
/* last_limb_width = 48 (from "48") */
/* last_reduction = 2 (from "2") */
/* s-c = 2^256 - [(1, 4294968273)] (from "2^256 - 4294968273") */
-/* inbounds_multiplier: None (from "") */
+/* inbounds_multiplier: Some 4 (from "4") */
/* */
/* Computed values: */
/* */
@@ -36,10 +36,10 @@
* eval out1 mod 115792089237316195423570985008687907853269984665640564039457584007908834671663 = (eval arg1 * eval arg2) mod 115792089237316195423570985008687907853269984665640564039457584007908834671663
*
* Input Bounds:
- * arg1: [[0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1fffffffffffe]]
- * arg2: [[0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1fffffffffffe]]
+ * arg1: [[0x0 ~> 0x7ffffffffffff8], [0x0 ~> 0x7ffffffffffff8], [0x0 ~> 0x7ffffffffffff8], [0x0 ~> 0x7ffffffffffff8], [0x0 ~> 0x7fffffffffff8]]
+ * arg2: [[0x0 ~> 0x7ffffffffffff8], [0x0 ~> 0x7ffffffffffff8], [0x0 ~> 0x7ffffffffffff8], [0x0 ~> 0x7ffffffffffff8], [0x0 ~> 0x7fffffffffff8]]
* Output Bounds:
- * out1: [[0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x17fffffffffff]]
+ * out1: [[0x0 ~> 0x4ffffffffffffb], [0x0 ~> 0x4ffffffffffffb], [0x0 ~> 0x4ffffffffffffb], [0x0 ~> 0x4ffffffffffffb], [0x0 ~> 0x47ffffffffffc]]
*/
static FIAT_SECP256K1_DETTMAN_FIAT_INLINE void fiat_secp256k1_dettman_mul(uint64_t out1[5], const uint64_t arg1[5], const uint64_t arg2[5]) {
fiat_secp256k1_dettman_uint128 x1;
@@ -122,9 +122,9 @@
* eval out1 mod 115792089237316195423570985008687907853269984665640564039457584007908834671663 = (eval arg1 * eval arg1) mod 115792089237316195423570985008687907853269984665640564039457584007908834671663
*
* Input Bounds:
- * arg1: [[0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1fffffffffffe]]
+ * arg1: [[0x0 ~> 0x7ffffffffffff8], [0x0 ~> 0x7ffffffffffff8], [0x0 ~> 0x7ffffffffffff8], [0x0 ~> 0x7ffffffffffff8], [0x0 ~> 0x7fffffffffff8]]
* Output Bounds:
- * out1: [[0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x17fffffffffff]]
+ * out1: [[0x0 ~> 0x4ffffffffffffb], [0x0 ~> 0x4ffffffffffffb], [0x0 ~> 0x4ffffffffffffb], [0x0 ~> 0x4ffffffffffffb], [0x0 ~> 0x47ffffffffffc]]
*/
static FIAT_SECP256K1_DETTMAN_FIAT_INLINE void fiat_secp256k1_dettman_square(uint64_t out1[5], const uint64_t arg1[5]) {
uint64_t x1;
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
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.
Maybe third time is the charm. Taking into account my reading of what "magnitude" means, setting input magnitude to 8, and also asserting output magnitude 1 gives us
diff --git a/fiat-c/src/secp256k1_dettman_64.c b/fiat-c/src/secp256k1_dettman_64.c
index e666946ea..bfd47ce91 100644
--- a/fiat-c/src/secp256k1_dettman_64.c
+++ b/fiat-c/src/secp256k1_dettman_64.c
@@ -36,10 +36,10 @@ FIAT_SECP256K1_DETTMAN_FIAT_EXTENSION typedef unsigned __int128 fiat_secp256k1_d
* eval out1 mod 115792089237316195423570985008687907853269984665640564039457584007908834671663 = (eval arg1 * eval arg2) mod 115792089237316195423570985008687907853269984665640564039457584007908834671663
*
* Input Bounds:
- * arg1: [[0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1fffffffffffe]]
- * arg2: [[0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1fffffffffffe]]
+ * arg1: [[0x0 ~> 0xfffffffffffff0], [0x0 ~> 0xfffffffffffff0], [0x0 ~> 0xfffffffffffff0], [0x0 ~> 0xfffffffffffff0], [0x0 ~> 0xffffffffffff0]]
+ * arg2: [[0x0 ~> 0xfffffffffffff0], [0x0 ~> 0xfffffffffffff0], [0x0 ~> 0xfffffffffffff0], [0x0 ~> 0xfffffffffffff0], [0x0 ~> 0xffffffffffff0]]
* Output Bounds:
- * out1: [[0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x17fffffffffff]]
+ * out1: [[0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1fffffffffffe]]
*/
static FIAT_SECP256K1_DETTMAN_FIAT_INLINE void fiat_secp256k1_dettman_mul(uint64_t out1[5], const uint64_t arg1[5], const uint64_t arg2[5]) {
fiat_secp256k1_dettman_uint128 x1;
@@ -122,9 +122,9 @@ static FIAT_SECP256K1_DETTMAN_FIAT_INLINE void fiat_secp256k1_dettman_mul(uint64
* eval out1 mod 115792089237316195423570985008687907853269984665640564039457584007908834671663 = (eval arg1 * eval arg1) mod 115792089237316195423570985008687907853269984665640564039457584007908834671663
*
* Input Bounds:
- * arg1: [[0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1fffffffffffe]]
+ * arg1: [[0x0 ~> 0xfffffffffffff0], [0x0 ~> 0xfffffffffffff0], [0x0 ~> 0xfffffffffffff0], [0x0 ~> 0xfffffffffffff0], [0x0 ~> 0xffffffffffff0]]
* Output Bounds:
- * out1: [[0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x17fffffffffff]]
+ * out1: [[0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1fffffffffffe]]
*/
static FIAT_SECP256K1_DETTMAN_FIAT_INLINE void fiat_secp256k1_dettman_square(uint64_t out1[5], const uint64_t arg1[5]) {
uint64_t x1;
with no changes 64-bit code still. However, this change causes a number of intermediates in the 32-bit code to be assigned larger types than before, which I figure is why the more restrictive magnitude specifications got checked into fiat-crypto in the first place.
(Note on hiding previous comments: the generated specifications there are still covered by all proofs in fiat-crypto, and so is this one, but I think the previous specifications do not capture libsecp256k1 relies on.)
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 the correction. Magnitude 8 means the limbs are bounded by 0xFFFFFFFFFFFFF * 2 * 8, so I was off by a factor of 2.
* eval out1 mod 115792089237316195423570985008687907853269984665640564039457584007908834671663 = (eval arg1 * eval arg2) mod 115792089237316195423570985008687907853269984665640564039457584007908834671663 | ||
* | ||
* Input Bounds: | ||
* arg1: [[0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1ffffffffffffe], [0x0 ~> 0x1fffffffffffe]] |
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.
Would you willing to format your printed values in the comments with leading zeros to make everything 64 bits (or 32-bits) wide?
This comment was marked as spam.
This comment was marked as spam.
Sorry, something went wrong.
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.
@andres-erbsen, would you want to do that on fiat's side or should I just manually edit that?
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 don't have specific preferences, but perhaps a more general solution here is more likely to be typo-free. I'd be happy to implement this if that's what this PR needs to move forward.
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 assume it is not too tricky to do it on the fiat side. Let's do that and let the maintainers comment, what else is needed to get this merged :)
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 believe it is still unclear whether it is better to merge this or simply prove the existing C code correct in VST. Unfortunately, I don't think any conclusions were reached in the last meeting.
I've offered to move up my priority on creating a VST proof of the correctness of these functions (right after I finish up with the correctness of secp256k1_modinv64_divsteps_62_var
) so we have something to compare with, but I only work 1 day a week on VST.
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.
kindly bumping this as it has been a couple of weeks now. Has there been any decision made yet to (not) integrate that?
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've begun work on a VST proof of correctness of the current implementation of secp256k1_fe_mul_inner
. I've gone as far as proving the correctness of a functional implementation of the dettman algorithm. Next up is proving that if the input has suitably bounded inputs then the outputs will be suitably bounded, and then proving the correctness of the actual C implementation by showing none of the operations overflow and none of the VERIFY_CHECK statements fail (given the magnitude preconditions are satisfied).
Speaking only for myself, my thinking is that I complete this proof and then we can look at were we are with regards to these two (unfortunately incompatible) approaches to correctness and decide from there. Or I give up on my work. Or you folks call me out in not making progress.
Again, I only work 1 day a week on this project, so I'm guessing several more weeks will still be needed on my end.
I've finished the correctness proofs in VST of The proof itself is probably not all that interesting beyond the fact of its existence. You can step through it if you like and watch the Hoare clauses evolve as it is progresses through the C implementation. The proof also covers all the VERIFY_CHECK statements. I've also taken the liberty of applying #1438 and #1442, presuming that they will be merged, though the proof script itself works even without these PR applied. The more relevant bit is the formal specification of the functions that the proofs relate to. These are These specifications are written in the formal separation logic language of VST. These specification are made somewhat complicated by needing to support the fact that the I'm happy to go over these specification in this thread here, or perhaps at the next IRC meeting, or more generally discuss the future of this PR. The last meeting where this topic was discussed was https://gnusha.org/secp256k1/2023-07-31.log, and the formalization of these two functions is me addressing the comment
which we can now do. It's been 17 weeks since the last meeting, which means I worked a little bit less than 17 days on the effort. Let's call it three weeks of work. (Edit: I didn't start working on it right away, so maybe closer to two weeks of work.) As for this PR; my personal preference is to close it. With this VST proof in hand we have comparable assurance to what the fiat-crypto code gives us, but in a way that can let us proof the (functional) correctness of subsequent functions. At some point in time, if VST ever supports passing/returning structs, or fiat-crypto is able to generate VST compatible C code, or we get some other formal system able to practically reason about arbitrary C code, we can revisit the inclusion of the fiat-crypto code. However, whether or not to close this PR is certainly debatable, and we can discuss the issue here and/or at the next IRC meeting. I am not the decider. |
I think that's a good idea. |
This PR is result of #1261
In a nutshell, it replaces the current C-Implementation for the field arithmetic multiply and Square with the proven-correct implementation from the Fiat Cryptography Framework.