Skip to content
This repository has been archived by the owner on May 28, 2019. It is now read-only.

Add extmod for secp256k1-zkp #500

Merged
merged 1 commit into from
Apr 15, 2019
Merged

Conversation

real-or-random
Copy link
Contributor

@real-or-random real-or-random commented Mar 14, 2019

This includes the https://github.com/ElementsProject/secp256k1-zkp library (which is a fork from sipa/secp256k1 used in Bitcoin Core) and uses it for signing transactions and signing and verifying messages for ECDSA on the secp256k1 curve.

The Ethereum app is left untouched. I assume because they use Keccak-256 as a message digest and not SHA256. @apoelstra

This is the first step towards solving #282.

Some background to understand the open issues below:
secp256k1-zkp (and in fact sipa/secp256k1) relies on two precomputation tables to speed up signing and for verification. secp256k1(-zkp) supports that the precomputed table values for the signing table are stored in the executable (by defining USE_ECMULT_STATIC_PRECOMPUTATION) and thus do not need to be created at running time.

It's WIP because the following is still open (and maybe more):

  • secp256k1-zkp uses two "error callbacks". These are called when some bad error occurs and we don't know how to continue except for shutting down the execution properly, see https://github.com/bitcoin-core/secp256k1/blob/77668c09da756190e188521d87aaaa08736fa196/include/secp256k1.h#L218 and the function below for details. The default callbacks use abort() and fprintf(), so we need to replace them. I replaced them by functions which throw Python exceptions. Is this the right thing to do on trezor?
  • The size of the verification table is configurable. A larger table makes verification possibly faster. We set it to 4096 bytes (of RAM). Is this is fine for you as memory footprint? I guess even smaller is okay because verification speed is not so crucial. We need to see if we want to move that to CCMRAM too as mentioned in https://github.com/trezor/trezor-core/issues/282#issuecomment-467508352 .
  • The table(s) are stored in a secp256k1_context which is currently created when it's first used. Is that okay or do we want to create at program startup?
  • This currently relies on a dev branch of secp256k1-zkp. We'll merge this into master once we know that we're on the right track.
  • We wanted to enable USE_ECMULT_STATIC_PRECOMPUTATION but it increases the executable by 64 KiB, which is about 17 KiB too much after we rebased on master. To make the build work I've added a HACK commit that disables `USE_ECMULT_STATIC_PRECOMPUTATION in the firmware build again. But that means calling into secp256k1-zkp functions will fail at runtime because we don't give it enough memory to create the table. Note that it's probably really better to build this in the executable because otherwise we need 64 KiB of RAM instead of flash. Also, we need to keep in mind that currently no rangeproof functions are used, so those will be removed from the executable. If we really call these, the size will increase further.
  • There is naming collision between with the header secp256k1.h. For some reason the build currently works (at least on my machine) but this is probably fragile. Our goal is to make all of this work without the need to vendor specific secp256k1-zkp changes, and we'd rather not want to rename our header. I think it's easiest to rename your currently used secp256k1.h? Is that fine for you? Otherwise we can also use #includes with directories.

We opened this mostly to get some feedback on the build system. We can also decide to use this PR just for the build system and handle the other stuff in a different PR if that works better for you.

cc @romanz @apoelstra

secp256k1_pubkey pk;
if (!secp256k1_ecdsa_recover(ctx, &pk, &ec_sig, (const uint8_t *)dig.buf)) {
// Copying "return None" behaviour from secp256k1 module; but why do
// we do this rather than raising an exception?
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a TODO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is still open. We raise an exception for every other failure mode, so I see no reason why we should not raise an exception here. Any ideas?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, so the documentation of the function says that it should return None if the signature is invalid, so None makes sense. However, the actual behavior of the function is that either returns None or raises ValueError if the signature is invalid.

This inconsistency lead to a bug (#422), which was fixed in d5fb2a4 by handling both None and an exceptions equally in the caller code. (The Ethereum app is the only caller.)

I think this can be improved by making the function consistently return None no matter what the failure is, but in a different PR. I will remove your comment then at least here. If you agree with this conclusion, I'm volunteering to create that other PR, it should just take a few minutes.

@real-or-random
Copy link
Contributor Author

Note to self: We should also call secp256k1_context_randomize (https://github.com/bitcoin-core/secp256k1/blob/master/include/secp256k1.h#L641) after creating the context

@prusnak
Copy link
Member

prusnak commented Mar 15, 2019

  1. I would prefer not changing import secp256k1 to import secp256k1_zkp in our Python code via this pull request. It simplifies reviewing and I am not sure whether we are going to switch immediately. It makes sense to use secp256k1_zkp for Liquid, but I am not in favor of using it everywhere unless there are benchmarks done which prove the zkp implementation is faster and/or has other big advantages.
  2. I agree we should use directories in #include to avoid clashes. I will address this in another pull request.

@real-or-random
Copy link
Contributor Author

I would prefer not changing import secp256k1 to import secp256k1_zkp in our Python code via this pull request. It simplifies reviewing and I am not sure whether we are going to switch immediately. It makes sense to use secp256k1_zkp for Liquid, but I am not in favor of using it everywhere unless there are benchmarks done which prove the zkp implementation is faster and/or has other big advantages.

No problem, then we should first focus on just getting zkp imported and included in the build. I'll update this PR then. Anyway, it was still good to have these other commits to demonstrate that the build worked and can actually be used, and for the future.

I agree we should use directories in #include to avoid clashes. I will address this in another pull request.

Okay, I can also just do it when I update this.

@prusnak
Copy link
Member

prusnak commented Mar 15, 2019

Anyway, it was still good to have these other commits to demonstrate that the build worked and can actually be used, and for the future.

Sure, I 100% agree. To be clear, I am not opposed to using secp256k1_zkp globally for ECDSA/ECDH operations. But let's test that first.

SConscript.firmware Outdated Show resolved Hide resolved
@prusnak
Copy link
Member

prusnak commented Mar 15, 2019

Maybe we can leverage the fact the tests for secp256k1 and secp256k1_zkp ar the same.
Something like this comes to mind:

from trezor.crypto.curve import secp256k1, secp256k1_zkp

class Secp256k1Common(unittest.TestCase):
   # all existing unit tests go here

   def test_foo(self):
       # use impl.method instead of secp256k1.method

class TestCryptoSecp256k1(Secp256k1Common): 
    def __init__(self):
         self.impl = secp256k1

class TestCryptoSecp256k1Zkp(Secp256k1Common): 
    def __init__(self):
         self.impl = secp256k1_zkp

@romanz
Copy link
Contributor

romanz commented Mar 17, 2019

A small update to the PR (following a discussion with @real-or-random):
I have replaced f8b5bbc by 55a3948 (moving the read-only precomputed tables from the .flash to .flash2 section, which AFAIU is not used for storing executable code).
The resulting binary should compile and run successfully on TREZOR model T.

@prusnak
Copy link
Member

prusnak commented Mar 17, 2019

moving the read-only precomputed tables from the .flash to .flash2 section, which AFAIU is not used for storing executable code

Good!

@real-or-random
Copy link
Contributor Author

real-or-random commented Mar 20, 2019

I added two commits to address the file name issues and the size of the context.
(I'm leaving the replacement of the internal secp256k1 in at the moment for testing, we can remove that if everything looks good.)

@prusnak Do the callbacks look right to you?

@prusnak prusnak added this to the v2.1.1 milestone Mar 20, 2019
@prusnak
Copy link
Member

prusnak commented Mar 25, 2019

@real-or-random offtopic: How hard is to add NIST P-256 curve to secp256k1 library? Is this even possible or desired?

@apoelstra
Copy link
Contributor

@prusnak it would basically mean rewriting the library. It is neither possible nor desired :)

@prusnak
Copy link
Member

prusnak commented Mar 25, 2019

Got it :)

@real-or-random
Copy link
Contributor Author

We have now BlockstreamResearch/secp256k1-zkp#53 which (when merged) removes the need to use a modified branch/version of secp256k-zkp.

The remaining issues are the top three items in the list in first comment here in this PR
@prusnak Can you comment on these? :)

@prusnak
Copy link
Member

prusnak commented Apr 2, 2019

I replaced them by functions which throw Python exceptions. Is this the right thing to do on trezor?

Yes

4096 bytes (of RAM). Is this is fine for you as memory footprint?

Yes

The table(s) are stored in a secp256k1_context which is currently created when it's first used. Is that okay or do we want to create at program startup?

I think that's fine as a PoC.

@prusnak
Copy link
Member

prusnak commented Apr 2, 2019

Can you please squash the changes into a single commit and resolve conflicts (rebase on top of the current master)? We have reformatted our code using clang-format. Config is in the .clang-format file but it should be picked up automatically, just use clang-format -i on newly added C files. This should help with the resolution.

@real-or-random real-or-random changed the title WIP: Replace internal secp256k1 by secp256k1-zkp library Add extmod for secp256k1-zkp Apr 2, 2019
@real-or-random real-or-random force-pushed the wip-secp256k1-zkp branch 3 times, most recently from d6d2299 to f1934b9 Compare April 2, 2019 15:47
@real-or-random
Copy link
Contributor Author

Okay, I squashed and changed it to remove the commits that change the apps.
Two issues open left:

@real-or-random
Copy link
Contributor Author

The table(s) are stored in a secp256k1_context which is currently created when it's first used. Is that okay or do we want to create at program startup?

I think that's fine as a PoC.

Yes, indeed. I think in a PR that actually changes apps to use secp256k1-zkp, it's best to change this to a model where the verification table is only created when it's necessary (which is rare). Currently, we would create the verification table even when we would call into secp256k1-zkp for signing, which is kind of silly because signing is much more common and we don't need to build a table for signing (because the signing table it's statically built).

@real-or-random
Copy link
Contributor Author

real-or-random commented Apr 2, 2019

I added two commits (will squash again if you approve):

With these two commits, this PR is ready to be merged except that we need to wait for BlockstreamResearch/secp256k1-zkp#53 to be merged and adjust the submodule.

@prusnak
Copy link
Member

prusnak commented Apr 3, 2019

(will squash again if you approve):

Agreed. Please squash.

this PR is ready to be merged except that we need to wait for #53 to be merged and adjust the submodule.

Ack. we'll review it in the meantime. @jpochyla can you have a look, please?

@prusnak prusnak self-requested a review April 3, 2019 11:12
@real-or-random
Copy link
Contributor Author

Squashed.

@real-or-random
Copy link
Contributor Author

Okay, sorry, I pushed again because I additionally enabled some ASM optimizations for ASM in secp256k1-zkp.

@prusnak
Copy link
Member

prusnak commented Apr 11, 2019

Okay @real-or-random, great job! We are ready to merge. We'll wait until you merge BlockstreamResearch/secp256k1-zkp#53 and adjust the submodule in this PR.

@real-or-random
Copy link
Contributor Author

Okay force-pushed, I touched only .gitmodules. The other PR is not merged yet but should be today.

@apoelstra
Copy link
Contributor

Merged the other PR.

@prusnak
Copy link
Member

prusnak commented Apr 12, 2019

@real-or-random do you want to change the revision of the included submodule to reflect the last change from @apoelstra or should we merge as is?

@real-or-random
Copy link
Contributor Author

Oh yes I should have done this. I'll update it later. :)

This includes the https://github.com/ElementsProject/secp256k1-zkp library
(which is a fork from sipa/secp256k1 used in Bitcoin Core) as a module. It
is currently not used in any app.

This commit the first step towards integrating Liquid (tracking issue #282).

Note that this creates a new 64 kiB read-only data section in .flash2 for
pre-computed tables (secp256k1_ecmult_static_context) which speed up
signature creation and related private key operations.

Co-authored-by: Roman Zeyde <me@romanzey.de>
Co-authored-by: Andrew Poelstra <apoelstra@wpsoftware.net>
Co-authored-by: Pavol Rusnak <pavol@rusnak.io>
@real-or-random
Copy link
Contributor Author

fixed

@jpochyla jpochyla merged commit 52d3495 into trezor:master Apr 15, 2019
@prusnak
Copy link
Member

prusnak commented Apr 15, 2019

Great work @real-or-random, thanks for the contribution!

gmaxwell added a commit to bitcoin-core/secp256k1 that referenced this pull request May 9, 2019
d4d270a Allow field_10x26_arm.s to compile for ARMv7 architecture (Roman Zeyde)

Pull request description:

  It would allow using optimized field operations on the TREZOR device, which is using ARMv7 Cortex-M4.
  Following trezor/trezor-core#500 and part of trezor/trezor-firmware#66.

Tree-SHA512: 73c0f03503feff01c6f4efd884e916ae1f43f55d525e8c3ea9372cf777aef6901585b74774c316dd7937abfff5e86be5b1acb569f9eeee9b73ae088f0f6b589d
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants