-
Notifications
You must be signed in to change notification settings - Fork 46
mirage-crypto-ec: implementation of SECP256K1 #259
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
base: main
Are you sure you want to change the base?
Conversation
|
CI failures seem to be unrelated. |
|
As an alternative I can also migrate the NIST curves to ECCKiila code, which is about 10% faster and would make the code simpler and more unified. On my Mac (M3, arm64) ecckiila: |
|
FWIW: I will also implement Brainpool curves. Please let me know, if you are interested in these as well. |
|
Thank you for your PR. Now, I have some questions:
Is this true across CPU architectures, or only on your Apple silicon? As you may have noticed, the other EC code is taken from projects with verification in mind, how much review and formal verification did ECCkiila get?
Sure, in the end it all depends on code clarity and binary size (we dropped P224 for the binary size reason). |
The license (MIT) is included in the generated file: mirage-crypto/ec/native/secp256k1.h Lines 1 to 24 in 1a4e3f2
The fiat code is automatically embedded in the file generated by ECCKiila. So, you want me to create the fiat files separately and remove them from the generated ECCKiila file? I sure can do that, just want to make sure we are on one page.
Unfortunately there doesn't seem to be any official or common test vectors for ECDSA signing and secp256k1, so I frankensteined them from some single test vectors I found at different places. (One of them is from Bouncy Castle IIRC)
Ok, but some code I had to change in order to make it reusable. Or would you prefer code duplication and untouched old code?
I just tested it on x86_64, there it is also around 10%.
You mean other projects than fiat-crypto? They don't have any formal verification beyond fiat. From their paper:
If the current EC arithmetic in mirage-crypto has stronger guarantees, that is of course an argument to keep the implementation, despite the slight performance difference. I don't know how much review it received, I only know that the fiat project referred you to it, which can be interpreted as a slight endorsement. Let me know what you prefer, and I will proceed accordingly. |
This change modularizes the point representation in preparation for the SECP256K1 implementation, which is based on ECCKiila and uses a different point representation.
|
I reduced the diff as much as possible without creating too much duplicate code, and split it up in two commits. The first is a pure restructuring without functional difference. The second is the addition of the new curve. Hope that helps to digest it. I added makefile targets for the fiat code generator (took ages to compile it), and replaced the embedded ones in the ECCKiila generated ones with |
I looked a bit more into the point add and double formulas ECCKiila is using for code generation. They are taken from this paper (which is also referenced in the comments of the generated code) that provides at least Magma code which provides "verification of the complete addition and exception-free doubling formulas". So there seems to be indeed some verification of the algorithm itself, and I guess if the tooling has no bugs, this is also true for the generated C code? Unfortunately I can't really tell how that compares to the guarantees of the implementations in |
This change implements the SECP256K1 curve (also known as the Bitcoin curve). - field primitives are generated by the fiat-crypto project[1] - point primitives are generated by the ECCKiila project[2] - Ocaml point operations are taken from NIST implementation, adapted to ECCKiila point primitives and optimized for a=0. - testvectors for ECDH and ECDSA verification from wycheproof[3] Closes: mirage#187 [1] https://github.com/mit-plv/fiat-crypto [2] https://gitlab.com/nisec/ecckiila [3] https://github.com/C2SP/wycheproof
|
FWIW: I did more comparisons of the ECCKiila implementation with the current implementation on x86_64 and arm64: x86_64 (Intel Core i7, 2.6GHz)
arm64 (MacBook M3)
The "u-sol" column is a ECCKiila with unsaturated-solinas fiat code for field arithmetic. (scalar arithmetic is still montgomery, couldn't create unsaturated-solinas for the order of P-521, probably wouldn't be that efficient anyway.) As you can see, there are some vast improvements for P-521, but the unsaturated-solinas could probably also be used independently of ECCKiila, I guess. |
CHANGES: * Use arc4random_buf instead of getrandom on Android before getrandom became available in API 28 (mirage/mirage-crypto#259 @jonahbeckford) * Define fill_bytes for MSVC (mirage/mirage-crypto#259 @jonahbeckford) * Update CI and remove DKML (mirage/mirage-crypto#262 mirage/mirage-crypto#265 @hannesm) * Update README (reported by @kit-ty-kat in mirage/mirage-crypto#263)
|
Briefly coming back to your PR. Thanks a lot. I wonder what are the benefits to include it into mirage-crypto-ec? Would a separate opam package (secp256k1) contain a lot of code duplication? (and given the amount of updates to mirage-crypto-ec, would that be a bad idea to maintain?) |
Since the package also includes Ed/X25519, which is a completely separate implementation, and it is an EC, it was just natural for me to include it in the In general I guess I have less issues with code duplication than the average developer. But in this specific case, where we would duplicate sensitive code like EC point arithmetic and transformations, I would rather swing to the reuseable-code side. BTW: I also implemented the BIP-340 Schnorr signature, which - like Secp256k1 - is important for Bitcoin applications. If you are interested, please have a look at my fork, which is completely ECCKiila based. I'm happy to bring upstream whatever is of interest. |
Code size. Maintenance. Since OCaml doesn't have a way to avoid unused modules in a binary (see ocaml/ocaml#608), every user of mirage-crypto-ec would inhibit this code (+ brainpool curves) -- where apart from "crypto" (Bitcoin, other blockchains) I see not much usage (i.e. in security protocols - ssh, tls, ...). Maintenance: if we merge this PR, suddenly someone needs to maintain the ~30_000 lines of code (plus another ~300_000 for the brainpool curves). Since some is generated, this may be simple (nevertheless, needs an eye on upstream to update the generated code when they change it [which may be due to security issues, performance, ...]) - but nevertheless it is something that needs to be done. I don't have that much time and energy to say "yes, of course, I want to maintain these curves". This is different for more widely used and useful curves (Ed448 comes to mind). This is why I thought, maybe you want to create a separate opam package that you maintain, and contribute it to the opam-repository? |
Binary size is indeed a good reason. I didn't think about that, because in our case we always need all of them, but of course for other users that should only be compiled and included if desired.
That is understandable. We need these curves anyway and we plan to be more "up-to-date" with latest mirage versions, if it is feasible. So we can
Ok, so to make sure I understand correctly: you are suggesting to use a separate repository for that, and not including these opam packages in the Since I have no experience maintaining an opam package: how would you recommend to organise the code? Thinking out loud: Certainly a separate opam package for each group of curves (secp256k1, brainpool, ...) to avoid binary bloat, but I would like to depend on mirage-crypto then as much as possible. OTOH I will also be using ECCKiila for the other curves, so there is indeed not much overlap left then, if you prefer to stay with the current implementation. Mainly just the ECDSA implementation. So I guess it's not worth the hassle to make the |
|
I reorganized the code and created a bunch of new packages, which allow to selectively include only the curves that are required. See my branch here: https://github.com/Nitrokey/nethsm-mirage-crypto/tree/nethsm-crypto Unfortunately, after all that work I realized, that we don't use Otherwise at the moment it looks like a lot more feasible to me to just extend |
This change implements the SECP256K1 curve (also known as the Bitcoin curve).
Closes: #187
[1] https://github.com/mit-plv/fiat-crypto
[2] https://gitlab.com/nisec/ecckiila
[3] https://github.com/C2SP/wycheproof